From 1e59cd9e3561be8df396619fcf3e6f87a89a5e57 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:14:54 -0400 Subject: [PATCH 001/223] fix(utils): honor string drop_params values from config and DB deployments --- litellm/litellm_core_utils/core_helpers.py | 12 +++++++ litellm/types/router.py | 7 ++++ litellm/utils.py | 9 +++-- .../litellm_core_utils/test_core_helpers.py | 22 ++++++++++++ tests/test_litellm/test_router.py | 34 +++++++++++++++++++ tests/test_litellm/test_utils.py | 28 +++++++++++++++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 4 +++ 7 files changed, 113 insertions(+), 3 deletions(-) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index 002a46771e3..838019264c9 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -36,6 +36,18 @@ def safe_divide_seconds(seconds: float, denominator: float, default: Optional[fl return float(seconds / denominator) +def normalize_drop_params(value: object) -> bool | None: + if isinstance(value, bool): + return value + if isinstance(value, str): + lowered = value.strip().lower() + if lowered == "true": + return True + if lowered == "false": + return False + return None + + def safe_divide( numerator: Union[int, float], denominator: Union[int, float], diff --git a/litellm/types/router.py b/litellm/types/router.py index 28e4a8272e8..77d1815d28d 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida from typing_extensions import Protocol, Required, TypedDict, runtime_checkable from litellm._uuid import uuid +from litellm.litellm_core_utils.core_helpers import normalize_drop_params from .completion import CompletionRequest from .embedding import EmbeddingRequest @@ -233,6 +234,7 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): None # timeout when making stream=True calls, if str, pass in as os.environ/ ) max_retries: Optional[int] = None + drop_params: Optional[bool] = None organization: Optional[str] = None # for openai orgs configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS = None litellm_credential_name: Optional[str] = None @@ -311,6 +313,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): return filtered return data + @field_validator("drop_params", mode="before") + @classmethod + def coerce_drop_params(cls, value: object) -> Optional[bool]: + return normalize_drop_params(value) + def __contains__(self, key): # Define custom behavior for the 'in' operator return hasattr(self, key) diff --git a/litellm/utils.py b/litellm/utils.py index e19d2b36a52..e10b63ceb37 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -60,6 +60,7 @@ from litellm._lazy_imports import ( _get_token_counter_new, ) from litellm._uuid import uuid +from litellm.litellm_core_utils.core_helpers import normalize_drop_params from litellm.litellm_core_utils.fallback_generalizations import ( match_capability_generalizations, ) @@ -2852,7 +2853,7 @@ def get_optional_params_transcription( passed_params.pop("OPENAI_TRANSCRIPTION_PARAMS") custom_llm_provider = passed_params.pop("custom_llm_provider") - drop_params = passed_params.pop("drop_params") + drop_params = normalize_drop_params(passed_params.pop("drop_params")) special_params = passed_params.pop("kwargs") for k, v in special_params.items(): passed_params[k] = v @@ -2960,7 +2961,7 @@ def get_optional_params_image_gen( model = passed_params.pop("model", None) custom_llm_provider = passed_params.pop("custom_llm_provider") provider_config = passed_params.pop("provider_config", None) - drop_params = passed_params.pop("drop_params", None) + drop_params = normalize_drop_params(passed_params.pop("drop_params", None)) additional_drop_params = passed_params.pop("additional_drop_params", None) special_params = passed_params.pop("kwargs") for k, v in special_params.items(): @@ -3084,7 +3085,7 @@ def get_optional_params_embeddings( custom_llm_provider = passed_params.pop("custom_llm_provider", None) special_params = passed_params.pop("kwargs") - drop_params = passed_params.pop("drop_params", None) + drop_params = normalize_drop_params(passed_params.pop("drop_params", None)) additional_drop_params = passed_params.pop("additional_drop_params", None) allowed_openai_params = passed_params.pop("allowed_openai_params", None) or [] # Remove function objects from passed_params to avoid JSON serialization errors @@ -3797,6 +3798,8 @@ def get_optional_params( ): passed_params = locals().copy() special_params = passed_params.pop("kwargs") + drop_params = normalize_drop_params(drop_params) + passed_params["drop_params"] = drop_params # Remove base_model from passed_params so it doesn't interfere with # non_default_params / _check_valid_arg — it's a routing hint, not an # OpenAI param. diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index b67ea91bb0b..a7f93e3c997 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -5,6 +5,7 @@ import pytest from litellm.litellm_core_utils.core_helpers import ( _FINISH_REASON_MAP, map_finish_reason, + normalize_drop_params, reconstruct_model_name, redact_nested_match_and_regex_keys, ) @@ -201,3 +202,24 @@ class TestRedactNestedMatchAndRegexKeys: def test_passes_through_none_and_str(self): assert redact_nested_match_and_regex_keys(None) is None assert redact_nested_match_and_regex_keys("plain") == "plain" + + +@pytest.mark.parametrize( + "value, expected", + [ + (True, True), + (False, False), + ("true", True), + ("True", True), + (" TRUE ", True), + ("false", False), + ("False", False), + (None, None), + ("yes", None), + ("", None), + (1, None), + (0, None), + ], +) +def test_normalize_drop_params(value, expected): + assert normalize_drop_params(value) is expected diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index c2c98c8869c..81927d7e959 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -5430,3 +5430,37 @@ class TestRouterRequestTimeoutPropagation: ) == 60 ) + + +@pytest.mark.asyncio +async def test_router_deployment_drop_params_string_true_is_honored(monkeypatch): + from litellm import Router + + monkeypatch.setattr(litellm, "drop_params", False) + router = Router( + model_list=[ + { + "model_name": "gpt-5-nano", + "litellm_params": { + "model": "openai/gpt-5-nano", + "api_key": "sk-fake", + "temperature": 1, + "reasoning_effort": "minimal", + "drop_params": "true", + "mock_response": "Hello, world!", + }, + } + ], + num_retries=0, + ) + + deployment = router.get_deployment_by_model_group_name(model_group_name="gpt-5-nano") + assert deployment is not None + assert deployment.litellm_params.drop_params is True + + response = await router.acompletion( + model="gpt-5-nano", + messages=[{"role": "user", "content": "hi"}], + temperature=0.1, + ) + assert response.choices[0].message.content == "Hello, world!" diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 073ff17991e..0fbf9c0db20 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -4814,3 +4814,31 @@ def test_is_prompt_caching_valid_prompt_explicit_min_token_count_overrides_model is_prompt_caching_valid_prompt(model="claude-opus-4-8", messages=PROMPT_CACHE_MESSAGES, min_token_count=8192) is False ) + + +class TestDropParamsStringCoercion: + @pytest.mark.parametrize("drop_params", ["true", "True", True]) + def test_truthy_drop_params_drops_unsupported_temperature(self, drop_params, monkeypatch): + from litellm.utils import get_optional_params + + monkeypatch.setattr(litellm, "drop_params", False) + result = get_optional_params( + model="gpt-5-nano", + custom_llm_provider="openai", + temperature=0.1, + drop_params=drop_params, + ) + assert "temperature" not in result + + @pytest.mark.parametrize("drop_params", ["false", False, None]) + def test_falsy_drop_params_still_raises(self, drop_params, monkeypatch): + from litellm.utils import get_optional_params + + monkeypatch.setattr(litellm, "drop_params", False) + with pytest.raises(litellm.UnsupportedParamsError): + get_optional_params( + model="gpt-5-nano", + custom_llm_provider="openai", + temperature=0.1, + drop_params=drop_params, + ) diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 0d8f55164f9..62103e4f742 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -25667,6 +25667,8 @@ export interface components { default_api_key_rpm_limit?: number | null; /** Default Api Key Tpm Limit */ default_api_key_tpm_limit?: number | null; + /** Drop Params */ + drop_params?: boolean | null; /** Gcs Bucket Name */ gcs_bucket_name?: string | null; /** Input Cost Per Audio Per Second */ @@ -33503,6 +33505,8 @@ export interface components { default_api_key_rpm_limit?: number | null; /** Default Api Key Tpm Limit */ default_api_key_tpm_limit?: number | null; + /** Drop Params */ + drop_params?: boolean | null; /** Gcs Bucket Name */ gcs_bucket_name?: string | null; /** Input Cost Per Audio Per Second */ From 3831e66d2bcd4e458ba4a5dd6cfa5095636e4c7f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 01:32:13 +0000 Subject: [PATCH 002/223] fix(budget_reservation): don't reserve budget on token counting routes Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../spend_tracking/budget_reservation.py | 12 ++++- .../proxy/test_budget_reservation.py | 47 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/spend_tracking/budget_reservation.py b/litellm/proxy/spend_tracking/budget_reservation.py index 58a85171cc7..7b62fd44d09 100644 --- a/litellm/proxy/spend_tracking/budget_reservation.py +++ b/litellm/proxy/spend_tracking/budget_reservation.py @@ -144,6 +144,16 @@ async def _apply_over_budget_reservation_policy( ) +_UNBILLED_ROUTES: Final[frozenset[str]] = frozenset({"/models", "/v1/models", "/utils/token_counter"}) +_UNBILLED_ROUTE_SUFFIXES: Final[tuple[str, ...]] = ("/v1/messages/count_tokens", ":countTokens") + + +def _is_unbilled_route(route: str) -> bool: + """Routes that never emit a cost-tracking callback. Reserving budget for them + is a permanent leak: nothing ever reconciles or releases the reservation.""" + return route in _UNBILLED_ROUTES or route.endswith(_UNBILLED_ROUTE_SUFFIXES) + + async def reserve_budget_for_request( request_body: dict, route: str, @@ -161,7 +171,7 @@ async def reserve_budget_for_request( ) -> dict | None: if valid_token is None or not RouteChecks.is_llm_api_route(route=route): return None - if route in {"/models", "/v1/models", "/utils/token_counter"}: + if _is_unbilled_route(route): return None if get_model_from_request(request_body, route, llm_router=llm_router) is None: return None diff --git a/tests/test_litellm/proxy/test_budget_reservation.py b/tests/test_litellm/proxy/test_budget_reservation.py index 34adb4d2091..7bdc73abf32 100644 --- a/tests/test_litellm/proxy/test_budget_reservation.py +++ b/tests/test_litellm/proxy/test_budget_reservation.py @@ -2583,3 +2583,50 @@ async def test_streaming_slow_path_processes_and_yields_chunk(spend_counter_stat assert received == [{"content": "hi"}] streaming_logging_obj.async_post_call_streaming_hook.assert_awaited_once() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "route", + [ + "/v1/messages/count_tokens", + "/anthropic/v1/messages/count_tokens", + "/v1beta/models/gemini-2.5-pro:countTokens", + "/models/gemini-2.5-pro:countTokens", + ], +) +async def test_token_counting_routes_never_reserve_budget(spend_counter_state, route): + """Token counting is free and never fires a cost callback, so a reservation + there is never reconciled and permanently bricks the key's spend counter.""" + counter_cache, key_cache = spend_counter_state + proxy_logging_obj = ProxyLogging(user_api_key_cache=key_cache) + valid_token = UserAPIKeyAuth( + token="key-count-tokens", + spend=0.0, + max_budget=0.01, + ) + + with patch( + "litellm.proxy.spend_tracking.budget_reservation.estimate_request_max_cost", + return_value=0.01, + ): + for _ in range(2): + assert ( + await reserve_budget_for_request( + request_body=_request_body(), + route=route, + llm_router=None, + valid_token=valid_token, + team_object=None, + user_object=None, + prisma_client=None, + user_api_key_cache=key_cache, + proxy_logging_obj=proxy_logging_obj, + ) + is None + ) + + assert counter_cache.in_memory_cache.get_cache(key="spend:key:key-count-tokens") is None + + # a real completion on the same key is still budget enforced + assert await _reserve(valid_token, 0.01, key_cache, proxy_logging_obj) is not None From f01309c5afaf576e15170699d94185bd01b4835c Mon Sep 17 00:00:00 2001 From: ZXT-zjbiliy <3240102335@zju.edu.cn> Date: Fri, 21 Aug 2026 13:56:30 +0800 Subject: [PATCH 003/223] fix(stream_chunk_builder): guard empty choices and missing role in build_base_response build_base_response() read the assistant role via first_chunk_with_choices["choices"][0]["delta"]["role"] with no bounds or key check, causing two failures: - IndexError when no chunk carries a non-empty "choices" array, because next() fell back to the first chunk whose "choices" may be [] - KeyError when the first choice's "delta" omits "role" or is {} Both surface as "litellm.APIError: Error building chunks for logging/streaming usage calculation". async_data_generator() writes that into the response stream, so the client's answer is truncated mid-stream with no data: [DONE], and the request never reaches SpendLogs. Observed in production on Anthropic streaming. Fall back to None, guard the array length, and default the role to "assistant". The loop directly below already guards with len(chunk["choices"]) > 0. --- .../streaming_chunk_builder_utils.py | 11 +- .../test_streaming_chunk_builder_utils.py | 103 ++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py index ee0518c4aec..59096cfaff7 100644 --- a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py +++ b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py @@ -302,8 +302,15 @@ class ChunkProcessor: model: Final = ChunkProcessor._get_model_from_chunks(chunks, first_chunk_model) system_fingerprint: Final = chunk.get("system_fingerprint", None) - first_chunk_with_choices: Final = next((c for c in chunks if c.get("choices")), chunk) - role: Final = first_chunk_with_choices["choices"][0]["delta"]["role"] + # Fall back to None rather than `chunk`: if no chunk carries a non-empty + # `choices` array, indexing [0] on the first chunk raises IndexError. + first_chunk_with_choices = next((c for c in chunks if c.get("choices")), None) + role: str = "assistant" + if first_chunk_with_choices is not None: + _choices = first_chunk_with_choices["choices"] + if len(_choices) > 0: + # `delta` may be absent or omit `role` (e.g. content-only deltas). + role = _choices[0].get("delta", {}).get("role") or "assistant" finish_reason = "stop" for chunk in chunks: if "choices" in chunk and len(chunk["choices"]) > 0: diff --git a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py index 0f21cce476b..aec189da653 100644 --- a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py @@ -1342,3 +1342,106 @@ def test_calculate_usage_fills_unknown_split_from_reasoning_estimate( assert usage.completion_tokens == 100 assert usage.completion_tokens_details.reasoning_tokens == expected_reasoning_tokens assert usage.completion_tokens_details.text_tokens == expected_text_tokens + + +def _empty_choices_chunk(**extra): + chunk = { + "id": "chatcmpl-empty-choices", + "object": "chat.completion.chunk", + "created": 1, + "model": "claude-opus-4-8", + "choices": [], + } + chunk.update(extra) + return chunk + + +@pytest.mark.parametrize( + "chunks", + [ + pytest.param( + [_empty_choices_chunk(), _empty_choices_chunk()], + id="all_chunks_have_empty_choices", + ), + pytest.param( + [ + _empty_choices_chunk(usage={"prompt_tokens": 10}), + _empty_choices_chunk(usage={"completion_tokens": 0}), + ], + id="usage_only_chunks", + ), + ], +) +def test_build_base_response_handles_empty_choices(chunks): + """Empty `choices` arrays must not raise IndexError. + + `next((c for c in chunks if c.get("choices")), chunk)` used to fall back to the + first chunk, whose `choices` may be `[]`, so `["choices"][0]` went out of range. + The resulting error is surfaced to the client mid-stream and the request never + reaches SpendLogs. + """ + processor = ChunkProcessor(chunks=list(chunks)) + + response = processor.build_base_response(list(chunks)) + + assert response.choices[0].message.role == "assistant" + + +@pytest.mark.parametrize( + "delta", + [ + pytest.param({"content": "Hello"}, id="delta_without_role"), + pytest.param({}, id="delta_empty_dict"), + ], +) +def test_build_base_response_handles_delta_without_role(delta): + """A `delta` that omits `role` must not raise KeyError.""" + chunks = [ + { + "id": "chatcmpl-no-role", + "object": "chat.completion.chunk", + "created": 1, + "model": "claude-opus-4-8", + "choices": [{"index": 0, "delta": delta, "finish_reason": None}], + } + ] + processor = ChunkProcessor(chunks=list(chunks)) + + response = processor.build_base_response(list(chunks)) + + assert response.choices[0].message.role == "assistant" + + +def test_build_base_response_still_reads_role_and_finish_reason(): + """Regression guard: well-formed chunks keep their role and finish_reason.""" + chunks = [ + _empty_choices_chunk(), + { + "id": "chatcmpl-normal", + "object": "chat.completion.chunk", + "created": 1, + "model": "claude-opus-4-8", + "choices": [ + { + "index": 0, + "delta": {"role": "assistant", "content": "Hi"}, + "finish_reason": None, + } + ], + }, + { + "id": "chatcmpl-normal", + "object": "chat.completion.chunk", + "created": 2, + "model": "claude-opus-4-8", + "choices": [ + {"index": 0, "delta": {"content": "!"}, "finish_reason": "stop"} + ], + }, + ] + processor = ChunkProcessor(chunks=list(chunks)) + + response = processor.build_base_response(list(chunks)) + + assert response.choices[0].message.role == "assistant" + assert response.choices[0].finish_reason == "stop" From dfcea2c1866630313ec3083794a922ff6971583a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:16:00 -0700 Subject: [PATCH 004/223] fix(policy_engine): execute post_call guardrail pipelines on responses --- litellm/proxy/utils.py | 47 +++++- .../proxy_logging/test_guardrail_pipeline.py | 152 +++++++++++++++++- 2 files changed, 196 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index d880b529727..bd8ba6ae9f0 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -455,6 +455,30 @@ def _pipeline_managed_guardrail_names(data: Mapping[str, object]) -> frozenset[s ) +def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None: + if data.get("stream") is not True: + return + post_call_policies: Final = tuple( + policy_name for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == "post_call" + ) + if not post_call_policies: + return + raise HTTPException( + status_code=400, + detail={ + "error": { + "message": ( + "Policies with post_call guardrail pipelines cannot govern streaming responses yet: " + f"{', '.join(post_call_policies)}. Retry with stream=false, or move these policies' output " + "guardrails from pipeline steps to guardrails.add, which scans streamed output." + ), + "type": "guardrail_pipeline_error", + "policies": list(post_call_policies), + } + }, + ) + + def _prompt_block_text(block: object) -> str: if isinstance(block, str): return block @@ -1578,6 +1602,7 @@ class ProxyLogging: call_type: str, event_hook: str, raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data + response: LLMResponseTypes | None = None, ) -> dict: """ Execute guardrail pipelines if any are configured for this request. @@ -1596,6 +1621,8 @@ class ProxyLogging: if not pipelines: return data + step_input: Final = {**data, "response": response} if response is not None else data + for policy_name, pipeline in pipelines: if pipeline.mode != event_hook: continue @@ -1603,7 +1630,7 @@ class ProxyLogging: result: PipelineExecutionResult = await PipelineExecutor.execute_steps( steps=pipeline.steps, mode=pipeline.mode, - data=data, + data=step_input, user_api_key_dict=user_api_key_dict, call_type=call_type, policy_name=policy_name, @@ -1614,6 +1641,7 @@ class ProxyLogging: result=result, data=data, policy_name=policy_name, + original_response=response, ) return data @@ -1623,14 +1651,18 @@ class ProxyLogging: result: PipelineExecutionResult, data: dict, policy_name: str, + original_response: LLMResponseTypes | None = None, ) -> dict: """ Handle a PipelineExecutionResult — allow, block, or modify_response. Returns data dict if allowed, raises on block/modify_response. + ``original_response`` is set on the post_call path, where allowed + modifications land on the response object in place, so the request + payload (already sent upstream) is left untouched. """ if result.terminal_action == "allow": - if result.modified_data is not None: + if result.modified_data is not None and original_response is None: data.update(result.modified_data) return data @@ -1671,6 +1703,7 @@ class ProxyLogging: request_data=data, guardrail_name=f"pipeline:{policy_name}", detection_info=None, + original_response=original_response, ) return data @@ -1786,6 +1819,8 @@ class ProxyLogging: ) try: + _raise_for_streaming_post_call_pipelines(data) + # Execute guardrail pipelines before the normal callback loop data = await self._maybe_execute_pipelines( data=data, @@ -2774,6 +2809,14 @@ class ProxyLogging: from litellm.proxy.proxy_server import llm_router from litellm.types.guardrails import GuardrailEventHooks + await self._maybe_execute_pipelines( + data=data, + user_api_key_dict=user_api_key_dict, + call_type=getattr(data.get("litellm_logging_obj"), "call_type", None) or "acompletion", + event_hook="post_call", + response=response, + ) + guardrail_callbacks: Final[list[CustomGuardrail]] = [] other_callbacks: Final[list[CustomLogger]] = [] try: diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index e99e34d65d4..b7e86b68fe4 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -23,7 +23,7 @@ from litellm.integrations.custom_guardrail import ( ModifyResponseException, ) from litellm.integrations.prometheus import PrometheusLogger -from litellm.proxy.utils import ProxyLogging +from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines from litellm.types.guardrails import GuardrailEventHooks from litellm.types.proxy.policy_engine.pipeline_types import ( GuardrailPipeline, @@ -865,3 +865,153 @@ async def test_process_prompt_template_aresponses_swaps_model_and_merges_input(p hook_kwargs = logging_obj.async_get_chat_completion_prompt.await_args.kwargs assert hook_kwargs["messages"] == [{"role": "user", "content": "Who are you?"}] assert hook_kwargs["prompt_spec"] is prompt_spec + + +# --------------------------------------------------------------------------- +# post_call pipeline execution (LIT-6410) +# --------------------------------------------------------------------------- + + +def _post_call_pipeline_data(guardrail: str = "gr-post", **extra: Any) -> Dict[str, Any]: + pipeline = GuardrailPipeline( + mode="post_call", + steps=[PipelineStep(guardrail=guardrail, on_pass="allow", on_fail="block")], + ) + return { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [("response-governance", pipeline)], + "_pipeline_managed_guardrails": {guardrail}, + }, + **extra, + } + + +@pytest.mark.asyncio +async def test_post_call_success_hook_runs_post_call_pipeline_and_reraises_block( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + + class OutputBlockingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["response"] = response + raise HTTPException(status_code=400, detail={"error": "output blocked"}) + + monkeypatch.setattr( + litellm, + "callbacks", + [OutputBlockingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + response = litellm.ModelResponse() + + with pytest.raises(HTTPException) as info: + await proxy_logging.post_call_success_hook( + data=data, response=response, user_api_key_dict=make_user_api_key_auth() + ) + + assert info.value.detail["error"] == "output blocked" + assert seen["response"] is response + + +@pytest.mark.asyncio +async def test_post_call_pipeline_pass_runs_once_and_leaves_request_data_untouched( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class RecordingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["count"] += 1 + seen["response"] = response + return None + + monkeypatch.setattr( + litellm, + "callbacks", + [RecordingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + response = litellm.ModelResponse() + + out = await proxy_logging.post_call_success_hook( + data=data, response=response, user_api_key_dict=make_user_api_key_auth() + ) + + assert out is response + assert seen["response"] is response + assert seen["count"] == 1 + assert "response" not in data + assert "guardrails" not in data["metadata"] + + +def test_handle_pipeline_result_modify_response_carries_original_response(): + result = MagicMock() + result.terminal_action = "modify_response" + result.modify_response_message = "filtered" + response = litellm.ModelResponse() + + with pytest.raises(ModifyResponseException) as info: + ProxyLogging._handle_pipeline_result( + result=result, data={"model": "m"}, policy_name="p", original_response=response + ) + + assert info.value.original_response is response + + +def test_handle_pipeline_result_allow_discards_modifications_on_post_call(): + data = {"a": 1, "metadata": {"guardrails": ["other"]}} + result = MagicMock() + result.terminal_action = "allow" + result.modified_data = {"metadata": {"guardrails": ["gr-post"]}, "response": object()} + + out = ProxyLogging._handle_pipeline_result( + result=result, data=data, policy_name="p", original_response=litellm.ModelResponse() + ) + + assert out is data + assert data == {"a": 1, "metadata": {"guardrails": ["other"]}} + + +@pytest.mark.asyncio +async def test_pre_call_hook_rejects_streaming_request_with_post_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch +): + monkeypatch.setattr(litellm, "callbacks", []) + data = _post_call_pipeline_data(stream=True) + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), + data=data, + call_type="completion", + guardrails_only=True, + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["policies"] == ["response-governance"] + assert "stream=false" in info.value.detail["error"]["message"] + + +def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_call(): + post_call = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) + pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) + + assert ( + _raise_for_streaming_post_call_pipelines( + {"stream": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}} + ) + is None + ) + assert _raise_for_streaming_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", post_call)]}}) is None + assert ( + _raise_for_streaming_post_call_pipelines( + {"stream": True, "metadata": {"_guardrail_pipelines": [("p", pre_call)]}} + ) + is None + ) + assert _raise_for_streaming_post_call_pipelines({"stream": True}) is None From e6edd62f5d0f010d34c203d9df8192462a1622c0 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:40:48 -0700 Subject: [PATCH 005/223] fix(policy_engine): propagate post_call pipeline replacement responses to the client --- .../proxy/policy_engine/pipeline_executor.py | 19 +++-- litellm/proxy/utils.py | 32 +++++--- .../proxy_logging/test_guardrail_pipeline.py | 81 ++++++++++++++++++- .../utils/proxy_logging/test_pre_call_hook.py | 2 +- 4 files changed, 112 insertions(+), 22 deletions(-) diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index a5619821197..190784a2a60 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -108,8 +108,10 @@ class PipelineExecutor: action, ) - # Forward modified data to next step if pass_data is True - if step.pass_data and modified_data is not None: + # Forward modified data to the next step if pass_data is True; + # post_call response replacements always chain, matching the flat + # callback loop where each hook sees the previous hook's response + if modified_data is not None and (step.pass_data or mode == "post_call"): working_data = {**working_data, **modified_data} # Handle terminal actions @@ -227,11 +229,14 @@ class PipelineExecutor: # same contract as run_in_parallel/scan_raw_request elsewhere: any # data it returned is discarded, since applying it on top of the # raw snapshot would silently undo whatever an earlier step in - # this pipeline already did. - modified_data = None - if response is not None and isinstance(response, dict) and not scans_raw_request: - modified_data = response - return ("pass", modified_data, None, None) + # this pipeline already did. A post_call hook's non-None return is + # a replacement response (the flat callback-loop contract), carried + # under the same "response" key the step input uses. + if response is None or scans_raw_request: + return ("pass", None, None, None) + if mode == "post_call": + return ("pass", {"response": response}, None, None) + return ("pass", response if isinstance(response, dict) else None, None, None) except Exception as e: if CustomGuardrail._is_guardrail_intervention(e): diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index bd8ba6ae9f0..25c1068cc6d 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1603,7 +1603,7 @@ class ProxyLogging: event_hook: str, raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data response: LLMResponseTypes | None = None, - ) -> dict: + ) -> tuple[dict, LLMResponseTypes | None]: """ Execute guardrail pipelines if any are configured for this request. @@ -1615,18 +1615,21 @@ class ProxyLogging: ``scan_raw_request`` evaluates the pristine request, not whatever an earlier ``pass_data`` step in the same pipeline already rewrote. - Returns the (possibly modified) data dict. + Returns the (possibly modified) data dict, plus the replacement + response when a post_call pipeline step returned one (None when the + response is unchanged), matching the flat callback-loop contract. """ pipelines: Final = _policy_pipelines(data) if not pipelines: - return data - - step_input: Final = {**data, "response": response} if response is not None else data + return data, None + current_response = response # rebind-ok: chains each pipeline's replacement response into the next for policy_name, pipeline in pipelines: if pipeline.mode != event_hook: continue + step_input: dict = {**data, "response": current_response} if current_response is not None else data + result: PipelineExecutionResult = await PipelineExecutor.execute_steps( steps=pipeline.steps, mode=pipeline.mode, @@ -1641,10 +1644,13 @@ class ProxyLogging: result=result, data=data, policy_name=policy_name, - original_response=response, + original_response=current_response, ) - return data + if current_response is not None and result.modified_data is not None: + current_response = result.modified_data.get("response", current_response) + + return data, current_response if current_response is not response else None @staticmethod def _handle_pipeline_result( @@ -1657,9 +1663,9 @@ class ProxyLogging: Handle a PipelineExecutionResult — allow, block, or modify_response. Returns data dict if allowed, raises on block/modify_response. - ``original_response`` is set on the post_call path, where allowed - modifications land on the response object in place, so the request - payload (already sent upstream) is left untouched. + ``original_response`` is set on the post_call path, where the request + payload (already sent upstream) must stay untouched; a replacement + response carried in ``modified_data`` is adopted by the caller. """ if result.terminal_action == "allow": if result.modified_data is not None and original_response is None: @@ -1822,7 +1828,7 @@ class ProxyLogging: _raise_for_streaming_post_call_pipelines(data) # Execute guardrail pipelines before the normal callback loop - data = await self._maybe_execute_pipelines( + data, _ = await self._maybe_execute_pipelines( data=data, user_api_key_dict=user_api_key_dict, call_type=call_type, @@ -2809,13 +2815,15 @@ class ProxyLogging: from litellm.proxy.proxy_server import llm_router from litellm.types.guardrails import GuardrailEventHooks - await self._maybe_execute_pipelines( + _, pipeline_response = await self._maybe_execute_pipelines( data=data, user_api_key_dict=user_api_key_dict, call_type=getattr(data.get("litellm_logging_obj"), "call_type", None) or "acompletion", event_hook="post_call", response=response, ) + if pipeline_response is not None: + response = pipeline_response # rebind-ok: adopt the pipeline's replacement response, same contract as the callback loops below guardrail_callbacks: Final[list[CustomGuardrail]] = [] other_callbacks: Final[list[CustomLogger]] = [] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index b7e86b68fe4..8bc71f6e178 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -326,13 +326,14 @@ def test_process_guardrail_metadata_invalid_data_raises(proxy_logging): @pytest.mark.asyncio async def test_maybe_execute_pipelines_no_pipelines_returns_data(proxy_logging, make_user_api_key_auth): data = {"messages": [{"role": "user"}], "model": "m", "temperature": 0.1} - out = await proxy_logging._maybe_execute_pipelines( + out, replacement = await proxy_logging._maybe_execute_pipelines( data=data, user_api_key_dict=make_user_api_key_auth(), call_type="completion", event_hook="pre_call", ) assert out == {"messages": [{"role": "user"}], "model": "m", "temperature": 0.1} + assert replacement is None @pytest.mark.asyncio @@ -344,7 +345,7 @@ async def test_maybe_execute_pipelines_skips_pipelines_with_other_mode(proxy_log monkeypatch.setattr( "litellm.proxy.policy_engine.pipeline_executor.PipelineExecutor.execute_steps", executed ) - out = await proxy_logging._maybe_execute_pipelines( + out, replacement = await proxy_logging._maybe_execute_pipelines( data=data, user_api_key_dict=make_user_api_key_auth(), call_type="completion", @@ -352,6 +353,7 @@ async def test_maybe_execute_pipelines_skips_pipelines_with_other_mode(proxy_log ) executed.assert_not_called() assert out is data + assert replacement is None @pytest.mark.parametrize( @@ -949,6 +951,81 @@ async def test_post_call_pipeline_pass_runs_once_and_leaves_request_data_untouch assert "guardrails" not in data["metadata"] +@pytest.mark.asyncio +async def test_post_call_pipeline_replacement_response_reaches_caller( + proxy_logging, make_user_api_key_auth, monkeypatch +): + masked = litellm.ModelResponse() + + class MaskingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + return masked + + monkeypatch.setattr( + litellm, + "callbacks", + [MaskingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + + out = await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert out is masked + assert "response" not in data + + +@pytest.mark.asyncio +async def test_post_call_pipeline_replacement_chains_to_next_step_without_pass_data( + proxy_logging, make_user_api_key_auth, monkeypatch +): + masked = litellm.ModelResponse() + seen: Dict[str, Any] = {} + + class MaskingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + return masked + + class RecordingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["response"] = response + return None + + pipeline = GuardrailPipeline( + mode="post_call", + steps=[ + PipelineStep(guardrail="gr-mask", on_pass="next", on_fail="block"), + PipelineStep(guardrail="gr-audit", on_pass="allow", on_fail="block"), + ], + ) + monkeypatch.setattr( + litellm, + "callbacks", + [ + MaskingGuardrail(guardrail_name="gr-mask", event_hook=GuardrailEventHooks.post_call, default_on=False), + RecordingGuardrail(guardrail_name="gr-audit", event_hook=GuardrailEventHooks.post_call, default_on=False), + ], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [("response-governance", pipeline)], + "_pipeline_managed_guardrails": {"gr-mask", "gr-audit"}, + }, + } + + out = await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert out is masked + assert seen["response"] is masked + + def test_handle_pipeline_result_modify_response_carries_original_response(): result = MagicMock() result.terminal_action = "modify_response" diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_pre_call_hook.py b/tests/test_litellm/proxy/utils/proxy_logging/test_pre_call_hook.py index 0971ce09d79..06cf328a20c 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_pre_call_hook.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_pre_call_hook.py @@ -660,7 +660,7 @@ async def test_scan_raw_request_snapshot_taken_before_pipelines( for msg in data.get("messages", []): if "SECRET" in msg.get("content", ""): msg["content"] = msg["content"].replace("SECRET", "[REDACTED]") - return data + return data, None monkeypatch.setattr(ProxyLogging, "_maybe_execute_pipelines", fake_pipelines) monkeypatch.setattr(litellm, "callbacks", [_BlockOnSecretGuardrail(scan_raw_request=True)]) From aeac6a412c98c07cce64c5ddbd74d005f2e60e7e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:59:15 -0700 Subject: [PATCH 006/223] fix(policy_engine): skip pipeline-managed guardrails in the response-path guardrail loop --- litellm/proxy/utils.py | 9 ++++++- .../proxy_logging/test_guardrail_pipeline.py | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 25c1068cc6d..75cc5c259a7 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -2825,6 +2825,7 @@ class ProxyLogging: if pipeline_response is not None: response = pipeline_response # rebind-ok: adopt the pipeline's replacement response, same contract as the callback loops below + pipeline_managed: Final = _pipeline_managed_guardrail_names(data) guardrail_callbacks: Final[list[CustomGuardrail]] = [] other_callbacks: Final[list[CustomLogger]] = [] try: @@ -2849,12 +2850,18 @@ class ProxyLogging: guardrail_data: Final = _check_and_merge_model_level_guardrails(data=data, llm_router=llm_router) parallel_guardrails: Final[tuple[CustomGuardrail, ...]] = tuple( - callback for callback in guardrail_callbacks if getattr(callback, "run_in_parallel", False) + callback + for callback in guardrail_callbacks + if getattr(callback, "run_in_parallel", False) + and not (callback.guardrail_name and callback.guardrail_name in pipeline_managed) ) for callback in guardrail_callbacks: # Main - V2 Guardrails implementation + if callback.guardrail_name and callback.guardrail_name in pipeline_managed: + continue + if getattr(callback, "run_in_parallel", False): continue diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 8bc71f6e178..c92f5fa6c55 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -951,6 +951,32 @@ async def test_post_call_pipeline_pass_runs_once_and_leaves_request_data_untouch assert "guardrails" not in data["metadata"] +@pytest.mark.asyncio +async def test_post_call_pipeline_managed_default_on_guardrail_runs_exactly_once( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class CountingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["count"] += 1 + return None + + monkeypatch.setattr( + litellm, + "callbacks", + [CountingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=True)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + + await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert seen["count"] == 1 + + @pytest.mark.asyncio async def test_post_call_pipeline_replacement_response_reaches_caller( proxy_logging, make_user_api_key_auth, monkeypatch From 55569729b05d601c139e43b8faba447983e89f74 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 28 Aug 2026 18:16:35 -0700 Subject: [PATCH 007/223] fix(policy_engine): scope pipeline-managed guardrail skips to the pipeline's mode --- litellm/proxy/utils.py | 20 ++--- .../proxy_logging/test_guardrail_pipeline.py | 73 +++++++++++++++++++ 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 75cc5c259a7..d3f2e1d7301 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -11,7 +11,7 @@ import sys import threading import time import traceback -from collections.abc import AsyncGenerator, Awaitable, Callable, Collection, Coroutine, Mapping, Sequence +from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Mapping, Sequence from dataclasses import dataclass, field from datetime import date, datetime, timedelta, timezone from email.mime.multipart import MIMEMultipart @@ -446,12 +446,14 @@ def _policy_pipelines(data: Mapping[str, object]) -> tuple[tuple[str, "Guardrail ) -def _pipeline_managed_guardrail_names(data: Mapping[str, object]) -> frozenset[str]: - managed: Final = _policy_state_metadata(data).get("_pipeline_managed_guardrails") - return ( - frozenset(cast("Collection[str]", managed)) # cast-ok: the policy engine wrote these guardrail names - if managed - else frozenset() +def _pipeline_managed_guardrail_names( + data: Mapping[str, object], mode: Literal["pre_call", "post_call"] +) -> frozenset[str]: + return frozenset( + step.guardrail + for _policy_name, pipeline in _policy_pipelines(data) + if pipeline.mode == mode + for step in pipeline.steps ) @@ -1837,7 +1839,7 @@ class ProxyLogging: ) # Get pipeline-managed guardrails to skip in normal loop - pipeline_managed: Final = _pipeline_managed_guardrail_names(data) + pipeline_managed: Final = _pipeline_managed_guardrail_names(data, "pre_call") caps: Final = ProxyLogging._callback_capabilities() # Skip the per-request callback walk entirely when nothing in @@ -2825,7 +2827,7 @@ class ProxyLogging: if pipeline_response is not None: response = pipeline_response # rebind-ok: adopt the pipeline's replacement response, same contract as the callback loops below - pipeline_managed: Final = _pipeline_managed_guardrail_names(data) + pipeline_managed: Final = _pipeline_managed_guardrail_names(data, "post_call") guardrail_callbacks: Final[list[CustomGuardrail]] = [] other_callbacks: Final[list[CustomLogger]] = [] try: diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index c92f5fa6c55..4e1ccf71c5c 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -977,6 +977,79 @@ async def test_post_call_pipeline_managed_default_on_guardrail_runs_exactly_once assert seen["count"] == 1 +@pytest.mark.asyncio +async def test_post_call_hook_still_runs_guardrail_managed_only_by_pre_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class DualStageGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["count"] += 1 + return None + + pre_call_pipeline = GuardrailPipeline( + mode="pre_call", + steps=[PipelineStep(guardrail="gr-dual", on_pass="allow", on_fail="block")], + ) + monkeypatch.setattr( + litellm, + "callbacks", + [DualStageGuardrail(guardrail_name="gr-dual", event_hook=["pre_call", "post_call"], default_on=True)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [("request-governance", pre_call_pipeline)], + "_pipeline_managed_guardrails": {"gr-dual"}, + }, + } + + await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert seen["count"] == 1 + + +@pytest.mark.asyncio +async def test_pre_call_hook_still_runs_guardrail_managed_only_by_post_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class DualStageGuardrail(CustomGuardrail): + async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): + seen["count"] += 1 + return data + + post_call_pipeline = GuardrailPipeline( + mode="post_call", + steps=[PipelineStep(guardrail="gr-dual", on_pass="allow", on_fail="block")], + ) + monkeypatch.setattr( + litellm, + "callbacks", + [DualStageGuardrail(guardrail_name="gr-dual", event_hook=["pre_call", "post_call"], default_on=True)], + ) + data = { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [("response-governance", post_call_pipeline)], + "_pipeline_managed_guardrails": {"gr-dual"}, + }, + } + + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), data=data, call_type="completion" + ) + + assert seen["count"] == 1 + + @pytest.mark.asyncio async def test_post_call_pipeline_replacement_response_reaches_caller( proxy_logging, make_user_api_key_auth, monkeypatch From 996019cd23423c7b2a35dbd50f4b3b3571362cd3 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 00:14:23 -0700 Subject: [PATCH 008/223] fix(policy_engine): keep post_call pipeline guardrail logging and reject background bypass Post_call pipelines run step hooks against a copied request dict, so guardrail writes into the metadata bucket (applied_guardrails for the response header, standard_logging_guardrail_information for spend logs) were dropped when the guardrail was the first writer. Merge those writes back onto the request on the post_call allow path, keeping the request payload and the executor's per-step guardrails activation flag out of it. Background /v1/responses requests dodge the streaming 400: pre_call sees stream unset, then the polling task forces stream=true with pre-call logic skipped and the streaming branch returns before post_call_success_hook, silently bypassing post_call pipelines. Reject background=true at pre_call the same way as stream=true. Also pin the run_in_parallel pipeline-managed exclusion in both hook loops with regression tests. --- litellm/proxy/utils.py | 49 +++++- .../proxy_logging/test_guardrail_pipeline.py | 148 +++++++++++++++++- 2 files changed, 187 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index d3f2e1d7301..0f97473312c 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -457,8 +457,37 @@ def _pipeline_managed_guardrail_names( ) +def _merge_pipeline_metadata_bucket(data: dict, bucket_key: str, modified_bucket_value: object) -> None: + if not isinstance(modified_bucket_value, dict): + return + modified_bucket: Final = cast("dict[str, object]", modified_bucket_value) # cast-ok: metadata buckets are str-keyed + surviving_writes: Final = {key: value for key, value in modified_bucket.items() if key != "guardrails"} + existing_bucket: Final = data.get(bucket_key) + if isinstance(existing_bucket, dict): + cast("dict[str, object]", existing_bucket).update(surviving_writes) # cast-ok: metadata buckets are str-keyed + else: + data[bucket_key] = surviving_writes + + +def _merge_pipeline_metadata_writes(data: dict, modified_data: Mapping[str, object]) -> None: + """ + Copy metadata-bucket writes from a pipeline's working copy back onto the request. + + Post_call pipelines run step hooks against a copied request dict so the payload + already sent upstream stays untouched, but hooks record proxy-internal logging + state in the metadata buckets (``applied_guardrails`` for response headers, + ``standard_logging_guardrail_information`` for spend logs), and those writes + must reach the request dict the proxy keeps reading after the pipeline returns. + + The ``guardrails`` key is the executor's per-step activation flag for + ``should_run_guardrail``, not a hook write, so it stays in the working copy. + """ + for bucket_key in ("metadata", "litellm_metadata"): + _merge_pipeline_metadata_bucket(data, bucket_key, modified_data.get(bucket_key)) + + def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None: - if data.get("stream") is not True: + if data.get("stream") is not True and data.get("background") is not True: return post_call_policies: Final = tuple( policy_name for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == "post_call" @@ -470,9 +499,10 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None detail={ "error": { "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming responses yet: " - f"{', '.join(post_call_policies)}. Retry with stream=false, or move these policies' output " - "guardrails from pipeline steps to guardrails.add, which scans streamed output." + "Policies with post_call guardrail pipelines cannot govern streaming or background " + f"responses yet: {', '.join(post_call_policies)}. Retry with stream=false and " + "background=false, or move these policies' output guardrails from pipeline steps to " + "guardrails.add, which scans streamed output." ), "type": "guardrail_pipeline_error", "policies": list(post_call_policies), @@ -1667,11 +1697,16 @@ class ProxyLogging: Returns data dict if allowed, raises on block/modify_response. ``original_response`` is set on the post_call path, where the request payload (already sent upstream) must stay untouched; a replacement - response carried in ``modified_data`` is adopted by the caller. + response carried in ``modified_data`` is adopted by the caller, and + metadata-bucket writes (applied guardrails, guardrail logging info) + are merged back so headers and spend logs still see them. """ if result.terminal_action == "allow": - if result.modified_data is not None and original_response is None: - data.update(result.modified_data) + if result.modified_data is not None: + if original_response is None: + data.update(result.modified_data) + else: + _merge_pipeline_metadata_writes(data, result.modified_data) return data if result.terminal_action == "block": diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 4e1ccf71c5c..34a25d75bc0 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -23,6 +23,7 @@ from litellm.integrations.custom_guardrail import ( ModifyResponseException, ) from litellm.integrations.prometheus import PrometheusLogger +from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines from litellm.types.guardrails import GuardrailEventHooks from litellm.types.proxy.policy_engine.pipeline_types import ( @@ -1139,18 +1140,132 @@ def test_handle_pipeline_result_modify_response_carries_original_response(): assert info.value.original_response is response -def test_handle_pipeline_result_allow_discards_modifications_on_post_call(): +def test_handle_pipeline_result_allow_on_post_call_keeps_metadata_writes_only(): data = {"a": 1, "metadata": {"guardrails": ["other"]}} result = MagicMock() result.terminal_action = "allow" - result.modified_data = {"metadata": {"guardrails": ["gr-post"]}, "response": object()} + result.modified_data = { + "a": 2, + "metadata": {"guardrails": ["other"], "applied_guardrails": ["gr-post"]}, + "response": object(), + } out = ProxyLogging._handle_pipeline_result( result=result, data=data, policy_name="p", original_response=litellm.ModelResponse() ) assert out is data - assert data == {"a": 1, "metadata": {"guardrails": ["other"]}} + assert data["a"] == 1 + assert "response" not in data + assert data["metadata"] == {"guardrails": ["other"], "applied_guardrails": ["gr-post"]} + + +@pytest.mark.asyncio +async def test_post_call_pipeline_guardrail_metadata_writes_reach_request_data( + proxy_logging, make_user_api_key_auth, monkeypatch +): + class HeaderWritingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + add_guardrail_to_applied_guardrails_header(request_data=data, guardrail_name="gr-post") + self.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={"verdict": "pass"}, + request_data=data, + guardrail_status="success", + ) + return None + + monkeypatch.setattr( + litellm, + "callbacks", + [HeaderWritingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + + await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert data["metadata"]["applied_guardrails"] == ["gr-post"] + slg_entries = data["metadata"]["standard_logging_guardrail_information"] + assert len(slg_entries) == 1 + assert slg_entries[0]["guardrail_name"] == "gr-post" + + +@pytest.mark.asyncio +async def test_post_call_pipeline_managed_parallel_guardrail_runs_exactly_once( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class CountingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["count"] += 1 + return None + + monkeypatch.setattr( + litellm, + "callbacks", + [ + CountingGuardrail( + guardrail_name="gr-post", + event_hook=GuardrailEventHooks.post_call, + default_on=True, + run_in_parallel=True, + ) + ], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + + await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert seen["count"] == 1 + + +@pytest.mark.asyncio +async def test_pre_call_pipeline_managed_parallel_guardrail_runs_exactly_once( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {"count": 0} + + class CountingGuardrail(CustomGuardrail): + async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): + seen["count"] += 1 + return data + + pre_call_pipeline = GuardrailPipeline( + mode="pre_call", + steps=[PipelineStep(guardrail="gr-pre", on_pass="allow", on_fail="block")], + ) + monkeypatch.setattr( + litellm, + "callbacks", + [ + CountingGuardrail( + guardrail_name="gr-pre", + event_hook=GuardrailEventHooks.pre_call, + default_on=True, + run_in_parallel=True, + ) + ], + ) + data = { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [("request-governance", pre_call_pipeline)], + "_pipeline_managed_guardrails": {"gr-pre"}, + }, + } + + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), data=data, call_type="completion" + ) + + assert seen["count"] == 1 @pytest.mark.asyncio @@ -1173,6 +1288,26 @@ async def test_pre_call_hook_rejects_streaming_request_with_post_call_pipeline( assert "stream=false" in info.value.detail["error"]["message"] +@pytest.mark.asyncio +async def test_pre_call_hook_rejects_background_request_with_post_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch +): + monkeypatch.setattr(litellm, "callbacks", []) + data = _post_call_pipeline_data(background=True) + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), + data=data, + call_type="aresponses", + guardrails_only=True, + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["policies"] == ["response-governance"] + assert "background=false" in info.value.detail["error"]["message"] + + def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_call(): post_call = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) @@ -1183,6 +1318,12 @@ def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_c ) is None ) + assert ( + _raise_for_streaming_post_call_pipelines( + {"background": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}} + ) + is None + ) assert _raise_for_streaming_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", post_call)]}}) is None assert ( _raise_for_streaming_post_call_pipelines( @@ -1191,3 +1332,4 @@ def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_c is None ) assert _raise_for_streaming_post_call_pipelines({"stream": True}) is None + assert _raise_for_streaming_post_call_pipelines({"background": True}) is None From c5bcf3a73594ce5fad662a47781d89dbe7955718 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:06:43 -0700 Subject: [PATCH 009/223] feat(policy_engine): execute post_call guardrail pipelines on streaming responses --- .../unified_guardrail/unified_guardrail.py | 46 ++++- .../proxy/policy_engine/pipeline_executor.py | 47 ++++- litellm/proxy/utils.py | 187 ++++++++++++++++-- .../test_unified_guardrail.py | 2 +- .../proxy_logging/test_guardrail_pipeline.py | 168 +++++++++++++++- 5 files changed, 417 insertions(+), 33 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py index e95e97bfe74..60b4444e1d4 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py +++ b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py @@ -19,7 +19,7 @@ from litellm.cost_calculator import _infer_call_type from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.integrations.custom_logger import CustomLogger from litellm.litellm_core_utils.api_route_to_call_types import get_call_types_for_route -from litellm.llms import load_guardrail_translation_mappings +from litellm.llms import get_guardrail_translation_mapping, load_guardrail_translation_mappings from litellm.proxy._types import UserAPIKeyAuth from litellm.types.guardrails import GuardrailEventHooks from litellm.types.utils import ( @@ -62,6 +62,36 @@ def _as_endpoint_translation(translation: _EndpointTranslation) -> _EndpointTran return translation +def resolve_endpoint_translation( + user_api_key_dict: UserAPIKeyAuth, first_response_item: object | None +) -> "tuple[str, BaseTranslation] | None": + """ + Resolve the endpoint guardrail translation for a streamed response: the + request route wins, falling back to inferring the call type from the first + response chunk (the same resolution order the streaming iterator hook uses). + Returns None when the call type is unresolvable or has no translation. + """ + route_call_types: Final = ( + get_call_types_for_route(user_api_key_dict.request_route) if user_api_key_dict.request_route else None + ) + call_type: Final = ( + route_call_types[0].value + if route_call_types + else ( + _infer_call_type(call_type=None, completion_response=first_response_item) + if first_response_item is not None + else None + ) + ) + if call_type is None: + return None + try: + handler_cls: Final = get_guardrail_translation_mapping(CallTypes(call_type)) + except ValueError: + return None + return call_type, handler_cls() + + def _chunk_choices(item: object) -> Sequence[object]: choices: Final[Sequence[object]] = getattr(item, "choices", None) or [] return choices @@ -346,7 +376,7 @@ class UnifiedLLMGuardrails(CustomLogger): return response - async def _handle_streaming_block( + async def handle_streaming_block( self, exc: "ModifyResponseException", endpoint_translation: _EndpointTranslation, @@ -402,7 +432,7 @@ class UnifiedLLMGuardrails(CustomLogger): return None return call_type - async def _emit_streaming_http_error( + async def emit_streaming_http_error( self, exc: HTTPException, call_type: str | None, @@ -577,7 +607,7 @@ class UnifiedLLMGuardrails(CustomLogger): except ModifyResponseException as e: if e.original_response is None: e.original_response = responses_so_far - async for block_chunk in self._handle_streaming_block( + async for block_chunk in self.handle_streaming_block( e, endpoint_translation, stream_started=bool(responses_yielded), @@ -586,7 +616,7 @@ class UnifiedLLMGuardrails(CustomLogger): yield block_chunk raise _StreamTerminated() except HTTPException as e: - async for error_item in self._emit_streaming_http_error(e, call_type, responses_so_far, request_data): + async for error_item in self.emit_streaming_http_error(e, call_type, responses_so_far, request_data): yield error_item raise _StreamTerminated() @@ -758,7 +788,7 @@ class UnifiedLLMGuardrails(CustomLogger): except ModifyResponseException as e: if e.original_response is None: e.original_response = responses_so_far - async for block_chunk in self._handle_streaming_block( + async for block_chunk in self.handle_streaming_block( e, endpoint_translation, stream_started=bool(responses_yielded), @@ -1060,7 +1090,7 @@ class UnifiedLLMGuardrails(CustomLogger): # The current chunk was appended to responses_so_far but not # yet yielded, so exclude it: the continuation must reflect # only what the client has actually received. - async for block_chunk in self._handle_streaming_block( + async for block_chunk in self.handle_streaming_block( e, endpoint_translation, stream_started=chunks_yielded, @@ -1124,7 +1154,7 @@ class UnifiedLLMGuardrails(CustomLogger): # terminating SSE sequence with the block message rather than # propagating into a bare error blob that truncates the stream. # The withheld original chunks are never released. - async for block_chunk in self._handle_streaming_block( + async for block_chunk in self.handle_streaming_block( e, endpoint_translation, stream_started=bool(responses_yielded), diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 190784a2a60..c422a7c0964 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -6,7 +6,7 @@ pass/fail actions (allow, block, next, modify_response) and data forwarding. """ import time -from typing import Any, Final, Literal +from typing import TYPE_CHECKING, Any, Final, Literal import litellm from litellm._logging import verbose_proxy_logger @@ -25,6 +25,11 @@ from litellm.types.proxy.policy_engine.pipeline_types import ( PipelineStepResult, ) +if TYPE_CHECKING: + from litellm.llms.base_llm.guardrail_translation.base_translation import ( + BaseTranslation, + ) + try: from fastapi.exceptions import HTTPException except ImportError: @@ -43,6 +48,8 @@ class PipelineExecutor: call_type: str, policy_name: str, raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data + streaming_chunks: list[Any] | None = None, # mutable-ok: shared buffered-stream chunks, read per step + endpoint_translation: "BaseTranslation | None" = None, ) -> PipelineExecutionResult: """ Execute pipeline steps sequentially with conditional actions. @@ -59,6 +66,12 @@ class PipelineExecutor: step whose guardrail opted into ``scan_raw_request`` evaluates the original request instead of whatever an earlier ``pass_data`` step in this same pipeline already rewrote. + streaming_chunks: buffered chunks of a completed stream. When set + (with ``endpoint_translation``), post_call steps scan the + assembled streamed output through the endpoint translation + instead of calling ``async_post_call_success_hook``. + endpoint_translation: the guardrail translation for the streamed + endpoint, resolved by the caller. Returns: PipelineExecutionResult with terminal action and step results @@ -83,6 +96,8 @@ class PipelineExecutor: user_api_key_dict=user_api_key_dict, call_type=call_type, raw_request_snapshot=raw_request_snapshot, + streaming_chunks=streaming_chunks, + endpoint_translation=endpoint_translation, ) duration = time.perf_counter() - start_time @@ -154,6 +169,8 @@ class PipelineExecutor: user_api_key_dict: Any, call_type: str, raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data + streaming_chunks: list[Any] | None = None, # mutable-ok: shared buffered-stream chunks, read per step + endpoint_translation: "BaseTranslation | None" = None, ) -> tuple[ Literal["pass", "fail", "error"], dict | None, @@ -198,10 +215,8 @@ class PipelineExecutor: # Use unified_guardrail path if callback implements apply_guardrail target: CustomLogger = callback - use_unified: Final = ( - "apply_guardrail" in type(callback).__dict__ and not callback.use_native_lifecycle_hooks - ) - if use_unified: + use_unified: Final = PipelineExecutor.supports_unified_execution(callback) + if use_unified and streaming_chunks is None: hook_input["guardrail_to_apply"] = callback target = UnifiedLLMGuardrails() @@ -216,6 +231,22 @@ class PipelineExecutor: callback.mark_pre_call_hook_ran(data) if isinstance(response, dict): callback.mark_pre_call_hook_ran(response) + elif mode == "post_call" and streaming_chunks is not None: + if not use_unified or endpoint_translation is None: + return ( + "error", + None, + f"Guardrail '{step.guardrail}' does not support streaming pipeline execution", + None, + ) + await endpoint_translation.process_output_streaming_response( + responses_so_far=streaming_chunks, + guardrail_to_apply=callback, + litellm_logging_obj=data.get("litellm_logging_obj"), + user_api_key_dict=user_api_key_dict, + request_data=hook_input, + ) + response = None elif mode == "post_call": response = await target.async_post_call_success_hook( user_api_key_dict=user_api_key_dict, @@ -246,6 +277,12 @@ class PipelineExecutor: verbose_proxy_logger.error("Pipeline: unexpected error from guardrail '%s': %s", step.guardrail, e) return ("error", None, str(e), e) + @staticmethod + def supports_unified_execution(callback: CustomGuardrail) -> bool: + """Whether this guardrail runs through the unified apply_guardrail path, + the interface streaming pipeline execution requires.""" + return "apply_guardrail" in type(callback).__dict__ and not callback.use_native_lifecycle_hooks + @staticmethod def find_guardrail_callback(guardrail_name: str) -> CustomGuardrail | None: """Look up an initialized guardrail callback by name from litellm.callbacks.""" diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 80e991640a9..5642eb5383e 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -19,7 +19,7 @@ from email.mime.text import MIMEText from types import MappingProxyType from typing import TYPE_CHECKING, Any, ClassVar, Final, Literal, Optional, Protocol, TypeVar, Union, cast, overload -from typing_extensions import ReadOnly, TypedDict +from typing_extensions import NotRequired, ReadOnly, TypedDict from litellm import _custom_logger_compatible_callbacks_literal from litellm.constants import ( @@ -486,29 +486,80 @@ def _merge_pipeline_metadata_writes(data: dict, modified_data: Mapping[str, obje _merge_pipeline_metadata_bucket(data, bucket_key, modified_data.get(bucket_key)) +def _pipeline_step_supports_streaming(guardrail_name: str) -> bool: + callback: Final = PipelineExecutor.find_guardrail_callback(guardrail_name) + return callback is not None and PipelineExecutor.supports_unified_execution(callback) + + +class _PipelineErrorBody(TypedDict): + message: ReadOnly[str] + type: ReadOnly[str] + policies: ReadOnly[tuple[str, ...]] + guardrails: NotRequired[ReadOnly[tuple[str, ...]]] + + +class _PipelineErrorDetail(TypedDict): + error: ReadOnly[_PipelineErrorBody] + + def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None: - if data.get("stream") is not True and data.get("background") is not True: + """ + Reject up front the requests whose post_call pipelines could never run. + + Background responses skip the post_call hooks entirely, so a pipeline + governing one would silently never execute. Streaming responses execute + pipelines against the buffered stream through the endpoint guardrail + translations, which requires every step's guardrail to support the unified + apply_guardrail interface; steps that cannot (native-lifecycle guardrails, + or guardrails not registered at all) keep the 400 rather than letting + ungoverned output stream through. + """ + is_stream: Final = data.get("stream") is True + is_background: Final = data.get("background") is True + if not is_stream and not is_background: return - post_call_policies: Final = tuple( - policy_name for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == "post_call" + post_call_pipelines: Final = tuple( + (policy_name, pipeline) for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == "post_call" ) - if not post_call_policies: + if not post_call_pipelines: return - raise HTTPException( - status_code=400, - detail={ + post_call_policies: Final = tuple(policy_name for policy_name, _pipeline in post_call_pipelines) + if is_background: + background_detail: Final[_PipelineErrorDetail] = { "error": { "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming or background " - f"responses yet: {', '.join(post_call_policies)}. Retry with stream=false and " - "background=false, or move these policies' output guardrails from pipeline steps to " - "guardrails.add, which scans streamed output." + "Policies with post_call guardrail pipelines cannot govern background " + f"responses: {', '.join(post_call_policies)}. Retry with background=false." ), "type": "guardrail_pipeline_error", - "policies": list(post_call_policies), + "policies": post_call_policies, } - }, + } + raise HTTPException(status_code=400, detail=background_detail) + unsupported_guardrails: Final = tuple( + dict.fromkeys( + step.guardrail + for _policy_name, pipeline in post_call_pipelines + for step in pipeline.steps + if not _pipeline_step_supports_streaming(step.guardrail) + ) ) + if not unsupported_guardrails: + return + unsupported_detail: Final[_PipelineErrorDetail] = { + "error": { + "message": ( + "Policies with post_call guardrail pipelines cannot govern streaming responses " + "because these pipeline guardrails do not support the unified apply_guardrail " + f"interface: {', '.join(unsupported_guardrails)}. Retry with stream=false, or move " + "them from pipeline steps to guardrails.add, which scans streamed output." + ), + "type": "guardrail_pipeline_error", + "policies": post_call_policies, + "guardrails": unsupported_guardrails, + } + } + raise HTTPException(status_code=400, detail=unsupported_detail) def _prompt_block_text(block: object) -> str: @@ -1689,7 +1740,7 @@ class ProxyLogging: result: PipelineExecutionResult, data: dict, policy_name: str, - original_response: LLMResponseTypes | None = None, + original_response: "LLMResponseTypes | Sequence[object] | None" = None, ) -> dict: """ Handle a PipelineExecutionResult — allow, block, or modify_response. @@ -1699,7 +1750,9 @@ class ProxyLogging: payload (already sent upstream) must stay untouched; a replacement response carried in ``modified_data`` is adopted by the caller, and metadata-bucket writes (applied guardrails, guardrail logging info) - are merged back so headers and spend logs still see them. + are merged back so headers and spend logs still see them. On the + streaming path it is the buffered chunk list, carried into + ``ModifyResponseException.original_response`` for usage reporting. """ if result.terminal_action == "allow": if result.modified_data is not None: @@ -3195,11 +3248,16 @@ class ProxyLogging: # dict lookups + llm_router.get_deployment() per callback per chunk. _cached_guardrail_data: dict | None = None _guardrail_data_computed = False + pipeline_managed: Final = ( + _pipeline_managed_guardrail_names(data, "post_call") if caps.has_guardrail else frozenset() + ) for callback in litellm.callbacks: try: _callback: CustomLogger | None = None if isinstance(callback, CustomGuardrail): + if callback.guardrail_name in pipeline_managed: + continue # Main - V2 Guardrails implementation from litellm.types.guardrails import GuardrailEventHooks @@ -3256,12 +3314,17 @@ class ProxyLogging: 1. /chat/completions """ caps: Final = ProxyLogging._callback_capabilities() + post_call_pipelines: Final = tuple( + (policy_name, pipeline) + for policy_name, pipeline in _policy_pipelines(request_data) + if pipeline.mode == "post_call" + ) # Fast path: no real overrides. Internal proxy CustomLogger callbacks # (e.g. _PROXY_MaxBudgetLimiter, ManagedFiles) inherit the default # ``async for chunk: yield chunk`` body, so wrapping the iterator # through each of them adds N pass-through trampolines per chunk for # zero behavior change. Skip the chain entirely and stream through. - if not caps.iterator_overrides: + if not caps.iterator_overrides and not post_call_pipelines: try: async for chunk in response: yield chunk @@ -3281,8 +3344,11 @@ class ProxyLogging: current_response = response stream_needs_translation: Final = ProxyLogging._stream_requires_guardrail_translation(user_api_key_dict) + pipeline_managed_names: Final = _pipeline_managed_guardrail_names(request_data, "post_call") for resolved_callback, kind in caps.iterator_overrides: if isinstance(resolved_callback, CustomGuardrail): + if resolved_callback.guardrail_name in pipeline_managed_names: + continue if ( resolved_callback.should_run_guardrail(data=request_data, event_type=GuardrailEventHooks.post_call) is not True @@ -3322,6 +3388,17 @@ class ProxyLogging: ), ) + # Policy pipelines run last, over the fully buffered stream, so a + # pipeline verdict covers whatever the flat guardrail chain above + # already let through. + if post_call_pipelines: + current_response = self._pipeline_gated_stream( + response=current_response, + user_api_key_dict=user_api_key_dict, + request_data=request_data, + pipelines=post_call_pipelines, + ) + try: async for chunk in current_response: yield chunk @@ -3337,6 +3414,82 @@ class ProxyLogging: # we reach this point the metadata is fully populated. ProxyLogging._fire_deferred_stream_logging(request_data) + async def _pipeline_gated_stream( + self, + response: "AsyncGenerator[object, None]", + user_api_key_dict: UserAPIKeyAuth, + request_data: dict, + pipelines: "tuple[tuple[str, GuardrailPipeline], ...]", + ) -> "AsyncGenerator[Any, None]": + """ + Execute post_call policy pipelines against a streamed response. + + Buffers the whole stream (nothing reaches the client until every + pipeline allows it), then runs each pipeline's steps against the + assembled output through the endpoint guardrail translation, the same + machinery flat post_call guardrails use at end of stream. An allow + releases the buffered chunks verbatim; a block or modify_response + terminates with the translation's block chunks or the raised error. + """ + from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( + resolve_endpoint_translation, + ) + + buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict + async for item in response: + buffered.append(item) + if not buffered: + return + + resolved: Final = resolve_endpoint_translation(user_api_key_dict, buffered[0]) + if resolved is None: + policy_names: Final = tuple(policy_name for policy_name, _pipeline in pipelines) + unresolvable_detail: Final[_PipelineErrorDetail] = { + "error": { + "message": ( + "Policy pipelines could not govern this streaming response shape; " + f"the response was withheld: {', '.join(policy_names)}." + ), + "type": "guardrail_pipeline_error", + "policies": policy_names, + } + } + raise HTTPException(status_code=500, detail=unresolvable_detail) + call_type, endpoint_translation = resolved + + for policy_name, pipeline in pipelines: + result: PipelineExecutionResult = await PipelineExecutor.execute_steps( + steps=pipeline.steps, + mode="post_call", + data=request_data, + user_api_key_dict=user_api_key_dict, + call_type=call_type, + policy_name=policy_name, + streaming_chunks=buffered, + endpoint_translation=endpoint_translation, + ) + try: + ProxyLogging._handle_pipeline_result( + result, data=request_data, policy_name=policy_name, original_response=buffered + ) + except ModifyResponseException as e: + if e.original_response is None: + e.original_response = buffered + async for block_chunk in unified_guardrail.handle_streaming_block( + e, endpoint_translation, stream_started=False, responses_so_far=() + ): + yield block_chunk + return + except HTTPException as e: + async for error_chunk in unified_guardrail.emit_streaming_http_error( + e, call_type, buffered, request_data + ): + yield error_chunk + return + + for buffered_item in buffered: + yield buffered_item + @staticmethod def _fire_deferred_stream_logging(request_data: dict) -> None: """ diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py index 8b9ecfbbeee..fe4acbf8277 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py @@ -1026,7 +1026,7 @@ class TestStreamingTransform: ) emitted = [] - async for item in handler._emit_streaming_http_error( + async for item in handler.emit_streaming_http_error( exc, call_type=CallTypes.asend_message.value, responses_so_far=[{"id": "req-1"}], diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 34a25d75bc0..2b36ae111dc 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1284,7 +1284,8 @@ async def test_pre_call_hook_rejects_streaming_request_with_post_call_pipeline( ) assert info.value.status_code == 400 - assert info.value.detail["error"]["policies"] == ["response-governance"] + assert info.value.detail["error"]["policies"] == ("response-governance",) + assert info.value.detail["error"]["guardrails"] == ("gr-post",) assert "stream=false" in info.value.detail["error"]["message"] @@ -1304,7 +1305,7 @@ async def test_pre_call_hook_rejects_background_request_with_post_call_pipeline( ) assert info.value.status_code == 400 - assert info.value.detail["error"]["policies"] == ["response-governance"] + assert info.value.detail["error"]["policies"] == ("response-governance",) assert "background=false" in info.value.detail["error"]["message"] @@ -1333,3 +1334,166 @@ def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_c ) assert _raise_for_streaming_post_call_pipelines({"stream": True}) is None assert _raise_for_streaming_post_call_pipelines({"background": True}) is None + + +# --------------------------------------------------------------------------- +# post_call pipelines on streaming responses +# --------------------------------------------------------------------------- + + +def _unified_stream_guardrail(seen: Dict[str, Any], block: bool = False) -> CustomGuardrail: + class UnifiedStreamGuardrail(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + seen["count"] = seen.get("count", 0) + 1 + seen["input_type"] = input_type + if block: + raise HTTPException(status_code=400, detail={"error": "output blocked"}) + return inputs + + return UnifiedStreamGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False) + + +def _stream_chunks() -> List[Any]: + return [ + litellm.ModelResponseStream(choices=[{"index": 0, "delta": {"content": "hello "}, "finish_reason": None}]), + litellm.ModelResponseStream(choices=[{"index": 0, "delta": {"content": "world"}, "finish_reason": "stop"}]), + ] + + +async def _async_chunk_iter(chunks: List[Any]): + for chunk in chunks: + yield chunk + + +@pytest.mark.asyncio +async def test_pre_call_hook_allows_streaming_when_pipeline_guardrail_supports_unified( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) + data = _post_call_pipeline_data(stream=True) + + out = await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), + data=data, + call_type="completion", + guardrails_only=True, + ) + + assert out is not None + assert out.get("stream") is True + + +@pytest.mark.asyncio +@pytest.mark.parametrize("native_lifecycle", [False, True]) +async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_lacks_unified_support( + proxy_logging, make_user_api_key_auth, monkeypatch, native_lifecycle +): + if native_lifecycle: + + class NativeOnlyGuardrail(CustomGuardrail): + use_native_lifecycle_hooks = True + + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + return inputs + + else: + + class NativeOnlyGuardrail(CustomGuardrail): + pass + + monkeypatch.setattr( + litellm, + "callbacks", + [NativeOnlyGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], + ) + data = _post_call_pipeline_data(stream=True) + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), + data=data, + call_type="completion", + guardrails_only=True, + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["guardrails"] == ("gr-post",) + assert "apply_guardrail" in info.value.detail["error"]["message"] + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_allow_releases_buffered_chunks( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + chunks = _stream_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + assert [id(item) for item in delivered] == [id(chunk) for chunk in chunks] + assert seen["count"] == 1 + assert seen["input_type"] == "response" + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_block_withholds_all_chunks( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen, block=True)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + delivered: List[Any] = [] + + async def _drain() -> None: + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(_stream_chunks()), + request_data=data, + ): + delivered.append(item) + + with pytest.raises(HTTPException) as info: + await _drain() + + assert delivered == [] + assert info.value.status_code == 400 + assert "output blocked" in str(info.value.detail) + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_withholds_unresolvable_response_shape( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + delivered: List[Any] = [] + + async def _drain() -> None: + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(), + response=_async_chunk_iter([object(), object()]), + request_data=data, + ): + delivered.append(item) + + with pytest.raises(HTTPException) as info: + await _drain() + + assert delivered == [] + assert info.value.status_code == 500 + assert "withheld" in info.value.detail["error"]["message"] + assert seen.get("count") is None From fa5a10941e713968fbe7251a99d867ef0050dd69 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:28:11 -0700 Subject: [PATCH 010/223] fix(policy_engine): fail closed on streaming for content-rewriting pipeline steps and untranslatable routes --- .../unified_guardrail/unified_guardrail.py | 19 ++- litellm/proxy/utils.py | 111 ++++++++++++------ .../proxy_logging/test_guardrail_pipeline.py | 84 +++++++++++-- 3 files changed, 155 insertions(+), 59 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py index 60b4444e1d4..89527c05eb6 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py +++ b/litellm/proxy/guardrails/guardrail_hooks/unified_guardrail/unified_guardrail.py @@ -876,6 +876,14 @@ class UnifiedLLMGuardrails(CustomLogger): choices: Final = _chunk_choices(item) return any(getattr(choice, "finish_reason", None) is not None for choice in choices) + def resolve_streaming_flag(self, guardrail_to_apply: CustomGuardrail | None, name: str, default: object) -> object: + """Streaming flag resolution order (later wins): default < guardrail + attribute < guardrail_config dict < this callback's optional_params.""" + attribute_value: Final = default if guardrail_to_apply is None else getattr(guardrail_to_apply, name, default) + config: Final = None if guardrail_to_apply is None else getattr(guardrail_to_apply, "guardrail_config", None) + config_value: Final = config.get(name, attribute_value) if isinstance(config, dict) else attribute_value + return self.optional_params.get(name, config_value) + async def async_post_call_streaming_iterator_hook( self, user_api_key_dict: UserAPIKeyAuth, @@ -906,17 +914,8 @@ class UnifiedLLMGuardrails(CustomLogger): if guardrail_to_apply is None: guardrail_to_apply = request_data.pop("guardrail_to_apply", None) - # Get streaming configuration. Resolution order (later wins): default - # < guardrail attribute < guardrail_config dict < this callback's - # optional_params. def _streaming_flag(name: str, default: object) -> Any: - value = default - if guardrail_to_apply is not None: - value = getattr(guardrail_to_apply, name, value) - config: Final[Mapping[str, object]] = getattr(guardrail_to_apply, "guardrail_config", {}) - if isinstance(config, dict): - value = config.get(name, value) - return self.optional_params.get(name, value) + return self.resolve_streaming_flag(guardrail_to_apply, name, default) sampling_rate: Final[int] = _streaming_flag("streaming_sampling_rate", 5) # Only apply the guardrail at end of stream (not per chunk). diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 5642eb5383e..8b2c38d457f 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -139,6 +139,7 @@ from litellm.proxy.db.token_auth import ( ) from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( UnifiedLLMGuardrails, + resolve_endpoint_translation, ) from litellm.proxy.hooks import PROXY_HOOKS, get_proxy_hook from litellm.proxy.hooks.cache_control_check import _PROXY_CacheControlCheck @@ -486,11 +487,19 @@ def _merge_pipeline_metadata_writes(data: dict, modified_data: Mapping[str, obje _merge_pipeline_metadata_bucket(data, bucket_key, modified_data.get(bucket_key)) -def _pipeline_step_supports_streaming(guardrail_name: str) -> bool: +def _pipeline_step_supports_unified_streaming(guardrail_name: str) -> bool: callback: Final = PipelineExecutor.find_guardrail_callback(guardrail_name) return callback is not None and PipelineExecutor.supports_unified_execution(callback) +def _pipeline_step_rewrites_streamed_content(guardrail_name: str) -> bool: + callback: Final = PipelineExecutor.find_guardrail_callback(guardrail_name) + if callback is None: + return False + transform_mode: Final = unified_guardrail.resolve_streaming_flag(callback, "streaming_transform_mode", "block_only") + return callback.mask_response_content or transform_mode == "incremental_diff" + + class _PipelineErrorBody(TypedDict): message: ReadOnly[str] type: ReadOnly[str] @@ -502,17 +511,20 @@ class _PipelineErrorDetail(TypedDict): error: ReadOnly[_PipelineErrorBody] -def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None: +def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_api_key_dict: UserAPIKeyAuth) -> None: """ Reject up front the requests whose post_call pipelines could never run. Background responses skip the post_call hooks entirely, so a pipeline governing one would silently never execute. Streaming responses execute pipelines against the buffered stream through the endpoint guardrail - translations, which requires every step's guardrail to support the unified - apply_guardrail interface; steps that cannot (native-lifecycle guardrails, - or guardrails not registered at all) keep the 400 rather than letting - ungoverned output stream through. + translation of the request route, releasing the buffered chunks verbatim + on allow. That needs every step's guardrail to support the unified + apply_guardrail interface and to only allow or block (a step that rewrites + streamed content, via mask_response_content or + streaming_transform_mode=incremental_diff, would have its rewrite silently + dropped), and needs the route to have a translation at all; anything else + keeps the 400 rather than letting ungoverned output stream through. """ is_stream: Final = data.get("stream") is True is_background: Final = data.get("background") is True @@ -536,30 +548,61 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object]) -> None } } raise HTTPException(status_code=400, detail=background_detail) - unsupported_guardrails: Final = tuple( - dict.fromkeys( - step.guardrail - for _policy_name, pipeline in post_call_pipelines - for step in pipeline.steps - if not _pipeline_step_supports_streaming(step.guardrail) - ) + step_guardrails: Final = tuple( + dict.fromkeys(step.guardrail for _policy_name, pipeline in post_call_pipelines for step in pipeline.steps) ) - if not unsupported_guardrails: + unsupported_guardrails: Final = tuple( + guardrail for guardrail in step_guardrails if not _pipeline_step_supports_unified_streaming(guardrail) + ) + if unsupported_guardrails: + unsupported_detail: Final[_PipelineErrorDetail] = { + "error": { + "message": ( + "Policies with post_call guardrail pipelines cannot govern streaming responses " + "because these pipeline guardrails do not support the unified apply_guardrail " + f"interface: {', '.join(unsupported_guardrails)}. Retry with stream=false, or drop " + "them from the pipeline steps so guardrails.add scans them on streamed output." + ), + "type": "guardrail_pipeline_error", + "policies": post_call_policies, + "guardrails": unsupported_guardrails, + } + } + raise HTTPException(status_code=400, detail=unsupported_detail) + rewriting_guardrails: Final = tuple( + guardrail for guardrail in step_guardrails if _pipeline_step_rewrites_streamed_content(guardrail) + ) + if rewriting_guardrails: + rewriting_detail: Final[_PipelineErrorDetail] = { + "error": { + "message": ( + "Policies with post_call guardrail pipelines cannot govern streaming responses " + "because these pipeline guardrails rewrite streamed content (mask_response_content " + "or streaming_transform_mode=incremental_diff), which pipeline steps would release " + f"unmodified: {', '.join(rewriting_guardrails)}. Retry with stream=false, or drop " + "them from the pipeline steps so guardrails.add applies them to streamed output." + ), + "type": "guardrail_pipeline_error", + "policies": post_call_policies, + "guardrails": rewriting_guardrails, + } + } + raise HTTPException(status_code=400, detail=rewriting_detail) + route: Final = user_api_key_dict.request_route + if not route or resolve_endpoint_translation(user_api_key_dict, None) is not None: return - unsupported_detail: Final[_PipelineErrorDetail] = { + route_detail: Final[_PipelineErrorDetail] = { "error": { "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming responses " - "because these pipeline guardrails do not support the unified apply_guardrail " - f"interface: {', '.join(unsupported_guardrails)}. Retry with stream=false, or move " - "them from pipeline steps to guardrails.add, which scans streamed output." + "Policies with post_call guardrail pipelines cannot govern streaming responses on " + f"route {route} because it has no endpoint guardrail translation to scan the stream " + f"through: {', '.join(post_call_policies)}. Retry with stream=false." ), "type": "guardrail_pipeline_error", "policies": post_call_policies, - "guardrails": unsupported_guardrails, } } - raise HTTPException(status_code=400, detail=unsupported_detail) + raise HTTPException(status_code=400, detail=route_detail) def _prompt_block_text(block: object) -> str: @@ -1915,7 +1958,7 @@ class ProxyLogging: ) try: - _raise_for_streaming_post_call_pipelines(data) + _raise_for_streaming_post_call_pipelines(data, user_api_key_dict) # Execute guardrail pipelines before the normal callback loop data, _ = await self._maybe_execute_pipelines( @@ -3431,10 +3474,6 @@ class ProxyLogging: releases the buffered chunks verbatim; a block or modify_response terminates with the translation's block chunks or the raised error. """ - from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( - resolve_endpoint_translation, - ) - buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict async for item in response: buffered.append(item) @@ -3444,17 +3483,15 @@ class ProxyLogging: resolved: Final = resolve_endpoint_translation(user_api_key_dict, buffered[0]) if resolved is None: policy_names: Final = tuple(policy_name for policy_name, _pipeline in pipelines) - unresolvable_detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - "Policy pipelines could not govern this streaming response shape; " - f"the response was withheld: {', '.join(policy_names)}." - ), - "type": "guardrail_pipeline_error", - "policies": policy_names, - } - } - raise HTTPException(status_code=500, detail=unresolvable_detail) + raise ProxyException( + message=( + "Policy pipelines could not govern this streaming response shape; " + f"the response was withheld: {', '.join(policy_names)}." + ), + type="guardrail_pipeline_error", + param=None, + code=500, + ) call_type, endpoint_translation = resolved for policy_name, pipeline in pipelines: diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 2b36ae111dc..6c90637158e 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -23,6 +23,7 @@ from litellm.integrations.custom_guardrail import ( ModifyResponseException, ) from litellm.integrations.prometheus import PrometheusLogger +from litellm.proxy._types import ProxyException from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines from litellm.types.guardrails import GuardrailEventHooks @@ -1309,31 +1310,35 @@ async def test_pre_call_hook_rejects_background_request_with_post_call_pipeline( assert "background=false" in info.value.detail["error"]["message"] -def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_call(): +def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_call(make_user_api_key_auth): post_call = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) + auth = make_user_api_key_auth(request_route="/custom/stream") assert ( _raise_for_streaming_post_call_pipelines( - {"stream": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}} + {"stream": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth ) is None ) assert ( _raise_for_streaming_post_call_pipelines( - {"background": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}} + {"background": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth ) is None ) - assert _raise_for_streaming_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", post_call)]}}) is None + assert ( + _raise_for_streaming_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth) + is None + ) assert ( _raise_for_streaming_post_call_pipelines( - {"stream": True, "metadata": {"_guardrail_pipelines": [("p", pre_call)]}} + {"stream": True, "metadata": {"_guardrail_pipelines": [("p", pre_call)]}}, auth ) is None ) - assert _raise_for_streaming_post_call_pipelines({"stream": True}) is None - assert _raise_for_streaming_post_call_pipelines({"background": True}) is None + assert _raise_for_streaming_post_call_pipelines({"stream": True}, auth) is None + assert _raise_for_streaming_post_call_pipelines({"background": True}, auth) is None # --------------------------------------------------------------------------- @@ -1366,15 +1371,16 @@ async def _async_chunk_iter(chunks: List[Any]): @pytest.mark.asyncio +@pytest.mark.parametrize("request_route", [None, "/v1/chat/completions"]) async def test_pre_call_hook_allows_streaming_when_pipeline_guardrail_supports_unified( - proxy_logging, make_user_api_key_auth, monkeypatch + proxy_logging, make_user_api_key_auth, monkeypatch, request_route ): seen: Dict[str, Any] = {} monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) data = _post_call_pipeline_data(stream=True) out = await proxy_logging.pre_call_hook( - user_api_key_dict=make_user_api_key_auth(), + user_api_key_dict=make_user_api_key_auth(request_route=request_route), data=data, call_type="completion", guardrails_only=True, @@ -1422,6 +1428,60 @@ async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_lacks_uni assert "apply_guardrail" in info.value.detail["error"]["message"] +@pytest.mark.asyncio +@pytest.mark.parametrize( + "rewrite_attribute, value", + [ + ("mask_response_content", True), + ("streaming_transform_mode", "incremental_diff"), + ("guardrail_config", {"streaming_transform_mode": "incremental_diff"}), + ], +) +async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_rewrites_streamed_content( + proxy_logging, make_user_api_key_auth, monkeypatch, rewrite_attribute, value +): + seen: Dict[str, Any] = {} + guardrail = _unified_stream_guardrail(seen) + setattr(guardrail, rewrite_attribute, value) + monkeypatch.setattr(litellm, "callbacks", [guardrail]) + data = _post_call_pipeline_data(stream=True) + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + data=data, + call_type="completion", + guardrails_only=True, + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["guardrails"] == ("gr-post",) + assert "rewrite streamed content" in info.value.detail["error"]["message"] + assert seen.get("count") is None + + +@pytest.mark.asyncio +async def test_pre_call_hook_rejects_streaming_when_route_has_no_guardrail_translation( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) + data = _post_call_pipeline_data(stream=True) + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/custom/stream"), + data=data, + call_type="completion", + guardrails_only=True, + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["policies"] == ("response-governance",) + assert "/custom/stream" in info.value.detail["error"]["message"] + assert seen.get("count") is None + + @pytest.mark.asyncio async def test_streaming_iterator_hook_pipeline_allow_releases_buffered_chunks( proxy_logging, make_user_api_key_auth, monkeypatch @@ -1490,10 +1550,10 @@ async def test_streaming_iterator_hook_pipeline_withholds_unresolvable_response_ ): delivered.append(item) - with pytest.raises(HTTPException) as info: + with pytest.raises(ProxyException) as info: await _drain() assert delivered == [] - assert info.value.status_code == 500 - assert "withheld" in info.value.detail["error"]["message"] + assert info.value.code == "500" + assert "withheld" in info.value.message assert seen.get("count") is None From 1bed9bae43a7c28e46d6b38d4b58d10d35d87c58 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:41:33 -0700 Subject: [PATCH 011/223] chore(policy_engine): drop control-flow comment flagged in review --- litellm/proxy/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 8b2c38d457f..231d4f18aa0 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -3431,9 +3431,6 @@ class ProxyLogging: ), ) - # Policy pipelines run last, over the fully buffered stream, so a - # pipeline verdict covers whatever the flat guardrail chain above - # already let through. if post_call_pipelines: current_response = self._pipeline_gated_stream( response=current_response, From d51198fdeb3ccc7fcf70fbb116f94d8f360ef4d6 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:07:34 -0700 Subject: [PATCH 012/223] test(policy_engine): cover streaming pipeline gate branches Adds regression tests for the modify_response block on the Anthropic route, the gate with no iterator overrides, and the per-chunk hook skipping pipeline-managed guardrails. Corrects the gate docstring: an allow releases the chunks as the endpoint translation left them, not verbatim --- litellm/proxy/utils.py | 7 +- .../proxy_logging/test_guardrail_pipeline.py | 110 ++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 231d4f18aa0..a64b57c1388 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -3468,8 +3468,11 @@ class ProxyLogging: pipeline allows it), then runs each pipeline's steps against the assembled output through the endpoint guardrail translation, the same machinery flat post_call guardrails use at end of stream. An allow - releases the buffered chunks verbatim; a block or modify_response - terminates with the translation's block chunks or the raised error. + releases the buffered chunks as that machinery left them (the + Responses and A2A translations write guardrail output back into the + final chunk, exactly as they do for flat guardrails); a block or + modify_response terminates with the translation's block chunks or the + raised error. """ buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict async for item in response: diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 6c90637158e..a258442c79a 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -10,6 +10,7 @@ Covers ``_should_use_guardrail_load_balancing``, ``_execute_guardrail_hook``, from __future__ import annotations import asyncio +import json from typing import Any, Dict, List from unittest.mock import AsyncMock, MagicMock, patch @@ -1557,3 +1558,112 @@ async def test_streaming_iterator_hook_pipeline_withholds_unresolvable_response_ assert info.value.code == "500" assert "withheld" in info.value.message assert seen.get("count") is None + + +def _anthropic_sse_chunks() -> List[bytes]: + events = [ + ("message_start", {"type": "message_start", "message": {"id": "msg_1", "type": "message", "role": "assistant", "model": "m", "content": [], "stop_reason": None, "usage": {"input_tokens": 1, "output_tokens": 0}}}), + ("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "hello world"}}), + ("content_block_stop", {"type": "content_block_stop", "index": 0}), + ("message_delta", {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 2}}), + ("message_stop", {"type": "message_stop"}), + ] + return [f"event: {name}\ndata: {json.dumps(payload)}\n\n".encode() for name, payload in events] + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_modify_response_emits_translated_block( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen, block=True)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + pipeline = GuardrailPipeline( + mode="post_call", + steps=[ + PipelineStep( + guardrail="gr-post", + on_pass="allow", + on_fail="modify_response", + modify_response_message="content policy block", + ) + ], + ) + data = _post_call_pipeline_data(stream=True) + data["metadata"]["_guardrail_pipelines"] = [("response-governance", pipeline)] + chunks = _anthropic_sse_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/messages"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + raw = b"".join(delivered).decode() + assert seen["count"] == 1 + assert "content policy block" in raw + assert "hello world" not in raw + assert not any(item is chunk for item in delivered for chunk in chunks) + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_gates_without_iterator_overrides( + proxy_logging, make_user_api_key_auth, monkeypatch +): + monkeypatch.setattr(litellm, "callbacks", []) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + delivered: List[Any] = [] + + async def _drain() -> None: + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(_stream_chunks()), + request_data=data, + ): + delivered.append(item) + + with pytest.raises(HTTPException) as info: + await _drain() + + assert delivered == [] + assert info.value.status_code == 400 + assert info.value.detail["error"]["pipeline_context"]["step_results"] == [ + {"guardrail": "gr-post", "outcome": "error", "action": "block"} + ] + + +@pytest.mark.asyncio +async def test_per_chunk_streaming_hook_skips_pipeline_managed_guardrail( + proxy_logging, make_user_api_key_auth, monkeypatch +): + seen: Dict[str, Any] = {} + + class RecordingGuardrail(CustomGuardrail): + async def async_post_call_streaming_hook(self, user_api_key_dict, response): + seen[self.guardrail_name] = seen.get(self.guardrail_name, 0) + 1 + return None + + managed = RecordingGuardrail( + guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=True + ) + free = RecordingGuardrail( + guardrail_name="gr-free", event_hook=GuardrailEventHooks.post_call, default_on=True + ) + monkeypatch.setattr(litellm, "callbacks", [managed, free]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + + result = await proxy_logging.async_post_call_streaming_hook( + data=data, + response=_stream_chunks()[0], + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + ) + + assert result is not None + assert seen.get("gr-post") is None + assert seen["gr-free"] == 1 From 0c1f33dff7084559cf1612a38b5dde4ea0afe9b8 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:19:21 -0700 Subject: [PATCH 013/223] fix(policy_engine): fail closed on content filter MASK steps for streaming pipelines A litellm_content_filter step with a MASK action masks chat streams through its own iterator hook under guardrails.add, which pipeline-managed guardrails skip, so the pipeline path released the stream unmasked. CustomGuardrail now declares rewrites_streamed_output (mask_response_content by default, any MASK action for the content filter) and the upfront streaming check names such steps in the same 400 it gives mask_response_content and incremental_diff --- litellm/integrations/custom_guardrail.py | 3 ++ .../litellm_content_filter/content_filter.py | 7 ++++ litellm/proxy/utils.py | 14 ++++---- .../content_filter/test_content_filter.py | 36 +++++++++++++++++++ .../proxy_logging/test_guardrail_pipeline.py | 34 +++++++++++++++++- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py index 8dc6881d23e..a6e3d000120 100644 --- a/litellm/integrations/custom_guardrail.py +++ b/litellm/integrations/custom_guardrail.py @@ -762,6 +762,9 @@ class CustomGuardrail(CustomLogger): def uses_apply_guardrail_interface(self) -> bool: return type(self).apply_guardrail is not CustomGuardrail.apply_guardrail + def rewrites_streamed_output(self) -> bool: + return self.mask_response_content + def _deployment_pre_call_target(self) -> "CustomLogger": if not self.uses_apply_guardrail_interface() or self.use_native_lifecycle_hooks: return self diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index 722f96ef814..bd31882841e 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -1947,6 +1947,13 @@ class ContentFilterGuardrail(CustomGuardrail): exception_str=exception_str, ) + def rewrites_streamed_output(self) -> bool: + return ( + super().rewrites_streamed_output() + or any(entry["action"] == ContentFilterAction.MASK for entry in self.compiled_patterns) + or any(action == ContentFilterAction.MASK for action, _ in self.blocked_words.values()) + ) + async def async_post_call_streaming_iterator_hook( self, user_api_key_dict: UserAPIKeyAuth, diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index a64b57c1388..138f272af42 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -497,7 +497,7 @@ def _pipeline_step_rewrites_streamed_content(guardrail_name: str) -> bool: if callback is None: return False transform_mode: Final = unified_guardrail.resolve_streaming_flag(callback, "streaming_transform_mode", "block_only") - return callback.mask_response_content or transform_mode == "incremental_diff" + return callback.rewrites_streamed_output() or transform_mode == "incremental_diff" class _PipelineErrorBody(TypedDict): @@ -518,10 +518,10 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_ap Background responses skip the post_call hooks entirely, so a pipeline governing one would silently never execute. Streaming responses execute pipelines against the buffered stream through the endpoint guardrail - translation of the request route, releasing the buffered chunks verbatim - on allow. That needs every step's guardrail to support the unified - apply_guardrail interface and to only allow or block (a step that rewrites - streamed content, via mask_response_content or + translation of the request route, releasing the buffered chunks on allow. + That needs every step's guardrail to support the unified apply_guardrail + interface and to only allow or block (a step that rewrites streamed + content, via mask_response_content, a MASK action, or streaming_transform_mode=incremental_diff, would have its rewrite silently dropped), and needs the route to have a translation at all; anything else keeps the 400 rather than letting ungoverned output stream through. @@ -577,8 +577,8 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_ap "error": { "message": ( "Policies with post_call guardrail pipelines cannot govern streaming responses " - "because these pipeline guardrails rewrite streamed content (mask_response_content " - "or streaming_transform_mode=incremental_diff), which pipeline steps would release " + "because these pipeline guardrails rewrite streamed content (mask_response_content, " + "a MASK action, or streaming_transform_mode=incremental_diff), which pipeline steps would release " f"unmodified: {', '.join(rewriting_guardrails)}. Retry with stream=false, or drop " "them from the pipeline steps so guardrails.add applies them to streamed output." ), diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index be55ac47bde..ffbedfa43ff 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -3068,3 +3068,39 @@ class TestContentFilterToolCallArguments: request_data={}, input_type="response", ) + + +class TestRewritesStreamedOutput: + def test_block_only_rules_do_not_rewrite(self): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + patterns=[ContentFilterPattern(pattern_type="prebuilt", pattern_name="us_ssn", action=ContentFilterAction.BLOCK)], + blocked_words=[BlockedWord(keyword="kumquat", action=ContentFilterAction.BLOCK)], + ) + + assert guardrail.rewrites_streamed_output() is False + + def test_mask_blocked_word_rewrites(self): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + blocked_words=[BlockedWord(keyword="persimmon", action=ContentFilterAction.MASK)], + ) + + assert guardrail.rewrites_streamed_output() is True + + def test_mask_pattern_rewrites(self): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + patterns=[ContentFilterPattern(pattern_type="prebuilt", pattern_name="us_ssn", action=ContentFilterAction.MASK)], + ) + + assert guardrail.rewrites_streamed_output() is True + + def test_mask_response_content_rewrites(self): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + blocked_words=[BlockedWord(keyword="kumquat", action=ContentFilterAction.BLOCK)], + mask_response_content=True, + ) + + assert guardrail.rewrites_streamed_output() is True diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index a258442c79a..ee0a9a10172 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -27,7 +27,8 @@ from litellm.integrations.prometheus import PrometheusLogger from litellm.proxy._types import ProxyException from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines -from litellm.types.guardrails import GuardrailEventHooks +from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ContentFilterGuardrail +from litellm.types.guardrails import BlockedWord, ContentFilterAction, GuardrailEventHooks from litellm.types.proxy.policy_engine.pipeline_types import ( GuardrailPipeline, PipelineStep, @@ -1461,6 +1462,37 @@ async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_rewrites_ assert seen.get("count") is None +@pytest.mark.asyncio +@pytest.mark.parametrize("action, rejected", [(ContentFilterAction.MASK, True), (ContentFilterAction.BLOCK, False)]) +async def test_pre_call_hook_rejects_streaming_only_when_content_filter_step_masks( + proxy_logging, make_user_api_key_auth, monkeypatch, action, rejected +): + guardrail = ContentFilterGuardrail( + guardrail_name="gr-post", + event_hook=GuardrailEventHooks.post_call, + blocked_words=[BlockedWord(keyword="persimmon", action=action)], + ) + monkeypatch.setattr(litellm, "callbacks", [guardrail]) + data = _post_call_pipeline_data(stream=True) + user_api_key_dict = make_user_api_key_auth(request_route="/v1/chat/completions") + + if not rejected: + out = await proxy_logging.pre_call_hook( + user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True + ) + assert out is not None and out.get("stream") is True + return + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["guardrails"] == ("gr-post",) + assert "a MASK action" in info.value.detail["error"]["message"] + + @pytest.mark.asyncio async def test_pre_call_hook_rejects_streaming_when_route_has_no_guardrail_translation( proxy_logging, make_user_api_key_auth, monkeypatch From 90c8031dd76c5565c25b2adfe301a4ce715a6964 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:32:35 -0700 Subject: [PATCH 014/223] fix(policy_engine): fail closed on content filter category MASK steps for streaming pipelines --- .../litellm_content_filter/content_filter.py | 2 ++ .../content_filter/test_content_filter.py | 20 +++++++++++++++++ .../proxy_logging/test_guardrail_pipeline.py | 22 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index bd31882841e..85eb50c78e7 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -1952,6 +1952,8 @@ class ContentFilterGuardrail(CustomGuardrail): super().rewrites_streamed_output() or any(entry["action"] == ContentFilterAction.MASK for entry in self.compiled_patterns) or any(action == ContentFilterAction.MASK for action, _ in self.blocked_words.values()) + or any(action == ContentFilterAction.MASK for _, _, action in self.category_keywords.values()) + or any(action == ContentFilterAction.MASK for _, _, action in self.always_block_category_keywords.values()) ) async def async_post_call_streaming_iterator_hook( diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index ffbedfa43ff..73020fe3e6f 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -3104,3 +3104,23 @@ class TestRewritesStreamedOutput: ) assert guardrail.rewrites_streamed_output() is True + + @pytest.mark.parametrize("action, expected", [("MASK", True), ("BLOCK", False)]) + def test_category_keywords_follow_the_category_action(self, action, expected): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + categories=[{"category": "bias_gender", "enabled": True, "action": action}], + ) + + assert guardrail.category_keywords and not guardrail.always_block_category_keywords + assert guardrail.rewrites_streamed_output() is expected + + @pytest.mark.parametrize("action, expected", [("MASK", True), ("BLOCK", False)]) + def test_always_block_category_keywords_follow_the_category_action(self, action, expected): + guardrail = ContentFilterGuardrail( + guardrail_name="cf", + categories=[{"category": "age_discrimination", "enabled": True, "action": action}], + ) + + assert guardrail.always_block_category_keywords and not guardrail.category_keywords + assert guardrail.rewrites_streamed_output() is expected diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index ee0a9a10172..895a986f348 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1493,6 +1493,28 @@ async def test_pre_call_hook_rejects_streaming_only_when_content_filter_step_mas assert "a MASK action" in info.value.detail["error"]["message"] +@pytest.mark.asyncio +async def test_pre_call_hook_rejects_streaming_when_content_filter_category_masks( + proxy_logging, make_user_api_key_auth, monkeypatch +): + guardrail = ContentFilterGuardrail( + guardrail_name="gr-post", + event_hook=GuardrailEventHooks.post_call, + categories=[{"category": "bias_gender", "enabled": True, "action": "MASK"}], + ) + monkeypatch.setattr(litellm, "callbacks", [guardrail]) + data = _post_call_pipeline_data(stream=True) + user_api_key_dict = make_user_api_key_auth(request_route="/v1/chat/completions") + + with pytest.raises(HTTPException) as info: + await proxy_logging.pre_call_hook( + user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True + ) + + assert info.value.status_code == 400 + assert info.value.detail["error"]["guardrails"] == ("gr-post",) + + @pytest.mark.asyncio async def test_pre_call_hook_rejects_streaming_when_route_has_no_guardrail_translation( proxy_logging, make_user_api_key_auth, monkeypatch From 2247fbc66df9769c3e3661b457b0b132513c1bd5 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:44:08 -0700 Subject: [PATCH 015/223] fix(policy_engine): withhold streams when a pipeline guardrail rewrites output at runtime --- .../proxy/policy_engine/pipeline_executor.py | 114 +++++++++++++++--- litellm/proxy/utils.py | 60 ++++++--- .../policy_engine/test_pipeline_executor.py | 63 +++++++++- .../proxy_logging/test_guardrail_pipeline.py | 103 +++++++++++++++- 4 files changed, 302 insertions(+), 38 deletions(-) diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index c422a7c0964..acd2c2c973a 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -6,8 +6,11 @@ pass/fail actions (allow, block, next, modify_response) and data forwarding. """ import time +from collections.abc import Mapping, Sequence from typing import TYPE_CHECKING, Any, Final, Literal +from pydantic import BaseModel + import litellm from litellm._logging import verbose_proxy_logger from litellm.integrations.custom_guardrail import ( @@ -24,8 +27,10 @@ from litellm.types.proxy.policy_engine.pipeline_types import ( PipelineStep, PipelineStepResult, ) +from litellm.types.utils import GenericGuardrailAPIInputs if TYPE_CHECKING: + from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.llms.base_llm.guardrail_translation.base_translation import ( BaseTranslation, ) @@ -36,6 +41,90 @@ except ImportError: HTTPException = None +class UndeliverableStreamRewrite(Exception): + def __init__(self, guardrail_name: str) -> None: + super().__init__( + f"Guardrail '{guardrail_name}' rewrote the streamed response, which streaming pipelines cannot deliver" + ) + self.guardrail_name: Final = guardrail_name + + +def _tool_call_shape(tool_call: object) -> tuple[object, object]: + plain: Final = tool_call.model_dump() if isinstance(tool_call, BaseModel) else tool_call + function: Final = plain.get("function") if isinstance(plain, Mapping) else None + if not isinstance(function, Mapping): + return (None, None) + return (function.get("name"), function.get("arguments")) + + +def _rewrote_texts(sent: Sequence[str] | None, returned: Sequence[str] | None) -> bool: + return sent is not None and returned is not None and list(returned) != list(sent) + + +def _rewrote_tool_calls(sent: Sequence[object] | None, returned: Sequence[object] | None) -> bool: + if sent is None or returned is None: + return False + return [_tool_call_shape(tool_call) for tool_call in returned] != [ + _tool_call_shape(tool_call) for tool_call in sent + ] + + +class _StreamRewriteObserver(CustomGuardrail): + """Stand-in handed to the endpoint translation in place of a streaming pipeline step's + guardrail. Translations cannot rewrite every buffered chunk consistently, so the gate + withholds the stream whenever the guardrail returned different output than it was given, + which for guardrails like Bedrock's ANONYMIZED action is only known at runtime.""" + + def __init__(self, inner: CustomGuardrail) -> None: + super().__init__(guardrail_name=inner.guardrail_name) + self.inner: Final = inner + self.rewrote = False + + def structured_messages_cover_full_request(self) -> bool: + return self.inner.structured_messages_cover_full_request() + + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, # mutable-ok: matches CustomGuardrail.apply_guardrail + input_type: Literal["request", "response"], + logging_obj: "LiteLLMLoggingObj | None" = None, + ) -> GenericGuardrailAPIInputs: + outputs: Final = await self.inner.apply_guardrail( + inputs=inputs, request_data=request_data, input_type=input_type, logging_obj=logging_obj + ) + self.rewrote = ( + self.rewrote + or _rewrote_texts(inputs.get("texts"), outputs.get("texts")) + or _rewrote_tool_calls(inputs.get("tool_calls"), outputs.get("tool_calls")) + ) + return outputs + + +def _prepare_hook_input( + step: PipelineStep, + callback: CustomLogger, + data: dict, # mutable-ok: same request-payload shape the hooks mutate + raw_request_snapshot: dict | None, # mutable-ok: same request-payload shape as data +) -> tuple[dict, bool]: # mutable-ok: returns that same request-payload dict + """Inject the step's guardrail name into metadata so should_run_guardrail() allows it, + and pick the payload the step scans: a scan_raw_request step evaluates the pristine + pre-pipeline snapshot instead of `data` (which earlier pass_data steps in this same + pipeline may have already rewritten), same reason the normal sequential/parallel + guardrail loops do this.""" + if "metadata" not in data: + data["metadata"] = {} + data["metadata"]["guardrails"] = [step.guardrail] + + scans_raw_request: Final = getattr(callback, "scan_raw_request", False) + hook_input: Final[dict] = ( # mutable-ok: same request-payload shape as data + independent_snapshot(raw_request_snapshot) if scans_raw_request and raw_request_snapshot is not None else data + ) + if hook_input is not data: + hook_input.setdefault("metadata", {})["guardrails"] = [step.guardrail] + return hook_input, scans_raw_request + + class PipelineExecutor: """Executes guardrail pipelines with ordered, conditional step logic.""" @@ -195,23 +284,7 @@ class PipelineExecutor: return ("error", None, f"Guardrail '{step.guardrail}' not found", None) try: - # Inject guardrail name into metadata so should_run_guardrail() allows it - if "metadata" not in data: - data["metadata"] = {} - data["metadata"]["guardrails"] = [step.guardrail] - - # A scan_raw_request step evaluates the pristine pre-pipeline - # snapshot instead of `data` (which earlier pass_data steps in - # this same pipeline may have already rewritten), same reason - # the normal sequential/parallel guardrail loops do this. - scans_raw_request: Final = getattr(callback, "scan_raw_request", False) - hook_input: Final[dict] = ( # mutable-ok: same request-payload shape as data - independent_snapshot(raw_request_snapshot) - if scans_raw_request and raw_request_snapshot is not None - else data - ) - if hook_input is not data: - hook_input.setdefault("metadata", {})["guardrails"] = [step.guardrail] + hook_input, scans_raw_request = _prepare_hook_input(step, callback, data, raw_request_snapshot) # Use unified_guardrail path if callback implements apply_guardrail target: CustomLogger = callback @@ -239,13 +312,16 @@ class PipelineExecutor: f"Guardrail '{step.guardrail}' does not support streaming pipeline execution", None, ) + observer: Final = _StreamRewriteObserver(callback) await endpoint_translation.process_output_streaming_response( responses_so_far=streaming_chunks, - guardrail_to_apply=callback, + guardrail_to_apply=observer, litellm_logging_obj=data.get("litellm_logging_obj"), user_api_key_dict=user_api_key_dict, request_data=hook_input, ) + if observer.rewrote: + raise UndeliverableStreamRewrite(step.guardrail) response = None elif mode == "post_call": response = await target.async_post_call_success_hook( @@ -269,6 +345,8 @@ class PipelineExecutor: return ("pass", {"response": response}, None, None) return ("pass", response if isinstance(response, dict) else None, None, None) + except UndeliverableStreamRewrite: + raise except Exception as e: if CustomGuardrail._is_guardrail_intervention(e): error_msg: Final = _extract_error_message(e) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 138f272af42..6c990222e51 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -155,7 +155,7 @@ from litellm.proxy.hooks.sensitive_data_routing import ( ) from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup from litellm.proxy.management_helpers.key_settings_audit import with_settings_updated_at -from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor +from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor, UndeliverableStreamRewrite from litellm.repositories.budget_repository import BudgetRepository from litellm.repositories.config_repository import ConfigRepository from litellm.repositories.table_repositories import ( @@ -511,6 +511,23 @@ class _PipelineErrorDetail(TypedDict): error: ReadOnly[_PipelineErrorBody] +def _undeliverable_stream_rewrite_error(policy_name: str, guardrail_name: str) -> HTTPException: + detail: Final[_PipelineErrorDetail] = { + "error": { + "message": ( + f"Streaming response withheld by policy pipeline '{policy_name}' because guardrail " + f"'{guardrail_name}' rewrote the streamed output, and streaming pipelines cannot deliver " + "rewrites. Retry with stream=false, or drop it from the pipeline steps so guardrails.add " + "applies it to streamed output." + ), + "type": "guardrail_pipeline_error", + "policies": (policy_name,), + "guardrails": (guardrail_name,), + } + } + return HTTPException(status_code=400, detail=detail) + + def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_api_key_dict: UserAPIKeyAuth) -> None: """ Reject up front the requests whose post_call pipelines could never run. @@ -3468,11 +3485,12 @@ class ProxyLogging: pipeline allows it), then runs each pipeline's steps against the assembled output through the endpoint guardrail translation, the same machinery flat post_call guardrails use at end of stream. An allow - releases the buffered chunks as that machinery left them (the - Responses and A2A translations write guardrail output back into the - final chunk, exactly as they do for flat guardrails); a block or - modify_response terminates with the translation's block chunks or the - raised error. + releases the buffered chunks verbatim; a step whose guardrail rewrote + the output withholds the stream with a 400 instead, since no + translation rewrites every buffered chunk consistently and some + rewrites (Bedrock's ANONYMIZED action, for one) are only decided at + runtime; a block or modify_response terminates with the translation's + block chunks or the raised error. """ buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict async for item in response: @@ -3495,16 +3513,26 @@ class ProxyLogging: call_type, endpoint_translation = resolved for policy_name, pipeline in pipelines: - result: PipelineExecutionResult = await PipelineExecutor.execute_steps( - steps=pipeline.steps, - mode="post_call", - data=request_data, - user_api_key_dict=user_api_key_dict, - call_type=call_type, - policy_name=policy_name, - streaming_chunks=buffered, - endpoint_translation=endpoint_translation, - ) + try: + result: PipelineExecutionResult = await PipelineExecutor.execute_steps( + steps=pipeline.steps, + mode="post_call", + data=request_data, + user_api_key_dict=user_api_key_dict, + call_type=call_type, + policy_name=policy_name, + streaming_chunks=buffered, + endpoint_translation=endpoint_translation, + ) + except UndeliverableStreamRewrite as rewrite: + async for error_chunk in unified_guardrail.emit_streaming_http_error( + _undeliverable_stream_rewrite_error(policy_name, rewrite.guardrail_name), + call_type, + buffered, + request_data, + ): + yield error_chunk + return try: ProxyLogging._handle_pipeline_result( result, data=request_data, policy_name=policy_name, original_response=buffered diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index 054a5af4148..52fd8777a19 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -13,7 +13,7 @@ from litellm.integrations.custom_guardrail import CustomGuardrail from litellm.proxy.guardrails.guardrail_hooks.custom_code.custom_code_guardrail import ( CustomCodeGuardrail, ) -from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor +from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor, UndeliverableStreamRewrite from litellm.types.proxy.policy_engine.pipeline_types import ( GuardrailPipeline, PipelineStep, @@ -811,3 +811,64 @@ async def test_pipeline_step_keeps_native_hook_when_opted_out(monkeypatch): assert outcome == "pass" assert guardrail.native_pre_call_ran is True assert "guardrail_to_apply" not in data + + +class _TextReturningGuardrail(CustomGuardrail): + def __init__(self, returned_texts): + super().__init__(guardrail_name="masker", event_hook="post_call", default_on=True) + self.returned_texts = returned_texts + + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + return {**inputs, "texts": self.returned_texts} + + +class _TextTranslation: + def __init__(self): + self.seen_guardrail_names = [] + + async def process_output_streaming_response( + self, responses_so_far, guardrail_to_apply, litellm_logging_obj=None, user_api_key_dict=None, request_data=None + ): + self.seen_guardrail_names.append(guardrail_to_apply.guardrail_name) + await guardrail_to_apply.apply_guardrail( + inputs={"texts": ["hello world"]}, + request_data=request_data or {}, + input_type="response", + logging_obj=litellm_logging_obj, + ) + return responses_so_far + + +async def _run_streaming_step(returned_texts, translation): + return await PipelineExecutor.execute_steps( + steps=[PipelineStep(guardrail="masker", on_pass="allow", on_fail="next", on_error="next")], + mode="post_call", + data={"model": "m"}, + user_api_key_dict=MagicMock(), + call_type="completion", + policy_name="p", + streaming_chunks=[object()], + endpoint_translation=translation, + ) + + +@pytest.mark.asyncio +async def test_streaming_step_rewrite_escapes_execute_steps_regardless_of_step_actions(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) + translation = _TextTranslation() + + with pytest.raises(UndeliverableStreamRewrite) as info: + await _run_streaming_step(["hello [MASKED]"], translation) + + assert info.value.guardrail_name == "masker" + assert translation.seen_guardrail_names == ["masker"] + + +@pytest.mark.asyncio +async def test_streaming_step_unchanged_texts_in_another_container_allow(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(("hello world",))]) + + result = await _run_streaming_step(("hello world",), _TextTranslation()) + + assert result.terminal_action == "allow" + assert [step.outcome for step in result.step_results] == ["pass"] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 895a986f348..74bd2e483cc 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -11,7 +11,7 @@ from __future__ import annotations import asyncio import json -from typing import Any, Dict, List +from typing import Any, Callable, Dict, List from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -878,10 +878,12 @@ async def test_process_prompt_template_aresponses_swaps_model_and_merges_input(p # --------------------------------------------------------------------------- -def _post_call_pipeline_data(guardrail: str = "gr-post", **extra: Any) -> Dict[str, Any]: +def _post_call_pipeline_data( + guardrail: str = "gr-post", step: PipelineStep | None = None, **extra: Any +) -> Dict[str, Any]: pipeline = GuardrailPipeline( mode="post_call", - steps=[PipelineStep(guardrail=guardrail, on_pass="allow", on_fail="block")], + steps=[step or PipelineStep(guardrail=guardrail, on_pass="allow", on_fail="block")], ) return { "model": "m", @@ -1587,6 +1589,101 @@ async def test_streaming_iterator_hook_pipeline_block_withholds_all_chunks( assert "output blocked" in str(info.value.detail) +def _rewriting_stream_guardrail(transform: Callable[[Dict[str, Any]], Dict[str, Any]]) -> CustomGuardrail: + class RewritingStreamGuardrail(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + return {**inputs, **transform(inputs)} + + return RewritingStreamGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False) + + +def _tool_call_stream_chunks() -> List[Any]: + tool_call = { + "index": 0, + "id": "call_1", + "type": "function", + "function": {"name": "lookup", "arguments": '{"ssn": "123"}'}, + } + return [ + litellm.ModelResponseStream( + choices=[{"index": 0, "delta": {"tool_calls": [tool_call]}, "finish_reason": None}] + ), + litellm.ModelResponseStream(choices=[{"index": 0, "delta": {}, "finish_reason": "tool_calls"}]), + ] + + +def _echoed_tool_call_dicts(arguments: str) -> List[Dict[str, Any]]: + return [{"id": "call_1", "type": "function", "function": {"name": "lookup", "arguments": arguments}}] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("on_fail, on_error", [("block", None), ("next", "next")]) +@pytest.mark.parametrize( + "make_chunks, transform", + [ + (_stream_chunks, lambda inputs: {"texts": ["hello [MASKED]"]}), + (_tool_call_stream_chunks, lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "[MASKED]"}')}), + ], + ids=["texts", "tool_calls"], +) +async def test_streaming_iterator_hook_pipeline_withholds_runtime_rewrite( + proxy_logging, make_user_api_key_auth, monkeypatch, make_chunks, transform, on_fail, on_error +): + monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + step = PipelineStep(guardrail="gr-post", on_pass="allow", on_fail=on_fail, on_error=on_error) + data = _post_call_pipeline_data(step=step, stream=True) + delivered: List[Any] = [] + + async def _drain() -> None: + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(make_chunks()), + request_data=data, + ): + delivered.append(item) + + with pytest.raises(HTTPException) as info: + await _drain() + + error = info.value.detail["error"] + assert delivered == [] + assert info.value.status_code == 400 + assert error["type"] == "guardrail_pipeline_error" + assert error["policies"] == ("response-governance",) + assert error["guardrails"] == ("gr-post",) + assert "stream=false" in error["message"] + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "make_chunks, transform", + [ + (_stream_chunks, lambda inputs: {"texts": tuple(inputs["texts"])}), + (_tool_call_stream_chunks, lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "123"}')}), + ], + ids=["texts_as_tuple", "tool_calls_as_dicts"], +) +async def test_streaming_iterator_hook_pipeline_releases_stream_echoed_in_another_shape( + proxy_logging, make_user_api_key_auth, monkeypatch, make_chunks, transform +): + monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + chunks = make_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + assert [id(item) for item in delivered] == [id(chunk) for chunk in chunks] + + @pytest.mark.asyncio async def test_streaming_iterator_hook_pipeline_withholds_unresolvable_response_shape( proxy_logging, make_user_api_key_auth, monkeypatch From badefa395cbfea283e2bac3d7ba545638a0792d0 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 21:30:31 -0700 Subject: [PATCH 016/223] fix(policy_engine): snapshot guardrail inputs before apply_guardrail so in-place stream rewrites are withheld --- .../proxy/policy_engine/pipeline_executor.py | 22 ++++++++++--------- .../policy_engine/test_pipeline_executor.py | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 21f3aca3f58..4c192a50096 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -57,16 +57,16 @@ def _tool_call_shape(tool_call: object) -> tuple[object, object]: return (function.get("name"), function.get("arguments")) -def _rewrote_texts(sent: Sequence[str] | None, returned: Sequence[str] | None) -> bool: - return sent is not None and returned is not None and tuple(returned) != tuple(sent) +def _text_snapshot(texts: Sequence[str] | None) -> tuple[str, ...] | None: + return None if texts is None else tuple(texts) -def _rewrote_tool_calls(sent: Sequence[object] | None, returned: Sequence[object] | None) -> bool: - if sent is None or returned is None: - return False - return tuple(_tool_call_shape(tool_call) for tool_call in returned) != tuple( - _tool_call_shape(tool_call) for tool_call in sent - ) +def _tool_call_shapes(tool_calls: Sequence[object] | None) -> tuple[tuple[object, object], ...] | None: + return None if tool_calls is None else tuple(_tool_call_shape(tool_call) for tool_call in tool_calls) + + +def _rewrote(sent: tuple[object, ...] | None, returned: tuple[object, ...] | None) -> bool: + return sent is not None and returned is not None and returned != sent class _StreamRewriteObserver(CustomGuardrail): @@ -90,13 +90,15 @@ class _StreamRewriteObserver(CustomGuardrail): input_type: Literal["request", "response"], logging_obj: "LiteLLMLoggingObj | None" = None, ) -> GenericGuardrailAPIInputs: + sent_texts: Final = _text_snapshot(inputs.get("texts")) + sent_tool_shapes: Final = _tool_call_shapes(inputs.get("tool_calls")) outputs: Final = await self.inner.apply_guardrail( inputs=inputs, request_data=request_data, input_type=input_type, logging_obj=logging_obj ) self.rewrote = ( self.rewrote - or _rewrote_texts(inputs.get("texts"), outputs.get("texts")) - or _rewrote_tool_calls(inputs.get("tool_calls"), outputs.get("tool_calls")) + or _rewrote(sent_texts, _text_snapshot(outputs.get("texts"))) + or _rewrote(sent_tool_shapes, _tool_call_shapes(outputs.get("tool_calls"))) ) return outputs diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index 52fd8777a19..ef5d206f1d8 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -872,3 +872,25 @@ async def test_streaming_step_unchanged_texts_in_another_container_allow(monkeyp assert result.terminal_action == "allow" assert [step.outcome for step in result.step_results] == ["pass"] + + +class _InPlaceMutatingGuardrail(CustomGuardrail): + """Rewrites like bedrock/presidio do: rebinds inputs["texts"] on the dict it was handed + and returns that same dict, so a post-call comparison against inputs sees no change.""" + + def __init__(self): + super().__init__(guardrail_name="masker", event_hook="post_call", default_on=True) + + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + inputs["texts"] = ["hello [MASKED]"] + return inputs + + +@pytest.mark.asyncio +async def test_streaming_step_in_place_rewrite_still_withholds_stream(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_InPlaceMutatingGuardrail()]) + + with pytest.raises(UndeliverableStreamRewrite) as info: + await _run_streaming_step(["hello [MASKED]"], _TextTranslation()) + + assert info.value.guardrail_name == "masker" From bcee01a7a7a3a29c5f6e54a0045ff3688d2dbdef Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 21:52:52 -0700 Subject: [PATCH 017/223] fix(policy_engine): merge guardrail metadata writes back on block and modify_response so failure spend records keep guardrail cost and status --- .../proxy/policy_engine/pipeline_executor.py | 2 + litellm/proxy/utils.py | 7 +++- .../proxy_logging/test_guardrail_pipeline.py | 38 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 4c192a50096..0c3ceb53707 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -236,6 +236,7 @@ class PipelineExecutor: step_results=step_results, error_message=error_detail, original_exception=original_exception, + modified_data=working_data if working_data != data else None, ) if action == "modify_response": @@ -243,6 +244,7 @@ class PipelineExecutor: terminal_action="modify_response", step_results=step_results, modify_response_message=step.modify_response_message or error_detail, + modified_data=working_data if working_data != data else None, ) # action == "next" → continue to next step diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index f9d018bc452..dbcea834d50 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1838,7 +1838,9 @@ class ProxyLogging: payload (already sent upstream) must stay untouched; a replacement response carried in ``modified_data`` is adopted by the caller, and metadata-bucket writes (applied guardrails, guardrail logging info) - are merged back so headers and spend logs still see them. On the + are merged back so headers and spend logs still see them, on block + and modify_response too, so failure spend records keep guardrail + cost and status. On the streaming path it is the buffered chunk list, carried into ``ModifyResponseException.original_response`` for usage reporting. """ @@ -1850,6 +1852,9 @@ class ProxyLogging: _merge_pipeline_metadata_writes(data, result.modified_data) return data + if result.modified_data is not None: + _merge_pipeline_metadata_writes(data, result.modified_data) + if result.terminal_action == "block": original_exception: Final = result.original_exception if original_exception is not None and not _exception_changes_request_flow(original_exception): diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 74bd2e483cc..d9b3578c966 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1197,6 +1197,44 @@ async def test_post_call_pipeline_guardrail_metadata_writes_reach_request_data( assert slg_entries[0]["guardrail_name"] == "gr-post" +@pytest.mark.asyncio +async def test_post_call_pipeline_block_keeps_guardrail_metadata_writes( + proxy_logging, make_user_api_key_auth, monkeypatch +): + class BlockingWriterGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + add_guardrail_to_applied_guardrails_header(request_data=data, guardrail_name="gr-post") + self.add_standard_logging_guardrail_information_to_request_data( + guardrail_json_response={"verdict": "fail"}, + request_data=data, + guardrail_status="guardrail_intervened", + ) + raise HTTPException(status_code=400, detail={"error": "output blocked"}) + + monkeypatch.setattr( + litellm, + "callbacks", + [ + BlockingWriterGuardrail( + guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False + ) + ], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + + with pytest.raises(HTTPException): + await proxy_logging.post_call_success_hook( + data=data, response=litellm.ModelResponse(), user_api_key_dict=make_user_api_key_auth() + ) + + assert data["metadata"]["applied_guardrails"] == ["gr-post"] + slg_entries = data["metadata"]["standard_logging_guardrail_information"] + assert len(slg_entries) == 1 + assert slg_entries[0]["guardrail_name"] == "gr-post" + assert slg_entries[0]["guardrail_status"] == "guardrail_intervened" + + @pytest.mark.asyncio async def test_post_call_pipeline_managed_parallel_guardrail_runs_exactly_once( proxy_logging, make_user_api_key_auth, monkeypatch From 673d1743a66363022777f0b3b261142ef77964ab Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:12:59 -0700 Subject: [PATCH 018/223] fix(policy_engine): apply post_call pipeline text rewrites on streams Buffered streams governed by post_call policy pipelines now deliver text rewrites back into the stream per surface (chat SSE, responses SSE, anthropic messages SSE) instead of rejecting the request with a 400 upfront. Rewrites chain across pipeline steps; tool-call rewrites and translations without stream write-back still withhold the stream. --- .../chat/guardrail_translation/handler.py | 74 +++++- .../guardrail_translation/base_translation.py | 15 +- .../chat/guardrail_translation/handler.py | 91 ++++++- .../guardrail_translation/handler.py | 83 ++++++- .../proxy/policy_engine/pipeline_executor.py | 90 +++++-- litellm/proxy/utils.py | 61 ++--- .../test_anthropic_guardrail_handler.py | 65 +++++ .../test_openai_guardrail_handler.py | 55 +++++ ...test_openai_responses_guardrail_handler.py | 83 +++++++ .../policy_engine/test_pipeline_executor.py | 2 + .../proxy_logging/test_guardrail_pipeline.py | 222 ++++++++++++++---- 11 files changed, 711 insertions(+), 130 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index b9ca18c7843..89c8431dfe4 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -13,9 +13,10 @@ Pattern Overview: """ import json -from collections.abc import Mapping, Sequence +from collections.abc import Iterator, Mapping, Sequence from copy import deepcopy from dataclasses import dataclass +from itertools import chain, repeat from typing import TYPE_CHECKING, Any, Final, cast from typing_extensions import assert_never @@ -120,6 +121,8 @@ class AnthropicMessagesHandler(BaseTranslation): them through guardrail rewrites; downstream provider handling is out of scope. """ + delivers_ended_stream_text_rewrites = True + def __init__(self): super().__init__() self.adapter = LiteLLMAnthropicMessagesAdapter() @@ -931,11 +934,14 @@ class AnthropicMessagesHandler(BaseTranslation): litellm_logging_obj: "LiteLLMLoggingObj | None" = None, user_api_key_dict: "UserAPIKeyAuth | None" = None, request_data: dict | None = None, + deliver_ended_stream_rewrites: bool = False, ) -> list[Any]: """ Process output streaming response by applying guardrails to text content. Get the string so far, check the apply guardrail to the string so far, and return the list of responses so far. + With ``deliver_ended_stream_rewrites``, an ended stream whose guardrail rewrote the text gets the rewrite + written back across the buffered chunks (full rewritten text in the first ``text_delta``, the rest blanked). """ from litellm.integrations.custom_guardrail import ModifyResponseException @@ -982,6 +988,15 @@ class AnthropicMessagesHandler(BaseTranslation): responses_so_far, request_data ) raise + guardrailed_texts: Final = _guardrailed_inputs.get("texts") + if ( + deliver_ended_stream_rewrites + and isinstance(string_so_far, str) + and string_so_far + and guardrailed_texts + and guardrailed_texts[0] != string_so_far + ): + self._write_ended_stream_text_rewrite(responses_so_far, guardrailed_texts[0]) else: verbose_proxy_logger.debug("Skipping output guardrail - model response has no choices") return responses_so_far @@ -1093,6 +1108,63 @@ class AnthropicMessagesHandler(BaseTranslation): inputs["model"] = response_model return inputs + @staticmethod + def _write_ended_stream_text_rewrite( + responses_so_far: list[Any], # mutable-ok: rewrites the caller's buffered chunks in place + rewritten_text: str, + ) -> None: + """Deliver an ended-stream guardrail text rewrite by rewriting the + buffered chunks in place: the first ``text_delta`` carries the full + rewritten text and every later one is blanked, leaving the surrounding + message and content-block framing untouched. Handles both chunk formats + this stream carries (parsed event dicts and raw SSE bytes).""" + replacements: Final = chain((rewritten_text,), repeat("")) + for idx, item in enumerate(responses_so_far): + if isinstance(item, dict): + delta = item.get("delta") + if item.get("type") == "content_block_delta" and isinstance(delta, dict): + if delta.get("type") == "text_delta": + delta["text"] = next(replacements) + elif isinstance(item, (bytes, bytearray)): + responses_so_far[idx] = ( # rebind-ok: delivers the rewrite into the caller's buffer + AnthropicMessagesHandler._rewrite_sse_text_deltas(bytes(item), replacements) + ) + + @staticmethod + def _rewrite_sse_text_deltas(sse_bytes: bytes, replacements: "Iterator[str]") -> bytes: + """Rewrite every ``text_delta`` data line in one SSE chunk with the next + replacement text, leaving all other events and framing byte-identical.""" + try: + decoded: Final = sse_bytes.decode("utf-8") + except UnicodeDecodeError: + return sse_bytes + return "\n\n".join( + AnthropicMessagesHandler._rewrite_sse_block(block, replacements) for block in decoded.split("\n\n") + ).encode("utf-8") + + @staticmethod + def _rewrite_sse_block(block: str, replacements: "Iterator[str]") -> str: + return "\n".join(AnthropicMessagesHandler._rewrite_sse_line(line, replacements) for line in block.split("\n")) + + @staticmethod + def _rewrite_sse_line(line: str, replacements: "Iterator[str]") -> str: + if not line.startswith("data:"): + return line + try: + data: Final[str | int | float | bool | None | Sequence[object] | Mapping[str, object]] = json.loads( + line[len("data:") :].strip() + ) + except json.JSONDecodeError: + return line + if not isinstance(data, dict) or data.get("type") != "content_block_delta": + return line + delta: Final = data.get("delta") + if not isinstance(delta, dict) or delta.get("type") != "text_delta": + return line + return "data: " + json.dumps( + {**data, "delta": {**delta, "text": next(replacements)}} # mutable-ok: json.dumps needs plain dicts + ) + def get_streaming_string_so_far(self, responses_so_far: list[Any]) -> str: """ Parse streaming responses and extract accumulated text content. diff --git a/litellm/llms/base_llm/guardrail_translation/base_translation.py b/litellm/llms/base_llm/guardrail_translation/base_translation.py index ba96ab3dc99..4b0cc0fd97c 100644 --- a/litellm/llms/base_llm/guardrail_translation/base_translation.py +++ b/litellm/llms/base_llm/guardrail_translation/base_translation.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, Final, Optional +from typing import TYPE_CHECKING, Any, ClassVar, Final, Optional if TYPE_CHECKING: from litellm.integrations.custom_guardrail import ( @@ -33,6 +33,13 @@ class StreamTransformSink: class BaseTranslation(ABC): + delivers_ended_stream_text_rewrites: ClassVar[bool] = False + """Whether ``process_output_streaming_response`` accepts + ``deliver_ended_stream_rewrites=True`` and, on an ended (fully buffered) + stream, writes guardrail text rewrites back across ``responses_so_far`` so + a buffered pipeline can release rewritten chunks instead of withholding the + stream. Tool-call rewrites stay undeliverable everywhere.""" + @staticmethod def transform_user_api_key_dict_to_metadata( user_api_key_dict: Any | None, @@ -113,6 +120,7 @@ class BaseTranslation(ABC): user_api_key_dict: Optional["UserAPIKeyAuth"] = None, request_data: dict | None = None, stream_transform_sink: StreamTransformSink | None = None, + deliver_ended_stream_rewrites: bool = False, ) -> Any: """ Process output streaming response with guardrails. @@ -120,6 +128,11 @@ class BaseTranslation(ABC): Optional to override in subclasses. ``stream_transform_sink`` is the out-parameter used by handlers that support streaming text transformations (see ``StreamTransformSink``); base handlers ignore it. + ``deliver_ended_stream_rewrites`` is passed True only when the caller + holds the whole buffered stream and the subclass declares + ``delivers_ended_stream_text_rewrites``: the handler then writes + guardrail text rewrites back across ``responses_so_far`` instead of + discarding them. """ return responses_so_far diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index 54673c77f80..1358cf7c37a 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -61,6 +61,8 @@ class OpenAIChatCompletionsHandler(BaseTranslation): Methods can be overridden to customize behavior for different message formats. """ + delivers_ended_stream_text_rewrites = True + def get_structured_messages(self, data: dict) -> list[AllMessageValues] | None: """ Convert chat completions request data to OpenAI-spec structured messages. @@ -440,6 +442,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): user_api_key_dict: Any | None = None, request_data: dict | None = None, stream_transform_sink: StreamTransformSink | None = None, + deliver_ended_stream_rewrites: bool = False, ) -> list["ModelResponseStream"]: """ Process output streaming responses by applying guardrails to text content. @@ -454,6 +457,10 @@ class OpenAIChatCompletionsHandler(BaseTranslation): accumulated text (``responses_so_far`` is left untouched so it stays a correct raw accumulator across rounds) and the guardrailed text plus requested holdback are reported per choice on the sink. + deliver_ended_stream_rewrites: When True and the buffered stream has + ended, guardrail text rewrites are written back across + ``responses_so_far`` (full rewritten text in each choice's first + content-carrying chunk, the rest blanked) instead of discarded. Returns: The (unmodified) list of responses. @@ -479,6 +486,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): litellm_logging_obj=litellm_logging_obj, user_api_key_dict=user_api_key_dict, request_data=request_data, + deliver_ended_stream_rewrites=deliver_ended_stream_rewrites, ) async def _process_streaming_block_only( @@ -489,10 +497,12 @@ class OpenAIChatCompletionsHandler(BaseTranslation): litellm_logging_obj: "LiteLLMLoggingObj | None", user_api_key_dict: Any | None, request_data: dict | None, + deliver_ended_stream_rewrites: bool = False, ) -> list["ModelResponseStream"]: """Block-only streaming path: run the guardrail so an in-flight BLOCK can terminate the stream. Text rewrites are not propagated to the client here - (see ``_process_streaming_transform`` for the incremental_diff path).""" + (see ``_process_streaming_transform`` for the incremental_diff path) unless + ``deliver_ended_stream_rewrites`` opts the ended-stream branch in.""" # check if the stream has ended has_stream_ended = False for chunk in responses_so_far: @@ -501,20 +511,14 @@ class OpenAIChatCompletionsHandler(BaseTranslation): break if has_stream_ended: - # convert to model response - model_response: Final = cast( - ModelResponse, - stream_chunk_builder(chunks=responses_so_far, logging_obj=litellm_logging_obj), - ) - # run process_output_response - await self.process_output_response( - response=model_response, + await self._process_ended_stream( + responses_so_far=responses_so_far, guardrail_to_apply=guardrail_to_apply, litellm_logging_obj=litellm_logging_obj, user_api_key_dict=user_api_key_dict, request_data=request_data, + deliver_ended_stream_rewrites=deliver_ended_stream_rewrites, ) - return responses_so_far # Step 0: Check if any response has text content to process @@ -591,6 +595,38 @@ class OpenAIChatCompletionsHandler(BaseTranslation): return responses_so_far + async def _process_ended_stream( + self, + *, + responses_so_far: list["ModelResponseStream"], # mutable-ok: rewrites the caller's buffered chunks in place + guardrail_to_apply: "CustomGuardrail", + litellm_logging_obj: "LiteLLMLoggingObj | None", + user_api_key_dict: object, + request_data: dict[str, object] | None, # mutable-ok: same request-payload shape the hooks take + deliver_ended_stream_rewrites: bool, + ) -> None: + """Ended-stream path: rebuild the full response, run the non-streaming + output guardrail against it, and (when opted in) write any text rewrite + back across the buffered chunks.""" + model_response: Final = cast( + ModelResponse, + stream_chunk_builder(chunks=responses_so_far, logging_obj=litellm_logging_obj), + ) + pre_guardrail_texts: Final = self._string_choice_contents(model_response) + await self.process_output_response( + response=model_response, + guardrail_to_apply=guardrail_to_apply, + litellm_logging_obj=litellm_logging_obj, + user_api_key_dict=user_api_key_dict, + request_data=request_data, + ) + if deliver_ended_stream_rewrites: + await self._write_ended_stream_text_rewrites( + responses_so_far=responses_so_far, + guardrailed_response=model_response, + pre_guardrail_texts=pre_guardrail_texts, + ) + @staticmethod def _accumulate_string_content_by_choice_index( responses_so_far: list["ModelResponseStream"], @@ -922,6 +958,41 @@ class OpenAIChatCompletionsHandler(BaseTranslation): if "name" in func_dict: existing_tool_call.function.name = func_dict["name"] + @staticmethod + def _string_choice_contents(response: "ModelResponse") -> tuple[str | None, ...]: + return tuple( + choice.message.content if isinstance(choice.message.content, str) else None for choice in response.choices + ) + + async def _write_ended_stream_text_rewrites( + self, + responses_so_far: list["ModelResponseStream"], # mutable-ok: rewrites the caller's buffered chunks in place + guardrailed_response: "ModelResponse", + pre_guardrail_texts: tuple[str | None, ...], + ) -> None: + """Write ended-stream guardrail text rewrites back across the buffered + chunks: each rewritten choice's full text lands in its first + content-carrying chunk and the rest are blanked, the same shape the + in-flight write-back uses. Chunks carrying only finish_reason or usage + stay untouched.""" + post_guardrail_texts: Final = self._string_choice_contents(guardrailed_response) + changed: Final = tuple( + (choice_idx, after) + for choice_idx, (before, after) in enumerate(zip(pre_guardrail_texts, post_guardrail_texts)) + if before is not None and after is not None and after != before + ) + if not changed: + return + await self._apply_guardrail_responses_to_output_streaming( + responses=responses_so_far, + guardrailed_texts=[ + after for _choice_idx, after in changed + ], # mutable-ok: the callee's signature predates this change and takes lists + task_mappings=[ + (choice_idx, None) for choice_idx, _after in changed + ], # mutable-ok: the callee's signature predates this change and takes lists + ) + async def _apply_guardrail_responses_to_output_streaming( self, responses: list["ModelResponseStream"], diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 7c5d8ac99ad..0f475aa04c8 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -28,7 +28,9 @@ Output: response.output is List[GenericResponseOutputItem] where each has: - text: str """ -from collections.abc import Sequence +from collections.abc import Mapping, Sequence +from itertools import chain, repeat +from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, Union, cast from openai.types.responses.response_function_tool_call import ResponseFunctionToolCall @@ -91,6 +93,8 @@ class OpenAIResponsesHandler(BaseTranslation): Methods can be overridden to customize behavior for different message formats. """ + delivers_ended_stream_text_rewrites = True + def get_structured_messages(self, data: dict) -> list[AllMessageValues] | None: """ Convert Responses API request data to OpenAI-spec structured messages. @@ -482,6 +486,7 @@ class OpenAIResponsesHandler(BaseTranslation): litellm_logging_obj: "LiteLLMLoggingObj | None" = None, user_api_key_dict: "UserAPIKeyAuth | None" = None, request_data: dict | None = None, + deliver_ended_stream_rewrites: bool = False, ) -> list[Any]: """ Process output streaming response by applying guardrails to text content. @@ -493,7 +498,11 @@ class OpenAIResponsesHandler(BaseTranslation): For ``response.completed`` events (the normal end-of-stream signal) we use the same per-item extraction + task-mapping approach as ``process_output_response`` so that unmasking / blocking works correctly - for every output item. + for every output item. With ``deliver_ended_stream_rewrites`` the earlier + text-carrying events (``response.output_text.delta`` / ``.done``, + ``response.content_part.done``, ``response.output_item.done``) are synced + to the rewritten completed response too, so a client reading deltas sees + the rewrite instead of the raw model output. """ if not responses_so_far: return responses_so_far @@ -562,6 +571,19 @@ class OpenAIResponsesHandler(BaseTranslation): responses=guardrailed_texts, task_mappings=task_mappings, ) + if deliver_ended_stream_rewrites: + rewrites_by_position: Final = MappingProxyType( + { + task_mappings[task_idx]: rewritten + for task_idx, rewritten in enumerate(guardrailed_texts) + if task_idx < len(texts_to_check) and rewritten != texts_to_check[task_idx] + } + ) + if rewrites_by_position: + self._sync_stream_events_with_rewrites( + stream_events=responses_so_far[:-1], + rewrites_by_position=rewrites_by_position, + ) return responses_so_far @@ -607,6 +629,63 @@ class OpenAIResponsesHandler(BaseTranslation): ) return responses_so_far + @staticmethod + def _write_event_field(event: object, field: str, value: str) -> None: + if isinstance(event, dict): + event[field] = value # rebind-ok: delivering the rewrite means editing the buffered event in place + else: + setattr(event, field, value) + + def _sync_stream_events_with_rewrites( + self, + stream_events: Sequence[Any], + rewrites_by_position: Mapping[tuple[int, int], str], + ) -> None: + """Sync pre-completion stream events with the rewritten completed + response, keyed by ``(output_index, content_index)``: the first + ``output_text.delta`` for a rewritten item carries the full rewritten + text and the rest are blanked, while ``output_text.done``, + ``content_part.done``, and ``output_item.done`` events carry the full + rewritten text, so every event a client may read agrees with the + rewritten ``response.completed`` payload.""" + delta_replacements: Final = MappingProxyType( + {position: chain((rewritten,), repeat("")) for position, rewritten in rewrites_by_position.items()} + ) + for event in stream_events: + if not (isinstance(event, dict) or hasattr(event, "get")): + continue + event_type = event.get("type") + output_index = event.get("output_index") + content_index = event.get("content_index") + if event_type == "response.output_item.done" and isinstance(output_index, int): + self._sync_output_item_done_event(event.get("item"), output_index, rewrites_by_position) + continue + if not isinstance(output_index, int) or not isinstance(content_index, int): + continue + position = (output_index, content_index) + if event_type == "response.output_text.delta" and position in delta_replacements: + self._write_event_field(event, "delta", next(delta_replacements[position])) + elif event_type == "response.output_text.done" and position in rewrites_by_position: + self._write_event_field(event, "text", rewrites_by_position[position]) + elif event_type == "response.content_part.done" and position in rewrites_by_position: + part = event.get("part") + if isinstance(part, dict) or hasattr(part, "text"): + self._write_event_field(part, "text", rewrites_by_position[position]) + + @staticmethod + def _sync_output_item_done_event( + item: object, + output_index: int, + rewrites_by_position: Mapping[tuple[int, int], str], + ) -> None: + content: Final = item.get("content") if isinstance(item, dict) else getattr(item, "content", None) + if not isinstance(content, list): + return + for (item_idx, content_idx), rewritten in rewrites_by_position.items(): + if item_idx != output_index or content_idx >= len(content): + continue + OpenAIResponsesHandler._write_event_field(content[content_idx], "text", rewritten) + def _check_streaming_has_ended(self, responses_so_far: Sequence[ResponsesStreamChunk]) -> bool: """ Check if the streaming has ended. diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index acd2c2c973a..1264cdfc14a 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -34,6 +34,7 @@ if TYPE_CHECKING: from litellm.llms.base_llm.guardrail_translation.base_translation import ( BaseTranslation, ) + from litellm.proxy._types import UserAPIKeyAuth try: from fastapi.exceptions import HTTPException @@ -44,7 +45,8 @@ except ImportError: class UndeliverableStreamRewrite(Exception): def __init__(self, guardrail_name: str) -> None: super().__init__( - f"Guardrail '{guardrail_name}' rewrote the streamed response, which streaming pipelines cannot deliver" + f"Guardrail '{guardrail_name}' rewrote the streamed response in a way this endpoint's " + "streaming pipeline cannot deliver" ) self.guardrail_name: Final = guardrail_name @@ -57,28 +59,31 @@ def _tool_call_shape(tool_call: object) -> tuple[object, object]: return (function.get("name"), function.get("arguments")) -def _rewrote_texts(sent: Sequence[str] | None, returned: Sequence[str] | None) -> bool: - return sent is not None and returned is not None and list(returned) != list(sent) +def _text_snapshot(texts: Sequence[str] | None) -> tuple[str, ...] | None: + return None if texts is None else tuple(texts) -def _rewrote_tool_calls(sent: Sequence[object] | None, returned: Sequence[object] | None) -> bool: - if sent is None or returned is None: - return False - return [_tool_call_shape(tool_call) for tool_call in returned] != [ - _tool_call_shape(tool_call) for tool_call in sent - ] +def _tool_call_shapes(tool_calls: Sequence[object] | None) -> tuple[tuple[object, object], ...] | None: + return None if tool_calls is None else tuple(_tool_call_shape(tool_call) for tool_call in tool_calls) + + +def _rewrote(sent: tuple[object, ...] | None, returned: tuple[object, ...] | None) -> bool: + return sent is not None and returned is not None and returned != sent class _StreamRewriteObserver(CustomGuardrail): """Stand-in handed to the endpoint translation in place of a streaming pipeline step's - guardrail. Translations cannot rewrite every buffered chunk consistently, so the gate - withholds the stream whenever the guardrail returned different output than it was given, - which for guardrails like Bedrock's ANONYMIZED action is only known at runtime.""" + guardrail. It records whether the guardrail returned different output than it was given, + which for guardrails like Bedrock's ANONYMIZED action is only known at runtime. Text + rewrites are deliverable on translations that write them back across the buffered chunks + (``delivers_ended_stream_text_rewrites``); tool-call rewrites and text rewrites on any + other translation make the gate withhold the stream.""" def __init__(self, inner: CustomGuardrail) -> None: super().__init__(guardrail_name=inner.guardrail_name) self.inner: Final = inner - self.rewrote = False + self.rewrote_texts = False + self.rewrote_tool_calls = False def structured_messages_cover_full_request(self) -> bool: return self.inner.structured_messages_cover_full_request() @@ -90,13 +95,14 @@ class _StreamRewriteObserver(CustomGuardrail): input_type: Literal["request", "response"], logging_obj: "LiteLLMLoggingObj | None" = None, ) -> GenericGuardrailAPIInputs: + sent_texts: Final = _text_snapshot(inputs.get("texts")) + sent_tool_shapes: Final = _tool_call_shapes(inputs.get("tool_calls")) outputs: Final = await self.inner.apply_guardrail( inputs=inputs, request_data=request_data, input_type=input_type, logging_obj=logging_obj ) - self.rewrote = ( - self.rewrote - or _rewrote_texts(inputs.get("texts"), outputs.get("texts")) - or _rewrote_tool_calls(inputs.get("tool_calls"), outputs.get("tool_calls")) + self.rewrote_texts = self.rewrote_texts or _rewrote(sent_texts, _text_snapshot(outputs.get("texts"))) + self.rewrote_tool_calls = self.rewrote_tool_calls or _rewrote( + sent_tool_shapes, _tool_call_shapes(outputs.get("tool_calls")) ) return outputs @@ -250,6 +256,41 @@ class PipelineExecutor: modified_data=working_data if working_data != data else None, ) + @staticmethod + async def _run_streaming_step( + step: PipelineStep, + callback: CustomGuardrail, + endpoint_translation: "BaseTranslation", + streaming_chunks: list[object], # mutable-ok: shared buffered-stream chunks the translation rewrites in place + hook_input: dict[str, object], # mutable-ok: same request-payload shape as data + user_api_key_dict: "UserAPIKeyAuth | None", + litellm_logging_obj: "LiteLLMLoggingObj | None", + ) -> None: + """Run one streaming post_call step through the endpoint translation, delivering + text rewrites on translations that support ended-stream write-back and raising + ``UndeliverableStreamRewrite`` for any rewrite that cannot reach the client.""" + observer: Final = _StreamRewriteObserver(callback) + deliver_rewrites: Final = type(endpoint_translation).delivers_ended_stream_text_rewrites + if deliver_rewrites: + await endpoint_translation.process_output_streaming_response( + responses_so_far=streaming_chunks, + guardrail_to_apply=observer, + litellm_logging_obj=litellm_logging_obj, + user_api_key_dict=user_api_key_dict, + request_data=hook_input, + deliver_ended_stream_rewrites=True, + ) + else: + await endpoint_translation.process_output_streaming_response( + responses_so_far=streaming_chunks, + guardrail_to_apply=observer, + litellm_logging_obj=litellm_logging_obj, + user_api_key_dict=user_api_key_dict, + request_data=hook_input, + ) + if observer.rewrote_tool_calls or (observer.rewrote_texts and not deliver_rewrites): + raise UndeliverableStreamRewrite(step.guardrail) + @staticmethod async def _run_step( step: PipelineStep, @@ -312,16 +353,15 @@ class PipelineExecutor: f"Guardrail '{step.guardrail}' does not support streaming pipeline execution", None, ) - observer: Final = _StreamRewriteObserver(callback) - await endpoint_translation.process_output_streaming_response( - responses_so_far=streaming_chunks, - guardrail_to_apply=observer, - litellm_logging_obj=data.get("litellm_logging_obj"), + await PipelineExecutor._run_streaming_step( + step=step, + callback=callback, + endpoint_translation=endpoint_translation, + streaming_chunks=streaming_chunks, + hook_input=hook_input, user_api_key_dict=user_api_key_dict, - request_data=hook_input, + litellm_logging_obj=data.get("litellm_logging_obj"), ) - if observer.rewrote: - raise UndeliverableStreamRewrite(step.guardrail) response = None elif mode == "post_call": response = await target.async_post_call_success_hook( diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 6c990222e51..7f3c3aecd87 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -492,14 +492,6 @@ def _pipeline_step_supports_unified_streaming(guardrail_name: str) -> bool: return callback is not None and PipelineExecutor.supports_unified_execution(callback) -def _pipeline_step_rewrites_streamed_content(guardrail_name: str) -> bool: - callback: Final = PipelineExecutor.find_guardrail_callback(guardrail_name) - if callback is None: - return False - transform_mode: Final = unified_guardrail.resolve_streaming_flag(callback, "streaming_transform_mode", "block_only") - return callback.rewrites_streamed_output() or transform_mode == "incremental_diff" - - class _PipelineErrorBody(TypedDict): message: ReadOnly[str] type: ReadOnly[str] @@ -516,9 +508,10 @@ def _undeliverable_stream_rewrite_error(policy_name: str, guardrail_name: str) - "error": { "message": ( f"Streaming response withheld by policy pipeline '{policy_name}' because guardrail " - f"'{guardrail_name}' rewrote the streamed output, and streaming pipelines cannot deliver " - "rewrites. Retry with stream=false, or drop it from the pipeline steps so guardrails.add " - "applies it to streamed output." + f"'{guardrail_name}' rewrote the streamed output in a way this endpoint's streaming " + "pipeline cannot deliver (a tool-call rewrite, or a text rewrite on a route without " + "stream write-back). Retry with stream=false, or drop it from the pipeline steps so " + "guardrails.add applies it to streamed output." ), "type": "guardrail_pipeline_error", "policies": (policy_name,), @@ -535,13 +528,13 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_ap Background responses skip the post_call hooks entirely, so a pipeline governing one would silently never execute. Streaming responses execute pipelines against the buffered stream through the endpoint guardrail - translation of the request route, releasing the buffered chunks on allow. - That needs every step's guardrail to support the unified apply_guardrail - interface and to only allow or block (a step that rewrites streamed - content, via mask_response_content, a MASK action, or - streaming_transform_mode=incremental_diff, would have its rewrite silently - dropped), and needs the route to have a translation at all; anything else - keeps the 400 rather than letting ungoverned output stream through. + translation of the request route, releasing the buffered chunks on allow + (rewritten in place when a guardrail rewrote text and the translation + delivers ended-stream rewrites; a rewrite the translation cannot deliver + fails closed at runtime instead). That needs every step's guardrail to + support the unified apply_guardrail interface, and needs the route to have + a translation at all; anything else keeps the 400 rather than letting + ungoverned output stream through. """ is_stream: Final = data.get("stream") is True is_background: Final = data.get("background") is True @@ -586,25 +579,6 @@ def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_ap } } raise HTTPException(status_code=400, detail=unsupported_detail) - rewriting_guardrails: Final = tuple( - guardrail for guardrail in step_guardrails if _pipeline_step_rewrites_streamed_content(guardrail) - ) - if rewriting_guardrails: - rewriting_detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming responses " - "because these pipeline guardrails rewrite streamed content (mask_response_content, " - "a MASK action, or streaming_transform_mode=incremental_diff), which pipeline steps would release " - f"unmodified: {', '.join(rewriting_guardrails)}. Retry with stream=false, or drop " - "them from the pipeline steps so guardrails.add applies them to streamed output." - ), - "type": "guardrail_pipeline_error", - "policies": post_call_policies, - "guardrails": rewriting_guardrails, - } - } - raise HTTPException(status_code=400, detail=rewriting_detail) route: Final = user_api_key_dict.request_route if not route or resolve_endpoint_translation(user_api_key_dict, None) is not None: return @@ -3485,12 +3459,13 @@ class ProxyLogging: pipeline allows it), then runs each pipeline's steps against the assembled output through the endpoint guardrail translation, the same machinery flat post_call guardrails use at end of stream. An allow - releases the buffered chunks verbatim; a step whose guardrail rewrote - the output withholds the stream with a 400 instead, since no - translation rewrites every buffered chunk consistently and some - rewrites (Bedrock's ANONYMIZED action, for one) are only decided at - runtime; a block or modify_response terminates with the translation's - block chunks or the raised error. + releases the buffered chunks: verbatim when no guardrail rewrote the + output, rewritten in place when one rewrote text and the translation + delivers ended-stream rewrites (later steps then re-scan the rewritten + chunks, so rewrites chain). A rewrite the translation cannot deliver + (a tool-call rewrite, or a text rewrite on a route without write-back) + withholds the stream with a 400; a block or modify_response terminates + with the translation's block chunks or the raised error. """ buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict async for item in response: diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index af3ccd65b11..ba26da50bc8 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -263,6 +263,71 @@ class TestAnthropicMessagesHandlerStreamingOutputProcessing: # Should return the responses unchanged assert result == responses_so_far + @staticmethod + def _ended_sse_chunks() -> list: + events = [ + ("message_start", {"type": "message_start", "message": {"id": "msg_1", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [], "stop_reason": None, "usage": {"input_tokens": 1, "output_tokens": 0}}}), + ("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "hello "}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "world"}}), + ("content_block_stop", {"type": "content_block_stop", "index": 0}), + ("message_delta", {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 2}}), + ("message_stop", {"type": "message_stop"}), + ] + return [f"event: {name}\ndata: {json.dumps(payload)}\n\n".encode() for name, payload in events] + + @staticmethod + def _masking_guardrail() -> CustomGuardrail: + class MaskWorld(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + return {**inputs, "texts": [text.replace("world", "[MASKED]") for text in inputs.get("texts", [])]} + + return MaskWorld(guardrail_name="test") + + @staticmethod + def _delta_texts(chunks: list) -> list: + texts = [] + for chunk in chunks: + for line in chunk.decode().split("\n"): + if not line.startswith("data:"): + continue + data = json.loads(line[len("data:") :].strip()) + if data.get("type") == "content_block_delta": + texts.append(data["delta"]["text"]) + return texts + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_writes_text_back_into_sse_chunks(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_sse_chunks() + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=MagicMock(), + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert self._delta_texts(chunks) == ["hello [MASKED]", ""] + raw = b"".join(chunks).decode() + assert "event: message_start" in raw and "event: message_stop" in raw + assert '"stop_reason": "end_turn"' in raw + + @pytest.mark.asyncio + async def test_ended_stream_rewrite_leaves_chunks_untouched_by_default(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_sse_chunks() + original = [bytes(chunk) for chunk in chunks] + + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=MagicMock(), + ) + + assert chunks == original + class TestAnthropicMessagesHandlerInputProcessing: """Test input processing preserves litellm_metadata for dynamic guardrails.""" diff --git a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py index a29e0be4655..26442a4a6ed 100644 --- a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py @@ -1073,6 +1073,61 @@ class TestOpenAIChatCompletionsHandlerStreamingOutput: # Should return the responses assert result == responses_so_far + @staticmethod + def _ended_stream_chunks() -> list: + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + + return [ + ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[StreamingChoices(index=0, delta=Delta(content="Hello"), finish_reason=None)], + ), + ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[StreamingChoices(index=0, delta=Delta(content=" world"), finish_reason="stop")], + ), + ] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_writes_text_back_into_chunks(self): + handler = OpenAIChatCompletionsHandler() + guardrail = MockGuardrail(guardrail_name="test") + chunks = self._ended_stream_chunks() + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert chunks[0].choices[0].delta.content == "HELLO WORLD" + assert chunks[1].choices[0].delta.content in (None, "") + assert chunks[1].choices[0].finish_reason == "stop" + + @pytest.mark.asyncio + async def test_ended_stream_rewrite_leaves_chunks_untouched_by_default(self): + handler = OpenAIChatCompletionsHandler() + guardrail = MockGuardrail(guardrail_name="test") + chunks = self._ended_stream_chunks() + + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + ) + + assert chunks[0].choices[0].delta.content == "Hello" + assert chunks[1].choices[0].delta.content == " world" + assert chunks[1].choices[0].finish_reason == "stop" + class TestGetStructuredMessages: """Test the get_structured_messages method.""" diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index 447175b09a6..4f95e08cb71 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1104,6 +1104,89 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: output_text = result[-1]["response"]["output"][0]["content"][0]["text"] assert output_text == original_text + @staticmethod + def _ended_stream_events() -> List[dict]: + content = [{"type": "output_text", "text": "hello world"}] + item = { + "type": "message", + "id": "msg_123", + "status": "completed", + "role": "assistant", + "content": content, + } + return [ + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "hello "}, + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "world"}, + {"type": "response.output_text.done", "output_index": 0, "content_index": 0, "text": "hello world"}, + { + "type": "response.content_part.done", + "output_index": 0, + "content_index": 0, + "part": {"type": "output_text", "text": "hello world"}, + }, + {"type": "response.output_item.done", "output_index": 0, "item": {**item, "content": [dict(c) for c in content]}}, + { + "type": "response.completed", + "response": { + "id": "resp_123", + "model": "gpt-4o", + "output": [{**item, "content": [dict(c) for c in content]}], + "status": "completed", + }, + }, + ] + + @staticmethod + def _masking_guardrail() -> CustomGuardrail: + class MaskWorld(CustomGuardrail): + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + texts = inputs.get("texts", []) + return {**inputs, "texts": [t.replace("world", "[MASKED]") for t in texts]} + + return MaskWorld(guardrail_name="test-mask") + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_syncs_all_stream_events(self): + handler = OpenAIResponsesHandler() + events = self._ended_stream_events() + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is events + assert events[0]["delta"] == "hello [MASKED]" + assert events[1]["delta"] == "" + assert events[2]["text"] == "hello [MASKED]" + assert events[3]["part"]["text"] == "hello [MASKED]" + assert events[4]["item"]["content"][0]["text"] == "hello [MASKED]" + assert events[5]["response"]["output"][0]["content"][0]["text"] == "hello [MASKED]" + + @pytest.mark.asyncio + async def test_ended_stream_rewrite_leaves_delta_events_untouched_by_default(self): + handler = OpenAIResponsesHandler() + events = self._ended_stream_events() + + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + ) + + assert events[0]["delta"] == "hello " + assert events[1]["delta"] == "world" + assert events[2]["text"] == "hello world" + assert events[5]["response"]["output"][0]["content"][0]["text"] == "hello [MASKED]" + class TestGetStructuredMessages: """Test the get_structured_messages method for Responses API handler.""" diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index 52fd8777a19..908c9f12c9e 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -823,6 +823,8 @@ class _TextReturningGuardrail(CustomGuardrail): class _TextTranslation: + delivers_ended_stream_text_rewrites = False + def __init__(self): self.seen_guardrail_names = [] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 74bd2e483cc..73270ec5671 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -24,7 +24,7 @@ from litellm.integrations.custom_guardrail import ( ModifyResponseException, ) from litellm.integrations.prometheus import PrometheusLogger -from litellm.proxy._types import ProxyException +from litellm.proxy._types import ProxyException, UserAPIKeyAuth from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ContentFilterGuardrail @@ -1441,7 +1441,7 @@ async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_lacks_uni ("guardrail_config", {"streaming_transform_mode": "incremental_diff"}), ], ) -async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_rewrites_streamed_content( +async def test_pre_call_hook_allows_streaming_when_pipeline_guardrail_rewrites_streamed_content( proxy_logging, make_user_api_key_auth, monkeypatch, rewrite_attribute, value ): seen: Dict[str, Any] = {} @@ -1450,24 +1450,21 @@ async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_rewrites_ monkeypatch.setattr(litellm, "callbacks", [guardrail]) data = _post_call_pipeline_data(stream=True) - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( - user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), - data=data, - call_type="completion", - guardrails_only=True, - ) + out = await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + data=data, + call_type="completion", + guardrails_only=True, + ) - assert info.value.status_code == 400 - assert info.value.detail["error"]["guardrails"] == ("gr-post",) - assert "rewrite streamed content" in info.value.detail["error"]["message"] - assert seen.get("count") is None + assert out is not None + assert out.get("stream") is True @pytest.mark.asyncio -@pytest.mark.parametrize("action, rejected", [(ContentFilterAction.MASK, True), (ContentFilterAction.BLOCK, False)]) -async def test_pre_call_hook_rejects_streaming_only_when_content_filter_step_masks( - proxy_logging, make_user_api_key_auth, monkeypatch, action, rejected +@pytest.mark.parametrize("action", [ContentFilterAction.MASK, ContentFilterAction.BLOCK]) +async def test_pre_call_hook_allows_streaming_when_content_filter_step_masks_or_blocks( + proxy_logging, make_user_api_key_auth, monkeypatch, action ): guardrail = ContentFilterGuardrail( guardrail_name="gr-post", @@ -1478,25 +1475,15 @@ async def test_pre_call_hook_rejects_streaming_only_when_content_filter_step_mas data = _post_call_pipeline_data(stream=True) user_api_key_dict = make_user_api_key_auth(request_route="/v1/chat/completions") - if not rejected: - out = await proxy_logging.pre_call_hook( - user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True - ) - assert out is not None and out.get("stream") is True - return + out = await proxy_logging.pre_call_hook( + user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True + ) - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( - user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True - ) - - assert info.value.status_code == 400 - assert info.value.detail["error"]["guardrails"] == ("gr-post",) - assert "a MASK action" in info.value.detail["error"]["message"] + assert out is not None and out.get("stream") is True @pytest.mark.asyncio -async def test_pre_call_hook_rejects_streaming_when_content_filter_category_masks( +async def test_pre_call_hook_allows_streaming_when_content_filter_category_masks( proxy_logging, make_user_api_key_auth, monkeypatch ): guardrail = ContentFilterGuardrail( @@ -1508,13 +1495,11 @@ async def test_pre_call_hook_rejects_streaming_when_content_filter_category_mask data = _post_call_pipeline_data(stream=True) user_api_key_dict = make_user_api_key_auth(request_route="/v1/chat/completions") - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( - user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True - ) + out = await proxy_logging.pre_call_hook( + user_api_key_dict=user_api_key_dict, data=data, call_type="completion", guardrails_only=True + ) - assert info.value.status_code == 400 - assert info.value.detail["error"]["guardrails"] == ("gr-post",) + assert out is not None and out.get("stream") is True @pytest.mark.asyncio @@ -1618,17 +1603,10 @@ def _echoed_tool_call_dicts(arguments: str) -> List[Dict[str, Any]]: @pytest.mark.asyncio @pytest.mark.parametrize("on_fail, on_error", [("block", None), ("next", "next")]) -@pytest.mark.parametrize( - "make_chunks, transform", - [ - (_stream_chunks, lambda inputs: {"texts": ["hello [MASKED]"]}), - (_tool_call_stream_chunks, lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "[MASKED]"}')}), - ], - ids=["texts", "tool_calls"], -) -async def test_streaming_iterator_hook_pipeline_withholds_runtime_rewrite( - proxy_logging, make_user_api_key_auth, monkeypatch, make_chunks, transform, on_fail, on_error +async def test_streaming_iterator_hook_pipeline_withholds_runtime_tool_call_rewrite( + proxy_logging, make_user_api_key_auth, monkeypatch, on_fail, on_error ): + transform = lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "[MASKED]"}')} # noqa: E731 monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) step = PipelineStep(guardrail="gr-post", on_pass="allow", on_fail=on_fail, on_error=on_error) @@ -1638,7 +1616,7 @@ async def test_streaming_iterator_hook_pipeline_withholds_runtime_rewrite( async def _drain() -> None: async for item in proxy_logging.async_post_call_streaming_iterator_hook( user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), - response=_async_chunk_iter(make_chunks()), + response=_async_chunk_iter(_tool_call_stream_chunks()), request_data=data, ): delivered.append(item) @@ -1655,6 +1633,81 @@ async def test_streaming_iterator_hook_pipeline_withholds_runtime_rewrite( assert "stream=false" in error["message"] +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_delivers_runtime_text_rewrite( + proxy_logging, make_user_api_key_auth, monkeypatch +): + transform = lambda inputs: {"texts": ["hello [MASKED]"]} # noqa: E731 + monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + chunks = _stream_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + assert [id(item) for item in delivered] == [id(chunk) for chunk in chunks] + assert delivered[0].choices[0].delta.content == "hello [MASKED]" + assert delivered[1].choices[0].delta.content in (None, "") + assert delivered[1].choices[0].finish_reason == "stop" + + +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_chains_text_rewrites_across_steps( + proxy_logging, make_user_api_key_auth, monkeypatch +): + second_step_saw: Dict[str, Any] = {} + + class FirstMask(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + return {**inputs, "texts": [text.replace("world", "[MASKED]") for text in inputs["texts"]]} + + class SecondMask(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + second_step_saw["texts"] = list(inputs["texts"]) + return {**inputs, "texts": [text.replace("hello", "[GREETING]") for text in inputs["texts"]]} + + monkeypatch.setattr( + litellm, + "callbacks", + [ + FirstMask(guardrail_name="gr-first", event_hook=GuardrailEventHooks.post_call, default_on=False), + SecondMask(guardrail_name="gr-second", event_hook=GuardrailEventHooks.post_call, default_on=False), + ], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + pipeline = GuardrailPipeline( + mode="post_call", + steps=[ + PipelineStep(guardrail="gr-first", on_pass="next", on_fail="block"), + PipelineStep(guardrail="gr-second", on_pass="allow", on_fail="block"), + ], + ) + data = _post_call_pipeline_data(stream=True) + data["metadata"]["_guardrail_pipelines"] = [("response-governance", pipeline)] + chunks = _stream_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + assert second_step_saw["texts"] == ["hello [MASKED]"] + assert delivered[0].choices[0].delta.content == "[GREETING] [MASKED]" + assert delivered[1].choices[0].delta.content in (None, "") + assert delivered[1].choices[0].finish_reason == "stop" + + @pytest.mark.asyncio @pytest.mark.parametrize( "make_chunks, transform", @@ -1761,6 +1814,79 @@ async def test_streaming_iterator_hook_pipeline_modify_response_emits_translated assert not any(item is chunk for item in delivered for chunk in chunks) +@pytest.mark.asyncio +async def test_streaming_iterator_hook_pipeline_delivers_text_rewrite_on_anthropic_sse( + proxy_logging, make_user_api_key_auth, monkeypatch +): + transform = lambda inputs: {"texts": ["hello [MASKED]"]} # noqa: E731 + monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + chunks = _anthropic_sse_chunks() + + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/messages"), + response=_async_chunk_iter(chunks), + request_data=data, + ) + ] + + raw = b"".join(delivered).decode() + assert "hello [MASKED]" in raw + assert "hello world" not in raw + assert raw.count("event: content_block_delta") == 1 + for expected_event in ("message_start", "content_block_start", "content_block_stop", "message_delta", "message_stop"): + assert f"event: {expected_event}" in raw + + +@pytest.mark.asyncio +async def test_pipeline_executor_withholds_text_rewrite_when_translation_lacks_write_back(monkeypatch): + from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation + from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor, UndeliverableStreamRewrite + + class NoWriteBackTranslation(BaseTranslation): + async def process_input_messages(self, data, guardrail_to_apply, litellm_logging_obj): + return data + + async def process_output_response(self, response, guardrail_to_apply, litellm_logging_obj, **kwargs): + return response + + async def process_output_streaming_response( + self, + responses_so_far, + guardrail_to_apply, + litellm_logging_obj, + user_api_key_dict=None, + request_data=None, + stream_transform_sink=None, + deliver_ended_stream_rewrites=False, + ): + assert deliver_ended_stream_rewrites is False + await guardrail_to_apply.apply_guardrail( + inputs={"texts": ["hello world"]}, + request_data=request_data or {}, + input_type="response", + ) + return responses_so_far + + transform = lambda inputs: {"texts": ["hello [MASKED]"]} # noqa: E731 + monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + + with pytest.raises(UndeliverableStreamRewrite): + await PipelineExecutor.execute_steps( + steps=[PipelineStep(guardrail="gr-post", on_pass="allow", on_fail="block")], + mode="post_call", + data={"metadata": {}}, + user_api_key_dict=UserAPIKeyAuth(api_key="sk-test"), + call_type="acompletion", + policy_name="response-governance", + streaming_chunks=_stream_chunks(), + endpoint_translation=NoWriteBackTranslation(), + ) + + @pytest.mark.asyncio async def test_streaming_iterator_hook_pipeline_gates_without_iterator_overrides( proxy_logging, make_user_api_key_auth, monkeypatch From 85fea1a6752ac5f0c4c0415c5d3da4f6bc542911 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 1 Sep 2026 17:15:11 -0700 Subject: [PATCH 019/223] fix(policy_engine): move mutable-ok suppressions onto the flagged lines --- litellm/llms/openai/chat/guardrail_translation/handler.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index fcb447d5277..02206a36b0e 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -989,12 +989,8 @@ class OpenAIChatCompletionsHandler(BaseTranslation): return await self._apply_guardrail_responses_to_output_streaming( responses=responses_so_far, - guardrailed_texts=[ - after for _choice_idx, after in changed - ], # mutable-ok: the callee's signature predates this change and takes lists - task_mappings=[ - (choice_idx, None) for choice_idx, _after in changed - ], # mutable-ok: the callee's signature predates this change and takes lists + guardrailed_texts=[after for _choice_idx, after in changed], # mutable-ok: callee takes lists + task_mappings=[(choice_idx, None) for choice_idx, _after in changed], # mutable-ok: callee takes lists ) async def _apply_guardrail_responses_to_output_streaming( From 4fbe4ce2e225940b009d390543bd76694eece343 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 1 Sep 2026 17:29:52 -0700 Subject: [PATCH 020/223] fix(guardrail_translation): deliver stream rewrites on incomplete and failed responses terminals --- .../guardrail_translation/handler.py | 60 ++++++++++++------- ...test_openai_responses_guardrail_handler.py | 56 +++++++++++++++++ 2 files changed, 93 insertions(+), 23 deletions(-) diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index ac894401628..3c902f1b829 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -86,6 +86,15 @@ class ResponsesStreamChunk(TypedDict, total=False): text: ReadOnly[str] +_TERMINAL_ENVELOPE_EVENT_TYPES: Final = frozenset( + { + ResponsesAPIStreamEvents.RESPONSE_COMPLETED.value, + ResponsesAPIStreamEvents.RESPONSE_FAILED.value, + ResponsesAPIStreamEvents.RESPONSE_INCOMPLETE.value, + } +) + + def _next_stream_sequence_number(responses_so_far: Sequence[Any] | None) -> int: sequence_numbers: Final = ( item.get("sequence_number") if isinstance(item, dict) else getattr(item, "sequence_number", None) @@ -507,14 +516,17 @@ class OpenAIResponsesHandler(BaseTranslation): chunk, apply the guardrail, then write the result back in-place so the caller sees the modified content (e.g. PII tokens replaced). - For ``response.completed`` events (the normal end-of-stream signal) we - use the same per-item extraction + task-mapping approach as - ``process_output_response`` so that unmasking / blocking works correctly - for every output item. With ``deliver_ended_stream_rewrites`` the earlier - text-carrying events (``response.output_text.delta`` / ``.done``, + For terminal envelope events (``response.completed``, and equally + ``response.incomplete`` / ``response.failed``, whose envelopes carry the + partial output) we use the same per-item extraction + task-mapping + approach as ``process_output_response`` so that unmasking / blocking + works correctly for every output item. With + ``deliver_ended_stream_rewrites`` the earlier text-carrying events + (``response.output_text.delta`` / ``.done``, ``response.content_part.done``, ``response.output_item.done``) are synced - to the rewritten completed response too, so a client reading deltas sees - the rewrite instead of the raw model output. + to the rewritten envelope too, so a client reading deltas sees the + rewrite instead of the raw model output; a rewrite observed where no + write-back is possible fails closed instead of releasing raw output. """ if not responses_so_far: return responses_so_far @@ -526,14 +538,16 @@ class OpenAIResponsesHandler(BaseTranslation): return responses_so_far # ------------------------------------------------------------------ # - # Case 1: response.completed — full response is available in the # - # final chunk; iterate output items, apply guardrail, write back. # + # Case 1: terminal envelope events (completed/incomplete/failed). # + # the accumulated response is available in the final chunk; iterate # + # output items, apply guardrail, write back. Falls through to the # + # string fallback when the envelope yields nothing to check. # # ------------------------------------------------------------------ # - if final_chunk.get("type") == "response.completed": + if final_chunk.get("type") in _TERMINAL_ENVELOPE_EVENT_TYPES: response_obj: Final[ResponseOutputEnvelope] = final_chunk.get("response") or {} - if not hasattr(response_obj, "get"): - return responses_so_far - outputs: Final[Sequence[object]] = response_obj.get("output") or [] + outputs: Final[Sequence[object]] = ( + (response_obj.get("output") or []) if hasattr(response_obj, "get") else [] + ) texts_to_check: Final[list[str]] = [] tool_calls_to_check: Final[list[ChatCompletionToolCallChunk]] = [] @@ -596,8 +610,7 @@ class OpenAIResponsesHandler(BaseTranslation): stream_events=responses_so_far[:-1], rewrites_by_position=rewrites_by_position, ) - - return responses_so_far + return responses_so_far # ------------------------------------------------------------------ # # Case 2: response.output_item.done — extract tool calls only. # @@ -623,7 +636,8 @@ class OpenAIResponsesHandler(BaseTranslation): # ------------------------------------------------------------------ # # Fallback: apply guardrail to the accumulated text string. # # No structured write-back is possible here; guardrails that only # - # need to block/flag (not rewrite) still work correctly. # + # need to block/flag (not rewrite) still work correctly, and a # + # rewrite a caller expects delivered fails closed instead. # # ------------------------------------------------------------------ # string_so_far: Final = self.get_streaming_string_so_far(responses_so_far) if string_so_far: @@ -633,12 +647,17 @@ class OpenAIResponsesHandler(BaseTranslation): ) if response_model: fallback_inputs["model"] = response_model - await guardrail_to_apply.apply_guardrail( + fallback_outputs: Final = await guardrail_to_apply.apply_guardrail( inputs=fallback_inputs, request_data=request_data if request_data is not None else {}, input_type="response", logging_obj=litellm_logging_obj, ) + fallback_texts: Final = fallback_outputs.get("texts") + if deliver_ended_stream_rewrites and fallback_texts and tuple(fallback_texts) != (string_so_far,): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_to_apply.guardrail_name or "unknown") return responses_so_far @staticmethod @@ -704,12 +723,7 @@ class OpenAIResponsesHandler(BaseTranslation): """ if not responses_so_far: return False - terminal_types: Final = { - ResponsesAPIStreamEvents.RESPONSE_COMPLETED.value, - ResponsesAPIStreamEvents.RESPONSE_FAILED.value, - ResponsesAPIStreamEvents.RESPONSE_INCOMPLETE.value, - } - return responses_so_far[-1].get("type") in terminal_types + return responses_so_far[-1].get("type") in _TERMINAL_ENVELOPE_EVENT_TYPES def build_stream_error_items( self, diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index 4f95e08cb71..dfd6352f9ec 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1171,6 +1171,62 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: assert events[4]["item"]["content"][0]["text"] == "hello [MASKED]" assert events[5]["response"]["output"][0]["content"][0]["text"] == "hello [MASKED]" + @pytest.mark.asyncio + @pytest.mark.parametrize("terminal_type", ["response.incomplete", "response.failed"]) + async def test_deliver_ended_stream_rewrites_syncs_non_completed_terminals(self, terminal_type): + handler = OpenAIResponsesHandler() + events = self._ended_stream_events() + events[-1]["type"] = terminal_type + events[-1]["response"]["status"] = terminal_type.split(".")[-1] + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is events + assert events[0]["delta"] == "hello [MASKED]" + assert events[1]["delta"] == "" + assert events[2]["text"] == "hello [MASKED]" + assert events[3]["part"]["text"] == "hello [MASKED]" + assert events[4]["item"]["content"][0]["text"] == "hello [MASKED]" + assert events[5]["response"]["output"][0]["content"][0]["text"] == "hello [MASKED]" + + @pytest.mark.asyncio + async def test_fallback_rewrite_with_delivery_expected_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIResponsesHandler() + events = [ + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "hello "}, + {"type": "response.output_text.done", "output_index": 0, "content_index": 0, "text": "hello world"}, + ] + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + @pytest.mark.asyncio + async def test_fallback_rewrite_without_delivery_expected_does_not_raise(self): + handler = OpenAIResponsesHandler() + events = [ + {"type": "response.output_text.done", "output_index": 0, "content_index": 0, "text": "hello world"}, + ] + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + ) + + assert result is events + @pytest.mark.asyncio async def test_ended_stream_rewrite_leaves_delta_events_untouched_by_default(self): handler = OpenAIResponsesHandler() From c9435b5ff3f72c6d9c5ebfa30175498bc5928daf Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:00:42 -0700 Subject: [PATCH 021/223] fix(guardrails): key stream rewrites by choice index and scan delta-only responses buffers Chat streaming write-backs now match chunks by the choice's index field instead of its list position, delivering rewrites to the right choice on n>1 streams; an ended-stream rewrite on a multi-choice buffer fails closed since stream_chunk_builder collapses the choices. The Responses fallback joins output_text.delta events when delivery is expected, so a delta-only buffer is guardrail-checked instead of released raw. --- .../chat/guardrail_translation/handler.py | 50 ++++++---- .../guardrail_translation/handler.py | 31 +++++-- .../test_openai_guardrail_handler.py | 93 +++++++++++++++++++ ...test_openai_responses_guardrail_handler.py | 71 ++++++++++++++ 4 files changed, 222 insertions(+), 23 deletions(-) diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index 02206a36b0e..182dec81937 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -620,6 +620,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): responses_so_far=responses_so_far, guardrailed_response=model_response, pre_guardrail_texts=pre_guardrail_texts, + guardrail_name=guardrail_to_apply.guardrail_name or "unknown", ) def build_stream_error_items( @@ -747,8 +748,8 @@ class OpenAIChatCompletionsHandler(BaseTranslation): """ combined_texts: Final[dict[tuple[int, int | None], str]] = {} - for response_idx, response in enumerate(responses_so_far): - for choice_idx, choice in enumerate(response.choices): + for response in responses_so_far: + for choice in response.choices: if isinstance(choice, litellm.StreamingChoices): content = choice.delta.content elif isinstance(choice, litellm.Choices): @@ -761,7 +762,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): if isinstance(content, str): # String content - accumulate for this choice - str_key: tuple[int, int | None] = (choice_idx, None) + str_key: tuple[int, int | None] = (choice.index, None) if str_key not in combined_texts: combined_texts[str_key] = "" combined_texts[str_key] += content @@ -772,7 +773,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): text_str = content_item.get("text") if text_str: list_key: tuple[int, int | None] = ( - choice_idx, + choice.index, content_idx, ) if list_key not in combined_texts: @@ -973,24 +974,38 @@ class OpenAIChatCompletionsHandler(BaseTranslation): responses_so_far: list["ModelResponseStream"], # mutable-ok: rewrites the caller's buffered chunks in place guardrailed_response: "ModelResponse", pre_guardrail_texts: tuple[str | None, ...], + guardrail_name: str, ) -> None: """Write ended-stream guardrail text rewrites back across the buffered - chunks: each rewritten choice's full text lands in its first + chunks: the full rewritten text lands in the choice's first content-carrying chunk and the rest are blanked, the same shape the in-flight write-back uses. Chunks carrying only finish_reason or usage - stay untouched.""" + stay untouched. A rewrite on a stream carrying more than one distinct + choice index fails closed.""" post_guardrail_texts: Final = self._string_choice_contents(guardrailed_response) changed: Final = tuple( - (choice_idx, after) - for choice_idx, (before, after) in enumerate(zip(pre_guardrail_texts, post_guardrail_texts)) + after + for before, after in zip(pre_guardrail_texts, post_guardrail_texts) if before is not None and after is not None and after != before ) if not changed: return + stream_choice_indices: Final = frozenset( + choice.index for response in responses_so_far for choice in response.choices + ) + if len(stream_choice_indices) != 1: + # stream_chunk_builder collapses every choice into one index-0 + # choice, so a rewrite of the rebuilt response cannot be attributed + # back to a single choice on an n>1 stream: withhold the stream + # rather than deliver the rewrite on the wrong choice + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_name) + target_choice_index: Final = next(iter(stream_choice_indices)) await self._apply_guardrail_responses_to_output_streaming( responses=responses_so_far, - guardrailed_texts=[after for _choice_idx, after in changed], # mutable-ok: callee takes lists - task_mappings=[(choice_idx, None) for choice_idx, _after in changed], # mutable-ok: callee takes lists + guardrailed_texts=list(changed), # mutable-ok: callee takes lists + task_mappings=[(target_choice_index, None) for _ in changed], # mutable-ok: callee takes lists ) async def _apply_guardrail_responses_to_output_streaming( @@ -1008,7 +1023,8 @@ class OpenAIChatCompletionsHandler(BaseTranslation): Args: responses: List of ModelResponseStream objects to modify guardrailed_texts: List of guardrailed text responses (combined from all chunks) - task_mappings: List of tuples (choice_idx, content_idx) + task_mappings: List of tuples (choice_idx, content_idx), where choice_idx + is the choice's ``index`` field, not its position in a chunk's list Override this method to customize how responses are applied to streaming responses. """ @@ -1024,9 +1040,11 @@ class OpenAIChatCompletionsHandler(BaseTranslation): # Key: (choice_idx, content_idx), Value: boolean (True if already set) already_set: Final[dict[tuple[int, int | None], bool]] = {} - # Iterate through all responses and update content - for response_idx, response in enumerate(responses): - for choice_idx_in_response, choice in enumerate(response.choices): + # Iterate through all responses and update content, matching each chunk's + # choice by its index field: on n>1 streams a chunk usually carries one + # choice at list position 0 whose index names the logical choice. + for response in responses: + for choice in response.choices: if isinstance(choice, litellm.StreamingChoices): content = choice.delta.content elif isinstance(choice, litellm.Choices): @@ -1039,7 +1057,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): if isinstance(content, str): # String content - str_key: tuple[int, int | None] = (choice_idx_in_response, None) + str_key: tuple[int, int | None] = (choice.index, None) if str_key in guardrail_map: if str_key not in already_set: # First chunk - set the complete guardrailed text @@ -1060,7 +1078,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): for content_idx, content_item in enumerate(content): if "text" in content_item: list_key: tuple[int, int | None] = ( - choice_idx_in_response, + choice.index, content_idx, ) if list_key in guardrail_map: diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 3c902f1b829..ebd1070a8e8 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -634,14 +634,20 @@ class OpenAIResponsesHandler(BaseTranslation): return responses_so_far # ------------------------------------------------------------------ # - # Fallback: apply guardrail to the accumulated text string. # - # No structured write-back is possible here; guardrails that only # - # need to block/flag (not rewrite) still work correctly, and a # - # rewrite a caller expects delivered fails closed instead. # + # Fallback: apply guardrail to the accumulated text string. When a # + # caller expects rewrites delivered and only output_text.delta events # + # carried the text (a stream cut off before any .done or terminal # + # envelope), the delta text is scanned instead so nothing escapes # + # unchecked. No structured write-back is possible here; guardrails # + # that only need to block/flag (not rewrite) still work correctly, # + # and a rewrite a caller expects delivered fails closed instead. # # ------------------------------------------------------------------ # string_so_far: Final = self.get_streaming_string_so_far(responses_so_far) - if string_so_far: - fallback_inputs: Final = GenericGuardrailAPIInputs(texts=[string_so_far]) + text_to_check: Final = string_so_far or ( + self._delta_text_so_far(responses_so_far) if deliver_ended_stream_rewrites else "" + ) + if text_to_check: + fallback_inputs: Final = GenericGuardrailAPIInputs(texts=[text_to_check]) response_model = ( final_chunk.get("response", {}).get("model") if isinstance(final_chunk.get("response"), dict) else None ) @@ -654,7 +660,7 @@ class OpenAIResponsesHandler(BaseTranslation): logging_obj=litellm_logging_obj, ) fallback_texts: Final = fallback_outputs.get("texts") - if deliver_ended_stream_rewrites and fallback_texts and tuple(fallback_texts) != (string_so_far,): + if deliver_ended_stream_rewrites and fallback_texts and tuple(fallback_texts) != (text_to_check,): from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite raise UndeliverableStreamRewrite(guardrail_to_apply.guardrail_name or "unknown") @@ -754,6 +760,17 @@ class OpenAIResponsesHandler(BaseTranslation): """ return "".join([response.get("text", "") for response in responses_so_far]) + @staticmethod + def _delta_text_so_far(responses_so_far: Sequence[ResponsesStreamChunk]) -> str: + """Accumulate the text carried by ``response.output_text.delta`` events, + for buffers where no ``.done`` event or terminal envelope repeats it.""" + deltas: Final = ( + response.get("delta") + for response in responses_so_far + if response.get("type") == ResponsesAPIStreamEvents.OUTPUT_TEXT_DELTA.value + ) + return "".join(delta for delta in deltas if isinstance(delta, str)) + def _has_text_content(self, response: "ResponsesAPIResponse") -> bool: """ Check if response has any text content to process. diff --git a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py index 26442a4a6ed..b177d4a73d4 100644 --- a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py @@ -1128,6 +1128,99 @@ class TestOpenAIChatCompletionsHandlerStreamingOutput: assert chunks[1].choices[0].delta.content == " world" assert chunks[1].choices[0].finish_reason == "stop" + @staticmethod + def _two_choice_stream_chunks() -> list: + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + + def chunk(index: int, content: str, finish_reason: Optional[str] = None) -> ModelResponseStream: + return ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[StreamingChoices(index=index, delta=Delta(content=content), finish_reason=finish_reason)], + ) + + return [ + chunk(0, "safe "), + chunk(1, "hello "), + chunk(0, "text", "stop"), + chunk(1, "world", "stop"), + ] + + @staticmethod + def _world_masking_guardrail() -> CustomGuardrail: + class MaskWorld(CustomGuardrail): + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + texts = inputs.get("texts", []) + return {**inputs, "texts": [t.replace("world", "[MASKED]") for t in texts]} + + return MaskWorld(guardrail_name="test-mask") + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrite_on_multi_choice_stream_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIChatCompletionsHandler() + chunks = self._two_choice_stream_chunks() + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._world_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + @pytest.mark.asyncio + async def test_deliver_ended_stream_clean_multi_choice_stream_released_untouched(self): + handler = OpenAIChatCompletionsHandler() + chunks = self._two_choice_stream_chunks() + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=MockPassThroughGuardrail(guardrail_name="test"), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert [c.choices[0].delta.content for c in chunks] == ["safe ", "hello ", "text", "world"] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrite_lands_on_nonzero_choice_index(self): + from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices + + handler = OpenAIChatCompletionsHandler() + + def chunk(content: str, finish_reason: Optional[str]) -> ModelResponseStream: + return ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[StreamingChoices(index=1, delta=Delta(content=content), finish_reason=finish_reason)], + ) + + chunks = [chunk("hello ", None), chunk("world", "stop")] + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._world_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert chunks[0].choices[0].delta.content == "hello [MASKED]" + assert chunks[1].choices[0].delta.content in (None, "") + class TestGetStructuredMessages: """Test the get_structured_messages method.""" diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index dfd6352f9ec..ae3700b1123 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1212,6 +1212,77 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: deliver_ended_stream_rewrites=True, ) + @staticmethod + def _recording_guardrail() -> "tuple[CustomGuardrail, List[List[str]]]": + seen: List[List[str]] = [] + + class Recorder(CustomGuardrail): + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + seen.append(list(inputs.get("texts", []))) + return inputs + + return Recorder(guardrail_name="recorder"), seen + + @pytest.mark.asyncio + async def test_fallback_delta_only_rewrite_with_delivery_expected_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIResponsesHandler() + events = [ + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "hello "}, + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "world"}, + ] + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + @pytest.mark.asyncio + async def test_fallback_scans_delta_text_when_delivery_expected(self): + handler = OpenAIResponsesHandler() + guardrail, seen = self._recording_guardrail() + events = [ + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "hello "}, + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "there"}, + ] + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is events + assert seen == [["hello there"]] + + @pytest.mark.asyncio + async def test_fallback_ignores_delta_text_without_delivery_expected(self): + handler = OpenAIResponsesHandler() + guardrail, seen = self._recording_guardrail() + events = [ + {"type": "response.output_text.delta", "output_index": 0, "content_index": 0, "delta": "hello world"}, + ] + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + ) + + assert result is events + assert seen == [] + @pytest.mark.asyncio async def test_fallback_rewrite_without_delivery_expected_does_not_raise(self): handler = OpenAIResponsesHandler() From c09db7c7a3d77d3ccd6a1a2163778b6f22f29a8f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 1 Sep 2026 22:01:04 -0700 Subject: [PATCH 022/223] Fail closed on rewrites for buffers that never reached their terminal event An Anthropic buffer without a stop_reason only ran the flat text scan, so a rewrite there was dropped while the executor trusted the translation to have delivered it. A Responses buffer ending at response.output_item.done returned after the tool-call scan without ever checking the text. Both now reach the flat scan and raise UndeliverableStreamRewrite when a caller expects the rewrite delivered, matching the existing Responses no-envelope fallback. --- .../chat/guardrail_translation/handler.py | 8 +++- .../guardrail_translation/handler.py | 7 ++- .../test_anthropic_guardrail_handler.py | 46 +++++++++++++++++++ ...test_openai_responses_guardrail_handler.py | 46 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index 091ad8ecba9..fc276dd7fe6 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -1020,7 +1020,8 @@ class AnthropicMessagesHandler(BaseTranslation): Get the string so far, check the apply guardrail to the string so far, and return the list of responses so far. With ``deliver_ended_stream_rewrites``, an ended stream whose guardrail rewrote the text gets the rewrite - written back across the buffered chunks (full rewritten text in the first ``text_delta``, the rest blanked). + written back across the buffered chunks (full rewritten text in the first ``text_delta``, the rest blanked); + a rewrite on a stream that never reported a ``stop_reason`` has no write-back and fails closed instead. """ from litellm.integrations.custom_guardrail import ModifyResponseException @@ -1098,6 +1099,11 @@ class AnthropicMessagesHandler(BaseTranslation): if e.original_response is None: e.original_response = self._build_streaming_usage_response(responses_so_far, request_data) raise + unended_texts: Final = _guardrailed_inputs.get("texts") + if deliver_ended_stream_rewrites and unended_texts and tuple(unended_texts) != (string_so_far,): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_to_apply.guardrail_name or "unknown") return responses_so_far def _prepare_request_data( diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 8b808348c79..724a0a1d2f0 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -638,7 +638,9 @@ class OpenAIResponsesHandler(BaseTranslation): return responses_so_far # ------------------------------------------------------------------ # - # Case 2: response.output_item.done — extract tool calls only. # + # Case 2: response.output_item.done — extract tool calls only, then # + # fall through to the text fallback when a caller expects rewrites # + # delivered, so a buffer truncated here still fails closed on text. # # ------------------------------------------------------------------ # if final_chunk.get("type") == "response.output_item.done": model_response_stream: Final = ( @@ -656,7 +658,8 @@ class OpenAIResponsesHandler(BaseTranslation): input_type="response", logging_obj=litellm_logging_obj, ) - return responses_so_far + if not deliver_ended_stream_rewrites: + return responses_so_far # ------------------------------------------------------------------ # # Fallback: apply guardrail to the accumulated text string. # diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index f60655cd7ec..274c351ebc7 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -328,6 +328,52 @@ class TestAnthropicMessagesHandlerStreamingOutputProcessing: assert chunks == original + @pytest.mark.asyncio + async def test_unended_stream_rewrite_with_delivery_expected_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = AnthropicMessagesHandler() + chunks = self._ended_sse_chunks()[:-2] + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=MagicMock(), + deliver_ended_stream_rewrites=True, + ) + + @pytest.mark.asyncio + async def test_unended_stream_without_rewrite_is_released_with_delivery_expected(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_sse_chunks()[:-2] + original = [bytes(chunk) for chunk in chunks] + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=MockPassThroughGuardrail(guardrail_name="test"), + litellm_logging_obj=MagicMock(), + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert chunks == original + + @pytest.mark.asyncio + async def test_unended_stream_rewrite_without_delivery_expected_does_not_raise(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_sse_chunks()[:-2] + original = [bytes(chunk) for chunk in chunks] + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=MagicMock(), + ) + + assert result is chunks + assert chunks == original + class TestAnthropicMessagesHandlerInputProcessing: """Test input processing preserves litellm_metadata for dynamic guardrails.""" diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index 155b9ef9810..bd07e924d12 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1248,6 +1248,52 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: deliver_ended_stream_rewrites=True, ) + @pytest.mark.asyncio + async def test_output_item_done_last_rewrite_with_delivery_expected_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIResponsesHandler() + events = self._ended_stream_events()[:-1] + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + @pytest.mark.asyncio + async def test_output_item_done_last_scans_text_with_delivery_expected(self): + handler = OpenAIResponsesHandler() + events = self._ended_stream_events()[:-1] + guardrail = MockRecordingGuardrail(guardrail_name="test") + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is events + assert [inputs.get("texts") for inputs in guardrail.seen_inputs] == [["hello world"]] + + @pytest.mark.asyncio + async def test_output_item_done_last_without_delivery_expected_skips_text(self): + handler = OpenAIResponsesHandler() + events = self._ended_stream_events()[:-1] + guardrail = MockRecordingGuardrail(guardrail_name="test") + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + ) + + assert result is events + assert guardrail.seen_inputs == [] + @pytest.mark.asyncio async def test_fallback_rewrite_without_delivery_expected_does_not_raise(self): handler = OpenAIResponsesHandler() From bede8b5ea46c24b20d138c79025dd67beee97763 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 02:49:49 -0700 Subject: [PATCH 023/223] fix(proxy): stop shipping the literal string "None" as error type and param The proxy's exception tails defaulted `type` and `param` to the four-character string "None", which is neither a known OpenAI error type nor the JSON null the nullable `param` field is typed as, so a client's error handler matched nothing and fell into its generic branch. Lifts the helpers PR #39521 added for the unified LLM endpoints into litellm/proxy/common_utils/openai_error_payload.py and calls them from the file, rerank, image, realtime, anthropic, and pass-through route families, plus the shared handle_exception_on_proxy handler that the management, batches, fine-tuning, credential, SCIM, guardrail, and customer routes funnel through. The remaining families (proxy_server, auth, health, spend tracking, and management endpoints) follow in separate PRs so each slice stays QA'able on a live proxy. --- .../proxy/anthropic_endpoints/endpoints.py | 11 +- litellm/proxy/common_request_processing.py | 78 ++++-------- .../common_utils/openai_error_payload.py | 48 +++++++ litellm/proxy/image_endpoints/endpoints.py | 17 ++- .../openai_files_endpoints/files_endpoints.py | 109 ++++++++-------- .../pass_through_endpoints.py | 23 ++-- litellm/proxy/realtime_endpoints/endpoints.py | 41 +++--- litellm/proxy/rerank_endpoints/endpoints.py | 17 ++- litellm/proxy/utils.py | 5 +- .../common_utils/test_openai_error_payload.py | 117 ++++++++++++++++++ .../test_files_endpoint.py | 10 +- .../proxy/utils/helpers/test_error_helpers.py | 2 +- 12 files changed, 320 insertions(+), 158 deletions(-) create mode 100644 litellm/proxy/common_utils/openai_error_payload.py create mode 100644 tests/test_litellm/proxy/common_utils/test_openai_error_payload.py diff --git a/litellm/proxy/anthropic_endpoints/endpoints.py b/litellm/proxy/anthropic_endpoints/endpoints.py index 7f0045c1d93..f26cb4d41f7 100644 --- a/litellm/proxy/anthropic_endpoints/endpoints.py +++ b/litellm/proxy/anthropic_endpoints/endpoints.py @@ -25,6 +25,11 @@ from litellm.proxy.common_request_processing import ( proxy_exception_from_http_exception, ) from litellm.proxy.common_utils.http_parsing_utils import _read_request_body +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.types.utils import TokenCountResponse router: Final = APIRouter() @@ -221,9 +226,9 @@ async def anthropic_response( error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), headers=headers, ) diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 6542842f5e4..99028c3645a 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -54,6 +54,12 @@ from litellm.proxy.common_utils.callback_utils import ( get_logging_caching_headers, get_remaining_tokens_and_requests_from_request_data, ) +from litellm.proxy.common_utils.openai_error_payload import ( + attribute_of, + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.proxy.common_utils.sse_keepalive import ( SSE_COMMENT_PING_BYTES, coerce_keepalive_interval, @@ -463,46 +469,6 @@ def _stream_usage_tracking_updates( } -def _getattr_object(value: object, name: str, default: object = None) -> object: - return getattr(value, name, default) - - -_OPENAI_ERROR_TYPE_BY_STATUS: Final[Mapping[int, str]] = MappingProxyType( - { - status.HTTP_401_UNAUTHORIZED: "authentication_error", - status.HTTP_403_FORBIDDEN: "permission_error", - status.HTTP_429_TOO_MANY_REQUESTS: "rate_limit_error", - } -) - - -def _error_status_code(exc: object, default: int) -> int: - """The HTTP status an exception carries, or ``default`` when it carries none.""" - carried: Final = _getattr_object(exc, "status_code") - return carried if isinstance(carried, int) and not isinstance(carried, bool) else default - - -def _openai_error_type(exc: object, status_code: int) -> str: - """OpenAI types ``error.type`` as a required string, so an exception carrying none - falls back to the type its status code stands for.""" - carried: Final = _getattr_object(exc, "type") - if isinstance(carried, str): - return carried - mapped: Final = _OPENAI_ERROR_TYPE_BY_STATUS.get(status_code) - if mapped is not None: - return mapped - if status_code < status.HTTP_500_INTERNAL_SERVER_ERROR: - return "invalid_request_error" - return "internal_server_error" - - -def _openai_error_param(exc: object) -> str | None: - """OpenAI types ``error.param`` as nullable, so an exception carrying none - serializes as JSON ``null``.""" - carried: Final = _getattr_object(exc, "param") - return carried if isinstance(carried, str) else None - - class _UpstreamHttpResponse(Protocol): @property def status_code(self) -> int: ... @@ -572,15 +538,15 @@ def serialize_http_exception_detail( def proxy_exception_from_http_exception(exc: HTTPException, headers: dict[str, str]) -> ProxyException: - raw_detail: Final = _getattr_object(exc, "detail", str(exc)) + raw_detail: Final = attribute_of(exc, "detail", str(exc)) message, structured_fields = serialize_http_exception_detail(raw_detail) existing_fields: Final = getattr(exc, "provider_specific_fields", None) or {} merged_fields: Final = {**existing_fields, **structured_fields} if structured_fields else (existing_fields or None) - error_status: Final = _error_status_code(exc, status.HTTP_400_BAD_REQUEST) + error_status: Final = error_status_code(exc, status.HTTP_400_BAD_REQUEST) return ProxyException( message=message, - type=_openai_error_type(exc, error_status), - param=_openai_error_param(exc), + type=openai_error_type(exc, error_status), + param=openai_error_param(exc), code=error_status, provider_specific_fields=merged_fields, headers=headers, @@ -864,8 +830,8 @@ def sse_error_payload(exc: BaseException) -> tuple[int, Mapping[str, object]]: are byte-identical. """ # Preserve status code from HTTPException (e.g. guardrail blocks) - error_status: Final = _error_status_code(exc, status.HTTP_500_INTERNAL_SERVER_ERROR) - raw_detail: Final = _getattr_object(exc, "detail", "Error processing stream start") + error_status: Final = error_status_code(exc, status.HTTP_500_INTERNAL_SERVER_ERROR) + raw_detail: Final = attribute_of(exc, "detail", "Error processing stream start") message, structured_fields = serialize_http_exception_detail(raw_detail) existing_fields: Final = getattr(exc, "provider_specific_fields", None) or {} @@ -873,8 +839,8 @@ def sse_error_payload(exc: BaseException) -> tuple[int, Mapping[str, object]]: error_obj: Final = { "message": message, - "type": _openai_error_type(exc, error_status), - "param": _openai_error_param(exc), + "type": openai_error_type(exc, error_status), + "param": openai_error_param(exc), "code": str(error_status), } if not merged_fields: @@ -2755,10 +2721,10 @@ class ProxyBaseLLMRequestProcessing: ``ResponsesAPIResponse`` directly. Handle both shapes so the container-ownership recording path can walk ``.output`` either way. """ - completed: Final = _getattr_object(stream_response, "completed_response") + completed: Final = attribute_of(stream_response, "completed_response") if completed is None: return None - response_obj: Final = _getattr_object(completed, "response") + response_obj: Final = attribute_of(completed, "response") if response_obj is not None: return response_obj return completed @@ -3380,7 +3346,7 @@ class ProxyBaseLLMRequestProcessing: headers = getattr(e, "headers", None) or {} if not headers: # Try to get headers from e.response.headers (httpx.Response) - _response: Final = _getattr_object(e, "response") + _response: Final = attribute_of(e, "response") if _response is not None: _response_headers: Final = getattr(_response, "headers", None) if _response_headers: @@ -3451,8 +3417,8 @@ class ProxyBaseLLMRequestProcessing: _code = status.HTTP_500_INTERNAL_SERVER_ERROR raise ProxyException( message=redact_internal_details_from_client_message(getattr(e, "message", error_msg)), - type=_openai_error_type(e, _code), - param=_openai_error_param(e), + type=openai_error_type(e, _code), + param=openai_error_param(e), openai_code=getattr(e, "code", None), code=_code, provider_specific_fields=getattr(e, "provider_specific_fields", None), @@ -3662,11 +3628,11 @@ class ProxyBaseLLMRequestProcessing: if isinstance(e, HTTPException): raise e - stream_error_status: Final = _error_status_code(e, status.HTTP_500_INTERNAL_SERVER_ERROR) + stream_error_status: Final = error_status_code(e, status.HTTP_500_INTERNAL_SERVER_ERROR) proxy_exception: Final = ProxyException( message=redact_internal_details_from_client_message(getattr(e, "message", str(e))), - type=_openai_error_type(e, stream_error_status), - param=_openai_error_param(e), + type=openai_error_type(e, stream_error_status), + param=openai_error_param(e), code=stream_error_status, ) stream_completed = True diff --git a/litellm/proxy/common_utils/openai_error_payload.py b/litellm/proxy/common_utils/openai_error_payload.py new file mode 100644 index 00000000000..180ec152094 --- /dev/null +++ b/litellm/proxy/common_utils/openai_error_payload.py @@ -0,0 +1,48 @@ +"""Shapes the ``error`` object the proxy answers with so it matches OpenAI's contract: +``type`` is a required string and ``param`` is nullable, neither of which the literal +string ``"None"`` satisfies.""" + +from collections.abc import Mapping +from types import MappingProxyType +from typing import Final + +from fastapi import status + +_OPENAI_ERROR_TYPE_BY_STATUS: Final[Mapping[int, str]] = MappingProxyType( + { + status.HTTP_401_UNAUTHORIZED: "authentication_error", + status.HTTP_403_FORBIDDEN: "permission_error", + status.HTTP_429_TOO_MANY_REQUESTS: "rate_limit_error", + } +) + + +def attribute_of(value: object, name: str, default: object = None) -> object: + return getattr(value, name, default) + + +def error_status_code(exc: object, default: int) -> int: + """The HTTP status an exception carries, or ``default`` when it carries none.""" + carried: Final = attribute_of(exc, "status_code") + return carried if isinstance(carried, int) and not isinstance(carried, bool) else default + + +def openai_error_type(exc: object, status_code: int) -> str: + """OpenAI types ``error.type`` as a required string, so an exception carrying none + falls back to the type its status code stands for.""" + carried: Final = attribute_of(exc, "type") + if isinstance(carried, str): + return carried + mapped: Final = _OPENAI_ERROR_TYPE_BY_STATUS.get(status_code) + if mapped is not None: + return mapped + if status_code < status.HTTP_500_INTERNAL_SERVER_ERROR: + return "invalid_request_error" + return "internal_server_error" + + +def openai_error_param(exc: object) -> str | None: + """OpenAI types ``error.param`` as nullable, so an exception carrying none + serializes as JSON ``null``.""" + carried: Final = attribute_of(exc, "param") + return carried if isinstance(carried, str) else None diff --git a/litellm/proxy/image_endpoints/endpoints.py b/litellm/proxy/image_endpoints/endpoints.py index 83caa92ede5..b83f7e5cd60 100644 --- a/litellm/proxy/image_endpoints/endpoints.py +++ b/litellm/proxy/image_endpoints/endpoints.py @@ -16,6 +16,11 @@ from litellm.litellm_core_utils.prompt_templates.common_utils import ( from litellm.proxy._types import * from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth, user_api_key_auth from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.proxy.route_llm_request import route_request from litellm.types.llms.openai import ChatCompletionUserMessage @@ -193,18 +198,18 @@ async def image_generation( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), openai_code=getattr(e, "code", None), - code=getattr(e, "status_code", 500), + code=error_status_code(e, 500), ) diff --git a/litellm/proxy/openai_files_endpoints/files_endpoints.py b/litellm/proxy/openai_files_endpoints/files_endpoints.py index bf07f4748ef..c315d30b8f3 100644 --- a/litellm/proxy/openai_files_endpoints/files_endpoints.py +++ b/litellm/proxy/openai_files_endpoints/files_endpoints.py @@ -45,6 +45,11 @@ from litellm.proxy.common_utils.openai_endpoint_utils import ( get_custom_llm_provider_from_request_headers, get_custom_llm_provider_from_request_query, ) +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.proxy.openai_files_endpoints.batch_file_validation import ( check_batch_file_upload, raise_batch_file_validation_failure, @@ -296,22 +301,22 @@ async def route_create_file( if managed_files_obj is None: raise ProxyException( message="Managed files hook not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if llm_router is None: raise ProxyException( message="LLM Router not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if not isinstance(managed_files_obj, BaseFileEndpoints): raise ProxyException( message="Managed files hook is not a BaseFileEndpoints", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) # Managed files internally calls llm_router.acreate_file() which includes loadbalancing @@ -713,17 +718,17 @@ async def create_file( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) finally: for spool in spools: @@ -812,22 +817,22 @@ async def get_file_content( if managed_files_obj is None: raise ProxyException( message="Managed files hook not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if llm_router is None: raise ProxyException( message="LLM Router not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if not isinstance(managed_files_obj, BaseFileEndpoints): raise ProxyException( message="Managed files hook is not a BaseFileEndpoints", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) @@ -1021,17 +1026,17 @@ async def get_file_content( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) @@ -1151,15 +1156,15 @@ async def get_file( if managed_files_obj is None: raise ProxyException( message="Managed files hook not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if not isinstance(managed_files_obj, BaseFileEndpoints): raise ProxyException( message="Managed files hook is not a BaseFileEndpoints", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) response = await managed_files_obj.afile_retrieve( @@ -1215,17 +1220,17 @@ async def get_file( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) @@ -1355,22 +1360,22 @@ async def delete_file( if managed_files_obj is None: raise ProxyException( message="Managed files hook not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if llm_router is None: raise ProxyException( message="LLM Router not found", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) if not isinstance(managed_files_obj, BaseFileEndpoints): raise ProxyException( message="Managed files hook is not a BaseFileEndpoints", - type="None", - param="None", + type=ProxyErrorTypes.internal_server_error.value, + param=None, code=500, ) @@ -1427,17 +1432,17 @@ async def delete_file( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) @@ -1629,15 +1634,15 @@ async def list_files( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 79d5d0a016f..64ce461990a 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -77,6 +77,11 @@ from litellm.proxy.common_utils.http_parsing_utils import ( _read_request_body, _safe_get_request_headers, ) +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.proxy.common_utils.sse_keepalive import ( wrap_passthrough_sse_bytes_with_keepalive_pings, ) @@ -310,9 +315,9 @@ async def chat_completion_pass_through_endpoint( error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) @@ -1677,18 +1682,18 @@ async def pass_through_request( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(getattr(e, "detail", str(e)))), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), headers=custom_headers, ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), headers=custom_headers, ) diff --git a/litellm/proxy/realtime_endpoints/endpoints.py b/litellm/proxy/realtime_endpoints/endpoints.py index 7f9cd251a8a..9996f60098d 100644 --- a/litellm/proxy/realtime_endpoints/endpoints.py +++ b/litellm/proxy/realtime_endpoints/endpoints.py @@ -17,6 +17,11 @@ from litellm.proxy.common_utils.encrypt_decrypt_utils import ( encrypt_value_helper, ) from litellm.proxy.common_utils.http_parsing_utils import _read_request_body +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) from litellm.types.realtime import ( RealtimeClientSecretRequest, RealtimeClientSecretResponse, @@ -301,15 +306,15 @@ async def create_realtime_client_secret( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", http_status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, http_status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, http_status.HTTP_400_BAD_REQUEST), ) raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) if upstream_resp.status_code != 200: @@ -492,15 +497,15 @@ async def proxy_realtime_calls( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", http_status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, http_status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, http_status.HTTP_400_BAD_REQUEST), ) raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) return Response( @@ -605,15 +610,15 @@ async def create_realtime_transcription_session( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "detail", getattr(e, "message", str(e))), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", http_status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, http_status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, http_status.HTTP_400_BAD_REQUEST), ) raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) if upstream_resp.status_code != 200: diff --git a/litellm/proxy/rerank_endpoints/endpoints.py b/litellm/proxy/rerank_endpoints/endpoints.py index dd5803796b7..16cd7368e4a 100644 --- a/litellm/proxy/rerank_endpoints/endpoints.py +++ b/litellm/proxy/rerank_endpoints/endpoints.py @@ -11,6 +11,11 @@ from litellm._logging import verbose_proxy_logger from litellm.proxy._types import * from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) router: Final = APIRouter() @@ -112,15 +117,15 @@ async def rerank( if isinstance(e, HTTPException): raise ProxyException( message=getattr(e, "message", str(e)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), + type=openai_error_type(e, error_status_code(e, status.HTTP_400_BAD_REQUEST)), + param=openai_error_param(e), + code=error_status_code(e, status.HTTP_400_BAD_REQUEST), ) else: error_msg: Final = f"{e}" raise ProxyException( message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), + type=openai_error_type(e, error_status_code(e, 500)), + param=openai_error_param(e), + code=error_status_code(e, 500), ) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index cab2bd6d9db..5c0fbd64744 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -37,6 +37,7 @@ from litellm.proxy._types import ( SpendLogsMetadata, SpendLogsPayload, ) +from litellm.proxy.common_utils.openai_error_payload import openai_error_param from litellm.proxy.spend_tracking.spend_log_error_logger import spend_log_error from litellm.types.guardrails import GuardrailEventHooks from litellm.types.proxy.model_listing import ModelInfoResponse @@ -7098,7 +7099,7 @@ def handle_exception_on_proxy(e: Exception) -> ProxyException: return ProxyException( message=getattr(e, "detail", f"error({e})"), type=ProxyErrorTypes.internal_server_error, - param=getattr(e, "param", "None"), + param=openai_error_param(e), code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR), ) elif isinstance(e, ProxyException): @@ -7107,7 +7108,7 @@ def handle_exception_on_proxy(e: Exception) -> ProxyException: return ProxyException( message=str(e), type=ProxyErrorTypes.internal_server_error, - param=getattr(e, "param", "None"), + param=openai_error_param(e), code=_status_code, ) diff --git a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py new file mode 100644 index 00000000000..c165d7ffdb1 --- /dev/null +++ b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py @@ -0,0 +1,117 @@ +import json + +import pytest +from fastapi import HTTPException + +from litellm.proxy._types import ProxyErrorTypes, ProxyException +from litellm.proxy.common_utils.openai_error_payload import ( + error_status_code, + openai_error_param, + openai_error_type, +) + + +@pytest.mark.parametrize( + "status_code, expected_type", + [ + (400, "invalid_request_error"), + (401, "authentication_error"), + (403, "permission_error"), + (404, "invalid_request_error"), + (422, "invalid_request_error"), + (429, "rate_limit_error"), + (499, "invalid_request_error"), + (500, "internal_server_error"), + (502, "internal_server_error"), + (503, "internal_server_error"), + ], +) +def test_status_code_decides_the_type_when_the_exception_carries_none(status_code, expected_type): + """A route that raises a bare HTTPException carries no error type, so the status it + answered with is the only thing left to name the OpenAI type from.""" + assert openai_error_type(HTTPException(status_code=status_code, detail="boom"), status_code) == expected_type + + +def test_a_carried_type_wins_over_the_one_the_status_would_imply(): + """A ProxyException raised mid-request already names its own type, and relabelling a + 402 budget_exceeded as the status map's guess would lose what the client branches on.""" + carried = ProxyException( + message="Budget has been exceeded", + type=ProxyErrorTypes.budget_exceeded.value, + param=None, + code=400, + ) + + assert openai_error_type(carried, 400) == ProxyErrorTypes.budget_exceeded.value + + +@pytest.mark.parametrize("carried_type", [None, 400, {"type": "invalid_request_error"}, ["invalid_request_error"]]) +def test_a_non_string_carried_type_falls_back_to_the_status(carried_type): + """OpenAI types error.type as a string, so anything else on the exception is not one and + must not reach the wire the way the literal "None" used to.""" + + class _Carrier(Exception): + type = carried_type + + assert openai_error_type(_Carrier("boom"), 401) == "authentication_error" + + +def test_the_type_is_never_the_string_none_after_a_json_round_trip(): + """The bug this module exists for: json.dumps of a "None" default is indistinguishable + from a real type to a client's error handler.""" + payload = json.loads( + json.dumps( + { + "type": openai_error_type(HTTPException(status_code=400, detail="boom"), 400), + "param": openai_error_param(HTTPException(status_code=400, detail="boom")), + } + ) + ) + + assert payload == {"type": "invalid_request_error", "param": None} + + +def test_a_carried_param_names_the_offending_field(): + carried = ProxyException(message="Invalid purpose", type="invalid_request_error", param="purpose", code=400) + + assert openai_error_param(carried) == "purpose" + + +@pytest.mark.parametrize("exc", [HTTPException(status_code=400, detail="boom"), ValueError("boom"), None]) +def test_param_is_json_null_when_the_exception_names_no_field(exc): + assert openai_error_param(exc) is None + + +def test_a_non_string_carried_param_is_json_null(): + class _Carrier(Exception): + param = 42 + + assert openai_error_param(_Carrier("boom")) is None + + +def test_a_carried_status_code_wins_over_the_default(): + assert error_status_code(HTTPException(status_code=429, detail="slow down"), 400) == 429 + + +@pytest.mark.parametrize("default", [400, 500]) +def test_the_default_status_stands_when_the_exception_carries_none(default): + assert error_status_code(ValueError("boom"), default) == default + + +@pytest.mark.parametrize("carried_status", [True, False, "429", None, 429.0]) +def test_a_non_int_carried_status_falls_back_to_the_default(carried_status): + """True is an int in Python but not an HTTP status, and a stringified one would break + every caller that compares the code numerically.""" + + class _Carrier(Exception): + status_code = carried_status + + assert error_status_code(_Carrier("boom"), 500) == 500 + + +def test_a_status_carried_by_an_exception_drives_the_type_it_reports(): + """The two helpers compose at every call site: the status the exception carries is what + names its type, not the default the route would have used.""" + exc = HTTPException(status_code=403, detail="blocked by policy") + + assert openai_error_type(exc, error_status_code(exc, 400)) == "permission_error" diff --git a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py index bca97915347..123d54789bf 100644 --- a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py +++ b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py @@ -2920,9 +2920,9 @@ def test_unscoped_list_files_accepts_every_documented_purpose( def test_list_files_reports_a_bad_target_model_names_as_a_400( mocker: MockerFixture, monkeypatch, llm_router: Router ): - """The exception tail reports an HTTPException with its own status and error - type rather than relabelling it, so a client that branches on either keeps - reading the same thing off a bad request.""" + """The exception tail answers with the OpenAI error object a client can branch on: + the type its 400 status stands for, and a JSON null param rather than the literal + string "None" no OpenAI SDK has a case for.""" _setup_unscoped_list_files_route(mocker, monkeypatch, llm_router, _permissive_afile_list) response = _get_list_files("/v1/files?target_model_names=gpt-3.5-turbo,gpt-4o") @@ -2931,8 +2931,8 @@ def test_list_files_reports_a_bad_target_model_names_as_a_400( assert response.json() == { "error": { "message": "target_model_names on list files must be a list of one model name. Example: ['gpt-4o']", - "type": "None", - "param": "None", + "type": "invalid_request_error", + "param": None, "code": "400", } } diff --git a/tests/test_litellm/proxy/utils/helpers/test_error_helpers.py b/tests/test_litellm/proxy/utils/helpers/test_error_helpers.py index e73e3c151e0..dc30798df55 100644 --- a/tests/test_litellm/proxy/utils/helpers/test_error_helpers.py +++ b/tests/test_litellm/proxy/utils/helpers/test_error_helpers.py @@ -135,7 +135,7 @@ def test_handle_exception_on_proxy_happy_path_generic_exception_defaults_to_500( "message": "kaboom", "type": ProxyErrorTypes.internal_server_error.value, "code": "500", - "param": "None", + "param": None, } From 7a5b8bce7e42157cbfcbed306e2ea33672c7e5ea Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 03:40:58 -0700 Subject: [PATCH 024/223] test(proxy): type the parametrized inputs of the error payload helper tests --- .../proxy/common_utils/test_openai_error_payload.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py index c165d7ffdb1..3b39ea706fe 100644 --- a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py +++ b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py @@ -26,7 +26,7 @@ from litellm.proxy.common_utils.openai_error_payload import ( (503, "internal_server_error"), ], ) -def test_status_code_decides_the_type_when_the_exception_carries_none(status_code, expected_type): +def test_status_code_decides_the_type_when_the_exception_carries_none(status_code: int, expected_type: str): """A route that raises a bare HTTPException carries no error type, so the status it answered with is the only thing left to name the OpenAI type from.""" assert openai_error_type(HTTPException(status_code=status_code, detail="boom"), status_code) == expected_type @@ -46,7 +46,7 @@ def test_a_carried_type_wins_over_the_one_the_status_would_imply(): @pytest.mark.parametrize("carried_type", [None, 400, {"type": "invalid_request_error"}, ["invalid_request_error"]]) -def test_a_non_string_carried_type_falls_back_to_the_status(carried_type): +def test_a_non_string_carried_type_falls_back_to_the_status(carried_type: object): """OpenAI types error.type as a string, so anything else on the exception is not one and must not reach the wire the way the literal "None" used to.""" @@ -78,7 +78,7 @@ def test_a_carried_param_names_the_offending_field(): @pytest.mark.parametrize("exc", [HTTPException(status_code=400, detail="boom"), ValueError("boom"), None]) -def test_param_is_json_null_when_the_exception_names_no_field(exc): +def test_param_is_json_null_when_the_exception_names_no_field(exc: Exception | None): assert openai_error_param(exc) is None @@ -94,12 +94,12 @@ def test_a_carried_status_code_wins_over_the_default(): @pytest.mark.parametrize("default", [400, 500]) -def test_the_default_status_stands_when_the_exception_carries_none(default): +def test_the_default_status_stands_when_the_exception_carries_none(default: int): assert error_status_code(ValueError("boom"), default) == default @pytest.mark.parametrize("carried_status", [True, False, "429", None, 429.0]) -def test_a_non_int_carried_status_falls_back_to_the_default(carried_status): +def test_a_non_int_carried_status_falls_back_to_the_default(carried_status: object): """True is an int in Python but not an HTTP status, and a stringified one would break every caller that compares the code numerically.""" From cedf35992bdec398e7623d28e45b5218e4eff9bf Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:02:55 -0700 Subject: [PATCH 025/223] fix(proxy): give each spend-log queue monitor its own flush event `PrismaClient.spend_log_flush_requested` was an `asyncio.Event` built at import time, so it bound to whichever event loop first awaited it and every later loop got `RuntimeError: ... is bound to a different event loop` out of `_wait_for_spend_log_flush_request`. The queue monitor's blanket `except Exception` swallowed that into its error logger, so the flush silently never happened and the row sat in the worker's queue until the next poll. The monitor now creates its own Event inside the loop that awaits it and hands it to the client, and `request_spend_log_flush` signals through the client instead of the class. A request that arrives before the monitor is running is dropped and loses nothing, because the monitor reads the queue on its first pass before it ever waits. In CI this showed up as the proxy-endpoints shard flaking on test_monitor_spend_logs_queue_flushes_as_soon_as_one_is_requested whenever --dist=loadscope put the health-endpoint tests, which boot a proxy TestClient and start a monitor, on the same worker ahead of the spend-log tests. --- litellm/proxy/db/db_spend_update_writer.py | 2 +- litellm/proxy/utils.py | 22 ++++--- .../proxy/db/test_db_spend_update_writer.py | 7 +- .../prisma_and_spend/test_spend_functions.py | 66 +++++++++++++++++-- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/litellm/proxy/db/db_spend_update_writer.py b/litellm/proxy/db/db_spend_update_writer.py index e6880d521f1..18058f31385 100644 --- a/litellm/proxy/db/db_spend_update_writer.py +++ b/litellm/proxy/db/db_spend_update_writer.py @@ -940,7 +940,7 @@ class DBSpendUpdateWriter: await enqueue_spend_logs(prisma_client, (payload,)) if payload.get("call_type") in RESPONSES_SESSION_CALL_TYPES: - request_spend_log_flush() + request_spend_log_flush(prisma_client) else: verbose_proxy_logger.debug("prisma_client is None. Skipping writing spend logs to db.") diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index cab2bd6d9db..aa3076dbd19 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -3519,7 +3519,7 @@ class _StaleReadEngine: class PrismaClient: spend_log_transactions: list = [] _spend_log_transactions_lock = asyncio.Lock() - spend_log_flush_requested: ClassVar[asyncio.Event] = asyncio.Event() + spend_log_flush_requested: "asyncio.Event | None" = None spend_log_queue_bytes: ClassVar[int] = 0 spend_logs_queue_monitor_task: "asyncio.Task[None] | None" = None tool_usage_transactions: list["ToolUsageTransaction"] = [] @@ -6245,23 +6245,27 @@ async def enqueue_spend_logs( ) -def request_spend_log_flush() -> None: - """Wake the queue monitor now rather than leaving the rows for its next poll. +def request_spend_log_flush(prisma_client: PrismaClient) -> None: + """Wake this client's queue monitor now rather than leaving the rows for its next poll. The Responses API hands the client an id it can chain from straight away, and that lookup reads the DB, so the row cannot sit in this worker's queue for a poll interval. Repeated requests coalesce into the monitor's next pass, so the batching holds. + A request made before the monitor is running is dropped, and loses nothing: the + monitor reads the queue on its first pass, before it ever waits on a request. """ - PrismaClient.spend_log_flush_requested.set() + flush_requested: Final = prisma_client.spend_log_flush_requested + if flush_requested is not None: + flush_requested.set() -async def _wait_for_spend_log_flush_request(interval: float) -> bool: +async def _wait_for_spend_log_flush_request(flush_requested: asyncio.Event, interval: float) -> bool: """Wait out ``interval``, returning early and True when a flush was requested.""" try: - await asyncio.wait_for(PrismaClient.spend_log_flush_requested.wait(), timeout=interval) + await asyncio.wait_for(flush_requested.wait(), timeout=interval) except asyncio.TimeoutError: return False - PrismaClient.spend_log_flush_requested.clear() + flush_requested.clear() return True @@ -6681,6 +6685,8 @@ async def _monitor_spend_logs_queue( max_backoff: Final = 30.0 # Maximum backoff interval in seconds backoff_multiplier: Final = 1.5 # Exponential backoff multiplier current_interval = base_interval + flush_requested: Final = asyncio.Event() + prisma_client.spend_log_flush_requested = flush_requested # rebind-ok: the client owns its monitor's flush signal verbose_proxy_logger.info( "Starting spend logs queue monitor (threshold: %s, poll_interval: %ss)", threshold, base_interval @@ -6719,7 +6725,7 @@ async def _monitor_spend_logs_queue( # Exponential backoff when no logs to process current_interval = min(current_interval * backoff_multiplier, max_backoff) - if await _wait_for_spend_log_flush_request(current_interval): + if await _wait_for_spend_log_flush_request(flush_requested, current_interval): current_interval = base_interval except Exception as e: spend_log_error("Error in spend logs queue monitor: %s", str(e), exc=e) diff --git a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py index 11ef911de3e..104dcf55e66 100644 --- a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py +++ b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py @@ -2941,11 +2941,9 @@ async def test_insert_spend_log_asks_for_an_immediate_flush_on_responses_calls(c A `previous_response_id` chained straight off the previous turn reads the DB, so a Responses row cannot sit in this worker's queue until the monitor's next poll. """ - from litellm.proxy.utils import PrismaClient - db_writer = DBSpendUpdateWriter() prisma = _tool_usage_prisma() - PrismaClient.spend_log_flush_requested.clear() + prisma.spend_log_flush_requested = asyncio.Event() await db_writer._insert_spend_log_to_db( payload={"request_id": "req-1", "call_type": call_type}, @@ -2953,8 +2951,7 @@ async def test_insert_spend_log_asks_for_an_immediate_flush_on_responses_calls(c ) assert prisma.spend_log_transactions == [{"request_id": "req-1", "call_type": call_type}] - assert PrismaClient.spend_log_flush_requested.is_set() is expects_flush - PrismaClient.spend_log_flush_requested.clear() + assert prisma.spend_log_flush_requested.is_set() is expects_flush @pytest.mark.asyncio diff --git a/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py b/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py index a1eb88a7834..ed854d2c95c 100644 --- a/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py +++ b/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py @@ -538,10 +538,9 @@ async def test_monitor_spend_logs_queue_flushes_as_soon_as_one_is_requested( """ import litellm.constants as constants_mod import litellm.proxy.utils as utils_mod - from litellm.proxy.utils import PrismaClient, request_spend_log_flush + from litellm.proxy.utils import request_spend_log_flush monkeypatch.setattr(constants_mod, "SPEND_LOG_QUEUE_POLL_INTERVAL", 30.0, raising=False) - PrismaClient.spend_log_flush_requested.clear() mock_prisma_client.spend_log_transactions = [] mock_prisma_client.tool_usage_transactions = [] @@ -562,16 +561,75 @@ async def test_monitor_spend_logs_queue_flushes_as_soon_as_one_is_requested( try: await asyncio.sleep(0.05) assert not flushed.is_set() + assert isinstance(mock_prisma_client.spend_log_flush_requested, asyncio.Event) mock_prisma_client.spend_log_transactions.append(make_spend_log_row(request_id="r1")) - request_spend_log_flush() + request_spend_log_flush(mock_prisma_client) await asyncio.wait_for(flushed.wait(), timeout=5.0) finally: monitor.cancel() with suppress(asyncio.CancelledError): await monitor - PrismaClient.spend_log_flush_requested.clear() + + +def test_monitor_spend_logs_queue_flush_survives_an_earlier_event_loop( + mock_prisma_client: Any, + make_spend_log_row: Any, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A second monitor, started in a fresh event loop, is still woken by a flush request, + so a worker whose first loop is gone keeps flushing Responses rows instead of stalling. + """ + import litellm.constants as constants_mod + import litellm.proxy.utils as utils_mod + from litellm.proxy.utils import request_spend_log_flush + + monkeypatch.setattr(constants_mod, "SPEND_LOG_QUEUE_POLL_INTERVAL", 30.0, raising=False) + mock_prisma_client.tool_usage_transactions = [] + + async def _flush_once_under_a_monitor() -> None: + flushed: Final = asyncio.Event() + + async def _fake_job(*args: Any, **kwargs: Any) -> None: + flushed.set() + + monkeypatch.setattr(utils_mod, "update_spend_logs_job", _fake_job) + mock_prisma_client.spend_log_transactions = [] + + monitor: Final = asyncio.create_task( + _monitor_spend_logs_queue( + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=MagicMock(), + ) + ) + try: + await asyncio.sleep(0.05) + assert not flushed.is_set() + + mock_prisma_client.spend_log_transactions.append(make_spend_log_row(request_id="r1")) + request_spend_log_flush(mock_prisma_client) + + await asyncio.wait_for(flushed.wait(), timeout=5.0) + finally: + monitor.cancel() + with suppress(asyncio.CancelledError): + await monitor + + asyncio.run(_flush_once_under_a_monitor()) + asyncio.run(_flush_once_under_a_monitor()) + + +def test_request_spend_log_flush_is_a_no_op_before_the_monitor_starts(mock_prisma_client: Any) -> None: + """A Responses row enqueued before the monitor's first pass must not fail the request.""" + from litellm.proxy.utils import request_spend_log_flush + + mock_prisma_client.spend_log_flush_requested = None + + request_spend_log_flush(mock_prisma_client) + + assert mock_prisma_client.spend_log_flush_requested is None def test_raise_failed_update_spend_exception_emits_failure_handler() -> None: From 29bcb0eeb9b085492764cdd970bc4b28340f554b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 04:55:57 -0700 Subject: [PATCH 026/223] test(proxy): pin that a flush requested before the monitor starts costs the row nothing The monitor reads the queue on its first pass, before it ever waits on a request, so dropping a request made while spend_log_flush_requested is still None delays nothing. Reordering the loop to wait first would turn that drop into a real delay for the Responses chaining flow, and now fails this test. --- .../prisma_and_spend/test_spend_functions.py | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py b/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py index ed854d2c95c..c8b87bd671e 100644 --- a/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py +++ b/tests/test_litellm/proxy/utils/prisma_and_spend/test_spend_functions.py @@ -621,16 +621,48 @@ def test_monitor_spend_logs_queue_flush_survives_an_earlier_event_loop( asyncio.run(_flush_once_under_a_monitor()) -def test_request_spend_log_flush_is_a_no_op_before_the_monitor_starts(mock_prisma_client: Any) -> None: - """A Responses row enqueued before the monitor's first pass must not fail the request.""" +@pytest.mark.asyncio +async def test_flush_requested_before_the_monitor_starts_costs_the_row_nothing( + mock_prisma_client: Any, + make_spend_log_row: Any, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A Responses row enqueued before the monitor exists still reaches the DB on its first + pass, so dropping that early request delays nothing. + """ + import litellm.constants as constants_mod + import litellm.proxy.utils as utils_mod from litellm.proxy.utils import request_spend_log_flush + monkeypatch.setattr(constants_mod, "SPEND_LOG_QUEUE_POLL_INTERVAL", 30.0, raising=False) mock_prisma_client.spend_log_flush_requested = None + mock_prisma_client.tool_usage_transactions = [] + mock_prisma_client.spend_log_transactions = [make_spend_log_row(request_id="r1")] + + flushed: Final = asyncio.Event() + + async def _fake_job(*args: Any, **kwargs: Any) -> None: + flushed.set() + + monkeypatch.setattr(utils_mod, "update_spend_logs_job", _fake_job) request_spend_log_flush(mock_prisma_client) - assert mock_prisma_client.spend_log_flush_requested is None + monitor: Final = asyncio.create_task( + _monitor_spend_logs_queue( + prisma_client=mock_prisma_client, + db_writer_client=None, + proxy_logging_obj=MagicMock(), + ) + ) + try: + await asyncio.wait_for(flushed.wait(), timeout=5.0) + finally: + monitor.cancel() + with suppress(asyncio.CancelledError): + await monitor + def test_raise_failed_update_spend_exception_emits_failure_handler() -> None: proxy_logging = MagicMock() From 2a2c49ad4cd711aded30da0384c1a200ddfca5d7 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 16:09:44 -0400 Subject: [PATCH 027/223] feat(ui): surface batch results on the logs page --- .../LogDetailContent.test.tsx | 78 ++++++++++++++++ .../LogDetailsDrawer/LogDetailContent.tsx | 61 +++++++++++++ .../RequestLogsTableColumns.test.tsx | 53 +++++++++++ .../view_logs/RequestLogsTableColumns.tsx | 29 +++++- .../components/view_logs/TypeBadges.test.tsx | 14 ++- .../src/components/view_logs/TypeBadges.tsx | 26 ++++++ .../view_logs/batchLogUtils.test.ts | 90 +++++++++++++++++++ .../src/components/view_logs/batchLogUtils.ts | 67 ++++++++++++++ .../src/components/view_logs/constants.ts | 3 + 9 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/batchLogUtils.test.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/batchLogUtils.ts diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx index a679dc49427..91778d2a98a 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx @@ -113,6 +113,84 @@ describe("LogDetailContent", () => { expect(screen.getAllByText("$0.00200000").length).toBeGreaterThanOrEqual(1); }); + it("shows reasoning tokens in Metrics when the usage breakout carries them", () => { + render( + , + ); + + expect(screen.getByText("Reasoning Tokens")).toBeInTheDocument(); + expect(screen.getByText("224")).toBeInTheDocument(); + }); + + it("hides the reasoning metric when the breakout is absent or zero", () => { + render( + , + ); + + expect(screen.queryByText("Reasoning Tokens")).not.toBeInTheDocument(); + }); + + describe("Batch Results section", () => { + const batchCostEntry = (metadata: Record) => + createLogEntry({ + request_id: "batch_abc123_batch_cost", + call_type: "aretrieve_batch", + metadata: { status: "success", ...metadata }, + }); + + it("renders batch id, per-request outcome counts, and batch models for a batch cost row", () => { + render( + , + ); + + const section = screen.getByText("Batch Results").closest('[data-slot="card"]') as HTMLElement; + expect(within(section).getByText("batch_abc123")).toBeInTheDocument(); + expect(within(section).getByText("2")).toBeInTheDocument(); + expect(within(section).getByText("1")).toBeInTheDocument(); + expect(within(section).getByText("gemini-2.5-flash")).toBeInTheDocument(); + }); + + it("still renders the batch id when a legacy row carries no counts", () => { + render(); + + expect(screen.getByText("Batch Results")).toBeInTheDocument(); + expect(screen.getByText("batch_abc123")).toBeInTheDocument(); + expect(screen.queryByText("Successful Requests")).not.toBeInTheDocument(); + }); + + it("never renders for a non-batch call type", () => { + render( + , + ); + + expect(screen.queryByText("Batch Results")).not.toBeInTheDocument(); + }); + }); + it("should show Input Tokens and Output Tokens for anthropic_messages when uncached text_tokens exist", () => { render( + {/* Batch Results */} + {isBatchCallType(logEntry.call_type) && } + {/* Routing */} @@ -374,6 +384,53 @@ function MetricLabel({ label, tooltip, docsUrl }: { label: string; tooltip: stri ); } +/** + * Aggregate per-request outcomes for a batch cost row: batch id, success/failure counts + * from the parsed output and error files, and the models the batch actually ran on. + */ +function BatchResultsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: Record }) { + const counts = getBatchRequestCounts(metadata); + const batchId = getBatchIdFromRequestId(logEntry.request_id); + const batchModels = getBatchModels(metadata); + if (!counts && !batchId && !batchModels) return null; + + return ( +
+ + + Batch Results + + + + {batchId && ( + + + + )} + {counts && ( + <> + + {formatNumberWithCommas(counts.successful)} + + + {counts.failed > 0 ? ( + + {formatNumberWithCommas(counts.failed)} + + ) : ( + formatNumberWithCommas(counts.failed) + )} + + + )} + {batchModels && {batchModels.join(", ")}} + + + +
+ ); +} + function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: Record }) { const completionStartTime = logEntry.completionStartTime; const ttftMs = @@ -391,6 +448,7 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: const uncachedInputTokens = getUncachedInputTextTokens(metadata); const showAnthropicMessagesInputOutput = logEntry.call_type === "anthropic_messages" && uncachedInputTokens !== undefined; + const reasoningTokens = getReasoningTokens(metadata); return (
@@ -416,6 +474,9 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: /> )} + {reasoningTokens !== undefined && reasoningTokens > 0 && ( + {formatNumberWithCommas(reasoningTokens)} + )} ${formatNumberWithCommas(logEntry.spend || 0, 8)} {logEntry.request_duration_ms != null ? (logEntry.request_duration_ms / 1000).toFixed(3) : "-"} s diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx index 3c9e6543c1c..3e8e522afe1 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx @@ -134,6 +134,59 @@ describe("Type column", () => { expect(screen.getByText("MCP")).toBeInTheDocument(); }); + + it("marks a batch cost row with the Batch badge instead of LLM", () => { + renderRows([logEntry({ request_id: "batch_1_batch_cost", call_type: "aretrieve_batch" })]); + + expect(screen.getByText("Batch")).toBeInTheDocument(); + expect(screen.queryByText("LLM")).not.toBeInTheDocument(); + }); +}); + +describe("batch rows", () => { + const batchRow = (overrides: Partial): LogEntry => + logEntry({ + request_id: "batch_abc123_batch_cost", + call_type: "aretrieve_batch", + ...overrides, + }); + + it("rolls partial failures into the status badge instead of reporting blanket Success", async () => { + const user = userEvent.setup(); + renderRows([batchRow({ metadata: { batch_successful_requests: 2, batch_failed_requests: 1 } })]); + + expect(screen.queryByText("Success")).not.toBeInTheDocument(); + await user.hover(screen.getByText("2/3 succeeded")); + expect(await screen.findByText("1 of 3 batch requests failed")).toBeInTheDocument(); + }); + + it("keeps the Success badge when every batch request succeeded", () => { + renderRows([batchRow({ metadata: { batch_successful_requests: 3, batch_failed_requests: 0 } })]); + + expect(screen.getByText("Success")).toBeInTheDocument(); + }); + + it("keeps the Failure badge when the batch row itself failed, whatever the counts say", () => { + renderRows([batchRow({ metadata: { status: "failure", batch_successful_requests: 2, batch_failed_requests: 1 } })]); + + expect(screen.getByText("Failure")).toBeInTheDocument(); + expect(screen.queryByText("2/3 succeeded")).not.toBeInTheDocument(); + }); + + it("shows the provider batch id, not the synthetic _batch_cost request id", () => { + renderRows([batchRow({ metadata: { batch_successful_requests: 1, batch_failed_requests: 0 } })]); + + expect(screen.getByText("batch_abc123")).toBeInTheDocument(); + expect(screen.queryByText("batch_abc123_batch_cost")).not.toBeInTheDocument(); + expect(screen.getByText("batch cost")).toBeInTheDocument(); + }); + + it("leaves ordinary request ids untouched", () => { + renderRows([logEntry({ request_id: "chatcmpl-42" })]); + + expect(screen.getByText("chatcmpl-42")).toBeInTheDocument(); + expect(screen.queryByText("batch cost")).not.toBeInTheDocument(); + }); }); describe("Model column", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx index 9d4dc4f7898..b4296abe266 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx @@ -7,9 +7,10 @@ import { CellTooltip, DateCell, IdCell, MoneyCell, StatusBadge } from "@/compone import { getSpendString } from "@/utils/dataUtils"; import { getProviderLogoAndName } from "../provider_info_helpers"; +import { getBatchIdFromRequestId, getBatchRequestCounts, isBatchCallType } from "./batchLogUtils"; import type { LogEntry } from "./columns"; import { AGENT_CALL_TYPES, MCP_CALL_TYPES } from "./constants"; -import { AgentBadge, AgentIcon, LlmBadge, McpBadge, SparkleIcon, WrenchIcon } from "./TypeBadges"; +import { AgentBadge, AgentIcon, BatchBadge, LlmBadge, McpBadge, SparkleIcon, WrenchIcon } from "./TypeBadges"; export interface RequestLogsTableColumnsDeps { onKeyHashClick: (keyHash: string) => void; @@ -66,6 +67,7 @@ export const getRequestLogsTableColumns = ({ if (sessionCount <= 1) { if (isMcp) return ; if (isAgent) return ; + if (isBatchCallType(log.call_type)) return ; return ; } @@ -106,6 +108,17 @@ export const getRequestLogsTableColumns = ({ cell: ({ row }) => { const status = readMetaString(row.original.metadata, "status") ?? "Success"; const isSuccess = status.toLowerCase() !== "failure"; + const batchCounts = isSuccess ? getBatchRequestCounts(row.original.metadata) : undefined; + if (batchCounts && batchCounts.failed > 0) { + const total = batchCounts.successful + batchCounts.failed; + return ( + + ); + } return ; }, }, @@ -122,7 +135,19 @@ export const getRequestLogsTableColumns = ({ accessorKey: "request_id", header: "Request ID", enableSorting: false, - cell: ({ row }) => , + cell: ({ row }) => { + const log = row.original; + const batchId = isBatchCallType(log.call_type) ? getBatchIdFromRequestId(log.request_id) : undefined; + if (batchId) { + return ( +
+ + batch cost +
+ ); + } + return ; + }, }, { id: "spend", diff --git a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx index e3467310265..8a964ea2124 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; -import { LlmBadge, McpBadge, AgentBadge } from "./TypeBadges"; +import { LlmBadge, McpBadge, AgentBadge, BatchBadge } from "./TypeBadges"; describe("TypeBadges", () => { describe("LlmBadge", () => { @@ -43,4 +43,16 @@ describe("TypeBadges", () => { expect(screen.getByText("12")).toBeInTheDocument(); }); }); + + describe("BatchBadge", () => { + it("should render with default 'Batch' text when no count is provided", () => { + render(); + expect(screen.getByText("Batch")).toBeInTheDocument(); + }); + + it("should render the count when provided", () => { + render(); + expect(screen.getByText("4")).toBeInTheDocument(); + }); + }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx index 1ba4365f66e..84079848487 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx @@ -57,6 +57,25 @@ export const AgentIcon = ({ size = 12 }: { size?: number }) => ( ); +/** Stacked-layers icon for Batch API call types (Lucide Layers-style). */ +export const LayersIcon = ({ size = 12 }: { size?: number }) => ( + + + + + +); + export const LlmBadge = ({ count }: { count?: number }) => ( @@ -77,3 +96,10 @@ export const AgentBadge = ({ count }: { count?: number }) => ( {count != null ? count : "Agent"} ); + +export const BatchBadge = ({ count }: { count?: number }) => ( + + + {count != null ? count : "Batch"} + +); diff --git a/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.test.ts b/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.test.ts new file mode 100644 index 00000000000..4da1e389646 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it } from "vitest"; + +import { + getBatchIdFromRequestId, + getBatchModels, + getBatchRequestCounts, + getReasoningTokens, + isBatchCallType, +} from "./batchLogUtils"; + +/** Metadata shape the batch cost poller writes on an aretrieve_batch spend row. */ +const batchCostMetadata = { + batch_models: ["gemini-2.5-flash"], + batch_successful_requests: 2, + batch_failed_requests: 1, + usage_object: { + total_tokens: 270, + prompt_tokens: 14, + completion_tokens: 256, + completion_tokens_details: { text_tokens: 32, reasoning_tokens: 224 }, + }, +}; + +describe("isBatchCallType", () => { + it("recognizes the poller's aretrieve_batch and the create call types", () => { + for (const callType of ["aretrieve_batch", "retrieve_batch", "acreate_batch", "create_batch"]) { + expect(isBatchCallType(callType)).toBe(true); + } + expect(isBatchCallType("acompletion")).toBe(false); + }); +}); + +describe("getBatchRequestCounts", () => { + it("reads both counts off a batch cost row", () => { + expect(getBatchRequestCounts(batchCostMetadata)).toEqual({ successful: 2, failed: 1 }); + }); + + it("returns undefined for a non-batch row and for null counts, so no rollup renders", () => { + expect(getBatchRequestCounts({ status: "success" })).toBeUndefined(); + expect(getBatchRequestCounts({ batch_successful_requests: null, batch_failed_requests: null })).toBeUndefined(); + expect(getBatchRequestCounts(undefined)).toBeUndefined(); + }); + + it("treats a lone present count as the other being 0, for rows logged mid-rollout", () => { + expect(getBatchRequestCounts({ batch_successful_requests: 3 })).toEqual({ successful: 3, failed: 0 }); + }); +}); + +describe("getBatchIdFromRequestId", () => { + it("strips the poller's synthetic _batch_cost suffix down to the provider batch id", () => { + expect(getBatchIdFromRequestId("batch_abc123_batch_cost")).toBe("batch_abc123"); + }); + + it("returns undefined for ordinary request ids and a bare suffix", () => { + expect(getBatchIdFromRequestId("chatcmpl-123")).toBeUndefined(); + expect(getBatchIdFromRequestId("_batch_cost")).toBeUndefined(); + }); +}); + +describe("getBatchModels", () => { + it("returns the model list from metadata.batch_models", () => { + expect(getBatchModels(batchCostMetadata)).toEqual(["gemini-2.5-flash"]); + }); + + it("returns undefined when absent, null, or empty", () => { + expect(getBatchModels({})).toBeUndefined(); + expect(getBatchModels({ batch_models: null })).toBeUndefined(); + expect(getBatchModels({ batch_models: [] })).toBeUndefined(); + }); +}); + +describe("getReasoningTokens", () => { + it("reads reasoning tokens from usage_object on a batch cost row", () => { + expect(getReasoningTokens(batchCostMetadata)).toBe(224); + }); + + it("prefers additional_usage_values, which per-request rows carry", () => { + const metadata = { + additional_usage_values: { completion_tokens_details: { reasoning_tokens: 40 } }, + usage_object: { completion_tokens_details: { reasoning_tokens: 999 } }, + }; + expect(getReasoningTokens(metadata)).toBe(40); + }); + + it("returns undefined when the breakout is null or missing", () => { + expect(getReasoningTokens({ usage_object: { completion_tokens_details: null } })).toBeUndefined(); + expect(getReasoningTokens({})).toBeUndefined(); + expect(getReasoningTokens(undefined)).toBeUndefined(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.ts b/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.ts new file mode 100644 index 00000000000..ce7793065d2 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/batchLogUtils.ts @@ -0,0 +1,67 @@ +/** + * Helpers for reading batch-specific fields off a spend log row. + * + * The proxy's batch cost poller (CheckBatchCost) writes one spend log per completed batch + * with request_id "_batch_cost" and call_type "aretrieve_batch", carrying + * batch_models / batch_successful_requests / batch_failed_requests in metadata + * (see litellm/proxy/spend_tracking/spend_tracking_utils.py). + */ + +import { BATCH_CALL_TYPES } from "./constants"; + +export const BATCH_COST_REQUEST_ID_SUFFIX = "_batch_cost"; + +export interface BatchRequestCounts { + successful: number; + failed: number; +} + +export const isBatchCallType = (callType: string): boolean => BATCH_CALL_TYPES.includes(callType); + +const readMetaNumber = (metadata: Record | undefined, key: string): number | undefined => { + const value = metadata?.[key]; + return typeof value === "number" && Number.isFinite(value) ? value : undefined; +}; + +/** + * Per-request outcome counts of a batch cost row. Undefined when the row carries neither + * count (a non-batch row, or a batch logged before counts were tracked). + */ +export const getBatchRequestCounts = ( + metadata: Record | undefined, +): BatchRequestCounts | undefined => { + const successful = readMetaNumber(metadata, "batch_successful_requests"); + const failed = readMetaNumber(metadata, "batch_failed_requests"); + if (successful === undefined && failed === undefined) return undefined; + return { successful: successful ?? 0, failed: failed ?? 0 }; +}; + +/** The provider batch id behind a poller-written "_batch_cost" spend row. */ +export const getBatchIdFromRequestId = (requestId: string): string | undefined => + requestId.endsWith(BATCH_COST_REQUEST_ID_SUFFIX) && requestId.length > BATCH_COST_REQUEST_ID_SUFFIX.length + ? requestId.slice(0, -BATCH_COST_REQUEST_ID_SUFFIX.length) + : undefined; + +/** The models the batch's requests actually ran on, from metadata.batch_models. */ +export const getBatchModels = (metadata: Record | undefined): string[] | undefined => { + const models = metadata?.["batch_models"]; + if (!Array.isArray(models)) return undefined; + const names = models.filter((model): model is string => typeof model === "string" && model !== ""); + return names.length > 0 ? names : undefined; +}; + +/** + * Reasoning tokens aggregated across the row's completion usage. Read from the same two + * metadata containers the drawer already uses for prompt-token details: per-request rows + * carry additional_usage_values, batch cost rows carry usage_object. + */ +export const getReasoningTokens = (metadata: Record | undefined): number | undefined => { + const readDetails = (container: unknown): number | undefined => { + if (typeof container !== "object" || container === null) return undefined; + const details = (container as Record)["completion_tokens_details"]; + if (typeof details !== "object" || details === null) return undefined; + const reasoning = (details as Record)["reasoning_tokens"]; + return typeof reasoning === "number" && Number.isFinite(reasoning) ? reasoning : undefined; + }; + return readDetails(metadata?.["additional_usage_values"]) ?? readDetails(metadata?.["usage_object"]); +}; diff --git a/ui/litellm-dashboard/src/components/view_logs/constants.ts b/ui/litellm-dashboard/src/components/view_logs/constants.ts index 1c17f398e35..51889ae7d04 100644 --- a/ui/litellm-dashboard/src/components/view_logs/constants.ts +++ b/ui/litellm-dashboard/src/components/view_logs/constants.ts @@ -21,6 +21,9 @@ export const MCP_CALL_TYPES = ["call_mcp_tool", "list_mcp_tools"]; /** Call types that represent agent/A2A requests (e.g. asend_message). */ export const AGENT_CALL_TYPES = ["asend_message"]; +/** Call types that represent Batch API operations (creation and retrieval, sync and async). */ +export const BATCH_CALL_TYPES = ["acreate_batch", "create_batch", "aretrieve_batch", "retrieve_batch"]; + export const QUICK_SELECT_OPTIONS: { label: string; value: number; unit: string }[] = [ { label: "Last Minute", value: 1, unit: "minutes" }, { label: "Last 15 Minutes", value: 15, unit: "minutes" }, From c276813cb46f57b1934d5e18bedfc53a09e74df0 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 17:23:53 -0400 Subject: [PATCH 028/223] feat(batches): enrich batch cost rows with breakdown, identity, session, and org spend --- .../proxy/common_utils/check_batch_cost.py | 48 ++++++++++---- litellm/batches/batch_utils.py | 52 +++++++++------- litellm/litellm_core_utils/litellm_logging.py | 15 +++++ .../spend_tracking/spend_tracking_utils.py | 27 +++++++- .../test_batches_logging_unit_tests.py | 60 ++++++++++++++++-- .../proxy_unit_tests/test_check_batch_cost.py | 45 ++++++++++++++ .../test_litellm/batches/test_batch_utils.py | 34 +++++++--- .../test_spend_tracking_utils.py | 62 +++++++++++++++++++ .../view_logs/RequestLogsTableColumns.tsx | 4 +- 9 files changed, 295 insertions(+), 52 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index 354a6ed2fd0..be6e52681fb 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -112,35 +112,39 @@ class CheckBatchCost: verbose_proxy_logger.error(f"CheckBatchCost: could not look up user {user_id} for batch {batch_id}: {e}") return {} - async def _get_key_alias(self, batch_id: str, api_key: str | None) -> str | None: - """Resolve the creating virtual key's alias from its hashed token.""" + async def _get_key_attribution(self, batch_id: str, api_key: str | None) -> tuple[str | None, str | None]: + """Resolve the creating virtual key's (alias, org_id) from its hashed token.""" if not api_key: - return None + return None, None try: key_row: prisma_models.LiteLLM_VerificationToken | None = ( await self.prisma_client.db.litellm_verificationtoken.find_unique( where={"token": api_key} ) ) - return getattr(key_row, "key_alias", None) if key_row is not None else None + if key_row is None: + return None, None + return getattr(key_row, "key_alias", None), getattr(key_row, "org_id", None) except Exception as e: verbose_proxy_logger.error(f"CheckBatchCost: could not look up key alias for batch {batch_id}: {e}") - return None + return None, None - async def _get_team_alias(self, team_id: str | None) -> str | None: - """Resolve a team's alias from its id.""" + async def _get_team_attribution(self, team_id: str | None) -> tuple[str | None, str | None]: + """Resolve a team's (alias, organization_id) from its id.""" if not team_id: - return None + return None, None try: team_row: prisma_models.LiteLLM_TeamTable | None = ( await self.prisma_client.db.litellm_teamtable.find_unique( where={"team_id": team_id} ) ) - return getattr(team_row, "team_alias", None) if team_row is not None else None + if team_row is None: + return None, None + return getattr(team_row, "team_alias", None), getattr(team_row, "organization_id", None) except Exception as e: verbose_proxy_logger.error(f"CheckBatchCost: could not look up team alias for team {team_id}: {e}") - return None + return None, None async def _build_creator_attribution_metadata( self, job: "LiteLLM_ManagedObjectTable", batch_id: str @@ -153,6 +157,10 @@ class CheckBatchCost: user_api_key_alias; when it has no alias, or the key has since been rotated or deleted, the field keeps the creating user's alias that _get_user_info filled in, because a resolvable name is more useful on the spend row than a null. + + user_api_key_org_id must be resolved here too: the spend update writer reads it + off this metadata to increment organization spend, so leaving it out silently + drops batch cost from org accounting for keys and teams that belong to one. """ api_key = getattr(job, "api_key", None) team_id = getattr(job, "team_id", None) @@ -165,12 +173,15 @@ class CheckBatchCost: **(await self._get_user_info(batch_id, job.created_by)), } - key_alias = await self._get_key_alias(batch_id, api_key) + key_alias, key_org_id = await self._get_key_attribution(batch_id, api_key) if key_alias is not None: metadata["user_api_key_alias"] = key_alias - team_alias = await self._get_team_alias(team_id) + team_alias, team_org_id = await self._get_team_attribution(team_id) if team_alias is not None: metadata["user_api_key_team_alias"] = team_alias + org_id: Final = key_org_id or team_org_id + if org_id is not None: + metadata["user_api_key_org_id"] = org_id if isinstance(request_tags, list) and request_tags: metadata["tags"] = [tag for tag in request_tags if isinstance(tag, str)] @@ -804,6 +815,7 @@ class CheckBatchCost: function_id=str(uuid.uuid4()), ) + deployment_api_base: Final = deployment_info.litellm_params.api_base logging_obj.update_environment_variables( litellm_params={ # set the user-agent header so that S3 callback consumers can easily identify CheckBatchCost callbacks @@ -812,9 +824,17 @@ class CheckBatchCost: "user-agent": CHECK_BATCH_COST_USER_AGENT, } }, - "metadata": await self._build_creator_attribution_metadata(job, batch_id), + **({"api_base": deployment_api_base} if deployment_api_base else {}), + "metadata": { + **(await self._build_creator_attribution_metadata(job, batch_id)), + # spend logs read the deployment identity off these metadata keys, so + # without them the batch cost row carries no model_id or model_group + "model_info": {"id": model_id}, + "model_group": deployment_info.model_name, + }, }, optional_params={}, + custom_llm_provider=str(llm_provider) if llm_provider else None, ) if not await self._claim_job_for_costing(job): @@ -832,6 +852,8 @@ class CheckBatchCost: batch_models=batch_result.models, batch_successful_requests=batch_result.successful_requests, batch_failed_requests=batch_result.failed_requests, + batch_prompt_cost=batch_result.prompt_cost, + batch_completion_cost=batch_result.completion_cost, ) except Exception: await self._release_job_claim(job) diff --git a/litellm/batches/batch_utils.py b/litellm/batches/batch_utils.py index 3831f57a10d..a688e9f69fb 100644 --- a/litellm/batches/batch_utils.py +++ b/litellm/batches/batch_utils.py @@ -10,7 +10,7 @@ from litellm._logging import verbose_logger from litellm.litellm_core_utils.get_litellm_params import AWS_CREDENTIAL_KWARGS_KEYS from litellm.litellm_core_utils.llm_cost_calc.utils import parse_prompt_tokens_details from litellm.types.llms.openai import Batch -from litellm.types.utils import CallTypes, ModelInfo, Usage +from litellm.types.utils import ModelInfo, Usage from litellm.utils import token_counter @@ -23,6 +23,8 @@ class BatchCostUsageResult: models: list[str] successful_requests: int failed_requests: int + prompt_cost: float = 0.0 + completion_cost: float = 0.0 async def calculate_batch_cost_and_usage( @@ -130,7 +132,8 @@ class _LineOutcome(Enum): @dataclass(frozen=True, slots=True) class _BatchOutputLineStats: - cost: float + prompt_cost: float + completion_cost: float prompt_tokens: int completion_tokens: int total_tokens: int @@ -193,15 +196,16 @@ def _compute_output_line_stats( raw_model: Final = response_body.get("model") response_model: Final = raw_model if isinstance(raw_model, str) and raw_model else None completion_details: Final = usage.completion_tokens_details + line_prompt_cost, line_completion_cost = _output_line_cost( + usage=usage, + custom_llm_provider=custom_llm_provider, + model_name=model_name, + response_model=response_model, + model_info=model_info, + ) return _BatchOutputLineStats( - cost=_output_line_cost( - response_body=response_body, - usage=usage, - custom_llm_provider=custom_llm_provider, - model_name=model_name, - response_model=response_model, - model_info=model_info, - ), + prompt_cost=line_prompt_cost, + completion_cost=line_completion_cost, prompt_tokens=usage.prompt_tokens, completion_tokens=usage.completion_tokens, total_tokens=usage.total_tokens, @@ -213,31 +217,24 @@ def _compute_output_line_stats( def _output_line_cost( - response_body: Mapping[str, Any], usage: Usage, custom_llm_provider: Literal["openai", "azure", "vertex_ai", "hosted_vllm", "anthropic", "bedrock"], model_name: str | None, response_model: str | None, model_info: ModelInfo | None, -) -> float: +) -> tuple[float, float]: + """(prompt_cost, completion_cost) for one output line, priced at batch rates.""" from litellm.cost_calculator import batch_cost_calculator - if model_info is None and custom_llm_provider not in ("anthropic", "bedrock"): - return litellm.completion_cost( - completion_response=response_body, - custom_llm_provider=custom_llm_provider, - call_type=CallTypes.aretrieve_batch.value, - ) cost_model: Final = ( model_name if custom_llm_provider == "bedrock" and model_name else response_model or model_name or "" ) - prompt_cost, completion_cost = batch_cost_calculator( + return batch_cost_calculator( usage=usage, model=cost_model, custom_llm_provider=custom_llm_provider, model_info=model_info, ) - return prompt_cost + completion_cost def _aggregate_batch_cost_usage_models( @@ -270,7 +267,9 @@ def _aggregate_batch_cost_usage_models( **cache_token_params, ) batch_models: Final = [model_name] if model_name else [stats.model for stats in line_stats if stats.model] - total_cost: Final = sum((stats.cost for stats in line_stats), 0.0) + total_prompt_cost: Final = sum((stats.prompt_cost for stats in line_stats), 0.0) + total_completion_cost: Final = sum((stats.completion_cost for stats in line_stats), 0.0) + total_cost: Final = total_prompt_cost + total_completion_cost verbose_logger.debug( "batch output aggregate: cost=%s usage=%s models=%s successful=%d failed=%d", total_cost, @@ -285,6 +284,8 @@ def _aggregate_batch_cost_usage_models( models=batch_models, successful_requests=successful_requests, failed_requests=failed_requests, + prompt_cost=total_prompt_cost, + completion_cost=total_completion_cost, ) @@ -309,7 +310,8 @@ def calculate_vertex_ai_batch_cost_and_usage( """ from litellm.cost_calculator import batch_cost_calculator - total_cost = 0.0 + total_prompt_cost = 0.0 # rebind-ok: loop accumulator, matches total_tokens below + total_completion_cost = 0.0 # rebind-ok: loop accumulator, matches total_tokens below total_tokens = 0 prompt_tokens = 0 completion_tokens = 0 @@ -341,7 +343,8 @@ def calculate_vertex_ai_batch_cost_and_usage( model=actual_model_name, custom_llm_provider="vertex_ai", ) - total_cost += p_cost + c_cost + total_prompt_cost += p_cost + total_completion_cost += c_cost except Exception as e: verbose_logger.debug("vertex_ai batch cost calculation error for line: %s", str(e)) @@ -349,6 +352,7 @@ def calculate_vertex_ai_batch_cost_and_usage( completion_tokens += _completion total_tokens += _total + total_cost: Final = total_prompt_cost + total_completion_cost verbose_logger.info( "vertex_ai batch cost: cost=%s, prompt=%d, completion=%d, total=%d, successful=%d, failed=%d", total_cost, @@ -369,6 +373,8 @@ def calculate_vertex_ai_batch_cost_and_usage( models=[actual_model_name], successful_requests=successful_requests, failed_requests=failed_requests, + prompt_cost=total_prompt_cost, + completion_cost=total_completion_cost, ) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index f54eeca5178..3a616e7961d 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2904,6 +2904,15 @@ class Logging(LiteLLMLoggingBaseClass): result._hidden_params["batch_successful_requests"] = batch_successful_requests # pyright: ignore[reportPrivateUsage] # rebind-ok: same result._hidden_params pattern as response_cost/batch_models above result._hidden_params["batch_failed_requests"] = batch_failed_requests # pyright: ignore[reportPrivateUsage] # rebind-ok: same pattern as above result.usage = batch_usage + batch_prompt_cost: Final = kwargs.get("batch_prompt_cost", None) + batch_completion_cost: Final = kwargs.get("batch_completion_cost", None) + if batch_prompt_cost is not None and batch_completion_cost is not None: + self.set_cost_breakdown( + input_cost=batch_prompt_cost, + output_cost=batch_completion_cost, + total_cost=batch_cost, + cost_for_built_in_tools_cost_usd_dollar=0.0, + ) elif should_compute_batch_data: batch_result: Final = await _handle_completed_batch( @@ -2919,6 +2928,12 @@ class Logging(LiteLLMLoggingBaseClass): result._hidden_params["batch_successful_requests"] = batch_result.successful_requests # pyright: ignore[reportPrivateUsage] # rebind-ok: same pattern as above result._hidden_params["batch_failed_requests"] = batch_result.failed_requests # pyright: ignore[reportPrivateUsage] # rebind-ok: same pattern as above result.usage = batch_result.usage + self.set_cost_breakdown( + input_cost=batch_result.prompt_cost, + output_cost=batch_result.completion_cost, + total_cost=batch_result.cost, + cost_for_built_in_tools_cost_usd_dollar=0.0, + ) start_time, end_time, result = self._success_handler_helper_fn( start_time=start_time, diff --git a/litellm/proxy/spend_tracking/spend_tracking_utils.py b/litellm/proxy/spend_tracking/spend_tracking_utils.py index a37c3ba4405..10db82d1b64 100644 --- a/litellm/proxy/spend_tracking/spend_tracking_utils.py +++ b/litellm/proxy/spend_tracking/spend_tracking_utils.py @@ -582,6 +582,7 @@ def get_logging_payload(kwargs, response_obj, start_time, end_time) -> SpendLogs metadata=metadata, standard_logging_payload=standard_logging_payload, omit_when_missing=_omits_session_id_when_missing(metadata), + batch_trace_session_id=_get_batch_trace_session_id(call_type=call_type, request_id=id), ), request_duration_ms=_get_request_duration_ms(start_time, end_time), status=_get_status_for_spend_log( @@ -620,20 +621,44 @@ def _omits_session_id_when_missing(metadata: Mapping[str, object] | None) -> boo return general_settings.get("missing_session_id") == "omit" +_BATCH_TRACE_CALL_TYPES: Final = frozenset( + { + CallTypes.create_batch.value, + CallTypes.acreate_batch.value, + CallTypes.retrieve_batch.value, + CallTypes.aretrieve_batch.value, + } +) + + +def _get_batch_trace_session_id(call_type: str | None, request_id: str | None) -> str | None: + """A batch's create row and its poller-written cost row both derive their request id + from the same batch id (the cost row appends BATCH_COST_REQUEST_ID_SUFFIX), so using + that id as the session groups the batch lifecycle into one trace on the logs UI. The + poller builds its own logging context, so per-request trace ids can never link them.""" + if call_type not in _BATCH_TRACE_CALL_TYPES or not request_id: + return None + return request_id.removesuffix(BATCH_COST_REQUEST_ID_SUFFIX) + + def _get_session_id_for_spend_log( kwargs: Mapping[str, object], metadata: Mapping[str, object] | None, standard_logging_payload: StandardLoggingPayload | None, omit_when_missing: bool, + batch_trace_session_id: str | None = None, ) -> str | None: """Under `omit` only `metadata.session_id`, the key Langfuse reads, counts as a session; `litellm_session_id` may - be a copied trace id.""" + be a copied trace id. Batch call types carry a deterministic session derived from the batch id, which outranks + the per-request trace ids because those differ between the create call and the cost poller's row.""" if omit_when_missing: session_id: Final = metadata.get("session_id") if metadata else None return str(session_id) if session_id else None from litellm._uuid import uuid + if batch_trace_session_id is not None: + return batch_trace_session_id if standard_logging_payload is not None and standard_logging_payload.get("trace_id") is not None: return str(standard_logging_payload.get("trace_id")) if kwargs.get("litellm_trace_id") is not None: diff --git a/tests/batches_tests/test_batches_logging_unit_tests.py b/tests/batches_tests/test_batches_logging_unit_tests.py index 5bde40d90b0..73adb391481 100644 --- a/tests/batches_tests/test_batches_logging_unit_tests.py +++ b/tests/batches_tests/test_batches_logging_unit_tests.py @@ -144,18 +144,20 @@ def test_get_batch_job_total_usage_from_file_content(sample_file_content_dict): @pytest.mark.asyncio async def test_batch_cost_calculator(sample_file_content_dict): """ - mock litellm.completion_cost to return 0.5 + mock batch_cost_calculator to return (0.3, 0.2) per line we know sample_file_content_dict has 2 successful responses - so we expect the cost to be 0.5 * 2 = 1.0 + so we expect the cost to be (0.3 + 0.2) * 2 = 1.0, split 0.6 / 0.4 """ - with patch("litellm.completion_cost", return_value=0.5): + with patch("litellm.cost_calculator.batch_cost_calculator", return_value=(0.3, 0.2)): result = _aggregate_batch_cost_usage_models( entries=sample_file_content_dict, custom_llm_provider="openai", ) - assert result.cost == 1.0 # 0.5 * 2 successful responses + assert result.cost == pytest.approx(1.0) # (0.3 + 0.2) * 2 successful responses + assert result.prompt_cost == pytest.approx(0.6) + assert result.completion_cost == pytest.approx(0.4) def test_get_response_from_batch_job_output_file(sample_file_content_dict): @@ -402,6 +404,56 @@ async def test_batch_retrieve_cost_tracking_with_explicit_cost_data(): assert mock_batch.usage == explicit_usage +@pytest.mark.asyncio +async def test_batch_retrieve_explicit_cost_split_sets_cost_breakdown(): + """The poller passes the batch's prompt/completion cost split so the spend row's + cost_breakdown carries real input/output costs; without it the UI's Cost Breakdown + card renders blank for every batch. Regression for the split being dropped.""" + from litellm.litellm_core_utils.litellm_logging import Logging + from litellm.types.utils import CallTypes, LiteLLMBatch + + mock_batch = LiteLLMBatch( + id="batch-breakdown-1", + object="batch", + endpoint="/v1/chat/completions", + errors=None, + input_file_id="file-input-1", + completion_window="24h", + status="completed", + output_file_id="file-output-1", + created_at=1234567890, + ) + mock_batch._hidden_params = {} + + logging_obj = Logging( + model="gpt-5-mini", + messages=[{"role": "user", "content": "test"}], + stream=False, + call_type=CallTypes.aretrieve_batch.value, + litellm_call_id="test-call-breakdown", + function_id="test-function", + start_time=time.time(), + dynamic_success_callbacks=[], + ) + logging_obj.custom_llm_provider = "openai" + + await logging_obj.async_success_handler( + result=mock_batch, + start_time=time.time(), + end_time=time.time() + 1, + batch_cost=0.10, + batch_usage=litellm.Usage(prompt_tokens=200, completion_tokens=100, total_tokens=300), + batch_models=["gpt-5-mini"], + batch_prompt_cost=0.06, + batch_completion_cost=0.04, + ) + + assert logging_obj.cost_breakdown is not None + assert logging_obj.cost_breakdown["input_cost"] == 0.06 + assert logging_obj.cost_breakdown["output_cost"] == 0.04 + assert logging_obj.cost_breakdown["total_cost"] == 0.10 + + @pytest.mark.asyncio async def test_batch_retrieve_cost_tracking_with_unified_file_id_incomplete_batch(): """ diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index ff5e8f89d64..9ee0a36f00b 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -2553,6 +2553,51 @@ class TestBatchCostAttribution: assert metadata["user_api_key_alias"] == "prod-key" + @pytest.mark.asyncio + async def test_org_id_comes_from_the_creating_key(self): + """The spend update writer increments organization spend from user_api_key_org_id, + so an org-scoped key's batch cost must carry the key's org id.""" + from types import SimpleNamespace + + instance = self._instance( + key_row=SimpleNamespace(key_alias="prod-key", org_id="org-42"), + team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), + ) + + metadata = await instance._build_creator_attribution_metadata(self._job(), "batch-1") + + assert metadata["user_api_key_org_id"] == "org-42" + + @pytest.mark.asyncio + async def test_org_id_falls_back_to_the_team_organization(self): + """A key with no org of its own still books batch spend against its team's + organization, matching how the request path resolves org attribution.""" + from types import SimpleNamespace + + instance = self._instance( + key_row=SimpleNamespace(key_alias="prod-key", org_id=None), + team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), + ) + + metadata = await instance._build_creator_attribution_metadata(self._job(), "batch-1") + + assert metadata["user_api_key_org_id"] == "org-team" + + @pytest.mark.asyncio + async def test_no_org_leaves_the_key_unset(self): + """Without any org the key is absent entirely, so the spend writer's org update + stays skipped instead of matching an empty-string organization.""" + from types import SimpleNamespace + + instance = self._instance( + key_row=SimpleNamespace(key_alias="prod-key", org_id=None), + team_row=SimpleNamespace(team_alias="Team Alpha", organization_id=None), + ) + + metadata = await instance._build_creator_attribution_metadata(self._job(), "batch-1") + + assert "user_api_key_org_id" not in metadata + class TestPollPageStarvation: """LIT-5462 regression: a row that can never be costed used to keep its slot in the diff --git a/tests/test_litellm/batches/test_batch_utils.py b/tests/test_litellm/batches/test_batch_utils.py index c86c7c4df03..1fd78870481 100644 --- a/tests/test_litellm/batches/test_batch_utils.py +++ b/tests/test_litellm/batches/test_batch_utils.py @@ -489,7 +489,9 @@ def test_aggregate_counts_successful_and_failed_requests(monkeypatch): def test_aggregate_returns_batch_cost_usage_result_dataclass(monkeypatch): - monkeypatch.setattr(litellm, "completion_cost", lambda **kw: 1.0) + import litellm.cost_calculator as cc + + monkeypatch.setattr(cc, "batch_cost_calculator", lambda **kw: (0.4, 0.6)) result = bu._aggregate_batch_cost_usage_models( entries=[_success_row(usage=_usage(10, 5))], custom_llm_provider="openai" ) @@ -500,6 +502,7 @@ def test_aggregate_returns_batch_cost_usage_result_dataclass(monkeypatch): 1, 0, ) + assert (result.prompt_cost, result.completion_cost) == (0.4, 0.6) # =========================================================================== # @@ -507,15 +510,17 @@ def test_aggregate_returns_batch_cost_usage_result_dataclass(monkeypatch): # =========================================================================== # -def test_cost_from_content_completion_cost_path(monkeypatch): - # model_info is None -> litellm.completion_cost per successful row. +def test_cost_without_model_info_prices_each_row_by_its_response_model(monkeypatch): + # model_info is None -> batch_cost_calculator per successful row, model from the response body. + import litellm.cost_calculator as cc + calls = [] - def _completion_cost(**kw): + def _batch_cost(**kw): calls.append(kw) - return 0.5 + return (0.3, 0.2) - monkeypatch.setattr(litellm, "completion_cost", _completion_cost) + monkeypatch.setattr(cc, "batch_cost_calculator", _batch_cost) rows = [ _success_row(usage=_usage(10, 5)), _failed_row(), # excluded -> not costed @@ -524,8 +529,10 @@ def test_cost_from_content_completion_cost_path(monkeypatch): result = bu._aggregate_batch_cost_usage_models(entries=rows, custom_llm_provider="openai") - assert result.cost == 1.0 # 2 successful * 0.5 + assert result.cost == pytest.approx(1.0) # 2 successful * (0.3 + 0.2) + assert (result.prompt_cost, result.completion_cost) == (pytest.approx(0.6), pytest.approx(0.4)) assert len(calls) == 2 # failed row not costed + assert all(call["model"] == "gpt-4o" and call["model_info"] is None for call in calls) assert result.successful_requests == 2 assert result.failed_requests == 1 @@ -578,7 +585,9 @@ def test_aggregate_consumes_entries_in_a_single_pass(monkeypatch): """A one-shot generator: any implementation that iterates the entries twice (e.g. separate cost and usage passes) sees nothing on the second pass and returns wrong totals for at least one of cost/usage/models.""" - monkeypatch.setattr(litellm, "completion_cost", lambda **kw: 0.5) + import litellm.cost_calculator as cc + + monkeypatch.setattr(cc, "batch_cost_calculator", lambda **kw: (0.25, 0.25)) one_shot = (row for row in [_success_row(usage=_usage(10, 5)), _failed_row(), _success_row(usage=_usage(20, 10))]) result = bu._aggregate_batch_cost_usage_models(entries=one_shot, custom_llm_provider="openai") @@ -753,12 +762,15 @@ def test_vertex_cost_error_in_line_is_swallowed(monkeypatch): @pytest.mark.asyncio async def test_calculate_batch_cost_and_usage_orchestration(monkeypatch): + import litellm.cost_calculator as cc + rows = [_success_row(model="gpt-4o", usage=_usage(10, 5))] - monkeypatch.setattr(litellm, "completion_cost", lambda **kw: 2.5) + monkeypatch.setattr(cc, "batch_cost_calculator", lambda **kw: (1.5, 1.0)) result = await bu.calculate_batch_cost_and_usage(file_content_dictionary=rows, custom_llm_provider="openai") assert result.cost == 2.5 + assert (result.prompt_cost, result.completion_cost) == (1.5, 1.0) assert (result.usage.prompt_tokens, result.usage.completion_tokens, result.usage.total_tokens) == (10, 5, 15) assert result.models == ["gpt-4o"] @@ -1107,8 +1119,10 @@ async def test_handle_completed_batch_orchestration(monkeypatch): async def fake_fetch(batch, custom_llm_provider, litellm_params=None): return _vertex_jsonl(rows) + import litellm.cost_calculator as cc + monkeypatch.setattr(bu, "_fetch_batch_output_file_content", fake_fetch) - monkeypatch.setattr(litellm, "completion_cost", lambda **kw: 3.3) + monkeypatch.setattr(cc, "batch_cost_calculator", lambda **kw: (2.0, 1.3)) result = await bu._handle_completed_batch(_batch("of"), custom_llm_provider="openai") diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py index 323930eee60..be274befd25 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py @@ -128,6 +128,68 @@ def test_legacy_policy_keeps_trace_id_fallback(): assert len(str(generated)) == 36 +def test_batch_lifecycle_rows_derive_the_same_session_from_the_batch_id(): + """The create call's request id IS the batch id and the poller's cost row appends + _batch_cost to it, so deriving the session from the request id lands both rows in one + trace on the logs UI even though the poller builds a fresh logging context per cycle.""" + from litellm.proxy.spend_tracking.spend_tracking_utils import _get_batch_trace_session_id + + create_session: Final = _get_batch_trace_session_id(call_type="acreate_batch", request_id="batch-uid-1") + cost_session: Final = _get_batch_trace_session_id( + call_type="aretrieve_batch", request_id="batch-uid-1_batch_cost" + ) + assert create_session == cost_session == "batch-uid-1" + + +def test_non_batch_call_types_derive_no_batch_session(): + from litellm.proxy.spend_tracking.spend_tracking_utils import _get_batch_trace_session_id + + assert _get_batch_trace_session_id(call_type="acompletion", request_id="chatcmpl-1") is None + + +def test_batch_session_outranks_the_per_request_trace_id(): + """Each batch lifecycle call carries its own auto-generated trace id, so letting the + trace id win would scatter the rows across sessions again.""" + session_id: Final = _get_session_id_for_spend_log( + kwargs={"litellm_trace_id": "trace-abc"}, + metadata={"trace_id": "trace-abc"}, + standard_logging_payload=_TRACE_ONLY_STANDARD_LOGGING, + omit_when_missing=False, + batch_trace_session_id="batch-uid-1", + ) + assert session_id == "batch-uid-1" + + +def test_omit_policy_still_suppresses_batch_sessions(): + session_id: Final = _get_session_id_for_spend_log( + kwargs={}, + metadata=None, + standard_logging_payload=None, + omit_when_missing=True, + batch_trace_session_id="batch-uid-1", + ) + assert session_id is None + + +def test_get_logging_payload_groups_batch_create_and_cost_rows_in_one_session(): + def _payload(call_type: str) -> SpendLogsPayload: + return get_logging_payload( + kwargs={ + "call_type": call_type, + "model": "gpt-4o-mini", + "litellm_params": {"metadata": {"user_api_key": "test-key"}}, + }, + response_obj=litellm.ModelResponse(id="batch-uid-1", choices=[], usage=litellm.Usage()), + start_time=datetime.datetime.now(timezone.utc), + end_time=datetime.datetime.now(timezone.utc), + ) + + create_payload: Final = _payload("acreate_batch") + cost_payload: Final = _payload("aretrieve_batch") + assert cost_payload["request_id"] == "batch-uid-1_batch_cost" + assert create_payload["session_id"] == cost_payload["session_id"] == "batch-uid-1" + + @pytest.mark.parametrize( ("request_metadata", "expected"), [ diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx index b4296abe266..a444acb9517 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx @@ -64,10 +64,12 @@ export const getRequestLogsTableColumns = ({ const sessionAgentCount = log.session_agent_count ?? (isAgent ? sessionCount : 0); const sessionMcpCount = log.mcp_tool_call_count ?? (isMcp ? sessionCount : 0); + if (isBatchCallType(log.call_type)) { + return 1 ? sessionCount : undefined} />; + } if (sessionCount <= 1) { if (isMcp) return ; if (isAgent) return ; - if (isBatchCallType(log.call_type)) return ; return ; } From 06c860b60a5c4a2d14a02dead9c809b8b7eaf22f Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 18:09:18 -0400 Subject: [PATCH 029/223] fix(ui): type batch results metadata as unknown instead of any --- .../components/view_logs/LogDetailsDrawer/LogDetailContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx index 8e0f041b88d..710d82b2f9d 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx @@ -388,7 +388,7 @@ function MetricLabel({ label, tooltip, docsUrl }: { label: string; tooltip: stri * Aggregate per-request outcomes for a batch cost row: batch id, success/failure counts * from the parsed output and error files, and the models the batch actually ran on. */ -function BatchResultsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: Record }) { +function BatchResultsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata: Record }) { const counts = getBatchRequestCounts(metadata); const batchId = getBatchIdFromRequestId(logEntry.request_id); const batchModels = getBatchModels(metadata); From 538cd2c3b00d31fcb516c8326de87883e0e27f94 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 18:16:31 -0400 Subject: [PATCH 030/223] fix(batches): narrow batch cost kwargs before the breakdown and drop node access in test --- litellm/litellm_core_utils/litellm_logging.py | 6 +++++- .../LogDetailsDrawer/LogDetailContent.test.tsx | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 3a616e7961d..03486f4f729 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2906,7 +2906,11 @@ class Logging(LiteLLMLoggingBaseClass): result.usage = batch_usage batch_prompt_cost: Final = kwargs.get("batch_prompt_cost", None) batch_completion_cost: Final = kwargs.get("batch_completion_cost", None) - if batch_prompt_cost is not None and batch_completion_cost is not None: + if ( + isinstance(batch_prompt_cost, float) + and isinstance(batch_completion_cost, float) + and isinstance(batch_cost, float) + ): self.set_cost_breakdown( input_cost=batch_prompt_cost, output_cost=batch_completion_cost, diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx index 91778d2a98a..a893e9bffd0 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx @@ -163,11 +163,13 @@ describe("LogDetailContent", () => { />, ); - const section = screen.getByText("Batch Results").closest('[data-slot="card"]') as HTMLElement; - expect(within(section).getByText("batch_abc123")).toBeInTheDocument(); - expect(within(section).getByText("2")).toBeInTheDocument(); - expect(within(section).getByText("1")).toBeInTheDocument(); - expect(within(section).getByText("gemini-2.5-flash")).toBeInTheDocument(); + expect(screen.getByText("Batch Results")).toBeInTheDocument(); + expect(screen.getByText("batch_abc123")).toBeInTheDocument(); + expect(screen.getByText("Successful Requests")).toBeInTheDocument(); + expect(screen.getByText("2")).toBeInTheDocument(); + expect(screen.getByText("Failed Requests")).toBeInTheDocument(); + expect(screen.getByText("1")).toBeInTheDocument(); + expect(screen.getByText("gemini-2.5-flash")).toBeInTheDocument(); }); it("still renders the batch id when a legacy row carries no counts", () => { From 7fb4b427cec0db7d3bc85dd6c2055ca987f2390b Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 18:36:05 -0400 Subject: [PATCH 031/223] feat(batches): snapshot the creating key's org on the managed object row --- .../proxy/common_utils/check_batch_cost.py | 61 +++++++++++++------ .../proxy/hooks/managed_files.py | 1 + .../migration.sql | 4 ++ .../litellm_proxy_extras/schema.prisma | 1 + litellm/models/managed_files.py | 1 + schema.prisma | 1 + .../proxy_unit_tests/test_check_batch_cost.py | 21 ++++++- ..._batch_update_db_managed_output_file_id.py | 4 +- 8 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20260903230000_add_org_id_to_managed_object_table/migration.sql diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index be6e52681fb..595a8d04bed 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -112,39 +112,66 @@ class CheckBatchCost: verbose_proxy_logger.error(f"CheckBatchCost: could not look up user {user_id} for batch {batch_id}: {e}") return {} - async def _get_key_attribution(self, batch_id: str, api_key: str | None) -> tuple[str | None, str | None]: - """Resolve the creating virtual key's (alias, org_id) from its hashed token.""" + async def _get_key_alias(self, batch_id: str, api_key: str | None) -> str | None: + """Resolve the creating virtual key's alias from its hashed token.""" if not api_key: - return None, None + return None try: key_row: prisma_models.LiteLLM_VerificationToken | None = ( await self.prisma_client.db.litellm_verificationtoken.find_unique( where={"token": api_key} ) ) - if key_row is None: - return None, None - return getattr(key_row, "key_alias", None), getattr(key_row, "org_id", None) + return getattr(key_row, "key_alias", None) if key_row is not None else None except Exception as e: verbose_proxy_logger.error(f"CheckBatchCost: could not look up key alias for batch {batch_id}: {e}") - return None, None + return None - async def _get_team_attribution(self, team_id: str | None) -> tuple[str | None, str | None]: - """Resolve a team's (alias, organization_id) from its id.""" + async def _get_team_alias(self, team_id: str | None) -> str | None: + """Resolve a team's alias from its id.""" if not team_id: - return None, None + return None try: team_row: prisma_models.LiteLLM_TeamTable | None = ( await self.prisma_client.db.litellm_teamtable.find_unique( where={"team_id": team_id} ) ) - if team_row is None: - return None, None - return getattr(team_row, "team_alias", None), getattr(team_row, "organization_id", None) + return getattr(team_row, "team_alias", None) if team_row is not None else None except Exception as e: verbose_proxy_logger.error(f"CheckBatchCost: could not look up team alias for team {team_id}: {e}") - return None, None + return None + + async def _get_org_id(self, job: "LiteLLM_ManagedObjectTable", batch_id: str) -> str | None: + """Organization to bill the batch against, snapshotted on the row at creation + like team_id. Rows created before the org_id column existed carry None, so they + fall back to the creating key's org (or its team's) as resolved today.""" + org_id = getattr(job, "org_id", None) + if org_id: + return org_id + api_key = getattr(job, "api_key", None) + team_id = getattr(job, "team_id", None) + try: + if api_key: + key_row: prisma_models.LiteLLM_VerificationToken | None = ( + await self.prisma_client.db.litellm_verificationtoken.find_unique( + where={"token": api_key} + ) + ) + key_org_id = getattr(key_row, "org_id", None) if key_row is not None else None + if key_org_id: + return key_org_id + if team_id: + team_row: prisma_models.LiteLLM_TeamTable | None = ( + await self.prisma_client.db.litellm_teamtable.find_unique( + where={"team_id": team_id} + ) + ) + return getattr(team_row, "organization_id", None) if team_row is not None else None + return None + except Exception as e: + verbose_proxy_logger.error(f"CheckBatchCost: could not resolve org for batch {batch_id}: {e}") + return None async def _build_creator_attribution_metadata( self, job: "LiteLLM_ManagedObjectTable", batch_id: str @@ -173,13 +200,13 @@ class CheckBatchCost: **(await self._get_user_info(batch_id, job.created_by)), } - key_alias, key_org_id = await self._get_key_attribution(batch_id, api_key) + key_alias = await self._get_key_alias(batch_id, api_key) if key_alias is not None: metadata["user_api_key_alias"] = key_alias - team_alias, team_org_id = await self._get_team_attribution(team_id) + team_alias = await self._get_team_alias(team_id) if team_alias is not None: metadata["user_api_key_team_alias"] = team_alias - org_id: Final = key_org_id or team_org_id + org_id: Final = await self._get_org_id(job, batch_id) if org_id is not None: metadata["user_api_key_org_id"] = org_id if isinstance(request_tags, list) and request_tags: diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 5cfcf6129f0..4f0ca787280 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -349,6 +349,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): "file_purpose": file_purpose, "created_by": resolve_resource_owner_id(user_api_key_dict), "team_id": user_api_key_dict.team_id, + "org_id": user_api_key_dict.org_id, "updated_by": user_api_key_dict.user_id, "status": file_object.status, **attribution_columns, diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260903230000_add_org_id_to_managed_object_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260903230000_add_org_id_to_managed_object_table/migration.sql new file mode 100644 index 00000000000..bbe980bb66f --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260903230000_add_org_id_to_managed_object_table/migration.sql @@ -0,0 +1,4 @@ +-- Add org_id column to LiteLLM_ManagedObjectTable +-- Snapshots the creating key's organization at submission time, like team_id, +-- so CheckBatchCost can bill organization spend hours later without re-resolving +ALTER TABLE "LiteLLM_ManagedObjectTable" ADD COLUMN IF NOT EXISTS "org_id" TEXT; diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index 7604ceadf7a..e386446d5bd 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -1034,6 +1034,7 @@ model LiteLLM_ManagedObjectTable { // for batches or finetuning jobs which use t created_at DateTime @default(now()) created_by String? team_id String? + org_id String? // creating key's organization at submission time; CheckBatchCost bills org spend against it api_key String? request_tags Json? @default("[]") updated_at DateTime @updatedAt diff --git a/litellm/models/managed_files.py b/litellm/models/managed_files.py index 23d70ef5c48..c90f9b535ea 100644 --- a/litellm/models/managed_files.py +++ b/litellm/models/managed_files.py @@ -32,6 +32,7 @@ class LiteLLM_ManagedObjectTable(LiteLLMPydanticObjectBase): file_object: LiteLLMBatch | LiteLLMFineTuningJob | ResponsesAPIResponse created_by: str | None = None team_id: str | None = None + org_id: str | None = None class LiteLLM_ManagedVectorStoreTable(LiteLLMPydanticObjectBase): diff --git a/schema.prisma b/schema.prisma index 7604ceadf7a..e386446d5bd 100644 --- a/schema.prisma +++ b/schema.prisma @@ -1034,6 +1034,7 @@ model LiteLLM_ManagedObjectTable { // for batches or finetuning jobs which use t created_at DateTime @default(now()) created_by String? team_id String? + org_id String? // creating key's organization at submission time; CheckBatchCost bills org spend against it api_key String? request_tags Json? @default("[]") updated_at DateTime @updatedAt diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index 9ee0a36f00b..92b946e19b1 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -2553,10 +2553,27 @@ class TestBatchCostAttribution: assert metadata["user_api_key_alias"] == "prod-key" + @pytest.mark.asyncio + async def test_org_id_snapshotted_on_the_row_wins(self): + """The org_id column captures the creating key's organization at submission time, + like team_id, so a key later moved to another org still bills the original one.""" + from types import SimpleNamespace + + instance = self._instance( + key_row=SimpleNamespace(key_alias="prod-key", org_id="org-moved-to"), + team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), + ) + + metadata = await instance._build_creator_attribution_metadata( + self._job(org_id="org-at-creation"), "batch-1" + ) + + assert metadata["user_api_key_org_id"] == "org-at-creation" + @pytest.mark.asyncio async def test_org_id_comes_from_the_creating_key(self): - """The spend update writer increments organization spend from user_api_key_org_id, - so an org-scoped key's batch cost must carry the key's org id.""" + """The spend update writer increments organization spend from user_api_key_org_id. + A legacy row without the org_id column falls back to the creating key's org.""" from types import SimpleNamespace instance = self._instance( diff --git a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py index ebd33aa2e53..63884e0a779 100644 --- a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py +++ b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py @@ -390,7 +390,7 @@ async def test_store_unified_object_id_persists_key_and_tags_on_create(): """Regression (spend loss): the batch create persists the creating key hash and tags so CheckBatchCost can write an attributed spend row instead of a blank one the DB drops.""" instance, store = _in_memory_managed_files() - creator = UserAPIKeyAuth(user_id="alice", team_id="team-alpha", api_key="hash-alice") + creator = UserAPIKeyAuth(user_id="alice", team_id="team-alpha", api_key="hash-alice", org_id="org-acme") await instance.store_unified_object_id( unified_object_id="unified-b", @@ -407,6 +407,7 @@ async def test_store_unified_object_id_persists_key_and_tags_on_create(): assert row["api_key"] == "hash-alice" assert row["created_by"] == "alice" assert row["team_id"] == "team-alpha" + assert row["org_id"] == "org-acme" assert row["request_tags"].data == ["env:prod"] @@ -471,6 +472,7 @@ async def test_store_unified_object_id_attribution_columns_are_write_once(): upsert_data = instance.prisma_client.db.litellm_managedobjecttable.upsert.call_args.kwargs["data"] assert "api_key" not in upsert_data["update"] assert "request_tags" not in upsert_data["update"] + assert "org_id" not in upsert_data["update"] @pytest.mark.asyncio From 9859d1e64ca1e6930d2f3e440a51222e834c7a5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:36:28 +0000 Subject: [PATCH 032/223] chore: sync schema.prisma copies from root --- litellm/proxy/schema.prisma | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 7604ceadf7a..e386446d5bd 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -1034,6 +1034,7 @@ model LiteLLM_ManagedObjectTable { // for batches or finetuning jobs which use t created_at DateTime @default(now()) created_by String? team_id String? + org_id String? // creating key's organization at submission time; CheckBatchCost bills org spend against it api_key String? request_tags Json? @default("[]") updated_at DateTime @updatedAt From 0d7976116eff0a9090f846e0066067bd867e67fb Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 18:51:18 -0400 Subject: [PATCH 033/223] fix(batches): resolve team-scoped keys' org at creation for the snapshot --- .../proxy/hooks/managed_files.py | 20 +++++++++++++- ..._batch_update_db_managed_output_file_id.py | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 4f0ca787280..2ed446ca44e 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -277,6 +277,24 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): ) verbose_logger.debug(f"LiteLLM Managed File object with id={file_id} stored in db: {result}") + async def _resolve_creator_org_id(self, user_api_key_dict: UserAPIKeyAuth) -> Optional[str]: + """Organization to snapshot on the managed object row, like team_id. A key that + belongs to an org only through its team carries no org_id on the auth object, so + resolve the team's organization at creation time; costing then bills the org the + batch was submitted under even if the key or team moves before it completes.""" + if user_api_key_dict.org_id: + return user_api_key_dict.org_id + if not user_api_key_dict.team_id: + return None + try: + team_row = await self.prisma_client.db.litellm_teamtable.find_unique( + where={"team_id": user_api_key_dict.team_id} + ) + return getattr(team_row, "organization_id", None) if team_row is not None else None + except Exception as e: + verbose_logger.warning(f"could not resolve org for managed object attribution: {e}") + return None + async def store_unified_object_id( self, unified_object_id: str, @@ -349,7 +367,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): "file_purpose": file_purpose, "created_by": resolve_resource_owner_id(user_api_key_dict), "team_id": user_api_key_dict.team_id, - "org_id": user_api_key_dict.org_id, + "org_id": await self._resolve_creator_org_id(user_api_key_dict), "updated_by": user_api_key_dict.user_id, "status": file_object.status, **attribution_columns, diff --git a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py index 63884e0a779..b5865ab4a13 100644 --- a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py +++ b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py @@ -375,6 +375,7 @@ def _in_memory_managed_files(): table.upsert = AsyncMock(side_effect=_upsert) prisma = MagicMock() prisma.db.litellm_managedobjecttable = table + prisma.db.litellm_teamtable.find_unique = AsyncMock(return_value=None) cache = MagicMock() cache.async_set_cache = AsyncMock() @@ -411,6 +412,32 @@ async def test_store_unified_object_id_persists_key_and_tags_on_create(): assert row["request_tags"].data == ["env:prod"] +@pytest.mark.asyncio +async def test_store_unified_object_id_resolves_org_through_the_team(): + """Most keys belong to an org only through their team, so the auth object carries no + org_id. The create resolves the team's organization so org spend is snapshotted at + submission time instead of never being billed.""" + from types import SimpleNamespace + + instance, store = _in_memory_managed_files() + instance.prisma_client.db.litellm_teamtable.find_unique = AsyncMock( + return_value=SimpleNamespace(organization_id="org-via-team") + ) + creator = UserAPIKeyAuth(user_id="alice", team_id="team-alpha", api_key="hash-alice") + + await instance.store_unified_object_id( + unified_object_id="unified-b", + file_object=_build_batch_response(batch_id="b", status="validating"), + litellm_parent_otel_span=None, + model_object_id="b", + file_purpose="batch", + user_api_key_dict=creator, + persist_attribution=True, + ) + + assert store["unified-b"]["org_id"] == "org-via-team" + + @pytest.mark.asyncio async def test_store_unified_object_id_omits_key_and_tags_without_persist_attribution(): """Regression (spend redirect): a caller that is not the batch create (a poll, or the From 35371a34c1c873a0afafe9dffc1873fcee42928e Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 3 Sep 2026 19:09:45 -0400 Subject: [PATCH 034/223] fix(batches): keep team org attribution when the key lookup fails --- .../proxy/common_utils/check_batch_cost.py | 24 ++++++++++++------- .../proxy_unit_tests/test_check_batch_cost.py | 17 +++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index 595a8d04bed..bd7f3c93ff3 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -151,8 +151,8 @@ class CheckBatchCost: return org_id api_key = getattr(job, "api_key", None) team_id = getattr(job, "team_id", None) - try: - if api_key: + if api_key: + try: key_row: prisma_models.LiteLLM_VerificationToken | None = ( await self.prisma_client.db.litellm_verificationtoken.find_unique( where={"token": api_key} @@ -161,16 +161,22 @@ class CheckBatchCost: key_org_id = getattr(key_row, "org_id", None) if key_row is not None else None if key_org_id: return key_org_id - if team_id: - team_row: prisma_models.LiteLLM_TeamTable | None = ( - await self.prisma_client.db.litellm_teamtable.find_unique( - where={"team_id": team_id} - ) + except Exception as e: + verbose_proxy_logger.error( + f"CheckBatchCost: could not resolve the key's org for batch {batch_id}, " + f"still trying the team's: {e}" ) - return getattr(team_row, "organization_id", None) if team_row is not None else None + if not team_id: return None + try: + team_row: prisma_models.LiteLLM_TeamTable | None = ( + await self.prisma_client.db.litellm_teamtable.find_unique( + where={"team_id": team_id} + ) + ) + return getattr(team_row, "organization_id", None) if team_row is not None else None except Exception as e: - verbose_proxy_logger.error(f"CheckBatchCost: could not resolve org for batch {batch_id}: {e}") + verbose_proxy_logger.error(f"CheckBatchCost: could not resolve the team's org for batch {batch_id}: {e}") return None async def _build_creator_attribution_metadata( diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index 92b946e19b1..59dfb67c486 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -2600,6 +2600,23 @@ class TestBatchCostAttribution: assert metadata["user_api_key_org_id"] == "org-team" + @pytest.mark.asyncio + async def test_key_lookup_failure_still_bills_the_team_org(self): + """A key-table error while resolving a legacy row's org must not drop the team's + organization: the two lookups fail independently, so org spend still lands.""" + from types import SimpleNamespace + + instance = self._instance( + team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), + ) + instance.prisma_client.db.litellm_verificationtoken.find_unique = AsyncMock( + side_effect=Exception("db down") + ) + + metadata = await instance._build_creator_attribution_metadata(self._job(), "batch-1") + + assert metadata["user_api_key_org_id"] == "org-team" + @pytest.mark.asyncio async def test_no_org_leaves_the_key_unset(self): """Without any org the key is absent entirely, so the spend writer's org update From 8debf8294cbe5e8d66d470b8dda1254668aa65df Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:42:39 -0700 Subject: [PATCH 035/223] fix(batches): resolve a legacy row's org from the key's organization_id and keep the Batch label on grouped rows --- .../proxy/common_utils/check_batch_cost.py | 2 +- tests/proxy_unit_tests/test_check_batch_cost.py | 8 ++++---- .../view_logs/RequestLogsTableColumns.test.tsx | 13 +++++++++++++ .../view_logs/RequestLogsTableColumns.tsx | 2 +- .../src/components/view_logs/TypeBadges.test.tsx | 7 +------ .../src/components/view_logs/TypeBadges.tsx | 4 ++-- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index ce80ba6493f..6b3ccfd3694 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -158,7 +158,7 @@ class CheckBatchCost: where={"token": api_key} ) ) - key_org_id = getattr(key_row, "org_id", None) if key_row is not None else None + key_org_id = getattr(key_row, "organization_id", None) if key_row is not None else None if key_org_id: return key_org_id except Exception as e: diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index 9ce91a8aee4..f9fb782b052 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -2561,7 +2561,7 @@ class TestBatchCostAttribution: from types import SimpleNamespace instance = self._instance( - key_row=SimpleNamespace(key_alias="prod-key", org_id="org-moved-to"), + key_row=SimpleNamespace(key_alias="prod-key", organization_id="org-moved-to"), team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), ) @@ -2578,7 +2578,7 @@ class TestBatchCostAttribution: from types import SimpleNamespace instance = self._instance( - key_row=SimpleNamespace(key_alias="prod-key", org_id="org-42"), + key_row=SimpleNamespace(key_alias="prod-key", organization_id="org-42"), team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), ) @@ -2593,7 +2593,7 @@ class TestBatchCostAttribution: from types import SimpleNamespace instance = self._instance( - key_row=SimpleNamespace(key_alias="prod-key", org_id=None), + key_row=SimpleNamespace(key_alias="prod-key", organization_id=None), team_row=SimpleNamespace(team_alias="Team Alpha", organization_id="org-team"), ) @@ -2625,7 +2625,7 @@ class TestBatchCostAttribution: from types import SimpleNamespace instance = self._instance( - key_row=SimpleNamespace(key_alias="prod-key", org_id=None), + key_row=SimpleNamespace(key_alias="prod-key", organization_id=None), team_row=SimpleNamespace(team_alias="Team Alpha", organization_id=None), ) diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx index 3e8e522afe1..9f0e659cb1f 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.test.tsx @@ -141,6 +141,19 @@ describe("Type column", () => { expect(screen.getByText("Batch")).toBeInTheDocument(); expect(screen.queryByText("LLM")).not.toBeInTheDocument(); }); + + it("keeps the Batch label on the grouped create-plus-cost session instead of a row count", () => { + const groupedCostRow: Partial = { + request_id: "batch_1_batch_cost", + call_type: "aretrieve_batch", + session_id: "batch_1", + session_total_count: 2, + }; + renderRows([logEntry(groupedCostRow)]); + + expect(screen.getByText("Batch")).toBeInTheDocument(); + expect(screen.queryByText("2")).not.toBeInTheDocument(); + }); }); describe("batch rows", () => { diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx index a444acb9517..1ec1087a1a4 100644 --- a/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/RequestLogsTableColumns.tsx @@ -65,7 +65,7 @@ export const getRequestLogsTableColumns = ({ const sessionMcpCount = log.mcp_tool_call_count ?? (isMcp ? sessionCount : 0); if (isBatchCallType(log.call_type)) { - return 1 ? sessionCount : undefined} />; + return ; } if (sessionCount <= 1) { if (isMcp) return ; diff --git a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx index 8a964ea2124..9a3b53685ca 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.test.tsx @@ -45,14 +45,9 @@ describe("TypeBadges", () => { }); describe("BatchBadge", () => { - it("should render with default 'Batch' text when no count is provided", () => { + it("should render 'Batch'", () => { render(); expect(screen.getByText("Batch")).toBeInTheDocument(); }); - - it("should render the count when provided", () => { - render(); - expect(screen.getByText("4")).toBeInTheDocument(); - }); }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx index 84079848487..db64bfbbe73 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TypeBadges.tsx @@ -97,9 +97,9 @@ export const AgentBadge = ({ count }: { count?: number }) => ( ); -export const BatchBadge = ({ count }: { count?: number }) => ( +export const BatchBadge = () => ( - {count != null ? count : "Batch"} + Batch ); From 217cb7da653e025f7c6d10eaae8e291b826fd76f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 4 Sep 2026 20:30:33 -0700 Subject: [PATCH 036/223] fix(managed_files): resolve the creator org through the cached team lookup Batch creation snapshotted the team's organization with a direct litellm_teamtable query on every create. Go through get_team_object instead, which serves the team auth already cached and only falls back to the database when the team was never cached. --- .../proxy/hooks/managed_files.py | 13 +++- ..._batch_update_db_managed_output_file_id.py | 68 ++++++++++++++----- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 850e5aadf0e..68782c5516d 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -288,11 +288,18 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): return user_api_key_dict.org_id if not user_api_key_dict.team_id: return None + from litellm.proxy.auth.auth_checks import get_team_object + from litellm.proxy.proxy_server import proxy_logging_obj, user_api_key_cache + try: - team_row = await self.prisma_client.db.litellm_teamtable.find_unique( - where={"team_id": user_api_key_dict.team_id} + team: Final = await get_team_object( + team_id=user_api_key_dict.team_id, + prisma_client=self.prisma_client, + user_api_key_cache=user_api_key_cache, + parent_otel_span=user_api_key_dict.parent_otel_span, + proxy_logging_obj=proxy_logging_obj, ) - return getattr(team_row, "organization_id", None) if team_row is not None else None + return team.organization_id except Exception as e: verbose_logger.warning(f"could not resolve org for managed object attribution: {e}") return None diff --git a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py index b5865ab4a13..d3e668b8987 100644 --- a/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py +++ b/tests/test_litellm/enterprise/proxy/test_batch_update_db_managed_output_file_id.py @@ -413,29 +413,63 @@ async def test_store_unified_object_id_persists_key_and_tags_on_create(): @pytest.mark.asyncio -async def test_store_unified_object_id_resolves_org_through_the_team(): +async def test_store_unified_object_id_resolves_org_through_the_cached_team(): """Most keys belong to an org only through their team, so the auth object carries no - org_id. The create resolves the team's organization so org spend is snapshotted at - submission time instead of never being billed.""" - from types import SimpleNamespace + org_id. The create reads the team that auth already cached, so org spend is snapshotted + at submission time without a database query in the request path.""" + from litellm.models.team import LiteLLM_TeamTableCachedObj + from litellm.proxy.proxy_server import user_api_key_cache + + instance, store = _in_memory_managed_files() + creator = UserAPIKeyAuth(user_id="alice", team_id="team-cached", api_key="hash-alice") + await user_api_key_cache.async_set_cache( + key="team_id:team-cached", + value=LiteLLM_TeamTableCachedObj(team_id="team-cached", organization_id="org-via-team"), + model_type=LiteLLM_TeamTableCachedObj, + ) + try: + await instance.store_unified_object_id( + unified_object_id="unified-b", + file_object=_build_batch_response(batch_id="b", status="validating"), + litellm_parent_otel_span=None, + model_object_id="b", + file_purpose="batch", + user_api_key_dict=creator, + persist_attribution=True, + ) + finally: + user_api_key_cache.delete_cache(key="team_id:team-cached") + + assert store["unified-b"]["org_id"] == "org-via-team" + instance.prisma_client.db.litellm_teamtable.find_unique.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_store_unified_object_id_resolves_org_from_the_db_when_the_team_is_not_cached(): + """A team no request has run under yet is absent from the auth cache; its organization + still comes back from the table so the org is billed rather than dropped.""" + from litellm.models.team import LiteLLM_TeamTable + from litellm.proxy.proxy_server import user_api_key_cache instance, store = _in_memory_managed_files() instance.prisma_client.db.litellm_teamtable.find_unique = AsyncMock( - return_value=SimpleNamespace(organization_id="org-via-team") + return_value=LiteLLM_TeamTable(team_id="team-uncached", organization_id="org-via-db") ) - creator = UserAPIKeyAuth(user_id="alice", team_id="team-alpha", api_key="hash-alice") + creator = UserAPIKeyAuth(user_id="alice", team_id="team-uncached", api_key="hash-alice") + try: + await instance.store_unified_object_id( + unified_object_id="unified-b", + file_object=_build_batch_response(batch_id="b", status="validating"), + litellm_parent_otel_span=None, + model_object_id="b", + file_purpose="batch", + user_api_key_dict=creator, + persist_attribution=True, + ) + finally: + user_api_key_cache.delete_cache(key="team_id:team-uncached") - await instance.store_unified_object_id( - unified_object_id="unified-b", - file_object=_build_batch_response(batch_id="b", status="validating"), - litellm_parent_otel_span=None, - model_object_id="b", - file_purpose="batch", - user_api_key_dict=creator, - persist_attribution=True, - ) - - assert store["unified-b"]["org_id"] == "org-via-team" + assert store["unified-b"]["org_id"] == "org-via-db" @pytest.mark.asyncio From 814c151b02b41e4fb333f4f505203d7db7c4343a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 5 Sep 2026 01:35:09 -0700 Subject: [PATCH 037/223] fix(batches): mask api base credentials on batch cost rows --- .../proxy/common_utils/check_batch_cost.py | 4 +- litellm/litellm_core_utils/litellm_logging.py | 16 ++-- .../proxy_unit_tests/test_check_batch_cost.py | 84 +++++++++++++++++++ 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index 6b3ccfd3694..64a86387c39 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -685,7 +685,7 @@ class CheckBatchCost: from litellm.files.main import afile_content from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging - from litellm.litellm_core_utils.litellm_logging import deployment_pricing_model_info + from litellm.litellm_core_utils.litellm_logging import deployment_pricing_model_info, mask_api_base_credentials from litellm.proxy.openai_files_endpoints.common_utils import ( _is_base64_encoded_unified_file_id, ) @@ -858,7 +858,7 @@ class CheckBatchCost: "user-agent": CHECK_BATCH_COST_USER_AGENT, } }, - **({"api_base": deployment_api_base} if deployment_api_base else {}), + **({"api_base": mask_api_base_credentials(deployment_api_base)} if deployment_api_base else {}), "metadata": { **(await self._build_creator_attribution_metadata(job, batch_id)), # spend logs read the deployment identity off these metadata keys, so diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 73c06b891fb..7e800139228 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -419,6 +419,13 @@ def _provider_response_id(source: object) -> str | None: return candidate if isinstance(candidate, str) and candidate else None +def mask_api_base_credentials(api_base: str) -> str: + if "key=" not in api_base: + return api_base + key_end: Final = api_base.find("key=") + 4 + return api_base[:key_end] + "*" * 5 + api_base[-4:] + + class Logging(LiteLLMLoggingBaseClass): global \ supabaseClient, \ @@ -1160,14 +1167,7 @@ class Logging(LiteLLMLoggingBaseClass): return data def _get_masked_api_base(self, api_base: str) -> str: - if "key=" in api_base: - # Find the position of "key=" in the string - key_index: Final = api_base.find("key=") + 4 - # Mask the last 5 characters after "key=" - masked_api_base = api_base[:key_index] + "*" * 5 + api_base[-4:] - else: - masked_api_base = api_base - return str(masked_api_base) + return str(mask_api_base_credentials(api_base)) def _pre_call(self, input, api_key, model=None, additional_args={}): """ diff --git a/tests/proxy_unit_tests/test_check_batch_cost.py b/tests/proxy_unit_tests/test_check_batch_cost.py index f9fb782b052..36177b44930 100644 --- a/tests/proxy_unit_tests/test_check_batch_cost.py +++ b/tests/proxy_unit_tests/test_check_batch_cost.py @@ -583,6 +583,90 @@ class TestCheckBatchCost: assert passed_model_info["input_cost_per_token_batches"] == 2e-06 assert passed_model_info["output_cost_per_token_batches"] == 4e-06 + @pytest.mark.asyncio + async def test_poller_masks_api_base_credentials_before_logging( + self, check_batch_cost_instance, mock_prisma_client, mock_llm_router + ): + """Request rows mask `key=` query credentials out of api_base before it is + logged, but the poller skips that pre-call step, so an unmasked deployment + api_base would land verbatim on the batch cost row: regression test for the + poller masking the same way. + """ + import base64 + from unittest.mock import patch + + import httpx + import respx + + from litellm.litellm_core_utils.litellm_logging import Logging + + mock_prisma_client.db.litellm_managedobjecttable.update_many = AsyncMock(return_value=1) + mock_prisma_client.db.litellm_managedobjecttable.update = AsyncMock() + mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock(return_value=None) + + mock_job = MagicMock() + mock_job.id = "job-masked-api-base-1" + mock_job.unified_object_id = base64.urlsafe_b64encode( + b"litellm_proxy;model_id:model-123;llm_batch_id:batch-456" + ).decode() + mock_job.created_by = "user-1" + mock_prisma_client.db.litellm_managedobjecttable.find_many = AsyncMock(return_value=[mock_job]) + + mock_response = MagicMock() + mock_response.status = "completed" + mock_response.output_file_id = "file-output-123" + mock_response.error_file_id = None + mock_response.model_dump_json.return_value = '{"id":"batch-1","status":"completed"}' + mock_llm_router.aretrieve_batch = AsyncMock(return_value=mock_response) + mock_llm_router.get_deployment_credentials_with_provider = MagicMock(return_value={"api_key": "sk-test"}) + + mock_deployment = MagicMock() + mock_deployment.litellm_params.custom_llm_provider = "openai" + mock_deployment.litellm_params.model = "gpt-5.4-mini" + mock_deployment.litellm_params.api_base = "https://gateway.example.com/v1?key=AIzaSyVERYSECRET7890" + mock_deployment.model_info.model_dump.return_value = {} + mock_llm_router.get_deployment = MagicMock(return_value=mock_deployment) + + output_line = json.dumps( + { + "custom_id": "req-1", + "response": { + "status_code": 200, + "body": { + "id": "chatcmpl-1", + "object": "chat.completion", + "model": "gpt-5.4-mini", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "hi"}, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}, + }, + }, + "error": None, + } + ) + + with ( + respx.mock(assert_all_called=True) as provider, + patch.object( # test-quality-ok: the poller builds Logging inline, the only seam to the row it logs + Logging, "async_success_handler", autospec=True + ) as success_handler, + ): + provider.get("https://api.openai.com/v1/files/file-output-123/content").mock( + return_value=httpx.Response(200, content=f"{output_line}\n".encode()) + ) + await check_batch_cost_instance.check_batch_cost() + + cost_row_calls = [call for call in success_handler.await_args_list if "batch_cost" in call.kwargs] + assert len(cost_row_calls) == 1 + logged_api_base = cost_row_calls[0].args[0].litellm_params["api_base"] + assert logged_api_base == "https://gateway.example.com/v1?key=*****7890" + assert "VERYSECRET" not in logged_api_base + @pytest.mark.asyncio async def test_primary_path_completion_update_includes_batch_processed( self, check_batch_cost_instance, mock_prisma_client, mock_llm_router From 95d721ffe60e6fa8409e2ff3e57ab9a85093fcc7 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sat, 5 Sep 2026 02:06:40 -0700 Subject: [PATCH 038/223] refactor(batches): drop docstrings restating the org fallbacks --- .../litellm_enterprise/proxy/common_utils/check_batch_cost.py | 3 --- enterprise/litellm_enterprise/proxy/hooks/managed_files.py | 4 ---- 2 files changed, 7 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py index 64a86387c39..13e9e5093a8 100644 --- a/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py +++ b/enterprise/litellm_enterprise/proxy/common_utils/check_batch_cost.py @@ -143,9 +143,6 @@ class CheckBatchCost: return None async def _get_org_id(self, job: "LiteLLM_ManagedObjectTable", batch_id: str) -> str | None: - """Organization to bill the batch against, snapshotted on the row at creation - like team_id. Rows created before the org_id column existed carry None, so they - fall back to the creating key's org (or its team's) as resolved today.""" org_id = getattr(job, "org_id", None) if org_id: return org_id diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 6474475f1bf..486904d0abe 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -281,10 +281,6 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): verbose_logger.debug(f"LiteLLM Managed File object with id={file_id} stored in db: {result}") async def _resolve_creator_org_id(self, user_api_key_dict: UserAPIKeyAuth) -> Optional[str]: - """Organization to snapshot on the managed object row, like team_id. A key that - belongs to an org only through its team carries no org_id on the auth object, so - resolve the team's organization at creation time; costing then bills the org the - batch was submitted under even if the key or team moves before it completes.""" if user_api_key_dict.org_id: return user_api_key_dict.org_id if not user_api_key_dict.team_id: From 7015bf37bb3830dab79b3efd886eee9b5b7ffad6 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Sat, 5 Sep 2026 17:32:17 -0700 Subject: [PATCH 039/223] fix(proxy): apply team model aliases on the JWT auth path Team reads on the auth path never loaded the team's alias table, and the team-based JWT branch copied a hand-picked subset of team fields onto UserAPIKeyAuth, so aliases (and a few other team grants) never reached JWT callers: restricted teams 403'd alias requests and open teams 400'd them Load the alias relation where the team row is read and cached, project the team onto every team_* token field through one shared team_grants helper used by both JWT returns, and keep the relation when team model add/delete rewrites the cached team. ui_sso reuses the shared alias table model Resolves LIT-5858 Claude-Session: https://claude.ai/code/session_01EX13mWex6RaBo9PYnkAtFW --- basedpyright-code-budget.json | 2 +- litellm/proxy/auth/auth_checks.py | 9 +- litellm/proxy/auth/handle_jwt.py | 5 +- litellm/proxy/auth/team_grants.py | 122 +++++++++++++++++ litellm/proxy/auth/user_api_key_auth.py | 22 +-- .../management_endpoints/team_endpoints.py | 4 +- litellm/proxy/management_endpoints/ui_sso.py | 23 +--- .../proxy/auth/test_auth_checks.py | 64 +++++++++ .../proxy/auth/test_handle_jwt.py | 52 +++++++ .../proxy/auth/test_team_grants.py | 129 ++++++++++++++++++ .../proxy/auth/test_user_api_key_auth.py | 116 ++++++++++++++++ .../test_team_endpoints.py | 44 ++++++ type-discipline-budget.json | 2 +- 13 files changed, 547 insertions(+), 47 deletions(-) create mode 100644 litellm/proxy/auth/team_grants.py create mode 100644 tests/test_litellm/proxy/auth/test_team_grants.py diff --git a/basedpyright-code-budget.json b/basedpyright-code-budget.json index 0b0a61192e6..8bdc251c684 100644 --- a/basedpyright-code-budget.json +++ b/basedpyright-code-budget.json @@ -105,7 +105,7 @@ "limit": 109 }, "reportUnknownMemberType": { - "limit": 38271 + "limit": 38269 }, "reportUnknownParameterType": { "limit": 19584 diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index dc693317de0..a71a1993064 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -475,6 +475,7 @@ def _is_model_cost_zero(model: str | list[str] | None, llm_router: Router | None _NO_MODEL_INFO: Final[Mapping[str, object]] = MappingProxyType({}) +_TEAM_GRANT_RELATIONS: Final[Mapping[str, object]] = MappingProxyType({"litellm_model_table": True}) def _has_ptu_flat_cost(model: str, llm_router: "Router") -> bool: @@ -2858,7 +2859,9 @@ class TeamNotFoundError(HTTPException): async def _get_team_db_check( team_id: str, prisma_client: PrismaClient, team_id_upsert: bool | None = None ) -> "_PrismaTeamRow | None": - response = await _team_table(TeamRepository(prisma_client)).find_unique(where={"team_id": team_id}) + response = await _team_table(TeamRepository(prisma_client)).find_unique( + where={"team_id": team_id}, include=_TEAM_GRANT_RELATIONS + ) if response is None and team_id_upsert: from litellm.proxy.management_endpoints.team_endpoints import new_team @@ -3158,7 +3161,9 @@ async def get_team_object_by_alias( # Query database by team_alias try: - teams: Final = await _team_table(TeamRepository(prisma_client)).find_many(where={"team_alias": team_alias}) + teams: Final = await _team_table(TeamRepository(prisma_client)).find_many( + where={"team_alias": team_alias}, include=_TEAM_GRANT_RELATIONS + ) if not teams: raise HTTPException( diff --git a/litellm/proxy/auth/handle_jwt.py b/litellm/proxy/auth/handle_jwt.py index 0795cee7409..69091ee8344 100644 --- a/litellm/proxy/auth/handle_jwt.py +++ b/litellm/proxy/auth/handle_jwt.py @@ -53,6 +53,7 @@ from litellm.proxy._types import ( ) from litellm.proxy.auth.auth_checks import can_team_access_model from litellm.proxy.auth.route_checks import RouteChecks +from litellm.proxy.auth.team_grants import team_model_aliases from litellm.proxy.common_utils.user_api_key_cache import ( UserApiKeyCache, get_management_object_ttl, @@ -1595,7 +1596,7 @@ class JWTAuthManager: model=requested_model, team_object=team_object, llm_router=llm_router, - team_model_aliases=None, + team_model_aliases=team_model_aliases(team_object), ) ): is_allowed = allowed_routes_check( @@ -2132,7 +2133,7 @@ class JWTAuthManager: model=requested_model, team_object=team_object, llm_router=llm_router, - team_model_aliases=None, + team_model_aliases=team_model_aliases(team_object), ) except ProxyException: continue diff --git a/litellm/proxy/auth/team_grants.py b/litellm/proxy/auth/team_grants.py new file mode 100644 index 00000000000..1196011dcdd --- /dev/null +++ b/litellm/proxy/auth/team_grants.py @@ -0,0 +1,122 @@ +"""Project a team row (plus the caller's membership in it) onto the ``team_*`` fields of ``UserAPIKeyAuth``. + +The virtual-key path gets these fields for free from the combined-view SQL join. Every other auth path +starts from a ``LiteLLM_TeamTable`` object instead and has to copy them over by hand, which is how JWT +callers kept losing grants (aliases, permissions, limits) one field at a time. Build the badge through +``team_grants`` and the two paths cannot drift. +""" + +from collections.abc import Mapping, Sequence +from types import MappingProxyType +from typing import Annotated, Final + +from pydantic import BaseModel, BeforeValidator, ConfigDict, TypeAdapter, ValidationError +from pydantic.main import IncEx +from typing_extensions import ReadOnly, TypedDict + +from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + LiteLLM_TeamMembership, + LiteLLM_TeamTable, + Member, +) + +_MODEL_ALIASES_ADAPTER: Final = TypeAdapter(dict[str, str]) +_JSON_COLUMNS: Final[Mapping[str, IncEx | bool]] = MappingProxyType( + {"metadata": True, "litellm_model_table": MappingProxyType({"model_aliases": True})} +) + + +def _decode_model_aliases(value: object) -> object: + """``LiteLLM_ModelTable.model_aliases`` is typed ``str | dict``; writers hand Prisma ``json.dumps(...)``, so take both.""" + if not isinstance(value, str): + return value + try: + return _MODEL_ALIASES_ADAPTER.validate_json(value) + except ValidationError: + return None + + +class TeamModelAliasTable(BaseModel): + model_config = ConfigDict(protected_namespaces=()) + + model_aliases: Annotated[Mapping[str, str] | None, BeforeValidator(_decode_model_aliases)] = None + + +class _TeamJsonColumns(BaseModel): + """The two loosely typed columns on ``LiteLLM_TeamTable``, re-read with the shape the badge needs.""" + + metadata: Mapping[str, object] | None = None + litellm_model_table: TeamModelAliasTable | None = None + + +class TeamGrants(TypedDict, total=False): + """Keyword arguments for ``UserAPIKeyAuth``. Empty when the caller has no team, so the model's own defaults apply.""" + + team_alias: ReadOnly[str | None] + team_tpm_limit: ReadOnly[int | None] + team_rpm_limit: ReadOnly[int | None] + team_max_budget: ReadOnly[float | None] + team_soft_budget: ReadOnly[float | None] + team_spend: ReadOnly[float | None] + team_models: ReadOnly[Sequence[str]] + team_blocked: ReadOnly[bool] + team_metadata: ReadOnly[Mapping[str, object] | None] + team_model_aliases: ReadOnly[Mapping[str, str] | None] + team_object_permission_id: ReadOnly[str | None] + team_object_permission: ReadOnly[LiteLLM_ObjectPermissionTable | None] + team_member: ReadOnly[Member | None] + team_member_spend: ReadOnly[float | None] + team_member_tpm_limit: ReadOnly[int | None] + team_member_rpm_limit: ReadOnly[int | None] + + +def _json_columns(team_object: LiteLLM_TeamTable) -> _TeamJsonColumns: + try: + return _TeamJsonColumns.model_validate(team_object.model_dump(include=_JSON_COLUMNS)) + except ValidationError: + return _TeamJsonColumns() + + +def team_model_aliases(team_object: LiteLLM_TeamTable | None) -> Mapping[str, str] | None: + if team_object is None: + return None + alias_table: Final = _json_columns(team_object).litellm_model_table + return alias_table.model_aliases if alias_table is not None else None + + +def team_grants( + team_object: LiteLLM_TeamTable | None, + team_membership: LiteLLM_TeamMembership | None, + user_id: str | None, +) -> TeamGrants: + if team_object is None: + return TeamGrants() + json_columns: Final = _json_columns(team_object) + return TeamGrants( + team_alias=team_object.team_alias, + team_tpm_limit=team_object.tpm_limit, + team_rpm_limit=team_object.rpm_limit, + team_max_budget=team_object.max_budget, + team_soft_budget=team_object.soft_budget, + team_spend=team_object.spend, + team_models=tuple(team_object.models), + team_blocked=team_object.blocked, + team_metadata=json_columns.metadata, + team_model_aliases=( + json_columns.litellm_model_table.model_aliases if json_columns.litellm_model_table is not None else None + ), + team_object_permission_id=team_object.object_permission_id, + team_object_permission=team_object.object_permission, + team_member=next( + (m for m in team_object.members_with_roles if user_id is not None and m.user_id == user_id), + None, + ), + team_member_spend=team_membership.spend if team_membership is not None else None, + team_member_tpm_limit=( + team_membership.safe_get_team_member_tpm_limit() if team_membership is not None else None + ), + team_member_rpm_limit=( + team_membership.safe_get_team_member_rpm_limit() if team_membership is not None else None + ), + ) diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 93293db24c6..08dbe2508ec 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -82,6 +82,7 @@ from litellm.proxy.auth.oauth2_proxy_hook import handle_oauth2_proxy_request from litellm.proxy.auth.resolvers import CredentialRef, Principal from litellm.proxy.auth.resolvers.store import IdentityStore from litellm.proxy.auth.route_checks import RouteChecks +from litellm.proxy.auth.team_grants import team_grants from litellm.proxy.auth.trusted_proxy_utils import get_trusted_proxy_cidrs from litellm.proxy.common_utils.cache_coordinator import EventDrivenCacheCoordinator from litellm.proxy.common_utils.http_parsing_utils import ( @@ -1476,24 +1477,16 @@ async def _user_api_key_auth_builder( user_id=user_id, user_email=user_email, team_id=team_id, - team_alias=(team_object.team_alias if team_object is not None else None), - team_tpm_limit=(team_object.tpm_limit if team_object is not None else None), - team_rpm_limit=(team_object.rpm_limit if team_object is not None else None), - team_models=(team_object.models if team_object is not None else []), - team_metadata=(team_object.metadata if team_object is not None else None), org_id=org_id, end_user_id=end_user_id, parent_otel_span=parent_otel_span, jwt_claims=jwt_claims, + **team_grants(team_object=team_object, team_membership=team_membership, user_id=user_id), ) valid_token = UserAPIKeyAuth( api_key=None, team_id=team_id, - team_alias=(team_object.team_alias if team_object is not None else None), - team_tpm_limit=(team_object.tpm_limit if team_object is not None else None), - team_rpm_limit=(team_object.rpm_limit if team_object is not None else None), - team_models=(team_object.models if team_object is not None else []), user_role=( LitellmUserRoles(user_object.user_role) if user_object is not None and user_object.user_role is not None @@ -1507,17 +1500,8 @@ async def _user_api_key_auth_builder( user_tpm_limit=(user_object.tpm_limit if user_object is not None else None), user_rpm_limit=(user_object.rpm_limit if user_object is not None else None), user_model_max_budget=(user_object.model_max_budget if user_object is not None else None), - team_member_rpm_limit=( - team_membership.safe_get_team_member_rpm_limit() if team_membership is not None else None - ), - team_member_tpm_limit=( - team_membership.safe_get_team_member_tpm_limit() if team_membership is not None else None - ), - team_metadata=(team_object.metadata if team_object is not None else None), jwt_claims=jwt_claims, - ) - valid_token.team_object_permission = ( - team_object.object_permission if team_object is not None else None + **team_grants(team_object=team_object, team_membership=team_membership, user_id=user_id), ) # AUTO_REGISTER deferred from _resolve_jwt_to_virtual_key. diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index 2ec68a10f65..c050368b3fe 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -5601,7 +5601,7 @@ async def team_model_add( updated_team: Final = await _team_db(prisma_client).update( where={"team_id": data.team_id}, data={"updated_at": datetime.now(timezone.utc)}, - include={"object_permission": True}, + include={"litellm_model_table": True, "object_permission": True}, ) if updated_team is None: raise HTTPException( @@ -5688,7 +5688,7 @@ async def team_model_delete( updated_team: Final = await _team_db(prisma_client).update( where={"team_id": data.team_id}, data={"models": updated_models}, - include={"object_permission": True}, + include={"litellm_model_table": True, "object_permission": True}, ) if updated_team is None: raise HTTPException( diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index 3e6434a5afd..c60888e298f 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -22,7 +22,6 @@ from html import escape from types import MappingProxyType from typing import ( TYPE_CHECKING, - Annotated, Any, Final, Literal, @@ -42,7 +41,7 @@ if TYPE_CHECKING: import jwt from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status from fastapi.responses import RedirectResponse -from pydantic import BaseModel, BeforeValidator, ConfigDict, TypeAdapter, ValidationError +from pydantic import BaseModel, TypeAdapter, ValidationError import litellm from litellm._logging import verbose_proxy_logger @@ -95,6 +94,7 @@ from litellm.proxy.auth.auth_utils import ( ) from litellm.proxy.auth.handle_jwt import JWTHandler from litellm.proxy.auth.ip_address_utils import IPAddressUtils +from litellm.proxy.auth.team_grants import TeamModelAliasTable from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.admin_ui_utils import ( admin_ui_disabled, @@ -209,31 +209,14 @@ def _team_detail_db(repo: TeamRepository) -> "TableActions[_TeamDetailRow]": return repo.table -_MODEL_ALIASES_ADAPTER: Final = TypeAdapter(dict[str, str]) _SSO_TOKEN_CLAIMS_ADAPTER: Final = TypeAdapter(Mapping[str, object]) -def _decode_model_aliases(value: object) -> object: - """``/team/new`` stores team model aliases as a JSON-encoded string in the Json column.""" - if not isinstance(value, str): - return value - try: - return _MODEL_ALIASES_ADAPTER.validate_json(value) - except ValidationError: - return None - - -class _TeamModelAliasTable(BaseModel): - model_config = ConfigDict(protected_namespaces=()) - - model_aliases: Annotated[Mapping[str, str] | None, BeforeValidator(_decode_model_aliases)] = None - - class _TeamRowGrants(BaseModel): team_id: str team_alias: str | None = None models: tuple[str, ...] = () - litellm_model_table: _TeamModelAliasTable | None = None + litellm_model_table: TeamModelAliasTable | None = None class CliSsoTeamDetail(BaseModel): diff --git a/tests/test_litellm/proxy/auth/test_auth_checks.py b/tests/test_litellm/proxy/auth/test_auth_checks.py index 2284a05b2e9..5bfef2b6445 100644 --- a/tests/test_litellm/proxy/auth/test_auth_checks.py +++ b/tests/test_litellm/proxy/auth/test_auth_checks.py @@ -2374,6 +2374,44 @@ def _mock_prisma_for_team_lookup(find_unique): return mock_prisma_client +_TEAM_ALIAS_TABLE_ROW = {"id": 1, "model_aliases": '{"fast": "gpt-4o"}', "created_by": "admin", "updated_by": "admin"} + + +def _prisma_team_row(include): + """Mimics Prisma: the `litellm_model_table` relation rides on the row only when the query `include`s it.""" + columns = {"team_id": "team-aliases", "team_alias": "aliases", "models": ["gpt-4o"]} + row = ( + {**columns, "litellm_model_table": _TEAM_ALIAS_TABLE_ROW} + if (include or {}).get("litellm_model_table") + else columns + ) + return SimpleNamespace(dict=lambda: row, model_dump=lambda: row) + + +@pytest.mark.asyncio +async def test_get_team_object_loads_model_aliases_relation(): + """LIT-5858: the auth path read teams without `include`ing `litellm_model_table`, so every JWT + team came back with `model_aliases=None` and alias requests 403'd.""" + from litellm.proxy.auth.auth_checks import get_team_object + from litellm.proxy.auth.team_grants import team_model_aliases + + async def find_unique(where, include=None): + return _prisma_team_row(include) + + mock_cache = MagicMock() + mock_cache.async_get_cache = AsyncMock(return_value=None) + mock_cache.async_set_cache = AsyncMock() + + team = await get_team_object( + team_id="team-aliases", + prisma_client=_mock_prisma_for_team_lookup(AsyncMock(side_effect=find_unique)), + user_api_key_cache=mock_cache, + check_db_only=True, + ) + + assert team_model_aliases(team) == {"fast": "gpt-4o"} + + @pytest.mark.asyncio async def test_get_team_object_distinguishes_absent_team_from_unreadable_row(): """A deleted team and a database that would not answer both surface as a 404, @@ -6195,6 +6233,32 @@ async def test_get_team_object_by_alias_db_fetch_returns_cached_obj(): assert result.models == ["gpt-4"] +@pytest.mark.asyncio +async def test_get_team_object_by_alias_loads_model_aliases_relation(): + """LIT-5858: same regression as `test_get_team_object_loads_model_aliases_relation`, for the + `team_alias_jwt_field` lookup.""" + from litellm.proxy.auth.auth_checks import get_team_object_by_alias + from litellm.proxy.auth.team_grants import team_model_aliases + + async def find_many(where, include=None): + return [_prisma_team_row(include)] + + mock_prisma_client = MagicMock() + mock_prisma_client.db.litellm_teamtable.find_many = AsyncMock(side_effect=find_many) + + mock_cache = MagicMock() + mock_cache.async_get_cache = AsyncMock(return_value=None) + mock_cache.async_set_cache = AsyncMock() + + team = await get_team_object_by_alias( + team_alias="aliases", + prisma_client=mock_prisma_client, + user_api_key_cache=mock_cache, + ) + + assert team_model_aliases(team) == {"fast": "gpt-4o"} + + @pytest.mark.asyncio async def test_get_org_object_by_alias_db_fetch_returns_validated_org(): from litellm.proxy._types import LiteLLM_OrganizationTable diff --git a/tests/test_litellm/proxy/auth/test_handle_jwt.py b/tests/test_litellm/proxy/auth/test_handle_jwt.py index 99a0a4c0a8b..94226b5404d 100644 --- a/tests/test_litellm/proxy/auth/test_handle_jwt.py +++ b/tests/test_litellm/proxy/auth/test_handle_jwt.py @@ -13,6 +13,7 @@ from litellm.proxy._types import ( DEFAULT_JWKS_STALE_TTL, JWTLiteLLMRoleMap, LiteLLM_JWTAuth, + LiteLLM_ModelTable, LiteLLM_TeamMembership, LiteLLM_TeamTable, LiteLLM_UserTable, @@ -1255,6 +1256,57 @@ async def test_find_team_with_model_access_model_group(monkeypatch): assert team_obj.team_id == "team-1" +@pytest.mark.asyncio +@pytest.mark.parametrize( + "model_aliases", + ['{"fast": "gpt-4o"}', {"fast": "gpt-4o"}], + ids=["json-string", "dict"], +) +async def test_find_team_with_model_access_resolves_team_model_alias(monkeypatch, model_aliases): + """LIT-5858: a JWT team that grants `gpt-4o` under the alias `fast` must resolve a request + for `fast`. The JWT path used to pass `team_model_aliases=None`, so every alias request 403'd.""" + import sys + import types + + from litellm.caching import DualCache + from litellm.proxy.utils import ProxyLogging + from litellm.router import Router + + router = Router(model_list=[{"model_name": "gpt-4o", "litellm_params": {"model": "gpt-4o"}}]) + proxy_server_module = types.ModuleType("proxy_server") + proxy_server_module.llm_router = router + monkeypatch.setitem(sys.modules, "litellm.proxy.proxy_server", proxy_server_module) + + team = LiteLLM_TeamTable( + team_id="team-aliases", + models=["gpt-4o"], + litellm_model_table=LiteLLM_ModelTable(model_aliases=model_aliases, created_by="admin", updated_by="admin"), + ) + + async def mock_get_team_object(*args, **kwargs): + return team + + monkeypatch.setattr("litellm.proxy.auth.handle_jwt.get_team_object", mock_get_team_object) + + jwt_handler = JWTHandler() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth() + user_api_key_cache = DualCache() + + team_id, team_obj = await JWTAuthManager.find_team_with_model_access( + team_ids={"team-aliases"}, + requested_model="fast", + route="/chat/completions", + jwt_handler=jwt_handler, + prisma_client=None, + user_api_key_cache=user_api_key_cache, + parent_otel_span=None, + proxy_logging_obj=ProxyLogging(user_api_key_cache=user_api_key_cache), + ) + + assert team_id == "team-aliases" + assert team_obj is team + + @pytest.mark.asyncio async def test_find_team_with_model_access_v1_messages_default_routes(monkeypatch): """Regression for #31189: a single-team JWT that grants the requested model diff --git a/tests/test_litellm/proxy/auth/test_team_grants.py b/tests/test_litellm/proxy/auth/test_team_grants.py new file mode 100644 index 00000000000..447fc1c93a1 --- /dev/null +++ b/tests/test_litellm/proxy/auth/test_team_grants.py @@ -0,0 +1,129 @@ +import pytest + +from litellm.proxy._types import ( + LiteLLM_BudgetTable, + LiteLLM_ObjectPermissionTable, + LiteLLM_TeamMembership, + LiteLLM_TeamTable, + LiteLLM_VerificationTokenView, + Member, + UserAPIKeyAuth, +) +from litellm.models.team import LiteLLM_ModelTable +from litellm.proxy.auth.team_grants import team_grants, team_model_aliases + +TEAM_ID = "team-grants" +USER_ID = "user-in-team" +ALIASES = {"fast": "gpt-4o-mini", "smart": "gpt-4o"} + + +def _alias_table(model_aliases) -> LiteLLM_ModelTable: + return LiteLLM_ModelTable(model_aliases=model_aliases, created_by="admin", updated_by="admin") + + +def _full_team(model_aliases=ALIASES) -> LiteLLM_TeamTable: + return LiteLLM_TeamTable( + team_id=TEAM_ID, + team_alias="grants-team", + tpm_limit=1000, + rpm_limit=10, + max_budget=50.0, + soft_budget=25.0, + spend=12.5, + models=["gpt-4o", "gpt-4o-mini"], + blocked=True, + metadata={"tier": "gold"}, + litellm_model_table=_alias_table(model_aliases), + object_permission_id="op-1", + object_permission=LiteLLM_ObjectPermissionTable(object_permission_id="op-1", mcp_servers=["mcp-a"]), + members_with_roles=[ + Member(user_id="someone-else", role="user"), + Member(user_id=USER_ID, role="admin"), + ], + ) + + +def _membership() -> LiteLLM_TeamMembership: + return LiteLLM_TeamMembership( + user_id=USER_ID, + team_id=TEAM_ID, + spend=3.25, + litellm_budget_table=LiteLLM_BudgetTable(tpm_limit=500, rpm_limit=5), + ) + + +def test_team_grants_cover_every_team_field_the_key_path_gets(): + """Class guard for LIT-5858 and its siblings: every ``team_*`` column the combined-view SQL hands the + virtual-key path must come out of the projection too, with the team's actual value, so adding a column + to ``LiteLLM_VerificationTokenView`` without teaching ``team_grants`` fails here instead of in prod.""" + team = _full_team() + grants = team_grants(team_object=team, team_membership=_membership(), user_id=USER_ID) + token = UserAPIKeyAuth(team_id=TEAM_ID, **grants) + + view_team_fields = {name for name in LiteLLM_VerificationTokenView.model_fields if name.startswith("team_")} + assert view_team_fields - {"team_id"} <= set(grants) + assert all(grants[name] is not None for name in view_team_fields - {"team_id"}) + + assert token.team_alias == "grants-team" + assert token.team_tpm_limit == 1000 + assert token.team_rpm_limit == 10 + assert token.team_max_budget == 50.0 + assert token.team_soft_budget == 25.0 + assert token.team_spend == 12.5 + assert token.team_models == ["gpt-4o", "gpt-4o-mini"] + assert token.team_blocked is True + assert token.team_metadata == {"tier": "gold"} + assert token.team_model_aliases == ALIASES + assert token.team_object_permission_id == "op-1" + assert token.team_object_permission is not None + assert token.team_object_permission.mcp_servers == ["mcp-a"] + assert token.team_member == Member(user_id=USER_ID, role="admin") + assert token.team_member_spend == 3.25 + assert token.team_member_tpm_limit == 500 + assert token.team_member_rpm_limit == 5 + + +def test_team_grants_without_team_leave_token_defaults(): + token = UserAPIKeyAuth(**team_grants(team_object=None, team_membership=None, user_id=USER_ID)) + assert token == UserAPIKeyAuth() + + +@pytest.mark.parametrize( + "stored_aliases", + [ALIASES, '{"fast": "gpt-4o-mini", "smart": "gpt-4o"}'], + ids=["json-object", "json-string-as-written-by-team-new"], +) +def test_team_model_aliases_decode_both_storage_shapes(stored_aliases): + team = _full_team(model_aliases=stored_aliases) + assert team_model_aliases(team) == ALIASES + assert team_grants(team_object=team, team_membership=None, user_id=None)["team_model_aliases"] == ALIASES + + +@pytest.mark.parametrize("stored_aliases", [None, "not json", '["a", "b"]', {"fast": 3}], ids=str) +def test_team_model_aliases_treat_unusable_column_as_no_aliases(stored_aliases): + team = _full_team(model_aliases=stored_aliases) + assert team_model_aliases(team) is None + assert team_grants(team_object=team, team_membership=None, user_id=None)["team_model_aliases"] is None + + +def test_team_model_aliases_none_without_relation_loaded(): + team = _full_team() + team.litellm_model_table = None + assert team_model_aliases(team) is None + assert team_model_aliases(None) is None + + +def test_team_member_is_the_callers_row_only(): + team = _full_team() + assert team_grants(team_object=team, team_membership=None, user_id="someone-else")["team_member"] == Member( + user_id="someone-else", role="user" + ) + assert team_grants(team_object=team, team_membership=None, user_id="stranger")["team_member"] is None + assert team_grants(team_object=team, team_membership=None, user_id=None)["team_member"] is None + + +def test_membership_limits_absent_without_membership_row(): + grants = team_grants(team_object=_full_team(), team_membership=None, user_id=USER_ID) + assert grants["team_member_spend"] is None + assert grants["team_member_tpm_limit"] is None + assert grants["team_member_rpm_limit"] is None diff --git a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py index d44f96d95bf..78ffbb0db23 100644 --- a/tests/test_litellm/proxy/auth/test_user_api_key_auth.py +++ b/tests/test_litellm/proxy/auth/test_user_api_key_auth.py @@ -6872,3 +6872,119 @@ class TestLitellmReceivedAtStamping: assert result == earlier assert request.state.litellm_received_at == earlier + + +@pytest.mark.asyncio +@pytest.mark.parametrize("is_proxy_admin", [False, True], ids=["standard-return", "proxy-admin-return"]) +async def test_jwt_builder_returns_every_team_grant_the_key_path_gets(is_proxy_admin): + """LIT-5858: the team-based JWT path hand-built ``UserAPIKeyAuth`` from a short list of team fields, so the + team's model aliases (and on the admin return, its object permission) never reached the token and alias + requests 403'd. Both returns now go through ``team_grants``; pin the fields that used to be dropped.""" + import litellm.proxy.proxy_server as _proxy_server_mod + from fastapi import Request + from starlette.datastructures import URL + + from litellm.models.team import LiteLLM_ModelTable + from litellm.proxy._types import ( + LiteLLM_ObjectPermissionTable, + LiteLLM_TeamMembership, + LiteLLM_TeamTable, + Member, + ) + + class _AcceptEveryJwt(JWTHandler): + def is_jwt(self, token: str) -> bool: + return True + + jwt_handler = _AcceptEveryJwt() + jwt_handler.litellm_jwtauth = LiteLLM_JWTAuth() + + team = LiteLLM_TeamTable( + team_id="team-jwt-aliases", + team_alias="jwt-aliases", + models=["gpt-4o"], + max_budget=40.0, + spend=4.0, + blocked=False, + metadata={"tier": "gold"}, + litellm_model_table=LiteLLM_ModelTable( + model_aliases='{"fast": "gpt-4o"}', created_by="admin", updated_by="admin" + ), + object_permission_id="op-jwt", + object_permission=LiteLLM_ObjectPermissionTable(object_permission_id="op-jwt", mcp_servers=["mcp-a"]), + members_with_roles=[Member(user_id="jwt-user", role="admin")], + ) + membership = LiteLLM_TeamMembership(user_id="jwt-user", team_id="team-jwt-aliases", spend=1.5) + builder_result = { + "is_proxy_admin": is_proxy_admin, + "team_object": team, + "user_object": None, + "end_user_object": None, + "org_object": None, + "token": "jwt", + "team_id": "team-jwt-aliases", + "user_id": "jwt-user", + "user_email": "jwt-user@example.com", + "end_user_id": None, + "org_id": None, + "team_membership": membership, + "jwt_claims": {"sub": "jwt-user"}, + } + + mock_proxy_logging_obj = MagicMock() + mock_proxy_logging_obj.internal_usage_cache = MagicMock() + mock_proxy_logging_obj.internal_usage_cache.dual_cache = AsyncMock() + mock_proxy_logging_obj.post_call_failure_hook = AsyncMock(return_value=None) + attrs = { + "prisma_client": MagicMock(), + "user_api_key_cache": DualCache(), + "proxy_logging_obj": mock_proxy_logging_obj, + "master_key": "sk-master-key", + "general_settings": {"enable_jwt_auth": True}, + "llm_model_list": [], + "llm_router": None, + "open_telemetry_logger": None, + "model_max_budget_limiter": MagicMock(), + "user_custom_auth": None, + "jwt_handler": jwt_handler, + "premium_user": True, + "litellm_proxy_admin_name": "admin", + } + originals = {a: getattr(_proxy_server_mod, a, None) for a in attrs} + try: + for k, v in attrs.items(): + setattr(_proxy_server_mod, k, v) + request = Request(scope={"type": "http", "headers": [], "method": "POST"}) + request._url = URL(url="/chat/completions") + with patch( # test-quality-ok: auth_builder is the claim-resolution seam; the regression is how its result is projected onto the token + "litellm.proxy.auth.user_api_key_auth.JWTAuthManager.auth_builder", + new_callable=AsyncMock, + return_value=builder_result, + ): + token = await _user_api_key_auth_builder( + request=request, + api_key="Bearer header.payload.signature", + azure_api_key_header="", + anthropic_api_key_header=None, + google_ai_studio_api_key_header=None, + azure_apim_header=None, + request_data={}, + ) + finally: + for k, v in originals.items(): + setattr(_proxy_server_mod, k, v) + + assert token.team_id == "team-jwt-aliases" + assert token.user_role == (LitellmUserRoles.PROXY_ADMIN if is_proxy_admin else LitellmUserRoles.INTERNAL_USER) + assert token.team_model_aliases == {"fast": "gpt-4o"} + assert token.team_object_permission is not None + assert token.team_object_permission.mcp_servers == ["mcp-a"] + assert token.team_object_permission_id == "op-jwt" + assert token.team_alias == "jwt-aliases" + assert token.team_models == ["gpt-4o"] + assert token.team_max_budget == 40.0 + assert token.team_spend == 4.0 + assert token.team_metadata == {"tier": "gold"} + assert token.team_member == Member(user_id="jwt-user", role="admin") + assert token.team_member_spend == 1.5 + assert token.jwt_claims == {"sub": "jwt-user"} diff --git a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py index 051e6bed4fd..2f6561046b1 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py @@ -2205,6 +2205,50 @@ async def test_team_model_add_delete_refresh_team_cache(endpoint_name): assert update_call_kwargs.get("include", {}).get("object_permission") is True +@pytest.mark.asyncio +@pytest.mark.parametrize("endpoint_name", ["team_model_add", "team_model_delete"]) +async def test_team_model_add_delete_keep_model_aliases_in_team_cache(endpoint_name, monkeypatch): + """LIT-5858: Prisma only returns `litellm_model_table` when the `update` asks for it, so the refreshed + cache entry lost the team's model aliases and JWT alias requests 403'd until the next DB read.""" + from litellm.proxy._types import TeamModelAddRequest, TeamModelDeleteRequest + from litellm.proxy.auth.team_grants import team_model_aliases + from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache + from litellm.proxy.management_endpoints.team_endpoints import team_model_add, team_model_delete + + columns = {"team_id": "team-1234", "models": ["gpt-4o", "openai/*"]} + alias_table = {"id": 1, "model_aliases": '{"fast": "gpt-4o"}', "created_by": "admin", "updated_by": "admin"} + + async def update(where, data, include=None): + row = {**columns, "litellm_model_table": alias_table} if (include or {}).get("litellm_model_table") else columns + return SimpleNamespace(team_id="team-1234", model_dump=lambda: row) + + prisma_client = MagicMock() + prisma_client.db.litellm_teamtable.find_unique = AsyncMock(return_value=SimpleNamespace(model_dump=lambda: columns)) + prisma_client.db.litellm_teamtable.update = AsyncMock(side_effect=update) + prisma_client.db.execute_raw = AsyncMock(return_value=None) + cache = UserApiKeyCache() + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", prisma_client) + monkeypatch.setattr("litellm.proxy.proxy_server.user_api_key_cache", cache) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", None) + + admin = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin") + if endpoint_name == "team_model_add": + await team_model_add( + data=TeamModelAddRequest(team_id="team-1234", models=["team-byok-1"]), + http_request=MagicMock(), + user_api_key_dict=admin, + ) + else: + await team_model_delete( + data=TeamModelDeleteRequest(team_id="team-1234", models=["openai/*"]), + http_request=MagicMock(), + user_api_key_dict=admin, + ) + + cached_team = await cache.async_get_cache(key="team_id:team-1234", model_type=LiteLLM_TeamTableCachedObj) + assert team_model_aliases(cached_team) == {"fast": "gpt-4o"} + + @pytest.mark.asyncio @pytest.mark.parametrize( "endpoint_name", diff --git a/type-discipline-budget.json b/type-discipline-budget.json index e7186dfe186..7db8ed501f8 100644 --- a/type-discipline-budget.json +++ b/type-discipline-budget.json @@ -3,7 +3,7 @@ "limit": 22180 }, "LIT002": { - "limit": 26729 + "limit": 26727 }, "LIT003": { "limit": 261 From 16fd14f53705dab68ee8c4b0fd2c9093c8e3cdac Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Sat, 5 Sep 2026 18:03:24 -0700 Subject: [PATCH 040/223] fix(docker): ship pymongo in the proxy images for the MongoDB vector store The MongoDB Atlas vector store provider imports pymongo lazily from the opt-in `mongodb` extra, but none of the shipped images installed that extra. Any image-based deployment that configured a MongoDB vector store failed at search time with "requires the 'pymongo' package", which the user cannot fix without extending the image Adds `--extra mongodb` to every uv sync in the root Dockerfile, Dockerfile.database, Dockerfile.non_root, and the gateway component image. The backend component does not serve /vector_stores so it is left as is. The extra resolves from the existing uv.lock to pymongo 4.17.0 plus dnspython 2.8.0, no lock change needed --- Dockerfile | 2 ++ docker/Dockerfile.database | 2 ++ docker/Dockerfile.non_root | 3 +++ gateway/Dockerfile | 2 ++ 4 files changed, 9 insertions(+) diff --git a/Dockerfile b/Dockerfile index 0a92aa9a68c..1648ec69d13 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,6 +67,7 @@ RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-gr --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 # Copy full source tree @@ -89,6 +90,7 @@ RUN uv sync --frozen --no-default-groups --no-editable \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index e9ad2849bb2..cc81ad6b3d3 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -65,6 +65,7 @@ RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-gr --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 # Copy full source tree @@ -87,6 +88,7 @@ RUN uv sync --frozen --no-default-groups --no-editable \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index edf20e8bbff..358425af901 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -71,6 +71,7 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 # Copy full source tree @@ -99,6 +100,7 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 \ --no-sources-package litellm-proxy-extras; \ else \ @@ -109,6 +111,7 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13; \ fi diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 308d70a6b26..e42e488d57f 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -47,6 +47,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ --extra extra_proxy \ --extra semantic-router \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 # Stage 2 — copy source and install the project + workspace members. @@ -59,6 +60,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ --extra extra_proxy \ --extra semantic-router \ --extra bedrock-realtime \ + --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ From 8576cf74c2ef4eaa15f48eaaf99447f9d6fb394d Mon Sep 17 00:00:00 2001 From: Oliver Jensen Date: Mon, 7 Sep 2026 14:09:37 +0200 Subject: [PATCH 041/223] feat(auth): add disable_env_credential_login setting with admin ui warning Env-credential login (UI_USERNAME/UI_PASSWORD, or the master key when UI_PASSWORD is unset) is always live today. This adds a general_settings flag to turn that login path off once real admin accounts exist, and a warning banner shown to any admin while it remains enabled. The banner flag is served through /health/readiness/details and stays quiet when disable_password_login_when_sso_enabled already makes the env path unreachable. --- litellm/proxy/_types.py | 12 ++ litellm/proxy/auth/login_utils.py | 32 +++- .../health_endpoints/_health_endpoints.py | 20 +++ .../proxy/auth/test_login_utils.py | 154 ++++++++++++++++++ .../health_endpoints/test_health_endpoints.py | 27 +++ .../useHealthReadinessDetails.ts | 1 + .../src/app/(dashboard)/layout.test.tsx | 4 + .../src/app/(dashboard)/layout.tsx | 3 + .../EnvCredentialLoginWarningBanner.test.tsx | 76 +++++++++ .../EnvCredentialLoginWarningBanner.tsx | 35 ++++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 5 + 11 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.test.tsx create mode 100644 ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.tsx diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index c28ac8848ba..c30a3c5bea8 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -2817,6 +2817,18 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase): "UI username/password login. Default is False." ), ) + disable_env_credential_login: bool | None = Field( + None, + description=( + "If True, disables signing in to the Admin UI with the environment credentials: " + "UI_USERNAME/UI_PASSWORD, or the master key when UI_PASSWORD is unset (that fallback " + "means env-credential login is always live by default). Database users with passwords " + "are unaffected. LOCKOUT RISK: create at least one proxy admin user with a password " + "before enabling, or nobody can sign in to the UI. A locked-out admin can still " + "administer the proxy over the API with the master key, and can unset this setting " + "and restart the proxy to restore env-credential login. Default is False." + ), + ) disable_budget_reservation: bool | None = Field( None, description=( diff --git a/litellm/proxy/auth/login_utils.py b/litellm/proxy/auth/login_utils.py index 8d4f6f81363..6ca7ea5074d 100644 --- a/litellm/proxy/auth/login_utils.py +++ b/litellm/proxy/auth/login_utils.py @@ -85,6 +85,29 @@ def get_ui_credentials(master_key: str | None) -> tuple[str, str]: return ui_username, ui_password +def _matches_env_credentials(username: str, password: str, master_key: str | None) -> bool: + ui_username, ui_password = get_ui_credentials(master_key) + return secrets.compare_digest(username.encode("utf-8"), ui_username.encode("utf-8")) and secrets.compare_digest( + password.encode("utf-8"), ui_password.encode("utf-8") + ) + + +def is_env_credential_login_enabled(general_settings: Mapping[str, object]) -> bool: + """Whether a login with UI_USERNAME/UI_PASSWORD (or the master-key fallback) can succeed. + + Two settings can turn it off: `disable_env_credential_login` unconditionally, and + `disable_password_login_when_sso_enabled` as a side effect, since its gate rejects + every username/password login before the env comparison runs. Feeds both the + `authenticate_user` gate and the Admin UI warning banner, so the banner never nags + about a login path that is already unreachable. + """ + if general_settings.get("disable_env_credential_login") is True: + return False + if general_settings.get("disable_password_login_when_sso_enabled") is True and is_sso_provider_fully_configured(): + return False + return True + + class LoginResult: """Result object containing authentication data from login.""" @@ -129,7 +152,8 @@ async def authenticate_user( master_key: Master key for the proxy (required) prisma_client: Prisma database client (optional) general_settings: Proxy general_settings, checked for - `disable_password_login_when_sso_enabled` + `disable_password_login_when_sso_enabled` and + `disable_env_credential_login` Returns: LoginResult: Object containing authentication data @@ -170,8 +194,6 @@ async def authenticate_user( code=500, ) - ui_username, ui_password = get_ui_credentials(master_key) - # Check if we can find the `username` in the db. On the UI, users can enter username=their email _user_row: LiteLLM_UserTable | None = None user_role: ( @@ -197,8 +219,8 @@ async def authenticate_user( - Login with UI_USERNAME and UI_PASSWORD - Login with Invite Link `user_email` and `password` combination """ - if secrets.compare_digest(username.encode("utf-8"), ui_username.encode("utf-8")) and secrets.compare_digest( - password.encode("utf-8"), ui_password.encode("utf-8") + if general_settings.get("disable_env_credential_login") is not True and _matches_env_credentials( + username, password, master_key ): # Non SSO -> If user is using UI_USERNAME and UI_PASSWORD they are Proxy admin user_role = LitellmUserRoles.PROXY_ADMIN diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 1785a2f0992..e5e4f89234b 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -1582,6 +1582,23 @@ async def _show_no_redis_warning() -> bool: return await count_live_proxy_workers(prisma_client) != 1 +def _show_env_credential_login_warning() -> bool: + """ + Whether the UI should warn admins that env-credential login is still enabled. + + UI_USERNAME/UI_PASSWORD (or the master key, when UI_PASSWORD is unset) grant + proxy-admin access with a shared static secret: no per-person identity, no + audit trail, no password policy, and it stays valid until the env var or + master key rotates. That is fine for first-time setup, so it is on by + default, but once real admin accounts exist it should be turned off with + `general_settings.disable_env_credential_login`. + """ + from litellm.proxy.auth.login_utils import is_env_credential_login_enabled + from litellm.proxy.proxy_server import general_settings + + return is_env_credential_login_enabled(general_settings) + + async def _get_health_readiness_details( response: Response | None = None, ) -> dict[str, Any]: @@ -1623,6 +1640,7 @@ async def _get_health_readiness_details( log_level_name: Final = logging.getLevelName(verbose_logger.getEffectiveLevel()) is_detailed_debug: Final = verbose_logger.isEnabledFor(logging.DEBUG) show_no_redis_warning: Final = await _show_no_redis_warning() + show_env_credential_login_warning: Final = _show_env_credential_login_warning() # check DB if prisma_client is not None: # if db passed in, check if it's connected @@ -1650,6 +1668,7 @@ async def _get_health_readiness_details( "log_level": log_level_name, "is_detailed_debug": is_detailed_debug, "show_no_redis_warning": show_no_redis_warning, + "show_env_credential_login_warning": show_env_credential_login_warning, } else: return { @@ -1662,6 +1681,7 @@ async def _get_health_readiness_details( "log_level": log_level_name, "is_detailed_debug": is_detailed_debug, "show_no_redis_warning": show_no_redis_warning, + "show_env_credential_login_warning": show_env_credential_login_warning, } except Exception as e: raise HTTPException(status_code=503, detail=f"Service Unhealthy ({e})") diff --git a/tests/test_litellm/proxy/auth/test_login_utils.py b/tests/test_litellm/proxy/auth/test_login_utils.py index 8d93d801bfd..72cc6e04a12 100644 --- a/tests/test_litellm/proxy/auth/test_login_utils.py +++ b/tests/test_litellm/proxy/auth/test_login_utils.py @@ -23,6 +23,7 @@ from litellm.proxy.auth.login_utils import ( LoginResult, authenticate_user, get_ui_credentials, + is_env_credential_login_enabled, ) @@ -799,3 +800,156 @@ class TestDisablePasswordLoginWhenSSOEnabled: assert isinstance(result, LoginResult) assert result.user_id == LITELLM_PROXY_ADMIN_NAME + + +class TestDisableEnvCredentialLogin: + """`disable_env_credential_login` must reject a login with the env + credentials (UI_USERNAME/UI_PASSWORD, or the master-key fallback when + UI_PASSWORD is unset) while leaving database-user password logins + untouched, so admins with real accounts keep a way in.""" + + @pytest.mark.asyncio + async def test_rejects_correct_env_credentials_when_disabled(self): + master_key = "sk-1234" + ui_username = "admin" + ui_password = "env-only-password" + + mock_prisma_client = MagicMock() + mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=None) + + with patch.dict(os.environ, {"UI_USERNAME": ui_username, "UI_PASSWORD": ui_password}): + with pytest.raises(ProxyException) as exc_info: + await authenticate_user( + username=ui_username, + password=ui_password, + master_key=master_key, + prisma_client=mock_prisma_client, + general_settings={"disable_env_credential_login": True}, + ) + + assert exc_info.value.type == ProxyErrorTypes.auth_error + assert exc_info.value.code == "401" + + @pytest.mark.asyncio + async def test_rejects_master_key_fallback_when_disabled(self): + """With UI_PASSWORD unset, the master key IS the env password, so the + setting must reject it too or it protects nothing by default.""" + master_key = "sk-1234" + + mock_prisma_client = MagicMock() + mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=None) + + with patch.dict(os.environ, {"UI_USERNAME": "admin"}, clear=True): + with pytest.raises(ProxyException) as exc_info: + await authenticate_user( + username="admin", + password=master_key, + master_key=master_key, + prisma_client=mock_prisma_client, + general_settings={"disable_env_credential_login": True}, + ) + + assert exc_info.value.code == "401" + + @pytest.mark.asyncio + async def test_db_user_login_still_works_when_disabled(self): + master_key = "sk-1234" + user_email = "admin@example.com" + password = "Str0ng!Passw0rd" + + mock_user = LiteLLM_UserTable( + user_id="db-admin-1", + user_email=user_email, + password=hash_token(token=password), + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + mock_prisma_client = MagicMock() + mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=mock_user) + + with patch.dict( + os.environ, + { + "UI_USERNAME": "admin", + "UI_PASSWORD": "env-password", + "DATABASE_URL": "postgresql://test:test@localhost/test", + }, + clear=True, + ): + with ExitStack() as stack: + stack.enter_context( + patch( # test-quality-ok: internal orchestration, no HTTP boundary; matches pre-existing tests + "litellm.proxy.auth.login_utils.generate_key_helper_fn", + new_callable=AsyncMock, + return_value={"token": "db-user-token"}, + ) + ) + result = await authenticate_user( + username=user_email, + password=password, + master_key=master_key, + prisma_client=mock_prisma_client, + general_settings={"disable_env_credential_login": True}, + ) + + assert isinstance(result, LoginResult) + assert result.user_id == "db-admin-1" + assert result.user_role == LitellmUserRoles.PROXY_ADMIN + + @pytest.mark.asyncio + async def test_env_login_still_works_when_setting_absent(self): + """Env-credential login is the bootstrap path on a fresh install and + must stay on by default.""" + master_key = "sk-1234" + ui_username = "admin" + + mock_prisma_client = MagicMock() + mock_prisma_client.db.litellm_usertable.find_first = AsyncMock(return_value=None) + + with patch.dict( + os.environ, + { + "UI_USERNAME": ui_username, + "UI_PASSWORD": master_key, + "DATABASE_URL": "postgresql://test:test@localhost/test", + }, + clear=True, + ): + with ExitStack() as stack: + _patch_successful_admin_login_deps(stack) + result = await authenticate_user( + username=ui_username, + password=master_key, + master_key=master_key, + prisma_client=mock_prisma_client, + general_settings={}, + ) + + assert isinstance(result, LoginResult) + assert result.user_id == LITELLM_PROXY_ADMIN_NAME + + +class TestIsEnvCredentialLoginEnabled: + """Drives the Admin UI warning banner: it must be True exactly when a + login with the env credentials could actually succeed.""" + + def test_enabled_by_default(self): + assert is_env_credential_login_enabled({}) is True + + def test_disabled_by_dedicated_setting(self): + assert is_env_credential_login_enabled({"disable_env_credential_login": True}) is False + + def test_explicit_false_keeps_it_enabled(self): + assert is_env_credential_login_enabled({"disable_env_credential_login": False}) is True + + def test_disabled_when_sso_gate_blocks_all_password_logins(self): + """`disable_password_login_when_sso_enabled` with SSO configured + rejects every username/password login before the env comparison runs, + so the banner must not nag about an already-unreachable path.""" + with ExitStack() as stack: + _patch_sso_configured(stack, configured=True) + assert is_env_credential_login_enabled({"disable_password_login_when_sso_enabled": True}) is False + + def test_enabled_when_sso_gate_is_set_but_sso_not_configured(self): + with ExitStack() as stack: + _patch_sso_configured(stack, configured=False) + assert is_env_credential_login_enabled({"disable_password_login_when_sso_enabled": True}) is True diff --git a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py index e9e58347337..b06d87ac67e 100644 --- a/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py +++ b/tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py @@ -1301,6 +1301,33 @@ def test_health_readiness_details_returns_diagnostic_fields(monkeypatch): assert "cache" in response_data +@pytest.mark.parametrize( + "general_settings, expected_warning", + [ + ({}, True), + ({"disable_env_credential_login": True}, False), + ], +) +def test_health_readiness_details_reports_env_credential_login_warning(monkeypatch, general_settings, expected_warning): + """ + The Admin UI banner is driven by this flag: it must be True while + env-credential login is possible and False once + `disable_env_credential_login` turns that login path off. + """ + app = FastAPI() + app.include_router(_health_endpoints_module.router) + app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN) + client = TestClient(app) + + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", general_settings) + + response = client.get("/health/readiness/details") + + assert response.status_code == 200, response.text + assert response.json()["show_env_credential_login_warning"] is expected_warning + + def test_health_readiness_allows_explicit_legacy_public_details(monkeypatch): """ Operators can explicitly preserve the legacy public readiness payload. diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails.ts index 307fa9e1691..44d9092df34 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails.ts @@ -14,6 +14,7 @@ export interface HealthReadinessDetailsResponse { log_level?: string; is_detailed_debug?: boolean; show_no_redis_warning?: boolean; + show_env_credential_login_warning?: boolean; } const fetchHealthReadinessDetails = async (accessToken: string): Promise => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx index 7f1f4cc4bd5..3fe34610260 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.test.tsx @@ -29,6 +29,10 @@ vi.mock("@/components/NoRedisWarningBanner", () => ({ NoRedisWarningBanner: () => null, })); +vi.mock("@/components/EnvCredentialLoginWarningBanner", () => ({ + EnvCredentialLoginWarningBanner: () => null, +})); + vi.mock("@/components/LicenseExpiryBanner", () => ({ LicenseExpiryBanner: () => null, })); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx index 98f2a36d6f3..fa6df7f176a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx @@ -10,6 +10,7 @@ import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider"; import { useRouter, useSearchParams } from "next/navigation"; import { DebugWarningBanner } from "@/components/DebugWarningBanner"; import { NoRedisWarningBanner } from "@/components/NoRedisWarningBanner"; +import { EnvCredentialLoginWarningBanner } from "@/components/EnvCredentialLoginWarningBanner"; import { LicenseExpiryBanner } from "@/components/LicenseExpiryBanner"; import { UserBanner } from "@/components/UserBanner"; import { uiHref } from "@/utils/uiHref"; @@ -113,6 +114,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) { +
@@ -132,6 +134,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) { +
{children}
diff --git a/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.test.tsx b/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.test.tsx new file mode 100644 index 00000000000..535694f14da --- /dev/null +++ b/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.test.tsx @@ -0,0 +1,76 @@ +import { renderWithProviders, screen } from "../../tests/test-utils"; +import { vi } from "vitest"; +import { EnvCredentialLoginWarningBanner } from "./EnvCredentialLoginWarningBanner"; +import type { HealthReadinessDetailsResponse } from "@/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails"; +import type { UseQueryResult } from "@tanstack/react-query"; + +vi.mock("@/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails", () => ({ + useHealthReadinessDetails: vi.fn(), +})); +vi.mock("@/contexts/AuthContext", () => ({ + useAuth: vi.fn(), +})); + +import { useHealthReadinessDetails } from "@/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails"; +import { useAuth } from "@/contexts/AuthContext"; + +const mockDetails = (data: Partial | undefined) => { + vi.mocked(useHealthReadinessDetails).mockReturnValue({ data } as UseQueryResult); +}; + +const mockRole = (userRole: string) => { + vi.mocked(useAuth).mockReturnValue({ userRole } as ReturnType); +}; + +describe("EnvCredentialLoginWarningBanner", () => { + it("should warn an admin when env-credential login is enabled", () => { + mockRole("Admin"); + mockDetails({ status: "healthy", show_env_credential_login_warning: true }); + renderWithProviders(); + expect(screen.getByRole("alert")).toBeInTheDocument(); + expect(screen.getByText("Environment-credential login is enabled")).toBeInTheDocument(); + }); + + it("should tell the admin to create a regular admin account before disabling", () => { + mockRole("Admin"); + mockDetails({ status: "healthy", show_env_credential_login_warning: true }); + renderWithProviders(); + expect(screen.getByText(/First create a regular admin account/i)).toBeInTheDocument(); + expect(screen.getByText("general_settings.disable_env_credential_login: true")).toBeInTheDocument(); + }); + + it("should warn an admin viewer too", () => { + mockRole("Admin Viewer"); + mockDetails({ status: "healthy", show_env_credential_login_warning: true }); + renderWithProviders(); + expect(screen.getByRole("alert")).toBeInTheDocument(); + }); + + it("should render nothing for a non-admin even when the proxy reports the warning", () => { + mockRole("Internal User"); + mockDetails({ status: "healthy", show_env_credential_login_warning: true }); + const { container } = renderWithProviders(); + expect(container).toBeEmptyDOMElement(); + }); + + it("should render nothing when env-credential login is disabled", () => { + mockRole("Admin"); + mockDetails({ status: "healthy", show_env_credential_login_warning: false }); + const { container } = renderWithProviders(); + expect(container).toBeEmptyDOMElement(); + }); + + it("should render nothing when readiness details are unavailable", () => { + mockRole("Admin"); + mockDetails(undefined); + const { container } = renderWithProviders(); + expect(container).toBeEmptyDOMElement(); + }); + + it("should pass the access token to the readiness hook", () => { + mockRole("Admin"); + mockDetails(undefined); + renderWithProviders(); + expect(useHealthReadinessDetails).toHaveBeenCalledWith("my-token"); + }); +}); diff --git a/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.tsx b/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.tsx new file mode 100644 index 00000000000..3a9d50011f1 --- /dev/null +++ b/ui/litellm-dashboard/src/components/EnvCredentialLoginWarningBanner.tsx @@ -0,0 +1,35 @@ +"use client"; + +import React from "react"; +import { TriangleAlert } from "lucide-react"; +import { useHealthReadinessDetails } from "@/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails"; +import { useAuth } from "@/contexts/AuthContext"; +import { isAdminRole } from "@/utils/roles"; + +export const EnvCredentialLoginWarningBanner: React.FC<{ accessToken: string | null }> = ({ accessToken }) => { + const { userRole } = useAuth(); + const { data: healthData } = useHealthReadinessDetails(accessToken); + + if (!isAdminRole(userRole) || !healthData?.show_env_credential_login_warning) { + return null; + } + + return ( +
+
+ ); +}; diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index b1534c19670..dae0af3d3b1 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -25778,6 +25778,11 @@ export interface components { * @description If True, disables the optimistic per-request budget reservation introduced in v1.84.0. WARNING: This weakens hard budget enforcement. Without the reservation, a burst of concurrent requests from a single key can each pass the read-time spend check before any of them is charged, allowing a configured budget to be exceeded under high concurrency. Budgets are still evaluated on every request at read time, so an already-exhausted budget is still rejected. Enable only if your deployment is experiencing phantom BudgetExceededError responses caused by leaked reservations (see GitHub issue #27639). A proxy-level WARNING is logged on every request while this flag is active as a reminder that hard enforcement is relaxed. */ disable_budget_reservation?: boolean | null; + /** + * Disable Env Credential Login + * @description If True, disables signing in to the Admin UI with the environment credentials: UI_USERNAME/UI_PASSWORD, or the master key when UI_PASSWORD is unset (that fallback means env-credential login is always live by default). Database users with passwords are unaffected. LOCKOUT RISK: create at least one proxy admin user with a password before enabling, or nobody can sign in to the UI. A locked-out admin can still administer the proxy over the API with the master key, and can unset this setting and restart the proxy to restore env-credential login. Default is False. + */ + disable_env_credential_login?: boolean | null; /** * Disable Password Login When Sso Enabled * @description If True and SSO is configured (MICROSOFT_CLIENT_ID, GOOGLE_CLIENT_ID, GENERIC_CLIENT_ID, or SAML_IDP_METADATA_URL/XML), disables username/password login on /login, /v2/login, and /v3/login so SSO is the only way to reach the Admin UI. An admin locked out of the UI can still administer the proxy over the API with the master key; unset this setting and restart the proxy to restore UI username/password login. Default is False. From 6c21be619441dbd24879e1f8a4b897c6dbd667fe Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 12:15:06 -0700 Subject: [PATCH 042/223] fix(e2e): clean up batch files reliably and expire Azure inputs --- tests/e2e/batches/COVERAGE.md | 18 ++ tests/e2e/batches/batch_cleanup.py | 90 +++++++++ tests/e2e/batches/batch_client.py | 16 +- tests/e2e/batches/capabilities.py | 4 + tests/e2e/batches/conftest.py | 10 +- tests/e2e/batches/test_batch_cleanup.py | 187 ++++++++++++++++++ tests/e2e/batches/test_batches_e2e.py | 80 ++++---- .../test_managed_files_enforcement_e2e.py | 3 +- tests/e2e/lifecycle.py | 23 ++- 9 files changed, 381 insertions(+), 50 deletions(-) create mode 100644 tests/e2e/batches/batch_cleanup.py create mode 100644 tests/e2e/batches/test_batch_cleanup.py diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md index 8a7b68511ec..899530dde2b 100644 --- a/tests/e2e/batches/COVERAGE.md +++ b/tests/e2e/batches/COVERAGE.md @@ -120,6 +120,24 @@ create traverse gateway -> gateway -> OpenAI (LIT-5347, PR #36240). The pin: nested managed ids round-trip retrieve. This self-chaining only needs the proxy to reach its own `PROXY_BASE_URL`, which holds both locally and on the e2e stage. +## Cleanup + +Batch teardown cancels active batches before deleting their input files and keys. +Raw file IDs from both `model_param` and `provider_fallback` uploads use the upload +provider when deleted. Model-encoded and managed file IDs route themselves + +File deletion and batch cancellation check their responses and retry transient +failures up to three times. Teardown attempts every registered cleanup before +reporting failures as test errors. Already deleted files and batches that are +terminal are safe to clean up again. Cancellation polls for up to ten minutes +before input deletion, because accepting cancellation does not finish it + +Azure input uploads request `expires_after` anchored to `created_at` with +`seconds=1209600`, and the lifecycle tests check the returned expiry. This is a +fallback for interrupted runs: immediate deletion remains the normal cleanup. +Azure's minimum supported native expiry is 14 days, so a three-day expiry cannot +be requested through its Files API + ## Terminal state + cost write-back (cross-run marker baton) The 24h completion window rules out submit-and-wait inside one run, so diff --git a/tests/e2e/batches/batch_cleanup.py b/tests/e2e/batches/batch_cleanup.py new file mode 100644 index 00000000000..a5b5e9bba37 --- /dev/null +++ b/tests/e2e/batches/batch_cleanup.py @@ -0,0 +1,90 @@ +from collections.abc import Callable +from time import monotonic, sleep +from typing import Final, Protocol + +from pydantic import BaseModel + +from batch_client import BatchObject, FileDeleteResponse +from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApiError + +CLEANUP_DELAYS: Final = (1.0, 2.0, 4.0) +BATCH_TERMINAL_STATUSES: Final = frozenset({"completed", "failed", "expired", "cancelled"}) +BATCH_CANCEL_TIMEOUT_SECONDS: Final = 600.0 +BATCH_CANCEL_POLL_SECONDS: Final = 10.0 + + +class BatchCleanupClient(Protocol): + def delete_file(self, file_id: str, *, key: str, provider: str | None = None) -> Result[FileDeleteResponse]: ... + + def retrieve_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: ... + + def cancel_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: ... + + +def cleanup_result[R: BaseModel]( + action: Callable[[], Result[R]], *, wait: Callable[[float], None] = sleep +) -> Result[R]: + for delay, result in ((delay, action()) for delay in CLEANUP_DELAYS): + match result: + case NetworkError() | RateLimitedError(): + wait(delay) + case UnknownApiError(status_code=code) if code in {408, 429, 500, 502, 503, 504}: + wait(delay) + case _: + return result + return action() + + +def _require_cleanup_success[R: BaseModel](result: Result[R], operation: str) -> R: + match result: + case Success(data=data): + return data + case UnknownApiError(status_code=code): + raise AssertionError(f"{operation} failed: HTTP {code}") + case _: + raise AssertionError(f"{operation} failed: {result.kind}") + + +def cleanup_file(client: BatchCleanupClient, file_id: str, *, key: str, provider: str | None = None) -> None: + result: Final = cleanup_result(lambda: client.delete_file(file_id, key=key, provider=provider)) + if isinstance(result, UnknownApiError) and result.status_code == 404: + return + deleted: Final = _require_cleanup_success(result, f"Delete file {file_id}") + assert deleted.deleted, f"Delete file {file_id} did not confirm deletion" + + +def cleanup_batch( + client: BatchCleanupClient, + batch_id: str, + *, + key: str, + provider: str | None = None, + wait: Callable[[float], None] = sleep, + clock: Callable[[], float] = monotonic, +) -> None: + fetched: Final = _require_cleanup_success( + cleanup_result(lambda: client.retrieve_batch(batch_id, key=key, provider=provider)), + f"Retrieve batch {batch_id} for cleanup", + ) + if fetched.status in BATCH_TERMINAL_STATUSES: + return + if fetched.status != "cancelling": + result: Final = cleanup_result(lambda: client.cancel_batch(batch_id, key=key, provider=provider)) + if not (isinstance(result, UnknownApiError) and result.status_code in {400, 409}): + cancelled: Final = _require_cleanup_success(result, f"Cancel batch {batch_id}") + assert cancelled.status in BATCH_TERMINAL_STATUSES | {"cancelling"}, ( + f"Cancel batch {batch_id} left status {cancelled.status}" + ) + deadline: Final = clock() + BATCH_CANCEL_TIMEOUT_SECONDS + while True: + current = _require_cleanup_success( + cleanup_result(lambda: client.retrieve_batch(batch_id, key=key, provider=provider)), + f"Retrieve batch {batch_id} after cancellation", + ) + if current.status in BATCH_TERMINAL_STATUSES: + return + assert current.status == "cancelling", f"Cancel batch {batch_id} left status {current.status}" + assert clock() < deadline, ( + f"Batch {batch_id} cancellation did not finish within {BATCH_CANCEL_TIMEOUT_SECONDS}s" + ) + wait(BATCH_CANCEL_POLL_SECONDS) diff --git a/tests/e2e/batches/batch_client.py b/tests/e2e/batches/batch_client.py index 31e49f22450..84a902b0b11 100644 --- a/tests/e2e/batches/batch_client.py +++ b/tests/e2e/batches/batch_client.py @@ -13,8 +13,9 @@ co-located here because only this suite uses them. from __future__ import annotations from dataclasses import dataclass +from typing import Final, Literal -from pydantic import BaseModel +from pydantic import BaseModel, Field from proxy_client import ProxyClient from e2e_http import ( @@ -27,6 +28,18 @@ from e2e_http import ( from models import LiteLLMParamsBody UPLOAD_FILENAME = "batch_input.jsonl" +AZURE_FILE_EXPIRY_SECONDS: Final = 14 * 24 * 60 * 60 + + +class ExpiringFileUploadForm(FileUploadForm): + expires_after_anchor: Literal["created_at"] = Field(default="created_at", alias="expires_after[anchor]") + expires_after_seconds: int = Field(default=AZURE_FILE_EXPIRY_SECONDS, alias="expires_after[seconds]") + + +def batch_upload_form(provider: str, *, target_model_names: str | None = None) -> FileUploadForm: + if provider == "azure": + return ExpiringFileUploadForm(target_model_names=target_model_names) + return FileUploadForm(target_model_names=target_model_names) class FileObject(BaseModel): @@ -37,6 +50,7 @@ class FileObject(BaseModel): bytes: int | None = None status: str | None = None created_at: int | None = None + expires_at: int | None = None class FileList(BaseModel): diff --git a/tests/e2e/batches/capabilities.py b/tests/e2e/batches/capabilities.py index 1bcea0a61ee..17749c2fb87 100644 --- a/tests/e2e/batches/capabilities.py +++ b/tests/e2e/batches/capabilities.py @@ -108,6 +108,10 @@ class Capability: def id(self) -> str: return f"{self.provider}-{self.scenario}" + @property + def file_provider(self) -> str | None: + return self.provider if self.scenario in {"model_param", "provider_fallback"} else None + @property def jsonl_model(self) -> str: # Always the provider deployment name. Unified routes via diff --git a/tests/e2e/batches/conftest.py b/tests/e2e/batches/conftest.py index 3b133fab680..91a365b6b92 100644 --- a/tests/e2e/batches/conftest.py +++ b/tests/e2e/batches/conftest.py @@ -13,7 +13,7 @@ the proxy config. from __future__ import annotations import os -from typing import Iterator +from typing import Final, Iterator import pytest @@ -21,6 +21,7 @@ from batch_client import BatchClient, build_client from capabilities import PROVIDERS from e2e_config import MANAGED_FILES_OPT_IN_ENV from e2e_http import NoBody +from lifecycle import ResourceManager from proxy_client import ProxyClient @@ -52,6 +53,13 @@ def client(proxy: ProxyClient) -> BatchClient: return build_client(proxy) +@pytest.fixture +def resources(client: BatchClient) -> Iterator[ResourceManager]: + manager: Final = ResourceManager(client=client.proxy, strict_cleanup=True) + yield manager + manager.teardown() + + @pytest.fixture(scope="session") def batch_deployments(client: BatchClient) -> Iterator[None]: probe = client.proxy.probe("/health/liveliness", params=NoBody()) diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py new file mode 100644 index 00000000000..8ebfd750360 --- /dev/null +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -0,0 +1,187 @@ +from builtins import ExceptionGroup +from collections.abc import Iterator +from dataclasses import dataclass, field +from typing import Final + +import pytest + +from batch_cleanup import BATCH_CANCEL_TIMEOUT_SECONDS, CLEANUP_DELAYS, cleanup_batch, cleanup_file, cleanup_result +from batch_client import AZURE_FILE_EXPIRY_SECONDS, BatchObject, FileDeleteResponse, batch_upload_form +from capabilities import CAPABILITIES, Capability +from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApiError +from lifecycle import ResourceManager +from models import KeyGenerateBody + + +@dataclass +class CleanupClient: + files: Iterator[Result[FileDeleteResponse]] = field(default_factory=lambda: iter(())) + batches: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) + cancellations: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) + calls: list[str] = field(default_factory=list) + + def delete_file(self, file_id: str, *, key: str, provider: str | None = None) -> Result[FileDeleteResponse]: + self.calls.append(f"delete {provider} {file_id}") + return next(self.files) + + def retrieve_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: + self.calls.append(f"retrieve {provider} {batch_id}") + return next(self.batches) + + def cancel_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: + self.calls.append(f"cancel {provider} {batch_id}") + return next(self.cancellations) + + def generate_key(self, body: KeyGenerateBody) -> str: + return "test-key" + + def delete_key(self, key: str) -> None: + self.calls.append(f"delete key {key}") + + def delete_customers(self, user_ids: list[str]) -> None: + self.calls.append(f"delete customers {user_ids}") + + +def batch(status: str) -> Success[BatchObject]: + return Success(status_code=200, data=BatchObject(id="batch-1", status=status)) + + +def deleted_file(*, deleted: bool = True) -> Success[FileDeleteResponse]: + return Success(status_code=200, data=FileDeleteResponse(id="file-1", deleted=deleted)) + + +class TestFileCleanup: + @pytest.mark.parametrize("cap", CAPABILITIES, ids=[cap.id for cap in CAPABILITIES]) + def test_deletes_raw_files_through_the_upload_provider(self, cap: Capability) -> None: + client: Final = CleanupClient(files=iter((deleted_file(),))) + cleanup_file(client, "file-1", key="test-key", provider=cap.file_provider) + expected_provider: Final = cap.provider if cap.scenario in {"model_param", "provider_fallback"} else None + assert client.calls == [f"delete {expected_provider} file-1"] + + def test_failed_delete_is_reported_after_remaining_resources_are_cleaned(self) -> None: + client: Final = CleanupClient(files=iter((UnknownApiError(status_code=403, body="secret response"),))) + manager: Final = ResourceManager(client=client, strict_cleanup=True) + key: Final = manager.key() + manager.defer(lambda: cleanup_file(client, "file-1", key=key, provider="azure")) + with pytest.raises(ExceptionGroup) as caught: + manager.teardown() + assert client.calls == ["delete azure file-1", "delete key test-key"] + assert len(caught.value.exceptions) == 1 + assert str(caught.value.exceptions[0]) == "Delete file file-1 failed: HTTP 403" + + def test_success_response_must_confirm_deletion(self) -> None: + client: Final = CleanupClient(files=iter((deleted_file(deleted=False),))) + with pytest.raises(AssertionError, match="did not confirm deletion"): + cleanup_file(client, "file-1", key="test-key") + + def test_cleanup_is_idempotent_when_file_is_already_deleted(self) -> None: + client: Final = CleanupClient(files=iter((UnknownApiError(status_code=404, body="missing"),))) + cleanup_file(client, "file-1", key="test-key", provider="azure") + assert client.calls == ["delete azure file-1"] + + def test_default_resource_cleanup_keeps_existing_best_effort_behavior(self) -> None: + client: Final = CleanupClient(files=iter((UnknownApiError(status_code=403, body="forbidden"),))) + manager: Final = ResourceManager(client=client) + key: Final = manager.key() + manager.defer(lambda: cleanup_file(client, "file-1", key=key)) + manager.teardown() + assert client.calls == ["delete None file-1", "delete key test-key"] + + +class TestCleanupRetries: + @pytest.mark.parametrize( + "failure", + [NetworkError(message="offline"), RateLimitedError(), UnknownApiError(status_code=503, body="unavailable")], + ) + def test_transient_error_retries_and_returns_success(self, failure: Result[FileDeleteResponse]) -> None: + outcomes: Final = iter((failure, deleted_file())) + delays: Final[list[float]] = [] + result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays.append) + assert isinstance(result, Success) and result.data.deleted + assert delays == [1.0] + + def test_persistent_error_has_bounded_retries(self) -> None: + failure: Final = UnknownApiError(status_code=503, body="unavailable") + outcomes: Final[Iterator[Result[FileDeleteResponse]]] = iter((failure,) * (len(CLEANUP_DELAYS) + 1)) + delays: Final[list[float]] = [] + result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays.append) + assert result is failure + assert tuple(delays) == CLEANUP_DELAYS + assert next(outcomes, None) is None + + def test_permanent_error_is_not_retried(self) -> None: + failure: Final = UnknownApiError(status_code=403, body="forbidden") + outcomes: Final = iter((failure, deleted_file())) + delays: Final[list[float]] = [] + assert cleanup_result(lambda: next(outcomes), wait=delays.append) is failure + assert delays == [] + assert isinstance(next(outcomes), Success) + + +class TestBatchCancellation: + def test_cancelling_batch_is_polled_until_terminal_without_cancelling_again(self) -> None: + client: Final = CleanupClient(batches=iter((batch("cancelling"), batch("cancelling"), batch("cancelled")))) + delays: Final[list[float]] = [] + cleanup_batch(client, "batch-1", key="test-key", wait=delays.append) + assert client.calls == ["retrieve None batch-1"] * 3 + assert delays == [10.0] + + def test_cancellation_timeout_is_reported_but_file_and_key_cleanup_still_run(self) -> None: + client: Final = CleanupClient( + batches=iter((batch("cancelling"), batch("cancelling"))), files=iter((deleted_file(),)) + ) + ticks: Final = iter((0.0, BATCH_CANCEL_TIMEOUT_SECONDS)) + manager: Final = ResourceManager(client=client, strict_cleanup=True) + key: Final = manager.key() + manager.defer(lambda: cleanup_file(client, "file-1", key=key)) + manager.defer(lambda: cleanup_batch(client, "batch-1", key=key, clock=lambda: next(ticks))) + with pytest.raises(ExceptionGroup) as caught: + manager.teardown() + assert "cancellation did not finish" in str(caught.value.exceptions[0]) + assert client.calls == [ + "retrieve None batch-1", + "retrieve None batch-1", + "delete None file-1", + "delete key test-key", + ] + + @pytest.mark.parametrize("status", ["completed", "failed", "expired", "cancelled"]) + def test_inactive_batch_needs_no_cancellation(self, status: str) -> None: + client: Final = CleanupClient(batches=iter((batch(status),))) + cleanup_batch(client, "batch-1", key="test-key") + assert client.calls == ["retrieve None batch-1"] + + def test_active_batch_is_cancelled_through_its_provider(self) -> None: + client: Final = CleanupClient( + batches=iter((batch("in_progress"), batch("cancelled"))), cancellations=iter((batch("cancelling"),)) + ) + cleanup_batch(client, "batch-1", key="test-key", provider="azure") + assert client.calls == ["retrieve azure batch-1", "cancel azure batch-1", "retrieve azure batch-1"] + + @pytest.mark.parametrize("status", ["completed", "in_progress"]) + def test_cancellation_conflict_is_accepted_only_when_batch_became_inactive(self, status: str) -> None: + client: Final = CleanupClient( + batches=iter((batch("in_progress"), batch(status))), + cancellations=iter((UnknownApiError(status_code=409, body="conflict"),)), + ) + if status == "completed": + cleanup_batch(client, "batch-1", key="test-key") + else: + with pytest.raises(AssertionError, match="Cancel batch batch-1 left status in_progress"): + cleanup_batch(client, "batch-1", key="test-key") + assert client.calls == ["retrieve None batch-1", "cancel None batch-1", "retrieve None batch-1"] + + +class TestAzureFileExpiry: + def test_azure_form_serializes_native_expiry_for_the_proxy(self) -> None: + form: Final = batch_upload_form("azure", target_model_names="azure-test") + assert form.model_dump(by_alias=True, exclude_none=True) == { + "purpose": "batch", + "target_model_names": "azure-test", + "expires_after[anchor]": "created_at", + "expires_after[seconds]": AZURE_FILE_EXPIRY_SECONDS, + } + + @pytest.mark.parametrize("provider", ["openai", "vertex_ai", "bedrock"]) + def test_other_providers_keep_their_existing_upload_fields(self, provider: str) -> None: + assert batch_upload_form(provider).model_dump(by_alias=True, exclude_none=True) == {"purpose": "batch"} diff --git a/tests/e2e/batches/test_batches_e2e.py b/tests/e2e/batches/test_batches_e2e.py index ed7cf656d01..1b4a6ed266f 100644 --- a/tests/e2e/batches/test_batches_e2e.py +++ b/tests/e2e/batches/test_batches_e2e.py @@ -21,14 +21,16 @@ import os import re import time from datetime import datetime, timedelta, timezone -from typing import Callable import pytest from pydantic import BaseModel from e2e_config import PROXY_BASE_URL, unique_marker +from batch_cleanup import cleanup_batch, cleanup_file from batch_client import ( + AZURE_FILE_EXPIRY_SECONDS, + batch_upload_form, UPLOAD_FILENAME, BatchClient, BatchCreateBody, @@ -155,19 +157,19 @@ def upload_for_scenario( if cap.scenario == "encoded": return client.upload_file( content=content, - form=FileUploadForm(purpose="batch"), + form=batch_upload_form(cap.provider), model=cap.model, key=key, ) if cap.scenario == "unified": return client.upload_file( content=content, - form=FileUploadForm(purpose="batch", target_model_names=cap.model), + form=batch_upload_form(cap.provider, target_model_names=cap.model), key=key, ) return client.upload_file( content=content, - form=FileUploadForm(purpose="batch"), + form=batch_upload_form(cap.provider), key=key, provider=cap.provider, ) @@ -188,20 +190,11 @@ def create_for_scenario( def op_provider(cap: Capability) -> str | None: - """provider_fallback ids are raw, so retrieve/cancel/list/delete need the provider + """provider_fallback batch ids are raw, so retrieve/cancel/list need the provider hint; the other scenarios encode it into the id and route automatically.""" return cap.provider if cap.scenario == "provider_fallback" else None -def quietly(action: Callable[[], object]) -> Callable[[], None]: - """Adapt a value-returning call into a best-effort cleanup the teardown can run.""" - - def run() -> None: - action() - - return run - - def assert_file_object(file: FileObject, *, provider: str) -> None: assert file.object == "file", f"file.object={file.object!r}" assert file.purpose == "batch", f"file.purpose={file.purpose!r}" @@ -209,6 +202,10 @@ def assert_file_object(file: FileObject, *, provider: str) -> None: if provider != "bedrock": assert file.bytes > 0, f"file.bytes={file.bytes!r}" assert file.status, "file.status missing" + if provider == "azure": + assert file.expires_at is not None, "Azure batch input has no automatic expiry" + assert file.created_at is not None + assert file.expires_at - file.created_at == AZURE_FILE_EXPIRY_SECONDS assert ( file.created_at is not None and file.created_at > 0 ), "file.created_at missing" @@ -249,7 +246,7 @@ def test_batch_lifecycle( file = unwrap(upload_for_scenario(client, cap, render_jsonl(cap.jsonl_model), key)) resources.defer( - quietly(lambda: client.delete_file(file.id, key=key, provider=provider)) + lambda: cleanup_file(client, file.id, key=key, provider=cap.file_provider) ) assert_file_object(file, provider=cap.provider) assert matches_id_shape( @@ -260,7 +257,7 @@ def test_batch_lifecycle( require_successful_call(created) batch = BatchObject.model_validate_json(created.body) resources.defer( - quietly(lambda: client.cancel_batch(batch.id, key=key, provider=provider)) + lambda: cleanup_batch(client, batch.id, key=key, provider=provider) ) assert batch.id, f"create returned no batch id (body={created.body[:200]})" @@ -339,7 +336,7 @@ def test_batch_key_model_access_denied( denied_upload = client.upload_file( content=render_jsonl(AZURE_BATCH_MODEL), - form=FileUploadForm(purpose="batch"), + form=batch_upload_form("azure"), model=AZURE_BATCH_MODEL, key=key, ) @@ -356,7 +353,7 @@ def test_batch_key_model_access_denied( ) ).id resources.defer( - quietly(lambda: client.delete_file(raw_file, key=key, provider="openai")) + lambda: cleanup_file(client, raw_file, key=key, provider="openai") ) denied_create = client.create_batch( @@ -383,6 +380,7 @@ def test_file_upload_and_delete_outputs( key=key, ) ) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert_file_object(file, provider="openai") deleted = unwrap(client.delete_file(file.id, key=key)) @@ -458,12 +456,12 @@ def test_rate_limited_batch_create_leaves_no_unattributed_spend_row( key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) _ = client.proxy.poll_logs_for_key(key, min_rows=1) @@ -517,7 +515,7 @@ class TestBatchFileContent: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert file.id downloaded = client.proxy.transport.download( @@ -559,11 +557,11 @@ class TestBatchFileContent: file = unwrap( client.upload_file( content=payload, - form=FileUploadForm(purpose="batch", target_model_names=provider.model), + form=batch_upload_form(provider.name, target_model_names=provider.model), key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert_file_object(file, provider=provider.name) assert is_managed_id(file.id), ( f"{provider.name}: unified upload must return a managed file id, got {file.id!r}" @@ -626,7 +624,7 @@ class TestOpenAIFiles: ) ) resources.defer( - quietly(lambda: client.delete_file(file.id, key=key, provider="openai")) + lambda: cleanup_file(client, file.id, key=key, provider="openai") ) listed = unwrap(client.list_files(key=key)) @@ -690,7 +688,7 @@ class TestOpenAIFiles: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) fetched = unwrap(client.retrieve_file(file.id, key=key)) assert fetched.id == file.id, "retrieve must echo the uploaded file id" @@ -760,7 +758,7 @@ class TestBatchRateLimitErrorMapping: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) @@ -813,7 +811,7 @@ class TestBatchEnqueuedTokenLimit: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) return file def _generate_enqueued_key( @@ -861,7 +859,7 @@ class TestBatchEnqueuedTokenLimit: ) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) @pytest.mark.covers( "quota_management.ratelimit.batch_enqueued_tokens.blocks_when_exhausted", @@ -904,7 +902,7 @@ class TestBatchEnqueuedTokenLimit: first = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(first) first_batch = BatchObject.model_validate_json(first.body) - resources.defer(quietly(lambda: client.cancel_batch(first_batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, first_batch.id, key=key)) blocked = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) assert blocked.status_code == 429, ( @@ -928,7 +926,7 @@ class TestBatchEnqueuedTokenLimit: ) require_successful_call(retried) retry_batch = BatchObject.model_validate_json(retried.body) - resources.defer(quietly(lambda: client.cancel_batch(retry_batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, retry_batch.id, key=key)) ASSUME_ROLE_RAW_MODEL = "bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0" @@ -984,13 +982,13 @@ class TestBedrockBatchAssumeRole: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert_file_object(file, provider="bedrock") created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) assert batch.id, f"assume-role create returned no batch id: {created.body[:200]}" assert is_managed_id(batch.id), ( @@ -1044,7 +1042,7 @@ class TestGeminiFiles: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert_file_object(file, provider="gemini") assert file.id, "gemini file upload returned no id" @@ -1099,13 +1097,13 @@ class TestHostedVllmBatch: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert_file_object(file, provider="hosted_vllm") created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) assert batch.id, f"hosted_vllm create returned no batch id: {created.body[:200]}" assert batch.status in CREATED_BATCH_STATUSES, ( @@ -1192,7 +1190,7 @@ class TestBatchFailurePaths: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(created) @@ -1243,12 +1241,12 @@ class TestBatchFailurePaths: file = unwrap( client.upload_file( content=render_jsonl(AZURE_BATCH_RAW_MODEL), - form=FileUploadForm(purpose="batch"), + form=batch_upload_form("azure"), model=AZURE_BATCH_MODEL, key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert decoded_model_from_id(file.id) == AZURE_BATCH_MODEL, ( f"upload did not encode the azure deployment into the file id: {file.id!r}" ) @@ -1258,7 +1256,7 @@ class TestBatchFailurePaths: ) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) assert decoded_model_from_id(batch.id) == AZURE_BATCH_MODEL, ( "create with a foreign encoded file id must route by the file's embedded model, " @@ -1307,7 +1305,7 @@ class TestBatchSecondHop: key=key, ) ) - resources.defer(quietly(lambda: client.delete_file(file.id, key=key))) + resources.defer(lambda: cleanup_file(client, file.id, key=key)) assert is_managed_id(file.id), ( f"second-hop unified upload must return a managed file id, got {file.id!r}" ) @@ -1315,7 +1313,7 @@ class TestBatchSecondHop: created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(quietly(lambda: client.cancel_batch(batch.id, key=key))) + resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) assert is_managed_id(batch.id), ( f"second-hop create must return a managed batch id, got {batch.id!r}" diff --git a/tests/e2e/batches/test_managed_files_enforcement_e2e.py b/tests/e2e/batches/test_managed_files_enforcement_e2e.py index 7ad0b16adc3..4f703cf0fdc 100644 --- a/tests/e2e/batches/test_managed_files_enforcement_e2e.py +++ b/tests/e2e/batches/test_managed_files_enforcement_e2e.py @@ -21,6 +21,7 @@ from typing import Iterator import pytest from batch_client import BatchClient, FileObject +from batch_cleanup import cleanup_file from capabilities import batch_model_name, is_managed_id, openai_batch_params from e2e_config import unique_marker from e2e_http import FileUploadForm, Result, UnknownApiError, unwrap @@ -108,7 +109,7 @@ def test_cross_user_managed_id_denied_owner_allowed( key=owner_key, ) ) - resources.defer(lambda: client.delete_file(uploaded.id, key=owner_key)) + resources.defer(lambda: cleanup_file(client, uploaded.id, key=owner_key)) assert is_managed_id(uploaded.id), f"expected a managed unified file id, got {uploaded.id}" denied = client.retrieve_file(uploaded.id, key=other_key) diff --git a/tests/e2e/lifecycle.py b/tests/e2e/lifecycle.py index c9a67ebdb8c..eb9704d4dcb 100644 --- a/tests/e2e/lifecycle.py +++ b/tests/e2e/lifecycle.py @@ -8,8 +8,9 @@ ResourceManager; the test registers a cleanup for every resource it creates, and the fixture's teardown releases them all even when the test body raises. """ +from builtins import ExceptionGroup from dataclasses import dataclass, field -from typing import Callable, List, Protocol, runtime_checkable +from typing import Callable, Final, List, Protocol, runtime_checkable from proxy_client import ProxyClient from models import KeyGenerateBody @@ -52,6 +53,7 @@ class ResourceManager: """ client: ResourceClient + strict_cleanup: bool = False _cleanups: List[Callable[[], object]] = field( default_factory=list ) # mutable-ok: append-only teardown registry @@ -82,8 +84,17 @@ class ResourceManager: return customer_id def teardown(self) -> None: - for cleanup in reversed(self._cleanups): - try: - cleanup() - except Exception: - pass # best-effort: a failed cleanup must not block the rest + failures: Final = tuple( + failure for cleanup in reversed(self._cleanups) + if (failure := _run_cleanup(cleanup)) is not None + ) + if failures and self.strict_cleanup: + raise ExceptionGroup("Resource cleanup failed", failures) + + +def _run_cleanup(cleanup: Callable[[], object]) -> Exception | None: + try: + cleanup() + except Exception as exc: + return exc + return None From a56c60e8924f6f956200c816142fb0cb994fa3ed Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 12:27:51 -0700 Subject: [PATCH 043/223] fix(e2e): wait for managed batch cancellation before deleting inputs --- tests/e2e/batches/COVERAGE.md | 5 +++-- tests/e2e/batches/batch_cleanup.py | 10 +++++++++- tests/e2e/batches/test_batch_cleanup.py | 14 ++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md index 899530dde2b..69ba9d781ec 100644 --- a/tests/e2e/batches/COVERAGE.md +++ b/tests/e2e/batches/COVERAGE.md @@ -129,8 +129,9 @@ provider when deleted. Model-encoded and managed file IDs route themselves File deletion and batch cancellation check their responses and retry transient failures up to three times. Teardown attempts every registered cleanup before reporting failures as test errors. Already deleted files and batches that are -terminal are safe to clean up again. Cancellation polls for up to ten minutes -before input deletion, because accepting cancellation does not finish it +terminal are safe to clean up again. Managed batch cancellation polls for up to eleven minutes +before input deletion: the ten-minute provider window plus a propagation margin. +Raw and model-encoded inputs can be deleted after cancellation is accepted Azure input uploads request `expires_after` anchored to `created_at` with `seconds=1209600`, and the lifecycle tests check the returned expiry. This is a diff --git a/tests/e2e/batches/batch_cleanup.py b/tests/e2e/batches/batch_cleanup.py index a5b5e9bba37..dd79c776758 100644 --- a/tests/e2e/batches/batch_cleanup.py +++ b/tests/e2e/batches/batch_cleanup.py @@ -5,11 +5,12 @@ from typing import Final, Protocol from pydantic import BaseModel from batch_client import BatchObject, FileDeleteResponse +from capabilities import is_managed_id from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApiError CLEANUP_DELAYS: Final = (1.0, 2.0, 4.0) BATCH_TERMINAL_STATUSES: Final = frozenset({"completed", "failed", "expired", "cancelled"}) -BATCH_CANCEL_TIMEOUT_SECONDS: Final = 600.0 +BATCH_CANCEL_TIMEOUT_SECONDS: Final = 660.0 BATCH_CANCEL_POLL_SECONDS: Final = 10.0 @@ -62,12 +63,15 @@ def cleanup_batch( wait: Callable[[float], None] = sleep, clock: Callable[[], float] = monotonic, ) -> None: + needs_terminal_state: Final = is_managed_id(batch_id) fetched: Final = _require_cleanup_success( cleanup_result(lambda: client.retrieve_batch(batch_id, key=key, provider=provider)), f"Retrieve batch {batch_id} for cleanup", ) if fetched.status in BATCH_TERMINAL_STATUSES: return + if fetched.status == "cancelling" and not needs_terminal_state: + return if fetched.status != "cancelling": result: Final = cleanup_result(lambda: client.cancel_batch(batch_id, key=key, provider=provider)) if not (isinstance(result, UnknownApiError) and result.status_code in {400, 409}): @@ -75,6 +79,8 @@ def cleanup_batch( assert cancelled.status in BATCH_TERMINAL_STATUSES | {"cancelling"}, ( f"Cancel batch {batch_id} left status {cancelled.status}" ) + if not needs_terminal_state: + return deadline: Final = clock() + BATCH_CANCEL_TIMEOUT_SECONDS while True: current = _require_cleanup_success( @@ -84,6 +90,8 @@ def cleanup_batch( if current.status in BATCH_TERMINAL_STATUSES: return assert current.status == "cancelling", f"Cancel batch {batch_id} left status {current.status}" + if not needs_terminal_state: + return assert clock() < deadline, ( f"Batch {batch_id} cancellation did not finish within {BATCH_CANCEL_TIMEOUT_SECONDS}s" ) diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py index 8ebfd750360..9715370d9b7 100644 --- a/tests/e2e/batches/test_batch_cleanup.py +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -12,6 +12,8 @@ from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApi from lifecycle import ResourceManager from models import KeyGenerateBody +MANAGED_BATCH_ID: Final = "bGl0ZWxsbV9wcm94eTtiYXRjaC0x" + @dataclass class CleanupClient: @@ -122,8 +124,8 @@ class TestBatchCancellation: def test_cancelling_batch_is_polled_until_terminal_without_cancelling_again(self) -> None: client: Final = CleanupClient(batches=iter((batch("cancelling"), batch("cancelling"), batch("cancelled")))) delays: Final[list[float]] = [] - cleanup_batch(client, "batch-1", key="test-key", wait=delays.append) - assert client.calls == ["retrieve None batch-1"] * 3 + cleanup_batch(client, MANAGED_BATCH_ID, key="test-key", wait=delays.append) + assert client.calls == [f"retrieve None {MANAGED_BATCH_ID}"] * 3 assert delays == [10.0] def test_cancellation_timeout_is_reported_but_file_and_key_cleanup_still_run(self) -> None: @@ -134,13 +136,13 @@ class TestBatchCancellation: manager: Final = ResourceManager(client=client, strict_cleanup=True) key: Final = manager.key() manager.defer(lambda: cleanup_file(client, "file-1", key=key)) - manager.defer(lambda: cleanup_batch(client, "batch-1", key=key, clock=lambda: next(ticks))) + manager.defer(lambda: cleanup_batch(client, MANAGED_BATCH_ID, key=key, clock=lambda: next(ticks))) with pytest.raises(ExceptionGroup) as caught: manager.teardown() assert "cancellation did not finish" in str(caught.value.exceptions[0]) assert client.calls == [ - "retrieve None batch-1", - "retrieve None batch-1", + f"retrieve None {MANAGED_BATCH_ID}", + f"retrieve None {MANAGED_BATCH_ID}", "delete None file-1", "delete key test-key", ] @@ -156,7 +158,7 @@ class TestBatchCancellation: batches=iter((batch("in_progress"), batch("cancelled"))), cancellations=iter((batch("cancelling"),)) ) cleanup_batch(client, "batch-1", key="test-key", provider="azure") - assert client.calls == ["retrieve azure batch-1", "cancel azure batch-1", "retrieve azure batch-1"] + assert client.calls == ["retrieve azure batch-1", "cancel azure batch-1"] @pytest.mark.parametrize("status", ["completed", "in_progress"]) def test_cancellation_conflict_is_accepted_only_when_batch_became_inactive(self, status: str) -> None: From 6bdf206cc5228e85b47c908a2f4977374656289b Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 12:48:07 -0700 Subject: [PATCH 044/223] fix(e2e): accept managed file deletion responses --- tests/e2e/batches/COVERAGE.md | 3 +++ tests/e2e/batches/batch_cleanup.py | 4 +++- tests/e2e/batches/batch_client.py | 2 +- tests/e2e/batches/test_batch_cleanup.py | 15 +++++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md index 69ba9d781ec..f95ea1f2649 100644 --- a/tests/e2e/batches/COVERAGE.md +++ b/tests/e2e/batches/COVERAGE.md @@ -139,6 +139,9 @@ fallback for interrupted runs: immediate deletion remains the normal cleanup. Azure's minimum supported native expiry is 14 days, so a three-day expiry cannot be requested through its Files API +The Azure entry in `files_settings` must use `api_version: 2025-04-01-preview` +for raw uploads to honor expiry, matching the batch deployment's API version + ## Terminal state + cost write-back (cross-run marker baton) The 24h completion window rules out submit-and-wait inside one run, so diff --git a/tests/e2e/batches/batch_cleanup.py b/tests/e2e/batches/batch_cleanup.py index dd79c776758..3fd6802f696 100644 --- a/tests/e2e/batches/batch_cleanup.py +++ b/tests/e2e/batches/batch_cleanup.py @@ -51,7 +51,9 @@ def cleanup_file(client: BatchCleanupClient, file_id: str, *, key: str, provider if isinstance(result, UnknownApiError) and result.status_code == 404: return deleted: Final = _require_cleanup_success(result, f"Delete file {file_id}") - assert deleted.deleted, f"Delete file {file_id} did not confirm deletion" + assert deleted.deleted is True or ( + deleted.deleted is None and is_managed_id(file_id) and deleted.id == file_id and deleted.object == "file" + ), f"Delete file {file_id} did not confirm deletion" def cleanup_batch( diff --git a/tests/e2e/batches/batch_client.py b/tests/e2e/batches/batch_client.py index 84a902b0b11..c9c77e1f12e 100644 --- a/tests/e2e/batches/batch_client.py +++ b/tests/e2e/batches/batch_client.py @@ -99,7 +99,7 @@ class BatchList(BaseModel): class FileDeleteResponse(BaseModel): id: str object: str | None = None - deleted: bool + deleted: bool | None = None class BatchCreateBody(BaseModel): diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py index 9715370d9b7..15dead6d36d 100644 --- a/tests/e2e/batches/test_batch_cleanup.py +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -12,6 +12,7 @@ from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApi from lifecycle import ResourceManager from models import KeyGenerateBody +MANAGED_FILE_ID: Final = "bGl0ZWxsbV9wcm94eTtmaWxlLTE=" MANAGED_BATCH_ID: Final = "bGl0ZWxsbV9wcm94eTtiYXRjaC0x" @@ -53,6 +54,20 @@ def deleted_file(*, deleted: bool = True) -> Success[FileDeleteResponse]: class TestFileCleanup: + def test_managed_delete_accepts_the_deleted_file_object(self) -> None: + response: Final = Success( + status_code=200, data=FileDeleteResponse.model_validate({"id": MANAGED_FILE_ID, "object": "file"}) + ) + client: Final = CleanupClient(files=iter((response,))) + cleanup_file(client, MANAGED_FILE_ID, key="test-key") + assert client.calls == [f"delete None {MANAGED_FILE_ID}"] + + @pytest.mark.parametrize("file_id", ["file-1", MANAGED_FILE_ID]) + def test_a_success_status_without_a_deletion_confirmation_is_rejected(self, file_id: str) -> None: + client: Final = CleanupClient(files=iter((Success(status_code=200, data=FileDeleteResponse(id=file_id)),))) + with pytest.raises(AssertionError, match="did not confirm deletion"): + cleanup_file(client, file_id, key="test-key") + @pytest.mark.parametrize("cap", CAPABILITIES, ids=[cap.id for cap in CAPABILITIES]) def test_deletes_raw_files_through_the_upload_provider(self, cap: Capability) -> None: client: Final = CleanupClient(files=iter((deleted_file(),))) From a096dd615c71e40be9473338ffeb8d1c17284f51 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 14:39:25 -0700 Subject: [PATCH 045/223] refactor(e2e): use immutable batch cleanup test expectations --- tests/e2e/batches/batch_cleanup.py | 7 +- tests/e2e/batches/test_batch_cleanup.py | 137 ++++++++++++++++-------- 2 files changed, 96 insertions(+), 48 deletions(-) diff --git a/tests/e2e/batches/batch_cleanup.py b/tests/e2e/batches/batch_cleanup.py index 3fd6802f696..722df0c29bc 100644 --- a/tests/e2e/batches/batch_cleanup.py +++ b/tests/e2e/batches/batch_cleanup.py @@ -1,4 +1,5 @@ from collections.abc import Callable +from itertools import count from time import monotonic, sleep from typing import Final, Protocol @@ -84,11 +85,13 @@ def cleanup_batch( if not needs_terminal_state: return deadline: Final = clock() + BATCH_CANCEL_TIMEOUT_SECONDS - while True: - current = _require_cleanup_success( + for current in ( + _require_cleanup_success( cleanup_result(lambda: client.retrieve_batch(batch_id, key=key, provider=provider)), f"Retrieve batch {batch_id} after cancellation", ) + for _ in count() + ): if current.status in BATCH_TERMINAL_STATUSES: return assert current.status == "cancelling", f"Cancel batch {batch_id} left status {current.status}" diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py index 15dead6d36d..66b7079ccc2 100644 --- a/tests/e2e/batches/test_batch_cleanup.py +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -16,33 +16,44 @@ MANAGED_FILE_ID: Final = "bGl0ZWxsbV9wcm94eTtmaWxlLTE=" MANAGED_BATCH_ID: Final = "bGl0ZWxsbV9wcm94eTtiYXRjaC0x" -@dataclass +@dataclass(frozen=True, slots=True) +class ExpectedCalls[T]: + values: Iterator[T] + + def __call__(self, value: T) -> None: + assert next(self.values, None) == value + + def assert_done(self) -> None: + assert tuple(self.values) == () + + +@dataclass(frozen=True, slots=True) class CleanupClient: + calls: ExpectedCalls[str] files: Iterator[Result[FileDeleteResponse]] = field(default_factory=lambda: iter(())) batches: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) cancellations: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) - calls: list[str] = field(default_factory=list) def delete_file(self, file_id: str, *, key: str, provider: str | None = None) -> Result[FileDeleteResponse]: - self.calls.append(f"delete {provider} {file_id}") + self.calls(f"delete {provider} {file_id}") return next(self.files) def retrieve_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: - self.calls.append(f"retrieve {provider} {batch_id}") + self.calls(f"retrieve {provider} {batch_id}") return next(self.batches) def cancel_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: - self.calls.append(f"cancel {provider} {batch_id}") + self.calls(f"cancel {provider} {batch_id}") return next(self.cancellations) def generate_key(self, body: KeyGenerateBody) -> str: return "test-key" def delete_key(self, key: str) -> None: - self.calls.append(f"delete key {key}") + self.calls(f"delete key {key}") def delete_customers(self, user_ids: list[str]) -> None: - self.calls.append(f"delete customers {user_ids}") + self.calls(f"delete customers {user_ids}") def batch(status: str) -> Success[BatchObject]: @@ -58,51 +69,71 @@ class TestFileCleanup: response: Final = Success( status_code=200, data=FileDeleteResponse.model_validate({"id": MANAGED_FILE_ID, "object": "file"}) ) - client: Final = CleanupClient(files=iter((response,))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter((f"delete None {MANAGED_FILE_ID}",))), files=iter((response,)) + ) cleanup_file(client, MANAGED_FILE_ID, key="test-key") - assert client.calls == [f"delete None {MANAGED_FILE_ID}"] + client.calls.assert_done() @pytest.mark.parametrize("file_id", ["file-1", MANAGED_FILE_ID]) def test_a_success_status_without_a_deletion_confirmation_is_rejected(self, file_id: str) -> None: - client: Final = CleanupClient(files=iter((Success(status_code=200, data=FileDeleteResponse(id=file_id)),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter((f"delete None {file_id}",))), + files=iter((Success(status_code=200, data=FileDeleteResponse(id=file_id)),)), + ) with pytest.raises(AssertionError, match="did not confirm deletion"): cleanup_file(client, file_id, key="test-key") + client.calls.assert_done() @pytest.mark.parametrize("cap", CAPABILITIES, ids=[cap.id for cap in CAPABILITIES]) def test_deletes_raw_files_through_the_upload_provider(self, cap: Capability) -> None: - client: Final = CleanupClient(files=iter((deleted_file(),))) - cleanup_file(client, "file-1", key="test-key", provider=cap.file_provider) expected_provider: Final = cap.provider if cap.scenario in {"model_param", "provider_fallback"} else None - assert client.calls == [f"delete {expected_provider} file-1"] + client: Final = CleanupClient( + calls=ExpectedCalls(iter((f"delete {expected_provider} file-1",))), files=iter((deleted_file(),)) + ) + cleanup_file(client, "file-1", key="test-key", provider=cap.file_provider) + client.calls.assert_done() def test_failed_delete_is_reported_after_remaining_resources_are_cleaned(self) -> None: - client: Final = CleanupClient(files=iter((UnknownApiError(status_code=403, body="secret response"),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter(("delete azure file-1", "delete key test-key"))), + files=iter((UnknownApiError(status_code=403, body="secret response"),)), + ) manager: Final = ResourceManager(client=client, strict_cleanup=True) key: Final = manager.key() manager.defer(lambda: cleanup_file(client, "file-1", key=key, provider="azure")) with pytest.raises(ExceptionGroup) as caught: manager.teardown() - assert client.calls == ["delete azure file-1", "delete key test-key"] + client.calls.assert_done() assert len(caught.value.exceptions) == 1 assert str(caught.value.exceptions[0]) == "Delete file file-1 failed: HTTP 403" def test_success_response_must_confirm_deletion(self) -> None: - client: Final = CleanupClient(files=iter((deleted_file(deleted=False),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter(("delete None file-1",))), files=iter((deleted_file(deleted=False),)) + ) with pytest.raises(AssertionError, match="did not confirm deletion"): cleanup_file(client, "file-1", key="test-key") + client.calls.assert_done() def test_cleanup_is_idempotent_when_file_is_already_deleted(self) -> None: - client: Final = CleanupClient(files=iter((UnknownApiError(status_code=404, body="missing"),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter(("delete azure file-1",))), + files=iter((UnknownApiError(status_code=404, body="missing"),)), + ) cleanup_file(client, "file-1", key="test-key", provider="azure") - assert client.calls == ["delete azure file-1"] + client.calls.assert_done() def test_default_resource_cleanup_keeps_existing_best_effort_behavior(self) -> None: - client: Final = CleanupClient(files=iter((UnknownApiError(status_code=403, body="forbidden"),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter(("delete None file-1", "delete key test-key"))), + files=iter((UnknownApiError(status_code=403, body="forbidden"),)), + ) manager: Final = ResourceManager(client=client) key: Final = manager.key() manager.defer(lambda: cleanup_file(client, "file-1", key=key)) manager.teardown() - assert client.calls == ["delete None file-1", "delete key test-key"] + client.calls.assert_done() class TestCleanupRetries: @@ -112,40 +143,54 @@ class TestCleanupRetries: ) def test_transient_error_retries_and_returns_success(self, failure: Result[FileDeleteResponse]) -> None: outcomes: Final = iter((failure, deleted_file())) - delays: Final[list[float]] = [] - result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays.append) + delays: Final = ExpectedCalls(iter((1.0,))) + result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays) assert isinstance(result, Success) and result.data.deleted - assert delays == [1.0] + delays.assert_done() def test_persistent_error_has_bounded_retries(self) -> None: failure: Final = UnknownApiError(status_code=503, body="unavailable") outcomes: Final[Iterator[Result[FileDeleteResponse]]] = iter((failure,) * (len(CLEANUP_DELAYS) + 1)) - delays: Final[list[float]] = [] - result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays.append) + delays: Final = ExpectedCalls(iter(CLEANUP_DELAYS)) + result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays) assert result is failure - assert tuple(delays) == CLEANUP_DELAYS + delays.assert_done() assert next(outcomes, None) is None def test_permanent_error_is_not_retried(self) -> None: failure: Final = UnknownApiError(status_code=403, body="forbidden") outcomes: Final = iter((failure, deleted_file())) - delays: Final[list[float]] = [] - assert cleanup_result(lambda: next(outcomes), wait=delays.append) is failure - assert delays == [] + delays: Final = ExpectedCalls[float](iter(())) + assert cleanup_result(lambda: next(outcomes), wait=delays) is failure + delays.assert_done() assert isinstance(next(outcomes), Success) class TestBatchCancellation: def test_cancelling_batch_is_polled_until_terminal_without_cancelling_again(self) -> None: - client: Final = CleanupClient(batches=iter((batch("cancelling"), batch("cancelling"), batch("cancelled")))) - delays: Final[list[float]] = [] - cleanup_batch(client, MANAGED_BATCH_ID, key="test-key", wait=delays.append) - assert client.calls == [f"retrieve None {MANAGED_BATCH_ID}"] * 3 - assert delays == [10.0] + client: Final = CleanupClient( + calls=ExpectedCalls(iter((f"retrieve None {MANAGED_BATCH_ID}",) * 3)), + batches=iter((batch("cancelling"), batch("cancelling"), batch("cancelled"))), + ) + delays: Final = ExpectedCalls(iter((10.0,))) + cleanup_batch(client, MANAGED_BATCH_ID, key="test-key", wait=delays) + client.calls.assert_done() + delays.assert_done() def test_cancellation_timeout_is_reported_but_file_and_key_cleanup_still_run(self) -> None: client: Final = CleanupClient( - batches=iter((batch("cancelling"), batch("cancelling"))), files=iter((deleted_file(),)) + calls=ExpectedCalls( + iter( + ( + f"retrieve None {MANAGED_BATCH_ID}", + f"retrieve None {MANAGED_BATCH_ID}", + "delete None file-1", + "delete key test-key", + ) + ) + ), + batches=iter((batch("cancelling"), batch("cancelling"))), + files=iter((deleted_file(),)), ) ticks: Final = iter((0.0, BATCH_CANCEL_TIMEOUT_SECONDS)) manager: Final = ResourceManager(client=client, strict_cleanup=True) @@ -155,29 +200,29 @@ class TestBatchCancellation: with pytest.raises(ExceptionGroup) as caught: manager.teardown() assert "cancellation did not finish" in str(caught.value.exceptions[0]) - assert client.calls == [ - f"retrieve None {MANAGED_BATCH_ID}", - f"retrieve None {MANAGED_BATCH_ID}", - "delete None file-1", - "delete key test-key", - ] + client.calls.assert_done() @pytest.mark.parametrize("status", ["completed", "failed", "expired", "cancelled"]) def test_inactive_batch_needs_no_cancellation(self, status: str) -> None: - client: Final = CleanupClient(batches=iter((batch(status),))) + client: Final = CleanupClient( + calls=ExpectedCalls(iter(("retrieve None batch-1",))), batches=iter((batch(status),)) + ) cleanup_batch(client, "batch-1", key="test-key") - assert client.calls == ["retrieve None batch-1"] + client.calls.assert_done() def test_active_batch_is_cancelled_through_its_provider(self) -> None: client: Final = CleanupClient( - batches=iter((batch("in_progress"), batch("cancelled"))), cancellations=iter((batch("cancelling"),)) + calls=ExpectedCalls(iter(("retrieve azure batch-1", "cancel azure batch-1"))), + batches=iter((batch("in_progress"), batch("cancelled"))), + cancellations=iter((batch("cancelling"),)), ) cleanup_batch(client, "batch-1", key="test-key", provider="azure") - assert client.calls == ["retrieve azure batch-1", "cancel azure batch-1"] + client.calls.assert_done() @pytest.mark.parametrize("status", ["completed", "in_progress"]) def test_cancellation_conflict_is_accepted_only_when_batch_became_inactive(self, status: str) -> None: client: Final = CleanupClient( + calls=ExpectedCalls(iter(("retrieve None batch-1", "cancel None batch-1", "retrieve None batch-1"))), batches=iter((batch("in_progress"), batch(status))), cancellations=iter((UnknownApiError(status_code=409, body="conflict"),)), ) @@ -186,7 +231,7 @@ class TestBatchCancellation: else: with pytest.raises(AssertionError, match="Cancel batch batch-1 left status in_progress"): cleanup_batch(client, "batch-1", key="test-key") - assert client.calls == ["retrieve None batch-1", "cancel None batch-1", "retrieve None batch-1"] + client.calls.assert_done() class TestAzureFileExpiry: From c84131b81ab6feb552e23c5996559159e8810066 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 7 Sep 2026 16:00:20 -0700 Subject: [PATCH 046/223] feat(proxy): price cache and reasoning tokens in /cost/estimate POST /cost/estimate now accepts cache_read_input_tokens, cache_creation_input_tokens and reasoning_tokens, bills them at the model's cache and reasoning rates, and reports each share per request, per day and per month next to the rates it used. Custom-priced deployments also get cache and reasoning lines in the cost breakdown now, so the estimate and the spend logs reconcile with their totals instead of showing zero for those tokens. Requested by a customer (Pylon #7365). Claude-Session: https://claude.ai/code/session_011Tn3657NkV6ojLqewL64Kb --- litellm/cost_calculator.py | 1 + .../litellm_core_utils/llm_cost_calc/utils.py | 68 +++-- litellm/proxy/_types.py | 36 +++ .../cost_tracking_settings.py | 253 +++++++++++------- .../llm_cost_calc/test_llm_cost_calc_utils.py | 64 +++++ .../test_cost_tracking_settings.py | 187 ++++++++++++- tests/test_litellm/test_cost_calculator.py | 50 ++++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 103 ++++++- 8 files changed, 641 insertions(+), 121 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 9a9d2ceda03..a1181ee4124 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -1746,6 +1746,7 @@ def completion_cost( service_tier=service_tier, data_residency=data_residency, vertex_location=vertex_location, + custom_cost_per_token=custom_cost_per_token, ) _reasoning_cost = _token_type_breakdown.reasoning_cost _cache_read_cost = _token_type_breakdown.cache_read_cost diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 68dc27ec25e..8d53c8da3e6 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -19,6 +19,7 @@ from litellm.types.utils import ( CacheCreationTokenDetails, CallTypes, CompletionTokensDetailsWrapper, + CostPerToken, DataResidency, ImageResponse, ModelInfo, @@ -1307,6 +1308,40 @@ class TokenTypeCostBreakdown: cache_creation_cost: float +def _reasoning_token_count(usage: Usage) -> int: + parsed: Final = ( + parse_completion_tokens_details(usage)["reasoning_tokens"] if usage.completion_tokens_details is not None else 0 + ) + return parsed or _coerce_token_count(getattr(usage, "reasoning_tokens", 0)) + + +def _cache_token_counts(usage: Usage) -> tuple[int, int, CacheCreationTokenDetails | None]: + """(cache read tokens, cache creation tokens, cache creation details): read from prompt_tokens_details + first, then the private top-level counters the Usage constructor mirrors cache tokens onto for + providers/callers that bypass the details.""" + parsed: Final = parse_prompt_tokens_details(usage) if usage.prompt_tokens_details is not None else None + parsed_read: Final = parsed["cache_hit_tokens"] if parsed is not None else 0 + parsed_creation: Final = parsed["cache_creation_tokens"] if parsed is not None else 0 + return ( + parsed_read or _coerce_token_count(getattr(usage, "_cache_read_input_tokens", 0)), + parsed_creation or _coerce_token_count(getattr(usage, "_cache_creation_input_tokens", 0)), + parsed["cache_creation_token_details"] if parsed is not None else None, + ) + + +def _custom_pricing_token_type_breakdown(usage: Usage, custom_cost_per_token: CostPerToken) -> TokenTypeCostBreakdown: + """Flat custom pricing has no tiers, uplifts or reasoning rate: cache tokens bill at the configured + cache rates (else the input rate) and reasoning at the output rate, as _cost_per_token_custom_pricing_helper does.""" + input_rate: Final = custom_cost_per_token["input_cost_per_token"] + cache_read_tokens, cache_creation_tokens, _ = _cache_token_counts(usage) + return TokenTypeCostBreakdown( + reasoning_cost=float(_reasoning_token_count(usage)) * custom_cost_per_token["output_cost_per_token"], + cache_read_cost=float(cache_read_tokens) * custom_cost_per_token.get("cache_read_input_token_cost", input_rate), + cache_creation_cost=float(cache_creation_tokens) + * custom_cost_per_token.get("cache_creation_input_token_cost", input_rate), + ) + + def get_token_type_cost_breakdown( model: str, custom_llm_provider: str | None, @@ -1315,6 +1350,7 @@ def get_token_type_cost_breakdown( data_residency: str | None = None, vertex_location: str | None = None, current_time: datetime | None = None, + custom_cost_per_token: CostPerToken | None = None, ) -> TokenTypeCostBreakdown: """ Provider-agnostic cost of reasoning and cache tokens, derived from the usage @@ -1325,9 +1361,13 @@ def get_token_type_cost_breakdown( land on ``prompt_tokens_details`` (via the Usage constructor and provider transformations) and reasoning tokens on ``completion_tokens_details``. It reuses the same rate-resolution primitives as the total-cost path so the breakdown can - never drift from the totals. Returns zeros (never raises) when the model or its - pricing cannot be resolved. + never drift from the totals. A deployment billed by ``custom_cost_per_token`` is + priced from those flat rates instead of the cost map, for the same reason. + Returns zeros (never raises) when the model or its pricing cannot be resolved. """ + if custom_cost_per_token is not None: + return _custom_pricing_token_type_breakdown(usage=usage, custom_cost_per_token=custom_cost_per_token) + try: model_info: Final = get_model_info(model=model, custom_llm_provider=custom_llm_provider) except Exception: @@ -1348,12 +1388,6 @@ def get_token_type_cost_breakdown( threshold_is_inclusive=_uses_inclusive_token_thresholds(custom_llm_provider), ) - reasoning_tokens = ( - parse_completion_tokens_details(usage)["reasoning_tokens"] if usage.completion_tokens_details is not None else 0 - ) - if not reasoning_tokens: - reasoning_tokens = _coerce_token_count(getattr(usage, "reasoning_tokens", 0)) - reasoning_rate: Final = _resolve_billed_reasoning_rate( model_info=model_info, usage=usage, @@ -1361,23 +1395,9 @@ def get_token_type_cost_breakdown( completion_base_cost=completion_base_cost, current_time=billing_time, ) - reasoning_cost = float(reasoning_tokens) * reasoning_rate - - cache_read_tokens = 0 - cache_creation_tokens = 0 - cache_creation_token_details: CacheCreationTokenDetails | None = None - if usage.prompt_tokens_details is not None: - prompt_tokens_details: Final = parse_prompt_tokens_details(usage) - cache_read_tokens = prompt_tokens_details["cache_hit_tokens"] - cache_creation_tokens = prompt_tokens_details["cache_creation_tokens"] - cache_creation_token_details = prompt_tokens_details["cache_creation_token_details"] - # Fall back to the private top-level counters the Usage constructor mirrors cache - # tokens onto, so providers/callers that bypass prompt_tokens_details are covered. - if not cache_read_tokens: - cache_read_tokens = _coerce_token_count(getattr(usage, "_cache_read_input_tokens", 0)) - if not cache_creation_tokens: - cache_creation_tokens = _coerce_token_count(getattr(usage, "_cache_creation_input_tokens", 0)) + reasoning_cost = float(_reasoning_token_count(usage)) * reasoning_rate + cache_read_tokens, cache_creation_tokens, cache_creation_token_details = _cache_token_counts(usage) cache_read_cost = float(cache_read_tokens) * cache_read_cost_rate cache_creation_cost = calculate_cache_writing_cost( cache_creation_tokens=cache_creation_tokens, diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index abce10690e5..65b7d409d7c 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -5137,9 +5137,26 @@ class CostEstimateRequest(LiteLLMPydanticObjectBase): model: str = Field(description="Model name (from /model_group/info)") input_tokens: int = Field(description="Expected input tokens per request", ge=0) output_tokens: int = Field(description="Expected output tokens per request", ge=0) + cache_read_input_tokens: int = Field( + default=0, description="Input tokens read from the prompt cache; counted within input_tokens", ge=0 + ) + cache_creation_input_tokens: int = Field( + default=0, description="Input tokens written to the prompt cache; counted within input_tokens", ge=0 + ) + reasoning_tokens: int = Field( + default=0, description="Reasoning tokens the model emits; counted within output_tokens", ge=0 + ) num_requests_per_day: int | None = Field(default=None, description="Number of requests per day", ge=0) num_requests_per_month: int | None = Field(default=None, description="Number of requests per month", ge=0) + @model_validator(mode="after") + def validate_token_subsets(self) -> "CostEstimateRequest": + if self.cache_read_input_tokens + self.cache_creation_input_tokens > self.input_tokens: + raise ValueError("cache_read_input_tokens plus cache_creation_input_tokens cannot exceed input_tokens") + if self.reasoning_tokens > self.output_tokens: + raise ValueError("reasoning_tokens cannot exceed output_tokens") + return self + class CostEstimateResponse(LiteLLMPydanticObjectBase): """Response body for /cost/estimate endpoint.""" @@ -5147,6 +5164,9 @@ class CostEstimateResponse(LiteLLMPydanticObjectBase): model: str input_tokens: int output_tokens: int + cache_read_input_tokens: int = 0 + cache_creation_input_tokens: int = 0 + reasoning_tokens: int = 0 num_requests_per_day: int | None = None num_requests_per_month: int | None = None # Per-request costs @@ -5154,17 +5174,33 @@ class CostEstimateResponse(LiteLLMPydanticObjectBase): input_cost_per_request: float = Field(description="Input token cost per request (before margin)") output_cost_per_request: float = Field(description="Output token cost per request (before margin)") margin_cost_per_request: float = Field(default=0.0, description="Margin/fee added per request") + cache_read_cost_per_request: float = Field(default=0.0, description="Cache-read share of input_cost_per_request") + cache_creation_cost_per_request: float = Field( + default=0.0, description="Cache-write share of input_cost_per_request" + ) + reasoning_cost_per_request: float = Field(default=0.0, description="Reasoning share of output_cost_per_request") # Daily costs (if num_requests_per_day provided) daily_cost: float | None = Field(default=None, description="Total daily cost (includes margin)") daily_input_cost: float | None = Field(default=None, description="Daily input token cost") daily_output_cost: float | None = Field(default=None, description="Daily output token cost") daily_margin_cost: float | None = Field(default=None, description="Daily margin/fee") + daily_cache_read_cost: float | None = Field(default=None, description="Cache-read share of daily_input_cost") + daily_cache_creation_cost: float | None = Field(default=None, description="Cache-write share of daily_input_cost") + daily_reasoning_cost: float | None = Field(default=None, description="Reasoning share of daily_output_cost") # Monthly costs (if num_requests_per_month provided) monthly_cost: float | None = Field(default=None, description="Total monthly cost (includes margin)") monthly_input_cost: float | None = Field(default=None, description="Monthly input token cost") monthly_output_cost: float | None = Field(default=None, description="Monthly output token cost") monthly_margin_cost: float | None = Field(default=None, description="Monthly margin/fee") + monthly_cache_read_cost: float | None = Field(default=None, description="Cache-read share of monthly_input_cost") + monthly_cache_creation_cost: float | None = Field( + default=None, description="Cache-write share of monthly_input_cost" + ) + monthly_reasoning_cost: float | None = Field(default=None, description="Reasoning share of monthly_output_cost") # Pricing info input_cost_per_token: float | None = None output_cost_per_token: float | None = None + cache_read_input_token_cost: float | None = Field(default=None, description="Rate billed per cache-read token") + cache_creation_input_token_cost: float | None = Field(default=None, description="Rate billed per cache-write token") + output_cost_per_reasoning_token: float | None = Field(default=None, description="Rate billed per reasoning token") provider: str | None = None diff --git a/litellm/proxy/management_endpoints/cost_tracking_settings.py b/litellm/proxy/management_endpoints/cost_tracking_settings.py index 204051c3715..51f31a757cd 100644 --- a/litellm/proxy/management_endpoints/cost_tracking_settings.py +++ b/litellm/proxy/management_endpoints/cost_tracking_settings.py @@ -27,7 +27,15 @@ from litellm.proxy._types import ( UserAPIKeyAuth, ) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth -from litellm.types.utils import CostPerToken, LlmProvidersSet, ModelInfo +from litellm.types.utils import ( + CostBreakdown, + CostPerToken, + LlmProvidersSet, + ModelInfo, + ModelResponse, + PromptTokensDetailsWrapper, + Usage, +) router: Final = APIRouter() @@ -46,13 +54,15 @@ def _configured_price(key: str, sources: tuple[Mapping[str, object], ...]) -> fl def _extract_custom_pricing( - litellm_params: Mapping[str, object], model_info: Mapping[str, object] + litellm_params: Mapping[str, object], model_info: Mapping[str, object], builtin: ModelInfo | None ) -> CostPerToken | None: """ Pull per-token pricing configured on a deployment so on-prem / self-hosted models (absent from the public cost map) still estimate a real cost. Pricing may live on ``litellm_params`` or ``model_info``; ``litellm_params`` - wins, matching the router's cost-map registration precedence. + wins, matching the router's cost-map registration precedence. Cache rates the + deployment leaves unset come from the backend model's built-in entry, then its + own input rate, again matching what the router registers for live billing. """ sources: Final = (litellm_params, model_info) input_price: Final = _configured_price("input_cost_per_token", sources) @@ -61,9 +71,15 @@ def _extract_custom_pricing( if input_price is None and output_price is None: return None + input_rate: Final = input_price or 0.0 + cache_sources: Final = sources if builtin is None else (*sources, builtin) + cache_read_price: Final = _configured_price("cache_read_input_token_cost", cache_sources) + cache_creation_price: Final = _configured_price("cache_creation_input_token_cost", cache_sources) return CostPerToken( - input_cost_per_token=input_price or 0.0, + input_cost_per_token=input_rate, output_cost_per_token=output_price or 0.0, + cache_read_input_token_cost=input_rate if cache_read_price is None else cache_read_price, + cache_creation_input_token_cost=input_rate if cache_creation_price is None else cache_creation_price, ) @@ -98,17 +114,14 @@ def _resolve_model_for_cost_lookup(model: str) -> ResolvedCostModel: model_info: Final = first_deployment.get("model_info", {}) custom_llm_provider: Final = litellm_params.get("custom_llm_provider") provider: Final = str(custom_llm_provider) if custom_llm_provider is not None else None - custom_cost_per_token: Final = _extract_custom_pricing(litellm_params, model_info) - - # Check base_model first (needed for Azure custom deployment names) + # base_model wins (needed for Azure custom deployment names) base_model: Final = model_info.get("base_model") or litellm_params.get("base_model") - if base_model: - verbose_proxy_logger.debug("Resolved model '%s' to base_model '%s' from router", model, base_model) - return ResolvedCostModel(str(base_model), provider, custom_cost_per_token) - - resolved_model: Final = litellm_params.get("model") + resolved_model: Final = base_model or litellm_params.get("model") if resolved_model: verbose_proxy_logger.debug("Resolved model '%s' to '%s' from router", model, resolved_model) + custom_cost_per_token: Final = _extract_custom_pricing( + litellm_params, model_info, _lookup_model_info(str(resolved_model)) + ) return ResolvedCostModel(str(resolved_model), provider, custom_cost_per_token) except Exception as e: verbose_proxy_logger.debug("Could not resolve model '%s' from router: %s", model, e) @@ -117,19 +130,97 @@ def _resolve_model_for_cost_lookup(model: str) -> ResolvedCostModel: return ResolvedCostModel(model, None, None) -def _calculate_period_costs(num_requests, cost_per_request, input_cost, output_cost, margin_cost): - """ - Calculate costs for a given number of requests. +@dataclass(frozen=True, slots=True) +class CostLines: + """Cost of one request split the way the spend logs split it: the cache lines are + shares of input_cost and the reasoning line is a share of output_cost.""" - Returns tuple of (total_cost, input_cost, output_cost, margin_cost) or all None if num_requests is None/0. - """ - if not num_requests: - return None, None, None, None - return ( - cost_per_request * num_requests, - input_cost * num_requests, - output_cost * num_requests, - margin_cost * num_requests, + total_cost: float + input_cost: float + output_cost: float + margin_cost: float + cache_read_cost: float + cache_creation_cost: float + reasoning_cost: float + + def times(self, num_requests: int | None) -> "CostLines | None": + if not num_requests: + return None + return CostLines( + total_cost=self.total_cost * num_requests, + input_cost=self.input_cost * num_requests, + output_cost=self.output_cost * num_requests, + margin_cost=self.margin_cost * num_requests, + cache_read_cost=self.cache_read_cost * num_requests, + cache_creation_cost=self.cache_creation_cost * num_requests, + reasoning_cost=self.reasoning_cost * num_requests, + ) + + +def _cost_lines(cost_per_request: float, cost_breakdown: CostBreakdown | None) -> CostLines: + breakdown: Final = cost_breakdown if cost_breakdown is not None else CostBreakdown() + return CostLines( + total_cost=cost_per_request, + input_cost=breakdown.get("input_cost", 0.0), + output_cost=breakdown.get("output_cost", 0.0), + margin_cost=breakdown.get("margin_total_amount", 0.0), + cache_read_cost=breakdown.get("cache_read_cost", 0.0), + cache_creation_cost=breakdown.get("cache_creation_cost", 0.0), + reasoning_cost=breakdown.get("reasoning_cost", 0.0), + ) + + +@dataclass(frozen=True, slots=True) +class EffectiveTokenRates: + input_cost_per_token: float | None + output_cost_per_token: float | None + cache_read_input_token_cost: float | None + cache_creation_input_token_cost: float | None + output_cost_per_reasoning_token: float | None + + +def _custom_token_rates(custom_cost_per_token: CostPerToken) -> EffectiveTokenRates: + input_rate: Final = custom_cost_per_token["input_cost_per_token"] + output_rate: Final = custom_cost_per_token["output_cost_per_token"] + return EffectiveTokenRates( + input_cost_per_token=input_rate, + output_cost_per_token=output_rate, + cache_read_input_token_cost=custom_cost_per_token.get("cache_read_input_token_cost", input_rate), + cache_creation_input_token_cost=custom_cost_per_token.get("cache_creation_input_token_cost", input_rate), + output_cost_per_reasoning_token=output_rate, + ) + + +def _cost_map_token_rates(model_info: ModelInfo | None) -> EffectiveTokenRates: + """Base rates the cost calculator bills flat usage at: a cost-map model without a cache price + bills cache tokens at zero, and one without a reasoning price bills reasoning at the output rate.""" + if model_info is None: + return EffectiveTokenRates(None, None, None, None, None) + sources: Final = (model_info,) + output_rate: Final = _configured_price("output_cost_per_token", sources) + reasoning_rate: Final = _configured_price("output_cost_per_reasoning_token", sources) + return EffectiveTokenRates( + input_cost_per_token=_configured_price("input_cost_per_token", sources), + output_cost_per_token=output_rate, + cache_read_input_token_cost=_configured_price("cache_read_input_token_cost", sources) or 0.0, + cache_creation_input_token_cost=_configured_price("cache_creation_input_token_cost", sources) or 0.0, + output_cost_per_reasoning_token=output_rate if reasoning_rate is None else reasoning_rate, + ) + + +def _usage_for_estimate(request: CostEstimateRequest) -> Usage: + cache_tokens: Final = request.cache_read_input_tokens + request.cache_creation_input_tokens + return Usage( + prompt_tokens=request.input_tokens, + completion_tokens=request.output_tokens, + total_tokens=request.input_tokens + request.output_tokens, + reasoning_tokens=request.reasoning_tokens, + prompt_tokens_details=PromptTokensDetailsWrapper( + cached_tokens=request.cache_read_input_tokens, + cache_creation_tokens=request.cache_creation_input_tokens, + ) + if cache_tokens + else None, ) @@ -530,11 +621,14 @@ async def estimate_cost( - model: Model name (e.g., "gpt-4", "claude-3-opus") - input_tokens: Expected input tokens per request - output_tokens: Expected output tokens per request + - cache_read_input_tokens: Cache-read tokens per request, counted within input_tokens (optional) + - cache_creation_input_tokens: Cache-write tokens per request, counted within input_tokens (optional) + - reasoning_tokens: Reasoning tokens per request, counted within output_tokens (optional) - num_requests_per_day: Number of requests per day (optional) - num_requests_per_month: Number of requests per month (optional) Returns cost breakdown including: - - Per-request costs (input, output, margin) + - Per-request costs (input, output, margin, plus the cache-read, cache-write and reasoning shares) - Daily costs (if num_requests_per_day provided) - Monthly costs (if num_requests_per_month provided) @@ -543,14 +637,15 @@ async def estimate_cost( { "model": "gpt-4", "input_tokens": 1000, + "cache_read_input_tokens": 800, "output_tokens": 500, + "reasoning_tokens": 200, "num_requests_per_day": 100, "num_requests_per_month": 3000 } ``` """ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj - from litellm.types.utils import ModelResponse, Usage # Resolve model name (handles router aliases like 'e-model-router' -> 'azure_ai/gpt-4') resolved: Final = _resolve_model_for_cost_lookup(request.model) @@ -559,15 +654,7 @@ async def estimate_cost( verbose_proxy_logger.debug("Cost estimate: request.model='%s' resolved to '%s'", request.model, resolved_model) - # Create a mock response with usage for completion_cost - mock_response: Final = ModelResponse( - model=resolved_model, - usage=Usage( - prompt_tokens=request.input_tokens, - completion_tokens=request.output_tokens, - total_tokens=request.input_tokens + request.output_tokens, - ), - ) + mock_response: Final = ModelResponse(model=resolved_model, usage=_usage_for_estimate(request)) # Create a logging object to capture cost breakdown litellm_logging_obj: Final = LiteLLMLoggingObj( @@ -597,75 +684,53 @@ async def estimate_cost( }, ) - # Get cost breakdown from the logging object - cost_breakdown: Final = litellm_logging_obj.cost_breakdown - - input_cost: Final = cost_breakdown.get("input_cost", 0.0) if cost_breakdown else 0.0 - output_cost: Final = cost_breakdown.get("output_cost", 0.0) if cost_breakdown else 0.0 - margin_cost: Final = cost_breakdown.get("margin_total_amount", 0.0) if cost_breakdown else 0.0 + per_request: Final = _cost_lines(cost_per_request, litellm_logging_obj.cost_breakdown) + daily: Final = per_request.times(request.num_requests_per_day) + monthly: Final = per_request.times(request.num_requests_per_month) model_info: Final = _lookup_model_info(resolved_model) - mapped_input_price: Final = model_info.get("input_cost_per_token") if model_info is not None else None - mapped_output_price: Final = model_info.get("output_cost_per_token") if model_info is not None else None + rates: Final = ( + _custom_token_rates(resolved.custom_cost_per_token) + if resolved.custom_cost_per_token is not None + else _cost_map_token_rates(model_info) + ) mapped_provider: Final = model_info.get("litellm_provider") if model_info is not None else None - - input_cost_per_token: Final = ( - resolved.custom_cost_per_token["input_cost_per_token"] - if resolved.custom_cost_per_token is not None - else mapped_input_price - ) - output_cost_per_token: Final = ( - resolved.custom_cost_per_token["output_cost_per_token"] - if resolved.custom_cost_per_token is not None - else mapped_output_price - ) custom_llm_provider: Final = mapped_provider if mapped_provider is not None else resolved_provider - # Calculate daily and monthly costs - ( - daily_cost, - daily_input_cost, - daily_output_cost, - daily_margin_cost, - ) = _calculate_period_costs( - num_requests=request.num_requests_per_day, - cost_per_request=cost_per_request, - input_cost=input_cost, - output_cost=output_cost, - margin_cost=margin_cost, - ) - ( - monthly_cost, - monthly_input_cost, - monthly_output_cost, - monthly_margin_cost, - ) = _calculate_period_costs( - num_requests=request.num_requests_per_month, - cost_per_request=cost_per_request, - input_cost=input_cost, - output_cost=output_cost, - margin_cost=margin_cost, - ) - return CostEstimateResponse( model=request.model, input_tokens=request.input_tokens, output_tokens=request.output_tokens, + cache_read_input_tokens=request.cache_read_input_tokens, + cache_creation_input_tokens=request.cache_creation_input_tokens, + reasoning_tokens=request.reasoning_tokens, num_requests_per_day=request.num_requests_per_day, num_requests_per_month=request.num_requests_per_month, - cost_per_request=cost_per_request, - input_cost_per_request=input_cost, - output_cost_per_request=output_cost, - margin_cost_per_request=margin_cost, - daily_cost=daily_cost, - daily_input_cost=daily_input_cost, - daily_output_cost=daily_output_cost, - daily_margin_cost=daily_margin_cost, - monthly_cost=monthly_cost, - monthly_input_cost=monthly_input_cost, - monthly_output_cost=monthly_output_cost, - monthly_margin_cost=monthly_margin_cost, - input_cost_per_token=input_cost_per_token, - output_cost_per_token=output_cost_per_token, + cost_per_request=per_request.total_cost, + input_cost_per_request=per_request.input_cost, + output_cost_per_request=per_request.output_cost, + margin_cost_per_request=per_request.margin_cost, + cache_read_cost_per_request=per_request.cache_read_cost, + cache_creation_cost_per_request=per_request.cache_creation_cost, + reasoning_cost_per_request=per_request.reasoning_cost, + daily_cost=daily.total_cost if daily is not None else None, + daily_input_cost=daily.input_cost if daily is not None else None, + daily_output_cost=daily.output_cost if daily is not None else None, + daily_margin_cost=daily.margin_cost if daily is not None else None, + daily_cache_read_cost=daily.cache_read_cost if daily is not None else None, + daily_cache_creation_cost=daily.cache_creation_cost if daily is not None else None, + daily_reasoning_cost=daily.reasoning_cost if daily is not None else None, + monthly_cost=monthly.total_cost if monthly is not None else None, + monthly_input_cost=monthly.input_cost if monthly is not None else None, + monthly_output_cost=monthly.output_cost if monthly is not None else None, + monthly_margin_cost=monthly.margin_cost if monthly is not None else None, + monthly_cache_read_cost=monthly.cache_read_cost if monthly is not None else None, + monthly_cache_creation_cost=monthly.cache_creation_cost if monthly is not None else None, + monthly_reasoning_cost=monthly.reasoning_cost if monthly is not None else None, + input_cost_per_token=rates.input_cost_per_token, + output_cost_per_token=rates.output_cost_per_token, + cache_read_input_token_cost=rates.cache_read_input_token_cost, + cache_creation_input_token_cost=rates.cache_creation_input_token_cost, + output_cost_per_reasoning_token=rates.output_cost_per_reasoning_token, provider=custom_llm_provider, ) diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index 59f0938e338..7065003839b 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -3874,6 +3874,70 @@ def test_token_type_cost_breakdown_reconciles_with_generic_total(_local_model_co assert text_input_cost + breakdown.cache_read_cost == pytest.approx(prompt_cost) +def _custom_priced_usage() -> Usage: + return Usage( + prompt_tokens=1000, + completion_tokens=500, + total_tokens=1500, + prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=800, cache_creation_tokens=100), + completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=200), + ) + + +def test_token_type_cost_breakdown_prices_custom_pricing_from_its_flat_rates(): + """ + A custom-priced deployment, usually absent from the cost map, used to get zero cache and + reasoning lines while its total already billed cache tokens at the custom cache rates. + The lines must come from the same flat rates: a configured cache rate, else the input + rate for cache tokens and the output rate for reasoning tokens. + """ + from litellm.types.utils import CostPerToken + + breakdown = get_token_type_cost_breakdown( + model="openai/onprem-model", + custom_llm_provider="openai", + usage=_custom_priced_usage(), + custom_cost_per_token=CostPerToken( + input_cost_per_token=1e-6, output_cost_per_token=2e-6, cache_read_input_token_cost=1e-7 + ), + ) + + assert breakdown.cache_read_cost == pytest.approx(800 * 1e-7) + assert breakdown.cache_creation_cost == pytest.approx(100 * 1e-6) + assert breakdown.reasoning_cost == pytest.approx(200 * 2e-6) + + +def test_token_type_cost_breakdown_reconciles_with_custom_pricing_totals(): + from litellm.cost_calculator import cost_per_token + from litellm.types.utils import CostPerToken + + usage = _custom_priced_usage() + custom_cost_per_token = CostPerToken( + input_cost_per_token=1e-6, + output_cost_per_token=2e-6, + cache_read_input_token_cost=1e-7, + cache_creation_input_token_cost=1.25e-6, + ) + + prompt_cost, completion_cost = cost_per_token( + model="openai/onprem-model", + custom_llm_provider="openai", + prompt_tokens=1000, + completion_tokens=500, + usage_object=usage, + custom_cost_per_token=custom_cost_per_token, + ) + breakdown = get_token_type_cost_breakdown( + model="openai/onprem-model", + custom_llm_provider="openai", + usage=usage, + custom_cost_per_token=custom_cost_per_token, + ) + + assert 100 * 1e-6 + breakdown.cache_read_cost + breakdown.cache_creation_cost == pytest.approx(prompt_cost) + assert 300 * 2e-6 + breakdown.reasoning_cost == pytest.approx(completion_cost) + + def test_token_type_cost_breakdown_zero_without_special_tokens(_local_model_cost_map): usage = Usage(prompt_tokens=100, completion_tokens=50, total_tokens=150) diff --git a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py index ec62cc47018..6d3ef2ffea9 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py +++ b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py @@ -8,9 +8,11 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi.testclient import TestClient +from pydantic import ValidationError import litellm +from litellm.proxy._types import CostEstimateRequest from litellm.proxy.management_endpoints.cost_tracking_settings import router from litellm.proxy.proxy_server import app @@ -789,13 +791,13 @@ INPUT_TOKENS = 1000 OUTPUT_TOKENS = 500 -def _router_pricing(**pricing: float) -> MagicMock: +def _router_pricing(model: str = AN_UNDERLYING_MODEL, **pricing: float) -> MagicMock: mock_router = MagicMock() mock_router.get_model_list.return_value = [ { "model_name": AN_ALIAS, "litellm_params": { - "model": AN_UNDERLYING_MODEL, + "model": model, "custom_llm_provider": "openai", **pricing, }, @@ -909,3 +911,184 @@ class TestEstimateCostPeriodTotals: assert response.cost_per_request == pytest.approx(0.0022) assert response.daily_margin_cost == pytest.approx(0.02) assert response.daily_cost == pytest.approx(0.22) + + +CACHE_READ_TOKENS = 800 +CACHE_CREATION_TOKENS = 100 +REASONING_TOKENS = 200 +TEXT_INPUT_TOKENS = INPUT_TOKENS - CACHE_READ_TOKENS - CACHE_CREATION_TOKENS +TEXT_OUTPUT_TOKENS = OUTPUT_TOKENS - REASONING_TOKENS + + +async def _estimate_with_cache_and_reasoning(mock_router: MagicMock | None, model: str = AN_ALIAS, **overrides: int): + return await _estimate( + mock_router, + model=model, + cache_read_input_tokens=CACHE_READ_TOKENS, + cache_creation_input_tokens=CACHE_CREATION_TOKENS, + reasoning_tokens=REASONING_TOKENS, + **overrides, + ) + + +class TestEstimateCostCacheAndReasoningTokens: + @pytest.mark.asyncio + async def test_a_mapped_model_bills_cache_and_reasoning_tokens_at_their_own_rates(self, monkeypatch): + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, + "output_cost_per_reasoning_token": 1e-5, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + + response = await _estimate_with_cache_and_reasoning(None, model=A_MAPPED_MODEL, num_requests_per_day=10) + + assert response.cache_read_cost_per_request == pytest.approx(CACHE_READ_TOKENS * 3e-7) + assert response.cache_creation_cost_per_request == pytest.approx(CACHE_CREATION_TOKENS * 3.75e-6) + assert response.reasoning_cost_per_request == pytest.approx(REASONING_TOKENS * 1e-5) + assert response.input_cost_per_request == pytest.approx( + TEXT_INPUT_TOKENS * 3e-6 + CACHE_READ_TOKENS * 3e-7 + CACHE_CREATION_TOKENS * 3.75e-6 + ) + assert response.output_cost_per_request == pytest.approx(TEXT_OUTPUT_TOKENS * 15e-6 + REASONING_TOKENS * 1e-5) + assert response.cost_per_request == pytest.approx( + response.input_cost_per_request + response.output_cost_per_request + ) + assert response.daily_cache_read_cost == pytest.approx(10 * CACHE_READ_TOKENS * 3e-7) + assert response.daily_cache_creation_cost == pytest.approx(10 * CACHE_CREATION_TOKENS * 3.75e-6) + assert response.daily_reasoning_cost == pytest.approx(10 * REASONING_TOKENS * 1e-5) + assert response.monthly_cache_read_cost is None + assert response.cache_read_input_token_cost == pytest.approx(3e-7) + assert response.cache_creation_input_token_cost == pytest.approx(3.75e-6) + assert response.output_cost_per_reasoning_token == pytest.approx(1e-5) + assert ( + response.cache_read_input_tokens, + response.cache_creation_input_tokens, + response.reasoning_tokens, + ) == (CACHE_READ_TOKENS, CACHE_CREATION_TOKENS, REASONING_TOKENS) + + @pytest.mark.asyncio + async def test_a_model_without_cache_or_reasoning_prices_estimates_what_the_proxy_bills(self, monkeypatch): + """The cost calculator bills cache tokens of a cost-map model without cache prices at zero + and its reasoning tokens at the output rate. The estimate reports those effective rates.""" + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + {"input_cost_per_token": 5e-6, "output_cost_per_token": 6e-6, "litellm_provider": "openai", "mode": "chat"}, + ) + + response = await _estimate_with_cache_and_reasoning(None, model=A_MAPPED_MODEL) + + assert response.cache_read_cost_per_request == 0.0 + assert response.cache_creation_cost_per_request == 0.0 + assert response.reasoning_cost_per_request == pytest.approx(REASONING_TOKENS * 6e-6) + assert response.input_cost_per_request == pytest.approx(TEXT_INPUT_TOKENS * 5e-6) + assert response.cost_per_request == pytest.approx(TEXT_INPUT_TOKENS * 5e-6 + OUTPUT_TOKENS * 6e-6) + assert response.cache_read_input_token_cost == 0.0 + assert response.cache_creation_input_token_cost == 0.0 + assert response.output_cost_per_reasoning_token == pytest.approx(6e-6) + + @pytest.mark.asyncio + async def test_a_request_without_cache_or_reasoning_tokens_estimates_as_before(self, monkeypatch): + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, + "output_cost_per_reasoning_token": 1e-5, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + + response = await _estimate(None, model=A_MAPPED_MODEL, num_requests_per_day=10) + + assert response.cost_per_request == pytest.approx(INPUT_TOKENS * 3e-6 + OUTPUT_TOKENS * 15e-6) + assert response.cache_read_cost_per_request == 0.0 + assert response.cache_creation_cost_per_request == 0.0 + assert response.reasoning_cost_per_request == 0.0 + assert response.daily_cache_read_cost == 0.0 + assert response.daily_reasoning_cost == 0.0 + + @pytest.mark.asyncio + async def test_a_custom_priced_deployment_bills_cache_and_reasoning_tokens_from_its_flat_rates(self): + response = await _estimate_with_cache_and_reasoning( + _router_pricing(input_cost_per_token=1e-6, output_cost_per_token=2e-6, cache_read_input_token_cost=1e-7) + ) + + assert response.cache_read_cost_per_request == pytest.approx(CACHE_READ_TOKENS * 1e-7) + assert response.cache_creation_cost_per_request == pytest.approx(CACHE_CREATION_TOKENS * 1e-6) + assert response.reasoning_cost_per_request == pytest.approx(REASONING_TOKENS * 2e-6) + assert response.cost_per_request == pytest.approx( + TEXT_INPUT_TOKENS * 1e-6 + CACHE_READ_TOKENS * 1e-7 + CACHE_CREATION_TOKENS * 1e-6 + OUTPUT_TOKENS * 2e-6 + ) + assert response.cache_read_input_token_cost == pytest.approx(1e-7) + assert response.cache_creation_input_token_cost == pytest.approx(1e-6) + assert response.output_cost_per_reasoning_token == pytest.approx(2e-6) + + @pytest.mark.asyncio + async def test_a_custom_priced_deployment_of_a_mapped_model_inherits_its_built_in_cache_rates(self, monkeypatch): + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + { + "input_cost_per_token": 5e-6, + "output_cost_per_token": 6e-6, + "cache_read_input_token_cost": 5e-7, + "cache_creation_input_token_cost": 6.25e-6, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + + response = await _estimate_with_cache_and_reasoning( + _router_pricing(model=A_MAPPED_MODEL, input_cost_per_token=1e-6, output_cost_per_token=2e-6) + ) + + assert response.cache_read_cost_per_request == pytest.approx(CACHE_READ_TOKENS * 5e-7) + assert response.cache_creation_cost_per_request == pytest.approx(CACHE_CREATION_TOKENS * 6.25e-6) + assert response.input_cost_per_request == pytest.approx( + TEXT_INPUT_TOKENS * 1e-6 + CACHE_READ_TOKENS * 5e-7 + CACHE_CREATION_TOKENS * 6.25e-6 + ) + assert response.cache_read_input_token_cost == pytest.approx(5e-7) + assert response.cache_creation_input_token_cost == pytest.approx(6.25e-6) + + +class TestCostEstimateRequestTokenSubsets: + def test_cache_tokens_beyond_the_input_tokens_are_rejected(self): + with pytest.raises(ValidationError, match="cannot exceed input_tokens"): + CostEstimateRequest( + model=AN_ALIAS, + input_tokens=INPUT_TOKENS, + output_tokens=OUTPUT_TOKENS, + cache_read_input_tokens=INPUT_TOKENS, + cache_creation_input_tokens=1, + ) + + def test_reasoning_tokens_beyond_the_output_tokens_are_rejected(self): + with pytest.raises(ValidationError, match="cannot exceed output_tokens"): + CostEstimateRequest( + model=AN_ALIAS, + input_tokens=INPUT_TOKENS, + output_tokens=OUTPUT_TOKENS, + reasoning_tokens=OUTPUT_TOKENS + 1, + ) + + def test_the_endpoint_answers_422_when_cache_tokens_exceed_input_tokens(self): + response = client.post( + "/cost/estimate", + headers={"Authorization": "Bearer sk-1234"}, + json={"model": AN_ALIAS, "input_tokens": 1000, "output_tokens": 100, "cache_read_input_tokens": 8000}, + ) + + assert response.status_code == 422 + assert "cannot exceed input_tokens" in response.text diff --git a/tests/test_litellm/test_cost_calculator.py b/tests/test_litellm/test_cost_calculator.py index f8fa2231597..910587d3dc5 100644 --- a/tests/test_litellm/test_cost_calculator.py +++ b/tests/test_litellm/test_cost_calculator.py @@ -3736,6 +3736,56 @@ def test_completion_cost_logs_reasoning_and_cache_breakdown(_local_model_cost_ma assert logging_obj.cost_breakdown["cache_read_cost"] == pytest.approx(100 * 3e-08) +def test_completion_cost_logs_cache_and_reasoning_breakdown_for_custom_pricing(): + """ + A custom-priced deployment bills cache tokens at its custom cache rates, but the + breakdown stored for the spend logs carried no cache or reasoning lines for it. + """ + from datetime import datetime + + from litellm.litellm_core_utils.litellm_logging import Logging + from litellm.types.utils import CompletionTokensDetailsWrapper, CostPerToken + + logging_obj = Logging( + model="openai/onprem-model", + messages=[{"role": "user", "content": "Hello"}], + stream=False, + call_type="completion", + start_time=datetime.now(), + litellm_call_id="custom-pricing-breakdown", + function_id="f", + ) + response = ModelResponse( + model="openai/onprem-model", + usage=Usage( + prompt_tokens=1000, + completion_tokens=500, + total_tokens=1500, + prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=800, cache_creation_tokens=100), + completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=200), + ), + ) + + total = completion_cost( + completion_response=response, + model="openai/onprem-model", + custom_llm_provider="openai", + custom_cost_per_token=CostPerToken( + input_cost_per_token=1e-6, + output_cost_per_token=2e-6, + cache_read_input_token_cost=1e-7, + cache_creation_input_token_cost=1.25e-6, + ), + litellm_logging_obj=logging_obj, + ) + + assert logging_obj.cost_breakdown is not None + assert logging_obj.cost_breakdown["cache_read_cost"] == pytest.approx(800 * 1e-7) + assert logging_obj.cost_breakdown["cache_creation_cost"] == pytest.approx(100 * 1.25e-6) + assert logging_obj.cost_breakdown["reasoning_cost"] == pytest.approx(200 * 2e-6) + assert total == pytest.approx(100 * 1e-6 + 800 * 1e-7 + 100 * 1.25e-6 + 500 * 2e-6) + + def test_cost_per_token_per_second_pricing(monkeypatch): """ Models priced by duration (input/output_cost_per_second) with no per-token rates diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 6fb08445aff..6a9c86cc1b5 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -3301,11 +3301,14 @@ export interface paths { * - model: Model name (e.g., "gpt-4", "claude-3-opus") * - input_tokens: Expected input tokens per request * - output_tokens: Expected output tokens per request + * - cache_read_input_tokens: Cache-read tokens per request, counted within input_tokens (optional) + * - cache_creation_input_tokens: Cache-write tokens per request, counted within input_tokens (optional) + * - reasoning_tokens: Reasoning tokens per request, counted within output_tokens (optional) * - num_requests_per_day: Number of requests per day (optional) * - num_requests_per_month: Number of requests per month (optional) * * Returns cost breakdown including: - * - Per-request costs (input, output, margin) + * - Per-request costs (input, output, margin, plus the cache-read, cache-write and reasoning shares) * - Daily costs (if num_requests_per_day provided) * - Monthly costs (if num_requests_per_month provided) * @@ -3314,7 +3317,9 @@ export interface paths { * { * "model": "gpt-4", * "input_tokens": 1000, + * "cache_read_input_tokens": 800, * "output_tokens": 500, + * "reasoning_tokens": 200, * "num_requests_per_day": 100, * "num_requests_per_month": 3000 * } @@ -26392,6 +26397,18 @@ export interface components { * @description Request body for /cost/estimate endpoint. */ CostEstimateRequest: { + /** + * Cache Creation Input Tokens + * @description Input tokens written to the prompt cache; counted within input_tokens + * @default 0 + */ + cache_creation_input_tokens: number; + /** + * Cache Read Input Tokens + * @description Input tokens read from the prompt cache; counted within input_tokens + * @default 0 + */ + cache_read_input_tokens: number; /** * Input Tokens * @description Expected input tokens per request @@ -26417,17 +26434,65 @@ export interface components { * @description Expected output tokens per request */ output_tokens: number; + /** + * Reasoning Tokens + * @description Reasoning tokens the model emits; counted within output_tokens + * @default 0 + */ + reasoning_tokens: number; }; /** * CostEstimateResponse * @description Response body for /cost/estimate endpoint. */ CostEstimateResponse: { + /** + * Cache Creation Cost Per Request + * @description Cache-write share of input_cost_per_request + * @default 0 + */ + cache_creation_cost_per_request: number; + /** + * Cache Creation Input Token Cost + * @description Rate billed per cache-write token + */ + cache_creation_input_token_cost?: number | null; + /** + * Cache Creation Input Tokens + * @default 0 + */ + cache_creation_input_tokens: number; + /** + * Cache Read Cost Per Request + * @description Cache-read share of input_cost_per_request + * @default 0 + */ + cache_read_cost_per_request: number; + /** + * Cache Read Input Token Cost + * @description Rate billed per cache-read token + */ + cache_read_input_token_cost?: number | null; + /** + * Cache Read Input Tokens + * @default 0 + */ + cache_read_input_tokens: number; /** * Cost Per Request * @description Total cost per request (includes margin) */ cost_per_request: number; + /** + * Daily Cache Creation Cost + * @description Cache-write share of daily_input_cost + */ + daily_cache_creation_cost?: number | null; + /** + * Daily Cache Read Cost + * @description Cache-read share of daily_input_cost + */ + daily_cache_read_cost?: number | null; /** * Daily Cost * @description Total daily cost (includes margin) @@ -26448,6 +26513,11 @@ export interface components { * @description Daily output token cost */ daily_output_cost?: number | null; + /** + * Daily Reasoning Cost + * @description Reasoning share of daily_output_cost + */ + daily_reasoning_cost?: number | null; /** * Input Cost Per Request * @description Input token cost per request (before margin) @@ -26465,6 +26535,16 @@ export interface components { margin_cost_per_request: number; /** Model */ model: string; + /** + * Monthly Cache Creation Cost + * @description Cache-write share of monthly_input_cost + */ + monthly_cache_creation_cost?: number | null; + /** + * Monthly Cache Read Cost + * @description Cache-read share of monthly_input_cost + */ + monthly_cache_read_cost?: number | null; /** * Monthly Cost * @description Total monthly cost (includes margin) @@ -26485,10 +26565,20 @@ export interface components { * @description Monthly output token cost */ monthly_output_cost?: number | null; + /** + * Monthly Reasoning Cost + * @description Reasoning share of monthly_output_cost + */ + monthly_reasoning_cost?: number | null; /** Num Requests Per Day */ num_requests_per_day?: number | null; /** Num Requests Per Month */ num_requests_per_month?: number | null; + /** + * Output Cost Per Reasoning Token + * @description Rate billed per reasoning token + */ + output_cost_per_reasoning_token?: number | null; /** * Output Cost Per Request * @description Output token cost per request (before margin) @@ -26500,6 +26590,17 @@ export interface components { output_tokens: number; /** Provider */ provider?: string | null; + /** + * Reasoning Cost Per Request + * @description Reasoning share of output_cost_per_request + * @default 0 + */ + reasoning_cost_per_request: number; + /** + * Reasoning Tokens + * @default 0 + */ + reasoning_tokens: number; }; /** CreateCredentialItem */ CreateCredentialItem: { From 7bff9bf9a2594d3cd5dd6a80a44e4e3b1cd38f61 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 16:22:32 -0700 Subject: [PATCH 047/223] fix(batches): handle provider cancellation and file cleanup gaps --- litellm/llms/bedrock/files/transformation.py | 70 ++++++++------ tests/e2e/batches/COVERAGE.md | 7 +- tests/e2e/batches/batch_cleanup.py | 63 +++++++++--- tests/e2e/batches/test_batch_cleanup.py | 69 +++++++++++++- tests/e2e/batches/test_batches_e2e.py | 14 +-- .../test_bedrock_files_transformation.py | 95 ++++++++++++++++--- 6 files changed, 258 insertions(+), 60 deletions(-) diff --git a/litellm/llms/bedrock/files/transformation.py b/litellm/llms/bedrock/files/transformation.py index 33b27943ad8..90b539ff37c 100644 --- a/litellm/llms/bedrock/files/transformation.py +++ b/litellm/llms/bedrock/files/transformation.py @@ -7,7 +7,7 @@ from contextlib import suppress from functools import cache from itertools import chain from types import MappingProxyType -from typing import Any, Final, TypeAlias, TypedDict +from typing import Any, Final, Literal, TypeAlias, TypedDict from urllib.parse import unquote import httpx @@ -60,11 +60,8 @@ from litellm.utils import get_llm_provider from ..base_aws_llm import BaseAWSLLM from ..common_utils import BedrockError, merge_bedrock_aws_request_params, resolve_s3_encryption_key_id -# litellm_params key used to hand the SigV4-signed GET headers from -# `transform_file_content_request` to `validate_environment` (the only hook -# the shared file-content HTTP handler exposes for setting request headers). -# Same pattern as the `upload_url` handoff in `transform_create_file_request`. -S3_SIGNED_GET_HEADERS_PARAM: Final = "_s3_signed_get_headers" +S3_SIGNED_REQUEST_HEADERS_PARAM: Final = "_s3_signed_request_headers" +S3_DELETE_FILE_ID_PARAM: Final = "_s3_delete_file_id" # litellm_params key carrying the size of the body uploaded to S3, handed from # `transform_create_file_request` to `transform_create_file_response`. @@ -291,7 +288,7 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig): ) -> dict: result: Final[dict[str, object]] = {} result.update(headers) - signed_headers: Final = litellm_params.pop(S3_SIGNED_GET_HEADERS_PARAM, None) + signed_headers: Final = litellm_params.pop(S3_SIGNED_REQUEST_HEADERS_PARAM, None) if isinstance(signed_headers, Mapping): result.update(signed_headers) # any-ok: untyped handoff headers # otherwise no extra headers - AWS credentials are handled by BaseAWSLLM @@ -1187,18 +1184,31 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig): def transform_delete_file_request( self, file_id: str, - optional_params: dict, - litellm_params: dict, - ) -> tuple[str, dict]: - raise NotImplementedError("BedrockFilesConfig does not support file deletion") + optional_params: Mapping[str, object], + litellm_params: MutableMapping[str, object], + ) -> tuple[str, dict[str, str]]: + request: Final = self._transform_s3_file_request( + file_id=file_id, method="DELETE", optional_params=optional_params, litellm_params=litellm_params + ) + litellm_params[S3_DELETE_FILE_ID_PARAM] = file_id + return request def transform_delete_file_response( self, raw_response: httpx.Response, logging_obj: LiteLLMLoggingObj, - litellm_params: dict, + litellm_params: Mapping[str, object], ) -> FileDeleted: - raise NotImplementedError("BedrockFilesConfig does not support file deletion") + if raw_response.status_code != 204: + raise BedrockError( + status_code=raw_response.status_code if raw_response.status_code >= 400 else 502, + message=raw_response.text or f"S3 file deletion returned HTTP {raw_response.status_code}", + headers=raw_response.headers, + ) + file_id: Final = litellm_params.get(S3_DELETE_FILE_ID_PARAM) + if not isinstance(file_id, str) or not file_id: + raise ValueError("Missing file id for Bedrock file deletion response") + return FileDeleted(id=file_id, deleted=True, object="file") def transform_list_files_request( self, @@ -1233,6 +1243,18 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig): if not file_id: raise ValueError("file_id is required for Bedrock file content retrieval") + return self._transform_s3_file_request( + file_id=file_id, method="GET", optional_params=optional_params, litellm_params=litellm_params + ) + + def _transform_s3_file_request( + self, + *, + file_id: str, + method: Literal["GET", "DELETE"], + optional_params: Mapping[str, object], + litellm_params: MutableMapping[str, object], + ) -> tuple[str, dict[str, str]]: s3_uri: Final = extract_s3_uri_from_file_id(file_id) bucket_name, object_key = _validate_file_id_against_configured_buckets( s3_uri=s3_uri, @@ -1240,40 +1262,32 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig): allow_legacy_cloud_file_ids=should_allow_legacy_cloud_file_ids(litellm_params), ) - # The shared file-content handler passes optional_params={}, so AWS - # credentials/region arrive via litellm_params here (unlike the upload - # path). s3_region_name wins over aws_region_name, same priority as - # get_complete_file_url above. - merged_params: Final[dict[str, object]] = {} - merged_params.update(litellm_params) - merged_params.update(optional_params) - request_params: Final = _BedrockS3RequestParams.model_validate(merged_params) + request_params: Final = _BedrockS3RequestParams.model_validate({**litellm_params, **optional_params}) region_preference: Final = request_params.s3_region_name or request_params.aws_region_name region_params: Final[dict[str, str | None]] = {"aws_region_name": region_preference} aws_region_name: Final = self._get_aws_region_name(optional_params=region_params, model="") - s3_endpoint_url = ( + s3_endpoint_url: Final = ( request_params.s3_endpoint_url or f"https://s3.{aws_region_name}.{get_aws_dns_suffix(aws_region_name)}" ).rstrip("/") url: Final = f"{s3_endpoint_url}/{bucket_name}/{encode_s3_object_key_for_url(object_key)}" - litellm_params[S3_SIGNED_GET_HEADERS_PARAM] = self._sign_s3_get_request( + litellm_params[S3_SIGNED_REQUEST_HEADERS_PARAM] = self._sign_s3_request_without_body( api_base=url, aws_region_name=aws_region_name, request_params=request_params, + method=method, ) return url, {} - def _sign_s3_get_request( + def _sign_s3_request_without_body( self, api_base: str, aws_region_name: str, request_params: _BedrockS3RequestParams, + method: Literal["GET", "DELETE"] = "GET", ) -> dict[str, str]: - """ - SigV4-sign an S3 GetObject request, mirroring `_sign_s3_request` (PUT). - """ try: import hashlib @@ -1297,7 +1311,7 @@ class BedrockFilesConfig(BaseAWSLLM, BaseFilesConfig): empty_body_hash: Final = hashlib.sha256(b"").hexdigest() aws_request: Final = AWSRequest( # any-ok: botocore AWSRequest is untyped - method="GET", + method=method, url=api_base, headers={"x-amz-content-sha256": empty_body_hash}, ) diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md index f95ea1f2649..ca44fc95e25 100644 --- a/tests/e2e/batches/COVERAGE.md +++ b/tests/e2e/batches/COVERAGE.md @@ -131,7 +131,12 @@ failures up to three times. Teardown attempts every registered cleanup before reporting failures as test errors. Already deleted files and batches that are terminal are safe to clean up again. Managed batch cancellation polls for up to eleven minutes before input deletion: the ten-minute provider window plus a propagation margin. -Raw and model-encoded inputs can be deleted after cancellation is accepted +Accepted cancellation may still report validating or in_progress while the provider +updates its state. Raw and model-encoded batches are polled until cancelling or +terminal before input deletion. OpenAI and Azure lifecycle cleanup also deletes +output and error files returned by terminal batches. Bedrock deletion uses a signed S3 DELETE +restricted to the configured storage buckets and managed file prefixes. The low-RPM +test submits with its restricted key and cleans up with the test administrator key Azure input uploads request `expires_after` anchored to `created_at` with `seconds=1209600`, and the lifecycle tests check the returned expiry. This is a diff --git a/tests/e2e/batches/batch_cleanup.py b/tests/e2e/batches/batch_cleanup.py index 722df0c29bc..9284882ad82 100644 --- a/tests/e2e/batches/batch_cleanup.py +++ b/tests/e2e/batches/batch_cleanup.py @@ -1,16 +1,17 @@ +from builtins import ExceptionGroup from collections.abc import Callable from itertools import count from time import monotonic, sleep from typing import Final, Protocol -from pydantic import BaseModel - from batch_client import BatchObject, FileDeleteResponse from capabilities import is_managed_id from e2e_http import NetworkError, RateLimitedError, Result, Success, UnknownApiError +from pydantic import BaseModel CLEANUP_DELAYS: Final = (1.0, 2.0, 4.0) BATCH_TERMINAL_STATUSES: Final = frozenset({"completed", "failed", "expired", "cancelled"}) +BATCH_PENDING_STATUSES: Final = frozenset({"validating", "in_progress", "finalizing", "cancelling"}) BATCH_CANCEL_TIMEOUT_SECONDS: Final = 660.0 BATCH_CANCEL_POLL_SECONDS: Final = 10.0 @@ -63,6 +64,7 @@ def cleanup_batch( *, key: str, provider: str | None = None, + delete_output_files: bool = False, wait: Callable[[float], None] = sleep, clock: Callable[[], float] = monotonic, ) -> None: @@ -72,18 +74,28 @@ def cleanup_batch( f"Retrieve batch {batch_id} for cleanup", ) if fetched.status in BATCH_TERMINAL_STATUSES: + if delete_output_files: + _cleanup_batch_outputs(client, fetched, key=key, provider=provider) return if fetched.status == "cancelling" and not needs_terminal_state: return - if fetched.status != "cancelling": - result: Final = cleanup_result(lambda: client.cancel_batch(batch_id, key=key, provider=provider)) - if not (isinstance(result, UnknownApiError) and result.status_code in {400, 409}): - cancelled: Final = _require_cleanup_success(result, f"Cancel batch {batch_id}") - assert cancelled.status in BATCH_TERMINAL_STATUSES | {"cancelling"}, ( - f"Cancel batch {batch_id} left status {cancelled.status}" - ) - if not needs_terminal_state: - return + result: Final = ( + Success(status_code=200, data=fetched) + if fetched.status == "cancelling" + else cleanup_result(lambda: client.cancel_batch(batch_id, key=key, provider=provider)) + ) + conflicted: Final = isinstance(result, UnknownApiError) and result.status_code in {400, 409} + if not conflicted: + cancelled: Final = _require_cleanup_success(result, f"Cancel batch {batch_id}") + assert cancelled.status in BATCH_TERMINAL_STATUSES | BATCH_PENDING_STATUSES, ( + f"Cancel batch {batch_id} left status {cancelled.status}" + ) + if cancelled.status in BATCH_TERMINAL_STATUSES: + if delete_output_files: + _cleanup_batch_outputs(client, cancelled, key=key, provider=provider) + return + if cancelled.status == "cancelling" and not needs_terminal_state: + return deadline: Final = clock() + BATCH_CANCEL_TIMEOUT_SECONDS for current in ( _require_cleanup_success( @@ -93,11 +105,36 @@ def cleanup_batch( for _ in count() ): if current.status in BATCH_TERMINAL_STATUSES: + if delete_output_files: + _cleanup_batch_outputs(client, current, key=key, provider=provider) return - assert current.status == "cancelling", f"Cancel batch {batch_id} left status {current.status}" - if not needs_terminal_state: + assert current.status in ({"cancelling"} if conflicted else BATCH_PENDING_STATUSES), ( + f"Cancel batch {batch_id} left status {current.status}" + ) + if current.status == "cancelling" and not needs_terminal_state: return assert clock() < deadline, ( f"Batch {batch_id} cancellation did not finish within {BATCH_CANCEL_TIMEOUT_SECONDS}s" ) wait(BATCH_CANCEL_POLL_SECONDS) + + +def _cleanup_batch_outputs(client: BatchCleanupClient, batch: BatchObject, *, key: str, provider: str | None) -> None: + errors: Final = tuple( + error + for file_id in dict.fromkeys((batch.output_file_id, batch.error_file_id)) + if file_id is not None and file_id != batch.input_file_id + if (error := _output_cleanup_error(client, file_id, key=key, provider=provider)) is not None + ) + if errors: + raise ExceptionGroup(f"Batch {batch.id} output cleanup failed", errors) + + +def _output_cleanup_error( + client: BatchCleanupClient, file_id: str, *, key: str, provider: str | None +) -> Exception | None: + try: + cleanup_file(client, file_id, key=key, provider=provider) + except Exception as error: + return error + return None diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py index 66b7079ccc2..a875aee719b 100644 --- a/tests/e2e/batches/test_batch_cleanup.py +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -4,7 +4,6 @@ from dataclasses import dataclass, field from typing import Final import pytest - from batch_cleanup import BATCH_CANCEL_TIMEOUT_SECONDS, CLEANUP_DELAYS, cleanup_batch, cleanup_file, cleanup_result from batch_client import AZURE_FILE_EXPIRY_SECONDS, BatchObject, FileDeleteResponse, batch_upload_form from capabilities import CAPABILITIES, Capability @@ -219,6 +218,74 @@ class TestBatchCancellation: cleanup_batch(client, "batch-1", key="test-key", provider="azure") client.calls.assert_done() + @pytest.mark.parametrize("batch_id", ["batch-1", MANAGED_BATCH_ID]) + @pytest.mark.parametrize("pending_status", ["validating", "in_progress"]) + def test_accepted_cancellation_waits_through_stale_provider_status( + self, batch_id: str, pending_status: str + ) -> None: + client: Final = CleanupClient( + calls=ExpectedCalls( + iter( + ( + f"retrieve vertex_ai {batch_id}", + f"cancel vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + "delete vertex_ai file-1", + "delete key test-key", + ) + ) + ), + batches=iter((batch("validating"), batch(pending_status), batch(pending_status), batch("cancelled"))), + cancellations=iter((batch(pending_status),)), + files=iter((deleted_file(),)), + ) + delays: Final = ExpectedCalls(iter((10.0, 10.0))) + manager: Final = ResourceManager(client=client, strict_cleanup=True) + key: Final = manager.key() + manager.defer(lambda: cleanup_file(client, "file-1", key=key, provider="vertex_ai")) + manager.defer(lambda: cleanup_batch(client, batch_id, key=key, provider="vertex_ai", wait=delays)) + manager.teardown() + client.calls.assert_done() + delays.assert_done() + + @pytest.mark.parametrize("output_delete_fails", [False, True]) + def test_batch_that_completed_before_cleanup_deletes_output_and_error_files( + self, output_delete_fails: bool + ) -> None: + client: Final = CleanupClient( + calls=ExpectedCalls( + iter(("retrieve openai batch-1", "delete openai file-output", "delete openai file-error")) + ), + batches=iter( + ( + Success( + status_code=200, + data=BatchObject( + id="batch-1", + status="completed", + input_file_id="file-input", + output_file_id="file-output", + error_file_id="file-error", + ), + ), + ) + ), + files=iter( + ( + UnknownApiError(status_code=403, body="forbidden") if output_delete_fails else deleted_file(), + deleted_file(), + ) + ), + ) + if output_delete_fails: + with pytest.raises(ExceptionGroup, match="output cleanup failed"): + cleanup_batch(client, "batch-1", key="test-key", provider="openai", delete_output_files=True) + else: + cleanup_batch(client, "batch-1", key="test-key", provider="openai", delete_output_files=True) + client.calls.assert_done() + @pytest.mark.parametrize("status", ["completed", "in_progress"]) def test_cancellation_conflict_is_accepted_only_when_batch_became_inactive(self, status: str) -> None: client: Final = CleanupClient( diff --git a/tests/e2e/batches/test_batches_e2e.py b/tests/e2e/batches/test_batches_e2e.py index 1b4a6ed266f..c4b699190b8 100644 --- a/tests/e2e/batches/test_batches_e2e.py +++ b/tests/e2e/batches/test_batches_e2e.py @@ -25,7 +25,7 @@ from datetime import datetime, timedelta, timezone import pytest from pydantic import BaseModel -from e2e_config import PROXY_BASE_URL, unique_marker +from e2e_config import MASTER_KEY, PROXY_BASE_URL, unique_marker from batch_cleanup import cleanup_batch, cleanup_file from batch_client import ( @@ -257,7 +257,9 @@ def test_batch_lifecycle( require_successful_call(created) batch = BatchObject.model_validate_json(created.body) resources.defer( - lambda: cleanup_batch(client, batch.id, key=key, provider=provider) + lambda: cleanup_batch( + client, batch.id, key=key, provider=provider, delete_output_files=cap.provider in {"openai", "azure"} + ) ) assert batch.id, f"create returned no batch id (body={created.body[:200]})" @@ -801,7 +803,7 @@ class TestBatchEnqueuedTokenLimit: """ def _upload_batch_file( - self, client: BatchClient, resources: ResourceManager, key: str + self, client: BatchClient, resources: ResourceManager, key: str, *, cleanup_key: str | None = None ) -> FileObject: file = unwrap( client.upload_file( @@ -811,7 +813,7 @@ class TestBatchEnqueuedTokenLimit: key=key, ) ) - resources.defer(lambda: cleanup_file(client, file.id, key=key)) + resources.defer(lambda: cleanup_file(client, file.id, key=cleanup_key or key)) return file def _generate_enqueued_key( @@ -848,7 +850,7 @@ class TestBatchEnqueuedTokenLimit: marker="rpm", rpm_limit=BATCH_RL_RPM_LIMIT, ) - file = self._upload_batch_file(client, resources, key) + file = self._upload_batch_file(client, resources, key, cleanup_key=MASTER_KEY) created = client.create_batch(body=BatchCreateBody(input_file_id=file.id), key=key) @@ -859,7 +861,7 @@ class TestBatchEnqueuedTokenLimit: ) require_successful_call(created) batch = BatchObject.model_validate_json(created.body) - resources.defer(lambda: cleanup_batch(client, batch.id, key=key)) + resources.defer(lambda: cleanup_batch(client, batch.id, key=MASTER_KEY, delete_output_files=True)) @pytest.mark.covers( "quota_management.ratelimit.batch_enqueued_tokens.blocks_when_exhausted", diff --git a/tests/test_litellm/llms/bedrock/files/test_bedrock_files_transformation.py b/tests/test_litellm/llms/bedrock/files/test_bedrock_files_transformation.py index 541c0db15d8..2c02a58663e 100644 --- a/tests/test_litellm/llms/bedrock/files/test_bedrock_files_transformation.py +++ b/tests/test_litellm/llms/bedrock/files/test_bedrock_files_transformation.py @@ -5,6 +5,8 @@ Test bedrock files transformation functionality import json import os from collections.abc import Mapping +from contextlib import AsyncExitStack, closing +from typing import Final from unittest.mock import MagicMock from urllib.parse import unquote, urlparse @@ -1855,6 +1857,77 @@ class TestBedrockBatchNonChatEndpointRecords: ] +class TestBedrockFileDeletion: + S3_URI: Final = "s3://my-bucket/litellm-bedrock-files-model-abc.jsonl" + URL: Final = "https://s3.us-west-2.amazonaws.com/my-bucket/litellm-bedrock-files-model-abc.jsonl" + + def test_delete_file_sends_signed_delete_and_returns_matching_id(self, monkeypatch: pytest.MonkeyPatch) -> None: + import httpx + import respx + + import litellm + from litellm.llms.custom_httpx.http_handler import HTTPHandler + + monkeypatch.setenv("AWS_S3_BUCKET_NAME", "my-bucket") + with respx.mock, closing(HTTPHandler()) as client: + route: Final = respx.delete(self.URL).mock(return_value=httpx.Response(204)) + deleted: Final = litellm.file_delete( + file_id=self.S3_URI, custom_llm_provider="bedrock", client=client, + aws_access_key_id="AKIAEXAMPLE", aws_secret_access_key="test-secret", aws_region_name="us-west-2", + ) + assert route.call_count == 1 + request: Final = route.calls[0].request + assert request.content == b"" + signed: Final = AWSRequest(method="DELETE", url=self.URL, headers={ + "X-Amz-Date": request.headers["X-Amz-Date"], + "X-Amz-Content-SHA256": request.headers["X-Amz-Content-SHA256"], + }) + signed.context["timestamp"] = request.headers["X-Amz-Date"] + auth: Final = S3SigV4Auth(Credentials("AKIAEXAMPLE", "test-secret"), "s3", "us-west-2") + signature: Final = auth.signature(auth.string_to_sign(signed, auth.canonical_request(signed)), signed) + assert request.headers["Authorization"].endswith(f"Signature={signature}") + assert deleted.id == self.S3_URI and deleted.deleted is True + + @pytest.mark.asyncio + async def test_adelete_file_propagates_s3_errors(self, monkeypatch: pytest.MonkeyPatch) -> None: + import httpx + import respx + + import litellm + from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler + + monkeypatch.setenv("AWS_S3_BUCKET_NAME", "my-bucket") + monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True") + async with AsyncExitStack() as stack: + client: Final = AsyncHTTPHandler() + stack.push_async_callback(client.close) + with respx.mock: + route: Final = respx.delete(self.URL).mock( + return_value=httpx.Response(403, content=b"AccessDenied") + ) + from litellm.llms.bedrock.common_utils import BedrockError + + with pytest.raises(BedrockError, match="AccessDenied"): + await litellm.afile_delete( + file_id=self.S3_URI, custom_llm_provider="bedrock", client=client, + aws_access_key_id="AKIAEXAMPLE", aws_secret_access_key="test-secret", aws_region_name="us-west-2", + ) + assert route.call_count == 1 + + @pytest.mark.parametrize("file_id, message", [ + ("s3://other-bucket/litellm-bedrock-files-model-abc.jsonl", "configured storage bucket"), + ("s3://my-bucket/private/data.jsonl", "LiteLLM-managed"), + ]) + def test_delete_rejects_untrusted_objects_before_signing( + self, file_id: str, message: str, monkeypatch: pytest.MonkeyPatch + ) -> None: + from litellm.llms.bedrock.files.transformation import BedrockFilesConfig + + monkeypatch.setenv("AWS_S3_BUCKET_NAME", "my-bucket") + with pytest.raises(ValueError, match=message): + BedrockFilesConfig().transform_delete_file_request(file_id=file_id, optional_params={}, litellm_params={}) + + class TestBedrockFileContentTransformation: """SigV4-signed S3 GetObject retrieval of Bedrock batch output files.""" @@ -1873,7 +1946,7 @@ class TestBedrockFileContentTransformation: import hashlib from litellm.llms.bedrock.files.transformation import ( - S3_SIGNED_GET_HEADERS_PARAM, + S3_SIGNED_REQUEST_HEADERS_PARAM, BedrockFilesConfig, ) @@ -1889,7 +1962,7 @@ class TestBedrockFileContentTransformation: assert url == self.EXPECTED_URL assert params == {} - signed_headers = litellm_params[S3_SIGNED_GET_HEADERS_PARAM] + signed_headers = litellm_params[S3_SIGNED_REQUEST_HEADERS_PARAM] content_hashes = { value for name, value in signed_headers.items() @@ -2139,7 +2212,7 @@ class TestBedrockFileContentTransformation: def test_s3_region_name_wins_for_content_signing(self, monkeypatch): """s3_region_name must override aws_region_name for both the URL and the signature.""" from litellm.llms.bedrock.files.transformation import ( - S3_SIGNED_GET_HEADERS_PARAM, + S3_SIGNED_REQUEST_HEADERS_PARAM, BedrockFilesConfig, ) @@ -2154,17 +2227,17 @@ class TestBedrockFileContentTransformation: ) assert url.startswith("https://s3.eu-west-1.amazonaws.com/") - authorization = litellm_params[S3_SIGNED_GET_HEADERS_PARAM]["Authorization"] + authorization = litellm_params[S3_SIGNED_REQUEST_HEADERS_PARAM]["Authorization"] assert "/eu-west-1/s3/aws4_request" in authorization def test_validate_environment_merges_and_pops_signed_get_headers(self): from litellm.llms.bedrock.files.transformation import ( - S3_SIGNED_GET_HEADERS_PARAM, + S3_SIGNED_REQUEST_HEADERS_PARAM, BedrockFilesConfig, ) litellm_params = { - S3_SIGNED_GET_HEADERS_PARAM: {"Authorization": "AWS4-HMAC-SHA256 test"} + S3_SIGNED_REQUEST_HEADERS_PARAM: {"Authorization": "AWS4-HMAC-SHA256 test"} } headers = BedrockFilesConfig().validate_environment( @@ -2179,7 +2252,7 @@ class TestBedrockFileContentTransformation: "x-custom": "kept", "Authorization": "AWS4-HMAC-SHA256 test", } - assert S3_SIGNED_GET_HEADERS_PARAM not in litellm_params + assert S3_SIGNED_REQUEST_HEADERS_PARAM not in litellm_params def test_transform_file_content_response_wraps_binary_content(self): import httpx @@ -2379,7 +2452,7 @@ class TestBedrockFilesS3SignatureEncoding: self, monkeypatch: pytest.MonkeyPatch ) -> None: from litellm.llms.bedrock.files.transformation import ( - S3_SIGNED_GET_HEADERS_PARAM, + S3_SIGNED_REQUEST_HEADERS_PARAM, BedrockFilesConfig, ) @@ -2402,7 +2475,7 @@ class TestBedrockFilesS3SignatureEncoding: method="GET", url=url, body=None, - headers=litellm_params[S3_SIGNED_GET_HEADERS_PARAM], + headers=litellm_params[S3_SIGNED_REQUEST_HEADERS_PARAM], ) @@ -2457,7 +2530,7 @@ def test_sign_s3_request_assumes_role_with_external_id(monkeypatch): assert "ASIAFILESPUTROLE" in authorization -def test_sign_s3_get_request_assumes_role_with_external_id(monkeypatch): +def test_sign_s3_request_without_body_assumes_role_with_external_id(monkeypatch): """A trust policy requiring sts:ExternalId must be satisfied when signing the S3 download request.""" import datetime from unittest.mock import patch @@ -2504,7 +2577,7 @@ def test_sign_s3_get_request_assumes_role_with_external_id(monkeypatch): assert request_params.aws_external_id == "external-id-files-get" with patch.object(boto3, "client", return_value=FakeSTSClient()): - signed_headers = BedrockFilesConfig()._sign_s3_get_request( + signed_headers = BedrockFilesConfig()._sign_s3_request_without_body( api_base="https://s3.us-east-1.amazonaws.com/safe-bucket/litellm-bedrock-files-model-id-abc.jsonl", aws_region_name="us-east-1", request_params=request_params, From 4ab5719ff9e0770ecb9f2d1b53c4caf58f19e5db Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 16:36:27 -0700 Subject: [PATCH 048/223] test(batches): use immutable expectations with explicit test doubles --- litellm/files/main.py | 2 +- tests/e2e/batches/test_batch_cleanup.py | 187 ++++++++++++------------ 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/litellm/files/main.py b/litellm/files/main.py index 19da77b7364..218518eb3cd 100644 --- a/litellm/files/main.py +++ b/litellm/files/main.py @@ -31,7 +31,7 @@ FileCreateProvider = Literal[ FileRetrieveProvider = Literal[ "openai", "azure", "gemini", "vertex_ai", "hosted_vllm", "litellm_proxy", "manus", "anthropic" ] -FileDeleteProvider = Literal["openai", "azure", "gemini", "litellm_proxy", "manus", "anthropic"] +FileDeleteProvider = Literal["openai", "azure", "gemini", "bedrock", "litellm_proxy", "manus", "anthropic"] FileListProvider = Literal["openai", "azure", "litellm_proxy", "manus", "anthropic"] import litellm from litellm import get_secret_str diff --git a/tests/e2e/batches/test_batch_cleanup.py b/tests/e2e/batches/test_batch_cleanup.py index a875aee719b..d0038139dcf 100644 --- a/tests/e2e/batches/test_batch_cleanup.py +++ b/tests/e2e/batches/test_batch_cleanup.py @@ -1,7 +1,7 @@ from builtins import ExceptionGroup -from collections.abc import Iterator -from dataclasses import dataclass, field +from collections.abc import Callable from typing import Final +from unittest.mock import Mock, call import pytest from batch_cleanup import BATCH_CANCEL_TIMEOUT_SECONDS, CLEANUP_DELAYS, cleanup_batch, cleanup_file, cleanup_result @@ -15,35 +15,43 @@ MANAGED_FILE_ID: Final = "bGl0ZWxsbV9wcm94eTtmaWxlLTE=" MANAGED_BATCH_ID: Final = "bGl0ZWxsbV9wcm94eTtiYXRjaC0x" -@dataclass(frozen=True, slots=True) class ExpectedCalls[T]: - values: Iterator[T] + def __init__(self, values: tuple[T, ...]) -> None: + self.values: Final = values + self.recorder: Final = Mock() def __call__(self, value: T) -> None: - assert next(self.values, None) == value + self.recorder(value) def assert_done(self) -> None: - assert tuple(self.values) == () + assert tuple(self.recorder.call_args_list) == tuple(call(value) for value in self.values) -@dataclass(frozen=True, slots=True) class CleanupClient: - calls: ExpectedCalls[str] - files: Iterator[Result[FileDeleteResponse]] = field(default_factory=lambda: iter(())) - batches: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) - cancellations: Iterator[Result[BatchObject]] = field(default_factory=lambda: iter(())) + def __init__( + self, + *, + calls: ExpectedCalls[str], + files: tuple[Result[FileDeleteResponse], ...] = (), + batches: tuple[Result[BatchObject], ...] = (), + cancellations: tuple[Result[BatchObject], ...] = (), + ) -> None: + self.calls: Final = calls + self.file_response: Final[Callable[[], Result[FileDeleteResponse]]] = Mock(side_effect=files) + self.batch_response: Final[Callable[[], Result[BatchObject]]] = Mock(side_effect=batches) + self.cancel_response: Final[Callable[[], Result[BatchObject]]] = Mock(side_effect=cancellations) def delete_file(self, file_id: str, *, key: str, provider: str | None = None) -> Result[FileDeleteResponse]: self.calls(f"delete {provider} {file_id}") - return next(self.files) + return self.file_response() def retrieve_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: self.calls(f"retrieve {provider} {batch_id}") - return next(self.batches) + return self.batch_response() def cancel_batch(self, batch_id: str, *, key: str, provider: str | None = None) -> Result[BatchObject]: self.calls(f"cancel {provider} {batch_id}") - return next(self.cancellations) + return self.cancel_response() def generate_key(self, body: KeyGenerateBody) -> str: return "test-key" @@ -68,17 +76,15 @@ class TestFileCleanup: response: Final = Success( status_code=200, data=FileDeleteResponse.model_validate({"id": MANAGED_FILE_ID, "object": "file"}) ) - client: Final = CleanupClient( - calls=ExpectedCalls(iter((f"delete None {MANAGED_FILE_ID}",))), files=iter((response,)) - ) + client: Final = CleanupClient(calls=ExpectedCalls((f"delete None {MANAGED_FILE_ID}",)), files=(response,)) cleanup_file(client, MANAGED_FILE_ID, key="test-key") client.calls.assert_done() @pytest.mark.parametrize("file_id", ["file-1", MANAGED_FILE_ID]) def test_a_success_status_without_a_deletion_confirmation_is_rejected(self, file_id: str) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter((f"delete None {file_id}",))), - files=iter((Success(status_code=200, data=FileDeleteResponse(id=file_id)),)), + calls=ExpectedCalls((f"delete None {file_id}",)), + files=(Success(status_code=200, data=FileDeleteResponse(id=file_id)),), ) with pytest.raises(AssertionError, match="did not confirm deletion"): cleanup_file(client, file_id, key="test-key") @@ -88,15 +94,15 @@ class TestFileCleanup: def test_deletes_raw_files_through_the_upload_provider(self, cap: Capability) -> None: expected_provider: Final = cap.provider if cap.scenario in {"model_param", "provider_fallback"} else None client: Final = CleanupClient( - calls=ExpectedCalls(iter((f"delete {expected_provider} file-1",))), files=iter((deleted_file(),)) + calls=ExpectedCalls((f"delete {expected_provider} file-1",)), files=(deleted_file(),) ) cleanup_file(client, "file-1", key="test-key", provider=cap.file_provider) client.calls.assert_done() def test_failed_delete_is_reported_after_remaining_resources_are_cleaned(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("delete azure file-1", "delete key test-key"))), - files=iter((UnknownApiError(status_code=403, body="secret response"),)), + calls=ExpectedCalls(("delete azure file-1", "delete key test-key")), + files=(UnknownApiError(status_code=403, body="secret response"),), ) manager: Final = ResourceManager(client=client, strict_cleanup=True) key: Final = manager.key() @@ -109,7 +115,7 @@ class TestFileCleanup: def test_success_response_must_confirm_deletion(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("delete None file-1",))), files=iter((deleted_file(deleted=False),)) + calls=ExpectedCalls(("delete None file-1",)), files=(deleted_file(deleted=False),) ) with pytest.raises(AssertionError, match="did not confirm deletion"): cleanup_file(client, "file-1", key="test-key") @@ -117,16 +123,16 @@ class TestFileCleanup: def test_cleanup_is_idempotent_when_file_is_already_deleted(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("delete azure file-1",))), - files=iter((UnknownApiError(status_code=404, body="missing"),)), + calls=ExpectedCalls(("delete azure file-1",)), + files=(UnknownApiError(status_code=404, body="missing"),), ) cleanup_file(client, "file-1", key="test-key", provider="azure") client.calls.assert_done() def test_default_resource_cleanup_keeps_existing_best_effort_behavior(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("delete None file-1", "delete key test-key"))), - files=iter((UnknownApiError(status_code=403, body="forbidden"),)), + calls=ExpectedCalls(("delete None file-1", "delete key test-key")), + files=(UnknownApiError(status_code=403, body="forbidden"),), ) manager: Final = ResourceManager(client=client) key: Final = manager.key() @@ -141,37 +147,39 @@ class TestCleanupRetries: [NetworkError(message="offline"), RateLimitedError(), UnknownApiError(status_code=503, body="unavailable")], ) def test_transient_error_retries_and_returns_success(self, failure: Result[FileDeleteResponse]) -> None: - outcomes: Final = iter((failure, deleted_file())) - delays: Final = ExpectedCalls(iter((1.0,))) - result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays) + responses: Final = (failure, deleted_file()) + outcomes: Final = Mock(side_effect=responses) + delays: Final = ExpectedCalls((1.0,)) + result: Final[Result[FileDeleteResponse]] = cleanup_result(outcomes, wait=delays) assert isinstance(result, Success) and result.data.deleted delays.assert_done() def test_persistent_error_has_bounded_retries(self) -> None: failure: Final = UnknownApiError(status_code=503, body="unavailable") - outcomes: Final[Iterator[Result[FileDeleteResponse]]] = iter((failure,) * (len(CLEANUP_DELAYS) + 1)) - delays: Final = ExpectedCalls(iter(CLEANUP_DELAYS)) - result: Final[Result[FileDeleteResponse]] = cleanup_result(lambda: next(outcomes), wait=delays) + outcomes: Final = Mock(return_value=failure) + delays: Final = ExpectedCalls(CLEANUP_DELAYS) + result: Final[Result[FileDeleteResponse]] = cleanup_result(outcomes, wait=delays) assert result is failure delays.assert_done() - assert next(outcomes, None) is None + assert outcomes.call_count == len(CLEANUP_DELAYS) + 1 def test_permanent_error_is_not_retried(self) -> None: failure: Final = UnknownApiError(status_code=403, body="forbidden") - outcomes: Final = iter((failure, deleted_file())) - delays: Final = ExpectedCalls[float](iter(())) - assert cleanup_result(lambda: next(outcomes), wait=delays) is failure + responses: Final = (failure, deleted_file()) + outcomes: Final = Mock(side_effect=responses) + delays: Final = ExpectedCalls[float](()) + assert cleanup_result(outcomes, wait=delays) is failure delays.assert_done() - assert isinstance(next(outcomes), Success) + assert outcomes.call_count == 1 class TestBatchCancellation: def test_cancelling_batch_is_polled_until_terminal_without_cancelling_again(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter((f"retrieve None {MANAGED_BATCH_ID}",) * 3)), - batches=iter((batch("cancelling"), batch("cancelling"), batch("cancelled"))), + calls=ExpectedCalls((f"retrieve None {MANAGED_BATCH_ID}",) * 3), + batches=(batch("cancelling"), batch("cancelling"), batch("cancelled")), ) - delays: Final = ExpectedCalls(iter((10.0,))) + delays: Final = ExpectedCalls((10.0,)) cleanup_batch(client, MANAGED_BATCH_ID, key="test-key", wait=delays) client.calls.assert_done() delays.assert_done() @@ -179,23 +187,22 @@ class TestBatchCancellation: def test_cancellation_timeout_is_reported_but_file_and_key_cleanup_still_run(self) -> None: client: Final = CleanupClient( calls=ExpectedCalls( - iter( - ( - f"retrieve None {MANAGED_BATCH_ID}", - f"retrieve None {MANAGED_BATCH_ID}", - "delete None file-1", - "delete key test-key", - ) + ( + f"retrieve None {MANAGED_BATCH_ID}", + f"retrieve None {MANAGED_BATCH_ID}", + "delete None file-1", + "delete key test-key", ) ), - batches=iter((batch("cancelling"), batch("cancelling"))), - files=iter((deleted_file(),)), + batches=(batch("cancelling"), batch("cancelling")), + files=(deleted_file(),), ) - ticks: Final = iter((0.0, BATCH_CANCEL_TIMEOUT_SECONDS)) + times: Final = (0.0, BATCH_CANCEL_TIMEOUT_SECONDS) + ticks: Final[Callable[[], float]] = Mock(side_effect=times) manager: Final = ResourceManager(client=client, strict_cleanup=True) key: Final = manager.key() manager.defer(lambda: cleanup_file(client, "file-1", key=key)) - manager.defer(lambda: cleanup_batch(client, MANAGED_BATCH_ID, key=key, clock=lambda: next(ticks))) + manager.defer(lambda: cleanup_batch(client, MANAGED_BATCH_ID, key=key, clock=ticks)) with pytest.raises(ExceptionGroup) as caught: manager.teardown() assert "cancellation did not finish" in str(caught.value.exceptions[0]) @@ -203,17 +210,15 @@ class TestBatchCancellation: @pytest.mark.parametrize("status", ["completed", "failed", "expired", "cancelled"]) def test_inactive_batch_needs_no_cancellation(self, status: str) -> None: - client: Final = CleanupClient( - calls=ExpectedCalls(iter(("retrieve None batch-1",))), batches=iter((batch(status),)) - ) + client: Final = CleanupClient(calls=ExpectedCalls(("retrieve None batch-1",)), batches=(batch(status),)) cleanup_batch(client, "batch-1", key="test-key") client.calls.assert_done() def test_active_batch_is_cancelled_through_its_provider(self) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("retrieve azure batch-1", "cancel azure batch-1"))), - batches=iter((batch("in_progress"), batch("cancelled"))), - cancellations=iter((batch("cancelling"),)), + calls=ExpectedCalls(("retrieve azure batch-1", "cancel azure batch-1")), + batches=(batch("in_progress"), batch("cancelled")), + cancellations=(batch("cancelling"),), ) cleanup_batch(client, "batch-1", key="test-key", provider="azure") client.calls.assert_done() @@ -225,23 +230,21 @@ class TestBatchCancellation: ) -> None: client: Final = CleanupClient( calls=ExpectedCalls( - iter( - ( - f"retrieve vertex_ai {batch_id}", - f"cancel vertex_ai {batch_id}", - f"retrieve vertex_ai {batch_id}", - f"retrieve vertex_ai {batch_id}", - f"retrieve vertex_ai {batch_id}", - "delete vertex_ai file-1", - "delete key test-key", - ) + ( + f"retrieve vertex_ai {batch_id}", + f"cancel vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + f"retrieve vertex_ai {batch_id}", + "delete vertex_ai file-1", + "delete key test-key", ) ), - batches=iter((batch("validating"), batch(pending_status), batch(pending_status), batch("cancelled"))), - cancellations=iter((batch(pending_status),)), - files=iter((deleted_file(),)), + batches=(batch("validating"), batch(pending_status), batch(pending_status), batch("cancelled")), + cancellations=(batch(pending_status),), + files=(deleted_file(),), ) - delays: Final = ExpectedCalls(iter((10.0, 10.0))) + delays: Final = ExpectedCalls((10.0, 10.0)) manager: Final = ResourceManager(client=client, strict_cleanup=True) key: Final = manager.key() manager.defer(lambda: cleanup_file(client, "file-1", key=key, provider="vertex_ai")) @@ -255,28 +258,22 @@ class TestBatchCancellation: self, output_delete_fails: bool ) -> None: client: Final = CleanupClient( - calls=ExpectedCalls( - iter(("retrieve openai batch-1", "delete openai file-output", "delete openai file-error")) - ), - batches=iter( - ( - Success( - status_code=200, - data=BatchObject( - id="batch-1", - status="completed", - input_file_id="file-input", - output_file_id="file-output", - error_file_id="file-error", - ), + calls=ExpectedCalls(("retrieve openai batch-1", "delete openai file-output", "delete openai file-error")), + batches=( + Success( + status_code=200, + data=BatchObject( + id="batch-1", + status="completed", + input_file_id="file-input", + output_file_id="file-output", + error_file_id="file-error", ), - ) + ), ), - files=iter( - ( - UnknownApiError(status_code=403, body="forbidden") if output_delete_fails else deleted_file(), - deleted_file(), - ) + files=( + UnknownApiError(status_code=403, body="forbidden") if output_delete_fails else deleted_file(), + deleted_file(), ), ) if output_delete_fails: @@ -289,9 +286,9 @@ class TestBatchCancellation: @pytest.mark.parametrize("status", ["completed", "in_progress"]) def test_cancellation_conflict_is_accepted_only_when_batch_became_inactive(self, status: str) -> None: client: Final = CleanupClient( - calls=ExpectedCalls(iter(("retrieve None batch-1", "cancel None batch-1", "retrieve None batch-1"))), - batches=iter((batch("in_progress"), batch(status))), - cancellations=iter((UnknownApiError(status_code=409, body="conflict"),)), + calls=ExpectedCalls(("retrieve None batch-1", "cancel None batch-1", "retrieve None batch-1")), + batches=(batch("in_progress"), batch(status)), + cancellations=(UnknownApiError(status_code=409, body="conflict"),), ) if status == "completed": cleanup_batch(client, "batch-1", key="test-key") From 43a02e2dbcd485c3cc5dd31d8b11161da650c356 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 7 Sep 2026 16:53:41 -0700 Subject: [PATCH 049/223] fix(proxy): report the billed token rates in /cost/estimate The rate fields reported base cost-map prices while the cost lines were billed at the token tier, off-peak window and regional multipliers the calculator picks for the request, so a line did not always equal tokens times its reported rate. get_billed_token_rates now resolves the rates once, the token-type breakdown and the endpoint both read from it, and a tiered-model test asserts every line equals its token count times the rate reported next to it Claude-Session: https://claude.ai/code/session_011Tn3657NkV6ojLqewL64Kb --- .../litellm_core_utils/llm_cost_calc/utils.py | 206 ++++++++++++------ litellm/proxy/_types.py | 6 +- .../cost_tracking_settings.py | 63 ++---- .../llm_cost_calc/test_llm_cost_calc_utils.py | 49 +++++ .../test_cost_tracking_settings.py | 52 ++++- ui/litellm-dashboard/src/lib/http/schema.d.ts | 10 +- 6 files changed, 258 insertions(+), 128 deletions(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 8d53c8da3e6..2bb15fb4c48 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -1329,16 +1329,118 @@ def _cache_token_counts(usage: Usage) -> tuple[int, int, CacheCreationTokenDetai ) -def _custom_pricing_token_type_breakdown(usage: Usage, custom_cost_per_token: CostPerToken) -> TokenTypeCostBreakdown: +@dataclass(frozen=True, slots=True) +class BilledTokenRates: + """Per-token rates one request's usage bills at, after token tiers, off-peak windows and the + regional multipliers the totals apply, so each cost line equals its token count times its rate.""" + + input_cost_per_token: float + output_cost_per_token: float + cache_read_input_token_cost: float + cache_creation_input_token_cost: float + cache_creation_input_token_cost_above_1hr: float + output_cost_per_reasoning_token: float + + def scaled(self, multiplier: float) -> "BilledTokenRates": + if multiplier == 1.0: + return self + return BilledTokenRates( + input_cost_per_token=self.input_cost_per_token * multiplier, + output_cost_per_token=self.output_cost_per_token * multiplier, + cache_read_input_token_cost=self.cache_read_input_token_cost * multiplier, + cache_creation_input_token_cost=self.cache_creation_input_token_cost * multiplier, + cache_creation_input_token_cost_above_1hr=self.cache_creation_input_token_cost_above_1hr * multiplier, + output_cost_per_reasoning_token=self.output_cost_per_reasoning_token * multiplier, + ) + + +def _custom_pricing_rates(custom_cost_per_token: CostPerToken) -> BilledTokenRates: """Flat custom pricing has no tiers, uplifts or reasoning rate: cache tokens bill at the configured cache rates (else the input rate) and reasoning at the output rate, as _cost_per_token_custom_pricing_helper does.""" input_rate: Final = custom_cost_per_token["input_cost_per_token"] - cache_read_tokens, cache_creation_tokens, _ = _cache_token_counts(usage) - return TokenTypeCostBreakdown( - reasoning_cost=float(_reasoning_token_count(usage)) * custom_cost_per_token["output_cost_per_token"], - cache_read_cost=float(cache_read_tokens) * custom_cost_per_token.get("cache_read_input_token_cost", input_rate), - cache_creation_cost=float(cache_creation_tokens) - * custom_cost_per_token.get("cache_creation_input_token_cost", input_rate), + output_rate: Final = custom_cost_per_token["output_cost_per_token"] + cache_creation_rate: Final = custom_cost_per_token.get("cache_creation_input_token_cost", input_rate) + return BilledTokenRates( + input_cost_per_token=input_rate, + output_cost_per_token=output_rate, + cache_read_input_token_cost=custom_cost_per_token.get("cache_read_input_token_cost", input_rate), + cache_creation_input_token_cost=cache_creation_rate, + cache_creation_input_token_cost_above_1hr=cache_creation_rate, + output_cost_per_reasoning_token=output_rate, + ) + + +def _cost_map_billed_rates( + model_info: ModelInfo, + usage: Usage, + custom_llm_provider: str | None, + service_tier: str | None, + data_residency: str | None, + vertex_location: str | None, + current_time: datetime | None, +) -> BilledTokenRates: + billing_time: Final = current_time if current_time is not None else datetime.now(timezone.utc) + ( + prompt_base_cost, + completion_base_cost, + cache_creation_cost_rate, + cache_creation_cost_above_1hr_rate, + cache_read_cost_rate, + ) = _get_token_base_cost( + model_info=model_info, + usage=usage, + service_tier=service_tier, + current_time=billing_time, + threshold_is_inclusive=_uses_inclusive_token_thresholds(custom_llm_provider), + ) + reasoning_rate: Final = _resolve_billed_reasoning_rate( + model_info=model_info, + usage=usage, + service_tier=service_tier, + completion_base_cost=completion_base_cost, + current_time=billing_time, + ) + multiplier: Final = ( + _get_regional_uplift_multiplier(model_info, data_residency) + * get_vertex_regional_endpoint_uplift(model_info, vertex_location) + * get_provider_specific_geo_multiplier(model_info=model_info, usage=usage) + ) + return BilledTokenRates( + input_cost_per_token=prompt_base_cost, + output_cost_per_token=completion_base_cost, + cache_read_input_token_cost=cache_read_cost_rate, + cache_creation_input_token_cost=cache_creation_cost_rate, + cache_creation_input_token_cost_above_1hr=cache_creation_cost_above_1hr_rate, + output_cost_per_reasoning_token=reasoning_rate, + ).scaled(multiplier) + + +def get_billed_token_rates( + model: str, + custom_llm_provider: str | None, + usage: Usage, + service_tier: str | None = None, + data_residency: str | None = None, + vertex_location: str | None = None, + current_time: datetime | None = None, + custom_cost_per_token: CostPerToken | None = None, +) -> BilledTokenRates | None: + """Rates the cost calculator bills ``usage`` at, resolved exactly as the totals and the token-type + breakdown resolve them. None when the model's pricing cannot be resolved.""" + if custom_cost_per_token is not None: + return _custom_pricing_rates(custom_cost_per_token) + try: + model_info: Final = get_model_info(model=model, custom_llm_provider=custom_llm_provider) + except Exception: + return None + return _cost_map_billed_rates( + model_info=model_info, + usage=usage, + custom_llm_provider=custom_llm_provider, + service_tier=service_tier, + data_residency=data_residency, + vertex_location=vertex_location, + current_time=current_time, ) @@ -1360,77 +1462,39 @@ def get_token_type_cost_breakdown( cost calculators bypass ``generic_cost_per_token``, because cache tokens always land on ``prompt_tokens_details`` (via the Usage constructor and provider transformations) and reasoning tokens on ``completion_tokens_details``. It reuses - the same rate-resolution primitives as the total-cost path so the breakdown can - never drift from the totals. A deployment billed by ``custom_cost_per_token`` is - priced from those flat rates instead of the cost map, for the same reason. + the same rate resolution as the total-cost path (``get_billed_token_rates``) so the + breakdown can never drift from the totals. A deployment billed by + ``custom_cost_per_token`` is priced from those flat rates instead of the cost map and, + like its totals, bills cache writes flat rather than by their 5m/1h split. Returns zeros (never raises) when the model or its pricing cannot be resolved. """ - if custom_cost_per_token is not None: - return _custom_pricing_token_type_breakdown(usage=usage, custom_cost_per_token=custom_cost_per_token) - - try: - model_info: Final = get_model_info(model=model, custom_llm_provider=custom_llm_provider) - except Exception: + rates: Final = get_billed_token_rates( + model=model, + custom_llm_provider=custom_llm_provider, + usage=usage, + service_tier=service_tier, + data_residency=data_residency, + vertex_location=vertex_location, + current_time=current_time, + custom_cost_per_token=custom_cost_per_token, + ) + if rates is None: return TokenTypeCostBreakdown(0.0, 0.0, 0.0) - billing_time: Final = current_time if current_time is not None else datetime.now(timezone.utc) - ( - _prompt_base_cost, - completion_base_cost, - cache_creation_cost_rate, - cache_creation_cost_above_1hr_rate, - cache_read_cost_rate, - ) = _get_token_base_cost( - model_info=model_info, - usage=usage, - service_tier=service_tier, - current_time=billing_time, - threshold_is_inclusive=_uses_inclusive_token_thresholds(custom_llm_provider), - ) - - reasoning_rate: Final = _resolve_billed_reasoning_rate( - model_info=model_info, - usage=usage, - service_tier=service_tier, - completion_base_cost=completion_base_cost, - current_time=billing_time, - ) - reasoning_cost = float(_reasoning_token_count(usage)) * reasoning_rate - cache_read_tokens, cache_creation_tokens, cache_creation_token_details = _cache_token_counts(usage) - cache_read_cost = float(cache_read_tokens) * cache_read_cost_rate - cache_creation_cost = calculate_cache_writing_cost( - cache_creation_tokens=cache_creation_tokens, - cache_creation_token_details=cache_creation_token_details, - cache_creation_cost_above_1hr=cache_creation_cost_above_1hr_rate, - cache_creation_cost=cache_creation_cost_rate, + cache_creation_cost: Final = ( + float(cache_creation_tokens) * rates.cache_creation_input_token_cost + if custom_cost_per_token is not None + else calculate_cache_writing_cost( + cache_creation_tokens=cache_creation_tokens, + cache_creation_token_details=cache_creation_token_details, + cache_creation_cost_above_1hr=rates.cache_creation_input_token_cost_above_1hr, + cache_creation_cost=rates.cache_creation_input_token_cost, + ) ) - - # Apply the same flat regional-processing uplift the totals get, so per-type - # costs stay reconciled with input_cost/output_cost for regionalized OpenAI hosts. - uplift: Final = _get_regional_uplift_multiplier(model_info, data_residency) - if uplift != 1.0: - reasoning_cost *= uplift - cache_read_cost *= uplift - cache_creation_cost *= uplift - - vertex_uplift: Final = get_vertex_regional_endpoint_uplift(model_info, vertex_location) - if vertex_uplift != 1.0: - reasoning_cost *= vertex_uplift - cache_read_cost *= vertex_uplift - cache_creation_cost *= vertex_uplift - - # Mirror the provider-specific geo uplift (e.g. Anthropic us: 1.1) the totals - # apply, so cache and reasoning line items stay reconciled with them. - geo_multiplier: Final = get_provider_specific_geo_multiplier(model_info=model_info, usage=usage) - if geo_multiplier != 1.0: - reasoning_cost *= geo_multiplier - cache_read_cost *= geo_multiplier - cache_creation_cost *= geo_multiplier - return TokenTypeCostBreakdown( - reasoning_cost=reasoning_cost, - cache_read_cost=cache_read_cost, + reasoning_cost=float(_reasoning_token_count(usage)) * rates.output_cost_per_reasoning_token, + cache_read_cost=float(cache_read_tokens) * rates.cache_read_input_token_cost, cache_creation_cost=cache_creation_cost, ) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 65b7d409d7c..3c1cd9bfc69 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -5197,9 +5197,9 @@ class CostEstimateResponse(LiteLLMPydanticObjectBase): default=None, description="Cache-write share of monthly_input_cost" ) monthly_reasoning_cost: float | None = Field(default=None, description="Reasoning share of monthly_output_cost") - # Pricing info - input_cost_per_token: float | None = None - output_cost_per_token: float | None = None + # Pricing info: the rates this request's usage bills at, after token tiers and regional multipliers + input_cost_per_token: float | None = Field(default=None, description="Rate billed per input token") + output_cost_per_token: float | None = Field(default=None, description="Rate billed per output token") cache_read_input_token_cost: float | None = Field(default=None, description="Rate billed per cache-read token") cache_creation_input_token_cost: float | None = Field(default=None, description="Rate billed per cache-write token") output_cost_per_reasoning_token: float | None = Field(default=None, description="Rate billed per reasoning token") diff --git a/litellm/proxy/management_endpoints/cost_tracking_settings.py b/litellm/proxy/management_endpoints/cost_tracking_settings.py index 51f31a757cd..17d82fd17e3 100644 --- a/litellm/proxy/management_endpoints/cost_tracking_settings.py +++ b/litellm/proxy/management_endpoints/cost_tracking_settings.py @@ -20,6 +20,7 @@ from pydantic import BaseModel import litellm from litellm._logging import verbose_proxy_logger from litellm.cost_calculator import completion_cost +from litellm.litellm_core_utils.llm_cost_calc.utils import get_billed_token_rates from litellm.proxy._types import ( CommonProxyErrors, CostEstimateRequest, @@ -170,44 +171,6 @@ def _cost_lines(cost_per_request: float, cost_breakdown: CostBreakdown | None) - ) -@dataclass(frozen=True, slots=True) -class EffectiveTokenRates: - input_cost_per_token: float | None - output_cost_per_token: float | None - cache_read_input_token_cost: float | None - cache_creation_input_token_cost: float | None - output_cost_per_reasoning_token: float | None - - -def _custom_token_rates(custom_cost_per_token: CostPerToken) -> EffectiveTokenRates: - input_rate: Final = custom_cost_per_token["input_cost_per_token"] - output_rate: Final = custom_cost_per_token["output_cost_per_token"] - return EffectiveTokenRates( - input_cost_per_token=input_rate, - output_cost_per_token=output_rate, - cache_read_input_token_cost=custom_cost_per_token.get("cache_read_input_token_cost", input_rate), - cache_creation_input_token_cost=custom_cost_per_token.get("cache_creation_input_token_cost", input_rate), - output_cost_per_reasoning_token=output_rate, - ) - - -def _cost_map_token_rates(model_info: ModelInfo | None) -> EffectiveTokenRates: - """Base rates the cost calculator bills flat usage at: a cost-map model without a cache price - bills cache tokens at zero, and one without a reasoning price bills reasoning at the output rate.""" - if model_info is None: - return EffectiveTokenRates(None, None, None, None, None) - sources: Final = (model_info,) - output_rate: Final = _configured_price("output_cost_per_token", sources) - reasoning_rate: Final = _configured_price("output_cost_per_reasoning_token", sources) - return EffectiveTokenRates( - input_cost_per_token=_configured_price("input_cost_per_token", sources), - output_cost_per_token=output_rate, - cache_read_input_token_cost=_configured_price("cache_read_input_token_cost", sources) or 0.0, - cache_creation_input_token_cost=_configured_price("cache_creation_input_token_cost", sources) or 0.0, - output_cost_per_reasoning_token=output_rate if reasoning_rate is None else reasoning_rate, - ) - - def _usage_for_estimate(request: CostEstimateRequest) -> Usage: cache_tokens: Final = request.cache_read_input_tokens + request.cache_creation_input_tokens return Usage( @@ -654,7 +617,8 @@ async def estimate_cost( verbose_proxy_logger.debug("Cost estimate: request.model='%s' resolved to '%s'", request.model, resolved_model) - mock_response: Final = ModelResponse(model=resolved_model, usage=_usage_for_estimate(request)) + usage: Final = _usage_for_estimate(request) + mock_response: Final = ModelResponse(model=resolved_model, usage=usage) # Create a logging object to capture cost breakdown litellm_logging_obj: Final = LiteLLMLoggingObj( @@ -688,12 +652,13 @@ async def estimate_cost( daily: Final = per_request.times(request.num_requests_per_day) monthly: Final = per_request.times(request.num_requests_per_month) - model_info: Final = _lookup_model_info(resolved_model) - rates: Final = ( - _custom_token_rates(resolved.custom_cost_per_token) - if resolved.custom_cost_per_token is not None - else _cost_map_token_rates(model_info) + rates: Final = get_billed_token_rates( + model=resolved_model, + custom_llm_provider=resolved_provider, + usage=usage, + custom_cost_per_token=resolved.custom_cost_per_token, ) + model_info: Final = _lookup_model_info(resolved_model) mapped_provider: Final = model_info.get("litellm_provider") if model_info is not None else None custom_llm_provider: Final = mapped_provider if mapped_provider is not None else resolved_provider @@ -727,10 +692,10 @@ async def estimate_cost( monthly_cache_read_cost=monthly.cache_read_cost if monthly is not None else None, monthly_cache_creation_cost=monthly.cache_creation_cost if monthly is not None else None, monthly_reasoning_cost=monthly.reasoning_cost if monthly is not None else None, - input_cost_per_token=rates.input_cost_per_token, - output_cost_per_token=rates.output_cost_per_token, - cache_read_input_token_cost=rates.cache_read_input_token_cost, - cache_creation_input_token_cost=rates.cache_creation_input_token_cost, - output_cost_per_reasoning_token=rates.output_cost_per_reasoning_token, + input_cost_per_token=rates.input_cost_per_token if rates is not None else None, + output_cost_per_token=rates.output_cost_per_token if rates is not None else None, + cache_read_input_token_cost=rates.cache_read_input_token_cost if rates is not None else None, + cache_creation_input_token_cost=rates.cache_creation_input_token_cost if rates is not None else None, + output_cost_per_reasoning_token=rates.output_cost_per_reasoning_token if rates is not None else None, provider=custom_llm_provider, ) diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index 7065003839b..4de3c059e63 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -27,6 +27,7 @@ from litellm.types.utils import ( ) from litellm.litellm_core_utils.llm_cost_calc.utils import ( + BilledTokenRates, CostCalculatorUtils, PromptTokensDetailsResult, TokenRates, @@ -38,6 +39,7 @@ from litellm.litellm_core_utils.llm_cost_calc.utils import ( apply_off_peak_pricing, calculate_cache_writing_cost, generic_cost_per_token, + get_billed_token_rates, get_token_type_cost_breakdown, ) from litellm.types.utils import CacheCreationTokenDetails, Usage @@ -3938,6 +3940,53 @@ def test_token_type_cost_breakdown_reconciles_with_custom_pricing_totals(): assert 300 * 2e-6 + breakdown.reasoning_cost == pytest.approx(completion_cost) +def test_billed_token_rates_follow_the_token_tier_the_breakdown_bills_at(monkeypatch): + monkeypatch.setitem( + litellm.model_cost, + "tiered-cache-model", + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, + "input_cost_per_token_above_200k_tokens": 6e-6, + "output_cost_per_token_above_200k_tokens": 3e-5, + "cache_read_input_token_cost_above_200k_tokens": 6e-7, + "cache_creation_input_token_cost_above_200k_tokens": 7.5e-6, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + usage = Usage( + prompt_tokens=250_000, + completion_tokens=1_000, + total_tokens=251_000, + prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=200_000, cache_creation_tokens=10_000), + completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=200), + ) + + rates = get_billed_token_rates(model="tiered-cache-model", custom_llm_provider="openai", usage=usage) + breakdown = get_token_type_cost_breakdown(model="tiered-cache-model", custom_llm_provider="openai", usage=usage) + + assert rates == BilledTokenRates( + input_cost_per_token=6e-6, + output_cost_per_token=3e-5, + cache_read_input_token_cost=6e-7, + cache_creation_input_token_cost=7.5e-6, + cache_creation_input_token_cost_above_1hr=0.0, + output_cost_per_reasoning_token=3e-5, + ) + assert breakdown.cache_read_cost == pytest.approx(200_000 * rates.cache_read_input_token_cost) + assert breakdown.cache_creation_cost == pytest.approx(10_000 * rates.cache_creation_input_token_cost) + assert breakdown.reasoning_cost == pytest.approx(200 * rates.output_cost_per_reasoning_token) + + +def test_billed_token_rates_are_none_for_an_unpriced_model(): + usage = Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15) + + assert get_billed_token_rates(model="no-such-model-anywhere", custom_llm_provider="openai", usage=usage) is None + + def test_token_type_cost_breakdown_zero_without_special_tokens(_local_model_cost_map): usage = Usage(prompt_tokens=100, completion_tokens=50, total_tokens=150) diff --git a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py index 6d3ef2ffea9..9847c4092df 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py +++ b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py @@ -813,9 +813,7 @@ async def _estimate(mock_router: MagicMock | None, model: str = AN_ALIAS, **over request = CostEstimateRequest( model=model, - input_tokens=INPUT_TOKENS, - output_tokens=OUTPUT_TOKENS, - **overrides, + **{"input_tokens": INPUT_TOKENS, "output_tokens": OUTPUT_TOKENS, **overrides}, ) with patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.llm_router", mock_router @@ -1062,6 +1060,54 @@ class TestEstimateCostCacheAndReasoningTokens: assert response.cache_read_input_token_cost == pytest.approx(5e-7) assert response.cache_creation_input_token_cost == pytest.approx(6.25e-6) + @pytest.mark.asyncio + async def test_a_tiered_model_reports_the_rates_its_lines_were_billed_at(self, monkeypatch): + """Above a token tier the calculator bills every line at the tier's rate, so the reported + rates must be the tier's too: each line equals its token count times the rate next to it.""" + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "cache_read_input_token_cost": 3e-7, + "cache_creation_input_token_cost": 3.75e-6, + "input_cost_per_token_above_200k_tokens": 6e-6, + "output_cost_per_token_above_200k_tokens": 3e-5, + "cache_read_input_token_cost_above_200k_tokens": 6e-7, + "cache_creation_input_token_cost_above_200k_tokens": 7.5e-6, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + + response = await _estimate( + None, + model=A_MAPPED_MODEL, + input_tokens=250_000, + cache_read_input_tokens=200_000, + cache_creation_input_tokens=10_000, + output_tokens=1_000, + reasoning_tokens=200, + ) + + assert response.input_cost_per_token == pytest.approx(6e-6) + assert response.output_cost_per_token == pytest.approx(3e-5) + assert response.cache_read_input_token_cost == pytest.approx(6e-7) + assert response.cache_creation_input_token_cost == pytest.approx(7.5e-6) + assert response.output_cost_per_reasoning_token == pytest.approx(3e-5) + assert response.cache_read_cost_per_request == pytest.approx(200_000 * response.cache_read_input_token_cost) + assert response.cache_creation_cost_per_request == pytest.approx( + 10_000 * response.cache_creation_input_token_cost + ) + assert response.reasoning_cost_per_request == pytest.approx(200 * response.output_cost_per_reasoning_token) + assert response.input_cost_per_request == pytest.approx( + 40_000 * response.input_cost_per_token + + response.cache_read_cost_per_request + + response.cache_creation_cost_per_request + ) + assert response.output_cost_per_request == pytest.approx(1_000 * response.output_cost_per_token) + class TestCostEstimateRequestTokenSubsets: def test_cache_tokens_beyond_the_input_tokens_are_rejected(self): diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 6a9c86cc1b5..46cb5ad892a 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -26523,7 +26523,10 @@ export interface components { * @description Input token cost per request (before margin) */ input_cost_per_request: number; - /** Input Cost Per Token */ + /** + * Input Cost Per Token + * @description Rate billed per input token + */ input_cost_per_token?: number | null; /** Input Tokens */ input_tokens: number; @@ -26584,7 +26587,10 @@ export interface components { * @description Output token cost per request (before margin) */ output_cost_per_request: number; - /** Output Cost Per Token */ + /** + * Output Cost Per Token + * @description Rate billed per output token + */ output_cost_per_token?: number | null; /** Output Tokens */ output_tokens: number; From 0710231acc349f1cb8229fb5d691678dc2402e80 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:57:09 -0700 Subject: [PATCH 050/223] feat(cost_map): stamp and surface generated_at and source revision provenance The cost map JSON now carries a top-level `_metadata` block with `generated_at` and `source_revision`, written by the two bot writers only when model data changed. The loader pops it before the map becomes `litellm.model_cost`, records it next to the fetch ETag, and `/reload/model_cost_map`, `/model/cost_map/source`, and the reload schedule status return it. The Price Data Reload card shows the stamp, the ETag, and when the pod loaded the map. The schema and the cost map guard treat `_metadata` as a non-model root key --- ...to_update_price_and_context_window_file.py | 27 +++- ci_cd/cost_map_guard.py | 7 +- ci_cd/generate_model_prices_schema.py | 19 ++- .../litellm_core_utils/get_model_cost_map.py | 82 ++++++++++- ...odel_prices_and_context_window_backup.json | 4 + litellm/proxy/proxy_server.py | 15 +- model_prices_and_context_window.json | 4 + model_prices_and_context_window.schema.json | 20 ++- scripts/sync_together_ai_models.py | 23 +++- .../test_get_model_cost_map.py | 129 +++++++++++++++++- .../test_routes_model_cost_map.py | 83 ++++++++++- ...to_update_price_and_context_window_file.py | 54 ++++++++ tests/test_litellm/test_cost_map_guard.py | 20 +++ .../test_litellm/test_model_prices_schema.py | 19 +++ .../test_sync_together_ai_models.py | 53 +++++++ .../src/components/price_data_reload.test.tsx | 34 +++++ .../src/components/price_data_reload.tsx | 68 +++++++-- ui/litellm-dashboard/src/lib/http/schema.d.ts | 3 + 18 files changed, 636 insertions(+), 28 deletions(-) create mode 100644 tests/test_litellm/test_auto_update_price_and_context_window_file.py diff --git a/.github/scripts/auto_update_price_and_context_window_file.py b/.github/scripts/auto_update_price_and_context_window_file.py index 461d8d347d9..a7a3194f262 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -1,6 +1,9 @@ import asyncio import aiohttp import json +import os +import subprocess +from datetime import datetime, timezone # Asynchronously fetch data from a given URL async def fetch_data(url): @@ -31,13 +34,28 @@ def sync_local_data_with_remote(local_data, remote_data): for key in (set(remote_data) - set(local_data)): local_data[key] = remote_data[key] +def utc_now_iso(): + return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + +def source_revision(): + from_env = os.environ.get("GITHUB_SHA") + if from_env: + return from_env + return subprocess.run(["git", "rev-parse", "HEAD"], check=True, capture_output=True, text=True).stdout.strip() + + +def stamp_metadata(data, generated_at, revision): + return {**data, "_metadata": {"generated_at": generated_at, "source_revision": revision}} + + # Write data to the json file def write_to_file(file_path, data): try: # Open the file in write mode with open(file_path, "w") as file: # Dump the data as JSON into the file - json.dump(data, file, indent=4) + file.write(json.dumps(data, indent=4) + "\n") print("Values updated successfully.") except Exception as e: # Print an error message if writing to file fails @@ -149,8 +167,13 @@ def main(): # If both local and openrouter data are available, synchronize and save if local_data and all_remote_data: + before = json.dumps(local_data, sort_keys=True) sync_local_data_with_remote(local_data, all_remote_data) - write_to_file(local_file_path, local_data) + changed = json.dumps(local_data, sort_keys=True) != before + write_to_file( + local_file_path, + stamp_metadata(local_data, utc_now_iso(), source_revision()) if changed else local_data, + ) else: print("Failed to fetch model data from either local file or URL.") diff --git a/ci_cd/cost_map_guard.py b/ci_cd/cost_map_guard.py index 50aa40ba220..351c06c74eb 100644 --- a/ci_cd/cost_map_guard.py +++ b/ci_cd/cost_map_guard.py @@ -2,7 +2,8 @@ Every pull request gets the file checks: the three cost map files parse, the backup copy matches the root file, and the JSON schema is in sync and validates the map. Pull requests from the cost map sync bot (branches named -litellm_cost_map_sync_*) additionally may only touch those three files and may only add or update models. +litellm_cost_map_sync_*) additionally may only touch those three files and may only add or update models, plus +restamp the _metadata provenance block. """ from __future__ import annotations @@ -15,7 +16,7 @@ from collections.abc import Sequence from dataclasses import dataclass from typing import Final -from generate_model_prices_schema import SPECIAL_ROOT_KEYS, build_schema, render, validation_errors +from generate_model_prices_schema import BOT_LOCKED_ROOT_KEYS, build_schema, render, validation_errors COST_MAP_PATH: Final = "model_prices_and_context_window.json" BACKUP_PATH: Final = "litellm/model_prices_and_context_window_backup.json" @@ -102,7 +103,7 @@ def _bot_failures(base: Snapshot, head_map: CostMap, changed_files: Sequence[str *(f"bot PRs may not remove fields: {ref}" for ref in removed_fields), *( f"bot PRs may not change {key}" - for key in sorted(SPECIAL_ROOT_KEYS) + for key in sorted(BOT_LOCKED_ROOT_KEYS) if base_map.get(key) != head_map.get(key) ), ) diff --git a/ci_cd/generate_model_prices_schema.py b/ci_cd/generate_model_prices_schema.py index ab29b70bdd4..557afa50128 100644 --- a/ci_cd/generate_model_prices_schema.py +++ b/ci_cd/generate_model_prices_schema.py @@ -11,7 +11,9 @@ REPO_ROOT = Path(__file__).parent.parent PRICES_PATH = REPO_ROOT / "model_prices_and_context_window.json" SCHEMA_PATH = REPO_ROOT / "model_prices_and_context_window.schema.json" -SPECIAL_ROOT_KEYS = frozenset({"sample_spec", "fallback_generalizations"}) +METADATA_KEY = "_metadata" +SPECIAL_ROOT_KEYS = frozenset({"sample_spec", "fallback_generalizations", METADATA_KEY}) +BOT_LOCKED_ROOT_KEYS = SPECIAL_ROOT_KEYS - {METADATA_KEY} JsonSchema = dict @@ -271,13 +273,26 @@ def build_schema(prices: dict) -> JsonSchema: "description": ( "Schema for LiteLLM's model price and context window registry " "(https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). " - "Every top-level key except 'sample_spec' and 'fallback_generalizations' is a model id, " + "Every top-level key except '_metadata', 'sample_spec', and 'fallback_generalizations' is a model id, " "optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. " "All costs are USD per unit. New optional fields are added regularly, so consumers should " "ignore unknown fields rather than reject them." ), "type": "object", "properties": { + METADATA_KEY: { + "type": "object", + "description": ( + "Provenance of this file: when an automated sync last regenerated it and the commit it " + "ran against. Human edits leave it untouched; not a model entry." + ), + "properties": { + "generated_at": {"type": "string", "format": "date-time"}, + "source_revision": STRING, + }, + "required": ["generated_at", "source_revision"], + "additionalProperties": False, + }, "sample_spec": { "type": "object", "description": ( diff --git a/litellm/litellm_core_utils/get_model_cost_map.py b/litellm/litellm_core_utils/get_model_cost_map.py index ba8738c8de0..a538e7cb330 100644 --- a/litellm/litellm_core_utils/get_model_cost_map.py +++ b/litellm/litellm_core_utils/get_model_cost_map.py @@ -20,6 +20,8 @@ from importlib.resources import files from typing import Final, Protocol import httpx +from pydantic import BaseModel, ConfigDict, ValidationError +from typing_extensions import ReadOnly, TypedDict from litellm import verbose_logger from litellm.constants import ( @@ -31,10 +33,11 @@ from litellm.litellm_core_utils.fallback_generalizations import ( ) FALLBACK_GENERALIZATIONS_KEY: Final = "fallback_generalizations" +METADATA_KEY: Final = "_metadata" # Reserved top-level keys that are not model entries. They must be excluded # from the model-count integrity check so a real upstream shrink can't be masked. -RESERVED_TOP_LEVEL_KEYS: Final = frozenset({"sample_spec", FALLBACK_GENERALIZATIONS_KEY}) +RESERVED_TOP_LEVEL_KEYS: Final = frozenset({"sample_spec", FALLBACK_GENERALIZATIONS_KEY, METADATA_KEY}) def _count_model_entries(model_cost: dict) -> int: @@ -166,6 +169,7 @@ MODEL_COST_MAP_FETCH_MAX_WAIT_SECONDS: Final = 30.0 @dataclass(frozen=True, slots=True) class ModelCostMapReloaded: model_cost_map: dict # mutable-ok: adopted as litellm.model_cost, whose consumer contract is a plain mutable dict + etag: str | None = None @dataclass(frozen=True, slots=True) @@ -254,7 +258,7 @@ def _classify_fetch_response(response: httpx.Response, url: str) -> _FetchAttemp return ModelCostMapReloadUnavailable(reason=f"invalid JSON from {url}: {e}") if not isinstance(parsed, dict): return ModelCostMapReloadUnavailable(reason=f"expected a JSON object from {url}, got {type(parsed).__name__}") - return ModelCostMapReloaded(model_cost_map=parsed) + return ModelCostMapReloaded(model_cost_map=parsed, etag=response.headers.get("etag")) def _next_retry_wait( @@ -328,10 +332,12 @@ async def refetch_model_cost_map( map they already have. """ if os.getenv("LITELLM_LOCAL_MODEL_COST_MAP", "").lower() == "true": + _cost_map_source_info.loaded_at = datetime.now(timezone.utc) _cost_map_source_info.source = "local" _cost_map_source_info.url = None _cost_map_source_info.is_env_forced = True _cost_map_source_info.fallback_reason = None + _cost_map_source_info.etag = None return ModelCostMapReloaded( model_cost_map=_finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) ) @@ -355,11 +361,13 @@ async def refetch_model_cost_map( backup_model_count=GetModelCostMap._get_backup_model_count(), ): return ModelCostMapReloadUnavailable(reason=f"model cost map from {url} failed integrity validation") + _cost_map_source_info.loaded_at = datetime.now(timezone.utc) _cost_map_source_info.source = "remote" _cost_map_source_info.url = url _cost_map_source_info.is_env_forced = False _cost_map_source_info.fallback_reason = None - return ModelCostMapReloaded(model_cost_map=_finalize_model_cost_map(result.model_cost_map)) + _cost_map_source_info.etag = result.etag + return ModelCostMapReloaded(model_cost_map=_finalize_model_cost_map(result.model_cost_map), etag=result.etag) class ModelCostMapSourceInfo: @@ -370,13 +378,60 @@ class ModelCostMapSourceInfo: is_env_forced: bool = False fallback_reason: str | None = None loaded_at: "datetime | None" = None + generated_at: str | None = None + source_revision: str | None = None + etag: str | None = None # Module-level singleton tracking the source of the current cost map _cost_map_source_info: Final = ModelCostMapSourceInfo() -def get_model_cost_map_source_info() -> dict: +class CostMapMetadata(BaseModel): + model_config = ConfigDict(frozen=True, extra="ignore") + + generated_at: str | None = None + source_revision: str | None = None + + +_EMPTY_METADATA: Final = CostMapMetadata() + + +def _parse_metadata(raw: object) -> CostMapMetadata: + if raw is None: + return _EMPTY_METADATA + try: + return CostMapMetadata.model_validate(raw) + except ValidationError as error: + verbose_logger.warning("LiteLLM: ignoring a malformed %s block in the model cost map: %s", METADATA_KEY, error) + return _EMPTY_METADATA + + +class CostMapProvenance(TypedDict): + generated_at: ReadOnly[str | None] + source_revision: ReadOnly[str | None] + etag: ReadOnly[str | None] + + +class CostMapSourceInfo(CostMapProvenance): + source: ReadOnly[str] + url: ReadOnly[str | None] + is_env_forced: ReadOnly[bool] + fallback_reason: ReadOnly[str | None] + loaded_at: ReadOnly[str | None] + + +def get_model_cost_map_provenance() -> CostMapProvenance: + """Which revision of the cost map this process serves: the ``_metadata`` stamp the file + carries plus the ETag the remote fetch returned (None for the bundled backup)""" + return { + "generated_at": _cost_map_source_info.generated_at, + "source_revision": _cost_map_source_info.source_revision, + "etag": _cost_map_source_info.etag, + } + + +def get_model_cost_map_source_info() -> CostMapSourceInfo: """ Return metadata about where the current model cost map was loaded from. @@ -385,12 +440,20 @@ def get_model_cost_map_source_info() -> dict: - url: the remote URL attempted (or None for local-only) - is_env_forced: True if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage - fallback_reason: human-readable reason if remote failed and local was used + - loaded_at: ISO 8601 time this process last loaded the map + - generated_at, source_revision: the ``_metadata`` stamp inside the loaded file + - etag: the ETag of the remote fetch (None for the bundled backup) """ + loaded_at: Final = _cost_map_source_info.loaded_at return { "source": _cost_map_source_info.source, "url": _cost_map_source_info.url, "is_env_forced": _cost_map_source_info.is_env_forced, "fallback_reason": _cost_map_source_info.fallback_reason, + "loaded_at": loaded_at.isoformat() if loaded_at is not None else None, + "generated_at": _cost_map_source_info.generated_at, + "source_revision": _cost_map_source_info.source_revision, + "etag": _cost_map_source_info.etag, } @@ -455,14 +518,18 @@ def _expand_model_aliases(model_cost: dict) -> dict: def _finalize_model_cost_map(model_cost: dict) -> dict: - """Extract fallback generalizations out of the raw map, then expand aliases. + """Extract fallback generalizations and the provenance stamp out of the raw map, then expand aliases. The ``fallback_generalizations`` block is installed into the generalizations - module and removed from the map so it is never treated as a model entry. + module and the ``_metadata`` block into the source info; both are removed from + the map so neither is ever treated as a model entry. """ raw: Final = model_cost.pop(FALLBACK_GENERALIZATIONS_KEY, None) rules: Final = raw.get("rules") if isinstance(raw, dict) else None set_fallback_generalizations(rules) + metadata: Final = _parse_metadata(model_cost.pop(METADATA_KEY, None)) + _cost_map_source_info.generated_at = metadata.generated_at + _cost_map_source_info.source_revision = metadata.source_revision return _expand_model_aliases(model_cost) @@ -494,10 +561,12 @@ def get_model_cost_map( _cost_map_source_info.url = None _cost_map_source_info.is_env_forced = True _cost_map_source_info.fallback_reason = None + _cost_map_source_info.etag = None return _finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) _cost_map_source_info.url = url _cost_map_source_info.is_env_forced = False + _cost_map_source_info.etag = None result: Final = _fetch_remote_model_cost_map_with_retry_sync( url=url, @@ -533,4 +602,5 @@ def get_model_cost_map( _cost_map_source_info.source = "remote" _cost_map_source_info.fallback_reason = None + _cost_map_source_info.etag = result.etag return _finalize_model_cost_map(content) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index b1ffc1583e4..5edb3c0e9d8 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -1,4 +1,8 @@ { + "_metadata": { + "generated_at": "2026-09-07T23:38:47Z", + "source_revision": "cd681a573fd9f5b6f15a1355f46178e4e9d374d2" + }, "sample_spec": { "code_interpreter_cost_per_session": 0.0, "computer_use_input_cost_per_1k_tokens": 0.0, diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 0915b8dd1b9..4741e4cd9d3 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -17749,6 +17749,7 @@ async def reload_model_cost_map( # Immediately reload the model cost map in the current pod from litellm.litellm_core_utils.get_model_cost_map import ( ModelCostMapReloadUnavailable, + get_model_cost_map_provenance, refetch_model_cost_map, ) @@ -17762,6 +17763,7 @@ async def reload_model_cost_map( models_count = _swap_in_model_cost_map(reload_result.model_cost_map) current_time = utc_now() proxy_config.model_cost_map_loaded_at = current_time + provenance: Final = get_model_cost_map_provenance() # Publish a new revision so every other pod reloads on its next poll; this pod has # already served it, so adopt it here rather than reloading again a tick later @@ -17776,6 +17778,7 @@ async def reload_model_cost_map( "status": "success", "models_count": models_count, "timestamp": current_time.isoformat(), + **provenance, } except HTTPException: raise @@ -17896,12 +17899,17 @@ async def get_model_cost_map_reload_status( try: global prisma_client + from litellm.litellm_core_utils.get_model_cost_map import ( + get_model_cost_map_provenance, + ) + provenance: Final = get_model_cost_map_provenance() if prisma_client is None: verbose_proxy_logger.info("No database connection, returning not scheduled") - return reload_schedule_status(None) + return {**reload_schedule_status(None), **provenance} - return reload_schedule_status(await read_reload_schedule(prisma_client, MODEL_COST_MAP_RELOAD_PARAM_NAME)) + schedule: Final = await read_reload_schedule(prisma_client, MODEL_COST_MAP_RELOAD_PARAM_NAME) + return {**reload_schedule_status(schedule), **provenance} except Exception as e: verbose_proxy_logger.exception("Failed to get model cost map reload status: %s", e) raise HTTPException( @@ -17929,6 +17937,9 @@ async def get_model_cost_map_source( - url: the remote URL that was attempted (null when env-forced local) - is_env_forced: true if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage - fallback_reason: human-readable reason why remote failed (null on success) + - loaded_at: when this pod last loaded the map + - generated_at, source_revision: the _metadata stamp inside the loaded file + - etag: the ETag of the remote fetch (null for the bundled backup) - model_count: number of models in the currently loaded cost map """ # Read-only source info — admin viewers can read. diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index b1ffc1583e4..5edb3c0e9d8 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -1,4 +1,8 @@ { + "_metadata": { + "generated_at": "2026-09-07T23:38:47Z", + "source_revision": "cd681a573fd9f5b6f15a1355f46178e4e9d374d2" + }, "sample_spec": { "code_interpreter_cost_per_session": 0.0, "computer_use_input_cost_per_1k_tokens": 0.0, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index 47a1934a703..c40c2a67682 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -1,9 +1,27 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "LiteLLM model_prices_and_context_window.json", - "description": "Schema for LiteLLM's model price and context window registry (https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). Every top-level key except 'sample_spec' and 'fallback_generalizations' is a model id, optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. All costs are USD per unit. New optional fields are added regularly, so consumers should ignore unknown fields rather than reject them.", + "description": "Schema for LiteLLM's model price and context window registry (https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). Every top-level key except '_metadata', 'sample_spec', and 'fallback_generalizations' is a model id, optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. All costs are USD per unit. New optional fields are added regularly, so consumers should ignore unknown fields rather than reject them.", "type": "object", "properties": { + "_metadata": { + "type": "object", + "description": "Provenance of this file: when an automated sync last regenerated it and the commit it ran against. Human edits leave it untouched; not a model entry.", + "properties": { + "generated_at": { + "type": "string", + "format": "date-time" + }, + "source_revision": { + "type": "string" + } + }, + "required": [ + "generated_at", + "source_revision" + ], + "additionalProperties": false + }, "sample_spec": { "type": "object", "description": "Documentation placeholder illustrating the entry shape; not a real model and not schema-conformant (several values are prose)." diff --git a/scripts/sync_together_ai_models.py b/scripts/sync_together_ai_models.py index 12b128890f1..e009f1a7ce6 100644 --- a/scripts/sync_together_ai_models.py +++ b/scripts/sync_together_ai_models.py @@ -19,9 +19,11 @@ import argparse import json import os import re +import subprocess import sys from collections.abc import Mapping, Sequence from dataclasses import dataclass, field +from datetime import datetime, timezone from pathlib import Path from types import MappingProxyType from typing import Final @@ -33,6 +35,7 @@ MODELS_URL: Final = "https://api.together.ai/v1/models?serverless" DEPRECATIONS_URL: Final = "https://docs.together.ai/docs/deprecations.md" PROVIDER: Final = "together_ai" PREFIX: Final = "together_ai/" +METADATA_KEY: Final = "_metadata" SOURCE_URL: Final = "https://docs.together.ai/docs/serverless-models" COST_MAP_RELPATHS: Final = ( "model_prices_and_context_window.json", @@ -495,6 +498,23 @@ def _serialize(cost_map: CostMap) -> str: return json.dumps(cost_map, indent=4, ensure_ascii=False) + "\n" +def stamp_metadata(cost_map: CostMap, generated_at: str, source_revision: str) -> CostMap: + return {**cost_map, METADATA_KEY: {"generated_at": generated_at, "source_revision": source_revision}} + + +def _utc_now_iso() -> str: + return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + +def _source_revision(repo_root: Path) -> str: + from_env: Final = os.environ.get("GITHUB_SHA") + if from_env: + return from_env + return subprocess.run( + ("git", "rev-parse", "HEAD"), cwd=repo_root, check=True, capture_output=True, text=True + ).stdout.strip() + + def main(argv: Sequence[str]) -> int: parser: Final = argparse.ArgumentParser(description=__doc__) parser.add_argument("--write", action="store_true", help="apply the sync to the cost map files (default: dry run)") @@ -527,8 +547,9 @@ def main(argv: Sequence[str]) -> int: if args.pr_body_file is not None: args.pr_body_file.write_text(body) if args.write and outcome.has_changes: + stamped: Final = _serialize(stamp_metadata(outcome.cost_map, _utc_now_iso(), _source_revision(args.repo_root))) for relpath in COST_MAP_RELPATHS: - (args.repo_root / relpath).write_text(_serialize(outcome.cost_map)) + (args.repo_root / relpath).write_text(stamped) print(render_summary(outcome)) print() print(body) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index 18185126775..62f72495491 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -17,9 +17,11 @@ from litellm.litellm_core_utils.fallback_generalizations import ( ) from litellm.litellm_core_utils.get_model_cost_map import ( FALLBACK_GENERALIZATIONS_KEY, + METADATA_KEY, GetModelCostMap, _count_model_entries, _finalize_model_cost_map, + get_model_cost_map_provenance, ) @@ -31,6 +33,20 @@ def _load_root_cost_map() -> dict: return json.load(f) +def _load_bundled_stamp() -> dict: + path = os.path.join( + os.path.dirname(__file__), "../../../litellm/model_prices_and_context_window_backup.json" + ) + with open(path) as f: + return json.load(f)[METADATA_KEY] + + +_STAMP = { + "generated_at": "2026-09-07T00:00:00Z", + "source_revision": "0123456789abcdef0123456789abcdef01234567", +} + + def _make_models(n: int) -> dict: return { f"model-{i}": {"litellm_provider": "openai", "mode": "chat"} for i in range(n) @@ -41,6 +57,7 @@ def test_count_model_entries_excludes_reserved_keys(): m = _make_models(3) m["sample_spec"] = {"foo": "bar"} m[FALLBACK_GENERALIZATIONS_KEY] = {"rules": []} + m[METADATA_KEY] = dict(_STAMP) assert _count_model_entries(m) == 3 @@ -126,6 +143,39 @@ def test_finalize_with_no_block_clears_rules(): set_fallback_generalizations(previous) +def test_finalize_pops_metadata_and_records_provenance(): + finalized = _finalize_model_cost_map({**_make_models(2), METADATA_KEY: dict(_STAMP)}) + + assert METADATA_KEY not in finalized + assert len(finalized) == 2 + provenance = get_model_cost_map_provenance() + assert provenance["generated_at"] == _STAMP["generated_at"] + assert provenance["source_revision"] == _STAMP["source_revision"] + + +def test_finalize_without_metadata_clears_the_previous_stamp(): + _finalize_model_cost_map({**_make_models(2), METADATA_KEY: dict(_STAMP)}) + + _finalize_model_cost_map(_make_models(2)) + + provenance = get_model_cost_map_provenance() + assert provenance["generated_at"] is None + assert provenance["source_revision"] is None + + +@pytest.mark.parametrize( + "raw", + ["2026-09-07T00:00:00Z", {"generated_at": 42}, ["2026-09-07T00:00:00Z"]], + ids=["string", "wrong_field_type", "list"], +) +def test_finalize_tolerates_a_malformed_metadata_block(raw): + finalized = _finalize_model_cost_map({**_make_models(2), METADATA_KEY: raw}) + + assert METADATA_KEY not in finalized + assert len(finalized) == 2 + assert get_model_cost_map_provenance()["generated_at"] is None + + def test_shipped_backup_carries_the_claude_routing_rules(): """The bundled backup must ship the Claude routing rules so a fresh install (or an offline fallback) routes unknown Claude models without code changes. @@ -340,6 +390,10 @@ def _real_map_bytes() -> bytes: return json.dumps(_load_root_cost_map()).encode() +def _stamped_map_bytes(stamp: dict) -> bytes: + return json.dumps({**_load_root_cost_map(), METADATA_KEY: stamp}).encode() + + class _SleepRecorder: """Injected in place of asyncio.sleep so tests assert waits without real delay.""" @@ -500,6 +554,43 @@ async def test_refetch_respects_local_env_override(monkeypatch): assert len(result.model_cost_map) > 100 +@pytest.mark.asyncio +async def test_refetch_records_the_file_stamp_and_the_fetch_etag(): + """A reload reports which revision of the map it swapped in: the ``_metadata`` stamp the file + carries plus the ETag the fetch returned, with the stamp itself kept out of the model map.""" + client, _ = _mock_client( + [httpx.Response(200, headers={"ETag": 'W/"abc123"'}, content=_stamped_map_bytes(_STAMP))] + ) + + result = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) + + assert isinstance(result, ModelCostMapReloaded) + assert result.etag == 'W/"abc123"' + assert METADATA_KEY not in result.model_cost_map + assert get_model_cost_map_provenance() == { + "generated_at": _STAMP["generated_at"], + "source_revision": _STAMP["source_revision"], + "etag": 'W/"abc123"', + } + + +@pytest.mark.asyncio +async def test_refetch_local_override_reports_the_bundled_stamp_without_an_etag(monkeypatch): + """Forcing the bundled backup after a remote reload must drop the remote ETag, since the map + served is no longer the one that ETag identifies.""" + remote, _ = _mock_client( + [httpx.Response(200, headers={"ETag": 'W/"remote"'}, content=_stamped_map_bytes(_STAMP))] + ) + await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=remote) + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + + result = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0)) + + assert isinstance(result, ModelCostMapReloaded) + assert METADATA_KEY not in result.model_cost_map + assert get_model_cost_map_provenance() == {**_load_bundled_stamp(), "etag": None} + + # --------------------------------------------------------------------------- # get_model_cost_map: the boot-time load retries transient failures like a reload does # --------------------------------------------------------------------------- @@ -542,7 +633,7 @@ def test_boot_load_retries_transient_failures_instead_of_falling_back(): source = get_model_cost_map_source_info() assert source["source"] == "remote" assert source["fallback_reason"] is None - assert cost_map.keys() >= _load_root_cost_map().keys() - {"sample_spec", FALLBACK_GENERALIZATIONS_KEY} + assert cost_map.keys() >= _load_root_cost_map().keys() - {"sample_spec", FALLBACK_GENERALIZATIONS_KEY, METADATA_KEY} def test_boot_load_honors_retry_after_then_falls_back_after_max_attempts(): @@ -592,3 +683,39 @@ def test_boot_load_respects_local_env_override(monkeypatch): ) assert len(cost_map) > 100 assert get_model_cost_map_source_info()["is_env_forced"] is True + + +def test_boot_load_records_the_file_stamp_and_the_fetch_etag(): + client, _ = _mock_client( + [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_stamped_map_bytes(_STAMP))], + client_cls=httpx.Client, + ) + + cost_map = get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=client) + + assert METADATA_KEY not in cost_map + source = get_model_cost_map_source_info() + assert source["source"] == "remote" + assert source["etag"] == 'W/"boot"' + assert source["generated_at"] == _STAMP["generated_at"] + assert source["source_revision"] == _STAMP["source_revision"] + assert source["loaded_at"] is not None + + +def test_boot_load_fallback_to_the_backup_drops_the_remote_etag(): + """A boot that lands on the bundled backup reports the backup's own stamp and no ETag, even + when an earlier load in the same process had fetched the remote map.""" + remote, _ = _mock_client( + [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_stamped_map_bytes(_STAMP))], + client_cls=httpx.Client, + ) + get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=remote) + failing, _ = _mock_client([httpx.Response(404)], client_cls=httpx.Client) + + cost_map = get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=failing) + + assert METADATA_KEY not in cost_map + source = get_model_cost_map_source_info() + assert source["source"] == "local" + assert source["etag"] is None + assert {"generated_at": source["generated_at"], "source_revision": source["source_revision"]} == _load_bundled_stamp() diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py index b75ee1caccf..fb3583a7dd2 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py @@ -11,6 +11,7 @@ Routes covered: from __future__ import annotations import json +from pathlib import Path from unittest.mock import AsyncMock, MagicMock @@ -20,6 +21,13 @@ from .conftest import VOLATILE_KEYS, normalize # dict-equality assertions remain stable. _VOLATILE = VOLATILE_KEYS | frozenset({"timestamp"}) +_PROVENANCE = { + "generated_at": "2026-09-07T00:00:00Z", + "source_revision": "0123456789abcdef0123456789abcdef01234567", + "etag": 'W/"cost-map-etag"', +} +_ROOT_COST_MAP = Path(__file__).resolve().parents[4] / "model_prices_and_context_window.json" + # --------------------------------------------------------------------------- # Helpers @@ -42,6 +50,14 @@ def _attach_litellm_config(mock_prisma): return table +def _pin_provenance(monkeypatch): + """Fix what this process reports as its cost map revision, independent of the map loaded at import.""" + monkeypatch.setattr( + "litellm.litellm_core_utils.get_model_cost_map.get_model_cost_map_provenance", + lambda: dict(_PROVENANCE), + ) + + # --------------------------------------------------------------------------- # POST /reload/model_cost_map # --------------------------------------------------------------------------- @@ -55,6 +71,7 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): table = _attach_litellm_config(mock_prisma) monkeypatch.setattr(ps, "prisma_client", mock_prisma) + _pin_provenance(monkeypatch) fake_cost_map = {"gpt-4": {"input_cost": 0.03}, "gpt-3.5": {"input_cost": 0.002}} monkeypatch.setattr( @@ -83,6 +100,7 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): "status": "success", "models_count": 2, "timestamp": "", + **_PROVENANCE, } assert table.upsert.await_count == 1 update_payload = table.upsert.await_args.kwargs["data"]["update"] @@ -90,6 +108,57 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): assert update_payload["reload_revision"] == {"increment": 1} +def test_reload_model_cost_map_surfaces_provenance_and_keeps_metadata_out_of_the_model_list( + client, auth_as, monkeypatch, mock_prisma +): + """A real refetch through the reload route reports the file's stamp and the fetch ETag on every + status surface, while the ``_metadata`` block never shows up as a model anywhere.""" + import httpx + + import litellm + from litellm.proxy import proxy_server as ps + from litellm.proxy._types import LitellmUserRoles + + _attach_litellm_config(mock_prisma) + monkeypatch.setattr(ps, "prisma_client", mock_prisma) + monkeypatch.delenv("LITELLM_LOCAL_MODEL_COST_MAP", raising=False) + stamped = {**json.loads(_ROOT_COST_MAP.read_text()), "_metadata": {k: v for k, v in _PROVENANCE.items() if k != "etag"}} + served = httpx.Response(200, headers={"ETag": _PROVENANCE["etag"]}, content=json.dumps(stamped).encode()) + monkeypatch.setattr( + "litellm.litellm_core_utils.get_model_cost_map._default_reload_client", + lambda: httpx.AsyncClient(transport=httpx.MockTransport(lambda request: served)), + ) + monkeypatch.setattr("litellm.add_known_models", lambda model_cost_map=None: None) + monkeypatch.setattr("litellm.model_cost", {}, raising=False) + + async def _fake_invalidate(name): + return None + + monkeypatch.setattr(ps, "invalidate_config_param", _fake_invalidate) + + with auth_as(LitellmUserRoles.PROXY_ADMIN): + reload_response = client.post("/reload/model_cost_map") + source_response = client.get("/model/cost_map/source") + status_response = client.get("/schedule/model_cost_map_reload/status") + public_response = client.get("/public/litellm_model_cost_map") + + assert reload_response.status_code == 200 + reload_body = reload_response.json() + assert {key: reload_body[key] for key in _PROVENANCE} == _PROVENANCE + assert source_response.status_code == 200 + source_body = source_response.json() + assert {key: source_body[key] for key in _PROVENANCE} == _PROVENANCE + assert source_body["source"] == "remote" + assert status_response.status_code == 200 + assert {key: status_response.json()[key] for key in _PROVENANCE} == _PROVENANCE + assert public_response.status_code == 200 + public_body = public_response.json() + assert "_metadata" not in public_body + assert "_metadata" not in litellm.model_cost + assert "gpt-4o" in public_body + assert reload_body["models_count"] == len(litellm.model_cost) + + def test_reload_model_cost_map_fetch_failure_502_keeps_map( client, auth_as, monkeypatch, mock_prisma ): @@ -270,11 +339,12 @@ def test_cancel_model_cost_map_reload_no_db_500(client, auth_as, monkeypatch): def test_get_model_cost_map_reload_status_no_db_not_scheduled( client, auth_as, monkeypatch ): - """No prisma client → returns the not-scheduled shape (4 keys, all-null).""" + """No prisma client → returns the not-scheduled shape (all-null) plus the cost map provenance.""" from litellm.proxy import proxy_server as ps from litellm.proxy._types import LitellmUserRoles monkeypatch.setattr(ps, "prisma_client", None) + _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") assert response.status_code == 200 @@ -283,6 +353,7 @@ def test_get_model_cost_map_reload_status_no_db_not_scheduled( "interval_hours": None, "last_run": None, "next_run": None, + **_PROVENANCE, } @@ -300,6 +371,7 @@ def test_get_model_cost_map_reload_status_scheduled( config_row.last_run_at = None table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) + _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -309,6 +381,7 @@ def test_get_model_cost_map_reload_status_scheduled( "interval_hours": 12, "last_run": None, "next_run": None, + **_PROVENANCE, } @@ -328,6 +401,7 @@ def test_get_model_cost_map_reload_status_reports_persisted_last_run( config_row.last_run_at = datetime(2024, 1, 1, 6, 0, 0, tzinfo=timezone.utc) table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) + _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -337,6 +411,7 @@ def test_get_model_cost_map_reload_status_reports_persisted_last_run( "interval_hours": 6, "last_run": "2024-01-01T06:00:00+00:00", "next_run": "2024-01-01T12:00:00+00:00", + **_PROVENANCE, } @@ -356,6 +431,7 @@ def test_get_model_cost_map_reload_status_no_config_not_scheduled( config_row.last_run_at = None table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) + _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -365,6 +441,7 @@ def test_get_model_cost_map_reload_status_no_config_not_scheduled( "interval_hours": None, "last_run": None, "next_run": None, + **_PROVENANCE, } @@ -391,6 +468,8 @@ def test_get_model_cost_map_source_happy(client, auth_as, monkeypatch): "url": "https://example.invalid/cost_map.json", "is_env_forced": False, "fallback_reason": None, + "loaded_at": "2026-09-07T01:02:03+00:00", + **_PROVENANCE, } monkeypatch.setattr( "litellm.litellm_core_utils.get_model_cost_map.get_model_cost_map_source_info", @@ -406,6 +485,8 @@ def test_get_model_cost_map_source_happy(client, auth_as, monkeypatch): "url": "https://example.invalid/cost_map.json", "is_env_forced": False, "fallback_reason": None, + "loaded_at": "2026-09-07T01:02:03+00:00", + **_PROVENANCE, "model_count": 3, } diff --git a/tests/test_litellm/test_auto_update_price_and_context_window_file.py b/tests/test_litellm/test_auto_update_price_and_context_window_file.py new file mode 100644 index 00000000000..d3cda09cd96 --- /dev/null +++ b/tests/test_litellm/test_auto_update_price_and_context_window_file.py @@ -0,0 +1,54 @@ +"""Tests for .github/scripts/auto_update_price_and_context_window_file.py.""" + +import importlib.util +import json +import re +import sys +from pathlib import Path +from typing import Final + +_REPO_ROOT: Final = Path(__file__).resolve().parents[2] +_MODULE_PATH: Final = _REPO_ROOT / ".github" / "scripts" / "auto_update_price_and_context_window_file.py" +_spec: Final = importlib.util.spec_from_file_location("auto_update_price_and_context_window_file", _MODULE_PATH) +script: Final = importlib.util.module_from_spec(_spec) +sys.modules[_spec.name] = script +_spec.loader.exec_module(script) + +_LOCAL_FILE: Final = "model_prices_and_context_window.json" +_GENERATED_AT: Final = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z") + + +def _openrouter_row(model_id: str) -> dict: + return {"id": model_id, "context_length": 8192, "pricing": {"prompt": "0.000001", "completion": "0.000002"}} + + +def _serve(openrouter_rows: list) -> object: + async def fetch_data(url: str) -> list: + return openrouter_rows if "openrouter" in url else [] + + return fetch_data + + +def _read_local(tmp_path: Path) -> dict: + return json.loads((tmp_path / _LOCAL_FILE).read_text()) + + +def test_main_stamps_provenance_only_when_the_sync_changed_the_file(tmp_path: Path, monkeypatch) -> None: + monkeypatch.chdir(tmp_path) + monkeypatch.setenv("GITHUB_SHA", "feedface") + monkeypatch.setattr(script, "fetch_data", _serve([_openrouter_row("acme/x")])) + (tmp_path / _LOCAL_FILE).write_text(json.dumps({"sample_spec": {"input_cost_per_token": "USD"}}, indent=4) + "\n") + + script.main() + + written = _read_local(tmp_path) + assert written["openrouter/acme/x"]["litellm_provider"] == "openrouter" + assert written["_metadata"]["source_revision"] == "feedface" + assert _GENERATED_AT.fullmatch(written["_metadata"]["generated_at"]) + + sentinel = {**written, "_metadata": {**written["_metadata"], "generated_at": "2000-01-01T00:00:00Z"}} + (tmp_path / _LOCAL_FILE).write_text(json.dumps(sentinel, indent=4) + "\n") + + script.main() + + assert _read_local(tmp_path) == sentinel diff --git a/tests/test_litellm/test_cost_map_guard.py b/tests/test_litellm/test_cost_map_guard.py index 1b4330ed62c..1a60cf81164 100644 --- a/tests/test_litellm/test_cost_map_guard.py +++ b/tests/test_litellm/test_cost_map_guard.py @@ -141,6 +141,26 @@ def test_bot_may_not_change_special_root_keys() -> None: assert _failures(head) == ("bot PRs may not change fallback_generalizations",) +STAMP: Final = {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef0123456789abcdef01234567"} + + +def test_bot_may_stamp_and_restamp_metadata() -> None: + stamped = _snapshot({**BASE_MAP, "_metadata": STAMP}) + assert _failures(stamped) == () + assert _failures(stamped, bot=False) == () + + restamped = _snapshot( + { + **BASE_MAP, + "_metadata": {**STAMP, "generated_at": "2026-09-14T00:00:00Z"}, + "fallback_generalizations": {"rules": []}, + } + ) + assert guard.guard_failures(stamped, restamped, MAP_FILES, True) == ( + "bot PRs may not change fallback_generalizations", + ) + + def _commit(repo: Path, cost_map: dict[str, object], message: str) -> str: text = _serialize(cost_map) (repo / guard.COST_MAP_PATH).write_text(text) diff --git a/tests/test_litellm/test_model_prices_schema.py b/tests/test_litellm/test_model_prices_schema.py index c2c22c25998..3517f5840e8 100644 --- a/tests/test_litellm/test_model_prices_schema.py +++ b/tests/test_litellm/test_model_prices_schema.py @@ -98,6 +98,25 @@ def test_schema_rejects_malformed_entries(committed_schema: dict, entry: dict): assert not validator.is_valid({"some-model": entry}) +@pytest.mark.parametrize( + "metadata", + [ + "2026-09-07T00:00:00Z", + {"generated_at": "2026-09-07T00:00:00Z"}, + {"source_revision": "0123456789abcdef"}, + {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef", "author": "bot"}, + ], + ids=["not_an_object", "missing_revision", "missing_generated_at", "unknown_field"], +) +def test_schema_rejects_a_malformed_metadata_block(committed_schema: dict, metadata: object): + assert not build_validator(committed_schema).is_valid({"_metadata": metadata}) + + +def test_schema_accepts_the_provenance_stamp_as_a_non_model_root_key(committed_schema: dict): + stamp = {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef0123456789abcdef01234567"} + assert build_validator(committed_schema).is_valid({"_metadata": stamp}) + + def test_schema_accepts_minimal_and_unknown_optional_fields(committed_schema: dict): validator = build_validator(committed_schema) assert validator.is_valid({"some-model": {"litellm_provider": "openai"}}) diff --git a/tests/test_litellm/test_sync_together_ai_models.py b/tests/test_litellm/test_sync_together_ai_models.py index b8a85bcfbdc..f58f573c208 100644 --- a/tests/test_litellm/test_sync_together_ai_models.py +++ b/tests/test_litellm/test_sync_together_ai_models.py @@ -1,5 +1,6 @@ import importlib.util import json +import re from pathlib import Path from types import MappingProxyType @@ -369,6 +370,58 @@ def test_sync_is_idempotent_over_the_repo_cost_map() -> None: assert second.cost_map == first.cost_map +def test_stamp_metadata_adds_the_provenance_block_without_touching_models() -> None: + cost_map = {"sample_spec": {"input_cost_per_token": "USD"}, "together_ai/acme/x": {"mode": "chat"}} + + stamped = sync.stamp_metadata(cost_map, "2026-09-07T00:00:00Z", "feedface") + + assert stamped["_metadata"] == {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "feedface"} + assert {key: value for key, value in stamped.items() if key != "_metadata"} == cost_map + assert "_metadata" not in cost_map + + +def _write_registry(repo_root: Path, cost_map: dict) -> None: + for relpath in sync.COST_MAP_RELPATHS: + target = repo_root / relpath + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(json.dumps(cost_map, indent=4) + "\n") + + +def _read_registries(repo_root: Path) -> tuple[dict, ...]: + return tuple(json.loads((repo_root / relpath).read_text()) for relpath in sync.COST_MAP_RELPATHS) + + +def test_write_stamps_provenance_into_both_files_only_when_the_sync_changed_them(tmp_path: Path, monkeypatch) -> None: + monkeypatch.setenv("GITHUB_SHA", "feedface") + cost_map = json.loads((ROOT / "model_prices_and_context_window.json").read_text()) + dropped = next(f"together_ai/{model.id}" for model in RECORDED_CATALOG if f"together_ai/{model.id}" in cost_map) + _write_registry(tmp_path, {key: value for key, value in cost_map.items() if key not in {dropped, "_metadata"}}) + argv = ( + "--write", + "--models-json", + str(FIXTURES / "models_serverless.json"), + "--deprecations-md", + str(FIXTURES / "deprecations.md"), + "--repo-root", + str(tmp_path), + ) + + assert sync.main(argv) == 0 + + written = _read_registries(tmp_path) + assert written[0] == written[1] + assert dropped in written[0] + assert written[0]["_metadata"]["source_revision"] == "feedface" + assert re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", written[0]["_metadata"]["generated_at"]) + + sentinel = {**written[0], "_metadata": {**written[0]["_metadata"], "generated_at": "2000-01-01T00:00:00Z"}} + _write_registry(tmp_path, sentinel) + + assert sync.main(argv) == 0 + + assert _read_registries(tmp_path) == (sentinel, sentinel) + + def test_pr_body_lists_every_section_and_the_skipped_types() -> None: outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) body = sync.render_pr_body(outcome) diff --git a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx index 01381df1620..a85211f498c 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx @@ -32,8 +32,18 @@ const remoteSource = { url: "https://pricing.example.test/model_prices.json", is_env_forced: false, fallback_reason: null, + loaded_at: null, + generated_at: null, + source_revision: null, + etag: null, model_count: 1234, }; +const provenance = { + loaded_at: "2026-09-07T10:00:00Z", + generated_at: "2026-09-06T23:38:47Z", + source_revision: "cd681a573fd9f5b6f15a1355f46178e4e9d374d2", + etag: 'W/"eb8e9a53f4cc284b"', +}; describe("PriceDataReload", () => { beforeEach(() => { @@ -51,6 +61,30 @@ describe("PriceDataReload", () => { expect(screen.getByText("No periodic reload scheduled")).toBeInTheDocument(); }); + it("shows which revision of the cost map is loaded when the source reports one", async () => { + vi.mocked(getModelCostMapSource).mockResolvedValue({ ...remoteSource, ...provenance } as never); + render(); + + expect(await screen.findByText("Source revision:")).toBeInTheDocument(); + expect(screen.getByText("cd681a573fd9")).toBeInTheDocument(); + expect(screen.getByText("ETag:")).toBeInTheDocument(); + expect(screen.getByText('W/"eb8e9a53f4cc284b"')).toBeInTheDocument(); + expect(screen.getByText("Generated at:")).toBeInTheDocument(); + expect(screen.getByText(new Date(provenance.generated_at).toLocaleString())).toBeInTheDocument(); + expect(screen.getByText("Loaded at:")).toBeInTheDocument(); + expect(screen.getByText(new Date(provenance.loaded_at).toLocaleString())).toBeInTheDocument(); + }); + + it("hides the provenance rows when the loaded map carries no stamp", async () => { + render(); + + expect(await screen.findByText("Pricing Data Source")).toBeInTheDocument(); + expect(screen.queryByText("Generated at:")).not.toBeInTheDocument(); + expect(screen.queryByText("Source revision:")).not.toBeInTheDocument(); + expect(screen.queryByText("ETag:")).not.toBeInTheDocument(); + expect(screen.queryByText("Loaded at:")).not.toBeInTheDocument(); + }); + it("confirms an immediate reload and refreshes dependent data", async () => { const user = userEvent.setup(); const onReloadSuccess = vi.fn(); diff --git a/ui/litellm-dashboard/src/components/price_data_reload.tsx b/ui/litellm-dashboard/src/components/price_data_reload.tsx index 1c6801eede2..3bb70072937 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.tsx @@ -49,9 +49,17 @@ interface CostMapSourceInfo { url: string | null; is_env_forced: boolean; fallback_reason: string | null; + loaded_at: string | null; + generated_at: string | null; + source_revision: string | null; + etag: string | null; model_count: number; } +const SHORT_REVISION_LENGTH = 12; + +const shortRevision = (revision: string) => revision.slice(0, SHORT_REVISION_LENGTH); + const EMPTY_RELOAD_STATUS: ReloadStatus = { scheduled: false, interval_hours: null, @@ -89,6 +97,55 @@ const isValidReloadInterval = (value: number) => { return value >= 1 && value <= 168; }; +const formatDateTime = (dateTimeString: string | null) => { + if (!dateTimeString) return "Never"; + try { + return new Date(dateTimeString).toLocaleString(); + } catch { + return dateTimeString; + } +}; + +const CostMapProvenanceRows: React.FC<{ sourceInfo: CostMapSourceInfo }> = ({ sourceInfo }) => ( + <> + {sourceInfo.generated_at && ( +
+ Generated at: + {formatDateTime(sourceInfo.generated_at)} +
+ )} + + {sourceInfo.source_revision && ( +
+ Source revision: + + }> + {shortRevision(sourceInfo.source_revision)} + + {sourceInfo.source_revision} + +
+ )} + + {sourceInfo.etag && ( +
+ ETag: + + }>{sourceInfo.etag} + {sourceInfo.etag} + +
+ )} + + {sourceInfo.loaded_at && ( +
+ Loaded at: + {formatDateTime(sourceInfo.loaded_at)} +
+ )} + +); + const PriceDataReload: React.FC = ({ accessToken, onReloadSuccess, @@ -227,15 +284,6 @@ const PriceDataReload: React.FC = ({ } }; - const formatDateTime = (dateTimeString: string | null) => { - if (!dateTimeString) return "Never"; - try { - return new Date(dateTimeString).toLocaleString(); - } catch { - return dateTimeString; - } - }; - const getStatusText = () => { if (!reloadStatus?.scheduled) return "Not scheduled"; if (!reloadStatus.last_run) return "Ready"; @@ -334,6 +382,8 @@ const PriceDataReload: React.FC = ({
)} + + {sourceInfo.is_env_forced && (
diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 6fb08445aff..7b9b24c9627 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -8684,6 +8684,9 @@ export interface paths { * - url: the remote URL that was attempted (null when env-forced local) * - is_env_forced: true if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage * - fallback_reason: human-readable reason why remote failed (null on success) + * - loaded_at: when this pod last loaded the map + * - generated_at, source_revision: the _metadata stamp inside the loaded file + * - etag: the ETag of the remote fetch (null for the bundled backup) * - model_count: number of models in the currently loaded cost map */ get: operations["get_model_cost_map_source_model_cost_map_source_get"]; From 7d3b68fea5a6343781401e52f40b02f6c581541c Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 16:59:53 -0700 Subject: [PATCH 051/223] fix(files): preserve managed deletion routing and response identity --- .../proxy/hooks/managed_files.py | 13 ++- tests/e2e/batches/COVERAGE.md | 3 + .../proxy/test_managed_files_hook.py | 104 ++++++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index bc1eb6cebc2..6e0bb0da3f4 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -1779,7 +1779,16 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): # Remove conflicting keys from data to avoid duplicate keyword arguments filtered_data = {k: v for k, v in data.items() if k not in ("model", "file_id")} for model_id, model_file_id in specific_model_file_id_mapping.items(): - delete_response = await llm_router.afile_delete(model=model_id, file_id=model_file_id, **filtered_data) # type: ignore + credentials = llm_router.get_deployment_credentials_with_provider(model_id=model_id) + delete_data = { + **{k: v for k, v in filtered_data.items() if k != "_litellm_internal_model_credentials"}, + **( + {"_litellm_internal_model_credentials": MappingProxyType(dict(credentials))} + if credentials is not None + else {} + ), + } + delete_response = await llm_router.afile_delete(model=model_id, file_id=model_file_id, **delete_data) stored_file_object = await self.delete_unified_file_id(file_id, litellm_parent_otel_span) @@ -1790,7 +1799,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): prom_logger.record_managed_file_deleted(result="success") if stored_file_object: - return stored_file_object + return OpenAIFileObject.model_validate(stored_file_object).model_copy(update={"id": file_id}) elif delete_response: delete_response.id = file_id return delete_response diff --git a/tests/e2e/batches/COVERAGE.md b/tests/e2e/batches/COVERAGE.md index ca44fc95e25..919c39f21a2 100644 --- a/tests/e2e/batches/COVERAGE.md +++ b/tests/e2e/batches/COVERAGE.md @@ -138,6 +138,9 @@ output and error files returned by terminal batches. Bedrock deletion uses a sig restricted to the configured storage buckets and managed file prefixes. The low-RPM test submits with its restricted key and cleans up with the test administrator key +Managed deletion forwards the deployment's trusted bucket configuration and returns +the requested managed file ID even when stored output metadata carries a provider ID + Azure input uploads request `expires_after` anchored to `created_at` with `seconds=1209600`, and the lifecycle tests check the returned expiry. This is a fallback for interrupted runs: immediate deletion remains the normal cleanup. diff --git a/tests/test_litellm/enterprise/proxy/test_managed_files_hook.py b/tests/test_litellm/enterprise/proxy/test_managed_files_hook.py index 091b958d7c3..48fceb50403 100644 --- a/tests/test_litellm/enterprise/proxy/test_managed_files_hook.py +++ b/tests/test_litellm/enterprise/proxy/test_managed_files_hook.py @@ -1095,6 +1095,110 @@ async def test_afile_content_passes_trusted_model_credentials_to_router(): assert trusted_credentials["s3_bucket_name"] == "my-bucket" +def _managed_deletion_file_id(provider_file_id): + from litellm.types.utils import SpecialEnums + + value = SpecialEnums.LITELLM_MANAGED_FILE_COMPLETE_STR.value.format( + "application/json", "test-file", "batch-model", provider_file_id, "model-123" + ) + return base64.urlsafe_b64encode(value.encode()).decode().rstrip("=") + + +def _managed_files_with_deletion_row(unified_file_id, provider_file_id, file_object): + from litellm.caching import DualCache + from litellm.models.managed_files import LiteLLM_ManagedFileTable + from litellm_enterprise.proxy.hooks.managed_files import _PROXY_LiteLLMManagedFiles + + row = LiteLLM_ManagedFileTable( + unified_file_id=unified_file_id, + model_mappings={"model-123": provider_file_id}, + flat_model_file_ids=[provider_file_id], + file_object=file_object, + ) + table = MagicMock( + find_first=AsyncMock(return_value=row), + delete=AsyncMock(), + ) + return _PROXY_LiteLLMManagedFiles( + internal_usage_cache=DualCache(), + prisma_client=MagicMock(db=MagicMock(litellm_managedfiletable=table)), + ), table + + +@pytest.mark.asyncio +async def test_afile_delete_bedrock_uses_deployment_bucket_and_signed_s3_delete(monkeypatch): + import httpx + import respx + + from litellm import Router + + monkeypatch.delenv("AWS_S3_BUCKET_NAME", raising=False) + monkeypatch.delenv("AWS_S3_OUTPUT_BUCKET_NAME", raising=False) + monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True") + router = Router( + model_list=[ + { + "model_name": "bedrock-batch", + "litellm_params": { + "model": "bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0", + "aws_access_key_id": "AKIAEXAMPLE", + "aws_secret_access_key": "secret", + "aws_region_name": "us-west-2", + "s3_bucket_name": "my-bucket", + }, + "model_info": {"id": "model-123"}, + } + ], + num_retries=0, + ) + s3_uri = "s3://my-bucket/litellm-bedrock-files/input.jsonl" + unified_file_id = _managed_deletion_file_id(s3_uri) + managed_files, table = _managed_files_with_deletion_row(unified_file_id, s3_uri, None) + with respx.mock: + route = respx.delete( + "https://s3.us-west-2.amazonaws.com/my-bucket/litellm-bedrock-files/input.jsonl" + ).mock(return_value=httpx.Response(204)) + response = await managed_files.afile_delete( + file_id=unified_file_id, + litellm_parent_otel_span=None, + llm_router=router, + _litellm_internal_model_credentials={"s3_bucket_name": "request-bucket"}, + ) + + assert len(route.calls) == 1 + assert route.calls[0].request.headers["Authorization"].startswith("AWS4-HMAC-SHA256") + assert response.id == unified_file_id + assert response.deleted is True + table.delete.assert_awaited_once_with(where={"unified_file_id": unified_file_id}) + + +@pytest.mark.asyncio +async def test_afile_delete_returns_managed_id_for_stored_provider_output(): + from openai.types import FileDeleted + + provider_file_id = "file-error-output" + unified_file_id = _managed_deletion_file_id(provider_file_id) + stored_file = _make_file_object(provider_file_id) + managed_files, table = _managed_files_with_deletion_row(unified_file_id, provider_file_id, stored_file) + router = MagicMock( + get_deployment_credentials_with_provider=MagicMock(return_value=None), + afile_delete=AsyncMock(return_value=FileDeleted(id=provider_file_id, object="file", deleted=True)), + ) + response = await managed_files.afile_delete( + file_id=unified_file_id, + litellm_parent_otel_span=None, + llm_router=router, + _litellm_internal_model_credentials={"s3_bucket_name": "request-bucket"}, + ) + + assert response.id == unified_file_id + assert response.object == "file" + assert response.filename == stored_file.filename + assert stored_file.id == provider_file_id + router.afile_delete.assert_awaited_once_with(model="model-123", file_id=provider_file_id) + table.delete.assert_awaited_once_with(where={"unified_file_id": unified_file_id}) + + @pytest.mark.asyncio async def test_afile_content_bedrock_unified_id_end_to_end(monkeypatch): """ From 4cc0180eab7482a59722e68999def58e11077a72 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:06:34 -0700 Subject: [PATCH 052/223] fix(router): keep unresolved drop_params strings so DB rows and env refs survive The drop_params validator collapsed every string it did not recognize to None. A pre-fix DB row holds the flag as ciphertext, so a partial PATCH rebuilt the deployment without it and dropped the key from the stored row, and /model/new turned an os.environ/ reference into nothing before the loader could resolve it. The validator now returns the raw value when it is not a boolean flag, the field admits strings the way timeout already does, and the flag set follows pydantic's lax bool parsing instead of a hand-rolled true/false pair --- litellm/litellm_core_utils/core_helpers.py | 15 +++++----- litellm/types/router.py | 7 +++-- .../litellm_core_utils/test_core_helpers.py | 11 +++++-- .../test_model_management_endpoints.py | 27 ++++++++++++++++- .../proxy/proxy_server/test_proxy_config.py | 30 +++++++++++++++++++ tests/test_litellm/types/test_router.py | 23 ++++++++++++++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 4 +-- 7 files changed, 101 insertions(+), 16 deletions(-) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index 671b2ddce99..bd7a1b8f384 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -5,6 +5,7 @@ from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Any, Final, Literal import httpx +from pydantic import TypeAdapter, ValidationError from litellm._logging import verbose_logger from litellm.types.llms.openai import AllMessageValues, OpenAIChatCompletionFinishReason @@ -37,16 +38,16 @@ def safe_divide_seconds(seconds: float, denominator: float, default: float | Non return float(seconds / denominator) +_DROP_PARAMS_BOOL: Final = TypeAdapter(bool) + + def normalize_drop_params(value: object) -> bool | None: if isinstance(value, bool): return value - if isinstance(value, str): - lowered: Final = value.strip().lower() - if lowered == "true": - return True - if lowered == "false": - return False - return None + try: + return _DROP_PARAMS_BOOL.validate_python(value.strip() if isinstance(value, str) else value) + except ValidationError: + return None def safe_divide( diff --git a/litellm/types/router.py b/litellm/types/router.py index e2a04f9da77..47aea6430c3 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -315,7 +315,7 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): timeout: float | str | httpx.Timeout | None = None # if str, pass in as os.environ/ stream_timeout: float | str | None = None # timeout when making stream=True calls, if str, pass in as os.environ/ max_retries: int | None = None - drop_params: bool | None = None + drop_params: bool | str | None = None organization: str | None = None # for openai orgs configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS = None litellm_credential_name: str | None = None @@ -408,8 +408,9 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): @field_validator("drop_params", mode="before") @classmethod - def coerce_drop_params(cls, value: object) -> bool | None: - return normalize_drop_params(value) + def coerce_drop_params(cls, value: object) -> object: + normalized: Final = normalize_drop_params(value) + return value if normalized is None else normalized def __contains__(self, key) -> bool: # Define custom behavior for the 'in' operator diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index ab43141af23..8797f7c3591 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -268,11 +268,16 @@ class TestRedactNestedMatchAndRegexKeys: (" TRUE ", True), ("false", False), ("False", False), + ("yes", True), + ("off", False), + ("1", True), + (1, True), + (0, False), (None, None), - ("yes", None), ("", None), - (1, None), - (0, None), + ("os.environ/DROP_PARAMS", None), + ("v2:gcm:not-a-flag", None), + (2, None), ], ) def test_normalize_drop_params(value, expected): diff --git a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py index d90338f8480..c02f886fc31 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -30,7 +30,7 @@ from litellm.proxy.management_endpoints.model_management_endpoints import ( ) from litellm.proxy.utils import PrismaClient from litellm.router import Router -from litellm.types.router import Deployment, LiteLLM_Params, updateDeployment +from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo, updateDeployment, updateLiteLLMParams async def _passthrough_row(update_data): @@ -3070,6 +3070,31 @@ class TestUpdateDBModelBlocked: assert "blocked" not in result +class TestUpdateDBModelKeepsLegacyDropParams: + def test_partial_patch_keeps_encrypted_string_drop_params(self, monkeypatch): + from litellm.proxy.common_utils.encrypt_decrypt_utils import decrypt_value_helper + from litellm.proxy.management_endpoints.model_management_endpoints import update_db_model + + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-1234") + legacy_row = Deployment( + model_name="gpt-5-nano", + litellm_params=LiteLLM_Params( + model="openai/gpt-5-nano", + api_key=encrypt_value_helper(value="sk-old"), + drop_params=encrypt_value_helper(value="true"), + ), + model_info=ModelInfo(id="legacy-row"), + ) + + result = update_db_model( + db_model=legacy_row, + updated_patch=updateDeployment(litellm_params=updateLiteLLMParams(api_key="sk-new")), + ) + + stored = json.loads(result["litellm_params"]) + assert decrypt_value_helper(value=stored["drop_params"], key="drop_params") == "true" + + def _build_db_model_with_pricing(): """Wildcard deployment with custom pricing in litellm_params; Deployment.__init__ mirrors SPECIAL_MODEL_INFO_PARAMS into model_info, so both blobs hold the rate.""" diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 2babfe432f3..a4be9574f89 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -19,6 +19,7 @@ import pytest import litellm from litellm.proxy._types import CommonProxyErrors +from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper from litellm.proxy.proxy_server import ( ProxyConfig, _is_remote_module_url, @@ -2401,6 +2402,35 @@ def test_ProxyConfig__add_deployment_resolves_env_refs_on_arbitrary_field(monkey assert deployment.litellm_params.some_future_field == "resolved-custom-value" +@pytest.mark.parametrize( + "stored_drop_params", + ["true", "os.environ/DROP_PARAMS_FLAG"], +) +def test_ProxyConfig__add_deployment_turns_stored_drop_params_string_into_bool(monkeypatch, stored_drop_params): + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-1234") + monkeypatch.setenv("DROP_PARAMS_FLAG", "true") + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + db_model = SimpleNamespace( + model_id="model-1", + model_name="gpt-5-nano", + model_info={"id": "model-1"}, + litellm_params={ + "model": encrypt_value_helper(value="openai/gpt-5-nano"), + "drop_params": encrypt_value_helper(value=stored_drop_params), + }, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model]) + deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] + + assert added == 1 + assert deployment.litellm_params.drop_params is True + + # --------------------------------------------------------------------------- # ProxyConfig.decrypt_model_list_from_db # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/types/test_router.py b/tests/test_litellm/types/test_router.py index accd3b32a0d..2f4a29473c3 100644 --- a/tests/test_litellm/types/test_router.py +++ b/tests/test_litellm/types/test_router.py @@ -1,8 +1,10 @@ import pytest +from pydantic import ValidationError from litellm.types.router import ( SPECIAL_MODEL_INFO_PARAMS, Deployment, + GenericLiteLLMParams, LiteLLM_Params, ModelInfo, ) @@ -89,3 +91,24 @@ def test_pricing_strings_are_coerced_to_float(): def test_invalid_pricing_is_rejected(): with pytest.raises(ValueError, match='validation error for ModelInfo'): ModelInfo(id="x", input_cost_per_token="free") + + +@pytest.mark.parametrize( + "value, expected", + [ + (True, True), + ("true", True), + (" False ", False), + ("yes", True), + (None, None), + ("os.environ/DROP_PARAMS", "os.environ/DROP_PARAMS"), + ("v2:gcm:ciphertext-from-a-pre-fix-row", "v2:gcm:ciphertext-from-a-pre-fix-row"), + ], +) +def test_drop_params_coerces_flags_and_keeps_unresolved_strings(value, expected): + assert GenericLiteLLMParams(drop_params=value).drop_params == expected + + +def test_drop_params_rejects_non_flag_non_string_values(): + with pytest.raises(ValidationError): + GenericLiteLLMParams(drop_params=2) diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index f7552bfb397..7b5edf6acfd 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -29397,7 +29397,7 @@ export interface components { /** Default Api Key Tpm Limit */ default_api_key_tpm_limit?: number | null; /** Drop Params */ - drop_params?: boolean | null; + drop_params?: boolean | string | null; /** Gcs Bucket Name */ gcs_bucket_name?: string | null; /** Google Maps Grounding Cost Per Query */ @@ -39570,7 +39570,7 @@ export interface components { /** Default Api Key Tpm Limit */ default_api_key_tpm_limit?: number | null; /** Drop Params */ - drop_params?: boolean | null; + drop_params?: boolean | string | null; /** Gcs Bucket Name */ gcs_bucket_name?: string | null; /** Google Maps Grounding Cost Per Query */ From 1067697c7b471200cd26ee93834f71b09d013415 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:11:56 -0700 Subject: [PATCH 053/223] refactor(utils): gate the triton branch on bool(drop_params) like every other provider --- litellm/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/utils.py b/litellm/utils.py index 59db4162410..50282674868 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4302,7 +4302,7 @@ def get_optional_params( non_default_params=non_default_params, optional_params=optional_params, model=model, - drop_params=drop_params if drop_params is not None else False, + drop_params=bool(drop_params), ) elif custom_llm_provider == "maritalk": From dc09d9e7cfbd62f590f73fdb1844bc2aac5578bf Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:24:17 -0700 Subject: [PATCH 054/223] feat(bedrock): add TwelveLabs Marengo Embed 3.0 embeddings --- litellm/constants.py | 1 + litellm/llms/bedrock/embed/embedding.py | 2 +- .../twelvelabs_marengo_3_transformation.py | 204 +++++++++++++ .../twelvelabs_marengo_transformation.py | 51 +++- ...odel_prices_and_context_window_backup.json | 39 +++ litellm/types/llms/bedrock.py | 113 +++++++- litellm/utils.py | 2 +- model_prices_and_context_window.json | 39 +++ .../test_bedrock_async_invoke_embedding.py | 38 +++ .../bedrock/embed/test_bedrock_embedding.py | 129 +++++++++ ...est_twelvelabs_marengo_3_transformation.py | 268 ++++++++++++++++++ ..._bedrock_marengo_embed_3_model_metadata.py | 88 ++++++ 12 files changed, 961 insertions(+), 13 deletions(-) create mode 100644 litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py create mode 100644 tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py create mode 100644 tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py diff --git a/litellm/constants.py b/litellm/constants.py index d53686e5e5b..78cca3c6212 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1370,6 +1370,7 @@ bedrock_embedding_models: Final[set] = set( "cohere.embed-multilingual-v3", "cohere.embed-v4:0", "twelvelabs.marengo-embed-2-7-v1:0", + "twelvelabs.marengo-embed-3-0-v1:0", ] ) diff --git a/litellm/llms/bedrock/embed/embedding.py b/litellm/llms/bedrock/embed/embedding.py index 5fb86d476f4..ab27afcf817 100644 --- a/litellm/llms/bedrock/embed/embedding.py +++ b/litellm/llms/bedrock/embed/embedding.py @@ -474,7 +474,7 @@ class BedrockEmbedding(BaseAWSLLM): elif provider == "twelvelabs": batch_data = [] for i in input: - twelvelabs_request = TwelveLabsMarengoEmbeddingConfig()._transform_request( + twelvelabs_request = TwelveLabsMarengoEmbeddingConfig(model=model)._transform_request( input=i, inference_params=inference_params, async_invoke_route=has_async_invoke, diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py new file mode 100644 index 00000000000..2ea99db47f0 --- /dev/null +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py @@ -0,0 +1,204 @@ +""" +Request builder for Bedrock TwelveLabs Marengo Embed 3.0, whose payload nests the input under a key named after +``inputType`` instead of the flat 2.7 layout. + +Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-marengo-3.html +""" + +from collections.abc import Mapping +from types import MappingProxyType +from typing import Final, assert_never + +from pydantic import BaseModel, ConfigDict, TypeAdapter, ValidationError + +from litellm.llms.bedrock.common_utils import BedrockError +from litellm.types.llms.bedrock import ( + TWELVELABS_MARENGO_3_EMBEDDING_OPTIONS, + TWELVELABS_MARENGO_3_EMBEDDING_SCOPES, + TWELVELABS_MARENGO_3_EMBEDDING_TYPES, + TWELVELABS_MARENGO_3_INPUT_TYPES, + TwelveLabsMarengo3AudioRequest, + TwelveLabsMarengo3EmbeddingRequest, + TwelveLabsMarengo3ImageRequest, + TwelveLabsMarengo3MultiInputRequest, + TwelveLabsMarengo3NamedMediaSource, + TwelveLabsMarengo3RequestBase, + TwelveLabsMarengo3Segmentation, + TwelveLabsMarengo3TextImageRequest, + TwelveLabsMarengo3TextRequest, + TwelveLabsMarengo3TimedMediaInput, + TwelveLabsMarengo3TimedMediaOptions, + TwelveLabsMarengo3VideoRequest, + TwelveLabsMediaSource, + TwelveLabsS3Location, +) +from litellm.utils import get_base64_str + +MARENGO_3_MODEL_MARKER: Final = "marengo-embed-3" +S3_URI_PREFIX: Final = "s3://" +TIMED_MEDIA_OPTION_FIELDS: Final = MappingProxyType( + { + "startSec": True, + "endSec": True, + "segmentation": True, + "embeddingOption": True, + "embeddingType": True, + "embeddingScope": True, + } +) +TIMED_MEDIA_OPTIONS: Final = TypeAdapter(TwelveLabsMarengo3TimedMediaOptions) + + +def is_marengo_3_model(model: str | None) -> bool: + return MARENGO_3_MODEL_MARKER in (model or "") + + +class Marengo3Params(BaseModel): + model_config = ConfigDict(extra="ignore", frozen=True) + + inputType: TWELVELABS_MARENGO_3_INPUT_TYPES | None = None + input_type: TWELVELABS_MARENGO_3_INPUT_TYPES | None = None + media_source: str | None = None + media_sources: Mapping[str, str] | None = None + bucketOwner: str | None = None + startSec: float | None = None + endSec: float | None = None + segmentation: TwelveLabsMarengo3Segmentation | None = None + embeddingOption: tuple[TWELVELABS_MARENGO_3_EMBEDDING_OPTIONS, ...] | None = None + embeddingType: tuple[TWELVELABS_MARENGO_3_EMBEDDING_TYPES, ...] | None = None + embeddingScope: tuple[TWELVELABS_MARENGO_3_EMBEDDING_SCOPES, ...] | None = None + inferenceId: str | None = None + + @property + def resolved_input_type(self) -> TWELVELABS_MARENGO_3_INPUT_TYPES: + return self.inputType or self.input_type or "text" + + def timed_media_options(self) -> TwelveLabsMarengo3TimedMediaOptions: + return TIMED_MEDIA_OPTIONS.validate_python( + self.model_dump(include=TIMED_MEDIA_OPTION_FIELDS, exclude_none=True) + ) + + +def _s3_location(uri: str, bucket_owner: str | None) -> TwelveLabsS3Location: + if bucket_owner is None: + unowned: Final[TwelveLabsS3Location] = {"uri": uri} + return unowned + owned: Final[TwelveLabsS3Location] = {"uri": uri, "bucketOwner": bucket_owner} + return owned + + +def _media_source(media: str, bucket_owner: str | None) -> TwelveLabsMediaSource: + if not media.startswith(S3_URI_PREFIX): + inline: Final[TwelveLabsMediaSource] = {"base64String": get_base64_str(media)} + return inline + remote: Final[TwelveLabsMediaSource] = {"s3Location": _s3_location(media, bucket_owner)} + return remote + + +def _named_media_source(name: str, media: str, bucket_owner: str | None) -> TwelveLabsMarengo3NamedMediaSource: + named: Final[TwelveLabsMarengo3NamedMediaSource] = { + "name": name, + "mediaType": "image", + **_media_source(media, bucket_owner), + } + return named + + +def _timed_media_input(media: str, params: Marengo3Params) -> TwelveLabsMarengo3TimedMediaInput: + timed: Final[TwelveLabsMarengo3TimedMediaInput] = { + "mediaSource": _media_source(media, params.bucketOwner), + **params.timed_media_options(), + } + return timed + + +def _validated_params(inference_params: Mapping[str, object]) -> Marengo3Params: + try: + return Marengo3Params.model_validate(inference_params) + except ValidationError as error: + raise BedrockError(status_code=400, message=f"Invalid Marengo 3.0 parameters: {error}") from error + + +def _require(value: str | None, input_type: str, param_name: str) -> str: + if value is None: + raise BedrockError(status_code=400, message=f"Input type '{input_type}' requires the '{param_name}' parameter") + return value + + +def _require_media_sources(value: Mapping[str, str] | None) -> Mapping[str, str]: + if not value: + raise BedrockError( + status_code=400, + message="Input type 'multi_input' requires a non-empty 'media_sources' mapping of name to media", + ) + return value + + +def _request_base(inference_id: str | None) -> TwelveLabsMarengo3RequestBase: + if inference_id is None: + anonymous: Final[TwelveLabsMarengo3RequestBase] = {} + return anonymous + identified: Final[TwelveLabsMarengo3RequestBase] = {"inferenceId": inference_id} + return identified + + +def build_marengo_3_request(input: str, inference_params: Mapping[str, object]) -> TwelveLabsMarengo3EmbeddingRequest: + params: Final = _validated_params(inference_params) + base: Final = _request_base(params.inferenceId) + input_type: Final = params.resolved_input_type + match input_type: + case "text": + text_request: Final[TwelveLabsMarengo3TextRequest] = { + **base, + "inputType": "text", + "text": {"inputText": input}, + } + return text_request + case "image": + image_request: Final[TwelveLabsMarengo3ImageRequest] = { + **base, + "inputType": "image", + "image": {"mediaSource": _media_source(input, params.bucketOwner)}, + } + return image_request + case "video": + video_request: Final[TwelveLabsMarengo3VideoRequest] = { + **base, + "inputType": "video", + "video": _timed_media_input(input, params), + } + return video_request + case "audio": + audio_request: Final[TwelveLabsMarengo3AudioRequest] = { + **base, + "inputType": "audio", + "audio": _timed_media_input(input, params), + } + return audio_request + case "text_image": + text_image_request: Final[TwelveLabsMarengo3TextImageRequest] = { + **base, + "inputType": "text_image", + "text_image": { + "inputText": input, + "mediaSource": _media_source( + _require(params.media_source, input_type, "media_source"), params.bucketOwner + ), + }, + } + return text_image_request + case "multi_input": + media_sources: Final = tuple( + _named_media_source(name, media, params.bucketOwner) + for name, media in _require_media_sources(params.media_sources).items() + ) + multi_input_request: Final[TwelveLabsMarengo3MultiInputRequest] = { + **base, + "inputType": "multi_input", + "multi_input": {"inputText": input, "mediaSources": media_sources} + if input + else {"mediaSources": media_sources}, + } + return multi_input_request + case _: + assert_never(input_type) diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py index a39c59b0efd..79b5825d2eb 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py @@ -4,13 +4,19 @@ Transformation logic from OpenAI /v1/embeddings format to Bedrock TwelveLabs Mar Why separate file? Make it easy to see how transformation works Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-marengo.html +Marengo 3.0 docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-marengo-3.html """ from typing import Final, cast +from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( + build_marengo_3_request, + is_marengo_3_model, +) from litellm.types.llms.bedrock import ( TWELVELABS_EMBEDDING_INPUT_TYPES, TwelveLabsAsyncInvokeRequest, + TwelveLabsMarengo3EmbeddingRequest, TwelveLabsMarengoEmbeddingRequest, TwelveLabsOutputDataConfig, TwelveLabsS3Location, @@ -26,10 +32,13 @@ class TwelveLabsMarengoEmbeddingConfig: Supports text, image, video, and audio inputs. - InvokeModel: text and image inputs - StartAsyncInvoke: video, audio, image, and text inputs + + Marengo 3.0 (model ids containing "marengo-embed-3") nests the input under a key named after inputType and + adds the text_image and multi_input input types; that payload is built by build_marengo_3_request. """ - def __init__(self) -> None: - pass + def __init__(self, model: str | None = None) -> None: + self.is_marengo_3: Final = is_marengo_3_model(model) def get_supported_openai_params(self) -> list[str]: return [ @@ -41,13 +50,20 @@ class TwelveLabsMarengoEmbeddingConfig: "useFixedLengthSec", "minClipSec", "input_type", + "endSec", + "segmentation", + "embeddingType", + "embeddingScope", + "inferenceId", + "media_source", + "media_sources", ] def map_openai_params(self, non_default_params: dict, optional_params: dict) -> dict: for k, v in non_default_params.items(): if k == "encoding_format": # TwelveLabs doesn't have encoding_format, but we can map it to embeddingOption - if v == "float": + if v == "float" and not self.is_marengo_3: optional_params["embeddingOption"] = ["visual-text", "visual-image"] elif k == "textTruncate": optional_params["textTruncate"] = v @@ -56,7 +72,19 @@ class TwelveLabsMarengoEmbeddingConfig: elif k == "input_type": # Map input_type to inputType for Bedrock optional_params["inputType"] = v - elif k in ["startSec", "lengthSec", "useFixedLengthSec", "minClipSec"]: + elif k in ( + "startSec", + "lengthSec", + "useFixedLengthSec", + "minClipSec", + "endSec", + "segmentation", + "embeddingType", + "embeddingScope", + "inferenceId", + "media_source", + "media_sources", + ): optional_params[k] = v return optional_params @@ -77,7 +105,7 @@ class TwelveLabsMarengoEmbeddingConfig: async_invoke_route: bool = False, model_id: str | None = None, output_s3_uri: str | None = None, - ) -> TwelveLabsMarengoEmbeddingRequest | TwelveLabsAsyncInvokeRequest: + ) -> TwelveLabsMarengoEmbeddingRequest | TwelveLabsMarengo3EmbeddingRequest | TwelveLabsAsyncInvokeRequest: """ Transform OpenAI-style input to TwelveLabs Marengo format/async-invoke format. @@ -87,20 +115,27 @@ class TwelveLabsMarengoEmbeddingConfig: - Video inputs (async-invoke only) - Audio inputs (async-invoke only) - S3 URLs for all media types (async-invoke only) + - Marengo 3.0 only: text_image and multi_input inputs (nested payload) """ - # Get input_type or default to "text" input_type: Final = cast( TWELVELABS_EMBEDDING_INPUT_TYPES, inference_params.get("inputType") or inference_params.get("input_type") or "text", ) - # Validate that async-invoke is used for video/audio if input_type in ["video", "audio"] and not async_invoke_route: raise ValueError( f"Input type '{input_type}' requires async_invoke route. " f"Use model format: 'bedrock/async_invoke/model_id'" ) + if self.is_marengo_3: + marengo_3_request: Final = build_marengo_3_request(input=input, inference_params=inference_params) + if async_invoke_route and model_id: + return self._wrap_async_invoke_request( + model_input=marengo_3_request, model_id=model_id, output_s3_uri=output_s3_uri + ) + return marengo_3_request + transformed_request: Final[TwelveLabsMarengoEmbeddingRequest] = {"inputType": input_type} if input_type == "text": @@ -154,7 +189,7 @@ class TwelveLabsMarengoEmbeddingConfig: def _wrap_async_invoke_request( self, - model_input: TwelveLabsMarengoEmbeddingRequest, + model_input: TwelveLabsMarengoEmbeddingRequest | TwelveLabsMarengo3EmbeddingRequest, model_id: str, output_s3_uri: str | None = None, ) -> TwelveLabsAsyncInvokeRequest: diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index b1ffc1583e4..cc75354a495 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -690,6 +690,45 @@ "supports_embedding_image_input": true, "supports_image_input": true }, + "twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, + "us.twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, + "eu.twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, "twelvelabs.pegasus-1-2-v1:0": { "input_cost_per_video_per_second": 0.00049, "output_cost_per_token": 7.5e-06, diff --git a/litellm/types/llms/bedrock.py b/litellm/types/llms/bedrock.py index bed0ba3dc08..9f93886a9c6 100644 --- a/litellm/types/llms/bedrock.py +++ b/litellm/types/llms/bedrock.py @@ -1,7 +1,7 @@ import json from collections.abc import Sequence from enum import Enum -from typing import TYPE_CHECKING, Any, Final, Literal +from typing import TYPE_CHECKING, Any, Final, Literal, TypeAlias from typing_extensions import ReadOnly, Required, TypedDict, override @@ -557,7 +557,7 @@ class AmazonTitanMultimodalEmbeddingResponse(TypedDict): message: str # Specifies any errors that occur during generation. -# TwelveLabs Marengo Embed 2.7 types +# TwelveLabs Marengo Embed types TWELVELABS_EMBEDDING_INPUT_TYPES = Literal["text", "image", "video", "audio"] TWELVELABS_EMBEDDING_OPTIONS = Literal["visual-text", "visual-image", "audio"] @@ -591,6 +591,113 @@ class TwelveLabsMarengoEmbeddingResponse(TypedDict): endSec: float +TWELVELABS_MARENGO_3_INPUT_TYPES: TypeAlias = Literal["text", "image", "video", "audio", "text_image", "multi_input"] +TWELVELABS_MARENGO_3_EMBEDDING_OPTIONS: TypeAlias = Literal["visual", "audio", "transcription"] +TWELVELABS_MARENGO_3_EMBEDDING_TYPES: TypeAlias = Literal["separate_embedding", "fused_embedding"] +TWELVELABS_MARENGO_3_EMBEDDING_SCOPES: TypeAlias = Literal["clip", "asset"] + + +class TwelveLabsMarengo3FixedSegmentationConfig(TypedDict): + durationSec: ReadOnly[int] + + +class TwelveLabsMarengo3FixedSegmentation(TypedDict): + method: ReadOnly[Literal["fixed"]] + fixed: ReadOnly[TwelveLabsMarengo3FixedSegmentationConfig] + + +class TwelveLabsMarengo3DynamicSegmentationConfig(TypedDict): + minDurationSec: ReadOnly[int] + + +class TwelveLabsMarengo3DynamicSegmentation(TypedDict): + method: ReadOnly[Literal["dynamic"]] + dynamic: ReadOnly[TwelveLabsMarengo3DynamicSegmentationConfig] + + +TwelveLabsMarengo3Segmentation: TypeAlias = TwelveLabsMarengo3FixedSegmentation | TwelveLabsMarengo3DynamicSegmentation + + +class TwelveLabsMarengo3TextInput(TypedDict): + inputText: ReadOnly[str] + + +class TwelveLabsMarengo3ImageInput(TypedDict): + mediaSource: ReadOnly[TwelveLabsMediaSource] + + +class TwelveLabsMarengo3TimedMediaOptions(TypedDict, total=False): + startSec: ReadOnly[float] + endSec: ReadOnly[float] + segmentation: ReadOnly[TwelveLabsMarengo3Segmentation] + embeddingOption: ReadOnly[Sequence[TWELVELABS_MARENGO_3_EMBEDDING_OPTIONS]] + embeddingType: ReadOnly[Sequence[TWELVELABS_MARENGO_3_EMBEDDING_TYPES]] + embeddingScope: ReadOnly[Sequence[TWELVELABS_MARENGO_3_EMBEDDING_SCOPES]] + + +class TwelveLabsMarengo3TimedMediaInput(TwelveLabsMarengo3TimedMediaOptions): + mediaSource: Required[ReadOnly[TwelveLabsMediaSource]] + + +class TwelveLabsMarengo3TextImageInput(TypedDict): + inputText: ReadOnly[str] + mediaSource: ReadOnly[TwelveLabsMediaSource] + + +class TwelveLabsMarengo3NamedMediaSource(TwelveLabsMediaSource): + name: Required[ReadOnly[str]] + mediaType: Required[ReadOnly[Literal["image"]]] + + +class TwelveLabsMarengo3MultiInput(TypedDict, total=False): + inputText: ReadOnly[str] + mediaSources: Required[ReadOnly[Sequence[TwelveLabsMarengo3NamedMediaSource]]] + + +class TwelveLabsMarengo3RequestBase(TypedDict, total=False): + inferenceId: ReadOnly[str] + + +class TwelveLabsMarengo3TextRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["text"]] + text: ReadOnly[TwelveLabsMarengo3TextInput] + + +class TwelveLabsMarengo3ImageRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["image"]] + image: ReadOnly[TwelveLabsMarengo3ImageInput] + + +class TwelveLabsMarengo3VideoRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["video"]] + video: ReadOnly[TwelveLabsMarengo3TimedMediaInput] + + +class TwelveLabsMarengo3AudioRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["audio"]] + audio: ReadOnly[TwelveLabsMarengo3TimedMediaInput] + + +class TwelveLabsMarengo3TextImageRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["text_image"]] + text_image: ReadOnly[TwelveLabsMarengo3TextImageInput] + + +class TwelveLabsMarengo3MultiInputRequest(TwelveLabsMarengo3RequestBase): + inputType: ReadOnly[Literal["multi_input"]] + multi_input: ReadOnly[TwelveLabsMarengo3MultiInput] + + +TwelveLabsMarengo3EmbeddingRequest: TypeAlias = ( + TwelveLabsMarengo3TextRequest + | TwelveLabsMarengo3ImageRequest + | TwelveLabsMarengo3VideoRequest + | TwelveLabsMarengo3AudioRequest + | TwelveLabsMarengo3TextImageRequest + | TwelveLabsMarengo3MultiInputRequest +) + + class TwelveLabsS3OutputDataConfig(TypedDict): s3Uri: str @@ -601,7 +708,7 @@ class TwelveLabsOutputDataConfig(TypedDict): class TwelveLabsAsyncInvokeRequest(TypedDict): modelId: str - modelInput: TwelveLabsMarengoEmbeddingRequest + modelInput: ReadOnly[TwelveLabsMarengoEmbeddingRequest | TwelveLabsMarengo3EmbeddingRequest] outputDataConfig: TwelveLabsOutputDataConfig diff --git a/litellm/utils.py b/litellm/utils.py index d0e11bc9551..b98aa821ff3 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -3623,7 +3623,7 @@ def get_optional_params_embeddings( elif "cohere.embed" in model: object = litellm.BedrockCohereEmbeddingConfig() elif "twelvelabs" in model or "marengo" in model: - object = litellm.TwelveLabsMarengoEmbeddingConfig() + object = litellm.TwelveLabsMarengoEmbeddingConfig(model=model) elif "nova" in model.lower(): object = litellm.AmazonNovaEmbeddingConfig() else: # unmapped model diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index b1ffc1583e4..cc75354a495 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -690,6 +690,45 @@ "supports_embedding_image_input": true, "supports_image_input": true }, + "twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, + "us.twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, + "eu.twelvelabs.marengo-embed-3-0-v1:0": { + "input_cost_per_token": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, + "litellm_provider": "bedrock", + "max_input_tokens": 500, + "max_tokens": 500, + "mode": "embedding", + "output_cost_per_token": 0.0, + "output_vector_size": 512, + "supports_embedding_image_input": true, + "supports_image_input": true + }, "twelvelabs.pegasus-1-2-v1:0": { "input_cost_per_video_per_second": 0.00049, "output_cost_per_token": 7.5e-06, diff --git a/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py b/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py index 74a55cc1ef2..00f5145269a 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py +++ b/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py @@ -184,6 +184,44 @@ class TestBedrockAsyncInvokeEmbedding: request_url = mock_post.call_args.kwargs.get("url", "") assert "/async-invoke" in request_url + def test_async_invoke_marengo_3_wraps_the_nested_payload_with_the_base_model_id(self): + client = HTTPHandler() + + with patch.object(client, "post") as mock_post: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.text = json.dumps(async_invoke_response) + mock_response.json = lambda: json.loads(mock_response.text) + mock_post.return_value = mock_response + + response = litellm.embedding( + model="bedrock/async_invoke/twelvelabs.marengo-embed-3-0-v1:0", + input="s3://test-bucket/clip.mp4", + client=client, + aws_region_name="us-east-1", + aws_bedrock_runtime_endpoint="https://bedrock-runtime.us-east-1.amazonaws.com", + api_key="test-bearer-token-12345", + input_type="video", + embeddingOption=["visual", "audio"], + segmentation={"method": "fixed", "fixed": {"durationSec": 6}}, + output_s3_uri="s3://test-bucket/async-invoke-output/", + ) + + assert response._hidden_params._invocation_arn == async_invoke_response["invocationArn"] + assert mock_post.call_args.kwargs["url"].endswith("/async-invoke") + assert json.loads(mock_post.call_args.kwargs["data"]) == { + "modelId": "twelvelabs.marengo-embed-3-0-v1:0", + "modelInput": { + "inputType": "video", + "video": { + "mediaSource": {"s3Location": {"uri": "s3://test-bucket/clip.mp4"}}, + "segmentation": {"method": "fixed", "fixed": {"durationSec": 6}}, + "embeddingOption": ["visual", "audio"], + }, + }, + "outputDataConfig": {"s3OutputDataConfig": {"s3Uri": "s3://test-bucket/async-invoke-output/"}}, + } + @pytest.mark.asyncio async def test_async_invoke_twelvelabs_embedding_async_with_mock(self): """Test async invoke embedding with async calls.""" diff --git a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py index 50f8bbcf584..b37e991b0b2 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py +++ b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py @@ -1059,3 +1059,132 @@ def test_bedrock_embedding_bearer_token_never_runs_the_sigv4_credential_chain(mo assert response.data[0]["embedding"] == titan_embedding_response["embedding"] assert mock_post.call_args.kwargs["headers"]["Authorization"] == "Bearer env-bearer-token-12345" + + +marengo_3_embedding_response = {"data": [{"embedding": [0.01 * i for i in range(512)]}]} +MARENGO_3_DUCK = "data:image/png;base64,ZHVjaw==" + + +@pytest.mark.parametrize( + "model,kwargs,expected_body", + [ + ( + "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", + {"input_type": "text"}, + {"inputType": "text", "text": {"inputText": "a duck on water"}}, + ), + ( + "bedrock/twelvelabs.marengo-embed-3-0-v1:0", + {"input_type": "text"}, + {"inputType": "text", "text": {"inputText": "a duck on water"}}, + ), + ( + "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", + {"input_type": "text_image", "media_source": MARENGO_3_DUCK}, + { + "inputType": "text_image", + "text_image": {"inputText": "a duck on water", "mediaSource": {"base64String": "ZHVjaw=="}}, + }, + ), + ( + "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", + {"input_type": "multi_input", "media_sources": {"bird": MARENGO_3_DUCK}}, + { + "inputType": "multi_input", + "multi_input": { + "inputText": "a duck on water", + "mediaSources": [{"name": "bird", "mediaType": "image", "base64String": "ZHVjaw=="}], + }, + }, + ), + ], +) +def test_marengo_3_embedding_sends_the_nested_payload_and_parses_512_dims(model, kwargs, expected_body): + client = HTTPHandler() + + with patch.object(client, "post") as mock_post: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.text = json.dumps(marengo_3_embedding_response) + mock_response.json = lambda: json.loads(mock_response.text) + mock_post.return_value = mock_response + + response = litellm.embedding( + model=model, + input="a duck on water", + client=client, + aws_region_name="us-east-1", + api_key="test-bearer-token-12345", + **kwargs, + ) + + assert json.loads(mock_post.call_args.kwargs["data"]) == expected_body + assert mock_post.call_args.kwargs["url"].endswith(f"/model/{model.removeprefix('bedrock/').replace(':', '%3A')}/invoke") + assert len(response.data[0]["embedding"]) == 512 + assert response.data[0]["embedding"][:2] == [0.0, 0.01] + assert response.usage.prompt_tokens == 128 + + +def test_marengo_3_image_embedding_sends_the_media_under_the_image_key(): + client = HTTPHandler() + + with patch.object(client, "post") as mock_post: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.text = json.dumps(marengo_3_embedding_response) + mock_response.json = lambda: json.loads(mock_response.text) + mock_post.return_value = mock_response + + response = litellm.embedding( + model="bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", + input=MARENGO_3_DUCK, + client=client, + aws_region_name="us-east-1", + api_key="test-bearer-token-12345", + input_type="image", + ) + + assert json.loads(mock_post.call_args.kwargs["data"]) == { + "inputType": "image", + "image": {"mediaSource": {"base64String": "ZHVjaw=="}}, + } + assert len(response.data[0]["embedding"]) == 512 + assert response.data[0]["embedding"][:2] == [0.0, 0.01] + + +def test_marengo_2_7_embedding_keeps_the_flat_payload(): + client = HTTPHandler() + + with patch.object(client, "post") as mock_post: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.text = json.dumps(twelvelabs_embedding_response) + mock_response.json = lambda: json.loads(mock_response.text) + mock_post.return_value = mock_response + + response = litellm.embedding( + model="bedrock/us.twelvelabs.marengo-embed-2-7-v1:0", + input="a duck on water", + client=client, + aws_region_name="us-east-1", + api_key="test-bearer-token-12345", + input_type="text", + ) + + assert json.loads(mock_post.call_args.kwargs["data"]) == { + "inputType": "text", + "inputText": "a duck on water", + "textTruncate": "end", + } + assert response.data[0]["embedding"] == [0.1, 0.2, 0.3] + + +def test_marengo_3_text_image_without_media_source_is_a_bad_request(): + with pytest.raises(litellm.BadRequestError, match=r"text_image.*media_source"): + litellm.embedding( + model="bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", + input="a duck on water", + aws_region_name="us-east-1", + api_key="test-bearer-token-12345", + input_type="text_image", + ) diff --git a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py new file mode 100644 index 00000000000..0bf86352a5e --- /dev/null +++ b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py @@ -0,0 +1,268 @@ +import json + +import pytest + +from litellm.llms.bedrock.common_utils import BedrockError +from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( + build_marengo_3_request, + is_marengo_3_model, +) +from litellm.llms.bedrock.embed.twelvelabs_marengo_transformation import ( + TwelveLabsMarengoEmbeddingConfig, +) + +MARENGO_3_BASE = "twelvelabs.marengo-embed-3-0-v1:0" +MARENGO_3_US = "us.twelvelabs.marengo-embed-3-0-v1:0" +MARENGO_27_US = "us.twelvelabs.marengo-embed-2-7-v1:0" +DUCK_DATA_URL = "data:image/png;base64,ZHVjaw==" +OUTPUT_S3_URI = "s3://out-bucket/marengo/" + + +@pytest.mark.parametrize( + "model,expected", + [ + (MARENGO_3_BASE, True), + (MARENGO_3_US, True), + ("eu.twelvelabs.marengo-embed-3-0-v1:0", True), + ("async_invoke/twelvelabs.marengo-embed-3-0-v1:0", True), + (MARENGO_27_US, False), + ("twelvelabs.marengo-embed-2-7-v1:0", False), + (None, False), + ], +) +def test_is_marengo_3_model(model, expected): + assert is_marengo_3_model(model) is expected + + +def wire(request: object) -> object: + return json.loads(json.dumps(request)) + + +def test_text_request_nests_input_text_under_text(): + assert build_marengo_3_request("a dog on the beach", {"input_type": "text"}) == { + "inputType": "text", + "text": {"inputText": "a dog on the beach"}, + } + + +def test_missing_input_type_defaults_to_text(): + assert build_marengo_3_request("hello", {})["inputType"] == "text" + + +def test_camel_case_input_type_wins_over_snake_case(): + request = build_marengo_3_request(DUCK_DATA_URL, {"inputType": "image", "input_type": "text"}) + assert request["inputType"] == "image" + + +def test_image_request_strips_data_url_prefix(): + assert build_marengo_3_request(DUCK_DATA_URL, {"input_type": "image"}) == { + "inputType": "image", + "image": {"mediaSource": {"base64String": "ZHVjaw=="}}, + } + + +def test_image_request_from_s3_carries_bucket_owner(): + request = build_marengo_3_request("s3://media/duck.png", {"input_type": "image", "bucketOwner": "123456789012"}) + assert request == { + "inputType": "image", + "image": {"mediaSource": {"s3Location": {"uri": "s3://media/duck.png", "bucketOwner": "123456789012"}}}, + } + + +def test_s3_media_without_bucket_owner_omits_the_key(): + request = build_marengo_3_request("s3://media/duck.png", {"input_type": "image"}) + assert request["image"]["mediaSource"] == {"s3Location": {"uri": "s3://media/duck.png"}} + + +def test_text_image_request_pairs_text_with_media_source(): + request = build_marengo_3_request( + "a duck", {"input_type": "text_image", "media_source": DUCK_DATA_URL, "output_s3_uri": OUTPUT_S3_URI} + ) + assert request == { + "inputType": "text_image", + "text_image": {"inputText": "a duck", "mediaSource": {"base64String": "ZHVjaw=="}}, + } + + +def test_text_image_request_requires_media_source(): + with pytest.raises(BedrockError, match=r"text_image.*media_source") as excinfo: + build_marengo_3_request("a duck", {"input_type": "text_image"}) + assert excinfo.value.status_code == 400 + + +def test_multi_input_request_names_each_media_source(): + request = build_marengo_3_request( + "a photo of <@bird> next to <@dog>", + { + "input_type": "multi_input", + "media_sources": {"bird": DUCK_DATA_URL, "dog": "s3://media/dog.png"}, + "bucketOwner": "123456789012", + }, + ) + assert wire(request) == { + "inputType": "multi_input", + "multi_input": { + "inputText": "a photo of <@bird> next to <@dog>", + "mediaSources": [ + {"name": "bird", "mediaType": "image", "base64String": "ZHVjaw=="}, + { + "name": "dog", + "mediaType": "image", + "s3Location": {"uri": "s3://media/dog.png", "bucketOwner": "123456789012"}, + }, + ], + }, + } + + +def test_multi_input_without_text_omits_input_text(): + request = build_marengo_3_request("", {"input_type": "multi_input", "media_sources": {"bird": DUCK_DATA_URL}}) + assert "inputText" not in request["multi_input"] + assert request["multi_input"]["mediaSources"][0]["name"] == "bird" + + +@pytest.mark.parametrize("params", [{"input_type": "multi_input"}, {"input_type": "multi_input", "media_sources": {}}]) +def test_multi_input_request_requires_media_sources(params): + with pytest.raises(BedrockError, match=r"multi_input.*media_sources") as excinfo: + build_marengo_3_request("<@bird>", params) + assert excinfo.value.status_code == 400 + + +@pytest.mark.parametrize("input_type", ["video", "audio"]) +def test_timed_media_request_nests_every_option_under_the_media_key(input_type): + request = build_marengo_3_request( + "s3://media/clip.mp4", + { + "input_type": input_type, + "startSec": 2, + "endSec": 12.5, + "segmentation": {"method": "dynamic", "dynamic": {"minDurationSec": 4}}, + "embeddingOption": ["visual", "audio"], + "embeddingType": ["fused_embedding"], + "embeddingScope": ["clip", "asset"], + "inferenceId": "req-42", + }, + ) + assert wire(request) == { + "inputType": input_type, + input_type: { + "mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}, + "startSec": 2.0, + "endSec": 12.5, + "segmentation": {"method": "dynamic", "dynamic": {"minDurationSec": 4}}, + "embeddingOption": ["visual", "audio"], + "embeddingType": ["fused_embedding"], + "embeddingScope": ["clip", "asset"], + }, + "inferenceId": "req-42", + } + + +def test_timed_media_request_without_options_carries_only_the_media_source(): + request = build_marengo_3_request("s3://media/clip.mp4", {"input_type": "video"}) + assert request["video"] == {"mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}} + + +@pytest.mark.parametrize( + "params", + [ + {"input_type": "clip"}, + {"input_type": "video", "embeddingOption": ["visual-text"]}, + {"input_type": "video", "segmentation": {"method": "fixed", "dynamic": {"minDurationSec": 4}}}, + {"input_type": "multi_input", "media_sources": ["not", "a", "mapping"]}, + ], +) +def test_invalid_marengo_3_params_are_rejected_before_the_request_is_sent(params): + with pytest.raises(BedrockError, match=r"Invalid Marengo 3\.0 parameters") as excinfo: + build_marengo_3_request("s3://media/clip.mp4", params) + assert excinfo.value.status_code == 400 + + +def test_config_sends_the_nested_payload_for_marengo_3_and_the_flat_one_for_2_7(): + nested = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_US)._transform_request( + input="hello", inference_params={"input_type": "text"} + ) + flat = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_27_US)._transform_request( + input="hello", inference_params={"input_type": "text"} + ) + assert nested == {"inputType": "text", "text": {"inputText": "hello"}} + assert flat == {"inputType": "text", "inputText": "hello", "textTruncate": "end"} + + +def test_config_without_a_model_keeps_the_2_7_payload(): + request = TwelveLabsMarengoEmbeddingConfig()._transform_request(input="hello", inference_params={}) + assert request == {"inputType": "text", "inputText": "hello", "textTruncate": "end"} + + +@pytest.mark.parametrize("input_type", ["video", "audio"]) +def test_marengo_3_video_and_audio_still_require_the_async_route(input_type): + with pytest.raises(ValueError, match=f"Input type '{input_type}' requires async_invoke route"): + TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_BASE)._transform_request( + input="s3://media/clip.mp4", inference_params={"input_type": input_type} + ) + + +def test_marengo_3_async_invoke_wraps_the_nested_payload_with_the_base_model_id(): + request = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_BASE)._transform_request( + input="s3://media/clip.mp4", + inference_params={"input_type": "video", "embeddingOption": ["visual"], "output_s3_uri": OUTPUT_S3_URI}, + async_invoke_route=True, + model_id="async_invoke%2Ftwelvelabs.marengo-embed-3-0-v1%3A0", + output_s3_uri=OUTPUT_S3_URI, + ) + assert wire(request) == { + "modelId": MARENGO_3_BASE, + "modelInput": { + "inputType": "video", + "video": {"mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}, "embeddingOption": ["visual"]}, + }, + "outputDataConfig": {"s3OutputDataConfig": {"s3Uri": OUTPUT_S3_URI}}, + } + + +def test_marengo_3_async_invoke_requires_an_output_s3_uri(): + with pytest.raises(ValueError, match="output_s3_uri cannot be empty"): + TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_BASE)._transform_request( + input="hello", + inference_params={"input_type": "text"}, + async_invoke_route=True, + model_id=MARENGO_3_BASE, + output_s3_uri="", + ) + + +def test_encoding_format_float_no_longer_injects_2_7_embedding_options_for_marengo_3(): + marengo_3 = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_US).map_openai_params( + non_default_params={"encoding_format": "float"}, optional_params={} + ) + marengo_27 = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_27_US).map_openai_params( + non_default_params={"encoding_format": "float"}, optional_params={} + ) + assert marengo_3 == {} + assert marengo_27 == {"embeddingOption": ["visual-text", "visual-image"]} + + +def test_marengo_3_only_params_are_forwarded_by_map_openai_params(): + mapped = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_US).map_openai_params( + non_default_params={ + "input_type": "text_image", + "media_source": DUCK_DATA_URL, + "media_sources": {"bird": DUCK_DATA_URL}, + "endSec": 5, + "segmentation": {"method": "fixed", "fixed": {"durationSec": 6}}, + "embeddingType": ["separate_embedding"], + "embeddingScope": ["clip"], + "inferenceId": "req-1", + }, + optional_params={}, + ) + assert mapped == { + "inputType": "text_image", + "media_source": DUCK_DATA_URL, + "media_sources": {"bird": DUCK_DATA_URL}, + "endSec": 5, + "segmentation": {"method": "fixed", "fixed": {"durationSec": 6}}, + "embeddingType": ["separate_embedding"], + "embeddingScope": ["clip"], + "inferenceId": "req-1", + } diff --git a/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py b/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py new file mode 100644 index 00000000000..300dfeb5238 --- /dev/null +++ b/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py @@ -0,0 +1,88 @@ +import json +from pathlib import Path + +import pytest + +import litellm +from litellm.constants import bedrock_embedding_models +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.types.utils import Usage + +REPO_ROOT = Path(__file__).parents[2] +MAIN_PATH = REPO_ROOT / "model_prices_and_context_window.json" +BACKUP_PATH = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json" + +BASE_MODEL = "twelvelabs.marengo-embed-3-0-v1:0" +PROFILE_MODELS = ("us.twelvelabs.marengo-embed-3-0-v1:0", "eu.twelvelabs.marengo-embed-3-0-v1:0") +ALL_MODELS = (BASE_MODEL, *PROFILE_MODELS) + +TEXT_REQUEST_COST = 7e-05 +IMAGE_REQUEST_COST = 0.0001 +VIDEO_COST_PER_SECOND = 0.0007 +AUDIO_COST_PER_SECOND = 0.00014 + + +def _load(path): + with open(path) as f: + return json.load(f) + + +@pytest.mark.parametrize("model", ALL_MODELS) +def test_marengo_embed_3_specs(model): + info = _load(MAIN_PATH).get(model) + assert info is not None, f"{model} missing from model_prices_and_context_window.json" + + assert info["litellm_provider"] == "bedrock" + assert info["mode"] == "embedding" + assert info["input_cost_per_token"] == TEXT_REQUEST_COST + assert info["output_cost_per_token"] == 0.0 + assert info["max_input_tokens"] == 500 + assert info["max_tokens"] == 500 + assert info["output_vector_size"] == 512 + assert info["supports_embedding_image_input"] is True + assert info["supports_image_input"] is True + assert "deprecation_date" not in info + + routed_model, provider, _, _ = get_llm_provider(model=f"bedrock/{model}") + assert routed_model == model + assert provider == "bedrock" + + +@pytest.mark.parametrize("model", PROFILE_MODELS) +def test_marengo_embed_3_inference_profiles_price_image_video_and_audio(model): + info = _load(MAIN_PATH)[model] + assert info["input_cost_per_image"] == IMAGE_REQUEST_COST + assert info["input_cost_per_video_per_second"] == VIDEO_COST_PER_SECOND + assert info["input_cost_per_audio_per_second"] == AUDIO_COST_PER_SECOND + + +@pytest.mark.parametrize("model", ALL_MODELS) +def test_marengo_embed_3_is_visible_to_callers(model, local_model_cost_map): + info = litellm.get_model_info(model=model, custom_llm_provider="bedrock") + assert info["mode"] == "embedding" + assert info["output_vector_size"] == 512 + assert info["max_input_tokens"] == 500 + + +@pytest.mark.parametrize("model", ALL_MODELS) +def test_marengo_embed_3_text_request_is_billed(model, local_model_cost_map): + usage = Usage(prompt_tokens=128, completion_tokens=0, total_tokens=128) + prompt_cost, completion_cost = litellm.cost_per_token( + model=model, usage_object=usage, custom_llm_provider="bedrock" + ) + assert prompt_cost == pytest.approx(128 * TEXT_REQUEST_COST) + assert completion_cost == 0.0 + + +def test_marengo_embed_3_is_a_known_bedrock_embedding_model(): + assert BASE_MODEL in bedrock_embedding_models + + +@pytest.mark.parametrize("model", ALL_MODELS) +def test_backup_matches_main(model): + main_cost = _load(MAIN_PATH) + backup_cost = _load(BACKUP_PATH) + + assert model in main_cost, f"{model} missing from model_prices_and_context_window.json" + assert model in backup_cost, f"{model} missing from model_prices_and_context_window_backup.json" + assert backup_cost[model] == main_cost[model], f"{model} differs between main and backup model cost maps" From 6c1bba54c2c2d8e2b8c47673678eccdcda4f36a2 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:28:58 -0700 Subject: [PATCH 055/223] fix(ui): show a malformed generated_at stamp as-is on the Price Data Reload card --- .../src/components/price_data_reload.test.tsx | 13 +++++++++++++ .../src/components/price_data_reload.tsx | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx index a85211f498c..fde69675b72 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx @@ -75,6 +75,19 @@ describe("PriceDataReload", () => { expect(screen.getByText(new Date(provenance.loaded_at).toLocaleString())).toBeInTheDocument(); }); + it("shows a malformed generated_at stamp as-is instead of Invalid Date", async () => { + vi.mocked(getModelCostMapSource).mockResolvedValue({ + ...remoteSource, + ...provenance, + generated_at: "yesterday-ish", + } as never); + render(); + + expect(await screen.findByText("Generated at:")).toBeInTheDocument(); + expect(screen.getByText("yesterday-ish")).toBeInTheDocument(); + expect(screen.queryByText("Invalid Date")).not.toBeInTheDocument(); + }); + it("hides the provenance rows when the loaded map carries no stamp", async () => { render(); diff --git a/ui/litellm-dashboard/src/components/price_data_reload.tsx b/ui/litellm-dashboard/src/components/price_data_reload.tsx index 3bb70072937..c152916f271 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.tsx @@ -99,11 +99,8 @@ const isValidReloadInterval = (value: number) => { const formatDateTime = (dateTimeString: string | null) => { if (!dateTimeString) return "Never"; - try { - return new Date(dateTimeString).toLocaleString(); - } catch { - return dateTimeString; - } + const parsed = new Date(dateTimeString); + return Number.isNaN(parsed.getTime()) ? dateTimeString : parsed.toLocaleString(); }; const CostMapProvenanceRows: React.FC<{ sourceInfo: CostMapSourceInfo }> = ({ sourceInfo }) => ( From aa1c76bc3b601e8beef987101297a9e7f94f8560 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:28:59 -0700 Subject: [PATCH 056/223] test(cost_map): skip every reserved top-level key in the price map schema test --- tests/test_litellm/test_utils.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 8a56a84ade7..f6c9a4537a1 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -21,6 +21,7 @@ from litellm._logging import ( verbose_logger, ) from litellm.integrations.custom_logger import CustomLogger +from litellm.litellm_core_utils.get_model_cost_map import RESERVED_TOP_LEVEL_KEYS from litellm.proxy.utils import is_valid_api_key from litellm.types.utils import ( CallTypes, @@ -1218,15 +1219,12 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): with open(prod_json, "r") as model_prices_file: actual_json = json.load(model_prices_file) assert isinstance(actual_json, dict) - actual_json.pop( - "sample_spec", None - ) # remove the sample, whose schema is inconsistent with the real data - actual_json.pop( - "fallback_generalizations", None - ) # reserved meta key, not a model entry + model_entries: Final = { + key: value for key, value in actual_json.items() if key not in RESERVED_TOP_LEVEL_KEYS + } # Validate schema - validate(actual_json, INTENDED_SCHEMA) + validate(model_entries, INTENDED_SCHEMA) # Validate cost values # Define exceptions for models that are allowed to have costs > 1 @@ -1237,7 +1235,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "runwayml/seedance2", # 4K output is 150 credits/second = $1.50/second ] - is_valid, violations = validate_model_cost_values(actual_json, exceptions) + is_valid, violations = validate_model_cost_values(model_entries, exceptions) if not is_valid: error_message = "Cost validation failed:\n" + "\n".join(violations) @@ -1268,8 +1266,7 @@ def test_max_tokens_consistency(): inconsistencies = [] for model_name, config in models.items(): - # Skip the sample_spec - if model_name == "sample_spec": + if model_name in RESERVED_TOP_LEVEL_KEYS: continue # Check if both max_tokens and max_output_tokens exist From fb7d06da4b75f04ab6487e8dabb3ac0f0a54407a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:45:53 -0700 Subject: [PATCH 057/223] test(budget_reservation): type the tiny-budget reservation helper --- .../proxy/spend_tracking/test_budget_reservation.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py b/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py index 9935dbceb7d..5e88268c283 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py +++ b/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py @@ -53,7 +53,7 @@ async def test_non_exempt_llm_route_still_reserves_budget(): ANTHROPIC_MESSAGES: Final = [{"role": "user", "content": "hello!!!"}] -COUNT_TOKENS_REQUESTS: Final = ( +COUNT_TOKENS_REQUESTS: Final[tuple[tuple[str, dict[str, object]], ...]] = ( ("/v1/messages/count_tokens", {"model": "claude-sonnet-5", "messages": ANTHROPIC_MESSAGES}), ("/v1beta/models/gemini-3.8-flash:countTokens", {"contents": [{"role": "user", "parts": [{"text": "hello!!!"}]}]}), ) @@ -68,7 +68,7 @@ def spend_counter_cache(monkeypatch: pytest.MonkeyPatch) -> DualCache: return cache -async def _reserve_for_tiny_budget_key(route: str, request_body: dict) -> dict | None: +async def _reserve_for_tiny_budget_key(route: str, request_body: dict[str, object]) -> dict[str, object] | None: return await reserve_budget_for_request( request_body=request_body, route=route, @@ -85,7 +85,7 @@ async def _reserve_for_tiny_budget_key(route: str, request_body: dict) -> dict | @pytest.mark.asyncio @pytest.mark.parametrize(("route", "request_body"), COUNT_TOKENS_REQUESTS) async def test_repeated_token_counting_never_touches_a_tiny_budget( - spend_counter_cache: DualCache, route: str, request_body: dict + spend_counter_cache: DualCache, route: str, request_body: dict[str, object] ): counter_key: Final = f"spend:key:{TINY_BUDGET_KEY_TOKEN}" @@ -97,8 +97,10 @@ async def test_repeated_token_counting_never_touches_a_tiny_budget( "/v1/messages", {"model": "claude-sonnet-5", "max_tokens": 16, "messages": ANTHROPIC_MESSAGES} ) assert completion is not None - assert completion["reserved_cost"] > 0 - assert spend_counter_cache.in_memory_cache.get_cache(key=counter_key) == pytest.approx(completion["reserved_cost"]) + reserved_cost: Final = completion["reserved_cost"] + assert isinstance(reserved_cost, float) + assert reserved_cost > 0 + assert spend_counter_cache.in_memory_cache.get_cache(key=counter_key) == pytest.approx(reserved_cost) BEDROCK_SONNET: Final = "us.anthropic.claude-sonnet-4-6" From 1d7e81cf5d3a29dd4731b3282cf0842aac854ea1 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:47:06 -0700 Subject: [PATCH 058/223] fix(streaming): guard empty choices and missing role when assembling stream chunks --- .../streaming_chunk_builder_utils.py | 23 ++-- .../test_streaming_chunk_builder_utils.py | 129 +++++++----------- 2 files changed, 66 insertions(+), 86 deletions(-) diff --git a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py index 81955fe769e..cf9604a0fd5 100644 --- a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py +++ b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py @@ -24,6 +24,7 @@ from litellm.types.utils import ( Choices, CompletionTokensDetails, CompletionTokensDetailsWrapper, + Delta, Function, FunctionCall, ModelResponse, @@ -326,6 +327,18 @@ class ChunkProcessor: return chunk_id return "" + @staticmethod + def _get_role_from_chunks(chunks: Sequence["_BaseChunk"]) -> str: + return ChunkProcessor._role_of_choice(next((c["choices"][0] for c in chunks if c.get("choices")), None)) + + @staticmethod + def _role_of_choice(choice: object) -> str: + match choice: + case StreamingChoices(delta=Delta(role=str() as role)) | {"delta": {"role": str() as role}} if role: + return role + case _: + return "assistant" + @staticmethod def _get_model_from_chunks(chunks: Sequence["_BaseChunk"], first_chunk_model: str) -> str: """ @@ -353,15 +366,7 @@ class ChunkProcessor: model: Final = ChunkProcessor._get_model_from_chunks(chunks, first_chunk_model) system_fingerprint: Final = chunk.get("system_fingerprint", None) - # Fall back to None rather than `chunk`: if no chunk carries a non-empty - # `choices` array, indexing [0] on the first chunk raises IndexError. - first_chunk_with_choices = next((c for c in chunks if c.get("choices")), None) - role: str = "assistant" - if first_chunk_with_choices is not None: - _choices = first_chunk_with_choices["choices"] - if len(_choices) > 0: - # `delta` may be absent or omit `role` (e.g. content-only deltas). - role = _choices[0].get("delta", {}).get("role") or "assistant" + role: Final = ChunkProcessor._get_role_from_chunks(chunks) finish_reason = "stop" for chunk in chunks: if "choices" in chunk and len(chunk["choices"]) > 0: diff --git a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py index 2d2451e73f7..626b8a63b20 100644 --- a/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py @@ -1,4 +1,6 @@ import json +from collections.abc import Mapping, Sequence +from typing import Final import pytest @@ -1478,104 +1480,77 @@ def test_calculate_usage_fills_unknown_split_from_reasoning_estimate( assert usage.completion_tokens_details.text_tokens == expected_text_tokens -def _empty_choices_chunk(**extra): - chunk = { - "id": "chatcmpl-empty-choices", +def _openai_chunk( + choices: Sequence[Mapping[str, object]], usage: Mapping[str, int] | None = None +) -> dict[str, object]: + base: Final = { + "id": "chatcmpl-lit6552", "object": "chat.completion.chunk", "created": 1, - "model": "claude-opus-4-8", - "choices": [], + "model": "gpt-5.4-mini", + "choices": list(choices), } - chunk.update(extra) - return chunk + return base if usage is None else {**base, "usage": dict(usage)} @pytest.mark.parametrize( "chunks", [ + pytest.param([_openai_chunk(choices=[]), _openai_chunk(choices=[])], id="all_empty_choices_dicts"), pytest.param( - [_empty_choices_chunk(), _empty_choices_chunk()], - id="all_chunks_have_empty_choices", - ), - pytest.param( - [ - _empty_choices_chunk(usage={"prompt_tokens": 10}), - _empty_choices_chunk(usage={"completion_tokens": 0}), - ], - id="usage_only_chunks", + [ModelResponseStream(model="gpt-5.4-mini", choices=[]) for _ in range(2)], + id="all_empty_choices_objects", ), ], ) -def test_build_base_response_handles_empty_choices(chunks): - """Empty `choices` arrays must not raise IndexError. - - `next((c for c in chunks if c.get("choices")), chunk)` used to fall back to the - first chunk, whose `choices` may be `[]`, so `["choices"][0]` went out of range. - The resulting error is surfaced to the client mid-stream and the request never - reaches SpendLogs. - """ - processor = ChunkProcessor(chunks=list(chunks)) - - response = processor.build_base_response(list(chunks)) +def test_stream_chunk_builder_survives_all_empty_choices(chunks: Sequence[object]) -> None: + response: Final = stream_chunk_builder(chunks=list(chunks)) + assert response is not None assert response.choices[0].message.role == "assistant" + assert response.choices[0].finish_reason == "stop" + + +def test_stream_chunk_builder_keeps_usage_from_usage_only_frames() -> None: + usage_frame: Final = _openai_chunk( + choices=[], usage={"prompt_tokens": 10, "completion_tokens": 0, "total_tokens": 10} + ) + + response: Final = stream_chunk_builder(chunks=[usage_frame]) + + assert response is not None + assert response.choices[0].message.role == "assistant" + assert response.usage.prompt_tokens == 10 + assert response.usage.total_tokens == 10 @pytest.mark.parametrize( "delta", - [ - pytest.param({"content": "Hello"}, id="delta_without_role"), - pytest.param({}, id="delta_empty_dict"), - ], + [pytest.param({"content": "Hi"}, id="delta_without_role"), pytest.param({}, id="empty_delta")], ) -def test_build_base_response_handles_delta_without_role(delta): - """A `delta` that omits `role` must not raise KeyError.""" - chunks = [ - { - "id": "chatcmpl-no-role", - "object": "chat.completion.chunk", - "created": 1, - "model": "claude-opus-4-8", - "choices": [{"index": 0, "delta": delta, "finish_reason": None}], - } +def test_stream_chunk_builder_defaults_role_when_delta_omits_it(delta: Mapping[str, str]) -> None: + chunks: Final = [ + _openai_chunk(choices=[{"index": 0, "delta": dict(delta), "finish_reason": None}]), + _openai_chunk(choices=[{"index": 0, "delta": {"content": "!"}, "finish_reason": "stop"}]), ] - processor = ChunkProcessor(chunks=list(chunks)) - response = processor.build_base_response(list(chunks)) - - assert response.choices[0].message.role == "assistant" - - -def test_build_base_response_still_reads_role_and_finish_reason(): - """Regression guard: well-formed chunks keep their role and finish_reason.""" - chunks = [ - _empty_choices_chunk(), - { - "id": "chatcmpl-normal", - "object": "chat.completion.chunk", - "created": 1, - "model": "claude-opus-4-8", - "choices": [ - { - "index": 0, - "delta": {"role": "assistant", "content": "Hi"}, - "finish_reason": None, - } - ], - }, - { - "id": "chatcmpl-normal", - "object": "chat.completion.chunk", - "created": 2, - "model": "claude-opus-4-8", - "choices": [ - {"index": 0, "delta": {"content": "!"}, "finish_reason": "stop"} - ], - }, - ] - processor = ChunkProcessor(chunks=list(chunks)) - - response = processor.build_base_response(list(chunks)) + response: Final = stream_chunk_builder(chunks=chunks) + assert response is not None assert response.choices[0].message.role == "assistant" + assert response.choices[0].message.content == delta.get("content", "") + "!" assert response.choices[0].finish_reason == "stop" + + +def test_stream_chunk_builder_reads_role_from_first_frame_with_choices() -> None: + chunks: Final = [ + _openai_chunk(choices=[]), + _openai_chunk(choices=[{"index": 0, "delta": {"role": "user", "content": "Hi"}, "finish_reason": None}]), + _openai_chunk(choices=[{"index": 0, "delta": {}, "finish_reason": "stop"}]), + ] + + response: Final = stream_chunk_builder(chunks=chunks) + + assert response is not None + assert response.choices[0].message.role == "user" + assert response.choices[0].message.content == "Hi" From 9041768fb43715dc8c28e5dc139c86adac2659ce Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:47:51 -0700 Subject: [PATCH 059/223] feat(cost_map): derive source_revision from the loaded bytes instead of a _metadata stamp The revision an operator checks is now the git blob id of the exact bytes the process loaded, the same id git rev-parse :model_prices_and_context_window.json prints, so it is always present, never goes stale between bot writes, and needs no stamp in the JSON that every PR touching the file would have to regenerate. The _metadata block, the generated_at field, the schema and guard changes, and the bot stamping are dropped --- ...to_update_price_and_context_window_file.py | 27 +--- ci_cd/cost_map_guard.py | 7 +- ci_cd/generate_model_prices_schema.py | 19 +-- .../litellm_core_utils/get_model_cost_map.py | 98 ++++++------- ...odel_prices_and_context_window_backup.json | 4 - litellm/proxy/proxy_server.py | 2 +- model_prices_and_context_window.json | 4 - model_prices_and_context_window.schema.json | 20 +-- scripts/sync_together_ai_models.py | 23 +-- .../test_get_model_cost_map.py | 135 +++++++----------- .../test_routes_model_cost_map.py | 24 ++-- ...to_update_price_and_context_window_file.py | 54 ------- tests/test_litellm/test_cost_map_guard.py | 20 --- .../test_litellm/test_model_prices_schema.py | 19 --- .../test_sync_together_ai_models.py | 53 ------- tests/test_litellm/test_utils.py | 17 ++- .../src/components/price_data_reload.test.tsx | 15 +- .../src/components/price_data_reload.tsx | 8 -- ui/litellm-dashboard/src/lib/http/schema.d.ts | 2 +- 19 files changed, 131 insertions(+), 420 deletions(-) delete mode 100644 tests/test_litellm/test_auto_update_price_and_context_window_file.py diff --git a/.github/scripts/auto_update_price_and_context_window_file.py b/.github/scripts/auto_update_price_and_context_window_file.py index a7a3194f262..461d8d347d9 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -1,9 +1,6 @@ import asyncio import aiohttp import json -import os -import subprocess -from datetime import datetime, timezone # Asynchronously fetch data from a given URL async def fetch_data(url): @@ -34,28 +31,13 @@ def sync_local_data_with_remote(local_data, remote_data): for key in (set(remote_data) - set(local_data)): local_data[key] = remote_data[key] -def utc_now_iso(): - return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - -def source_revision(): - from_env = os.environ.get("GITHUB_SHA") - if from_env: - return from_env - return subprocess.run(["git", "rev-parse", "HEAD"], check=True, capture_output=True, text=True).stdout.strip() - - -def stamp_metadata(data, generated_at, revision): - return {**data, "_metadata": {"generated_at": generated_at, "source_revision": revision}} - - # Write data to the json file def write_to_file(file_path, data): try: # Open the file in write mode with open(file_path, "w") as file: # Dump the data as JSON into the file - file.write(json.dumps(data, indent=4) + "\n") + json.dump(data, file, indent=4) print("Values updated successfully.") except Exception as e: # Print an error message if writing to file fails @@ -167,13 +149,8 @@ def main(): # If both local and openrouter data are available, synchronize and save if local_data and all_remote_data: - before = json.dumps(local_data, sort_keys=True) sync_local_data_with_remote(local_data, all_remote_data) - changed = json.dumps(local_data, sort_keys=True) != before - write_to_file( - local_file_path, - stamp_metadata(local_data, utc_now_iso(), source_revision()) if changed else local_data, - ) + write_to_file(local_file_path, local_data) else: print("Failed to fetch model data from either local file or URL.") diff --git a/ci_cd/cost_map_guard.py b/ci_cd/cost_map_guard.py index 351c06c74eb..50aa40ba220 100644 --- a/ci_cd/cost_map_guard.py +++ b/ci_cd/cost_map_guard.py @@ -2,8 +2,7 @@ Every pull request gets the file checks: the three cost map files parse, the backup copy matches the root file, and the JSON schema is in sync and validates the map. Pull requests from the cost map sync bot (branches named -litellm_cost_map_sync_*) additionally may only touch those three files and may only add or update models, plus -restamp the _metadata provenance block. +litellm_cost_map_sync_*) additionally may only touch those three files and may only add or update models. """ from __future__ import annotations @@ -16,7 +15,7 @@ from collections.abc import Sequence from dataclasses import dataclass from typing import Final -from generate_model_prices_schema import BOT_LOCKED_ROOT_KEYS, build_schema, render, validation_errors +from generate_model_prices_schema import SPECIAL_ROOT_KEYS, build_schema, render, validation_errors COST_MAP_PATH: Final = "model_prices_and_context_window.json" BACKUP_PATH: Final = "litellm/model_prices_and_context_window_backup.json" @@ -103,7 +102,7 @@ def _bot_failures(base: Snapshot, head_map: CostMap, changed_files: Sequence[str *(f"bot PRs may not remove fields: {ref}" for ref in removed_fields), *( f"bot PRs may not change {key}" - for key in sorted(BOT_LOCKED_ROOT_KEYS) + for key in sorted(SPECIAL_ROOT_KEYS) if base_map.get(key) != head_map.get(key) ), ) diff --git a/ci_cd/generate_model_prices_schema.py b/ci_cd/generate_model_prices_schema.py index 557afa50128..ab29b70bdd4 100644 --- a/ci_cd/generate_model_prices_schema.py +++ b/ci_cd/generate_model_prices_schema.py @@ -11,9 +11,7 @@ REPO_ROOT = Path(__file__).parent.parent PRICES_PATH = REPO_ROOT / "model_prices_and_context_window.json" SCHEMA_PATH = REPO_ROOT / "model_prices_and_context_window.schema.json" -METADATA_KEY = "_metadata" -SPECIAL_ROOT_KEYS = frozenset({"sample_spec", "fallback_generalizations", METADATA_KEY}) -BOT_LOCKED_ROOT_KEYS = SPECIAL_ROOT_KEYS - {METADATA_KEY} +SPECIAL_ROOT_KEYS = frozenset({"sample_spec", "fallback_generalizations"}) JsonSchema = dict @@ -273,26 +271,13 @@ def build_schema(prices: dict) -> JsonSchema: "description": ( "Schema for LiteLLM's model price and context window registry " "(https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). " - "Every top-level key except '_metadata', 'sample_spec', and 'fallback_generalizations' is a model id, " + "Every top-level key except 'sample_spec' and 'fallback_generalizations' is a model id, " "optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. " "All costs are USD per unit. New optional fields are added regularly, so consumers should " "ignore unknown fields rather than reject them." ), "type": "object", "properties": { - METADATA_KEY: { - "type": "object", - "description": ( - "Provenance of this file: when an automated sync last regenerated it and the commit it " - "ran against. Human edits leave it untouched; not a model entry." - ), - "properties": { - "generated_at": {"type": "string", "format": "date-time"}, - "source_revision": STRING, - }, - "required": ["generated_at", "source_revision"], - "additionalProperties": False, - }, "sample_spec": { "type": "object", "description": ( diff --git a/litellm/litellm_core_utils/get_model_cost_map.py b/litellm/litellm_core_utils/get_model_cost_map.py index a538e7cb330..2bdfbc66088 100644 --- a/litellm/litellm_core_utils/get_model_cost_map.py +++ b/litellm/litellm_core_utils/get_model_cost_map.py @@ -9,18 +9,18 @@ export LITELLM_LOCAL_MODEL_COST_MAP=True """ import asyncio +import hashlib import json import os import random import time from collections.abc import Awaitable, Callable -from dataclasses import dataclass +from dataclasses import dataclass, replace from datetime import datetime, timezone from importlib.resources import files from typing import Final, Protocol import httpx -from pydantic import BaseModel, ConfigDict, ValidationError from typing_extensions import ReadOnly, TypedDict from litellm import verbose_logger @@ -33,11 +33,10 @@ from litellm.litellm_core_utils.fallback_generalizations import ( ) FALLBACK_GENERALIZATIONS_KEY: Final = "fallback_generalizations" -METADATA_KEY: Final = "_metadata" # Reserved top-level keys that are not model entries. They must be excluded # from the model-count integrity check so a real upstream shrink can't be masked. -RESERVED_TOP_LEVEL_KEYS: Final = frozenset({"sample_spec", FALLBACK_GENERALIZATIONS_KEY, METADATA_KEY}) +RESERVED_TOP_LEVEL_KEYS: Final = frozenset({"sample_spec", FALLBACK_GENERALIZATIONS_KEY}) def _count_model_entries(model_cost: dict) -> int: @@ -45,6 +44,11 @@ def _count_model_entries(model_cost: dict) -> int: return sum(1 for key in model_cost if key not in RESERVED_TOP_LEVEL_KEYS) +def git_blob_id(body: bytes) -> str: + """The sha1 git gives these bytes as a blob, so ``git rev-parse :`` reproduces it for the file""" + return hashlib.sha1(b"blob %d\0" % len(body) + body, usedforsecurity=False).hexdigest() + + class GetModelCostMap: """ Handles fetching, validating, and loading the model cost map. @@ -56,15 +60,25 @@ class GetModelCostMap: _backup_model_count: int = -1 # -1 = not yet loaded + @staticmethod + def read_local_model_cost_map_bytes() -> bytes: + return files("litellm").joinpath("model_prices_and_context_window_backup.json").read_bytes() + @staticmethod def read_local_model_cost_map_text() -> str: - return files("litellm").joinpath("model_prices_and_context_window_backup.json").read_text(encoding="utf-8") + return GetModelCostMap.read_local_model_cost_map_bytes().decode("utf-8") + + @staticmethod + def load_local_model_cost_map_with_revision() -> "ModelCostMapReloaded": + """The bundled backup map together with the git blob id of the file it was parsed from""" + body: Final = GetModelCostMap.read_local_model_cost_map_bytes() + content: Final = json.loads(body) + return ModelCostMapReloaded(model_cost_map=content, revision=git_blob_id(body)) @staticmethod def load_local_model_cost_map() -> dict: """Load the local backup model cost map bundled with the package.""" - content: Final = json.loads(GetModelCostMap.read_local_model_cost_map_text()) - return content + return GetModelCostMap.load_local_model_cost_map_with_revision().model_cost_map @classmethod def _get_backup_model_count(cls) -> int: @@ -169,6 +183,7 @@ MODEL_COST_MAP_FETCH_MAX_WAIT_SECONDS: Final = 30.0 @dataclass(frozen=True, slots=True) class ModelCostMapReloaded: model_cost_map: dict # mutable-ok: adopted as litellm.model_cost, whose consumer contract is a plain mutable dict + revision: str | None = None etag: str | None = None @@ -258,7 +273,9 @@ def _classify_fetch_response(response: httpx.Response, url: str) -> _FetchAttemp return ModelCostMapReloadUnavailable(reason=f"invalid JSON from {url}: {e}") if not isinstance(parsed, dict): return ModelCostMapReloadUnavailable(reason=f"expected a JSON object from {url}, got {type(parsed).__name__}") - return ModelCostMapReloaded(model_cost_map=parsed, etag=response.headers.get("etag")) + return ModelCostMapReloaded( + model_cost_map=parsed, revision=git_blob_id(response.content), etag=response.headers.get("etag") + ) def _next_retry_wait( @@ -337,10 +354,7 @@ async def refetch_model_cost_map( _cost_map_source_info.url = None _cost_map_source_info.is_env_forced = True _cost_map_source_info.fallback_reason = None - _cost_map_source_info.etag = None - return ModelCostMapReloaded( - model_cost_map=_finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) - ) + return _finalize_loaded_model_cost_map(GetModelCostMap.load_local_model_cost_map_with_revision()) result: Final = await _fetch_remote_model_cost_map_with_retry( url=url, @@ -366,8 +380,7 @@ async def refetch_model_cost_map( _cost_map_source_info.url = url _cost_map_source_info.is_env_forced = False _cost_map_source_info.fallback_reason = None - _cost_map_source_info.etag = result.etag - return ModelCostMapReloaded(model_cost_map=_finalize_model_cost_map(result.model_cost_map), etag=result.etag) + return _finalize_loaded_model_cost_map(result) class ModelCostMapSourceInfo: @@ -378,7 +391,6 @@ class ModelCostMapSourceInfo: is_env_forced: bool = False fallback_reason: str | None = None loaded_at: "datetime | None" = None - generated_at: str | None = None source_revision: str | None = None etag: str | None = None @@ -387,28 +399,7 @@ class ModelCostMapSourceInfo: _cost_map_source_info: Final = ModelCostMapSourceInfo() -class CostMapMetadata(BaseModel): - model_config = ConfigDict(frozen=True, extra="ignore") - - generated_at: str | None = None - source_revision: str | None = None - - -_EMPTY_METADATA: Final = CostMapMetadata() - - -def _parse_metadata(raw: object) -> CostMapMetadata: - if raw is None: - return _EMPTY_METADATA - try: - return CostMapMetadata.model_validate(raw) - except ValidationError as error: - verbose_logger.warning("LiteLLM: ignoring a malformed %s block in the model cost map: %s", METADATA_KEY, error) - return _EMPTY_METADATA - - class CostMapProvenance(TypedDict): - generated_at: ReadOnly[str | None] source_revision: ReadOnly[str | None] etag: ReadOnly[str | None] @@ -422,10 +413,10 @@ class CostMapSourceInfo(CostMapProvenance): def get_model_cost_map_provenance() -> CostMapProvenance: - """Which revision of the cost map this process serves: the ``_metadata`` stamp the file - carries plus the ETag the remote fetch returned (None for the bundled backup)""" + """Which revision of the cost map this process serves: the git blob id of the bytes it loaded, the + same id ``git rev-parse :model_prices_and_context_window.json`` prints for a checkout, plus + the ETag the remote fetch returned (None for the bundled backup)""" return { - "generated_at": _cost_map_source_info.generated_at, "source_revision": _cost_map_source_info.source_revision, "etag": _cost_map_source_info.etag, } @@ -441,7 +432,7 @@ def get_model_cost_map_source_info() -> CostMapSourceInfo: - is_env_forced: True if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage - fallback_reason: human-readable reason if remote failed and local was used - loaded_at: ISO 8601 time this process last loaded the map - - generated_at, source_revision: the ``_metadata`` stamp inside the loaded file + - source_revision: git blob id of the loaded file's bytes - etag: the ETag of the remote fetch (None for the bundled backup) """ loaded_at: Final = _cost_map_source_info.loaded_at @@ -451,7 +442,6 @@ def get_model_cost_map_source_info() -> CostMapSourceInfo: "is_env_forced": _cost_map_source_info.is_env_forced, "fallback_reason": _cost_map_source_info.fallback_reason, "loaded_at": loaded_at.isoformat() if loaded_at is not None else None, - "generated_at": _cost_map_source_info.generated_at, "source_revision": _cost_map_source_info.source_revision, "etag": _cost_map_source_info.etag, } @@ -518,21 +508,24 @@ def _expand_model_aliases(model_cost: dict) -> dict: def _finalize_model_cost_map(model_cost: dict) -> dict: - """Extract fallback generalizations and the provenance stamp out of the raw map, then expand aliases. + """Extract fallback generalizations out of the raw map, then expand aliases. The ``fallback_generalizations`` block is installed into the generalizations - module and the ``_metadata`` block into the source info; both are removed from - the map so neither is ever treated as a model entry. + module and removed from the map so it is never treated as a model entry. """ raw: Final = model_cost.pop(FALLBACK_GENERALIZATIONS_KEY, None) rules: Final = raw.get("rules") if isinstance(raw, dict) else None set_fallback_generalizations(rules) - metadata: Final = _parse_metadata(model_cost.pop(METADATA_KEY, None)) - _cost_map_source_info.generated_at = metadata.generated_at - _cost_map_source_info.source_revision = metadata.source_revision return _expand_model_aliases(model_cost) +def _finalize_loaded_model_cost_map(loaded: ModelCostMapReloaded) -> ModelCostMapReloaded: + """Record which bytes this process now serves, then finalize the map they parsed into""" + _cost_map_source_info.source_revision = loaded.revision + _cost_map_source_info.etag = loaded.etag + return replace(loaded, model_cost_map=_finalize_model_cost_map(loaded.model_cost_map)) + + def get_model_cost_map( url: str, timeout: int = 5, @@ -561,12 +554,10 @@ def get_model_cost_map( _cost_map_source_info.url = None _cost_map_source_info.is_env_forced = True _cost_map_source_info.fallback_reason = None - _cost_map_source_info.etag = None - return _finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) + return _finalize_loaded_model_cost_map(GetModelCostMap.load_local_model_cost_map_with_revision()).model_cost_map _cost_map_source_info.url = url _cost_map_source_info.is_env_forced = False - _cost_map_source_info.etag = None result: Final = _fetch_remote_model_cost_map_with_retry_sync( url=url, @@ -584,7 +575,7 @@ def get_model_cost_map( ) _cost_map_source_info.source = "local" _cost_map_source_info.fallback_reason = f"Remote fetch failed: {result.reason}" - return _finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) + return _finalize_loaded_model_cost_map(GetModelCostMap.load_local_model_cost_map_with_revision()).model_cost_map content: Final = result.model_cost_map # Validate using cached count (cheap int comparison, no file I/O) @@ -598,9 +589,8 @@ def get_model_cost_map( ) _cost_map_source_info.source = "local" _cost_map_source_info.fallback_reason = "Remote data failed integrity validation" - return _finalize_model_cost_map(GetModelCostMap.load_local_model_cost_map()) + return _finalize_loaded_model_cost_map(GetModelCostMap.load_local_model_cost_map_with_revision()).model_cost_map _cost_map_source_info.source = "remote" _cost_map_source_info.fallback_reason = None - _cost_map_source_info.etag = result.etag - return _finalize_model_cost_map(content) + return _finalize_loaded_model_cost_map(result).model_cost_map diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 5edb3c0e9d8..b1ffc1583e4 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -1,8 +1,4 @@ { - "_metadata": { - "generated_at": "2026-09-07T23:38:47Z", - "source_revision": "cd681a573fd9f5b6f15a1355f46178e4e9d374d2" - }, "sample_spec": { "code_interpreter_cost_per_session": 0.0, "computer_use_input_cost_per_1k_tokens": 0.0, diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 4741e4cd9d3..818a1506754 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -17938,7 +17938,7 @@ async def get_model_cost_map_source( - is_env_forced: true if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage - fallback_reason: human-readable reason why remote failed (null on success) - loaded_at: when this pod last loaded the map - - generated_at, source_revision: the _metadata stamp inside the loaded file + - source_revision: git blob id of the loaded file, what git rev-parse : prints for it - etag: the ETag of the remote fetch (null for the bundled backup) - model_count: number of models in the currently loaded cost map """ diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 5edb3c0e9d8..b1ffc1583e4 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -1,8 +1,4 @@ { - "_metadata": { - "generated_at": "2026-09-07T23:38:47Z", - "source_revision": "cd681a573fd9f5b6f15a1355f46178e4e9d374d2" - }, "sample_spec": { "code_interpreter_cost_per_session": 0.0, "computer_use_input_cost_per_1k_tokens": 0.0, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index c40c2a67682..47a1934a703 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -1,27 +1,9 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "LiteLLM model_prices_and_context_window.json", - "description": "Schema for LiteLLM's model price and context window registry (https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). Every top-level key except '_metadata', 'sample_spec', and 'fallback_generalizations' is a model id, optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. All costs are USD per unit. New optional fields are added regularly, so consumers should ignore unknown fields rather than reject them.", + "description": "Schema for LiteLLM's model price and context window registry (https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json). Every top-level key except 'sample_spec' and 'fallback_generalizations' is a model id, optionally prefixed with its provider (e.g. 'azure/gpt-5.4'), mapping to a model entry. All costs are USD per unit. New optional fields are added regularly, so consumers should ignore unknown fields rather than reject them.", "type": "object", "properties": { - "_metadata": { - "type": "object", - "description": "Provenance of this file: when an automated sync last regenerated it and the commit it ran against. Human edits leave it untouched; not a model entry.", - "properties": { - "generated_at": { - "type": "string", - "format": "date-time" - }, - "source_revision": { - "type": "string" - } - }, - "required": [ - "generated_at", - "source_revision" - ], - "additionalProperties": false - }, "sample_spec": { "type": "object", "description": "Documentation placeholder illustrating the entry shape; not a real model and not schema-conformant (several values are prose)." diff --git a/scripts/sync_together_ai_models.py b/scripts/sync_together_ai_models.py index e009f1a7ce6..12b128890f1 100644 --- a/scripts/sync_together_ai_models.py +++ b/scripts/sync_together_ai_models.py @@ -19,11 +19,9 @@ import argparse import json import os import re -import subprocess import sys from collections.abc import Mapping, Sequence from dataclasses import dataclass, field -from datetime import datetime, timezone from pathlib import Path from types import MappingProxyType from typing import Final @@ -35,7 +33,6 @@ MODELS_URL: Final = "https://api.together.ai/v1/models?serverless" DEPRECATIONS_URL: Final = "https://docs.together.ai/docs/deprecations.md" PROVIDER: Final = "together_ai" PREFIX: Final = "together_ai/" -METADATA_KEY: Final = "_metadata" SOURCE_URL: Final = "https://docs.together.ai/docs/serverless-models" COST_MAP_RELPATHS: Final = ( "model_prices_and_context_window.json", @@ -498,23 +495,6 @@ def _serialize(cost_map: CostMap) -> str: return json.dumps(cost_map, indent=4, ensure_ascii=False) + "\n" -def stamp_metadata(cost_map: CostMap, generated_at: str, source_revision: str) -> CostMap: - return {**cost_map, METADATA_KEY: {"generated_at": generated_at, "source_revision": source_revision}} - - -def _utc_now_iso() -> str: - return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") - - -def _source_revision(repo_root: Path) -> str: - from_env: Final = os.environ.get("GITHUB_SHA") - if from_env: - return from_env - return subprocess.run( - ("git", "rev-parse", "HEAD"), cwd=repo_root, check=True, capture_output=True, text=True - ).stdout.strip() - - def main(argv: Sequence[str]) -> int: parser: Final = argparse.ArgumentParser(description=__doc__) parser.add_argument("--write", action="store_true", help="apply the sync to the cost map files (default: dry run)") @@ -547,9 +527,8 @@ def main(argv: Sequence[str]) -> int: if args.pr_body_file is not None: args.pr_body_file.write_text(body) if args.write and outcome.has_changes: - stamped: Final = _serialize(stamp_metadata(outcome.cost_map, _utc_now_iso(), _source_revision(args.repo_root))) for relpath in COST_MAP_RELPATHS: - (args.repo_root / relpath).write_text(stamped) + (args.repo_root / relpath).write_text(_serialize(outcome.cost_map)) print(render_summary(outcome)) print() print(body) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index 62f72495491..d9fe6d2f979 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -17,11 +17,11 @@ from litellm.litellm_core_utils.fallback_generalizations import ( ) from litellm.litellm_core_utils.get_model_cost_map import ( FALLBACK_GENERALIZATIONS_KEY, - METADATA_KEY, GetModelCostMap, _count_model_entries, _finalize_model_cost_map, get_model_cost_map_provenance, + git_blob_id, ) @@ -33,18 +33,16 @@ def _load_root_cost_map() -> dict: return json.load(f) -def _load_bundled_stamp() -> dict: - path = os.path.join( - os.path.dirname(__file__), "../../../litellm/model_prices_and_context_window_backup.json" - ) - with open(path) as f: - return json.load(f)[METADATA_KEY] +def _bundled_blob_id() -> str: + path = os.path.join(os.path.dirname(__file__), "../../../litellm/model_prices_and_context_window_backup.json") + with open(path, "rb") as f: + return git_blob_id(f.read()) -_STAMP = { - "generated_at": "2026-09-07T00:00:00Z", - "source_revision": "0123456789abcdef0123456789abcdef01234567", -} +def test_git_blob_id_is_what_git_hash_object_prints(): + """An operator checks a reported revision with ``git hash-object`` or ``git rev-parse :``, + so the id must be git's blob sha1 of the exact bytes, not a plain sha1 or a hash of the parsed JSON.""" + assert git_blob_id(b'{"gpt-5.4-mini": {"mode": "chat"}}\n') == "18b9a8381e13a3b38a2128f184f631f95829e987" def _make_models(n: int) -> dict: @@ -57,7 +55,6 @@ def test_count_model_entries_excludes_reserved_keys(): m = _make_models(3) m["sample_spec"] = {"foo": "bar"} m[FALLBACK_GENERALIZATIONS_KEY] = {"rules": []} - m[METADATA_KEY] = dict(_STAMP) assert _count_model_entries(m) == 3 @@ -143,39 +140,6 @@ def test_finalize_with_no_block_clears_rules(): set_fallback_generalizations(previous) -def test_finalize_pops_metadata_and_records_provenance(): - finalized = _finalize_model_cost_map({**_make_models(2), METADATA_KEY: dict(_STAMP)}) - - assert METADATA_KEY not in finalized - assert len(finalized) == 2 - provenance = get_model_cost_map_provenance() - assert provenance["generated_at"] == _STAMP["generated_at"] - assert provenance["source_revision"] == _STAMP["source_revision"] - - -def test_finalize_without_metadata_clears_the_previous_stamp(): - _finalize_model_cost_map({**_make_models(2), METADATA_KEY: dict(_STAMP)}) - - _finalize_model_cost_map(_make_models(2)) - - provenance = get_model_cost_map_provenance() - assert provenance["generated_at"] is None - assert provenance["source_revision"] is None - - -@pytest.mark.parametrize( - "raw", - ["2026-09-07T00:00:00Z", {"generated_at": 42}, ["2026-09-07T00:00:00Z"]], - ids=["string", "wrong_field_type", "list"], -) -def test_finalize_tolerates_a_malformed_metadata_block(raw): - finalized = _finalize_model_cost_map({**_make_models(2), METADATA_KEY: raw}) - - assert METADATA_KEY not in finalized - assert len(finalized) == 2 - assert get_model_cost_map_provenance()["generated_at"] is None - - def test_shipped_backup_carries_the_claude_routing_rules(): """The bundled backup must ship the Claude routing rules so a fresh install (or an offline fallback) routes unknown Claude models without code changes. @@ -390,10 +354,6 @@ def _real_map_bytes() -> bytes: return json.dumps(_load_root_cost_map()).encode() -def _stamped_map_bytes(stamp: dict) -> bytes: - return json.dumps({**_load_root_cost_map(), METADATA_KEY: stamp}).encode() - - class _SleepRecorder: """Injected in place of asyncio.sleep so tests assert waits without real delay.""" @@ -555,40 +515,51 @@ async def test_refetch_respects_local_env_override(monkeypatch): @pytest.mark.asyncio -async def test_refetch_records_the_file_stamp_and_the_fetch_etag(): - """A reload reports which revision of the map it swapped in: the ``_metadata`` stamp the file - carries plus the ETag the fetch returned, with the stamp itself kept out of the model map.""" - client, _ = _mock_client( - [httpx.Response(200, headers={"ETag": 'W/"abc123"'}, content=_stamped_map_bytes(_STAMP))] - ) +async def test_refetch_records_the_blob_id_of_the_bytes_served_and_the_fetch_etag(): + """A reload reports which revision of the map it swapped in: the git blob id of the exact bytes the + fetch returned, so ``git rev-parse :model_prices_and_context_window.json`` can confirm it, + plus the ETag the fetch returned.""" + body = _real_map_bytes() + client, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"abc123"'}, content=body)]) result = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) assert isinstance(result, ModelCostMapReloaded) + assert result.revision == git_blob_id(body) assert result.etag == 'W/"abc123"' - assert METADATA_KEY not in result.model_cost_map - assert get_model_cost_map_provenance() == { - "generated_at": _STAMP["generated_at"], - "source_revision": _STAMP["source_revision"], - "etag": 'W/"abc123"', - } + assert get_model_cost_map_provenance() == {"source_revision": git_blob_id(body), "etag": 'W/"abc123"'} @pytest.mark.asyncio -async def test_refetch_local_override_reports_the_bundled_stamp_without_an_etag(monkeypatch): - """Forcing the bundled backup after a remote reload must drop the remote ETag, since the map - served is no longer the one that ETag identifies.""" - remote, _ = _mock_client( - [httpx.Response(200, headers={"ETag": 'W/"remote"'}, content=_stamped_map_bytes(_STAMP))] +async def test_refetch_revision_follows_the_bytes_not_the_url(): + """Two fetches of the same URL that return different bytes report different revisions.""" + edited = json.loads(_real_map_bytes()) + edited["gpt-5.4-mini"]["input_cost_per_token"] = 0.5 + client, _ = _mock_client( + [httpx.Response(200, content=_real_map_bytes()), httpx.Response(200, content=json.dumps(edited).encode())] ) + + first = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) + second = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) + + assert isinstance(first, ModelCostMapReloaded) and isinstance(second, ModelCostMapReloaded) + assert first.revision != second.revision + assert get_model_cost_map_provenance()["source_revision"] == second.revision + + +@pytest.mark.asyncio +async def test_refetch_local_override_reports_the_bundled_blob_id_without_an_etag(monkeypatch): + """Forcing the bundled backup after a remote reload must report the backup's own blob id and drop the + remote ETag, since the map served is no longer the one that ETag identifies.""" + remote, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"remote"'}, content=_real_map_bytes())]) await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=remote) monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") result = await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0)) assert isinstance(result, ModelCostMapReloaded) - assert METADATA_KEY not in result.model_cost_map - assert get_model_cost_map_provenance() == {**_load_bundled_stamp(), "etag": None} + assert result.revision == _bundled_blob_id() + assert get_model_cost_map_provenance() == {"source_revision": _bundled_blob_id(), "etag": None} # --------------------------------------------------------------------------- @@ -633,7 +604,7 @@ def test_boot_load_retries_transient_failures_instead_of_falling_back(): source = get_model_cost_map_source_info() assert source["source"] == "remote" assert source["fallback_reason"] is None - assert cost_map.keys() >= _load_root_cost_map().keys() - {"sample_spec", FALLBACK_GENERALIZATIONS_KEY, METADATA_KEY} + assert cost_map.keys() >= _load_root_cost_map().keys() - {"sample_spec", FALLBACK_GENERALIZATIONS_KEY} def test_boot_load_honors_retry_after_then_falls_back_after_max_attempts(): @@ -685,37 +656,31 @@ def test_boot_load_respects_local_env_override(monkeypatch): assert get_model_cost_map_source_info()["is_env_forced"] is True -def test_boot_load_records_the_file_stamp_and_the_fetch_etag(): - client, _ = _mock_client( - [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_stamped_map_bytes(_STAMP))], - client_cls=httpx.Client, - ) +def test_boot_load_records_the_blob_id_of_the_bytes_served_and_the_fetch_etag(): + body = _real_map_bytes() + client, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=body)], client_cls=httpx.Client) - cost_map = get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=client) + get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=client) - assert METADATA_KEY not in cost_map source = get_model_cost_map_source_info() assert source["source"] == "remote" assert source["etag"] == 'W/"boot"' - assert source["generated_at"] == _STAMP["generated_at"] - assert source["source_revision"] == _STAMP["source_revision"] + assert source["source_revision"] == git_blob_id(body) assert source["loaded_at"] is not None -def test_boot_load_fallback_to_the_backup_drops_the_remote_etag(): - """A boot that lands on the bundled backup reports the backup's own stamp and no ETag, even +def test_boot_load_fallback_to_the_backup_reports_its_blob_id_and_drops_the_remote_etag(): + """A boot that lands on the bundled backup reports the backup's own blob id and no ETag, even when an earlier load in the same process had fetched the remote map.""" remote, _ = _mock_client( - [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_stamped_map_bytes(_STAMP))], - client_cls=httpx.Client, + [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_real_map_bytes())], client_cls=httpx.Client ) get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=remote) failing, _ = _mock_client([httpx.Response(404)], client_cls=httpx.Client) - cost_map = get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=failing) + get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=failing) - assert METADATA_KEY not in cost_map source = get_model_cost_map_source_info() assert source["source"] == "local" assert source["etag"] is None - assert {"generated_at": source["generated_at"], "source_revision": source["source_revision"]} == _load_bundled_stamp() + assert source["source_revision"] == _bundled_blob_id() diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py index fb3583a7dd2..0490993a314 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py @@ -22,7 +22,6 @@ from .conftest import VOLATILE_KEYS, normalize _VOLATILE = VOLATILE_KEYS | frozenset({"timestamp"}) _PROVENANCE = { - "generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef0123456789abcdef01234567", "etag": 'W/"cost-map-etag"', } @@ -108,22 +107,24 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): assert update_payload["reload_revision"] == {"increment": 1} -def test_reload_model_cost_map_surfaces_provenance_and_keeps_metadata_out_of_the_model_list( +def test_reload_model_cost_map_surfaces_the_blob_id_of_the_bytes_served_on_every_status_surface( client, auth_as, monkeypatch, mock_prisma ): - """A real refetch through the reload route reports the file's stamp and the fetch ETag on every - status surface, while the ``_metadata`` block never shows up as a model anywhere.""" + """A real refetch through the reload route reports the git blob id of the exact bytes it fetched and + the fetch ETag on the reload response, the source route, and the schedule status alike.""" import httpx import litellm + from litellm.litellm_core_utils.get_model_cost_map import git_blob_id from litellm.proxy import proxy_server as ps from litellm.proxy._types import LitellmUserRoles _attach_litellm_config(mock_prisma) monkeypatch.setattr(ps, "prisma_client", mock_prisma) monkeypatch.delenv("LITELLM_LOCAL_MODEL_COST_MAP", raising=False) - stamped = {**json.loads(_ROOT_COST_MAP.read_text()), "_metadata": {k: v for k, v in _PROVENANCE.items() if k != "etag"}} - served = httpx.Response(200, headers={"ETag": _PROVENANCE["etag"]}, content=json.dumps(stamped).encode()) + body = _ROOT_COST_MAP.read_bytes() + expected = {"source_revision": git_blob_id(body), "etag": _PROVENANCE["etag"]} + served = httpx.Response(200, headers={"ETag": _PROVENANCE["etag"]}, content=body) monkeypatch.setattr( "litellm.litellm_core_utils.get_model_cost_map._default_reload_client", lambda: httpx.AsyncClient(transport=httpx.MockTransport(lambda request: served)), @@ -144,18 +145,15 @@ def test_reload_model_cost_map_surfaces_provenance_and_keeps_metadata_out_of_the assert reload_response.status_code == 200 reload_body = reload_response.json() - assert {key: reload_body[key] for key in _PROVENANCE} == _PROVENANCE + assert {key: reload_body[key] for key in expected} == expected assert source_response.status_code == 200 source_body = source_response.json() - assert {key: source_body[key] for key in _PROVENANCE} == _PROVENANCE + assert {key: source_body[key] for key in expected} == expected assert source_body["source"] == "remote" assert status_response.status_code == 200 - assert {key: status_response.json()[key] for key in _PROVENANCE} == _PROVENANCE + assert {key: status_response.json()[key] for key in expected} == expected assert public_response.status_code == 200 - public_body = public_response.json() - assert "_metadata" not in public_body - assert "_metadata" not in litellm.model_cost - assert "gpt-4o" in public_body + assert "gpt-4o" in public_response.json() assert reload_body["models_count"] == len(litellm.model_cost) diff --git a/tests/test_litellm/test_auto_update_price_and_context_window_file.py b/tests/test_litellm/test_auto_update_price_and_context_window_file.py deleted file mode 100644 index d3cda09cd96..00000000000 --- a/tests/test_litellm/test_auto_update_price_and_context_window_file.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Tests for .github/scripts/auto_update_price_and_context_window_file.py.""" - -import importlib.util -import json -import re -import sys -from pathlib import Path -from typing import Final - -_REPO_ROOT: Final = Path(__file__).resolve().parents[2] -_MODULE_PATH: Final = _REPO_ROOT / ".github" / "scripts" / "auto_update_price_and_context_window_file.py" -_spec: Final = importlib.util.spec_from_file_location("auto_update_price_and_context_window_file", _MODULE_PATH) -script: Final = importlib.util.module_from_spec(_spec) -sys.modules[_spec.name] = script -_spec.loader.exec_module(script) - -_LOCAL_FILE: Final = "model_prices_and_context_window.json" -_GENERATED_AT: Final = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z") - - -def _openrouter_row(model_id: str) -> dict: - return {"id": model_id, "context_length": 8192, "pricing": {"prompt": "0.000001", "completion": "0.000002"}} - - -def _serve(openrouter_rows: list) -> object: - async def fetch_data(url: str) -> list: - return openrouter_rows if "openrouter" in url else [] - - return fetch_data - - -def _read_local(tmp_path: Path) -> dict: - return json.loads((tmp_path / _LOCAL_FILE).read_text()) - - -def test_main_stamps_provenance_only_when_the_sync_changed_the_file(tmp_path: Path, monkeypatch) -> None: - monkeypatch.chdir(tmp_path) - monkeypatch.setenv("GITHUB_SHA", "feedface") - monkeypatch.setattr(script, "fetch_data", _serve([_openrouter_row("acme/x")])) - (tmp_path / _LOCAL_FILE).write_text(json.dumps({"sample_spec": {"input_cost_per_token": "USD"}}, indent=4) + "\n") - - script.main() - - written = _read_local(tmp_path) - assert written["openrouter/acme/x"]["litellm_provider"] == "openrouter" - assert written["_metadata"]["source_revision"] == "feedface" - assert _GENERATED_AT.fullmatch(written["_metadata"]["generated_at"]) - - sentinel = {**written, "_metadata": {**written["_metadata"], "generated_at": "2000-01-01T00:00:00Z"}} - (tmp_path / _LOCAL_FILE).write_text(json.dumps(sentinel, indent=4) + "\n") - - script.main() - - assert _read_local(tmp_path) == sentinel diff --git a/tests/test_litellm/test_cost_map_guard.py b/tests/test_litellm/test_cost_map_guard.py index 1a60cf81164..1b4330ed62c 100644 --- a/tests/test_litellm/test_cost_map_guard.py +++ b/tests/test_litellm/test_cost_map_guard.py @@ -141,26 +141,6 @@ def test_bot_may_not_change_special_root_keys() -> None: assert _failures(head) == ("bot PRs may not change fallback_generalizations",) -STAMP: Final = {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef0123456789abcdef01234567"} - - -def test_bot_may_stamp_and_restamp_metadata() -> None: - stamped = _snapshot({**BASE_MAP, "_metadata": STAMP}) - assert _failures(stamped) == () - assert _failures(stamped, bot=False) == () - - restamped = _snapshot( - { - **BASE_MAP, - "_metadata": {**STAMP, "generated_at": "2026-09-14T00:00:00Z"}, - "fallback_generalizations": {"rules": []}, - } - ) - assert guard.guard_failures(stamped, restamped, MAP_FILES, True) == ( - "bot PRs may not change fallback_generalizations", - ) - - def _commit(repo: Path, cost_map: dict[str, object], message: str) -> str: text = _serialize(cost_map) (repo / guard.COST_MAP_PATH).write_text(text) diff --git a/tests/test_litellm/test_model_prices_schema.py b/tests/test_litellm/test_model_prices_schema.py index 3517f5840e8..c2c22c25998 100644 --- a/tests/test_litellm/test_model_prices_schema.py +++ b/tests/test_litellm/test_model_prices_schema.py @@ -98,25 +98,6 @@ def test_schema_rejects_malformed_entries(committed_schema: dict, entry: dict): assert not validator.is_valid({"some-model": entry}) -@pytest.mark.parametrize( - "metadata", - [ - "2026-09-07T00:00:00Z", - {"generated_at": "2026-09-07T00:00:00Z"}, - {"source_revision": "0123456789abcdef"}, - {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef", "author": "bot"}, - ], - ids=["not_an_object", "missing_revision", "missing_generated_at", "unknown_field"], -) -def test_schema_rejects_a_malformed_metadata_block(committed_schema: dict, metadata: object): - assert not build_validator(committed_schema).is_valid({"_metadata": metadata}) - - -def test_schema_accepts_the_provenance_stamp_as_a_non_model_root_key(committed_schema: dict): - stamp = {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "0123456789abcdef0123456789abcdef01234567"} - assert build_validator(committed_schema).is_valid({"_metadata": stamp}) - - def test_schema_accepts_minimal_and_unknown_optional_fields(committed_schema: dict): validator = build_validator(committed_schema) assert validator.is_valid({"some-model": {"litellm_provider": "openai"}}) diff --git a/tests/test_litellm/test_sync_together_ai_models.py b/tests/test_litellm/test_sync_together_ai_models.py index f58f573c208..b8a85bcfbdc 100644 --- a/tests/test_litellm/test_sync_together_ai_models.py +++ b/tests/test_litellm/test_sync_together_ai_models.py @@ -1,6 +1,5 @@ import importlib.util import json -import re from pathlib import Path from types import MappingProxyType @@ -370,58 +369,6 @@ def test_sync_is_idempotent_over_the_repo_cost_map() -> None: assert second.cost_map == first.cost_map -def test_stamp_metadata_adds_the_provenance_block_without_touching_models() -> None: - cost_map = {"sample_spec": {"input_cost_per_token": "USD"}, "together_ai/acme/x": {"mode": "chat"}} - - stamped = sync.stamp_metadata(cost_map, "2026-09-07T00:00:00Z", "feedface") - - assert stamped["_metadata"] == {"generated_at": "2026-09-07T00:00:00Z", "source_revision": "feedface"} - assert {key: value for key, value in stamped.items() if key != "_metadata"} == cost_map - assert "_metadata" not in cost_map - - -def _write_registry(repo_root: Path, cost_map: dict) -> None: - for relpath in sync.COST_MAP_RELPATHS: - target = repo_root / relpath - target.parent.mkdir(parents=True, exist_ok=True) - target.write_text(json.dumps(cost_map, indent=4) + "\n") - - -def _read_registries(repo_root: Path) -> tuple[dict, ...]: - return tuple(json.loads((repo_root / relpath).read_text()) for relpath in sync.COST_MAP_RELPATHS) - - -def test_write_stamps_provenance_into_both_files_only_when_the_sync_changed_them(tmp_path: Path, monkeypatch) -> None: - monkeypatch.setenv("GITHUB_SHA", "feedface") - cost_map = json.loads((ROOT / "model_prices_and_context_window.json").read_text()) - dropped = next(f"together_ai/{model.id}" for model in RECORDED_CATALOG if f"together_ai/{model.id}" in cost_map) - _write_registry(tmp_path, {key: value for key, value in cost_map.items() if key not in {dropped, "_metadata"}}) - argv = ( - "--write", - "--models-json", - str(FIXTURES / "models_serverless.json"), - "--deprecations-md", - str(FIXTURES / "deprecations.md"), - "--repo-root", - str(tmp_path), - ) - - assert sync.main(argv) == 0 - - written = _read_registries(tmp_path) - assert written[0] == written[1] - assert dropped in written[0] - assert written[0]["_metadata"]["source_revision"] == "feedface" - assert re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", written[0]["_metadata"]["generated_at"]) - - sentinel = {**written[0], "_metadata": {**written[0]["_metadata"], "generated_at": "2000-01-01T00:00:00Z"}} - _write_registry(tmp_path, sentinel) - - assert sync.main(argv) == 0 - - assert _read_registries(tmp_path) == (sentinel, sentinel) - - def test_pr_body_lists_every_section_and_the_skipped_types() -> None: outcome = sync.compute_sync({}, RECORDED_CATALOG, RECORDED_DOC) body = sync.render_pr_body(outcome) diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index f6c9a4537a1..8a56a84ade7 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -21,7 +21,6 @@ from litellm._logging import ( verbose_logger, ) from litellm.integrations.custom_logger import CustomLogger -from litellm.litellm_core_utils.get_model_cost_map import RESERVED_TOP_LEVEL_KEYS from litellm.proxy.utils import is_valid_api_key from litellm.types.utils import ( CallTypes, @@ -1219,12 +1218,15 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): with open(prod_json, "r") as model_prices_file: actual_json = json.load(model_prices_file) assert isinstance(actual_json, dict) - model_entries: Final = { - key: value for key, value in actual_json.items() if key not in RESERVED_TOP_LEVEL_KEYS - } + actual_json.pop( + "sample_spec", None + ) # remove the sample, whose schema is inconsistent with the real data + actual_json.pop( + "fallback_generalizations", None + ) # reserved meta key, not a model entry # Validate schema - validate(model_entries, INTENDED_SCHEMA) + validate(actual_json, INTENDED_SCHEMA) # Validate cost values # Define exceptions for models that are allowed to have costs > 1 @@ -1235,7 +1237,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid(): "runwayml/seedance2", # 4K output is 150 credits/second = $1.50/second ] - is_valid, violations = validate_model_cost_values(model_entries, exceptions) + is_valid, violations = validate_model_cost_values(actual_json, exceptions) if not is_valid: error_message = "Cost validation failed:\n" + "\n".join(violations) @@ -1266,7 +1268,8 @@ def test_max_tokens_consistency(): inconsistencies = [] for model_name, config in models.items(): - if model_name in RESERVED_TOP_LEVEL_KEYS: + # Skip the sample_spec + if model_name == "sample_spec": continue # Check if both max_tokens and max_output_tokens exist diff --git a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx index fde69675b72..101612993b0 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx @@ -33,15 +33,13 @@ const remoteSource = { is_env_forced: false, fallback_reason: null, loaded_at: null, - generated_at: null, source_revision: null, etag: null, model_count: 1234, }; const provenance = { loaded_at: "2026-09-07T10:00:00Z", - generated_at: "2026-09-06T23:38:47Z", - source_revision: "cd681a573fd9f5b6f15a1355f46178e4e9d374d2", + source_revision: "4273ec544726bf255ea920533e209e6022653bb4", etag: 'W/"eb8e9a53f4cc284b"', }; @@ -66,24 +64,22 @@ describe("PriceDataReload", () => { render(); expect(await screen.findByText("Source revision:")).toBeInTheDocument(); - expect(screen.getByText("cd681a573fd9")).toBeInTheDocument(); + expect(screen.getByText("4273ec544726")).toBeInTheDocument(); expect(screen.getByText("ETag:")).toBeInTheDocument(); expect(screen.getByText('W/"eb8e9a53f4cc284b"')).toBeInTheDocument(); - expect(screen.getByText("Generated at:")).toBeInTheDocument(); - expect(screen.getByText(new Date(provenance.generated_at).toLocaleString())).toBeInTheDocument(); expect(screen.getByText("Loaded at:")).toBeInTheDocument(); expect(screen.getByText(new Date(provenance.loaded_at).toLocaleString())).toBeInTheDocument(); }); - it("shows a malformed generated_at stamp as-is instead of Invalid Date", async () => { + it("shows a malformed loaded_at as-is instead of Invalid Date", async () => { vi.mocked(getModelCostMapSource).mockResolvedValue({ ...remoteSource, ...provenance, - generated_at: "yesterday-ish", + loaded_at: "yesterday-ish", } as never); render(); - expect(await screen.findByText("Generated at:")).toBeInTheDocument(); + expect(await screen.findByText("Loaded at:")).toBeInTheDocument(); expect(screen.getByText("yesterday-ish")).toBeInTheDocument(); expect(screen.queryByText("Invalid Date")).not.toBeInTheDocument(); }); @@ -92,7 +88,6 @@ describe("PriceDataReload", () => { render(); expect(await screen.findByText("Pricing Data Source")).toBeInTheDocument(); - expect(screen.queryByText("Generated at:")).not.toBeInTheDocument(); expect(screen.queryByText("Source revision:")).not.toBeInTheDocument(); expect(screen.queryByText("ETag:")).not.toBeInTheDocument(); expect(screen.queryByText("Loaded at:")).not.toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/price_data_reload.tsx b/ui/litellm-dashboard/src/components/price_data_reload.tsx index c152916f271..e5977a1b6e3 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.tsx @@ -50,7 +50,6 @@ interface CostMapSourceInfo { is_env_forced: boolean; fallback_reason: string | null; loaded_at: string | null; - generated_at: string | null; source_revision: string | null; etag: string | null; model_count: number; @@ -105,13 +104,6 @@ const formatDateTime = (dateTimeString: string | null) => { const CostMapProvenanceRows: React.FC<{ sourceInfo: CostMapSourceInfo }> = ({ sourceInfo }) => ( <> - {sourceInfo.generated_at && ( -
- Generated at: - {formatDateTime(sourceInfo.generated_at)} -
- )} - {sourceInfo.source_revision && (
Source revision: diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 7b9b24c9627..fc73d8264ef 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -8685,7 +8685,7 @@ export interface paths { * - is_env_forced: true if LITELLM_LOCAL_MODEL_COST_MAP=True forced local usage * - fallback_reason: human-readable reason why remote failed (null on success) * - loaded_at: when this pod last loaded the map - * - generated_at, source_revision: the _metadata stamp inside the loaded file + * - source_revision: git blob id of the loaded file, what git rev-parse : prints for it * - etag: the ETag of the remote fetch (null for the bundled backup) * - model_count: number of models in the currently loaded cost map */ From e2560390770bd4387c82889eff2d606ba88c42c5 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:49:03 -0700 Subject: [PATCH 060/223] fix(bedrock): import assert_never from typing_extensions for Python 3.10 --- .../llms/bedrock/embed/twelvelabs_marengo_3_transformation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py index 2ea99db47f0..0f61d37258f 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py @@ -7,9 +7,10 @@ Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mar from collections.abc import Mapping from types import MappingProxyType -from typing import Final, assert_never +from typing import Final from pydantic import BaseModel, ConfigDict, TypeAdapter, ValidationError +from typing_extensions import assert_never from litellm.llms.bedrock.common_utils import BedrockError from litellm.types.llms.bedrock import ( From 192ea9ec80ed97d771f9258320ac948080e87d2e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:55:37 -0700 Subject: [PATCH 061/223] fix(policy_engine): fail open on streaming shapes post_call pipelines cannot govern yet A post_call pipeline now releases the original stream instead of refusing the request on every shape it has no handler for: a background request, a pipeline guardrail without the unified apply_guardrail interface, a route with no endpoint translation, a buffered stream no translation resolves, and a rewrite the translation cannot write back (tool-call edits, text edits on translations without write-back, n>1 chat, an unended Anthropic stream, a Responses dump with no event envelope). Each case logs a warning naming the policy and guardrail. Real blocks and writable text masks are unchanged. --- .../chat/guardrail_translation/handler.py | 3 +- .../guardrail_translation/base_translation.py | 5 +- .../chat/guardrail_translation/handler.py | 5 +- .../guardrail_translation/handler.py | 7 +- .../proxy/policy_engine/pipeline_executor.py | 66 ++-- litellm/proxy/utils.py | 226 ++++++-------- .../policy_engine/test_pipeline_executor.py | 137 ++++++++- .../proxy_logging/test_guardrail_pipeline.py | 282 +++++++++++------- 8 files changed, 437 insertions(+), 294 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index da30d85e26b..e486be12fe2 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -1027,7 +1027,8 @@ class AnthropicMessagesHandler(BaseTranslation): Get the string so far, check the apply guardrail to the string so far, and return the list of responses so far. With ``deliver_ended_stream_rewrites``, an ended stream whose guardrail rewrote the text gets the rewrite written back across the buffered chunks (full rewritten text in the first ``text_delta``, the rest blanked); - a rewrite on a stream that never reported a ``stop_reason`` has no write-back and fails closed instead. + a rewrite on a stream that never reported a ``stop_reason`` has no write-back and is reported as + undeliverable, so the pipeline executor discards it and releases the original chunks. """ from litellm.integrations.custom_guardrail import ModifyResponseException diff --git a/litellm/llms/base_llm/guardrail_translation/base_translation.py b/litellm/llms/base_llm/guardrail_translation/base_translation.py index 770fac6e443..afd8e0f67f7 100644 --- a/litellm/llms/base_llm/guardrail_translation/base_translation.py +++ b/litellm/llms/base_llm/guardrail_translation/base_translation.py @@ -56,8 +56,9 @@ class BaseTranslation(ABC): """Whether ``process_output_streaming_response`` accepts ``deliver_ended_stream_rewrites=True`` and, on an ended (fully buffered) stream, writes guardrail text rewrites back across ``responses_so_far`` so - a buffered pipeline can release rewritten chunks instead of withholding the - stream. Tool-call rewrites stay undeliverable everywhere.""" + a buffered pipeline can release rewritten chunks. Tool-call rewrites, and + text rewrites on every other translation, are undeliverable: the pipeline + executor discards them and releases the original chunks.""" @staticmethod def transform_user_api_key_dict_to_metadata( diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index 4b7b1cc2700..80292aef2cf 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -1015,7 +1015,8 @@ class OpenAIChatCompletionsHandler(BaseTranslation): content-carrying chunk and the rest are blanked, the same shape the in-flight write-back uses. Chunks carrying only finish_reason or usage stay untouched. A rewrite on a stream carrying more than one distinct - choice index fails closed.""" + choice index is reported as undeliverable, so the pipeline executor + discards it and releases the original chunks.""" post_guardrail_texts: Final = self._string_choice_contents(guardrailed_response) changed: Final = tuple( after @@ -1030,7 +1031,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): if len(stream_choice_indices) != 1: # stream_chunk_builder collapses every choice into one index-0 # choice, so a rewrite of the rebuilt response cannot be attributed - # back to a single choice on an n>1 stream: withhold the stream + # back to a single choice on an n>1 stream: report it undeliverable # rather than deliver the rewrite on the wrong choice from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index b543c7f1e17..b0f79552bc5 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -699,7 +699,8 @@ class OpenAIResponsesHandler(BaseTranslation): ``response.content_part.done``, ``response.output_item.done``) are synced to the rewritten envelope too, so a client reading deltas sees the rewrite instead of the raw model output; a rewrite observed where no - write-back is possible fails closed instead of releasing raw output. + write-back is possible is reported as undeliverable, so the pipeline + executor discards it and releases the original events. """ if not responses_so_far: return responses_so_far @@ -788,7 +789,7 @@ class OpenAIResponsesHandler(BaseTranslation): # ------------------------------------------------------------------ # # Case 2: response.output_item.done — extract tool calls only, then # # fall through to the text fallback when a caller expects rewrites # - # delivered, so a buffer truncated here still fails closed on text. # + # delivered, so a truncated buffer still reports text undeliverable. # # ------------------------------------------------------------------ # if final_chunk.get("type") == "response.output_item.done": model_response_stream: Final = ( @@ -813,7 +814,7 @@ class OpenAIResponsesHandler(BaseTranslation): # Fallback: apply guardrail to the accumulated text string. # # No structured write-back is possible here; guardrails that only # # need to block/flag (not rewrite) still work correctly, and a # - # rewrite a caller expects delivered fails closed instead. # + # rewrite a caller expects delivered is reported undeliverable. # # ------------------------------------------------------------------ # string_so_far: Final = self.get_streaming_string_so_far(responses_so_far) if string_so_far: diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 160938b723c..970ac20487c 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -5,6 +5,7 @@ Runs guardrails sequentially per pipeline step definitions, handling pass/fail actions (allow, block, next, modify_response) and data forwarding. """ +import copy import time from collections.abc import Mapping, Sequence from typing import TYPE_CHECKING, Any, Final, Literal @@ -77,7 +78,7 @@ class _StreamRewriteObserver(CustomGuardrail): which for guardrails like Bedrock's ANONYMIZED action is only known at runtime. Text rewrites are deliverable on translations that write them back across the buffered chunks (``delivers_ended_stream_text_rewrites``); tool-call rewrites and text rewrites on any - other translation make the gate withhold the stream.""" + other translation are discarded by the executor, which releases the original chunks.""" def __init__(self, inner: CustomGuardrail) -> None: super().__init__(guardrail_name=inner.guardrail_name) @@ -133,6 +134,19 @@ def _prepare_hook_input( return hook_input, scans_raw_request +def _release_original_chunks( + guardrail_name: str, + streaming_chunks: list[object], # mutable-ok: shared buffered-stream chunks, restored in place + originals: Sequence[object], +) -> None: + streaming_chunks[:] = originals # rebind-ok: the caller's buffer is the stream the client receives + verbose_proxy_logger.warning( + "Pipeline: guardrail '%s' rewrote the streamed response in a way this endpoint's streaming " + "pipeline cannot deliver yet; the rewrite was discarded and the original stream released", + guardrail_name, + ) + + class PipelineExecutor: """Executes guardrail pipelines with ordered, conditional step logic.""" @@ -263,29 +277,37 @@ class PipelineExecutor: litellm_logging_obj: "LiteLLMLoggingObj | None", ) -> None: """Run one streaming post_call step through the endpoint translation, delivering - text rewrites on translations that support ended-stream write-back and raising - ``UndeliverableStreamRewrite`` for any rewrite that cannot reach the client.""" + text rewrites on translations that support ended-stream write-back. A rewrite that + cannot reach the client yet (a tool-call rewrite, a text rewrite on a translation + without write-back, or one the translation refused with + ``UndeliverableStreamRewrite``) is discarded: the buffered chunks go back to the + originals and the step passes, so the client gets the stream the merge base sent.""" observer: Final = _StreamRewriteObserver(callback) deliver_rewrites: Final = type(endpoint_translation).delivers_ended_stream_text_rewrites - if deliver_rewrites: - await endpoint_translation.process_output_streaming_response( - responses_so_far=streaming_chunks, - guardrail_to_apply=observer, - litellm_logging_obj=litellm_logging_obj, - user_api_key_dict=user_api_key_dict, - request_data=hook_input, - deliver_ended_stream_rewrites=True, - ) - else: - await endpoint_translation.process_output_streaming_response( - responses_so_far=streaming_chunks, - guardrail_to_apply=observer, - litellm_logging_obj=litellm_logging_obj, - user_api_key_dict=user_api_key_dict, - request_data=hook_input, - ) + originals: Final = copy.deepcopy(streaming_chunks) + try: + if deliver_rewrites: + await endpoint_translation.process_output_streaming_response( + responses_so_far=streaming_chunks, + guardrail_to_apply=observer, + litellm_logging_obj=litellm_logging_obj, + user_api_key_dict=user_api_key_dict, + request_data=hook_input, + deliver_ended_stream_rewrites=True, + ) + else: + await endpoint_translation.process_output_streaming_response( + responses_so_far=streaming_chunks, + guardrail_to_apply=observer, + litellm_logging_obj=litellm_logging_obj, + user_api_key_dict=user_api_key_dict, + request_data=hook_input, + ) + except UndeliverableStreamRewrite: + _release_original_chunks(step.guardrail, streaming_chunks, originals) + return if observer.rewrote_tool_calls or (observer.rewrote_texts and not deliver_rewrites): - raise UndeliverableStreamRewrite(step.guardrail) + _release_original_chunks(step.guardrail, streaming_chunks, originals) @staticmethod async def _run_step( @@ -386,8 +408,6 @@ class PipelineExecutor: ) # mutable-ok: modified-data contract is a plain dict return ("pass", response if isinstance(response, dict) else None, None, None) - except UndeliverableStreamRewrite: - raise except Exception as e: if CustomGuardrail._is_guardrail_intervention(e): error_msg: Final = _extract_error_message(e) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index ca8e48b5dfa..d7bf832bca4 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -19,7 +19,7 @@ from email.mime.text import MIMEText from types import MappingProxyType from typing import TYPE_CHECKING, Any, ClassVar, Final, Literal, Optional, Protocol, TypeVar, Union, cast, overload -from typing_extensions import NotRequired, ReadOnly, TypedDict +from typing_extensions import ReadOnly, TypedDict from litellm import _custom_logger_compatible_callbacks_literal from litellm.constants import ( @@ -155,7 +155,7 @@ from litellm.proxy.hooks.sensitive_data_routing import ( ) from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup, add_guardrails_from_auth_metadata from litellm.proxy.management_helpers.key_settings_audit import with_settings_updated_at -from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor, UndeliverableStreamRewrite +from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor from litellm.repositories.budget_repository import BudgetRepository from litellm.repositories.config_repository import ConfigRepository from litellm.repositories.table_repositories import ( @@ -518,108 +518,72 @@ def _pipeline_step_supports_unified_streaming(guardrail_name: str) -> bool: return callback is not None and PipelineExecutor.supports_unified_execution(callback) -class _PipelineErrorBody(TypedDict): - message: ReadOnly[str] - type: ReadOnly[str] - policies: ReadOnly[tuple[str, ...]] - guardrails: NotRequired[ReadOnly[tuple[str, ...]]] - - -class _PipelineErrorDetail(TypedDict): - error: ReadOnly[_PipelineErrorBody] - - -def _undeliverable_stream_rewrite_error(policy_name: str, guardrail_name: str) -> HTTPException: - detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - f"Streaming response withheld by policy pipeline '{policy_name}' because guardrail " - f"'{guardrail_name}' rewrote the streamed output in a way this endpoint's streaming " - "pipeline cannot deliver (a tool-call rewrite, or a text rewrite on a route without " - "stream write-back). Retry with stream=false, or drop it from the pipeline steps so " - "guardrails.add applies it to streamed output." - ), - "type": "guardrail_pipeline_error", - "policies": (policy_name,), - "guardrails": (guardrail_name,), - } - } - return HTTPException(status_code=400, detail=detail) - - -def _raise_for_streaming_post_call_pipelines(data: Mapping[str, object], user_api_key_dict: UserAPIKeyAuth) -> None: - """ - Reject up front the requests whose post_call pipelines could never run. - - Background responses skip the post_call hooks entirely, so a pipeline - governing one would silently never execute. Streaming responses execute - pipelines against the buffered stream through the endpoint guardrail - translation of the request route, releasing the buffered chunks on allow - (rewritten in place when a guardrail rewrote text and the translation - delivers ended-stream rewrites; a rewrite the translation cannot deliver - fails closed at runtime instead). That needs every step's guardrail to - support the unified apply_guardrail interface, and needs the route to have - a translation at all; anything else keeps the 400 rather than letting - ungoverned output stream through. - """ - is_stream: Final = data.get("stream") is True - is_background: Final = data.get("background") is True - if not is_stream and not is_background: - return - post_call_pipelines: Final = tuple( +def _post_call_pipelines(data: Mapping[str, object]) -> tuple[tuple[str, "GuardrailPipeline"], ...]: + return tuple( (policy_name, pipeline) for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == "post_call" ) + + +def _warn_background_skips_post_call_pipelines(data: Mapping[str, object]) -> None: + if data.get("background") is not True: + return + policy_names: Final = tuple(policy_name for policy_name, _pipeline in _post_call_pipelines(data)) + if not policy_names: + return + verbose_proxy_logger.warning( + "Policies with post_call guardrail pipelines do not run on background responses yet; " + "the response is released ungoverned by them: %s", + ", ".join(policy_names), + ) + + +def _pipeline_is_streamable(policy_name: str, pipeline: "GuardrailPipeline") -> bool: + unsupported: Final = tuple( + dict.fromkeys( + step.guardrail for step in pipeline.steps if not _pipeline_step_supports_unified_streaming(step.guardrail) + ) + ) + if not unsupported: + return True + verbose_proxy_logger.warning( + "Policy '%s' has post_call pipeline guardrails without the unified apply_guardrail interface, " + "which streaming pipelines need; the stream is released ungoverned by it: %s", + policy_name, + ", ".join(unsupported), + ) + return False + + +def _streamable_post_call_pipelines( + request_data: Mapping[str, object], user_api_key_dict: UserAPIKeyAuth +) -> tuple[tuple[str, "GuardrailPipeline"], ...]: + """ + The post_call pipelines a streaming response can be gated through. + + Streaming pipelines scan the buffered stream through the endpoint guardrail + translation of the request route, so every step's guardrail needs the + unified apply_guardrail interface and the route needs a translation. A + pipeline that cannot be run that way yet is left out and the stream is + released the way it was before pipelines ran on streams at all, with a + warning naming what went ungoverned. + """ + post_call_pipelines: Final = _post_call_pipelines(request_data) if not post_call_pipelines: - return - post_call_policies: Final = tuple(policy_name for policy_name, _pipeline in post_call_pipelines) - if is_background: - background_detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - "Policies with post_call guardrail pipelines cannot govern background " - f"responses: {', '.join(post_call_policies)}. Retry with background=false." - ), - "type": "guardrail_pipeline_error", - "policies": post_call_policies, - } - } - raise HTTPException(status_code=400, detail=background_detail) - step_guardrails: Final = tuple( - dict.fromkeys(step.guardrail for _policy_name, pipeline in post_call_pipelines for step in pipeline.steps) - ) - unsupported_guardrails: Final = tuple( - guardrail for guardrail in step_guardrails if not _pipeline_step_supports_unified_streaming(guardrail) - ) - if unsupported_guardrails: - unsupported_detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming responses " - "because these pipeline guardrails do not support the unified apply_guardrail " - f"interface: {', '.join(unsupported_guardrails)}. Retry with stream=false, or drop " - "them from the pipeline steps so guardrails.add scans them on streamed output." - ), - "type": "guardrail_pipeline_error", - "policies": post_call_policies, - "guardrails": unsupported_guardrails, - } - } - raise HTTPException(status_code=400, detail=unsupported_detail) + return () route: Final = user_api_key_dict.request_route - if not route or resolve_endpoint_translation(user_api_key_dict, None) is not None: - return - route_detail: Final[_PipelineErrorDetail] = { - "error": { - "message": ( - "Policies with post_call guardrail pipelines cannot govern streaming responses on " - f"route {route} because it has no endpoint guardrail translation to scan the stream " - f"through: {', '.join(post_call_policies)}. Retry with stream=false." - ), - "type": "guardrail_pipeline_error", - "policies": post_call_policies, - } - } - raise HTTPException(status_code=400, detail=route_detail) + if route and resolve_endpoint_translation(user_api_key_dict, None) is None: + verbose_proxy_logger.warning( + "Policies with post_call guardrail pipelines cannot scan streaming responses on route %s yet " + "(no endpoint guardrail translation); the stream is released ungoverned by them: %s", + route, + ", ".join(policy_name for policy_name, _pipeline in post_call_pipelines), + ) + return () + return tuple( + (policy_name, pipeline) + for policy_name, pipeline in post_call_pipelines + if _pipeline_is_streamable(policy_name, pipeline) + ) def _prompt_block_text(block: object) -> str: @@ -1990,7 +1954,7 @@ class ProxyLogging: ) try: - _raise_for_streaming_post_call_pipelines(data, user_api_key_dict) + _warn_background_skips_post_call_pipelines(data) # Execute guardrail pipelines before the normal callback loop data, _ = await self._maybe_execute_pipelines( # rebind-ok: pipeline edits feed the callback loop below @@ -3371,11 +3335,7 @@ class ProxyLogging: 1. /chat/completions """ caps: Final = ProxyLogging._callback_capabilities() - post_call_pipelines: Final = tuple( - (policy_name, pipeline) - for policy_name, pipeline in _policy_pipelines(request_data) - if pipeline.mode == "post_call" - ) + post_call_pipelines: Final = _streamable_post_call_pipelines(request_data, user_api_key_dict) # Fast path: no real overrides. Internal proxy CustomLogger callbacks # (e.g. _PROXY_MaxBudgetLimiter, ManagedFiles) inherit the default # ``async for chunk: yield chunk`` body, so wrapping the iterator @@ -3486,9 +3446,11 @@ class ProxyLogging: output, rewritten in place when one rewrote text and the translation delivers ended-stream rewrites (later steps then re-scan the rewritten chunks, so rewrites chain). A rewrite the translation cannot deliver - (a tool-call rewrite, or a text rewrite on a route without write-back) - withholds the stream with a 400; a block or modify_response terminates - with the translation's block chunks or the raised error. + yet (a tool-call rewrite, or a text rewrite on a route without + write-back) is discarded by the executor and the original chunks are + released, as is a buffered shape no translation resolves; a block or + modify_response terminates with the translation's block chunks or the + raised error. """ buffered: Final[list[object]] = [] # mutable-ok: accumulates the stream before the pipeline verdict async for item in response: @@ -3498,39 +3460,27 @@ class ProxyLogging: resolved: Final = resolve_endpoint_translation(user_api_key_dict, buffered[0]) if resolved is None: - policy_names: Final = tuple(policy_name for policy_name, _pipeline in pipelines) - raise ProxyException( - message=( - "Policy pipelines could not govern this streaming response shape; " - f"the response was withheld: {', '.join(policy_names)}." - ), - type="guardrail_pipeline_error", - param=None, - code=500, + verbose_proxy_logger.warning( + "Policies with post_call guardrail pipelines cannot scan this streaming response shape yet; " + "the stream is released ungoverned by them: %s", + ", ".join(policy_name for policy_name, _pipeline in pipelines), ) + for buffered_item in buffered: + yield buffered_item + return call_type, endpoint_translation = resolved for policy_name, pipeline in pipelines: - try: - result: PipelineExecutionResult = await PipelineExecutor.execute_steps( - steps=pipeline.steps, - mode="post_call", - data=request_data, - user_api_key_dict=user_api_key_dict, - call_type=call_type, - policy_name=policy_name, - streaming_chunks=buffered, - endpoint_translation=endpoint_translation, - ) - except UndeliverableStreamRewrite as rewrite: - async for error_chunk in unified_guardrail.emit_streaming_http_error( - _undeliverable_stream_rewrite_error(policy_name, rewrite.guardrail_name), - call_type, - buffered, - request_data, - ): - yield error_chunk - return + result: PipelineExecutionResult = await PipelineExecutor.execute_steps( + steps=pipeline.steps, + mode="post_call", + data=request_data, + user_api_key_dict=user_api_key_dict, + call_type=call_type, + policy_name=policy_name, + streaming_chunks=buffered, + endpoint_translation=endpoint_translation, + ) try: ProxyLogging._handle_pipeline_result( result, data=request_data, policy_name=policy_name, original_response=buffered diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index d399b4f01cb..0685bc6aa1e 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -4,6 +4,7 @@ Tests for the pipeline executor. Uses mock guardrails to validate pipeline execution without external services. """ +import logging from unittest.mock import MagicMock import pytest @@ -941,7 +942,55 @@ class _TextTranslation: return responses_so_far -async def _run_streaming_step(returned_texts, translation): +class _WritingTranslation: + """Writes the guardrail's text (and tool-call) outputs back into the buffered chunks the way the + chat/Responses/Messages handlers do on an ended stream.""" + + delivers_ended_stream_text_rewrites = True + + async def process_output_streaming_response( + self, + responses_so_far, + guardrail_to_apply, + litellm_logging_obj=None, + user_api_key_dict=None, + request_data=None, + deliver_ended_stream_rewrites=False, + ): + assert deliver_ended_stream_rewrites is True + outputs = await guardrail_to_apply.apply_guardrail( + inputs={"texts": [responses_so_far[0]["text"]], "tool_calls": [dict(responses_so_far[0]["tool_call"])]}, + request_data=request_data or {}, + input_type="response", + logging_obj=litellm_logging_obj, + ) + responses_so_far[0]["text"] = outputs["texts"][0] + responses_so_far[0]["tool_call"] = outputs["tool_calls"][0] + return responses_so_far + + +class _RefusingTranslation: + delivers_ended_stream_text_rewrites = True + + async def process_output_streaming_response( + self, + responses_so_far, + guardrail_to_apply, + litellm_logging_obj=None, + user_api_key_dict=None, + request_data=None, + deliver_ended_stream_rewrites=False, + ): + responses_so_far[0]["text"] = "half-written" + raise UndeliverableStreamRewrite(guardrail_to_apply.guardrail_name) + + +def _chunk(): + return {"text": "hello world", "tool_call": {"function": {"name": "lookup", "arguments": '{"ssn": "123"}'}}} + + +async def _run_streaming_step(translation, streaming_chunks=None): + chunks = [object()] if streaming_chunks is None else streaming_chunks return await PipelineExecutor.execute_steps( steps=[PipelineStep(guardrail="masker", on_pass="allow", on_fail="next", on_error="next")], mode="post_call", @@ -949,31 +998,41 @@ async def _run_streaming_step(returned_texts, translation): user_api_key_dict=MagicMock(), call_type="completion", policy_name="p", - streaming_chunks=[object()], + streaming_chunks=chunks, endpoint_translation=translation, ) +def _assert_passed_with_discard_warning(result, caplog): + assert result.terminal_action == "allow" + assert [step.outcome for step in result.step_results] == ["pass"] + assert any("'masker'" in record.getMessage() and "discarded" in record.getMessage() for record in caplog.records) + + @pytest.mark.asyncio -async def test_streaming_step_rewrite_escapes_execute_steps_regardless_of_step_actions(monkeypatch): +async def test_streaming_step_discards_text_rewrite_when_translation_lacks_write_back(monkeypatch, caplog): monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) translation = _TextTranslation() + chunks = [_chunk()] - with pytest.raises(UndeliverableStreamRewrite) as info: - await _run_streaming_step(["hello [MASKED]"], translation) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(translation, chunks) - assert info.value.guardrail_name == "masker" + _assert_passed_with_discard_warning(result, caplog) + assert chunks == [_chunk()] assert translation.seen_guardrail_names == ["masker"] @pytest.mark.asyncio -async def test_streaming_step_unchanged_texts_in_another_container_allow(monkeypatch): +async def test_streaming_step_unchanged_texts_in_another_container_allow(monkeypatch, caplog): monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(("hello world",))]) - result = await _run_streaming_step(("hello world",), _TextTranslation()) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_TextTranslation()) assert result.terminal_action == "allow" assert [step.outcome for step in result.step_results] == ["pass"] + assert not any("discarded" in record.getMessage() for record in caplog.records) class _InPlaceMutatingGuardrail(CustomGuardrail): @@ -989,10 +1048,64 @@ class _InPlaceMutatingGuardrail(CustomGuardrail): @pytest.mark.asyncio -async def test_streaming_step_in_place_rewrite_still_withholds_stream(monkeypatch): +async def test_streaming_step_in_place_rewrite_is_discarded_without_write_back(monkeypatch, caplog): monkeypatch.setattr(litellm, "callbacks", [_InPlaceMutatingGuardrail()]) + chunks = [_chunk()] - with pytest.raises(UndeliverableStreamRewrite) as info: - await _run_streaming_step(["hello [MASKED]"], _TextTranslation()) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_TextTranslation(), chunks) - assert info.value.guardrail_name == "masker" + _assert_passed_with_discard_warning(result, caplog) + assert chunks == [_chunk()] + + +class _TextAndToolCallRewritingGuardrail(CustomGuardrail): + def __init__(self, rewrite_tool_call): + super().__init__(guardrail_name="masker", event_hook="post_call", default_on=True) + self.rewrite_tool_call = rewrite_tool_call + + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + tool_calls = ( + [{"function": {"name": "lookup", "arguments": '{"ssn": "[MASKED]"}'}}] + if self.rewrite_tool_call + else inputs["tool_calls"] + ) + return {**inputs, "texts": ["hello [MASKED]"], "tool_calls": tool_calls} + + +@pytest.mark.asyncio +async def test_streaming_step_delivers_text_rewrite_through_writing_translation(monkeypatch, caplog): + monkeypatch.setattr(litellm, "callbacks", [_TextAndToolCallRewritingGuardrail(rewrite_tool_call=False)]) + chunks = [_chunk()] + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_WritingTranslation(), chunks) + + assert result.terminal_action == "allow" + assert chunks[0]["text"] == "hello [MASKED]" + assert chunks[0]["tool_call"]["function"]["arguments"] == '{"ssn": "123"}' + assert not any("discarded" in record.getMessage() for record in caplog.records) + + +@pytest.mark.asyncio +async def test_streaming_step_discards_tool_call_rewrite_and_restores_written_text(monkeypatch, caplog): + monkeypatch.setattr(litellm, "callbacks", [_TextAndToolCallRewritingGuardrail(rewrite_tool_call=True)]) + chunks = [_chunk()] + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_WritingTranslation(), chunks) + + _assert_passed_with_discard_warning(result, caplog) + assert chunks == [_chunk()] + + +@pytest.mark.asyncio +async def test_streaming_step_restores_chunks_when_translation_refuses_the_rewrite(monkeypatch, caplog): + monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) + chunks = [_chunk()] + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_RefusingTranslation(), chunks) + + _assert_passed_with_discard_warning(result, caplog) + assert chunks == [_chunk()] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 2e73bebb07c..ad2b4a0efbb 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -11,6 +11,7 @@ from __future__ import annotations import asyncio import json +import logging from typing import Any, Callable, Dict, List from unittest.mock import AsyncMock, MagicMock, patch @@ -24,9 +25,9 @@ from litellm.integrations.custom_guardrail import ( ModifyResponseException, ) from litellm.integrations.prometheus import PrometheusLogger -from litellm.proxy._types import ProxyException, UserAPIKeyAuth +from litellm.proxy._types import UserAPIKeyAuth from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header -from litellm.proxy.utils import ProxyLogging, _raise_for_streaming_post_call_pipelines +from litellm.proxy.utils import ProxyLogging, _streamable_post_call_pipelines from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ContentFilterGuardrail from litellm.types.guardrails import BlockedWord, ContentFilterAction, GuardrailEventHooks from litellm.types.proxy.policy_engine.pipeline_types import ( @@ -1384,76 +1385,87 @@ async def test_pre_call_pipeline_managed_parallel_guardrail_runs_exactly_once( assert seen["count"] == 1 +def _warnings(caplog: pytest.LogCaptureFixture) -> List[str]: + return [record.getMessage() for record in caplog.records if record.levelno >= logging.WARNING] + + @pytest.mark.asyncio -async def test_pre_call_hook_rejects_streaming_request_with_post_call_pipeline( - proxy_logging, make_user_api_key_auth, monkeypatch +async def test_streaming_request_whose_pipeline_guardrail_is_missing_streams_verbatim( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog ): monkeypatch.setattr(litellm, "callbacks", []) data = _post_call_pipeline_data(stream=True) + chunks = _stream_chunks() + delivered: List[Any] = [] - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + out = await proxy_logging.pre_call_hook( user_api_key_dict=make_user_api_key_auth(), data=data, call_type="completion", guardrails_only=True, ) + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ): + delivered.append(item) - assert info.value.status_code == 400 - assert info.value.detail["error"]["policies"] == ("response-governance",) - assert info.value.detail["error"]["guardrails"] == ("gr-post",) - assert "stream=false" in info.value.detail["error"]["message"] + assert out is not None + assert out.get("stream") is True + assert [item is chunk for item, chunk in zip(delivered, chunks)] == [True, True] + assert len(delivered) == 2 + assert any("response-governance" in message and "gr-post" in message for message in _warnings(caplog)) @pytest.mark.asyncio -async def test_pre_call_hook_rejects_background_request_with_post_call_pipeline( - proxy_logging, make_user_api_key_auth, monkeypatch +async def test_pre_call_hook_accepts_background_request_with_post_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog ): monkeypatch.setattr(litellm, "callbacks", []) data = _post_call_pipeline_data(background=True) - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + out = await proxy_logging.pre_call_hook( user_api_key_dict=make_user_api_key_auth(), data=data, call_type="aresponses", guardrails_only=True, ) - assert info.value.status_code == 400 - assert info.value.detail["error"]["policies"] == ("response-governance",) - assert "background=false" in info.value.detail["error"]["message"] + assert out is not None + assert out.get("background") is True + assert any("response-governance" in message and "background" in message for message in _warnings(caplog)) -def test_raise_for_streaming_post_call_pipelines_ignores_non_streaming_and_pre_call(make_user_api_key_auth): - post_call = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) - pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) - auth = make_user_api_key_auth(request_route="/custom/stream") +@pytest.mark.asyncio +async def test_pre_call_hook_stays_quiet_on_background_request_without_post_call_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog +): + seen: Dict[str, Any] = {} + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) + pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="gr-post", on_fail="block")]) + data = { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "background": True, + "metadata": { + "_guardrail_pipelines": [("request-governance", pre_call)], + "_pipeline_managed_guardrails": {"gr-post"}, + }, + } - assert ( - _raise_for_streaming_post_call_pipelines( - {"stream": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + out = await proxy_logging.pre_call_hook( + user_api_key_dict=make_user_api_key_auth(), + data=data, + call_type="aresponses", + guardrails_only=True, ) - is None - ) - assert ( - _raise_for_streaming_post_call_pipelines( - {"background": False, "metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth - ) - is None - ) - assert ( - _raise_for_streaming_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", post_call)]}}, auth) - is None - ) - assert ( - _raise_for_streaming_post_call_pipelines( - {"stream": True, "metadata": {"_guardrail_pipelines": [("p", pre_call)]}}, auth - ) - is None - ) - assert _raise_for_streaming_post_call_pipelines({"stream": True}, auth) is None - assert _raise_for_streaming_post_call_pipelines({"background": True}, auth) is None + + assert out is not None + assert not any("background" in message for message in _warnings(caplog)) # --------------------------------------------------------------------------- @@ -1485,6 +1497,56 @@ async def _async_chunk_iter(chunks: List[Any]): yield chunk +def test_streamable_post_call_pipelines_keeps_supported_and_drops_unsupported( + make_user_api_key_auth, monkeypatch, caplog +): + class NativeOnlyGuardrail(CustomGuardrail): + pass + + supported = _unified_stream_guardrail({}) + native_only = NativeOnlyGuardrail(guardrail_name="gr-native", event_hook=GuardrailEventHooks.post_call) + monkeypatch.setattr(litellm, "callbacks", [supported, native_only]) + governed = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="gr-post", on_fail="block")]) + ungoverned = GuardrailPipeline( + mode="post_call", + steps=[PipelineStep(guardrail="gr-post", on_fail="next"), PipelineStep(guardrail="gr-native", on_fail="block")], + ) + pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="gr-native", on_fail="block")]) + data = {"metadata": {"_guardrail_pipelines": [("governed", governed), ("ungoverned", ungoverned), ("req", pre_call)]}} + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + streamable = _streamable_post_call_pipelines(data, make_user_api_key_auth(request_route="/v1/chat/completions")) + + assert streamable == (("governed", governed),) + assert any("'ungoverned'" in message and "gr-native" in message for message in _warnings(caplog)) + assert not any("'governed'" in message for message in _warnings(caplog)) + + +def test_streamable_post_call_pipelines_is_empty_on_route_without_translation( + make_user_api_key_auth, monkeypatch, caplog +): + monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail({})]) + governed = GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="gr-post", on_fail="block")]) + data = {"metadata": {"_guardrail_pipelines": [("governed", governed)]}} + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + streamable = _streamable_post_call_pipelines(data, make_user_api_key_auth(request_route="/custom/stream")) + + assert streamable == () + assert any("/custom/stream" in message and "governed" in message for message in _warnings(caplog)) + + +def test_streamable_post_call_pipelines_is_empty_without_post_call_pipelines(make_user_api_key_auth, caplog): + pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="g", on_fail="block")]) + auth = make_user_api_key_auth(request_route="/custom/stream") + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + assert _streamable_post_call_pipelines({"metadata": {"_guardrail_pipelines": [("p", pre_call)]}}, auth) == () + assert _streamable_post_call_pipelines({"stream": True}, auth) == () + + assert _warnings(caplog) == [] + + @pytest.mark.asyncio @pytest.mark.parametrize("request_route", [None, "/v1/chat/completions"]) async def test_pre_call_hook_allows_streaming_when_pipeline_guardrail_supports_unified( @@ -1507,21 +1569,25 @@ async def test_pre_call_hook_allows_streaming_when_pipeline_guardrail_supports_u @pytest.mark.asyncio @pytest.mark.parametrize("native_lifecycle", [False, True]) -async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_lacks_unified_support( - proxy_logging, make_user_api_key_auth, monkeypatch, native_lifecycle +async def test_streaming_iterator_hook_releases_stream_when_pipeline_guardrail_lacks_unified_support( + proxy_logging, make_user_api_key_auth, monkeypatch, native_lifecycle, caplog ): + seen: Dict[str, Any] = {} if native_lifecycle: class NativeOnlyGuardrail(CustomGuardrail): use_native_lifecycle_hooks = True async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + seen["count"] = seen.get("count", 0) + 1 return inputs else: class NativeOnlyGuardrail(CustomGuardrail): - pass + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["count"] = seen.get("count", 0) + 1 + return response monkeypatch.setattr( litellm, @@ -1529,18 +1595,29 @@ async def test_pre_call_hook_rejects_streaming_when_pipeline_guardrail_lacks_uni [NativeOnlyGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)], ) data = _post_call_pipeline_data(stream=True) + chunks = _stream_chunks() + delivered: List[Any] = [] - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + out = await proxy_logging.pre_call_hook( user_api_key_dict=make_user_api_key_auth(), data=data, call_type="completion", guardrails_only=True, ) + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(chunks), + request_data=data, + ): + delivered.append(item) - assert info.value.status_code == 400 - assert info.value.detail["error"]["guardrails"] == ("gr-post",) - assert "apply_guardrail" in info.value.detail["error"]["message"] + assert out is not None + assert out.get("stream") is True + assert [item is chunk for item, chunk in zip(delivered, chunks)] == [True, True] + assert len(delivered) == 2 + assert seen.get("count") is None + assert any("'response-governance'" in message and "gr-post" in message for message in _warnings(caplog)) @pytest.mark.asyncio @@ -1614,25 +1691,34 @@ async def test_pre_call_hook_allows_streaming_when_content_filter_category_masks @pytest.mark.asyncio -async def test_pre_call_hook_rejects_streaming_when_route_has_no_guardrail_translation( - proxy_logging, make_user_api_key_auth, monkeypatch +async def test_streaming_iterator_hook_releases_stream_when_route_has_no_guardrail_translation( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog ): seen: Dict[str, Any] = {} monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) data = _post_call_pipeline_data(stream=True) + chunks = _stream_chunks() + delivered: List[Any] = [] - with pytest.raises(HTTPException) as info: - await proxy_logging.pre_call_hook( + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + out = await proxy_logging.pre_call_hook( user_api_key_dict=make_user_api_key_auth(request_route="/custom/stream"), data=data, call_type="completion", guardrails_only=True, ) + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/custom/stream"), + response=_async_chunk_iter(chunks), + request_data=data, + ): + delivered.append(item) - assert info.value.status_code == 400 - assert info.value.detail["error"]["policies"] == ("response-governance",) - assert "/custom/stream" in info.value.detail["error"]["message"] + assert out is not None + assert [item is chunk for item, chunk in zip(delivered, chunks)] == [True, True] + assert len(delivered) == 2 assert seen.get("count") is None + assert any("/custom/stream" in message and "response-governance" in message for message in _warnings(caplog)) @pytest.mark.asyncio @@ -1714,8 +1800,8 @@ def _echoed_tool_call_dicts(arguments: str) -> List[Dict[str, Any]]: @pytest.mark.asyncio @pytest.mark.parametrize("on_fail, on_error", [("block", None), ("next", "next")]) -async def test_streaming_iterator_hook_pipeline_withholds_runtime_tool_call_rewrite( - proxy_logging, make_user_api_key_auth, monkeypatch, on_fail, on_error +async def test_streaming_iterator_hook_pipeline_releases_originals_on_runtime_tool_call_rewrite( + proxy_logging, make_user_api_key_auth, monkeypatch, on_fail, on_error, caplog ): transform = lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "[MASKED]"}')} # noqa: E731 monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) @@ -1724,7 +1810,7 @@ async def test_streaming_iterator_hook_pipeline_withholds_runtime_tool_call_rewr data = _post_call_pipeline_data(step=step, stream=True) delivered: List[Any] = [] - async def _drain() -> None: + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): async for item in proxy_logging.async_post_call_streaming_iterator_hook( user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), response=_async_chunk_iter(_tool_call_stream_chunks()), @@ -1732,16 +1818,10 @@ async def test_streaming_iterator_hook_pipeline_withholds_runtime_tool_call_rewr ): delivered.append(item) - with pytest.raises(HTTPException) as info: - await _drain() - - error = info.value.detail["error"] - assert delivered == [] - assert info.value.status_code == 400 - assert error["type"] == "guardrail_pipeline_error" - assert error["policies"] == ("response-governance",) - assert error["guardrails"] == ("gr-post",) - assert "stream=false" in error["message"] + assert len(delivered) == 2 + assert delivered[0].choices[0].delta.tool_calls[0].function.arguments == '{"ssn": "123"}' + assert delivered[1].choices[0].finish_reason == "tool_calls" + assert any("'gr-post'" in message and "discarded" in message for message in _warnings(caplog)) @pytest.mark.asyncio @@ -1849,30 +1929,28 @@ async def test_streaming_iterator_hook_pipeline_releases_stream_echoed_in_anothe @pytest.mark.asyncio -async def test_streaming_iterator_hook_pipeline_withholds_unresolvable_response_shape( - proxy_logging, make_user_api_key_auth, monkeypatch +async def test_streaming_iterator_hook_pipeline_releases_originals_on_unresolvable_response_shape( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog ): seen: Dict[str, Any] = {} monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) data = _post_call_pipeline_data(stream=True) + chunks = [object(), object()] delivered: List[Any] = [] - async def _drain() -> None: + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): async for item in proxy_logging.async_post_call_streaming_iterator_hook( user_api_key_dict=make_user_api_key_auth(), - response=_async_chunk_iter([object(), object()]), + response=_async_chunk_iter(chunks), request_data=data, ): delivered.append(item) - with pytest.raises(ProxyException) as info: - await _drain() - - assert delivered == [] - assert info.value.code == "500" - assert "withheld" in info.value.message + assert [item is chunk for item, chunk in zip(delivered, chunks)] == [True, True] + assert len(delivered) == 2 assert seen.get("count") is None + assert any("response-governance" in message and "shape" in message for message in _warnings(caplog)) def _anthropic_sse_chunks() -> List[bytes]: @@ -1953,9 +2031,9 @@ async def test_streaming_iterator_hook_pipeline_delivers_text_rewrite_on_anthrop @pytest.mark.asyncio -async def test_pipeline_executor_withholds_text_rewrite_when_translation_lacks_write_back(monkeypatch): +async def test_pipeline_executor_discards_text_rewrite_when_translation_lacks_write_back(monkeypatch, caplog): from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation - from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor, UndeliverableStreamRewrite + from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor class NoWriteBackTranslation(BaseTranslation): async def process_input_messages(self, data, guardrail_to_apply, litellm_logging_obj): @@ -1984,45 +2062,23 @@ async def test_pipeline_executor_withholds_text_rewrite_when_translation_lacks_w transform = lambda inputs: {"texts": ["hello [MASKED]"]} # noqa: E731 monkeypatch.setattr(litellm, "callbacks", [_rewriting_stream_guardrail(transform)]) + chunks = _stream_chunks() - with pytest.raises(UndeliverableStreamRewrite): - await PipelineExecutor.execute_steps( + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await PipelineExecutor.execute_steps( steps=[PipelineStep(guardrail="gr-post", on_pass="allow", on_fail="block")], mode="post_call", data={"metadata": {}}, user_api_key_dict=UserAPIKeyAuth(api_key="sk-test"), call_type="acompletion", policy_name="response-governance", - streaming_chunks=_stream_chunks(), + streaming_chunks=chunks, endpoint_translation=NoWriteBackTranslation(), ) - -@pytest.mark.asyncio -async def test_streaming_iterator_hook_pipeline_gates_without_iterator_overrides( - proxy_logging, make_user_api_key_auth, monkeypatch -): - monkeypatch.setattr(litellm, "callbacks", []) - monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) - data = _post_call_pipeline_data(stream=True) - delivered: List[Any] = [] - - async def _drain() -> None: - async for item in proxy_logging.async_post_call_streaming_iterator_hook( - user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), - response=_async_chunk_iter(_stream_chunks()), - request_data=data, - ): - delivered.append(item) - - with pytest.raises(HTTPException) as info: - await _drain() - - assert delivered == [] - assert info.value.status_code == 400 - assert info.value.detail["error"]["pipeline_context"]["step_results"] == [ - {"guardrail": "gr-post", "outcome": "error", "action": "block"} - ] + assert result.terminal_action == "allow" + assert [chunk.choices[0].delta.content for chunk in chunks] == ["hello ", "world"] + assert any("'gr-post'" in message and "discarded" in message for message in _warnings(caplog)) @pytest.mark.asyncio From 2f397fa12812afba555dbdeae407597e95968123 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:06:29 -0700 Subject: [PATCH 062/223] fix(drop_params): honor string flags in litellm_settings and responses, and fail open on non-flag values --- litellm/proxy/proxy_server.py | 3 ++ litellm/responses/main.py | 5 ++- litellm/types/router.py | 6 ++- .../proxy/proxy_server/test_proxy_config.py | 43 +++++++++++++++++++ .../test_responses_api_request_body.py | 4 +- tests/test_litellm/types/test_router.py | 7 ++- 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 0915b8dd1b9..76acd414976 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -279,6 +279,7 @@ from litellm.litellm_core_utils.audio_utils.utils import resolve_speech_media_ty from litellm.litellm_core_utils.core_helpers import ( _get_parent_otel_span_from_kwargs, get_litellm_metadata_from_kwargs, + normalize_drop_params, ) from litellm.litellm_core_utils.credential_accessor import CredentialAccessor from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj @@ -5508,6 +5509,8 @@ class ProxyConfig: parse_budget_reset_time(value) setattr(litellm, key, value) + elif key == "drop_params": + litellm.drop_params = bool(normalize_drop_params(value)) else: verbose_proxy_logger.debug( "%s setting litellm.%s=%s%s", diff --git a/litellm/responses/main.py b/litellm/responses/main.py index 5e74b7324b4..52ca6ebb07a 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -17,6 +17,7 @@ from litellm.completion_extras.litellm_responses_transformation.transformation i from litellm.constants import request_timeout from litellm.integrations.anthropic_cache_control_hook import CARRY_UNMATCHED_MESSAGE_POINTS from litellm.litellm_core_utils.asyncify import run_async_function +from litellm.litellm_core_utils.core_helpers import normalize_drop_params from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.litellm_core_utils.prompt_templates.common_utils import ( update_responses_input_with_model_file_ids, @@ -1253,7 +1254,7 @@ def responses( responses_api_provider_config=responses_api_provider_config, response_api_optional_params=response_api_optional_params, allowed_openai_params=allowed_openai_params, - drop_params=request_drop_params if isinstance(request_drop_params, bool) else None, + drop_params=normalize_drop_params(request_drop_params), ) litellm_logging_obj.update_from_kwargs( @@ -2081,7 +2082,7 @@ def compact_responses( responses_api_provider_config=responses_api_provider_config, response_api_optional_params=response_api_optional_params, allowed_openai_params=None, - drop_params=request_drop_params if isinstance(request_drop_params, bool) else None, + drop_params=normalize_drop_params(request_drop_params), ) # Pre Call logging diff --git a/litellm/types/router.py b/litellm/types/router.py index 47aea6430c3..e8aec027a5e 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -408,9 +408,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): @field_validator("drop_params", mode="before") @classmethod - def coerce_drop_params(cls, value: object) -> object: + def coerce_drop_params(cls, value: object) -> bool | str | None: normalized: Final = normalize_drop_params(value) - return value if normalized is None else normalized + if normalized is not None: + return normalized + return value if isinstance(value, str) else None def __contains__(self, key) -> bool: # Define custom behavior for the 'in' operator diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index a4be9574f89..c57cd387d13 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -2431,6 +2431,49 @@ def test_ProxyConfig__add_deployment_turns_stored_drop_params_string_into_bool(m assert deployment.litellm_params.drop_params is True +def test_ProxyConfig__add_deployment_keeps_loading_rows_after_a_non_flag_drop_params(monkeypatch): + monkeypatch.setenv("LITELLM_SALT_KEY", "sk-1234") + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + + def db_model(model_id, drop_params): + return SimpleNamespace( + model_id=model_id, + model_name="gpt-5-nano", + model_info={"id": model_id}, + litellm_params={ + "model": encrypt_value_helper(value="openai/gpt-5-nano"), + "drop_params": encrypt_value_helper(value=drop_params), + }, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model("bad-row", 2), db_model("good-after", "true")]) + deployments = [call.kwargs["deployment"] for call in fake_router.upsert_deployment.call_args_list] + + assert added == 2 + assert [d.litellm_params.drop_params for d in deployments] == [None, True] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("configured, expected", [("true", True), ("false", False)]) +async def test_ProxyConfig_load_config_turns_litellm_settings_drop_params_string_into_bool( + tmp_path, monkeypatch, configured, expected +): + f = tmp_path / "c.yaml" + f.write_text(f'model_list: []\nlitellm_settings:\n drop_params: "{configured}"\n') + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False) + monkeypatch.setattr(litellm, "drop_params", not expected) + + await ProxyConfig().load_config(router=None, config_file_path=str(f)) + + assert litellm.drop_params is expected + + # --------------------------------------------------------------------------- # ProxyConfig.decrypt_model_list_from_db # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/responses/test_responses_api_request_body.py b/tests/test_litellm/responses/test_responses_api_request_body.py index 3e60906ec6d..5fced458208 100644 --- a/tests/test_litellm/responses/test_responses_api_request_body.py +++ b/tests/test_litellm/responses/test_responses_api_request_body.py @@ -246,8 +246,10 @@ async def test_aresponses_keeps_include_obfuscation_in_stream_options(): @pytest.mark.asyncio +@pytest.mark.parametrize("drop_params", [True, "true"]) async def test_aresponses_request_level_drop_params_drops_bedrock_mantle_service_tier( monkeypatch, + drop_params, ): """ Request-level drop_params=True (as the proxy injects for agentic CLIs) must @@ -271,7 +273,7 @@ async def test_aresponses_request_level_drop_params_drops_bedrock_mantle_service aws_region_name="us-east-1", input="hi", service_tier="priority", - drop_params=True, + drop_params=drop_params, ) mock_post.assert_called_once() diff --git a/tests/test_litellm/types/test_router.py b/tests/test_litellm/types/test_router.py index 2f4a29473c3..47fc08167e3 100644 --- a/tests/test_litellm/types/test_router.py +++ b/tests/test_litellm/types/test_router.py @@ -1,5 +1,4 @@ import pytest -from pydantic import ValidationError from litellm.types.router import ( SPECIAL_MODEL_INFO_PARAMS, @@ -109,6 +108,6 @@ def test_drop_params_coerces_flags_and_keeps_unresolved_strings(value, expected) assert GenericLiteLLMParams(drop_params=value).drop_params == expected -def test_drop_params_rejects_non_flag_non_string_values(): - with pytest.raises(ValidationError): - GenericLiteLLMParams(drop_params=2) +@pytest.mark.parametrize("value", [2, 2.5, [], {}]) +def test_drop_params_ignores_non_flag_non_string_values(value): + assert GenericLiteLLMParams(drop_params=value).drop_params is None From 3b199cd3da3fc97a6a373a1247fb798fa2353ca1 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:11:58 -0700 Subject: [PATCH 063/223] fix(azure_ai): price seven Foundry catalog names and charge the model router fee once Add cost map entries for azure_ai/gpt-chat-latest, codex-mini, whisper, model-router, cohere-command-a, grok-4-20-reasoning, and grok-4-20-non-reasoning, priced from the live Azure AI Foundry and Azure OpenAI pricing pages and the Azure Retail Prices API. Skip the model router flat fee when the response model is the router entry itself, since the generic cost already priced that fee. Before, azure_ai/model_router charged it twice. Resolves LIT-3157 --- litellm/llms/azure_ai/cost_calculator.py | 76 +++----- ...odel_prices_and_context_window_backup.json | 130 +++++++++++++ model_prices_and_context_window.json | 130 +++++++++++++ .../azure_ai/test_azure_ai_cost_calculator.py | 26 +++ ...azure_ai_foundry_catalog_model_metadata.py | 176 ++++++++++++++++++ 5 files changed, 492 insertions(+), 46 deletions(-) create mode 100644 tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py diff --git a/litellm/llms/azure_ai/cost_calculator.py b/litellm/llms/azure_ai/cost_calculator.py index 95f536296a2..141148f06e7 100644 --- a/litellm/llms/azure_ai/cost_calculator.py +++ b/litellm/llms/azure_ai/cost_calculator.py @@ -56,6 +56,27 @@ def calculate_azure_model_router_flat_cost(model: str, prompt_tokens: int) -> fl return 0.0 +ROUTER_FEE_ENTRY_NAMES: Final = frozenset({"model-router", "model_router"}) + + +def _prices_router_fee_itself(model: str) -> bool: + return model.lower().rsplit("/", 1)[-1] in ROUTER_FEE_ENTRY_NAMES + + +def _base_cost_per_token(model: str, usage: Usage, service_tier: str | None) -> tuple[float, float] | None: + try: + return generic_cost_per_token( + model=model, usage=usage, custom_llm_provider="azure_ai", service_tier=service_tier + ) + except Exception as e: + if not _is_azure_model_router(model): + raise + verbose_logger.debug( + "Azure AI Model Router: model '%s' not in cost map, calculating routing flat cost only. Error: %s", model, e + ) + return None + + def cost_per_token( model: str, usage: Usage, @@ -66,9 +87,9 @@ def cost_per_token( """ Calculate the cost per token for Azure AI models. - For Azure AI Foundry Model Router: - - Adds a flat cost of $0.14 per million input tokens (from model_prices_and_context_window.json) - - Plus the cost of the actual model used (handled by generic_cost_per_token) + For Azure AI Foundry Model Router the routing fee (the azure_ai/model_router entry, $0.14 per + million input tokens) is added on top of the routed model's cost. When the response model is + the router entry itself, generic_cost_per_token has already charged that fee. Args: model: str, the model name without provider prefix (from response) @@ -83,49 +104,12 @@ def cost_per_token( ValueError: If the model is not found in the cost map and cost cannot be calculated (except for Model Router models where we return just the routing flat cost) """ - prompt_cost = 0.0 - completion_cost = 0.0 - - # Determine if this was a model router request - # Check both the response model and the request model is_router_request: Final = _is_azure_model_router(model) or ( request_model is not None and _is_azure_model_router(request_model) ) - - # Calculate base cost using generic cost calculator - # This may raise an exception if the model is not in the cost map - try: - prompt_cost, completion_cost = generic_cost_per_token( - model=model, - usage=usage, - custom_llm_provider="azure_ai", - service_tier=service_tier, - ) - except Exception as e: - # For Model Router, the model name (e.g., "azure-model-router") may not be in the cost map - # because it's a routing service, not an actual model. In this case, we continue - # to calculate just the routing flat cost. - if not _is_azure_model_router(model): - # Re-raise for non-router models - they should have pricing defined - raise - verbose_logger.debug( - "Azure AI Model Router: model '%s' not in cost map, calculating routing flat cost only. Error: %s", model, e - ) - - # Add flat cost for Azure Model Router - # The flat cost is defined in model_prices_and_context_window.json for azure_ai/model_router - if is_router_request: - # Use the request model for flat cost calculation if available, otherwise use response model - router_model_for_calc: Final = request_model if request_model else model - router_flat_cost: Final = calculate_azure_model_router_flat_cost(router_model_for_calc, usage.prompt_tokens) - - if router_flat_cost > 0: - verbose_logger.debug( - f"Azure AI Model Router flat cost: ${router_flat_cost:.6f} " - f"({usage.prompt_tokens} tokens × ${router_flat_cost / usage.prompt_tokens:.9f}/token)" - ) - - # Add flat cost to prompt cost - prompt_cost += router_flat_cost - - return prompt_cost, completion_cost + base_cost: Final = _base_cost_per_token(model=model, usage=usage, service_tier=service_tier) + prompt_cost, completion_cost = base_cost if base_cost is not None else (0.0, 0.0) + if not is_router_request or (base_cost is not None and _prices_router_fee_itself(model)): + return prompt_cost, completion_cost + router_flat_cost: Final = calculate_azure_model_router_flat_cost(request_model or model, usage.prompt_tokens) + return prompt_cost + router_flat_cost, completion_cost diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index b1ffc1583e4..6649fa831d7 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3581,6 +3581,82 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": false }, + "azure_ai/gpt-chat-latest": { + "cache_read_input_token_cost": 5e-07, + "deprecation_date": "2026-12-02", + "input_cost_per_token": 5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "reasoning_effort_levels": [ + "medium" + ], + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "azure_ai/codex-mini": { + "cache_read_input_token_cost": 3.75e-07, + "deprecation_date": "2026-11-15", + "input_cost_per_token": 1.5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 100000, + "max_tokens": 100000, + "mode": "responses", + "output_cost_per_token": 6e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure_ai/whisper": { + "deprecation_date": "2026-12-15", + "input_cost_per_second": 0.0001, + "litellm_provider": "azure_ai", + "mode": "audio_transcription", + "output_cost_per_second": 0.0001, + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/" + }, "azure_ai/gpt-5.5-2026-04-23": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -3991,6 +4067,17 @@ "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" }, + "azure_ai/model-router": { + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 0, + "litellm_provider": "azure_ai", + "max_input_tokens": 1048576, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", + "comment": "Catalog-name twin of azure_ai/model_router: the flat $0.14 per M input tokens is the router's own fee, the routed model is priced on top of it" + }, "azure/eu/gpt-4o-2024-08-06": { "deprecation_date": "2027-04-14", "cache_read_input_token_cost": 1.375e-06, @@ -10302,6 +10389,18 @@ "/v1/ocr" ] }, + "azure_ai/cohere-command-a": { + "input_cost_per_token": 2.5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 131072, + "max_output_tokens": 4096, + "max_tokens": 4096, + "mode": "chat", + "output_cost_per_token": 1e-05, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/", + "supports_function_calling": true, + "supports_tool_choice": true + }, "azure_ai/doc-intelligence/prebuilt-read": { "litellm_provider": "azure_ai", "ocr_cost_per_page": 0.0015, @@ -10653,6 +10752,37 @@ "supports_vision": true, "supports_web_search": true }, + "azure_ai/grok-4-20-reasoning": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 262000, + "max_output_tokens": 8192, + "max_tokens": 8192, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_reasoning": true + }, + "azure_ai/grok-4-20-non-reasoning": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 262000, + "max_output_tokens": 8192, + "max_tokens": 8192, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "azure_ai/grok-4-fast-non-reasoning": { "deprecation_date": "2026-05-01", "input_cost_per_token": 2e-07, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index b1ffc1583e4..6649fa831d7 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3581,6 +3581,82 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": false }, + "azure_ai/gpt-chat-latest": { + "cache_read_input_token_cost": 5e-07, + "deprecation_date": "2026-12-02", + "input_cost_per_token": 5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-05, + "reasoning_effort_levels": [ + "medium" + ], + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, + "azure_ai/codex-mini": { + "cache_read_input_token_cost": 3.75e-07, + "deprecation_date": "2026-11-15", + "input_cost_per_token": 1.5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 100000, + "max_tokens": 100000, + "mode": "responses", + "output_cost_per_token": 6e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", + "supported_endpoints": [ + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "azure_ai/whisper": { + "deprecation_date": "2026-12-15", + "input_cost_per_second": 0.0001, + "litellm_provider": "azure_ai", + "mode": "audio_transcription", + "output_cost_per_second": 0.0001, + "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/" + }, "azure_ai/gpt-5.5-2026-04-23": { "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, @@ -3991,6 +4067,17 @@ "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" }, + "azure_ai/model-router": { + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 0, + "litellm_provider": "azure_ai", + "max_input_tokens": 1048576, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", + "comment": "Catalog-name twin of azure_ai/model_router: the flat $0.14 per M input tokens is the router's own fee, the routed model is priced on top of it" + }, "azure/eu/gpt-4o-2024-08-06": { "deprecation_date": "2027-04-14", "cache_read_input_token_cost": 1.375e-06, @@ -10302,6 +10389,18 @@ "/v1/ocr" ] }, + "azure_ai/cohere-command-a": { + "input_cost_per_token": 2.5e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 131072, + "max_output_tokens": 4096, + "max_tokens": 4096, + "mode": "chat", + "output_cost_per_token": 1e-05, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/", + "supports_function_calling": true, + "supports_tool_choice": true + }, "azure_ai/doc-intelligence/prebuilt-read": { "litellm_provider": "azure_ai", "ocr_cost_per_page": 0.0015, @@ -10653,6 +10752,37 @@ "supports_vision": true, "supports_web_search": true }, + "azure_ai/grok-4-20-reasoning": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 262000, + "max_output_tokens": 8192, + "max_tokens": 8192, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true, + "supports_reasoning": true + }, + "azure_ai/grok-4-20-non-reasoning": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "azure_ai", + "max_input_tokens": 262000, + "max_output_tokens": 8192, + "max_tokens": 8192, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/", + "supports_function_calling": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "azure_ai/grok-4-fast-non-reasoning": { "deprecation_date": "2026-05-01", "input_cost_per_token": 2e-07, diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py index 9612d97d946..80cd99bd46b 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py @@ -528,3 +528,29 @@ def test_mai_thinking_1_model_info_and_cost(local_model_cost_map): assert model_info["supports_function_calling"] is True assert prompt_cost == pytest.approx(2.0) assert completion_cost == pytest.approx(8.0) + + +@pytest.mark.usefixtures("local_model_cost_map") +@pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) +def test_router_entry_as_response_model_charges_the_fee_once(router_entry_name: str) -> None: + usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) + prompt_cost, completion_cost = cost_per_token(model=router_entry_name, usage=usage) + assert prompt_cost == pytest.approx(0.14, rel=1e-9) + assert completion_cost == 0.0 + + +@pytest.mark.usefixtures("local_model_cost_map") +def test_unmapped_router_deployment_name_still_charges_the_fee() -> None: + usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) + prompt_cost, completion_cost = cost_per_token(model="azure-model-router", usage=usage) + assert prompt_cost == pytest.approx(0.14, rel=1e-9) + assert completion_cost == 0.0 + + +@pytest.mark.usefixtures("local_model_cost_map") +def test_routed_model_response_adds_the_fee_on_top() -> None: + usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) + routed_prompt_cost, _ = cost_per_token(model="gpt-5-nano", usage=usage) + prompt_cost, _ = cost_per_token(model="gpt-5-nano", usage=usage, request_model="azure_ai/model-router") + assert routed_prompt_cost > 0 + assert prompt_cost == pytest.approx(routed_prompt_cost + 0.14, rel=1e-9) diff --git a/tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py b/tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py new file mode 100644 index 00000000000..9c5ca26a89c --- /dev/null +++ b/tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py @@ -0,0 +1,176 @@ +from dataclasses import dataclass +from pathlib import Path +from typing import Final + +import pytest +from pydantic import TypeAdapter + +from litellm import cost_per_token, get_model_info +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + +REPO_ROOT: Final = Path(__file__).parents[2] +COST_MAP_ADAPTER: Final = TypeAdapter(dict[str, dict[str, object]]) +AZURE_OPENAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/" +FOUNDRY_AOAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/" +FOUNDRY_COHERE_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/" +FOUNDRY_GROK_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/" + + +@dataclass(frozen=True, slots=True) +class TokenPricedCatalogModel: + catalog_name: str + mode: str + source: str + input_cost_per_token: float + output_cost_per_token: float + max_input_tokens: int + max_output_tokens: int + cache_read_input_token_cost: float | None + supported_flags: tuple[str, ...] + + +TOKEN_PRICED_MODELS: Final = ( + TokenPricedCatalogModel( + catalog_name="gpt-chat-latest", + mode="chat", + source=AZURE_OPENAI_PRICING, + input_cost_per_token=5e-06, + output_cost_per_token=3e-05, + max_input_tokens=200000, + max_output_tokens=128000, + cache_read_input_token_cost=5e-07, + supported_flags=( + "supports_function_calling", + "supports_prompt_caching", + "supports_reasoning", + "supports_response_schema", + "supports_tool_choice", + "supports_vision", + "supports_web_search", + ), + ), + TokenPricedCatalogModel( + catalog_name="codex-mini", + mode="responses", + source=AZURE_OPENAI_PRICING, + input_cost_per_token=1.5e-06, + output_cost_per_token=6e-06, + max_input_tokens=200000, + max_output_tokens=100000, + cache_read_input_token_cost=3.75e-07, + supported_flags=("supports_function_calling", "supports_prompt_caching", "supports_reasoning", "supports_vision"), + ), + TokenPricedCatalogModel( + catalog_name="model-router", + mode="chat", + source=FOUNDRY_AOAI_PRICING, + input_cost_per_token=1.4e-07, + output_cost_per_token=0.0, + max_input_tokens=1048576, + max_output_tokens=32768, + cache_read_input_token_cost=None, + supported_flags=(), + ), + TokenPricedCatalogModel( + catalog_name="cohere-command-a", + mode="chat", + source=FOUNDRY_COHERE_PRICING, + input_cost_per_token=2.5e-06, + output_cost_per_token=1e-05, + max_input_tokens=131072, + max_output_tokens=4096, + cache_read_input_token_cost=None, + supported_flags=("supports_function_calling", "supports_tool_choice"), + ), + TokenPricedCatalogModel( + catalog_name="grok-4-20-reasoning", + mode="chat", + source=FOUNDRY_GROK_PRICING, + input_cost_per_token=1.25e-06, + output_cost_per_token=2.5e-06, + max_input_tokens=262000, + max_output_tokens=8192, + cache_read_input_token_cost=None, + supported_flags=( + "supports_function_calling", + "supports_reasoning", + "supports_response_schema", + "supports_tool_choice", + "supports_vision", + "supports_web_search", + ), + ), + TokenPricedCatalogModel( + catalog_name="grok-4-20-non-reasoning", + mode="chat", + source=FOUNDRY_GROK_PRICING, + input_cost_per_token=1.25e-06, + output_cost_per_token=2.5e-06, + max_input_tokens=262000, + max_output_tokens=8192, + cache_read_input_token_cost=None, + supported_flags=( + "supports_function_calling", + "supports_response_schema", + "supports_tool_choice", + "supports_vision", + "supports_web_search", + ), + ), +) +CATALOG_NAMES: Final = tuple(spec.catalog_name for spec in TOKEN_PRICED_MODELS) + ("whisper",) + + +def _cost_map_entry(path: Path, catalog_name: str) -> dict[str, object]: + return COST_MAP_ADAPTER.validate_json(path.read_bytes())[f"azure_ai/{catalog_name}"] + + +@pytest.mark.usefixtures("local_model_cost_map") +@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) +def test_azure_ai_catalog_name_is_priced_and_routed(spec: TokenPricedCatalogModel) -> None: + routed_model, provider, _, _ = get_llm_provider(model=f"azure_ai/{spec.catalog_name}") + assert (routed_model, provider) == (spec.catalog_name, "azure_ai") + + info = get_model_info(model=routed_model, custom_llm_provider=provider) + assert info["litellm_provider"] == "azure_ai" + assert info["mode"] == spec.mode + assert info["input_cost_per_token"] == spec.input_cost_per_token + assert info["output_cost_per_token"] == spec.output_cost_per_token + assert info["cache_read_input_token_cost"] == spec.cache_read_input_token_cost + assert info["max_input_tokens"] == spec.max_input_tokens + assert info["max_output_tokens"] == spec.max_output_tokens + assert info["max_tokens"] == spec.max_output_tokens + for flag in spec.supported_flags: + assert info[flag] is True, flag + + +@pytest.mark.usefixtures("local_model_cost_map") +@pytest.mark.parametrize( + "spec", [spec for spec in TOKEN_PRICED_MODELS if spec.catalog_name != "model-router"], ids=lambda spec: spec.catalog_name +) +def test_azure_ai_catalog_name_costs_a_million_tokens_at_list_price(spec: TokenPricedCatalogModel) -> None: + prompt_cost, completion_cost = cost_per_token( + model=f"azure_ai/{spec.catalog_name}", prompt_tokens=1_000_000, completion_tokens=1_000_000 + ) + assert prompt_cost == pytest.approx(spec.input_cost_per_token * 1_000_000) + assert completion_cost == pytest.approx(spec.output_cost_per_token * 1_000_000) + + +@pytest.mark.usefixtures("local_model_cost_map") +def test_azure_ai_whisper_catalog_name_is_priced_per_second() -> None: + routed_model, provider, _, _ = get_llm_provider(model="azure_ai/whisper") + assert (routed_model, provider) == ("whisper", "azure_ai") + + info = get_model_info(model=routed_model, custom_llm_provider=provider) + assert info["mode"] == "audio_transcription" + assert info["input_cost_per_second"] == 0.0001 + assert info["output_cost_per_second"] == 0.0001 + + +@pytest.mark.parametrize("catalog_name", CATALOG_NAMES) +def test_azure_ai_catalog_entry_source_and_backup_match(catalog_name: str) -> None: + main_entry = _cost_map_entry(REPO_ROOT / "model_prices_and_context_window.json", catalog_name) + backup_entry = _cost_map_entry(REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json", catalog_name) + + assert str(main_entry["source"]).startswith("https://azure.microsoft.com/en-us/pricing/details/") + assert backup_entry == main_entry From 86790a7723892c338ea3fcc680296a721c7fc47f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:20:28 -0700 Subject: [PATCH 064/223] fix(bedrock): bill Marengo embeddings per request instead of per estimated token AWS prices Marengo 2.7 and 3.0 text and image embeddings per request, never per token, and their responses carry no token count. The old transform estimated prompt tokens from the vector length, which billed a text request at 128 tokens times the per-token rate (0.00896 instead of 0.00007). Marengo responses now report zero tokens with query_count and image_count derived from the request batch, and all six Marengo cost-map entries price per request (with the video and audio per-second and per-image rates on the base entries). query_count is a new prompt_tokens_details field wired to input_cost_per_query in the cost calculator. --- .../litellm_core_utils/llm_cost_calc/utils.py | 10 ++ litellm/llms/bedrock/embed/embedding.py | 2 +- .../twelvelabs_marengo_transformation.py | 142 +++++++++++------- ...odel_prices_and_context_window_backup.json | 18 ++- litellm/types/utils.py | 7 +- litellm/utils.py | 2 +- model_prices_and_context_window.json | 18 ++- .../llm_cost_calc/test_llm_cost_calc_utils.py | 32 ++++ .../bedrock/embed/test_bedrock_embedding.py | 47 +++++- ..._bedrock_marengo_embed_3_model_metadata.py | 45 +++++- 10 files changed, 240 insertions(+), 83 deletions(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 68dc27ec25e..46574ebae3f 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -780,6 +780,7 @@ class PromptTokensDetailsResult(TypedDict): image_count: int video_length_seconds: float audio_length_seconds: float + query_count: int def parse_prompt_tokens_details(usage: Usage) -> PromptTokensDetailsResult: @@ -828,6 +829,7 @@ def parse_prompt_tokens_details(usage: Usage) -> PromptTokensDetailsResult: ) or 0.0 ) + query_count: Final = _coerce_token_count(getattr(usage.prompt_tokens_details, "query_count", 0)) return PromptTokensDetailsResult( cache_hit_tokens=cache_hit_tokens, @@ -841,6 +843,7 @@ def parse_prompt_tokens_details(usage: Usage) -> PromptTokensDetailsResult: image_count=image_count, video_length_seconds=float(video_length_seconds), audio_length_seconds=float(audio_length_seconds), + query_count=query_count, ) @@ -978,6 +981,12 @@ def _calculate_input_cost( prompt_tokens_details["audio_length_seconds"], ) + ### QUERY COUNT COST + if prompt_tokens_details["query_count"]: + prompt_cost += calculate_cost_component( + model_info, "input_cost_per_query", prompt_tokens_details["query_count"] + ) + return prompt_cost @@ -1149,6 +1158,7 @@ def generic_cost_per_token( image_count=0, video_length_seconds=0.0, audio_length_seconds=0.0, + query_count=0, ) if usage.prompt_tokens_details: prompt_tokens_details = parse_prompt_tokens_details(usage) diff --git a/litellm/llms/bedrock/embed/embedding.py b/litellm/llms/bedrock/embed/embedding.py index ab27afcf817..69eb9b693b1 100644 --- a/litellm/llms/bedrock/embed/embedding.py +++ b/litellm/llms/bedrock/embed/embedding.py @@ -229,7 +229,7 @@ class BedrockEmbedding(BaseAWSLLM): returned_response = AmazonTitanG1Config()._transform_response(response_list=response_list, model=model) elif provider == "twelvelabs": returned_response = TwelveLabsMarengoEmbeddingConfig()._transform_response( - response_list=response_list, model=model + response_list=response_list, model=model, batch_data=batch_data ) elif provider == "nova": returned_response = AmazonNovaEmbeddingConfig()._transform_response( diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py index 79b5825d2eb..ddf6dfbcc4d 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py @@ -7,14 +7,19 @@ Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mar Marengo 3.0 docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-marengo-3.html """ +from collections.abc import Mapping from typing import Final, cast +from pydantic import BaseModel, ConfigDict, TypeAdapter +from typing_extensions import assert_never + from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( build_marengo_3_request, is_marengo_3_model, ) from litellm.types.llms.bedrock import ( TWELVELABS_EMBEDDING_INPUT_TYPES, + TWELVELABS_MARENGO_3_INPUT_TYPES, TwelveLabsAsyncInvokeRequest, TwelveLabsMarengo3EmbeddingRequest, TwelveLabsMarengoEmbeddingRequest, @@ -22,7 +27,76 @@ from litellm.types.llms.bedrock import ( TwelveLabsS3Location, TwelveLabsS3OutputDataConfig, ) -from litellm.types.utils import Embedding, EmbeddingResponse, Usage +from litellm.types.utils import Embedding, EmbeddingResponse, PromptTokensDetailsWrapper, Usage + + +class MarengoEmbeddingItem(BaseModel): + model_config = ConfigDict(extra="ignore", frozen=True) + + embedding: tuple[float, ...] + + +class MarengoInvokeResponse(BaseModel): + model_config = ConfigDict(extra="ignore", frozen=True) + + data: tuple[MarengoEmbeddingItem, ...] = () + embedding: tuple[float, ...] | None = None + embeddings: tuple[MarengoEmbeddingItem, ...] = () + + def vectors(self) -> tuple[tuple[float, ...], ...]: + if self.data: + return tuple(item.embedding for item in self.data) + if self.embedding is not None: + return (self.embedding,) + return tuple(item.embedding for item in self.embeddings) + + +class MarengoBilledMultiInput(BaseModel): + model_config = ConfigDict(extra="ignore", frozen=True) + + inputText: str | None = None + mediaSources: tuple[Mapping[str, object], ...] = () + + +class MarengoBilledRequest(BaseModel): + model_config = ConfigDict(extra="ignore", frozen=True) + + inputType: TWELVELABS_MARENGO_3_INPUT_TYPES | None = None + multi_input: MarengoBilledMultiInput | None = None + + +INVOKE_RESPONSES: Final = TypeAdapter(tuple[MarengoInvokeResponse, ...]) +BILLED_REQUESTS: Final = TypeAdapter(tuple[MarengoBilledRequest, ...]) + + +def _billed_units(request: MarengoBilledRequest) -> tuple[int, int]: + input_type: Final = request.inputType + match input_type: + case "text": + return (1, 0) + case "image": + return (0, 1) + case "text_image": + return (1, 1) + case "multi_input": + multi_input: Final = request.multi_input or MarengoBilledMultiInput() + return (1 if multi_input.inputText else 0, len(multi_input.mediaSources)) + case "video" | "audio" | None: + return (0, 0) + case _: + assert_never(input_type) + + +def _billed_usage(batch_data: list[dict] | None) -> Usage: + units: Final = tuple(_billed_units(request) for request in BILLED_REQUESTS.validate_python(batch_data or ())) + query_count: Final = sum(text_requests for text_requests, _ in units) + image_count: Final = sum(images for _, images in units) + details: Final = ( + PromptTokensDetailsWrapper(query_count=query_count or None, image_count=image_count or None) + if query_count or image_count + else None + ) + return Usage(prompt_tokens=0, completion_tokens=0, total_tokens=0, prompt_tokens_details=details) class TwelveLabsMarengoEmbeddingConfig: @@ -223,62 +297,16 @@ class TwelveLabsMarengoEmbeddingConfig: ), ) - def _transform_response(self, response_list: list[dict], model: str) -> EmbeddingResponse: - """ - Transform TwelveLabs response to OpenAI format. - Handles the actual TwelveLabs response format: {"data": [{"embedding": [...]}]} - """ - embeddings: Final[list[Embedding]] = [] - total_tokens = 0 - - for response in response_list: - # TwelveLabs response format has a "data" field containing the embeddings - if "data" in response and isinstance(response["data"], list): - for item in response["data"]: - if "embedding" in item: - # Single embedding response - embedding = Embedding( - embedding=item["embedding"], - index=len(embeddings), - object="embedding", - ) - embeddings.append(embedding) - - # Estimate token count (rough approximation) - if "inputTextTokenCount" in item: - total_tokens += item["inputTextTokenCount"] - else: - # Rough estimate: 1 token per 4 characters for text, or use embedding size - total_tokens += len(item["embedding"]) // 4 - elif "embedding" in response: - # Direct embedding response (fallback for other formats) - embedding = Embedding( - embedding=response["embedding"], - index=len(embeddings), - object="embedding", - ) - embeddings.append(embedding) - - # Estimate token count (rough approximation) - if "inputTextTokenCount" in response: - total_tokens += response["inputTextTokenCount"] - else: - # Rough estimate: 1 token per 4 characters for text - total_tokens += len(response.get("inputText", "")) // 4 - elif "embeddings" in response: - # Multiple embeddings response (from video/audio) - for i, emb in enumerate(response["embeddings"]): - embedding = Embedding( - embedding=emb["embedding"], - index=len(embeddings), - object="embedding", - ) - embeddings.append(embedding) - total_tokens += len(emb["embedding"]) // 4 # Rough estimate - - usage: Final = Usage(prompt_tokens=total_tokens, total_tokens=total_tokens) - - return EmbeddingResponse(data=embeddings, model=model, usage=usage) + def _transform_response( + self, response_list: list[dict], model: str, batch_data: list[dict] | None = None + ) -> EmbeddingResponse: + vectors: Final = tuple( + vector for response in INVOKE_RESPONSES.validate_python(response_list) for vector in response.vectors() + ) + embeddings: Final = [ + Embedding(embedding=list(vector), index=index, object="embedding") for index, vector in enumerate(vectors) + ] + return EmbeddingResponse(data=embeddings, model=model, usage=_billed_usage(batch_data)) def _transform_async_invoke_response(self, response: dict, model: str) -> EmbeddingResponse: """ diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index cc75354a495..7784ed2a6ac 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -650,7 +650,10 @@ }, "twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, "max_tokens": 77, @@ -662,7 +665,7 @@ }, "us.twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -677,7 +680,7 @@ }, "eu.twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -691,7 +694,10 @@ "supports_image_input": true }, "twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, "max_tokens": 500, @@ -702,7 +708,7 @@ "supports_image_input": true }, "us.twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -716,7 +722,7 @@ "supports_image_input": true }, "eu.twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 9b5fb08a45f..c55eb6831c7 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -272,7 +272,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False): input_cost_per_token_above_272k_tokens_flex: float | None input_cost_per_token_above_512k_tokens: float | None # MiniMax-M3: prompts >512K priced at 2x input input_cost_per_character_above_128k_tokens: float | None # only for vertex ai models - input_cost_per_query: float | None # only for rerank models + input_cost_per_query: float | None # per-request pricing: rerank, search, and Bedrock Marengo embeddings input_cost_per_image: float | None # only for vertex ai models input_cost_per_image_token: float | None # for gpt-image-1 and similar models input_cost_per_video_token: float | None # for gemini omni models with video input @@ -1693,6 +1693,9 @@ class PromptTokensDetailsWrapper( audio_length_seconds: float | None = None """Length of audio sent to the model. Used for multimodal embeddings priced per audio-second.""" + query_count: int | None = None + """Number of billable requests sent to the model. Used for embeddings priced per request, such as Bedrock Marengo.""" + cache_write_tokens: int | None = None """Number of cache write (creation) tokens sent to the model. OpenAI naming (prompt_tokens_details.cache_write_tokens); this is the canonical field.""" @@ -1734,6 +1737,8 @@ class PromptTokensDetailsWrapper( del self.video_length_seconds if self.audio_length_seconds is None: del self.audio_length_seconds + if self.query_count is None: + del self.query_count if self.web_search_requests is None: del self.web_search_requests if self.google_maps_grounding_requests is None: diff --git a/litellm/utils.py b/litellm/utils.py index b98aa821ff3..2ed1ad84e9c 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6033,7 +6033,7 @@ def get_model_info( input_cost_per_character_above_128k_tokens: Optional[ float ] # only for vertex ai models - input_cost_per_query: Optional[float] # only for rerank models + input_cost_per_query: Optional[float] # per-request pricing: rerank, search, and Bedrock Marengo embeddings input_cost_per_image: Optional[float] # only for vertex ai models input_cost_per_audio_token: Optional[float] input_cost_per_audio_per_second: Optional[float] # only for vertex ai models diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index cc75354a495..7784ed2a6ac 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -650,7 +650,10 @@ }, "twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 77, "max_tokens": 77, @@ -662,7 +665,7 @@ }, "us.twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -677,7 +680,7 @@ }, "eu.twelvelabs.marengo-embed-2-7-v1:0": { "deprecation_date": "2026-11-30", - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -691,7 +694,10 @@ "supports_image_input": true }, "twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, + "input_cost_per_video_per_second": 0.0007, + "input_cost_per_audio_per_second": 0.00014, + "input_cost_per_image": 0.0001, "litellm_provider": "bedrock", "max_input_tokens": 500, "max_tokens": 500, @@ -702,7 +708,7 @@ "supports_image_input": true }, "us.twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, @@ -716,7 +722,7 @@ "supports_image_input": true }, "eu.twelvelabs.marengo-embed-3-0-v1:0": { - "input_cost_per_token": 7e-05, + "input_cost_per_query": 7e-05, "input_cost_per_video_per_second": 0.0007, "input_cost_per_audio_per_second": 0.00014, "input_cost_per_image": 0.0001, diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index 59f0938e338..65a6dd2a4ca 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -2658,6 +2658,7 @@ def test_cache_writing_cost_with_zero_creation_tokens_and_ephemeral_details(): "image_count": 0, "video_length_seconds": 0.0, "audio_length_seconds": 0.0, + "query_count": 0, } model_info: ModelInfo = {} @@ -3239,6 +3240,37 @@ def test_image_count_prevents_text_tokens_fallback(_local_model_cost_map): assert completion_cost == 0.0 +def test_query_count_bills_input_cost_per_query(_local_model_cost_map): + usage = Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + prompt_tokens_details=PromptTokensDetailsWrapper(query_count=3, image_count=1), + ) + + prompt_cost, completion_cost = generic_cost_per_token( + model="us.twelvelabs.marengo-embed-3-0-v1:0", + usage=usage, + custom_llm_provider="bedrock", + ) + + assert prompt_cost == pytest.approx(3 * 7e-05 + 1e-04) + assert completion_cost == 0.0 + + +def test_query_count_is_free_without_a_per_query_price(_local_model_cost_map): + usage = Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + prompt_tokens_details=PromptTokensDetailsWrapper(query_count=1), + ) + + prompt_cost, _ = generic_cost_per_token(model="text-embedding-3-small", usage=usage, custom_llm_provider="openai") + + assert prompt_cost == 0.0 + + # --------------------------------------------------------------------------- # Data-residency (OpenAI regional processing) tests # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py index b37e991b0b2..c29a87cd0cf 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py +++ b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py @@ -5,6 +5,7 @@ from unittest.mock import Mock, patch import pytest import litellm +from litellm.llms.bedrock.embed.twelvelabs_marengo_transformation import TwelveLabsMarengoEmbeddingConfig from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler # Mock responses for different embedding models @@ -1066,17 +1067,19 @@ MARENGO_3_DUCK = "data:image/png;base64,ZHVjaw==" @pytest.mark.parametrize( - "model,kwargs,expected_body", + "model,kwargs,expected_body,expected_usage_details", [ ( "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", {"input_type": "text"}, {"inputType": "text", "text": {"inputText": "a duck on water"}}, + {"query_count": 1}, ), ( "bedrock/twelvelabs.marengo-embed-3-0-v1:0", {"input_type": "text"}, {"inputType": "text", "text": {"inputText": "a duck on water"}}, + {"query_count": 1}, ), ( "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", @@ -1085,6 +1088,7 @@ MARENGO_3_DUCK = "data:image/png;base64,ZHVjaw==" "inputType": "text_image", "text_image": {"inputText": "a duck on water", "mediaSource": {"base64String": "ZHVjaw=="}}, }, + {"query_count": 1, "image_count": 1}, ), ( "bedrock/us.twelvelabs.marengo-embed-3-0-v1:0", @@ -1096,10 +1100,13 @@ MARENGO_3_DUCK = "data:image/png;base64,ZHVjaw==" "mediaSources": [{"name": "bird", "mediaType": "image", "base64String": "ZHVjaw=="}], }, }, + {"query_count": 1, "image_count": 1}, ), ], ) -def test_marengo_3_embedding_sends_the_nested_payload_and_parses_512_dims(model, kwargs, expected_body): +def test_marengo_3_embedding_sends_the_nested_payload_and_parses_512_dims( + model, kwargs, expected_body, expected_usage_details +): client = HTTPHandler() with patch.object(client, "post") as mock_post: @@ -1122,7 +1129,9 @@ def test_marengo_3_embedding_sends_the_nested_payload_and_parses_512_dims(model, assert mock_post.call_args.kwargs["url"].endswith(f"/model/{model.removeprefix('bedrock/').replace(':', '%3A')}/invoke") assert len(response.data[0]["embedding"]) == 512 assert response.data[0]["embedding"][:2] == [0.0, 0.01] - assert response.usage.prompt_tokens == 128 + assert response.usage.prompt_tokens == 0 + assert response.usage.total_tokens == 0 + assert response.usage.prompt_tokens_details.model_dump(exclude_none=True) == expected_usage_details def test_marengo_3_image_embedding_sends_the_media_under_the_image_key(): @@ -1150,6 +1159,8 @@ def test_marengo_3_image_embedding_sends_the_media_under_the_image_key(): } assert len(response.data[0]["embedding"]) == 512 assert response.data[0]["embedding"][:2] == [0.0, 0.01] + assert response.usage.prompt_tokens == 0 + assert response.usage.prompt_tokens_details.model_dump(exclude_none=True) == {"image_count": 1} def test_marengo_2_7_embedding_keeps_the_flat_payload(): @@ -1177,6 +1188,36 @@ def test_marengo_2_7_embedding_keeps_the_flat_payload(): "textTruncate": "end", } assert response.data[0]["embedding"] == [0.1, 0.2, 0.3] + assert response.usage.prompt_tokens == 0 + assert response.usage.prompt_tokens_details.model_dump(exclude_none=True) == {"query_count": 1} + + +def test_marengo_usage_counts_text_requests_and_images_across_a_batch(): + duck = {"mediaType": "image", "base64String": "ZHVjaw=="} + response = TwelveLabsMarengoEmbeddingConfig()._transform_response( + response_list=[marengo_3_embedding_response, marengo_3_embedding_response, marengo_3_embedding_response], + model="us.twelvelabs.marengo-embed-3-0-v1:0", + batch_data=[ + {"inputType": "text", "text": {"inputText": "a duck"}}, + {"inputType": "image", "image": {"mediaSource": {"base64String": "ZHVjaw=="}}}, + {"inputType": "multi_input", "multi_input": {"mediaSources": [{"name": "a", **duck}, {"name": "b", **duck}]}}, + ], + ) + + assert [item["index"] for item in response.data] == [0, 1, 2] + assert response.usage.prompt_tokens == 0 + assert response.usage.total_tokens == 0 + assert response.usage.prompt_tokens_details.model_dump(exclude_none=True) == {"query_count": 1, "image_count": 3} + + +def test_marengo_usage_without_request_data_bills_nothing(): + response = TwelveLabsMarengoEmbeddingConfig()._transform_response( + response_list=[marengo_3_embedding_response], model="us.twelvelabs.marengo-embed-3-0-v1:0" + ) + + assert len(response.data[0]["embedding"]) == 512 + assert response.usage.prompt_tokens == 0 + assert response.usage.prompt_tokens_details is None def test_marengo_3_text_image_without_media_source_is_a_bad_request(): diff --git a/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py b/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py index 300dfeb5238..0bb99339435 100644 --- a/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py +++ b/tests/test_litellm/test_bedrock_marengo_embed_3_model_metadata.py @@ -6,7 +6,7 @@ import pytest import litellm from litellm.constants import bedrock_embedding_models from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider -from litellm.types.utils import Usage +from litellm.types.utils import PromptTokensDetailsWrapper, Usage REPO_ROOT = Path(__file__).parents[2] MAIN_PATH = REPO_ROOT / "model_prices_and_context_window.json" @@ -15,6 +15,12 @@ BACKUP_PATH = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.js BASE_MODEL = "twelvelabs.marengo-embed-3-0-v1:0" PROFILE_MODELS = ("us.twelvelabs.marengo-embed-3-0-v1:0", "eu.twelvelabs.marengo-embed-3-0-v1:0") ALL_MODELS = (BASE_MODEL, *PROFILE_MODELS) +MARENGO_2_7_MODELS = ( + "twelvelabs.marengo-embed-2-7-v1:0", + "us.twelvelabs.marengo-embed-2-7-v1:0", + "eu.twelvelabs.marengo-embed-2-7-v1:0", +) +PER_REQUEST_MODELS = (*ALL_MODELS, *MARENGO_2_7_MODELS) TEXT_REQUEST_COST = 7e-05 IMAGE_REQUEST_COST = 0.0001 @@ -34,7 +40,7 @@ def test_marengo_embed_3_specs(model): assert info["litellm_provider"] == "bedrock" assert info["mode"] == "embedding" - assert info["input_cost_per_token"] == TEXT_REQUEST_COST + assert info["input_cost_per_query"] == TEXT_REQUEST_COST assert info["output_cost_per_token"] == 0.0 assert info["max_input_tokens"] == 500 assert info["max_tokens"] == 500 @@ -48,9 +54,11 @@ def test_marengo_embed_3_specs(model): assert provider == "bedrock" -@pytest.mark.parametrize("model", PROFILE_MODELS) -def test_marengo_embed_3_inference_profiles_price_image_video_and_audio(model): +@pytest.mark.parametrize("model", PER_REQUEST_MODELS) +def test_marengo_prices_are_per_request_not_per_token(model): info = _load(MAIN_PATH)[model] + assert "input_cost_per_token" not in info + assert info["input_cost_per_query"] == TEXT_REQUEST_COST assert info["input_cost_per_image"] == IMAGE_REQUEST_COST assert info["input_cost_per_video_per_second"] == VIDEO_COST_PER_SECOND assert info["input_cost_per_audio_per_second"] == AUDIO_COST_PER_SECOND @@ -64,13 +72,34 @@ def test_marengo_embed_3_is_visible_to_callers(model, local_model_cost_map): assert info["max_input_tokens"] == 500 -@pytest.mark.parametrize("model", ALL_MODELS) -def test_marengo_embed_3_text_request_is_billed(model, local_model_cost_map): +@pytest.mark.parametrize("model", PER_REQUEST_MODELS) +@pytest.mark.parametrize( + "details,expected_cost", + [ + (PromptTokensDetailsWrapper(query_count=1), TEXT_REQUEST_COST), + (PromptTokensDetailsWrapper(image_count=1), IMAGE_REQUEST_COST), + (PromptTokensDetailsWrapper(query_count=1, image_count=1), TEXT_REQUEST_COST + IMAGE_REQUEST_COST), + (PromptTokensDetailsWrapper(query_count=1, image_count=2), TEXT_REQUEST_COST + 2 * IMAGE_REQUEST_COST), + (PromptTokensDetailsWrapper(video_length_seconds=10), 10 * VIDEO_COST_PER_SECOND), + (PromptTokensDetailsWrapper(audio_length_seconds=10), 10 * AUDIO_COST_PER_SECOND), + ], +) +def test_marengo_requests_are_billed_per_request(model, details, expected_cost, local_model_cost_map): + usage = Usage(prompt_tokens=0, completion_tokens=0, total_tokens=0, prompt_tokens_details=details) + prompt_cost, completion_cost = litellm.cost_per_token( + model=model, usage_object=usage, custom_llm_provider="bedrock" + ) + assert prompt_cost == pytest.approx(expected_cost) + assert completion_cost == 0.0 + + +@pytest.mark.parametrize("model", PER_REQUEST_MODELS) +def test_marengo_token_counts_bill_nothing(model, local_model_cost_map): usage = Usage(prompt_tokens=128, completion_tokens=0, total_tokens=128) prompt_cost, completion_cost = litellm.cost_per_token( model=model, usage_object=usage, custom_llm_provider="bedrock" ) - assert prompt_cost == pytest.approx(128 * TEXT_REQUEST_COST) + assert prompt_cost == 0.0 assert completion_cost == 0.0 @@ -78,7 +107,7 @@ def test_marengo_embed_3_is_a_known_bedrock_embedding_model(): assert BASE_MODEL in bedrock_embedding_models -@pytest.mark.parametrize("model", ALL_MODELS) +@pytest.mark.parametrize("model", PER_REQUEST_MODELS) def test_backup_matches_main(model): main_cost = _load(MAIN_PATH) backup_cost = _load(BACKUP_PATH) From bb52fd44fa425033313c7eac85bb5edaf92d71be Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:20:41 -0700 Subject: [PATCH 065/223] fix(cost_map): label the card's loaded_at as per-worker and cover the integrity-failure fallback --- .../test_get_model_cost_map.py | 20 +++++++++++++++++++ .../src/components/price_data_reload.test.tsx | 2 ++ .../src/components/price_data_reload.tsx | 9 +++++++++ 3 files changed, 31 insertions(+) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index d9fe6d2f979..7c3ad283639 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -684,3 +684,23 @@ def test_boot_load_fallback_to_the_backup_reports_its_blob_id_and_drops_the_remo assert source["source"] == "local" assert source["etag"] is None assert source["source_revision"] == _bundled_blob_id() + + +def test_boot_load_that_fails_the_integrity_check_reports_the_backup_not_the_rejected_fetch(): + """A fetch that succeeds but fails integrity validation is thrown away, so the provenance must + describe the backup that got loaded, never the ETag or bytes of the map that was rejected.""" + remote, _ = _mock_client( + [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_real_map_bytes())], client_cls=httpx.Client + ) + get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=remote) + shrunk_body = b'{"gpt-5.4-mini": {"mode": "chat", "input_cost_per_token": 1e-06, "output_cost_per_token": 2e-06}}' + shrunk, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"shrunk"'}, content=shrunk_body)], client_cls=httpx.Client) + + get_model_cost_map(url=_URL, sleep=_SyncSleepRecorder(), rng=random.Random(0), client=shrunk) + + source = get_model_cost_map_source_info() + assert source["source"] == "local" + assert source["fallback_reason"] == "Remote data failed integrity validation" + assert source["etag"] is None + assert source["source_revision"] == _bundled_blob_id() + assert source["source_revision"] != git_blob_id(shrunk_body) diff --git a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx index 101612993b0..3566ec2c3f2 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx @@ -68,6 +68,7 @@ describe("PriceDataReload", () => { expect(screen.getByText("ETag:")).toBeInTheDocument(); expect(screen.getByText('W/"eb8e9a53f4cc284b"')).toBeInTheDocument(); expect(screen.getByText("Loaded at:")).toBeInTheDocument(); + expect(screen.getByText(/worker that answered this request/)).toBeInTheDocument(); expect(screen.getByText(new Date(provenance.loaded_at).toLocaleString())).toBeInTheDocument(); }); @@ -91,6 +92,7 @@ describe("PriceDataReload", () => { expect(screen.queryByText("Source revision:")).not.toBeInTheDocument(); expect(screen.queryByText("ETag:")).not.toBeInTheDocument(); expect(screen.queryByText("Loaded at:")).not.toBeInTheDocument(); + expect(screen.queryByText(/worker that answered this request/)).not.toBeInTheDocument(); }); it("confirms an immediate reload and refreshes dependent data", async () => { diff --git a/ui/litellm-dashboard/src/components/price_data_reload.tsx b/ui/litellm-dashboard/src/components/price_data_reload.tsx index e5977a1b6e3..1363e306a31 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.tsx @@ -132,6 +132,15 @@ const CostMapProvenanceRows: React.FC<{ sourceInfo: CostMapSourceInfo }> = ({ so {formatDateTime(sourceInfo.loaded_at)}
)} + + {sourceInfo.loaded_at && ( +
+ + + Reported by the worker that answered this request. Other workers pick up a reload on their next poll + +
+ )} ); From 95402ccb711cdcd93f1c296579b18ec8bd32cab4 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:36:40 -0700 Subject: [PATCH 066/223] test(azure_ai): move the Foundry catalog metadata test into the mapped azure_ai directory The new metadata test sat at the top of tests/test_litellm. The azure_ai metadata tests live in tests/test_litellm/llms/azure_ai next to the cost calculator test, so this moves it there and bumps its repo-root lookup by the two extra directory levels. No test changes. --- .../azure_ai}/test_azure_ai_foundry_catalog_model_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/test_litellm/{ => llms/azure_ai}/test_azure_ai_foundry_catalog_model_metadata.py (99%) diff --git a/tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py similarity index 99% rename from tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py rename to tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py index 9c5ca26a89c..1b4e83438a6 100644 --- a/tests/test_litellm/test_azure_ai_foundry_catalog_model_metadata.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py @@ -8,7 +8,7 @@ from pydantic import TypeAdapter from litellm import cost_per_token, get_model_info from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider -REPO_ROOT: Final = Path(__file__).parents[2] +REPO_ROOT: Final = Path(__file__).parents[4] COST_MAP_ADAPTER: Final = TypeAdapter(dict[str, dict[str, object]]) AZURE_OPENAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/" FOUNDRY_AOAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/" From ba91588b15d308daf229f464b9aa89a0f7480afd Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 7 Sep 2026 18:38:12 -0700 Subject: [PATCH 067/223] fix(proxy): price one cost estimate at one moment The totals, the per-token-type lines and the reported rates each resolved off-peak pricing on their own clock read, so a quote taken as a window opened could bill on one side of the boundary and report rates from the other. /cost/estimate now pins a billing moment for the whole quote, and every rate lookup answers for the pinned moment instead of reading the clock again Claude-Session: https://claude.ai/code/session_011Tn3657NkV6ojLqewL64Kb --- litellm/_internal_context.py | 24 +++++++++++ .../litellm_core_utils/llm_cost_calc/utils.py | 9 ++-- .../cost_tracking_settings.py | 43 +++++++++++-------- .../llm_cost_calc/test_llm_cost_calc_utils.py | 42 ++++++++++++++++++ .../test_cost_tracking_settings.py | 30 +++++++++++++ 5 files changed, 126 insertions(+), 22 deletions(-) diff --git a/litellm/_internal_context.py b/litellm/_internal_context.py index f856fe0f2b3..8132008731f 100644 --- a/litellm/_internal_context.py +++ b/litellm/_internal_context.py @@ -6,9 +6,33 @@ be settable from user input. Context variables are scoped to the current asyncio task and cannot be injected via HTTP request bodies. """ +from collections.abc import Generator +from contextlib import contextmanager from contextvars import ContextVar +from datetime import datetime, timezone from typing import Final # When True, suppresses async logging and billing for internal sub-calls # (e.g., emulated file-search steps that make nested LLM calls). is_internal_call: Final[ContextVar[bool]] = ContextVar("is_internal_call", default=False) + +# One request prices its totals, its per-token-type lines and the rates it reports on +# separate code paths. Each reads the clock for off-peak pricing, so without a pinned +# moment they can land on either side of a window boundary and disagree with each other. +_billing_time: Final[ContextVar[datetime | None]] = ContextVar("billing_time", default=None) + + +@contextmanager +def pinned_billing_time(moment: datetime) -> Generator[None]: + """Price every rate lookup inside this block at ``moment`` rather than at each one's own clock read.""" + token: Final = _billing_time.set(moment) + try: + yield + finally: + _billing_time.reset(token) + + +def current_billing_time() -> datetime: + """The pinned billing moment, or now in UTC outside a pinned block.""" + pinned: Final = _billing_time.get() + return pinned if pinned is not None else datetime.now(timezone.utc) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 2bb15fb4c48..e2168528e6b 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -10,6 +10,7 @@ from typing import Any, Final, Literal, TypedDict, cast from zoneinfo import ZoneInfo, ZoneInfoNotFoundError import litellm +from litellm._internal_context import current_billing_time from litellm._logging import verbose_logger from litellm.litellm_core_utils.llm_cost_calc.tiered_pricing import ( select_tier_for_input, @@ -306,7 +307,7 @@ def _is_within_off_peak_window(off_peak_hours_utc: str | Sequence[str], current_ than being localised, so callers must pass datetime.now(timezone.utc), never datetime.now(), or every window shifts by the host's offset. """ - reference: Final = current_time if current_time is not None else datetime.now(timezone.utc) + reference: Final = current_time if current_time is not None else current_billing_time() now: Final = (reference.astimezone(timezone.utc) if reference.tzinfo is not None else reference).time() windows: Final = (off_peak_hours_utc,) if isinstance(off_peak_hours_utc, str) else off_peak_hours_utc for window in windows: @@ -393,7 +394,7 @@ def _is_off_peak(off_peak: Mapping[str, object], current_time: datetime | None = rules: the flat hours_utc windows, which apply every day, or any entry in windows, whose hours apply only on its weekdays. """ - reference: Final = current_time if current_time is not None else datetime.now(timezone.utc) + reference: Final = current_time if current_time is not None else current_billing_time() reference_utc: Final = ( reference.astimezone(timezone.utc) if reference.tzinfo is not None else reference.replace(tzinfo=timezone.utc) ) @@ -1187,7 +1188,7 @@ def generic_cost_per_token( usage.prompt_tokens - cache_hit - audio_tokens - cache_creation - image_tokens - video_tokens, 0 ) - billing_time: Final = current_time if current_time is not None else datetime.now(timezone.utc) + billing_time: Final = current_time if current_time is not None else current_billing_time() ( prompt_base_cost, completion_base_cost, @@ -1379,7 +1380,7 @@ def _cost_map_billed_rates( vertex_location: str | None, current_time: datetime | None, ) -> BilledTokenRates: - billing_time: Final = current_time if current_time is not None else datetime.now(timezone.utc) + billing_time: Final = current_time if current_time is not None else current_billing_time() ( prompt_base_cost, completion_base_cost, diff --git a/litellm/proxy/management_endpoints/cost_tracking_settings.py b/litellm/proxy/management_endpoints/cost_tracking_settings.py index 17d82fd17e3..1faa66584d5 100644 --- a/litellm/proxy/management_endpoints/cost_tracking_settings.py +++ b/litellm/proxy/management_endpoints/cost_tracking_settings.py @@ -18,6 +18,7 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel import litellm +from litellm._internal_context import current_billing_time, pinned_billing_time from litellm._logging import verbose_proxy_logger from litellm.cost_calculator import completion_cost from litellm.litellm_core_utils.llm_cost_calc.utils import get_billed_token_rates @@ -631,33 +632,39 @@ async def estimate_cost( function_id="cost-estimate", ) - # Use completion_cost which handles all the logic including margins/discounts - try: - cost_per_request: Final = completion_cost( - completion_response=mock_response, + # The totals, the per-token-type lines and the reported rates each resolve pricing on their + # own path. Pinning one moment keeps an off-peak window that opens mid-quote from splitting them. + billed_at: Final = current_billing_time() + with pinned_billing_time(billed_at): + # Use completion_cost which handles all the logic including margins/discounts + try: + cost_per_request: Final = completion_cost( + completion_response=mock_response, + model=resolved_model, + custom_llm_provider=resolved_provider, + custom_cost_per_token=resolved.custom_cost_per_token, + litellm_logging_obj=litellm_logging_obj, + ) + except Exception as e: + raise HTTPException( + status_code=404, + detail={ + "error": f"Could not calculate cost for model '{request.model}' (resolved to '{resolved_model}'): {e}" + }, + ) + + rates: Final = get_billed_token_rates( model=resolved_model, custom_llm_provider=resolved_provider, + usage=usage, custom_cost_per_token=resolved.custom_cost_per_token, - litellm_logging_obj=litellm_logging_obj, - ) - except Exception as e: - raise HTTPException( - status_code=404, - detail={ - "error": f"Could not calculate cost for model '{request.model}' (resolved to '{resolved_model}'): {e}" - }, + current_time=billed_at, ) per_request: Final = _cost_lines(cost_per_request, litellm_logging_obj.cost_breakdown) daily: Final = per_request.times(request.num_requests_per_day) monthly: Final = per_request.times(request.num_requests_per_month) - rates: Final = get_billed_token_rates( - model=resolved_model, - custom_llm_provider=resolved_provider, - usage=usage, - custom_cost_per_token=resolved.custom_cost_per_token, - ) model_info: Final = _lookup_model_info(resolved_model) mapped_provider: Final = model_info.get("litellm_provider") if model_info is not None else None custom_llm_provider: Final = mapped_provider if mapped_provider is not None else resolved_provider diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index 4de3c059e63..90178428018 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -1,9 +1,11 @@ import json +from datetime import datetime, timezone import pytest from fastapi.testclient import TestClient import litellm +from litellm._internal_context import pinned_billing_time from litellm.litellm_core_utils.llm_cost_calc.tool_call_cost_tracking import ( StandardBuiltInToolCostTracking, ) @@ -3981,6 +3983,46 @@ def test_billed_token_rates_follow_the_token_tier_the_breakdown_bills_at(monkeyp assert breakdown.reasoning_cost == pytest.approx(200 * rates.output_cost_per_reasoning_token) +def test_a_pinned_billing_time_prices_the_totals_and_the_reported_rates_at_one_moment(monkeypatch): + """Totals and reported rates resolve off-peak pricing on separate paths that each read the + clock, so a window opening between the two reads used to leave them describing one request + at two different prices. Pinned, both must answer for the pinned moment.""" + monkeypatch.setitem( + litellm.model_cost, + "off-peak-model", + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "off_peak_pricing": { + "hours_utc": "02:00-03:00", + "input_cost_per_token": 1e-6, + "output_cost_per_token": 5e-6, + }, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) + + with pinned_billing_time(datetime(2026, 1, 1, 2, 30, tzinfo=timezone.utc)): + off_peak_prompt_cost, off_peak_completion_cost = generic_cost_per_token( + model="off-peak-model", usage=usage, custom_llm_provider="openai" + ) + off_peak_rates = get_billed_token_rates(model="off-peak-model", custom_llm_provider="openai", usage=usage) + with pinned_billing_time(datetime(2026, 1, 1, 12, 30, tzinfo=timezone.utc)): + peak_prompt_cost, peak_completion_cost = generic_cost_per_token( + model="off-peak-model", usage=usage, custom_llm_provider="openai" + ) + peak_rates = get_billed_token_rates(model="off-peak-model", custom_llm_provider="openai", usage=usage) + + assert off_peak_rates.input_cost_per_token == pytest.approx(1e-6) + assert peak_rates.input_cost_per_token == pytest.approx(3e-6) + assert off_peak_prompt_cost == pytest.approx(1000 * off_peak_rates.input_cost_per_token) + assert off_peak_completion_cost == pytest.approx(500 * off_peak_rates.output_cost_per_token) + assert peak_prompt_cost == pytest.approx(1000 * peak_rates.input_cost_per_token) + assert peak_completion_cost == pytest.approx(500 * peak_rates.output_cost_per_token) + + def test_billed_token_rates_are_none_for_an_unpriced_model(): usage = Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15) diff --git a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py index 9847c4092df..e9485f3a044 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py +++ b/tests/test_litellm/proxy/management_endpoints/test_cost_tracking_settings.py @@ -4,6 +4,7 @@ Tests for cost tracking settings management endpoints. Tests the GET and PATCH endpoints for managing cost discount configuration. """ +from datetime import datetime, timezone from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -12,6 +13,7 @@ from pydantic import ValidationError import litellm +from litellm._internal_context import pinned_billing_time from litellm.proxy._types import CostEstimateRequest from litellm.proxy.management_endpoints.cost_tracking_settings import router from litellm.proxy.proxy_server import app @@ -1108,6 +1110,34 @@ class TestEstimateCostCacheAndReasoningTokens: ) assert response.output_cost_per_request == pytest.approx(1_000 * response.output_cost_per_token) + @pytest.mark.asyncio + async def test_a_quote_prices_its_totals_and_its_rates_at_the_same_moment(self, monkeypatch): + """The totals and the reported rates resolve off-peak pricing on separate paths. A quote + taken as a window opens must not bill on one side of it and report rates from the other.""" + monkeypatch.setitem( + litellm.model_cost, + A_MAPPED_MODEL, + { + "input_cost_per_token": 3e-6, + "output_cost_per_token": 15e-6, + "off_peak_pricing": { + "hours_utc": "02:00-03:00", + "input_cost_per_token": 1e-6, + "output_cost_per_token": 5e-6, + }, + "litellm_provider": "openai", + "mode": "chat", + }, + ) + + with pinned_billing_time(datetime(2026, 1, 1, 2, 30, tzinfo=timezone.utc)): + response = await _estimate(None, model=A_MAPPED_MODEL) + + assert response.input_cost_per_token == pytest.approx(1e-6) + assert response.output_cost_per_token == pytest.approx(5e-6) + assert response.input_cost_per_request == pytest.approx(INPUT_TOKENS * response.input_cost_per_token) + assert response.output_cost_per_request == pytest.approx(OUTPUT_TOKENS * response.output_cost_per_token) + class TestCostEstimateRequestTokenSubsets: def test_cache_tokens_beyond_the_input_tokens_are_rejected(self): From 80fea089b63e6b89e989f3a109b96a26bac90224 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:40:31 -0700 Subject: [PATCH 068/223] fix(bedrock): reject Marengo 2.7-only and misplaced media params on 3.0 unless drop_params Marengo 3.0 requests now get a 400 naming any textTruncate, lengthSec, useFixedLengthSec, or minClipSec parameter, and any video or audio option sent with a text, image, text_image, or multi_input request, instead of silently dropping them. drop_params (global, per deployment, or per request) drops them instead. Pydantic validation errors name the field and the reason, and the 3.0 marker is the exact "marengo-embed-3-" model id segment. --- litellm/llms/bedrock/embed/embedding.py | 3 +- .../twelvelabs_marengo_3_transformation.py | 44 +++++++-- .../twelvelabs_marengo_transformation.py | 46 ++++++---- ...est_twelvelabs_marengo_3_transformation.py | 90 +++++++++++++++++++ 4 files changed, 158 insertions(+), 25 deletions(-) diff --git a/litellm/llms/bedrock/embed/embedding.py b/litellm/llms/bedrock/embed/embedding.py index 69eb9b693b1..987c7cbf981 100644 --- a/litellm/llms/bedrock/embed/embedding.py +++ b/litellm/llms/bedrock/embed/embedding.py @@ -35,7 +35,7 @@ from .amazon_titan_multimodal_transformation import ( ) from .amazon_titan_v2_transformation import AmazonTitanV2Config from .cohere_transformation import BedrockCohereEmbeddingConfig -from .twelvelabs_marengo_transformation import TwelveLabsMarengoEmbeddingConfig +from .twelvelabs_marengo_transformation import TwelveLabsMarengoEmbeddingConfig, drop_params_enabled if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj @@ -480,6 +480,7 @@ class BedrockEmbedding(BaseAWSLLM): async_invoke_route=has_async_invoke, model_id=modelId, output_s3_uri=inference_params.get("output_s3_uri"), + drop_params=drop_params_enabled(litellm_params), ) batch_data.append(twelvelabs_request) elif provider == "nova": diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py index 0f61d37258f..f4f9cb03dab 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py @@ -35,7 +35,7 @@ from litellm.types.llms.bedrock import ( ) from litellm.utils import get_base64_str -MARENGO_3_MODEL_MARKER: Final = "marengo-embed-3" +MARENGO_3_MODEL_MARKER: Final = "marengo-embed-3-" S3_URI_PREFIX: Final = "s3://" TIMED_MEDIA_OPTION_FIELDS: Final = MappingProxyType( { @@ -48,6 +48,9 @@ TIMED_MEDIA_OPTION_FIELDS: Final = MappingProxyType( } ) TIMED_MEDIA_OPTIONS: Final = TypeAdapter(TwelveLabsMarengo3TimedMediaOptions) +TIMED_INPUT_TYPES: Final = frozenset({"video", "audio"}) +MARENGO_2_7_ONLY_PARAMS: Final = ("textTruncate", "lengthSec", "useFixedLengthSec", "minClipSec") +MARENGO_2_7_ONLY_FIELDS: Final = MappingProxyType({name: True for name in MARENGO_2_7_ONLY_PARAMS}) def is_marengo_3_model(model: str | None) -> bool: @@ -69,15 +72,23 @@ class Marengo3Params(BaseModel): embeddingType: tuple[TWELVELABS_MARENGO_3_EMBEDDING_TYPES, ...] | None = None embeddingScope: tuple[TWELVELABS_MARENGO_3_EMBEDDING_SCOPES, ...] | None = None inferenceId: str | None = None + textTruncate: object = None + lengthSec: object = None + useFixedLengthSec: object = None + minClipSec: object = None @property def resolved_input_type(self) -> TWELVELABS_MARENGO_3_INPUT_TYPES: return self.inputType or self.input_type or "text" def timed_media_options(self) -> TwelveLabsMarengo3TimedMediaOptions: - return TIMED_MEDIA_OPTIONS.validate_python( - self.model_dump(include=TIMED_MEDIA_OPTION_FIELDS, exclude_none=True) - ) + return TIMED_MEDIA_OPTIONS.validate_python(self.given_timed_media_options()) + + def given_timed_media_options(self) -> dict[str, object]: + return self.model_dump(include=TIMED_MEDIA_OPTION_FIELDS, exclude_none=True) + + def given_2_7_only_params(self) -> dict[str, object]: + return self.model_dump(include=MARENGO_2_7_ONLY_FIELDS, exclude_none=True) def _s3_location(uri: str, bucket_owner: str | None) -> TwelveLabsS3Location: @@ -113,11 +124,23 @@ def _timed_media_input(media: str, params: Marengo3Params) -> TwelveLabsMarengo3 return timed +def _describe(error: ValidationError) -> str: + return "; ".join( + f"{'.'.join(str(part) for part in problem['loc'])}: {problem['msg']}" for problem in error.errors() + ) + + def _validated_params(inference_params: Mapping[str, object]) -> Marengo3Params: try: return Marengo3Params.model_validate(inference_params) except ValidationError as error: - raise BedrockError(status_code=400, message=f"Invalid Marengo 3.0 parameters: {error}") from error + raise BedrockError(status_code=400, message=f"Invalid Marengo 3.0 parameters: {_describe(error)}") from error + + +def _reject_unless_dropped(given: Mapping[str, object], drop_params: bool, reason: str) -> None: + if not given or drop_params: + return + raise BedrockError(status_code=400, message=f"{reason} {', '.join(given)}; set drop_params to drop them") def _require(value: str | None, input_type: str, param_name: str) -> str: @@ -143,10 +166,19 @@ def _request_base(inference_id: str | None) -> TwelveLabsMarengo3RequestBase: return identified -def build_marengo_3_request(input: str, inference_params: Mapping[str, object]) -> TwelveLabsMarengo3EmbeddingRequest: +def build_marengo_3_request( + input: str, inference_params: Mapping[str, object], drop_params: bool = False +) -> TwelveLabsMarengo3EmbeddingRequest: params: Final = _validated_params(inference_params) base: Final = _request_base(params.inferenceId) input_type: Final = params.resolved_input_type + _reject_unless_dropped( + params.given_2_7_only_params(), drop_params, "Marengo 3.0 does not accept the Marengo 2.7 parameters" + ) + if input_type not in TIMED_INPUT_TYPES: + _reject_unless_dropped( + params.given_timed_media_options(), drop_params, f"Input type '{input_type}' does not accept" + ) match input_type: case "text": text_request: Final[TwelveLabsMarengo3TextRequest] = { diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py index ddf6dfbcc4d..35163ecf848 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py @@ -13,7 +13,9 @@ from typing import Final, cast from pydantic import BaseModel, ConfigDict, TypeAdapter from typing_extensions import assert_never +import litellm from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( + MARENGO_2_7_ONLY_PARAMS, build_marengo_3_request, is_marengo_3_model, ) @@ -99,6 +101,25 @@ def _billed_usage(batch_data: list[dict] | None) -> Usage: return Usage(prompt_tokens=0, completion_tokens=0, total_tokens=0, prompt_tokens_details=details) +MARENGO_SHARED_PARAMS: Final = ( + "encoding_format", + "embeddingOption", + "startSec", + "input_type", + "endSec", + "segmentation", + "embeddingType", + "embeddingScope", + "inferenceId", + "media_source", + "media_sources", +) + + +def drop_params_enabled(litellm_params: Mapping[str, object]) -> bool: + return litellm.drop_params is True or litellm_params.get("drop_params") is True + + class TwelveLabsMarengoEmbeddingConfig: """ Reference - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-marengo.html @@ -115,23 +136,9 @@ class TwelveLabsMarengoEmbeddingConfig: self.is_marengo_3: Final = is_marengo_3_model(model) def get_supported_openai_params(self) -> list[str]: - return [ - "encoding_format", - "textTruncate", - "embeddingOption", - "startSec", - "lengthSec", - "useFixedLengthSec", - "minClipSec", - "input_type", - "endSec", - "segmentation", - "embeddingType", - "embeddingScope", - "inferenceId", - "media_source", - "media_sources", - ] + if self.is_marengo_3: + return list(MARENGO_SHARED_PARAMS) + return [*MARENGO_SHARED_PARAMS, *MARENGO_2_7_ONLY_PARAMS] def map_openai_params(self, non_default_params: dict, optional_params: dict) -> dict: for k, v in non_default_params.items(): @@ -179,6 +186,7 @@ class TwelveLabsMarengoEmbeddingConfig: async_invoke_route: bool = False, model_id: str | None = None, output_s3_uri: str | None = None, + drop_params: bool = False, ) -> TwelveLabsMarengoEmbeddingRequest | TwelveLabsMarengo3EmbeddingRequest | TwelveLabsAsyncInvokeRequest: """ Transform OpenAI-style input to TwelveLabs Marengo format/async-invoke format. @@ -203,7 +211,9 @@ class TwelveLabsMarengoEmbeddingConfig: ) if self.is_marengo_3: - marengo_3_request: Final = build_marengo_3_request(input=input, inference_params=inference_params) + marengo_3_request: Final = build_marengo_3_request( + input=input, inference_params=inference_params, drop_params=drop_params + ) if async_invoke_route and model_id: return self._wrap_async_invoke_request( model_input=marengo_3_request, model_id=model_id, output_s3_uri=output_s3_uri diff --git a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py index 0bf86352a5e..5033256d089 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py +++ b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py @@ -2,13 +2,16 @@ import json import pytest +import litellm from litellm.llms.bedrock.common_utils import BedrockError from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( + MARENGO_2_7_ONLY_PARAMS, build_marengo_3_request, is_marengo_3_model, ) from litellm.llms.bedrock.embed.twelvelabs_marengo_transformation import ( TwelveLabsMarengoEmbeddingConfig, + drop_params_enabled, ) MARENGO_3_BASE = "twelvelabs.marengo-embed-3-0-v1:0" @@ -27,6 +30,7 @@ OUTPUT_S3_URI = "s3://out-bucket/marengo/" ("async_invoke/twelvelabs.marengo-embed-3-0-v1:0", True), (MARENGO_27_US, False), ("twelvelabs.marengo-embed-2-7-v1:0", False), + ("twelvelabs.marengo-embed-30-v1:0", False), (None, False), ], ) @@ -266,3 +270,89 @@ def test_marengo_3_only_params_are_forwarded_by_map_openai_params(): "embeddingScope": ["clip"], "inferenceId": "req-1", } + + +@pytest.mark.parametrize( + "params,problem", + [ + ( + {"input_type": "clip"}, + "input_type: Input should be 'text', 'image', 'video', 'audio', 'text_image' or 'multi_input'", + ), + ({"input_type": "video", "embeddingOption": "visual"}, "embeddingOption: Input should be a valid tuple"), + ( + {"input_type": "multi_input", "media_sources": ["not", "a", "mapping"]}, + "media_sources: Input should be a valid dictionary", + ), + ], +) +def test_invalid_marengo_3_params_name_the_field_and_the_reason(params, problem): + with pytest.raises(BedrockError) as excinfo: + build_marengo_3_request("s3://media/clip.mp4", params) + assert excinfo.value.message == f"Invalid Marengo 3.0 parameters: {problem}" + + +MARENGO_2_7_ONLY_VALUES = {"textTruncate": "end", "lengthSec": 5, "useFixedLengthSec": True, "minClipSec": 2} + + +@pytest.mark.parametrize("name", MARENGO_2_7_ONLY_PARAMS) +def test_marengo_2_7_only_params_are_rejected_on_3_0_unless_dropped(name): + params = {"input_type": "text", name: MARENGO_2_7_ONLY_VALUES[name]} + with pytest.raises(BedrockError) as excinfo: + build_marengo_3_request("hello", params) + assert excinfo.value.status_code == 400 + assert excinfo.value.message == ( + f"Marengo 3.0 does not accept the Marengo 2.7 parameters {name}; set drop_params to drop them" + ) + assert build_marengo_3_request("hello", params, drop_params=True) == { + "inputType": "text", + "text": {"inputText": "hello"}, + } + + +def test_marengo_2_7_only_params_are_advertised_only_for_2_7(): + marengo_3 = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_US).get_supported_openai_params() + marengo_27 = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_27_US).get_supported_openai_params() + assert set(MARENGO_2_7_ONLY_PARAMS).isdisjoint(marengo_3) + assert set(MARENGO_2_7_ONLY_PARAMS) <= set(marengo_27) + assert set(marengo_3) <= set(marengo_27) + + +def test_drop_params_comes_from_the_call_or_the_global(monkeypatch): + monkeypatch.setattr(litellm, "drop_params", False) + assert drop_params_enabled({}) is False + assert drop_params_enabled({"drop_params": True}) is True + monkeypatch.setattr(litellm, "drop_params", True) + assert drop_params_enabled({}) is True + + +def test_config_drops_marengo_2_7_only_params_only_when_asked(): + config = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_US) + with pytest.raises(BedrockError, match=r"Marengo 2\.7 parameters textTruncate"): + config._transform_request("hello", {"textTruncate": "end"}) + assert config._transform_request("hello", {"textTruncate": "end"}, drop_params=True) == { + "inputType": "text", + "text": {"inputText": "hello"}, + } + + +@pytest.mark.parametrize( + "params", + [ + {"input_type": "text"}, + {"input_type": "image"}, + {"input_type": "text_image", "media_source": DUCK_DATA_URL}, + {"input_type": "multi_input", "media_sources": {"bird": DUCK_DATA_URL}}, + ], +) +def test_timed_media_options_are_rejected_on_untimed_input_types_unless_dropped(params): + timed = {**params, "startSec": 0, "embeddingOption": ["visual"]} + with pytest.raises(BedrockError) as excinfo: + build_marengo_3_request(DUCK_DATA_URL, timed) + assert excinfo.value.status_code == 400 + assert excinfo.value.message == ( + f"Input type '{params['input_type']}' does not accept startSec, embeddingOption; set drop_params to drop them" + ) + assert build_marengo_3_request(DUCK_DATA_URL, timed, drop_params=True) == build_marengo_3_request( + DUCK_DATA_URL, params + ) From 9c980b96d6f32fb2b1568e1e26659e8a8912b37e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:44:04 -0700 Subject: [PATCH 069/223] fix(budget_reservation): exempt vertex and bedrock count-tokens routes from budget reservation --- litellm/proxy/spend_tracking/budget_reservation.py | 10 ++++++++-- .../proxy/spend_tracking/test_budget_reservation.py | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/spend_tracking/budget_reservation.py b/litellm/proxy/spend_tracking/budget_reservation.py index 31d7d236657..d985075464f 100644 --- a/litellm/proxy/spend_tracking/budget_reservation.py +++ b/litellm/proxy/spend_tracking/budget_reservation.py @@ -183,11 +183,17 @@ _UNBILLED_ROUTES: Final[frozenset[str]] = frozenset( "/openai/v1/responses/input_tokens", } ) -_UNBILLED_ROUTE_SUFFIXES: Final[tuple[str, ...]] = ("/v1/messages/count_tokens", ":countTokens") +_TOKEN_COUNTING_SEGMENTS: Final[frozenset[str]] = frozenset({"count_tokens", "count-tokens"}) +_TOKEN_COUNTING_ACTION: Final = "countTokens" + + +def _is_token_counting_route(route: str) -> bool: + resource, _, action = route.rsplit("/", 1)[-1].partition(":") + return resource in _TOKEN_COUNTING_SEGMENTS or action == _TOKEN_COUNTING_ACTION def _is_unbilled_route(route: str) -> bool: - return route in _UNBILLED_ROUTES or route.endswith(_UNBILLED_ROUTE_SUFFIXES) + return route in _UNBILLED_ROUTES or _is_token_counting_route(route) async def reserve_budget_for_request( diff --git a/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py b/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py index 5e88268c283..de6c7c2a40a 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py +++ b/tests/test_litellm/proxy/spend_tracking/test_budget_reservation.py @@ -17,6 +17,10 @@ TOKEN_COUNTING_ROUTES: Final = ( "/v1/messages/count_tokens", "/v1beta/models/gemini-3.8-flash:countTokens", "/models/gemini-3.8-flash:countTokens", + "/bedrock/v1/messages/count-tokens", + "/bedrock/model/us.anthropic.claude-sonnet-4-6/count-tokens", + "/vertex_ai/v1/projects/p/locations/us-east5/publishers/anthropic/models/count-tokens:rawPredict", + "/vertex-ai/v1/projects/p/locations/us-east5/publishers/anthropic/models/count-tokens:rawPredict", ) @@ -56,6 +60,11 @@ ANTHROPIC_MESSAGES: Final = [{"role": "user", "content": "hello!!!"}] COUNT_TOKENS_REQUESTS: Final[tuple[tuple[str, dict[str, object]], ...]] = ( ("/v1/messages/count_tokens", {"model": "claude-sonnet-5", "messages": ANTHROPIC_MESSAGES}), ("/v1beta/models/gemini-3.8-flash:countTokens", {"contents": [{"role": "user", "parts": [{"text": "hello!!!"}]}]}), + ( + "/vertex_ai/v1/projects/p/locations/us-east5/publishers/anthropic/models/count-tokens:rawPredict", + {"model": "claude-sonnet-5", "messages": ANTHROPIC_MESSAGES}, + ), + ("/bedrock/v1/messages/count-tokens", {"model": "claude-sonnet-5", "messages": ANTHROPIC_MESSAGES}), ) TINY_BUDGET_KEY_TOKEN: Final = "hashed-count-tokens-key" From a601c00afdc5ddda50cb7552aef0dc5f3d9dcf0c Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:47:25 -0700 Subject: [PATCH 070/223] fix(bedrock): pass litellm_params into the Bedrock embedding call so drop_params reaches Marengo 3.0 --- litellm/main.py | 2 +- ...est_twelvelabs_marengo_3_transformation.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/litellm/main.py b/litellm/main.py index 75b7f7f10a5..56f9cb2c0d0 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -6545,7 +6545,7 @@ def embedding( client=client, timeout=timeout, aembedding=aembedding, - litellm_params={}, + litellm_params=litellm_params_dict, api_base=api_base, print_verbose=print_verbose, extra_headers=headers, diff --git a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py index 5033256d089..d8d29cac35e 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py +++ b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py @@ -1,9 +1,11 @@ import json +from unittest.mock import Mock, patch import pytest import litellm from litellm.llms.bedrock.common_utils import BedrockError +from litellm.llms.custom_httpx.http_handler import HTTPHandler from litellm.llms.bedrock.embed.twelvelabs_marengo_3_transformation import ( MARENGO_2_7_ONLY_PARAMS, build_marengo_3_request, @@ -356,3 +358,35 @@ def test_timed_media_options_are_rejected_on_untimed_input_types_unless_dropped( assert build_marengo_3_request(DUCK_DATA_URL, timed, drop_params=True) == build_marengo_3_request( DUCK_DATA_URL, params ) + + +def _embed_marengo_3_us(client: HTTPHandler, **params: object): + return litellm.embedding( + model=f"bedrock/{MARENGO_3_US}", + input="hello", + client=client, + aws_region_name="us-east-1", + aws_bedrock_runtime_endpoint="https://bedrock-runtime.us-east-1.amazonaws.com", + api_key="test-bearer-token", + **params, + ) + + +def test_per_request_drop_params_reaches_the_marengo_3_builder(monkeypatch): + monkeypatch.setattr(litellm, "drop_params", False) + client = HTTPHandler() + with patch.object(client, "post") as mock_post: + mock_response = Mock() + mock_response.status_code = 200 + mock_response.text = json.dumps({"data": [{"embedding": [0.1, 0.2]}]}) + mock_response.json = lambda: json.loads(mock_response.text) + mock_post.return_value = mock_response + + with pytest.raises(litellm.BadRequestError, match=r"Marengo 2\.7 parameters textTruncate"): + _embed_marengo_3_us(client, textTruncate="end") + assert mock_post.call_count == 0 + + response = _embed_marengo_3_us(client, textTruncate="end", drop_params=True) + + assert response.data[0]["embedding"] == [0.1, 0.2] + assert json.loads(mock_post.call_args.kwargs["data"]) == {"inputType": "text", "text": {"inputText": "hello"}} From 6076e9f61103f9f8054b8dfaa00539e47fc9163f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:54:31 -0700 Subject: [PATCH 071/223] chore(cost_calc): drop the query count section label comment --- litellm/litellm_core_utils/llm_cost_calc/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 46574ebae3f..c05d4c29a5e 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -981,7 +981,6 @@ def _calculate_input_cost( prompt_tokens_details["audio_length_seconds"], ) - ### QUERY COUNT COST if prompt_tokens_details["query_count"]: prompt_cost += calculate_cost_component( model_info, "input_cost_per_query", prompt_tokens_details["query_count"] From b7c2decb7db01b54463d94112f7943032d9309da Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 19:00:47 -0700 Subject: [PATCH 072/223] fix(drop_params): honor string values in litellm_params and the LITELLM_DROP_PARAMS env var get_litellm_params normalizes drop_params once, so a client-body string and router_settings.default_litellm_params reach the anthropic, bedrock, and azure_ai gates as a bool. LITELLM_DROP_PARAMS=false now means off. A value that is neither a flag nor a string logs one warning and counts as unset, both in the deployment validator and in litellm_settings. --- litellm/__init__.py | 3 +- litellm/litellm_core_utils/core_helpers.py | 2 +- .../litellm_core_utils/get_litellm_params.py | 5 +-- litellm/proxy/proxy_server.py | 9 ++++- litellm/types/router.py | 7 +++- .../test_get_litellm_params.py | 8 +++++ .../proxy/proxy_server/test_proxy_config.py | 34 +++++++++++++++++++ .../test_litellm/test_drop_params_env_var.py | 17 ++++++++++ tests/test_litellm/types/test_router.py | 15 ++++++-- 9 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 tests/test_litellm/test_drop_params_env_var.py diff --git a/litellm/__init__.py b/litellm/__init__.py index 62477dd6264..36f143376ff 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -47,6 +47,7 @@ from typing import ( ) from litellm.types.integrations.datadog import DatadogInitParams from litellm.types.integrations.newrelic import NewRelicInitParams +from litellm.litellm_core_utils.core_helpers import normalize_drop_params from litellm._logging import ( set_verbose, _turn_on_debug, @@ -238,7 +239,7 @@ token: Optional[str] = ( ) telemetry = True max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults -drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False)) +drop_params = bool(normalize_drop_params(os.getenv("LITELLM_DROP_PARAMS"))) modify_params = bool(os.getenv("LITELLM_MODIFY_PARAMS", False)) use_chat_completions_url_for_anthropic_messages: bool = bool( os.getenv("LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES", False) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index bd7a1b8f384..c1f1076c710 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -42,7 +42,7 @@ _DROP_PARAMS_BOOL: Final = TypeAdapter(bool) def normalize_drop_params(value: object) -> bool | None: - if isinstance(value, bool): + if value is None or isinstance(value, bool): return value try: return _DROP_PARAMS_BOOL.validate_python(value.strip() if isinstance(value, str) else value) diff --git a/litellm/litellm_core_utils/get_litellm_params.py b/litellm/litellm_core_utils/get_litellm_params.py index 1fd79db15a6..92b32d32dc0 100644 --- a/litellm/litellm_core_utils/get_litellm_params.py +++ b/litellm/litellm_core_utils/get_litellm_params.py @@ -2,6 +2,7 @@ from collections.abc import Mapping, MutableMapping from types import MappingProxyType from typing import Final +from litellm.litellm_core_utils.core_helpers import normalize_drop_params from litellm.llms.openai.data_residency import infer_openai_data_residency AWS_CREDENTIAL_KWARGS_KEYS: Final = frozenset( @@ -113,7 +114,7 @@ def get_litellm_params( custom_prompt_dict: dict | None = None, litellm_metadata: dict | None = None, disable_add_transform_inline_image_block: bool | None = None, - drop_params: bool | None = None, + drop_params: bool | str | None = None, prompt_id: str | None = None, prompt_variables: dict | None = None, async_call: bool | None = None, @@ -175,7 +176,7 @@ def get_litellm_params( "custom_prompt_dict": custom_prompt_dict, "litellm_metadata": litellm_metadata, "disable_add_transform_inline_image_block": disable_add_transform_inline_image_block, - "drop_params": drop_params, + "drop_params": normalize_drop_params(drop_params), "prompt_id": prompt_id, "prompt_variables": prompt_variables, "async_call": async_call, diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 76acd414976..8a30274cc9f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5510,7 +5510,7 @@ class ProxyConfig: parse_budget_reset_time(value) setattr(litellm, key, value) elif key == "drop_params": - litellm.drop_params = bool(normalize_drop_params(value)) + litellm.drop_params = _drop_params_from_litellm_settings(value) else: verbose_proxy_logger.debug( "%s setting litellm.%s=%s%s", @@ -16915,6 +16915,13 @@ def _redact_config_param_value_for_logging(param_name: str | None, param_value: return param_value +def _drop_params_from_litellm_settings(value: object) -> bool: + normalized: Final = normalize_drop_params(value) + if normalized is None and value is not None: + verbose_proxy_logger.warning("litellm_settings.drop_params=%r is not a flag value, treating it as off", value) + return bool(normalized) + + def _redact_general_setting_value(field_name: str, value: JsonValue, is_full_admin: bool) -> JsonValue: if is_full_admin: return value diff --git a/litellm/types/router.py b/litellm/types/router.py index e8aec027a5e..5c9eab30f3d 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -12,6 +12,7 @@ import httpx from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from typing_extensions import Protocol, ReadOnly, Required, TypedDict, runtime_checkable +from litellm._logging import verbose_logger from litellm._uuid import uuid from litellm.litellm_core_utils.core_helpers import normalize_drop_params @@ -412,7 +413,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): normalized: Final = normalize_drop_params(value) if normalized is not None: return normalized - return value if isinstance(value, str) else None + if isinstance(value, str): + return value + if value is not None: + verbose_logger.warning("drop_params=%r is not a flag value, treating it as unset", value) + return None def __contains__(self, key) -> bool: # Define custom behavior for the 'in' operator diff --git a/tests/test_litellm/litellm_core_utils/test_get_litellm_params.py b/tests/test_litellm/litellm_core_utils/test_get_litellm_params.py index fb4cb494bee..f026ff57719 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_litellm_params.py +++ b/tests/test_litellm/litellm_core_utils/test_get_litellm_params.py @@ -215,3 +215,11 @@ class TestMetadataFallsBackToLitellmMetadata: assert result["metadata"] is not litellm_metadata result["metadata"].pop("trace_id") assert litellm_metadata == {"trace_id": "trace-1"} + + +@pytest.mark.parametrize( + "value, expected", + [("true", True), ("false", False), (" TRUE ", True), (True, True), (None, None), ("os.environ/DROP_PARAMS", None)], +) +def test_drop_params_strings_reach_litellm_params_as_flags(value, expected): + assert get_litellm_params(drop_params=value)["drop_params"] is expected diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index c57cd387d13..774c63d754a 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -9,6 +9,7 @@ Pins covered: from __future__ import annotations import json +import logging import os import re from types import SimpleNamespace @@ -2474,6 +2475,39 @@ async def test_ProxyConfig_load_config_turns_litellm_settings_drop_params_string assert litellm.drop_params is expected +@pytest.mark.asyncio +async def test_ProxyConfig_load_config_resolves_a_litellm_settings_drop_params_env_ref(tmp_path, monkeypatch): + f = tmp_path / "c.yaml" + f.write_text("model_list: []\nlitellm_settings:\n drop_params: os.environ/DROP_PARAMS_FROM_ENV\n") + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False) + monkeypatch.setenv("DROP_PARAMS_FROM_ENV", "true") + monkeypatch.setattr(litellm, "drop_params", False) + + await ProxyConfig().load_config(router=None, config_file_path=str(f)) + + assert litellm.drop_params is True + + +@pytest.mark.asyncio +async def test_ProxyConfig_load_config_warns_and_turns_off_a_non_flag_litellm_settings_drop_params( + tmp_path, monkeypatch, caplog +): + f = tmp_path / "c.yaml" + f.write_text("model_list: []\nlitellm_settings:\n drop_params: ture\n") + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None) + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False) + monkeypatch.setattr(litellm, "drop_params", True) + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + await ProxyConfig().load_config(router=None, config_file_path=str(f)) + + assert litellm.drop_params is False + assert "litellm_settings.drop_params='ture' is not a flag value, treating it as off" in caplog.text + + # --------------------------------------------------------------------------- # ProxyConfig.decrypt_model_list_from_db # --------------------------------------------------------------------------- diff --git a/tests/test_litellm/test_drop_params_env_var.py b/tests/test_litellm/test_drop_params_env_var.py new file mode 100644 index 00000000000..a1ef3648f95 --- /dev/null +++ b/tests/test_litellm/test_drop_params_env_var.py @@ -0,0 +1,17 @@ +import os +import subprocess +import sys + +import pytest + + +@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")]) +def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected): + result = subprocess.run( + [sys.executable, "-c", "import litellm; print(litellm.drop_params)"], + env={**os.environ, "LITELLM_DROP_PARAMS": configured}, + capture_output=True, + text=True, + check=True, + ) + assert result.stdout.strip() == expected diff --git a/tests/test_litellm/types/test_router.py b/tests/test_litellm/types/test_router.py index 47fc08167e3..fd933a9d993 100644 --- a/tests/test_litellm/types/test_router.py +++ b/tests/test_litellm/types/test_router.py @@ -1,3 +1,5 @@ +import logging + import pytest from litellm.types.router import ( @@ -109,5 +111,14 @@ def test_drop_params_coerces_flags_and_keeps_unresolved_strings(value, expected) @pytest.mark.parametrize("value", [2, 2.5, [], {}]) -def test_drop_params_ignores_non_flag_non_string_values(value): - assert GenericLiteLLMParams(drop_params=value).drop_params is None +def test_drop_params_ignores_non_flag_non_string_values_with_a_warning(value, caplog): + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + assert GenericLiteLLMParams(drop_params=value).drop_params is None + assert f"drop_params={value!r} is not a flag value" in caplog.text + + +@pytest.mark.parametrize("value", [True, "true", None, "os.environ/DROP_PARAMS", "v2:gcm:ciphertext-from-a-pre-fix-row"]) +def test_drop_params_flags_and_strings_log_nothing(value, caplog): + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + GenericLiteLLMParams(drop_params=value) + assert caplog.text == "" From 0c6d4c539942f5f5ac2a723735d020f6ad91444f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 19:06:30 -0700 Subject: [PATCH 073/223] feat(cost_map): say on the card that Last run is deployment-wide while provenance is per worker --- ui/litellm-dashboard/src/components/price_data_reload.test.tsx | 1 + ui/litellm-dashboard/src/components/price_data_reload.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx index 3566ec2c3f2..4828d557053 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.test.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.test.tsx @@ -69,6 +69,7 @@ describe("PriceDataReload", () => { expect(screen.getByText('W/"eb8e9a53f4cc284b"')).toBeInTheDocument(); expect(screen.getByText("Loaded at:")).toBeInTheDocument(); expect(screen.getByText(/worker that answered this request/)).toBeInTheDocument(); + expect(screen.getByText(/Last run time is the latest reload any worker recorded/)).toBeInTheDocument(); expect(screen.getByText(new Date(provenance.loaded_at).toLocaleString())).toBeInTheDocument(); }); diff --git a/ui/litellm-dashboard/src/components/price_data_reload.tsx b/ui/litellm-dashboard/src/components/price_data_reload.tsx index 1363e306a31..bd2fb6721e0 100644 --- a/ui/litellm-dashboard/src/components/price_data_reload.tsx +++ b/ui/litellm-dashboard/src/components/price_data_reload.tsx @@ -137,7 +137,8 @@ const CostMapProvenanceRows: React.FC<{ sourceInfo: CostMapSourceInfo }> = ({ so
- Reported by the worker that answered this request. Other workers pick up a reload on their next poll + Reported by the worker that answered this request. Other workers pick up a reload on their next poll, and the + Last run time is the latest reload any worker recorded
)} From 415bdbfd8f6ba9bd0422087cc34509bde962ce55 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:46:39 -0700 Subject: [PATCH 074/223] fix(azure_ai): charge the Model Router fee once and correct catalog limits The router fee was folded into azure_ai.cost_per_token and then added again by the additional_costs hook, so every routed request paid it twice. The hook now owns the fee, the entry named by the deployment supplies the price, and a response priced as the router entry itself is not charged again model-router, gpt-chat-latest and cohere-command-a carry the limits from the Foundry models page, and model-router and grok-4-20-* carry their retirement dates. The router tests now run at the completion_cost level with a Logging object, which is the path the proxy takes, and fail at the merge base --- litellm/cost_calculator.py | 9 +- litellm/llms/azure_ai/cost_calculator.py | 87 ++- ...odel_prices_and_context_window_backup.json | 11 +- model_prices_and_context_window.json | 11 +- .../azure_ai/test_azure_ai_cost_calculator.py | 561 ++++++------------ ...azure_ai_foundry_catalog_model_metadata.py | 30 +- 6 files changed, 246 insertions(+), 463 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 9a9d2ceda03..fc896a098b3 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -45,6 +45,9 @@ from litellm.llms.azure.cost_calculation import ( from litellm.llms.azure_ai.cost_calculator import ( cost_per_token as azure_ai_cost_per_token, ) +from litellm.llms.azure_ai.cost_calculator import ( + is_router_fee_entry as azure_ai_is_router_fee_entry, +) from litellm.llms.base_llm.search.transformation import SearchResponse from litellm.llms.bedrock.cost_calculation import ( cost_per_token as bedrock_cost_per_token, @@ -338,8 +341,6 @@ def cost_per_token( ### VERTEX LOCATION ### vertex_location: str | None = None, # for Vertex AI regional-endpoint uplift (e.g. "us-east5", "global") response: Any | None = None, - ### REQUEST MODEL ### - request_model: str | None = None, # original request model for router detection ) -> tuple[float, float]: """ Calculates the cost per token for a given model, prompt tokens, and completion tokens. @@ -661,7 +662,6 @@ def cost_per_token( model=model, usage=usage_block, response_time_ms=response_time_ms, - request_model=request_model, service_tier=service_tier, ) else: @@ -1659,11 +1659,10 @@ def completion_cost( data_residency=data_residency, vertex_location=vertex_location, response=completion_response, - request_model=request_model_for_cost, ) # Get additional costs from provider (e.g., routing fees, infrastructure costs) - if custom_llm_provider == "azure_ai": + if custom_llm_provider == "azure_ai" and not azure_ai_is_router_fee_entry(model): model_for_additional_costs = request_model_for_cost if completion_response is not None: hidden_params = getattr(completion_response, "_hidden_params", None) or {} diff --git a/litellm/llms/azure_ai/cost_calculator.py b/litellm/llms/azure_ai/cost_calculator.py index 141148f06e7..8a48860c0c4 100644 --- a/litellm/llms/azure_ai/cost_calculator.py +++ b/litellm/llms/azure_ai/cost_calculator.py @@ -31,6 +31,18 @@ def _is_azure_model_router(model: str) -> bool: return "model-router" in model_lower or "model_router" in model_lower or model_lower == "azure-model-router" +ROUTER_FEE_ENTRY_NAMES: Final = frozenset({"model-router", "model_router"}) + + +def is_router_fee_entry(model: str) -> bool: + return model.lower().removeprefix("azure_ai/") in ROUTER_FEE_ENTRY_NAMES + + +def _router_fee_entry_name(model: str) -> str: + entry_name: Final = model.lower().removeprefix("azure_ai/") + return entry_name if entry_name in ROUTER_FEE_ENTRY_NAMES else "model_router" + + def calculate_azure_model_router_flat_cost(model: str, prompt_tokens: int) -> float: """ Calculate the flat cost for Azure AI Foundry Model Router. @@ -44,26 +56,39 @@ def calculate_azure_model_router_flat_cost(model: str, prompt_tokens: int) -> fl """ if not _is_azure_model_router(model): return 0.0 - - # Get the model router pricing from model_prices_and_context_window.json - # Use "model_router" as the key (without actual model name suffix) - model_info: Final = get_model_info(model="model_router", custom_llm_provider="azure_ai") + model_info: Final = get_model_info(model=_router_fee_entry_name(model), custom_llm_provider="azure_ai") router_flat_cost_per_token: Final = model_info.get("input_cost_per_token", 0) - if router_flat_cost_per_token and router_flat_cost_per_token > 0: return prompt_tokens * router_flat_cost_per_token - return 0.0 -ROUTER_FEE_ENTRY_NAMES: Final = frozenset({"model-router", "model_router"}) +def cost_per_token( + model: str, + usage: Usage, + response_time_ms: float | None = 0.0, + service_tier: str | None = None, +) -> tuple[float, float]: + """ + Price the response model's own tokens for Azure AI. + The Azure AI Foundry Model Router fee is not part of this: completion_cost charges it once through + AzureModelRouterConfig.calculate_additional_costs as the "Azure Model Router Flat Cost" line of the cost + breakdown, and a response priced as the router entry itself already carries it. A router deployment name + that is missing from the cost map prices at zero here so that line item is the whole cost. -def _prices_router_fee_itself(model: str) -> bool: - return model.lower().rsplit("/", 1)[-1] in ROUTER_FEE_ENTRY_NAMES + Args: + model: str, the model name without provider prefix (from response) + usage: LiteLLM Usage block + response_time_ms: Optional response time in milliseconds + service_tier: Optional service tier the request was priced on + Returns: + Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd -def _base_cost_per_token(model: str, usage: Usage, service_tier: str | None) -> tuple[float, float] | None: + Raises: + ValueError: If a model that is not a Model Router name is missing from the cost map + """ try: return generic_cost_per_token( model=model, usage=usage, custom_llm_provider="azure_ai", service_tier=service_tier @@ -72,44 +97,6 @@ def _base_cost_per_token(model: str, usage: Usage, service_tier: str | None) -> if not _is_azure_model_router(model): raise verbose_logger.debug( - "Azure AI Model Router: model '%s' not in cost map, calculating routing flat cost only. Error: %s", model, e + "Azure AI Model Router: model '%s' not in cost map, only the routing fee applies. Error: %s", model, e ) - return None - - -def cost_per_token( - model: str, - usage: Usage, - response_time_ms: float | None = 0.0, - request_model: str | None = None, - service_tier: str | None = None, -) -> tuple[float, float]: - """ - Calculate the cost per token for Azure AI models. - - For Azure AI Foundry Model Router the routing fee (the azure_ai/model_router entry, $0.14 per - million input tokens) is added on top of the routed model's cost. When the response model is - the router entry itself, generic_cost_per_token has already charged that fee. - - Args: - model: str, the model name without provider prefix (from response) - usage: LiteLLM Usage block - response_time_ms: Optional response time in milliseconds - request_model: Optional[str], the original request model name (to detect router usage) - - Returns: - Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd - - Raises: - ValueError: If the model is not found in the cost map and cost cannot be calculated - (except for Model Router models where we return just the routing flat cost) - """ - is_router_request: Final = _is_azure_model_router(model) or ( - request_model is not None and _is_azure_model_router(request_model) - ) - base_cost: Final = _base_cost_per_token(model=model, usage=usage, service_tier=service_tier) - prompt_cost, completion_cost = base_cost if base_cost is not None else (0.0, 0.0) - if not is_router_request or (base_cost is not None and _prices_router_fee_itself(model)): - return prompt_cost, completion_cost - router_flat_cost: Final = calculate_azure_model_router_flat_cost(request_model or model, usage.prompt_tokens) - return prompt_cost + router_flat_cost, completion_cost + return 0.0, 0.0 diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 6649fa831d7..483ebd2431c 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3586,7 +3586,7 @@ "deprecation_date": "2026-12-02", "input_cost_per_token": 5e-06, "litellm_provider": "azure_ai", - "max_input_tokens": 200000, + "max_input_tokens": 272000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", @@ -4068,10 +4068,11 @@ "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" }, "azure_ai/model-router": { + "deprecation_date": "2027-05-20", "input_cost_per_token": 1.4e-07, "output_cost_per_token": 0, "litellm_provider": "azure_ai", - "max_input_tokens": 1048576, + "max_input_tokens": 200000, "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", @@ -10393,8 +10394,8 @@ "input_cost_per_token": 2.5e-06, "litellm_provider": "azure_ai", "max_input_tokens": 131072, - "max_output_tokens": 4096, - "max_tokens": 4096, + "max_output_tokens": 8182, + "max_tokens": 8182, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/", @@ -10753,6 +10754,7 @@ "supports_web_search": true }, "azure_ai/grok-4-20-reasoning": { + "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", "max_input_tokens": 262000, @@ -10769,6 +10771,7 @@ "supports_reasoning": true }, "azure_ai/grok-4-20-non-reasoning": { + "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", "max_input_tokens": 262000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 6649fa831d7..483ebd2431c 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3586,7 +3586,7 @@ "deprecation_date": "2026-12-02", "input_cost_per_token": 5e-06, "litellm_provider": "azure_ai", - "max_input_tokens": 200000, + "max_input_tokens": 272000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", @@ -4068,10 +4068,11 @@ "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" }, "azure_ai/model-router": { + "deprecation_date": "2027-05-20", "input_cost_per_token": 1.4e-07, "output_cost_per_token": 0, "litellm_provider": "azure_ai", - "max_input_tokens": 1048576, + "max_input_tokens": 200000, "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", @@ -10393,8 +10394,8 @@ "input_cost_per_token": 2.5e-06, "litellm_provider": "azure_ai", "max_input_tokens": 131072, - "max_output_tokens": 4096, - "max_tokens": 4096, + "max_output_tokens": 8182, + "max_tokens": 8182, "mode": "chat", "output_cost_per_token": 1e-05, "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/", @@ -10753,6 +10754,7 @@ "supports_web_search": true }, "azure_ai/grok-4-20-reasoning": { + "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", "max_input_tokens": 262000, @@ -10769,6 +10771,7 @@ "supports_reasoning": true }, "azure_ai/grok-4-20-non-reasoning": { + "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", "max_input_tokens": 262000, diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py index 80cd99bd46b..20d0ec03a2a 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py @@ -2,20 +2,25 @@ Test Azure AI cost calculator, especially Model Router flat cost. """ +from datetime import datetime +from typing import Final + import pytest +import litellm +from litellm.cost_calculator import completion_cost +from litellm.litellm_core_utils.litellm_logging import Logging from litellm.llms.azure_ai.cost_calculator import ( _is_azure_model_router, + calculate_azure_model_router_flat_cost, cost_per_token, ) -from litellm.types.utils import Usage +from litellm.types.utils import Choices, Message, ModelResponse, Usage from litellm.utils import get_model_info # Get the flat cost from model_prices_and_context_window.json _model_info = get_model_info(model="model_router", custom_llm_provider="azure_ai") -AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS = ( - _model_info.get("input_cost_per_token", 0) * 1_000_000 -) +AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS = _model_info.get("input_cost_per_token", 0) * 1_000_000 class TestAzureModelRouterDetection: @@ -80,377 +85,172 @@ class TestAzureModelRouterPrefix: assert result == expected +ROUTER_FEE_PER_TOKEN: Final = AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS / 1_000_000 +ROUTED_MODEL: Final = "gpt-4.1-nano-2025-04-14" +ROUTED_USAGE: Final = Usage(prompt_tokens=5000, completion_tokens=2000, total_tokens=7000) +ROUTED_FEE: Final = 5000 * ROUTER_FEE_PER_TOKEN + + +def _router_logging(request_model: str) -> Logging: + return Logging( + model=request_model, + messages=[{"role": "user", "content": "Hello"}], + stream=False, + call_type="completion", + start_time=datetime.now(), + litellm_call_id="test-123", + function_id="test-function", + ) + + +def _azure_ai_response(response_model: str, litellm_model_name: str | None = None) -> ModelResponse: + response: Final = ModelResponse( + id="test-123", + choices=[Choices(finish_reason="stop", index=0, message=Message(role="assistant", content="Hello"))], + created=1234567890, + model=response_model, + object="chat.completion", + usage=ROUTED_USAGE, + ) + response._hidden_params = ( + {"custom_llm_provider": "azure_ai"} + if litellm_model_name is None + else {"custom_llm_provider": "azure_ai", "litellm_model_name": litellm_model_name} + ) + return response + + +def _routed_model_cost() -> tuple[float, float]: + routed_info: Final = get_model_info(model=ROUTED_MODEL, custom_llm_provider="azure_ai") + return ( + ROUTED_USAGE.prompt_tokens * (routed_info["input_cost_per_token"] or 0.0), + ROUTED_USAGE.completion_tokens * (routed_info["output_cost_per_token"] or 0.0), + ) + + +@pytest.mark.usefixtures("local_model_cost_map") class TestAzureModelRouterFlatCost: - """Test Azure AI Foundry Model Router flat cost calculation.""" + """cost_per_token prices the response model only; the router fee is the cost breakdown's own line item.""" - def test_model_router_flat_cost_basic(self): - """Test that flat cost is added for Model Router requests.""" - model = "azure-model-router" - usage = Usage( - prompt_tokens=1000, - completion_tokens=500, - total_tokens=1500, + def test_unmapped_router_deployment_name_prices_at_zero(self) -> None: + usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) + assert cost_per_token(model="azure-model-router", usage=usage) == (0.0, 0.0) + + @pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) + def test_router_entry_prices_its_own_fee(self, router_entry_name: str) -> None: + usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) + prompt_cost, completion_cost_usd = cost_per_token(model=router_entry_name, usage=usage) + assert prompt_cost == pytest.approx(0.14, rel=1e-9) + assert completion_cost_usd == 0.0 + + def test_routed_model_is_priced_as_itself(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + prompt_cost, completion_cost_usd = cost_per_token(model=ROUTED_MODEL, usage=ROUTED_USAGE) + assert routed_prompt_cost > 0 + assert prompt_cost == pytest.approx(routed_prompt_cost, rel=1e-9) + assert completion_cost_usd == pytest.approx(routed_completion_cost, rel=1e-9) + + def test_unmapped_model_that_is_not_a_router_name_raises(self) -> None: + usage = Usage(prompt_tokens=10, completion_tokens=10, total_tokens=20) + with pytest.raises(Exception, match="no-such-azure-ai-model"): + cost_per_token(model="no-such-azure-ai-model", usage=usage) + + def test_flat_cost_helper(self) -> None: + assert calculate_azure_model_router_flat_cost( + model="azure-model-router", prompt_tokens=10_000 + ) == pytest.approx(0.0014, rel=1e-9) + assert calculate_azure_model_router_flat_cost(model="gpt-5-nano", prompt_tokens=10_000) == 0.0 + + def test_flat_cost_reads_the_fee_from_the_deployment_named_entry(self) -> None: + litellm.register_model( + {"azure_ai/model-router": {"input_cost_per_token": 2e-07, "litellm_provider": "azure_ai", "mode": "chat"}} ) - - prompt_cost, completion_cost = cost_per_token(model=model, usage=usage) - - # Calculate expected flat cost - expected_flat_cost = ( - usage.prompt_tokens - * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS - / 1_000_000 + litellm.get_model_info.cache_clear() + assert calculate_azure_model_router_flat_cost(model="model-router", prompt_tokens=1_000_000) == pytest.approx( + 0.2, rel=1e-9 ) - - # Flat cost should be $0.00014 (1000 tokens × $0.14 / 1M tokens) - assert expected_flat_cost == pytest.approx(0.00014, rel=1e-9) - - # Prompt cost should include the flat cost - # (plus any base cost from the actual model used, which might be 0 if not in model_cost) - assert prompt_cost >= expected_flat_cost - print( - f"Model Router flat cost for {usage.prompt_tokens} tokens: ${expected_flat_cost:.6f}" - ) - print(f"Total prompt cost: ${prompt_cost:.6f}") - - def test_model_router_flat_cost_large_request(self): - """Test flat cost calculation for larger requests.""" - model = "model-router" - usage = Usage( - prompt_tokens=100_000, - completion_tokens=50_000, - total_tokens=150_000, - ) - - prompt_cost, completion_cost = cost_per_token(model=model, usage=usage) - - # Calculate expected flat cost - expected_flat_cost = ( - usage.prompt_tokens - * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS - / 1_000_000 - ) - - # Flat cost should be $0.014 (100k tokens × $0.14 / 1M tokens) - assert expected_flat_cost == pytest.approx(0.014, rel=1e-9) - # Use approx for floating-point comparison - assert prompt_cost >= expected_flat_cost or prompt_cost == pytest.approx( - expected_flat_cost, rel=1e-9 - ) - print( - f"Model Router flat cost for {usage.prompt_tokens} tokens: ${expected_flat_cost:.6f}" - ) - print(f"Total prompt cost: ${prompt_cost:.6f}") - - def test_model_router_flat_cost_1m_tokens(self): - """Test flat cost for exactly 1 million input tokens.""" - model = "azure-model-router" - usage = Usage( - prompt_tokens=1_000_000, - completion_tokens=100_000, - total_tokens=1_100_000, - ) - - prompt_cost, completion_cost = cost_per_token(model=model, usage=usage) - - # Calculate expected flat cost - expected_flat_cost = AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS - - # Flat cost should be exactly $0.14 for 1M tokens - assert expected_flat_cost == pytest.approx(0.14, rel=1e-9) - assert prompt_cost >= expected_flat_cost - print(f"Model Router flat cost for 1M tokens: ${expected_flat_cost:.6f}") - print(f"Total prompt cost: ${prompt_cost:.6f}") - - def test_non_model_router_no_flat_cost(self): - """Test that non-Model Router models don't get the flat cost.""" - model = "gpt-4o" - usage = Usage( - prompt_tokens=1000, - completion_tokens=500, - total_tokens=1500, - ) - - prompt_cost, completion_cost = cost_per_token(model=model, usage=usage) - - # No flat cost should be added for non-Model Router models - # The cost might be 0 or based on the model's pricing - print(f"Non-Model Router prompt cost: ${prompt_cost:.6f}") - # We just ensure it doesn't crash and returns valid values - assert prompt_cost >= 0 - assert completion_cost >= 0 - - def test_model_router_with_cached_tokens(self): - """Test Model Router flat cost with cached tokens.""" - model = "azure-model-router" - usage = Usage( - prompt_tokens=2000, - completion_tokens=800, - total_tokens=2800, - cache_read_input_tokens=500, - cache_creation_input_tokens=200, - ) - - prompt_cost, completion_cost = cost_per_token(model=model, usage=usage) - - # Flat cost is based on ALL prompt tokens (including cached) - expected_flat_cost = ( - usage.prompt_tokens - * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS - / 1_000_000 - ) - - assert expected_flat_cost == pytest.approx(0.00028, rel=1e-9) - assert prompt_cost >= expected_flat_cost - print( - f"Model Router flat cost with caching for {usage.prompt_tokens} tokens: ${expected_flat_cost:.6f}" - ) - print(f"Total prompt cost: ${prompt_cost:.6f}") - - def test_router_flat_cost_when_response_has_actual_model(self): - """ - Test that router flat cost is added when request was via router but response - contains the actual model (e.g., gpt-5-nano). - - This is the key fix: Azure returns the actual model in the response, but we - must still add the router flat cost because the request was made via model router. - """ - usage = Usage( - prompt_tokens=10000, - completion_tokens=5000, - total_tokens=15000, - ) - - # Response model is the actual model Azure used (not a router name) - response_model = "gpt-5-nano-2025-08-07" - # Request model is the router - user called azure_ai/model_router/model-router - request_model = "azure_ai/model_router/model-router" - - prompt_cost, completion_cost = cost_per_token( - model=response_model, - usage=usage, - request_model=request_model, - ) - - # Expected: model cost (from gpt-5-nano) + router flat cost - expected_flat_cost = ( - usage.prompt_tokens - * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS - / 1_000_000 - ) - assert expected_flat_cost == pytest.approx(0.0014, rel=1e-9) - - # Total cost should be model cost + flat cost - total_cost = prompt_cost + completion_cost - assert total_cost >= expected_flat_cost - - # Prompt cost should include both model prompt cost and router flat cost - assert prompt_cost >= expected_flat_cost + assert calculate_azure_model_router_flat_cost( + model="azure-model-router", prompt_tokens=1_000_000 + ) == pytest.approx(0.14, rel=1e-9) +@pytest.mark.usefixtures("local_model_cost_map") class TestAzureModelRouterCostBreakdown: - """Test that Azure Model Router flat cost is tracked in cost breakdown.""" + """completion_cost charges the router fee exactly once, as the cost breakdown's additional cost line.""" - def test_flat_cost_calculation_helper(self): - """Test that flat cost can be calculated using the helper function.""" - from litellm.llms.azure_ai.cost_calculator import ( - calculate_azure_model_router_flat_cost, - ) - - model = "azure-model-router" - prompt_tokens = 10000 - - # Calculate flat cost using helper function - flat_cost = calculate_azure_model_router_flat_cost( - model=model, prompt_tokens=prompt_tokens - ) - - # Expected flat cost - expected_flat_cost = ( - prompt_tokens * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS / 1_000_000 - ) - - assert flat_cost > 0 - assert flat_cost == pytest.approx(expected_flat_cost, rel=1e-9) - print(f"Flat cost calculated: ${flat_cost:.6f}") - - def test_flat_cost_integration_with_completion_cost(self): - """Test that flat cost is properly integrated into completion_cost calculation.""" - import litellm - from litellm.cost_calculator import completion_cost - from litellm.types.utils import Choices, Message, ModelResponse, Usage - - # Create a mock response for azure_ai model router - response = ModelResponse( - id="test-123", - choices=[ - Choices( - finish_reason="stop", - index=0, - message=Message( - role="assistant", - content="Test response", - ), - ) - ], - created=1234567890, - model="azure-model-router", - object="chat.completion", - usage=Usage( - prompt_tokens=5000, - completion_tokens=2000, - total_tokens=7000, - ), - ) - - # Set hidden params for provider - response._hidden_params = {"custom_llm_provider": "azure_ai"} - - # Calculate cost + def test_unmapped_router_deployment_name_costs_only_the_fee(self) -> None: cost = completion_cost( - completion_response=response, + completion_response=_azure_ai_response("azure-model-router"), model="azure-model-router", custom_llm_provider="azure_ai", ) + assert cost == pytest.approx(ROUTED_FEE, rel=1e-9) - # Expected flat cost - expected_flat_cost = ( - 5000 * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS / 1_000_000 - ) - - # Cost should include the flat cost (use approx for floating-point comparison) - assert cost >= expected_flat_cost or cost == pytest.approx( - expected_flat_cost, rel=1e-9 - ) - print(f"Total cost with flat fee: ${cost:.6f}") - print(f"Expected minimum flat cost: ${expected_flat_cost:.6f}") - - def test_additional_costs_in_cost_breakdown(self): - """Test that Azure Model Router flat cost appears in additional_costs dict.""" - from datetime import datetime - - from litellm.cost_calculator import completion_cost - from litellm.litellm_core_utils.litellm_logging import Logging - from litellm.types.utils import Choices, Message, ModelResponse, Usage - - # Create logging object with required parameters - logging_obj = Logging( - model="azure-model-router", - messages=[{"role": "user", "content": "Hello"}], - stream=False, - call_type="completion", - start_time=datetime.now(), - litellm_call_id="test-123", - function_id="test-function", - ) - - # Create a mock response for azure_ai model router - response = ModelResponse( - id="test-123", - choices=[ - Choices( - finish_reason="stop", - index=0, - message=Message( - role="assistant", - content="Test response", - ), - ) - ], - created=1234567890, - model="azure-model-router", - object="chat.completion", - usage=Usage( - prompt_tokens=5000, - completion_tokens=2000, - total_tokens=7000, - ), - ) - - # Set hidden params for provider - response._hidden_params = {"custom_llm_provider": "azure_ai"} - - # Calculate cost with logging object + def test_fee_is_the_breakdown_line_item_for_an_unmapped_router_name(self) -> None: + logging_obj = _router_logging("azure-model-router") cost = completion_cost( - completion_response=response, + completion_response=_azure_ai_response("azure-model-router"), model="azure-model-router", custom_llm_provider="azure_ai", litellm_logging_obj=logging_obj, ) - - # Check that cost breakdown contains additional_costs - assert hasattr(logging_obj, "cost_breakdown") - assert logging_obj.cost_breakdown is not None - assert "additional_costs" in logging_obj.cost_breakdown - assert isinstance(logging_obj.cost_breakdown["additional_costs"], dict) - - # Check that the Azure Model Router flat cost is in additional_costs - additional_costs = logging_obj.cost_breakdown["additional_costs"] - assert "Azure Model Router Flat Cost" in additional_costs - - # Verify the flat cost value - expected_flat_cost = ( - 5000 * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS / 1_000_000 + breakdown = logging_obj.cost_breakdown + assert breakdown is not None + assert breakdown["input_cost"] == 0.0 + assert breakdown.get("additional_costs") == pytest.approx( + {"Azure Model Router Flat Cost": ROUTED_FEE}, rel=1e-9 ) - actual_flat_cost = additional_costs["Azure Model Router Flat Cost"] - assert actual_flat_cost == pytest.approx(expected_flat_cost, rel=1e-9) + assert cost == pytest.approx(ROUTED_FEE, rel=1e-9) - print(f"Additional costs in breakdown: {additional_costs}") - print(f"Azure Model Router Flat Cost: ${actual_flat_cost:.6f}") - - def test_additional_costs_when_response_has_actual_model_via_hidden_params(self): - """additional_costs populated when response has actual model but request was via model router (hidden_params).""" - from datetime import datetime - - from litellm.cost_calculator import completion_cost - from litellm.litellm_core_utils.litellm_logging import Logging - from litellm.types.utils import Choices, Message, ModelResponse, Usage - - logging_obj = Logging( - model="gpt-4.1-nano-2025-04-14", - messages=[{"role": "user", "content": "Hello"}], - stream=False, - call_type="completion", - start_time=datetime.now(), - litellm_call_id="test-123", - function_id="test-function", - ) - response = ModelResponse( - id="test-123", - choices=[ - Choices( - finish_reason="stop", - index=0, - message=Message(role="assistant", content="Hello"), - ) - ], - created=1234567890, - model="gpt-4.1-nano-2025-04-14", - object="chat.completion", - usage=Usage(prompt_tokens=5000, completion_tokens=2000, total_tokens=7000), - ) - response._hidden_params = { - "custom_llm_provider": "azure_ai", - "litellm_model_name": "azure_ai/model-router", - } + def test_router_request_with_routed_response_charges_the_fee_once(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + logging_obj = _router_logging("model-router") cost = completion_cost( - completion_response=response, - model="gpt-4.1-nano-2025-04-14", + completion_response=_azure_ai_response(ROUTED_MODEL), + model=ROUTED_MODEL, custom_llm_provider="azure_ai", litellm_logging_obj=logging_obj, ) - expected_flat_cost = ( - 5000 * AZURE_MODEL_ROUTER_FLAT_COST_PER_M_INPUT_TOKENS / 1_000_000 + breakdown = logging_obj.cost_breakdown + assert breakdown is not None + assert breakdown["input_cost"] == pytest.approx(routed_prompt_cost, rel=1e-9) + assert breakdown["output_cost"] == pytest.approx(routed_completion_cost, rel=1e-9) + assert breakdown.get("additional_costs") == pytest.approx( + {"Azure Model Router Flat Cost": ROUTED_FEE}, rel=1e-9 ) - assert cost >= expected_flat_cost - assert logging_obj.cost_breakdown is not None - assert "additional_costs" in logging_obj.cost_breakdown - assert ( - "Azure Model Router Flat Cost" - in logging_obj.cost_breakdown["additional_costs"] + assert cost == pytest.approx(routed_prompt_cost + routed_completion_cost + ROUTED_FEE, rel=1e-9) + + def test_routed_response_named_by_hidden_params_charges_the_fee_once(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + logging_obj = _router_logging(ROUTED_MODEL) + cost = completion_cost( + completion_response=_azure_ai_response(ROUTED_MODEL, litellm_model_name="azure_ai/model-router"), + model=ROUTED_MODEL, + custom_llm_provider="azure_ai", + litellm_logging_obj=logging_obj, ) - assert logging_obj.cost_breakdown["additional_costs"][ - "Azure Model Router Flat Cost" - ] == pytest.approx(expected_flat_cost, rel=1e-9) + breakdown = logging_obj.cost_breakdown + assert breakdown is not None + assert breakdown["input_cost"] == pytest.approx(routed_prompt_cost, rel=1e-9) + assert breakdown.get("additional_costs") == pytest.approx( + {"Azure Model Router Flat Cost": ROUTED_FEE}, rel=1e-9 + ) + assert cost == pytest.approx(routed_prompt_cost + routed_completion_cost + ROUTED_FEE, rel=1e-9) + + @pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) + def test_response_priced_as_the_router_entry_charges_the_fee_once(self, router_entry_name: str) -> None: + logging_obj = _router_logging(router_entry_name) + cost = completion_cost( + completion_response=_azure_ai_response(router_entry_name), + model=router_entry_name, + custom_llm_provider="azure_ai", + litellm_logging_obj=logging_obj, + ) + breakdown = logging_obj.cost_breakdown + assert breakdown is not None + assert "additional_costs" not in breakdown + assert breakdown["input_cost"] == pytest.approx(ROUTED_FEE, rel=1e-9) + assert cost == pytest.approx(ROUTED_FEE, rel=1e-9) class TestAzureAIServiceTierCostCalculation: @@ -459,26 +259,27 @@ class TestAzureAIServiceTierCostCalculation: @pytest.fixture(autouse=True) def register_test_model(self): import litellm - litellm.register_model(model_cost={ - "test-azure-ai-model": { - "input_cost_per_token": 0.001, - "output_cost_per_token": 0.002, - "input_cost_per_token_priority": 0.01, - "output_cost_per_token_priority": 0.02, - "input_cost_per_token_flex": 0.0005, - "output_cost_per_token_flex": 0.001, - "litellm_provider": "azure_ai", - "max_tokens": 8192, + + litellm.register_model( + model_cost={ + "test-azure-ai-model": { + "input_cost_per_token": 0.001, + "output_cost_per_token": 0.002, + "input_cost_per_token_priority": 0.01, + "output_cost_per_token_priority": 0.02, + "input_cost_per_token_flex": 0.0005, + "output_cost_per_token_flex": 0.001, + "litellm_provider": "azure_ai", + "max_tokens": 8192, + } } - }) + ) def test_service_tier_priority_higher_cost(self): """Priority tier should cost more than standard for azure_ai.""" usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) - standard_prompt, standard_completion = cost_per_token( - model="test-azure-ai-model", usage=usage - ) + standard_prompt, standard_completion = cost_per_token(model="test-azure-ai-model", usage=usage) priority_prompt, priority_completion = cost_per_token( model="test-azure-ai-model", usage=usage, service_tier="priority" ) @@ -490,12 +291,8 @@ class TestAzureAIServiceTierCostCalculation: """Flex tier should cost less than standard for azure_ai.""" usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) - standard_prompt, standard_completion = cost_per_token( - model="test-azure-ai-model", usage=usage - ) - flex_prompt, flex_completion = cost_per_token( - model="test-azure-ai-model", usage=usage, service_tier="flex" - ) + standard_prompt, standard_completion = cost_per_token(model="test-azure-ai-model", usage=usage) + flex_prompt, flex_completion = cost_per_token(model="test-azure-ai-model", usage=usage, service_tier="flex") assert flex_prompt < standard_prompt assert flex_completion < standard_completion @@ -528,29 +325,3 @@ def test_mai_thinking_1_model_info_and_cost(local_model_cost_map): assert model_info["supports_function_calling"] is True assert prompt_cost == pytest.approx(2.0) assert completion_cost == pytest.approx(8.0) - - -@pytest.mark.usefixtures("local_model_cost_map") -@pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) -def test_router_entry_as_response_model_charges_the_fee_once(router_entry_name: str) -> None: - usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) - prompt_cost, completion_cost = cost_per_token(model=router_entry_name, usage=usage) - assert prompt_cost == pytest.approx(0.14, rel=1e-9) - assert completion_cost == 0.0 - - -@pytest.mark.usefixtures("local_model_cost_map") -def test_unmapped_router_deployment_name_still_charges_the_fee() -> None: - usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) - prompt_cost, completion_cost = cost_per_token(model="azure-model-router", usage=usage) - assert prompt_cost == pytest.approx(0.14, rel=1e-9) - assert completion_cost == 0.0 - - -@pytest.mark.usefixtures("local_model_cost_map") -def test_routed_model_response_adds_the_fee_on_top() -> None: - usage = Usage(prompt_tokens=1_000_000, completion_tokens=0, total_tokens=1_000_000) - routed_prompt_cost, _ = cost_per_token(model="gpt-5-nano", usage=usage) - prompt_cost, _ = cost_per_token(model="gpt-5-nano", usage=usage, request_model="azure_ai/model-router") - assert routed_prompt_cost > 0 - assert prompt_cost == pytest.approx(routed_prompt_cost + 0.14, rel=1e-9) diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py index 1b4e83438a6..fab9be1b42c 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py @@ -26,6 +26,7 @@ class TokenPricedCatalogModel: max_input_tokens: int max_output_tokens: int cache_read_input_token_cost: float | None + deprecation_date: str | None supported_flags: tuple[str, ...] @@ -36,9 +37,10 @@ TOKEN_PRICED_MODELS: Final = ( source=AZURE_OPENAI_PRICING, input_cost_per_token=5e-06, output_cost_per_token=3e-05, - max_input_tokens=200000, + max_input_tokens=272000, max_output_tokens=128000, cache_read_input_token_cost=5e-07, + deprecation_date="2026-12-02", supported_flags=( "supports_function_calling", "supports_prompt_caching", @@ -58,7 +60,13 @@ TOKEN_PRICED_MODELS: Final = ( max_input_tokens=200000, max_output_tokens=100000, cache_read_input_token_cost=3.75e-07, - supported_flags=("supports_function_calling", "supports_prompt_caching", "supports_reasoning", "supports_vision"), + deprecation_date="2026-11-15", + supported_flags=( + "supports_function_calling", + "supports_prompt_caching", + "supports_reasoning", + "supports_vision", + ), ), TokenPricedCatalogModel( catalog_name="model-router", @@ -66,9 +74,10 @@ TOKEN_PRICED_MODELS: Final = ( source=FOUNDRY_AOAI_PRICING, input_cost_per_token=1.4e-07, output_cost_per_token=0.0, - max_input_tokens=1048576, + max_input_tokens=200000, max_output_tokens=32768, cache_read_input_token_cost=None, + deprecation_date="2027-05-20", supported_flags=(), ), TokenPricedCatalogModel( @@ -78,8 +87,9 @@ TOKEN_PRICED_MODELS: Final = ( input_cost_per_token=2.5e-06, output_cost_per_token=1e-05, max_input_tokens=131072, - max_output_tokens=4096, + max_output_tokens=8182, cache_read_input_token_cost=None, + deprecation_date=None, supported_flags=("supports_function_calling", "supports_tool_choice"), ), TokenPricedCatalogModel( @@ -91,6 +101,7 @@ TOKEN_PRICED_MODELS: Final = ( max_input_tokens=262000, max_output_tokens=8192, cache_read_input_token_cost=None, + deprecation_date="2027-04-06", supported_flags=( "supports_function_calling", "supports_reasoning", @@ -109,6 +120,7 @@ TOKEN_PRICED_MODELS: Final = ( max_input_tokens=262000, max_output_tokens=8192, cache_read_input_token_cost=None, + deprecation_date="2027-04-06", supported_flags=( "supports_function_calling", "supports_response_schema", @@ -146,7 +158,9 @@ def test_azure_ai_catalog_name_is_priced_and_routed(spec: TokenPricedCatalogMode @pytest.mark.usefixtures("local_model_cost_map") @pytest.mark.parametrize( - "spec", [spec for spec in TOKEN_PRICED_MODELS if spec.catalog_name != "model-router"], ids=lambda spec: spec.catalog_name + "spec", + [spec for spec in TOKEN_PRICED_MODELS if spec.catalog_name != "model-router"], + ids=lambda spec: spec.catalog_name, ) def test_azure_ai_catalog_name_costs_a_million_tokens_at_list_price(spec: TokenPricedCatalogModel) -> None: prompt_cost, completion_cost = cost_per_token( @@ -174,3 +188,9 @@ def test_azure_ai_catalog_entry_source_and_backup_match(catalog_name: str) -> No assert str(main_entry["source"]).startswith("https://azure.microsoft.com/en-us/pricing/details/") assert backup_entry == main_entry + + +@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) +def test_azure_ai_catalog_entry_carries_its_retirement_date(spec: TokenPricedCatalogModel) -> None: + entry = _cost_map_entry(REPO_ROOT / "model_prices_and_context_window.json", spec.catalog_name) + assert entry.get("deprecation_date") == spec.deprecation_date From d594b9385eb8eae760c809e43ac9174bceadfd46 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:05:01 -0700 Subject: [PATCH 075/223] fix(drop_params): warn when a deployment or env drop_params value is not a flag A deployment drop_params string that is not a flag value (a typo like ture) stayed silently off. The router now logs one warning per deployment. LITELLM_DROP_PARAMS and litellm_settings.drop_params share the same helper, so a non-flag value there warns as well instead of flipping silently from on to off --- litellm/__init__.py | 4 +-- litellm/litellm_core_utils/core_helpers.py | 8 +++++ litellm/proxy/proxy_server.py | 11 ++----- litellm/router.py | 6 ++++ .../litellm_core_utils/test_core_helpers.py | 17 ++++++++++ .../test_litellm/test_drop_params_env_var.py | 19 +++++++++-- tests/test_litellm/test_router.py | 33 +++++++++++++++++++ 7 files changed, 84 insertions(+), 14 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 36f143376ff..f7d4dce87d2 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -47,7 +47,7 @@ from typing import ( ) from litellm.types.integrations.datadog import DatadogInitParams from litellm.types.integrations.newrelic import NewRelicInitParams -from litellm.litellm_core_utils.core_helpers import normalize_drop_params +from litellm.litellm_core_utils.core_helpers import drop_params_flag from litellm._logging import ( set_verbose, _turn_on_debug, @@ -239,7 +239,7 @@ token: Optional[str] = ( ) telemetry = True max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults -drop_params = bool(normalize_drop_params(os.getenv("LITELLM_DROP_PARAMS"))) +drop_params = drop_params_flag(os.getenv("LITELLM_DROP_PARAMS"), "LITELLM_DROP_PARAMS", verbose_logger) modify_params = bool(os.getenv("LITELLM_MODIFY_PARAMS", False)) use_chat_completions_url_for_anthropic_messages: bool = bool( os.getenv("LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES", False) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index c1f1076c710..a3dfac81cc1 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -1,6 +1,7 @@ # What is this? ## Helper utilities import copy +import logging from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Any, Final, Literal @@ -50,6 +51,13 @@ def normalize_drop_params(value: object) -> bool | None: return None +def drop_params_flag(value: object, source: str, logger: logging.Logger) -> bool: + normalized: Final = normalize_drop_params(value) + if normalized is None and value is not None: + logger.warning("%s=%r is not a flag value, treating it as off", source, value) + return bool(normalized) + + def safe_divide( numerator: float, denominator: float, diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 8a30274cc9f..83b99822b4c 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -278,8 +278,8 @@ from litellm.litellm_core_utils.asyncify import asyncify from litellm.litellm_core_utils.audio_utils.utils import resolve_speech_media_type from litellm.litellm_core_utils.core_helpers import ( _get_parent_otel_span_from_kwargs, + drop_params_flag, get_litellm_metadata_from_kwargs, - normalize_drop_params, ) from litellm.litellm_core_utils.credential_accessor import CredentialAccessor from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj @@ -5510,7 +5510,7 @@ class ProxyConfig: parse_budget_reset_time(value) setattr(litellm, key, value) elif key == "drop_params": - litellm.drop_params = _drop_params_from_litellm_settings(value) + litellm.drop_params = drop_params_flag(value, "litellm_settings.drop_params", verbose_proxy_logger) else: verbose_proxy_logger.debug( "%s setting litellm.%s=%s%s", @@ -16915,13 +16915,6 @@ def _redact_config_param_value_for_logging(param_name: str | None, param_value: return param_value -def _drop_params_from_litellm_settings(value: object) -> bool: - normalized: Final = normalize_drop_params(value) - if normalized is None and value is not None: - verbose_proxy_logger.warning("litellm_settings.drop_params=%r is not a flag value, treating it as off", value) - return bool(normalized) - - def _redact_general_setting_value(field_name: str, value: JsonValue, is_full_admin: bool) -> JsonValue: if is_full_admin: return value diff --git a/litellm/router.py b/litellm/router.py index 95cabfad4bd..4bbdba08e13 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -9366,6 +9366,12 @@ class Router: #### VALIDATE MODEL ######## # Check if this is a prompt management model before validating as LLM provider litellm_model: Final = deployment.litellm_params.model + if isinstance(deployment.litellm_params.drop_params, str): + verbose_router_logger.warning( + "model=%s drop_params=%r is not a flag value, treating it as unset", + deployment.model_name, + deployment.litellm_params.drop_params, + ) is_prompt_management_model = False if "/" in litellm_model: diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index 8797f7c3591..e3880175759 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -1,9 +1,12 @@ """Tests for litellm_core_utils.core_helpers module.""" +import logging + import pytest from litellm.litellm_core_utils.core_helpers import ( _FINISH_REASON_MAP, + drop_params_flag, get_or_create_metadata_bucket, map_finish_reason, normalize_drop_params, @@ -284,6 +287,20 @@ def test_normalize_drop_params(value, expected): assert normalize_drop_params(value) is expected +@pytest.mark.parametrize("value, expected", [("true", True), ("off", False), (None, False)]) +def test_drop_params_flag_returns_a_bool_without_a_warning(value, expected, caplog): + with caplog.at_level(logging.WARNING, logger="drop-params-test"): + assert drop_params_flag(value, "LITELLM_DROP_PARAMS", logging.getLogger("drop-params-test")) is expected + assert caplog.text == "" + + +@pytest.mark.parametrize("value", ["temperature", "ture", 2]) +def test_drop_params_flag_treats_non_flag_values_as_off_with_a_warning(value, caplog): + with caplog.at_level(logging.WARNING, logger="drop-params-test"): + assert drop_params_flag(value, "LITELLM_DROP_PARAMS", logging.getLogger("drop-params-test")) is False + assert f"LITELLM_DROP_PARAMS={value!r} is not a flag value, treating it as off" in caplog.text + + class TestIsExpectedClientError: def test_status_ranges(self): from litellm.litellm_core_utils.core_helpers import is_expected_client_error diff --git a/tests/test_litellm/test_drop_params_env_var.py b/tests/test_litellm/test_drop_params_env_var.py index a1ef3648f95..339298df3d1 100644 --- a/tests/test_litellm/test_drop_params_env_var.py +++ b/tests/test_litellm/test_drop_params_env_var.py @@ -5,13 +5,26 @@ import sys import pytest -@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")]) -def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected): - result = subprocess.run( +def _import_litellm_with(configured: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( [sys.executable, "-c", "import litellm; print(litellm.drop_params)"], env={**os.environ, "LITELLM_DROP_PARAMS": configured}, capture_output=True, text=True, check=True, ) + + +@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")]) +def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected): + result = _import_litellm_with(configured) + assert result.stdout.strip() == expected + assert "is not a flag value" not in result.stderr + + +def test_litellm_drop_params_env_var_non_flag_value_is_off_with_a_warning(): + result = _import_litellm_with("temperature") + + assert result.stdout.strip() == "False" + assert "LITELLM_DROP_PARAMS='temperature' is not a flag value, treating it as off" in result.stderr diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index 201a4d84ebe..0c0bad8080d 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -14424,3 +14424,36 @@ async def test_router_deployment_drop_params_string_true_is_honored(monkeypatch) temperature=0.1, ) assert response.choices[0].message.content == "Hello, world!" + + +@pytest.mark.parametrize("value", ["ture", "enabled"]) +def test_router_warns_when_a_deployment_drop_params_string_is_not_a_flag(value, caplog): + with caplog.at_level(logging.WARNING, logger="LiteLLM Router"): + router = Router( + model_list=[ + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-nano", "api_key": "sk-fake", "drop_params": value}, + } + ] + ) + + deployment = router.get_deployment_by_model_group_name(model_group_name="gpt-5-nano") + assert deployment is not None + assert deployment.litellm_params.drop_params == value + assert f"model=gpt-5-nano drop_params={value!r} is not a flag value, treating it as unset" in caplog.text + + +@pytest.mark.parametrize("value", [True, "true", "off", None]) +def test_router_stays_quiet_when_a_deployment_drop_params_is_a_flag(value, caplog): + with caplog.at_level(logging.WARNING, logger="LiteLLM Router"): + Router( + model_list=[ + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-nano", "api_key": "sk-fake", "drop_params": value}, + } + ] + ) + + assert "is not a flag value" not in caplog.text From c02f2dc0feff1f95d61b1be699565a835402a122 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:10:54 -0700 Subject: [PATCH 076/223] fix(azure_ai): keep the request_model keyword on cost_per_token Restores the public keyword removed at 415bdbfd8f. A direct caller that names the Model Router as the request model gets the routing fee folded into the prompt cost once; completion_cost never passes it and charges the fee through the additional-costs hook as before --- litellm/cost_calculator.py | 3 + litellm/llms/azure_ai/cost_calculator.py | 62 +++++++++++-------- .../azure_ai/test_azure_ai_cost_calculator.py | 33 ++++++++++ 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index fc896a098b3..e135503d11d 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -341,6 +341,8 @@ def cost_per_token( ### VERTEX LOCATION ### vertex_location: str | None = None, # for Vertex AI regional-endpoint uplift (e.g. "us-east5", "global") response: Any | None = None, + ### REQUEST MODEL ### + request_model: str | None = None, # original request model for router detection ) -> tuple[float, float]: """ Calculates the cost per token for a given model, prompt tokens, and completion tokens. @@ -662,6 +664,7 @@ def cost_per_token( model=model, usage=usage_block, response_time_ms=response_time_ms, + request_model=request_model, service_tier=service_tier, ) else: diff --git a/litellm/llms/azure_ai/cost_calculator.py b/litellm/llms/azure_ai/cost_calculator.py index 8a48860c0c4..e57ba055587 100644 --- a/litellm/llms/azure_ai/cost_calculator.py +++ b/litellm/llms/azure_ai/cost_calculator.py @@ -63,32 +63,7 @@ def calculate_azure_model_router_flat_cost(model: str, prompt_tokens: int) -> fl return 0.0 -def cost_per_token( - model: str, - usage: Usage, - response_time_ms: float | None = 0.0, - service_tier: str | None = None, -) -> tuple[float, float]: - """ - Price the response model's own tokens for Azure AI. - - The Azure AI Foundry Model Router fee is not part of this: completion_cost charges it once through - AzureModelRouterConfig.calculate_additional_costs as the "Azure Model Router Flat Cost" line of the cost - breakdown, and a response priced as the router entry itself already carries it. A router deployment name - that is missing from the cost map prices at zero here so that line item is the whole cost. - - Args: - model: str, the model name without provider prefix (from response) - usage: LiteLLM Usage block - response_time_ms: Optional response time in milliseconds - service_tier: Optional service tier the request was priced on - - Returns: - Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd - - Raises: - ValueError: If a model that is not a Model Router name is missing from the cost map - """ +def _response_model_cost(model: str, usage: Usage, service_tier: str | None) -> tuple[float, float]: try: return generic_cost_per_token( model=model, usage=usage, custom_llm_provider="azure_ai", service_tier=service_tier @@ -100,3 +75,38 @@ def cost_per_token( "Azure AI Model Router: model '%s' not in cost map, only the routing fee applies. Error: %s", model, e ) return 0.0, 0.0 + + +def cost_per_token( + model: str, + usage: Usage, + response_time_ms: float | None = 0.0, + request_model: str | None = None, + service_tier: str | None = None, +) -> tuple[float, float]: + """ + Price the response model's own tokens for Azure AI, plus the Model Router fee when the caller names the + router as the request model. + + completion_cost never passes request_model: it charges the fee once through + AzureModelRouterConfig.calculate_additional_costs as the "Azure Model Router Flat Cost" line of the cost + breakdown. A response priced as the router entry itself already carries the fee, so request_model adds + nothing on top of it, and a router deployment name that is missing from the cost map prices at zero here. + + Args: + model: str, the model name without provider prefix (from response) + usage: LiteLLM Usage block + response_time_ms: Optional response time in milliseconds + request_model: Optional[str], the original request model name; a Model Router name adds the routing fee + service_tier: Optional service tier the request was priced on + + Returns: + Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd + + Raises: + ValueError: If a model that is not a Model Router name is missing from the cost map + """ + prompt_cost, completion_cost = _response_model_cost(model=model, usage=usage, service_tier=service_tier) + if request_model is None or not _is_azure_model_router(request_model) or is_router_fee_entry(model): + return prompt_cost, completion_cost + return prompt_cost + calculate_azure_model_router_flat_cost(request_model, usage.prompt_tokens), completion_cost diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py index 20d0ec03a2a..0deb79d14d3 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py @@ -155,6 +155,39 @@ class TestAzureModelRouterFlatCost: with pytest.raises(Exception, match="no-such-azure-ai-model"): cost_per_token(model="no-such-azure-ai-model", usage=usage) + def test_request_model_through_the_router_adds_the_fee_once(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + prompt_cost, completion_cost_usd = cost_per_token( + model=ROUTED_MODEL, usage=ROUTED_USAGE, request_model="azure_ai/model-router" + ) + assert prompt_cost == pytest.approx(routed_prompt_cost + ROUTED_FEE, rel=1e-9) + assert completion_cost_usd == pytest.approx(routed_completion_cost, rel=1e-9) + + def test_request_model_that_is_not_the_router_adds_nothing(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + assert cost_per_token( + model=ROUTED_MODEL, usage=ROUTED_USAGE, request_model=f"azure_ai/{ROUTED_MODEL}" + ) == pytest.approx((routed_prompt_cost, routed_completion_cost), rel=1e-9) + + @pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) + def test_request_model_does_not_double_the_router_entry(self, router_entry_name: str) -> None: + prompt_cost, completion_cost_usd = cost_per_token( + model=router_entry_name, usage=ROUTED_USAGE, request_model=f"azure_ai/{router_entry_name}" + ) + assert prompt_cost == pytest.approx(ROUTED_FEE, rel=1e-9) + assert completion_cost_usd == 0.0 + + def test_public_cost_per_token_keeps_the_request_model_keyword(self) -> None: + routed_prompt_cost, routed_completion_cost = _routed_model_cost() + prompt_cost, completion_cost_usd = litellm.cost_per_token( + model=ROUTED_MODEL, + custom_llm_provider="azure_ai", + usage_object=ROUTED_USAGE, + request_model="azure_ai/model-router", + ) + assert prompt_cost == pytest.approx(routed_prompt_cost + ROUTED_FEE, rel=1e-9) + assert completion_cost_usd == pytest.approx(routed_completion_cost, rel=1e-9) + def test_flat_cost_helper(self) -> None: assert calculate_azure_model_router_flat_cost( model="azure-model-router", prompt_tokens=10_000 From 55c10c1983c92dbad1d62dfc3c14aab94696639a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:34:34 -0700 Subject: [PATCH 077/223] fix(azure_ai): charge the router fee once for any router name and price grok-4-20 cache reads Direct litellm.cost_per_token callers that name a Model Router deployment as the model get the routing fee again, as they did before this branch, and the fee is still charged exactly once on every completion_cost path. The grok-4-20 entries bill cached prompt tokens at the input rate, since Azure has no cached-input meter for them, and the model_router twin carries the same limits and retirement date as model-router. The catalog test now exercises the cost calculator and map relations instead of pinning map fields. --- litellm/cost_calculator.py | 4 +- litellm/llms/azure_ai/cost_calculator.py | 33 ++- ...odel_prices_and_context_window_backup.json | 6 + model_prices_and_context_window.json | 6 + .../azure_ai/test_azure_ai_cost_calculator.py | 31 ++- ...azure_ai_foundry_catalog_model_metadata.py | 219 ++++++------------ 6 files changed, 125 insertions(+), 174 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index e135503d11d..8a00ffa4d37 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -46,7 +46,7 @@ from litellm.llms.azure_ai.cost_calculator import ( cost_per_token as azure_ai_cost_per_token, ) from litellm.llms.azure_ai.cost_calculator import ( - is_router_fee_entry as azure_ai_is_router_fee_entry, + is_azure_model_router as azure_ai_is_model_router_name, ) from litellm.llms.base_llm.search.transformation import SearchResponse from litellm.llms.bedrock.cost_calculation import ( @@ -1665,7 +1665,7 @@ def completion_cost( ) # Get additional costs from provider (e.g., routing fees, infrastructure costs) - if custom_llm_provider == "azure_ai" and not azure_ai_is_router_fee_entry(model): + if custom_llm_provider == "azure_ai" and not azure_ai_is_model_router_name(model): model_for_additional_costs = request_model_for_cost if completion_response is not None: hidden_params = getattr(completion_response, "_hidden_params", None) or {} diff --git a/litellm/llms/azure_ai/cost_calculator.py b/litellm/llms/azure_ai/cost_calculator.py index e57ba055587..5934525eca3 100644 --- a/litellm/llms/azure_ai/cost_calculator.py +++ b/litellm/llms/azure_ai/cost_calculator.py @@ -11,7 +11,7 @@ from litellm.types.utils import Usage from litellm.utils import get_model_info -def _is_azure_model_router(model: str) -> bool: +def is_azure_model_router(model: str) -> bool: """ Check if the model is Azure AI Foundry Model Router. @@ -54,7 +54,7 @@ def calculate_azure_model_router_flat_cost(model: str, prompt_tokens: int) -> fl Returns: float: The flat cost in USD, or 0.0 if not applicable """ - if not _is_azure_model_router(model): + if not is_azure_model_router(model): return 0.0 model_info: Final = get_model_info(model=_router_fee_entry_name(model), custom_llm_provider="azure_ai") router_flat_cost_per_token: Final = model_info.get("input_cost_per_token", 0) @@ -69,7 +69,7 @@ def _response_model_cost(model: str, usage: Usage, service_tier: str | None) -> model=model, usage=usage, custom_llm_provider="azure_ai", service_tier=service_tier ) except Exception as e: - if not _is_azure_model_router(model): + if not is_azure_model_router(model): raise verbose_logger.debug( "Azure AI Model Router: model '%s' not in cost map, only the routing fee applies. Error: %s", model, e @@ -77,6 +77,16 @@ def _response_model_cost(model: str, usage: Usage, service_tier: str | None) -> return 0.0, 0.0 +def _router_fee_name(model: str, request_model: str | None) -> str | None: + if is_router_fee_entry(model): + return None + if is_azure_model_router(model): + return model + if request_model is not None and is_azure_model_router(request_model): + return request_model + return None + + def cost_per_token( model: str, usage: Usage, @@ -85,13 +95,15 @@ def cost_per_token( service_tier: str | None = None, ) -> tuple[float, float]: """ - Price the response model's own tokens for Azure AI, plus the Model Router fee when the caller names the - router as the request model. + Price the response model's own tokens for Azure AI, plus the Model Router fee exactly once when either the + priced name or request_model is a Model Router name. - completion_cost never passes request_model: it charges the fee once through + A response priced as the router entry itself already carries the fee, so nothing is added on top of it. A + router deployment name that is missing from the cost map prices at the fee alone. + + completion_cost passes only the priced name: when that name is a routed model it adds the fee itself through AzureModelRouterConfig.calculate_additional_costs as the "Azure Model Router Flat Cost" line of the cost - breakdown. A response priced as the router entry itself already carries the fee, so request_model adds - nothing on top of it, and a router deployment name that is missing from the cost map prices at zero here. + breakdown, and when the name is router-shaped the fee is already in the prompt cost returned here. Args: model: str, the model name without provider prefix (from response) @@ -107,6 +119,7 @@ def cost_per_token( ValueError: If a model that is not a Model Router name is missing from the cost map """ prompt_cost, completion_cost = _response_model_cost(model=model, usage=usage, service_tier=service_tier) - if request_model is None or not _is_azure_model_router(request_model) or is_router_fee_entry(model): + fee_name: Final = _router_fee_name(model=model, request_model=request_model) + if fee_name is None: return prompt_cost, completion_cost - return prompt_cost + calculate_azure_model_router_flat_cost(request_model, usage.prompt_tokens), completion_cost + return prompt_cost + calculate_azure_model_router_flat_cost(fee_name, usage.prompt_tokens), completion_cost diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 483ebd2431c..674a8b98304 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -4060,9 +4060,13 @@ "supports_minimal_reasoning_effort": false }, "azure_ai/model_router": { + "deprecation_date": "2027-05-20", "input_cost_per_token": 1.4e-07, "output_cost_per_token": 0, "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" @@ -10754,6 +10758,7 @@ "supports_web_search": true }, "azure_ai/grok-4-20-reasoning": { + "cache_read_input_token_cost": 1.25e-06, "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", @@ -10771,6 +10776,7 @@ "supports_reasoning": true }, "azure_ai/grok-4-20-non-reasoning": { + "cache_read_input_token_cost": 1.25e-06, "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 483ebd2431c..674a8b98304 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -4060,9 +4060,13 @@ "supports_minimal_reasoning_effort": false }, "azure_ai/model_router": { + "deprecation_date": "2027-05-20", "input_cost_per_token": 1.4e-07, "output_cost_per_token": 0, "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 32768, + "max_tokens": 32768, "mode": "chat", "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/", "comment": "Flat cost of $0.14 per M input tokens for Azure AI Foundry Model Router infrastructure. Use pattern: azure_ai/model_router/ where deployment-name is your Azure deployment (e.g., azure-model-router)" @@ -10754,6 +10758,7 @@ "supports_web_search": true }, "azure_ai/grok-4-20-reasoning": { + "cache_read_input_token_cost": 1.25e-06, "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", @@ -10771,6 +10776,7 @@ "supports_reasoning": true }, "azure_ai/grok-4-20-non-reasoning": { + "cache_read_input_token_cost": 1.25e-06, "deprecation_date": "2027-04-06", "input_cost_per_token": 1.25e-06, "litellm_provider": "azure_ai", diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py index 0deb79d14d3..7df14b91741 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py @@ -11,9 +11,9 @@ import litellm from litellm.cost_calculator import completion_cost from litellm.litellm_core_utils.litellm_logging import Logging from litellm.llms.azure_ai.cost_calculator import ( - _is_azure_model_router, calculate_azure_model_router_flat_cost, cost_per_token, + is_azure_model_router, ) from litellm.types.utils import Choices, Message, ModelResponse, Usage from litellm.utils import get_model_info @@ -54,7 +54,7 @@ class TestAzureModelRouterDetection: ) def test_is_azure_model_router(self, model: str, expected: bool): """Test Azure Model Router detection.""" - assert _is_azure_model_router(model) == expected + assert is_azure_model_router(model) == expected class TestAzureModelRouterPrefix: @@ -130,11 +130,21 @@ def _routed_model_cost() -> tuple[float, float]: @pytest.mark.usefixtures("local_model_cost_map") class TestAzureModelRouterFlatCost: - """cost_per_token prices the response model only; the router fee is the cost breakdown's own line item.""" + """cost_per_token charges the router fee once, for whichever router name the caller gives it.""" - def test_unmapped_router_deployment_name_prices_at_zero(self) -> None: + def test_unmapped_router_deployment_name_prices_the_fee(self) -> None: usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) - assert cost_per_token(model="azure-model-router", usage=usage) == (0.0, 0.0) + prompt_cost, completion_cost_usd = cost_per_token(model="azure-model-router", usage=usage) + assert prompt_cost == pytest.approx(1000 * ROUTER_FEE_PER_TOKEN, rel=1e-9) + assert completion_cost_usd == 0.0 + + def test_router_deployment_name_as_both_names_charges_the_fee_once(self) -> None: + usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) + prompt_cost, completion_cost_usd = cost_per_token( + model="model_router/my-deployment", usage=usage, request_model="azure_ai/model_router/my-deployment" + ) + assert prompt_cost == pytest.approx(1000 * ROUTER_FEE_PER_TOKEN, rel=1e-9) + assert completion_cost_usd == 0.0 @pytest.mark.parametrize("router_entry_name", ["model_router", "model-router"]) def test_router_entry_prices_its_own_fee(self, router_entry_name: str) -> None: @@ -209,7 +219,8 @@ class TestAzureModelRouterFlatCost: @pytest.mark.usefixtures("local_model_cost_map") class TestAzureModelRouterCostBreakdown: - """completion_cost charges the router fee exactly once, as the cost breakdown's additional cost line.""" + """completion_cost charges the router fee exactly once: as the breakdown's additional cost line when a routed + model is priced as itself, inside the input cost when the priced name is the router.""" def test_unmapped_router_deployment_name_costs_only_the_fee(self) -> None: cost = completion_cost( @@ -219,7 +230,7 @@ class TestAzureModelRouterCostBreakdown: ) assert cost == pytest.approx(ROUTED_FEE, rel=1e-9) - def test_fee_is_the_breakdown_line_item_for_an_unmapped_router_name(self) -> None: + def test_unmapped_router_name_carries_the_fee_as_its_input_cost(self) -> None: logging_obj = _router_logging("azure-model-router") cost = completion_cost( completion_response=_azure_ai_response("azure-model-router"), @@ -229,10 +240,8 @@ class TestAzureModelRouterCostBreakdown: ) breakdown = logging_obj.cost_breakdown assert breakdown is not None - assert breakdown["input_cost"] == 0.0 - assert breakdown.get("additional_costs") == pytest.approx( - {"Azure Model Router Flat Cost": ROUTED_FEE}, rel=1e-9 - ) + assert breakdown["input_cost"] == pytest.approx(ROUTED_FEE, rel=1e-9) + assert "additional_costs" not in breakdown assert cost == pytest.approx(ROUTED_FEE, rel=1e-9) def test_router_request_with_routed_response_charges_the_fee_once(self) -> None: diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py index fab9be1b42c..19b082edd8a 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py @@ -5,131 +5,34 @@ from typing import Final import pytest from pydantic import TypeAdapter -from litellm import cost_per_token, get_model_info +from litellm import completion_cost, cost_per_token from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider +from litellm.types.utils import TranscriptionResponse REPO_ROOT: Final = Path(__file__).parents[4] +MAIN_COST_MAP: Final = REPO_ROOT / "model_prices_and_context_window.json" +BACKUP_COST_MAP: Final = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json" COST_MAP_ADAPTER: Final = TypeAdapter(dict[str, dict[str, object]]) -AZURE_OPENAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/" -FOUNDRY_AOAI_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/aoai/" -FOUNDRY_COHERE_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/cohere/" -FOUNDRY_GROK_PRICING: Final = "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/grok/" +AZURE_PRICING_PREFIX: Final = "https://azure.microsoft.com/en-us/pricing/details/" +A_MILLION: Final = 1_000_000 @dataclass(frozen=True, slots=True) class TokenPricedCatalogModel: catalog_name: str - mode: str - source: str - input_cost_per_token: float - output_cost_per_token: float - max_input_tokens: int - max_output_tokens: int - cache_read_input_token_cost: float | None - deprecation_date: str | None - supported_flags: tuple[str, ...] + dollars_per_million_input: float + dollars_per_million_output: float TOKEN_PRICED_MODELS: Final = ( - TokenPricedCatalogModel( - catalog_name="gpt-chat-latest", - mode="chat", - source=AZURE_OPENAI_PRICING, - input_cost_per_token=5e-06, - output_cost_per_token=3e-05, - max_input_tokens=272000, - max_output_tokens=128000, - cache_read_input_token_cost=5e-07, - deprecation_date="2026-12-02", - supported_flags=( - "supports_function_calling", - "supports_prompt_caching", - "supports_reasoning", - "supports_response_schema", - "supports_tool_choice", - "supports_vision", - "supports_web_search", - ), - ), - TokenPricedCatalogModel( - catalog_name="codex-mini", - mode="responses", - source=AZURE_OPENAI_PRICING, - input_cost_per_token=1.5e-06, - output_cost_per_token=6e-06, - max_input_tokens=200000, - max_output_tokens=100000, - cache_read_input_token_cost=3.75e-07, - deprecation_date="2026-11-15", - supported_flags=( - "supports_function_calling", - "supports_prompt_caching", - "supports_reasoning", - "supports_vision", - ), - ), - TokenPricedCatalogModel( - catalog_name="model-router", - mode="chat", - source=FOUNDRY_AOAI_PRICING, - input_cost_per_token=1.4e-07, - output_cost_per_token=0.0, - max_input_tokens=200000, - max_output_tokens=32768, - cache_read_input_token_cost=None, - deprecation_date="2027-05-20", - supported_flags=(), - ), - TokenPricedCatalogModel( - catalog_name="cohere-command-a", - mode="chat", - source=FOUNDRY_COHERE_PRICING, - input_cost_per_token=2.5e-06, - output_cost_per_token=1e-05, - max_input_tokens=131072, - max_output_tokens=8182, - cache_read_input_token_cost=None, - deprecation_date=None, - supported_flags=("supports_function_calling", "supports_tool_choice"), - ), - TokenPricedCatalogModel( - catalog_name="grok-4-20-reasoning", - mode="chat", - source=FOUNDRY_GROK_PRICING, - input_cost_per_token=1.25e-06, - output_cost_per_token=2.5e-06, - max_input_tokens=262000, - max_output_tokens=8192, - cache_read_input_token_cost=None, - deprecation_date="2027-04-06", - supported_flags=( - "supports_function_calling", - "supports_reasoning", - "supports_response_schema", - "supports_tool_choice", - "supports_vision", - "supports_web_search", - ), - ), - TokenPricedCatalogModel( - catalog_name="grok-4-20-non-reasoning", - mode="chat", - source=FOUNDRY_GROK_PRICING, - input_cost_per_token=1.25e-06, - output_cost_per_token=2.5e-06, - max_input_tokens=262000, - max_output_tokens=8192, - cache_read_input_token_cost=None, - deprecation_date="2027-04-06", - supported_flags=( - "supports_function_calling", - "supports_response_schema", - "supports_tool_choice", - "supports_vision", - "supports_web_search", - ), - ), + TokenPricedCatalogModel("gpt-chat-latest", 5.0, 30.0), + TokenPricedCatalogModel("codex-mini", 1.5, 6.0), + TokenPricedCatalogModel("model-router", 0.14, 0.0), + TokenPricedCatalogModel("cohere-command-a", 2.5, 10.0), + TokenPricedCatalogModel("grok-4-20-reasoning", 1.25, 2.5), + TokenPricedCatalogModel("grok-4-20-non-reasoning", 1.25, 2.5), ) +GROK_4_20_NAMES: Final = ("grok-4-20-reasoning", "grok-4-20-non-reasoning") CATALOG_NAMES: Final = tuple(spec.catalog_name for spec in TOKEN_PRICED_MODELS) + ("whisper",) @@ -137,60 +40,74 @@ def _cost_map_entry(path: Path, catalog_name: str) -> dict[str, object]: return COST_MAP_ADAPTER.validate_json(path.read_bytes())[f"azure_ai/{catalog_name}"] +@pytest.mark.parametrize("catalog_name", CATALOG_NAMES) +def test_azure_ai_catalog_name_routes_to_azure_ai(catalog_name: str) -> None: + routed_model, provider, _, _ = get_llm_provider(model=f"azure_ai/{catalog_name}") + assert (routed_model, provider) == (catalog_name, "azure_ai") + + @pytest.mark.usefixtures("local_model_cost_map") @pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) -def test_azure_ai_catalog_name_is_priced_and_routed(spec: TokenPricedCatalogModel) -> None: - routed_model, provider, _, _ = get_llm_provider(model=f"azure_ai/{spec.catalog_name}") - assert (routed_model, provider) == (spec.catalog_name, "azure_ai") - - info = get_model_info(model=routed_model, custom_llm_provider=provider) - assert info["litellm_provider"] == "azure_ai" - assert info["mode"] == spec.mode - assert info["input_cost_per_token"] == spec.input_cost_per_token - assert info["output_cost_per_token"] == spec.output_cost_per_token - assert info["cache_read_input_token_cost"] == spec.cache_read_input_token_cost - assert info["max_input_tokens"] == spec.max_input_tokens - assert info["max_output_tokens"] == spec.max_output_tokens - assert info["max_tokens"] == spec.max_output_tokens - for flag in spec.supported_flags: - assert info[flag] is True, flag +def test_azure_ai_catalog_name_costs_a_million_tokens_at_list_price(spec: TokenPricedCatalogModel) -> None: + prompt_cost, completion_cost_usd = cost_per_token( + model=f"azure_ai/{spec.catalog_name}", prompt_tokens=A_MILLION, completion_tokens=A_MILLION + ) + assert prompt_cost == pytest.approx(spec.dollars_per_million_input) + assert completion_cost_usd == pytest.approx(spec.dollars_per_million_output) @pytest.mark.usefixtures("local_model_cost_map") -@pytest.mark.parametrize( - "spec", - [spec for spec in TOKEN_PRICED_MODELS if spec.catalog_name != "model-router"], - ids=lambda spec: spec.catalog_name, -) -def test_azure_ai_catalog_name_costs_a_million_tokens_at_list_price(spec: TokenPricedCatalogModel) -> None: - prompt_cost, completion_cost = cost_per_token( - model=f"azure_ai/{spec.catalog_name}", prompt_tokens=1_000_000, completion_tokens=1_000_000 +@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) +def test_azure_ai_catalog_name_prices_the_same_in_any_casing(spec: TokenPricedCatalogModel) -> None: + lowercase_cost = cost_per_token(model=f"azure_ai/{spec.catalog_name}", prompt_tokens=A_MILLION, completion_tokens=0) + upper_cost = cost_per_token(model=f"azure_ai/{spec.catalog_name.upper()}", prompt_tokens=A_MILLION, completion_tokens=0) + assert upper_cost == lowercase_cost + + +@pytest.mark.usefixtures("local_model_cost_map") +@pytest.mark.parametrize("catalog_name", GROK_4_20_NAMES) +def test_azure_ai_grok_4_20_bills_cached_prompt_tokens_at_the_input_price(catalog_name: str) -> None: + uncached_prompt_cost, _ = cost_per_token(model=f"azure_ai/{catalog_name}", prompt_tokens=A_MILLION, completion_tokens=0) + cached_prompt_cost, _ = cost_per_token( + model=f"azure_ai/{catalog_name}", + prompt_tokens=A_MILLION, + completion_tokens=0, + cache_read_input_tokens=A_MILLION, ) - assert prompt_cost == pytest.approx(spec.input_cost_per_token * 1_000_000) - assert completion_cost == pytest.approx(spec.output_cost_per_token * 1_000_000) + assert uncached_prompt_cost > 0 + assert cached_prompt_cost == pytest.approx(uncached_prompt_cost) @pytest.mark.usefixtures("local_model_cost_map") def test_azure_ai_whisper_catalog_name_is_priced_per_second() -> None: - routed_model, provider, _, _ = get_llm_provider(model="azure_ai/whisper") - assert (routed_model, provider) == ("whisper", "azure_ai") - - info = get_model_info(model=routed_model, custom_llm_provider=provider) - assert info["mode"] == "audio_transcription" - assert info["input_cost_per_second"] == 0.0001 - assert info["output_cost_per_second"] == 0.0001 + transcription: Final = TranscriptionResponse(text="hello") + transcription._hidden_params = { # pyright: ignore[reportPrivateUsage] # TranscriptionResponse exposes no public hidden-params setter + "custom_llm_provider": "azure_ai", + "model": "azure_ai/whisper", + "audio_transcription_duration": 3600, + } + cost = completion_cost( + completion_response=transcription, + model="azure_ai/whisper", + custom_llm_provider="azure_ai", + call_type="atranscription", + ) + assert cost == pytest.approx(0.36) @pytest.mark.parametrize("catalog_name", CATALOG_NAMES) def test_azure_ai_catalog_entry_source_and_backup_match(catalog_name: str) -> None: - main_entry = _cost_map_entry(REPO_ROOT / "model_prices_and_context_window.json", catalog_name) - backup_entry = _cost_map_entry(REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json", catalog_name) + main_entry = _cost_map_entry(MAIN_COST_MAP, catalog_name) + backup_entry = _cost_map_entry(BACKUP_COST_MAP, catalog_name) - assert str(main_entry["source"]).startswith("https://azure.microsoft.com/en-us/pricing/details/") + assert str(main_entry["source"]).startswith(AZURE_PRICING_PREFIX) assert backup_entry == main_entry -@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) -def test_azure_ai_catalog_entry_carries_its_retirement_date(spec: TokenPricedCatalogModel) -> None: - entry = _cost_map_entry(REPO_ROOT / "model_prices_and_context_window.json", spec.catalog_name) - assert entry.get("deprecation_date") == spec.deprecation_date +def test_azure_ai_model_router_spellings_share_one_entry() -> None: + underscore_entry = _cost_map_entry(MAIN_COST_MAP, "model_router") + hyphen_entry = _cost_map_entry(MAIN_COST_MAP, "model-router") + + assert {k: v for k, v in underscore_entry.items() if k != "comment"} == { + k: v for k, v in hyphen_entry.items() if k != "comment" + } From 69d2ac1edb83336723d4c9ce5024b93612230e5d Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:46:00 -0700 Subject: [PATCH 078/223] fix(policy_engine): run iterator-hook guardrails whose post_call pipeline cannot stream The streaming loop skipped every guardrail stepped by a post_call pipeline, even when the pipeline was dropped from the stream for lacking the unified apply_guardrail interface, so a default_on guardrail that only implements async_post_call_streaming_iterator_hook stopped governing streams it governed on the merge base. The skip set now comes from the pipelines that will gate the stream --- litellm/proxy/utils.py | 26 +++++++------- .../proxy_logging/test_guardrail_pipeline.py | 36 +++++++++++++++++++ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index d7bf832bca4..8b36061c6be 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -447,14 +447,15 @@ def _policy_pipelines(data: Mapping[str, object]) -> tuple[tuple[str, "Guardrail ) +def _pipeline_step_guardrail_names(pipelines: Sequence[tuple[str, "GuardrailPipeline"]]) -> frozenset[str]: + return frozenset(step.guardrail for _policy_name, pipeline in pipelines for step in pipeline.steps) + + def _pipeline_managed_guardrail_names( data: Mapping[str, object], mode: Literal["pre_call", "post_call"] ) -> frozenset[str]: - return frozenset( - step.guardrail - for _policy_name, pipeline in _policy_pipelines(data) - if pipeline.mode == mode - for step in pipeline.steps + return _pipeline_step_guardrail_names( + tuple((policy_name, pipeline) for policy_name, pipeline in _policy_pipelines(data) if pipeline.mode == mode) ) @@ -547,7 +548,7 @@ def _pipeline_is_streamable(policy_name: str, pipeline: "GuardrailPipeline") -> return True verbose_proxy_logger.warning( "Policy '%s' has post_call pipeline guardrails without the unified apply_guardrail interface, " - "which streaming pipelines need; the stream is released ungoverned by it: %s", + "which streaming pipelines need; the stream skips the pipeline and its guardrails run on their own: %s", policy_name, ", ".join(unsupported), ) @@ -563,9 +564,9 @@ def _streamable_post_call_pipelines( Streaming pipelines scan the buffered stream through the endpoint guardrail translation of the request route, so every step's guardrail needs the unified apply_guardrail interface and the route needs a translation. A - pipeline that cannot be run that way yet is left out and the stream is - released the way it was before pipelines ran on streams at all, with a - warning naming what went ungoverned. + pipeline that cannot be run that way yet is left out and its guardrails + run on the stream on their own, the way they did before pipelines ran on + streams at all, with a warning naming the pipeline. """ post_call_pipelines: Final = _post_call_pipelines(request_data) if not post_call_pipelines: @@ -574,7 +575,8 @@ def _streamable_post_call_pipelines( if route and resolve_endpoint_translation(user_api_key_dict, None) is None: verbose_proxy_logger.warning( "Policies with post_call guardrail pipelines cannot scan streaming responses on route %s yet " - "(no endpoint guardrail translation); the stream is released ungoverned by them: %s", + "(no endpoint guardrail translation); the stream skips the pipelines and their guardrails run " + "on their own: %s", route, ", ".join(policy_name for policy_name, _pipeline in post_call_pipelines), ) @@ -3361,10 +3363,10 @@ class ProxyLogging: current_response = response stream_needs_translation: Final = ProxyLogging._stream_requires_guardrail_translation(user_api_key_dict) - pipeline_managed_names: Final = _pipeline_managed_guardrail_names(request_data, "post_call") + pipeline_gated_names: Final = _pipeline_step_guardrail_names(post_call_pipelines) for resolved_callback, kind in caps.iterator_overrides: if isinstance(resolved_callback, CustomGuardrail): - if resolved_callback.guardrail_name in pipeline_managed_names: + if resolved_callback.guardrail_name in pipeline_gated_names: continue if ( resolved_callback.should_run_guardrail(data=request_data, event_type=GuardrailEventHooks.post_call) diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index ad2b4a0efbb..73dd6746b29 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1620,6 +1620,42 @@ async def test_streaming_iterator_hook_releases_stream_when_pipeline_guardrail_l assert any("'response-governance'" in message and "gr-post" in message for message in _warnings(caplog)) +@pytest.mark.asyncio +async def test_streaming_iterator_hook_runs_iterator_hook_guardrail_whose_pipeline_cannot_stream( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog +): + seen: Dict[str, Any] = {} + + class IteratorHookGuardrail(CustomGuardrail): + async def async_post_call_streaming_iterator_hook(self, user_api_key_dict, response, request_data): + seen["count"] = seen.get("count", 0) + 1 + async for item in response: + item.choices[0].delta.content = f"[governed] {item.choices[0].delta.content}" + yield item + + monkeypatch.setattr( + litellm, + "callbacks", + [IteratorHookGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=True)], + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(stream=True) + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + delivered = [ + item + async for item in proxy_logging.async_post_call_streaming_iterator_hook( + user_api_key_dict=make_user_api_key_auth(request_route="/v1/chat/completions"), + response=_async_chunk_iter(_stream_chunks()), + request_data=data, + ) + ] + + assert seen["count"] == 1 + assert [item.choices[0].delta.content for item in delivered] == ["[governed] hello ", "[governed] world"] + assert any("'response-governance'" in message and "gr-post" in message for message in _warnings(caplog)) + + @pytest.mark.asyncio @pytest.mark.parametrize( "rewrite_attribute, value", From 91fc1b201027b3d21a7ced292ac19a51661cba0e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:48:19 -0700 Subject: [PATCH 079/223] fix(policy_engine): record a streaming pipeline step once and in the applied guardrails header CustomGuardrail.__init_subclass__ wrapped _StreamRewriteObserver.apply_guardrail in log_guardrail_information, so every streaming step recorded a second standard_logging_guardrail_information entry and span next to the inner guardrail's own. The observer's method now carries the marker that skips the wrapper. The step also adds the guardrail to the applied guardrails header the way the non-streaming unified path does, so streamed spend rows name the guardrail that scanned them --- .../proxy/policy_engine/pipeline_executor.py | 27 +++++++++--- .../policy_engine/test_pipeline_executor.py | 44 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 970ac20487c..9bc10949e9f 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -7,19 +7,21 @@ pass/fail actions (allow, block, next, modify_response) and data forwarding. import copy import time -from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Final, Literal +from collections.abc import Callable, Mapping, Sequence +from typing import TYPE_CHECKING, Any, Final, Literal, TypeVar from pydantic import BaseModel import litellm from litellm._logging import verbose_proxy_logger +from litellm.constants import LOGS_GUARDRAIL_INFORMATION_MARKER from litellm.integrations.custom_guardrail import ( CustomGuardrail, ModifyResponseException, ) from litellm.integrations.custom_logger import CustomLogger from litellm.litellm_core_utils.core_helpers import independent_snapshot +from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_guardrails_header from litellm.proxy.guardrails.guardrail_hooks.unified_guardrail.unified_guardrail import ( UnifiedLLMGuardrails, ) @@ -72,13 +74,23 @@ def _rewrote(sent: tuple[object, ...] | None, returned: tuple[object, ...] | Non return sent is not None and returned is not None and returned != sent +_GuardrailMethodT = TypeVar("_GuardrailMethodT", bound=Callable[..., object]) + + +def _logged_by_inner_guardrail(method: _GuardrailMethodT) -> _GuardrailMethodT: + vars(method)[LOGS_GUARDRAIL_INFORMATION_MARKER] = True # rebind-ok: stamps the method the class body just defined + return method + + class _StreamRewriteObserver(CustomGuardrail): """Stand-in handed to the endpoint translation in place of a streaming pipeline step's guardrail. It records whether the guardrail returned different output than it was given, which for guardrails like Bedrock's ANONYMIZED action is only known at runtime. Text rewrites are deliverable on translations that write them back across the buffered chunks (``delivers_ended_stream_text_rewrites``); tool-call rewrites and text rewrites on any - other translation are discarded by the executor, which releases the original chunks.""" + other translation are discarded by the executor, which releases the original chunks. + The inner guardrail's ``apply_guardrail`` already records the guardrail information + and span, so the observer's stays out of ``log_guardrail_information``.""" def __init__(self, inner: CustomGuardrail) -> None: super().__init__(guardrail_name=inner.guardrail_name) @@ -89,6 +101,7 @@ class _StreamRewriteObserver(CustomGuardrail): def structured_messages_cover_full_request(self) -> bool: return self.inner.structured_messages_cover_full_request() + @_logged_by_inner_guardrail async def apply_guardrail( self, inputs: GenericGuardrailAPIInputs, @@ -305,9 +318,11 @@ class PipelineExecutor: ) except UndeliverableStreamRewrite: _release_original_chunks(step.guardrail, streaming_chunks, originals) - return - if observer.rewrote_tool_calls or (observer.rewrote_texts and not deliver_rewrites): - _release_original_chunks(step.guardrail, streaming_chunks, originals) + else: + if observer.rewrote_tool_calls or (observer.rewrote_texts and not deliver_rewrites): + _release_original_chunks(step.guardrail, streaming_chunks, originals) + if not callback.records_own_guardrail_information: + add_guardrail_to_applied_guardrails_header(request_data=hook_input, guardrail_name=step.guardrail) @staticmethod async def _run_step( diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index 0685bc6aa1e..54ef1f79f4d 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -1099,6 +1099,50 @@ async def test_streaming_step_discards_tool_call_rewrite_and_restores_written_te assert chunks == [_chunk()] +class _BlockingStreamGuardrail(CustomGuardrail): + def __init__(self): + super().__init__(guardrail_name="masker", event_hook="post_call", default_on=True) + + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + raise HTTPException(status_code=400, detail={"error": "output blocked"}) + + +def _recorded_guardrail_statuses(result): + return [ + entry["guardrail_status"] + for entry in result.modified_data["metadata"]["standard_logging_guardrail_information"] + ] + + +@pytest.mark.asyncio +async def test_streaming_step_records_guardrail_information_once_on_mask(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) + + result = await _run_streaming_step(_WritingTranslation(), [_chunk()]) + + assert result.terminal_action == "allow" + assert _recorded_guardrail_statuses(result) == ["success"] + + +@pytest.mark.asyncio +async def test_streaming_step_records_the_guardrail_in_the_applied_guardrails_header(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) + + result = await _run_streaming_step(_WritingTranslation(), [_chunk()]) + + assert result.modified_data["metadata"]["applied_guardrails"] == ["masker"] + + +@pytest.mark.asyncio +async def test_streaming_step_records_guardrail_information_once_on_block(monkeypatch): + monkeypatch.setattr(litellm, "callbacks", [_BlockingStreamGuardrail()]) + + result = await _run_streaming_step(_WritingTranslation(), [_chunk()]) + + assert [step.outcome for step in result.step_results] == ["fail"] + assert _recorded_guardrail_statuses(result) == ["guardrail_intervened"] + + @pytest.mark.asyncio async def test_streaming_step_restores_chunks_when_translation_refuses_the_rewrite(monkeypatch, caplog): monkeypatch.setattr(litellm, "callbacks", [_TextReturningGuardrail(["hello [MASKED]"])]) From d08a177bc75b45769e5e22efa2bbb4b0ea27ee86 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:51:34 -0700 Subject: [PATCH 080/223] fix(policy_engine): keep a policy-added guardrail's other stages when a pipeline steps it A policy that both adds a guardrail and steps it in a post_call pipeline used to drop the guardrail from the request's guardrail list outright, so its pre_call stage never ran. The per-hook loops already skip guardrails by pipeline mode, so the mode-agnostic subtraction only lost coverage --- litellm/proxy/litellm_pre_call_utils.py | 7 +--- .../proxy/test_litellm_pre_call_utils.py | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 56512570448..e4bce4378f0 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -3079,10 +3079,9 @@ def _apply_resolved_guardrails_to_metadata( if metadata_variable_name not in data: data[metadata_variable_name] = {} - # Track pipeline-managed guardrails to exclude from independent execution - pipeline_managed_guardrails: set = set() + # Record the pipelines and the guardrails they step; the hook loops skip those per pipeline mode if pipelines: - pipeline_managed_guardrails = PolicyResolver.get_pipeline_managed_guardrails(pipelines) + pipeline_managed_guardrails: Final = PolicyResolver.get_pipeline_managed_guardrails(pipelines) data[metadata_variable_name]["_guardrail_pipelines"] = pipelines data[metadata_variable_name]["_pipeline_managed_guardrails"] = pipeline_managed_guardrails verbose_proxy_logger.debug( @@ -3099,10 +3098,8 @@ def _apply_resolved_guardrails_to_metadata( existing_guardrails = [] # Combine existing guardrails with policy-resolved guardrails (no duplicates) - # Exclude pipeline-managed guardrails from the flat list combined = set(existing_guardrails) combined.update(resolved_guardrails) - combined -= pipeline_managed_guardrails data[metadata_variable_name]["guardrails"] = list(combined) verbose_proxy_logger.debug("Policy engine: added guardrails to request metadata: %s", list(combined)) diff --git a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py index 7070617ce3e..78fe2e6df88 100644 --- a/tests/test_litellm/proxy/test_litellm_pre_call_utils.py +++ b/tests/test_litellm/proxy/test_litellm_pre_call_utils.py @@ -4148,6 +4148,48 @@ async def test_add_guardrails_from_policy_engine(): attachment_registry._initialized = False +@pytest.mark.asyncio +async def test_add_guardrails_from_policy_engine_keeps_a_policy_added_guardrail_its_pipeline_also_steps(): + from litellm.proxy.policy_engine.attachment_registry import get_attachment_registry + from litellm.proxy.policy_engine.policy_registry import get_policy_registry + from litellm.types.proxy.policy_engine import ( + GuardrailPipeline, + PipelineStep, + Policy, + PolicyAttachment, + PolicyGuardrails, + ) + + data = {"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}], "metadata": {}} + policy_registry = get_policy_registry() + policy_registry._policies = { + "response-governance": Policy( + guardrails=PolicyGuardrails(add=["pii_blocker"]), + pipeline=GuardrailPipeline(mode="post_call", steps=[PipelineStep(guardrail="pii_blocker")]), + ), + } + policy_registry._initialized = True + attachment_registry = get_attachment_registry() + attachment_registry._attachments = [PolicyAttachment(policy="response-governance", scope="*")] + attachment_registry._initialized = True + + try: + await add_guardrails_from_policy_engine( + data=data, + metadata_variable_name="metadata", + user_api_key_dict=UserAPIKeyAuth(api_key="test-key"), + ) + finally: + policy_registry._policies = {} + policy_registry._initialized = False + attachment_registry._attachments = [] + attachment_registry._initialized = False + + assert data["metadata"]["guardrails"] == ["pii_blocker"] + assert data["metadata"]["_pipeline_managed_guardrails"] == {"pii_blocker"} + assert [pipeline.mode for _policy_name, pipeline in data["metadata"]["_guardrail_pipelines"]] == ["post_call"] + + @pytest.mark.asyncio async def test_add_guardrails_from_policy_engine_accepts_dynamic_policies_and_pops_from_data(): """ From 08b60c409a0b556efb6bbe6470402a4530666870 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:53:26 -0700 Subject: [PATCH 081/223] refactor(guardrails): drop the unused rewrites_streamed_output hook Nothing calls it since the streaming pipeline detects rewrites at run time through the stream observer, so the base method and the content filter's override were dead code with dead tests --- litellm/integrations/custom_guardrail.py | 3 - .../litellm_content_filter/content_filter.py | 9 --- .../content_filter/test_content_filter.py | 56 ------------------- 3 files changed, 68 deletions(-) diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py index 883329c9fa8..2d66a280663 100644 --- a/litellm/integrations/custom_guardrail.py +++ b/litellm/integrations/custom_guardrail.py @@ -773,9 +773,6 @@ class CustomGuardrail(CustomLogger): def uses_apply_guardrail_interface(self) -> bool: return type(self).apply_guardrail is not CustomGuardrail.apply_guardrail - def rewrites_streamed_output(self) -> bool: - return self.mask_response_content - def _deployment_pre_call_target(self) -> "CustomLogger": if not self.uses_apply_guardrail_interface() or self.use_native_lifecycle_hooks: return self diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index 85eb50c78e7..722f96ef814 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -1947,15 +1947,6 @@ class ContentFilterGuardrail(CustomGuardrail): exception_str=exception_str, ) - def rewrites_streamed_output(self) -> bool: - return ( - super().rewrites_streamed_output() - or any(entry["action"] == ContentFilterAction.MASK for entry in self.compiled_patterns) - or any(action == ContentFilterAction.MASK for action, _ in self.blocked_words.values()) - or any(action == ContentFilterAction.MASK for _, _, action in self.category_keywords.values()) - or any(action == ContentFilterAction.MASK for _, _, action in self.always_block_category_keywords.values()) - ) - async def async_post_call_streaming_iterator_hook( self, user_api_key_dict: UserAPIKeyAuth, diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index 73020fe3e6f..be55ac47bde 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -3068,59 +3068,3 @@ class TestContentFilterToolCallArguments: request_data={}, input_type="response", ) - - -class TestRewritesStreamedOutput: - def test_block_only_rules_do_not_rewrite(self): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - patterns=[ContentFilterPattern(pattern_type="prebuilt", pattern_name="us_ssn", action=ContentFilterAction.BLOCK)], - blocked_words=[BlockedWord(keyword="kumquat", action=ContentFilterAction.BLOCK)], - ) - - assert guardrail.rewrites_streamed_output() is False - - def test_mask_blocked_word_rewrites(self): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - blocked_words=[BlockedWord(keyword="persimmon", action=ContentFilterAction.MASK)], - ) - - assert guardrail.rewrites_streamed_output() is True - - def test_mask_pattern_rewrites(self): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - patterns=[ContentFilterPattern(pattern_type="prebuilt", pattern_name="us_ssn", action=ContentFilterAction.MASK)], - ) - - assert guardrail.rewrites_streamed_output() is True - - def test_mask_response_content_rewrites(self): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - blocked_words=[BlockedWord(keyword="kumquat", action=ContentFilterAction.BLOCK)], - mask_response_content=True, - ) - - assert guardrail.rewrites_streamed_output() is True - - @pytest.mark.parametrize("action, expected", [("MASK", True), ("BLOCK", False)]) - def test_category_keywords_follow_the_category_action(self, action, expected): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - categories=[{"category": "bias_gender", "enabled": True, "action": action}], - ) - - assert guardrail.category_keywords and not guardrail.always_block_category_keywords - assert guardrail.rewrites_streamed_output() is expected - - @pytest.mark.parametrize("action, expected", [("MASK", True), ("BLOCK", False)]) - def test_always_block_category_keywords_follow_the_category_action(self, action, expected): - guardrail = ContentFilterGuardrail( - guardrail_name="cf", - categories=[{"category": "age_discrimination", "enabled": True, "action": action}], - ) - - assert guardrail.always_block_category_keywords and not guardrail.category_keywords - assert guardrail.rewrites_streamed_output() is expected From 704013dbb6f83962e4cac97c79ac0dd2109cbabf Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:13:23 -0700 Subject: [PATCH 082/223] fix(init): keep non-flag LITELLM_DROP_PARAMS values on with a warning The merge base read the variable by truthiness, so any non-empty value turned the global flag on. Parsing it as a flag made a value such as temperature or enabled silently turn it off, and the only docs for the variable describe it as a list of parameter names, so keep those values on and log a warning that asks for true or false. A blank value stays off without a warning --- litellm/__init__.py | 4 +-- litellm/litellm_core_utils/core_helpers.py | 16 +++++++++++ .../litellm_core_utils/test_core_helpers.py | 28 +++++++++++++++++++ .../test_litellm/test_drop_params_env_var.py | 11 +++++--- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index f7d4dce87d2..fc6dc35fe55 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -47,7 +47,7 @@ from typing import ( ) from litellm.types.integrations.datadog import DatadogInitParams from litellm.types.integrations.newrelic import NewRelicInitParams -from litellm.litellm_core_utils.core_helpers import drop_params_flag +from litellm.litellm_core_utils.core_helpers import drop_params_env_flag from litellm._logging import ( set_verbose, _turn_on_debug, @@ -239,7 +239,7 @@ token: Optional[str] = ( ) telemetry = True max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults -drop_params = drop_params_flag(os.getenv("LITELLM_DROP_PARAMS"), "LITELLM_DROP_PARAMS", verbose_logger) +drop_params = drop_params_env_flag(os.environ, verbose_logger) modify_params = bool(os.getenv("LITELLM_MODIFY_PARAMS", False)) use_chat_completions_url_for_anthropic_messages: bool = bool( os.getenv("LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES", False) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index a3dfac81cc1..eacc3e4860a 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -58,6 +58,22 @@ def drop_params_flag(value: object, source: str, logger: logging.Logger) -> bool return bool(normalized) +DROP_PARAMS_ENV_VAR: Final = "LITELLM_DROP_PARAMS" + + +def drop_params_env_flag(environ: Mapping[str, str], logger: logging.Logger) -> bool: + configured: Final = environ.get(DROP_PARAMS_ENV_VAR, "").strip() + if configured == "": + return False + normalized: Final = normalize_drop_params(configured) + if normalized is None: + logger.warning( + "%s=%r is not a flag value, treating it as on. Set it to true or false", DROP_PARAMS_ENV_VAR, configured + ) + return True + return normalized + + def safe_divide( numerator: float, denominator: float, diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index e3880175759..e937be47441 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -6,6 +6,7 @@ import pytest from litellm.litellm_core_utils.core_helpers import ( _FINISH_REASON_MAP, + drop_params_env_flag, drop_params_flag, get_or_create_metadata_bucket, map_finish_reason, @@ -301,6 +302,33 @@ def test_drop_params_flag_treats_non_flag_values_as_off_with_a_warning(value, ca assert f"LITELLM_DROP_PARAMS={value!r} is not a flag value, treating it as off" in caplog.text +@pytest.mark.parametrize( + "environ, expected", + [ + ({}, False), + ({"LITELLM_DROP_PARAMS": ""}, False), + ({"LITELLM_DROP_PARAMS": " "}, False), + ({"LITELLM_DROP_PARAMS": "true"}, True), + ({"LITELLM_DROP_PARAMS": " False "}, False), + ({"LITELLM_DROP_PARAMS": "0"}, False), + ], +) +def test_drop_params_env_flag_reads_a_flag_without_a_warning(environ, expected, caplog): + with caplog.at_level(logging.WARNING, logger="drop-params-test"): + assert drop_params_env_flag(environ, logging.getLogger("drop-params-test")) is expected + assert caplog.text == "" + + +@pytest.mark.parametrize("configured", ["temperature", "temperature,top_p", "enabled"]) +def test_drop_params_env_flag_keeps_a_non_flag_value_on_with_a_warning(configured, caplog): + with caplog.at_level(logging.WARNING, logger="drop-params-test"): + assert drop_params_env_flag({"LITELLM_DROP_PARAMS": configured}, logging.getLogger("drop-params-test")) is True + assert ( + f"LITELLM_DROP_PARAMS={configured!r} is not a flag value, treating it as on. Set it to true or false" + in caplog.text + ) + + class TestIsExpectedClientError: def test_status_ranges(self): from litellm.litellm_core_utils.core_helpers import is_expected_client_error diff --git a/tests/test_litellm/test_drop_params_env_var.py b/tests/test_litellm/test_drop_params_env_var.py index 339298df3d1..1e0b7801ef1 100644 --- a/tests/test_litellm/test_drop_params_env_var.py +++ b/tests/test_litellm/test_drop_params_env_var.py @@ -15,7 +15,7 @@ def _import_litellm_with(configured: str) -> subprocess.CompletedProcess[str]: ) -@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")]) +@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True"), ("", "False")]) def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected): result = _import_litellm_with(configured) @@ -23,8 +23,11 @@ def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected): assert "is not a flag value" not in result.stderr -def test_litellm_drop_params_env_var_non_flag_value_is_off_with_a_warning(): +def test_litellm_drop_params_env_var_non_flag_value_stays_on_with_a_warning(): result = _import_litellm_with("temperature") - assert result.stdout.strip() == "False" - assert "LITELLM_DROP_PARAMS='temperature' is not a flag value, treating it as off" in result.stderr + assert result.stdout.strip() == "True" + assert ( + "LITELLM_DROP_PARAMS='temperature' is not a flag value, treating it as on. Set it to true or false" + in result.stderr + ) From 5706952588ee2b2445e864ce8a85af3339bb138b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:22:51 -0700 Subject: [PATCH 083/223] fix(azure_ai): drop gpt-chat-latest effort levels, test prices via calculator litellm's azure_ai config rejects reasoning_effort for gpt-chat-latest and Azure documents a fixed reasoning level for it, so the entry no longer advertises reasoning_effort_levels. The catalog metadata tests compare cost_per_token and the whisper transcription cost with the entry the calculator read instead of with list-price literals, the pattern #40195 removed --- ...odel_prices_and_context_window_backup.json | 3 - model_prices_and_context_window.json | 3 - ...azure_ai_foundry_catalog_model_metadata.py | 80 +++++++++---------- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 674a8b98304..7e7d8a9e930 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3591,9 +3591,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, - "reasoning_effort_levels": [ - "medium" - ], "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", "supported_endpoints": [ "/v1/chat/completions", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 674a8b98304..7e7d8a9e930 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3591,9 +3591,6 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-05, - "reasoning_effort_levels": [ - "medium" - ], "source": "https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/", "supported_endpoints": [ "/v1/chat/completions", diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py index 19b082edd8a..84d5cd2a7d4 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_foundry_catalog_model_metadata.py @@ -1,11 +1,10 @@ -from dataclasses import dataclass from pathlib import Path from typing import Final import pytest from pydantic import TypeAdapter -from litellm import completion_cost, cost_per_token +from litellm import completion_cost, cost_per_token, get_model_info from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider from litellm.types.utils import TranscriptionResponse @@ -15,31 +14,39 @@ BACKUP_COST_MAP: Final = REPO_ROOT / "litellm" / "model_prices_and_context_windo COST_MAP_ADAPTER: Final = TypeAdapter(dict[str, dict[str, object]]) AZURE_PRICING_PREFIX: Final = "https://azure.microsoft.com/en-us/pricing/details/" A_MILLION: Final = 1_000_000 +AN_HOUR_IN_SECONDS: Final = 3600 - -@dataclass(frozen=True, slots=True) -class TokenPricedCatalogModel: - catalog_name: str - dollars_per_million_input: float - dollars_per_million_output: float - - -TOKEN_PRICED_MODELS: Final = ( - TokenPricedCatalogModel("gpt-chat-latest", 5.0, 30.0), - TokenPricedCatalogModel("codex-mini", 1.5, 6.0), - TokenPricedCatalogModel("model-router", 0.14, 0.0), - TokenPricedCatalogModel("cohere-command-a", 2.5, 10.0), - TokenPricedCatalogModel("grok-4-20-reasoning", 1.25, 2.5), - TokenPricedCatalogModel("grok-4-20-non-reasoning", 1.25, 2.5), +TOKEN_PRICED_NAMES: Final = ( + "gpt-chat-latest", + "codex-mini", + "model-router", + "cohere-command-a", + "grok-4-20-reasoning", + "grok-4-20-non-reasoning", ) GROK_4_20_NAMES: Final = ("grok-4-20-reasoning", "grok-4-20-non-reasoning") -CATALOG_NAMES: Final = tuple(spec.catalog_name for spec in TOKEN_PRICED_MODELS) + ("whisper",) +CATALOG_NAMES: Final = TOKEN_PRICED_NAMES + ("whisper",) def _cost_map_entry(path: Path, catalog_name: str) -> dict[str, object]: return COST_MAP_ADAPTER.validate_json(path.read_bytes())[f"azure_ai/{catalog_name}"] +def _whisper_transcription_cost(duration_seconds: int) -> float: + transcription: Final = TranscriptionResponse(text="hello") + transcription._hidden_params = { # pyright: ignore[reportPrivateUsage] # TranscriptionResponse exposes no public hidden-params setter + "custom_llm_provider": "azure_ai", + "model": "azure_ai/whisper", + "audio_transcription_duration": duration_seconds, + } + return completion_cost( + completion_response=transcription, + model="azure_ai/whisper", + custom_llm_provider="azure_ai", + call_type="atranscription", + ) + + @pytest.mark.parametrize("catalog_name", CATALOG_NAMES) def test_azure_ai_catalog_name_routes_to_azure_ai(catalog_name: str) -> None: routed_model, provider, _, _ = get_llm_provider(model=f"azure_ai/{catalog_name}") @@ -47,20 +54,22 @@ def test_azure_ai_catalog_name_routes_to_azure_ai(catalog_name: str) -> None: @pytest.mark.usefixtures("local_model_cost_map") -@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) -def test_azure_ai_catalog_name_costs_a_million_tokens_at_list_price(spec: TokenPricedCatalogModel) -> None: +@pytest.mark.parametrize("catalog_name", TOKEN_PRICED_NAMES) +def test_azure_ai_catalog_name_charges_its_own_entry_per_token(catalog_name: str) -> None: + entry: Final = get_model_info(f"azure_ai/{catalog_name}") prompt_cost, completion_cost_usd = cost_per_token( - model=f"azure_ai/{spec.catalog_name}", prompt_tokens=A_MILLION, completion_tokens=A_MILLION + model=f"azure_ai/{catalog_name}", prompt_tokens=A_MILLION, completion_tokens=A_MILLION ) - assert prompt_cost == pytest.approx(spec.dollars_per_million_input) - assert completion_cost_usd == pytest.approx(spec.dollars_per_million_output) + assert prompt_cost > 0 + assert prompt_cost == pytest.approx(A_MILLION * entry["input_cost_per_token"]) + assert completion_cost_usd == pytest.approx(A_MILLION * entry["output_cost_per_token"]) @pytest.mark.usefixtures("local_model_cost_map") -@pytest.mark.parametrize("spec", TOKEN_PRICED_MODELS, ids=lambda spec: spec.catalog_name) -def test_azure_ai_catalog_name_prices_the_same_in_any_casing(spec: TokenPricedCatalogModel) -> None: - lowercase_cost = cost_per_token(model=f"azure_ai/{spec.catalog_name}", prompt_tokens=A_MILLION, completion_tokens=0) - upper_cost = cost_per_token(model=f"azure_ai/{spec.catalog_name.upper()}", prompt_tokens=A_MILLION, completion_tokens=0) +@pytest.mark.parametrize("catalog_name", TOKEN_PRICED_NAMES) +def test_azure_ai_catalog_name_prices_the_same_in_any_casing(catalog_name: str) -> None: + lowercase_cost = cost_per_token(model=f"azure_ai/{catalog_name}", prompt_tokens=A_MILLION, completion_tokens=0) + upper_cost = cost_per_token(model=f"azure_ai/{catalog_name.upper()}", prompt_tokens=A_MILLION, completion_tokens=0) assert upper_cost == lowercase_cost @@ -80,19 +89,10 @@ def test_azure_ai_grok_4_20_bills_cached_prompt_tokens_at_the_input_price(catalo @pytest.mark.usefixtures("local_model_cost_map") def test_azure_ai_whisper_catalog_name_is_priced_per_second() -> None: - transcription: Final = TranscriptionResponse(text="hello") - transcription._hidden_params = { # pyright: ignore[reportPrivateUsage] # TranscriptionResponse exposes no public hidden-params setter - "custom_llm_provider": "azure_ai", - "model": "azure_ai/whisper", - "audio_transcription_duration": 3600, - } - cost = completion_cost( - completion_response=transcription, - model="azure_ai/whisper", - custom_llm_provider="azure_ai", - call_type="atranscription", - ) - assert cost == pytest.approx(0.36) + one_second_cost: Final = _whisper_transcription_cost(1) + one_hour_cost: Final = _whisper_transcription_cost(AN_HOUR_IN_SECONDS) + assert one_second_cost > 0 + assert one_hour_cost == pytest.approx(AN_HOUR_IN_SECONDS * one_second_cost) @pytest.mark.parametrize("catalog_name", CATALOG_NAMES) From 3cadf2f8f7120bf10409a353ef08e4cdc6f78b80 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:35:19 -0700 Subject: [PATCH 084/223] test(azure_ai): charge the router fee over cached prompt tokens too --- .../llms/azure_ai/test_azure_ai_cost_calculator.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py index 7df14b91741..a43fc3332af 100644 --- a/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py +++ b/tests/test_litellm/llms/azure_ai/test_azure_ai_cost_calculator.py @@ -138,6 +138,18 @@ class TestAzureModelRouterFlatCost: assert prompt_cost == pytest.approx(1000 * ROUTER_FEE_PER_TOKEN, rel=1e-9) assert completion_cost_usd == 0.0 + def test_unmapped_router_deployment_name_charges_the_fee_over_cached_prompt_tokens_too(self) -> None: + usage = Usage( + prompt_tokens=2000, + completion_tokens=800, + total_tokens=2800, + cache_read_input_tokens=500, + cache_creation_input_tokens=200, + ) + prompt_cost, completion_cost_usd = cost_per_token(model="azure-model-router", usage=usage) + assert prompt_cost == pytest.approx(2000 * ROUTER_FEE_PER_TOKEN, rel=1e-9) + assert completion_cost_usd == 0.0 + def test_router_deployment_name_as_both_names_charges_the_fee_once(self) -> None: usage = Usage(prompt_tokens=1000, completion_tokens=500, total_tokens=1500) prompt_cost, completion_cost_usd = cost_per_token( From 5c037299f413c38609cbb7f5a582662e44b197d4 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 23:02:27 -0700 Subject: [PATCH 085/223] feat: move MongoDB vector search to an optional sidecar --- .github/workflows/_test-unit-base.yml | 2 +- Dockerfile | 2 - docker/Dockerfile.database | 2 - docker/Dockerfile.non_root | 3 - gateway/Dockerfile | 2 - .../base_llm/vector_store/transformation.py | 3 + litellm/llms/custom_httpx/llm_http_handler.py | 17 +- litellm/llms/mongodb/common_utils.py | 303 --- .../mongodb/vector_stores/transformation.py | 449 ++--- pyproject.toml | 1 - .../test_mongodb_transformation.py | 1691 ++--------------- .../_components/VectorStoreForm.test.tsx | 12 +- .../_components/VectorStoreForm.tsx | 4 +- .../vector_store_providers.test.tsx | 11 +- .../src/components/vector_store_providers.tsx | 27 +- uv.lock | 79 +- 16 files changed, 412 insertions(+), 2196 deletions(-) delete mode 100644 litellm/llms/mongodb/common_utils.py diff --git a/.github/workflows/_test-unit-base.yml b/.github/workflows/_test-unit-base.yml index 75b0f93fd77..62790e23143 100644 --- a/.github/workflows/_test-unit-base.yml +++ b/.github/workflows/_test-unit-base.yml @@ -113,7 +113,7 @@ jobs: if: steps.changes.outputs.decision != 'skip' timeout-minutes: 8 run: | - .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml --extra mongodb + .github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml uv run --no-sync python -c 'import os, sys; print(sys.version); assert f"{sys.version_info.major}.{sys.version_info.minor}" == os.environ["UV_PYTHON"]' - name: Cache Prisma binaries diff --git a/Dockerfile b/Dockerfile index 1648ec69d13..0a92aa9a68c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,7 +67,6 @@ RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-gr --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 # Copy full source tree @@ -90,7 +89,6 @@ RUN uv sync --frozen --no-default-groups --no-editable \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index cc81ad6b3d3..e9ad2849bb2 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -65,7 +65,6 @@ RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-gr --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 # Copy full source tree @@ -88,7 +87,6 @@ RUN uv sync --frozen --no-default-groups --no-editable \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index 358425af901..edf20e8bbff 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -71,7 +71,6 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 # Copy full source tree @@ -100,7 +99,6 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 \ --no-sources-package litellm-proxy-extras; \ else \ @@ -111,7 +109,6 @@ RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \ --extra semantic-router \ --extra saml \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13; \ fi diff --git a/gateway/Dockerfile b/gateway/Dockerfile index e42e488d57f..308d70a6b26 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -47,7 +47,6 @@ RUN --mount=type=cache,target=/root/.cache/uv \ --extra extra_proxy \ --extra semantic-router \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 # Stage 2 — copy source and install the project + workspace members. @@ -60,7 +59,6 @@ RUN --mount=type=cache,target=/root/.cache/uv \ --extra extra_proxy \ --extra semantic-router \ --extra bedrock-realtime \ - --extra mongodb \ --python python3.13 RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \ diff --git a/litellm/llms/base_llm/vector_store/transformation.py b/litellm/llms/base_llm/vector_store/transformation.py index c8d2b7fe522..07b60cb4b72 100644 --- a/litellm/llms/base_llm/vector_store/transformation.py +++ b/litellm/llms/base_llm/vector_store/transformation.py @@ -121,6 +121,9 @@ class RouterVectorStoreEmbeddingExecutor: class BaseVectorStoreConfig: + def validate_create_vector_store(self) -> None: + return None + def get_supported_openai_params(self, model: str) -> list[VECTOR_STORE_OPENAI_PARAMS]: return [] diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 2f561809940..1dc4b198890 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -9814,7 +9814,7 @@ class BaseLLMHTTPHandler: vector_store_search_optional_params=vector_store_search_optional_params, api_base=api_base, litellm_logging_obj=logging_obj, - litellm_params=dict(litellm_params), + litellm_params={**dict(litellm_params), "timeout": timeout}, extra_body=extra_body, embedding_executor=embedding_executor, ) @@ -9859,6 +9859,10 @@ class BaseLLMHTTPHandler: data=request_data, timeout=timeout, ) + except httpx.TimeoutException: + raise vector_store_provider_config.get_error_class( + error_message="Vector store search exceeded the caller timeout.", status_code=408, headers={} + ) from None except Exception as e: raise self._handle_error(e=e, provider_config=vector_store_provider_config) @@ -9943,7 +9947,7 @@ class BaseLLMHTTPHandler: vector_store_search_optional_params=vector_store_search_optional_params, api_base=api_base, litellm_logging_obj=logging_obj, - litellm_params=dict(litellm_params), + litellm_params={**dict(litellm_params), "timeout": timeout}, extra_body=extra_body, embedding_executor=embedding_executor, ) @@ -9988,7 +9992,12 @@ class BaseLLMHTTPHandler: url=url, headers=headers, data=request_data, + timeout=timeout, ) + except httpx.TimeoutException: + raise vector_store_provider_config.get_error_class( + error_message="Vector store search exceeded the caller timeout.", status_code=408, headers={} + ) from None except Exception as e: raise self._handle_error(e=e, provider_config=vector_store_provider_config) @@ -10018,6 +10027,8 @@ class BaseLLMHTTPHandler: else: async_httpx_client = client + vector_store_provider_config.validate_create_vector_store() + headers: Final = vector_store_provider_config.validate_environment( headers=extra_headers or {}, litellm_params=litellm_params ) @@ -10088,6 +10099,8 @@ class BaseLLMHTTPHandler: else: sync_httpx_client = client + vector_store_provider_config.validate_create_vector_store() + headers: Final = vector_store_provider_config.validate_environment( headers=extra_headers or {}, litellm_params=litellm_params ) diff --git a/litellm/llms/mongodb/common_utils.py b/litellm/llms/mongodb/common_utils.py deleted file mode 100644 index 02c0b359407..00000000000 --- a/litellm/llms/mongodb/common_utils.py +++ /dev/null @@ -1,303 +0,0 @@ -"""Shared helpers for the MongoDB integrations. pymongo lives in the optional ``mongodb`` extra, -so every import of it is deferred to call time.""" - -import asyncio -import threading -import weakref -from asyncio import AbstractEventLoop -from collections import OrderedDict -from collections.abc import Callable, Mapping -from dataclasses import dataclass -from types import MappingProxyType -from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar - -from litellm.exceptions import BadRequestError, ServiceUnavailableError, Timeout - -if TYPE_CHECKING: - from pymongo import AsyncMongoClient, MongoClient - -PYMONGO_INSTALL_HINT: Final = ( - "The MongoDB vector store requires the 'pymongo' package. " - "Run 'pip install litellm[mongodb]' (or 'pip install pymongo') to install it." -) - -MONGODB_PROVIDER: Final = "mongodb" - - -def config_error(message: str) -> BadRequestError: - """400 rather than the 500 a bare ValueError becomes once litellm.exception_type wraps it.""" - return BadRequestError(message=message, model=None, llm_provider=MONGODB_PROVIDER) - - -def timeout_error(message: str) -> Timeout: - return Timeout(message=message, model=None, llm_provider=MONGODB_PROVIDER) - - -def unavailable_error(message: str) -> ServiceUnavailableError: - """litellm only retries 408, 409, 429 and 5xx, so a 400 here would make a failover permanent.""" - return ServiceUnavailableError(message=message, model=None, llm_provider=MONGODB_PROVIDER) - - -DEFAULT_CONNECT_TIMEOUT_MS: Final = 10_000 -DEFAULT_SOCKET_TIMEOUT_MS: Final = 30_000 -DEFAULT_SERVER_SELECTION_TIMEOUT_MS: Final = 10_000 - -_MAX_CACHED_CLIENTS: Final = 32 - -_APP_NAME: Final = "litellm" - - -@dataclass(frozen=True, slots=True) -class MongoClientKey: - connection_string: str - connect_timeout_ms: int - socket_timeout_ms: int - server_selection_timeout_ms: int - - -SyncClientFactory: TypeAlias = Callable[..., "MongoClient"] -AsyncClientFactory: TypeAlias = Callable[..., "AsyncMongoClient"] - -_K = TypeVar("_K") -_V = TypeVar("_V") - -_AsyncClientCacheKey: TypeAlias = tuple[MongoClientKey, int] -# CPython recycles id() aggressively, so the id alone would hand a new loop a closed loop's client -_AsyncClientEntry: TypeAlias = tuple["weakref.ref[AbstractEventLoop]", "AsyncMongoClient"] - -_SyncClientCache: TypeAlias = "OrderedDict[MongoClientKey, MongoClient]" -_AsyncClientCache: TypeAlias = "OrderedDict[_AsyncClientCacheKey, _AsyncClientEntry]" - -_sync_clients: Final[_SyncClientCache] = OrderedDict() # mutable-ok: process-level client cache -_async_clients: Final[_AsyncClientCache] = OrderedDict() # mutable-ok: same cache, per loop -# async searches reach the sync client through executor threads, so both caches are shared state -_cache_lock: Final = threading.Lock() - - -def _store_bounded(cache: "OrderedDict[_K, _V]", cache_key: "_K", value: "_V") -> None: - """Eviction only drops this cache's reference; an in-flight search keeps its client alive.""" - with _cache_lock: - cache[cache_key] = value # mutable-ok: an LRU cache is mutable state by definition - cache.move_to_end(cache_key) - while len(cache) > _MAX_CACHED_CLIENTS: - cache.popitem(last=False) - - -def _mark_used(cache: "OrderedDict[_K, _V]", cache_key: "_K") -> None: - with _cache_lock: - if cache_key in cache: - cache.move_to_end(cache_key) - - -def import_sync_mongo_client() -> "type[MongoClient]": - try: - from pymongo import MongoClient as SyncMongoClient - except ImportError as e: - raise config_error(PYMONGO_INSTALL_HINT) from e - return SyncMongoClient - - -def import_async_mongo_client() -> "type[AsyncMongoClient]": - try: - from pymongo import AsyncMongoClient as AsyncMongoClientClass - except ImportError as e: - raise config_error(PYMONGO_INSTALL_HINT) from e - return AsyncMongoClientClass - - -def _client_kwargs(key: MongoClientKey) -> Mapping[str, object]: - return MappingProxyType( - { - "connectTimeoutMS": key.connect_timeout_ms, - "socketTimeoutMS": key.socket_timeout_ms, - "serverSelectionTimeoutMS": key.server_selection_timeout_ms, - "appname": _APP_NAME, - } - ) - - -def get_sync_client(key: MongoClientKey, client_class: SyncClientFactory | None = None) -> "MongoClient": - cached: Final = _sync_clients.get(key) - if cached is not None: - _mark_used(_sync_clients, key) - return cached - build: Final = client_class if client_class is not None else import_sync_mongo_client() - client: Final = build(key.connection_string, **_client_kwargs(key)) - _store_bounded(_sync_clients, key, client) - return client - - -def _purge_dead_loops() -> None: - """A cached client holds its loop alive, so a closed loop's entry would pin that client and its - sockets for the life of the process.""" - with _cache_lock: - for stale in tuple( - cache_key - for cache_key, (loop_ref, _) in _async_clients.items() - if (cached_loop := loop_ref()) is None or cached_loop.is_closed() - ): - del _async_clients[stale] - - -def get_async_client(key: MongoClientKey, client_class: AsyncClientFactory | None = None) -> "AsyncMongoClient": - """Async clients bind to the loop that created them, so the cache is keyed per loop.""" - loop: Final = asyncio.get_running_loop() - loop_key: Final = (key, id(loop)) - cached: Final = _async_clients.get(loop_key) - if cached is not None and cached[0]() is loop: - _mark_used(_async_clients, loop_key) - return cached[1] - _purge_dead_loops() - build: Final = client_class if client_class is not None else import_async_mongo_client() - client: Final = build(key.connection_string, **_client_kwargs(key)) - _store_bounded(_async_clients, loop_key, (weakref.ref(loop), client)) - return client - - -def reset_client_cache() -> None: - with _cache_lock: - _sync_clients.clear() - _async_clients.clear() - - -_AUTHENTICATION_FAILED_CODE: Final = 18 -_UNAUTHORIZED_CODE: Final = 13 -# Atlas reports a rejected user as code 8000 "AtlasError" where a self-managed mongod reports 18 -_AUTHENTICATION_MESSAGE_MARKERS: Final = ("bad auth", "authentication failed", "not authorized") -_RESOLUTION_TIMEOUT_MARKERS: Final = ("resolution lifetime expired", "dns operation timed out") -_UNKNOWN_HOSTNAME_MARKERS: Final = ("dns query name does not exist", "name or service not known") -_CREDENTIAL_ESCAPING_MARKERS: Final = ("must be escaped according to rfc 3986", "bad database name") - - -def _index_hint(index_name: str, database: str, collection: str) -> str: - return ( - f"No queryable MongoDB Vector Search index named '{index_name}' was found on " - f"'{database}.{collection}'. Confirm the index exists on that exact collection, that its " - "status is READY rather than still building, and that the vector store id matches the index name." - ) - - -def missing_index_error(index_name: str, database: str, collection: str) -> BadRequestError: - """$vectorSearch against a missing index, database or collection returns zero documents rather - than failing, so an empty result set is checked against the catalogue and reported as this.""" - return config_error( - f"{_index_hint(index_name, database, collection)} A vector search against a database, " - "collection or index that does not exist returns no results rather than an error, so this " - "was reported as an empty result set by MongoDB." - ) - - -def index_not_ready_error(index_name: str, database: str, collection: str, status: str) -> BadRequestError: - return config_error( - f"The MongoDB Vector Search index '{index_name}' on '{database}.{collection}' is not queryable " - f"yet; its status is {status}. Searches against it return no results until the build finishes." - ) - - -def translate_mongo_error(error: Exception, index_name: str, database: str, collection: str) -> Exception: - """Returns the exception to raise, so callers keep the driver error as ``__cause__``.""" - try: - from pymongo.errors import ( - ConfigurationError, - ConnectionFailure, - ExecutionTimeout, - InvalidOperation, - NetworkTimeout, - OperationFailure, - ServerSelectionTimeoutError, - ) - except ImportError: - return error - - if isinstance(error, ServerSelectionTimeoutError): - return timeout_error( - "Could not reach the MongoDB deployment before the timeout. On Atlas this is usually the " - "project's IP access list not containing this host, or a paused cluster. On a self-managed " - "deployment it is usually the host or port in the URI, or a firewall between this process " - f"and mongod. Either way it can also be an unresolvable hostname. Driver detail: {error}" - ) - # ExecutionTimeout subclasses OperationFailure, so it has to be matched before it - if isinstance(error, (NetworkTimeout, ExecutionTimeout)): - return timeout_error( - f"The MongoDB vector search against '{database}.{collection}' timed out before returning. " - f"Driver detail: {error}" - ) - # ServerSelectionTimeoutError and NetworkTimeout also subclass ConnectionFailure, so this only - # sees what those branches left - if isinstance(error, ConnectionFailure): - return unavailable_error( - f"The connection to '{database}.{collection}' was dropped or refused. That is usually a " - "replica set failover or a restarted node, so the search is worth retrying. If it keeps " - "happening: on Atlas the usual cause is a connection string with no username and password, " - "or a TLS failure, so confirm the URI is the one Atlas shows under Connect, Drivers; on a " - "self-managed deployment, check that mongod is listening on the host and port in the URI. " - f"Driver detail: {error}" - ) - if isinstance(error, OperationFailure): - code: Final = error.code - detail: Final = str(error).lower() - if code in (_AUTHENTICATION_FAILED_CODE, _UNAUTHORIZED_CODE) or any( - marker in detail for marker in _AUTHENTICATION_MESSAGE_MARKERS - ): - return config_error( - "MongoDB rejected the credentials in mongodb_connection_string, or the database user " - f"lacks read access to '{database}.{collection}'. Driver detail: {error.details}" - ) - if "dimension" in detail: - return config_error( - "The query embedding does not match the vector dimensions the index was built for. " - "litellm_embedding_model must be the same model that produced the stored vectors. " - f"Driver detail: {error}" - ) - if "is not indexed as vector" in detail: - return config_error( - "mongodb_embedding_field names a field the MongoDB Vector Search index does not cover. " - f"It must match the 'path' the index '{index_name}' was created on. Driver detail: {error}" - ) - if "index" in detail and ("not found" in detail or "does not exist" in detail or "unknown" in detail): - return config_error(f"{_index_hint(index_name, database, collection)} Driver detail: {error}") - return config_error( - f"MongoDB rejected the vector search against '{database}.{collection}' using index " - f"'{index_name}'. Driver detail: {error}" - ) - if isinstance(error, ConfigurationError): - configuration_detail: Final = str(error).lower() - if any(marker in configuration_detail for marker in _RESOLUTION_TIMEOUT_MARKERS): - return timeout_error( - "The DNS lookup for the cluster in mongodb_connection_string did not finish in time. " - "A mongodb+srv:// URI needs an SRV lookup before any connection is attempted, so this " - f"is DNS or the configured timeout, not MongoDB. Driver detail: {error}" - ) - if any(marker in configuration_detail for marker in _UNKNOWN_HOSTNAME_MARKERS): - return config_error( - "The hostname in mongodb_connection_string does not exist in DNS. On Atlas, check the " - "cluster name against the URI shown under Connect, Drivers. On a self-managed deployment, " - f"check that the hostname resolves from this process. Driver detail: {error}" - ) - if any(marker in configuration_detail for marker in _CREDENTIAL_ESCAPING_MARKERS): - return config_error( - "mongodb_connection_string could not be parsed. A username or password containing " - "'@', '/', ':' or '%' has to be percent-encoded per RFC 3986, so 'p@ss/word' becomes " - "'p%40ss%2Fword'. If the credentials are already encoded, check the database name in " - f"the URI path instead. Driver detail: {error}" - ) - return config_error( - f"mongodb_connection_string is not a usable MongoDB connection string. Driver detail: {error}" - ) - if isinstance(error, InvalidOperation): - return config_error(f"The MongoDB client was already closed or is unusable. Driver detail: {error}") - # An unreadable tlsCAFile or tlsCertificateKeyFile raises OSError, not a PyMongoError - if isinstance(error, OSError) and error.filename: - return config_error( - f"'{error.filename}', named by a TLS option in mongodb_connection_string, could not be read. " - "Check that tlsCAFile and tlsCertificateKeyFile point at files this process can open; inside " - f"a container that is the path in the container, not on the host. Driver detail: {error}" - ) - # pymongo raises a plain ValueError, not a PyMongoError, for an unusable port - if isinstance(error, ValueError): - return config_error( - "The host and port in mongodb_connection_string could not be parsed. If the port is a " - "number between 0 and 65535, the cause is usually an unescaped ':' in the password, which " - f"has to be percent-encoded per RFC 3986 as '%3A'. Driver detail: {error}" - ) - return error diff --git a/litellm/llms/mongodb/vector_stores/transformation.py b/litellm/llms/mongodb/vector_stores/transformation.py index 3382c931c96..92965ab745b 100644 --- a/litellm/llms/mongodb/vector_stores/transformation.py +++ b/litellm/llms/mongodb/vector_stores/transformation.py @@ -1,37 +1,28 @@ -"""MongoDB Vector Search has no HTTP query API, so this is a direct provider that runs the -``$vectorSearch`` aggregation through pymongo. ``vector_store_id`` is the search index name.""" - -from collections.abc import Callable, Mapping, Sequence +from collections.abc import Mapping, Sequence +from math import isfinite from types import MappingProxyType -from typing import TYPE_CHECKING, Final, NoReturn +from typing import TYPE_CHECKING, Final, Literal, NoReturn +from urllib.parse import quote, urlsplit import httpx -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, TypeAdapter, ValidationError +from litellm.exceptions import AuthenticationError, BadRequestError, ServiceUnavailableError, Timeout +from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.base_llm.vector_store.transformation import ( - BaseDirectVectorStoreConfig, + BaseQueryEmbeddingVectorStoreConfig, LiteLLMVectorStoreEmbeddingExecutor, VectorStoreEmbeddingExecutor, ) -from litellm.llms.mongodb.common_utils import ( - DEFAULT_CONNECT_TIMEOUT_MS, - DEFAULT_SERVER_SELECTION_TIMEOUT_MS, - DEFAULT_SOCKET_TIMEOUT_MS, - MongoClientKey, - config_error, - get_async_client, - get_sync_client, - index_not_ready_error, - missing_index_error, - translate_mongo_error, -) +from litellm.secret_managers.main import get_secret_str +from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import EmbeddingResponse from litellm.types.vector_stores import ( + BaseVectorStoreAuthCredentials, VectorStoreCreateOptionalRequestParams, - VectorStoreResultContent, + VectorStoreIndexEndpoints, VectorStoreSearchOptionalRequestParams, VectorStoreSearchResponse, - VectorStoreSearchResult, ) if TYPE_CHECKING: @@ -39,26 +30,45 @@ if TYPE_CHECKING: DEFAULT_EMBEDDING_FIELD_NAME: Final = "embedding" DEFAULT_TEXT_FIELD_NAME: Final = "text" -SCORE_FIELD_NAME: Final = "score" - DEFAULT_MAX_NUM_RESULTS: Final = 10 MIN_MAX_NUM_RESULTS: Final = 1 MAX_MAX_NUM_RESULTS: Final = 50 - NUM_CANDIDATES_MULTIPLIER: Final = 10 MIN_NUM_CANDIDATES: Final = 100 MAX_NUM_CANDIDATES: Final = 10_000 - MAX_QUERY_CHARACTERS: Final = 32_000 - _EMPTY_EMBEDDING_CONFIG: Final = MappingProxyType({}) - _SEARCH_ONLY_MESSAGE: Final = ( "MongoDB vector store is search-only. Create the collection and its MongoDB Vector Search " "index in MongoDB directly, then register it here by index name." ) +def config_error(message: str) -> BadRequestError: + return BadRequestError(message=message, model=None, llm_provider="mongodb") + + +class _Content(BaseModel): + model_config = ConfigDict(frozen=True, strict=True) + type: Literal["text"] + text: str + + +class _Result(BaseModel): + model_config = ConfigDict(frozen=True, strict=True, allow_inf_nan=False) + score: float | None + content: list[_Content] + file_id: str | None + filename: str | None + + +class _SearchResponse(BaseModel): + model_config = ConfigDict(frozen=True, strict=True) + object: Literal["vector_store.search_results.page"] + search_query: str + data: list[_Result] + + class _MongoDBSearchParams(BaseModel): """Typed view over the vector store's litellm_params; unrelated keys are ignored.""" @@ -66,7 +76,6 @@ class _MongoDBSearchParams(BaseModel): litellm_embedding_model: str | None = None litellm_embedding_config: Mapping[str, object] | None = None - mongodb_connection_string: str | None = None mongodb_database: str | None = None mongodb_collection: str | None = None mongodb_text_field: str | None = None @@ -91,21 +100,6 @@ class _MongoDBSearchParams(BaseModel): ) return self.litellm_embedding_model - def require_connection_string(self) -> str: - if not self.mongodb_connection_string: - raise config_error( - "mongodb_connection_string is required in litellm_params for the MongoDB vector store. " - "Example: mongodb+srv://:@.mongodb.net for Atlas, or " - "mongodb://:@:27017 for a self-managed deployment" - ) - scheme: Final = self.mongodb_connection_string.split("://", 1)[0].lower() - if scheme not in ("mongodb", "mongodb+srv"): - raise config_error( - "mongodb_connection_string must start with 'mongodb://' or 'mongodb+srv://', " - f"got '{self.mongodb_connection_string.split('://', 1)[0]}://'" - ) - return self.mongodb_connection_string - def require_database(self) -> str: if not self.mongodb_database: raise config_error( @@ -127,30 +121,28 @@ _MONGODB_PARAM_PREFIX: Final = "mongodb_" _KNOWN_MONGODB_PARAMS: Final = frozenset( name for name in _MongoDBSearchParams.model_fields if name.startswith(_MONGODB_PARAM_PREFIX) ) +_RESPONSE_ADAPTER: Final = TypeAdapter(VectorStoreSearchResponse) -class MongoDBVectorStoreConfig(BaseDirectVectorStoreConfig): - def __init__( - self, - embedding_executor: VectorStoreEmbeddingExecutor | None = None, - sync_client_factory: Callable[[MongoClientKey], object] | None = None, - async_client_factory: Callable[[MongoClientKey], object] | None = None, - ) -> None: - super().__init__() - self.embedding_executor: Final[VectorStoreEmbeddingExecutor] = ( - embedding_executor if embedding_executor is not None else LiteLLMVectorStoreEmbeddingExecutor() - ) - self.sync_client_factory: Final[Callable[[MongoClientKey], object]] = ( - sync_client_factory if sync_client_factory is not None else get_sync_client - ) - self.async_client_factory: Final[Callable[[MongoClientKey], object]] = ( - async_client_factory if async_client_factory is not None else get_async_client - ) +class MongoDBVectorStoreConfig(BaseQueryEmbeddingVectorStoreConfig): + def __init__(self, embedding_executor: VectorStoreEmbeddingExecutor | None = None) -> None: + self.embedding_executor: Final = embedding_executor or LiteLLMVectorStoreEmbeddingExecutor() + + def get_auth_credentials(self, litellm_params: Mapping[str, object]) -> BaseVectorStoreAuthCredentials: + return BaseVectorStoreAuthCredentials() + + def get_vector_store_endpoints_by_type(self) -> VectorStoreIndexEndpoints: + return VectorStoreIndexEndpoints(read=[], write=[]) # mutable-ok: the TypedDict declares list fields @staticmethod def _reject_unknown_params(litellm_params: Mapping[str, object]) -> None: """Without this a mistyped mongodb_collection reads as 'mongodb_collection is required', naming a key the reader can see they have set.""" + if litellm_params.get("mongodb_connection_string") is not None: + raise config_error( + "MongoDB vector stores now use the BETA sidecar. Move mongodb_connection_string to " + "MONGODB_CONNECTION_STRING in the sidecar, remove it from LiteLLM, and configure api_base and api_key." + ) unknown: Final = sorted( key for key in litellm_params if key.startswith(_MONGODB_PARAM_PREFIX) and key not in _KNOWN_MONGODB_PARAMS ) @@ -191,239 +183,182 @@ class MongoDBVectorStoreConfig(BaseDirectVectorStoreConfig): return configured return min(max(limit * NUM_CANDIDATES_MULTIPLIER, MIN_NUM_CANDIDATES), MAX_NUM_CANDIDATES) - @staticmethod - def _timeout_ms(timeout: float | httpx.Timeout | None) -> tuple[int, int]: - """The connect and socket budgets pymongo is built with, in that order.""" - if isinstance(timeout, httpx.Timeout): - return ( - int((timeout.connect or DEFAULT_CONNECT_TIMEOUT_MS / 1000) * 1000), - int((timeout.read or DEFAULT_SOCKET_TIMEOUT_MS / 1000) * 1000), + def validate_environment( + self, headers: dict[str, object], litellm_params: GenericLiteLLMParams | None + ) -> dict[str, object]: + if litellm_params is None: + raise config_error("Configure api_base and api_key for the MongoDB BETA sidecar.") + self._reject_unknown_params(dict(litellm_params)) + api_key: Final = litellm_params.api_key or get_secret_str("MONGODB_SIDECAR_API_KEY") + if not api_key: + raise config_error("MongoDB sidecar api_key is required. Set api_key or MONGODB_SIDECAR_API_KEY.") + return {**headers, "Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} + + def get_complete_url(self, api_base: str | None, litellm_params: dict[str, object]) -> str: + if not api_base: + raise config_error("MongoDB sidecar api_base is required, for example http://mongodb-sidecar:8080.") + try: + parsed: Final = urlsplit(api_base) + valid: Final = parsed.scheme in ("http", "https") and bool(parsed.hostname) and parsed.port != 0 + except ValueError: + raise config_error("MongoDB sidecar api_base must be a valid HTTP or HTTPS URL.") from None + if not valid or parsed.username or parsed.password or parsed.query or parsed.fragment: + raise config_error( + "MongoDB sidecar api_base must be an HTTP or HTTPS URL without credentials, query, or fragment." ) - if timeout is None: - return DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_SOCKET_TIMEOUT_MS - return min(int(float(timeout) * 1000), DEFAULT_CONNECT_TIMEOUT_MS), int(float(timeout) * 1000) + return api_base.rstrip("/") + + @staticmethod + def _timeout_ms(value: object) -> int: + seconds: Final = value.read if isinstance(value, httpx.Timeout) else value + if seconds is None: + return 30_000 + if not isinstance(seconds, (int, float)) or not isfinite(seconds) or seconds <= 0: + raise config_error("MongoDB search timeout must be a positive finite number.") + return max(1, min(int(seconds * 1000), 30_000)) @classmethod - def _client_key(cls, params: _MongoDBSearchParams, timeout: float | httpx.Timeout | None) -> MongoClientKey: - connect_ms, socket_ms = cls._timeout_ms(timeout) - return MongoClientKey( - connection_string=params.require_connection_string(), - connect_timeout_ms=connect_ms, - socket_timeout_ms=socket_ms, - server_selection_timeout_ms=min(connect_ms, DEFAULT_SERVER_SELECTION_TIMEOUT_MS), - ) + def _params( + cls, + litellm_params: Mapping[str, object], + optional_params: VectorStoreSearchOptionalRequestParams, + extra_body: Mapping[str, object] | None, + ) -> _MongoDBSearchParams: + cls._reject_unknown_params(litellm_params) + if extra_body: + raise config_error("MongoDB vector store does not support extra_body overrides.") + for unsupported in ("filters", "ranking_options", "rewrite_query"): + if optional_params.get(unsupported) is not None: + raise config_error(f"MongoDB vector store does not support the {unsupported} parameter.") + try: + params: Final = _MongoDBSearchParams.model_validate(litellm_params) + except ValidationError: + raise config_error( + "Invalid MongoDB vector-store configuration. Check the database, collection, fields, and candidate count." + ) from None + params.require_database() + params.require_collection() + params.require_embedding_model() + cls._num_candidates(cls._limit(optional_params), params.mongodb_num_candidates) + cls._timeout_ms(litellm_params.get("timeout")) + return params @classmethod - def _pipeline( + def _request( cls, vector_store_id: str, - query_vector: Sequence[float], + query_text: str, params: _MongoDBSearchParams, - vector_store_search_optional_params: VectorStoreSearchOptionalRequestParams, - ) -> Sequence[Mapping[str, object]]: - if vector_store_search_optional_params.get("filters") is not None: + optional_params: VectorStoreSearchOptionalRequestParams, + api_base: str, + embedding_response: EmbeddingResponse, + timeout: object, + ) -> tuple[str, dict[str, object]]: + if not embedding_response.data: raise config_error( - "MongoDB vector store does not support the filters parameter yet. " - "Restrict the collection or the MongoDB Vector Search index definition instead." + "The embedding model returned no embedding for the search query. Check litellm_embedding_model." ) - if vector_store_search_optional_params.get("ranking_options") is not None: - raise config_error( - "MongoDB vector store does not support the ranking_options parameter yet. " - "Every result already carries the vectorSearchScore, so filter or re-rank " - "on that rather than having the threshold silently ignored." - ) - if vector_store_search_optional_params.get("rewrite_query") is not None: - raise config_error( - "MongoDB vector store does not support the rewrite_query parameter. The query is " - "embedded exactly as sent; rewrite it before calling if you need that." - ) - limit: Final = cls._limit(vector_store_search_optional_params) - search: Final = MappingProxyType( - { - "index": vector_store_id, - "path": params.embedding_field, - "queryVector": tuple(query_vector), - "numCandidates": cls._num_candidates(limit, params.mongodb_num_candidates), - "limit": limit, - } - ) - projection: Final = MappingProxyType( - {params.text_field: 1, SCORE_FIELD_NAME: MappingProxyType({"$meta": "vectorSearchScore"})} - ) - return [ # mutable-ok: pymongo rejects any non-list pipeline in common.validate_list - MappingProxyType({"$vectorSearch": search}), - MappingProxyType({"$project": projection}), - ] + vector: Final = embedding_response.data[0]["embedding"] + if not vector or any(not isinstance(value, (float, int)) or not isfinite(value) for value in vector): + raise config_error("The embedding model must return a non-empty, finite query vector.") + limit: Final = cls._limit(optional_params) + return f"{api_base}/v1/vector_stores/{quote(vector_store_id, safe='')}/search", { + "query": query_text, + "query_vector": tuple(vector), + "mongodb_database": params.require_database(), + "mongodb_collection": params.require_collection(), + "mongodb_embedding_field": params.embedding_field, + "mongodb_text_field": params.text_field, + "mongodb_num_candidates": cls._num_candidates(limit, params.mongodb_num_candidates), + "max_num_results": limit, + "timeout_ms": cls._timeout_ms(timeout), + } - @classmethod - def _field_value(cls, document: Mapping[str, object], dotted_path: str) -> str | None: - """None means absent, which is what separates a mistyped field from genuinely empty text.""" - head, _, rest = dotted_path.partition(".") - if head not in document: - return None - value: Final = document[head] - if not rest: - return None if value is None else str(value) - return cls._field_value(value, rest) if isinstance(value, Mapping) else None - - @classmethod - def _to_result(cls, document: Mapping[str, object], text_field: str) -> VectorStoreSearchResult: - document_id: Final = document.get("_id") - identifier: Final = None if document_id is None else str(document_id) - content: Final = [ # mutable-ok: VectorStoreSearchResult declares a list of content parts - VectorStoreResultContent(text=cls._field_value(document, text_field) or "", type="text") - ] - raw_score: Final = document.get(SCORE_FIELD_NAME) - return VectorStoreSearchResult( - score=float(raw_score) if isinstance(raw_score, (int, float)) else None, - content=content, - file_id=identifier, - filename=identifier, - ) - - @classmethod - def _raise_for_missing_text_field( - cls, documents: Sequence[Mapping[str, object]], text_field: str, database: str, collection: str - ) -> None: - """$vectorSearch matches documents carrying no text, so a mistyped mongodb_text_field - returns well-scored results with empty content instead of failing.""" - if documents and all(cls._field_value(document, text_field) is None for document in documents): - raise config_error( - f"None of the {len(documents)} matched documents in '{database}.{collection}' has a " - f"'{text_field}' field, so every result would carry empty text. Set mongodb_text_field " - "to the field holding the readable text; it accepts a dotted path such as metadata.body." - ) - - @classmethod - def _to_response( - cls, documents: Sequence[Mapping[str, object]], query_text: str, text_field: str - ) -> VectorStoreSearchResponse: - return VectorStoreSearchResponse( - object="vector_store.search_results.page", - search_query=query_text, - data=[ # mutable-ok: VectorStoreSearchResponse declares data as a list - cls._to_result(document, text_field) for document in documents - ], - ) - - @staticmethod - def _raise_for_unusable_index( - catalogue: Sequence[Mapping[str, object]], index_name: str, database: str, collection: str - ) -> None: - """mongod returns zero documents both for a query that matched nothing and for a missing - database, collection or index, so the catalogue decides which one happened.""" - if not catalogue: - raise missing_index_error(index_name, database, collection) - entry: Final = catalogue[0] - if not entry.get("queryable"): - raise index_not_ready_error(index_name, database, collection, str(entry.get("status") or "unknown")) - - @staticmethod - def _embedding_vector(embedding_response: EmbeddingResponse) -> Sequence[float]: - data: Final = embedding_response.data - if not data: - raise config_error( - "The embedding model returned no embedding for the search query, so there is nothing " - "to search MongoDB with. Check the embedding deployment named by litellm_embedding_model." - ) - return data[0]["embedding"] - - def execute_search_vector_store_request( + def transform_search_vector_store_request( self, vector_store_id: str, query: str | Sequence[str], vector_store_search_optional_params: VectorStoreSearchOptionalRequestParams, + api_base: str, litellm_logging_obj: "LiteLLMLoggingObj", litellm_params: Mapping[str, object], + extra_body: Mapping[str, object] | None = None, embedding_executor: VectorStoreEmbeddingExecutor | None = None, - timeout: float | httpx.Timeout | None = None, - ) -> VectorStoreSearchResponse: - self._reject_unknown_params(litellm_params) - params: Final = _MongoDBSearchParams.model_validate(litellm_params) + ) -> tuple[str, dict[str, object]]: + params: Final = self._params(litellm_params, vector_store_search_optional_params, extra_body) query_text: Final = self._query_text(query) - key: Final = self._client_key(params, timeout) - database: Final = params.require_database() - collection: Final = params.require_collection() - - embedding_response: Final = (embedding_executor or self.embedding_executor).embed( - params.require_embedding_model(), + response: Final = (embedding_executor or self.embedding_executor).embed( + params.require_embedding_model(), query_text, params.litellm_embedding_config or _EMPTY_EMBEDDING_CONFIG + ) + return self._request( + vector_store_id, query_text, - params.litellm_embedding_config or _EMPTY_EMBEDDING_CONFIG, - ) - pipeline: Final = self._pipeline( - vector_store_id, self._embedding_vector(embedding_response), params, vector_store_search_optional_params + params, + vector_store_search_optional_params, + api_base, + response, + litellm_params.get("timeout"), ) - try: - client: Final = self.sync_client_factory(key) - target: Final = client[database][collection] # pyright: ignore[reportIndexIssue] # factory is typed as returning object so injected doubles are accepted - documents: Final = tuple(target.aggregate(pipeline)) - except Exception as e: - raise translate_mongo_error(e, index_name=vector_store_id, database=database, collection=collection) from e - if not documents: - try: - catalogue: Final = tuple(target.list_search_indexes(vector_store_id)) - except Exception as e: - raise translate_mongo_error( - e, index_name=vector_store_id, database=database, collection=collection - ) from e - self._raise_for_unusable_index(catalogue, vector_store_id, database, collection) - self._raise_for_missing_text_field(documents, params.text_field, database, collection) - return self._to_response(documents, query_text, params.text_field) - - async def aexecute_search_vector_store_request( + async def atransform_search_vector_store_request( self, vector_store_id: str, query: str | Sequence[str], vector_store_search_optional_params: VectorStoreSearchOptionalRequestParams, + api_base: str, litellm_logging_obj: "LiteLLMLoggingObj", litellm_params: Mapping[str, object], + extra_body: Mapping[str, object] | None = None, embedding_executor: VectorStoreEmbeddingExecutor | None = None, - timeout: float | httpx.Timeout | None = None, - ) -> VectorStoreSearchResponse: - self._reject_unknown_params(litellm_params) - params: Final = _MongoDBSearchParams.model_validate(litellm_params) + ) -> tuple[str, dict[str, object]]: + params: Final = self._params(litellm_params, vector_store_search_optional_params, extra_body) query_text: Final = self._query_text(query) - key: Final = self._client_key(params, timeout) - database: Final = params.require_database() - collection: Final = params.require_collection() - - embedding_response: Final = await (embedding_executor or self.embedding_executor).aembed( - params.require_embedding_model(), + response: Final = await (embedding_executor or self.embedding_executor).aembed( + params.require_embedding_model(), query_text, params.litellm_embedding_config or _EMPTY_EMBEDDING_CONFIG + ) + return self._request( + vector_store_id, query_text, - params.litellm_embedding_config or _EMPTY_EMBEDDING_CONFIG, - ) - pipeline: Final = self._pipeline( - vector_store_id, self._embedding_vector(embedding_response), params, vector_store_search_optional_params + params, + vector_store_search_optional_params, + api_base, + response, + litellm_params.get("timeout"), ) + def transform_search_vector_store_response( + self, response: httpx.Response, litellm_logging_obj: "LiteLLMLoggingObj" + ) -> VectorStoreSearchResponse: try: - client: Final = self.async_client_factory(key) - target: Final = client[database][collection] # pyright: ignore[reportIndexIssue] # factory is typed as returning object so injected doubles are accepted - cursor: Final = await target.aggregate(pipeline) - documents: Final = [ # mutable-ok: an async comprehension cannot build a tuple directly - document async for document in cursor - ] - except Exception as e: - raise translate_mongo_error(e, index_name=vector_store_id, database=database, collection=collection) from e - if not documents: - try: - index_cursor: Final = await target.list_search_indexes(vector_store_id) - catalogue: Final = [ # mutable-ok: an async comprehension cannot build a tuple directly - entry async for entry in index_cursor - ] - except Exception as e: - raise translate_mongo_error( - e, index_name=vector_store_id, database=database, collection=collection - ) from e - self._raise_for_unusable_index(catalogue, vector_store_id, database, collection) - self._raise_for_missing_text_field(documents, params.text_field, database, collection) - return self._to_response(documents, query_text, params.text_field) + validated: Final = _SearchResponse.model_validate_json(response.content) + return _RESPONSE_ADAPTER.validate_python(validated.model_dump()) + except ValidationError: + raise ServiceUnavailableError( + message="MongoDB sidecar returned an invalid search response. Check the sidecar version and deployment.", + model=None, + llm_provider="mongodb", + ) from None + + def get_error_class( + self, error_message: str, status_code: int, headers: dict[str, object] | httpx.Headers + ) -> BaseLLMException: + if status_code == 400: + raise config_error(error_message) + if status_code == 401: + raise AuthenticationError(message="MongoDB sidecar rejected api_key.", model=None, llm_provider="mongodb") + if status_code == 408: + raise Timeout(message=error_message, model=None, llm_provider="mongodb") + raise ServiceUnavailableError( + message="MongoDB sidecar is unavailable. Check its address, health, and logs.", + model=None, + llm_provider="mongodb", + ) + + def validate_create_vector_store(self) -> NoReturn: + raise config_error(_SEARCH_ONLY_MESSAGE) def transform_create_vector_store_request( - self, - vector_store_create_optional_params: VectorStoreCreateOptionalRequestParams, - api_base: str, + self, vector_store_create_optional_params: VectorStoreCreateOptionalRequestParams, api_base: str ) -> NoReturn: raise config_error(_SEARCH_ONLY_MESSAGE) diff --git a/pyproject.toml b/pyproject.toml index af35c77d259..b7eecfb2109 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,6 @@ caching = ["diskcache>=5.6.3,<6.0"] mcp = ["mcp>=1.28.1,<2.0"] # Driver for the MongoDB Atlas vector store; Atlas Vector Search has no HTTP query API. # The floor is 4.9 because that is the release AsyncMongoClient landed in. -mongodb = ["pymongo>=4.9,<5.0"] # SAML SSO for the admin UI. python3-saml pulls in xmlsec/lxml, whose wheels # bundle the native libxmlsec1/libxml2 libraries, so no system packages are # required. Kept out of the base `proxy` extra so it stays optional. diff --git a/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py b/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py index f5d31c0da54..bca2b544673 100644 --- a/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py +++ b/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py @@ -1,1537 +1,182 @@ -import asyncio -import gc -import sys -import threading -import weakref -from types import SimpleNamespace -from unittest.mock import MagicMock, patch +import json +from collections.abc import Mapping +from typing import Final +from unittest.mock import MagicMock import httpx import pytest import litellm -from litellm.exceptions import BadRequestError, ServiceUnavailableError, Timeout -from litellm.llms.mongodb.common_utils import ( - _MAX_CACHED_CLIENTS, - _async_clients, - _sync_clients, - MongoClientKey, - index_not_ready_error, - missing_index_error, - get_async_client, - get_sync_client, - reset_client_cache, - translate_mongo_error, -) -from litellm.llms.mongodb.vector_stores.transformation import ( - MongoDBVectorStoreConfig, - _MongoDBSearchParams, -) -from litellm.types.utils import LlmProviders -from litellm.utils import ProviderConfigManager +from litellm.llms.custom_httpx.http_handler import HTTPHandler +from litellm.llms.mongodb.vector_stores.transformation import MongoDBVectorStoreConfig +from litellm.types.utils import EmbeddingResponse +from litellm.types.vector_stores import VectorStoreSearchOptionalRequestParams -CONNECTION_STRING = "mongodb+srv://user:pw@cluster.example.mongodb.net" -INDEX = "movies_vector_index" - -BASE_PARAMS = { - "litellm_embedding_model": "openai/text-embedding-ada-002", - "mongodb_connection_string": CONNECTION_STRING, - "mongodb_database": "sample_mflix", - "mongodb_collection": "embedded_movies", +BASE_PARAMS: Final = { + "api_base": "https://sidecar.example/prefix", + "api_key": "test-sidecar-key", + "litellm_embedding_model": "embedding-alias", + "mongodb_database": "policies", + "mongodb_collection": "documents", +} +RESULT: Final = { + "object": "vector_store.search_results.page", + "search_query": "travel policy", + "data": [ + {"score": 0.9, "file_id": "123", "filename": "123", "content": [{"type": "text", "text": "Use code BLUE-42"}]} + ], } -READY_INDEX = [{"name": INDEX, "status": "READY", "queryable": True}] +class RecordingEmbeddingExecutor: + def __init__(self) -> None: + self.call: Final = MagicMock(return_value=EmbeddingResponse(data=[{"embedding": [0.1, 0.2, 0.3]}])) + + def embed(self, model: str, query: str, configuration: Mapping[str, object]) -> EmbeddingResponse: + return self.call(model, query, configuration) + + async def aembed(self, model: str, query: str, configuration: Mapping[str, object]) -> EmbeddingResponse: + return self.call(model, query, configuration) -class RecordingClient: - """Stands in for pymongo's client class so the cache tests inject a fake rather than - patching the importer, and so they can assert what the client was actually built with.""" - - def __init__(self, connection_string, **kwargs): - self.connection_string = connection_string - self.kwargs = kwargs - - -class FakeCollection: - def __init__(self, documents, error=None, search_indexes=None): - self.documents = documents - self.error = error - self.search_indexes = READY_INDEX if search_indexes is None else search_indexes - self.pipeline = None - self.listed_indexes = [] - - def aggregate(self, pipeline): - self.pipeline = pipeline - if self.error is not None: - raise self.error - return iter(self.documents) - - def list_search_indexes(self, name): - self.listed_indexes.append(name) - return iter(self.search_indexes) - - -class FakeAsyncCollection(FakeCollection): - async def aggregate(self, pipeline): - self.pipeline = pipeline - if self.error is not None: - raise self.error - - async def cursor(): - for document in self.documents: - yield document - - return cursor() - - async def list_search_indexes(self, name): - self.listed_indexes.append(name) - - async def cursor(): - for entry in self.search_indexes: - yield entry - - return cursor() - - -class FakeDatabase: - def __init__(self, collection): - self.collection = collection - self.requested_collection = None - - def __getitem__(self, name): - self.requested_collection = name - return self.collection - - -class FakeClient: - def __init__(self, collection): - self.database = FakeDatabase(collection) - self.requested_database = None - - def __getitem__(self, name): - self.requested_database = name - return self.database - - -class FakeEmbeddingExecutor: - def __init__(self, embedding): - self.embedding = embedding - self.captured = None - - def _respond(self, model, query, configuration): - self.captured = SimpleNamespace(model=model, query=query, configuration=configuration) - return SimpleNamespace(data=[{"embedding": self.embedding}] if self.embedding is not None else []) - - def embed(self, model, query, configuration): - return self._respond(model, query, configuration) - - async def aembed(self, model, query, configuration): - return self._respond(model, query, configuration) - - -def _config(documents=(), embedding=(0.1, 0.2, 0.3), error=None, search_indexes=None): - collection = FakeCollection(list(documents), error, search_indexes) - client = FakeClient(collection) - config = MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor(list(embedding) if embedding is not None else None), - sync_client_factory=lambda key: client, - ) - return config, client, collection - - -def _async_config(documents=(), embedding=(0.1, 0.2, 0.3), error=None, search_indexes=None): - collection = FakeAsyncCollection(list(documents), error, search_indexes) - client = FakeClient(collection) - config = MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor(list(embedding) if embedding is not None else None), - async_client_factory=lambda key: client, - ) - return config, client, collection - - -def _search(config, query="a lone astronaut", optional_params=None, litellm_params=None, timeout=None): - return config.execute_search_vector_store_request( - vector_store_id=INDEX, - query=query, - vector_store_search_optional_params=optional_params or {}, - litellm_logging_obj=MagicMock(), - litellm_params={**BASE_PARAMS, **(litellm_params or {})}, - timeout=timeout, - ) - - -async def _asearch(config, query="a lone astronaut", optional_params=None, litellm_params=None): - return await config.aexecute_search_vector_store_request( - vector_store_id=INDEX, - query=query, - vector_store_search_optional_params=optional_params or {}, - litellm_logging_obj=MagicMock(), - litellm_params={**BASE_PARAMS, **(litellm_params or {})}, - ) - - -def _stage(collection, name): - return next(stage[name] for stage in collection.pipeline if name in stage) - - -def test_search_builds_vector_search_stage_against_the_named_index(): - config, client, collection = _config() - - _search(config, optional_params={"max_num_results": 5}) - - assert client.requested_database == "sample_mflix" - assert client.database.requested_collection == "embedded_movies" - assert _stage(collection, "$vectorSearch") == { - "index": INDEX, - "path": "embedding", - "queryVector": (0.1, 0.2, 0.3), - "numCandidates": 100, - "limit": 5, +@pytest.mark.parametrize("asynchronous", [False, True]) +@pytest.mark.parametrize("limit,candidates", [(None, 100), (1, 100), (50, 500)]) +@pytest.mark.asyncio +async def test_search_preserves_embedding_and_http_contract( + asynchronous: bool, limit: int | None, candidates: int +) -> None: + executor: Final = RecordingEmbeddingExecutor() + config: Final = MongoDBVectorStoreConfig(executor) + params: Final = { + **BASE_PARAMS, + "mongodb_text_field": "metadata.body", + "mongodb_embedding_field": "stored_vector", + "litellm_embedding_config": {"dimensions": 3}, + "timeout": 0.75, } - - -def test_the_pipeline_reaches_pymongo_as_a_list(): - """pymongo's common.validate_list rejects any other sequence with - 'pipeline must be a list, not ', so the outer container is part of the contract.""" - config, _, collection = _config() - - _search(config) - - assert isinstance(collection.pipeline, list) - - -def test_search_projects_the_text_field_and_the_similarity_score(): - config, _, collection = _config() - - _search(config) - - assert _stage(collection, "$project") == {"text": 1, "score": {"$meta": "vectorSearchScore"}} - - -def test_search_defaults_to_ten_results(): - config, _, collection = _config() - - _search(config) - - assert _stage(collection, "$vectorSearch")["limit"] == 10 - - -def test_search_honors_custom_field_names(): - config, _, collection = _config() - - _search( - config, - litellm_params={"mongodb_embedding_field": "plot_embedding", "mongodb_text_field": "plot"}, - ) - - assert _stage(collection, "$vectorSearch")["path"] == "plot_embedding" - assert _stage(collection, "$project") == {"plot": 1, "score": {"$meta": "vectorSearchScore"}} - - -def test_num_candidates_scales_with_the_requested_limit(): - config, _, collection = _config() - - _search(config, optional_params={"max_num_results": 40}) - - assert _stage(collection, "$vectorSearch")["numCandidates"] == 400 - - -def test_num_candidates_can_be_overridden(): - config, _, collection = _config() - - _search(config, optional_params={"max_num_results": 5}, litellm_params={"mongodb_num_candidates": 250}) - - assert _stage(collection, "$vectorSearch")["numCandidates"] == 250 - - -@pytest.mark.parametrize("configured", [4, 10_001]) -def test_num_candidates_below_the_limit_or_above_the_ceiling_is_rejected(configured): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_num_candidates"): - _search(config, optional_params={"max_num_results": 5}, litellm_params={"mongodb_num_candidates": configured}) - - -def test_list_query_is_joined_into_one_embedding_input(): - config, _, _ = _config() - - _search(config, query=["deep", "space", "rescue"]) - - assert config.embedding_executor.captured.query == "deep space rescue" - - -def test_embedding_config_is_expanded_into_the_embedding_call(): - config, _, _ = _config() - - _search(config, litellm_params={"litellm_embedding_config": {"api_base": "https://example.test", "timeout": 7}}) - - captured = config.embedding_executor.captured - assert captured.configuration == {"api_base": "https://example.test", "timeout": 7} - assert captured.model == "openai/text-embedding-ada-002" - - -def test_response_maps_documents_to_openai_shaped_results(): - documents = [ - {"_id": "abc123", "text": "an astronaut adrift", "score": 0.94}, - {"_id": "def456", "text": "a robot dog", "score": 0.81}, - ] - config, _, _ = _config(documents=documents) - - response = _search(config) - - assert response["object"] == "vector_store.search_results.page" - assert response["search_query"] == "a lone astronaut" - assert [result["score"] for result in response["data"]] == [0.94, 0.81] - assert [result["content"][0]["text"] for result in response["data"]] == ["an astronaut adrift", "a robot dog"] - assert [result["file_id"] for result in response["data"]] == ["abc123", "def456"] - assert [result["filename"] for result in response["data"]] == ["abc123", "def456"] - assert response["data"][0]["content"][0]["type"] == "text" - - -def test_response_reads_a_dotted_text_field_path(): - config, _, _ = _config(documents=[{"_id": 1, "metadata": {"body": "nested text"}, "score": 0.5}]) - - response = _search(config, litellm_params={"mongodb_text_field": "metadata.body"}) - - assert response["data"][0]["content"][0]["text"] == "nested text" - - -def test_a_dotted_path_resolves_three_levels_deep(): - config, _, _ = _config(documents=[{"_id": 1, "a": {"b": {"c": "deep text"}}, "score": 0.5}]) - - response = _search(config, litellm_params={"mongodb_text_field": "a.b.c"}) - - assert response["data"][0]["content"][0]["text"] == "deep text" - - -def test_a_dotted_path_that_runs_through_a_scalar_counts_as_absent(): - """Walking 'plot.nope' when plot is a string must report the misconfiguration, not - stringify the scalar and hand the model text from the wrong field.""" - config, _, _ = _config(documents=[{"_id": 1, "plot": "a plain string", "score": 0.5}]) - - with pytest.raises(BadRequestError, match=r"has a 'plot\.nope' field"): - _search(config, litellm_params={"mongodb_text_field": "plot.nope"}) - - -def test_a_non_string_text_field_is_stringified(): - config, _, _ = _config(documents=[{"_id": 1, "year": 1979, "score": 0.5}]) - - response = _search(config, litellm_params={"mongodb_text_field": "year"}) - - assert response["data"][0]["content"][0]["text"] == "1979" - - -def test_a_null_text_field_counts_as_absent(): - config, _, _ = _config(documents=[{"_id": 1, "text": None, "score": 0.5}]) - - with pytest.raises(BadRequestError, match="has a 'text' field"): - _search(config) - - -def test_response_tolerates_a_sparse_document_missing_the_text_field(): - config, _, _ = _config(documents=[{"_id": 1, "score": 0.5}, {"_id": 2, "text": "has text", "score": 0.4}]) - - response = _search(config) - - assert response["data"][0]["content"][0]["text"] == "" - assert response["data"][1]["content"][0]["text"] == "has text" - - -def test_a_present_but_empty_text_field_is_not_treated_as_a_misconfiguration(): - config, _, _ = _config(documents=[{"_id": 1, "text": "", "score": 0.5}]) - - response = _search(config) - - assert response["data"][0]["content"][0]["text"] == "" - - -def test_matches_that_all_lack_the_text_field_name_the_setting_to_fix(): - """Atlas matches on the vector, so a mistyped mongodb_text_field returns confidently - scored results whose content is empty and hands the model an empty context.""" - config, _, _ = _config(documents=[{"_id": 1, "score": 0.9}, {"_id": 2, "score": 0.8}]) - - with pytest.raises(BadRequestError, match="mongodb_text_field"): - _search(config) - - -def test_response_tolerates_a_document_missing_a_score(): - config, _, _ = _config(documents=[{"_id": 1, "text": "no score"}]) - - response = _search(config) - - assert response["data"][0]["score"] is None - - -def test_response_stringifies_a_non_string_document_id(): - config, _, _ = _config(documents=[{"_id": 12345, "text": "numeric id", "score": 0.5}]) - - response = _search(config) - - assert response["data"][0]["file_id"] == "12345" - - -def test_search_requires_an_embedding_model(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="litellm_embedding_model is required"): - config.execute_search_vector_store_request( - vector_store_id=INDEX, - query="q", - vector_store_search_optional_params={}, + kwargs: Final = { + "vector_store_id": "exact index", + "query": ["travel", "policy"], + "vector_store_search_optional_params": {"max_num_results": limit}, + "api_base": BASE_PARAMS["api_base"], + "litellm_logging_obj": MagicMock(), + "litellm_params": params, + } + if asynchronous: + url, body = await config.atransform_search_vector_store_request(**kwargs) + else: + url, body = config.transform_search_vector_store_request(**kwargs) + assert url == "https://sidecar.example/prefix/v1/vector_stores/exact%20index/search" + assert body == { + "query": "travel policy", + "query_vector": (0.1, 0.2, 0.3), + "mongodb_database": "policies", + "mongodb_collection": "documents", + "mongodb_text_field": "metadata.body", + "mongodb_embedding_field": "stored_vector", + "mongodb_num_candidates": candidates, + "max_num_results": limit or 10, + "timeout_ms": 750, + } + executor.call.assert_called_once_with("embedding-alias", "travel policy", {"dimensions": 3}) + assert config.transform_search_vector_store_response(httpx.Response(200, json=RESULT), MagicMock()) == RESULT + + +@pytest.mark.parametrize( + "query,overrides,options", + [ + ("", {}, {}), + (" ", {}, {}), + ("x" * 32_001, {}, {}), + ("travel", {"litellm_embedding_model": None}, {}), + ("travel", {"mongodb_database": None}, {}), + ("travel", {"mongodb_collection": None}, {}), + ("travel", {"mongodb_connection_string": "mongodb://obsolete-secret"}, {}), + ("travel", {"mongodb_filter": {"private": True}}, {}), + ("travel", {"mongodb_num_candidates": 9}, {}), + ("travel", {"mongodb_num_candidates": 10_001}, {}), + ("travel", {}, {"max_num_results": 0}), + ("travel", {}, {"max_num_results": 51}), + ("travel", {}, {"filters": {}}), + ("travel", {}, {"ranking_options": {}}), + ("travel", {}, {"rewrite_query": False}), + ], +) +def test_invalid_search_is_rejected_before_embedding( + query: str, overrides: Mapping[str, object], options: VectorStoreSearchOptionalRequestParams +) -> None: + executor: Final = RecordingEmbeddingExecutor() + config: Final = MongoDBVectorStoreConfig(executor) + with pytest.raises(litellm.BadRequestError) as error: + config.transform_search_vector_store_request( + vector_store_id="policy_index", + query=query, + vector_store_search_optional_params=options, + api_base=BASE_PARAMS["api_base"], litellm_logging_obj=MagicMock(), - litellm_params={k: v for k, v in BASE_PARAMS.items() if k != "litellm_embedding_model"}, + litellm_params={**BASE_PARAMS, **overrides}, ) - - -def test_missing_embedding_model_message_names_the_field_being_searched(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match=r"embedded_movies\.embedding"): - config.execute_search_vector_store_request( - vector_store_id=INDEX, - query="q", - vector_store_search_optional_params={}, - litellm_logging_obj=MagicMock(), - litellm_params={k: v for k, v in BASE_PARAMS.items() if k != "litellm_embedding_model"}, - ) - - -def test_search_requires_a_connection_string(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_connection_string is required"): - _search(config, litellm_params={"mongodb_connection_string": None}) - - -@pytest.mark.parametrize("connection_string", ["postgres://host/db", "https://cluster.mongodb.net", "redis://host"]) -def test_search_rejects_a_non_mongodb_connection_scheme(connection_string): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="must start with 'mongodb://' or 'mongodb\\+srv://'"): - _search(config, litellm_params={"mongodb_connection_string": connection_string}) - - -def test_search_accepts_the_plain_mongodb_scheme(): - config, _, collection = _config() - - _search(config, litellm_params={"mongodb_connection_string": "mongodb://localhost:27017"}) - - assert collection.pipeline is not None - - -def test_search_requires_a_database(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_database is required"): - _search(config, litellm_params={"mongodb_database": None}) - - -def test_search_requires_a_collection(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_collection is required"): - _search(config, litellm_params={"mongodb_collection": None}) - - -def test_search_rejects_filters_rather_than_silently_ignoring_them(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="does not support the filters parameter"): - _search(config, optional_params={"filters": {"genre": "sci-fi"}}) - - -@pytest.mark.asyncio -async def test_async_search_rejects_filters_rather_than_silently_ignoring_them(): - config, _, _ = _async_config() - - with pytest.raises(BadRequestError, match="does not support the filters parameter"): - await _asearch(config, optional_params={"filters": {"genre": "sci-fi"}}) - - -def test_search_rejects_ranking_options_rather_than_silently_ignoring_them(): - """A score_threshold that is quietly dropped is worse than an error: the caller asked for - results above 0.9, gets results scoring 0.5, and nothing says the threshold never ran.""" - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="does not support the ranking_options parameter"): - _search(config, optional_params={"ranking_options": {"score_threshold": 0.9}}) - - -def test_search_rejects_rewrite_query_rather_than_silently_ignoring_it(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="does not support the rewrite_query parameter"): - _search(config, optional_params={"rewrite_query": True}) - - -@pytest.mark.asyncio -async def test_async_search_rejects_ranking_options_rather_than_silently_ignoring_them(): - config, _, _ = _async_config() - - with pytest.raises(BadRequestError, match="does not support the ranking_options parameter"): - await _asearch(config, optional_params={"ranking_options": {"score_threshold": 0.9}}) - - -@pytest.mark.parametrize("query", ["", " ", "\n\t", []]) -def test_search_rejects_an_empty_query(query): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="query must not be empty"): - _search(config, query=query) - - -def test_search_rejects_an_oversized_query(): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="at most 32000 characters"): - _search(config, query="x" * 32_001) - - -def test_search_accepts_a_query_at_the_size_ceiling(): - config, _, collection = _config() - - _search(config, query="x" * 32_000) - - assert collection.pipeline is not None - - -@pytest.mark.parametrize("max_num_results", [0, -1, 51, 1000]) -def test_search_rejects_out_of_range_max_num_results(max_num_results): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="max_num_results must be between 1 and 50"): - _search(config, optional_params={"max_num_results": max_num_results}) - - -@pytest.mark.parametrize("max_num_results", [1, 50]) -def test_search_allows_max_num_results_at_the_bounds(max_num_results): - config, _, collection = _config() - - _search(config, optional_params={"max_num_results": max_num_results}) - - assert _stage(collection, "$vectorSearch")["limit"] == max_num_results - - -def test_search_treats_an_explicit_null_max_num_results_as_the_default(): - config, _, collection = _config() - - _search(config, optional_params={"max_num_results": None}) - - assert _stage(collection, "$vectorSearch")["limit"] == 10 - - -def test_search_fails_when_the_embedding_model_returns_nothing(): - config, _, _ = _config(embedding=None) - - with pytest.raises(BadRequestError, match="returned no embedding"): - _search(config) - - -def test_validation_runs_before_any_connection_is_opened(): - opened = [] - config = MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor([0.1]), - sync_client_factory=lambda key: opened.append(key) or FakeClient(FakeCollection([])), - ) - - with pytest.raises(BadRequestError, match="query must not be empty"): - _search(config, query="") - - assert opened == [] - - -def test_create_vector_store_is_not_supported_and_says_why(): - """litellm.exception_type only passes its own exception types through untouched, so a - NotImplementedError here reaches the caller as APIConnectionError, which the proxy serves - as a 500 with a traceback. Refusing an unsupported operation is a client error.""" - config = MongoDBVectorStoreConfig() - - with pytest.raises(BadRequestError, match="search-only"): - config.transform_create_vector_store_request({}, "https://example.test") - - with pytest.raises(BadRequestError, match="search-only"): - config.transform_create_vector_store_response(httpx.Response(200)) - - -def test_the_create_refusal_survives_the_public_sdk_error_wrapper(): - import litellm - - with pytest.raises(BadRequestError) as raised: - litellm.vector_stores.create(custom_llm_provider="mongodb", name="anything") - - assert "search-only" in str(raised.value) - - -def test_provider_config_manager_returns_the_mongodb_config(): - config = ProviderConfigManager.get_provider_vector_stores_config(LlmProviders.MONGODB) - - assert isinstance(config, MongoDBVectorStoreConfig) - - -@pytest.mark.asyncio -async def test_async_search_builds_the_same_pipeline_and_maps_the_response(): - documents = [{"_id": "abc123", "text": "an astronaut adrift", "score": 0.94}] - config, client, collection = _async_config(documents=documents) - - response = await _asearch(config, optional_params={"max_num_results": 3}) - - assert client.requested_database == "sample_mflix" - assert client.database.requested_collection == "embedded_movies" - assert _stage(collection, "$vectorSearch")["limit"] == 3 - assert _stage(collection, "$vectorSearch")["queryVector"] == (0.1, 0.2, 0.3) - assert response["data"][0]["content"][0]["text"] == "an astronaut adrift" - assert response["data"][0]["score"] == 0.94 - - -@pytest.mark.asyncio -async def test_async_search_requires_an_embedding_model(): - config, _, _ = _async_config() - - with pytest.raises(BadRequestError, match="litellm_embedding_model is required"): - await config.aexecute_search_vector_store_request( - vector_store_id=INDEX, - query="q", - vector_store_search_optional_params={}, - litellm_logging_obj=MagicMock(), - litellm_params={k: v for k, v in BASE_PARAMS.items() if k != "litellm_embedding_model"}, - ) - - -class TestClientCache: - def setup_method(self): - reset_client_cache() - - def teardown_method(self): - reset_client_cache() - - def _key(self, connection_string=CONNECTION_STRING, socket_timeout_ms=30_000): - return MongoClientKey( - connection_string=connection_string, - connect_timeout_ms=10_000, - socket_timeout_ms=socket_timeout_ms, - server_selection_timeout_ms=10_000, - ) - - def test_the_same_connection_reuses_one_client(self): - first = get_sync_client(self._key(), RecordingClient) - second = get_sync_client(self._key(), RecordingClient) - - assert first is second - assert first.connection_string == CONNECTION_STRING - assert first.kwargs["socketTimeoutMS"] == 30_000 - assert first.kwargs["connectTimeoutMS"] == 10_000 - assert first.kwargs["appname"] == "litellm" - - def test_a_different_connection_gets_its_own_client(self): - first = get_sync_client(self._key(), RecordingClient) - second = get_sync_client(self._key(connection_string="mongodb://other.example.test"), RecordingClient) - - assert first is not second - assert second.connection_string == "mongodb://other.example.test" - - def test_a_different_timeout_gets_its_own_client(self): - first = get_sync_client(self._key(), RecordingClient) - second = get_sync_client(self._key(socket_timeout_ms=5_000), RecordingClient) - - assert first is not second - assert second.kwargs["socketTimeoutMS"] == 5_000 - - @pytest.mark.asyncio - async def test_async_clients_are_cached_per_event_loop(self): - first = get_async_client(self._key(), RecordingClient) - second = get_async_client(self._key(), RecordingClient) - - assert first is second - assert first.connection_string == CONNECTION_STRING - - - def _fill_cache(self): - for slot in range(_MAX_CACHED_CLIENTS): - get_sync_client(self._key(f"mongodb://cold-{slot}:27017"), RecordingClient) - - def test_a_store_added_after_the_cache_filled_is_still_cached(self): - """Rebuilding a client costs an SRV lookup, a TLS handshake and topology discovery, so a - store that misses the cache on every single search pays that on every search.""" - self._fill_cache() - latecomer = self._key("mongodb://latecomer:27017") - - first = get_sync_client(latecomer, RecordingClient) - - assert get_sync_client(latecomer, RecordingClient) is first - - def test_the_cache_evicts_the_least_recently_used_client(self): - self._fill_cache() - oldest = self._key("mongodb://cold-0:27017") - newest = self._key(f"mongodb://cold-{_MAX_CACHED_CLIENTS - 1}:27017") - kept = get_sync_client(newest, RecordingClient) - - get_sync_client(self._key("mongodb://latecomer:27017"), RecordingClient) - - assert get_sync_client(newest, RecordingClient) is kept - assert oldest not in _sync_clients - - def test_concurrent_searches_never_trip_over_an_eviction(self): - """Async searches run the sync client through executor threads, so a key can be evicted - between the lookup and the reordering that follows it.""" - errors = [] - churn = _MAX_CACHED_CLIENTS + 2 - - def hammer(offset): - try: - for step in range(3_000): - get_sync_client(self._key(f"mongodb://h-{(step + offset) % churn}:27017"), RecordingClient) - except Exception as e: - errors.append(repr(e)) - - previous = sys.getswitchinterval() - sys.setswitchinterval(1e-9) - try: - threads = [threading.Thread(target=hammer, args=(offset,)) for offset in range(16)] - for thread in threads: - thread.start() - for thread in threads: - thread.join() - finally: - sys.setswitchinterval(previous) - - assert errors == [] - - def test_the_cache_never_grows_past_its_cap(self): - for slot in range(_MAX_CACHED_CLIENTS * 3): - get_sync_client(self._key(f"mongodb://host-{slot}:27017"), RecordingClient) - - assert len(_sync_clients) == _MAX_CACHED_CLIENTS - - def test_a_new_loop_never_inherits_a_closed_loop_client(self): - """CPython recycles id() so aggressively that a fresh event loop almost always lands on - the id of one already collected: measured at 37 of 40 rounds. Keying the cache on the id - alone therefore hands the new loop an AsyncMongoClient bound to a closed loop, and every - operation on it raises "Event loop is closed".""" - - class LoopAgnosticClient: - """Holds no reference to the loop, unlike pymongo's, whose own reference happens to - keep ids from being recycled and hides the bug until the cache fills.""" - - def __init__(self, *args, **kwargs): - self.built_on = None - - key = self._key() - clients_handed_out = [] - - async def fetch(): - return get_async_client(key, LoopAgnosticClient) - - for _ in range(20): - loop = asyncio.new_event_loop() - client = loop.run_until_complete(fetch()) - clients_handed_out.append((client, client.built_on, loop.is_closed())) - client.built_on = weakref.ref(loop) - loop.close() - del loop - gc.collect() - - stale = [ - handed_out - for client, built_on, _ in clients_handed_out - if built_on is not None and (built_on() is None or built_on().is_closed()) - for handed_out in (client,) - ] - assert stale == [], f"{len(stale)} of 20 loops were handed a client built on a closed loop" - - def test_the_cache_releases_clients_built_on_closed_loops(self): - """pymongo's AsyncMongoClient keeps a reference to the loop it was built on, so an entry - for a closed loop holds that client, and its sockets, for the life of the process. A - script calling asyncio.run per search fills the cache to its cap that way: measured live - against Atlas at 32 pinned clients and 212 open descriptors after 40 loops.""" - - class LoopHoldingClient: - def __init__(self, *args, **kwargs): - self.loop = asyncio.get_running_loop() - - key = self._key() - - async def fetch(): - return get_async_client(key, LoopHoldingClient) - - for _ in range(_MAX_CACHED_CLIENTS + 8): - loop = asyncio.new_event_loop() - loop.run_until_complete(fetch()) - loop.close() - - assert len(_async_clients) == 1, f"{len(_async_clients)} closed-loop clients are still cached" - - -class TestClientKeyDerivation: - def test_no_timeout_uses_the_bounded_defaults(self): - key = MongoDBVectorStoreConfig._client_key(_MongoDBSearchParams.model_validate(BASE_PARAMS), None) - - assert key.connect_timeout_ms == 10_000 - assert key.socket_timeout_ms == 30_000 - assert key.server_selection_timeout_ms == 10_000 - - def test_a_numeric_timeout_bounds_the_connect_phase(self): - key = MongoDBVectorStoreConfig._client_key(_MongoDBSearchParams.model_validate(BASE_PARAMS), 3.0) - - assert key.socket_timeout_ms == 3_000 - assert key.connect_timeout_ms == 3_000 - - def test_a_short_timeout_also_shortens_server_selection(self): - """Server selection runs before the connect attempt, so leaving it at the 10s default - would let a caller asking for a 3s budget block for 10s before anything is tried.""" - key = MongoDBVectorStoreConfig._client_key(_MongoDBSearchParams.model_validate(BASE_PARAMS), 3.0) - - assert key.server_selection_timeout_ms == 3_000 - - def test_a_generous_timeout_does_not_raise_server_selection_above_the_default(self): - key = MongoDBVectorStoreConfig._client_key(_MongoDBSearchParams.model_validate(BASE_PARAMS), 120.0) - - assert key.socket_timeout_ms == 120_000 - assert key.server_selection_timeout_ms == 10_000 - - def test_an_httpx_timeout_maps_connect_and_read_separately(self): - key = MongoDBVectorStoreConfig._client_key( - _MongoDBSearchParams.model_validate(BASE_PARAMS), httpx.Timeout(connect=2.0, read=45.0, write=5.0, pool=5.0) - ) - - assert key.connect_timeout_ms == 2_000 - assert key.socket_timeout_ms == 45_000 - - -class TestErrorTranslation: - def _translate(self, error): - return translate_mongo_error(error, index_name=INDEX, database="sample_mflix", collection="embedded_movies") - - def test_server_selection_timeout_points_at_the_atlas_access_list(self): - from pymongo.errors import ServerSelectionTimeoutError - - translated = self._translate(ServerSelectionTimeoutError("no servers")) - - assert "IP access list" in str(translated) - assert "paused cluster" in str(translated) - - def test_authentication_failure_points_at_the_connection_string_credentials(self): - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("auth failed", code=18)) - - assert "rejected the credentials" in str(translated) - - def test_a_dropped_connection_stays_retryable(self): - """A replica set failover reaches the driver as AutoReconnect. litellm only retries 408, - 409, 429 and 5xx, so classifying it as a client error would turn one failover into a - permanently failed search.""" - from pymongo.errors import AutoReconnect - - translated = self._translate(AutoReconnect("connection closed")) - - assert litellm._should_retry(translated.status_code) - assert "dropped or refused" in str(translated) - - def test_a_dropped_connection_still_names_the_misconfigurations_behind_it(self): - """Atlas answers a URI with no credentials by closing the connection rather than failing - auth, so the retryable message still has to name that.""" - from pymongo.errors import AutoReconnect - - translated = self._translate(AutoReconnect("connection closed")) - - assert "no username and password" in str(translated) - assert "mongod is listening" in str(translated) - - def test_the_retryable_classification_survives_the_public_sdk_error_wrapper(self): - """litellm.exception_type only passes its own exception types through; anything else becomes - an APIConnectionError and a 500, which would drop the retryable classification.""" - from pymongo.errors import AutoReconnect - - translated = self._translate(AutoReconnect("connection closed")) - - wrapped = litellm.exception_type( - model=None, - original_exception=translated, - custom_llm_provider="mongodb", - completion_kwargs={}, - extra_kwargs={}, - ) - - assert isinstance(wrapped, ServiceUnavailableError) - assert litellm._should_retry(wrapped.status_code) - - def test_a_pool_wait_queue_timeout_stays_retryable(self): - from pymongo.errors import WaitQueueTimeoutError - - translated = self._translate(WaitQueueTimeoutError("timed out waiting for a connection")) - - assert litellm._should_retry(translated.status_code) - - def test_server_selection_timeout_still_wins_over_the_connection_branch(self): - from pymongo.errors import ServerSelectionTimeoutError - - translated = self._translate(ServerSelectionTimeoutError("no servers")) - - assert isinstance(translated, Timeout) - assert "dropped or refused" not in str(translated) - - def test_network_timeout_still_wins_over_the_connection_branch(self): - from pymongo.errors import NetworkTimeout - - translated = self._translate(NetworkTimeout("socket timed out")) - - assert isinstance(translated, Timeout) - assert "dropped or refused" not in str(translated) - - def test_an_unescaped_password_character_is_a_400_not_a_500(self): - """pymongo's URI parser raises a plain ValueError, not a PyMongoError, for an unusable port, - which is also what an unescaped ':' in a password produces. It must not be a 500.""" - translated = self._translate(ValueError("Port contains non-digit characters")) - - assert isinstance(translated, BadRequestError) - assert "percent-encoded" in str(translated) - - def test_unauthorized_points_at_the_database_user_permissions(self): - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("not authorized", code=13)) - - assert "sample_mflix.embedded_movies" in str(translated) - - def test_code_13_alone_is_enough_without_a_recognisable_message(self): - """The other unauthorized case carries "not authorized", which the message markers also - match, so it cannot tell whether the code is still being checked at all.""" - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("user lacks privileges on this namespace", code=13)) - - assert "rejected the credentials" in str(translated) - assert "sample_mflix.embedded_movies" in str(translated) - - def test_a_missing_index_names_the_index_and_the_collection(self): - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("Index not found for name movies_vector_index", code=27)) - - assert INDEX in str(translated) - assert "READY" in str(translated) - - def test_a_dimension_mismatch_points_at_the_embedding_model(self): - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("queryVector has 1536 dimensions, index expects 2048")) - - assert "litellm_embedding_model must be the same model" in str(translated) - - def test_an_unrecognised_operation_failure_still_names_the_target(self): - from pymongo.errors import OperationFailure - - translated = self._translate(OperationFailure("something else entirely")) - - assert "sample_mflix.embedded_movies" in str(translated) - assert INDEX in str(translated) - - def test_a_configuration_error_points_at_the_connection_string(self): - from pymongo.errors import ConfigurationError - - translated = self._translate(ConfigurationError("bad uri")) - - assert "not a usable MongoDB connection string" in str(translated) - - def test_a_non_driver_error_is_returned_unchanged(self): - original = RuntimeError("unrelated") - - assert self._translate(original) is original - - def test_search_surfaces_a_translated_driver_error(self): - from pymongo.errors import ServerSelectionTimeoutError - - config, _, _ = _config(error=ServerSelectionTimeoutError("no servers")) - - with pytest.raises(Timeout, match="IP access list"): - _search(config) - - @pytest.mark.asyncio - async def test_async_search_surfaces_a_translated_driver_error(self): - from pymongo.errors import OperationFailure - - config, _, _ = _async_config(error=OperationFailure("auth failed", code=18)) - - with pytest.raises(BadRequestError, match="rejected the credentials"): - await _asearch(config) - - -class TestMissingDriver: - def test_the_sync_import_names_the_extra_to_install(self): - from litellm.llms.mongodb.common_utils import import_sync_mongo_client - - with patch.dict(sys.modules, {"pymongo": None}): - with pytest.raises(BadRequestError, match=r"pip install litellm\[mongodb\]"): - import_sync_mongo_client() - - def test_the_async_import_names_the_extra_to_install(self): - from litellm.llms.mongodb.common_utils import import_async_mongo_client - - with patch.dict(sys.modules, {"pymongo": None}): - with pytest.raises(BadRequestError, match=r"pip install litellm\[mongodb\]"): - import_async_mongo_client() - - def test_error_translation_degrades_gracefully_without_the_driver(self): - original = RuntimeError("boom") - - with patch.dict(sys.modules, {"pymongo.errors": None}): - assert translate_mongo_error(original, INDEX, "db", "col") is original - - -class TestEmptyResultsAreDisambiguated: - """$vectorSearch returns zero documents for a missing database, collection or index just as it - does for a query that matched nothing, so an empty result set is checked against the index - catalogue before it is reported as 'no matches'.""" - - def test_a_missing_index_becomes_an_error_rather_than_an_empty_page(self): - config, _, collection = _config(documents=[], search_indexes=[]) - - with pytest.raises(BadRequestError, match="No queryable MongoDB Vector Search index"): - _search(config) - - assert collection.listed_indexes == [INDEX] - - def test_the_missing_index_error_explains_why_mongodb_reported_no_results(self): - config, _, _ = _config(documents=[], search_indexes=[]) - - with pytest.raises(BadRequestError, match="returns no results rather than an error"): - _search(config) - - def test_an_index_still_building_becomes_an_error_naming_its_status(self): - config, _, _ = _config( - documents=[], search_indexes=[{"name": INDEX, "status": "PENDING", "queryable": False}] - ) - - with pytest.raises(BadRequestError, match="not queryable yet; its status is PENDING"): - _search(config) - - def test_a_genuine_no_match_against_a_ready_index_returns_an_empty_page(self): - config, _, collection = _config(documents=[]) - - response = _search(config) - - assert response["data"] == [] - assert response["object"] == "vector_store.search_results.page" - assert collection.listed_indexes == [INDEX] - - def test_the_catalogue_is_not_consulted_when_the_search_returned_hits(self): - config, _, collection = _config(documents=[{"_id": 1, "text": "hit", "score": 0.9}]) - - _search(config) - - assert collection.listed_indexes == [] - - @pytest.mark.asyncio - async def test_async_missing_index_becomes_an_error_rather_than_an_empty_page(self): - config, _, collection = _async_config(documents=[], search_indexes=[]) - - with pytest.raises(BadRequestError, match="No queryable MongoDB Vector Search index"): - await _asearch(config) - - assert collection.listed_indexes == [INDEX] - - @pytest.mark.asyncio - async def test_async_index_still_building_becomes_an_error_naming_its_status(self): - config, _, _ = _async_config( - documents=[], search_indexes=[{"name": INDEX, "status": "PENDING", "queryable": False}] - ) - - with pytest.raises(BadRequestError, match="not queryable yet; its status is PENDING"): - await _asearch(config) - - @pytest.mark.asyncio - async def test_async_genuine_no_match_returns_an_empty_page(self): - config, _, _ = _async_config(documents=[]) - - response = await _asearch(config) - - assert response["data"] == [] - - @pytest.mark.asyncio - async def test_async_catalogue_is_not_consulted_when_the_search_returned_hits(self): - config, _, collection = _async_config(documents=[{"_id": 1, "text": "hit", "score": 0.9}]) - - await _asearch(config) - - assert collection.listed_indexes == [] - - def test_a_failure_while_checking_the_catalogue_is_translated_too(self): - from pymongo.errors import OperationFailure - - class ExplodingCollection(FakeCollection): - def list_search_indexes(self, name): - raise OperationFailure("not authorized", code=13) - - collection = ExplodingCollection([], None, []) - config = MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor([0.1]), - sync_client_factory=lambda key: FakeClient(collection), - ) - - with pytest.raises(BadRequestError, match="lacks read access"): - _search(config) - - -class TestAtlasPlanExecutorErrors: - """Atlas reports a wrong vector path and a dimension mismatch through the same error code, so - each one has to be told apart by its message or both come back as a generic index failure.""" - - def _translate(self, message): - from pymongo.errors import OperationFailure - - return translate_mongo_error( - OperationFailure(message, code=8), - index_name=INDEX, - database="sample_mflix", - collection="embedded_movies", - ) - - def test_a_wrong_vector_path_points_at_the_embedding_field_setting(self): - translated = self._translate( - "PlanExecutor error during aggregation :: caused by :: nope is not indexed as vector" - ) - - assert "mongodb_embedding_field names a field" in str(translated) - - def test_a_dimension_mismatch_is_not_reported_as_a_wrong_path(self): - translated = self._translate( - "PlanExecutor error during aggregation :: caused by :: vector field is indexed with " - "1536 dimensions but queried with 3072" - ) - - assert "does not match the vector dimensions" in str(translated) - assert "mongodb_embedding_field" not in str(translated) - - -class TestErrorsCarryTheRightHttpStatus: - """litellm.exception_type passes a litellm exception through untouched but wraps anything - else into APIConnectionError, which the proxy serves as a 500 with a Python traceback in the - body. A misconfigured connection string is the caller's to fix, so it has to arrive as a 400. - """ - - @pytest.mark.parametrize( - "invoke", - [ - pytest.param(lambda: _search(_config()[0], query=" "), id="empty-query"), - pytest.param( - lambda: _search(_config()[0], optional_params={"max_num_results": 999}), - id="max-num-results-out-of-range", - ), - pytest.param( - lambda: _search(_config()[0], optional_params={"filters": {"genre": "Action"}}), - id="unsupported-filters", - ), - pytest.param( - lambda: _search(_config()[0], litellm_params={"mongodb_connection_string": "postgres://host/db"}), - id="wrong-uri-scheme", - ), - pytest.param( - lambda: _search(_config()[0], litellm_params={"mongodb_database": None}), id="missing-database" - ), - pytest.param( - lambda: _search(_config()[0], litellm_params={"litellm_embedding_model": None}), - id="missing-embedding-model", - ), - ], - ) - def test_configuration_failures_are_400(self, invoke): - with pytest.raises(BadRequestError) as excinfo: - invoke() - assert excinfo.value.status_code == 400 - assert excinfo.value.llm_provider == "mongodb" - - def test_missing_index_is_400(self): - error = missing_index_error("idx", "db", "coll") - assert error.status_code == 400 - assert error.llm_provider == "mongodb" - - def test_index_still_building_is_400(self): - error = index_not_ready_error("idx", "db", "coll", "PENDING") - assert error.status_code == 400 - - def test_unreachable_deployment_is_a_timeout_not_a_bad_request(self): - from pymongo.errors import ServerSelectionTimeoutError - - translated = translate_mongo_error( - ServerSelectionTimeoutError("no servers"), index_name="idx", database="db", collection="coll" - ) - assert isinstance(translated, Timeout) - assert translated.status_code == 408 - - def test_query_execution_timeout_is_a_timeout(self): - from pymongo.errors import ExecutionTimeout - - translated = translate_mongo_error( - ExecutionTimeout("too slow"), index_name="idx", database="db", collection="coll" - ) - assert isinstance(translated, Timeout) - assert translated.status_code == 408 - - def test_unrecognised_errors_are_not_relabelled_as_bad_requests(self): - original = RuntimeError("something else entirely") - assert ( - translate_mongo_error(original, index_name="idx", database="db", collection="coll") - is original - ) - - -def test_atlas_rejected_credentials_are_named_even_though_the_code_is_8000(): - """Atlas answers a wrong password with code 8000 "AtlasError", not the 18 that a - self-hosted deployment returns, so a code-only check reports it as a generic - rejected search and never tells the caller to look at their connection string.""" - from pymongo.errors import OperationFailure - - error = OperationFailure( - "bad auth : authentication failed", - code=8000, - details={"ok": 0, "errmsg": "bad auth : authentication failed", "code": 8000, "codeName": "AtlasError"}, - ) - translated = translate_mongo_error(error, index_name="idx", database="sample_mflix", collection="embedded_movies") - - assert isinstance(translated, BadRequestError) - assert "mongodb_connection_string" in str(translated) - assert "sample_mflix.embedded_movies" in str(translated) - - -def test_a_rejected_search_that_is_not_an_auth_failure_keeps_the_generic_message(): - from pymongo.errors import OperationFailure - - error = OperationFailure("PlanExecutor error", code=8, details={"errmsg": "PlanExecutor error"}) - translated = translate_mongo_error(error, index_name="idx", database="db", collection="coll") - - assert "mongodb_connection_string" not in str(translated) - - -class TestUnrecognisedParameters: - """litellm_params carries plenty of keys this provider does not own, so the params model has - to ignore extras. That turns a mistyped mongodb_collection into 'mongodb_collection is - required', pointing the reader at a key they can see they have set.""" - - def test_a_mistyped_parameter_is_named(self): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_collectoin"): - _search(config, litellm_params={"mongodb_collectoin": "embedded_movies"}) - - def test_the_supported_names_are_listed(self): - config, _, _ = _config() - - with pytest.raises(BadRequestError, match="mongodb_connection_string"): - _search(config, litellm_params={"mongodb_databse": "sample_mflix"}) - - def test_unrelated_litellm_params_are_still_ignored(self): - config, _, _ = _config(documents=[{"_id": 1, "text": "hit", "score": 0.9}]) - - response = _search( - config, - litellm_params={"use_litellm_proxy": False, "use_in_pass_through": False, "vector_store_id": "x"}, - ) - - assert len(response["data"]) == 1 - - @pytest.mark.asyncio - async def test_the_async_path_rejects_them_too(self): - config, _, _ = _async_config() - - with pytest.raises(BadRequestError, match="mongodb_collectoin"): - await _asearch(config, litellm_params={"mongodb_collectoin": "embedded_movies"}) - - -class TestClientConstructionFailures: - """Building the client parses the URI and, for mongodb+srv://, performs a DNS SRV lookup, so it - fails on exactly the inputs a user is most likely to get wrong. Constructing it outside the - translation boundary let those escape as raw pymongo errors, which litellm.exception_type then - wrapped into a 500 with a traceback in the body.""" - - def _config_that_fails_to_connect(self, error): - def factory(_key): - raise error - - return MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor([0.1, 0.2, 0.3]), sync_client_factory=factory - ) - - def _async_config_that_fails_to_connect(self, error): - def factory(_key): - raise error - - return MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor([0.1, 0.2, 0.3]), async_client_factory=factory - ) - - def test_a_malformed_uri_is_a_bad_request_not_a_500(self): - from pymongo.errors import InvalidURI - - config = self._config_that_fails_to_connect(InvalidURI("Invalid URI scheme")) - - with pytest.raises(BadRequestError, match="not a usable MongoDB connection string"): - _search(config) - - def test_an_unresolvable_cluster_name_says_so(self): - from pymongo.errors import ConfigurationError - - config = self._config_that_fails_to_connect(ConfigurationError("The DNS query name does not exist")) - - with pytest.raises(BadRequestError, match="does not exist in DNS"): - _search(config) - - def test_a_dns_lookup_that_ran_out_of_time_is_a_timeout(self): - from pymongo.errors import ConfigurationError - - config = self._config_that_fails_to_connect( - ConfigurationError("The resolution lifetime expired after 0.291 seconds") - ) - - with pytest.raises(Timeout, match="did not finish in time"): - _search(config) - - @pytest.mark.asyncio - async def test_the_async_path_translates_them_too(self): - from pymongo.errors import InvalidURI - - config = self._async_config_that_fails_to_connect(InvalidURI("Invalid URI scheme")) - - with pytest.raises(BadRequestError, match="not a usable MongoDB connection string"): - await _asearch(config) - - -class TestSelfManagedDeploymentsAreFirstClass: - """mongod serves $vectorSearch identically whether mongot runs under Atlas or beside a - self-managed deployment, so an operator without an Atlas account has to be able to act on - every message. Guidance that only names Atlas remedies sends them looking for an IP access - list and a paused cluster that do not exist in their deployment.""" - - def _config_that_fails_to_connect(self, error): - def factory(_key): - raise error - - return MongoDBVectorStoreConfig( - embedding_executor=FakeEmbeddingExecutor([0.1, 0.2, 0.3]), sync_client_factory=factory - ) - - def test_a_plain_mongodb_uri_without_srv_or_credentials_is_accepted(self): - params = _MongoDBSearchParams.model_validate( - {**BASE_PARAMS, "mongodb_connection_string": "mongodb://mongod.internal:27017"} - ) - - assert params.require_connection_string() == "mongodb://mongod.internal:27017" - - def test_an_unreachable_deployment_names_a_self_managed_remedy(self): - from pymongo.errors import ServerSelectionTimeoutError - - config = self._config_that_fails_to_connect(ServerSelectionTimeoutError("connection refused")) - - with pytest.raises(Timeout) as excinfo: - _search(config) - - assert "self-managed" in str(excinfo.value) - assert "host or port" in str(excinfo.value) - - def test_a_refused_connection_names_a_self_managed_remedy(self): - from pymongo.errors import ConnectionFailure - - config = self._config_that_fails_to_connect(ConnectionFailure("connection closed")) - - with pytest.raises(ServiceUnavailableError) as excinfo: - _search(config) - - assert "self-managed" in str(excinfo.value) - assert "mongod is listening" in str(excinfo.value) - - def test_an_unresolvable_hostname_names_a_self_managed_remedy(self): - from pymongo.errors import ConfigurationError - - config = self._config_that_fails_to_connect(ConfigurationError("The DNS query name does not exist")) - - with pytest.raises(BadRequestError) as excinfo: - _search(config) - - assert "self-managed" in str(excinfo.value) - - def test_the_missing_index_message_does_not_claim_atlas(self): - message = str(missing_index_error(INDEX, "sample_mflix", "embedded_movies")) - - assert "MongoDB Vector Search index" in message - assert "Atlas" not in message - - def test_the_not_ready_message_does_not_claim_atlas(self): - message = str(index_not_ready_error(INDEX, "sample_mflix", "embedded_movies", "PENDING")) - - assert "MongoDB Vector Search index" in message - assert "Atlas" not in message - - def test_the_search_only_refusal_does_not_claim_atlas(self): - config = MongoDBVectorStoreConfig() - - with pytest.raises(BadRequestError) as excinfo: - config.transform_create_vector_store_request({}, api_base="") - - assert "Atlas" not in str(excinfo.value) - - def test_a_dimension_mismatch_does_not_claim_atlas(self): - from pymongo.errors import OperationFailure - - error = OperationFailure("vector field is indexed with 128 dimensions but queried with 256") - translated = translate_mongo_error(error, index_name=INDEX, database="db", collection="c") - - assert "Atlas" not in str(translated) - assert "dimensions the index was built for" in str(translated) - - def test_an_uncovered_embedding_field_does_not_claim_atlas(self): - from pymongo.errors import OperationFailure - - error = OperationFailure("embedding is not indexed as vector") - translated = translate_mongo_error(error, index_name=INDEX, database="db", collection="c") - - assert "MongoDB Vector Search index does not cover" in str(translated) - assert "Atlas" not in str(translated) - - def test_a_self_managed_auth_failure_is_still_recognised_by_code_18(self): - from pymongo.errors import OperationFailure - - error = OperationFailure("Authentication failed.", code=18, details={"code": 18}) - translated = translate_mongo_error(error, index_name=INDEX, database="db", collection="c") - - assert isinstance(translated, BadRequestError) - assert "rejected the credentials" in str(translated) - - -class TestUnescapedCredentialsAreDiagnosed: - """Self-managed deployments usually carry a generated password, so '@', '/', ':' and '%' in one - are routine. pymongo reports those as a port, a database name or an RFC 3986 complaint, none of - which points the operator at their password, so each has to be named for what it is. The errors - here come from pymongo's real parser rather than a synthetic stand-in.""" - - @staticmethod - def _real_parse_error(uri): - from pymongo import MongoClient - - try: - MongoClient(uri, serverSelectionTimeoutMS=1) - except Exception as e: - return e - raise AssertionError(f"expected {uri!r} to fail parsing") - - def _translated(self, uri): - return translate_mongo_error( - self._real_parse_error(uri), index_name=INDEX, database="db", collection="c" - ) - - @pytest.mark.parametrize( - "uri", - [ - "mongodb://user:pa@ss@host:27017/", - "mongodb://user:pa:ss@host:27017/", - "mongodb://user:pa%ss@host:27017/", - "mongodb://user@x:pw@host:27017/", - ], - ) - def test_rfc_3986_complaints_tell_the_operator_to_encode_the_password(self, uri): - translated = self._translated(uri) - - assert isinstance(translated, BadRequestError) - assert "percent-encoded per RFC 3986" in str(translated) - - @pytest.mark.parametrize( - "uri", - ["mongodb://user:pa/ss@host:27017/", "mongodb://user/x:pw@host:27017/"], - ) - def test_a_slash_in_the_credentials_is_not_reported_as_a_database_name(self, uri): - translated = self._translated(uri) - - assert isinstance(translated, BadRequestError) - assert "percent-encoded per RFC 3986" in str(translated) - - def test_an_unusable_port_names_the_host_and_port_not_the_database(self): - translated = self._translated("mongodb://host:99999/") - - assert isinstance(translated, BadRequestError) - assert "host and port" in str(translated) - - def test_a_genuinely_bad_database_name_still_mentions_the_uri_path(self): - translated = self._translated("mongodb://host:27017/has space") - - assert isinstance(translated, BadRequestError) - assert "database name in the URI path" in str(translated) - - -class TestUnreadableTlsFilesAreDiagnosed: - """A private CA is how self-managed deployments present TLS, so tlsCAFile and - tlsCertificateKeyFile are on-prem options in practice. pymongo opens those files itself and - lets OSError out, which is not a PyMongoError, so before this they reached the caller as a 500 - with a traceback. The errors here come from pymongo's real TLS setup.""" - - @staticmethod - def _real_tls_error(uri): - from pymongo import MongoClient - - try: - MongoClient(uri, serverSelectionTimeoutMS=1500).admin.command("ping") - except Exception as e: - return e - raise AssertionError(f"expected {uri!r} to fail") - - def _translated(self, uri): - return translate_mongo_error(self._real_tls_error(uri), index_name=INDEX, database="db", collection="c") - - @pytest.mark.parametrize( - "path", - ["/nonexistent-directory-for-tests/ca.pem", "/tmp"], - ) - def test_an_unreadable_ca_file_is_a_400_naming_the_path(self, path): - translated = self._translated(f"mongodb://localhost:27717/?tls=true&tlsCAFile={path}") - - assert isinstance(translated, BadRequestError) - assert path in str(translated) - assert "tlsCAFile" in str(translated) - - def test_an_unreadable_client_certificate_is_a_400_naming_the_path(self): - path = "/nonexistent-directory-for-tests/client.pem" - translated = self._translated(f"mongodb://localhost:27717/?tls=true&tlsCertificateKeyFile={path}") - - assert isinstance(translated, BadRequestError) - assert path in str(translated) - - def test_an_oserror_carrying_no_filename_is_left_for_the_other_branches(self): - translated = translate_mongo_error(OSError("socket hung up"), index_name=INDEX, database="db", collection="c") - - assert not isinstance(translated, BadRequestError) - - -class TestTheCallerSuppliedEmbeddingExecutorIsUsed: - """litellm.vector_stores.search always hands a direct provider an embedding_executor, so the - provider has to accept it and route the query through it rather than its own default.""" - - def test_the_supplied_executor_produces_the_query_vector(self): - config, _, collection = _config(embedding=(0.9, 0.9, 0.9), search_indexes=READY_INDEX) - caller = FakeEmbeddingExecutor([0.4, 0.5, 0.6]) - - config.execute_search_vector_store_request( - vector_store_id=INDEX, - query="a lone astronaut", - vector_store_search_optional_params={}, - litellm_logging_obj=MagicMock(), - litellm_params=BASE_PARAMS, - embedding_executor=caller, - ) - - assert caller.captured.query == "a lone astronaut" - assert _stage(collection, "$vectorSearch")["queryVector"] == (0.4, 0.5, 0.6) - - @pytest.mark.asyncio - async def test_the_supplied_executor_produces_the_query_vector_on_the_async_path(self): - config, _, collection = _async_config(embedding=(0.9, 0.9, 0.9), search_indexes=READY_INDEX) - caller = FakeEmbeddingExecutor([0.4, 0.5, 0.6]) - - await config.aexecute_search_vector_store_request( - vector_store_id=INDEX, - query="a lone astronaut", - vector_store_search_optional_params={}, - litellm_logging_obj=MagicMock(), - litellm_params=BASE_PARAMS, - embedding_executor=caller, - ) - - assert caller.captured.query == "a lone astronaut" - assert _stage(collection, "$vectorSearch")["queryVector"] == (0.4, 0.5, 0.6) + assert "obsolete-secret" not in str(error.value) + executor.call.assert_not_called() + + +@pytest.mark.parametrize( + "status,body,error_type", + [ + (400, {"error": {"message": "Index is not queryable"}}, litellm.BadRequestError), + (401, {}, litellm.AuthenticationError), + (408, {}, litellm.Timeout), + (503, {}, litellm.ServiceUnavailableError), + (200, {}, litellm.ServiceUnavailableError), + (200, {**RESULT, "data": [{"score": "wrong"}]}, litellm.ServiceUnavailableError), + (0, {}, litellm.Timeout), + (-1, {}, litellm.BadRequestError), + (200, RESULT, None), + ], +) +def test_public_sdk_preserves_http_errors_response_and_timeout( + status: int, body: Mapping[str, object], error_type: type[Exception] | None +) -> None: + executor: Final = RecordingEmbeddingExecutor() + if status == -1: + with pytest.raises(litellm.BadRequestError, match="search-only"): + litellm.vector_stores.create(custom_llm_provider="mongodb") + executor.call.assert_not_called() + return + + def respond(request: httpx.Request) -> httpx.Response: + assert request.url == "https://sidecar.example/prefix/v1/vector_stores/policy_index/search" + assert request.headers["authorization"] == "Bearer test-sidecar-key" + assert request.extensions["timeout"]["read"] == 0.75 + payload: Final = json.loads(request.content) + assert payload["timeout_ms"] == 750 + assert payload["query_vector"] == [0.1, 0.2, 0.3] + if status == 0: + raise httpx.ReadTimeout("timed out", request=request) + return httpx.Response(status, json=body) + + with httpx.Client(transport=httpx.MockTransport(respond)) as transport: + client: Final = HTTPHandler(client=transport) + if error_type is not None: + with pytest.raises(error_type): + litellm.vector_stores.search( + vector_store_id="policy_index", + query="travel policy", + custom_llm_provider="mongodb", + _direct_vector_store_embedding_executor=executor, + client=client, + timeout=0.75, + **BASE_PARAMS, + ) + else: + result: Final = litellm.vector_stores.search( + vector_store_id="policy_index", + query="travel policy", + custom_llm_provider="mongodb", + _direct_vector_store_embedding_executor=executor, + client=client, + timeout=0.75, + **BASE_PARAMS, + ) + assert result == RESULT + executor.call.assert_called_once_with("embedding-alias", "travel policy", {}) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx index 84a9314ecce..8da7098b695 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx @@ -69,10 +69,11 @@ describe("VectorStoreForm", () => { }); }); -const MONGODB_URI = "mongodb+srv://user:pass@cluster0.mongodb.net"; +const MONGODB_SIDECAR_URL = "http://mongodb-sidecar:8080"; const MONGODB_REQUIRED_FORM_VALUES = { - mongodb_connection_string: MONGODB_URI, + api_base: MONGODB_SIDECAR_URL, + api_key: "sidecar-test-key", mongodb_database: "sample_mflix", mongodb_collection: "embedded_movies", embedding_model: "text-embedding-ada-002", @@ -127,7 +128,8 @@ describe("buildVectorStoreLitellmParams", () => { mongodb_num_candidates: "200", }; const expected = { - mongodb_connection_string: MONGODB_URI, + api_base: MONGODB_SIDECAR_URL, + api_key: "sidecar-test-key", mongodb_database: "sample_mflix", mongodb_collection: "embedded_movies", mongodb_embedding_field: "plot_embedding", @@ -142,6 +144,7 @@ describe("buildVectorStoreLitellmParams", () => { it("sends only mongodb fields when an earlier provider left values in the form", () => { const formValues = { ...MONGODB_REQUIRED_FORM_VALUES, + mongodb_connection_string: "mongodb://obsolete-credentials", valkey_host: "left-over-from-valkey.example.com", valkey_port: "6379", aws_region_name: "us-west-2", @@ -152,7 +155,8 @@ describe("buildVectorStoreLitellmParams", () => { expect(params).not.toHaveProperty("valkey_host"); expect(params).not.toHaveProperty("valkey_port"); expect(params).not.toHaveProperty("aws_region_name"); - expect(params.mongodb_connection_string).toBe(MONGODB_URI); + expect(params.api_base).toBe(MONGODB_SIDECAR_URL); + expect(params).not.toHaveProperty("mongodb_connection_string"); }); it("omits a blank mongodb_num_candidates so litellm picks its own candidate count", () => { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx index 61da25874a5..67ef1b795ba 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx @@ -70,7 +70,6 @@ const PROVIDER_FIELD_NAMES = [ "vector_bucket_name", "index_name", "aws_region_name", - "mongodb_connection_string", "mongodb_database", "mongodb_collection", "mongodb_embedding_field", @@ -107,7 +106,6 @@ const vectorStoreShape = { vector_bucket_name: optionalText, index_name: optionalText, aws_region_name: optionalText, - mongodb_connection_string: optionalText, mongodb_database: optionalText, mongodb_collection: optionalText, mongodb_embedding_field: optionalText, @@ -142,7 +140,7 @@ const VECTOR_STORE_ID_PLACEHOLDERS: Record = { vertex_rag_engine: '6917529027641081856 (corpus ID from Vertex AI / "RAG Engine" console)', "vertex_ai/search_api": 'my-datastore_1234567890 (data store ID from Vertex AI / "Agent Search" console)', valkey: "my-search-index (FT index name in Valkey)", - mongodb: "my-vector-index (Atlas Vector Search index name)", + mongodb: "my-vector-index (MongoDB Vector Search index name)", }; const VERTEX_SEARCH_API_WITH_ENGINE_PLACEHOLDER = "Any identifier you'll use to reference this in LiteLLM"; diff --git a/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx b/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx index 8e3a3aa3402..32d0f5dccc0 100644 --- a/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx @@ -35,7 +35,8 @@ describe("getVectorStoreProviderLogoAndName", () => { }); expect(vectorStoreProviderMap.MongoDB).toBe("mongodb"); expect(getProviderSpecificFields("mongodb").map((field) => field.name)).toEqual([ - "mongodb_connection_string", + "api_base", + "api_key", "mongodb_database", "mongodb_collection", "embedding_model", @@ -45,12 +46,10 @@ describe("getVectorStoreProviderLogoAndName", () => { ]); }); - it("hides the mongodb connection string, which carries the database password", () => { - const connectionString = getProviderSpecificFields("mongodb").find( - (field) => field.name === "mongodb_connection_string", - ); + it("hides the mongodb sidecar API key", () => { + const apiKey = getProviderSpecificFields("mongodb").find((field) => field.name === "api_key"); - expect(connectionString).toMatchObject({ type: "password", required: true }); + expect(apiKey).toMatchObject({ type: "password", required: true }); }); it("picks the mongodb embedding model from the proxy's models rather than a fixed list", () => { diff --git a/ui/litellm-dashboard/src/components/vector_store_providers.tsx b/ui/litellm-dashboard/src/components/vector_store_providers.tsx index a75f10771a8..6a8b2f405d2 100644 --- a/ui/litellm-dashboard/src/components/vector_store_providers.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_providers.tsx @@ -14,7 +14,7 @@ export enum VectorStoreProviders { OpenAI = "OpenAI", Azure = "Azure OpenAI", Milvus = "Milvus", - MongoDB = "MongoDB Atlas", + MongoDB = "MongoDB (BETA)", Valkey = "Valkey", } @@ -175,18 +175,25 @@ export const vectorStoreProviderFields: Record ], mongodb: [ { - name: "mongodb_connection_string", - label: "Connection String", - tooltip: - "The full MongoDB connection string for your Atlas cluster, including the database user and password. Copy it from Atlas under Connect, Drivers (e.g. mongodb+srv://user:password@cluster.mongodb.net)", - placeholder: "mongodb+srv://user:password@cluster.mongodb.net", + name: "api_base", + label: "Sidecar URL", + tooltip: "The URL of your separately deployed MongoDB sidecar. Configure MongoDB credentials in the sidecar", + placeholder: "http://mongodb-sidecar:8080", + required: true, + type: "text", + }, + { + name: "api_key", + label: "Sidecar API Key", + tooltip: "The MONGODB_SIDECAR_API_KEY configured in your MongoDB sidecar", + placeholder: "Enter sidecar API key", required: true, type: "password", }, { name: "mongodb_database", label: "Database", - tooltip: "The Atlas database holding the collection you want to search", + tooltip: "The MongoDB database holding the collection you want to search", placeholder: "sample_mflix", required: true, type: "text", @@ -194,7 +201,7 @@ export const vectorStoreProviderFields: Record { name: "mongodb_collection", label: "Collection", - tooltip: "The collection your Atlas Vector Search index was built on", + tooltip: "The collection your MongoDB Vector Search index was built on", placeholder: "embedded_movies", required: true, type: "text", @@ -212,7 +219,7 @@ export const vectorStoreProviderFields: Record name: "mongodb_embedding_field", label: "Vector Field Name", tooltip: - "The field in each document that holds its embedding. It must match the path your Atlas Vector Search index was created on (default: embedding)", + "The field in each document that holds its embedding. It must match the path your MongoDB Vector Search index was created on (default: embedding)", placeholder: "embedding", required: false, type: "text", @@ -232,7 +239,7 @@ export const vectorStoreProviderFields: Record name: "mongodb_num_candidates", label: "Candidates Considered", tooltip: - "How many nearest neighbours Atlas examines before returning the top results. Higher is more accurate and slower. Leave blank to let LiteLLM scale it with the requested result count", + "How many nearest neighbours MongoDB examines before returning the top results. Higher is more accurate and slower. Leave blank to let LiteLLM scale it with the requested result count", placeholder: "100", required: false, type: "text", diff --git a/uv.lock b/uv.lock index 89205cd9527..ce5d96f4d9c 100644 --- a/uv.lock +++ b/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-09-02T16:58:34.594994Z" +exclude-newer = "2026-09-05T05:15:35.833796Z" exclude-newer-span = "P3D" [manifest] @@ -4415,9 +4415,6 @@ mcp = [ mlflow = [ { name = "mlflow" }, ] -mongodb = [ - { name = "pymongo" }, -] proxy = [ { name = "apscheduler" }, { name = "azure-identity" }, @@ -4649,7 +4646,6 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.10.0,<3.0.0" }, { name = "pydantic-settings", specifier = ">=2.14.1,<3.0" }, { name = "pyjwt", marker = "extra == 'proxy'", specifier = ">=2.13.0,<3.0" }, - { name = "pymongo", marker = "extra == 'mongodb'", specifier = ">=4.9,<5.0" }, { name = "pynacl", marker = "extra == 'proxy'", specifier = ">=1.6.2,<2.0" }, { name = "pypdf", marker = "extra == 'proxy-runtime'", specifier = ">=6.16.1,<7.0" }, { name = "pyroscope-io", marker = "sys_platform != 'win32' and extra == 'proxy'", specifier = ">=0.8.16,<1.0" }, @@ -4676,7 +4672,7 @@ requires-dist = [ { name = "uvloop", marker = "sys_platform != 'win32' and extra == 'proxy'", specifier = ">=0.22.1,<1.0" }, { name = "websockets", marker = "extra == 'proxy'", specifier = ">=15.0.1,<16.0" }, ] -provides-extras = ["proxy", "cli", "extra-proxy", "utils", "caching", "mcp", "mongodb", "saml", "semantic-router", "mlflow", "grpc", "stt-nvidia-riva", "google", "bedrock-realtime", "proxy-runtime"] +provides-extras = ["proxy", "cli", "extra-proxy", "utils", "caching", "mcp", "saml", "semantic-router", "mlflow", "grpc", "stt-nvidia-riva", "google", "bedrock-realtime", "proxy-runtime"] [package.metadata.requires-dev] ci = [ @@ -7620,77 +7616,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, ] -[[package]] -name = "pymongo" -version = "4.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/64/50be6fbac9c79fe2e4c17401a467da2d8764d82833d83cec325afe5cab32/pymongo-4.17.0.tar.gz", hash = "sha256:70ffa08ba641468cc068cf46c06b34f01a8ce3489f6411309fcb5ceabe6b2fc0", size = 2523370, upload-time = "2026-04-20T16:39:53.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/77/28ebbf69772a4341d530831c7a006cdb06877ac23075cb53b0a227df4fe1/pymongo-4.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47b021363cd923ace5edc7a1d63c0ff8a6d9d43859b8a1ba23645f5afae63221", size = 819234, upload-time = "2026-04-20T16:37:20.888Z" }, - { url = "https://files.pythonhosted.org/packages/88/cf/5a70cee503ff9a2fea20607607f14d189f4d975960ac0945ec306ee7b695/pymongo-4.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:422fa50d7d7f5c22ea0953554396c9ef95684a2d775f860bd75a7b510538dfca", size = 819969, upload-time = "2026-04-20T16:37:24.187Z" }, - { url = "https://files.pythonhosted.org/packages/23/d5/07b7e27e662c58d872efd104a0e8055eb6569aa1b6d4da436f3fdee7f897/pymongo-4.17.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:addd0498ebbdc6354227f6ed457ed9fce442d48a3bb30d5b5bad33e104996561", size = 1244510, upload-time = "2026-04-20T16:37:26.069Z" }, - { url = "https://files.pythonhosted.org/packages/fb/be/7cac5b1e89bd5a8e395067648241390321593a7c29243e36f91343c02a90/pymongo-4.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5c8e180cb2cabe37300e1e36c60aa4f2ff956cc579f0142135a5d2cba252243", size = 1263245, upload-time = "2026-04-20T16:37:28.003Z" }, - { url = "https://files.pythonhosted.org/packages/2e/20/40e8e99824c1fda18261411e65ce3b0cd3d9a6ed3c056cdd0a569adc870b/pymongo-4.17.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bd835cdb37a1adec359dd072c24f8bb14809e2644fde86fab4ee2fc9719b9483", size = 1304113, upload-time = "2026-04-20T16:37:30.048Z" }, - { url = "https://files.pythonhosted.org/packages/3a/94/fb7e25441dd66f2069a9b172380849b0eaa5881c18b3db217bf64a6d393c/pymongo-4.17.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4979e7e8887862bbb44d203f00cc8263a3f27237876fa691b6beba23e40e6d8", size = 1297046, upload-time = "2026-04-20T16:37:32.054Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c9/7352e0c20fe772541556e4d283c05e07ec48f8b0d2737ad930ac4a1b6655/pymongo-4.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77aa4bc164b4de60d5db193b322f0f5b6ead716e831031bfdef8e8bd92205556", size = 1265708, upload-time = "2026-04-20T16:37:33.934Z" }, - { url = "https://files.pythonhosted.org/packages/8d/e4/3df15494c2015ed297958517f0e4f6493e21b00990748068a973e66d45e0/pymongo-4.17.0-cp310-cp310-win32.whl", hash = "sha256:48bbc576677b50af043df870d84ded67cc3a9b4aa7553201beef4da5dc050a0a", size = 805533, upload-time = "2026-04-20T16:37:35.744Z" }, - { url = "https://files.pythonhosted.org/packages/22/fa/b4e71bb8cb82ad7d21bb4e8c476f2d573ba68b20368aac36ef06e4a196b4/pymongo-4.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46767f28dea610e02edf6c5d956ce615c3c7790ea396660b9b1efd5c5ead2e0", size = 815677, upload-time = "2026-04-20T16:37:37.808Z" }, - { url = "https://files.pythonhosted.org/packages/22/e2/0a4bba644f1cda3970ea1012149eeae3594ebfeed3f81fdaf32b61d90c95/pymongo-4.17.0-cp310-cp310-win_arm64.whl", hash = "sha256:757f2a4c0c2c46cab87df0333681ce69e86c9d5b45bc5203ceba5410b3489e59", size = 807293, upload-time = "2026-04-20T16:37:39.707Z" }, - { url = "https://files.pythonhosted.org/packages/c4/e2/336d86f221cf1b56b2ed9330d4a3b98f9f38f0b37829ae9a9184617d5419/pymongo-4.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4141e6c6a339789b2974efa00ecd9409101672d77a0e3ee2cc3839eedf8ec4df", size = 874668, upload-time = "2026-04-20T16:37:41.39Z" }, - { url = "https://files.pythonhosted.org/packages/34/8e/75d3c6c935d187ab59c61e9c15d9aab3f274b563eaf1706e8cae5f508dec/pymongo-4.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e68c76b84e0c132d9dbf9307f12ff8185702328187a87b9aca8c941303873433", size = 875294, upload-time = "2026-04-20T16:37:43.432Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ec/62e855744489dbcd54fd778aae4d80fa4c4819e8fb228ca0cf6f21a03997/pymongo-4.17.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ba2195d4f386f839a52a23ea1cfd60ffaaba78a3d7841db51b7e433001139918", size = 1496233, upload-time = "2026-04-20T16:37:45.518Z" }, - { url = "https://files.pythonhosted.org/packages/82/e8/93e4e5e5ce8fdf8929dabeefe24aafa5ce046028eed0dfa8eeb936e72c49/pymongo-4.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446ff4bfcb6ec2a2e50998c860986a1e992136f998b7f53e7a717fb8aa5a0b9", size = 1522927, upload-time = "2026-04-20T16:37:47.492Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ca/425dc1d21e0f17bdea0072fc463f662f7fa06d2852af52975c9eced3c07c/pymongo-4.17.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2a0d5ac205728c86e0a02192f1aa5f865b0d7d51f8df6101c01a69a7fc620d72", size = 1583468, upload-time = "2026-04-20T16:37:49.221Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9d/f08b07eeffda1a43c1759f0fa625e88ae12360996eb56d42aad832fa7dff/pymongo-4.17.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:485c8a8eaa4c739f00a331fc73757898ee7c092c214a79e63866ff76aaf282ff", size = 1572787, upload-time = "2026-04-20T16:37:51.061Z" }, - { url = "https://files.pythonhosted.org/packages/e9/c2/6855a07aafa7b894929af23675b6fb9634800ce43122b76a62f6eeb8da2a/pymongo-4.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b2dfcc795f5b9fedbe179a11fdf6051581479d196582a3fe819a92a00e9b9969", size = 1526184, upload-time = "2026-04-20T16:37:53.358Z" }, - { url = "https://files.pythonhosted.org/packages/4e/05/c952bac7db71c1942ea3559fcd308b49754cc5004b455935fb4000d1f37b/pymongo-4.17.0-cp311-cp311-win32.whl", hash = "sha256:c2292144505fb12156b981bd440f3dc994a883da06ac726c0c8692ccdbc1c510", size = 852621, upload-time = "2026-04-20T16:37:55.28Z" }, - { url = "https://files.pythonhosted.org/packages/11/c0/c04da9f4c0c6252404598f4e394b862a58a9e866822a70ae261c8a018fdf/pymongo-4.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:2e190827834fce70ecdf9d46796c6dbc0ce08ea87dc2ff5bc6f3f5579b605cb9", size = 867852, upload-time = "2026-04-20T16:37:57.233Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b2/c7b4870fbeef471e947d3e014676f5910d02e0197074d692ebcf24ec049a/pymongo-4.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:a8f9c40a09bb7d4b9fc8b1da65ecf6efa79bda5cb2756f39d9b6940fac1d19ae", size = 855019, upload-time = "2026-04-20T16:37:58.983Z" }, - { url = "https://files.pythonhosted.org/packages/98/90/60bcb508840135d5ee46b51b1a950f548338aa8145a8366dbe6639ae51ac/pymongo-4.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53ffa94b2340dbf6b055e09a0090618c60482c158ecfc9565642fc996bf0944", size = 930529, upload-time = "2026-04-20T16:38:00.936Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e9/313840f1e52c6dfac47f704428cbfbce59956ebe7633bffc92b03f74f0ad/pymongo-4.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6fe0de9d0f6791abce3471230b32b4817bf89d27b1182b6a550e1ec0fa72aa9a", size = 930665, upload-time = "2026-04-20T16:38:02.915Z" }, - { url = "https://files.pythonhosted.org/packages/78/35/9d3565ea45b1606f635c1e2cd2563c28d66caafdc50f7ad7d979fcd1b363/pymongo-4.17.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e537e95514dae1aaa718f481ec03151a0f0394bcd05f1322896d8fc1330cb729", size = 1762369, upload-time = "2026-04-20T16:38:05.375Z" }, - { url = "https://files.pythonhosted.org/packages/95/ee/149b0d4b1a11c38bff6f14c23d5814c9b0843fd6dc38ad40596bdb1a62d2/pymongo-4.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37a8385c29881b43eab31f584100fa0eaddedd5607adf010147ba1810118be90", size = 1798044, upload-time = "2026-04-20T16:38:07.195Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d4/4cee4a7b8d8f6f0550ef6cd2fea42455c5ed619a220cb6ba4fb40d6a5bc8/pymongo-4.17.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3ee3d241ed77a4fc99ce3cff3b289c3ebce37f61fdd7349d3592c23b82c8784", size = 1878567, upload-time = "2026-04-20T16:38:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/45/ef/7fe366c84952619ee2f69973566c214775e083dd4df465751912153e4b72/pymongo-4.17.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9eb5d63a3c518cb0804ed678f5e2b875af032d89a7cf57a57360322cf6a4d222", size = 1864881, upload-time = "2026-04-20T16:38:10.896Z" }, - { url = "https://files.pythonhosted.org/packages/2f/35/b577d82c6d1be7aee7ac7e249bc86f7847998345042e5f8360de238e177b/pymongo-4.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e97e03fa13327c87e3fdc5656acd01e71817f0c1dc3221cd8f30de136bf4ec3", size = 1800349, upload-time = "2026-04-20T16:38:13.589Z" }, - { url = "https://files.pythonhosted.org/packages/b8/69/dafcf04f66e130ddd91aeb92e7a692480eda46dcd04ec1dbe82c06619e10/pymongo-4.17.0-cp312-cp312-win32.whl", hash = "sha256:6877214bff5f06f6884a9fc8d9016a4a7a5f51f537f5c51ac3a576f93e7dfb32", size = 900518, upload-time = "2026-04-20T16:38:15.541Z" }, - { url = "https://files.pythonhosted.org/packages/11/35/5c9262a459f988b4eb2605f70815240b77a0d4131136c4326d18f1822b89/pymongo-4.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:9828485f72f63c7d802e0ec41f71906f633c2692621ab3af55ca990186b091b1", size = 920335, upload-time = "2026-04-20T16:38:17.665Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/e9c7265ee176faccf4e52c4797837e794d93569a1046f6b19a4acc36e5ad/pymongo-4.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:1195370a77baf003b59b10e91ecc4706297197f0dd9d29c840cc556dc08f7cee", size = 903289, upload-time = "2026-04-20T16:38:19.33Z" }, - { url = "https://files.pythonhosted.org/packages/2a/6b/c1206879708b94e82fcd8b9653440ec271f79a3674d122192df383047f5a/pymongo-4.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:809ec74de3b9148ae43fa8df9faf53470f511c8d384f13b99d6f671f2a379f15", size = 985829, upload-time = "2026-04-20T16:38:21.031Z" }, - { url = "https://files.pythonhosted.org/packages/cb/cf/bb044ed85160e5c40f568c7c4f4e8ea16f40764ff5d302e5befbe8f6f814/pymongo-4.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a431b737816bf4cddd4fa0fcef04e424ad36b7692734a64150f872fb8f3208be", size = 985899, upload-time = "2026-04-20T16:38:23.409Z" }, - { url = "https://files.pythonhosted.org/packages/74/0a/f6dfd5ea3901e5d6888da8de8ba728971a1d447debab681cfc56f90d1208/pymongo-4.17.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e4fab10f8403169ce92f3cea921609d9ee81107306caae06c08f592d4b8ad2b5", size = 2028569, upload-time = "2026-04-20T16:38:25.343Z" }, - { url = "https://files.pythonhosted.org/packages/4a/c5/081f59a1c02ae8c0dc73ae58e563838c44eec81aeafa7d0b93a637841c9b/pymongo-4.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20323b0b1c1d33770ad1fc68d429c757734ce9ad3594421c3d6618f10572b1b9", size = 2072916, upload-time = "2026-04-20T16:38:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/31/42/6e41d434297ffe8b30d9c3717916591a4a7be9075a0dcc2fafdfaaaa62ed/pymongo-4.17.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5a5de048e6da5c18e27cc2437e8c15b3b0cdc8385c15b41178b0caa3322a09c2", size = 2173234, upload-time = "2026-04-20T16:38:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/3d/cf/1e4a7db352ef9485831c7268dfe8402f0117b32a9ad54b16e810699e3617/pymongo-4.17.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dff3de1294fbbc1db0ba6b511f77b8e540601d092538a31312e99c8a91a78b1e", size = 2156784, upload-time = "2026-04-20T16:38:32.134Z" }, - { url = "https://files.pythonhosted.org/packages/12/10/6195be29962a61ebb5f4bd9e4c7519890b172f7968a0a0d880398c6ddb02/pymongo-4.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faf03e4c2aafd6de626dbd30ba246d369ae33f47f10629d1bbe40f72115027a6", size = 2074446, upload-time = "2026-04-20T16:38:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/33410b8819837ed370c738587306bdf060b59cef11823be212f4a07703c5/pymongo-4.17.0-cp313-cp313-win32.whl", hash = "sha256:c9786665926a09630c5d420c79762cfadbff35a9438bcbc4c81a9fb5ab9228b7", size = 948435, upload-time = "2026-04-20T16:38:35.922Z" }, - { url = "https://files.pythonhosted.org/packages/6f/77/c0ed522f798a286b99acaa7914ed8d9c80ab091f97f57c59ffed72906e5e/pymongo-4.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:5960519b4d7168f1ecdd3ea10c81b2aedeb9423651aca953cfbc8e76705d3b38", size = 972847, upload-time = "2026-04-20T16:38:37.888Z" }, - { url = "https://files.pythonhosted.org/packages/97/f0/c39480a2db385fde23861d0c8acda41cdaf1d43e46579db72c5c013a2e81/pymongo-4.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:0ff6bd2f735ab5356541e3e57d5b7dbfbc3f2ee1ccb10b6b0f82d58af69d1d8e", size = 951575, upload-time = "2026-04-20T16:38:40.544Z" }, - { url = "https://files.pythonhosted.org/packages/da/49/2b0250762a89737ed6f9cea238331baca061b89a8ddd10dd17fee52c3970/pymongo-4.17.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ff5aa3f1c7e3f08eb0e7a016c91ba468b1850ccfd63d9b1f12f56350f4974cef", size = 1040945, upload-time = "2026-04-20T16:38:42.783Z" }, - { url = "https://files.pythonhosted.org/packages/89/1c/7a9b5447a08be20e84b6e5b17330917e8d6d9507daa3cd099a9309f11ad7/pymongo-4.17.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e816db649ba5d7de0568cf3a9f287a9dc9aad21cf0ca667ab156a7ef47fca0b0", size = 1041187, upload-time = "2026-04-20T16:38:45.358Z" }, - { url = "https://files.pythonhosted.org/packages/78/a1/71704f61632dfc90407a5834fe5f6132854937c4a3648f6c05c351d85a45/pymongo-4.17.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c4fded3a9f1d6a687e36ebd384ac6d00b9b00de1969aa74048e7051ec2a713", size = 2294806, upload-time = "2026-04-20T16:38:47.734Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b9/aff42be75108b96c2469b1d9329b912c15108f3e7ef32fdc86da8423c330/pymongo-4.17.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2db66aa8dd253a0fc1fad3b0d23d5b3993f7ebde02fbbd7727128debf2853675", size = 2348231, upload-time = "2026-04-20T16:38:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/f2/30/44c115b8ba1479942c15fd9480eb29a7da0ba68acd56983423ba0deb4a94/pymongo-4.17.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3987e96e7c7be4083d42e8ac2cc6c0d5b78db9973c90fce42ae800b616ca6b20", size = 2467614, upload-time = "2026-04-20T16:38:52.665Z" }, - { url = "https://files.pythonhosted.org/packages/d2/84/21ee95c8bf0ca7acae7ec7eb365d740bf8fc0156c194baf2c3bdfcb85ec0/pymongo-4.17.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cee36b3c0d0354f880fa7a7fdcdaf2bb5e542c2281e25c1bfadf8cfe21eba7d2", size = 2445970, upload-time = "2026-04-20T16:38:55.175Z" }, - { url = "https://files.pythonhosted.org/packages/06/89/081d7f1809d5ca09d1e47e49f2111b245f5694de3a7af32cd3a353a6f43f/pymongo-4.17.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:320b34457b20bbcc79997801f95d25ce00472915ca5241167242b42c4359e027", size = 2348605, upload-time = "2026-04-20T16:38:57.557Z" }, - { url = "https://files.pythonhosted.org/packages/ea/c3/0d949f9d3f2a341c1f635c398c16615e96f89f51ff424ed81e914cf1a4de/pymongo-4.17.0-cp314-cp314-win32.whl", hash = "sha256:df4a644af9ae132d4bfdb2e9516ea51a615fd881caddfbfbd071cf1354844479", size = 1004119, upload-time = "2026-04-20T16:39:00.309Z" }, - { url = "https://files.pythonhosted.org/packages/f7/55/5c3a3db1048054c695c75c5964cc8bedc2247fdb5a75ef6fab4ec8bb013e/pymongo-4.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:c797f8a80957134f6dd9690367a0f8f5906d672119af2c6aa55f0c527b656bed", size = 1032314, upload-time = "2026-04-20T16:39:02.665Z" }, - { url = "https://files.pythonhosted.org/packages/e0/19/e235f39906134cb0ffd5574c5a59c355ef5380f0499644ab94994afbb109/pymongo-4.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:68fca71e05ee5da23a8d73cee8379dfb3d26e609a377cae731d742771ed96946", size = 1007627, upload-time = "2026-04-20T16:39:04.678Z" }, - { url = "https://files.pythonhosted.org/packages/1e/e0/c4c1a86791415b14c684fa0908f9da96de91594a3fd1fa1b8dc689fbb800/pymongo-4.17.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b4384700cffc3f1dd98e088bc0072dedf6d7d68a230bb4b972665cf69c071c1e", size = 1099151, upload-time = "2026-04-20T16:39:06.969Z" }, - { url = "https://files.pythonhosted.org/packages/81/4b/69c67f3e23fd9b23b9bedc7ebd23754881cc9d5c5d5b2a9811e96b07f475/pymongo-4.17.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:93641192644fa1ee0f34030e774fd31022a27ad11ba22cb1716142231524f8bd", size = 1099346, upload-time = "2026-04-20T16:39:08.996Z" }, - { url = "https://files.pythonhosted.org/packages/a2/19/a5208f62f9508a26d73acc69bd3821b8c8adae253679a3c26d2f9652f0d5/pymongo-4.17.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:75bc3aa5b94fdb7138d357ec6ca61cd97e0c79f4f7f0bd3efe9639b15cc50942", size = 2619034, upload-time = "2026-04-20T16:39:11.049Z" }, - { url = "https://files.pythonhosted.org/packages/77/27/426cba1ec5973082a56d4150798529bfdf4151c31391ed1fbbecb23ef2ac/pymongo-4.17.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e8f8e23c6df7c6d6929f5e734980b227706e73ee847517c9ba5af90f7fc466", size = 2689939, upload-time = "2026-04-20T16:39:13.617Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2e/f70993d1255e33f6ee59a4ec4371cc65bff7a7e3fda7d55c3386f25287e8/pymongo-4.17.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:15d3f3d732aecac1f8d481bde4029755615639bd3076f258a2147210aec8515a", size = 2824994, upload-time = "2026-04-20T16:39:16.057Z" }, - { url = "https://files.pythonhosted.org/packages/b3/eb/87b0e988ba889e1fcc3430c2cfc166b251872c813e92b43174298bee17ff/pymongo-4.17.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5f62862d0f87be481fa1fe8cb811994486773c94a2b61e509285e3f2890763", size = 2801745, upload-time = "2026-04-20T16:39:18.476Z" }, - { url = "https://files.pythonhosted.org/packages/67/4c/3f83412d086f682d4d468761d66ddc49cf161e786ea74073045eb4491c60/pymongo-4.17.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64837adbbd72073301af51bb0fc80e3d7707fe5527cea1033ba0320f0b2f881b", size = 2684636, upload-time = "2026-04-20T16:39:20.878Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d8/b75f6f4ab6c8beb50b0270a4f1e2530b5774f5e116563440e1677ca1820f/pymongo-4.17.0-cp314-cp314t-win32.whl", hash = "sha256:b93b22eedc62598cf5ee9d8c8007a8e9121c50fd88137012d8985500e9dc3151", size = 1056356, upload-time = "2026-04-20T16:39:22.996Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5e/648c8a238eef18a25ed8a169ea6542d4a860bbec3e95b3d9badac2935c71/pymongo-4.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3689ea34f6b647c7d1e7bdc60fcfb214b2789ed1359a7fb96569c69f50e5f18f", size = 1090964, upload-time = "2026-04-20T16:39:24.989Z" }, - { url = "https://files.pythonhosted.org/packages/dc/cb/d9780b66939c4fc1f024bcc7be23a2abcfe06a9745ca8fa76dc73395482e/pymongo-4.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9543d8f84c2e5608565c08ac679774811e6730770d8a645439b073422a4276fb", size = 1058526, upload-time = "2026-04-20T16:39:27.924Z" }, -] - [[package]] name = "pynacl" version = "1.6.2" From 0c519b162f2ca44effc7ff5bd14d80e9cb8935ae Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Mon, 7 Sep 2026 23:43:21 -0700 Subject: [PATCH 086/223] fix: preserve MongoDB deadlines and secure remote sidecar transport --- .../mongodb/vector_stores/transformation.py | 19 ++- .../test_mongodb_transformation.py | 114 ++++++++++++------ .../_components/VectorStoreForm.test.tsx | 2 +- .../src/components/vector_store_providers.tsx | 4 +- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/litellm/llms/mongodb/vector_stores/transformation.py b/litellm/llms/mongodb/vector_stores/transformation.py index 92965ab745b..606da9cffb4 100644 --- a/litellm/llms/mongodb/vector_stores/transformation.py +++ b/litellm/llms/mongodb/vector_stores/transformation.py @@ -1,4 +1,5 @@ from collections.abc import Mapping, Sequence +from ipaddress import ip_address from math import isfinite from types import MappingProxyType from typing import TYPE_CHECKING, Final, Literal, NoReturn @@ -196,7 +197,7 @@ class MongoDBVectorStoreConfig(BaseQueryEmbeddingVectorStoreConfig): def get_complete_url(self, api_base: str | None, litellm_params: dict[str, object]) -> str: if not api_base: - raise config_error("MongoDB sidecar api_base is required, for example http://mongodb-sidecar:8080.") + raise config_error("MongoDB sidecar api_base is required, for example http://127.0.0.1:8080.") try: parsed: Final = urlsplit(api_base) valid: Final = parsed.scheme in ("http", "https") and bool(parsed.hostname) and parsed.port != 0 @@ -206,6 +207,17 @@ class MongoDBVectorStoreConfig(BaseQueryEmbeddingVectorStoreConfig): raise config_error( "MongoDB sidecar api_base must be an HTTP or HTTPS URL without credentials, query, or fragment." ) + if parsed.scheme == "http": + try: + loopback: Final = ip_address(parsed.hostname or "").is_loopback + except ValueError: + raise config_error( + "MongoDB sidecar requires HTTPS. HTTP is supported only for a loopback IP such as 127.0.0.1." + ) from None + if not loopback: + raise config_error( + "MongoDB sidecar requires HTTPS. HTTP is supported only for a loopback IP such as 127.0.0.1." + ) return api_base.rstrip("/") @staticmethod @@ -215,7 +227,10 @@ class MongoDBVectorStoreConfig(BaseQueryEmbeddingVectorStoreConfig): return 30_000 if not isinstance(seconds, (int, float)) or not isfinite(seconds) or seconds <= 0: raise config_error("MongoDB search timeout must be a positive finite number.") - return max(1, min(int(seconds * 1000), 30_000)) + try: + return max(1, int(seconds * 1000)) + except (ValueError, OverflowError): + raise config_error("MongoDB search timeout must be a positive finite number.") from None @classmethod def _params( diff --git a/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py b/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py index bca2b544673..9de473fb1f0 100644 --- a/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py +++ b/tests/test_litellm/llms/mongodb/vector_stores/test_mongodb_transformation.py @@ -7,10 +7,10 @@ import httpx import pytest import litellm -from litellm.llms.custom_httpx.http_handler import HTTPHandler +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.llms.mongodb.vector_stores.transformation import MongoDBVectorStoreConfig from litellm.types.utils import EmbeddingResponse -from litellm.types.vector_stores import VectorStoreSearchOptionalRequestParams +from litellm.types.vector_stores import VectorStoreSearchOptionalRequestParams, VectorStoreSearchResponse BASE_PARAMS: Final = { "api_base": "https://sidecar.example/prefix", @@ -131,52 +131,92 @@ def test_invalid_search_is_rejected_before_embedding( (200, {**RESULT, "data": [{"score": "wrong"}]}, litellm.ServiceUnavailableError), (0, {}, litellm.Timeout), (-1, {}, litellm.BadRequestError), + (-2, {"api_base": "http://sidecar.example"}, litellm.BadRequestError), + (-2, {"api_base": "http://10.0.0.10:8080"}, litellm.BadRequestError), + (-2, {"api_base": "http://localhost:8080"}, litellm.BadRequestError), (200, RESULT, None), ], ) -def test_public_sdk_preserves_http_errors_response_and_timeout( - status: int, body: Mapping[str, object], error_type: type[Exception] | None +@pytest.mark.parametrize("asynchronous", [False, True]) +@pytest.mark.parametrize("timeout", [0.75, 120.0]) +@pytest.mark.parametrize("api_base", ["https://sidecar.example/prefix", "http://127.0.0.1:8080", "http://[::1]:8080"]) +@pytest.mark.asyncio +async def test_public_sdk_preserves_http_errors_response_and_timeout( + status: int, + body: Mapping[str, object], + error_type: type[Exception] | None, + asynchronous: bool, + timeout: float, + api_base: str, ) -> None: executor: Final = RecordingEmbeddingExecutor() if status == -1: - with pytest.raises(litellm.BadRequestError, match="search-only"): - litellm.vector_stores.create(custom_llm_provider="mongodb") - executor.call.assert_not_called() + if asynchronous: + with pytest.raises(litellm.BadRequestError, match="search-only"): + await litellm.vector_stores.acreate(custom_llm_provider="mongodb") + else: + with pytest.raises(litellm.BadRequestError, match="search-only"): + litellm.vector_stores.create(custom_llm_provider="mongodb") return - - def respond(request: httpx.Request) -> httpx.Response: - assert request.url == "https://sidecar.example/prefix/v1/vector_stores/policy_index/search" - assert request.headers["authorization"] == "Bearer test-sidecar-key" - assert request.extensions["timeout"]["read"] == 0.75 - payload: Final = json.loads(request.content) - assert payload["timeout_ms"] == 750 - assert payload["query_vector"] == [0.1, 0.2, 0.3] - if status == 0: - raise httpx.ReadTimeout("timed out", request=request) - return httpx.Response(status, json=body) - - with httpx.Client(transport=httpx.MockTransport(respond)) as transport: - client: Final = HTTPHandler(client=transport) - if error_type is not None: - with pytest.raises(error_type): + if status == -2: + rejected_params: Final = {**BASE_PARAMS, "api_base": str(body["api_base"])} + if asynchronous: + with pytest.raises(litellm.BadRequestError, match="requires HTTPS"): + await litellm.vector_stores.asearch( + vector_store_id="policy_index", + query="travel policy", + custom_llm_provider="mongodb", + _direct_vector_store_embedding_executor=executor, + **rejected_params, + ) + else: + with pytest.raises(litellm.BadRequestError, match="requires HTTPS"): litellm.vector_stores.search( vector_store_id="policy_index", query="travel policy", custom_llm_provider="mongodb", _direct_vector_store_embedding_executor=executor, - client=client, - timeout=0.75, - **BASE_PARAMS, + **rejected_params, ) - else: - result: Final = litellm.vector_stores.search( - vector_store_id="policy_index", - query="travel policy", - custom_llm_provider="mongodb", - _direct_vector_store_embedding_executor=executor, - client=client, - timeout=0.75, - **BASE_PARAMS, - ) - assert result == RESULT + executor.call.assert_not_called() + return + + def respond(request: httpx.Request) -> httpx.Response: + assert request.url == f"{api_base}/v1/vector_stores/policy_index/search" + assert request.headers["authorization"] == "Bearer test-sidecar-key" + assert request.extensions["timeout"]["read"] == timeout + payload: Final = json.loads(request.content) + assert payload["timeout_ms"] == int(timeout * 1000) + assert payload["query_vector"] == [0.1, 0.2, 0.3] + if status == 0: + raise httpx.ReadTimeout("timed out", request=request) + return httpx.Response(status, json=body) + + async with httpx.AsyncClient(transport=httpx.MockTransport(respond)) as async_transport: + with httpx.Client(transport=httpx.MockTransport(respond)) as transport: + client: Final = AsyncHTTPHandler() if asynchronous else HTTPHandler(client=transport) + if isinstance(client, AsyncHTTPHandler): + await client.client.aclose() + client.client = async_transport + + async def search() -> VectorStoreSearchResponse: + kwargs: Final = { + **BASE_PARAMS, + "api_base": api_base, + "vector_store_id": "policy_index", + "query": "travel policy", + "custom_llm_provider": "mongodb", + "_direct_vector_store_embedding_executor": executor, + "client": client, + "timeout": timeout, + } + if asynchronous: + return await litellm.vector_stores.asearch(**kwargs) + return litellm.vector_stores.search(**kwargs) + + if error_type is not None: + with pytest.raises(error_type): + await search() + else: + assert await search() == RESULT executor.call.assert_called_once_with("embedding-alias", "travel policy", {}) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx index 8da7098b695..03020bbc4a5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx @@ -69,7 +69,7 @@ describe("VectorStoreForm", () => { }); }); -const MONGODB_SIDECAR_URL = "http://mongodb-sidecar:8080"; +const MONGODB_SIDECAR_URL = "http://127.0.0.1:8080"; const MONGODB_REQUIRED_FORM_VALUES = { api_base: MONGODB_SIDECAR_URL, diff --git a/ui/litellm-dashboard/src/components/vector_store_providers.tsx b/ui/litellm-dashboard/src/components/vector_store_providers.tsx index 6a8b2f405d2..79e711f6f98 100644 --- a/ui/litellm-dashboard/src/components/vector_store_providers.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_providers.tsx @@ -177,8 +177,8 @@ export const vectorStoreProviderFields: Record { name: "api_base", label: "Sidecar URL", - tooltip: "The URL of your separately deployed MongoDB sidecar. Configure MongoDB credentials in the sidecar", - placeholder: "http://mongodb-sidecar:8080", + tooltip: "Use HTTPS for a remote sidecar, or HTTP with a loopback IP for a sidecar on the same host or Pod", + placeholder: "http://127.0.0.1:8080", required: true, type: "text", }, From 78893723f7b65cb73d2c5285cb412b25764a3857 Mon Sep 17 00:00:00 2001 From: Oliver Jensen Date: Tue, 8 Sep 2026 13:05:38 +0200 Subject: [PATCH 087/223] fix(auth): drop env-credential hint from 401 when env login is disabled --- litellm/proxy/auth/login_utils.py | 7 ++++++- tests/test_litellm/proxy/auth/test_login_utils.py | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/auth/login_utils.py b/litellm/proxy/auth/login_utils.py index 6ca7ea5074d..c0a76a4fc20 100644 --- a/litellm/proxy/auth/login_utils.py +++ b/litellm/proxy/auth/login_utils.py @@ -362,8 +362,13 @@ async def authenticate_user( code=401, ) else: + env_credentials_hint: Final = ( + "\nCheck 'UI_USERNAME', 'UI_PASSWORD' in .env file" + if is_env_credential_login_enabled(general_settings) + else "" + ) raise ProxyException( - message="Invalid credentials used to access UI.\nCheck 'UI_USERNAME', 'UI_PASSWORD' in .env file", + message=f"Invalid credentials used to access UI.{env_credentials_hint}", type=ProxyErrorTypes.auth_error, param="invalid_credentials", code=401, diff --git a/tests/test_litellm/proxy/auth/test_login_utils.py b/tests/test_litellm/proxy/auth/test_login_utils.py index 72cc6e04a12..e209a491b0a 100644 --- a/tests/test_litellm/proxy/auth/test_login_utils.py +++ b/tests/test_litellm/proxy/auth/test_login_utils.py @@ -186,6 +186,7 @@ async def test_authenticate_user_invalid_credentials(): assert exc_info.value.type == ProxyErrorTypes.auth_error assert exc_info.value.code == "401" assert "Invalid credentials" in exc_info.value.message + assert "UI_USERNAME" in exc_info.value.message @pytest.mark.asyncio @@ -829,6 +830,8 @@ class TestDisableEnvCredentialLogin: assert exc_info.value.type == ProxyErrorTypes.auth_error assert exc_info.value.code == "401" + assert "UI_USERNAME" not in exc_info.value.message + assert "UI_PASSWORD" not in exc_info.value.message @pytest.mark.asyncio async def test_rejects_master_key_fallback_when_disabled(self): From 992ee37258e7f3c9ed13c374610899388bc8d446 Mon Sep 17 00:00:00 2001 From: Oliver Jensen Date: Tue, 8 Sep 2026 13:21:45 +0200 Subject: [PATCH 088/223] Update litellm/proxy/health_endpoints/_health_endpoints.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- litellm/proxy/health_endpoints/_health_endpoints.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index e5e4f89234b..747cfa526d9 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -1583,16 +1583,6 @@ async def _show_no_redis_warning() -> bool: def _show_env_credential_login_warning() -> bool: - """ - Whether the UI should warn admins that env-credential login is still enabled. - - UI_USERNAME/UI_PASSWORD (or the master key, when UI_PASSWORD is unset) grant - proxy-admin access with a shared static secret: no per-person identity, no - audit trail, no password policy, and it stays valid until the env var or - master key rotates. That is fine for first-time setup, so it is on by - default, but once real admin accounts exist it should be turned off with - `general_settings.disable_env_credential_login`. - """ from litellm.proxy.auth.login_utils import is_env_credential_login_enabled from litellm.proxy.proxy_server import general_settings From deaadc21d3c84a9eb32c64e6a1e96c5852afdbb6 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:08:03 -0700 Subject: [PATCH 089/223] fix(fireworks_ai): fold instructions and developer items into one leading system message on the Responses path --- .../fireworks_ai/responses/transformation.py | 44 ++++++++---- ...t_fireworks_ai_responses_transformation.py | 70 ++++++++++++++++++- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/litellm/llms/fireworks_ai/responses/transformation.py b/litellm/llms/fireworks_ai/responses/transformation.py index fb0587553d4..61ef0731900 100644 --- a/litellm/llms/fireworks_ai/responses/transformation.py +++ b/litellm/llms/fireworks_ai/responses/transformation.py @@ -5,6 +5,7 @@ from urllib.parse import unquote import httpx from openai.types.responses import EasyInputMessageParam, ResponseInputItemParam +from pydantic import TypeAdapter from litellm.llms.fireworks_ai.common_utils import ( resolve_fireworks_api_key, @@ -31,16 +32,32 @@ def _session_params(litellm_params: GenericLiteLLMParams) -> Mapping[str, object ) -def _developer_item_as_system(item: ResponseInputItemParam) -> ResponseInputItemParam: - if "role" not in item or item["role"] != "developer": - return item - return EasyInputMessageParam(role="system", content=item["content"], type="message") +_instructions_adapter: Final = TypeAdapter[str | None](str | None) -def _developer_items_as_system(input: str | ResponseInputParam) -> str | ResponseInputParam: - if isinstance(input, str): +def _instruction_text(item: ResponseInputItemParam) -> str | None: + if "role" not in item or (item["role"] != "system" and item["role"] != "developer"): + return None + content: Final = item["content"] + if isinstance(content, str): + return content + return "\n\n".join(part["text"] for part in content if part["type"] == "input_text") + + +def _with_single_leading_system_item( + input: str | ResponseInputParam, instructions: str | None +) -> str | ResponseInputParam: + items: Final = () if isinstance(input, str) else tuple(input) + instruction_texts: Final = tuple(text for text in (instructions, *map(_instruction_text, items)) if text) + if not instruction_texts: return input - return [_developer_item_as_system(item) for item in input] + leading: Final = EasyInputMessageParam(role="system", content="\n\n".join(instruction_texts), type="message") + rest: Final = ( + (EasyInputMessageParam(role="user", content=input),) + if isinstance(input, str) + else tuple(item for item in items if _instruction_text(item) is None) + ) + return [leading, *rest] class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig): @@ -68,9 +85,6 @@ class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig): base: Final = (api_base or get_secret_str("FIREWORKS_API_BASE") or FIREWORKS_AI_DEFAULT_API_BASE).rstrip("/") return f"{base}/responses" - def _validate_input_param(self, input: str | ResponseInputParam) -> str | ResponseInputParam: - return _developer_items_as_system(super()._validate_input_param(input)) - def transform_responses_api_request( self, model: str, @@ -79,10 +93,16 @@ class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig): litellm_params: GenericLiteLLMParams, headers: dict, # mutable-ok: overrides the base class signature ) -> dict: # mutable-ok: overrides the base class signature + instructions: Final = _instructions_adapter.validate_python( + response_api_optional_request_params.get("instructions") + ) + folded_params: Final = { # mutable-ok: the base class takes the optional params as a dict + key: value for key, value in response_api_optional_request_params.items() if key != "instructions" + } return super().transform_responses_api_request( model=resolve_fireworks_resource_name(model), - input=input, - response_api_optional_request_params=response_api_optional_request_params, + input=_with_single_leading_system_item(self._validate_input_param(input), instructions), + response_api_optional_request_params=folded_params, litellm_params=litellm_params, headers=headers, ) diff --git a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py index b9408d44e9a..3462e041a19 100644 --- a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py +++ b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py @@ -162,7 +162,7 @@ def test_responses_call_forwards_previous_response_id_and_store() -> None: assert body["input"][0]["call_id"] == "call_abc123" -def test_responses_call_sends_developer_items_as_system_messages() -> None: +def test_responses_call_hoists_developer_items_into_one_leading_system_message() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): litellm.responses( @@ -176,12 +176,78 @@ def test_responses_call_sends_developer_items_as_system_messages() -> None: ) _, _, body = _sent_request(client) assert tuple(body["input"]) == ( - {"role": "user", "content": "Hi there"}, {"role": "system", "content": "Answer with exactly one word.", "type": "message"}, + {"role": "user", "content": "Hi there"}, {"role": "user", "content": [{"type": "input_text", "text": "What is the capital of France?"}]}, ) +def test_responses_call_folds_instructions_and_developer_item_into_one_leading_system_message() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/qwen3p8-2p4t-a95b", + instructions="You are a coding agent running in the Codex CLI.", + input=[ # mutable-ok: the Responses API takes input as a JSON list + { + "role": "developer", + "content": [{"type": "input_text", "text": "read-only"}], + }, + {"role": "user", "content": [{"type": "input_text", "text": "What is the capital of France?"}]}, + {"id": "rs_1", "type": "reasoning", "summary": [{"type": "summary_text", "text": "A trivial question."}]}, + { + "id": "msg_1", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Paris is the capital of France.", "annotations": []}], + }, + {"role": "user", "content": [{"type": "input_text", "text": "And of Spain?"}]}, + ], + store=False, + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert "instructions" not in body + assert tuple(body["input"]) == ( + { + "role": "system", + "content": ( + "You are a coding agent running in the Codex CLI.\n\n" + "read-only" + ), + "type": "message", + }, + {"role": "user", "content": [{"type": "input_text", "text": "What is the capital of France?"}]}, + {"id": "rs_1", "type": "reasoning", "summary": [{"type": "summary_text", "text": "A trivial question."}]}, + { + "id": "msg_1", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Paris is the capital of France.", "annotations": []}], + }, + {"role": "user", "content": [{"type": "input_text", "text": "And of Spain?"}]}, + ) + + +def test_responses_call_turns_string_input_with_instructions_into_system_then_user_messages() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/kimi-k3", + instructions="Answer with exactly one word.", + input="What is the capital of France?", + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert "instructions" not in body + assert tuple(body["input"]) == ( + {"role": "system", "content": "Answer with exactly one word.", "type": "message"}, + {"role": "user", "content": "What is the capital of France?"}, + ) + + def test_responses_call_maps_pydantic_developer_items_and_replays_pydantic_output_items() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) pydantic_input: Final = cast( From edde95197a5c37c7af3d9967a70b65ab47b6913c Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:11:48 -0700 Subject: [PATCH 090/223] test(proxy): cover every /v1/files route error type and param --- .../test_files_endpoint.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py index 123d54789bf..2257ae1ab29 100644 --- a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py +++ b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py @@ -4666,3 +4666,102 @@ def test_create_file_path_traversal_filename_rejected_before_forwarding(monkeypa assert error["param"] == "file" assert "traversal" in error["message"].lower() assert forwarded_calls == [] + + +def _setup_managed_file_route_answering_404(mocker: MockerFixture, monkeypatch, llm_router: Router): + """Wire the single-file routes to a managed file store that knows no file, the way the + managed files hook answers once a file has been deleted or was never the caller's.""" + import litellm.proxy.proxy_server as ps + from fastapi import HTTPException + from litellm.llms.base_llm.files.transformation import BaseFileEndpoints + from litellm.proxy._types import LitellmUserRoles + + async def _file_not_found(file_id: str, **kwargs): + raise HTTPException(status_code=404, detail=f"File not found: {file_id}") + + proxy_logging_obj = setup_proxy_logging_object(monkeypatch, llm_router) + managed_files = mocker.MagicMock(spec=BaseFileEndpoints) + managed_files.afile_retrieve = mocker.AsyncMock(side_effect=_file_not_found) + managed_files.afile_delete = mocker.AsyncMock(side_effect=_file_not_found) + managed_files.afile_content = mocker.AsyncMock(side_effect=_file_not_found) + proxy_logging_obj.proxy_hook_mapping["managed_files"] = managed_files + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", llm_router) + + app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( + api_key="test-key", + user_role=LitellmUserRoles.INTERNAL_USER, + user_id="test-user", + ) + + +def _call_managed_file_route(method: str, path: str): + try: + return client.request(method, path, headers={"Authorization": "Bearer test-key"}) + finally: + import litellm.proxy.proxy_server as ps + + app.dependency_overrides.pop(ps.user_api_key_auth, None) + + +def _missing_managed_file_error(file_id: str) -> dict: + return { + "error": { + "message": f"File not found: {file_id}", + "type": "invalid_request_error", + "param": None, + "code": "404", + } + } + + +def test_create_file_reports_a_half_specified_expires_after_as_a_400(monkeypatch, llm_router: Router): + """A 400 raised inside the route answers with the type a 400 stands for and a JSON null + param, not the literal string "None" in both fields, so a client can classify it.""" + setup_proxy_logging_object(monkeypatch, llm_router) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", llm_router) + + response = client.post( + "/v1/files", + files={"file": ("mydata.jsonl", VALID_BATCH_LINE, "application/jsonl")}, + data={"purpose": "batch", "target_model_names": "gpt-3.5-turbo", "expires_after[anchor]": "created_at"}, + headers={"Authorization": "Bearer test-key"}, + ) + + assert response.status_code == 400, response.text + error = response.json()["error"] + assert "expires_after[seconds]" in error["message"] + assert error["type"] == "invalid_request_error" + assert error["param"] is None + assert error["code"] == "400" + + +def test_get_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixture, monkeypatch, llm_router: Router): + _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) + file_id = _unified_managed_file_id() + + response = _call_managed_file_route("GET", f"/v1/files/{file_id}") + + assert response.status_code == 404, response.text + assert response.json() == _missing_managed_file_error(file_id) + + +def test_delete_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixture, monkeypatch, llm_router: Router): + _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) + file_id = _unified_managed_file_id() + + response = _call_managed_file_route("DELETE", f"/v1/files/{file_id}") + + assert response.status_code == 404, response.text + assert response.json() == _missing_managed_file_error(file_id) + + +def test_get_file_content_reports_a_missing_managed_file_as_a_404( + mocker: MockerFixture, monkeypatch, llm_router: Router +): + _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) + file_id = _unified_managed_file_id() + + response = _call_managed_file_route("GET", f"/v1/files/{file_id}/content") + + assert response.status_code == 404, response.text + assert response.json() == _missing_managed_file_error(file_id) From 41c0897c7aa21742968715d152b2948a4d0fb83f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:14:41 -0700 Subject: [PATCH 091/223] refactor(cost_map): drop docstrings from the provenance helpers and their tests --- litellm/litellm_core_utils/get_model_cost_map.py | 6 ------ .../litellm_core_utils/test_get_model_cost_map.py | 12 ------------ .../proxy/proxy_server/test_routes_model_cost_map.py | 3 --- 3 files changed, 21 deletions(-) diff --git a/litellm/litellm_core_utils/get_model_cost_map.py b/litellm/litellm_core_utils/get_model_cost_map.py index 2bdfbc66088..cdc4810ff04 100644 --- a/litellm/litellm_core_utils/get_model_cost_map.py +++ b/litellm/litellm_core_utils/get_model_cost_map.py @@ -45,7 +45,6 @@ def _count_model_entries(model_cost: dict) -> int: def git_blob_id(body: bytes) -> str: - """The sha1 git gives these bytes as a blob, so ``git rev-parse :`` reproduces it for the file""" return hashlib.sha1(b"blob %d\0" % len(body) + body, usedforsecurity=False).hexdigest() @@ -70,7 +69,6 @@ class GetModelCostMap: @staticmethod def load_local_model_cost_map_with_revision() -> "ModelCostMapReloaded": - """The bundled backup map together with the git blob id of the file it was parsed from""" body: Final = GetModelCostMap.read_local_model_cost_map_bytes() content: Final = json.loads(body) return ModelCostMapReloaded(model_cost_map=content, revision=git_blob_id(body)) @@ -413,9 +411,6 @@ class CostMapSourceInfo(CostMapProvenance): def get_model_cost_map_provenance() -> CostMapProvenance: - """Which revision of the cost map this process serves: the git blob id of the bytes it loaded, the - same id ``git rev-parse :model_prices_and_context_window.json`` prints for a checkout, plus - the ETag the remote fetch returned (None for the bundled backup)""" return { "source_revision": _cost_map_source_info.source_revision, "etag": _cost_map_source_info.etag, @@ -520,7 +515,6 @@ def _finalize_model_cost_map(model_cost: dict) -> dict: def _finalize_loaded_model_cost_map(loaded: ModelCostMapReloaded) -> ModelCostMapReloaded: - """Record which bytes this process now serves, then finalize the map they parsed into""" _cost_map_source_info.source_revision = loaded.revision _cost_map_source_info.etag = loaded.etag return replace(loaded, model_cost_map=_finalize_model_cost_map(loaded.model_cost_map)) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index 7c3ad283639..18794ea7eec 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -40,8 +40,6 @@ def _bundled_blob_id() -> str: def test_git_blob_id_is_what_git_hash_object_prints(): - """An operator checks a reported revision with ``git hash-object`` or ``git rev-parse :``, - so the id must be git's blob sha1 of the exact bytes, not a plain sha1 or a hash of the parsed JSON.""" assert git_blob_id(b'{"gpt-5.4-mini": {"mode": "chat"}}\n') == "18b9a8381e13a3b38a2128f184f631f95829e987" @@ -516,9 +514,6 @@ async def test_refetch_respects_local_env_override(monkeypatch): @pytest.mark.asyncio async def test_refetch_records_the_blob_id_of_the_bytes_served_and_the_fetch_etag(): - """A reload reports which revision of the map it swapped in: the git blob id of the exact bytes the - fetch returned, so ``git rev-parse :model_prices_and_context_window.json`` can confirm it, - plus the ETag the fetch returned.""" body = _real_map_bytes() client, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"abc123"'}, content=body)]) @@ -532,7 +527,6 @@ async def test_refetch_records_the_blob_id_of_the_bytes_served_and_the_fetch_eta @pytest.mark.asyncio async def test_refetch_revision_follows_the_bytes_not_the_url(): - """Two fetches of the same URL that return different bytes report different revisions.""" edited = json.loads(_real_map_bytes()) edited["gpt-5.4-mini"]["input_cost_per_token"] = 0.5 client, _ = _mock_client( @@ -549,8 +543,6 @@ async def test_refetch_revision_follows_the_bytes_not_the_url(): @pytest.mark.asyncio async def test_refetch_local_override_reports_the_bundled_blob_id_without_an_etag(monkeypatch): - """Forcing the bundled backup after a remote reload must report the backup's own blob id and drop the - remote ETag, since the map served is no longer the one that ETag identifies.""" remote, _ = _mock_client([httpx.Response(200, headers={"ETag": 'W/"remote"'}, content=_real_map_bytes())]) await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=remote) monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") @@ -670,8 +662,6 @@ def test_boot_load_records_the_blob_id_of_the_bytes_served_and_the_fetch_etag(): def test_boot_load_fallback_to_the_backup_reports_its_blob_id_and_drops_the_remote_etag(): - """A boot that lands on the bundled backup reports the backup's own blob id and no ETag, even - when an earlier load in the same process had fetched the remote map.""" remote, _ = _mock_client( [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_real_map_bytes())], client_cls=httpx.Client ) @@ -687,8 +677,6 @@ def test_boot_load_fallback_to_the_backup_reports_its_blob_id_and_drops_the_remo def test_boot_load_that_fails_the_integrity_check_reports_the_backup_not_the_rejected_fetch(): - """A fetch that succeeds but fails integrity validation is thrown away, so the provenance must - describe the backup that got loaded, never the ETag or bytes of the map that was rejected.""" remote, _ = _mock_client( [httpx.Response(200, headers={"ETag": 'W/"boot"'}, content=_real_map_bytes())], client_cls=httpx.Client ) diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py index 0490993a314..36c364fb82b 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py @@ -50,7 +50,6 @@ def _attach_litellm_config(mock_prisma): def _pin_provenance(monkeypatch): - """Fix what this process reports as its cost map revision, independent of the map loaded at import.""" monkeypatch.setattr( "litellm.litellm_core_utils.get_model_cost_map.get_model_cost_map_provenance", lambda: dict(_PROVENANCE), @@ -110,8 +109,6 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): def test_reload_model_cost_map_surfaces_the_blob_id_of_the_bytes_served_on_every_status_surface( client, auth_as, monkeypatch, mock_prisma ): - """A real refetch through the reload route reports the git blob id of the exact bytes it fetched and - the fetch ETag on the reload response, the source route, and the schedule status alike.""" import httpx import litellm From cbe340a31ca81c40be088520ebbaaaca644977ff Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:38:18 -0700 Subject: [PATCH 092/223] feat(guardrails): deliver tool-call rewrites into buffered streams A post_call pipeline guardrail that rewrites a streamed tool call (its arguments or its name) now has that rewrite written back across the buffered chunks on chat, Responses, and Messages streams, so the client receives the rewritten tool call instead of the original. The chat handler rewrites the first fragment of each tool-call index and blanks the rest, the Responses handler syncs the function_call output items and their argument events, and the Messages handler rewrites the tool_use content_block_start and input_json_delta events in both dict and SSE-bytes chunks. The delivers_ended_stream_text_rewrites flag becomes delivers_ended_stream_rewrites, since the write-back now covers both text and tool calls, and the executor only discards a tool-call rewrite on translations without write-back or on a shape the translation refuses. --- .../chat/guardrail_translation/handler.py | 176 +++++++++++++++--- .../guardrail_translation/base_translation.py | 17 +- .../chat/guardrail_translation/handler.py | 96 +++++++++- .../guardrail_translation/handler.py | 111 ++++++++++- .../proxy/policy_engine/pipeline_executor.py | 22 +-- litellm/proxy/utils.py | 10 +- .../test_anthropic_guardrail_handler.py | 66 +++++++ .../test_openai_guardrail_handler.py | 73 ++++++++ ...test_openai_responses_guardrail_handler.py | 88 +++++++++ .../policy_engine/test_pipeline_executor.py | 22 ++- .../proxy_logging/test_guardrail_pipeline.py | 9 +- 11 files changed, 618 insertions(+), 72 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index e486be12fe2..2049866b444 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -13,10 +13,11 @@ Pattern Overview: """ import json -from collections.abc import Iterator, Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence from copy import deepcopy from dataclasses import dataclass from itertools import chain, repeat +from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, Protocol, cast, overload, runtime_checkable from typing_extensions import ReadOnly, TypedDict, assert_never @@ -41,6 +42,7 @@ from litellm.llms.base_llm.guardrail_translation.utils import ( merge_guardrailed_scoped_messages, merge_returned_tools_into_request_tools, scoped_structured_message_indices, + stream_item_field, stream_item_fingerprint, ) from litellm.proxy.pass_through_endpoints.llm_provider_handlers.anthropic_passthrough_logging_handler import ( @@ -153,6 +155,28 @@ class ExtractedInput: EMPTY_EXTRACTED_INPUT: Final = ExtractedInput(scanned=(), images=()) +@dataclass(frozen=True, slots=True) +class _ToolCallShape: + name: str | None + arguments: str + + +_SSEEventRewriter = Callable[[Mapping[str, object]], Mapping[str, object] | None] + + +def _tool_call_shapes(tool_calls: Sequence[object]) -> tuple[_ToolCallShape, ...]: + """The guardrail-visible shape of each tool call, whether the guardrail handed + back the ``ChatCompletionMessageToolCall`` objects it was given or plain dicts.""" + functions: Final = tuple(stream_item_field(tool_call, "function") for tool_call in tool_calls) + return tuple( + _ToolCallShape( + name=name if isinstance(name := stream_item_field(function, "name"), str) else None, + arguments=arguments if isinstance(arguments := stream_item_field(function, "arguments"), str) else "", + ) + for function in functions + ) + + class _AnthropicSSEDelta(TypedDict, total=False): type: ReadOnly[str] text: ReadOnly[str] @@ -170,7 +194,7 @@ class AnthropicMessagesHandler(BaseTranslation): them through guardrail rewrites; downstream provider handling is out of scope. """ - delivers_ended_stream_text_rewrites = True + delivers_ended_stream_rewrites = True def __init__(self): super().__init__() @@ -1050,6 +1074,7 @@ class AnthropicMessagesHandler(BaseTranslation): first_choice.message.tool_calls, ) string_so_far = first_choice.message.content + pre_guardrail_tool_calls: Final = _tool_call_shapes(tool_calls_list or ()) guardrail_inputs: Final = GenericGuardrailAPIInputs() if string_so_far: guardrail_inputs["texts"] = [string_so_far] @@ -1084,6 +1109,19 @@ class AnthropicMessagesHandler(BaseTranslation): and guardrailed_texts[0] != string_so_far ): self._write_ended_stream_text_rewrite(responses_so_far, guardrailed_texts[0]) + if deliver_ended_stream_rewrites: + returned_tool_calls: Final = _guardrailed_inputs.get("tool_calls") + self._write_ended_stream_tool_call_rewrites( + responses_so_far, + pre_guardrail_tool_calls=pre_guardrail_tool_calls, + post_guardrail_tool_calls=_tool_call_shapes( + returned_tool_calls + if isinstance(returned_tool_calls, list) + and len(returned_tool_calls) == len(pre_guardrail_tool_calls) + else tool_calls_list or () + ), + guardrail_name=guardrail_to_apply.guardrail_name or "unknown", + ) else: verbose_proxy_logger.debug("Skipping output guardrail - model response has no choices") return responses_so_far @@ -1212,38 +1250,120 @@ class AnthropicMessagesHandler(BaseTranslation): """Deliver an ended-stream guardrail text rewrite by rewriting the buffered chunks in place: the first ``text_delta`` carries the full rewritten text and every later one is blanked, leaving the surrounding - message and content-block framing untouched. Handles both chunk formats - this stream carries (parsed event dicts and raw SSE bytes).""" + message and content-block framing untouched.""" replacements: Final = chain((rewritten_text,), repeat("")) - for idx, item in enumerate(responses_so_far): - if isinstance(item, dict): - delta = item.get("delta") - if item.get("type") == "content_block_delta" and isinstance(delta, dict): - if delta.get("type") == "text_delta": - delta["text"] = next(replacements) - elif isinstance(item, (bytes, bytearray)): - responses_so_far[idx] = ( # rebind-ok: delivers the rewrite into the caller's buffer - AnthropicMessagesHandler._rewrite_sse_text_deltas(bytes(item), replacements) - ) + + def rewrite_text_delta(event: Mapping[str, object]) -> Mapping[str, object] | None: + delta: Final = event.get("delta") + if event.get("type") != "content_block_delta" or not isinstance(delta, Mapping): + return None + if delta.get("type") != "text_delta": + return None + return {**event, "delta": {**delta, "text": next(replacements)}} + + AnthropicMessagesHandler._rewrite_ended_stream_events(responses_so_far, rewrite_text_delta) + + @classmethod + def _write_ended_stream_tool_call_rewrites( + cls, + responses_so_far: list[Any], # mutable-ok: rewrites the caller's buffered chunks in place + *, + pre_guardrail_tool_calls: tuple[_ToolCallShape, ...], + post_guardrail_tool_calls: tuple[_ToolCallShape, ...], + guardrail_name: str, + ) -> None: + """Deliver ended-stream guardrail tool-call rewrites by rewriting the + buffered chunks in place: the rebuilt response lists tool calls in the + order of the stream's ``tool_use`` blocks, so the nth rewritten call lands + on the nth block, its first ``input_json_delta`` carrying the full rewritten + arguments, every later one blanked, and ``content_block_start`` carrying the + rewritten name. Blocks that do not line up with the rebuilt tool calls make + the rewrite undeliverable, so the pipeline executor discards it and releases + the original chunks.""" + if post_guardrail_tool_calls == pre_guardrail_tool_calls: + return + block_indices: Final = tuple( + index + for item in responses_so_far + for event in cls._iter_sse_events(item) + if event.get("type") == "content_block_start" + and isinstance(block := event.get("content_block"), Mapping) + and block.get("type") == "tool_use" + and isinstance(index := event.get("index"), int) + ) + if len(block_indices) != len(post_guardrail_tool_calls): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_name) + rewrites_by_block: Final = MappingProxyType( + { + index: after + for index, before, after in zip(block_indices, pre_guardrail_tool_calls, post_guardrail_tool_calls) + if after != before + } + ) + argument_replacements: Final = MappingProxyType( + {index: chain((rewrite.arguments,), repeat("")) for index, rewrite in rewrites_by_block.items()} + ) + + def rewrite_tool_use(event: Mapping[str, object]) -> Mapping[str, object] | None: + index: Final = event.get("index") + if not isinstance(index, int) or index not in rewrites_by_block: + return None + match event.get("type"): + case "content_block_start": + block: Final = event.get("content_block") + name: Final = rewrites_by_block[index].name + if not isinstance(block, Mapping) or name is None: + return None + return {**event, "content_block": {**block, "name": name}} + case "content_block_delta": + delta: Final = event.get("delta") + if not isinstance(delta, Mapping) or delta.get("type") != "input_json_delta": + return None + return {**event, "delta": {**delta, "partial_json": next(argument_replacements[index])}} + case _: + return None + + cls._rewrite_ended_stream_events(responses_so_far, rewrite_tool_use) @staticmethod - def _rewrite_sse_text_deltas(sse_bytes: bytes, replacements: "Iterator[str]") -> bytes: - """Rewrite every ``text_delta`` data line in one SSE chunk with the next - replacement text, leaving all other events and framing byte-identical.""" + def _rewrite_ended_stream_events( + responses_so_far: list[Any], # mutable-ok: rewrites the caller's buffered chunks in place + rewrite_event: _SSEEventRewriter, + ) -> None: + """Replace every buffered event ``rewrite_event`` returns a rewrite for, in + both chunk formats this stream carries (parsed event dicts and raw SSE + bytes), leaving every other event and the framing untouched.""" + rewritten_items: Final = tuple( + AnthropicMessagesHandler._rewrite_buffered_item(item, rewrite_event) for item in responses_so_far + ) + responses_so_far[:] = rewritten_items # rebind-ok: delivers the rewrites into the caller's buffer + + @staticmethod + def _rewrite_buffered_item(item: object, rewrite_event: _SSEEventRewriter) -> object: + if isinstance(item, dict): + rewritten: Final = rewrite_event(_as_str_mapping(item)) + return item if rewritten is None else dict(rewritten) + if isinstance(item, (bytes, bytearray)): + return AnthropicMessagesHandler._rewrite_sse_events(bytes(item), rewrite_event) + return item + + @staticmethod + def _rewrite_sse_events(sse_bytes: bytes, rewrite_event: _SSEEventRewriter) -> bytes: + """Rewrite the data lines of one SSE chunk that ``rewrite_event`` rewrites, + leaving all other events and framing byte-identical.""" try: decoded: Final = sse_bytes.decode("utf-8") except UnicodeDecodeError: return sse_bytes return "\n\n".join( - AnthropicMessagesHandler._rewrite_sse_block(block, replacements) for block in decoded.split("\n\n") + "\n".join(AnthropicMessagesHandler._rewrite_sse_line(line, rewrite_event) for line in block.split("\n")) + for block in decoded.split("\n\n") ).encode("utf-8") @staticmethod - def _rewrite_sse_block(block: str, replacements: "Iterator[str]") -> str: - return "\n".join(AnthropicMessagesHandler._rewrite_sse_line(line, replacements) for line in block.split("\n")) - - @staticmethod - def _rewrite_sse_line(line: str, replacements: "Iterator[str]") -> str: + def _rewrite_sse_line(line: str, rewrite_event: _SSEEventRewriter) -> str: if not line.startswith("data:"): return line try: @@ -1252,14 +1372,10 @@ class AnthropicMessagesHandler(BaseTranslation): ) except json.JSONDecodeError: return line - if not isinstance(data, dict) or data.get("type") != "content_block_delta": + if not isinstance(data, dict): return line - delta: Final = data.get("delta") - if not isinstance(delta, dict) or delta.get("type") != "text_delta": - return line - return "data: " + json.dumps( - {**data, "delta": {**delta, "text": next(replacements)}} # mutable-ok: json.dumps needs plain dicts - ) + rewritten: Final = rewrite_event(_as_str_mapping(data)) + return line if rewritten is None else "data: " + json.dumps(rewritten) def get_streaming_scan_key(self, responses_so_far: Sequence[object]) -> StreamingScanKey | None: stream_ended: Final = self._check_streaming_has_ended(responses_so_far) diff --git a/litellm/llms/base_llm/guardrail_translation/base_translation.py b/litellm/llms/base_llm/guardrail_translation/base_translation.py index afd8e0f67f7..6d1a9ab1c3e 100644 --- a/litellm/llms/base_llm/guardrail_translation/base_translation.py +++ b/litellm/llms/base_llm/guardrail_translation/base_translation.py @@ -52,13 +52,14 @@ class StreamingScanKey: class BaseTranslation(ABC): - delivers_ended_stream_text_rewrites: ClassVar[bool] = False + delivers_ended_stream_rewrites: ClassVar[bool] = False """Whether ``process_output_streaming_response`` accepts ``deliver_ended_stream_rewrites=True`` and, on an ended (fully buffered) - stream, writes guardrail text rewrites back across ``responses_so_far`` so - a buffered pipeline can release rewritten chunks. Tool-call rewrites, and - text rewrites on every other translation, are undeliverable: the pipeline - executor discards them and releases the original chunks.""" + stream, writes guardrail text and tool-call rewrites back across + ``responses_so_far`` so a buffered pipeline can release rewritten chunks, + raising ``UndeliverableStreamRewrite`` for a shape it cannot place. Rewrites + on every other translation are undeliverable: the pipeline executor + discards them and releases the original chunks.""" @staticmethod def transform_user_api_key_dict_to_metadata( @@ -175,9 +176,9 @@ class BaseTranslation(ABC): transformations (see ``StreamTransformSink``); base handlers ignore it. ``deliver_ended_stream_rewrites`` is passed True only when the caller holds the whole buffered stream and the subclass declares - ``delivers_ended_stream_text_rewrites``: the handler then writes - guardrail text rewrites back across ``responses_so_far`` instead of - discarding them. + ``delivers_ended_stream_rewrites``: the handler then writes + guardrail text and tool-call rewrites back across ``responses_so_far`` + instead of discarding them. """ return responses_so_far diff --git a/litellm/llms/openai/chat/guardrail_translation/handler.py b/litellm/llms/openai/chat/guardrail_translation/handler.py index 80292aef2cf..42c95ac3316 100644 --- a/litellm/llms/openai/chat/guardrail_translation/handler.py +++ b/litellm/llms/openai/chat/guardrail_translation/handler.py @@ -49,6 +49,8 @@ from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import coerce_stream_holdback_value, ) from litellm.types.utils import ( + ChatCompletionDeltaToolCall, + ChatCompletionMessageToolCall, Choices, GenericGuardrailAPIInputs, ModelResponse, @@ -78,7 +80,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation): Methods can be overridden to customize behavior for different message formats. """ - delivers_ended_stream_text_rewrites = True + delivers_ended_stream_rewrites = True def get_structured_messages(self, data: dict) -> list[AllMessageValues] | None: """ @@ -610,13 +612,14 @@ class OpenAIChatCompletionsHandler(BaseTranslation): deliver_ended_stream_rewrites: bool, ) -> None: """Ended-stream path: rebuild the full response, run the non-streaming - output guardrail against it, and (when opted in) write any text rewrite - back across the buffered chunks.""" + output guardrail against it, and (when opted in) write any text or + tool-call rewrite back across the buffered chunks.""" model_response: Final = cast( ModelResponse, stream_chunk_builder(chunks=responses_so_far, logging_obj=litellm_logging_obj), ) pre_guardrail_texts: Final = self._string_choice_contents(model_response) + pre_guardrail_tool_calls: Final = self._function_tool_call_shapes(model_response) await self.process_output_response( response=model_response, guardrail_to_apply=guardrail_to_apply, @@ -624,13 +627,21 @@ class OpenAIChatCompletionsHandler(BaseTranslation): user_api_key_dict=user_api_key_dict, request_data=request_data, ) - if deliver_ended_stream_rewrites: - await self._write_ended_stream_text_rewrites( - responses_so_far=responses_so_far, - guardrailed_response=model_response, - pre_guardrail_texts=pre_guardrail_texts, - guardrail_name=guardrail_to_apply.guardrail_name or "unknown", - ) + if not deliver_ended_stream_rewrites: + return + guardrail_name: Final = guardrail_to_apply.guardrail_name or "unknown" + await self._write_ended_stream_text_rewrites( + responses_so_far=responses_so_far, + guardrailed_response=model_response, + pre_guardrail_texts=pre_guardrail_texts, + guardrail_name=guardrail_name, + ) + self._write_ended_stream_tool_call_rewrites( + responses_so_far=responses_so_far, + guardrailed_response=model_response, + pre_guardrail_tool_calls=pre_guardrail_tool_calls, + guardrail_name=guardrail_name, + ) def build_stream_error_items( self, @@ -1043,6 +1054,71 @@ class OpenAIChatCompletionsHandler(BaseTranslation): task_mappings=[(target_choice_index, None) for _ in changed], # mutable-ok: callee takes lists ) + @staticmethod + def _function_tool_call_shapes(response: "ModelResponse") -> tuple[tuple[str | None, str], ...]: + return tuple( + (tool_call.function.name, tool_call.function.arguments) + for choice in response.choices + for tool_call in choice.message.tool_calls or () + if isinstance(tool_call, ChatCompletionMessageToolCall) + ) + + @staticmethod + def _function_tool_call_fragments( + responses_so_far: Sequence["ModelResponseStream"], + ) -> tuple[tuple[ChatCompletionDeltaToolCall, ...], ...]: + """Group the stream's function tool-call fragments by their tool-call index, in + the index order ``stream_chunk_builder`` lists the rebuilt tool calls, keeping + only the indices the builder keeps (an id and a name somewhere in the stream).""" + fragments: Final = tuple( + tool_call + for response in responses_so_far + for choice in response.choices + for tool_call in choice.delta.tool_calls or () + if isinstance(tool_call, ChatCompletionDeltaToolCall) + ) + identified: Final = frozenset(fragment.index for fragment in fragments if fragment.id) + named: Final = frozenset(fragment.index for fragment in fragments if fragment.function.name) + return tuple( + tuple(fragment for fragment in fragments if fragment.index == index) for index in sorted(identified & named) + ) + + def _write_ended_stream_tool_call_rewrites( + self, + responses_so_far: list["ModelResponseStream"], # mutable-ok: rewrites the caller's buffered chunks in place + guardrailed_response: "ModelResponse", + pre_guardrail_tool_calls: tuple[tuple[str | None, str], ...], + guardrail_name: str, + ) -> None: + """Write ended-stream guardrail tool-call rewrites back across the buffered + chunks: the rewritten name and full arguments land in the tool call's first + fragment and the arguments of its later fragments are blanked, mirroring the + text write-back. A rewrite on a stream carrying more than one distinct choice + index, or whose fragments do not line up with the rebuilt tool calls, is + reported as undeliverable, so the pipeline executor discards it and releases + the original chunks.""" + post_guardrail_tool_calls: Final = self._function_tool_call_shapes(guardrailed_response) + if post_guardrail_tool_calls == pre_guardrail_tool_calls: + return + stream_choice_indices: Final = frozenset( + choice.index for response in responses_so_far for choice in response.choices + ) + fragments_by_tool_call: Final = self._function_tool_call_fragments(responses_so_far) + if len(stream_choice_indices) != 1 or len(fragments_by_tool_call) != len(post_guardrail_tool_calls): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_name) + for before, (name, arguments), fragments in zip( + pre_guardrail_tool_calls, post_guardrail_tool_calls, fragments_by_tool_call + ): + if (name, arguments) == before: + continue + head, *tail = fragments + head.function.name = name + head.function.arguments = arguments + for fragment in tail: + fragment.function.arguments = "" + async def _apply_guardrail_responses_to_output_streaming( self, responses: list["ModelResponseStream"], diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index b0f79552bc5..447870fc0be 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -101,6 +101,18 @@ if TYPE_CHECKING: from litellm.types.llms.openai import ResponseInputParam +class _ToolCallShape(NamedTuple): + name: str | None + arguments: str + + +def _tool_call_shapes(tool_calls: Sequence[ChatCompletionToolCallChunk]) -> tuple[_ToolCallShape, ...]: + return tuple( + _ToolCallShape(name=tool_call["function"].get("name"), arguments=tool_call["function"].get("arguments", "")) + for tool_call in tool_calls + ) + + class ResponseOutputEnvelope(TypedDict, total=False): """Dict form of a Responses API response, as far as guardrail write-back reads it.""" @@ -340,7 +352,7 @@ class OpenAIResponsesHandler(BaseTranslation): Methods can be overridden to customize behavior for different message formats. """ - delivers_ended_stream_text_rewrites = True + delivers_ended_stream_rewrites = True def get_structured_messages(self, data: dict) -> list[AllMessageValues] | None: """ @@ -754,6 +766,7 @@ class OpenAIResponsesHandler(BaseTranslation): if response_model: inputs["model"] = response_model + pre_guardrail_tool_calls: Final = _tool_call_shapes(tool_calls_to_check) guardrailed_inputs: Final = await guardrail_to_apply.apply_guardrail( inputs=inputs, request_data=request_data, @@ -762,6 +775,12 @@ class OpenAIResponsesHandler(BaseTranslation): ) guardrailed_texts: Final = guardrailed_inputs.get("texts", []) + returned_tool_calls: Final = guardrailed_inputs.get("tool_calls") + post_guardrail_tool_calls: Final = _tool_call_shapes( + returned_tool_calls + if isinstance(returned_tool_calls, list) and len(returned_tool_calls) == len(tool_calls_to_check) + else tool_calls_to_check + ) # Write guardrailed texts back into the output items in-place. # final_chunk is a reference into responses_so_far so this @@ -784,6 +803,13 @@ class OpenAIResponsesHandler(BaseTranslation): stream_events=responses_so_far[:-1], rewrites_by_position=rewrites_by_position, ) + self._deliver_ended_stream_tool_call_rewrites( + responses_so_far=responses_so_far, + outputs=outputs, + pre_guardrail_tool_calls=pre_guardrail_tool_calls, + post_guardrail_tool_calls=post_guardrail_tool_calls, + guardrail_name=guardrail_to_apply.guardrail_name or "unknown", + ) return responses_so_far # ------------------------------------------------------------------ # @@ -894,6 +920,89 @@ class OpenAIResponsesHandler(BaseTranslation): continue OpenAIResponsesHandler._write_event_field(content[content_idx], "text", rewritten) + def _deliver_ended_stream_tool_call_rewrites( + self, + responses_so_far: Sequence[object], + outputs: Sequence[object], + pre_guardrail_tool_calls: tuple[_ToolCallShape, ...], + post_guardrail_tool_calls: tuple[_ToolCallShape, ...], + guardrail_name: str, + ) -> None: + """Write ended-stream guardrail tool-call rewrites into the completed + envelope's ``function_call`` items and sync the earlier stream events, + keyed by ``output_index``. The guardrail sees the envelope's function + calls in output order, which is how a rewritten call finds its item; a + rewrite whose calls do not line up with the envelope is reported as + undeliverable, so the pipeline executor discards it and releases the + original events.""" + if post_guardrail_tool_calls == pre_guardrail_tool_calls: + return + function_call_indices: Final = tuple( + output_idx + for output_idx, output_item in enumerate(outputs) + if stream_item_field(output_item, "type") == "function_call" + ) + if len(function_call_indices) != len(post_guardrail_tool_calls): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + raise UndeliverableStreamRewrite(guardrail_name) + rewrites_by_output_index: Final = MappingProxyType( + { + output_idx: after + for output_idx, before, after in zip( + function_call_indices, pre_guardrail_tool_calls, post_guardrail_tool_calls + ) + if after != before + } + ) + for output_idx, rewrite in rewrites_by_output_index.items(): + self._write_function_call_item(outputs[output_idx], rewrite.name, rewrite.arguments) + self._sync_stream_events_with_tool_call_rewrites( + stream_events=responses_so_far[:-1], + rewrites_by_output_index=rewrites_by_output_index, + ) + + def _sync_stream_events_with_tool_call_rewrites( + self, + stream_events: Sequence[object], + rewrites_by_output_index: Mapping[int, _ToolCallShape], + ) -> None: + """Sync pre-completion function-call events with the rewritten completed + response: the first ``function_call_arguments.delta`` for a rewritten call + carries the full rewritten arguments and the rest are blanked, while + ``function_call_arguments.done`` and ``output_item.done`` carry the full + rewritten arguments and ``output_item.added`` / ``output_item.done`` the + rewritten name, so every event a client may read agrees with the + rewritten ``response.completed`` payload.""" + delta_replacements: Final = MappingProxyType( + {index: chain((rewrite.arguments,), repeat("")) for index, rewrite in rewrites_by_output_index.items()} + ) + for event in stream_events: + output_index = stream_item_field(event, "output_index") + if not isinstance(output_index, int) or output_index not in rewrites_by_output_index: + continue + rewrite = rewrites_by_output_index[output_index] + match stream_item_field(event, "type"): + case "response.function_call_arguments.delta": + self._write_event_field(event, "delta", next(delta_replacements[output_index])) + case "response.function_call_arguments.done": + self._write_event_field(event, "arguments", rewrite.arguments) + case "response.output_item.added": + self._write_function_call_item(stream_item_field(event, "item"), rewrite.name, None) + case "response.output_item.done": + self._write_function_call_item(stream_item_field(event, "item"), rewrite.name, rewrite.arguments) + case _: + pass + + @staticmethod + def _write_function_call_item(item: object, name: str | None, arguments: str | None) -> None: + if not (isinstance(item, dict) or hasattr(item, "get")): + return + if name is not None: + OpenAIResponsesHandler._write_event_field(item, "name", name) + if arguments is not None: + OpenAIResponsesHandler._write_event_field(item, "arguments", arguments) + def _check_streaming_has_ended(self, responses_so_far: Sequence[object]) -> bool: """ Check if the streaming has ended. diff --git a/litellm/proxy/policy_engine/pipeline_executor.py b/litellm/proxy/policy_engine/pipeline_executor.py index 9bc10949e9f..3e112bf8e67 100644 --- a/litellm/proxy/policy_engine/pipeline_executor.py +++ b/litellm/proxy/policy_engine/pipeline_executor.py @@ -85,10 +85,10 @@ def _logged_by_inner_guardrail(method: _GuardrailMethodT) -> _GuardrailMethodT: class _StreamRewriteObserver(CustomGuardrail): """Stand-in handed to the endpoint translation in place of a streaming pipeline step's guardrail. It records whether the guardrail returned different output than it was given, - which for guardrails like Bedrock's ANONYMIZED action is only known at runtime. Text - rewrites are deliverable on translations that write them back across the buffered chunks - (``delivers_ended_stream_text_rewrites``); tool-call rewrites and text rewrites on any - other translation are discarded by the executor, which releases the original chunks. + which for guardrails like Bedrock's ANONYMIZED action is only known at runtime. Text and + tool-call rewrites are deliverable on translations that write them back across the + buffered chunks (``delivers_ended_stream_rewrites``); rewrites on any other translation + are discarded by the executor, which releases the original chunks. The inner guardrail's ``apply_guardrail`` already records the guardrail information and span, so the observer's stays out of ``log_guardrail_information``.""" @@ -290,13 +290,13 @@ class PipelineExecutor: litellm_logging_obj: "LiteLLMLoggingObj | None", ) -> None: """Run one streaming post_call step through the endpoint translation, delivering - text rewrites on translations that support ended-stream write-back. A rewrite that - cannot reach the client yet (a tool-call rewrite, a text rewrite on a translation - without write-back, or one the translation refused with - ``UndeliverableStreamRewrite``) is discarded: the buffered chunks go back to the - originals and the step passes, so the client gets the stream the merge base sent.""" + text and tool-call rewrites on translations that support ended-stream write-back. A + rewrite that cannot reach the client yet (one on a translation without write-back, or + one the translation refused with ``UndeliverableStreamRewrite``) is discarded: the + buffered chunks go back to the originals and the step passes, so the client gets the + stream the merge base sent.""" observer: Final = _StreamRewriteObserver(callback) - deliver_rewrites: Final = type(endpoint_translation).delivers_ended_stream_text_rewrites + deliver_rewrites: Final = type(endpoint_translation).delivers_ended_stream_rewrites originals: Final = copy.deepcopy(streaming_chunks) try: if deliver_rewrites: @@ -319,7 +319,7 @@ class PipelineExecutor: except UndeliverableStreamRewrite: _release_original_chunks(step.guardrail, streaming_chunks, originals) else: - if observer.rewrote_tool_calls or (observer.rewrote_texts and not deliver_rewrites): + if not deliver_rewrites and (observer.rewrote_texts or observer.rewrote_tool_calls): _release_original_chunks(step.guardrail, streaming_chunks, originals) if not callback.records_own_guardrail_information: add_guardrail_to_applied_guardrails_header(request_data=hook_input, guardrail_name=step.guardrail) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 8b36061c6be..76b9b4b44ed 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -3445,11 +3445,11 @@ class ProxyLogging: assembled output through the endpoint guardrail translation, the same machinery flat post_call guardrails use at end of stream. An allow releases the buffered chunks: verbatim when no guardrail rewrote the - output, rewritten in place when one rewrote text and the translation - delivers ended-stream rewrites (later steps then re-scan the rewritten - chunks, so rewrites chain). A rewrite the translation cannot deliver - yet (a tool-call rewrite, or a text rewrite on a route without - write-back) is discarded by the executor and the original chunks are + output, rewritten in place when one rewrote text or a tool call and the + translation delivers ended-stream rewrites (later steps then re-scan the + rewritten chunks, so rewrites chain). A rewrite the translation cannot + deliver yet (one on a route without write-back, or a shape the route + refuses) is discarded by the executor and the original chunks are released, as is a buffered shape no translation resolves; a block or modify_response terminates with the translation's block chunks or the raised error. diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index bf40f781fa3..bd2dff9b33c 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -315,6 +315,72 @@ class TestAnthropicMessagesHandlerStreamingOutputProcessing: assert "event: message_start" in raw and "event: message_stop" in raw assert '"stop_reason": "end_turn"' in raw + @staticmethod + def _ended_tool_use_sse_chunks() -> list: + events = [ + ("message_start", {"type": "message_start", "message": {"id": "msg_1", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [], "stop_reason": None, "usage": {"input_tokens": 1, "output_tokens": 0}}}), + ("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "tool_use", "id": "toolu_1", "name": "lookup_fruit", "input": {}}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": ""}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": '{"fruit": "persim'}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": 'mon"}'}}), + ("content_block_stop", {"type": "content_block_stop", "index": 0}), + ("message_delta", {"type": "message_delta", "delta": {"stop_reason": "tool_use", "stop_sequence": None}, "usage": {"output_tokens": 2}}), + ("message_stop", {"type": "message_stop"}), + ] + return [f"event: {name}\ndata: {json.dumps(payload)}\n\n".encode() for name, payload in events] + + @staticmethod + def _argument_masking_guardrail() -> CustomGuardrail: + class MaskArguments(CustomGuardrail): + async def apply_guardrail(self, inputs, request_data, input_type, logging_obj=None): + for tool_call in inputs.get("tool_calls", []): + tool_call.function.arguments = '{"fruit": "[MASKED]"}' + return inputs + + return MaskArguments(guardrail_name="test") + + @staticmethod + def _partial_jsons(chunks: list) -> list: + return [ + json.loads(line[len("data:") :].strip())["delta"]["partial_json"] + for chunk in chunks + for line in chunk.decode().split("\n") + if line.startswith("data:") and json.loads(line[len("data:") :].strip()).get("type") == "content_block_delta" + ] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_writes_tool_use_input_back_into_sse_chunks(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_tool_use_sse_chunks() + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=MagicMock(), + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + assert self._partial_jsons(chunks) == ['{"fruit": "[MASKED]"}', "", ""] + raw = b"".join(chunks).decode() + assert '"name": "lookup_fruit"' in raw and '"id": "toolu_1"' in raw + assert '"stop_reason": "tool_use"' in raw + assert "persim" not in raw + + @pytest.mark.asyncio + async def test_ended_stream_tool_use_rewrite_leaves_chunks_untouched_by_default(self): + handler = AnthropicMessagesHandler() + chunks = self._ended_tool_use_sse_chunks() + original = [bytes(chunk) for chunk in chunks] + + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=MagicMock(), + ) + + assert chunks == original + @pytest.mark.asyncio async def test_ended_stream_rewrite_leaves_chunks_untouched_by_default(self): handler = AnthropicMessagesHandler() diff --git a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py index aff0530ee8b..be168ec83e6 100644 --- a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py @@ -1113,6 +1113,79 @@ class TestOpenAIChatCompletionsHandlerStreamingOutput: assert chunks[1].choices[0].delta.content in (None, "") assert chunks[1].choices[0].finish_reason == "stop" + @staticmethod + def _ended_tool_call_stream_chunks() -> list: + from litellm.types.utils import ( + ChatCompletionDeltaToolCall, + Delta, + Function, + ModelResponseStream, + StreamingChoices, + ) + + def chunk(tool_call: ChatCompletionDeltaToolCall | None, finish_reason: Optional[str] = None): + return ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[ + StreamingChoices( + index=0, + delta=Delta(tool_calls=[tool_call] if tool_call else None), + finish_reason=finish_reason, + ) + ], + ) + + def fragment(arguments: str, name: Optional[str] = None, call_id: Optional[str] = None): + return ChatCompletionDeltaToolCall( + id=call_id, index=0, type="function", function=Function(name=name, arguments=arguments) + ) + + return [ + chunk(fragment("", name="lookup_fruit", call_id="call_1")), + chunk(fragment('{"fruit":')), + chunk(fragment(' "persimmon"}')), + chunk(None, finish_reason="tool_calls"), + ] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_writes_tool_call_arguments_back_into_chunks(self): + handler = OpenAIChatCompletionsHandler() + guardrail = MockGuardrail(guardrail_name="test") + chunks = self._ended_tool_call_stream_chunks() + + result = await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is chunks + fragments = [chunk.choices[0].delta.tool_calls for chunk in chunks[:3]] + assert [fragment[0].function.arguments for fragment in fragments] == ['{"fruit": "PERSIMMON"}', "", ""] + assert fragments[0][0].function.name == "lookup_fruit" + assert fragments[0][0].id == "call_1" + assert chunks[3].choices[0].delta.tool_calls is None + assert chunks[3].choices[0].finish_reason == "tool_calls" + + @pytest.mark.asyncio + async def test_ended_stream_tool_call_rewrite_leaves_chunks_untouched_by_default(self): + handler = OpenAIChatCompletionsHandler() + guardrail = MockGuardrail(guardrail_name="test") + chunks = self._ended_tool_call_stream_chunks() + + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=guardrail, + litellm_logging_obj=None, + ) + + fragments = [chunk.choices[0].delta.tool_calls for chunk in chunks[:3]] + assert [fragment[0].function.arguments for fragment in fragments] == ["", '{"fruit":', ' "persimmon"}'] + @pytest.mark.asyncio async def test_ended_stream_rewrite_leaves_chunks_untouched_by_default(self): handler = OpenAIChatCompletionsHandler() diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index 33c8c97fea7..a024fc7ff81 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1195,6 +1195,94 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: assert events[4]["item"]["content"][0]["text"] == "hello [MASKED]" assert events[5]["response"]["output"][0]["content"][0]["text"] == "hello [MASKED]" + @staticmethod + def _ended_function_call_stream_events() -> List[dict]: + def item(arguments: str, status: str) -> dict: + return { + "type": "function_call", + "id": "fc_123", + "call_id": "call_123", + "name": "lookup_fruit", + "arguments": arguments, + "status": status, + } + + return [ + {"type": "response.output_item.added", "output_index": 0, "item": item("", "in_progress")}, + {"type": "response.function_call_arguments.delta", "item_id": "fc_123", "output_index": 0, "delta": '{"fruit":'}, + {"type": "response.function_call_arguments.delta", "item_id": "fc_123", "output_index": 0, "delta": ' "persimmon"}'}, + { + "type": "response.function_call_arguments.done", + "item_id": "fc_123", + "output_index": 0, + "arguments": '{"fruit": "persimmon"}', + }, + {"type": "response.output_item.done", "output_index": 0, "item": item('{"fruit": "persimmon"}', "completed")}, + { + "type": "response.completed", + "response": { + "id": "resp_123", + "model": "gpt-4o", + "output": [item('{"fruit": "persimmon"}', "completed")], + "status": "completed", + }, + }, + ] + + @staticmethod + def _argument_masking_guardrail() -> CustomGuardrail: + class MaskArguments(CustomGuardrail): + async def apply_guardrail( + self, + inputs: GenericGuardrailAPIInputs, + request_data: dict, + input_type: Literal["request", "response"], + logging_obj: Optional[Any] = None, + ) -> GenericGuardrailAPIInputs: + tool_calls = [ + {**tool_call, "function": {**tool_call["function"], "arguments": '{"fruit": "[MASKED]"}'}} + for tool_call in inputs.get("tool_calls", []) + ] + return {**inputs, "tool_calls": tool_calls} + + return MaskArguments(guardrail_name="test-mask-arguments") + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_syncs_function_call_events(self): + handler = OpenAIResponsesHandler() + events = self._ended_function_call_stream_events() + + result = await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert result is events + assert events[0]["item"]["arguments"] == "" + assert events[1]["delta"] == '{"fruit": "[MASKED]"}' + assert events[2]["delta"] == "" + assert events[3]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[4]["item"]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[5]["response"]["output"][0]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[5]["response"]["output"][0]["name"] == "lookup_fruit" + + @pytest.mark.asyncio + async def test_ended_stream_function_call_rewrite_leaves_events_untouched_by_default(self): + handler = OpenAIResponsesHandler() + events = self._ended_function_call_stream_events() + + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=None, + ) + + assert events[1]["delta"] == '{"fruit":' + assert events[3]["arguments"] == '{"fruit": "persimmon"}' + assert events[5]["response"]["output"][0]["arguments"] == '{"fruit": "persimmon"}' + @pytest.mark.asyncio @pytest.mark.parametrize("terminal_type", ["response.incomplete", "response.failed"]) async def test_deliver_ended_stream_rewrites_syncs_non_completed_terminals(self, terminal_type): diff --git a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py index 54ef1f79f4d..ee75003db9f 100644 --- a/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py +++ b/tests/test_litellm/proxy/policy_engine/test_pipeline_executor.py @@ -924,7 +924,7 @@ class _TextReturningGuardrail(CustomGuardrail): class _TextTranslation: - delivers_ended_stream_text_rewrites = False + delivers_ended_stream_rewrites = False def __init__(self): self.seen_guardrail_names = [] @@ -946,7 +946,7 @@ class _WritingTranslation: """Writes the guardrail's text (and tool-call) outputs back into the buffered chunks the way the chat/Responses/Messages handlers do on an ended stream.""" - delivers_ended_stream_text_rewrites = True + delivers_ended_stream_rewrites = True async def process_output_streaming_response( self, @@ -970,7 +970,7 @@ class _WritingTranslation: class _RefusingTranslation: - delivers_ended_stream_text_rewrites = True + delivers_ended_stream_rewrites = True async def process_output_streaming_response( self, @@ -1088,13 +1088,27 @@ async def test_streaming_step_delivers_text_rewrite_through_writing_translation( @pytest.mark.asyncio -async def test_streaming_step_discards_tool_call_rewrite_and_restores_written_text(monkeypatch, caplog): +async def test_streaming_step_delivers_tool_call_rewrite_through_writing_translation(monkeypatch, caplog): monkeypatch.setattr(litellm, "callbacks", [_TextAndToolCallRewritingGuardrail(rewrite_tool_call=True)]) chunks = [_chunk()] with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): result = await _run_streaming_step(_WritingTranslation(), chunks) + assert result.terminal_action == "allow" + assert chunks[0]["text"] == "hello [MASKED]" + assert chunks[0]["tool_call"]["function"]["arguments"] == '{"ssn": "[MASKED]"}' + assert not any("discarded" in record.getMessage() for record in caplog.records) + + +@pytest.mark.asyncio +async def test_streaming_step_discards_tool_call_rewrite_when_translation_lacks_write_back(monkeypatch, caplog): + monkeypatch.setattr(litellm, "callbacks", [_TextAndToolCallRewritingGuardrail(rewrite_tool_call=True)]) + chunks = [_chunk()] + + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + result = await _run_streaming_step(_TextTranslation(), chunks) + _assert_passed_with_discard_warning(result, caplog) assert chunks == [_chunk()] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 73dd6746b29..d27f504ba6d 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1836,7 +1836,7 @@ def _echoed_tool_call_dicts(arguments: str) -> List[Dict[str, Any]]: @pytest.mark.asyncio @pytest.mark.parametrize("on_fail, on_error", [("block", None), ("next", "next")]) -async def test_streaming_iterator_hook_pipeline_releases_originals_on_runtime_tool_call_rewrite( +async def test_streaming_iterator_hook_pipeline_delivers_runtime_tool_call_rewrite( proxy_logging, make_user_api_key_auth, monkeypatch, on_fail, on_error, caplog ): transform = lambda inputs: {"tool_calls": _echoed_tool_call_dicts('{"ssn": "[MASKED]"}')} # noqa: E731 @@ -1855,9 +1855,12 @@ async def test_streaming_iterator_hook_pipeline_releases_originals_on_runtime_to delivered.append(item) assert len(delivered) == 2 - assert delivered[0].choices[0].delta.tool_calls[0].function.arguments == '{"ssn": "123"}' + delivered_tool_call = delivered[0].choices[0].delta.tool_calls[0] + assert delivered_tool_call.function.arguments == '{"ssn": "[MASKED]"}' + assert delivered_tool_call.function.name == "lookup" + assert delivered_tool_call.id == "call_1" assert delivered[1].choices[0].finish_reason == "tool_calls" - assert any("'gr-post'" in message and "discarded" in message for message in _warnings(caplog)) + assert not any("discarded" in message for message in _warnings(caplog)) @pytest.mark.asyncio From e65c56876f9aae1f94120212a2092592792f6538 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:42:51 -0700 Subject: [PATCH 093/223] fix(guardrails): write tool-call rewrites into typed Responses envelopes The response.completed envelope carries its function_call output items as SDK objects without a get shim, so the write-back skipped them and the envelope still showed the original arguments after every stream event had been rewritten. Write the item whenever one is present, and cover the typed event shape the live proxy carries in the handler test. --- .../guardrail_translation/handler.py | 2 +- ...test_openai_responses_guardrail_handler.py | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 447870fc0be..208ceb959e6 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -996,7 +996,7 @@ class OpenAIResponsesHandler(BaseTranslation): @staticmethod def _write_function_call_item(item: object, name: str | None, arguments: str | None) -> None: - if not (isinstance(item, dict) or hasattr(item, "get")): + if item is None: return if name is not None: OpenAIResponsesHandler._write_event_field(item, "name", name) diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index a024fc7ff81..bdf7f83757c 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1222,6 +1222,7 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: "type": "response.completed", "response": { "id": "resp_123", + "created_at": 1, "model": "gpt-4o", "output": [item('{"fruit": "persimmon"}', "completed")], "status": "completed", @@ -1268,6 +1269,51 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: assert events[5]["response"]["output"][0]["arguments"] == '{"fruit": "[MASKED]"}' assert events[5]["response"]["output"][0]["name"] == "lookup_fruit" + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_syncs_typed_function_call_events(self): + from litellm.types.llms.openai import ( + FunctionCallArgumentsDeltaEvent, + FunctionCallArgumentsDoneEvent, + OutputItemAddedEvent, + OutputItemDoneEvent, + ResponseCompletedEvent, + ResponsesAPIResponse, + ) + + handler = OpenAIResponsesHandler() + typed_events: List[Any] = [ + model.model_validate(event) + for model, event in zip( + ( + OutputItemAddedEvent, + FunctionCallArgumentsDeltaEvent, + FunctionCallArgumentsDeltaEvent, + FunctionCallArgumentsDoneEvent, + OutputItemDoneEvent, + ResponseCompletedEvent, + ), + self._ended_function_call_stream_events(), + ) + ] + completed_event = typed_events[5] + assert isinstance(completed_event, ResponseCompletedEvent) + assert isinstance(completed_event.response, ResponsesAPIResponse) + assert isinstance(completed_event.response.output[0], ResponseFunctionToolCall) + + await handler.process_output_streaming_response( + responses_so_far=typed_events, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert typed_events[1].delta == '{"fruit": "[MASKED]"}' + assert typed_events[2].delta == "" + assert typed_events[3].arguments == '{"fruit": "[MASKED]"}' + assert typed_events[4].item.arguments == '{"fruit": "[MASKED]"}' + assert completed_event.response.output[0].arguments == '{"fruit": "[MASKED]"}' + assert completed_event.response.output[0].name == "lookup_fruit" + @pytest.mark.asyncio async def test_ended_stream_function_call_rewrite_leaves_events_untouched_by_default(self): handler = OpenAIResponsesHandler() From 3448175184d8b68dbcf9ec007d62f85651bb0f41 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:44:49 -0700 Subject: [PATCH 094/223] fix(responses bridge): keep mid-conversation system messages in input Only the leading run of system messages (before the first non-system message) is joined into the Responses `instructions` field. A system message that arrives after a user, assistant, or tool turn now becomes a system input item at its position, whether its content is a string or a list, so a client that re-sends the same reminder as a string on the next request produces byte-identical input and `instructions` stays stable. Claude Code >= 2.1.237 appends such reminders after every user turn, and folding them into `instructions` made Azure treat every request as a cold prompt (cached_tokens 0 on every request of a session). Fixes #40198 --- .../transformation.py | 9 +- ...responses_transformation_transformation.py | 140 ++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/litellm/completion_extras/litellm_responses_transformation/transformation.py b/litellm/completion_extras/litellm_responses_transformation/transformation.py index 87350b5479c..4fe069b0b7d 100644 --- a/litellm/completion_extras/litellm_responses_transformation/transformation.py +++ b/litellm/completion_extras/litellm_responses_transformation/transformation.py @@ -370,7 +370,12 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge): and isinstance(tool_call.get("custom"), dict) ) - for msg in messages: + leading_system_count: Final = next( + (index for index, msg in enumerate(messages) if msg.get("role") != "system"), + len(messages), + ) + + for index, msg in enumerate(messages): role = msg.get("role") content = msg.get("content", "") tool_calls = msg.get("tool_calls") @@ -378,7 +383,7 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge): if role == "system": # Extract system message as instructions - if isinstance(content, str): + if isinstance(content, str) and index < leading_system_count: if instructions: # Concatenate multiple system prompts with a space instructions = f"{instructions} {content}" diff --git a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py index d4d47b145d1..e4ada0a9b31 100644 --- a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py +++ b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py @@ -4147,3 +4147,143 @@ def test_streaming_final_chunk_carries_provider_metadata(): assert chunks[-1]["content_filters"] == content_filters assert "background" not in chunks[-1] assert all("service_tier" not in chunk for chunk in chunks[:-1]) + + +def _system_input_item(text: str) -> dict[str, object]: + return {"type": "message", "role": "system", "content": [{"type": "input_text", "text": text}]} + + +def test_mid_conversation_system_string_stays_in_input_after_a_user_turn(): + handler: Final = LiteLLMResponsesTransformationHandler() + + input_items, instructions = handler.convert_chat_completion_messages_to_responses_api( + [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Read the file."}, + {"role": "system", "content": "14982391 tokens left"}, + {"role": "user", "content": "Now summarize it."}, + ] + ) + + assert instructions == "You are a helpful assistant." + assert input_items == [ + {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Read the file."}]}, + _system_input_item("14982391 tokens left"), + {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Now summarize it."}]}, + ] + + +def test_leading_system_strings_still_join_instructions_without_a_following_turn(): + handler: Final = LiteLLMResponsesTransformationHandler() + + input_items, instructions = handler.convert_chat_completion_messages_to_responses_api( + [ + {"role": "system", "content": "Be brief."}, + {"role": "system", "content": "Answer in French."}, + ] + ) + + assert instructions == "Be brief. Answer in French." + assert input_items == [] + + +def test_mid_conversation_system_reminder_as_string_and_as_text_block_produce_identical_input_items(): + handler: Final = LiteLLMResponsesTransformationHandler() + reminder: Final = "14982391 tokens left" + + as_string, string_instructions = handler.convert_chat_completion_messages_to_responses_api( + [{"role": "user", "content": "Read the file."}, {"role": "system", "content": reminder}] + ) + as_block, block_instructions = handler.convert_chat_completion_messages_to_responses_api( + [ + {"role": "user", "content": "Read the file."}, + { + "role": "system", + "content": [{"type": "text", "text": reminder, "cache_control": {"type": "ephemeral"}}], + }, + ] + ) + + assert string_instructions is None + assert block_instructions is None + assert json.dumps(as_string) == json.dumps(as_block) + assert as_string[1] == _system_input_item(reminder) + + +def test_claude_code_shaped_history_keeps_a_byte_stable_input_prefix_across_requests(): + handler: Final = LiteLLMResponsesTransformationHandler() + top_level_system: Final = [{"type": "text", "text": "You are Claude Code.", "cache_control": {"type": "ephemeral"}}] + first_reminder: Final = "27k chars of deferred tools" + second_reminder: Final = "14982391 tokens left" + first_request_messages: Final = [ + {"role": "system", "content": top_level_system}, + {"role": "user", "content": "Read inventory.py."}, + { + "role": "system", + "content": [{"type": "text", "text": first_reminder, "cache_control": {"type": "ephemeral"}}], + }, + ] + second_request_messages: Final = [ + {"role": "system", "content": top_level_system}, + {"role": "user", "content": "Read inventory.py."}, + {"role": "system", "content": first_reminder}, + { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "Read", "arguments": '{"file_path": "inventory.py"}'}, + } + ], + }, + {"role": "tool", "tool_call_id": "call_1", "content": "ITEMS = []"}, + { + "role": "system", + "content": [{"type": "text", "text": second_reminder, "cache_control": {"type": "ephemeral"}}], + }, + ] + + first_request: Final = handler.transform_request( + model="gpt-5.6-luna", + messages=first_request_messages, + optional_params={}, + litellm_params={}, + headers={}, + litellm_logging_obj=Mock(), + ) + second_request: Final = handler.transform_request( + model="gpt-5.6-luna", + messages=second_request_messages, + optional_params={}, + litellm_params={}, + headers={}, + litellm_logging_obj=Mock(), + ) + + assert "instructions" not in first_request + assert "instructions" not in second_request + assert first_request["input"][0] == _system_input_item("You are Claude Code.") + assert json.dumps(second_request["input"][: len(first_request["input"])]) == json.dumps(first_request["input"]) + assert second_request["input"][len(first_request["input"]) :] == [ + {"type": "function_call", "call_id": "call_1", "name": "Read", "arguments": '{"file_path": "inventory.py"}'}, + {"type": "function_call_output", "call_id": "call_1", "output": [{"type": "input_text", "text": "ITEMS = []"}]}, + _system_input_item(second_reminder), + ] + + +def test_system_string_after_a_developer_message_stays_in_input_in_client_order(): + handler: Final = LiteLLMResponsesTransformationHandler() + + input_items, instructions = handler.convert_chat_completion_messages_to_responses_api( + [ + {"role": "developer", "content": "Always answer in French."}, + {"role": "system", "content": "Be brief."}, + {"role": "user", "content": "Bonjour"}, + ] + ) + + assert instructions is None + assert [item["role"] for item in input_items] == ["developer", "system", "user"] + assert input_items[1] == _system_input_item("Be brief.") From 4807630c2ab1ab04f3b51c9ef78a5361d78af11d Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:48:01 -0700 Subject: [PATCH 095/223] fix(fireworks_ai): keep non-text system and developer parts on the folded leading system message --- .../fireworks_ai/responses/transformation.py | 36 ++++++++++++++----- ...t_fireworks_ai_responses_transformation.py | 34 ++++++++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/litellm/llms/fireworks_ai/responses/transformation.py b/litellm/llms/fireworks_ai/responses/transformation.py index 61ef0731900..660a07181ff 100644 --- a/litellm/llms/fireworks_ai/responses/transformation.py +++ b/litellm/llms/fireworks_ai/responses/transformation.py @@ -4,7 +4,12 @@ from typing import TYPE_CHECKING, Final from urllib.parse import unquote import httpx -from openai.types.responses import EasyInputMessageParam, ResponseInputItemParam +from openai.types.responses import ( + EasyInputMessageParam, + ResponseInputContentParam, + ResponseInputItemParam, + ResponseInputTextParam, +) from pydantic import TypeAdapter from litellm.llms.fireworks_ai.common_utils import ( @@ -35,27 +40,42 @@ def _session_params(litellm_params: GenericLiteLLMParams) -> Mapping[str, object _instructions_adapter: Final = TypeAdapter[str | None](str | None) -def _instruction_text(item: ResponseInputItemParam) -> str | None: +def _instruction_parts(item: ResponseInputItemParam) -> tuple[ResponseInputContentParam, ...] | None: if "role" not in item or (item["role"] != "system" and item["role"] != "developer"): return None content: Final = item["content"] if isinstance(content, str): - return content - return "\n\n".join(part["text"] for part in content if part["type"] == "input_text") + return (ResponseInputTextParam(type="input_text", text=content),) + return tuple(content) + + +def _leading_system_content( + instructions: str | None, parts: tuple[ResponseInputContentParam, ...] +) -> str | list[ResponseInputContentParam]: + text: Final = "\n\n".join( + chunk for chunk in (instructions, *(part["text"] for part in parts if part["type"] == "input_text")) if chunk + ) + non_text: Final = tuple(part for part in parts if part["type"] != "input_text") + if not non_text: + return text + return [ResponseInputTextParam(type="input_text", text=text), *non_text] if text else list(non_text) def _with_single_leading_system_item( input: str | ResponseInputParam, instructions: str | None ) -> str | ResponseInputParam: items: Final = () if isinstance(input, str) else tuple(input) - instruction_texts: Final = tuple(text for text in (instructions, *map(_instruction_text, items)) if text) - if not instruction_texts: + instruction_parts: Final = tuple( + part for item_parts in map(_instruction_parts, items) if item_parts is not None for part in item_parts + ) + content: Final = _leading_system_content(instructions, instruction_parts) + if not content: return input - leading: Final = EasyInputMessageParam(role="system", content="\n\n".join(instruction_texts), type="message") + leading: Final = EasyInputMessageParam(role="system", content=content, type="message") rest: Final = ( (EasyInputMessageParam(role="user", content=input),) if isinstance(input, str) - else tuple(item for item in items if _instruction_text(item) is None) + else tuple(item for item in items if _instruction_parts(item) is None) ) return [leading, *rest] diff --git a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py index 3462e041a19..9d947b25d69 100644 --- a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py +++ b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py @@ -231,6 +231,40 @@ def test_responses_call_folds_instructions_and_developer_item_into_one_leading_s ) +def test_responses_call_keeps_non_text_developer_parts_on_the_leading_system_message() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/qwen3p8-2p4t-a95b", + instructions="Answer with one word.", + input=[ # mutable-ok: the Responses API takes input as a JSON list + { + "role": "developer", + "content": [ + {"type": "input_text", "text": "Match the style of this reference image."}, + {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo=", "detail": "auto"}, + ], + }, + {"role": "user", "content": "What is the capital of France?"}, + ], + store=False, + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert "instructions" not in body + assert tuple(body["input"]) == ( + { + "role": "system", + "content": [ + {"type": "input_text", "text": "Answer with one word.\n\nMatch the style of this reference image."}, + {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo=", "detail": "auto"}, + ], + "type": "message", + }, + {"role": "user", "content": "What is the capital of France?"}, + ) + + def test_responses_call_turns_string_input_with_instructions_into_system_then_user_messages() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): From 3caa3b60d5a319efa78871198081783dd4c70ad1 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:58:18 -0700 Subject: [PATCH 096/223] feat(guardrails): run post_call policy pipelines on background Responses retrieval A POST /v1/responses with background: true returns a queued response, so the post_call pipelines attached at submit time had nothing to inspect. They now defer on queued and in_progress responses and run on GET /v1/responses/{id} instead: the retrieval resolves the response id back to its deployment, re-attaches the policies that governed the original model, and reports them in the x-litellm-applied-* headers of the retrieval response. --- litellm/proxy/common_request_processing.py | 8 +- .../proxy/policy_engine/response_retrieval.py | 116 +++++++++++++ litellm/proxy/utils.py | 43 +++-- .../policy_engine/test_response_retrieval.py | 157 ++++++++++++++++++ .../proxy/test_common_request_processing.py | 109 ++++++++++++ .../proxy_logging/test_guardrail_pipeline.py | 95 +++++++---- 6 files changed, 485 insertions(+), 43 deletions(-) create mode 100644 litellm/proxy/policy_engine/response_retrieval.py create mode 100644 tests/test_litellm/proxy/policy_engine/test_response_retrieval.py diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 9720e4b1cf8..1c1eda73c0c 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -184,6 +184,7 @@ from litellm.proxy.litellm_pre_call_utils import ( refresh_proxy_server_request_body_snapshot, reject_url_valued_destination, ) +from litellm.proxy.policy_engine.response_retrieval import attach_post_call_pipelines_to_retrieval from litellm.types.utils import ( ModelResponse, ModelResponseStream, @@ -1883,7 +1884,6 @@ class ProxyBaseLLMRequestProcessing: data=self.data, user_api_key_dict=user_api_key_dict, ) - # Calculate request queue time after add_litellm_data_to_request # which sets arrival_time in proxy_server_request. Ends at start_time # (not a freshly captured time.time() here) so this window is exactly @@ -2031,6 +2031,12 @@ class ProxyBaseLLMRequestProcessing: data=self.data, call_type=route_type, ) + if route_type == "aget_responses": + attach_post_call_pipelines_to_retrieval( + data=self.data, + user_api_key_dict=user_api_key_dict, + llm_router=llm_router, + ) # Refresh AFTER pre_call_hook: guardrails (e.g. Presidio PII masking) may # have mutated `self.data` in place, and the audit-trail snapshot taken in diff --git a/litellm/proxy/policy_engine/response_retrieval.py b/litellm/proxy/policy_engine/response_retrieval.py new file mode 100644 index 00000000000..54b9acf6150 --- /dev/null +++ b/litellm/proxy/policy_engine/response_retrieval.py @@ -0,0 +1,116 @@ +from collections.abc import Mapping +from types import MappingProxyType +from typing import TYPE_CHECKING, Final, TypeAlias + +from pydantic import TypeAdapter + +from litellm._logging import verbose_proxy_logger +from litellm.litellm_core_utils.core_helpers import get_or_create_metadata_bucket +from litellm.proxy.common_utils.callback_utils import ( + add_guardrail_to_applied_guardrails_header, + add_policy_sources_to_metadata, + add_policy_to_applied_policies_header, +) +from litellm.proxy.common_utils.http_parsing_utils import get_tags_from_request_body +from litellm.proxy.policy_engine.attachment_registry import get_attachment_registry +from litellm.proxy.policy_engine.policy_matcher import PolicyMatcher +from litellm.proxy.policy_engine.policy_registry import get_policy_registry +from litellm.proxy.policy_engine.policy_resolver import PolicyResolver +from litellm.responses.utils import ResponsesAPIRequestUtils +from litellm.types.proxy.policy_engine import PolicyMatchContext +from litellm.types.proxy.policy_engine.pipeline_types import GuardrailPipeline + +if TYPE_CHECKING: + from litellm.proxy._types import UserAPIKeyAuth + from litellm.router import Router + +PolicyPipelines: TypeAlias = tuple[tuple[str, GuardrailPipeline], ...] + +_POLICY_PIPELINES_ADAPTER: Final = TypeAdapter(PolicyPipelines) + + +def _model_group_for_response_id(response_id: object, llm_router: "Router | None") -> str | None: + if llm_router is None or not isinstance(response_id, str): + return None + model_id: Final = ResponsesAPIRequestUtils.get_model_id_from_response_id(response_id) + if model_id is None: + return None + deployment: Final = llm_router.get_deployment(model_id) + return deployment.model_name if deployment is not None else None + + +def _retrieval_context( + data: Mapping[str, object], user_api_key_dict: "UserAPIKeyAuth", model_group: str +) -> PolicyMatchContext: + team_alias: Final = user_api_key_dict.team_alias + key_alias: Final = user_api_key_dict.key_alias + return PolicyMatchContext( + team_alias=team_alias if isinstance(team_alias, str) else None, + key_alias=key_alias if isinstance(key_alias, str) else None, + model=model_group, + tags=get_tags_from_request_body(data) or None, + ) + + +def _post_call_pipelines_for_context(context: PolicyMatchContext) -> tuple[PolicyPipelines, Mapping[str, str]]: + matches: Final = get_attachment_registry().get_attached_policies_with_reasons(context) + if not matches: + return (), MappingProxyType({}) + applied_policy_names: Final = PolicyMatcher.get_policies_with_matching_conditions( + policy_names=[match["policy_name"] for match in matches], # mutable-ok: the matcher takes a list + context=context, + ) + post_call_pipelines: Final = tuple( + (policy_name, pipeline) + for policy_name, pipeline in PolicyResolver.resolve_pipelines_for_context( + context=context, policy_names=applied_policy_names + ) + if pipeline.mode == "post_call" + ) + return post_call_pipelines, MappingProxyType({match["policy_name"]: match["matched_via"] for match in matches}) + + +def attach_post_call_pipelines_to_retrieval( + data: dict, # mutable-ok: the proxy's request-state dict, written in place like every other policy engine hook + user_api_key_dict: "UserAPIKeyAuth", + llm_router: "Router | None", +) -> None: + if not get_policy_registry().is_initialized(): + return + model_group: Final = _model_group_for_response_id(data.get("response_id"), llm_router) + if model_group is None: + return + context: Final = _retrieval_context(data, user_api_key_dict, model_group) + post_call_pipelines, policy_sources = _post_call_pipelines_for_context(context) + _, bucket = get_or_create_metadata_bucket(data) + already_attached: Final = _POLICY_PIPELINES_ADAPTER.validate_python(bucket.get("_guardrail_pipelines") or ()) + attached_policy_names: Final = frozenset(policy_name for policy_name, _pipeline in already_attached) + added: Final = tuple( + (policy_name, pipeline) + for policy_name, pipeline in post_call_pipelines + if policy_name not in attached_policy_names + ) + if not added: + return + pipelines: Final = (*already_attached, *added) + bucket["_guardrail_pipelines"] = pipelines + bucket["_pipeline_managed_guardrails"] = frozenset( + step.guardrail for _policy_name, pipeline in pipelines for step in pipeline.steps + ) + for policy_name, _pipeline in added: + add_policy_to_applied_policies_header(request_data=data, policy_name=policy_name) + for _policy_name, pipeline in added: + for step in pipeline.steps: + add_guardrail_to_applied_guardrails_header(request_data=data, guardrail_name=step.guardrail) + add_policy_sources_to_metadata( + request_data=data, + policy_sources={ # mutable-ok: add_policy_sources_to_metadata takes a dict + policy_name: policy_sources[policy_name] for policy_name, _pipeline in added + }, + ) + verbose_proxy_logger.debug( + "Policy engine: attached post_call pipelines to the retrieval of background response %s (model group %s): %s", + data.get("response_id"), + model_group, + ", ".join(policy_name for policy_name, _pipeline in added), + ) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 8b36061c6be..3d03d3858ee 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -171,6 +171,7 @@ from litellm.repositories.verification_token_repository import ( ) from litellm.secret_managers.main import str_to_bool from litellm.types.integrations.slack_alerting import DEFAULT_ALERT_TYPES +from litellm.types.llms.openai import ResponsesAPIResponse from litellm.types.mcp import ( MCPDuringCallResponseObject, MCPPreCallRequestObject, @@ -525,15 +526,21 @@ def _post_call_pipelines(data: Mapping[str, object]) -> tuple[tuple[str, "Guardr ) -def _warn_background_skips_post_call_pipelines(data: Mapping[str, object]) -> None: - if data.get("background") is not True: - return +_PENDING_BACKGROUND_RESPONSE_STATUSES: Final = frozenset(("queued", "in_progress")) + + +def _is_pending_background_response(response: LLMResponseTypes) -> bool: + return isinstance(response, ResponsesAPIResponse) and response.status in _PENDING_BACKGROUND_RESPONSE_STATUSES + + +def _log_deferred_post_call_pipelines(data: Mapping[str, object], response: ResponsesAPIResponse) -> None: policy_names: Final = tuple(policy_name for policy_name, _pipeline in _post_call_pipelines(data)) if not policy_names: return - verbose_proxy_logger.warning( - "Policies with post_call guardrail pipelines do not run on background responses yet; " - "the response is released ungoverned by them: %s", + verbose_proxy_logger.debug( + "Post_call guardrail pipelines wait for background response %s (status=%s) to be retrieved complete: %s", + response.id, + response.status, ", ".join(policy_names), ) @@ -1956,8 +1963,6 @@ class ProxyLogging: ) try: - _warn_background_skips_post_call_pipelines(data) - # Execute guardrail pipelines before the normal callback loop data, _ = await self._maybe_execute_pipelines( # rebind-ok: pipeline edits feed the callback loop below data=data, @@ -2927,6 +2932,24 @@ class ProxyLogging: daemon=True, ).start() + async def _run_post_call_pipelines( + self, + data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + user_api_key_dict: UserAPIKeyAuth, + response: LLMResponseTypes, + ) -> LLMResponseTypes | None: + if _is_pending_background_response(response): + _log_deferred_post_call_pipelines(data, response) + return None + _, pipeline_response = await self._maybe_execute_pipelines( + data=data, + user_api_key_dict=user_api_key_dict, + call_type=getattr(data.get("litellm_logging_obj"), "call_type", None) or "acompletion", + event_hook="post_call", + response=response, + ) + return pipeline_response + async def post_call_success_hook( self, data: dict, @@ -2946,11 +2969,9 @@ class ProxyLogging: from litellm.proxy.proxy_server import llm_router from litellm.types.guardrails import GuardrailEventHooks - _, pipeline_response = await self._maybe_execute_pipelines( + pipeline_response: Final = await self._run_post_call_pipelines( data=data, user_api_key_dict=user_api_key_dict, - call_type=getattr(data.get("litellm_logging_obj"), "call_type", None) or "acompletion", - event_hook="post_call", response=response, ) if pipeline_response is not None: diff --git a/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py b/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py new file mode 100644 index 00000000000..f7f29d6b46e --- /dev/null +++ b/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py @@ -0,0 +1,157 @@ +import pytest + +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.policy_engine.attachment_registry import get_attachment_registry +from litellm.proxy.policy_engine.policy_registry import get_policy_registry +from litellm.proxy.policy_engine.response_retrieval import attach_post_call_pipelines_to_retrieval +from litellm.responses.utils import ResponsesAPIRequestUtils +from litellm.types.router import Deployment, LiteLLM_Params + +GOVERNED_MODEL_GROUP = "gpt-5.4-mini" +GOVERNED_MODEL_ID = "deployment-governed" +UNGOVERNED_MODEL_GROUP = "gpt-4.1-mini" +UNGOVERNED_MODEL_ID = "deployment-ungoverned" + + +class FakeRouter: + def __init__(self, deployments: dict[str, Deployment]): + self._deployments = deployments + + def get_deployment(self, model_id: str) -> Deployment | None: + return self._deployments.get(model_id) + + +def _deployment(model_group: str, model_id: str) -> Deployment: + return Deployment( + model_name=model_group, + litellm_params=LiteLLM_Params(model=f"openai/{model_group}"), + model_info={"id": model_id}, + ) + + +def _router() -> FakeRouter: + return FakeRouter( + { + GOVERNED_MODEL_ID: _deployment(GOVERNED_MODEL_GROUP, GOVERNED_MODEL_ID), + UNGOVERNED_MODEL_ID: _deployment(UNGOVERNED_MODEL_GROUP, UNGOVERNED_MODEL_ID), + } + ) + + +def _encoded_response_id(model_id: str) -> str: + return ResponsesAPIRequestUtils._build_responses_api_response_id( + custom_llm_provider="openai", model_id=model_id, response_id="resp_upstream" + ) + + +def _pipeline_policy(guardrail: str, mode: str = "post_call") -> dict[str, object]: + return { + "guardrails": {"add": [guardrail]}, + "pipeline": {"mode": mode, "steps": [{"guardrail": guardrail, "on_pass": "allow", "on_fail": "block"}]}, + } + + +@pytest.fixture +def policy_engine(): + policy_registry = get_policy_registry() + attachment_registry = get_attachment_registry() + policy_registry.load_policies( + { + "response-governance": _pipeline_policy("output-word-filter"), + "input-governance": _pipeline_policy("input-word-filter", mode="pre_call"), + "team-governance": _pipeline_policy("team-word-filter"), + } + ) + attachment_registry.load_attachments( + [ + {"policy": "response-governance", "models": [GOVERNED_MODEL_GROUP]}, + {"policy": "input-governance", "models": [GOVERNED_MODEL_GROUP]}, + {"policy": "team-governance", "teams": ["governed-team"]}, + ] + ) + yield + policy_registry.clear() + attachment_registry.clear() + + +def _retrieval_data(model_id: str) -> dict: + return {"response_id": _encoded_response_id(model_id), "litellm_metadata": {}} + + +def _attached_pipelines(data: dict) -> tuple[tuple[str, str], ...]: + return tuple( + (policy_name, ",".join(step.guardrail for step in pipeline.steps)) + for policy_name, pipeline in data["litellm_metadata"]["_guardrail_pipelines"] + ) + + +def test_attaches_model_scoped_post_call_pipeline_to_retrieval(policy_engine): + data = _retrieval_data(GOVERNED_MODEL_ID) + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + + assert _attached_pipelines(data) == (("response-governance", "output-word-filter"),) + assert data["litellm_metadata"]["_pipeline_managed_guardrails"] == frozenset({"output-word-filter"}) + assert data["litellm_metadata"]["applied_policies"] == ["response-governance"] + assert data["litellm_metadata"]["applied_guardrails"] == ["output-word-filter"] + assert data["litellm_metadata"]["policy_sources"] == {"response-governance": "model:gpt-5.4-mini"} + assert "model" not in data + assert "guardrails" not in data["litellm_metadata"] + + +def test_key_and_team_context_also_governs_retrieval(policy_engine): + data = _retrieval_data(UNGOVERNED_MODEL_ID) + + attach_post_call_pipelines_to_retrieval( + data=data, user_api_key_dict=UserAPIKeyAuth(team_alias="governed-team"), llm_router=_router() + ) + + assert _attached_pipelines(data) == (("team-governance", "team-word-filter"),) + + +def test_retrieval_of_an_ungoverned_model_attaches_nothing(policy_engine): + data = _retrieval_data(UNGOVERNED_MODEL_ID) + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + + assert data == _retrieval_data(UNGOVERNED_MODEL_ID) + + +def test_already_attached_policy_is_not_attached_twice(policy_engine): + data = _retrieval_data(GOVERNED_MODEL_ID) + router = _router() + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=router) + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=router) + + assert _attached_pipelines(data) == (("response-governance", "output-word-filter"),) + assert data["litellm_metadata"]["applied_policies"] == ["response-governance"] + + +@pytest.mark.parametrize( + "response_id", + ["resp_plain_upstream_id", _encoded_response_id("deployment-missing-from-router"), None], +) +def test_unresolvable_response_id_attaches_nothing(policy_engine, response_id): + data = {"response_id": response_id, "litellm_metadata": {}} + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + + assert data == {"response_id": response_id, "litellm_metadata": {}} + + +def test_without_a_router_attaches_nothing(policy_engine): + data = _retrieval_data(GOVERNED_MODEL_ID) + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=None) + + assert data == _retrieval_data(GOVERNED_MODEL_ID) + + +def test_without_policy_engine_attaches_nothing(): + get_policy_registry().clear() + data = _retrieval_data(GOVERNED_MODEL_ID) + + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + + assert data == _retrieval_data(GOVERNED_MODEL_ID) diff --git a/tests/test_litellm/proxy/test_common_request_processing.py b/tests/test_litellm/proxy/test_common_request_processing.py index 6acd9d7258e..9283c1ed865 100644 --- a/tests/test_litellm/proxy/test_common_request_processing.py +++ b/tests/test_litellm/proxy/test_common_request_processing.py @@ -8259,3 +8259,112 @@ class TestPassthroughHeadersAcceptImmutableMappings: assert merged["content-type"] == "text/event-stream" # the excluded hop-by-hop header is still dropped assert "transfer-encoding" not in merged + + +class TestBackgroundResponseRetrievalGovernance: + """LIT-7175: retrieving a background Response attaches the model's post_call policy pipelines.""" + + GOVERNED_MODEL_GROUP = "gpt-5.4-mini" + GOVERNED_MODEL_ID = "deployment-governed" + + @pytest.fixture + def policy_engine(self): + from litellm.proxy.policy_engine.attachment_registry import get_attachment_registry + from litellm.proxy.policy_engine.policy_registry import get_policy_registry + + get_policy_registry().load_policies( + { + "response-governance": { + "guardrails": {"add": ["output-word-filter"]}, + "pipeline": { + "mode": "post_call", + "steps": [{"guardrail": "output-word-filter", "on_pass": "allow", "on_fail": "block"}], + }, + } + } + ) + get_attachment_registry().load_attachments( + [{"policy": "response-governance", "models": [self.GOVERNED_MODEL_GROUP]}] + ) + yield + get_policy_registry().clear() + get_attachment_registry().clear() + + def _router(self) -> MagicMock: + from litellm.types.router import Deployment, LiteLLM_Params + + router = MagicMock() + router.get_deployment.side_effect = lambda model_id: ( + Deployment( + model_name=self.GOVERNED_MODEL_GROUP, + litellm_params=LiteLLM_Params(model=f"openai/{self.GOVERNED_MODEL_GROUP}"), + model_info={"id": model_id}, + ) + if model_id == self.GOVERNED_MODEL_ID + else None + ) + return router + + async def _pre_call(self, route_type: str, monkeypatch) -> dict: + from litellm.responses.utils import ResponsesAPIRequestUtils + + client_facing_response_id = "resp_opaque-client-facing-id" + encoded_response_id = ResponsesAPIRequestUtils._build_responses_api_response_id( + custom_llm_provider="openai", model_id=self.GOVERNED_MODEL_ID, response_id="resp_upstream" + ) + processing_obj = ProxyBaseLLMRequestProcessing( + data={"response_id": client_facing_response_id, "litellm_metadata": {}} + ) + mock_request = MagicMock(spec=Request) + mock_request.headers = {} + + async def passthrough_add_litellm_data_to_request(data, **kwargs): + return data + + async def decrypting_pre_call_hook(user_api_key_dict, data, call_type): + if data.get("response_id") == client_facing_response_id: + data["response_id"] = encoded_response_id + return data + + monkeypatch.setattr( + litellm.proxy.common_request_processing, + "add_litellm_data_to_request", + passthrough_add_litellm_data_to_request, + ) + proxy_logging_obj = MagicMock(spec=ProxyLogging) + proxy_logging_obj.pre_call_hook = AsyncMock(side_effect=decrypting_pre_call_hook) + proxy_config = MagicMock(spec=ProxyConfig) + proxy_config._get_hierarchical_router_settings = AsyncMock(return_value=None) + returned_data, _ = await processing_obj.common_processing_pre_call_logic( + request=mock_request, + general_settings={}, + user_api_key_dict=ProxyUserAPIKeyAuth(), + proxy_logging_obj=proxy_logging_obj, + proxy_config=proxy_config, + route_type=route_type, + llm_router=self._router(), + ) + return returned_data + + @pytest.mark.asyncio + async def test_retrieving_a_background_response_attaches_its_model_post_call_pipeline( + self, policy_engine, monkeypatch + ): + data = await self._pre_call("aget_responses", monkeypatch) + + assert data["response_id"].startswith("resp_bGl0ZWxsbTpjdXN0b21f") + pipelines = data["litellm_metadata"]["_guardrail_pipelines"] + assert [(policy_name, [step.guardrail for step in pipeline.steps]) for policy_name, pipeline in pipelines] == [ + ("response-governance", ["output-word-filter"]) + ] + assert data["litellm_metadata"]["applied_policies"] == ["response-governance"] + assert data["model"] is None + + @pytest.mark.asyncio + async def test_submitting_a_response_does_not_attach_pipelines_from_its_response_id( + self, policy_engine, monkeypatch + ): + data = await self._pre_call("aresponses", monkeypatch) + + assert "_guardrail_pipelines" not in data["litellm_metadata"] + assert "applied_policies" not in data["litellm_metadata"] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 73dd6746b29..7d2c232ad97 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -30,6 +30,7 @@ from litellm.proxy.common_utils.callback_utils import add_guardrail_to_applied_g from litellm.proxy.utils import ProxyLogging, _streamable_post_call_pipelines from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ContentFilterGuardrail from litellm.types.guardrails import BlockedWord, ContentFilterAction, GuardrailEventHooks +from litellm.types.llms.openai import ResponsesAPIResponse from litellm.types.proxy.policy_engine.pipeline_types import ( GuardrailPipeline, PipelineStep, @@ -1419,8 +1420,69 @@ async def test_streaming_request_whose_pipeline_guardrail_is_missing_streams_ver assert any("response-governance" in message and "gr-post" in message for message in _warnings(caplog)) +def _background_response(status: str, text: str = "") -> ResponsesAPIResponse: + output = ( + [{"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": text}]}] if text else [] + ) + return ResponsesAPIResponse(id="resp_bg", created_at=0, output=output, status=status) + + +def _output_blocking_callbacks(seen: dict[str, object]) -> list[CustomGuardrail]: + class OutputBlockingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + seen["response"] = response + raise HTTPException(status_code=400, detail={"error": "output blocked"}) + + return [OutputBlockingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)] + + @pytest.mark.asyncio -async def test_pre_call_hook_accepts_background_request_with_post_call_pipeline( +@pytest.mark.parametrize("pending_status", ["queued", "in_progress"]) +async def test_post_call_success_hook_waits_for_pending_background_response_before_running_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch, caplog, pending_status +): + seen: dict[str, object] = {} + monkeypatch.setattr(litellm, "callbacks", _output_blocking_callbacks(seen)) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data(background=True) + response = _background_response(pending_status) + + with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"): + out = await proxy_logging.post_call_success_hook( + data=data, response=response, user_api_key_dict=make_user_api_key_auth() + ) + + assert out is response + assert "response" not in seen + assert not _warnings(caplog) + assert any( + "response-governance" in record.getMessage() and pending_status in record.getMessage() + for record in caplog.records + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("final_status", ["completed", "incomplete"]) +async def test_post_call_success_hook_runs_pipeline_on_retrieved_background_response( + proxy_logging, make_user_api_key_auth, monkeypatch, final_status +): + seen: dict[str, object] = {} + monkeypatch.setattr(litellm, "callbacks", _output_blocking_callbacks(seen)) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _post_call_pipeline_data() + response = _background_response(final_status, text="kumquat") + + with pytest.raises(HTTPException) as info: + await proxy_logging.post_call_success_hook( + data=data, response=response, user_api_key_dict=make_user_api_key_auth() + ) + + assert info.value.detail["error"] == "output blocked" + assert seen["response"] is response + + +@pytest.mark.asyncio +async def test_pre_call_hook_stays_quiet_on_background_request_with_post_call_pipeline( proxy_logging, make_user_api_key_auth, monkeypatch, caplog ): monkeypatch.setattr(litellm, "callbacks", []) @@ -1436,36 +1498,7 @@ async def test_pre_call_hook_accepts_background_request_with_post_call_pipeline( assert out is not None assert out.get("background") is True - assert any("response-governance" in message and "background" in message for message in _warnings(caplog)) - - -@pytest.mark.asyncio -async def test_pre_call_hook_stays_quiet_on_background_request_without_post_call_pipeline( - proxy_logging, make_user_api_key_auth, monkeypatch, caplog -): - seen: Dict[str, Any] = {} - monkeypatch.setattr(litellm, "callbacks", [_unified_stream_guardrail(seen)]) - pre_call = GuardrailPipeline(mode="pre_call", steps=[PipelineStep(guardrail="gr-post", on_fail="block")]) - data = { - "model": "m", - "messages": [{"role": "user", "content": "hi"}], - "background": True, - "metadata": { - "_guardrail_pipelines": [("request-governance", pre_call)], - "_pipeline_managed_guardrails": {"gr-post"}, - }, - } - - with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): - out = await proxy_logging.pre_call_hook( - user_api_key_dict=make_user_api_key_auth(), - data=data, - call_type="aresponses", - guardrails_only=True, - ) - - assert out is not None - assert not any("background" in message for message in _warnings(caplog)) + assert not _warnings(caplog) # --------------------------------------------------------------------------- From 2285640eeab7206f609d54286df0bac4130987f7 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:02:39 -0700 Subject: [PATCH 097/223] fix(spend-tracking): recover key alias for session tokens from spend logs CLI session tokens are in-memory only and never get a LiteLLM_VerificationToken row, so the usage APIs could not resolve key_alias, team_id, or user_email for their spend rows: the exact join and the reverse-hash recovery both miss. The owner is written to LiteLLM_SpendLogs.metadata at request time under the same hashed api_key, so read it back from there for keys still unresolved after the token-table passes. The lookup is sha256-gated like the existing reverse-hash recovery and bounded to the records' startTime window (min date minus one day, max date plus two) so it stays on the startTime index. No migration. Also guard the window parser against the date=None rollup rows GROUPING SETS aggregation emits, which raised TypeError from strptime and turned the aggregated usage endpoints into HTTP 500s. --- .../common_daily_activity.py | 51 ++++++++-- .../spend_tracking/key_metadata_recovery.py | 48 ++++++++++ .../test_common_daily_activity.py | 53 +++++++++++ .../test_key_metadata_recovery.py | 95 ++++++++++++++++++- 4 files changed, 238 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/management_endpoints/common_daily_activity.py b/litellm/proxy/management_endpoints/common_daily_activity.py index ce6a97708ab..c6c5aad5926 100644 --- a/litellm/proxy/management_endpoints/common_daily_activity.py +++ b/litellm/proxy/management_endpoints/common_daily_activity.py @@ -14,6 +14,7 @@ from litellm.proxy._types import CommonProxyErrors from litellm.proxy.spend_tracking.key_metadata_recovery import ( attach_user_emails, recover_double_hashed_key_metadata, + recover_key_metadata_from_spend_logs, ) from litellm.proxy.spend_tracking.ptu_feature_flag import is_ptu_cost_attribution_enabled from litellm.proxy.utils import PrismaClient @@ -433,15 +434,37 @@ def update_breakdown_metrics( return breakdown +def _spend_logs_window(dates: AbstractSet[str | None]) -> tuple[datetime, datetime] | None: + parsed: Final = sorted(day for day in (_parse_spend_date(raw) for raw in dates) if day is not None) + if not parsed: + return None + return (parsed[0] - timedelta(days=1), parsed[-1] + timedelta(days=2)) + + +def _parse_spend_date(raw: str | None) -> datetime | None: + if not isinstance(raw, str): + return None + try: + return datetime.fromisoformat(raw) + except ValueError: + return None + + +_EMPTY_KEY_METADATA: Final[Mapping[str, _KeyMetadataDict]] = MappingProxyType({}) + + async def get_api_key_metadata( prisma_client: PrismaClient, api_keys: AbstractSet[str], + spend_logs_window: tuple[datetime, datetime] | None = None, ) -> Mapping[str, _KeyMetadataDict]: """Get api key metadata, falling back to deleted keys table for keys not found in active table. This ensures that key_alias and team_id are preserved in historical activity logs even after a key is deleted or regenerated. Also recovers aliases for api_key - values that were double-hashed by the v1.99 spend-log provenance gate. + values that were double-hashed by the v1.99 spend-log provenance gate, and, when + spend_logs_window is given, for keys never written to either token table (CLI + session tokens) from the spend-log rows those requests wrote in that window. """ key_records: Sequence[PrismaVerificationToken] = await VerificationTokenRepository(prisma_client).table.find_many( where={"token": {"in": list(api_keys)}} @@ -481,11 +504,17 @@ async def get_api_key_metadata( ) still_missing: Final = api_keys - frozenset(result) - combined: Final = ( - result - if not still_missing - else MappingProxyType({**result, **(await recover_double_hashed_key_metadata(prisma_client, still_missing))}) + from_reverse_hash: Final = ( + await recover_double_hashed_key_metadata(prisma_client, still_missing) if still_missing else _EMPTY_KEY_METADATA ) + after_token_recovery: Final = MappingProxyType({**result, **from_reverse_hash}) + unresolved: Final = api_keys - frozenset(after_token_recovery) + from_spend_logs: Final = ( + await recover_key_metadata_from_spend_logs(prisma_client, unresolved, spend_logs_window) + if unresolved and spend_logs_window is not None + else _EMPTY_KEY_METADATA + ) + combined: Final = MappingProxyType({**after_token_recovery, **from_spend_logs}) return await attach_user_emails(prisma_client, combined) @@ -898,7 +927,9 @@ async def _aggregate_spend_records( api_key_metadata: dict[str, _KeyMetadataDict] = {} if api_keys: - api_key_metadata = await get_api_key_metadata(prisma_client, api_keys) + api_key_metadata = await get_api_key_metadata( + prisma_client, api_keys, _spend_logs_window(frozenset(record.date for record in records)) + ) return await asyncio.to_thread( _aggregate_spend_records_sync, @@ -1094,7 +1125,9 @@ async def _aggregate_grouping_sets_records( api_key_metadata: dict[str, _KeyMetadataDict] = {} if api_keys: - api_key_metadata = await get_api_key_metadata(prisma_client, api_keys) + api_key_metadata = await get_api_key_metadata( + prisma_client, api_keys, _spend_logs_window(frozenset(r.date for r in records)) + ) return await asyncio.to_thread( _aggregate_grouping_sets_records_sync, @@ -1357,7 +1390,9 @@ async def get_daily_activity_aggregated( r.api_key for r in entity_records if r.api_key and r.api_key != PTU_SENTINEL_API_KEY ) entity_key_metadata: Final = ( - await get_api_key_metadata(prisma_client, entity_api_keys) + await get_api_key_metadata( + prisma_client, entity_api_keys, _spend_logs_window(frozenset(r.date for r in entity_records)) + ) if entity_api_keys else {} # mutable-ok: matches the helper's dict return ) diff --git a/litellm/proxy/spend_tracking/key_metadata_recovery.py b/litellm/proxy/spend_tracking/key_metadata_recovery.py index 7de18521edd..81c1f4cf119 100644 --- a/litellm/proxy/spend_tracking/key_metadata_recovery.py +++ b/litellm/proxy/spend_tracking/key_metadata_recovery.py @@ -1,5 +1,6 @@ from collections.abc import Awaitable, Callable, Mapping, Sequence from collections.abc import Set as AbstractSet +from datetime import datetime from types import MappingProxyType from typing import Final, TypeVar @@ -27,6 +28,19 @@ WHERE encode(sha256(convert_to(token, 'UTF8')), 'hex') = ANY($1::text[]) ORDER BY token, deleted_at DESC """ +_SPEND_LOG_ALIAS_SQL: Final = """ +SELECT DISTINCT ON (api_key) + api_key AS digest, + metadata->>'user_api_key_alias' AS key_alias, + COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id, + COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id +FROM "LiteLLM_SpendLogs" +WHERE api_key = ANY($1::text[]) + AND "startTime" >= $2::timestamp + AND "startTime" < $3::timestamp +ORDER BY api_key, (metadata->>'user_api_key_alias') IS NULL, "startTime" DESC +""" + class KeyMetadataDict(TypedDict, total=False): key_alias: ReadOnly[str | None] @@ -168,6 +182,40 @@ async def recover_double_hashed_key_metadata( return MappingProxyType({**from_active, **from_deleted}) +async def recover_key_metadata_from_spend_logs( + prisma_client: PrismaClient, + missing_keys: AbstractSet[str], + window: tuple[datetime, datetime], +) -> Mapping[str, KeyMetadataDict]: + """ + Recover key_alias/team_id/user_id for hashed api_key values absent from both + verification-token tables, e.g. in-memory CLI session tokens that never get a + token row. Their owner is written to LiteLLM_SpendLogs metadata at request + time under the same hashed api_key, so it is the only surviving source. Only + sha256 digests are looked up, matching the reverse-hash recovery gate, since + every current api_key value in spend logs is a token hash. The [start, end) + bound keeps the lookup on the startTime index instead of scanning the table. + """ + sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key)) + if not sha_missing: + return _EMPTY_KEY_METADATA + start, end = window + rows: Final = await _db_or_empty( + lambda: prisma_client.db.query_raw(_SPEND_LOG_ALIAS_SQL, sorted(sha_missing), start, end), + "Failed spend-log alias recovery for %d missing keys: %s", + len(sha_missing), + ) + if rows is None: + return _EMPTY_KEY_METADATA + return MappingProxyType( + { + row.digest: KeyMetadataDict(key_alias=row.key_alias, team_id=row.team_id, user_id=row.user_id) + for row in _TOKEN_DIGEST_ROWS.validate_python(rows) + if row.digest in sha_missing and (row.key_alias or row.user_id or row.team_id) + } + ) + + def _row_with_recovered_fields( row: Mapping[str, object], recovered: Mapping[str, KeyMetadataDict], diff --git a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py index 37a54c4901a..4d3dc320d8f 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py +++ b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py @@ -2105,3 +2105,56 @@ async def test_get_daily_activity_aggregated_with_entity_breakdown(): # Rollups with the entity bit set must still land in their usual buckets assert daily.breakdown.models["gpt-4o"].metrics.spend == 18.0 assert daily.breakdown.api_keys["key-1"].metrics.spend == 12.0 + + +@pytest.mark.asyncio +async def test_get_api_key_metadata_resolves_session_key_via_spend_log_window(): + """ + A CLI session token has no verification-token row, so the active/deleted lookups + and reverse-hash all miss. Given a spend-log window, its alias and owner are + recovered from the spend-log metadata and its email is filled from the user table. + """ + from litellm.proxy.utils import hash_token + + session_digest = hash_token("cli-session-user-42") + mock_prisma = MagicMock() + mock_prisma.db.litellm_verificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_deletedverificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_usertable.find_many = AsyncMock( + return_value=[SimpleNamespace(user_id="user-42", user_email="user42@example.com")] + ) + + async def query_raw(sql, *params): + if "LiteLLM_SpendLogs" in sql: + return [{"digest": session_digest, "key_alias": "cli-session-user-42", "team_id": None, "user_id": "user-42"}] + return [] + + mock_prisma.db.query_raw = AsyncMock(side_effect=query_raw) + + result = await get_api_key_metadata( + prisma_client=mock_prisma, + api_keys={session_digest}, + spend_logs_window=(datetime(2026, 9, 7), datetime(2026, 9, 10)), + ) + + assert result[session_digest]["key_alias"] == "cli-session-user-42" + assert result[session_digest]["user_id"] == "user-42" + assert result[session_digest]["user_email"] == "user42@example.com" + spend_log_calls = [call.args for call in mock_prisma.db.query_raw.call_args_list if "LiteLLM_SpendLogs" in call.args[0]] + ((_, digests, start, end),) = spend_log_calls + assert digests == [session_digest] + assert (start, end) == (datetime(2026, 9, 7), datetime(2026, 9, 10)) + + +def test_spend_logs_window_pads_min_minus_one_day_and_max_plus_two_days(): + from litellm.proxy.management_endpoints.common_daily_activity import _spend_logs_window + + window = _spend_logs_window({"2026-09-08", "2026-09-05", "not-a-date"}) + + assert window == (datetime(2026, 9, 4), datetime(2026, 9, 10)) + + +def test_spend_logs_window_is_none_when_no_date_parses(): + from litellm.proxy.management_endpoints.common_daily_activity import _spend_logs_window + + assert _spend_logs_window({"garbage", ""}) is None diff --git a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py index 7a80319239d..9e42f017106 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py +++ b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py @@ -1,4 +1,5 @@ from collections.abc import Sequence +from datetime import datetime from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock @@ -8,14 +9,24 @@ from prisma.errors import PrismaError from litellm.proxy.spend_tracking.key_metadata_recovery import ( fill_missing_api_key_aliases, recover_double_hashed_key_metadata, + recover_key_metadata_from_spend_logs, ) from litellm.proxy.utils import hash_token -def _digest_row(digest: str, key_alias: str, team_id: str | None, user_id: str | None) -> dict[str, str | None]: +def _digest_row(digest: str, key_alias: str | None, team_id: str | None, user_id: str | None) -> dict[str, str | None]: return {"digest": digest, "key_alias": key_alias, "team_id": team_id, "user_id": user_id} +def _query_raw_spend_logs(rows: Sequence[dict[str, str | None]]) -> AsyncMock: + async def query_raw(sql: str, *params: object) -> list[dict[str, str | None]]: + if '"LiteLLM_SpendLogs"' in sql: + return list(rows) + raise AssertionError(f"unexpected query: {sql}") + + return AsyncMock(side_effect=query_raw) + + def _query_raw_by_table( active_rows: Sequence[dict[str, str | None]], deleted_rows: Sequence[dict[str, str | None]], @@ -218,3 +229,85 @@ async def test_fill_missing_api_key_aliases_skips_named_keys_that_have_no_email( assert filled == rows mock_prisma.db.query_raw.assert_not_called() + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_resolves_session_token_from_metadata(): + """ + CLI session tokens never get a verification-token row, so both the exact join and + the reverse-hash lookup miss them. Their owner survives only in the spend-log + metadata written at request time, keyed by the same hashed api_key. + """ + session_digest = hash_token("cli-session-repro-user-6852") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs( + [_digest_row(session_digest, "cli-session-repro-user-6852", None, "repro-user-6852")] + ) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {session_digest}, window) + + assert result[session_digest]["key_alias"] == "cli-session-repro-user-6852" + assert result[session_digest]["user_id"] == "repro-user-6852" + ((_, digests, start, end),) = [call.args for call in mock_prisma.db.query_raw.call_args_list] + assert digests == [session_digest] + assert (start, end) == window + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_skips_query_when_no_missing_keys(): + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = AsyncMock(return_value=[]) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, set(), window) + + assert result == {} + mock_prisma.db.query_raw.assert_not_called() + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_returns_empty_on_prisma_error(): + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = AsyncMock(side_effect=PrismaError("db down")) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {hash_token("cli-session-x")}, window) + + assert result == {} + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_ignores_foreign_and_all_null_rows(): + wanted = hash_token("cli-session-wanted") + all_null = hash_token("cli-session-null") + foreign = hash_token("cli-session-foreign") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs( + [ + _digest_row(wanted, "kept-alias", None, "owner-1"), + _digest_row(all_null, None, None, None), + _digest_row(foreign, "foreign-alias", None, "owner-2"), + ] + ) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {wanted, all_null}, window) + + assert set(result) == {wanted} + assert result[wanted]["key_alias"] == "kept-alias" + assert result[wanted]["user_id"] == "owner-1" + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_skips_non_sha256_keys(): + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = AsyncMock(return_value=[]) + + result = await recover_key_metadata_from_spend_logs( + mock_prisma, {"cli-session-raw-1798", "key-hash-short"}, window + ) + + assert result == {} + mock_prisma.db.query_raw.assert_not_called() From 629b464cd68114e595b584c9fdde86484f8c219d Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 8 Sep 2026 12:06:52 -0700 Subject: [PATCH 098/223] feat(auto_router): opt-in NON_REASONING tier below SIMPLE Agent harnesses send a lot of operational turns that relay or reformat tool output rather than reason about it, and the cheapest built-in tier was SIMPLE. NON_REASONING adds a rung below it, behind enable_non_reasoning_tier so an already-deployed router cannot move. The toggle is what keeps it safe. The tier set feeds the classifier rubric, the response-format enum, the escalation ladder and the savings baseline, so a default-on fifth tier would have changed what every existing router sends and where its traffic lands. Off, the ladder, rubric, wire labels and baseline are byte-identical to before. On, the rung is added at index 0, escalation walks up out of it, and it can never win the savings baseline. It requires an llm or custom classifier and a model of its own: the v1 score ladder has no rung below simple_medium and the v2 artifact is trained on four classes, so the heuristic scorers cannot produce the tier and a router that enabled it there would pay for a bullet nothing reaches. The dashboard follows the same flag, and the edit modal now reads the tier back from the stored config rather than assuming four keys, since it rewrites tiers wholesale on save and would otherwise delete a hand-written tier on any edit. --- .../public_endpoints/public_endpoints.py | 3 + .../classification_rubrics.py | 5 + .../complexity_router/complexity_router.py | 46 +++- .../complexity_router/config.py | 91 +++++++- .../public_endpoints/public_endpoints.py | 9 +- .../router_strategy/test_complexity_router.py | 197 +++++++++++++++++- .../add_model/ComplexityRouterConfig.test.tsx | 4 +- .../add_model/ComplexityRouterConfig.tsx | 68 +++++- .../components/add_model/KeywordTierRules.tsx | 2 +- .../add_model/add_auto_router_tab.tsx | 1 + .../build_complexity_router_config.ts | 6 + .../add_model/complexity_router_tiers.ts | 7 +- .../components/add_model/tier_rows.test.ts | 31 +++ .../src/components/add_model/tier_rows.ts | 18 +- .../components/add_model/tier_set_actions.ts | 9 +- ...d_updated_complexity_router_config.test.ts | 49 +++++ .../edit_auto_router_modal.tsx | 14 +- ui/litellm-dashboard/src/lib/http/schema.d.ts | 20 +- 18 files changed, 541 insertions(+), 39 deletions(-) diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index 94a59828451..a7a24e10bdd 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -534,6 +534,9 @@ async def get_autorouter_presets( "/public/autorouter_presets", tags=["public", "auto router"], # mutable-ok: FastAPI route tags take a list response_model=dict[str, AutoRouterPresetRecord], + # An optional tier a preset does not set must not reach the dashboard as a null pool, which the + # template picker would render as an empty tier row the operator never asked for. + response_model_exclude_none=True, ) async def get_public_autorouter_presets() -> Mapping[str, AutoRouterPresetRecord]: """ diff --git a/litellm/router_strategy/complexity_router/classification_rubrics.py b/litellm/router_strategy/complexity_router/classification_rubrics.py index 9f168eabbc4..1dae2902fad 100644 --- a/litellm/router_strategy/complexity_router/classification_rubrics.py +++ b/litellm/router_strategy/complexity_router/classification_rubrics.py @@ -108,6 +108,11 @@ _CALIBRATION_EXAMPLES: Final[Mapping[ClassificationRubric, str]] = MappingProxyT BUSINESS_TIER_CRITERIA: Final[Mapping[ComplexityTier, str]] = MappingProxyType( { + ComplexityTier.NON_REASONING: ( + "operational requests whose whole job is to pass information along or put it in a requested " + "shape: relaying or reformatting tool or system output, acknowledging a completed action, or " + "extracting a stated field. Use it only when no judgment about the content is asked for." + ), ComplexityTier.SIMPLE: ( "greetings, chitchat, or lookups of a fact, policy, price, or date with a short known answer. " "Never for analysis, strategy, or non-trivial work, even if the request is only one sentence." diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index c8644f52c57..fd0d53e0902 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -100,7 +100,12 @@ else: class TierClassification(BaseModel): - """Structured response schema for the LLM-based complexity classifier.""" + """Structured response schema for the LLM-based complexity classifier. + + The four-tier ladder, which is what a router that did not opt into NON_REASONING sends. The + enum actually put on the wire is rebuilt per router from `classifier_wire_labels`, so a + five-tier or renamed ladder widens it there rather than here. + """ tier: Literal["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] @@ -116,8 +121,20 @@ def _tier_name(tier: ComplexityTier | str) -> str: return tier.value if isinstance(tier, ComplexityTier) else tier +def _built_in_tier_or_none(tier_name: str) -> ComplexityTier | None: + """The built-in tier a `tiers` key names, or None when the key is an operator-defined name.""" + return ComplexityTier.__members__.get(tier_name) + + _CLASSIFICATION_TIER_CRITERIA: Final[Mapping[ComplexityTier, str]] = MappingProxyType( { + ComplexityTier.NON_REASONING: ( + "operational requests whose whole job is to pass information along or put it in a " + "requested shape: relaying or reformatting tool output, acknowledging a completed action, " + "or extracting a stated value. Use it only when no judgment about the content is asked for; " + "the moment the request is to summarize, compare, explain, debug, or decide, it belongs " + "in a higher tier however short it is." + ), ComplexityTier.SIMPLE: ( "greetings, chitchat, or factual lookups with a short known answer. Do not use this tier for " "unsolved problems, proofs, deep theory, multi-step analysis, or non-trivial code, even if the " @@ -1228,7 +1245,7 @@ class ComplexityRouter(CustomLogger): """ if self.config.has_custom_tiers: return tuple(dict.fromkeys(model for models in self._tier_pools().values() for model in models)) - for tier in reversed(TIER_SEVERITY_ORDER): + for tier in reversed(self.config.active_tier_severity_order()): models = self.config.tiers.get(tier.value) if models: return tuple(models) if isinstance(models, list) else (models,) @@ -1863,7 +1880,11 @@ class ComplexityRouter(CustomLogger): default_model: Final = self.config.default_model pools: Final = self._tier_pools() tier: Final = next( - (candidate for candidate in TIER_SEVERITY_ORDER if default_model in pools.get(candidate.value, ())), + ( + candidate + for candidate in self.config.active_tier_severity_order() + if default_model in pools.get(candidate.value, ()) + ), ComplexityTier.MEDIUM, ) return ClassificationOutcome( @@ -2248,7 +2269,8 @@ class ComplexityRouter(CustomLogger): return self._fitting_tier_fallback(classified_tier, fit_filter) request_type: Final = classify_prompt(user_message) - classified_idx: Final = TIER_SEVERITY_ORDER.index(classified_tier) + severity_order: Final = self.config.active_tier_severity_order() + classified_idx: Final = severity_order.index(classified_tier) pools: Final = self._tier_pools() classified_candidates: Final = _allowed(tuple(pools.get(_tier_name(classified_tier), ())), fit_filter) cold_start_candidates: Final = tuple( @@ -2313,7 +2335,7 @@ class ComplexityRouter(CustomLogger): else: model_tiers = self._model_tiers.get(model, (classified_tier,)) distance = min( - abs(TIER_SEVERITY_ORDER.index(model_tier) - classified_idx) for model_tier in model_tiers + abs(severity_order.index(model_tier) - classified_idx) for model_tier in model_tiers ) score = quality_weight * quality_sample + cost_weight * cost_score - penalty_weight * distance candidate_scores.append( @@ -2610,10 +2632,15 @@ class ComplexityRouter(CustomLogger): def _tier_for_model(self, model: str) -> ComplexityTier | None: """Return the most-severe configured tier whose pool contains this model.""" pools: Final = self._tier_pools() - matched: Final = tuple(ComplexityTier(tier_name) for tier_name, models in pools.items() if model in models) + order: Final = self.config.active_tier_severity_order() + matched: Final = tuple( + tier + for tier_name, models in pools.items() + if model in models and (tier := _built_in_tier_or_none(tier_name)) is not None and tier in order + ) if not matched: return None - return max(matched, key=TIER_SEVERITY_ORDER.index) + return max(matched, key=order.index) def _escalate_tier(self, tier: ComplexityTier | str) -> ComplexityTier | str: """Bump a tier one step up to the next-higher configured tier. @@ -2628,9 +2655,10 @@ class ComplexityRouter(CustomLogger): if self.config.has_custom_tiers: return tier configured: Final = frozenset(self.config.tiers) - current_index: Final = TIER_SEVERITY_ORDER.index(tier) + order: Final = self.config.active_tier_severity_order() + current_index: Final = order.index(tier) higher_tiers: Final = tuple( - candidate for candidate in TIER_SEVERITY_ORDER[current_index + 1 :] if candidate.value in configured + candidate for candidate in order[current_index + 1 :] if candidate.value in configured ) return higher_tiers[0] if higher_tiers else tier diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 3ec9f9b5394..bdfa0c9c389 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -29,6 +29,7 @@ from .tier_predictor import TrainedTierArtifact class ComplexityTier(str, Enum): """Complexity tiers for routing decisions.""" + NON_REASONING = "NON_REASONING" SIMPLE = "SIMPLE" MEDIUM = "MEDIUM" COMPLEX = "COMPLEX" @@ -55,6 +56,11 @@ DEFAULT_CLASSIFICATION_RUBRIC: Final[ClassificationRubric] = ClassificationRubri LLM_CLASSIFIER_TYPES: Final[frozenset[str]] = frozenset({"llm", "heuristic_first", "hybrid"}) +# The ladder as it has always shipped. NON_REASONING is absent because it is opt-in: an existing +# router must not gain a rubric bullet, a wire label, or a rung it never configured, and the +# heuristic_v2 artifact is trained on exactly these four classes. Read the active ladder off the +# config (`tier_names`, `active_tier_severity_order`) rather than this constant wherever the +# operator's `enable_non_reasoning_tier` can reach. TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( ComplexityTier.SIMPLE, ComplexityTier.MEDIUM, @@ -62,6 +68,16 @@ TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( ComplexityTier.REASONING, ) +NON_REASONING_TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( + ComplexityTier.NON_REASONING, + *TIER_SEVERITY_ORDER, +) + + +def tier_severity_order(non_reasoning_enabled: bool) -> tuple[ComplexityTier, ...]: + """The built-in ladder for one router, tier 0 included only when it opted in.""" + return NON_REASONING_TIER_SEVERITY_ORDER if non_reasoning_enabled else TIER_SEVERITY_ORDER + DEFAULT_TIER_DISTANCE_PENALTY: Final[float] = 0.5 DEFAULT_CLASSIFIER_CONTEXT_WINDOW_SIZE: Final[int] = 3 @@ -142,6 +158,9 @@ def normalize_classification_examples(value: str | None) -> str | None: return _normalize_operator_section(value, "classification_examples", MAX_CLASSIFICATION_EXAMPLES_CHARS) +_BUILT_IN_TIER_NAMES: Final[str] = ", ".join(ComplexityTier.__members__) + + class TierDefinition(BaseModel): """An operator-defined tier: the name the LLM classifier must return and its rubric description.""" @@ -152,7 +171,7 @@ class TierDefinition(BaseModel): default=None, description=( "What belongs in this tier; rendered as this tier's bullet in the classifier rubric. " - "Required unless the name is a built-in tier (SIMPLE/MEDIUM/COMPLEX/REASONING), which " + f"Required unless the name is a built-in tier ({_BUILT_IN_TIER_NAMES}), which " "inherits the built-in criteria when omitted" ), ) @@ -174,7 +193,7 @@ class TierDefinition(BaseModel): if description is None and name.upper() not in ComplexityTier.__members__: raise ValueError( f"tier_definitions entry {name!r} must have a description: only the built-in tiers " - "(SIMPLE, MEDIUM, COMPLEX, REASONING) carry one the rubric can inherit" + f"({_BUILT_IN_TIER_NAMES}) carry one the rubric can inherit" ) rendered_on_one_line: Final = (name, description or "") if any("\n" in part or "\r" in part for part in rendered_on_one_line): @@ -703,6 +722,20 @@ class ComplexityRouterConfig(BaseModel): default_factory=dict, ) + enable_non_reasoning_tier: bool = Field( + default=False, + description=( + "Add NON_REASONING as a fifth built-in tier below SIMPLE, for operational agent traffic " + "that relays or reformats information rather than reasoning about it. Off by default: " + "turning it on adds a rung to this router's ladder, a bullet to the LLM classifier's " + "rubric, and a value the classifier may return, all of which move tier decisions and " + "spend on an already-deployed router. Requires an LLM classifier or a custom classifier " + "plugin, since the heuristic scorers cannot produce the tier, and a model in `tiers` " + "under the NON_REASONING key. Escalation still walks up from it, and it is never the " + "savings baseline or a `heuristic_v2` prediction." + ), + ) + tier_definitions: tuple[TierDefinition, ...] | None = Field( default=None, description=( @@ -1491,11 +1524,16 @@ class ComplexityRouterConfig(BaseModel): which still makes it a dependency on every one of those requests.""" return self.classifier_type in LLM_CLASSIFIER_TYPES + def active_tier_severity_order(self) -> tuple[ComplexityTier, ...]: + """This router's built-in ladder, ascending. Meaningless for a custom tier set, whose + severity order is tier_definitions list order over names that are not enum members.""" + return tier_severity_order(self.enable_non_reasoning_tier) + def tier_names(self) -> tuple[str, ...]: """The active tier names: the defined names, or the built-in set in severity order.""" if self.tier_definitions is not None: return tuple(definition.name for definition in self.tier_definitions) - return tuple(tier.value for tier in TIER_SEVERITY_ORDER) + return tuple(tier.value for tier in self.active_tier_severity_order()) def classifier_wire_labels(self) -> tuple[str, ...]: """The tier names the classifier is told to emit: defined names, or the display labels.""" @@ -1587,6 +1625,41 @@ class ComplexityRouterConfig(BaseModel): if present ) + @model_validator(mode="after") + def _validate_non_reasoning_tier(self) -> "ComplexityRouterConfig": + """Gate the opt-in fifth tier on the two things that make it reachable and routable. + + The heuristic scorers cannot emit it (the v1 score ladder has no rung below simple_medium + and the v2 artifact is trained on four classes), so a router whose classifier can never + return the tier would pay for a rubric bullet and a configured pool that no request reaches. + """ + non_reasoning_key: Final = ComplexityTier.NON_REASONING.value + if not self.enable_non_reasoning_tier: + if not self.has_custom_tiers and non_reasoning_key in self.tiers: + raise ValueError( + f"tiers names {non_reasoning_key} but enable_non_reasoning_tier is False, so no request " + "can route there; set enable_non_reasoning_tier: true or drop the tier" + ) + return self + if self.has_custom_tiers: + raise ValueError( + "enable_non_reasoning_tier cannot be combined with tier_definitions: a custom tier set " + f"replaces the built-in ladder, so name a tier {non_reasoning_key} in tier_definitions instead" + ) + if self.classifier_type not in ("llm", "custom"): + raise ValueError( + f"enable_non_reasoning_tier requires classifier_type 'llm' or 'custom', got " + f"{self.classifier_type!r}: the heuristic scorers only produce the four tiers from SIMPLE up, " + f"so nothing would ever classify as {non_reasoning_key}" + ) + if not self.tiers.get(non_reasoning_key): + raise ValueError( + f"enable_non_reasoning_tier requires tiers to map {non_reasoning_key} to at least one model: " + "the tier exists to send operational traffic somewhere cheaper, and an unconfigured tier " + "would fall through to the default model" + ) + return self + @model_validator(mode="after") def _validate_tier_definitions(self) -> "ComplexityRouterConfig": if self.tier_definitions is None: @@ -1609,7 +1682,7 @@ class ComplexityRouterConfig(BaseModel): if self.classifier_type in ("heuristic", "heuristic_v2", "heuristic_first", "hybrid"): raise ValueError( "tier_definitions requires classifier_type 'llm' or 'custom': the heuristic scorer only " - "produces the four built-in tiers, as does heuristic_v2" + "produces the built-in tiers from SIMPLE up, as does heuristic_v2" ) conflicts: Final = self._tier_definition_conflicts() if conflicts: @@ -1762,16 +1835,18 @@ class ComplexityRouterConfig(BaseModel): return self.tier_labels.get(tier, "").strip() or tier.value def labeled_tiers(self) -> tuple[tuple[ComplexityTier, str], ...]: - """Every tier paired with its display name, in ascending severity order.""" - return tuple((tier, self.tier_label(tier)) for tier in TIER_SEVERITY_ORDER) + """Every active tier paired with its display name, in ascending severity order.""" + return tuple((tier, self.tier_label(tier)) for tier in self.active_tier_severity_order()) def tier_for_label(self, label: str) -> ComplexityTier | None: - """Resolve a display name back to its tier, case-insensitively, then canonical names.""" + """Resolve a display name back to its active tier, case-insensitively, then canonical + names. A tier this router did not opt into resolves to None, so a classifier naming + NON_REASONING on a four-tier router is an unparseable reply rather than a fifth rung.""" folded: Final = label.strip().casefold() labeled: Final = self.labeled_tiers() return next( (tier for tier, tier_label in labeled if tier_label.casefold() == folded), - next((tier for tier in TIER_SEVERITY_ORDER if tier.value.casefold() == folded), None), + next((tier for tier, _ in labeled if tier.value.casefold() == folded), None), ) diff --git a/litellm/types/proxy/public_endpoints/public_endpoints.py b/litellm/types/proxy/public_endpoints/public_endpoints.py index fa73926305b..fb5a37c45fc 100644 --- a/litellm/types/proxy/public_endpoints/public_endpoints.py +++ b/litellm/types/proxy/public_endpoints/public_endpoints.py @@ -74,10 +74,14 @@ class SupportedEndpointsResponse(BaseModel): class AutoRouterPresetTiers(BaseModel): - """Exactly the four built-in tiers the dashboard's preset prefill can apply. + """The built-in tiers the dashboard's preset prefill can apply. extra="forbid" on purpose: a tier name this dashboard cannot apply would grey out or crash the - picker, so such a catalog is rejected wholesale and the bundled one serves instead. + picker, so such a catalog is rejected wholesale and the bundled one serves instead. NON_REASONING + is optional rather than required so this proxy accepts both a catalog that ships the opt-in fifth + tier and the four-tier catalogs that predate it. It stays None when unset rather than defaulting + to an empty pool, so a four-tier preset serves the tier set it was published with instead of + growing a key the dashboard would render as an empty fifth tier row. """ model_config = ConfigDict(extra="forbid") @@ -86,6 +90,7 @@ class AutoRouterPresetTiers(BaseModel): MEDIUM: Sequence[str] COMPLEX: Sequence[str] REASONING: Sequence[str] + NON_REASONING: Sequence[str] | None = None class AutoRouterPresetConfig(BaseModel): diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index 5b1d8562abd..ce9cd5d3b7d 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -44,6 +44,7 @@ from litellm.router_strategy.complexity_router.config import ( DEFAULT_CLASSIFIER_CONTEXT_WINDOW_SIZE, DEFAULT_COMPLEXITY_CONFIG, DEFAULT_TECHNICAL_KEYWORDS, + TIER_SEVERITY_ORDER, ClassificationRubric, ClassifierLLMConfig, ComplexityRouterConfig, @@ -11044,7 +11045,7 @@ async def test_tier_model_params_reach_the_hook_response_and_override_client_val async def test_tier_params_mask_credentials_in_routing_decision(route, mock_router_instance): params = {"reasoning_effort": "xhigh", "api_key": "secret-tier-key"} config = { - "tiers": {tier.value: {"model_name": "opus", "litellm_params": params} for tier in ComplexityTier}, + "tiers": {tier.value: {"model_name": "opus", "litellm_params": params} for tier in TIER_SEVERITY_ORDER}, "keyword_tier_rules": [{"keywords": ["reason carefully"], "tier": "REASONING"}] if route == "keyword" else None, "session_affinity": route == "session", } @@ -13212,3 +13213,197 @@ class TestClassifierVision: def test_max_images_must_be_positive(self): with pytest.raises(ValidationError): ClassifierLLMConfig(model="clf", vision={"enabled": True, "max_images": 0}) + + +NON_REASONING_TIERS: Final = { + "NON_REASONING": "gpt-4o-mini", + "SIMPLE": "gpt-4o-mini", + "MEDIUM": "gpt-4o", + "COMPLEX": "claude-sonnet-4-20250514", + "REASONING": "o1-preview", +} + + +class TestNonReasoningTier: + """The opt-in fifth built-in tier below SIMPLE. + + Two properties carry the feature. A router that did not opt in must be byte-identical to one + built before the tier existed, because the tier set feeds the classifier rubric, the wire enum, + and the savings baseline, all of which move live routing decisions and spend. A router that did + opt in must be able to actually reach the tier and escalate off it. + """ + + @staticmethod + def _router(mock_router_instance, **overrides) -> ComplexityRouter: + config: Final = { + "tiers": dict(NON_REASONING_TIERS), + "enable_non_reasoning_tier": True, + "classifier_type": "llm", + "classifier_llm_config": {"model": "haiku-classifier"}, + **overrides, + } + return ComplexityRouter( + model_name="test-non-reasoning-router", + litellm_router_instance=mock_router_instance, + complexity_router_config=config, + ) + + def test_ladder_gains_a_rung_below_simple_only_when_enabled(self): + """Tier 0 sits at the bottom. Anywhere else and escalation, the savings baseline, and + heuristic_first's 'highest tier' check would all read a different ladder.""" + enabled: Final = ComplexityRouterConfig( + tiers=dict(NON_REASONING_TIERS), + enable_non_reasoning_tier=True, + classifier_type="llm", + classifier_llm_config={"model": "clf"}, + ) + assert enabled.tier_names() == ("NON_REASONING", "SIMPLE", "MEDIUM", "COMPLEX", "REASONING") + assert ComplexityRouterConfig().tier_names() == ("SIMPLE", "MEDIUM", "COMPLEX", "REASONING") + + def test_default_router_is_unchanged_by_the_tier_existing(self): + """The regression that matters for every already-deployed router: the enum grew a member, + and nothing a four-tier router sends or resolves may change because of it.""" + default: Final = ComplexityRouterConfig() + assert default.enable_non_reasoning_tier is False + assert "NON_REASONING" not in DEFAULT_COMPLEXITY_CONFIG.tiers + assert default.classifier_wire_labels() == ("SIMPLE", "MEDIUM", "COMPLEX", "REASONING") + assert default.labeled_tiers() == TIER_SEVERITY_ORDER_LABELED + assert default.resolve_classified_tier("NON_REASONING") is None + + @pytest.mark.parametrize("preset", tuple(ClassificationRubric)) + def test_rubric_gains_the_bullet_only_when_enabled(self, preset): + """Every preset renders one bullet per active tier, so an unset toggle must leave all four + shipped rubrics byte-identical while an enabled one must actually describe the new tier.""" + enabled: Final = ComplexityRouterConfig( + tiers=dict(NON_REASONING_TIERS), + enable_non_reasoning_tier=True, + classifier_type="llm", + classifier_llm_config={"model": "clf"}, + ) + on: Final = classification_system_prompt(3, None, enabled.labeled_tiers(), preset) + off: Final = classification_system_prompt(3, None, ComplexityRouterConfig().labeled_tiers(), preset) + assert "- NON_REASONING:" in on + assert "- NON_REASONING" not in off + + def test_enabled_router_puts_the_tier_on_the_classifier_wire(self, mock_router_instance): + """The response schema's enum is what the classifier may return; without the new label the + tier would be unreachable no matter what the rubric says.""" + router: Final = self._router(mock_router_instance) + enum: Final = router._classifier_response_format["json_schema"]["schema"]["properties"]["tier"]["enum"] + assert enum == ["NON_REASONING", "SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] + + @pytest.mark.asyncio + async def test_classifier_verdict_routes_to_the_tier_model(self, mock_router_instance): + """End to end on the LLM path: the classifier names the tier and the request lands on that + tier's model with the decision recording it.""" + mock_router_instance.acompletion = AsyncMock(return_value=_llm_response('{"tier": "NON_REASONING"}')) + router: Final = self._router(mock_router_instance, tiers={**NON_REASONING_TIERS, "NON_REASONING": "cheap-relay"}) + response = await router.async_pre_routing_hook( + model="test-non-reasoning-router", + request_kwargs={}, + messages=[{"role": "user", "content": "here is the file, pass it along"}], + ) + assert response.model == "cheap-relay" + assert response.routing_decision["tier"] == "NON_REASONING" + assert response.routing_decision["cause"] == "llm_classifier" + + @pytest.mark.asyncio + async def test_a_four_tier_router_ignores_a_non_reasoning_verdict(self, llm_complexity_router, mock_router_instance): + """A classifier that names the tier at a router which never opted in must be an unparseable + reply that falls back, not a silent route to a tier the operator did not configure.""" + mock_router_instance.acompletion = AsyncMock(return_value=_llm_response('{"tier": "NON_REASONING"}')) + outcome = await llm_complexity_router.aclassify("relay this") + assert outcome.tier != ComplexityTier.NON_REASONING + assert outcome.cause != "llm_classifier" + + def test_escalation_walks_up_off_the_tier(self, mock_router_instance): + """Escalation is a built-in-ladder feature and the issue asks for it from the new tier.""" + router: Final = self._router(mock_router_instance) + assert router._escalate_tier(ComplexityTier.NON_REASONING) == ComplexityTier.SIMPLE + assert router._escalate_tier(ComplexityTier.REASONING) == ComplexityTier.REASONING + + def test_escalation_skips_the_tier_when_unconfigured(self, mock_router_instance): + """SIMPLE must still escalate to MEDIUM rather than to the cheaper new rung, or escalation + would route below the model the caller would otherwise have received.""" + router: Final = self._router( + mock_router_instance, + tiers={"NON_REASONING": "cheap-relay", "SIMPLE": "gpt-4o-mini", "MEDIUM": "gpt-4o"}, + ) + assert router._escalate_tier(ComplexityTier.SIMPLE) == ComplexityTier.MEDIUM + + def test_tier_zero_is_never_the_savings_baseline(self, mock_router_instance): + """Savings are measured against the hardest configured tier. If tier 0 could win that pick, + every enabled router's reported savings would invert.""" + assert self._router(mock_router_instance)._hardest_tier_models() == ("o1-preview",) + cheap_only: Final = self._router( + mock_router_instance, tiers={"NON_REASONING": "cheap-relay", "SIMPLE": "gpt-4o-mini"} + ) + assert cheap_only._hardest_tier_models() == ("gpt-4o-mini",) + + def test_the_tier_gets_its_own_display_label(self, mock_router_instance): + """tier_labels covers the built-in tiers, so the new rung must be renameable like the rest.""" + router: Final = self._router(mock_router_instance, tier_labels={"NON_REASONING": "Relay"}) + assert router.config.classifier_wire_labels()[0] == "Relay" + assert router.config.resolve_classified_tier("relay") == ComplexityTier.NON_REASONING + + @pytest.mark.parametrize( + "overrides, expected", + ( + ({"classifier_type": "heuristic", "classifier_llm_config": None}, "requires classifier_type"), + ({"classifier_type": "heuristic_v2", "classifier_llm_config": None}, "requires classifier_type"), + ({"tiers": {"SIMPLE": "a", "MEDIUM": "b"}}, "at least one model"), + ), + ids=["heuristic", "heuristic_v2", "no_model"], + ) + def test_unreachable_or_unroutable_configs_are_rejected(self, overrides, expected): + """The toggle is refused wherever it could not do anything: the heuristic scorers cannot + emit the tier, and an unconfigured tier would fall through to the default model.""" + config: Final = { + "tiers": dict(NON_REASONING_TIERS), + "enable_non_reasoning_tier": True, + "classifier_type": "llm", + "classifier_llm_config": {"model": "clf"}, + **overrides, + } + with pytest.raises(ValidationError, match=expected): + ComplexityRouterConfig.model_validate(config) + + def test_the_tier_cannot_be_configured_without_the_toggle(self): + """Silently ignoring the key would leave an operator paying for a pool nothing routes to.""" + with pytest.raises(ValidationError, match="no request can route there"): + ComplexityRouterConfig(tiers={"NON_REASONING": "cheap", "SIMPLE": "a"}) + + def test_the_toggle_is_refused_alongside_a_custom_tier_set(self): + """A custom tier set replaces the built-in ladder, so both at once has no meaning.""" + with pytest.raises(ValidationError, match="cannot be combined with tier_definitions"): + ComplexityRouterConfig( + enable_non_reasoning_tier=True, + classifier_type="llm", + classifier_llm_config={"model": "clf"}, + tier_definitions=({"name": "lo", "description": "d"}, {"name": "hi", "description": "d"}), + tiers={"lo": "a", "hi": "b"}, + fallback_tier="lo", + ) + + def test_heuristic_v2_predictions_never_reach_the_new_tier(self, mock_router_instance): + """The bundled artifact is trained on four classes, so its 1-based tier index must keep + mapping onto SIMPLE..REASONING. Reading the enabled ladder here would shift every + prediction down a rung and make REASONING unreachable.""" + router: Final = ComplexityRouter( + model_name="v2-router", + litellm_router_instance=mock_router_instance, + complexity_router_config={ + "tiers": {k: v for k, v in NON_REASONING_TIERS.items() if k != "NON_REASONING"}, + "classifier_type": "heuristic_v2", + }, + ) + outcome = router._classify_with_heuristic_v2("implement a distributed rate limiter under concurrency") + assert outcome.tier in TIER_SEVERITY_ORDER + # One probability signal per trained class, named for the tier that class means. A ladder + # shifted by the new rung would relabel all four and lose REASONING off the end. + assert tuple(signal.split(":")[1].split("=")[0] for signal in outcome.signals[1:]) == ( + "simple", + "medium", + "complex", + "reasoning", + ) diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx index 2970e14b335..7632c8f9a70 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx @@ -102,7 +102,7 @@ describe("ComplexityRouterConfig", () => { renderWithProviders(); await user.click(screen.getByText("Advanced: Response Format")); - await user.click(screen.getByRole("switch")); + await user.click(screen.getByRole("switch", { name: "Return raw model name" })); expect(onChange).toHaveBeenCalledWith({ ...defaultValue, @@ -495,7 +495,7 @@ describe("ComplexityRouterConfig", () => { />, ); fireEvent.click(screen.getByText("Advanced: Keyword/Semantic Matching")); - await user.click(screen.getByRole("switch")); + await user.click(screen.getByRole("switch", { name: "Semantic keyword matching" })); expect(onSemanticMatchingEnabledChange).toHaveBeenCalledWith(true, expect.anything()); }); diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index d7df6ce33bb..01e65914c67 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -75,11 +75,16 @@ export const DEFAULT_CLASSIFICATION_MODE: ClassificationMode = "every_request"; */ export type ClassificationFrequency = ClassificationMode | "session"; +/** + * NON_REASONING is optional because it is the opt-in fifth tier: a router that never enabled it + * stores no such key, and hydrating one in would send an empty pool the backend rejects. + */ export type ComplexityTiers = { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; + NON_REASONING?: string[]; }; export type ClassificationRubric = "legacy" | "agentic" | "chat" | "business"; @@ -378,6 +383,11 @@ export type ComplexityTierLabels = Partial export interface ComplexityRouterConfigValue { tiers: ComplexityTiers; + /** + * Opt into the NON_REASONING tier below SIMPLE. Off means the router keeps the four-tier ladder + * it has always had, so an existing router's rubric and tier decisions cannot move under it. + */ + enable_non_reasoning_tier?: boolean; custom_tier_set?: CustomTierSet; tier_labels?: ComplexityTierLabels; /** An explicit pin. Unset means the default tracks the tiers - see resolveComplexityDefaultModel. */ @@ -494,6 +504,11 @@ export const TIER_DESCRIPTIONS: Record< keyof ComplexityTiers, { label: string; description: string; examples: string } > = { + NON_REASONING: { + label: "Non-reasoning", + description: "Operational relay work: passing information along with no judgment about it", + examples: '"Reformat this tool output", "Acknowledge the write succeeded"', + }, SIMPLE: { label: "Simple", description: "Basic questions, greetings, simple factual queries", @@ -516,8 +531,16 @@ export const TIER_DESCRIPTIONS: Record< }, }; +/** Every built-in tier name, including the opt-in one, for label and membership checks. */ export const TIER_KEYS = Object.keys(TIER_DESCRIPTIONS) as Array; +/** + * The four-tier ladder in ascending severity, which is what a router sends unless it opted into + * NON_REASONING. Mirrors TIER_SEVERITY_ORDER in the backend config; use tierOrderFor to get the + * ladder one router actually renders. + */ +export const BUILT_IN_TIER_ORDER: Array = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]; + export const effectiveTierLabel = (tier: keyof ComplexityTiers, tierLabels: ComplexityTierLabels | undefined): string => tierLabels?.[tier]?.trim() || TIER_DESCRIPTIONS[tier].label; @@ -528,9 +551,46 @@ export const DEFAULT_HYBRID_BOUNDARY_MARGIN = 0.03; /** * Tiers the heuristic_first threshold may name. The top tier is excluded because it would short - * circuit every request and leave the classifier unreachable, which the backend rejects. + * circuit every request and leave the classifier unreachable, which the backend rejects. So is + * NON_REASONING, which the backend refuses alongside heuristic_first because the local scorer + * cannot produce it. */ -export const HEURISTIC_FIRST_MAX_TIER_KEYS = TIER_KEYS.slice(0, -1); +export const HEURISTIC_FIRST_MAX_TIER_KEYS = BUILT_IN_TIER_ORDER.slice(0, -1); + +/** + * The opt-in fifth tier. Only offered on the LLM classification method, matching the backend: the + * heuristic scorers cannot produce the tier, so enabling it there would buy a rubric bullet and a + * model pool that no request ever reaches. + */ +const NonReasoningTierToggle: React.FC<{ + value: ComplexityRouterConfigValue; + onChange: (value: ComplexityRouterConfigValue) => void; + available: boolean; +}> = ({ value, onChange, available }) => ( + <> +
+ { + const { NON_REASONING: _dropped, ...keptTiers } = value.tiers; + onChange({ + ...value, + enable_non_reasoning_tier: enabled ? true : undefined, + tiers: enabled ? { ...keptTiers, NON_REASONING: value.tiers.NON_REASONING ?? [] } : keptTiers, + }); + }} + aria-label="Add a non-reasoning tier" + /> + Add a non-reasoning tier +
+ + Adds NON_REASONING below Simple, for operational agent traffic that relays or reformats information rather than + reasoning about it. Escalation still moves up out of it when a request needs more. + {!available && " Requires the LLM classification method."} + + +); const PlanModeOverrideControls: React.FC<{ value: ComplexityRouterConfigValue; @@ -742,6 +802,10 @@ const ComplexityRouterConfig: React.FC = ({ ); })} + {!customTierSet && ( + + )} + = ({ const complexityRouterConfigParams: BuildComplexityRouterConfigParams = { tiers: complexityRouterConfig.tiers, + enableNonReasoningTier: complexityRouterConfig.enable_non_reasoning_tier, customTierSet: complexityRouterConfig.custom_tier_set, defaultModel: complexityRouterConfig.default_model, planModeMinTier: complexityRouterConfig.plan_mode_min_tier, diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts index 7769fb832fe..2f36ec2f43d 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts @@ -119,6 +119,7 @@ const scorerKnobPayload = ({ export interface BuildComplexityRouterConfigParams { tiers: ComplexityTiers; + enableNonReasoningTier?: boolean; customTierSet?: CustomTierSet; defaultModel: string | undefined; planModeMinTier: string | undefined; @@ -180,6 +181,7 @@ export interface TierDefinitionPayload { export interface ComplexityRouterConfigPayload { tiers: ComplexityTiers | Record; + enable_non_reasoning_tier?: boolean; tier_definitions?: TierDefinitionPayload[]; fallback_tier?: string; default_model?: string; @@ -460,6 +462,7 @@ const classifierWireFields = ( export const buildComplexityRouterConfig = ({ tiers, + enableNonReasoningTier, customTierSet, defaultModel, planModeMinTier, @@ -535,6 +538,9 @@ export const buildComplexityRouterConfig = ({ const payload: ComplexityRouterConfigPayload = { tiers, + // Only written when on, and never beside a custom tier set: the backend rejects the two + // together, and an explicit false on a four-tier router would be a key it never carried. + ...(!customTierSet && enableNonReasoningTier && { enable_non_reasoning_tier: true }), ...(serializedTierModelConfigs && { tier_model_configs: serializedTierModelConfigs }), ...(defaultModel?.trim() && { default_model: defaultModel }), ...(planModeMinTier?.trim() && { plan_mode_min_tier: planModeMinTier }), diff --git a/ui/litellm-dashboard/src/components/add_model/complexity_router_tiers.ts b/ui/litellm-dashboard/src/components/add_model/complexity_router_tiers.ts index eb58c94b789..3fec63518e5 100644 --- a/ui/litellm-dashboard/src/components/add_model/complexity_router_tiers.ts +++ b/ui/litellm-dashboard/src/components/add_model/complexity_router_tiers.ts @@ -1,6 +1,6 @@ import type { ComplexityTier } from "./KeywordTierRules"; import type { ModelGroup } from "@/components/llm_calls/fetch_models"; -import { TIER_ORDER } from "./tier_rows"; +import { ALL_BUILT_IN_TIERS, TIER_ORDER } from "./tier_rows"; export type TierModelParams = Record; @@ -145,13 +145,14 @@ export const pruneTierModelParams = ( }; export const DEFAULT_TIER_LABELS: Record = { + NON_REASONING: "Non-reasoning", SIMPLE: "Simple", MEDIUM: "Medium", COMPLEX: "Complex", REASONING: "Reasoning", }; -const isBuiltInTier = (tier: string): tier is ComplexityTier => (TIER_ORDER as string[]).includes(tier); +const isBuiltInTier = (tier: string): tier is ComplexityTier => (ALL_BUILT_IN_TIERS as string[]).includes(tier); const builtInTierLabel = ( tierLabels: Partial> | undefined, @@ -164,7 +165,7 @@ export const tierRowLabel = ( row: { id: string; name: string }, tierLabels?: Partial>, ): string => { - const builtIn = TIER_ORDER.find((tier) => tier === row.id); + const builtIn = ALL_BUILT_IN_TIERS.find((tier) => tier === row.id); const named = row.name.trim(); if (!builtIn || named !== builtIn) return named || "New"; return builtInTierLabel(tierLabels, builtIn); diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts index df50f116f60..07b9a4702aa 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts @@ -168,3 +168,34 @@ describe("tierParamsByRowId", () => { expect(tierParamsByRowId(undefined, rows)).toBeUndefined(); }); }); + +describe("the opt-in non-reasoning tier", () => { + const withTierZero = { SIMPLE: ["a"], MEDIUM: ["b"], COMPLEX: ["c"], REASONING: ["d"], NON_REASONING: ["cheap"] }; + + it("renders no fifth row while the toggle is off", () => { + // The regression for every existing router: the tier exists in the type, and the form must + // still show the four rows it always showed. + expect(activeTierRows({ tiers: withTierZero }).map((row) => row.id)).toEqual([ + "SIMPLE", + "MEDIUM", + "COMPLEX", + "REASONING", + ]); + }); + + it("renders it first, as tier 0, when enabled", () => { + const rows = activeTierRows({ tiers: withTierZero, enable_non_reasoning_tier: true }); + expect(rows.map((row) => row.id)).toEqual(["NON_REASONING", "SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]); + expect(rows[0].models).toEqual(["cheap"]); + }); + + it("renders an enabled tier with no models as an empty row rather than crashing", () => { + const rows = activeTierRows({ tiers, enable_non_reasoning_tier: true }); + expect(rows[0]).toEqual({ id: "NON_REASONING", name: "NON_REASONING", definition: "", models: [], params: {} }); + }); + + it("counts as a built-in name either way, so a custom set cannot claim the name", () => { + expect(isBuiltInTierName("NON_REASONING")).toBe(true); + expect(isBuiltInTierName("non_reasoning")).toBe(true); + }); +}); diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts index 5e2a32addee..289a5645b51 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts @@ -4,6 +4,16 @@ import type { TierModelParams, TierModelParamsByTier } from "./complexity_router export const TIER_ORDER: ComplexityTier[] = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]; +/** Every built-in tier name, so a stored NON_REASONING row is recognized as built-in either way. */ +export const ALL_BUILT_IN_TIERS: ComplexityTier[] = ["NON_REASONING", ...TIER_ORDER]; + +/** + * The ladder one router renders, ascending. NON_REASONING is tier 0 and appears only when enabled, + * which is what keeps an existing four-tier router's form, payload, and rubric unchanged. + */ +export const tierOrderFor = (enableNonReasoningTier: boolean | undefined): ComplexityTier[] => + enableNonReasoningTier ? ALL_BUILT_IN_TIERS : TIER_ORDER; + export interface TierRow { id: string; name: string; @@ -27,6 +37,7 @@ export const MAX_TIER_DEFINITION_CHARS = 500; export interface ActiveTierSet { tiers: ComplexityTiers; + enable_non_reasoning_tier?: boolean; custom_tier_set?: CustomTierSet; tier_model_params?: TierModelParamsByTier; } @@ -39,7 +50,8 @@ export const activeTierName = (row: TierRow): string => row.name.trim(); export const sameTierIdentity = (left: string, right: string): boolean => left.trim().toLowerCase() === right.trim().toLowerCase(); -export const isBuiltInTierName = (name: string): boolean => TIER_ORDER.some((tier) => sameTierIdentity(tier, name)); +export const isBuiltInTierName = (name: string): boolean => + ALL_BUILT_IN_TIERS.some((tier) => sameTierIdentity(tier, name)); const builtInRow = (tier: keyof ComplexityTiers, tiers: ComplexityTiers): TierRow => ({ id: tier, @@ -51,7 +63,9 @@ const builtInRow = (tier: keyof ComplexityTiers, tiers: ComplexityTiers): TierRo // The only reader of the tier set. Built-in rows carry the canonical tier key as their id, so every // pointer into the set is a row id in both modes and nothing downstream branches on the mode. export const activeTierRows = (value: ActiveTierSet): ActiveTierRow[] => { - const rows = value.custom_tier_set?.tiers ?? TIER_ORDER.map((tier) => builtInRow(tier, value.tiers)); + const rows = + value.custom_tier_set?.tiers ?? + tierOrderFor(value.enable_non_reasoning_tier).map((tier) => builtInRow(tier, value.tiers)); return rows.map((row) => ({ ...row, params: value.tier_model_params?.[row.id] ?? {} })); }; diff --git a/ui/litellm-dashboard/src/components/add_model/tier_set_actions.ts b/ui/litellm-dashboard/src/components/add_model/tier_set_actions.ts index c8254b6bff9..f737e0fdff6 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_set_actions.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_set_actions.ts @@ -4,11 +4,12 @@ import { pruneTierModelParams } from "./complexity_router_tiers"; import { type ActiveTierRow, type TierRow, - TIER_ORDER, + ALL_BUILT_IN_TIERS, activeTierName, activeTierRows, rowParamsByTier, sameTierIdentity, + tierOrderFor, tierRowById, tierRowByName, } from "./tier_rows"; @@ -74,13 +75,13 @@ const rulesFollowingRows = ( // Models and params both come from these rows, so the two cannot be keyed differently. const exitToBuiltInTiers = (value: ComplexityRouterConfigValue, rows: readonly ActiveTierRow[]) => { const { custom_tier_set: _dropped, ...rest } = value; - const builtInRows: ActiveTierRow[] = TIER_ORDER.map( + const builtInRows: ActiveTierRow[] = tierOrderFor(value.enable_non_reasoning_tier).map( (tier) => tierRowById(rows, tier) ?? { id: tier, name: tier, definition: "", - models: value.tiers[tier], + models: value.tiers[tier] ?? [], params: value.tier_model_params?.[tier] ?? {}, }, ); @@ -121,7 +122,7 @@ const nextTierSetValue = ( case "remove": { const removed = tierRowById(rows, action.id); const snapshot = - removed && (TIER_ORDER as string[]).includes(action.id) + removed && (ALL_BUILT_IN_TIERS as string[]).includes(action.id) ? { ...value, tiers: { ...value.tiers, [action.id]: removed.models } } : value; return commitTierRows( diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/build_updated_complexity_router_config.test.ts b/ui/litellm-dashboard/src/components/edit_auto_router/build_updated_complexity_router_config.test.ts index 0144dff3498..5b09dabd214 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/build_updated_complexity_router_config.test.ts +++ b/ui/litellm-dashboard/src/components/edit_auto_router/build_updated_complexity_router_config.test.ts @@ -598,6 +598,10 @@ describe("managed keys survive an untouched open-and-save", () => { "stall_escalation_repeat_threshold", ]); + // The opt-in fifth tier requires the LLM classifier, which this heuristic_first fixture is not, + // so it gets its own round trip below. + const KEYS_ANOTHER_TIER_LADDER_OWNS = new Set(["enable_non_reasoning_tier"]); + it("carries every managed key a built-in router can hold through hydrate then save", () => { const hydrated = hydrateComplexityRouterConfig(STORED_ALL_MANAGED, undefined); const saved = buildUpdatedComplexityRouterConfig(STORED_ALL_MANAGED, hydrated); @@ -605,10 +609,55 @@ describe("managed keys survive an untouched open-and-save", () => { const dropped = [...MANAGED_COMPLEXITY_ROUTER_KEYS] .filter((key) => !KEYS_ANOTHER_CLASSIFIER_TYPE_OWNS.has(key)) .filter((key) => !KEYS_ANOTHER_CLASSIFICATION_FREQUENCY_OWNS.has(key)) + .filter((key) => !KEYS_ANOTHER_TIER_LADDER_OWNS.has(key)) .filter((key) => saved[key] === undefined); expect(dropped).toEqual([]); }); + it("carries an enabled non-reasoning tier and its models through their own round trip", () => { + // `tiers` is rewritten wholesale on save, so this is the regression that matters: opening an + // enabled router and saving an unrelated edit must not delete the tier or its pool. + const stored: Record = { + ...STORED_ALL_MANAGED, + classifier_type: "llm", + classifier_llm_config: { model: "haiku-classifier" }, + heuristic_first_max_tier: undefined, + enable_non_reasoning_tier: true, + tiers: { ...(STORED_ALL_MANAGED.tiers as object), NON_REASONING: ["gpt-4o-mini"] }, + }; + const hydrated = hydrateComplexityRouterConfig(stored, undefined); + const saved = buildUpdatedComplexityRouterConfig(stored, hydrated); + + expect(saved.enable_non_reasoning_tier).toBe(true); + expect((saved.tiers as Record).NON_REASONING).toEqual(["gpt-4o-mini"]); + }); + + it("keeps a stored non-reasoning tier when the stored config never wrote the flag", () => { + // A hand-written config that names the tier: the flag is inferred from the stored pool, so an + // edit made for an unrelated reason cannot silently turn the tier off. + const stored: Record = { + ...STORED_ALL_MANAGED, + classifier_type: "llm", + classifier_llm_config: { model: "haiku-classifier" }, + heuristic_first_max_tier: undefined, + tiers: { ...(STORED_ALL_MANAGED.tiers as object), NON_REASONING: ["gpt-4o-mini"] }, + }; + const saved = buildUpdatedComplexityRouterConfig(stored, hydrateComplexityRouterConfig(stored, undefined)); + + expect(saved.enable_non_reasoning_tier).toBe(true); + expect((saved.tiers as Record).NON_REASONING).toEqual(["gpt-4o-mini"]); + }); + + it("leaves the tier and its flag out of a saved config that never had it on", () => { + const saved = buildUpdatedComplexityRouterConfig( + STORED_ALL_MANAGED, + hydrateComplexityRouterConfig(STORED_ALL_MANAGED, undefined), + ); + + expect(saved).not.toHaveProperty("enable_non_reasoning_tier"); + expect(saved.tiers).not.toHaveProperty("NON_REASONING"); + }); + it("carries the stall-escalation keys through their own round trip", () => { const stored: Record = { ...STORED_ALL_MANAGED, diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index 3c0013e267e..283fe978790 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -91,6 +91,7 @@ interface EditAutoRouterModalProps { * hydrators validate themselves stay `unknown`; the ones assigned straight through carry their type. */ export interface StoredComplexityRouterConfig { tiers?: Partial>; + enable_non_reasoning_tier?: boolean; tier_model_configs?: unknown; default_model?: string | null; plan_mode_min_tier?: unknown; @@ -135,18 +136,27 @@ export const hydrateComplexityRouterConfig = ( parsedConfig: StoredComplexityRouterConfig, complexityRouterDefaultModel: string | null | undefined, ): ComplexityRouterConfigValue => { + // `tiers` is rewritten wholesale on save, so a stored tier this misses is deleted from the + // router by any edit at all, including one made for an unrelated reason. NON_REASONING is + // therefore read back from the stored config rather than assumed absent, and the toggle follows + // what is actually stored so the round-trip cannot silently turn the tier off. + const storedNonReasoning: string[] = normalizeTierModels(parsedConfig.tiers?.NON_REASONING); + const enable_non_reasoning_tier: boolean = + parsedConfig.enable_non_reasoning_tier === true || storedNonReasoning.length > 0; const hydratedTiers: ComplexityTiers = { SIMPLE: normalizeTierModels(parsedConfig.tiers?.SIMPLE), MEDIUM: normalizeTierModels(parsedConfig.tiers?.MEDIUM), COMPLEX: normalizeTierModels(parsedConfig.tiers?.COMPLEX), REASONING: normalizeTierModels(parsedConfig.tiers?.REASONING), + ...(enable_non_reasoning_tier && { NON_REASONING: storedNonReasoning }), }; const custom_tier_set = hydrateCustomTierSet(parsedConfig); - const activeTiers = { tiers: hydratedTiers, custom_tier_set }; + const activeTiers = { tiers: hydratedTiers, enable_non_reasoning_tier, custom_tier_set }; return { tiers: hydratedTiers, + enable_non_reasoning_tier, custom_tier_set, tier_model_params: tierParamsByRowId( hydrateTierModelParams(parsedConfig.tiers, parsedConfig.tier_model_configs), @@ -234,6 +244,7 @@ export const hydrateComplexityRouterConfig = ( export const MANAGED_COMPLEXITY_ROUTER_KEYS = new Set([ "tiers", + "enable_non_reasoning_tier", "tier_definitions", "fallback_tier", "tier_model_configs", @@ -339,6 +350,7 @@ export const buildUpdatedComplexityRouterConfig = ( const builderParams: BuildComplexityRouterConfigParams = { tiers: value.tiers, + enableNonReasoningTier: value.enable_non_reasoning_tier, customTierSet: value.custom_tier_set, defaultModel: value.default_model, planModeMinTier: value.plan_mode_min_tier, diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 232b366d8ce..6a60b0db58a 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -23573,16 +23573,22 @@ export interface components { }; /** * AutoRouterPresetTiers - * @description Exactly the four built-in tiers the dashboard's preset prefill can apply. + * @description The built-in tiers the dashboard's preset prefill can apply. * * extra="forbid" on purpose: a tier name this dashboard cannot apply would grey out or crash the - * picker, so such a catalog is rejected wholesale and the bundled one serves instead. + * picker, so such a catalog is rejected wholesale and the bundled one serves instead. NON_REASONING + * is optional rather than required so this proxy accepts both a catalog that ships the opt-in fifth + * tier and the four-tier catalogs that predate it. It stays None when unset rather than defaulting + * to an empty pool, so a four-tier preset serves the tier set it was published with instead of + * growing a key the dashboard would render as an empty fifth tier row. */ AutoRouterPresetTiers: { /** Complex */ COMPLEX: string[]; /** Medium */ MEDIUM: string[]; + /** Non Reasoning */ + NON_REASONING?: string[] | null; /** Reasoning */ REASONING: string[]; /** Simple */ @@ -25579,7 +25585,7 @@ export interface components { * @description Complexity tiers for routing decisions. * @enum {string} */ - ComplexityTier: "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING"; + ComplexityTier: "NON_REASONING" | "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING"; /** ComplexityTierModel */ ComplexityTierModel: { /** Litellm Params */ @@ -34947,6 +34953,12 @@ export interface components { * @default true */ enable_context_window_escalation: boolean; + /** + * Enable Non Reasoning Tier + * @description Add NON_REASONING as a fifth built-in tier below SIMPLE, for operational agent traffic that relays or reformats information rather than reasoning about it. Off by default: turning it on adds a rung to this router's ladder, a bullet to the LLM classifier's rubric, and a value the classifier may return, all of which move tier decisions and spend on an already-deployed router. Requires an LLM classifier or a custom classifier plugin, since the heuristic scorers cannot produce the tier, and a model in `tiers` under the NON_REASONING key. Escalation still walks up from it, and it is never the savings baseline or a `heuristic_v2` prediction. + * @default false + */ + enable_non_reasoning_tier: boolean; /** * Escalation Keywords * @description Case-sensitive phrases a user can include to force a bump to the next-higher complexity tier when they aren't satisfied with results (they can force a stronger model, but not choose which one). Defaults to ['LITELLM ESCALATE'] when unset; set to an empty list to disable. @@ -37212,7 +37224,7 @@ export interface components { TierDefinition: { /** * Description - * @description What belongs in this tier; rendered as this tier's bullet in the classifier rubric. Required unless the name is a built-in tier (SIMPLE/MEDIUM/COMPLEX/REASONING), which inherits the built-in criteria when omitted + * @description What belongs in this tier; rendered as this tier's bullet in the classifier rubric. Required unless the name is a built-in tier (NON_REASONING, SIMPLE, MEDIUM, COMPLEX, REASONING), which inherits the built-in criteria when omitted */ description?: string | null; /** From da2e1ff6190d68873a5c3af173ef71fe7dce51c9 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:12:34 -0700 Subject: [PATCH 099/223] fix(fireworks): fold system and developer items into instructions on the responses path Fireworks renders a Responses request through a chat template that only accepts a system message at the very beginning, so a request carrying `instructions`, a developer item, and a replayed reasoning item (the shape Codex CLI sends from its second prompt on) came back 400 with "System message must be at the beginning". The leading system or developer items, and any developer item later in the conversation, now fold their text into top-level `instructions`, joined with blank lines, and leave `input`. A developer item that closes the conversation right after an assistant turn stays where it is as a system item, as does any system or developer item with an image or file part, so those parts still reach Fireworks. Mid-conversation system items stay untouched. Non-string `instructions` pass through unchanged. Folding into `instructions` rather than a leading system item keeps `previous_response_id` chaining working, since Fireworks prepends the stored history to `input` and a leading system item would land after it. This supersedes the leading system item approach from deaadc21d3 and 4807630c2a on this branch. The leading and closing block rules match the chat path change in #39852. --- .../fireworks_ai/responses/transformation.py | 136 +++++++++++------ ...t_fireworks_ai_responses_transformation.py | 140 +++++++++++++----- 2 files changed, 196 insertions(+), 80 deletions(-) diff --git a/litellm/llms/fireworks_ai/responses/transformation.py b/litellm/llms/fireworks_ai/responses/transformation.py index 660a07181ff..f7dd774ea18 100644 --- a/litellm/llms/fireworks_ai/responses/transformation.py +++ b/litellm/llms/fireworks_ai/responses/transformation.py @@ -1,16 +1,10 @@ -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from types import MappingProxyType from typing import TYPE_CHECKING, Final from urllib.parse import unquote import httpx -from openai.types.responses import ( - EasyInputMessageParam, - ResponseInputContentParam, - ResponseInputItemParam, - ResponseInputTextParam, -) -from pydantic import TypeAdapter +from openai.types.responses import EasyInputMessageParam, ResponseInputContentParam, ResponseInputItemParam from litellm.llms.fireworks_ai.common_utils import ( resolve_fireworks_api_key, @@ -37,47 +31,90 @@ def _session_params(litellm_params: GenericLiteLLMParams) -> Mapping[str, object ) -_instructions_adapter: Final = TypeAdapter[str | None](str | None) +_INSTRUCTION_ROLES: Final = frozenset({"system", "developer"}) -def _instruction_parts(item: ResponseInputItemParam) -> tuple[ResponseInputContentParam, ...] | None: - if "role" not in item or (item["role"] != "system" and item["role"] != "developer"): - return None - content: Final = item["content"] - if isinstance(content, str): - return (ResponseInputTextParam(type="input_text", text=content),) - return tuple(content) +def _role(item: ResponseInputItemParam) -> str | None: + match item: + case {"role": str(role)}: + return role + case _: + return None -def _leading_system_content( - instructions: str | None, parts: tuple[ResponseInputContentParam, ...] -) -> str | list[ResponseInputContentParam]: - text: Final = "\n\n".join( - chunk for chunk in (instructions, *(part["text"] for part in parts if part["type"] == "input_text")) if chunk - ) - non_text: Final = tuple(part for part in parts if part["type"] != "input_text") - if not non_text: - return text - return [ResponseInputTextParam(type="input_text", text=text), *non_text] if text else list(non_text) +def _developer_item_as_system(item: ResponseInputItemParam) -> ResponseInputItemParam: + if "role" not in item or item["role"] != "developer": + return item + return EasyInputMessageParam(role="system", content=item["content"], type="message") -def _with_single_leading_system_item( - input: str | ResponseInputParam, instructions: str | None -) -> str | ResponseInputParam: - items: Final = () if isinstance(input, str) else tuple(input) - instruction_parts: Final = tuple( - part for item_parts in map(_instruction_parts, items) if item_parts is not None for part in item_parts - ) - content: Final = _leading_system_content(instructions, instruction_parts) - if not content: +def _developer_items_as_system(input: str | ResponseInputParam) -> str | ResponseInputParam: + if isinstance(input, str): return input - leading: Final = EasyInputMessageParam(role="system", content=content, type="message") - rest: Final = ( - (EasyInputMessageParam(role="user", content=input),) - if isinstance(input, str) - else tuple(item for item in items if _instruction_parts(item) is None) + return [_developer_item_as_system(item) for item in input] + + +def _text_part(part: ResponseInputContentParam) -> str | None: + match part: + case {"type": "input_text", "text": str(text)}: + return text + case _: + return None + + +def _text_only_content(item: ResponseInputItemParam) -> str | None: + match item: + case {"role": "system" | "developer", "content": str(text)}: + return text + case {"role": "system" | "developer", "content": [*parts]}: + texts: Final = tuple(map(_text_part, parts)) + return None if any(text is None for text in texts) else "\n\n".join(text for text in texts if text) + case _: + return None + + +def _leading_instruction_block_length(roles: Sequence[str | None]) -> int: + return next((index for index, role in enumerate(roles) if role not in _INSTRUCTION_ROLES), len(roles)) + + +def _closing_instruction_block_start(roles: Sequence[str | None], leading_length: int) -> int: + last_conversation_index: Final = next( + (index for index in range(len(roles) - 1, leading_length - 1, -1) if roles[index] not in _INSTRUCTION_ROLES), + None, + ) + if last_conversation_index is None or roles[last_conversation_index] != "assistant": + return len(roles) + return last_conversation_index + 1 + + +def _hoisted_indices(roles: Sequence[str | None]) -> tuple[int, ...]: + leading_length: Final = _leading_instruction_block_length(roles) + closing_start: Final = _closing_instruction_block_start(roles, leading_length) + return tuple( + index for index, role in enumerate(roles[:closing_start]) if index < leading_length or role == "developer" + ) + + +def _with_instruction_items_folded( + input: str | ResponseInputParam, instructions: str | None +) -> tuple[str | None, str | ResponseInputParam]: + if isinstance(input, str): + return instructions, input + items: Final = tuple(input) + folded: Final = MappingProxyType( + { + index: text + for index in _hoisted_indices(tuple(map(_role, items))) + if (text := _text_only_content(items[index])) is not None + } + ) + joined: Final = "\n\n".join(chunk for chunk in (instructions, *folded.values()) if chunk) + return ( + instructions if not folded else joined or None, + [ # mutable-ok: the base class takes the input items as a list + _developer_item_as_system(item) for index, item in enumerate(items) if index not in folded + ], ) - return [leading, *rest] class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig): @@ -113,15 +150,24 @@ class FireworksAIResponsesAPIConfig(OpenAIResponsesAPIConfig): litellm_params: GenericLiteLLMParams, headers: dict, # mutable-ok: overrides the base class signature ) -> dict: # mutable-ok: overrides the base class signature - instructions: Final = _instructions_adapter.validate_python( - response_api_optional_request_params.get("instructions") + instructions_param: Final[object] = response_api_optional_request_params.get("instructions") + validated_input: Final = self._validate_input_param(input) + instructions, folded_input = ( + _with_instruction_items_folded(validated_input, instructions_param) + if isinstance(instructions_param, str | None) + else (instructions_param, _developer_items_as_system(validated_input)) ) + instruction_entries: Final = () if instructions is None else (("instructions", instructions),) folded_params: Final = { # mutable-ok: the base class takes the optional params as a dict - key: value for key, value in response_api_optional_request_params.items() if key != "instructions" + key: value + for key, value in ( + *((key, value) for key, value in response_api_optional_request_params.items() if key != "instructions"), + *instruction_entries, + ) } return super().transform_responses_api_request( model=resolve_fireworks_resource_name(model), - input=_with_single_leading_system_item(self._validate_input_param(input), instructions), + input=folded_input, response_api_optional_request_params=folded_params, litellm_params=litellm_params, headers=headers, diff --git a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py index 9d947b25d69..d0697ca9b0e 100644 --- a/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py +++ b/tests/test_litellm/llms/fireworks_ai/responses/test_fireworks_ai_responses_transformation.py @@ -162,7 +162,7 @@ def test_responses_call_forwards_previous_response_id_and_store() -> None: assert body["input"][0]["call_id"] == "call_abc123" -def test_responses_call_hoists_developer_items_into_one_leading_system_message() -> None: +def test_responses_call_folds_developer_items_into_instructions() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): litellm.responses( @@ -175,14 +175,14 @@ def test_responses_call_hoists_developer_items_into_one_leading_system_message() api_key="fw-test-key", ) _, _, body = _sent_request(client) + assert body["instructions"] == "Answer with exactly one word." assert tuple(body["input"]) == ( - {"role": "system", "content": "Answer with exactly one word.", "type": "message"}, {"role": "user", "content": "Hi there"}, {"role": "user", "content": [{"type": "input_text", "text": "What is the capital of France?"}]}, ) -def test_responses_call_folds_instructions_and_developer_item_into_one_leading_system_message() -> None: +def test_responses_call_folds_instructions_and_developer_item_into_instructions_with_reasoning_replayed() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): litellm.responses( @@ -208,16 +208,10 @@ def test_responses_call_folds_instructions_and_developer_item_into_one_leading_s api_key="fw-test-key", ) _, _, body = _sent_request(client) - assert "instructions" not in body + assert body["instructions"] == ( + "You are a coding agent running in the Codex CLI.\n\nread-only" + ) assert tuple(body["input"]) == ( - { - "role": "system", - "content": ( - "You are a coding agent running in the Codex CLI.\n\n" - "read-only" - ), - "type": "message", - }, {"role": "user", "content": [{"type": "input_text", "text": "What is the capital of France?"}]}, {"id": "rs_1", "type": "reasoning", "summary": [{"type": "summary_text", "text": "A trivial question."}]}, { @@ -231,41 +225,103 @@ def test_responses_call_folds_instructions_and_developer_item_into_one_leading_s ) -def test_responses_call_keeps_non_text_developer_parts_on_the_leading_system_message() -> None: +def test_responses_call_folds_instructions_and_developer_item_with_previous_response_id() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): litellm.responses( model="fireworks_ai/accounts/fireworks/models/qwen3p8-2p4t-a95b", - instructions="Answer with one word.", + instructions="You are a terse assistant.", input=[ # mutable-ok: the Responses API takes input as a JSON list - { - "role": "developer", - "content": [ - {"type": "input_text", "text": "Match the style of this reference image."}, - {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo=", "detail": "auto"}, - ], - }, + {"role": "developer", "content": "Answer with exactly one word."}, + {"role": "user", "content": "And of Spain?"}, + ], + previous_response_id="resp_0e946f2d46bf4b49bf8b29ff78083583", + store=True, + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert body["instructions"] == "You are a terse assistant.\n\nAnswer with exactly one word." + assert body["previous_response_id"] == "resp_0e946f2d46bf4b49bf8b29ff78083583" + assert tuple(body["input"]) == ({"role": "user", "content": "And of Spain?"},) + + +def test_responses_call_keeps_a_closing_developer_item_after_an_assistant_turn_in_place() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) + assistant_turn: Final = { + "id": "msg_1", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Paris.", "annotations": []}], + } + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/qwen3p8-2p4t-a95b", + instructions="Be terse.", + input=[ # mutable-ok: the Responses API takes input as a JSON list + {"role": "developer", "content": "Answer with exactly one word."}, + {"role": "user", "content": "What is the capital of France?"}, + assistant_turn, + {"role": "developer", "content": "Now restate it in French."}, + ], + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert body["instructions"] == "Be terse.\n\nAnswer with exactly one word." + assert tuple(body["input"]) == ( + {"role": "user", "content": "What is the capital of France?"}, + assistant_turn, + {"role": "system", "content": "Now restate it in French.", "type": "message"}, + ) + + +def test_responses_call_keeps_a_mid_conversation_system_item_in_place() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/kimi-k3", + input=[ # mutable-ok: the Responses API takes input as a JSON list + {"role": "user", "content": "Hi there"}, + {"role": "system", "content": "Switch to French."}, {"role": "user", "content": "What is the capital of France?"}, ], - store=False, api_key="fw-test-key", ) _, _, body = _sent_request(client) assert "instructions" not in body assert tuple(body["input"]) == ( - { - "role": "system", - "content": [ - {"type": "input_text", "text": "Answer with one word.\n\nMatch the style of this reference image."}, - {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo=", "detail": "auto"}, - ], - "type": "message", - }, + {"role": "user", "content": "Hi there"}, + {"role": "system", "content": "Switch to French."}, {"role": "user", "content": "What is the capital of France?"}, ) -def test_responses_call_turns_string_input_with_instructions_into_system_then_user_messages() -> None: +def test_responses_call_keeps_a_developer_item_with_non_text_parts_in_place_as_a_system_item() -> None: + client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/qwen3p8-2p4t-a95b")) + developer_item: Final = { + "role": "developer", + "content": [ + {"type": "input_text", "text": "Match the style of this reference image."}, + {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo=", "detail": "auto"}, + ], + } + with patch(HTTPX_CLIENT_FACTORY, return_value=client): + litellm.responses( + model="fireworks_ai/accounts/fireworks/models/qwen3p8-2p4t-a95b", + instructions="Answer with one word.", + input=[developer_item, {"role": "user", "content": "What is the capital of France?"}], # mutable-ok: JSON list + store=False, + api_key="fw-test-key", + ) + _, _, body = _sent_request(client) + assert body["instructions"] == "Answer with one word." + assert tuple(body["input"]) == ( + {"role": "system", "content": developer_item["content"], "type": "message"}, + {"role": "user", "content": "What is the capital of France?"}, + ) + + +def test_responses_call_forwards_string_input_and_instructions_unchanged() -> None: client: Final = _mock_http_client(_fireworks_response("accounts/fireworks/models/kimi-k3")) with patch(HTTPX_CLIENT_FACTORY, return_value=client): litellm.responses( @@ -275,10 +331,24 @@ def test_responses_call_turns_string_input_with_instructions_into_system_then_us api_key="fw-test-key", ) _, _, body = _sent_request(client) - assert "instructions" not in body - assert tuple(body["input"]) == ( + assert body["instructions"] == "Answer with exactly one word." + assert body["input"] == "What is the capital of France?" + + +def test_transform_request_forwards_non_string_instructions_and_input_untouched() -> None: + developer_item: Final = {"role": "developer", "content": "Answer with exactly one word."} + user_item: Final = {"role": "user", "content": "What is the capital of France?"} + request: Final = FireworksAIResponsesAPIConfig().transform_responses_api_request( + model="accounts/fireworks/models/kimi-k3", + input=cast(ResponseInputParam, [developer_item, user_item]), # mutable-ok: JSON list + response_api_optional_request_params={"instructions": ["not", "a", "string"]}, # mutable-ok: base takes a dict + litellm_params=GenericLiteLLMParams(), + headers={}, # mutable-ok: base takes a dict + ) + assert request["instructions"] == ["not", "a", "string"] + assert tuple(request["input"]) == ( {"role": "system", "content": "Answer with exactly one word.", "type": "message"}, - {"role": "user", "content": "What is the capital of France?"}, + user_item, ) @@ -305,8 +375,8 @@ def test_responses_call_maps_pydantic_developer_items_and_replays_pydantic_outpu model="fireworks_ai/accounts/fireworks/models/kimi-k3", input=pydantic_input, api_key="fw-test-key" ) _, _, body = _sent_request(client) + assert body["instructions"] == "Answer with exactly one word." assert tuple(body["input"]) == ( - {"role": "system", "content": "Answer with exactly one word.", "type": "message"}, {"id": "rs_1", "summary": [], "type": "reasoning"}, { "id": "fc_1", From 8b89c909a9448a66a0f60cc3c9de7e236271b838 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:16:08 -0700 Subject: [PATCH 100/223] fix(proxy): keep a ProxyException's status and label 408s in the OpenAI error payload error_status_code only read status_code, so a ProxyException raised before routing (which stores its status as the string code) answered 500 with its 4xx type through the rerank, images, realtime, files, and pass-through tails. It now falls back to a decimal code. A 408 maps to timeout_error instead of invalid_request_error. Tail regressions for rerank, images, realtime calls, and the chat pass-through fail at the merge base with ('None', 'None'); the new files-test helpers are fully typed. --- .../common_utils/openai_error_payload.py | 9 ++- .../common_utils/test_openai_error_payload.py | 28 ++++++++++ .../proxy/image_endpoints/test_endpoints.py | 48 +++++++++++++++- .../test_files_endpoint.py | 24 +++++--- .../test_pass_through_endpoints.py | 38 ++++++++++++- .../test_realtime_webrtc_endpoints.py | 42 ++++++++++++++ .../proxy/rerank_endpoints/test_endpoints.py | 56 ++++++++++++++++++- 7 files changed, 230 insertions(+), 15 deletions(-) diff --git a/litellm/proxy/common_utils/openai_error_payload.py b/litellm/proxy/common_utils/openai_error_payload.py index 180ec152094..2d589871fea 100644 --- a/litellm/proxy/common_utils/openai_error_payload.py +++ b/litellm/proxy/common_utils/openai_error_payload.py @@ -12,6 +12,7 @@ _OPENAI_ERROR_TYPE_BY_STATUS: Final[Mapping[int, str]] = MappingProxyType( { status.HTTP_401_UNAUTHORIZED: "authentication_error", status.HTTP_403_FORBIDDEN: "permission_error", + status.HTTP_408_REQUEST_TIMEOUT: "timeout_error", status.HTTP_429_TOO_MANY_REQUESTS: "rate_limit_error", } ) @@ -22,9 +23,13 @@ def attribute_of(value: object, name: str, default: object = None) -> object: def error_status_code(exc: object, default: int) -> int: - """The HTTP status an exception carries, or ``default`` when it carries none.""" + """The HTTP status an exception carries as ``status_code`` or, the way ``ProxyException`` + stores it, as a stringified ``code``; ``default`` when it carries neither.""" carried: Final = attribute_of(exc, "status_code") - return carried if isinstance(carried, int) and not isinstance(carried, bool) else default + if isinstance(carried, int) and not isinstance(carried, bool): + return carried + stringified: Final = attribute_of(exc, "code") + return int(stringified) if isinstance(stringified, str) and stringified.isdecimal() else default def openai_error_type(exc: object, status_code: int) -> str: diff --git a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py index 3b39ea706fe..3145be2d522 100644 --- a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py +++ b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py @@ -18,6 +18,7 @@ from litellm.proxy.common_utils.openai_error_payload import ( (401, "authentication_error"), (403, "permission_error"), (404, "invalid_request_error"), + (408, "timeout_error"), (422, "invalid_request_error"), (429, "rate_limit_error"), (499, "invalid_request_error"), @@ -109,6 +110,33 @@ def test_a_non_int_carried_status_falls_back_to_the_default(carried_status: obje assert error_status_code(_Carrier("boom"), 500) == 500 +def test_a_proxy_exception_keeps_the_status_it_was_raised_with(): + """ProxyException stores its status as the string ``code`` rather than ``status_code``, + so a route tail that rewraps one used to answer a 4xx rejection as a 500.""" + rejection = ProxyException(message="session_id is required", type="bad_request_error", param="session_id", code=400) + + assert error_status_code(rejection, 500) == 400 + + +@pytest.mark.parametrize("carried_code", [None, "None", "", "rate_limited", "4xx", 404]) +def test_a_code_that_is_not_a_decimal_string_falls_back_to_the_default(carried_code: object): + """Only ProxyException's stringified status is a status; ``code`` on anything else + (OpenAI's ``invalid_api_key``, a stray int) says nothing about the HTTP answer.""" + + class _Carrier(Exception): + code = carried_code + + assert error_status_code(_Carrier("boom"), 500) == 500 + + +def test_a_status_code_wins_over_a_stringified_code(): + class _Carrier(Exception): + status_code = 429 + code = "400" + + assert error_status_code(_Carrier("boom"), 500) == 429 + + def test_a_status_carried_by_an_exception_drives_the_type_it_reports(): """The two helpers compose at every call site: the status the exception carries is what names its type, not the default the route would have used.""" diff --git a/tests/test_litellm/proxy/image_endpoints/test_endpoints.py b/tests/test_litellm/proxy/image_endpoints/test_endpoints.py index 203391aadad..d8b3eef98bd 100644 --- a/tests/test_litellm/proxy/image_endpoints/test_endpoints.py +++ b/tests/test_litellm/proxy/image_endpoints/test_endpoints.py @@ -5,12 +5,12 @@ from typing import Any, Dict import orjson import pytest -from fastapi import FastAPI +from fastapi import FastAPI, HTTPException from fastapi.testclient import TestClient from starlette.requests import Request from starlette.responses import Response -from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy._types import ProxyException, UserAPIKeyAuth from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.image_endpoints import endpoints @@ -167,3 +167,47 @@ def test_image_edit_multipart_n_that_is_not_a_number_is_left_alone(monkeypatch): assert response.status_code == 200 assert captured["n"] == "two" + + +@pytest.mark.asyncio +async def test_a_model_the_router_cannot_serve_answers_an_openai_typed_error(monkeypatch: pytest.MonkeyPatch): + """A bare HTTPException carries no type or param, so the tail used to ship the + literal string "None" in both fields.""" + + async def fake_add_litellm_data_to_request(**kwargs: object) -> object: + return kwargs["data"] + + async def fake_pre_call_hook(*, user_api_key_dict: UserAPIKeyAuth, data: dict[str, object], call_type: str) -> dict[str, object]: + return data + + async def fake_post_call_failure_hook(**_: object) -> None: + return None + + async def failing_route_request(**_: object) -> None: + raise HTTPException( + status_code=404, detail={"error": "image_generation: Invalid model name passed in model=dall-e-3"} + ) + + monkeypatch.setattr("litellm.proxy.proxy_server.add_litellm_data_to_request", fake_add_litellm_data_to_request) + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {}) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_config", {}) + monkeypatch.setattr( + "litellm.proxy.proxy_server.proxy_logging_obj", + SimpleNamespace(pre_call_hook=fake_pre_call_hook, post_call_failure_hook=fake_post_call_failure_hook), + ) + monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None) + monkeypatch.setattr("litellm.proxy.proxy_server.version", "test-version") + monkeypatch.setattr("litellm.proxy.image_endpoints.endpoints.route_request", failing_route_request) + + body = orjson.dumps({"model": "dall-e-3", "prompt": "a lighthouse at dusk"}) + + async def receive() -> dict[str, object]: + return {"type": "http.request", "body": body, "more_body": False} + + request = Request({"type": "http", "method": "POST", "path": "/v1/images/generations", "headers": []}, receive) + + with pytest.raises(ProxyException) as raised: + await endpoints.image_generation(request=request, fastapi_response=Response(), user_api_key_dict=UserAPIKeyAuth()) + + assert (raised.value.type, raised.value.param, raised.value.code) == ("invalid_request_error", None, "404") diff --git a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py index 2257ae1ab29..132b53792b0 100644 --- a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py +++ b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py @@ -4668,7 +4668,9 @@ def test_create_file_path_traversal_filename_rejected_before_forwarding(monkeypa assert forwarded_calls == [] -def _setup_managed_file_route_answering_404(mocker: MockerFixture, monkeypatch, llm_router: Router): +def _setup_managed_file_route_answering_404( + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router +) -> None: """Wire the single-file routes to a managed file store that knows no file, the way the managed files hook answers once a file has been deleted or was never the caller's.""" import litellm.proxy.proxy_server as ps @@ -4676,7 +4678,7 @@ def _setup_managed_file_route_answering_404(mocker: MockerFixture, monkeypatch, from litellm.llms.base_llm.files.transformation import BaseFileEndpoints from litellm.proxy._types import LitellmUserRoles - async def _file_not_found(file_id: str, **kwargs): + async def _file_not_found(file_id: str, **kwargs: object) -> None: raise HTTPException(status_code=404, detail=f"File not found: {file_id}") proxy_logging_obj = setup_proxy_logging_object(monkeypatch, llm_router) @@ -4694,7 +4696,7 @@ def _setup_managed_file_route_answering_404(mocker: MockerFixture, monkeypatch, ) -def _call_managed_file_route(method: str, path: str): +def _call_managed_file_route(method: str, path: str) -> httpx.Response: try: return client.request(method, path, headers={"Authorization": "Bearer test-key"}) finally: @@ -4703,7 +4705,7 @@ def _call_managed_file_route(method: str, path: str): app.dependency_overrides.pop(ps.user_api_key_auth, None) -def _missing_managed_file_error(file_id: str) -> dict: +def _missing_managed_file_error(file_id: str) -> dict[str, dict[str, str | None]]: return { "error": { "message": f"File not found: {file_id}", @@ -4714,7 +4716,9 @@ def _missing_managed_file_error(file_id: str) -> dict: } -def test_create_file_reports_a_half_specified_expires_after_as_a_400(monkeypatch, llm_router: Router): +def test_create_file_reports_a_half_specified_expires_after_as_a_400( + monkeypatch: pytest.MonkeyPatch, llm_router: Router +): """A 400 raised inside the route answers with the type a 400 stands for and a JSON null param, not the literal string "None" in both fields, so a client can classify it.""" setup_proxy_logging_object(monkeypatch, llm_router) @@ -4735,7 +4739,9 @@ def test_create_file_reports_a_half_specified_expires_after_as_a_400(monkeypatch assert error["code"] == "400" -def test_get_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixture, monkeypatch, llm_router: Router): +def test_get_file_reports_a_missing_managed_file_as_a_404( + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router +): _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) file_id = _unified_managed_file_id() @@ -4745,7 +4751,9 @@ def test_get_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixture, assert response.json() == _missing_managed_file_error(file_id) -def test_delete_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixture, monkeypatch, llm_router: Router): +def test_delete_file_reports_a_missing_managed_file_as_a_404( + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router +): _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) file_id = _unified_managed_file_id() @@ -4756,7 +4764,7 @@ def test_delete_file_reports_a_missing_managed_file_as_a_404(mocker: MockerFixtu def test_get_file_content_reports_a_missing_managed_file_as_a_404( - mocker: MockerFixture, monkeypatch, llm_router: Router + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router ): _setup_managed_file_route_answering_404(mocker, monkeypatch, llm_router) file_id = _unified_managed_file_id() diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py b/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py index fb4ed3db4d2..d57bed430c1 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py @@ -11,7 +11,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import httpx import pytest -from fastapi import Request, UploadFile +from fastapi import Request, Response, UploadFile from starlette.datastructures import FormData, Headers, QueryParams from starlette.datastructures import UploadFile as StarletteUploadFile @@ -22,6 +22,7 @@ from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( InitPassThroughEndpointHelpers, LITELLM_PASS_THROUGH_CUSTOM_BODY_STATE_KEY, _registered_pass_through_routes, + chat_completion_pass_through_endpoint, create_pass_through_route, initialize_pass_through_endpoints, pass_through_request, @@ -5837,3 +5838,38 @@ def test_passthrough_client_cannot_forge_session_id_omission(client_metadata_key ) == "per-call-random-trace-id" ) + + +@pytest.mark.asyncio +async def test_chat_completion_pass_through_endpoint_answers_an_openai_typed_error_for_an_unknown_model( + monkeypatch: pytest.MonkeyPatch, +): + """A bare HTTPException carries no type or param, so the tail used to ship the + literal string "None" in both fields.""" + proxy_logging = MagicMock() + proxy_logging.pre_call_hook = AsyncMock(side_effect=lambda **kwargs: kwargs["data"]) + proxy_logging.post_call_failure_hook = AsyncMock() + + async def fake_add_litellm_data_to_request(**kwargs: object) -> object: + return kwargs["data"] + + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging) + monkeypatch.setattr("litellm.proxy.proxy_server.add_litellm_data_to_request", fake_add_litellm_data_to_request) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None) + monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None) + monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {}) + + request = MagicMock(spec=Request) + request.body = AsyncMock( + return_value=json.dumps({"model": "unknown-model", "messages": [{"role": "user", "content": "hi"}]}).encode() + ) + + with pytest.raises(ProxyException) as raised: + await chat_completion_pass_through_endpoint( + fastapi_response=Response(), + request=request, + adapter_id="anthropic", + user_api_key_dict=UserAPIKeyAuth(api_key="sk-test"), + ) + + assert (raised.value.type, raised.value.param, raised.value.code) == ("invalid_request_error", None, "400") diff --git a/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py b/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py index 66eeb3cef34..8cc5994dc81 100644 --- a/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py +++ b/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py @@ -6,10 +6,12 @@ Tests for LiteLLM proxy realtime WebRTC HTTP endpoints: import json import time +from collections.abc import Awaitable, Callable from unittest.mock import AsyncMock, MagicMock, patch import httpx import pytest +from fastapi import FastAPI, HTTPException from fastapi.testclient import TestClient @@ -1199,3 +1201,43 @@ async def test_transcription_sessions_wraps_route_exception( assert "Model not allowed" in response.text finally: proxy_app.dependency_overrides.pop(user_api_key_auth, None) + + +def test_realtime_calls_upstream_rejection_answers_an_openai_typed_error( + proxy_app: FastAPI, + mock_add_litellm_data: Callable[..., Awaitable[object]], + mock_pre_call_hook: Callable[..., Awaitable[object]], + monkeypatch: pytest.MonkeyPatch, +): + """A bare HTTPException carries no type or param, so the tail used to ship the + literal string "None" in both fields of the error the browser client reads.""" + token_payload = _encode_realtime_token_payload( + ephemeral_key="fake_upstream_epk", + model_id="gpt-4o-realtime-preview", + user_id=None, + team_id=None, + expires_at=int(time.time()) + 3600, + ) + encrypted_token = encrypt_value_helper(token_payload) + + async def failing_route_request(*args: object, **kwargs: object) -> None: + raise HTTPException( + status_code=404, + detail={"error": "realtime: Invalid model name passed in model=gpt-4o-realtime-preview"}, + ) + + proxy_logging = MagicMock() + proxy_logging.pre_call_hook = AsyncMock(side_effect=mock_pre_call_hook) + proxy_logging.post_call_failure_hook = AsyncMock() + monkeypatch.setattr("litellm.proxy.proxy_server.route_request", failing_route_request) + monkeypatch.setattr("litellm.proxy.proxy_server.add_litellm_data_to_request", mock_add_litellm_data) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging) + + response = TestClient(proxy_app).post( + "/v1/realtime/calls", + headers={"Authorization": f"Bearer {encrypted_token}"}, + content=b"v=0\r\no=- 0 0 IN IP4 0.0.0.0\r\ns=-\r\n", + ) + + assert response.status_code == 404 + assert (response.json()["error"]["type"], response.json()["error"]["param"]) == ("invalid_request_error", None) diff --git a/tests/test_litellm/proxy/rerank_endpoints/test_endpoints.py b/tests/test_litellm/proxy/rerank_endpoints/test_endpoints.py index 9f11ff6f20d..ea858e04e0f 100644 --- a/tests/test_litellm/proxy/rerank_endpoints/test_endpoints.py +++ b/tests/test_litellm/proxy/rerank_endpoints/test_endpoints.py @@ -6,11 +6,11 @@ import json from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fastapi import Request, Response +from fastapi import HTTPException, Request, Response import litellm.proxy.common_request_processing as common_request_processing_mod import litellm.proxy.proxy_server as proxy_server_mod -from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy._types import ProxyException, UserAPIKeyAuth from litellm.proxy.rerank_endpoints.endpoints import rerank from litellm.types.utils import RerankResponse @@ -118,3 +118,55 @@ async def test_rerank_omits_detailed_timing_headers_when_disabled(): fastapi_response = await _call_rerank() assert "x-litellm-timing-llm-api-ms" not in fastapi_response.headers + + +async def _rerank_failure( + failure: Exception, *, raised_before_routing: bool, monkeypatch: pytest.MonkeyPatch +) -> ProxyException: + proxy_logging_obj = MagicMock() + proxy_logging_obj.pre_call_hook = AsyncMock( + side_effect=failure if raised_before_routing else lambda **kwargs: kwargs["data"] + ) + proxy_logging_obj.post_call_failure_hook = AsyncMock() + + async def fake_add_litellm_data_to_request(**kwargs: object) -> object: + return kwargs["data"] + + async def failing_route_request(**kwargs: object) -> None: + raise failure + + monkeypatch.setattr(proxy_server_mod, "add_litellm_data_to_request", fake_add_litellm_data_to_request) + monkeypatch.setattr(proxy_server_mod, "route_request", failing_route_request) + monkeypatch.setattr(proxy_server_mod, "proxy_logging_obj", proxy_logging_obj) + monkeypatch.setattr(proxy_server_mod, "llm_router", MagicMock()) + monkeypatch.setattr(proxy_server_mod, "version", "1.2.3") + + with pytest.raises(ProxyException) as raised: + await rerank( + request=_build_request(), + fastapi_response=Response(), + user_api_key_dict=UserAPIKeyAuth(api_key="sk-test"), + ) + return raised.value + + +@pytest.mark.asyncio +async def test_a_model_the_router_cannot_serve_answers_an_openai_typed_error(monkeypatch: pytest.MonkeyPatch): + """A bare HTTPException carries no type or param, so the tail used to ship the + literal string "None" in both fields.""" + failure = HTTPException(status_code=404, detail={"error": "rerank: Invalid model name passed in model=rerank-model"}) + + error = await _rerank_failure(failure, raised_before_routing=False, monkeypatch=monkeypatch) + + assert (error.type, error.param, error.code) == ("invalid_request_error", None, "404") + + +@pytest.mark.asyncio +async def test_a_rejection_raised_before_routing_keeps_its_own_status(monkeypatch: pytest.MonkeyPatch): + """A ProxyException stores its status as the string ``code``, which the tail used to + miss and rewrap as a 500 while keeping the 4xx type and param.""" + rejection = ProxyException(message="session_id is required", type="bad_request_error", param="session_id", code=400) + + error = await _rerank_failure(rejection, raised_before_routing=True, monkeypatch=monkeypatch) + + assert (error.type, error.param, error.code) == ("bad_request_error", "session_id", "400") From 946d6f665ed84c59c4545d4519d9e029258046a2 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:18:26 -0700 Subject: [PATCH 101/223] test(cost_map): assert runtime reloads refresh loaded_at --- .../test_get_model_cost_map.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index 18794ea7eec..f72d175d579 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -335,6 +335,7 @@ def test_get_model_cost_map_stamps_loaded_at(monkeypatch): import functools import random +from datetime import datetime, timezone import httpx @@ -554,6 +555,27 @@ async def test_refetch_local_override_reports_the_bundled_blob_id_without_an_eta assert get_model_cost_map_provenance() == {"source_revision": _bundled_blob_id(), "etag": None} +@pytest.mark.asyncio +async def test_refetch_stamps_loaded_at_on_remote_and_local_reloads(monkeypatch): + from litellm.litellm_core_utils import get_model_cost_map as module + + client, _ = _mock_client([httpx.Response(200, content=_real_map_bytes())]) + monkeypatch.setattr(module._cost_map_source_info, "loaded_at", None) + before_remote = datetime.now(timezone.utc) + await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) + remote_loaded_at = module.get_model_cost_map_loaded_at() + assert remote_loaded_at is not None + assert before_remote <= remote_loaded_at <= datetime.now(timezone.utc) + + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + monkeypatch.setattr(module._cost_map_source_info, "loaded_at", None) + before_local = datetime.now(timezone.utc) + await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0)) + local_loaded_at = module.get_model_cost_map_loaded_at() + assert local_loaded_at is not None + assert before_local <= local_loaded_at <= datetime.now(timezone.utc) + + # --------------------------------------------------------------------------- # get_model_cost_map: the boot-time load retries transient failures like a reload does # --------------------------------------------------------------------------- From 2823d09e1bcfc99cee126583f3552e5d5b32dac5 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:30:05 -0700 Subject: [PATCH 102/223] refactor(policy_engine): type the request-state parameter of the retrieval hook --- litellm/proxy/policy_engine/response_retrieval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/policy_engine/response_retrieval.py b/litellm/proxy/policy_engine/response_retrieval.py index 54b9acf6150..0aa9579ca71 100644 --- a/litellm/proxy/policy_engine/response_retrieval.py +++ b/litellm/proxy/policy_engine/response_retrieval.py @@ -71,7 +71,7 @@ def _post_call_pipelines_for_context(context: PolicyMatchContext) -> tuple[Polic def attach_post_call_pipelines_to_retrieval( - data: dict, # mutable-ok: the proxy's request-state dict, written in place like every other policy engine hook + data: dict[str, object], # mutable-ok: request-state dict the policy engine hooks all write in place user_api_key_dict: "UserAPIKeyAuth", llm_router: "Router | None", ) -> None: From 91f1d98fb08428339802e0f16206f1836709d9c7 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 8 Sep 2026 12:30:42 -0700 Subject: [PATCH 103/223] fix(auto_router): clear the non-reasoning tier when the classifier changes Three review findings, all in the dashboard. Switching off the LLM classifier left the toggle checked but disabled, so the flag could not be cleared and every save was refused by the backend. The classifier-change handler now drops the flag and the tier's pool the same way it already drops the other classifier-specific keys. builtInTierInfo resolved rows against the four-tier order, so the new row rendered with no description, no examples and no rename field. It now resolves against every built-in tier, and the duplicate BUILT_IN_TIER_ORDER constant is gone in favour of the one in tier_rows. The preset schema widening is reverted. It was speculative, no published catalog carries the tier, and prefill would have discarded it while the route-wide null exclusion changed the endpoint's passthrough contract for every other field. Also splits NonReasoningTierToggle and TierConfigIntro into their own files to get ComplexityRouterConfig.tsx back under the max-lines limit, and applies ruff format to the two backend files CI flagged. --- .../public_endpoints/public_endpoints.py | 3 - .../complexity_router/complexity_router.py | 4 +- .../complexity_router/config.py | 1 + .../public_endpoints/public_endpoints.py | 9 +-- .../add_model/ClassificationMethodConfig.tsx | 20 +++++ .../add_model/ComplexityRouterConfig.tsx | 73 ++----------------- .../add_model/NonReasoningTierToggle.tsx | 49 +++++++++++++ .../components/add_model/TierConfigIntro.tsx | 30 ++++++++ .../add_model/nonReasoningTierFields.test.ts | 52 +++++++++++++ .../components/add_model/tier_rows.test.ts | 9 ++- ui/litellm-dashboard/src/lib/http/schema.d.ts | 10 +-- ui/litellm-dashboard/tsconfig.tsbuildinfo | 2 +- 12 files changed, 171 insertions(+), 91 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx create mode 100644 ui/litellm-dashboard/src/components/add_model/TierConfigIntro.tsx create mode 100644 ui/litellm-dashboard/src/components/add_model/nonReasoningTierFields.test.ts diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index a7a24e10bdd..94a59828451 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -534,9 +534,6 @@ async def get_autorouter_presets( "/public/autorouter_presets", tags=["public", "auto router"], # mutable-ok: FastAPI route tags take a list response_model=dict[str, AutoRouterPresetRecord], - # An optional tier a preset does not set must not reach the dashboard as a null pool, which the - # template picker would render as an empty tier row the operator never asked for. - response_model_exclude_none=True, ) async def get_public_autorouter_presets() -> Mapping[str, AutoRouterPresetRecord]: """ diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index fd0d53e0902..c40405ecf0f 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -2334,9 +2334,7 @@ class ComplexityRouter(CustomLogger): distance = 0 else: model_tiers = self._model_tiers.get(model, (classified_tier,)) - distance = min( - abs(severity_order.index(model_tier) - classified_idx) for model_tier in model_tiers - ) + distance = min(abs(severity_order.index(model_tier) - classified_idx) for model_tier in model_tiers) score = quality_weight * quality_sample + cost_weight * cost_score - penalty_weight * distance candidate_scores.append( { diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index bdfa0c9c389..d917c1b8041 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -78,6 +78,7 @@ def tier_severity_order(non_reasoning_enabled: bool) -> tuple[ComplexityTier, .. """The built-in ladder for one router, tier 0 included only when it opted in.""" return NON_REASONING_TIER_SEVERITY_ORDER if non_reasoning_enabled else TIER_SEVERITY_ORDER + DEFAULT_TIER_DISTANCE_PENALTY: Final[float] = 0.5 DEFAULT_CLASSIFIER_CONTEXT_WINDOW_SIZE: Final[int] = 3 diff --git a/litellm/types/proxy/public_endpoints/public_endpoints.py b/litellm/types/proxy/public_endpoints/public_endpoints.py index fb5a37c45fc..fa73926305b 100644 --- a/litellm/types/proxy/public_endpoints/public_endpoints.py +++ b/litellm/types/proxy/public_endpoints/public_endpoints.py @@ -74,14 +74,10 @@ class SupportedEndpointsResponse(BaseModel): class AutoRouterPresetTiers(BaseModel): - """The built-in tiers the dashboard's preset prefill can apply. + """Exactly the four built-in tiers the dashboard's preset prefill can apply. extra="forbid" on purpose: a tier name this dashboard cannot apply would grey out or crash the - picker, so such a catalog is rejected wholesale and the bundled one serves instead. NON_REASONING - is optional rather than required so this proxy accepts both a catalog that ships the opt-in fifth - tier and the four-tier catalogs that predate it. It stays None when unset rather than defaulting - to an empty pool, so a four-tier preset serves the tier set it was published with instead of - growing a key the dashboard would render as an empty fifth tier row. + picker, so such a catalog is rejected wholesale and the bundled one serves instead. """ model_config = ConfigDict(extra="forbid") @@ -90,7 +86,6 @@ class AutoRouterPresetTiers(BaseModel): MEDIUM: Sequence[str] COMPLEX: Sequence[str] REASONING: Sequence[str] - NON_REASONING: Sequence[str] | None = None class AutoRouterPresetConfig(BaseModel): diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 15cfff01766..468dcf5b411 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -236,6 +236,22 @@ const ClassifierTypeRadios: React.FC<{ ); }; +/** + * The NON_REASONING keys a classifier switch should carry forward, or clear. Leaving the flag set + * under a classifier that cannot emit the tier produces a config the backend refuses on save, and + * the switch is disabled there, so the operator would have no way to undo it. + */ +export const nonReasoningTierFields = ( + classifierType: ClassifierType, + value: ComplexityRouterConfigValue, +): Pick => { + if (classifierType === "llm") { + return { enable_non_reasoning_tier: value.enable_non_reasoning_tier, tiers: value.tiers }; + } + const { NON_REASONING: _cleared, ...keptTiers } = value.tiers; + return { enable_non_reasoning_tier: undefined, tiers: keptTiers }; +}; + const ClassificationMethodConfig: React.FC = ({ value, onChange, @@ -287,6 +303,10 @@ const ClassificationMethodConfig: React.FC = ({ : undefined, hybrid_boundary_margin: classifierType === "hybrid" ? value.hybrid_boundary_margin ?? DEFAULT_HYBRID_BOUNDARY_MARGIN : undefined, + // Only the LLM classifier can produce NON_REASONING, and the backend rejects the flag + // beside any other type. Clearing it here (with the tier's own pool) is what keeps a + // switch away from LLM from stranding a config that can never be saved. + ...nonReasoningTierFields(classifierType, value), }; onChange(nextValue); }; diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index 01e65914c67..540b68cf9f7 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -5,6 +5,8 @@ import { ChevronRight, Info, Plus, Trash2, X } from "lucide-react"; import { Switch } from "@/components/ui/switch"; import { AffinityControls } from "./AffinityControls"; +import NonReasoningTierToggle from "./NonReasoningTierToggle"; +import TierConfigIntro from "./TierConfigIntro"; import TierRowSelect from "./TierRowSelect"; import { ModalityRoutingControls } from "./ModalityRoutingControls"; import { Card, CardContent } from "@/components/ui/card"; @@ -20,6 +22,7 @@ import { MAX_TIER_COUNT, MAX_TIER_DEFINITION_CHARS, MAX_TIER_NAME_CHARS, + ALL_BUILT_IN_TIERS, MIN_TIER_COUNT, TIER_ORDER, activeTierName, @@ -200,34 +203,10 @@ const defaultModelPlaceholderFor = (derivedDefaultModel: string | undefined, isC }; const builtInTierInfo = (rowId: string): { label: string; description: string; examples: string } | undefined => { - const builtIn = TIER_ORDER.find((tier) => tier === rowId); + const builtIn = ALL_BUILT_IN_TIERS.find((tier) => tier === rowId); return builtIn ? TIER_DESCRIPTIONS[builtIn] : undefined; }; -const tierConfigIntroText = (value: ComplexityRouterConfigValue): string => { - if (value.classifier_type === "heuristic_v2") { - return "The complexity router classifies each request with a calibrated local four-tier model (no API calls). Configure which model(s) handle each tier."; - } - if (heuristicScoringRole(value) === "never") { - return "The complexity router classifies each request with your classifier model and routes it to that tier. Configure which model(s) handle each tier."; - } - return "The complexity router automatically classifies requests by complexity using rule-based scoring (no API calls, <1ms latency). Configure which model(s) handle each tier."; -}; - -const TierConfigIntro: React.FC<{ value: ComplexityRouterConfigValue }> = ({ value }) => ( - <> - {tierConfigIntroText(value)} - - - {restrictedBy(value, "displayNames")?.reason ?? - "Rename a tier to use your own vocabulary in the dashboard and your spend logs. Renaming doesn't change how requests are classified, and callers never see these names."} - {!value.custom_tier_set && - usesLlmClassifier(value.classifier_type) && - " Your classifier model reads these names, so clearer ones can sharpen its choices."} - - -); - const TierSetToolbar: React.FC<{ editing: boolean; isCustomSet: boolean; @@ -534,13 +513,6 @@ export const TIER_DESCRIPTIONS: Record< /** Every built-in tier name, including the opt-in one, for label and membership checks. */ export const TIER_KEYS = Object.keys(TIER_DESCRIPTIONS) as Array; -/** - * The four-tier ladder in ascending severity, which is what a router sends unless it opted into - * NON_REASONING. Mirrors TIER_SEVERITY_ORDER in the backend config; use tierOrderFor to get the - * ladder one router actually renders. - */ -export const BUILT_IN_TIER_ORDER: Array = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]; - export const effectiveTierLabel = (tier: keyof ComplexityTiers, tierLabels: ComplexityTierLabels | undefined): string => tierLabels?.[tier]?.trim() || TIER_DESCRIPTIONS[tier].label; @@ -555,42 +527,7 @@ export const DEFAULT_HYBRID_BOUNDARY_MARGIN = 0.03; * NON_REASONING, which the backend refuses alongside heuristic_first because the local scorer * cannot produce it. */ -export const HEURISTIC_FIRST_MAX_TIER_KEYS = BUILT_IN_TIER_ORDER.slice(0, -1); - -/** - * The opt-in fifth tier. Only offered on the LLM classification method, matching the backend: the - * heuristic scorers cannot produce the tier, so enabling it there would buy a rubric bullet and a - * model pool that no request ever reaches. - */ -const NonReasoningTierToggle: React.FC<{ - value: ComplexityRouterConfigValue; - onChange: (value: ComplexityRouterConfigValue) => void; - available: boolean; -}> = ({ value, onChange, available }) => ( - <> -
- { - const { NON_REASONING: _dropped, ...keptTiers } = value.tiers; - onChange({ - ...value, - enable_non_reasoning_tier: enabled ? true : undefined, - tiers: enabled ? { ...keptTiers, NON_REASONING: value.tiers.NON_REASONING ?? [] } : keptTiers, - }); - }} - aria-label="Add a non-reasoning tier" - /> - Add a non-reasoning tier -
- - Adds NON_REASONING below Simple, for operational agent traffic that relays or reformats information rather than - reasoning about it. Escalation still moves up out of it when a request needs more. - {!available && " Requires the LLM classification method."} - - -); +export const HEURISTIC_FIRST_MAX_TIER_KEYS = TIER_ORDER.slice(0, -1); const PlanModeOverrideControls: React.FC<{ value: ComplexityRouterConfigValue; diff --git a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx new file mode 100644 index 00000000000..794c2e5ec70 --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx @@ -0,0 +1,49 @@ +import React from "react"; + +import { Switch } from "@/components/ui/switch"; + +import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; + +/** + * The opt-in fifth tier. Only offered on the LLM classification method, matching the backend: the + * heuristic scorers cannot produce the tier, so enabling it there would buy a rubric bullet and a + * model pool that no request ever reaches. + */ +const NonReasoningTierToggle: React.FC<{ + value: ComplexityRouterConfigValue; + onChange: (value: ComplexityRouterConfigValue) => void; + available: boolean; +}> = ({ value, onChange, available }) => { + // Turning it off drops the tier's key rather than leaving an empty pool, which the backend + // rejects; turning it back on restores whatever pool the form still held. + const handleToggle = (enabled: boolean): void => { + const { NON_REASONING: existingPool, ...keptTiers } = value.tiers; + const next: ComplexityRouterConfigValue = { + ...value, + enable_non_reasoning_tier: enabled ? true : undefined, + tiers: enabled ? { ...keptTiers, NON_REASONING: existingPool ?? [] } : keptTiers, + }; + onChange(next); + }; + + return ( + <> +
+ + Add a non-reasoning tier +
+ + Adds NON_REASONING below Simple, for operational agent traffic that relays or reformats information rather than + reasoning about it. Escalation still moves up out of it when a request needs more. + {!available && " Requires the LLM classification method."} + + + ); +}; + +export default NonReasoningTierToggle; diff --git a/ui/litellm-dashboard/src/components/add_model/TierConfigIntro.tsx b/ui/litellm-dashboard/src/components/add_model/TierConfigIntro.tsx new file mode 100644 index 00000000000..4b14307dda5 --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/TierConfigIntro.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +import { type ComplexityRouterConfigValue, heuristicScoringRole, usesLlmClassifier } from "./ComplexityRouterConfig"; +import { restrictedBy } from "./TierRestrictions"; + +const tierConfigIntroText = (value: ComplexityRouterConfigValue): string => { + if (value.classifier_type === "heuristic_v2") { + return "The complexity router classifies each request with a calibrated local four-tier model (no API calls). Configure which model(s) handle each tier."; + } + if (heuristicScoringRole(value) === "never") { + return "The complexity router classifies each request with your classifier model and routes it to that tier. Configure which model(s) handle each tier."; + } + return "The complexity router automatically classifies requests by complexity using rule-based scoring (no API calls, <1ms latency). Configure which model(s) handle each tier."; +}; + +const TierConfigIntro: React.FC<{ value: ComplexityRouterConfigValue }> = ({ value }) => ( + <> + {tierConfigIntroText(value)} + + + {restrictedBy(value, "displayNames")?.reason ?? + "Rename a tier to use your own vocabulary in the dashboard and your spend logs. Renaming doesn't change how requests are classified, and callers never see these names."} + {!value.custom_tier_set && + usesLlmClassifier(value.classifier_type) && + " Your classifier model reads these names, so clearer ones can sharpen its choices."} + + +); + +export default TierConfigIntro; diff --git a/ui/litellm-dashboard/src/components/add_model/nonReasoningTierFields.test.ts b/ui/litellm-dashboard/src/components/add_model/nonReasoningTierFields.test.ts new file mode 100644 index 00000000000..86e50130e43 --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/nonReasoningTierFields.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; + +import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; +import { nonReasoningTierFields } from "./ClassificationMethodConfig"; + +const enabledValue: ComplexityRouterConfigValue = { + classifier_type: "llm", + enable_non_reasoning_tier: true, + tiers: { + NON_REASONING: ["relay-cheap"], + SIMPLE: ["gpt-4o-mini"], + MEDIUM: ["gpt-4o"], + COMPLEX: ["sonnet"], + REASONING: ["opus"], + }, +}; + +describe("nonReasoningTierFields", () => { + it("keeps the tier and its pool while the classifier stays LLM", () => { + expect(nonReasoningTierFields("llm", enabledValue)).toEqual({ + enable_non_reasoning_tier: true, + tiers: enabledValue.tiers, + }); + }); + + it.each(["heuristic", "heuristic_v2", "heuristic_first", "hybrid"] as const)( + "clears the flag and the tier when the classifier becomes %s", + (classifierType) => { + // Leaving the flag set under a classifier that cannot emit the tier is a config the backend + // refuses, and the switch is disabled there, so the operator could never undo it. + const cleared = nonReasoningTierFields(classifierType, enabledValue); + expect(cleared.enable_non_reasoning_tier).toBeUndefined(); + expect(cleared.tiers).not.toHaveProperty("NON_REASONING"); + }, + ); + + it("leaves the other tiers untouched when it clears", () => { + const { NON_REASONING: _dropped, ...expectedTiers } = enabledValue.tiers; + expect(nonReasoningTierFields("heuristic", enabledValue).tiers).toEqual(expectedTiers); + }); + + it("is a no-op for a router that never enabled the tier", () => { + const fourTier: ComplexityRouterConfigValue = { + classifier_type: "heuristic", + tiers: { SIMPLE: ["gpt-4o-mini"], MEDIUM: ["gpt-4o"], COMPLEX: ["sonnet"], REASONING: ["opus"] }, + }; + expect(nonReasoningTierFields("heuristic", fourTier)).toEqual({ + enable_non_reasoning_tier: undefined, + tiers: fourTier.tiers, + }); + }); +}); diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts index 07b9a4702aa..872e8850d91 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts @@ -190,8 +190,15 @@ describe("the opt-in non-reasoning tier", () => { }); it("renders an enabled tier with no models as an empty row rather than crashing", () => { + const emptyTierZeroRow: ActiveTierRow = { + id: "NON_REASONING", + name: "NON_REASONING", + definition: "", + models: [], + params: {}, + }; const rows = activeTierRows({ tiers, enable_non_reasoning_tier: true }); - expect(rows[0]).toEqual({ id: "NON_REASONING", name: "NON_REASONING", definition: "", models: [], params: {} }); + expect(rows[0]).toEqual(emptyTierZeroRow); }); it("counts as a built-in name either way, so a custom set cannot claim the name", () => { diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 6a60b0db58a..81b5ed79abf 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -23573,22 +23573,16 @@ export interface components { }; /** * AutoRouterPresetTiers - * @description The built-in tiers the dashboard's preset prefill can apply. + * @description Exactly the four built-in tiers the dashboard's preset prefill can apply. * * extra="forbid" on purpose: a tier name this dashboard cannot apply would grey out or crash the - * picker, so such a catalog is rejected wholesale and the bundled one serves instead. NON_REASONING - * is optional rather than required so this proxy accepts both a catalog that ships the opt-in fifth - * tier and the four-tier catalogs that predate it. It stays None when unset rather than defaulting - * to an empty pool, so a four-tier preset serves the tier set it was published with instead of - * growing a key the dashboard would render as an empty fifth tier row. + * picker, so such a catalog is rejected wholesale and the bundled one serves instead. */ AutoRouterPresetTiers: { /** Complex */ COMPLEX: string[]; /** Medium */ MEDIUM: string[]; - /** Non Reasoning */ - NON_REASONING?: string[] | null; /** Reasoning */ REASONING: string[]; /** Simple */ diff --git a/ui/litellm-dashboard/tsconfig.tsbuildinfo b/ui/litellm-dashboard/tsconfig.tsbuildinfo index 38e28e4f476..1036720c31e 100644 --- a/ui/litellm-dashboard/tsconfig.tsbuildinfo +++ b/ui/litellm-dashboard/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/@img/colour/index.d.ts","./node_modules/sharp/dist/index.d.mts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/types/routes.d.ts","./next-env.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/tinyrainbow/dist/index-8b61d5bc.d.ts","./node_modules/tinyrainbow/dist/node.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/rollup/dist/rollup.d.ts","./node_modules/rollup/dist/parseast.d.ts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/esbuild/lib/main.d.ts","./node_modules/vite/types/internal/terseroptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/vite/types/internal/csspreprocessoroptions.d.ts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-cksck4of.d.ts","./node_modules/@vitest/runner/dist/types.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/vitest/dist/chunks/environment.d.cl3nlxbe.d.ts","./node_modules/@vitest/mocker/dist/registry.d-d765pazg.d.ts","./node_modules/@vitest/mocker/dist/types.d-d_arzrdy.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/vite-node/dist/index.d-dgmxd2u7.d.ts","./node_modules/vite-node/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-dhdq1csl.d.ts","./node_modules/@vitest/snapshot/dist/rawsnapshot.d-lfsmjfud.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/vitest/dist/chunks/config.d.bkdhh7zx.d.ts","./node_modules/vitest/dist/chunks/worker.d.cugipz9v.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.bwvbvtda.d.ts","./node_modules/vite-node/dist/client.d.ts","./node_modules/vitest/dist/chunks/coverage.d.s9rmnxie.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.buron0i0.d.ts","./node_modules/vitest/dist/chunks/vite.d.bnoppc46.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./src/types.ts","./node_modules/sonner/dist/index.d.mts","./src/lib/http/client.ts","./src/lib/toast.ts","./src/utils/securestorage.ts","./src/utils/mcptokenstore.ts","./src/utils/cookieutils.ts","./node_modules/jwt-decode/build/esm/index.d.ts","./src/utils/jwtutils.ts","./src/components/tag_management/types.tsx","./src/lib/http/schema.d.ts","./src/components/object_permission_types.ts","./node_modules/@base-ui/react/internals/reason-parts.d.mts","./node_modules/@base-ui/react/internals/reasons.d.mts","./node_modules/@base-ui/react/internals/createbaseuieventdetails.d.mts","./node_modules/@base-ui/react/types/index.d.mts","./node_modules/@base-ui/react/internals/types.d.mts","./node_modules/@base-ui/react/accordion/root/accordionroot.d.mts","./node_modules/@base-ui/react/internals/usetransitionstatus.d.mts","./node_modules/@base-ui/react/collapsible/root/collapsibleroot.d.mts","./node_modules/@base-ui/react/collapsible/root/usecollapsibleroot.d.mts","./node_modules/@base-ui/react/accordion/item/accordionitem.d.mts","./node_modules/@base-ui/react/accordion/header/accordionheader.d.mts","./node_modules/@base-ui/react/accordion/trigger/accordiontrigger.d.mts","./node_modules/@base-ui/react/accordion/panel/accordionpanel.d.mts","./node_modules/@base-ui/react/accordion/index.parts.d.mts","./node_modules/@base-ui/react/accordion/index.d.mts","./node_modules/reselect/dist/reselect.d.ts","./node_modules/@base-ui/utils/store/createselector.d.mts","./node_modules/@base-ui/utils/store/createselectormemoized.d.mts","./node_modules/@base-ui/utils/fasthooks.d.mts","./node_modules/@base-ui/utils/store/store.d.mts","./node_modules/@base-ui/utils/store/usestore.d.mts","./node_modules/@base-ui/utils/store/reactstore.d.mts","./node_modules/@base-ui/utils/store/storeinspector.d.mts","./node_modules/@base-ui/utils/store/index.d.mts","./node_modules/@base-ui/utils/useenhancedclickhandler.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.d.mts","./node_modules/@floating-ui/core/dist/floating-ui.core.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.d.mts","./node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts","./node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.d.mts","./node_modules/@base-ui/react/utils/popups/inlinerect.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtreestore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingrootstore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingfocusmanager.d.mts","./node_modules/@base-ui/react/internals/getstateattributesprops.d.mts","./node_modules/@base-ui/react/internals/userenderelement.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingportal.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclientpoint.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usedismiss.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefocus.d.mts","./node_modules/@base-ui/react/internals/shadowdom.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/element.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehovershared.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehover.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverfloatinginteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverreferenceinteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/composite.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/gridnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/uselistnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usetypeahead.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/safepolygon.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtree.d.mts","./node_modules/@base-ui/react/floating-ui-react/types.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingdelaygroup.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclick.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloating.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usesyncedfloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/index.d.mts","./node_modules/@base-ui/react/utils/popups/popuptriggermap.d.mts","./node_modules/@base-ui/react/utils/popups/store.d.mts","./node_modules/@base-ui/react/utils/popups/popupstoreutils.d.mts","./node_modules/@base-ui/react/utils/popups/index.d.mts","./node_modules/@base-ui/react/dialog/store/dialogstore.d.mts","./node_modules/@base-ui/react/dialog/store/dialoghandle.d.mts","./node_modules/@base-ui/react/dialog/root/dialogroot.d.mts","./node_modules/@base-ui/react/alert-dialog/handle.d.mts","./node_modules/@base-ui/react/alert-dialog/root/alertdialogroot.d.mts","./node_modules/@base-ui/react/dialog/backdrop/dialogbackdrop.d.mts","./node_modules/@base-ui/react/dialog/close/dialogclose.d.mts","./node_modules/@base-ui/react/dialog/description/dialogdescription.d.mts","./node_modules/@base-ui/react/dialog/popup/dialogpopup.d.mts","./node_modules/@base-ui/react/dialog/portal/dialogportal.d.mts","./node_modules/@base-ui/react/dialog/title/dialogtitle.d.mts","./node_modules/@base-ui/react/dialog/trigger/dialogtrigger.d.mts","./node_modules/@base-ui/react/alert-dialog/trigger/alertdialogtrigger.d.mts","./node_modules/@base-ui/react/dialog/viewport/dialogviewport.d.mts","./node_modules/@base-ui/react/alert-dialog/index.parts.d.mts","./node_modules/@base-ui/react/alert-dialog/index.d.mts","./node_modules/@base-ui/react/internals/resolvevaluelabel.d.mts","./node_modules/@base-ui/react/combobox/root/ariacombobox.d.mts","./node_modules/@base-ui/react/autocomplete/root/autocompleteroot.d.mts","./node_modules/@base-ui/react/autocomplete/value/autocompletevalue.d.mts","./node_modules/@base-ui/react/internals/form-context/formcontext.d.mts","./node_modules/@base-ui/react/form/form.d.mts","./node_modules/@base-ui/react/form/index.d.mts","./node_modules/@base-ui/react/field/root/fieldroot.d.mts","./node_modules/@base-ui/react/utils/useanchorpositioning.d.mts","./node_modules/@base-ui/react/autocomplete/trigger/autocompletetrigger.d.mts","./node_modules/@base-ui/react/combobox/input/comboboxinput.d.mts","./node_modules/@base-ui/react/autocomplete/input-group/autocompleteinputgroup.d.mts","./node_modules/@base-ui/react/combobox/icon/comboboxicon.d.mts","./node_modules/@base-ui/react/combobox/clear/comboboxclear.d.mts","./node_modules/@base-ui/react/combobox/list/comboboxlist.d.mts","./node_modules/@base-ui/react/combobox/status/comboboxstatus.d.mts","./node_modules/@base-ui/react/combobox/portal/comboboxportal.d.mts","./node_modules/@base-ui/react/combobox/backdrop/comboboxbackdrop.d.mts","./node_modules/@base-ui/react/combobox/positioner/comboboxpositioner.d.mts","./node_modules/@base-ui/react/combobox/popup/comboboxpopup.d.mts","./node_modules/@base-ui/react/combobox/arrow/comboboxarrow.d.mts","./node_modules/@base-ui/react/combobox/group/comboboxgroup.d.mts","./node_modules/@base-ui/react/combobox/group-label/comboboxgrouplabel.d.mts","./node_modules/@base-ui/react/autocomplete/item/autocompleteitem.d.mts","./node_modules/@base-ui/react/combobox/row/comboboxrow.d.mts","./node_modules/@base-ui/react/combobox/collection/comboboxcollection.d.mts","./node_modules/@base-ui/react/combobox/empty/comboboxempty.d.mts","./node_modules/@base-ui/react/separator/separator.d.mts","./node_modules/@base-ui/react/internals/filter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefilter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefiltereditems.d.mts","./node_modules/@base-ui/react/autocomplete/index.parts.d.mts","./node_modules/@base-ui/react/autocomplete/index.d.mts","./node_modules/@base-ui/react/avatar/root/avatarroot.d.mts","./node_modules/@base-ui/react/avatar/image/avatarimage.d.mts","./node_modules/@base-ui/react/avatar/fallback/avatarfallback.d.mts","./node_modules/@base-ui/react/avatar/index.parts.d.mts","./node_modules/@base-ui/react/avatar/index.d.mts","./node_modules/@base-ui/react/button/button.d.mts","./node_modules/@base-ui/react/button/index.d.mts","./node_modules/@base-ui/react/checkbox/root/checkboxroot.d.mts","./node_modules/@base-ui/react/checkbox/indicator/checkboxindicator.d.mts","./node_modules/@base-ui/react/checkbox/index.parts.d.mts","./node_modules/@base-ui/react/checkbox/index.d.mts","./node_modules/@base-ui/react/checkbox-group/checkboxgroup.d.mts","./node_modules/@base-ui/react/checkbox-group/index.d.mts","./node_modules/@base-ui/react/collapsible/trigger/collapsibletrigger.d.mts","./node_modules/@base-ui/react/collapsible/panel/collapsiblepanel.d.mts","./node_modules/@base-ui/react/collapsible/index.parts.d.mts","./node_modules/@base-ui/react/collapsible/index.d.mts","./node_modules/@base-ui/react/combobox/root/comboboxroot.d.mts","./node_modules/@base-ui/react/combobox/label/comboboxlabel.d.mts","./node_modules/@base-ui/react/combobox/value/comboboxvalue.d.mts","./node_modules/@base-ui/react/combobox/input-group/comboboxinputgroup.d.mts","./node_modules/@base-ui/react/combobox/trigger/comboboxtrigger.d.mts","./node_modules/@base-ui/react/combobox/item/comboboxitem.d.mts","./node_modules/@base-ui/react/combobox/item-indicator/comboboxitemindicator.d.mts","./node_modules/@base-ui/react/combobox/chips/comboboxchips.d.mts","./node_modules/@base-ui/react/combobox/chip/comboboxchip.d.mts","./node_modules/@base-ui/react/combobox/chip-remove/comboboxchipremove.d.mts","./node_modules/@base-ui/react/separator/index.d.mts","./node_modules/@base-ui/react/combobox/index.parts.d.mts","./node_modules/@base-ui/react/combobox/index.d.mts","./node_modules/@base-ui/react/menu/arrow/menuarrow.d.mts","./node_modules/@base-ui/react/menu/backdrop/menubackdrop.d.mts","./node_modules/@base-ui/react/menu/store/menustore.d.mts","./node_modules/@base-ui/react/menu/root/menurootcontext.d.mts","./node_modules/@base-ui/react/menubar/menubarcontext.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/store/menuhandle.d.mts","./node_modules/@base-ui/react/menu/root/menuroot.d.mts","./node_modules/@base-ui/react/menu/checkbox-item/menucheckboxitem.d.mts","./node_modules/@base-ui/react/menu/checkbox-item-indicator/menucheckboxitemindicator.d.mts","./node_modules/@base-ui/react/menu/group/menugroup.d.mts","./node_modules/@base-ui/react/menu/group-label/menugrouplabel.d.mts","./node_modules/@base-ui/react/menu/item/menuitem.d.mts","./node_modules/@base-ui/react/menu/link-item/menulinkitem.d.mts","./node_modules/@base-ui/react/menu/popup/menupopup.d.mts","./node_modules/@base-ui/react/menu/portal/menuportal.d.mts","./node_modules/@base-ui/react/menu/positioner/menupositioner.d.mts","./node_modules/@base-ui/react/menu/radio-group/menuradiogroup.d.mts","./node_modules/@base-ui/react/menu/radio-item/menuradioitem.d.mts","./node_modules/@base-ui/react/menu/radio-item-indicator/menuradioitemindicator.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenuroot.d.mts","./node_modules/@base-ui/react/menu/trigger/menutrigger.d.mts","./node_modules/@base-ui/react/menu/viewport/menuviewport.d.mts","./node_modules/@base-ui/react/menu/submenu-trigger/menusubmenutrigger.d.mts","./node_modules/@base-ui/react/menu/index.parts.d.mts","./node_modules/@base-ui/react/menu/index.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenuroot.d.mts","./node_modules/@base-ui/react/context-menu/trigger/contextmenutrigger.d.mts","./node_modules/@base-ui/react/context-menu/index.parts.d.mts","./node_modules/@base-ui/react/context-menu/index.d.mts","./node_modules/@base-ui/react/csp-provider/cspprovider.d.mts","./node_modules/@base-ui/react/csp-provider/index.parts.d.mts","./node_modules/@base-ui/react/csp-provider/index.d.mts","./node_modules/@base-ui/react/dialog/index.parts.d.mts","./node_modules/@base-ui/react/dialog/index.d.mts","./node_modules/@base-ui/react/internals/direction-context/directioncontext.d.mts","./node_modules/@base-ui/react/direction-provider/directionprovider.d.mts","./node_modules/@base-ui/react/direction-provider/index.parts.d.mts","./node_modules/@base-ui/react/direction-provider/index.d.mts","./node_modules/@base-ui/react/drawer/backdrop/drawerbackdrop.d.mts","./node_modules/@base-ui/react/drawer/close/drawerclose.d.mts","./node_modules/@base-ui/react/drawer/content/drawercontent.d.mts","./node_modules/@base-ui/react/drawer/description/drawerdescription.d.mts","./node_modules/@base-ui/react/drawer/indent/drawerindent.d.mts","./node_modules/@base-ui/react/drawer/indent-background/drawerindentbackground.d.mts","./node_modules/@base-ui/react/utils/useswipedismiss.d.mts","./node_modules/@base-ui/react/drawer/root/drawerroot.d.mts","./node_modules/@base-ui/react/drawer/root/drawerrootcontext.d.mts","./node_modules/@base-ui/react/drawer/popup/drawerpopup.d.mts","./node_modules/@base-ui/react/drawer/portal/drawerportal.d.mts","./node_modules/@base-ui/react/drawer/provider/drawerprovider.d.mts","./node_modules/@base-ui/react/drawer/swipe-area/drawerswipearea.d.mts","./node_modules/@base-ui/react/drawer/title/drawertitle.d.mts","./node_modules/@base-ui/react/drawer/trigger/drawertrigger.d.mts","./node_modules/@base-ui/react/drawer/viewport/drawerviewport.d.mts","./node_modules/@base-ui/react/drawer/virtual-keyboard-provider/drawervirtualkeyboardprovider.d.mts","./node_modules/@base-ui/react/drawer/index.parts.d.mts","./node_modules/@base-ui/react/drawer/index.d.mts","./node_modules/@base-ui/react/field/label/fieldlabel.d.mts","./node_modules/@base-ui/react/field/error/fielderror.d.mts","./node_modules/@base-ui/react/field/description/fielddescription.d.mts","./node_modules/@base-ui/react/field/control/fieldcontrol.d.mts","./node_modules/@base-ui/react/field/validity/fieldvalidity.d.mts","./node_modules/@base-ui/react/field/item/fielditem.d.mts","./node_modules/@base-ui/react/field/index.parts.d.mts","./node_modules/@base-ui/react/field/index.d.mts","./node_modules/@base-ui/react/fieldset/root/fieldsetroot.d.mts","./node_modules/@base-ui/react/fieldset/legend/fieldsetlegend.d.mts","./node_modules/@base-ui/react/fieldset/index.parts.d.mts","./node_modules/@base-ui/react/fieldset/index.d.mts","./node_modules/@base-ui/react/input/input.d.mts","./node_modules/@base-ui/react/input/index.d.mts","./node_modules/@base-ui/react/menubar/menubar.d.mts","./node_modules/@base-ui/react/menubar/index.d.mts","./node_modules/@base-ui/react/merge-props/mergeprops.d.mts","./node_modules/@base-ui/react/merge-props/index.d.mts","./node_modules/@base-ui/react/meter/root/meterroot.d.mts","./node_modules/@base-ui/react/meter/track/metertrack.d.mts","./node_modules/@base-ui/react/meter/indicator/meterindicator.d.mts","./node_modules/@base-ui/react/meter/value/metervalue.d.mts","./node_modules/@base-ui/react/meter/label/meterlabel.d.mts","./node_modules/@base-ui/react/meter/index.parts.d.mts","./node_modules/@base-ui/react/meter/index.d.mts","./node_modules/@base-ui/react/navigation-menu/root/navigationmenuroot.d.mts","./node_modules/@base-ui/react/navigation-menu/list/navigationmenulist.d.mts","./node_modules/@base-ui/react/navigation-menu/item/navigationmenuitem.d.mts","./node_modules/@base-ui/react/navigation-menu/content/navigationmenucontent.d.mts","./node_modules/@base-ui/react/navigation-menu/trigger/navigationmenutrigger.d.mts","./node_modules/@base-ui/react/navigation-menu/portal/navigationmenuportal.d.mts","./node_modules/@base-ui/react/navigation-menu/positioner/navigationmenupositioner.d.mts","./node_modules/@base-ui/react/navigation-menu/viewport/navigationmenuviewport.d.mts","./node_modules/@base-ui/react/navigation-menu/backdrop/navigationmenubackdrop.d.mts","./node_modules/@base-ui/react/navigation-menu/popup/navigationmenupopup.d.mts","./node_modules/@base-ui/react/navigation-menu/arrow/navigationmenuarrow.d.mts","./node_modules/@base-ui/react/navigation-menu/link/navigationmenulink.d.mts","./node_modules/@base-ui/react/navigation-menu/icon/navigationmenuicon.d.mts","./node_modules/@base-ui/react/navigation-menu/index.parts.d.mts","./node_modules/@base-ui/react/navigation-menu/index.d.mts","./node_modules/@base-ui/react/number-field/utils/types.d.mts","./node_modules/@base-ui/react/number-field/root/numberfieldroot.d.mts","./node_modules/@base-ui/react/number-field/group/numberfieldgroup.d.mts","./node_modules/@base-ui/react/number-field/increment/numberfieldincrement.d.mts","./node_modules/@base-ui/react/number-field/decrement/numberfielddecrement.d.mts","./node_modules/@base-ui/react/number-field/input/numberfieldinput.d.mts","./node_modules/@base-ui/react/number-field/scrub-area/numberfieldscrubarea.d.mts","./node_modules/@base-ui/react/number-field/scrub-area-cursor/numberfieldscrubareacursor.d.mts","./node_modules/@base-ui/react/number-field/index.parts.d.mts","./node_modules/@base-ui/react/number-field/index.d.mts","./node_modules/@base-ui/react/otp-field/utils/otp.d.mts","./node_modules/@base-ui/react/otp-field/root/otpfieldroot.d.mts","./node_modules/@base-ui/react/otp-field/input/otpfieldinput.d.mts","./node_modules/@base-ui/react/otp-field/index.parts.d.mts","./node_modules/@base-ui/react/otp-field/index.d.mts","./node_modules/@base-ui/utils/usetimeout.d.mts","./node_modules/@base-ui/react/popover/store/popoverstore.d.mts","./node_modules/@base-ui/react/popover/store/popoverhandle.d.mts","./node_modules/@base-ui/react/popover/root/popoverroot.d.mts","./node_modules/@base-ui/react/popover/trigger/popovertrigger.d.mts","./node_modules/@base-ui/react/popover/portal/popoverportal.d.mts","./node_modules/@base-ui/react/popover/positioner/popoverpositioner.d.mts","./node_modules/@base-ui/react/popover/popup/popoverpopup.d.mts","./node_modules/@base-ui/react/popover/arrow/popoverarrow.d.mts","./node_modules/@base-ui/react/popover/backdrop/popoverbackdrop.d.mts","./node_modules/@base-ui/react/popover/title/popovertitle.d.mts","./node_modules/@base-ui/react/popover/description/popoverdescription.d.mts","./node_modules/@base-ui/react/popover/close/popoverclose.d.mts","./node_modules/@base-ui/react/popover/viewport/popoverviewport.d.mts","./node_modules/@base-ui/react/popover/index.parts.d.mts","./node_modules/@base-ui/react/popover/index.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardstore.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardhandle.d.mts","./node_modules/@base-ui/react/preview-card/root/previewcardroot.d.mts","./node_modules/@base-ui/react/utils/floatingportallite.d.mts","./node_modules/@base-ui/react/preview-card/portal/previewcardportal.d.mts","./node_modules/@base-ui/react/preview-card/trigger/previewcardtrigger.d.mts","./node_modules/@base-ui/react/preview-card/positioner/previewcardpositioner.d.mts","./node_modules/@base-ui/react/preview-card/popup/previewcardpopup.d.mts","./node_modules/@base-ui/react/preview-card/arrow/previewcardarrow.d.mts","./node_modules/@base-ui/react/preview-card/backdrop/previewcardbackdrop.d.mts","./node_modules/@base-ui/react/preview-card/viewport/previewcardviewport.d.mts","./node_modules/@base-ui/react/preview-card/index.parts.d.mts","./node_modules/@base-ui/react/preview-card/index.d.mts","./node_modules/@base-ui/react/progress/root/progressroot.d.mts","./node_modules/@base-ui/react/progress/track/progresstrack.d.mts","./node_modules/@base-ui/react/progress/indicator/progressindicator.d.mts","./node_modules/@base-ui/react/progress/value/progressvalue.d.mts","./node_modules/@base-ui/react/progress/label/progresslabel.d.mts","./node_modules/@base-ui/react/progress/index.parts.d.mts","./node_modules/@base-ui/react/progress/index.d.mts","./node_modules/@base-ui/react/radio/root/radioroot.d.mts","./node_modules/@base-ui/react/radio/indicator/radioindicator.d.mts","./node_modules/@base-ui/react/radio/index.parts.d.mts","./node_modules/@base-ui/react/radio/index.d.mts","./node_modules/@base-ui/react/radio-group/radiogroup.d.mts","./node_modules/@base-ui/react/radio-group/index.d.mts","./node_modules/@base-ui/react/scroll-area/root/scrollarearoot.d.mts","./node_modules/@base-ui/react/scroll-area/viewport/scrollareaviewport.d.mts","./node_modules/@base-ui/react/scroll-area/scrollbar/scrollareascrollbar.d.mts","./node_modules/@base-ui/react/scroll-area/content/scrollareacontent.d.mts","./node_modules/@base-ui/react/scroll-area/thumb/scrollareathumb.d.mts","./node_modules/@base-ui/react/scroll-area/corner/scrollareacorner.d.mts","./node_modules/@base-ui/react/scroll-area/index.parts.d.mts","./node_modules/@base-ui/react/scroll-area/index.d.mts","./node_modules/@base-ui/react/select/root/selectroot.d.mts","./node_modules/@base-ui/react/select/label/selectlabel.d.mts","./node_modules/@base-ui/react/select/trigger/selecttrigger.d.mts","./node_modules/@base-ui/react/select/value/selectvalue.d.mts","./node_modules/@base-ui/react/select/icon/selecticon.d.mts","./node_modules/@base-ui/react/select/portal/selectportal.d.mts","./node_modules/@base-ui/react/select/backdrop/selectbackdrop.d.mts","./node_modules/@base-ui/react/select/positioner/selectpositioner.d.mts","./node_modules/@base-ui/react/select/popup/selectpopup.d.mts","./node_modules/@base-ui/react/select/list/selectlist.d.mts","./node_modules/@base-ui/react/select/item/selectitem.d.mts","./node_modules/@base-ui/react/select/item-indicator/selectitemindicator.d.mts","./node_modules/@base-ui/react/select/item-text/selectitemtext.d.mts","./node_modules/@base-ui/react/select/arrow/selectarrow.d.mts","./node_modules/@base-ui/react/select/scroll-down-arrow/selectscrolldownarrow.d.mts","./node_modules/@base-ui/react/select/scroll-up-arrow/selectscrolluparrow.d.mts","./node_modules/@base-ui/react/select/group/selectgroup.d.mts","./node_modules/@base-ui/react/select/group-label/selectgrouplabel.d.mts","./node_modules/@base-ui/react/select/index.parts.d.mts","./node_modules/@base-ui/react/select/index.d.mts","./node_modules/@base-ui/react/slider/root/sliderroot.d.mts","./node_modules/@base-ui/react/slider/label/sliderlabel.d.mts","./node_modules/@base-ui/react/slider/value/slidervalue.d.mts","./node_modules/@base-ui/react/slider/control/slidercontrol.d.mts","./node_modules/@base-ui/react/slider/track/slidertrack.d.mts","./node_modules/@base-ui/react/internals/labelable-provider/labelablecontext.d.mts","./node_modules/@base-ui/react/slider/thumb/sliderthumb.d.mts","./node_modules/@base-ui/react/slider/indicator/sliderindicator.d.mts","./node_modules/@base-ui/react/slider/index.parts.d.mts","./node_modules/@base-ui/react/slider/index.d.mts","./node_modules/@base-ui/react/switch/root/switchroot.d.mts","./node_modules/@base-ui/react/switch/thumb/switchthumb.d.mts","./node_modules/@base-ui/react/switch/index.parts.d.mts","./node_modules/@base-ui/react/switch/index.d.mts","./node_modules/@base-ui/react/tabs/tab/tabstab.d.mts","./node_modules/@base-ui/react/tabs/root/tabsroot.d.mts","./node_modules/@base-ui/react/tabs/indicator/tabsindicator.d.mts","./node_modules/@base-ui/react/tabs/panel/tabspanel.d.mts","./node_modules/@base-ui/react/tabs/list/tabslist.d.mts","./node_modules/@base-ui/react/tabs/index.parts.d.mts","./node_modules/@base-ui/react/tabs/index.d.mts","./node_modules/@base-ui/react/toast/positioner/toastpositioner.d.mts","./node_modules/@base-ui/react/toast/usetoastmanager.d.mts","./node_modules/@base-ui/react/toast/createtoastmanager.d.mts","./node_modules/@base-ui/react/toast/provider/toastprovider.d.mts","./node_modules/@base-ui/react/toast/viewport/toastviewport.d.mts","./node_modules/@base-ui/react/toast/root/toastroot.d.mts","./node_modules/@base-ui/react/toast/content/toastcontent.d.mts","./node_modules/@base-ui/react/toast/description/toastdescription.d.mts","./node_modules/@base-ui/react/toast/title/toasttitle.d.mts","./node_modules/@base-ui/react/toast/close/toastclose.d.mts","./node_modules/@base-ui/react/toast/action/toastaction.d.mts","./node_modules/@base-ui/react/toast/portal/toastportal.d.mts","./node_modules/@base-ui/react/toast/arrow/toastarrow.d.mts","./node_modules/@base-ui/react/toast/index.parts.d.mts","./node_modules/@base-ui/react/toast/index.d.mts","./node_modules/@base-ui/react/toggle/toggle.d.mts","./node_modules/@base-ui/react/toggle/index.d.mts","./node_modules/@base-ui/react/toggle-group/togglegroup.d.mts","./node_modules/@base-ui/react/toggle-group/index.d.mts","./node_modules/@base-ui/react/toolbar/separator/toolbarseparator.d.mts","./node_modules/@base-ui/react/toolbar/root/toolbarroot.d.mts","./node_modules/@base-ui/react/toolbar/group/toolbargroup.d.mts","./node_modules/@base-ui/react/toolbar/button/toolbarbutton.d.mts","./node_modules/@base-ui/react/toolbar/link/toolbarlink.d.mts","./node_modules/@base-ui/react/toolbar/input/toolbarinput.d.mts","./node_modules/@base-ui/react/toolbar/index.parts.d.mts","./node_modules/@base-ui/react/toolbar/index.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltipstore.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltiphandle.d.mts","./node_modules/@base-ui/react/tooltip/root/tooltiproot.d.mts","./node_modules/@base-ui/react/tooltip/trigger/tooltiptrigger.d.mts","./node_modules/@base-ui/react/tooltip/portal/tooltipportal.d.mts","./node_modules/@base-ui/react/tooltip/positioner/tooltippositioner.d.mts","./node_modules/@base-ui/react/tooltip/popup/tooltippopup.d.mts","./node_modules/@base-ui/react/tooltip/arrow/tooltiparrow.d.mts","./node_modules/@base-ui/react/tooltip/provider/tooltipprovider.d.mts","./node_modules/@base-ui/react/tooltip/viewport/tooltipviewport.d.mts","./node_modules/@base-ui/react/tooltip/index.parts.d.mts","./node_modules/@base-ui/react/tooltip/index.d.mts","./node_modules/@base-ui/react/use-render/userender.d.mts","./node_modules/@base-ui/react/use-render/index.d.mts","./node_modules/@base-ui/react/index.d.mts","./node_modules/clsx/clsx.d.mts","./node_modules/tailwind-merge/dist/types.d.ts","./node_modules/class-variance-authority/dist/types.d.ts","./node_modules/class-variance-authority/dist/index.d.ts","./src/lib/cva.config.ts","./src/components/ui/button.tsx","./src/components/ui/input.tsx","./src/components/ui/textarea.tsx","./src/components/ui/input-group.tsx","./node_modules/lucide-react/dist/lucide-react.d.ts","./src/components/ui/combobox.tsx","./src/components/shared/searchselect.tsx","./src/components/ui/label.tsx","./src/components/ui/separator.tsx","./src/components/ui/field.tsx","./src/components/ui/select.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","./src/components/key_team_helpers/key_list.tsx","./src/components/email_events/types.ts","./src/components/claude_code_plugins/types.ts","./src/components/ui/tooltip.tsx","./node_modules/react-hook-form/dist/constants.d.ts","./node_modules/react-hook-form/dist/utils/createsubject.d.ts","./node_modules/react-hook-form/dist/types/events.d.ts","./node_modules/react-hook-form/dist/types/path/common.d.ts","./node_modules/react-hook-form/dist/types/path/eager.d.ts","./node_modules/react-hook-form/dist/types/path/index.d.ts","./node_modules/react-hook-form/dist/types/fieldarray.d.ts","./node_modules/react-hook-form/dist/types/resolvers.d.ts","./node_modules/react-hook-form/dist/types/form.d.ts","./node_modules/react-hook-form/dist/types/utils.d.ts","./node_modules/react-hook-form/dist/types/fields.d.ts","./node_modules/react-hook-form/dist/types/errors.d.ts","./node_modules/react-hook-form/dist/types/validator.d.ts","./node_modules/react-hook-form/dist/types/controller.d.ts","./node_modules/react-hook-form/dist/types/watch.d.ts","./node_modules/react-hook-form/dist/types/index.d.ts","./node_modules/react-hook-form/dist/controller.d.ts","./node_modules/react-hook-form/dist/fieldarray.d.ts","./node_modules/react-hook-form/dist/form.d.ts","./node_modules/react-hook-form/dist/formstatesubscribe.d.ts","./node_modules/react-hook-form/dist/logic/appenderrors.d.ts","./node_modules/react-hook-form/dist/logic/createformcontrol.d.ts","./node_modules/react-hook-form/dist/logic/index.d.ts","./node_modules/react-hook-form/dist/usecontroller.d.ts","./node_modules/react-hook-form/dist/usefieldarray.d.ts","./node_modules/react-hook-form/dist/useform.d.ts","./node_modules/react-hook-form/dist/useformcontext.d.ts","./node_modules/react-hook-form/dist/useformstate.d.ts","./node_modules/react-hook-form/dist/usewatch.d.ts","./node_modules/react-hook-form/dist/utils/get.d.ts","./node_modules/react-hook-form/dist/utils/set.d.ts","./node_modules/react-hook-form/dist/utils/index.d.ts","./node_modules/react-hook-form/dist/watch.d.ts","./node_modules/react-hook-form/dist/index.d.ts","./src/utils/textutils.ts","./src/components/common_components/mountedformfield.tsx","./src/components/common_components/check_openapi_schema.tsx","./src/components/mcp_tools/types.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/types.ts","./src/components/mcp_tools/constants.ts","./src/components/shared/multiselect.tsx","./src/components/ui/card.tsx","./src/components/add_model/complexity_router_keywords.ts","./src/components/ui/switch.tsx","./src/components/ui/collapsible.tsx","./src/components/key_team_helpers/fetch_available_models_team_key.tsx","./src/components/llm_calls/fetch_models.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/slider.tsx","./src/components/add_model/adaptiveroutingconfig.tsx","./src/utils/returnurlutils.ts","./src/utils/roles.ts","./node_modules/@tanstack/query-core/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/query-core/build/modern/index.d.ts","./node_modules/@tanstack/react-query/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/react-query/build/modern/index.d.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.ts","./src/app/(dashboard)/hooks/useauthorized.ts","./src/components/ui/dialog.tsx","./src/components/add_model/classifierprompteditorstate.ts","./src/components/add_model/classifierprompteditor.tsx","./src/app/(dashboard)/hooks/autorouter/usecomplexityscorerdefaults.ts","./src/components/ui/badge.tsx","./src/components/add_model/heuristic_scoring_knobs.ts","./src/components/add_model/heuristicscoringconfig.tsx","./src/components/add_model/classificationmethodconfig.tsx","./src/components/add_model/tiermodeleffortrows.tsx","./src/components/add_model/escalationkeywords.tsx","./src/components/add_model/semantickeywordmatching.tsx","./src/components/add_model/complexityrouterconfig.tsx","./src/components/add_model/tier_rows.ts","./src/components/add_model/complexity_router_tiers.ts","./src/components/add_model/keywordtierrules.tsx","./src/components/add_model/build_complexity_router_config.ts","./src/components/vector_store_management/types.tsx","./node_modules/@tanstack/table-core/build/lib/utils.d.ts","./node_modules/@tanstack/table-core/build/lib/core/table.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnvisibility.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnordering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/core/headers.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/filterfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/sortingfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowsorting.d.ts","./node_modules/@tanstack/table-core/build/lib/aggregationfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columngrouping.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowexpanding.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnsizing.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpagination.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowselection.d.ts","./node_modules/@tanstack/table-core/build/lib/core/row.d.ts","./node_modules/@tanstack/table-core/build/lib/core/cell.d.ts","./node_modules/@tanstack/table-core/build/lib/core/column.d.ts","./node_modules/@tanstack/table-core/build/lib/types.d.ts","./node_modules/@tanstack/table-core/build/lib/columnhelper.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getcorerowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getexpandedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedminmaxvalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfaceteduniquevalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfilteredrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getgroupedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getpaginationrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getsortedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/index.d.ts","./node_modules/@tanstack/react-table/build/lib/index.d.ts","./src/components/shared/datatable/types.ts","./src/components/shared/datatable/columnmeta.ts","./src/components/ui/skeleton.tsx","./src/components/ui/table.tsx","./src/components/shared/datatable/datatablepagination.tsx","./src/components/shared/datatable/datatable.tsx","./src/components/ui/sheet.tsx","./src/components/shared/datatable/datatablefilterdrawer.tsx","./src/components/ui/checkbox.tsx","./src/components/shared/datatable/datatableselectioncolumn.tsx","./src/components/shared/datatable/datatableviewoptions.tsx","./src/components/shared/datatable/datatabletoolbar.tsx","./src/components/shared/datatable/datatablesortheader.tsx","./src/components/shared/datatable/index.ts","./src/app/(dashboard)/hooks/models/usemodels.ts","./src/components/shared/table_cells/autoroutertag.tsx","./src/components/shared/table_cells/cell_tooltip.tsx","./src/components/shared/table_cells/date_cell.tsx","./src/utils/datautils.ts","./src/components/shared/table_cells/id_cell.tsx","./src/components/shared/entitylink.tsx","./src/components/shared/table_cells/identity_cell.tsx","./src/components/key_scope.ts","./src/components/shared/table_cells/models_cell.tsx","./src/components/shared/table_cells/money_cell.tsx","./src/components/shared/inheritedbudgethint.tsx","./src/components/shared/meter.tsx","./src/components/shared/table_cells/spend_budget_cell.tsx","./src/components/shared/table_cells/status_badge.tsx","./src/components/shared/table_cells/index.ts","./src/utils/migratedpages.ts","./src/utils/entitylinks.ts","./src/app/(dashboard)/vector-stores/_components/indexestablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.tsx","./src/app/(dashboard)/vector-stores/_components/indexestab.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.tsx","./src/lib/http/resolveapibase.ts","./src/lib/http/runtime.ts","./src/lib/serverrootpath.ts","./src/components/networking.tsx","./src/app/(dashboard)/networking.ts","./src/app/(dashboard)/access-groups/_components/types.ts","./node_modules/vitest/dist/chunks/worker.d.uzwscv9x.d.ts","./node_modules/vitest/dist/chunks/global.d.mamajcmj.d.ts","./node_modules/vitest/dist/chunks/mocker.d.be_2ls6u.d.ts","./node_modules/vitest/dist/chunks/suite.d.fvehnv49.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./node_modules/zod/v4/core/standard-schema.d.cts","./node_modules/zod/v4/core/util.d.cts","./node_modules/zod/v4/core/versions.d.cts","./node_modules/zod/v4/core/schemas.d.cts","./node_modules/zod/v4/core/checks.d.cts","./node_modules/zod/v4/core/errors.d.cts","./node_modules/zod/v4/core/core.d.cts","./node_modules/zod/v4/core/parse.d.cts","./node_modules/zod/v4/core/regexes.d.cts","./node_modules/zod/v4/locales/ar.d.cts","./node_modules/zod/v4/locales/az.d.cts","./node_modules/zod/v4/locales/be.d.cts","./node_modules/zod/v4/locales/ca.d.cts","./node_modules/zod/v4/locales/cs.d.cts","./node_modules/zod/v4/locales/de.d.cts","./node_modules/zod/v4/locales/en.d.cts","./node_modules/zod/v4/locales/eo.d.cts","./node_modules/zod/v4/locales/es.d.cts","./node_modules/zod/v4/locales/fa.d.cts","./node_modules/zod/v4/locales/fi.d.cts","./node_modules/zod/v4/locales/fr.d.cts","./node_modules/zod/v4/locales/fr-ca.d.cts","./node_modules/zod/v4/locales/he.d.cts","./node_modules/zod/v4/locales/hu.d.cts","./node_modules/zod/v4/locales/id.d.cts","./node_modules/zod/v4/locales/it.d.cts","./node_modules/zod/v4/locales/ja.d.cts","./node_modules/zod/v4/locales/kh.d.cts","./node_modules/zod/v4/locales/ko.d.cts","./node_modules/zod/v4/locales/mk.d.cts","./node_modules/zod/v4/locales/ms.d.cts","./node_modules/zod/v4/locales/nl.d.cts","./node_modules/zod/v4/locales/no.d.cts","./node_modules/zod/v4/locales/ota.d.cts","./node_modules/zod/v4/locales/ps.d.cts","./node_modules/zod/v4/locales/pl.d.cts","./node_modules/zod/v4/locales/pt.d.cts","./node_modules/zod/v4/locales/ru.d.cts","./node_modules/zod/v4/locales/sl.d.cts","./node_modules/zod/v4/locales/sv.d.cts","./node_modules/zod/v4/locales/ta.d.cts","./node_modules/zod/v4/locales/th.d.cts","./node_modules/zod/v4/locales/tr.d.cts","./node_modules/zod/v4/locales/ua.d.cts","./node_modules/zod/v4/locales/ur.d.cts","./node_modules/zod/v4/locales/vi.d.cts","./node_modules/zod/v4/locales/zh-cn.d.cts","./node_modules/zod/v4/locales/zh-tw.d.cts","./node_modules/zod/v4/locales/index.d.cts","./node_modules/zod/v4/core/registries.d.cts","./node_modules/zod/v4/core/doc.d.cts","./node_modules/zod/v4/core/function.d.cts","./node_modules/zod/v4/core/api.d.cts","./node_modules/zod/v4/core/json-schema.d.cts","./node_modules/zod/v4/core/to-json-schema.d.cts","./node_modules/zod/v4/core/index.d.cts","./node_modules/zod/v4/classic/errors.d.cts","./node_modules/zod/v4/classic/parse.d.cts","./node_modules/zod/v4/classic/schemas.d.cts","./node_modules/zod/v4/classic/checks.d.cts","./node_modules/zod/v4/classic/compat.d.cts","./node_modules/zod/v4/classic/iso.d.cts","./node_modules/zod/v4/classic/coerce.d.cts","./node_modules/zod/v4/classic/external.d.cts","./node_modules/zod/v4/classic/index.d.cts","./node_modules/zod/v4/index.d.cts","./src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.test.ts","./src/app/(dashboard)/agents/_components/agent_config.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.test.ts","./src/components/agents/types.ts","./src/app/(dashboard)/agents/_components/agent_type_utils.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.test.ts","./src/app/(dashboard)/budgets/_components/constants.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsfields.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.test.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfields.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.test.ts","./src/components/usagepage/types.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.test.ts","./node_modules/openapi-typescript-helpers/dist/index.d.mts","./node_modules/openapi-fetch/dist/index.d.mts","./node_modules/openapi-react-query/dist/index.d.mts","./src/lib/http/api.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.ts","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.test.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.test.ts","./src/components/ui/alert-dialog.tsx","./src/components/ui/tabs.tsx","./src/app/(dashboard)/cost-tracking/_components/types.ts","./src/components/common_components/simple_table.tsx","./src/lib/assetpaths.ts","./src/components/provider_info_helpers.tsx","./src/components/molecules/logo/logo.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/types.ts","./src/hooks/use-safe-layout-effect.ts","./src/components/ui/ui-loading-spinner.tsx","./src/components/ui/dropdown-menu.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.tsx","./src/components/helplink.tsx","./node_modules/@types/react-syntax-highlighter/index.d.ts","./node_modules/next-themes/dist/index.d.ts","./src/hooks/usesyntaxtheme.ts","./src/components/codeblock.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.ts","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.tsx","./src/app/(dashboard)/cost-tracking/_components/index.ts","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.test.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@types/react-dom/test-utils/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.test.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_configs.ts","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.test.ts","./src/app/(dashboard)/guardrails/_components/content_filter/action_options.ts","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/index.ts","./src/app/(dashboard)/hooks/useauthorized.serverrootpath.test.ts","./src/app/(dashboard)/hooks/useauthorized.test.ts","./src/utils/capabilities.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.ts","./src/app/(dashboard)/hooks/useisorgadmin.ts","./src/app/(dashboard)/hooks/usecan.ts","./src/utils/localstorageutils.ts","./src/app/(dashboard)/hooks/usedisableblogposts.ts","./src/app/(dashboard)/hooks/usedisablebouncingicon.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.test.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.test.ts","./src/app/(dashboard)/hooks/usehideautorouterannouncement.ts","./src/app/(dashboard)/hooks/useisorgadmin.test.ts","./src/utils/proxyutils.ts","./src/app/(dashboard)/hooks/proxysettings/useproxysettings.ts","./src/app/(dashboard)/hooks/uselogout.ts","./src/utils/tabroutes.ts","./src/app/(dashboard)/hooks/usetabrouting.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroupdetails.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.test.ts","./src/app/(dashboard)/hooks/accessgroups/usedeleteaccessgroup.ts","./src/app/(dashboard)/hooks/accessgroups/useeditaccessgroup.ts","./src/app/(dashboard)/hooks/agents/useagents.ts","./src/app/(dashboard)/hooks/agents/useagents.test.ts","./src/app/(dashboard)/hooks/blogposts/useblogposts.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.test.ts","./node_modules/@tanstack/react-store/dist/createstorecontext.d.ts","./node_modules/@tanstack/store/dist/alien.d.ts","./node_modules/@tanstack/store/dist/types.d.ts","./node_modules/@tanstack/store/dist/atom.d.ts","./node_modules/@tanstack/store/dist/store.d.ts","./node_modules/@tanstack/store/dist/shallow.d.ts","./node_modules/@tanstack/store/dist/index.d.ts","./node_modules/@tanstack/react-store/dist/usecreateatom.d.ts","./node_modules/@tanstack/react-store/dist/usecreatestore.d.ts","./node_modules/@tanstack/react-store/dist/useselector.d.ts","./node_modules/@tanstack/react-store/dist/useatom.d.ts","./node_modules/@tanstack/react-store/dist/_usestore.d.ts","./node_modules/@tanstack/react-store/dist/usestore.d.ts","./node_modules/@tanstack/react-store/dist/index.d.ts","./node_modules/@tanstack/pacer/dist/types.d.ts","./node_modules/@tanstack/pacer/dist/debouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedcallback.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedstate.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedvalue.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/index.d.ts","./src/utils/debounceconstants.ts","./src/app/(dashboard)/hooks/common/useresourcelist.ts","./src/app/(dashboard)/hooks/budgets/usebudgets.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.test.ts","./src/components/cloudzerocosttracking/types.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.test.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.test.ts","./src/app/(dashboard)/hooks/configoverrides/hashicorpvaultapi.ts","./src/app/(dashboard)/hooks/configoverrides/usehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usedeletehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/useupdatehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/coordinationredis/usecoordinationredissettings.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.test.ts","./src/app/(dashboard)/hooks/customers/usecustomers.ts","./src/app/(dashboard)/hooks/customers/usecustomers.test.ts","./src/app/(dashboard)/hooks/guardrails/useguardrails.ts","./src/app/(dashboard)/hooks/guardrails/useguardrails.test.ts","./src/app/(dashboard)/hooks/guardrails/useregisterguardrail.ts","./src/app/(dashboard)/hooks/healthreadiness/usehealthreadinessdetails.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.test.ts","./src/app/(dashboard)/hooks/keys/usekeys.ts","./src/app/(dashboard)/hooks/keys/usekeyinfo.ts","./src/app/(dashboard)/hooks/keys/usekeys.test.ts","./src/app/(dashboard)/hooks/keys/useresetkeyspend.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.test.ts","./src/app/(dashboard)/hooks/license/uselicenseinfo.ts","./src/app/(dashboard)/hooks/logdetails/uselogdetails.ts","./src/app/(dashboard)/hooks/login/uselogin.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/usemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/useupdatemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcptoolsets.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.test.ts","./src/app/(dashboard)/hooks/models/usemodels.test.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.test.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.test.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.test.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.test.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.test.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.test.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.test.ts","./src/app/(dashboard)/hooks/router/userouterfields.ts","./src/app/(dashboard)/hooks/router/userouterfields.test.ts","./src/app/(dashboard)/hooks/routersettings/useupdateretrypolicy.ts","./src/components/routing_groups/types.ts","./src/app/(dashboard)/hooks/routinggroups/useroutinggroups.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.test.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.test.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.test.ts","./src/app/(dashboard)/hooks/sso/usessosettings.ts","./src/app/(dashboard)/hooks/sso/usessosettings.test.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.test.ts","./src/app/(dashboard)/hooks/storerequestinspendlogs/usestorerequestinspendlogs.ts","./src/app/(dashboard)/hooks/tags/usetags.ts","./src/app/(dashboard)/hooks/tags/usetags.test.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.test.ts","./src/app/(dashboard)/hooks/teams/useteams.ts","./src/app/(dashboard)/hooks/teams/useteams.test.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.test.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.test.ts","./src/app/(dashboard)/hooks/userbanner/useuserbanner.ts","./src/app/(dashboard)/hooks/userbanner/useupdateuserbanner.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.test.ts","./src/app/(dashboard)/hooks/users/useusers.ts","./src/app/(dashboard)/hooks/users/useusers.test.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.test.ts","./src/app/(dashboard)/mcp-servers/_components/utils.tsx","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.test.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.cases.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.test.ts","./node_modules/@testing-library/user-event/dist/types/event/eventmap.d.ts","./node_modules/@testing-library/user-event/dist/types/event/types.d.ts","./node_modules/@testing-library/user-event/dist/types/event/dispatchevent.d.ts","./node_modules/@testing-library/user-event/dist/types/event/focus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/input.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/click/isclickableinput.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/blob.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/datatransfer.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/filelist.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/clipboard.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/timevalue.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iscontenteditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iseditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/maxlength.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/setfiles.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/cursor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/getactiveelement.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/gettabdestination.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/isfocusable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selection.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selector.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/keydef/readnextdescriptor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/cloneevent.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/findclosest.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getdocumentfromnode.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/gettreediff.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getwindow.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdescendantorself.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/iselementtype.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isvisible.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdisabled.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/level.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/wait.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/pointer/csspointerevents.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/index.d.ts","./node_modules/@testing-library/user-event/dist/types/document/ui.d.ts","./node_modules/@testing-library/user-event/dist/types/document/getvalueortextcontent.d.ts","./node_modules/@testing-library/user-event/dist/types/document/copyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/document/trackvalue.d.ts","./node_modules/@testing-library/user-event/dist/types/document/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/getinputrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/moveselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/selectall.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/updateselectiononfocus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/buttons.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/shared.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/keyboard.d.ts","./node_modules/@testing-library/user-event/dist/types/options.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/click.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/hover.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/tab.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/index.d.ts","./node_modules/@testing-library/user-event/dist/types/keyboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/copy.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/cut.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/paste.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/clear.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/selectoptions.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/type.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/upload.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/index.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/api.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/directapi.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/setup.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/index.d.ts","./node_modules/@testing-library/user-event/dist/types/index.d.ts","./src/app/(dashboard)/mcp-servers/_components/testutils.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.test.ts","./node_modules/nuqs/dist/defs-butbdnwx.d.ts","./node_modules/nuqs/dist/context-3xask51n.d.ts","./node_modules/nuqs/dist/adapters/testing.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/nuqs/dist/index.d.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.test.ts","./src/app/(dashboard)/models-and-endpoints/usemodeldashboarddata.ts","./src/components/add_model/auto_router_strategies.ts","./src/utils/modelpermissions.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.test.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.test.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.test.ts","./src/components/chat_ui/mode_endpoint_mapping.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatconstants.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.test.ts","./src/app/(dashboard)/playground/llm_calls/fetch_agents.tsx","./src/app/(dashboard)/playground/components/compareui/endpoint_config.ts","./src/app/(dashboard)/playground/components/compareui/endpoint_config.test.ts","./src/utils/promptcacheusage.ts","./src/components/chat_ui/responsemetrics.tsx","./src/components/chat_ui/types.ts","./src/app/(dashboard)/playground/hooks/usechathistory.ts","./src/app/(dashboard)/playground/hooks/usechathistory.test.ts","./src/components/llm_calls/code_interpreter_handler.ts","./src/app/(dashboard)/playground/hooks/usecodeinterpreter.ts","./src/components/policies/types.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.test.ts","./src/app/(dashboard)/policies/_components/scope_validation.ts","./src/app/(dashboard)/policies/_components/scope_validation.test.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformschema.ts","./src/components/shared/usepaginatedcombobox.ts","./src/components/shared/paginatedsearchselect.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.tsx","./src/components/agent_management/agentselector.tsx","./src/components/common_components/accessgroupselector.tsx","./src/components/common_components/budget_duration_dropdown.tsx","./src/components/common_components/keylifecyclesettings.tsx","./node_modules/@heroicons/react/outline/academiccapicon.d.ts","./node_modules/@heroicons/react/outline/adjustmentsicon.d.ts","./node_modules/@heroicons/react/outline/annotationicon.d.ts","./node_modules/@heroicons/react/outline/archiveicon.d.ts","./node_modules/@heroicons/react/outline/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/outline/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/outline/arrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmupicon.d.ts","./node_modules/@heroicons/react/outline/arrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/outline/atsymbolicon.d.ts","./node_modules/@heroicons/react/outline/backspaceicon.d.ts","./node_modules/@heroicons/react/outline/badgecheckicon.d.ts","./node_modules/@heroicons/react/outline/banicon.d.ts","./node_modules/@heroicons/react/outline/beakericon.d.ts","./node_modules/@heroicons/react/outline/bellicon.d.ts","./node_modules/@heroicons/react/outline/bookopenicon.d.ts","./node_modules/@heroicons/react/outline/bookmarkalticon.d.ts","./node_modules/@heroicons/react/outline/bookmarkicon.d.ts","./node_modules/@heroicons/react/outline/briefcaseicon.d.ts","./node_modules/@heroicons/react/outline/cakeicon.d.ts","./node_modules/@heroicons/react/outline/calculatoricon.d.ts","./node_modules/@heroicons/react/outline/calendaricon.d.ts","./node_modules/@heroicons/react/outline/cameraicon.d.ts","./node_modules/@heroicons/react/outline/cashicon.d.ts","./node_modules/@heroicons/react/outline/chartbaricon.d.ts","./node_modules/@heroicons/react/outline/chartpieicon.d.ts","./node_modules/@heroicons/react/outline/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/outline/chatalt2icon.d.ts","./node_modules/@heroicons/react/outline/chatalticon.d.ts","./node_modules/@heroicons/react/outline/chaticon.d.ts","./node_modules/@heroicons/react/outline/checkcircleicon.d.ts","./node_modules/@heroicons/react/outline/checkicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/outline/chevrondownicon.d.ts","./node_modules/@heroicons/react/outline/chevronlefticon.d.ts","./node_modules/@heroicons/react/outline/chevronrighticon.d.ts","./node_modules/@heroicons/react/outline/chevronupicon.d.ts","./node_modules/@heroicons/react/outline/chipicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/outline/clipboardlisticon.d.ts","./node_modules/@heroicons/react/outline/clipboardicon.d.ts","./node_modules/@heroicons/react/outline/clockicon.d.ts","./node_modules/@heroicons/react/outline/clouddownloadicon.d.ts","./node_modules/@heroicons/react/outline/clouduploadicon.d.ts","./node_modules/@heroicons/react/outline/cloudicon.d.ts","./node_modules/@heroicons/react/outline/codeicon.d.ts","./node_modules/@heroicons/react/outline/cogicon.d.ts","./node_modules/@heroicons/react/outline/collectionicon.d.ts","./node_modules/@heroicons/react/outline/colorswatchicon.d.ts","./node_modules/@heroicons/react/outline/creditcardicon.d.ts","./node_modules/@heroicons/react/outline/cubetransparenticon.d.ts","./node_modules/@heroicons/react/outline/cubeicon.d.ts","./node_modules/@heroicons/react/outline/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/outline/currencydollaricon.d.ts","./node_modules/@heroicons/react/outline/currencyeuroicon.d.ts","./node_modules/@heroicons/react/outline/currencypoundicon.d.ts","./node_modules/@heroicons/react/outline/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/outline/currencyyenicon.d.ts","./node_modules/@heroicons/react/outline/cursorclickicon.d.ts","./node_modules/@heroicons/react/outline/databaseicon.d.ts","./node_modules/@heroicons/react/outline/desktopcomputericon.d.ts","./node_modules/@heroicons/react/outline/devicemobileicon.d.ts","./node_modules/@heroicons/react/outline/devicetableticon.d.ts","./node_modules/@heroicons/react/outline/documentaddicon.d.ts","./node_modules/@heroicons/react/outline/documentdownloadicon.d.ts","./node_modules/@heroicons/react/outline/documentduplicateicon.d.ts","./node_modules/@heroicons/react/outline/documentremoveicon.d.ts","./node_modules/@heroicons/react/outline/documentreporticon.d.ts","./node_modules/@heroicons/react/outline/documentsearchicon.d.ts","./node_modules/@heroicons/react/outline/documenttexticon.d.ts","./node_modules/@heroicons/react/outline/documenticon.d.ts","./node_modules/@heroicons/react/outline/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotsverticalicon.d.ts","./node_modules/@heroicons/react/outline/downloadicon.d.ts","./node_modules/@heroicons/react/outline/duplicateicon.d.ts","./node_modules/@heroicons/react/outline/emojihappyicon.d.ts","./node_modules/@heroicons/react/outline/emojisadicon.d.ts","./node_modules/@heroicons/react/outline/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/outline/exclamationicon.d.ts","./node_modules/@heroicons/react/outline/externallinkicon.d.ts","./node_modules/@heroicons/react/outline/eyeofficon.d.ts","./node_modules/@heroicons/react/outline/eyeicon.d.ts","./node_modules/@heroicons/react/outline/fastforwardicon.d.ts","./node_modules/@heroicons/react/outline/filmicon.d.ts","./node_modules/@heroicons/react/outline/filtericon.d.ts","./node_modules/@heroicons/react/outline/fingerprinticon.d.ts","./node_modules/@heroicons/react/outline/fireicon.d.ts","./node_modules/@heroicons/react/outline/flagicon.d.ts","./node_modules/@heroicons/react/outline/folderaddicon.d.ts","./node_modules/@heroicons/react/outline/folderdownloadicon.d.ts","./node_modules/@heroicons/react/outline/folderopenicon.d.ts","./node_modules/@heroicons/react/outline/folderremoveicon.d.ts","./node_modules/@heroicons/react/outline/foldericon.d.ts","./node_modules/@heroicons/react/outline/gifticon.d.ts","./node_modules/@heroicons/react/outline/globealticon.d.ts","./node_modules/@heroicons/react/outline/globeicon.d.ts","./node_modules/@heroicons/react/outline/handicon.d.ts","./node_modules/@heroicons/react/outline/hashtagicon.d.ts","./node_modules/@heroicons/react/outline/hearticon.d.ts","./node_modules/@heroicons/react/outline/homeicon.d.ts","./node_modules/@heroicons/react/outline/identificationicon.d.ts","./node_modules/@heroicons/react/outline/inboxinicon.d.ts","./node_modules/@heroicons/react/outline/inboxicon.d.ts","./node_modules/@heroicons/react/outline/informationcircleicon.d.ts","./node_modules/@heroicons/react/outline/keyicon.d.ts","./node_modules/@heroicons/react/outline/libraryicon.d.ts","./node_modules/@heroicons/react/outline/lightbulbicon.d.ts","./node_modules/@heroicons/react/outline/lightningbolticon.d.ts","./node_modules/@heroicons/react/outline/linkicon.d.ts","./node_modules/@heroicons/react/outline/locationmarkericon.d.ts","./node_modules/@heroicons/react/outline/lockclosedicon.d.ts","./node_modules/@heroicons/react/outline/lockopenicon.d.ts","./node_modules/@heroicons/react/outline/loginicon.d.ts","./node_modules/@heroicons/react/outline/logouticon.d.ts","./node_modules/@heroicons/react/outline/mailopenicon.d.ts","./node_modules/@heroicons/react/outline/mailicon.d.ts","./node_modules/@heroicons/react/outline/mapicon.d.ts","./node_modules/@heroicons/react/outline/menualt1icon.d.ts","./node_modules/@heroicons/react/outline/menualt2icon.d.ts","./node_modules/@heroicons/react/outline/menualt3icon.d.ts","./node_modules/@heroicons/react/outline/menualt4icon.d.ts","./node_modules/@heroicons/react/outline/menuicon.d.ts","./node_modules/@heroicons/react/outline/microphoneicon.d.ts","./node_modules/@heroicons/react/outline/minuscircleicon.d.ts","./node_modules/@heroicons/react/outline/minussmicon.d.ts","./node_modules/@heroicons/react/outline/minusicon.d.ts","./node_modules/@heroicons/react/outline/moonicon.d.ts","./node_modules/@heroicons/react/outline/musicnoteicon.d.ts","./node_modules/@heroicons/react/outline/newspapericon.d.ts","./node_modules/@heroicons/react/outline/officebuildingicon.d.ts","./node_modules/@heroicons/react/outline/paperairplaneicon.d.ts","./node_modules/@heroicons/react/outline/paperclipicon.d.ts","./node_modules/@heroicons/react/outline/pauseicon.d.ts","./node_modules/@heroicons/react/outline/pencilalticon.d.ts","./node_modules/@heroicons/react/outline/pencilicon.d.ts","./node_modules/@heroicons/react/outline/phoneincomingicon.d.ts","./node_modules/@heroicons/react/outline/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/outline/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/outline/phoneicon.d.ts","./node_modules/@heroicons/react/outline/photographicon.d.ts","./node_modules/@heroicons/react/outline/playicon.d.ts","./node_modules/@heroicons/react/outline/pluscircleicon.d.ts","./node_modules/@heroicons/react/outline/plussmicon.d.ts","./node_modules/@heroicons/react/outline/plusicon.d.ts","./node_modules/@heroicons/react/outline/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/outline/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/outline/printericon.d.ts","./node_modules/@heroicons/react/outline/puzzleicon.d.ts","./node_modules/@heroicons/react/outline/qrcodeicon.d.ts","./node_modules/@heroicons/react/outline/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/outline/receiptrefundicon.d.ts","./node_modules/@heroicons/react/outline/receipttaxicon.d.ts","./node_modules/@heroicons/react/outline/refreshicon.d.ts","./node_modules/@heroicons/react/outline/replyicon.d.ts","./node_modules/@heroicons/react/outline/rewindicon.d.ts","./node_modules/@heroicons/react/outline/rssicon.d.ts","./node_modules/@heroicons/react/outline/saveasicon.d.ts","./node_modules/@heroicons/react/outline/saveicon.d.ts","./node_modules/@heroicons/react/outline/scaleicon.d.ts","./node_modules/@heroicons/react/outline/scissorsicon.d.ts","./node_modules/@heroicons/react/outline/searchcircleicon.d.ts","./node_modules/@heroicons/react/outline/searchicon.d.ts","./node_modules/@heroicons/react/outline/selectoricon.d.ts","./node_modules/@heroicons/react/outline/servericon.d.ts","./node_modules/@heroicons/react/outline/shareicon.d.ts","./node_modules/@heroicons/react/outline/shieldcheckicon.d.ts","./node_modules/@heroicons/react/outline/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/outline/shoppingbagicon.d.ts","./node_modules/@heroicons/react/outline/shoppingcarticon.d.ts","./node_modules/@heroicons/react/outline/sortascendingicon.d.ts","./node_modules/@heroicons/react/outline/sortdescendingicon.d.ts","./node_modules/@heroicons/react/outline/sparklesicon.d.ts","./node_modules/@heroicons/react/outline/speakerphoneicon.d.ts","./node_modules/@heroicons/react/outline/staricon.d.ts","./node_modules/@heroicons/react/outline/statusofflineicon.d.ts","./node_modules/@heroicons/react/outline/statusonlineicon.d.ts","./node_modules/@heroicons/react/outline/stopicon.d.ts","./node_modules/@heroicons/react/outline/sunicon.d.ts","./node_modules/@heroicons/react/outline/supporticon.d.ts","./node_modules/@heroicons/react/outline/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/outline/switchverticalicon.d.ts","./node_modules/@heroicons/react/outline/tableicon.d.ts","./node_modules/@heroicons/react/outline/tagicon.d.ts","./node_modules/@heroicons/react/outline/templateicon.d.ts","./node_modules/@heroicons/react/outline/terminalicon.d.ts","./node_modules/@heroicons/react/outline/thumbdownicon.d.ts","./node_modules/@heroicons/react/outline/thumbupicon.d.ts","./node_modules/@heroicons/react/outline/ticketicon.d.ts","./node_modules/@heroicons/react/outline/translateicon.d.ts","./node_modules/@heroicons/react/outline/trashicon.d.ts","./node_modules/@heroicons/react/outline/trendingdownicon.d.ts","./node_modules/@heroicons/react/outline/trendingupicon.d.ts","./node_modules/@heroicons/react/outline/truckicon.d.ts","./node_modules/@heroicons/react/outline/uploadicon.d.ts","./node_modules/@heroicons/react/outline/useraddicon.d.ts","./node_modules/@heroicons/react/outline/usercircleicon.d.ts","./node_modules/@heroicons/react/outline/usergroupicon.d.ts","./node_modules/@heroicons/react/outline/userremoveicon.d.ts","./node_modules/@heroicons/react/outline/usericon.d.ts","./node_modules/@heroicons/react/outline/usersicon.d.ts","./node_modules/@heroicons/react/outline/variableicon.d.ts","./node_modules/@heroicons/react/outline/videocameraicon.d.ts","./node_modules/@heroicons/react/outline/viewboardsicon.d.ts","./node_modules/@heroicons/react/outline/viewgridaddicon.d.ts","./node_modules/@heroicons/react/outline/viewgridicon.d.ts","./node_modules/@heroicons/react/outline/viewlisticon.d.ts","./node_modules/@heroicons/react/outline/volumeofficon.d.ts","./node_modules/@heroicons/react/outline/volumeupicon.d.ts","./node_modules/@heroicons/react/outline/wifiicon.d.ts","./node_modules/@heroicons/react/outline/xcircleicon.d.ts","./node_modules/@heroicons/react/outline/xicon.d.ts","./node_modules/@heroicons/react/outline/zoominicon.d.ts","./node_modules/@heroicons/react/outline/zoomouticon.d.ts","./node_modules/@heroicons/react/outline/index.d.ts","./src/components/common_components/modelselector.tsx","./src/components/common_components/modelaliasmanager.tsx","./src/components/common_components/passthroughroutesselector.tsx","./src/components/callback_info_helpers.tsx","./src/components/shared/numerical_input.tsx","./src/components/team/loggingsettings.tsx","./src/components/common_components/premiumloggingsettings.tsx","./src/components/common_components/ratelimittypeformitem.tsx","./src/components/router_settings/latencybasedconfiguration.tsx","./src/components/router_settings/reliabilityretriessection.tsx","./src/components/router_settings/routingstrategyselector.tsx","./src/components/router_settings/tagfilteringtoggle.tsx","./src/components/router_settings/routersettingsform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.tsx","./src/components/settings/routersettings/fallbacks/fallbackgroupconfig.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.tsx","./src/components/common_components/routersettingsaccordion.tsx","./src/components/common_components/team_dropdown.tsx","./src/components/common_components/organizationdropdown.tsx","./src/components/common_components/projectdropdown.tsx","./src/components/shared/form/formfield.tsx","./src/components/ui/alert.tsx","./src/components/shared/alert.tsx","./node_modules/@types/react-copy-to-clipboard/index.d.ts","./src/components/onboarding_link.tsx","./src/components/createuserbutton.tsx","./src/components/key_team_helpers/budgetfallbackseditor.tsx","./src/components/key_team_helpers/budgetwindowseditor.tsx","./src/components/key_team_helpers/tagratelimiteditor.tsx","./src/components/mcp_server_management/mcpserverselector.tsx","./src/utils/mcptoolcrudclassification.ts","./src/components/mcp_tools/mcpcrudpermissionpanel.tsx","./src/components/mcp_server_management/mcptoolpermissions.tsx","./src/components/shared/createdkeydisplay.tsx","./src/components/vector_store_management/vectorstoreselector.tsx","./src/components/organisms/createkeypayload.ts","./src/components/organisms/utils.ts","./src/components/organisms/create_key_button.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/useconversation.ts","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.ts","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.test.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.test.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusageaggregations.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.test.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.test.ts","./src/components/key_scope.test.ts","./src/components/networking.test.ts","./src/components/page_metadata.ts","./src/contexts/themecontext.tsx","./src/components/ui/scroll-area.tsx","./src/components/shared/sidebar.tsx","./src/components/betabadge.tsx","./src/components/navbar/navdisplayname.ts","./src/components/shared/copybutton.tsx","./src/components/ui/avatar.tsx","./src/components/ui/popover.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.tsx","./src/utils/licenseutils.ts","./src/components/sidebarusagecard.tsx","./src/components/leftnav.tsx","./src/components/page_utils.ts","./src/components/page_utils.test.ts","./src/components/cloudzerocosttracking/cloudzeropayload.ts","./src/components/cloudzerocosttracking/cloudzeropayload.test.ts","./src/utils/teamutils.ts","./src/components/shared/date_picker_types.ts","./src/components/entityusageexport/types.ts","./src/components/entityusageexport/exportformatselector.tsx","./src/components/entityusageexport/exportsummary.tsx","./src/components/entityusageexport/exporttypeselector.tsx","./node_modules/@types/papaparse/index.d.ts","./src/components/entityusageexport/utils.ts","./src/components/entityusageexport/entityusageexportmodal.tsx","./src/components/entityusageexport/usageexportheader.tsx","./src/components/entityusageexport/index.ts","./src/components/entityusageexport/utils.test.ts","./src/components/guardrailsmonitor/mockdata.ts","./src/components/modelselect/modelutils.ts","./src/components/modelselect/modelutils.test.ts","./src/components/navbar/navdisplayname.test.ts","./src/components/navbar/navproductlinkclass.ts","./src/components/settings/adminsettings/hashicorpvault/constants.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.test.ts","./src/components/settings/adminsettings/pluginsettings/schema.ts","./src/components/settings/adminsettings/ssosettings/constants.ts","./src/components/settings/adminsettings/ssosettings/utils.ts","./src/components/settings/adminsettings/ssosettings/utils.test.ts","./src/components/settings/loggingandalerts/loggingcallbacks/types.ts","./src/components/teamspage/teamscsvexport.ts","./src/components/teamspage/teamscsvexport.test.ts","./src/components/toolpolicies/toolpoliciesqueries.ts","./src/components/usagepage/utils/value_formatters.tsx","./src/components/usagepage/utils/value_formatters.test.ts","./src/components/add_model/build_auto_router_routing_test_request.ts","./src/components/add_model/build_auto_router_routing_test_request.test.ts","./src/components/add_model/build_auto_router_test_targets.ts","./src/components/add_model/build_auto_router_test_targets.test.ts","./src/components/add_model/build_complexity_router_config.test.ts","./src/components/add_model/classifierprompteditorstate.test.ts","./src/components/add_model/complexity_router_keywords.test.ts","./src/components/add_model/complexity_router_tiers.test.ts","./src/components/add_model/heuristic_scoring_knobs.test.ts","./src/components/add_model/tier_rows.test.ts","./src/components/chat/types.ts","./src/components/chat/usechathistory.ts","./src/contexts/chatshellcontext.tsx","./node_modules/dayjs/locale/types.d.ts","./node_modules/dayjs/locale/index.d.ts","./node_modules/dayjs/index.d.ts","./src/components/chat/conversationlist.tsx","./src/components/chat/chatshell.tsx","./src/components/chat/chatshell.serverrootpath.test.ts","./src/components/chat/usechathistory.test.ts","./src/components/claude_code_plugins/helpers.ts","./src/components/claude_code_plugins/helpers.test.ts","./src/components/common_components/formrules.ts","./src/components/common_components/routersettingspayload.ts","./src/components/common_components/routersettingspayload.test.ts","./node_modules/zod/v3/helpers/typealiases.d.cts","./node_modules/zod/v3/helpers/util.d.cts","./node_modules/zod/v3/zoderror.d.cts","./node_modules/zod/v3/locales/en.d.cts","./node_modules/zod/v3/errors.d.cts","./node_modules/zod/v3/helpers/parseutil.d.cts","./node_modules/zod/v3/helpers/enumutil.d.cts","./node_modules/zod/v3/helpers/errorutil.d.cts","./node_modules/zod/v3/helpers/partialutil.d.cts","./node_modules/zod/v3/standard-schema.d.cts","./node_modules/zod/v3/types.d.cts","./node_modules/zod/v3/external.d.cts","./node_modules/zod/v3/index.d.cts","./node_modules/@hookform/resolvers/zod/dist/zod.d.ts","./node_modules/@hookform/resolvers/zod/dist/index.d.ts","./src/lib/forms/usezodform.ts","./src/components/add_model/accessgrouptagscombobox.tsx","./src/components/add_model/modelchoicecombobox.tsx","./src/components/add_model/routerconfigbuilder.tsx","./src/components/edit_auto_router/edit_auto_router_modal.tsx","./src/components/edit_auto_router/build_updated_complexity_router_config.test.ts","./src/components/edit_auto_router/edit_auto_router_modal.test.ts","./src/components/email_events/email_event_settings.tsx","./src/components/email_events/index.ts","./src/components/guardrails/types.ts","./src/components/key_team_helpers/modelmaxbudgeteditor.test.ts","./src/components/key_team_helpers/filter_helpers.ts","./src/components/key_team_helpers/filter_helpers.test.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.test.ts","./src/components/key_team_helpers/transform_key_info.tsx","./src/components/key_team_helpers/transform_key_info.test.ts","./src/components/key_team_helpers/useseededstate.ts","./src/components/key_team_helpers/usemodelmaxbudgetfield.ts","./src/components/llm_calls/mcp_tool_blocks.ts","./src/components/llm_calls/mcp_tool_blocks.test.ts","./src/components/mcp_server_management/mcpentitlement.ts","./src/components/model_add/credential_form_helpers.ts","./src/components/model_add/credential_form_helpers.test.ts","./src/components/model_dashboard/types.ts","./src/components/organisms/createkeypayload.test.ts","./src/components/organisms/regeneratekeypayload.ts","./src/components/organisms/regeneratekeypayload.test.ts","./src/components/organisms/utils.test.ts","./src/components/organization/org-settings/schema.ts","./src/components/organization/org-create/mapper.ts","./src/components/organization/org-create/mapper.test.ts","./src/components/organization/org-settings/mapper.ts","./src/components/organization/org-settings/mapper.test.ts","./src/components/routing_groups/routinggrouppayload.ts","./src/components/routing_groups/routinggrouppayload.test.ts","./src/components/routing_groups/strategy.ts","./src/components/shared/charts/colors.ts","./node_modules/@types/d3-time/index.d.ts","./node_modules/@types/d3-scale/index.d.ts","./node_modules/victory-vendor/d3-scale.d.ts","./node_modules/recharts/types/shape/dot.d.ts","./node_modules/recharts/types/component/text.d.ts","./node_modules/recharts/types/zindex/zindexlayer.d.ts","./node_modules/recharts/types/cartesian/getcartesianposition.d.ts","./node_modules/recharts/types/component/label.d.ts","./node_modules/recharts/types/cartesian/cartesianaxis.d.ts","./node_modules/recharts/types/util/scale/customscaledefinition.d.ts","./node_modules/redux/dist/redux.d.ts","./node_modules/immer/dist/immer.d.ts","./node_modules/redux-thunk/dist/redux-thunk.d.ts","./node_modules/@reduxjs/toolkit/dist/uncheckedindexed.ts","./node_modules/@reduxjs/toolkit/dist/index.d.mts","./node_modules/recharts/types/state/cartesianaxisslice.d.ts","./node_modules/recharts/types/synchronisation/types.d.ts","./node_modules/recharts/types/chart/types.d.ts","./node_modules/recharts/types/component/defaulttooltipcontent.d.ts","./node_modules/recharts/types/context/brushupdatecontext.d.ts","./node_modules/recharts/types/state/chartdataslice.d.ts","./node_modules/recharts/types/state/types/linesettings.d.ts","./node_modules/recharts/types/state/types/scattersettings.d.ts","./node_modules/@types/d3-path/index.d.ts","./node_modules/@types/d3-shape/index.d.ts","./node_modules/victory-vendor/d3-shape.d.ts","./node_modules/recharts/types/shape/curve.d.ts","./node_modules/recharts/types/component/labellist.d.ts","./node_modules/recharts/types/component/defaultlegendcontent.d.ts","./node_modules/recharts/types/util/payload/getuniqpayload.d.ts","./node_modules/recharts/types/util/useelementoffset.d.ts","./node_modules/recharts/types/component/legend.d.ts","./node_modules/recharts/types/state/legendslice.d.ts","./node_modules/recharts/types/state/types/stackedgraphicalitem.d.ts","./node_modules/recharts/types/util/stacks/stacktypes.d.ts","./node_modules/recharts/types/util/scale/rechartsscale.d.ts","./node_modules/recharts/types/util/chartutils.d.ts","./node_modules/recharts/types/state/selectors/areaselectors.d.ts","./node_modules/recharts/types/animation/easing.d.ts","./node_modules/recharts/types/animation/matchby.d.ts","./node_modules/recharts/types/animation/animateditems.d.ts","./node_modules/recharts/types/cartesian/arearevealshape.d.ts","./node_modules/recharts/types/cartesian/area.d.ts","./node_modules/recharts/types/state/types/areasettings.d.ts","./node_modules/recharts/types/shape/rectangle.d.ts","./node_modules/recharts/types/cartesian/bar.d.ts","./node_modules/recharts/types/util/barutils.d.ts","./node_modules/recharts/types/state/types/barsettings.d.ts","./node_modules/recharts/types/state/types/radialbarsettings.d.ts","./node_modules/recharts/types/util/svgpropertiesnoevents.d.ts","./node_modules/recharts/types/util/useuniqueid.d.ts","./node_modules/recharts/types/state/types/piesettings.d.ts","./node_modules/recharts/types/state/types/radarsettings.d.ts","./node_modules/recharts/types/state/graphicalitemsslice.d.ts","./node_modules/recharts/types/state/tooltipslice.d.ts","./node_modules/recharts/types/state/optionsslice.d.ts","./node_modules/recharts/types/state/layoutslice.d.ts","./node_modules/recharts/types/util/ifoverflow.d.ts","./node_modules/recharts/types/util/resolvedefaultprops.d.ts","./node_modules/recharts/types/cartesian/referenceline.d.ts","./node_modules/recharts/types/state/referenceelementsslice.d.ts","./node_modules/recharts/types/state/brushslice.d.ts","./node_modules/recharts/types/state/rootpropsslice.d.ts","./node_modules/recharts/types/state/polaraxisslice.d.ts","./node_modules/recharts/types/state/polaroptionsslice.d.ts","./node_modules/recharts/types/cartesian/linedrawshape.d.ts","./node_modules/recharts/types/cartesian/line.d.ts","./node_modules/recharts/types/shape/symbols.d.ts","./node_modules/recharts/types/util/constants.d.ts","./node_modules/recharts/types/util/scatterutils.d.ts","./node_modules/recharts/types/cartesian/scatter.d.ts","./node_modules/recharts/types/cartesian/errorbar.d.ts","./node_modules/recharts/types/state/errorbarslice.d.ts","./node_modules/recharts/types/state/zindexslice.d.ts","./node_modules/recharts/types/state/eventsettingsslice.d.ts","./node_modules/recharts/types/state/renderedticksslice.d.ts","./node_modules/recharts/types/state/store.d.ts","./node_modules/recharts/types/cartesian/getticks.d.ts","./node_modules/recharts/types/cartesian/cartesiangrid.d.ts","./node_modules/recharts/types/state/selectors/combiners/combinedisplayedstackeddata.d.ts","./node_modules/recharts/types/state/selectors/selecttooltipaxistype.d.ts","./node_modules/recharts/types/types.d.ts","./node_modules/recharts/types/hooks.d.ts","./node_modules/recharts/types/state/selectors/axisselectors.d.ts","./node_modules/recharts/types/component/dots.d.ts","./node_modules/recharts/types/util/typeddatakey.d.ts","./node_modules/recharts/types/util/types.d.ts","./node_modules/recharts/types/container/surface.d.ts","./node_modules/recharts/types/container/layer.d.ts","./node_modules/recharts/types/component/cursor.d.ts","./node_modules/recharts/types/component/tooltip.d.ts","./node_modules/recharts/types/component/responsivecontainer.d.ts","./node_modules/recharts/types/component/cell.d.ts","./node_modules/recharts/types/component/customized.d.ts","./node_modules/recharts/types/shape/sector.d.ts","./node_modules/recharts/types/shape/polygon.d.ts","./node_modules/recharts/types/shape/cross.d.ts","./node_modules/recharts/types/polar/polargrid.d.ts","./node_modules/recharts/types/polar/defaultpolarradiusaxisprops.d.ts","./node_modules/recharts/types/polar/polarradiusaxis.d.ts","./node_modules/recharts/types/polar/defaultpolarangleaxisprops.d.ts","./node_modules/recharts/types/polar/polarangleaxis.d.ts","./node_modules/recharts/types/context/tooltipcontext.d.ts","./node_modules/recharts/types/polar/pie.d.ts","./node_modules/recharts/types/polar/radar.d.ts","./node_modules/recharts/types/util/radialbarutils.d.ts","./node_modules/recharts/types/polar/radialbar.d.ts","./node_modules/recharts/types/cartesian/brush.d.ts","./node_modules/recharts/types/cartesian/referencedot.d.ts","./node_modules/recharts/types/util/excludeeventprops.d.ts","./node_modules/recharts/types/util/svgpropertiesandevents.d.ts","./node_modules/recharts/types/cartesian/referencearea.d.ts","./node_modules/recharts/types/cartesian/barstack.d.ts","./node_modules/recharts/types/cartesian/xaxis.d.ts","./node_modules/recharts/types/cartesian/yaxis.d.ts","./node_modules/recharts/types/cartesian/zaxis.d.ts","./node_modules/recharts/types/chart/linechart.d.ts","./node_modules/recharts/types/chart/barchart.d.ts","./node_modules/recharts/types/chart/piechart.d.ts","./node_modules/recharts/types/chart/treemap.d.ts","./node_modules/recharts/types/chart/sankey.d.ts","./node_modules/recharts/types/chart/radarchart.d.ts","./node_modules/recharts/types/chart/scatterchart.d.ts","./node_modules/recharts/types/chart/areachart.d.ts","./node_modules/recharts/types/chart/radialbarchart.d.ts","./node_modules/recharts/types/chart/composedchart.d.ts","./node_modules/recharts/types/chart/sunburstchart.d.ts","./node_modules/recharts/types/shape/trapezoid.d.ts","./node_modules/recharts/types/cartesian/funnel.d.ts","./node_modules/recharts/types/chart/funnelchart.d.ts","./node_modules/recharts/types/util/global.d.ts","./node_modules/recharts/types/animation/animationhandle.d.ts","./node_modules/recharts/types/animation/timeoutcontroller.d.ts","./node_modules/recharts/types/animation/animationcontroller.d.ts","./node_modules/recharts/types/animation/useanimationcontroller.d.ts","./node_modules/recharts/types/zindex/defaultzindexes.d.ts","./node_modules/decimal.js-light/decimal.d.ts","./node_modules/recharts/types/util/scale/getnicetickvalues.d.ts","./node_modules/recharts/types/context/chartlayoutcontext.d.ts","./node_modules/recharts/types/util/getrelativecoordinate.d.ts","./node_modules/recharts/types/util/createcartesiancharts.d.ts","./node_modules/recharts/types/util/createpolarcharts.d.ts","./node_modules/recharts/types/util/datautils.d.ts","./node_modules/recharts/types/index.d.ts","./src/components/ui/chart.tsx","./src/components/shared/charts/chart_tooltip.tsx","./src/components/shared/charts/area_chart.tsx","./src/components/shared/charts/bar_chart.tsx","./src/components/shared/charts/chart_legend.tsx","./src/components/shared/charts/donut_chart.tsx","./src/components/shared/charts/line_chart.tsx","./src/components/shared/charts/index.ts","./src/components/team/memberformvalues.ts","./src/components/team/memberformvalues.test.ts","./src/components/team/tabvisibilityutils.ts","./src/components/team/tabvisibilityutils.test.ts","./src/components/team/teammodelaccess.ts","./src/components/team/teammodelaccess.test.ts","./src/components/team/usemyteammember.ts","./src/components/templates/estimatedoutputtokens.ts","./src/components/templates/estimatedoutputtokens.test.ts","./src/components/templates/keyeditfieldnormalizers.ts","./src/components/key_info_utils.tsx","./src/components/templates/keyeditformvalues.ts","./src/components/templates/keyeditformvalues.test.ts","./src/components/view_logs/constants.ts","./src/components/view_logs/logdetailrouting.ts","./src/components/view_logs/utils.ts","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.tsx","./src/components/view_logs/guardrailviewer/__tests__/fixtures.ts","./src/components/view_logs/logdetailsdrawer/constants.ts","./src/components/view_logs/columns.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.tsx","./node_modules/moment/ts3.1-typings/moment.d.ts","./src/components/view_logs/logdetailsdrawer/drawerheader.tsx","./src/components/view_logs/logdetailsdrawer/usekeyboardnavigation.ts","./src/components/view_logs/guardrailviewer/presidiodetectedentities.tsx","./src/components/view_logs/guardrailviewer/contentfilterdetails.tsx","./src/components/view_logs/guardrailviewer/compliancepanel.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.tsx","./src/components/view_logs/evalviewer/evalviewer.tsx","./src/components/view_logs/costbreakdownviewer.tsx","./src/components/view_logs/configinfomessage.tsx","./src/components/view_logs/vectorstoreviewer.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.tsx","./node_modules/react-json-view-lite/dist/datarenderer.d.ts","./node_modules/react-json-view-lite/dist/index.d.ts","./src/components/view_logs/logdetailsdrawer/jsonviewer.tsx","./src/components/view_logs/logdetailsdrawer/utils.ts","./src/components/view_logs/toolssection/types.ts","./src/components/view_logs/toolssection/utils.ts","./src/components/view_logs/toolssection/formattedtoolview.tsx","./src/components/view_logs/toolssection/jsontoolview.tsx","./src/components/view_logs/toolssection/toolexpandedcontent.tsx","./src/components/view_logs/toolssection/toolitem.tsx","./src/components/view_logs/toolssection/toolssection.tsx","./src/components/view_logs/toolssection/index.ts","./src/components/view_logs/logdetailsdrawer/prettymessagestypes.ts","./src/components/view_logs/logdetailsdrawer/prettymessagesutils.ts","./src/components/view_logs/logdetailsdrawer/sectionheader.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.tsx","./src/components/view_logs/logdetailsdrawer/historytree.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.tsx","./src/components/view_logs/logdetailsdrawer/index.ts","./src/components/view_logs/logdetailsdrawer/utils.test.ts","./src/components/view_logs/toolssection/utils.test.ts","./src/data/insultscomplianceprompts.ts","./src/data/financialcomplianceprompts.ts","./src/data/codeexecutioncomplianceprompts.ts","./src/data/claimscomplianceprompts.ts","./src/data/complianceprompts.ts","./src/data/canadianpiicomplianceprompts.ts","./src/hooks/mcpoauthutils.ts","./src/hooks/usevisitedtabs.ts","./src/hooks/useworker.ts","./src/hooks/policies/usedeletepolicyattachment.ts","./src/lib/assetpaths.test.ts","./src/autorouter_presets.json","./src/lib/autorouter_presets.ts","./src/lib/autorouter_presets.test.ts","./src/lib/cva.config.test.ts","./src/lib/toast.test.ts","./src/lib/forms/pickdirty.ts","./src/lib/forms/pickdirty.test.ts","./src/lib/forms/urlvalidation.ts","./src/lib/forms/urlvalidation.test.ts","./src/lib/http/api.sameorigin.test.ts","./src/lib/http/api.test.ts","./src/lib/http/client.test.ts","./src/lib/http/resolveapibase.test.ts","./src/lib/http/runtime.test.ts","./src/utils/budgetutils.ts","./src/utils/capabilities.test.ts","./src/utils/cookieutils.test.ts","./src/utils/datautils.test.ts","./src/utils/errorpatterns.ts","./src/utils/errorutils.ts","./src/utils/errorutils.test.ts","./src/utils/jwtutils.test.ts","./node_modules/date-fns/constants.d.ts","./node_modules/date-fns/locale/types.d.ts","./node_modules/date-fns/fp/types.d.ts","./node_modules/date-fns/types.d.ts","./node_modules/date-fns/add.d.ts","./node_modules/date-fns/addbusinessdays.d.ts","./node_modules/date-fns/adddays.d.ts","./node_modules/date-fns/addhours.d.ts","./node_modules/date-fns/addisoweekyears.d.ts","./node_modules/date-fns/addmilliseconds.d.ts","./node_modules/date-fns/addminutes.d.ts","./node_modules/date-fns/addmonths.d.ts","./node_modules/date-fns/addquarters.d.ts","./node_modules/date-fns/addseconds.d.ts","./node_modules/date-fns/addweeks.d.ts","./node_modules/date-fns/addyears.d.ts","./node_modules/date-fns/areintervalsoverlapping.d.ts","./node_modules/date-fns/clamp.d.ts","./node_modules/date-fns/closestindexto.d.ts","./node_modules/date-fns/closestto.d.ts","./node_modules/date-fns/compareasc.d.ts","./node_modules/date-fns/comparedesc.d.ts","./node_modules/date-fns/constructfrom.d.ts","./node_modules/date-fns/constructnow.d.ts","./node_modules/date-fns/daystoweeks.d.ts","./node_modules/date-fns/differenceinbusinessdays.d.ts","./node_modules/date-fns/differenceincalendardays.d.ts","./node_modules/date-fns/differenceincalendarisoweekyears.d.ts","./node_modules/date-fns/differenceincalendarisoweeks.d.ts","./node_modules/date-fns/differenceincalendarmonths.d.ts","./node_modules/date-fns/differenceincalendarquarters.d.ts","./node_modules/date-fns/differenceincalendarweeks.d.ts","./node_modules/date-fns/differenceincalendaryears.d.ts","./node_modules/date-fns/differenceindays.d.ts","./node_modules/date-fns/differenceinhours.d.ts","./node_modules/date-fns/differenceinisoweekyears.d.ts","./node_modules/date-fns/differenceinmilliseconds.d.ts","./node_modules/date-fns/differenceinminutes.d.ts","./node_modules/date-fns/differenceinmonths.d.ts","./node_modules/date-fns/differenceinquarters.d.ts","./node_modules/date-fns/differenceinseconds.d.ts","./node_modules/date-fns/differenceinweeks.d.ts","./node_modules/date-fns/differenceinyears.d.ts","./node_modules/date-fns/eachdayofinterval.d.ts","./node_modules/date-fns/eachhourofinterval.d.ts","./node_modules/date-fns/eachminuteofinterval.d.ts","./node_modules/date-fns/eachmonthofinterval.d.ts","./node_modules/date-fns/eachquarterofinterval.d.ts","./node_modules/date-fns/eachweekofinterval.d.ts","./node_modules/date-fns/eachweekendofinterval.d.ts","./node_modules/date-fns/eachweekendofmonth.d.ts","./node_modules/date-fns/eachweekendofyear.d.ts","./node_modules/date-fns/eachyearofinterval.d.ts","./node_modules/date-fns/endofday.d.ts","./node_modules/date-fns/endofdecade.d.ts","./node_modules/date-fns/endofhour.d.ts","./node_modules/date-fns/endofisoweek.d.ts","./node_modules/date-fns/endofisoweekyear.d.ts","./node_modules/date-fns/endofminute.d.ts","./node_modules/date-fns/endofmonth.d.ts","./node_modules/date-fns/endofquarter.d.ts","./node_modules/date-fns/endofsecond.d.ts","./node_modules/date-fns/endoftoday.d.ts","./node_modules/date-fns/endoftomorrow.d.ts","./node_modules/date-fns/endofweek.d.ts","./node_modules/date-fns/endofyear.d.ts","./node_modules/date-fns/endofyesterday.d.ts","./node_modules/date-fns/_lib/format/formatters.d.ts","./node_modules/date-fns/_lib/format/longformatters.d.ts","./node_modules/date-fns/format.d.ts","./node_modules/date-fns/formatdistance.d.ts","./node_modules/date-fns/formatdistancestrict.d.ts","./node_modules/date-fns/formatdistancetonow.d.ts","./node_modules/date-fns/formatdistancetonowstrict.d.ts","./node_modules/date-fns/formatduration.d.ts","./node_modules/date-fns/formatiso.d.ts","./node_modules/date-fns/formatiso9075.d.ts","./node_modules/date-fns/formatisoduration.d.ts","./node_modules/date-fns/formatrfc3339.d.ts","./node_modules/date-fns/formatrfc7231.d.ts","./node_modules/date-fns/formatrelative.d.ts","./node_modules/date-fns/fromunixtime.d.ts","./node_modules/date-fns/getdate.d.ts","./node_modules/date-fns/getday.d.ts","./node_modules/date-fns/getdayofyear.d.ts","./node_modules/date-fns/getdaysinmonth.d.ts","./node_modules/date-fns/getdaysinyear.d.ts","./node_modules/date-fns/getdecade.d.ts","./node_modules/date-fns/_lib/defaultoptions.d.ts","./node_modules/date-fns/getdefaultoptions.d.ts","./node_modules/date-fns/gethours.d.ts","./node_modules/date-fns/getisoday.d.ts","./node_modules/date-fns/getisoweek.d.ts","./node_modules/date-fns/getisoweekyear.d.ts","./node_modules/date-fns/getisoweeksinyear.d.ts","./node_modules/date-fns/getmilliseconds.d.ts","./node_modules/date-fns/getminutes.d.ts","./node_modules/date-fns/getmonth.d.ts","./node_modules/date-fns/getoverlappingdaysinintervals.d.ts","./node_modules/date-fns/getquarter.d.ts","./node_modules/date-fns/getseconds.d.ts","./node_modules/date-fns/gettime.d.ts","./node_modules/date-fns/getunixtime.d.ts","./node_modules/date-fns/getweek.d.ts","./node_modules/date-fns/getweekofmonth.d.ts","./node_modules/date-fns/getweekyear.d.ts","./node_modules/date-fns/getweeksinmonth.d.ts","./node_modules/date-fns/getyear.d.ts","./node_modules/date-fns/hourstomilliseconds.d.ts","./node_modules/date-fns/hourstominutes.d.ts","./node_modules/date-fns/hourstoseconds.d.ts","./node_modules/date-fns/interval.d.ts","./node_modules/date-fns/intervaltoduration.d.ts","./node_modules/date-fns/intlformat.d.ts","./node_modules/date-fns/intlformatdistance.d.ts","./node_modules/date-fns/isafter.d.ts","./node_modules/date-fns/isbefore.d.ts","./node_modules/date-fns/isdate.d.ts","./node_modules/date-fns/isequal.d.ts","./node_modules/date-fns/isexists.d.ts","./node_modules/date-fns/isfirstdayofmonth.d.ts","./node_modules/date-fns/isfriday.d.ts","./node_modules/date-fns/isfuture.d.ts","./node_modules/date-fns/islastdayofmonth.d.ts","./node_modules/date-fns/isleapyear.d.ts","./node_modules/date-fns/ismatch.d.ts","./node_modules/date-fns/ismonday.d.ts","./node_modules/date-fns/ispast.d.ts","./node_modules/date-fns/issameday.d.ts","./node_modules/date-fns/issamehour.d.ts","./node_modules/date-fns/issameisoweek.d.ts","./node_modules/date-fns/issameisoweekyear.d.ts","./node_modules/date-fns/issameminute.d.ts","./node_modules/date-fns/issamemonth.d.ts","./node_modules/date-fns/issamequarter.d.ts","./node_modules/date-fns/issamesecond.d.ts","./node_modules/date-fns/issameweek.d.ts","./node_modules/date-fns/issameyear.d.ts","./node_modules/date-fns/issaturday.d.ts","./node_modules/date-fns/issunday.d.ts","./node_modules/date-fns/isthishour.d.ts","./node_modules/date-fns/isthisisoweek.d.ts","./node_modules/date-fns/isthisminute.d.ts","./node_modules/date-fns/isthismonth.d.ts","./node_modules/date-fns/isthisquarter.d.ts","./node_modules/date-fns/isthissecond.d.ts","./node_modules/date-fns/isthisweek.d.ts","./node_modules/date-fns/isthisyear.d.ts","./node_modules/date-fns/isthursday.d.ts","./node_modules/date-fns/istoday.d.ts","./node_modules/date-fns/istomorrow.d.ts","./node_modules/date-fns/istuesday.d.ts","./node_modules/date-fns/isvalid.d.ts","./node_modules/date-fns/iswednesday.d.ts","./node_modules/date-fns/isweekend.d.ts","./node_modules/date-fns/iswithininterval.d.ts","./node_modules/date-fns/isyesterday.d.ts","./node_modules/date-fns/lastdayofdecade.d.ts","./node_modules/date-fns/lastdayofisoweek.d.ts","./node_modules/date-fns/lastdayofisoweekyear.d.ts","./node_modules/date-fns/lastdayofmonth.d.ts","./node_modules/date-fns/lastdayofquarter.d.ts","./node_modules/date-fns/lastdayofweek.d.ts","./node_modules/date-fns/lastdayofyear.d.ts","./node_modules/date-fns/_lib/format/lightformatters.d.ts","./node_modules/date-fns/lightformat.d.ts","./node_modules/date-fns/max.d.ts","./node_modules/date-fns/milliseconds.d.ts","./node_modules/date-fns/millisecondstohours.d.ts","./node_modules/date-fns/millisecondstominutes.d.ts","./node_modules/date-fns/millisecondstoseconds.d.ts","./node_modules/date-fns/min.d.ts","./node_modules/date-fns/minutestohours.d.ts","./node_modules/date-fns/minutestomilliseconds.d.ts","./node_modules/date-fns/minutestoseconds.d.ts","./node_modules/date-fns/monthstoquarters.d.ts","./node_modules/date-fns/monthstoyears.d.ts","./node_modules/date-fns/nextday.d.ts","./node_modules/date-fns/nextfriday.d.ts","./node_modules/date-fns/nextmonday.d.ts","./node_modules/date-fns/nextsaturday.d.ts","./node_modules/date-fns/nextsunday.d.ts","./node_modules/date-fns/nextthursday.d.ts","./node_modules/date-fns/nexttuesday.d.ts","./node_modules/date-fns/nextwednesday.d.ts","./node_modules/date-fns/parse/_lib/types.d.ts","./node_modules/date-fns/parse/_lib/setter.d.ts","./node_modules/date-fns/parse/_lib/parser.d.ts","./node_modules/date-fns/parse/_lib/parsers.d.ts","./node_modules/date-fns/parse.d.ts","./node_modules/date-fns/parseiso.d.ts","./node_modules/date-fns/parsejson.d.ts","./node_modules/date-fns/previousday.d.ts","./node_modules/date-fns/previousfriday.d.ts","./node_modules/date-fns/previousmonday.d.ts","./node_modules/date-fns/previoussaturday.d.ts","./node_modules/date-fns/previoussunday.d.ts","./node_modules/date-fns/previousthursday.d.ts","./node_modules/date-fns/previoustuesday.d.ts","./node_modules/date-fns/previouswednesday.d.ts","./node_modules/date-fns/quarterstomonths.d.ts","./node_modules/date-fns/quarterstoyears.d.ts","./node_modules/date-fns/roundtonearesthours.d.ts","./node_modules/date-fns/roundtonearestminutes.d.ts","./node_modules/date-fns/secondstohours.d.ts","./node_modules/date-fns/secondstomilliseconds.d.ts","./node_modules/date-fns/secondstominutes.d.ts","./node_modules/date-fns/set.d.ts","./node_modules/date-fns/setdate.d.ts","./node_modules/date-fns/setday.d.ts","./node_modules/date-fns/setdayofyear.d.ts","./node_modules/date-fns/setdefaultoptions.d.ts","./node_modules/date-fns/sethours.d.ts","./node_modules/date-fns/setisoday.d.ts","./node_modules/date-fns/setisoweek.d.ts","./node_modules/date-fns/setisoweekyear.d.ts","./node_modules/date-fns/setmilliseconds.d.ts","./node_modules/date-fns/setminutes.d.ts","./node_modules/date-fns/setmonth.d.ts","./node_modules/date-fns/setquarter.d.ts","./node_modules/date-fns/setseconds.d.ts","./node_modules/date-fns/setweek.d.ts","./node_modules/date-fns/setweekyear.d.ts","./node_modules/date-fns/setyear.d.ts","./node_modules/date-fns/startofday.d.ts","./node_modules/date-fns/startofdecade.d.ts","./node_modules/date-fns/startofhour.d.ts","./node_modules/date-fns/startofisoweek.d.ts","./node_modules/date-fns/startofisoweekyear.d.ts","./node_modules/date-fns/startofminute.d.ts","./node_modules/date-fns/startofmonth.d.ts","./node_modules/date-fns/startofquarter.d.ts","./node_modules/date-fns/startofsecond.d.ts","./node_modules/date-fns/startoftoday.d.ts","./node_modules/date-fns/startoftomorrow.d.ts","./node_modules/date-fns/startofweek.d.ts","./node_modules/date-fns/startofweekyear.d.ts","./node_modules/date-fns/startofyear.d.ts","./node_modules/date-fns/startofyesterday.d.ts","./node_modules/date-fns/sub.d.ts","./node_modules/date-fns/subbusinessdays.d.ts","./node_modules/date-fns/subdays.d.ts","./node_modules/date-fns/subhours.d.ts","./node_modules/date-fns/subisoweekyears.d.ts","./node_modules/date-fns/submilliseconds.d.ts","./node_modules/date-fns/subminutes.d.ts","./node_modules/date-fns/submonths.d.ts","./node_modules/date-fns/subquarters.d.ts","./node_modules/date-fns/subseconds.d.ts","./node_modules/date-fns/subweeks.d.ts","./node_modules/date-fns/subyears.d.ts","./node_modules/date-fns/todate.d.ts","./node_modules/date-fns/transpose.d.ts","./node_modules/date-fns/weekstodays.d.ts","./node_modules/date-fns/yearstodays.d.ts","./node_modules/date-fns/yearstomonths.d.ts","./node_modules/date-fns/yearstoquarters.d.ts","./node_modules/date-fns/index.d.ts","./src/utils/keyexpiryutils.ts","./src/utils/keyexpiryutils.test.ts","./src/utils/keyupdateutils.ts","./src/utils/keyupdateutils.test.ts","./src/utils/licenseutils.test.ts","./src/utils/localstorageutils.test.ts","./src/utils/maskedsecretutils.ts","./src/utils/mcpheaderutils.ts","./src/utils/mcpheaderutils.test.ts","./src/utils/mcptokenstore.test.ts","./src/utils/mcptoolcrudclassification.test.ts","./src/utils/migratedpages.test.ts","./src/utils/modelpermissions.test.ts","./src/utils/pkce.ts","./src/utils/promptcacheusage.test.ts","./src/utils/proxyutils.test.ts","./node_modules/dayjs/plugin/utc.d.ts","./src/utils/ptudatetime.ts","./src/utils/ptudatetime.test.ts","./src/utils/ptuvalidation.ts","./src/utils/ptumodelinfo.ts","./src/utils/ptumodelinfo.test.ts","./src/utils/ptuvalidation.test.ts","./src/utils/returnurlutils.test.ts","./src/utils/roles.test.ts","./src/utils/tabroutes.test.ts","./src/utils/teamutils.test.ts","./src/utils/textutils.test.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@eslint/core/dist/cjs/types.d.cts","./node_modules/eslint/lib/types/use-at-your-own-risk.d.ts","./node_modules/eslint/lib/types/index.d.ts","./tests/fetch-location-rule.test.ts","./node_modules/vitest/dist/environments.d.ts","./tests/jsdomfetchenv.ts","./scripts/lint-budget-lib.mjs","./tests/lint-budget-lib.test.ts","./tests/setup.unit.ts","./node_modules/@testing-library/jest-dom/types/matchers.d.ts","./node_modules/@testing-library/jest-dom/types/jest.d.ts","./node_modules/@testing-library/jest-dom/types/index.d.ts","./tests/setuptests.ts","./scripts/eslint-rules/filename-pascal-case.mjs","./tests/eslint-rules/filename-pascal-case.test.ts","./scripts/eslint-rules/no-ad-hoc-z-index.mjs","./tests/eslint-rules/no-ad-hoc-z-index.test.ts","./scripts/eslint-rules/no-complex-jsx-arrow.mjs","./tests/eslint-rules/no-complex-jsx-arrow.test.ts","./scripts/eslint-rules/no-large-inline-object-arg.mjs","./tests/eslint-rules/no-large-inline-object-arg.test.ts","./scripts/eslint-rules/no-long-condition-chain.mjs","./tests/eslint-rules/no-long-condition-chain.test.ts","./scripts/eslint-rules/no-noop-hover-variant.mjs","./tests/eslint-rules/no-noop-hover-variant.test.ts","./tests/mocks/complexityscorerdefaults.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./node_modules/nuqs/dist/adapters/next/app.d.ts","./src/contexts/authcontext.tsx","./src/contexts/reactqueryprovider.tsx","./src/components/ui/sonner.tsx","./src/app/layout.tsx","./src/components/ui/breadcrumb.tsx","./src/components/shared/toolbarseparator.tsx","./src/components/navbar/blogdropdown/blogdropdown.tsx","./src/components/ui/button-group.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.tsx","./src/components/navbar/notificationsbell/notificationsbell.tsx","./src/contexts/pluginmodecontext.tsx","./src/components/navbar/viewswitcher.tsx","./src/components/themetoggle/themetoggle.tsx","./src/components/navbar/workerdropdown/workerdropdown.tsx","./src/components/dashboardheader.tsx","./src/components/navbar/userdropdown/userdropdown.tsx","./src/components/navbar.tsx","./src/components/common_components/loadingscreen.tsx","./src/app/(dashboard)/components/sidebarprovider.tsx","./src/components/debugwarningbanner.tsx","./src/components/norediswarningbanner.tsx","./src/components/licenseexpirybanner.tsx","./node_modules/@types/unist/index.d.ts","./node_modules/@types/hast/index.d.ts","./node_modules/vfile-message/lib/index.d.ts","./node_modules/vfile-message/index.d.ts","./node_modules/vfile/lib/index.d.ts","./node_modules/vfile/index.d.ts","./node_modules/unified/lib/callable-instance.d.ts","./node_modules/trough/lib/index.d.ts","./node_modules/trough/index.d.ts","./node_modules/unified/lib/index.d.ts","./node_modules/unified/index.d.ts","./node_modules/@types/mdast/index.d.ts","./node_modules/mdast-util-to-hast/lib/state.d.ts","./node_modules/mdast-util-to-hast/lib/footer.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/blockquote.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/delete.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/emphasis.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/footnote-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/heading.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/html.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/inline-code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list-item.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/paragraph.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/root.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/strong.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-cell.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-row.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/text.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/thematic-break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/index.d.ts","./node_modules/mdast-util-to-hast/lib/index.d.ts","./node_modules/mdast-util-to-hast/index.d.ts","./node_modules/remark-rehype/lib/index.d.ts","./node_modules/remark-rehype/index.d.ts","./node_modules/react-markdown/lib/index.d.ts","./node_modules/react-markdown/index.d.ts","./node_modules/micromark-util-types/index.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/html.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-footnote/index.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/html.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-strikethrough/index.d.ts","./node_modules/micromark-extension-gfm/index.d.ts","./node_modules/mdast-util-from-markdown/lib/types.d.ts","./node_modules/mdast-util-from-markdown/lib/index.d.ts","./node_modules/mdast-util-from-markdown/index.d.ts","./node_modules/mdast-util-to-markdown/lib/types.d.ts","./node_modules/mdast-util-to-markdown/lib/index.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/blockquote.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/definition.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/emphasis.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/heading.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/html.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/inline-code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list-item.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/paragraph.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/root.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/strong.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/text.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/thematic-break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/index.d.ts","./node_modules/mdast-util-to-markdown/index.d.ts","./node_modules/mdast-util-gfm-footnote/lib/index.d.ts","./node_modules/mdast-util-gfm-footnote/index.d.ts","./node_modules/markdown-table/index.d.ts","./node_modules/mdast-util-gfm-table/lib/index.d.ts","./node_modules/mdast-util-gfm-table/index.d.ts","./node_modules/mdast-util-gfm/lib/index.d.ts","./node_modules/mdast-util-gfm/index.d.ts","./node_modules/remark-gfm/lib/index.d.ts","./node_modules/remark-gfm/index.d.ts","./src/components/userbanner.tsx","./src/app/(dashboard)/layout.tsx","./src/app/(dashboard)/agentcontrolplaneview.test.tsx","./src/app/(dashboard)/layout.test.tsx","./src/components/common_components/fetch_teams.tsx","./src/components/shared/pageheader.tsx","./src/app/(dashboard)/hooks/useteams.tsx","./src/components/ui/hover-card.tsx","./src/components/common_components/defaultproxyadmintag.tsx","./src/components/common_components/labeledfield.tsx","./src/components/templates/keyinfoheader.tsx","./src/components/shared/advanced_date_picker.tsx","./src/components/shared/summarycard.tsx","./src/components/shared/savingstiles.tsx","./src/components/templates/keysavingstab.tsx","./src/components/common_components/autorotationview.tsx","./src/components/common_components/deleteresourcemodal.tsx","./src/components/common_components/routersettingssummary.tsx","./src/components/logging_settings_view.tsx","./src/components/permissions/vectorstorepermissions.tsx","./src/components/permissions/mcpserverpermissions.tsx","./src/components/permissions/agentpermissions.tsx","./src/components/object_permissions_view.tsx","./src/components/organisms/regeneratekeymodal.tsx","./src/components/shared/errorutils.tsx","./src/components/guardrails/guardrailselector.tsx","./src/components/policies/policyselector.tsx","./src/components/templates/keyeditviewcontrols.tsx","./src/components/team/editloggingsettings.tsx","./src/components/templates/key_edit_view.tsx","./src/components/templates/key_info_view.tsx","./src/components/virtualkeyspage/keytablecolumns.tsx","./src/components/virtualkeyspage/virtualkeystable.tsx","./src/components/user_dashboard.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.tsx","./src/app/(dashboard)/page.tsx","./src/app/(dashboard)/page.test.tsx","./src/components/modelselect/modelselect.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupbaseform.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstablecolumns.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstable.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.tsx","./src/app/(dashboard)/access-groups/page.tsx","./tests/test-utils.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.integration.test.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.test.tsx","./src/components/constants.tsx","./src/components/scim.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.tsx","./src/components/shared/passwordinput.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.tsx","./src/components/settings/adminsettings/uisettings/uisettings.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.tsx","./src/components/ssomodals.tsx","./src/components/uiaccesscontrolform.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.tsx","./src/app/(dashboard)/admin-panel/page.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.test.tsx","./src/app/(dashboard)/agents/_components/agentformkit.tsx","./src/app/(dashboard)/agents/_components/cost_config_fields.tsx","./src/app/(dashboard)/agents/_components/agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.tsx","./src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.tsx","./src/app/(dashboard)/agents/_components/agent_info.tsx","./src/app/(dashboard)/agents/_components/agentstablecolumns.tsx","./src/app/(dashboard)/agents/_components/agentstable.tsx","./src/app/(dashboard)/agents/_components/agentspanel.tsx","./src/app/(dashboard)/agents/page.tsx","./src/app/(dashboard)/agents/_components/agentspanel.test.tsx","./src/app/(dashboard)/agents/_components/agentstable.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.integration.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.test.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.integration.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.test.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.test.tsx","./src/app/(dashboard)/api-keys/page.tsx","./src/app/(dashboard)/api-reference/_components/doclink.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.tsx","./src/components/deprecationbanner.tsx","./src/app/(dashboard)/api-reference/page.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budgettablecolumns.tsx","./src/app/(dashboard)/budgets/_components/budgettable.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.tsx","./src/app/(dashboard)/budgets/page.tsx","./src/app/(dashboard)/budgets/_components/budgettable.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.integration.test.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.test.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.integration.test.tsx","./src/app/(dashboard)/caching/_components/response_time_indicator.tsx","./src/app/(dashboard)/caching/_components/cache_health.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cacheformfield.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cachefieldsection.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisformfield.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfieldsection.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.tsx","./src/app/(dashboard)/caching/page.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.test.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx","./src/app/(dashboard)/caching/_components/cache_health.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.test.tsx","./src/components/shared/paginationstatusalerts.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.tsx","./src/components/router_settings/index.tsx","./node_modules/openai/_shims/manual-types.d.ts","./node_modules/openai/_shims/auto/types.d.ts","./node_modules/openai/streaming.d.ts","./node_modules/openai/error.d.ts","./node_modules/openai/_shims/multipartbody.d.ts","./node_modules/openai/uploads.d.ts","./node_modules/openai/core.d.ts","./node_modules/openai/_shims/index.d.ts","./node_modules/openai/pagination.d.ts","./node_modules/openai/resources/shared.d.ts","./node_modules/openai/resources/batches.d.ts","./node_modules/openai/resources/chat/completions/messages.d.ts","./node_modules/openai/resources/chat/completions/completions.d.ts","./node_modules/openai/resources/completions.d.ts","./node_modules/openai/resources/embeddings.d.ts","./node_modules/openai/resources/files.d.ts","./node_modules/openai/resources/images.d.ts","./node_modules/openai/resources/models.d.ts","./node_modules/openai/resources/moderations.d.ts","./node_modules/openai/resources/audio/speech.d.ts","./node_modules/openai/resources/audio/transcriptions.d.ts","./node_modules/openai/resources/audio/translations.d.ts","./node_modules/openai/resources/audio/audio.d.ts","./node_modules/openai/resources/beta/threads/messages.d.ts","./node_modules/openai/resources/beta/threads/runs/steps.d.ts","./node_modules/openai/resources/beta/threads/runs/runs.d.ts","./node_modules/openai/lib/eventstream.d.ts","./node_modules/openai/lib/assistantstream.d.ts","./node_modules/openai/resources/beta/threads/threads.d.ts","./node_modules/openai/resources/beta/assistants.d.ts","./node_modules/openai/resources/chat/completions.d.ts","./node_modules/openai/lib/abstractchatcompletionrunner.d.ts","./node_modules/openai/lib/chatcompletionstream.d.ts","./node_modules/openai/lib/responsesparser.d.ts","./node_modules/openai/resources/responses/input-items.d.ts","./node_modules/openai/lib/responses/eventtypes.d.ts","./node_modules/openai/lib/responses/responsestream.d.ts","./node_modules/openai/resources/responses/responses.d.ts","./node_modules/openai/lib/parser.d.ts","./node_modules/openai/lib/chatcompletionstreamingrunner.d.ts","./node_modules/openai/lib/jsonschema.d.ts","./node_modules/openai/lib/runnablefunction.d.ts","./node_modules/openai/lib/chatcompletionrunner.d.ts","./node_modules/openai/resources/beta/chat/completions.d.ts","./node_modules/openai/resources/beta/chat/chat.d.ts","./node_modules/openai/resources/beta/realtime/sessions.d.ts","./node_modules/openai/resources/beta/realtime/transcription-sessions.d.ts","./node_modules/openai/resources/beta/realtime/realtime.d.ts","./node_modules/openai/resources/beta/beta.d.ts","./node_modules/openai/resources/containers/files/content.d.ts","./node_modules/openai/resources/containers/files/files.d.ts","./node_modules/openai/resources/containers/containers.d.ts","./node_modules/openai/resources/graders/grader-models.d.ts","./node_modules/openai/resources/evals/runs/output-items.d.ts","./node_modules/openai/resources/evals/runs/runs.d.ts","./node_modules/openai/resources/evals/evals.d.ts","./node_modules/openai/resources/fine-tuning/methods.d.ts","./node_modules/openai/resources/fine-tuning/alpha/graders.d.ts","./node_modules/openai/resources/fine-tuning/alpha/alpha.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/permissions.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/jobs.d.ts","./node_modules/openai/resources/fine-tuning/fine-tuning.d.ts","./node_modules/openai/resources/graders/graders.d.ts","./node_modules/openai/resources/uploads/parts.d.ts","./node_modules/openai/resources/uploads/uploads.d.ts","./node_modules/openai/resources/vector-stores/files.d.ts","./node_modules/openai/resources/vector-stores/file-batches.d.ts","./node_modules/openai/resources/vector-stores/vector-stores.d.ts","./node_modules/openai/index.d.ts","./node_modules/openai/resource.d.ts","./node_modules/openai/resources/chat/chat.d.ts","./node_modules/openai/resources/chat/completions/index.d.ts","./node_modules/openai/resources/chat/index.d.ts","./node_modules/openai/resources/index.d.ts","./node_modules/openai/index.d.mts","./src/components/molecules/models/providerlogo.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.tsx","./src/components/routing_groups/routinggroupusagepanel.tsx","./src/components/routing_groups/routinggroupstablecolumns.tsx","./src/components/routing_groups/routinggroupstable.tsx","./src/components/routing_groups/routinggroupmodal.tsx","./src/components/routing_groups/index.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.tsx","./src/components/shared/paginatedmultiselect.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.tsx","./src/app/(dashboard)/cost-optimization/page.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.activity.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.test.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.test.tsx","./src/app/(dashboard)/cost-tracking/page.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patterntable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordtable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentcategoryconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/guardrailformfield.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_optional_params.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx","./src/app/(dashboard)/guardrails/_components/llm_judge/llmjudgefields.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtablecolumns.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/categorytable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.tsx","./src/app/(dashboard)/guardrails/page.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.integration.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltertables.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.test.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.test.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.tsx","./src/components/guardrailsmonitor/logviewer.tsx","./src/components/guardrailsmonitor/metriccard.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.tsx","./src/components/shared/adminonlynotice.tsx","./src/app/(dashboard)/guardrails-monitor/page.tsx","./src/app/(dashboard)/guardrails-monitor/page.integration.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.test.tsx","./src/app/(dashboard)/hooks/usetabrouting.test.tsx","./src/app/(dashboard)/hooks/common/useresourcelist.test.tsx","./src/components/email_settings.tsx","./src/components/alerting/dynamic_form.tsx","./src/components/alerting/alerting_settings.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.tsx","./src/components/cloudzerocosttracking/cloudzeroformcontrols.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstablecolumns.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.tsx","./src/components/settings.tsx","./src/app/(dashboard)/logging-and-alerts/page.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystablecolumns.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.tsx","./src/components/deletedkeyspage/deletedkeyspage.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstablecolumns.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.tsx","./src/components/deletedteamspage/deletedteamspage.tsx","./src/components/view_logs/auditlogstablecolumns.tsx","./src/components/view_logs/auditlogstable.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.tsx","./src/components/view_logs/auditlogspanel.tsx","./src/components/view_logs/log_filter_logic.tsx","./src/components/view_logs/logs_utils.tsx","./src/components/view_logs/logstabletoolbar.tsx","./src/components/view_logs/requestlogsfilters.tsx","./src/components/view_logs/typebadges.tsx","./src/components/view_logs/requestlogstablecolumns.tsx","./src/components/view_logs/requestlogstable.tsx","./src/components/view_logs/requestlogspanel.tsx","./src/components/view_logs/index.tsx","./src/app/(dashboard)/logs/page.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpsubmissionstab.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.tsx","./src/app/(dashboard)/mcp-servers/_components/awssigv4fields.tsx","./src/app/(dashboard)/mcp-servers/_components/openapibyokfields.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenendpointauthmethodfield.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.tsx","./src/app/(dashboard)/mcp-servers/_components/dcrbridgetoggle.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenexchangeformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/idjagformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx","./src/app/(dashboard)/mcp-servers/_components/stdioconfiguration.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiformsection.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.tsx","./src/hooks/usemcpoauthflow.tsx","./src/hooks/usetestmcpconnection.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx","./src/components/mcp_tools/byokcredentialmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx","./src/app/(dashboard)/mcp-servers/_components/toolargumentsform.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.tsx","./src/hooks/usetoolsoauthflow.tsx","./src/hooks/useusermcpoauthflow.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx","./src/app/(dashboard)/mcp-servers/_components/index.tsx","./src/app/(dashboard)/mcp-servers/page.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.permissions.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformtestharness.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.test.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.test.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.test.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.test.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.test.tsx","./src/app/(dashboard)/mcp-servers/_components/utils.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.tsx","./src/app/(dashboard)/memory/_components/memorytablecolumns.tsx","./src/app/(dashboard)/memory/_components/memorytable.tsx","./src/app/(dashboard)/memory/_components/memoryview.tsx","./src/app/(dashboard)/memory/page.tsx","./src/app/(dashboard)/memory/page.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.test.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorytable.test.tsx","./src/app/(dashboard)/memory/_components/memoryview.test.tsx","./src/components/aihub/agenthubtablecolumns.tsx","./src/components/aihub/forms/makeagentpublicform.tsx","./src/components/aihub/mcphubtablecolumns.tsx","./src/components/aihub/forms/makemcppublicform.tsx","./src/components/model_filters.tsx","./src/components/aihub/forms/makemodelpublicform.tsx","./src/components/aihub/modelhubtablecolumns.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.tsx","./src/components/aihub/usefullinksmanagement.tsx","./src/components/aihub/skillhubtablecolumns.tsx","./src/components/claude_code_plugins/skill_detail.tsx","./src/components/aihub/skillhubdashboard.tsx","./src/components/claude_code_plugins/makeskillpublicform.tsx","./src/components/publicmodelhubtablecolumns.tsx","./src/components/chat_ui/codesnippets.tsx","./src/components/public_model_hub.tsx","./src/components/aihub/modelhubtable.tsx","./src/app/(dashboard)/model-hub-table/page.tsx","./src/components/molecules/cost_optimization_feedback_banner.tsx","./src/components/add_model/auto_router_connection_test.tsx","./src/components/model_add/reuse_credentials.tsx","./src/components/update_model_credentials_modal.tsx","./src/components/shared/form/utcdatetimeinput.tsx","./src/components/add_model/cache_control_settings.tsx","./src/components/modelinfoeditform.tsx","./src/components/view_model/model_name_display.tsx","./src/components/model_info_view.tsx","./src/components/common_components/user_search_modal.tsx","./src/components/shared/form/labelwithhint.tsx","./src/components/team/guardrailsselect.tsx","./src/components/common_components/metadatakeyvaluefields.tsx","./src/components/guardrailsettingsview.tsx","./src/components/search_tools/searchtoolselector.tsx","./src/components/team/editmembership.tsx","./src/components/team/permission_definitions.tsx","./src/components/team/member_permissions.tsx","./src/components/team/myusertab.tsx","./src/components/common_components/membertable.tsx","./src/components/team/teammembertab.tsx","./src/components/team/teamvirtualkeystable.tsx","./src/components/team/teaminfo.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/allmodelspanel.tsx","./src/components/add_model/handle_add_auto_router_submit.tsx","./src/components/add_model/autorouterroutingtest.tsx","./src/components/add_model/add_auto_router_tab.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/autorouterstabpanel.tsx","./src/components/add_model/advanced_settings.tsx","./src/components/add_model/conditional_public_model_name.tsx","./src/components/add_model/litellm_model_name.tsx","./src/components/add_model/handle_add_model_submit.tsx","./src/components/add_model/model_connection_test.tsx","./src/components/add_model/provider_specific_fields.tsx","./src/components/add_model/add_model_modes.tsx","./src/components/add_model/addmodelform.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.tsx","./src/components/model_add/credentialmodal.tsx","./src/components/model_add/credentialstablecolumns.tsx","./src/components/model_add/credentialstable.tsx","./src/components/model_add/credentialspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/llmcredentialspanel.tsx","./src/components/key_value_input.tsx","./src/components/query_param_input.tsx","./src/components/route_preview.tsx","./src/components/common_components/passthroughsecuritysection.tsx","./src/components/common_components/passthroughguardrailssection.tsx","./src/components/add_pass_through.tsx","./src/components/pass_through_info.tsx","./src/components/passthroughsettings/passthroughendpointstablecolumns.tsx","./src/components/passthroughsettings/passthroughendpointstable.tsx","./src/components/passthroughsettings/passthroughsettings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/passthroughpanel.tsx","./src/components/model_dashboard/healthcheckstablecolumns.tsx","./src/components/model_dashboard/healthcheckstable.tsx","./src/components/model_dashboard/healthcheckcomponent.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelretrysettingspanel.tsx","./src/components/model_group_alias_settings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelgroupaliaspanel.tsx","./src/components/price_data_reload.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/pricedatapanel.tsx","./src/app/(dashboard)/models-and-endpoints/page.tsx","./src/app/(dashboard)/models-and-endpoints/page.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.integration.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.test.tsx","./src/components/view_user_spend.tsx","./src/components/usagepage/components/entityusage/topkeyview.tsx","./src/app/(dashboard)/old-usage/_components/usage.tsx","./src/app/(dashboard)/old-usage/page.tsx","./src/app/(dashboard)/old-usage/_components/usage.test.tsx","./src/components/common_components/filters/filterinput.tsx","./src/components/common_components/filters/filtersbutton.tsx","./src/components/common_components/filters/resetfiltersbutton.tsx","./src/app/(dashboard)/organizations/organizationfilters.tsx","./src/app/(dashboard)/organizations/organizationfilters.test.tsx","./src/components/organization/org-settings/orgsettingsform.tsx","./src/components/organization/org-create/orgcreatedialog.tsx","./src/components/shared/badgelink.tsx","./src/components/organization/organization_view.tsx","./src/app/(dashboard)/organizations/_components/organizationstablecolumns.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.tsx","./src/app/(dashboard)/organizations/page.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.test.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.test.tsx","./src/components/llm_calls/chat_completion.tsx","./src/app/(dashboard)/playground/components/complianceui/complianceui.tsx","./node_modules/uuid/dist/max.d.ts","./node_modules/uuid/dist/nil.d.ts","./node_modules/uuid/dist/types.d.ts","./node_modules/uuid/dist/parse.d.ts","./node_modules/uuid/dist/stringify.d.ts","./node_modules/uuid/dist/v1.d.ts","./node_modules/uuid/dist/v1tov6.d.ts","./node_modules/uuid/dist/v35.d.ts","./node_modules/uuid/dist/v3.d.ts","./node_modules/uuid/dist/v4.d.ts","./node_modules/uuid/dist/v5.d.ts","./node_modules/uuid/dist/v6.d.ts","./node_modules/uuid/dist/v6tov1.d.ts","./node_modules/uuid/dist/v7.d.ts","./node_modules/uuid/dist/validate.d.ts","./node_modules/uuid/dist/version.d.ts","./node_modules/uuid/dist/index.d.ts","./src/components/mcp_tools/mcptoolargumentsform.tsx","./src/components/tag_management/tagselector.tsx","./src/app/(dashboard)/playground/llm_calls/a2a_send_message.tsx","./node_modules/@anthropic-ai/sdk/internal/builtin-types.d.mts","./node_modules/form-data/index.d.ts","./node_modules/@types/node-fetch/externals.d.ts","./node_modules/@types/node-fetch/index.d.ts","./node_modules/@anthropic-ai/sdk/internal/types.d.mts","./node_modules/@anthropic-ai/sdk/internal/headers.d.mts","./node_modules/@anthropic-ai/sdk/internal/shim-types.d.mts","./node_modules/@anthropic-ai/sdk/core/streaming.d.mts","./node_modules/@anthropic-ai/sdk/internal/request-options.d.mts","./node_modules/@anthropic-ai/sdk/internal/utils/log.d.mts","./node_modules/@anthropic-ai/sdk/resources/shared.d.mts","./node_modules/@anthropic-ai/sdk/core/error.d.mts","./node_modules/@anthropic-ai/sdk/internal/parse.d.mts","./node_modules/@anthropic-ai/sdk/core/api-promise.d.mts","./node_modules/@anthropic-ai/sdk/core/pagination.d.mts","./node_modules/@anthropic-ai/sdk/internal/uploads.d.mts","./node_modules/@anthropic-ai/sdk/internal/to-file.d.mts","./node_modules/@anthropic-ai/sdk/core/uploads.d.mts","./node_modules/@anthropic-ai/sdk/core/resource.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/environments.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/files.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/user-profiles.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/agents.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/line.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.mts","./node_modules/@anthropic-ai/sdk/error.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages.d.mts","./node_modules/@anthropic-ai/sdk/lib/parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/messagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/lib/beta-parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/betamessagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/events.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/sessions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/resources.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/skills.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/credentials.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/vaults.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betarunnabletool.d.mts","./node_modules/@anthropic-ai/sdk/resources.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/compactioncontrol.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betatoolrunner.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/toolerror.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/beta.d.mts","./node_modules/@anthropic-ai/sdk/resources/completions.d.mts","./node_modules/@anthropic-ai/sdk/resources/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/index.d.mts","./node_modules/@anthropic-ai/sdk/client.d.mts","./node_modules/@anthropic-ai/sdk/index.d.mts","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.tsx","./src/app/(dashboard)/playground/llm_calls/image_edits.tsx","./src/app/(dashboard)/playground/llm_calls/image_generation.tsx","./src/components/llm_calls/responses_api.tsx","./src/app/(dashboard)/playground/llm_calls/interactions_api.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpretertool.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.tsx","./src/app/(dashboard)/playground/components/chat_ui/a2ametrics.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.tsx","./src/components/chat_ui/mcpeventsdisplay.tsx","./src/components/chat_ui/reasoningcontent.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/searchresultsdisplay.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/sessionmanagement.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.tsx","./src/app/(dashboard)/playground/page.tsx","./src/app/(dashboard)/playground/page.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.test.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.test.tsx","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.test.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.test.tsx","./src/app/(dashboard)/policies/_components/policytablecolumns.tsx","./src/app/(dashboard)/policies/_components/policytable.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx","./src/app/(dashboard)/policies/_components/policy_info.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.tsx","./src/app/(dashboard)/policies/_components/impact_popover.tsx","./src/app/(dashboard)/policies/_components/attachmenttablecolumns.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.tsx","./src/app/(dashboard)/policies/_components/tokenselect.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.tsx","./src/app/(dashboard)/policies/_components/policy_templates.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx","./src/app/(dashboard)/policies/_components/index.tsx","./src/app/(dashboard)/policies/page.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.test.tsx","./src/app/(dashboard)/policies/_components/policytable.test.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.test.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.test.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.test.tsx","./src/app/(dashboard)/policies/_components/impact_popover.test.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.test.tsx","./src/app/(dashboard)/policies/_components/index.test.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.test.tsx","./src/app/(dashboard)/policies/_components/policy_info.test.tsx","./src/app/(dashboard)/policies/_components/policy_templates.test.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.test.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectkeystablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.tsx","./src/app/(dashboard)/projects/_components/projectstablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectstable.tsx","./src/app/(dashboard)/projects/_components/projectspage.tsx","./src/app/(dashboard)/projects/page.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.test.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.test.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.test.tsx","./src/app/(dashboard)/projects/_components/projectspage.test.tsx","./src/app/(dashboard)/projects/_components/projectstable.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_utils.tsx","./src/app/(dashboard)/prompts/_components/prompttablecolumns.tsx","./src/app/(dashboard)/prompts/_components/prompttable.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variablewarning.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/dotpromptviewtab.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view.tsx","./src/app/(dashboard)/prompts/_components/index.tsx","./src/app/(dashboard)/prompts/page.tsx","./src/app/(dashboard)/prompts/_components/prompttable.test.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.integration.test.tsx","./src/app/(dashboard)/prompts/_components/index.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.test.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.test.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.test.tsx","./src/app/(dashboard)/router-settings/page.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.tsx","./src/app/(dashboard)/search-tools/_components/types.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltablecolumns.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.tsx","./src/app/(dashboard)/search-tools/_components/index.tsx","./src/app/(dashboard)/search-tools/page.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.tsx","./src/app/(dashboard)/skills/_components/plugintablecolumns.tsx","./src/app/(dashboard)/skills/_components/plugintable.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.tsx","./src/app/(dashboard)/skills/page.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.test.tsx","./src/app/(dashboard)/skills/_components/plugintable.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.tsx","./src/app/(dashboard)/tag-management/_components/tagtablecolumns.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.tsx","./src/app/(dashboard)/tag-management/_components/index.tsx","./src/app/(dashboard)/tag-management/page.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.test.tsx","./src/app/(dashboard)/tag-management/_components/index.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.integration.test.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.test.tsx","./src/components/team/availableteamstablecolumns.tsx","./src/components/team/availableteamstable.tsx","./src/components/team/availableteamspanel.tsx","./src/components/teamssosettings.tsx","./src/components/teamspage/teamtablecolumns.tsx","./src/components/teamspage/teamstable.tsx","./src/components/teams.tsx","./src/app/(dashboard)/teams/page.tsx","./src/components/toolpolicies/policyselect.tsx","./src/components/tooldetail.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.tsx","./src/components/toolpolicies/toolpoliciestable.tsx","./src/components/toolpolicies/toolpoliciespanel.tsx","./src/components/toolpoliciesview.tsx","./src/app/(dashboard)/tool-policies/page.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.test.tsx","./src/app/(dashboard)/transform-request/page.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.test.tsx","./src/app/(dashboard)/ui-theme/page.tsx","./src/components/usagepage/components/keymodelusageview.tsx","./src/components/activity_metrics.tsx","./src/components/cloudzero_export_modal.tsx","./src/components/common_components/userdropdown.tsx","./src/components/shared/chart_loader.tsx","./src/components/per_user_usage.tsx","./src/components/user_agent_activity.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.tsx","./src/components/common_components/team_multi_select.tsx","./src/app/(dashboard)/usage/_components/components/modelviewtoggle.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.tsx","./src/app/(dashboard)/usage/page.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.test.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.test.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.tsx","./src/components/bulk_create_users_button.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.tsx","./src/app/(dashboard)/users/_components/view_users/userstablecolumns.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.tsx","./src/app/(dashboard)/users/_components/view_users.tsx","./src/app/(dashboard)/users/_components/index.tsx","./src/app/(dashboard)/users/page.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.test.tsx","./src/app/(dashboard)/users/_components/view_users.test.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.test.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.integration.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx","./src/components/vector_store_providers.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx","./src/app/(dashboard)/vector-stores/_components/documentstablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.tsx","./src/app/(dashboard)/vector-stores/_components/index.tsx","./src/app/(dashboard)/vector-stores/page.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.characterization.test.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.test.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.test.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.test.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.test.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.test.tsx","./src/app/(dashboard)/vector-stores/_components/index.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.test.tsx","./src/app/(dashboard)/workflows/workflowruns.tsx","./src/app/(dashboard)/workflows/workflowruns.test.tsx","./src/app/(dashboard)/workflows/page.tsx","./src/app/(dashboard)/workflows/page.integration.test.tsx","./src/app/chat/layout.tsx","./src/app/chat/layout.test.tsx","./src/components/chat/chatmessages.tsx","./src/components/chat/mcpconnectpicker.tsx","./src/app/chat/page.tsx","./src/app/chat/page.integration.test.tsx","./src/components/chat/keyspanel.tsx","./src/app/chat/api-keys/page.tsx","./src/components/chat/mcpcredentialstab.tsx","./src/app/chat/credentials/page.tsx","./src/components/chat/mcpappspanel.tsx","./src/components/chat/connectflowbanner.tsx","./src/app/chat/integrations/page.tsx","./src/components/chat/logspanel.tsx","./src/app/chat/logs/page.tsx","./src/components/chat/usagepanel.tsx","./src/app/chat/usage/page.tsx","./src/app/connect/layout.tsx","./src/app/connect/layout.test.tsx","./src/app/connect/page.tsx","./src/app/connect/page.test.tsx","./src/app/login/loginpage.tsx","./src/app/login/loginpage.integration.test.tsx","./src/app/login/loginpage.test.tsx","./src/app/login/page.tsx","./src/app/mcp/oauth/callback/page.tsx","./src/app/model_hub/page.tsx","./src/app/model_hub_table/page.tsx","./src/app/onboarding/onboardingerrorview.tsx","./src/app/onboarding/onboardingerrorview.test.tsx","./src/app/onboarding/onboardingloadingview.tsx","./src/app/onboarding/onboardingformbody.tsx","./src/app/onboarding/onboardingform.tsx","./src/app/onboarding/onboardingform.test.tsx","./src/app/onboarding/onboardingformbody.integration.test.tsx","./src/app/onboarding/onboardingformbody.test.tsx","./src/app/onboarding/onboardingloadingview.test.tsx","./src/app/onboarding/page.tsx","./src/components/betabadge.test.tsx","./src/components/createuserbutton.test.tsx","./src/components/dashboardheader.test.tsx","./src/components/debugwarningbanner.test.tsx","./src/components/deprecationbanner.test.tsx","./src/components/guardrailsettingsview.test.tsx","./src/components/helplink.test.tsx","./src/components/licenseexpirybanner.test.tsx","./src/components/norediswarningbanner.test.tsx","./src/components/scim.test.tsx","./src/components/ssomodals.test.tsx","./src/components/sidebarusagecard.test.tsx","./src/components/teamssosettings.test.tsx","./src/components/teams.test.tsx","./src/components/tooldetail.test.tsx","./src/components/toolpoliciesview.test.tsx","./src/components/uiaccesscontrolform.integration.test.tsx","./src/components/uiaccesscontrolform.unit.test.tsx","./src/components/userbanner.test.tsx","./src/components/activity_metrics.test.tsx","./src/components/add_pass_through.integration.test.tsx","./src/components/bulk_create_users_button.test.tsx","./src/components/cloudzero_export_modal.integration.test.tsx","./src/components/email_settings.test.tsx","./src/components/key_info_utils.test.tsx","./src/components/key_value_input.test.tsx","./src/components/leftnav.test.tsx","./src/components/logging_settings_view.test.tsx","./src/components/model_info_view.test.tsx","./src/components/navbar.test.tsx","./src/components/onboarding_link.test.tsx","./src/components/pass_through_info.integration.test.tsx","./src/components/per_user_usage.test.tsx","./src/components/price_data_reload.test.tsx","./src/components/provider_info_helpers.test.tsx","./src/components/public_model_hub.test.tsx","./src/components/query_param_input.test.tsx","./src/components/route_preview.test.tsx","./src/components/settings.test.tsx","./src/components/update_model_credentials_modal.test.tsx","./src/components/user_agent_activity.test.tsx","./src/components/user_dashboard.test.tsx","./src/components/vector_store_providers.test.tsx","./src/components/aihub/agenthubtablecolumns.test.tsx","./src/components/aihub/mcphubtablecolumns.test.tsx","./src/components/aihub/modelhubtable.test.tsx","./src/components/aihub/modelhubtablecolumns.test.tsx","./src/components/aihub/skillhubtablecolumns.test.tsx","./src/components/aihub/usefullinksmanagement.test.tsx","./src/components/aihub/forms/makeagentpublicform.test.tsx","./src/components/aihub/forms/makemcppublicform.test.tsx","./src/components/aihub/forms/makemodelpublicform.test.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.test.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.test.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.test.tsx","./src/components/deletedkeyspage/deletedkeyspage.test.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.test.tsx","./src/components/deletedteamspage/deletedteamspage.test.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.test.tsx","./src/components/entityusageexport/entityusageexportmodal.test.tsx","./src/components/entityusageexport/exportformatselector.test.tsx","./src/components/entityusageexport/exportsummary.test.tsx","./src/components/entityusageexport/exporttypeselector.test.tsx","./src/components/entityusageexport/usageexportheader.test.tsx","./src/components/guardrailsmonitor/metriccard.test.tsx","./src/components/modelselect/modelselect.test.tsx","./src/components/navbar/viewswitcher.test.tsx","./src/components/navbar/blogdropdown/blogdropdown.test.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.test.tsx","./src/components/navbar/notificationsbell/notificationsbell.test.tsx","./src/components/navbar/userdropdown/userdropdown.test.tsx","./src/components/navbar/workerdropdown/workerdropdown.test.tsx","./src/components/passthroughsettings/passthroughendpointstable.test.tsx","./src/components/passthroughsettings/passthroughsettings.test.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.test.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.test.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.test.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.test.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.test.tsx","./src/components/settings/adminsettings/uisettings/uisettings.test.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.test.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.test.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.test.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.test.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.test.tsx","./src/components/teamspage/teamstable.test.tsx","./src/components/themetoggle/themetoggle.test.tsx","./src/components/toolpolicies/policyselect.test.tsx","./src/components/toolpolicies/toolpoliciespanel.test.tsx","./src/components/toolpolicies/toolpoliciestable.test.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.test.tsx","./src/components/usagepage/components/keymodelusageview.test.tsx","./src/components/usagepage/components/entityusage/topkeyview.test.tsx","./src/components/virtualkeyspage/virtualkeystable.test.tsx","./src/components/add_model/addmodelform.test.tsx","./src/components/add_model/autorouterroutingtest.test.tsx","./src/components/add_model/classifierprompteditor.integration.test.tsx","./src/components/add_model/complexityrouterconfig.test.tsx","./src/components/add_model/heuristicscoringconfig.test.tsx","./src/components/add_model/routerconfigbuilder.test.tsx","./src/components/add_model/semantickeywordmatching.test.tsx","./src/components/add_model/tiermodeleffortrows.test.tsx","./src/components/add_model/add_auto_router_tab.test.tsx","./tests/mounted-form-host.tsx","./src/components/add_model/advanced_settings.test.tsx","./src/components/add_model/auto_router_connection_test.test.tsx","./src/components/add_model/cache_control_settings.test.tsx","./src/components/add_model/conditional_public_model_name.test.tsx","./src/components/add_model/handle_add_model_submit.test.tsx","./src/components/add_model/litellm_model_name.test.tsx","./src/components/add_model/model_connection_test.test.tsx","./src/components/add_model/provider_specific_fields.test.tsx","./src/components/agent_management/agentselector.test.tsx","./src/components/alerting/dynamic_form.integration.test.tsx","./src/components/chat/chatshell.test.tsx","./src/components/chat/connectflowbanner.test.tsx","./src/components/chat/logspanel.test.tsx","./src/components/chat/mcpappspanel.test.tsx","./src/components/chat/mcpconnectpicker.test.tsx","./src/components/chat_ui/codesnippets.test.tsx","./src/components/chat_ui/reasoningcontent.test.tsx","./src/components/chat_ui/responsemetrics.test.tsx","./src/components/common_components/defaultproxyadmintag.test.tsx","./src/components/common_components/deleteresourcemodal.test.tsx","./src/components/common_components/keylifecyclesettings.test.tsx","./src/components/common_components/labeledfield.test.tsx","./src/components/common_components/loadingscreen.test.tsx","./src/components/common_components/metadatakeyvaluefields.test.tsx","./src/components/common_components/modelaliasmanager.test.tsx","./src/components/common_components/modelselector.test.tsx","./src/components/common_components/mountedformfield.test.tsx","./src/components/common_components/newbadge.tsx","./src/components/common_components/newbadge.test.tsx","./src/components/common_components/organizationdropdown.test.tsx","./src/components/common_components/passthroughguardrailssection.test.tsx","./src/components/common_components/premiumloggingsettings.test.tsx","./src/components/common_components/ratelimittypeformitem.test.tsx","./src/components/common_components/routersettingsaccordion.test.tsx","./src/components/common_components/routersettingssummary.test.tsx","./src/components/common_components/userdropdown.test.tsx","./src/components/common_components/routersettingswiring.test.tsx","./src/components/common_components/team_multi_select.test.tsx","./src/components/common_components/user_search_modal.test.tsx","./src/components/common_components/filters/filterinput.test.tsx","./src/components/common_components/filters/filtersbutton.test.tsx","./src/components/common_components/filters/resetfiltersbutton.test.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.test.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.test.tsx","./src/components/email_events/email_event_settings.test.tsx","./src/components/guardrails/guardrailselector.test.tsx","./src/components/key_team_helpers/budgetfallbackseditor.test.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.integration.test.tsx","./src/components/key_team_helpers/tagratelimiteditor.test.tsx","./src/components/key_team_helpers/fetch_available_models_team_key.test.tsx","./src/components/llm_calls/chat_completion.test.tsx","./src/components/llm_calls/fetch_models.test.tsx","./src/components/llm_calls/responses_api.test.tsx","./src/components/mcp_server_management/mcpserverselector.test.tsx","./src/components/mcp_server_management/mcptoolpermissions.test.tsx","./src/components/mcp_tools/byokcredentialmodal.test.tsx","./src/components/mcp_tools/mcptoolargumentsform.test.tsx","./src/components/mcp_tools/types.test.tsx","./src/components/model_add/credentialmodal.test.tsx","./src/components/model_add/credentialspanel.test.tsx","./src/components/model_add/credentialstable.test.tsx","./src/components/model_add/reuse_credentials.test.tsx","./src/components/model_dashboard/healthcheckcomponent.test.tsx","./src/components/model_dashboard/healthcheckstable.test.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.test.tsx","./src/components/molecules/cost_optimization_feedback_banner.test.tsx","./src/components/molecules/logo/logo.test.tsx","./src/components/molecules/models/providerlogo.test.tsx","./src/components/organisms/regeneratekeymodal.integration.test.tsx","./src/components/organisms/regeneratekeymodal.test.tsx","./src/components/organisms/create_key_button.integration.test.tsx","./src/components/organisms/create_key_button.test.tsx","./src/components/organization/organization_view.test.tsx","./src/components/organization/org-create/orgcreatedialog.test.tsx","./src/components/organization/org-settings/orgsettingsform.test.tsx","./src/components/permissions/mcpserverpermissions.test.tsx","./src/components/policies/policyselector.test.tsx","./src/components/router_settings/latencybasedconfiguration.test.tsx","./src/components/router_settings/reliabilityretriessection.test.tsx","./src/components/router_settings/routersettingsform.test.tsx","./src/components/router_settings/routingstrategyselector.test.tsx","./src/components/router_settings/tagfilteringtoggle.test.tsx","./src/components/router_settings/index.test.tsx","./src/components/routing_groups/routinggroupmodal.test.tsx","./src/components/routing_groups/routinggroupstable.test.tsx","./src/components/routing_groups/index.integration.test.tsx","./src/components/search_tools/searchtoolselector.test.tsx","./src/components/shared/alert.test.tsx","./src/components/shared/badgelink.test.tsx","./src/components/shared/copybutton.test.tsx","./src/components/shared/createdkeydisplay.test.tsx","./src/components/shared/entitylink.test.tsx","./src/components/shared/inheritedbudgethint.test.tsx","./src/components/shared/meter.test.tsx","./src/components/shared/multiselect.test.tsx","./src/components/shared/pageheader.test.tsx","./src/components/shared/paginatedmultiselect.test.tsx","./src/components/shared/paginatedsearchselect.test.tsx","./src/components/shared/paginationstatusalerts.test.tsx","./src/components/shared/searchselect.test.tsx","./src/components/shared/sidebar.test.tsx","./src/components/shared/toolbarseparator.test.tsx","./src/components/shared/advanced_date_picker.test.tsx","./src/components/shared/chart_loader.test.tsx","./src/components/shared/datatable/datatable.test-d.tsx","./src/components/shared/datatable/datatable.test.tsx","./src/components/shared/datatable/datatablefilterdrawer.test.tsx","./src/components/shared/datatable/datatablepagination.test.tsx","./src/components/shared/datatable/datatablerowselection.test.tsx","./src/components/shared/datatable/datatablesortheader.test.tsx","./src/components/shared/datatable/datatabletoolbar.test.tsx","./src/components/shared/charts/area_chart.test.tsx","./src/components/shared/charts/bar_chart.test.tsx","./src/components/shared/charts/chart_legend.test.tsx","./src/components/shared/charts/chart_tooltip.test.tsx","./src/components/shared/charts/donut_chart.test.tsx","./src/components/shared/charts/line_chart.test.tsx","./src/components/shared/form/formfield.test.tsx","./src/components/shared/table_cells/autoroutertag.test.tsx","./src/components/shared/table_cells/date_cell.test.tsx","./src/components/shared/table_cells/id_cell.test.tsx","./src/components/shared/table_cells/identity_cell.test.tsx","./src/components/shared/table_cells/models_cell.test.tsx","./src/components/shared/table_cells/money_cell.test.tsx","./src/components/shared/table_cells/spend_budget_cell.test.tsx","./src/components/shared/table_cells/status_badge.test.tsx","./src/components/tag_management/tagselector.test.tsx","./src/components/team/availableteamspanel.test.tsx","./src/components/team/editmembership.integration.test.tsx","./src/components/team/editmembership.test.tsx","./src/components/team/loggingsettings.test.tsx","./src/components/team/myusertab.test.tsx","./src/components/team/teaminfo.test.tsx","./src/components/team/teammembertab.test.tsx","./src/components/team/teamvirtualkeystable.test.tsx","./src/components/team/member_permissions.test.tsx","./src/components/team/permission_definitions.test.tsx","./src/components/templates/keyinfoheader.test.tsx","./src/components/templates/keyinfoview.handlekeyupdate.test.tsx","./src/components/templates/keysavingstab.integration.test.tsx","./src/components/templates/key_edit_view.test.tsx","./src/components/templates/key_info_view.budget_display.test.tsx","./src/components/templates/key_info_view.test.tsx","./src/components/ui/alert-dialog.test.tsx","./src/components/ui/avatar.test.tsx","./src/components/ui/badge.test.tsx","./src/components/ui/breadcrumb.test.tsx","./src/components/ui/button.test.tsx","./src/components/ui/chart.test.tsx","./src/components/ui/field.test.tsx","./src/components/ui/ref-forwarding.test.tsx","./src/components/ui/select.test.tsx","./src/components/ui/tooltip.test.tsx","./src/components/ui/ui-loading-spinner.test.tsx","./src/components/vector_store_management/vectorstoreselector.test.tsx","./src/components/view_logs/auditlogstable.test.tsx","./src/components/view_logs/configinfomessage.test.tsx","./src/components/view_logs/costbreakdownviewer.test.tsx","./src/components/view_logs/requestlogsfilters.test.tsx","./src/components/view_logs/requestlogspanel.test.tsx","./src/components/view_logs/requestlogstablecolumns.test.tsx","./src/components/view_logs/typebadges.test.tsx","./src/components/view_logs/index.integration.test.tsx","./src/components/view_logs/index.test.tsx","./src/components/view_logs/log_filter_logic.test.tsx","./src/components/view_logs/logs_utils.test.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.test.tsx","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.test.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.test.tsx","./src/components/view_logs/guardrailviewer/presidiodetectedentities.test.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.test.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.test.tsx","./src/components/view_logs/logdetailsdrawer/historytree.test.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/jsonviewer.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.test.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.test.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.test.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.test.tsx","./src/components/view_logs/logdetailsdrawer/sectionheader.test.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.test.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.test.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.test.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.test.tsx","./src/components/view_logs/toolssection/toolssection.test.tsx","./src/contexts/pluginmodecontext.test.tsx","./src/hooks/usemcpoauthflow.test.tsx","./src/hooks/usesyntaxtheme.test.tsx","./src/hooks/usetoolsoauthflow.test.tsx","./src/hooks/policies/usedeletepolicyattachment.test.tsx","./src/lib/forms/usezodform.test.tsx","./tests/createkeypage.expiredtoken.test.tsx","./tests/top_key_view.test.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./node_modules/@types/d3-array/index.d.ts","./node_modules/@types/d3-color/index.d.ts","./node_modules/@types/d3-ease/index.d.ts","./node_modules/@types/d3-interpolate/index.d.ts","./node_modules/@types/d3-timer/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/debug/index.d.ts","./node_modules/@types/estree-jsx/index.d.ts","./node_modules/@types/json5/index.d.ts","./node_modules/@types/use-sync-external-store/index.d.ts"],"fileIdsList":[[97,143,484,485,486,487],[97,143],[97,143,226,528,531,2638,2745,2779,2789,2818,2832,2843,2847,2854,2871,2978,2990,3033,3071,3094,3114,3158,3194,3218,3290,3302,3316,3444,3486,3510,3547,3568,3579,3592,3601,3613,3620,3623,3626,3646,3666,3686,3702,3704,3708,3711,3713,3716,3718,3720,3721,3723,3728,3729,3730,3731,3741],[97,143,529,530,531],[97,143,3341,3345,3346,3349,3350,3352,3354,3355,3358,3377,3402,3403,3404,3405],[97,143,3345,3353,3406],[97,143,3351],[97,143,3349,3353,3354,3406],[97,143,3406],[97,143,3347,3406],[97,143,3356,3357],[97,143,3352],[97,143,3352,3354,3355,3358,3375,3406],[97,143,3369],[97,143,3349,3355,3406],[97,143,3341,3345,3346,3348],[97,143,176],[97,143,3341],[97,138,143,3344],[97,143,3341,3349,3406],[97,143,3349,3406],[97,143,3401,3406],[97,143,3349,3371,3379,3401,3406],[97,143,3349,3371,3374,3375,3406],[97,143,3377,3406],[97,143,3395],[97,143,3349,3380,3395,3396,3398,3407],[97,143,3397],[97,143,3405],[97,143,3394],[97,143,3349,3354,3355,3359,3364,3402],[97,143,3364,3365],[97,143,3349,3355,3359,3365,3402],[97,143,3359,3360,3361,3362,3363,3365,3368,3385,3389,3392,3401],[97,143,3349,3354,3355,3359,3402],[97,143,3349,3354,3355,3358,3359,3402],[97,143,3360,3361,3362,3363,3381,3382,3383,3387,3390,3393,3402],[97,143,3366,3367,3368],[97,143,3349,3354,3355,3359,3366,3367,3402],[97,143,3349,3354,3355,3359,3366,3402],[97,143,3349,3354,3355,3359,3370,3377,3401,3402],[97,143,3378,3401],[97,143,3348,3349,3354,3359,3377,3378,3379,3380,3399,3400,3401,3402],[97,143,3348,3349,3354,3355,3359,3402],[97,143,3384,3385,3386],[97,143,3349,3354,3355,3359,3385,3402],[97,143,3349,3354,3355,3359,3365,3384,3386,3402],[97,143,3388,3389],[97,143,3349,3354,3355,3358,3359,3388,3402],[97,143,3391,3392],[97,143,3349,3354,3355,3359,3391,3402],[97,143,3348,3349,3354,3359,3377,3402,3403],[97,143,3351,3377,3402,3403,3404],[97,143,3373],[97,143,3349,3351,3354,3355,3359,3370,3377],[97,143,3372,3377],[97,143,3348,3349,3354,3359,3372,3375,3376,3377],[85,97,143,630,635],[97,143,631,635,636,637,638,639],[97,143,631,635,636,637,638],[85,97,143,627,628,630,631,634],[85,97,143,630,631,632,635],[85,97,143,627,628,630],[97,143,689,690],[97,143,693,694,695,696,697,698,699,701,702,703],[97,143,692,693,694,695,696,697,698,699,701,702],[85,97,143,226,628,691,692],[85,97,143,692,700],[97,143,707,708,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,734,736],[97,143,707,708,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,734,735],[85,97,143,630,712,713],[85,97,143,630],[85,97,143,706],[85,97,143],[85,97,143,630,738],[85,97,143,630,632,738],[97,143,738,739,740,741],[97,143,738,739,740],[97,143,743],[85,97,143,627,628,630,712],[97,143,749],[97,143,745,746,747],[97,143,745,746],[85,97,143,630,632,745],[97,143,633,751,752,753],[97,143,633,751,752],[85,97,143,630,632,633],[85,97,143,627,628,630,634],[85,97,143,632,633],[85,97,143,630,633],[85,97,143,630,713],[85,97,143,630,632],[97,143,715,717,718,719,720,721,722,723,724,725,726,727,729,730,731,734,755,756,757,758,759,760,761,762,763,764,766],[97,143,715,717,718,719,720,721,722,723,724,725,726,727,729,730,731,734,735,755,756,757,758,759,760,761,762,763,764,765],[85,97,143,630,712],[85,97,143,630,632,650,713],[85,97,143,684],[85,97,143,627,628,705],[97,143,733],[97,143,768,769,776,777,778,779,780,781,782,783,784,785,786,787,789,792,795,796,797],[97,143,732,768,769,776,777,778,779,780,781,782,783,784,785,786,787,789,792,795,796],[97,143,226,629,775,794],[85,97,143,795],[85,97,143,226],[97,143,799,800],[97,143,799],[97,143,691,694,695,696,697,698,699,700,702,802],[97,143,690,691,694,695,696,697,698,699,700,702],[85,97,143,630,632,650],[85,97,143,226,627,628,688,690],[97,143,689],[85,97,143,632,649,650,658,684,688,691,1014],[85,97,143,630,690],[85,97,143,804],[97,143,805,806],[97,143,804,805],[97,143,808,809,810,811,812,813,815,817,818,819,820,821,822,823,824,825],[97,143,690,808,809,810,811,812,813,815,817,818,819,820,821,822,823,824],[85,97,143,630,632,650,816],[85,97,143,226,627,628,688,690,816],[85,97,143,814,815],[85,97,143,630,814,816],[85,97,143,630,632,712],[97,143,712,827,828,829,830,831,832,833],[97,143,712,827,828,829,830,831,832],[85,97,143,630,711],[85,97,143,632,712],[97,143,835,836,837],[97,143,835,836],[85,97,143,679],[85,97,143,650,657,679],[85,97,143,630,661],[85,97,143,628,632,649,679,688],[85,97,143,657,679],[97,143,679],[85,97,143,672],[97,143,627,679],[97,143,657,679],[97,143,628,658,679],[97,143,668,679],[85,97,143,630,657,668,679],[97,143,667,679],[85,97,143,657,673,679],[97,143,629,649,658,688],[85,97,143,672,679],[97,143,655,657,659,662,663,664,665,669,670,671,674,675,676,677,678,679,680,681,682,683],[97,143,668],[85,97,143,628,655,657,658,659,662,663,664,665,668,669,670,671,674,675,676,677,678,680,684],[97,143,666,688],[85,97,143,627,628,630,709],[97,143,710],[97,143,629,640,704,711,737,742,744,748,750,754,765,767,794,798,801,803,807,826,834,838,840,842,844,851,866,876,881,897,910,917,921,923,931,951,961,965,972,987,989,991,999,1011,1013],[97,143,839],[85,97,143,630,834],[97,143,627],[85,97,143,710,712],[97,143,626],[85,97,143,629],[85,97,143,630,660],[85,97,143,630,775],[97,143,768,769,775,776,777,778,779,780,781,782,783,784,785,786,787,789,790,791,792,793],[97,143,732,768,769,774,775,776,777,778,779,780,781,782,783,784,785,786,787,789,790,791,792],[85,97,143,226,627,628,688,770,771,772,773,774],[85,97,143,770,775],[97,143,770],[85,97,143,630,632,649,650,657,658,684,688,775,794],[85,97,143,226,775,788],[85,97,143,770],[85,97,143,630,774],[97,143,841],[85,97,143,775],[97,143,843],[97,143,845,846,847,848,849,850],[97,143,845,846,847,848,849],[85,97,143,630,845],[97,143,852,853,854,855,856,857,858,859,860,861,862,863,864,865],[97,143,852,853,854,855,856,857,858,859,860,861,862,863,864],[85,97,143,630,632,713],[85,97,143,630,868],[97,143,868,869,870,871,872,873,874,875],[97,143,868,869,870,871,872,873,874],[85,97,143,627,628,630,712,867],[97,143,878,879,880],[97,143,732,878,879],[85,97,143,630,878],[85,97,143,627,628,630,712,877],[97,143,885,886,887,888,889,890,891,892,893,894,895,896],[97,143,884,885,886,887,888,889,890,891,892,893,894,895],[85,97,143,226,627,628,688,884],[97,143,883],[85,97,143,632,649,650,658,684,688,882,885,897,1014],[85,97,143,630,884],[97,143,900,902,903,904,905,906,907,908,909],[97,143,899,900,902,903,904,905,906,907,908],[85,97,143,901],[85,97,143,226,627,628,688,899],[97,143,898],[85,97,143,632,649,658,684,688,900,1014],[85,97,143,630,899],[97,143,911,912,913,914,915,916],[97,143,911,912,913,914,915],[85,97,143,630,911],[97,143,922],[97,143,918,919,920],[97,143,918,919],[85,97,143,630,632,918],[85,97,143,630,924],[97,143,924,925,926,927,928,929,930],[97,143,924,925,926,927,928,929],[97,143,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950],[97,143,732,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949],[97,143,732],[85,97,143,630,952],[97,143,952,953,954,955,956,958,959,960],[97,143,952,953,954,955,956,958,959],[85,97,143,630,952,957],[97,143,962,963,964],[97,143,962,963],[85,97,143,627,629,630,712],[85,97,143,630,962],[97,143,966,967,968,969,970,971],[97,143,966,967,968,969,970],[85,97,143,630,966,967],[85,97,143,630,967],[85,97,143,630,632,966,967],[85,97,143,627,628,630,966],[97,143,974],[97,143,973,974,975,976,977,978,979,980,981,982,983,984,985,986],[97,143,973,974,975,976,977,978,979,980,981,982,983,984,985],[85,97,143,630,713,974],[85,97,143,975],[85,97,143,630,632,974],[85,97,143,973],[97,143,990],[97,143,988],[85,97,143,630,993],[97,143,992,993,994,995,996,997,998],[97,143,630,992,993,994,995,996,997],[85,97,143,630,765],[97,143,1002,1003,1004,1005,1006,1007,1008,1009,1010],[97,143,1001,1002,1003,1004,1005,1006,1007,1008,1009],[85,97,143,226,627,628,688,1001],[97,143,1000],[85,97,143,632,649,658,684,688,1002,1011,1014],[85,97,143,630,1001],[85,97,143,628],[97,143,630,1012],[97,143,656,685,686,687],[85,97,143,655],[85,97,143,627,628,632,649,650,686],[97,143,630,632,658,684,685],[85,97,143,651,684],[97,143,641],[97,143,642],[97,143,642,643,645,646,647,648],[97,143,645],[85,97,143,226,645],[97,143,644,645],[97,143,2604],[97,143,651],[97,143,652,653],[85,97,143,654],[97,143,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883],[97,143,2031],[97,143,1069,1255,2030],[97,143,641,2081,2082,2083,2084],[97,143,226],[97,143,1400,1408],[97,143,1088],[97,143,1409,1410,1411,1412,1413],[97,143,1408,1410],[97,143,1409,1410],[85,97,143,1407,1408,1409],[85,97,143,226,1089],[97,143,1090],[97,143,1400,1403],[97,143,1394,1400,1401,1402,1403,1404,1405,1406],[97,143,1400],[85,97,143,1146],[97,143,1396],[97,143,1396,1397,1398,1399],[97,143,1395],[97,143,1127],[97,143,1112,1135],[97,143,1135],[97,143,1135,1146],[97,143,1121,1135,1146],[97,143,1126,1135,1146],[97,143,1116,1135],[97,143,1124,1135,1146],[97,143,1122],[97,143,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145],[97,143,1125],[97,143,1112,1113,1114,1115,1116,1117,1118,1119,1120,1122,1123,1125,1127,1128,1129,1130,1131,1132,1133,1134],[97,143,1337],[97,143,1334,1335,1336,1337,1338,1341,1342,1343,1344,1345,1346,1347,1348],[97,143,1333],[97,143,1340],[97,143,1334,1335,1336],[97,143,1334,1335],[97,143,1337,1338,1340],[97,143,1335],[97,143,2615],[97,143,2614],[85,97,143,196,460,1349,1350],[97,143,1606],[97,143,1593,1594,1595],[97,143,1588,1589,1590],[97,143,1566,1567,1568,1569],[97,143,1532,1606],[97,143,1532],[97,143,1532,1533,1534,1535,1580],[97,143,1570],[97,143,1565,1571,1572,1573,1574,1575,1576,1577,1578,1579],[97,143,1580],[97,143,1531],[97,143,1584,1586,1587,1605,1606],[97,143,1584,1586],[97,143,1581,1584,1606],[97,143,1591,1592,1596,1597,1602],[97,143,1585,1587,1597,1605],[97,143,1604,1605],[97,143,1581,1585,1587,1603,1604],[97,143,1585,1606],[97,143,1583],[97,143,1583,1585,1606],[97,143,1581,1582],[97,143,1598,1599,1600,1601],[97,143,1587,1606],[97,143,1542],[97,143,1536,1543],[97,143,1536,1537,1538,1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1553,1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564],[97,143,1562,1606],[97,143,600,601],[97,143,4064],[97,143,2071],[97,143,2094],[97,143,4068],[97,143,546,547,4070],[97,143,2657],[97,143,157,184,191,3342,3343],[97,140,143],[97,142,143],[143],[97,143,148,176],[97,143,144,149,154,162,173,184],[97,143,144,145,154,162],[92,93,94,97,143],[97,143,146,185],[97,143,147,148,155,163],[97,143,148,173,181],[97,143,149,151,154,162],[97,142,143,150],[97,143,151,152],[97,143,153,154],[97,142,143,154],[97,143,154,155,156,173,184],[97,143,154,155,156,169,173,176],[97,143,151,154,157,162,173,184],[97,143,154,155,157,158,162,173,181,184],[97,143,157,159,173,181,184],[95,96,97,98,99,100,101,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,154,160],[97,143,161,184,189],[97,143,151,154,162,173],[97,143,163],[97,143,164],[97,142,143,165],[97,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,167],[97,143,168],[97,143,154,169,170],[97,143,169,171,185,187],[97,143,154,173,174,176],[97,143,175,176],[97,143,173,174],[97,143,177],[97,140,143,173,178],[97,143,154,179,180],[97,143,179,180],[97,143,148,162,173,181],[97,143,182],[97,143,162,183],[97,143,157,168,184],[97,143,148,185],[97,143,173,186],[97,143,161,187],[97,143,188],[97,138,143],[97,138,143,154,156,165,173,176,184,187,189],[97,143,173,190],[97,143,173,191],[85,89,97,143,192,193,194,195,196,479,524],[85,89,97,143,192,193,194,195,460,479,524],[85,89,97,143,192,193,195,196,479,524],[85,97,143,196,460,461],[85,97,143,196,460],[85,97,143,1321],[85,89,97,143,193,194,195,196,479,524],[85,89,97,143,192,194,195,196,479,524],[83,84,97,143],[97,143,533,538,539,541],[97,143,587,588],[97,143,539,541,581,582,583],[97,143,539],[97,143,539,541,581],[97,143,539,581],[97,143,594],[97,143,534,594,595],[97,143,534,594],[97,143,534,540],[97,143,535],[97,143,534,535,536,538],[97,143,534],[97,143,1015,1017],[97,143,1015],[97,143,2321],[97,143,2319,2321],[97,143,2319],[97,143,2321,2385,2386],[97,143,2321,2388],[97,143,2321,2389],[97,143,2406],[97,143,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,2361,2362,2363,2364,2365,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2384,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468,2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498,2499,2500,2501,2502,2507,2508,2509,2510,2511,2512,2513,2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,2544,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558,2559,2560,2561,2562,2563,2564,2565,2566,2567,2568,2569,2570,2571,2572,2573,2574],[97,143,2321,2482],[97,143,2321,2386,2506],[97,143,2319,2503,2504],[97,143,2505],[97,143,2321,2503],[97,143,2318,2319,2320],[97,143,2007],[97,143,2006],[97,143,2008],[97,143,546,547,2605,2606,4070],[97,143,2607],[97,143,1194,1195],[97,143,1194,1195,1196,1197],[97,143,1194,1196],[97,143,1194],[97,143,157,173,191],[97,143,574,575],[97,143,2701,2704,2707,2709,2710,2711],[97,143,2668,2696,2701,2704,2707,2709,2711],[97,143,2668,2696,2701,2704,2707,2711],[97,143,2734,2735,2739],[97,143,2711,2734,2736,2739],[97,143,2711,2734,2736,2738],[97,143,2668,2696,2711,2734,2736,2737,2739],[97,143,2736,2739,2740],[97,143,2711,2734,2736,2739,2741],[97,143,2658,2668,2669,2670,2694,2695,2696],[97,143,2658,2669,2696],[97,143,2658,2668,2669,2696],[97,143,2671,2672,2673,2674,2675,2676,2677,2678,2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690,2691,2692,2693],[97,143,2658,2662,2668,2670,2696],[97,143,2712,2713,2733],[97,143,2668,2696,2734,2736,2739],[97,143,2668,2696],[97,143,2714,2715,2716,2717,2718,2719,2720,2721,2722,2723,2724,2725,2726,2727,2728,2729,2730,2731,2732],[97,143,2657,2668,2696],[97,143,2701,2702,2703,2707,2711],[97,143,2701,2704,2707,2711],[97,143,2701,2704,2705,2706,2711],[97,143,482],[97,143,430,493,494],[97,143,201,202,204,216,240,355,366,475],[97,143,204,235,236,237,239,475],[97,143,204,372,374,376,377,379,475,477],[97,143,204,238,275,475],[97,143,202,204,215,216,222,228,233,354,355,356,365,475,477],[97,143,475],[97,143,211,217,236,256,351],[97,143,204],[97,143,197,211,217],[97,143,383],[97,143,380,381,383],[97,143,380,382,475],[97,143,157,256,454,472],[97,143,157,327,330,346,351,472],[97,143,157,299,472],[97,143,359],[97,143,358,359,360],[97,143,358],[91,97,143,157,197,204,216,222,228,234,236,240,241,254,255,322,352,353,366,475,479],[97,143,201,204,238,275,372,373,378,475,527],[97,143,238,527],[97,143,201,255,425,475,527],[97,143,527],[97,143,204,238,239,527],[97,143,375,527],[97,143,241,354,357,364],[85,97,143,430],[97,143,168,211,226],[97,143,211,226],[85,97,143,296],[85,97,143,217,226,430],[97,143,211,282,296,297,509,516],[97,143,281,510,511,512,513,515],[97,143,332],[97,143,332,333],[97,143,215,217,284,285],[97,143,217,291,292],[97,143,217,286,294],[97,143,291],[97,143,209,217,284,285,286,287,288,289,290,291,294],[97,143,217,284,291,292,293,295],[97,143,217,285,287,288],[97,143,285,287,290,292],[97,143,514],[97,143,217],[85,97,143,205,503],[85,97,143,184],[85,97,143,238,273],[85,97,143,238,366],[97,143,271,276],[85,97,143,272,481],[97,143,2631],[85,89,97,143,157,192,193,194,195,196,479,523],[97,143,157,217],[97,143,157,216,221,302,319,361,362,366,422,424,475,476],[97,143,254,363],[97,143,479],[97,143,203],[85,97,143,208,211,427,443,445],[97,143,168,211,427,442,443,444,526],[97,143,436,437,438,439,440,441],[97,143,438],[97,143,442],[97,143,226,390,391,393],[85,97,143,217,384,385,386,387,392],[97,143,390,392],[97,143,388],[97,143,389],[85,97,143,226,272,481],[85,97,143,226,480,481],[85,97,143,226,481],[97,143,319,320],[97,143,320],[97,143,157,476,481],[97,143,349],[97,142,143,348],[97,143,211,217,223,225,327,340,344,346,424,427,464,465,472,476],[97,143,217,266,288],[97,143,327,338,341,346],[85,97,143,208,211,327,330,346,349,383,431,432,433,434,435,446,447,448,449,450,451,452,453,527],[97,143,208,211,236,327,334,335,336,339,340],[97,143,173,217,236,338,345,427,428,472],[97,143,342],[97,143,157,168,205,217,221,231,263,264,267,319,322,387,422,423,464,475,476,477,479,527],[97,143,208,209,211],[97,143,327],[97,142,143,236,263,264,321,322,323,324,325,326,476],[97,143,346],[97,142,143,210,211,221,225,261,327,334,335,336,337,338,341,342,343,344,345,465],[97,143,157,261,262,334,476,477],[97,143,236,264,319,322,327,424,476],[97,143,157,475,477],[97,143,157,173,472,476,477],[97,143,157,168,197,211,216,223,225,228,231,238,258,263,264,265,266,267,302,303,305,308,310,313,314,315,316,318,366,422,424,472,475,476,477],[97,143,157,173],[97,143,204,205,206,234,472,473,474,479,481,527],[97,143,201,202,475],[97,143,395],[97,143,157,173,184,213,379,383,384,385,386,387,393,394,527],[97,143,168,184,197,211,213,225,228,264,303,308,318,319,372,399,400,401,408,411,412,422,424,472,475],[97,143,228,234,241,254,264,322,475],[97,143,157,184,205,216,225,264,406,472,475],[97,143,426],[97,143,157,395,409,410,419],[97,143,472,475],[97,143,324,465],[97,143,225,263,366,481],[97,143,157,168,203,308,368,372,401,408,411,414,472],[97,143,157,241,254,372,415],[97,143,204,265,366,417,475,477],[97,143,157,184,387,475],[97,143,157,238,265,366,367,368,377,395,416,418,475],[91,97,143,157,263,421,479,481],[97,143,317,422],[97,143,157,168,211,214,216,217,223,225,231,240,241,254,264,267,303,305,315,318,319,366,399,400,401,402,404,407,422,424,472,481],[97,143,157,173,241,408,413,419,472],[97,143,244,245,246,247,248,249,250,251,252,253],[97,143,258,309],[97,143,311],[97,143,309],[97,143,311,312],[97,143,157,215,216,217,221,222,476],[97,143,157,168,203,205,223,227,263,266,267,301,422,472,477,479,481],[97,143,157,168,184,207,214,215,225,227,264,420,465,471,476],[97,143,334],[97,143,335],[97,143,217,228,464],[97,143,336],[97,143,210],[97,143,212,224],[97,143,157,212,216,223],[97,143,219,224],[97,143,220],[97,143,212,213],[97,143,212,268],[97,143,212],[97,143,214,258,307],[97,143,306],[97,143,211,213,214],[97,143,214,304],[97,143,211,213],[97,143,263,366],[97,143,464],[97,143,157,184,223,225,229,263,366,421,424,427,428,429,455,456,459,463,465,472,476],[97,143,277,280,282,283,296,297],[85,97,143,194,195,196,226,457,458],[85,97,143,194,195,196,226,457,458,462],[97,143,350],[97,143,236,257,262,263,327,328,329,330,331,333,346,347,349,352,421,424,475,477],[97,143,296],[97,143,157,301,472],[97,143,301],[97,143,157,223,269,298,300,302,421,472,479,481],[97,143,277,278,279,280,282,283,296,297,480],[91,97,143,157,168,184,212,213,225,231,263,264,267,366,419,420,422,472,475,476,479],[97,143,208,211,218],[97,143,262,264,396,399],[97,143,262,397,466,467,468,469,470],[97,143,157,258,475],[97,143,157],[97,143,261,346],[97,143,260],[97,143,262,315],[97,143,259,261,475],[97,143,157,207,262,396,397,398,472,475,476],[85,97,143,211,217,295],[85,97,143,209],[97,143,199,200],[85,97,143,205],[85,97,143,211,281],[85,91,97,143,263,267,479,481],[97,143,205,503,504],[85,97,143,276],[85,97,143,168,184,203,270,272,274,275,481],[97,143,211,238,476],[97,143,211,403],[85,97,143,155,157,168,201,203,276,374,479,480],[85,97,143,192,193,194,195,196,479,524],[85,86,87,88,89,97,143],[97,143,148],[97,143,369,370,371],[97,143,369],[85,89,97,143,157,159,168,191,192,193,194,195,196,197,203,231,236,414,442,477,478,481,524],[97,143,489],[97,143,491],[97,143,495],[97,143,2632],[97,143,497],[97,143,499,500,501],[97,143,505],[90,97,143,483,488,490,492,496,498,502,506,508,518,519,521,525,526,527,528],[97,143,507],[97,143,517],[97,143,272],[97,143,520],[97,142,143,262,396,397,399,466,467,469,470,522,524],[97,143,191],[85,97,143,1612],[85,97,143,1611],[97,143,1611,1614],[97,143,2885,2886,2891],[97,143,2887,2888,2890,2892],[97,143,2891],[97,143,2888,2890,2891,2892,2893,2895,2897,2898,2899,2900,2901,2902,2903,2907,2922,2933,2936,2940,2948,2949,2951,2954,2957,2960],[97,143,2891,2898,2911,2915,2924,2926,2927,2928,2955],[97,143,2891,2892,2908,2909,2910,2911,2913,2914],[97,143,2915,2916,2923,2926,2955],[97,143,2891,2892,2897,2916,2928,2955],[97,143,2892,2915,2916,2917,2923,2926,2955],[97,143,2888],[97,143,2894,2915,2922,2928],[97,143,2922],[97,143,2891,2911,2918,2920,2922,2955],[97,143,2915,2922,2923],[97,143,2924,2925,2927],[97,143,2955],[97,143,2904,2905,2906,2956],[97,143,2891,2892,2956],[97,143,2887,2891,2905,2907,2956],[97,143,2891,2905,2907,2956],[97,143,2891,2893,2894,2895,2956],[97,143,2891,2893,2894,2908,2909,2910,2912,2913,2956],[97,143,2913,2914,2929,2932,2956],[97,143,2928,2956],[97,143,2891,2915,2916,2917,2923,2924,2926,2927,2956],[97,143,2894,2930,2931,2932,2956],[97,143,2891,2956],[97,143,2891,2893,2894,2914,2956],[97,143,2887,2891,2893,2894,2908,2909,2910,2912,2913,2914,2956],[97,143,2891,2893,2894,2909,2956],[97,143,2887,2891,2894,2908,2910,2912,2913,2914,2956],[97,143,2894,2897,2956],[97,143,2897],[97,143,2887,2891,2893,2894,2896,2897,2898,2956],[97,143,2896,2897],[97,143,2891,2893,2897,2956],[97,143,2957,2958],[97,143,2887,2891,2897,2898,2956],[97,143,2891,2893,2935,2956],[97,143,2891,2893,2934,2956],[97,143,2891,2893,2894,2922,2937,2939,2956],[97,143,2891,2893,2939,2956],[97,143,2891,2893,2894,2922,2938,2956],[97,143,2891,2892,2893,2956],[97,143,2942,2956],[97,143,2891,2937,2956],[97,143,2944,2956],[97,143,2891,2893,2956],[97,143,2941,2943,2945,2947,2956],[97,143,2891,2893,2941,2946,2956],[97,143,2937,2956],[97,143,2922,2956],[97,143,2894,2895,2898,2899,2900,2901,2902,2903,2907,2922,2933,2936,2940,2948,2949,2951,2954,2959],[97,143,2891,2893,2922,2956],[97,143,2887,2891,2893,2894,2918,2919,2921,2922,2956],[97,143,2891,2900,2950,2956],[97,143,2891,2893,2952,2954,2956],[97,143,2891,2893,2954,2956],[97,143,2891,2893,2894,2952,2953,2956],[97,143,2892],[97,143,2889,2891,2892],[97,143,1290],[97,143,1091,1290,1291],[97,143,568],[97,143,566,568],[97,143,557,565,566,567,569,571],[97,143,555],[97,143,558,563,568,571],[97,143,554,571],[97,143,558,559,562,563,564,571],[97,143,558,559,560,562,563,571],[97,143,555,556,557,558,559,563,564,565,567,568,569,571],[97,143,571],[97,143,553,555,556,557,558,559,560,562,563,564,565,566,567,568,569,570],[97,143,553,571],[97,143,558,560,561,563,564,571],[97,143,562,571],[97,143,563,564,568,571],[97,143,556,566],[97,143,1339],[85,97,143,1051],[97,143,1051,1052,1053,1054,1055,1058,1059,1060,1061,1062,1063,1064,1067,1068],[97,143,1051],[97,143,1056,1057],[85,97,143,1048,1051],[97,143,1045,1046,1048],[97,143,1041,1044,1046,1048],[97,143,1045,1048],[85,97,143,1036,1037,1038,1041,1042,1043,1045,1046,1047,1048],[97,143,1038,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050],[97,143,1045],[97,143,1039,1045,1046],[97,143,1039,1040],[97,143,1044,1046,1047],[97,143,1044],[97,143,1036,1041,1044,1046,1047],[85,97,143,1041,1044,1045,1046],[97,143,1065,1066],[85,97,143,2258],[85,97,143,2257],[97,143,2699],[85,97,143,2658,2667,2696,2698],[85,97,143,2109,2110,2157],[97,143,2202,2203],[97,143,2109],[97,143,2157],[85,97,143,2204],[85,97,143,2076,2086,2089,2091,2097,2098,2105,2107,2108,2110,2111,2112,2114,2154,2157],[85,97,143,2097,2157],[85,97,143,2076,2086,2089,2091,2096,2098,2107,2109,2110,2111,2115,2117,2118,2154,2157],[85,97,143,2107,2115,2159],[85,97,143,2090,2157],[85,97,143,2075,2076,2078,2086,2157],[85,97,143,2076,2086,2107,2148,2157],[85,97,143,2076,2116,2137,2141,2157],[85,97,143,2089,2098,2110,2111,2124,2125,2157,2198],[97,143,2075,2157],[97,143,2086,2157],[85,97,143,2076,2086,2089,2091,2097,2098,2110,2111,2136,2154,2157],[85,97,143,2076,2078,2115,2128,2181],[85,97,143,2074,2076,2078,2128],[85,97,143,2076,2078,2106,2128,2129,2157],[85,97,143,2076,2086,2089,2093,2097,2098,2110,2111,2125,2138,2140,2154,2157],[85,97,143,2080,2086,2157],[85,97,143,2080,2086,2154,2157],[85,97,143,2157],[85,97,143,2157,2214],[85,97,143,2115,2125,2157],[85,97,143,2075,2125,2157],[85,97,143,2125,2157],[85,97,143,2087],[85,97,143,2076,2125,2157],[85,97,143,2074,2076,2157],[85,97,143,2075,2076,2077,2157],[85,97,143,2075,2076,2078,2157,2214],[85,97,143,2099,2100,2101],[85,97,143,2086,2088,2089,2100,2125,2157,2160],[97,143,2147,2157],[97,143,2086,2087,2106,2152,2154,2157],[97,143,2074,2075,2076,2078,2079,2080,2086,2087,2089,2097,2098,2099,2102,2106,2108,2109,2110,2111,2112,2113,2115,2116,2125,2128,2130,2136,2137,2138,2140,2141,2142,2149,2152,2153,2154,2157,2158,2159,2161,2162,2163,2164,2165,2166,2167,2168,2170,2172,2174,2175,2176,2177,2178,2179,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2208,2209,2210,2211,2212,2213],[85,97,143,2076,2089,2091,2098,2110,2111,2120,2122,2124,2139,2157,2173,2214],[85,97,143,2076,2080,2086,2129,2157,2171],[85,97,143,2076,2086],[85,97,143,2076,2080,2086,2129,2157,2169],[85,97,143,2076,2098,2106,2110,2111,2121,2129,2157],[85,97,143,2076,2086,2089,2091,2096,2098,2107,2110,2111,2154,2157,2165,2173,2176],[85,97,143,2096,2157],[85,97,143,2109,2157],[97,143,2081,2085,2157],[97,143,2079,2080,2081,2085,2154,2157],[97,143,2081,2085,2090],[97,143,2081,2085,2124,2142,2157],[97,143,2081,2085,2086,2091,2092,2093,2114,2118,2119,2122,2123,2157],[97,143,2081,2085,2099,2102,2157],[97,143,2081,2085,2125,2157],[97,143,2081,2085,2086],[97,143,2081,2085],[97,143,2081,2082,2085,2086,2128,2130],[97,143,2081,2082,2085,2086,2157],[97,143,2081,2085,2087,2113,2157],[97,143,2105,2124,2147,2157],[97,143,2086,2091,2104,2105,2106,2124,2131,2134,2143,2147,2149,2150,2151,2153,2157],[97,143,2086,2091,2104,2105],[97,143,2147],[97,143,2085,2086,2091,2103,2124,2125,2126,2127,2131,2132,2133,2134,2135,2143,2144,2145,2146],[97,143,2081,2085,2086,2088,2089,2124,2157],[97,143,2091,2104,2113,2124,2157],[97,143,2104,2117,2124],[97,143,2091,2124,2157],[85,97,143,2089,2120,2121,2124,2157],[97,143,2124],[97,143,2104,2124],[97,143,2089,2091,2124,2157],[97,143,2107,2124,2157],[97,143,2125,2157],[85,97,143,2115,2116,2157],[97,143,2089,2096,2103,2105,2106,2125,2154,2157],[85,97,143,2089,2113,2116,2137,2141,2157,2161,2184,2185,2186,2199],[85,97,143,2089,2157,2161,2170,2172,2174,2175,2177],[85,97,143,2157,2177,2214],[97,143,2086,2157,2207],[97,143,2080,2157],[85,97,143,2124,2138,2139,2141,2157],[97,143,2096,2104,2107,2124],[85,97,143,2120,2180],[85,97,143,2073,2074,2075,2078,2079,2080,2086,2087,2088,2091,2109,2113,2120,2154,2155,2156,2214],[97,143,2081],[97,143,2708,2741,2742],[97,143,2743],[97,143,2696,2697],[97,143,2658,2662,2667,2668,2696],[97,143,547,579,580],[97,143,173,191,405],[97,143,537],[97,143,2664],[97,110,114,143,184],[97,110,143,173,184],[97,105,143],[97,107,110,143,181,184],[97,143,162,181],[97,105,143,191],[97,107,110,143,162,184],[97,102,103,106,109,143,154,173,184],[97,110,117,143],[97,102,108,143],[97,110,131,132,143],[97,106,110,143,176,184,191],[97,131,143,191],[97,104,105,143,191],[97,110,143],[97,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,143],[97,110,125,143],[97,110,117,118,143],[97,108,110,118,119,143],[97,109,143],[97,102,105,110,143],[97,110,114,118,119,143],[97,114,143],[97,108,110,113,143,184],[97,102,107,110,117,143],[97,143,173],[97,105,110,131,143,189,191],[97,143,2662,2666],[97,143,2657,2662,2663,2665,2667],[97,143,3321,3322,3323,3324,3325,3326,3327,3329,3330,3331,3332,3333,3334,3335,3336],[97,143,3323],[97,143,3323,3328],[97,143,2659],[97,143,2660,2661],[97,143,2657,2660,2662],[97,143,2072],[97,143,2095],[97,143,591,592],[97,143,591],[97,143,543],[97,143,154,155,157,158,159,162,173,181,184,190,191,543,544,545,547,548,550,551,552,572,573,577,578,579,580],[97,143,543,544,545,549],[97,143,545],[97,143,576],[97,143,547,580],[97,143,542,611,1191],[97,143,584,603,604,1191],[97,143,534,541,584,596,597,1191],[97,143,606],[97,143,585],[97,143,534,542,584,586,596,605,1191],[97,143,589],[97,143,146,155,173,534,539,541,580,584,586,589,590,593,596,598,599,602,605,607,608,610,1191],[97,143,584,603,604,605,1191],[97,143,580,609,610],[97,143,584,586,593,596,598,1191],[97,143,189,599],[97,143,146,155,173,534,539,541,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,1191],[97,143,585,586],[97,143,146,155,173,189,533,534,539,541,542,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,1190,1191,1192,1193,1198],[97,143,2020,2021],[97,143,2018,2019,2020,2022,2023,2028],[97,143,2019,2020],[97,143,2028],[97,143,2029],[97,143,2020],[97,143,2018,2019,2020,2023,2024,2025,2026,2027],[97,143,2018,2019,2030],[97,143,1255],[97,143,1255,1258],[97,143,1248,1255,1256,1257,1258,1259,1260,1261,1262],[97,143,1263],[97,143,1255,1256],[97,143,1255,1257],[97,143,1201,1203,1204,1205,1206],[97,143,1201,1203,1205,1206],[97,143,1201,1203,1205],[97,143,1201,1203,1204,1206],[97,143,1201,1203,1206],[97,143,1201,1202,1203,1204,1205,1206,1207,1208,1248,1249,1250,1251,1252,1253,1254],[97,143,1203,1206],[97,143,1200,1201,1202,1204,1205,1206],[97,143,1203,1249,1253],[97,143,1203,1204,1205,1206],[97,143,1264],[97,143,1205],[97,143,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247],[97,143,164,226],[85,97,143,226,1091,1199,1351,1607,2785],[85,97,143,226,617,1020,1021,1022,1024,1029,1030,1091,1095,1266,1267,1293,1301,1384,1389,1460,1906,2033,2781],[97,143,226,1199,1267],[97,143,226,624,1266],[97,143,226,1265],[97,143,226,1199,1351,1384,1385,1607,2784,2790],[85,97,143,226,1020,1024,1077,1099,1301,1313,1385,1952,2752,2783],[97,143,226,1021,1022,1024,1029,1030,1069,1265,1301,1389,1460,1906,2781],[97,143,226,1199,1384,1607,2783,2790],[85,97,143,226,617,1020,1095,1384,1388,2033,2782],[97,143,226,1199,1351,1384,1607,2788,2790],[85,97,143,226,1020,1023,1024,1087,1094,1189,1384,1387,2749,2760,2784,2785,2787],[85,97,143,226,1024,1147,1149,1161,1189,2786],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1189,1314],[97,143,226,1094,2788],[97,143,226,1199,1351,1607,2817],[85,97,143,226,617,1020,1021,1024,1029,1077,1094,1095,1151,1187,1265,1301,1906,1908,2033,2795,2796,2797,2799,2807,2809,2810,2813,2814,2815,2816],[97,143,226,1094,1380,2817],[97,143,226,1199,1351,2745],[85,97,143,226,1187,1199,1351,1607,2825],[85,97,143,226,617,1020,1021,1022,1024,1026,1028,1029,1030,1032,1035,1069,1079,1081,1083,1094,1095,1099,1176,1187,1269,1270,1306,1313,1903,1915,1918,1919,2769,2798,2820,2822,2823,2824],[85,97,143,226,1187,1199,1351,1607,2790,2823],[85,97,143,226,1020,1021,1022,1024,1035,1079,1080,1099,1156,1187,1270,1313,1414,1908],[85,97,143,226,1199,1272,1351,2790,2827],[85,97,143,226,1272],[97,143,226,1199,1270],[97,143,226,1187],[85,97,143,226,1020,1021,1022,1024,1029,1030,1069,1079,1269,2820,2821],[85,97,143,226,1187,1199,1351,1607,2828],[85,97,143,226,1187,1199,1272,1351,2828],[85,97,143,226,617,1019,1020,1021,1024,1028,1029,1032,1035,1069,1077,1187,1269,1270,1272,1273,1301,1313,1445,2774,2820,2822,2823,2824,2826,2827],[97,143,226,1187,1272],[85,97,143,226,1032,1199,1351,1607,2790,2826],[85,97,143,226,1020,1024,1032,1035],[85,97,143,226,1021,1024,1025,1029,1035,1069,1080],[85,97,143,226,1187,1199,1351,1607,2831],[85,97,143,226,617,1020,1024,1032,1087,1187,1272,1300,1908,2825,2828,2830],[97,143,226,1199,1272,1351,1607,2830],[85,97,143,226,1024,1035,1079,1147,1149,1161,1272,2829],[97,143,226,1019,1020,1024,1099,1147,1149,1161,1177,1272,1314],[85,97,143,226,1021,1269,2820],[85,97,143,226,1021,1022,1029,1030,1187,1269,2798,2820,2821],[97,143,226,1094,1503,2831],[97,143,226,1199,1351,2778],[85,97,143,226,518,1032,1094,1503,1923,2635,2777],[85,97,143,226,1094,2652,2778],[97,143,226,1199,1351,1607,2845],[85,97,143,226,1301,1324,2844],[85,97,143,226,1019,1024],[97,143,226,1094,1380,2845,2846],[85,97,143,226,1199,1351,1607,2849],[85,97,143,226,617,1020,1021,1024,1029,1030,1080,1095,1265,1274,1417,1906,2033],[85,97,143,226,616,1091,1199,1351,1607,2853],[85,97,143,226,617,1020,1024,1087,1094,1276,1301,1321,1323,1417,2749,2760,2849,2851,2852],[97,143,226,1199,1274],[97,143,226,616,1166,1199,1351,1416,1417,1607,2790,2851],[85,97,143,226,616,1021,1024,1027,1156,1161,1392,1416,1417,2850],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1314,1417,1652],[85,97,143,226,624,1199,1351,1607,2852],[85,97,143,226,617,1020,1021,1024,1029,1030,1069,1080,1095,1274,1417,1906],[97,143,226,1094,2853],[85,97,143,226,1199,1351,2790,2870],[85,97,143,226,617,1020,1024,1025,1077,1187,1301,1418,1964,2222,2755,2860,2864,2868,2869],[85,97,143,226,1199,1351,1607,2790,2860],[85,97,143,226,1020,1024,1301,2859],[85,97,143,226,1277,1278,2862],[85,97,143,226,1021,1022,1025,1069,1079,1277,1278,1906,2798],[97,143,226,1199,1277,1278],[97,143,226,1277],[97,143,226,1199,1351,1607,2864],[85,97,143,226,617,1020,1024,1069,1080,1082,1187,1277,1278,2861,2862,2863],[97,143,226,1199,1351,2861],[85,97,143,226,1030],[85,97,143,226,1280,1281,2865],[85,97,143,226,1021,1022,1069,1079,1280,1281,1906,2798],[85,97,143,226,1199,1280,1351,1607,2790,2867],[85,97,143,226,1030,1280],[97,143,226,1074,1199,1281],[97,143,226,1074,1176,1280],[97,143,226,617,1091,1187,1199,1351,1607,2868],[97,143,226,617,1074,1091,1187,1199,1351,1607,2868],[85,97,143,226,617,1020,1069,1176,1280,1281,1313,1434,2866,2867],[85,97,143,226,1199,1351,2869],[85,97,143,226,624,1020,1024,1077,2216,2222],[97,143,226,1094,2870],[85,97,143,226,1094,1187,1958],[97,143,226,1199,1283],[97,143,226,624],[85,97,143,226,616,1091,1162,1199,1283,1296,1351,2976],[85,97,143,226,616,1028,1030,1035,1077,1099,1151,1162,1166,1283,1286,1295,1296,1301,2755,2974,2975],[97,143,226,1199,1285,1295,1351,2971],[85,97,143,226,1024,1035,1077,1151,1166,1286,1295,1301,2755],[97,143,226,1187,1199,1285,1286],[97,143,226,1166,1187,1285],[85,97,143,226,1091,1199,1351,2977],[85,97,143,226,1024,1295,1301,1369,2749,2881,2882,2883,2972,2976],[97,143,226,1199,1288],[97,143,226,1199,1351,2972],[85,97,143,226,617,1187,1295,2970,2971],[97,143,226,1199,1351,1607,2883],[85,97,143,226,617,1020,1021,1024,1029,1035,1077,1079,1187,1265,1288,1313,1906,2033],[85,97,143,226,616,1199,1298,1351,1445,1607,2974],[85,97,143,226,616,1020,1021,1026,1027,1030,1077,1094,1099,1151,1162,1286,1298,1445,1463,2973],[85,97,143,226,1162,1199,1283,1351,2975],[85,97,143,226,1077,1106,1108,1110,1162,1283,2222],[97,143,226,1187,1199,1285,1351,1607,2882],[85,97,143,226,1077,1187,1286,1295,1301,1369,2222,2755,2757],[97,143,226,1199,1296],[97,143,226,1187,1293,1295],[97,143,226,1199,1295,1351],[97,143,226,1187,1199,1295,1351],[85,97,143,226,1087,1187,1285,1294],[97,143,226,1199,1298],[97,143,226,617,624,1091,1094,1293],[97,143,226,1094,2977],[85,97,143,226,1199,1302,1310,1351,1607,2790],[85,97,143,226,1020,1021,1024,1025,1029,1035,1083,1302,1305,1306],[97,143,226,1199,1302,1308,1351,1607,2790],[85,97,143,226,1199,1302,1305,1308,1351,1607,2790],[85,97,143,226,1020,1021,1023,1024,1025,1029,1035,1302,1305,1306],[85,97,143,226,1199,1330,1351,1607,2790],[85,97,143,226,1020,1024,1079,1080,1082,1095,1300,1301,1302,1307,1308,1309,1310,1319,1320,1325,1327,1328,1329],[85,97,143,226,1199,1325,1351,1607,2790],[85,97,143,226,1021,1027,1324],[97,143,226,1302,1307,1308,1309,1310,1325,1326,1327,1328,1330],[85,97,143,226,1199,1311,1319,1351,1607,2790],[85,97,143,226,1020,1021,1024,1026,1083,1151,1311,1317,1318],[85,97,143,226,1199,1302,1311,1317,1351,1607,2790],[85,97,143,226,1020,1024,1028,1077,1099,1151,1166,1302,1311,1313,1316],[85,97,143,226,1199,1311,1315,1316,1607,2790],[85,97,143,226,1020,1024,1311,1314,1315],[97,143,226,1199,1302,1311,1315],[97,143,226,1166,1302,1311],[97,143,226,1302],[97,143,226,1199,1302,1311,1318,1351],[85,97,143,226,1187,1302,1311],[85,97,143,226,1199,1307,1351,1607,2790],[85,97,143,226,1020,1021,1024,1302,1303,1305,1306],[97,143,226,1199,1326],[97,143,226,1305],[85,97,143,226,1199,1305,1309,1351,1607,2790],[97,143,226,617,1187,1199,1329,1351],[85,97,143,226,617,1187],[97,143,226,617,1199,1327,1351],[85,97,143,226,617,1187,1302,1305,1326],[97,143,226,617,1199,1328,1351],[97,143,226,1094,1331],[97,143,226,1199,1351,1607,3063],[85,97,143,226,1020,1022,1024,1026,1082,1095],[97,143,226,1199,1351,1607,3074],[85,97,143,226,1020,1021,1022,1024,1027,1030,1079],[97,143,226,1091,1199,1351,1607,3066],[85,97,143,226,1020,1024,1091,1099,1176,1187,1301,1313,1975,3063,3064,3065],[97,143,226,1091,1187,1199,1351,3069],[85,97,143,226,1187,1964,2755,3066,3068],[97,143,226,1091,1187,1199,1351,1607,3068],[85,97,143,226,1020,1024,1091,1147,1149,1161,1187,1313,1975,2749,3063,3065,3067],[85,97,143,226,1199,1351,2790,3067],[85,97,143,226,1077,2222],[97,143,226,1199,1351,2790,3071],[97,143,226,1094,1369,3069,3070],[85,97,143,226,1187,1199,1351,1607,2790,3018],[85,97,143,226,1199,1351,2790,3018],[85,97,143,226,617,1020,1021,1022,1025,1029,1030,1035,1069,1076,1095,1187,1306,1313,1358,3009,3010,3011,3012,3013,3014,3016,3017],[85,97,143,226,1020,1024,1030,1099,1147,1149,1161,1361],[85,97,143,226,1187,1199,1351,1607,3009],[85,97,143,226,1029,1030,1077,1079,1187,1649,3008],[85,97,143,226,1020,1024,1025,1030,1077,1080,1099,1147,1149,1161,1187,1361],[97,143,226,1199,1607,2790,3010],[85,97,143,226,617,1020,1024,1077,1187,1313,3002,3003,3004,3005,3006,3007,3009],[97,143,226,1199,2790,3022],[85,97,143,226,1077,1099,3005,3006,3021],[97,143,226,1199,1351,1607,3023],[85,97,143,226,1024,1028,1908,3009,3010,3022],[97,143,226,1199,1607,2790,3005,3006,3007,3021],[97,143,226,1199,1351,1607,3003],[85,97,143,226,1020,1021,1030,1095,1361],[97,143,226,1199,1351,1607,3004],[85,97,143,226,1020,1021,1022,1030,1095,1361],[85,97,143,226,1020,1024,1030,1147,1149,1161,1361],[97,143,226,1199,1351,1607,3002],[85,97,143,226,1020,1025,1030,1095,1361],[85,97,143,226,1199,1351,1607,1649],[85,97,143,226,1025],[85,97,143,226,1199,1351,1607,3008],[85,97,143,226,1021],[97,143,226,1187,1199,1351,1362,1607],[85,97,143,226,617,1020,1021,1022,1024,1025,1030,1079,1080,1095,1187,1313],[97,143,226,1362],[97,143,226,1199,1351,1359,1607,3030],[85,97,143,226,1023,1024,1359,3028,3029],[97,143,226,1199,1351,1359,1607,3028],[85,97,143,226,1024,1306,1359],[97,143,226,1199,1359],[97,143,226,1358],[97,143,226,1199,1351,1359,3029],[85,97,143,226,1020,1024,1306,1357,1359,3018],[97,143,226,1187,1199,1351,1607,2790,3024],[97,143,226,1187,1199,1351,1607,3024],[85,97,143,226,617,1020,1021,1022,1024,1028,1029,1030,1035,1069,1077,1099,1166,1187,1301,1306,1358,1362,3011,3012,3013,3016,3017,3023],[97,143,226,1199,1358],[97,143,226,530],[85,97,143,226,1020,1021,1030,1076,1889,2798,3011],[85,97,143,226,1021,1029,1030,1076,1084,1187,1313,1358,1889,2798,3011],[97,143,226,1199,1351,1607,2042,3020],[85,97,143,226,1024,1147,1149,1161,2042,3019],[85,97,143,226,1024,1029,1030,1035,1069],[85,97,143,226,1187,1199,1351,1358,3032],[85,97,143,226,617,1019,1020,1024,1087,1187,1301,1314,1358,1363,2042,2760,3018,3020,3024,3027,3030,3031],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1306,1314,1358,2042],[97,143,226,1199,1351,1607,3026],[85,97,143,226,617,1020,1022,1024,1035,1313,3025],[97,143,226,1199,1351,1607,3027],[85,97,143,226,617,1023,1024,1077,1187,1313,1358,2042,3026],[97,143,226,1199,1351,1607,3025],[85,97,143,226,617,1020,1024,1077],[85,97,143,226,1020,1021,1023,1024,1025,1029,1030,1069,3011],[97,143,226,1199,1351,2042,3015],[85,97,143,226,1020,1024,1025,1030,1035,1099,1156,2042],[97,143,226,1199,1351,3016],[85,97,143,226,2042,3015],[97,143,226,1094,1187,1199,1351,1607,2790,3031],[97,143,226,1094,1187,1199,1351,2790,3031],[85,97,143,226,617,1020,1021,1022,1024,1029,1030,1035,1087,1094,1095,1187,1265,1414,1415,1441,1903,1906,2033,2303],[85,97,143,226,1199,1351,1607,3017],[85,97,143,226,1020,1021,1022,1024,1028,1030,1035,1077],[97,143,226,1094,3032],[97,143,226,1087,1091,1094,1187,1384],[85,97,143,226,1091,1094,1187,1199,1351,1384],[97,143,226,1087,1091,1092,1094,1187],[97,143,226,1091,1094,1187,1384],[85,97,143,226,1091,1187,1199,1272,1351,1389],[97,143,226,1087,1091,1092,1094,1187,1272],[97,143,226,1091,1092,1187],[97,143,226,1091,1187],[97,143,226,1199,1392],[97,143,226,1147,1149],[85,97,143,226,624,1091,1092,1094,1147,1149,1187,1392,1416],[97,143,226,1199,1351,1418],[97,143,226,624,1094,1293],[85,97,143,226,1091,1199,1351,1420],[85,97,143,226,1091,1199,1351,1422],[85,97,143,226,1091,1199,1351,1424],[85,97,143,226,1091,1199,1351,1426,1427],[97,143,226,1091,1092,1187,1426],[97,143,226,1092,1199],[85,97,143,226,1091,1147,1149,1199,1351,1416],[85,97,143,226,624,1091,1147,1149,1414,1415],[97,143,226,1091,1430,1431],[97,143,226,1091,1092,1094,1430],[97,143,226,1074,1091,1092,1094,1187],[85,97,143,226,1091,1187,1199,1351,1435],[97,143,226,1091,1092,1094,1187],[97,143,226,1199,1351,1437],[97,143,226,624,1087,1094,1293],[85,97,143,226,1091,1187,1199,1351,1439],[85,97,143,226,1091,1187,1199,1351,1443],[97,143,226,1032,1091,1094,1187,1445],[85,97,143,226,1032,1091,1199,1351,1445],[97,143,226,1032,1091,1092,1094,1187],[97,143,226,1091,1094,1187,1445],[85,97,143,226,1091,1187,1199,1351,1449],[97,143,226,1091,1094,1187],[85,97,143,226,1091,1094,1187,1199,1351,1456],[85,97,143,226,1091,1094,1187,1199,1351,1458],[85,97,143,226,1091,1092,1094,1187],[85,97,143,226,1091,1094,1187,1199,1351,1460],[97,143,226,1073,1091,1092,1094,1187],[85,97,143,226,1091,1187,1199,1351,1463],[85,97,143,226,1091,1162,1187,1199,1351],[85,97,143,226,1091,1187,1199,1351,1466],[97,143,226,1091,1092,1093,1187],[85,97,143,226,1091,1187,1199,1351,1367],[85,97,143,226,1091,1199,1351,1469,1470],[97,143,226,1091,1094,1187,1469],[85,97,143,226,1091,1199,1351,1469,1472],[85,97,143,226,1091,1199,1351,1469,1474],[97,143,226,1087,1091,1094,1187,1469],[85,97,143,226,1091,1199,1351,1469],[85,97,143,226,1091,1199,1351,1469,1477],[85,97,143,226,1091,1187,1199,1351,1479],[85,97,143,226,1091,1199,1351,1481],[97,143,226,1091,1092,1379],[85,97,143,226,1091,1199,1351,1483],[97,143,226,1091,1092,1094,1187,1486],[97,143,226,1199,1351,1488],[97,143,226,1199,1351,1490],[97,143,226,1094,1293,1488],[85,97,143,226,1091,1187,1199,1351,1492],[85,97,143,226,1091,1187,1199,1351,1494],[85,97,143,226,1091,1094,1199,1351,1496],[97,143,226,1091,1094,1187,1481],[85,97,143,226,623,1091,1187,1199,1351,1499],[97,143,226,623,1091,1092,1094,1187],[97,143,226,1199,1501],[97,143,226,616,1091,1092,1094,1187],[85,97,143,226,1032,1091,1187,1188,1199,1351,1503],[97,143,226,1032,1087,1091,1092,1094,1187,1188],[85,97,143,226,1091,1093,1187,1199,1351],[85,97,143,226,1091,1187,1199,1351,1506,1507],[97,143,226,1506],[85,97,143,226,1091,1187,1199,1351,1506],[85,97,143,226,1091,1187,1199,1351,1510],[85,97,143,226,1091,1094,1199,1351],[85,97,143,226,620,622,1086,1091,1094,1187,1199,1351],[85,97,143,226,620,622,1086,1087,1093,1187],[97,143,226,1094,1366,1368],[85,97,143,226,1370],[97,143,226,1199,1351,1370,1373],[97,143,226,1199,1351,1370,1375],[97,143,226,1187,1199,1351,1368],[97,143,226,1087,1094,1367],[97,143,226,620,1086,1380],[97,143,226,1091,1187,1512],[85,97,143,226,1091,1187,1199,1351,1514],[85,97,143,226,1091,1187,1199,1351,1516],[97,143,226,1199,1351,1382,1383],[85,97,143,226,518,1382],[85,97,143,226,1032,1094,1188],[97,143,226,1187,1199,1351,2635,2745],[85,97,143,226,518,616,1178,1187,1947,2635,2645,2649,2651,2652,2653,2654,2655,2656,2744],[97,143,226,1094,3093],[97,143,226,1094,3113],[85,97,143,226,1021,1024,1035,1071,1526,2015,2798],[97,143,226,619,1187,1199,1351,1607,1608,3139],[97,143,226,1187,1199,1351,1607,1608,3139],[85,97,143,226,530,617,619,1020,1021,1024,1030,1035,1069,1071,1073,1080,1087,1095,1187,1313,1518,1520,1521,1526,1528,2015,2798,3119,3120,3122,3123,3125,3126,3127,3128,3129,3130,3131,3132,3134,3135,3136,3137,3138],[97,143,226,618,1199,1518],[97,143,226,618,1073],[97,143,226,1199,1521],[97,143,226,1073,1520],[85,97,143,226,1024,1035,1071,1073,1079,1526],[97,143,226,1073,1523],[97,143,226,1073,1199,1520,1521,1523,1524],[97,143,226,1073,1520,1521],[85,97,143,226,1069,1071,1199,1351,1607,3136],[85,97,143,226,1020,1021,1023,1024,1030,1035,1069,1071,1526,1528,2015],[85,97,143,226,1021,1022,1024,1035,1071,1076,1526,2015,2798],[97,143,226,3151,3156],[85,97,143,226,1199,1351,1607,3140],[85,97,143,226,1020,1024,1027,1077,1079,1166,1187,1301,1908],[85,97,143,226,1199,1351,1607,3129],[85,97,143,226,1020,1024,1077,1080,1313,1908],[97,143,226,1073,1187,1199,1351,1607,3148],[85,97,143,226,1019,1020,1023,1024,1073,1095,1150,1187,1304,3139],[97,143,226,1199,1351,1607,3128],[85,97,143,226,1023,1024,1035,1073,1077,1080,1099],[97,143,226,1199,1351,3143],[85,97,143,226,1073],[85,97,143,226,1073,1187,1199,1351,3142],[85,97,143,226,617,618,1187,1199,1351,1607,1608,3142],[85,97,143,226,617,618,619,1020,1021,1022,1024,1030,1035,1069,1071,1073,1076,1187,1301,1520,1523,1526,1528,1908,2015,2583,2798,3122,3123,3125,3126,3127,3128,3130,3131,3132,3135,3136,3137],[97,143,226,1073,1199,1351,1607,3144],[85,97,143,226,618,1020,1024,1073,1077,1099,1166,1301,1520,3142,3143,3157],[85,97,143,226,1091,1187,1199,1351,1607,3151],[85,97,143,226,617,618,1020,1023,1024,1030,1035,1073,1087,1091,1099,1187,1300,1301,1313,1458,1460,2291,3116,3118,3139,3140,3141,3144,3146,3147,3148,3149,3150],[85,97,143,226,1199,1351,3130],[85,97,143,226,1019,1020,1021,1022,1023,1024,1077,1099,1156,1313,1520,1917],[97,143,226,619,1091,1187,1199,1351,3156],[85,97,143,226,618,619,1019,1020,1023,1024,1073,1077,1091,1099,1187,1304,1313,2291,2583,3153,3154,3155],[97,143,226,1071,1199,1526],[85,97,143,226,1069,1071],[85,97,143,226,1069,1071,1199,1351,1528],[97,143,226,1069,1071],[85,97,143,226,1069,1071,1351],[85,97,143,226,1199,1351,1607,3135],[85,97,143,226,530,1019,1023,1024,1035,1306],[97,143,226,1187,1199,1351,1607,3147],[85,97,143,226,1020,1021,1024,1077,1099,1187,1313,2846],[85,97,143,226,1199,1351,1607,3132,3164],[85,97,143,226,1020,1023,1024,1029,1035,1069,1071,1073,1076,1079,1080,1526,1528,1908,2015],[85,97,143,226,1073,1186,1199,1351,3141],[85,97,143,226,1019,1020,1024,1035,1073,1099,1306,1314,1520],[97,143,226,1073,1199,3115],[97,143,226,1073],[85,97,143,226,617,1024,1073,1187,3115],[85,97,143,226,1073,1091,1187,1199,1351,1460,1462,1607,3118],[85,97,143,226,617,1020,1021,1023,1024,1029,1073,1091,1095,1147,1149,1161,1187,1265,1313,1460,1462,1906,2033,3117],[97,143,226,1073,1161,1199,1351,1607,3117],[97,143,226,1019,1020,1024,1073,1147,1149,1161,1166,1177,1187,1314],[97,143,226,1199,1529],[97,143,226,1073,1521],[85,97,143,226,1199,1351,3122,3164],[85,97,143,226,1020,1021,1022,1024,1030,1035,1071,1073,1076,1526,2015,2798,3121],[85,97,143,226,1021,1024,1035,1069,1071,1076,1079,1526],[85,97,143,226,1021,1024,1035,1071,1073,1526,1528,2015,3133],[97,143,226,1187,1199,1351,1607,3133],[85,97,143,226,1019,1187,1313],[85,97,143,226,1199,1351,3125,3164],[85,97,143,226,1020,1027,1071,1073,1156,1526,2798,3124],[85,97,143,226,1022,1024,1035,1071,1526,2015],[97,143,226,1199,1351,1607],[85,97,143,226,1024,1030,1035,1071,1526],[85,97,143,226,1021,1024,1030,1035,1069,1071,1076,1526,2015,2798],[85,97,143,226,1020,1021,1022,1024,1029,1030,1035,1069,1073,1313,1609,1906],[97,143,226,1073,1199,1609],[97,143,226,1069,1073],[85,97,143,226,1073,1199,1351,1607,3153],[85,97,143,226,617,1020,1024,1035,1073,1304,1609,3152],[97,143,226,1073,1199,1351,3123],[85,97,143,226,1024,1073,1908],[85,97,143,226,1073,1091,1187,1199,1351,1607,3150],[85,97,143,226,617,1020,1024,1029,1073,1091,1095,1099,1176,1187,1265,1313,1906,1908,2033,2798],[97,143,226,1199,1520],[97,143,226,1094,3157],[85,97,143,226,1187,1199,1351,1607,3189],[85,97,143,226,1154,1187],[85,97,143,226,1187,1199,1351,1607,3190],[85,97,143,226,1020,1021,1022,1024,1029,1035,1095,1187,1265,1906,2033],[85,97,143,226,1147,1149,1187,1199,1351,1607,3192],[85,97,143,226,1024,1147,1149,1161,1187,3191],[97,143,226,1019,1020,1024,1147,1149,1177,1187,1314],[85,97,143,226,1091,1187,1199,1351,1607,3193],[85,97,143,226,617,1020,1024,1091,1147,1149,1187,1414,1415,2760,3189,3190,3192],[97,143,226,1199,1351,2790,3194],[97,143,226,1094,1369,2846,3070,3193],[97,143,226,1087,1094,3216,3217],[97,143,226,1091,1094,1199,1351,1607,3243,3245],[85,97,143,226,617,1024,1091,1094,1147,1149,1162,1187,1414,1463,1503,1625,2057,2760,3242,3243,3244],[97,143,226,1199,1351,1607,2057,3244],[85,97,143,226,1019,1020,1024,1026,1030,1147,1149,1161,2057,2640,3243],[97,143,226,1199,1619,1621],[97,143,226,1108,1162,1187,1619,1620],[97,143,226,1199,1607,2790,3252],[85,97,143,226,617,1020,1024,1095,1162,1187,1616,1620,1621,2760,3249,3251],[85,97,143,226,1147,1149,1161,1177,1621,3250],[85,97,143,226,1019,1020,1024,1099,1147,1149,1161,1177,1314,1621,1623],[97,143,226,1199,1623],[97,143,226,1199,1351,1607,3283],[85,97,143,226,1020,1021,1024,1027,1030],[97,143,226,1020,1024,1079,1099,1147,1149,1161,1166,1177,2057,2751,2962,3226],[97,143,226,1199,1351,3288],[85,97,143,226,1094,1463,3287],[97,143,226,1199,1351,1613,1616],[85,97,143,226,1615],[97,143,226,1091,1199,1351,1607,3290],[85,97,143,226,1020,1024,1087,1091,1094,1301,1503,1506,1616,1618,1620,1950,3219,3227,3241,3246,3253,3262,3267,3278,3282,3284,3286,3289],[97,143,226,1187,1199,1607,2790,3262],[85,97,143,226,1069,1071,1091,1094,1305,1435,1463,1503,3257,3261],[85,97,143,226,1616,1618,3245],[97,143,226,1087,1094,1503,1506,1620,3252],[97,143,226,1199,1351,1613,3282],[85,97,143,226,1094,1147,1149,1162,1463,1503,1616,1625,3226,3281],[97,143,226,3266],[85,97,143,226,1094,1187,3285],[85,97,143,226,617,1094,1187,1485,1618,3283],[97,143,226,1094,3277],[97,143,226,3288],[85,97,143,226,1162],[97,143,226,1199,1625],[85,97,143,226,1199,1351,1607,2790,3301],[85,97,143,226,1020,1025,1030,1077,1151,1166,1174,1177,1187,1301,1366,2222,2755,2777,3299,3300],[97,143,226,1094,2846,3301],[85,97,143,226,1091,1199,1351,1613,3312,3314,3315],[85,97,143,226,617,1020,1091,1162,1187,1367,1615,2760,3307,3310,3312,3314],[85,97,143,226,1187,1199,1351,1607,3314],[85,97,143,226,1024,1147,1149,1161,1187,3313],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1187,1314],[97,143,226,1199,1351,1607,3307],[97,143,226,1024,3304,3305,3306],[97,143,226,1094,3315],[97,143,226,1199,1351,2779],[85,97,143,226,518,1086,1178,1187,2635,2652,2778],[85,97,143,226,1020,1024,1035,1080],[97,143,226,1199,1351,1607,3416],[85,97,143,226,1019,1021,1024,1035,1156,1954],[85,97,143,226,1199,1351,1607,1631,3438],[85,97,143,226,617,1020,1021,1022,1024,1030,1073,1076,1082,1187,1300,1301,1313,1324,1631,2292,3320,3437],[97,143,226,1199,1351,1636,3425],[85,97,143,226,1636],[97,143,226,1199,1351,3417],[85,97,143,226,1019,1020,1023,1024,1035],[97,143,226,1627],[85,97,143,226,506,1024,1636,3419],[85,97,143,226,617,1020,1024,1035,1629],[97,143,226,1199,1636,3419],[97,143,226,1636],[97,143,226,1199,1351,1627,1636,3433],[85,97,143,226,1024,1073,1321,1323,1627,1635,1636,1639,2700,3424,3425,3426,3427,3428,3429,3431,3432],[97,143,226,1082,1199,1351,1607,2790,3319,3437],[85,97,143,226,617,618,1020,1021,1024,1026,1030,1035,1073,1076,1082,1095,1187,1321,1323,1369,1414,1627,1628,1629,1631,1636,1637,1640,1920,1954,2769,2770,3149,3215,3319,3337,3338,3339,3340,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3428,3430,3433,3434,3435,3436],[97,143,226,1199,1351,1607,3427],[85,97,143,226,1020,1024,1080,1187,1321,1323],[85,97,143,226,617,1024,1035,1079],[97,143,226,1199,1351,1607,1628,3421],[85,97,143,226,1026,1628],[97,143,226,1082,1199,1627,3422],[97,143,226,1082,1627],[97,143,226,1199,1351,1607,3423],[97,143,226,1020,1024],[97,143,226,1199,1351,1607,3436],[85,97,143,226,1020,1021,1024,1030,1187,1628],[85,97,143,226,1024,1636,3430],[85,97,143,226,1020,1024,1080,1636],[85,97,143,226,617,1020,1024,1035,1079,1627],[97,143,226,1199,1629],[97,143,226,1199,1351,1607,3319,3443],[85,97,143,226,617,1020,1021,1024,1030,1035,1082,1414,1415,1631,1632,1635,1636,3319,3337,3340,3418,3419,3441,3442],[97,143,226,1199,1351,1607,1632,3441,3443],[85,97,143,226,1024,1028,1084,1156,1632,1920,1954,2769,3339,3439,3440,3443],[97,143,226,1199,1351,1636,3439],[85,97,143,226,1024,1321,1323,1635,1636,2700,3426,3429,3432],[97,143,226,1199,1351,1607,3442],[85,97,143,226,1020,1022,1024],[97,143,226,1199,1351,1607,3462],[85,97,143,226,1021,1026],[97,143,226,1199,1351,1607,1632,3440],[97,143,226,1025,1313,1632],[97,143,226,1199,1631,1632],[97,143,226,1631],[85,97,143,226,1024,1187,1369,1641,1969,2289,2770,3319],[97,143,226,1199,1351,1637],[85,97,143,226,1070,1073,1414,1635,1636],[85,97,143,226,1639],[97,143,226,1187,1636,3337],[97,143,226,1199,1635,3408],[97,143,226,617,1073,1187,1634,1635,1636,2052,3407],[97,143,226,1199,2961,3409],[97,143,226,617,1187,1628,2961],[97,143,226,1199,2961,3410],[97,143,226,617,1187,2961],[97,143,226,1199,3411],[97,143,226,617,1187],[97,143,226,1199,1351,3444],[85,97,143,226,1094,1301,1379,2846,3320,3437,3438,3443],[85,97,143,226,1187,1199,1351,1607,1641,2790,3479],[85,97,143,226,617,1020,1024,1028,1029,1035,1083,1094,1095,1187,1265,1313,1641,1642,1644,1906,2033,3477,3478],[97,143,226,1199,1351,1607,1641,2790,3473],[85,97,143,226,617,1020,1021,1022,1024,1026,1028,1029,1035,1076,1083,1094,1095,1099,1176,1187,1265,1313,1641,1906,1908,2033,2042],[85,97,143,226,1199,1351,1607,2790,3484],[85,97,143,226,1020,1021,1022,1024,1026,1035,1077,1095,1156,1187,1313],[85,97,143,226,1199,1351,1607,1641,2790,3476],[85,97,143,226,1024,1147,1149,1161,1641,3475],[97,143,226,1019,1020,1024,1147,1149,1161,1166,1177,1314,1641,3474],[97,143,226,1199,1642],[97,143,226,1641],[85,97,143,226,1199,1351,1607,2790,3482],[85,97,143,226,1020,1024,1028,1095,1099,1156,1358],[85,97,143,226,1187,1199,1607,1641,2790,3474],[85,97,143,226,1020,1024,1035,1099,1187,1641,1954],[97,143,226,1199,1351,2790,3477],[85,97,143,226,1024,1099,1908],[85,97,143,226,1199,1351,1607,2790,3485],[85,97,143,226,617,1020,1024,1087,1187,1301,1641,1908,2042,2294,2760,3470,3471,3472,3473,3476,3479,3480,3481,3482,3483,3484],[85,97,143,226,1199,1351,1607,1641,2042,2790,3471],[85,97,143,226,617,1020,1021,1024,1026,1030,1187,1313,1641,2042,2289],[97,143,226,1187,1199,1351,1607,1641,2790,3472],[85,97,143,226,1020,1024,1028,1077,1099,1150,1187,1641,1908,3471],[85,97,143,226,1187,1199,1351,1607,2790,3481],[85,97,143,226,617,1020,1024,1077,1099,1150,1156,1187],[85,97,143,226,1187,1199,1351,1607,2790,3480],[85,97,143,226,1020,1024,1025,1029,1069,1094,1099,1187,1313,1906,1908,3478],[85,97,143,226,1199,1351,1607,1641,2790,3470],[85,97,143,226,1024,1147,1149,1161,1641,3469],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1314,1641],[97,143,226,1199,1644],[85,97,143,226,1199,1351,1607,2790,3483],[85,97,143,226,1020,1021,1024,1026,1083,1095,1099,1187,1313],[97,143,226,1094,3485],[97,143,226,1199,1469,1607,2790,3506],[85,97,143,226,1020,1024,1077,1099,1174,1176,1313,1474,1503,1952,2222,2752,3502,3505],[97,143,226,1199,2790,3505],[85,97,143,226,1023,1024,1077,1147,1149,1445,3504],[97,143,226,1032,1199,1607,2790,3504],[85,97,143,226,1024,1032,1147,1149,1161,3503],[97,143,226,1032,1147,1149,1177,1179,2752],[97,143,226,1187,1199,1607,2790,3501],[97,143,226,1199,1607,2790,3501],[85,97,143,226,617,1020,1024,1095,1313,1470,1646,1924,1925,2033],[97,143,226,1187,1199,1469,1607,2790,3502],[97,143,226,1199,1469,1607,2790,3502],[85,97,143,226,617,1020,1024,1095,1313,1469,1477,1646,1924,1925,2033],[85,97,143,226,1199,1607,1646,1924,2033,2790],[85,97,143,226,1020,1021,1022,1023,1024,1026,1028,1029,1030,1032,1069,1079,1080,1081,1094,1187,1503,1646,1649,1906,1908,1923],[97,143,226,1199,1924,1925],[97,143,226,1924],[97,143,226,1199,1469,1607,1613,2790,3509],[85,97,143,226,1020,1023,1024,1469,1503,1615,2749,3501,3506,3508],[97,143,226,1199,1469,1607,1613,2790,3508],[85,97,143,226,1024,1147,1149,1161,1469,1615,3507],[97,143,226,1024,1099,1147,1149,1150,1161,1177,1469],[97,143,226,1094,3509],[85,97,143,226,617,1187,1199,1351,3526],[85,97,143,226,617,1020,1021,1024,1029,1030,1095,1187,1265,1313,1906,2033],[97,143,226,1187,1199,1351,1607,3546],[85,97,143,226,617,1020,1024,1030,1087,1187,1300,3523,3525,3526,3545],[97,143,226,1927,3544],[97,143,226,1199,1351,3535],[85,97,143,226,1024],[97,143,226,1199,1351,3540],[85,97,143,226,1020,1024,1930,1931,3534,3537,3538,3539],[97,143,226,1199,1351,3536],[85,97,143,226,1024,1321,1323,1635,1930,2700],[97,143,226,1199,1351,3539],[85,97,143,226,1199,1351,3537],[85,97,143,226,1024,1930,3535,3536],[97,143,226,1635],[85,97,143,226,617,1187,1635,1928,1930],[97,143,226,1199,1351,3534],[97,143,226,1199,1351,3532],[85,97,143,226,1077,3531],[85,97,143,226,1927,1928],[85,97,143,226,617,1187,1927,1928,3527,3528,3529,3530,3532,3533,3540,3541,3542,3543],[97,143,226,1199,1351,3529],[85,97,143,226,1020,1021,1024,1095,1885],[97,143,226,1199,1351,1607,3524],[85,97,143,226,617,1020,1024,1030,1095,1301,1321,1323],[97,143,226,1199,1351,3528],[85,97,143,226,1020,1021,1024,1030,1099,3524],[97,143,226,1199,1351,3533],[85,97,143,226,1020,1024,1030,1077,1927,3531],[97,143,226,1199,1351,3541],[85,97,143,226,1020,1021,1024,1095],[97,143,226,1199,1351,1927,3530],[85,97,143,226,1020,1024,1077,1927],[97,143,226,1199,1927,1928],[97,143,226,1927],[97,143,226,1095,1187,1199,1607,2790,3543],[85,97,143,226,1020,1024,1099,1150,1187],[85,97,143,226,1187,1199,1351,1607,3525],[85,97,143,226,617,1020,1024,1077,1095,1099,1151,1166,1187,1301,3521,3524],[97,143,226,1187,1928],[97,143,226,1187,1199,1351,1607,3523],[85,97,143,226,1024,1147,1149,1161,1187,3521,3522],[97,143,226,1019,1020,1024,1147,1149,1161,1166,1177,1187,1305,1314,3521],[97,143,226,1199,1351,3527],[85,97,143,226,1020,1095],[97,143,226,1199,1351,3531],[85,97,143,226,1020,1021,1022,1024,1099,1954],[97,143,226,1094,2846,3546],[97,143,226,1187,1199,1607,2790,2970],[85,97,143,226,1020,1021,1023,1024,1030,1077,1079,1151,1177,1187,1301,2884,2964,2969],[97,143,226,1094,2970],[97,143,226,1091,1187,1199,1351,1607,3572],[97,143,226,1199,1351,3572],[85,97,143,226,530,617,1020,1021,1022,1024,1025,1029,1035,1069,1087,1091,1095,1187,1265,1306,1313,1906,1932,2033,2798,3570,3571],[97,143,226,3577],[97,143,226,617,1187,1199,1351,1607,3570],[85,97,143,226,617,1020,1024,1028,1187,1313],[97,143,226,1199,1932],[97,143,226,1087,1091,1187,1199,1351,1607,3571,3577],[85,97,143,226,617,1020,1021,1022,1029,1030,1087,1091,1095,1187,1265,1313,1906,1932,2033,2760,2798,3571,3572,3574,3576],[97,143,226,1199,1351,1607,2790,3571,3574],[85,97,143,226,1024,1147,1149,1161,3571,3573],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1314,3571],[97,143,226,617,1187,1199,1351,1607,3575],[85,97,143,226,617,1020,1021,1024,1077,1187,1313],[97,143,226,1166,1199,1351,1607,3571,3576],[85,97,143,226,1020,1024,1077,1166,3571,3575],[97,143,226,1094,3578],[85,97,143,226,617,1187,1199,1351,2790,3588],[85,97,143,226,617,1020,1021,1022,1024,1025,1029,1034,1035,1095,1187,1265,1313,1906,2013,2033],[97,143,226,1034,1187,1199,1351,1607,3591],[85,97,143,226,617,1020,1034,1087,1187,1300,3211,3588,3590],[97,143,226,1034,1199,1351,1607,3590],[85,97,143,226,1024,1034,1147,1149,1161,3589],[97,143,226,1019,1020,1024,1034,1099,1147,1149,1161,1166,1177,1314,2013],[97,143,226,1094,3591],[97,143,226,1199,1351,1607,3599],[85,97,143,226,1020,1021,1022,1024,1029,1035,1076,1080,1095,1265,1652,1889,1906,2033],[97,143,226,1187,1199,1351,1607,3600],[85,97,143,226,617,623,1020,1024,1187,2760,3596,3598,3599],[97,143,226,623,1187,1199,1351,1607,3596],[85,97,143,226,617,623,1020,1021,1022,1024,1029,1035,1076,1077,1080,1081,1099,1166,1187,1265,1652,1889,1906,1923,2033],[97,143,226,623,1177,1199,1351,1607,3598],[85,97,143,226,623,1024,1147,1149,1161,3597],[97,143,226,623,1019,1020,1024,1099,1147,1149,1161,1177,1314],[97,143,226,1094,3600],[97,143,226,1094,3612],[97,143,226,1094,3619],[97,143,226,1094,3621],[97,143,226,617,1187,1199,1351,1607,3621],[85,97,143,226,617,1020,1022,1024,1077,1187,1313],[97,143,226,1094,3624],[97,143,226,617,1199,1351,1607,3624],[85,97,143,226,617,1020,1021,1027,1077,1187,1313,1947],[97,143,226,1199,1285,1351,2790,3634],[85,97,143,226,1077,1285,2222],[97,143,226,1199,1285,1351,2790,3635],[97,143,226,1199,2790,3636],[85,97,143,226,1147,1149,1161,1174,1177,1285],[97,143,226,1199,1351,3637],[85,97,143,226,1285,3634,3635,3636],[85,97,143,226,1187,1199,1351,1516,1607,3641],[85,97,143,226,1024,1035,1077,1147,1149,1161,1166,1177,1187,1285,1294,1301,1306,1366,1936,1937,1964,1965,1973,1991,2222,2750,2881,3300,3628,3630,3637,3638,3639,3640],[97,143,226,1285],[97,143,226,1199,1937],[97,143,226,1166],[97,143,226,1199,1351,3642],[85,97,143,226,1024,1035,1077,1079,1147,1149,1161,1166,1177,2222,2962,3631],[97,143,226,1199,1351,1607,3640],[85,97,143,226,1161,1166,1177,1301,2222],[97,143,226,1199,1934],[97,143,226,1199,1351,2790,3643],[85,97,143,226,1020,1022,1025,1187,1313,2700],[85,97,143,226,1094,1187,1199,1351,1368,1389,1437,1514,1516,1607,2790,3645],[85,97,143,226,623,1020,1024,1032,1035,1077,1087,1094,1166,1187,1285,1294,1301,1366,1368,1389,1437,1514,1908,1934,1964,1973,1991,2222,2755,2881,3299,3300,3628,3629,3630,3631,3633,3637,3639,3640,3641,3642,3643,3644],[97,143,226,1199,1351,1607,2790,3644],[85,97,143,226,1024,1030,1087,1099,1366],[97,143,226,1199,1285,1294,1351],[85,97,143,226,1285],[97,143,226,1094,1367,1503,3645],[97,143,226,617,1187,1199,1607,2790,3658],[85,97,143,226,617,1028,1076,1077,1094,1095,1151,1156,1177,1187,1889,3657],[85,97,143,226,617,1091,1199,1351,1607,1941,2781,3660],[85,97,143,226,617,1020,1021,1026,1029,1030,1069,1077,1091,1150,1293,1503,1648,1906,1940,1941,2033,2781],[97,143,226,1199,1940,1941],[97,143,226,624,1265,1940],[97,143,226,1199,1940],[97,143,226,3664],[97,143,226,1199,1351,1607,2790,3657],[85,97,143,226,625,1020,1021,1022,1024,1029,1030,1031,1035,1076,1081,1087,1156,1265,1652,1906,1915,1918,2033,2046,2050],[85,97,143,226,1091,1199,1351,1607,2790,3664],[85,97,143,226,617,1020,1087,1091,1147,1149,1150,1187,1301,1414,1415,1615,1910,1911,2760,3658,3659,3660,3662,3663],[97,143,226,1199,1351,1607,3663],[97,143,226,1199,1351,1607,2054,3663],[85,97,143,226,617,1020,1024,1025,1029,1030,1035,1077,1087,1094,1095,1151,1166,1179,1187,1301,1460,1462,1652,1910,2054,2760,2764,3311,3657],[85,97,143,226,1147,1149,1187,1199,1351,1607,3662],[85,97,143,226,1021,1024,1026,1147,1149,1161,1187,3661],[97,143,226,1019,1020,1024,1099,1147,1149,1161,1166,1177,1187,1314],[97,143,226,1094,1503,3665],[97,143,226,1082,1187,1199,1351,1607,3683],[97,143,226,1187,1199,1351,1607,3683],[85,97,143,226,617,1020,1021,1022,1024,1029,1030,1035,1077,1111,1187,1306,1313,1908,3337,3674,3681,3682],[97,143,226,1111,1199,1351,1607,3681],[85,97,143,226,1024,1111,1161,3680],[97,143,226,1019,1020,1024,1111,1147,1149,1166,1177,1314],[97,143,226,1187,1199,1351,1607,3685],[85,97,143,226,617,1020,1024,1087,1111,1182,1187,1301,2292,2760,3676,3677,3679,3683,3684],[85,97,143,226,617,1111,1181,1187],[97,143,226,1181,1182,1199,1351,1607],[85,97,143,226,1024,1147,1149,1161,1180,1182],[97,143,226,1147,1149,1161,1177,1179,1182],[97,143,226,1082,1199,1351,3682],[85,97,143,226,1021,1024,1025,1029,1035,1082,1908],[97,143,226,1111,1199,1351,1607,3684],[85,97,143,226,1025,1077,1111,3678],[97,143,226,617,1187,1199,1351,1607,3679],[85,97,143,226,1187,1199,1351,1607,3679],[85,97,143,226,617,1020,1021,1022,1024,1025,1029,1030,1035,1077,1099,1111,1187,1265,1301,1305,1306,1906,2033,3674,3678],[97,143,226,617,1187,1199,1351,1607,3677],[97,143,226,1187,1199,1305,1351,1607,3674,3677],[85,97,143,226,617,1020,1021,1022,1023,1024,1025,1029,1030,1035,1069,1082,1095,1187,1265,1306,1906,1908,2033,3674],[97,143,226,1111,1199,1351,1607,3676],[85,97,143,226,1024,1111,1147,1149,1161,3675],[97,143,226,1019,1020,1024,1111,1147,1149,1161,1166,1177,1314,3674],[97,143,226,617,1187,1199,1351,1607,3678],[85,97,143,226,617,1020,1022,1024,1028,1077,1187,1313],[97,143,226,1094,3685],[97,143,226,1199,1351,2790,3702],[97,143,226,1094,1369,2846,3070,3700],[97,143,226,1199,1351,1607,3700],[85,97,143,226,1019,1020,1021,1024,1030,1035,1080,1147,1149,1154,1161,1187,1313],[97,143,226,2005,3710],[97,143,226,2005,3712],[85,97,143,226,518,2005,3714,3715],[97,143,226,1199,1351,3704],[85,97,143,226,518,1094,1178,1506,1947,2005,2010,2651],[97,143,226,2005,3717],[85,97,143,226,1199,1351,2004,3708],[85,97,143,226,518,617,1020,1021,1024,1082,1150,1305,1635,1948,1954,2003,2005,2010,3414,3706,3707],[97,143,226,2005,3719],[97,143,226,1199,1351,3721],[97,143,226,1094,1947,2651],[97,143,226,1199,1351,3723],[85,97,143,226,518,1094,3714,3715],[97,143,226,526,529,1322,2633,2634,2635,2636,2637],[97,143,226,1091,1093,1187,1199,1351,1607,3725],[97,143,226,620,622,1091,1093,1187,1199,1351,3725],[85,97,143,226,518,620,622,1020,1021,1024,1029,1030,1035,1077,1086,1093,1187,1265,1313,1453,1906,1908,2033,2293,2652,2798],[97,143,226,3725],[85,97,143,226,518,618],[85,97,143,226,518,3216],[85,97,143,226,518,3217],[85,97,143,226,1199,1351,3732],[85,97,143,226,1020,1024,1086,1908],[85,97,143,226,1199,1351,3736],[85,97,143,226,518,620,621,1187,1466,3732,3734,3735],[97,143,226,1199,1351,1607,3735],[85,97,143,226,1199,1351,1607,3735],[85,97,143,226,1019,1020,1021,1024,1029,1077,1265,1313,1906,1908,2033,2798],[85,97,143,226,1199,1351,3734],[85,97,143,226,1313],[85,97,143,226,518,3736],[85,97,143,226,1032,1166,1199,1285,1351,3628],[85,97,143,226,1024,1032,1077,1080,1166,1285,1963,1991,2222,3627],[85,97,143,226,1021,1027,1077,1079,1083,1084,1106],[97,143,226,617,1082,1110,1162,1187,1199,1607,2297,2630,2790,3247,3249],[85,97,143,226,617,1020,1021,1024,1029,1030,1035,1069,1077,1082,1087,1091,1095,1104,1105,1106,1107,1108,1109,1110,1162,1187,1265,1313,1620,1903,1906,1995,2033,2034,2297,3220,3247,3248],[97,143,226,1032,1069,1071,1094,1187,1199,1305,1607,2790,3261],[85,97,143,226,1020,1024,1026,1029,1030,1032,1035,1069,1071,1077,1079,1087,1094,1095,1187,1305,1439,1479,1499,1620,1903,1908,2015,2034,2962,3229,3254,3255,3256,3258,3259,3260],[97,143,226,1199,1351,3254,3865],[85,97,143,226,623,1021,1022,1024,1030,1032,1035,1070,1071,1076,1079,1080,1507,1920,2008,2015,2595,3223,3224,3229],[97,143,226,1187,1199,1995,2790,3220],[85,97,143,226,1024,1187,1995],[97,143,226,1110,1187,1199,1607,2630,2790,3248],[85,97,143,226,1020,1022,1024,1099,1110,1183,1187,1993],[97,143,226,1110,1993],[97,143,226,1110,1187],[97,143,226,1995],[97,143,226,1107,1110],[97,143,226,1078,1106,1107,1108,1109],[97,143,226,1199,1351,1607,3224],[85,97,143,226,1020,1024,1027,1030,1035,1889],[85,97,143,226,1021,1024,1026,1027,1030,1035,1076,1077,1079,1083,1097,1098,1101,1106],[97,143,226,1097,1106,1199,1607,2630,2790],[85,97,143,226,617,1020,1022,1024,1094,1095,1096,1106,1187],[97,143,226,1096],[97,143,226,1078,1199],[97,143,226,1109],[97,143,226,1106,1107,1108,1199],[97,143,226,1107,1109],[97,143,226,1106,1199,1607,2630,2790],[85,97,143,226,1023,1024,1026,1028,1030,1035,1076,1077,1079,1080,1082,1085,1100,1102,1103,1104,1105,1107,1108,1109],[85,97,143,226,1069,1071,1199,1351,1607,3255,3865],[85,97,143,226,1021,1035,1069,1071,1147,1149,1161,1305,2015],[85,97,143,226,1024,1035,1076],[97,143,226,617,1110,1187],[97,143,226,1199,3257],[97,143,226,617,1187,1305,2593],[97,143,226,1100,1106,1199],[97,143,226,1098,1100,1101,1102,1106,1199,1607,2630,2790],[85,97,143,226,1020,1021,1024,1027,1080,1084,1098,1099,1100,1106],[85,97,143,226,1020,1024,1030,1035,1076,1077,1078,1108],[97,143,226,1199,1305,1351,3256,3865],[85,97,143,226,1021,1069,1071,1076,1305,2015,3229],[97,143,226,617,1187,1199,1351,3257,3258],[85,97,143,226,617,1020,1024,1028,1187,3257],[97,143,226,1069,1071,1091,1199,1305,1351,3259,3865],[85,97,143,226,1020,1021,1022,1024,1030,1069,1071,1187,1305,1479,2015,2798,3229],[97,143,226,1199,1351,1607,2036],[85,97,143,226,1020,1021,1022,1024,1026,1027,1028,1035,1077,1080,1082,1099],[97,143,226,1105,1199,1607,2790],[85,97,143,226,1021,1024,1026,1035,1079,1082],[97,143,226,1107,1199],[97,143,226,1106,1109],[97,143,226,1103,1199],[85,97,143,226,1024,1030,1035,1108],[97,143,226,1187,1199,1607,2790,3273],[85,97,143,226,617,1020,1021,1024,1030,1035,1069,1077,1079,1095,1187,1265,1313,1889,1906,1908,2033,3268,3269,3270,3271,3272,3277],[97,143,226,1199,1351,1607,1650],[85,97,143,226,1076,1187],[97,143,226,1161,1199,1351,1607,3200],[97,143,226,1019,1020,1024,1099,1147,1149,1161,1166,1177,1314],[97,143,226,1187,1199,1351,3200,3201],[85,97,143,226,617,1019,1020,1024,1095,1099,1156,1187,3200],[97,143,226,1187,1199,1351,3202,3203],[85,97,143,226,617,1019,1020,1024,1095,1099,1156,1187,3202],[97,143,226,1187,1199,1351,3205],[85,97,143,226,617,1019,1020,1024,1095,1099,1156,1187,3204],[97,143,226,1161,1199,1351,1607,3202],[97,143,226,1187,1199,1607,2790,3217],[85,97,143,226,518,620,622,1020,1024,1034,1077,1086,1087,1095,1099,1147,1149,1161,1166,1187,1301,1321,1323,1506,3200,3201,3202,3203,3204,3205,3206,3209,3212,3213,3216],[97,143,226,1161,1199,1351,1607,3206],[85,97,143,226,1023,1024,1030,1034,1147,1149,1161,3210,3211],[97,143,226,1034,1161,1199,1351,1607,3210],[97,143,226,1019,1020,1024,1034,1099,1147,1149,1161,1166,1177,1314],[97,143,226,617,1187,1199,1351,1607,3209],[85,97,143,226,508,617,1077,1087,1151,1187,1884,3208],[85,97,143,226,617,1187,3083],[85,97,143,226,1199,1351,1607,3083],[85,97,143,226,1020,1021,1024,1069,1079,1099,1151],[97,143,226,1199,1351,1373,1950],[97,143,226,1099,1373],[97,143,226,1199,1351,1607,3659],[85,97,143,226,617,1020,1024,1095,1151,1187,1884,1909,1969],[85,97,143,226,1020,1024,1035,1080,1321,1323,1635,2003,2700,2743,3428,3429],[97,143,226,1186,1199,2010],[97,143,226,1199,1351,2010],[85,97,143,226,518,1020,1024,1028,1178,2005,2009],[97,143,226,1199,1351,3715],[85,97,143,226,1024,1187],[85,97,143,226,1020,1021,1024,1035,1095,1300,1948,2003,2008],[85,97,143,226,617,1020,1021,1024,1027,1032,1091,1095,1099,1150,1151,1187,1909,2576],[97,143,226,1187,1199,1351,2790,3717],[85,97,143,226,1020,1024,1091,1095,1150,1151,1187,2244],[85,97,143,226,1073,1091,1186,1187,1199,1351,3714],[85,97,143,226,617,1020,1021,1024,1073,1091,1150,1187,1301,1306,3155],[85,97,143,226,1073,1186,1187,1199,1351,3707],[85,97,143,226,617,1024,1073,1079,1150,1187,1306],[85,97,143,226,617,1020,1024,1091,1099,1150,1151,1187,1300],[97,143,226,1073,1635],[85,97,143,226,1020,1024,1091,1150,1187],[97,143,226,1199,1351,2004],[85,97,143,226,2003],[97,143,226,1199,1627,3215],[97,143,226,1073,1627,1636],[85,97,143,226,1019,1024,1073,1080],[97,143,226,1199,1351,3429],[85,97,143,226,1020,1024,1080,1321,1323,2700],[97,143,226,1199,1351,1635],[85,97,143,226,1024,1035,1634],[97,143,226,1034,1199,2013],[97,143,226,1034],[85,97,143,226,617,1019,1020,1024,1034,1095,1099,1156,1187],[85,97,143,226,1024,1034,2013],[85,97,143,226,1199,1351,1607,3629],[85,97,143,226,617,1020,1021,1024,1029,1030,1095,1187,1265,1313,1906,1908,2033,2798],[97,143,226,1091,1199,1351,3090],[85,97,143,226,1077,1091,1092,1094,1427,3085,3087,3089],[97,143,226,1091,1199,1351,1607,3087],[97,143,226,1091,1199,1351,3087],[85,97,143,226,617,1020,1021,1029,1035,1094,1095,1265,1420,1906,1961,2033,3086],[97,143,226,1199,1351,3085],[85,97,143,226,1023,1024,1035],[97,143,226,1091,1199,1351,1426,3089],[85,97,143,226,617,1020,1024,1028,1077,1094,1099,1300,1422,1424,1426,1427,1908,2760,3088],[97,143,226,1199,1961],[97,143,226,1091,1199,1351,1426,1607,3088],[97,143,226,1091,1199,1351,1426,3088],[85,97,143,226,617,1020,1021,1029,1035,1094,1095,1265,1426,1427,1906,1961,2033,3086],[85,97,143,226,1024,1321,1323],[85,97,143,226,1024,1076,1150,1384],[85,97,143,226,1177,1884],[85,97,143,226,1021,1022,1024,1030,1035,1069,1070,1071,1187],[97,143,226,1199,1351,2752],[97,143,226,1099],[97,143,226,1199,1607,2760,2790],[85,97,143,226,1020,1023,1024,1077,1095,1908],[97,143,226,1199,1351,3304],[85,97,143,226,1019,1023,1024,1414,1415],[97,143,226,1199,1351,1607,3305],[85,97,143,226,1019,1020,1024],[97,143,226,1199,1351,1607,3306],[85,97,143,226,1020,1024],[97,143,226,1199,1351,1884,3207],[85,97,143,226,1019],[97,143,226,1199,1351,1607,3208],[97,143,226,1035,1884,3207],[85,97,143,226,1069,1199,1607,1653,2790],[85,97,143,226,1021,1024,1028,1030,1035,1079,1156],[97,143,226,1199,1351,2753],[85,97,143,226,1019,1168,1952,2752],[97,143,226,1199,1351,2652],[97,143,226,1019,1313],[85,97,143,226,1020,1024,1035,1151,1177,1187,3208],[85,97,143,226,1199,1265,1351,1501,1607,2033,3231],[85,97,143,226,1020,1021,1024,1069,1150,1265,1501,1906],[97,143,155,164,226,1199,1351,1607,1886],[85,97,143,226,617,1020,1021,1077,1151,1884,1885],[97,143,226,1199,1351,1607,1885],[85,97,143,226,1021,1024,1026,1082,1414],[85,97,143,226,1069,1071,1199,1351,1607],[85,97,143,226,1029,1069],[97,143,226,1199,1351,1373,3893],[97,143,226,1199,1351,1607,1904],[85,97,143,226,1026,1187],[97,143,226,1187,1199,1351,1607,3272],[85,97,143,226,1020,1024,1029,1035,1077,1649,1908,2769],[85,97,143,226,1077,1079],[85,97,143,155,164,226,1199,1891,2790],[85,97,143,226,1099,1890],[85,97,143,226,1026,1469],[97,143,226,1199,1607,1892,2790],[85,97,143,226,1024,1030,1035],[85,97,143,226,1082,1091,1199,1351,1897,1902],[85,97,143,226,1082,1091,1187,1301,1414,1897,1899,1900,1901],[97,143,226,1199,2016],[97,143,226,1902],[97,143,226,1199,1351,2761],[97,143,226,1099,2016],[85,97,143,226,1091,1199,1351,1897,1899,1902,2016],[85,97,143,226,1151],[85,97,143,226,1032,1503,1648],[85,97,143,226,1199,1351,1503,1607,3638],[85,97,143,226,1026,1503,2973],[97,143,226,1187,1199,1351,1415,1607,3228],[85,97,143,226,1020,1024,1029,1030,1035,1069,1095,1187,1313,1648,1906,1908],[97,143,226,1187,1199,1351,1516,1607,3630],[85,97,143,226,1026,1187,1516,1648],[97,143,226,617,1091,1187,1199,1351,1367,1607,1911],[85,97,143,226,617,1020,1021,1022,1024,1029,1030,1035,1069,1076,1080,1081,1091,1095,1156,1187,1367,1903,1906,1908,1910],[97,143,226,1199,1351,2649],[97,143,226,620,1020,1086,1375,1958,2293,2639,2640,2641,2643,2644,2646,2647,2648],[97,143,226,1199,1442,2654,2790],[85,97,143,226,1024,1442,1908],[97,143,226,1199,1351,1445,1607,2790,3097],[85,97,143,226,1024,1094,1147,1149,1445,1908,3096],[97,143,226,1199,1351,1445,1607,2790,3096],[85,97,143,226,1024,1147,1149,1161,1445,3095],[97,143,226,1147,1149,1161,1177,1445],[97,143,226,1199,1351,1503,2790,3100],[97,143,226,1024,1094,1503,1908,3099],[97,143,226,1199,1351,1503,2790,3099],[85,97,143,226,1024,1147,1149,1161,1503,3098],[97,143,226,1147,1149,1161,1177,1503],[97,143,226,1199,1351,1607,2846],[85,97,143,226,508,1024],[97,143,226,1199,2037],[97,143,226,2037],[85,97,143,226,617,1020,1021,1024,1029,1035,1078,1082,1095,1100,1105,1106,1107,1108,1109,1110,1187,1265,1313,1619,1906,2033,2034,2035,2036],[85,97,143,226,1199,1351,1607,2040,2790],[85,97,143,226,614,617,1020,1028,1033,1077,1150,1156,1187],[97,143,226,1033,2040],[97,143,226,614],[85,97,143,226,1199,1351,1607,2790,3082],[85,97,143,226,617,1020,1023,1024,1077,1187,2041],[97,143,226,1199,1607,1970,1971,2790],[85,97,143,226,617,1020,1024,1095,1150,1503,1963,1965,1966,1967,1968,1970],[97,143,226,1199,1966,2790],[85,97,143,226,1030,1965],[97,143,226,1967,2790],[85,97,143,226,1964],[97,143,226,1199,1607,1968,2790],[85,97,143,226,1083,1965],[97,143,226,1965,1971,1972],[97,143,226,1032,1964],[97,143,226,1199,1607,1965,1972,2790],[85,97,143,226,1020,1024,1025,1032,1964,1965,1971],[97,143,226,1199,1964,1965,1969,1970],[97,143,226,1166,1964,1965,1969],[97,143,226,1187,1199,1351,2769],[85,97,143,226,1076,1187,2042],[97,143,226,1199,2790,3232],[85,97,143,226,1019,1024,1077,1099],[85,97,143,226,1020,1024,1091,1187,1313,1975,2242,2244,2282],[85,97,143,226,2790,3065],[85,97,143,226,1199,1320,1351,1607,2790],[97,143,226,1199,2233],[97,143,226,1170,1199],[97,143,226,1199,1351,1607,1912],[85,97,143,226,1020,1024,1026,1076],[85,97,143,226,1020,1023,1030],[97,143,226,1081,1199],[97,143,226,1199,2044],[97,143,226,1032,1187],[85,97,143,226,614,625,1031,1187],[97,143,226,1031,1199,2790],[97,143,226,1031,1199],[85,97,143,226,1020,1023,1024,1026,1029,1030],[97,143,226,1199,2046],[97,143,226,1031],[85,97,143,226,1199,1351,1607,1914],[85,97,143,226,1020,1021],[97,143,226,1199,2048],[97,143,226,1032],[97,143,226,1031,2046,2050],[85,97,143,226,1199,1351,1607,3268],[85,97,143,226,1020,1021,1024],[97,143,226,1087,1199,1351,1958,2790],[85,97,143,226,508,1019,1020,1024,1087,1094,1099,1178,1187,1366,1368,1381,1442,1503,1947,1948,1949,1950,1955,1957],[85,97,143,226,1187,1199,1351,2656],[85,97,143,226,1020,1024,1187,1451,1908,1956],[97,143,226,1199,1635,3319],[97,143,226,1073,1187,1634,1635,1636,2915,2961],[97,143,226,1082,1187,1199],[97,143,226,1081,1187],[97,143,226,1073,1199,2052],[97,143,226,1199,1635,1636,3414],[97,143,226,617,1073,1187,1634,1635,1636,1639,2961],[97,143,226,1199,1351,2762],[85,97,143,226,1099,1306,1884,1888],[97,143,226,1073,1075],[97,143,226,1075,1199,1351,1456,1460,1462,1607,1915,2790],[85,97,143,226,1075,1076,1456,1460,1462],[85,97,143,226,1187,1199,1351,1607,1918,2790],[85,97,143,226,1073,1083,1187,1313,1460,1916,1917],[97,143,226,617,1073,1185,1199,1351,1607,3149],[85,97,143,226,616,617,1024,1073,1079,1095,1293,2798],[85,97,143,226,1024,1156,1916],[85,97,143,226,1073,1199,1351,1607,3338],[85,97,143,226,1021,1022,1024,1029,1030,1035,1069,1073,1906],[97,143,226,1073,1199],[97,143,226,1199,1305,2055],[97,143,226,1091,1187,1199,1305,1351,3263],[85,97,143,226,1020,1021,1026,1035,1069,1071,1095,1187,1305,1306,2015,2055,3229,3259],[97,143,226,617,1091,1187,1199,1351,1607,3266],[85,97,143,226,617,1020,1024,1087,1094,1187,1435,2582,2760,3263,3265],[97,143,226,1187,1199,1351,1607,3265],[85,97,143,226,1024,1147,1149,1161,1187,3264],[97,143,226,1019,1020,1024,1147,1149,1161,1166,1177,1187,1305,1314],[97,143,226,1187,1199,1607,2790,3221],[85,97,143,226,1020,1021,1029,1035,1095,1187,1265,1906,2033],[85,97,143,226,1147,1149,1199,1351,1607,3281],[85,97,143,226,1020,1032,1095,1147,1149,1187,2314,3279,3280],[85,97,143,226,1147,1149,1199,1351,1607,3279,3280],[85,97,143,226,1024,1032,1147,1149,1161,3279],[97,143,226,1019,1024,1032,1147,1149,1161,1177],[97,143,226,617,1199,1351,1481,1496,1607,2768,2790,3242],[85,97,143,226,617,1020,1024,1029,1035,1069,1079,1095,1150,1481,1496,1906,2768],[85,97,143,226,1077],[85,97,143,226,617,1077,1151,1187,1884],[85,97,143,226,617,1091,1187,1199,1351,1607,2630,3227],[85,97,143,226,617,623,1020,1024,1035,1070,1077,1091,1095,1108,1162,1166,1187,1301,1306,1463,1503,1507,1619,1620,1625,1884,1995,2037,2582,2596,2760,3220,3221,3222,3225,3226],[85,97,143,226,623,1020,1021,1022,1024,1030,1035,1069,1079,1099,1187,1265,1313,1649,1889,1906,1920,2008,2032,2582,2593,2595,3223,3224],[97,143,226,1162,1187,1199,1351,1367,1503,1514,1607,2781,2790],[97,143,226,1025,1035,1150,1162,1187,1367,1503,1514,1976],[97,143,226,1199,1976],[97,143,226,1199,1351,3219],[85,97,143,226,1199,1305,1306,1351],[85,97,143,226,1304,1305],[85,97,143,226,1199,1305,1351,2962],[85,97,143,226,1306],[85,97,143,226,620,1199,1370,1607,2651,2790],[85,97,143,226,508,620,1019,1024,1086,1099,1178,1187,1372,1375,1380,1442,1947,1979,2293,2641,2643,2644,2646,2647,2648,2650],[97,143,226,1199,1607,2641,2790],[85,97,143,226,1020,1024,1314,1371,1391,1979],[97,143,226,1199,2643,2790],[85,97,143,226,1019,1020,1024,1035,1375,2642],[97,143,226,1199,1951],[85,97,143,226,1607,2644,2790],[85,97,143,226,1019,1020,1024,1099,1370,1377,1954],[97,143,226,1199,1370,1607,2650,2790],[85,97,143,226,1019,1024,1028,1035,1079,1094,1099,1370,1371,1372,1375,1951,1952,1953,1954],[97,143,226,1199,1351,2646],[85,97,143,226,518,1024,1178,1314,1506,2645],[97,143,226,1199,1351,1607,2648],[85,97,143,226,1023,1024,1025,2293],[97,143,226,617,620,1178,1187,1199],[97,143,226,616,617,620,622,623,625,1031,1032,1033,1034,1072,1073,1074,1075,1110,1182,1183,1184,1185,1186],[97,143,226,1091,1199,1442,2655,2790],[85,97,143,226,1024,1442],[85,97,143,226,625,2763,2764,2765],[97,143,226,1199,1910],[85,97,143,226,617,1020,1095,1909],[97,143,226,617,1032,1187,1199,1607,1923,2790],[97,143,226,1187,1199,1923],[85,97,143,226,617,1020,1021,1022,1024,1026,1029,1030,1031,1032,1035,1069,1071,1072,1075,1076,1079,1080,1081,1083,1087,1091,1094,1095,1099,1166,1187,1367,1369,1445,1469,1499,1506,1648,1649,1650,1651,1652,1653,1886,1887,1889,1891,1892,1902,1903,1904,1905,1911,1912,1913,1914,1915,1918,1919,1920,1921,1922],[97,143,226,1199,1921],[97,143,226,1031,1652,1888,1902,1913,1914],[97,143,226,1032,1199,1607,2767,2790],[97,143,226,617,1032,1199,1607,2767,2790],[85,97,143,226,617,1020,1021,1024,1029,1032,1035,1069,1094,1095,1187,1265,1906,1908,1909,2033,2059,2576],[97,143,226,1199,2059],[97,143,226,1199,1922],[97,143,226,1199,2063],[97,143,226,624,1265,2062],[85,97,143,226,1091,1199,1351,1607,3310],[85,97,143,226,617,1020,1021,1022,1029,1030,1091,1095,1293,1367,1906,1915,1920,2033,2062,2063,2781,3309],[97,143,226,1187,1199,2065],[97,143,226,624,1187,1265,2062],[85,97,143,226,1091,1187,1199,1351,1607,3309],[85,97,143,226,617,1020,1021,1022,1029,1030,1091,1187,1293,1367,1906,1915,1920,2033,2062,2065,2301,2781],[85,97,143,226,1199,1351,1367,1607,2790,3312],[85,97,143,226,617,1020,1024,1077,1091,1166,1177,1179,1187,1301,1367,1503,1952,1963,2292,2766,3228,3234,3238,3309,3311],[97,143,226,1087,1199,1946,1958,1959],[97,143,226,1087,1946,1958],[97,143,226,1187,1199,1607,2790,3274],[85,97,143,226,617,1020,1021,1022,1023,1024,1030,1069,1077,1079,1099,1187,1265,1301,1906,2033,3270,3271,3272],[97,143,226,1199,1351,1607,3276,3277],[85,97,143,226,1024,1161,3275,3277],[85,97,143,226,1019,1020,1024,1099,1147,1149,1177,1314,3277],[97,143,226,1187,1199,1351,1607,3276,3277],[85,97,143,226,617,1020,1187,3273,3274,3276],[97,143,226,1187,1199,1351,3632],[85,97,143,226,1020,1147,1149,1161,1187,1301,2222],[85,97,143,226,1035,1099,1187,1884],[97,143,226,1075,1187,1199,1351,1607,2764],[85,97,143,226,1035,1073,1075,1099,1187,1884],[85,97,143,226,1099,1187,1884],[97,143,226,1187,1199,1351,1641,2770,2790],[85,97,143,226,1076,1187,1369,1641],[97,143,226,617,1187,1199,1351,1607,3287],[85,97,143,226,617,1019,1020,1023,1024,1028,1035,1077,1095,1099,1187,1300],[97,143,226,1199,1305],[97,143,226,530,1304],[97,143,226,1147,1149,1187,1199,1351,3214,3216],[85,97,143,226,617,1024,1025,1034,1035,1076,1077,1095,1099,1147,1149,1161,1187,1301,1305,1627,1636,1884,1947,2651,3212,3214,3215],[97,143,226,1099,1147,1149,1161,1177,1305],[85,97,143,226,1199,1351,1607,3268,3269],[85,97,143,226,1020,1021,1024,3268],[97,143,226,1199,1351,3270],[85,97,143,226,1024,1077,1187],[97,143,226,617,1187,1199,1607,2790,2884],[85,97,143,226,617,1020,1187,1897],[97,143,226,1199,1351,1893],[97,143,226,1199,1351,1894],[97,143,226,1199,1351,1607,1897],[85,97,143,226,1893,1894,1895,1896],[97,143,226,1199,1351,1607,1895],[97,143,226,1199,1351,1607,1896],[85,97,143,226,1079],[97,143,226,617,1199,1351,1486,1487,1607,2969],[85,97,143,226,617,1020,1023,1024,1077,1094,1095,1162,1380,1483,1486,1487,2967,2968],[97,143,226,1199,1486,1607,2790,2968],[85,97,143,226,1020,1021,1022,1025,1029,1030,1069,1095,1265,1486,1906,2033,2067],[97,143,226,1199,1486,2067],[97,143,226,1486],[97,143,226,1199,1351,1486,1607,2967],[85,97,143,226,1024,1147,1149,1161,1486,2965,2966],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1314,1486,2069],[85,97,143,226,1024,1301,1324,1486,2069],[97,143,226,617,1187,1199,1351,1607,2790,2796],[85,97,143,226,617,1020,1021,1024,1028,1029,1077,1187,1265,1313,1906,1908,1909,2033,2768],[97,143,226,1187,1199,1607,2790,3233],[85,97,143,226,1019,1025,1187],[97,143,226,1069,1187,1199,1351,1607,3093],[85,97,143,226,617,1020,1021,1025,1029,1069,1077,1079,1095,1151,1187,1301,1306,1987,2760,2768,3082,3084,3090,3092],[97,143,226,1199,1351,1431,1433,1607,2790,2811],[85,97,143,226,617,1020,1021,1028,1029,1094,1095,1265,1313,1431,1433,1906,1980,2033,2798],[97,143,226,1199,1351,1607,2790,2813],[85,97,143,226,617,1020,1024,1077,1094,1150,1430,1431,1432,1433,1908,1980,2760,2811,2812],[97,143,226,1199,1351,1607,2812],[97,143,226,617,1199,1351,1481,1498,1607,2768,2790,2797],[85,97,143,226,617,1020,1021,1023,1024,1029,1035,1069,1077,1079,1150,1313,1481,1498,1906,2768],[85,97,143,226,1082,1199,1351,1454,1455,1607,3146],[85,97,143,226,617,1020,1021,1024,1026,1029,1035,1069,1077,1079,1082,1084,1150,1313,1454,1455,1906,1908,1981,3145],[85,97,143,226,1199,1351,1607,1981,3145],[97,143,226,1020,1022,1024,1077,1301,1885,1908,1981],[97,143,226,617,1187,1199,1981],[97,143,226,1199,1351,1607,2814],[85,97,143,226,1020,1021,1023,1024,1029,1077,1094,1095,1151,1187,1313,1906,1983,2033],[97,143,226,1199,1351,2790,2800],[85,97,143,226,617,1020,1095,1313,1492,1985,2768,2799],[97,143,226,1199,1351,1607,2790,2799],[85,97,143,226,1021,1022,1029,1030,1069,1156,1265,1306,1906,1984,2033,2798],[97,143,226,1091,1199,1351,2801],[85,97,143,226,617,1492,1494,1985,2760,2768],[97,143,226,1199,1351,1492,1494,1607,2790,2802],[97,143,226,617,1199,1351,1492,1494,1985,2768,2799,2802],[85,97,143,226,617,1020,1095,1313,1492,1494,1985,2768,2799],[97,143,226,1199,1351,1607,2790,2803],[97,143,226,1199,1351,1494,2790,2804],[97,143,226,1024,1028,1077,1147,1149,1161,1176,1494,1984],[97,143,226,1091,1199,1351,2807],[85,97,143,226,1020,1024,1077,1099,1166,1306,1494,1984,1985,2800,2801,2802,2803,2804,2805,2806],[97,143,226,1199,1351,2805],[97,143,226,1199,1351,2790,2806],[97,143,226,1024,1077,1150],[97,143,226,1199,1494,1985],[97,143,226,1494],[97,143,226,1199,1607,2790,2808],[85,97,143,226,1020,1024,1080,1099,1156,1959],[97,143,226,617,1199,1351,2809],[97,143,226,617,1028,1077,1079,1094,1150,1506,1510,1908,2808],[97,143,226,1187,1199,1351,1512,1513,2790,2810],[85,97,143,226,617,1020,1022,1027,1030,1077,1079,1094,1150,1187,1512,1513,1908,2744],[97,143,226,1199,1351,1607,3092],[85,97,143,226,1020,1024,1161,1987,3091],[97,143,226,1019,1020,1024,1147,1149,1177,1314,1987],[97,143,226,617,1082,1199,1351,1607,1901],[85,97,143,226,617,1020,1082,1313,1898,1899,1900],[97,143,226,1199,1351,1898],[85,97,143,226,1024,1095],[97,143,226,1082,1091,1199,1351,1607,2963],[85,97,143,226,617,1020,1024,1082,1091,1898,1899],[85,97,143,226,1024,1026,1076],[97,143,226,1082,1091,1187,1199,1351,1607,2964],[85,97,143,226,617,1024,1035,1087,1151,1187,1463,1901,2760,2961,2962,2963],[97,143,226,617,1199,1351,1607,1899,1900],[85,97,143,226,617,1020,1024,1301,1899],[97,143,226,1199,1351,1607,2755],[85,97,143,226,1019,1020,1024,1964,2244],[97,143,226,1199,1351,1908],[85,97,143,226,1019,1907],[97,143,226,1199,1351,1607,3311],[85,97,143,226,1019,1099,1168],[97,143,226,1199,1351,3631],[85,97,143,226,1199,1351,2217],[85,97,143,226,1019,2070,2214,2215,2216],[85,97,143,226,1199,1351,2218],[85,97,143,226,1199,1351,2219],[85,97,143,226,2070,2216],[85,97,143,226,1199,1351,2216],[85,97,143,226,2214],[85,97,143,226,1199,1351,2220],[97,143,226,2070,2216,2217,2218,2219,2220,2221],[85,97,143,226,1199,1351,2221],[97,143,226,1199,1607,1952,2790],[97,143,226,617,1199,1351,1607,1919],[85,97,143,226,617,1020,1909],[85,97,143,226,1147,1148,1149],[97,143,226,1147,1149,1153],[85,97,143,226,1147,1149,1153,1158,1160,1199,1351,1607],[85,97,143,226,1019,1024,1147,1148,1149,1150,1151,1152],[85,97,143,226,1147,1149,1153,1155,1159,1199,1351,1607],[85,97,143,226,1020,1027,1147,1149,1154],[97,143,226,1152,1199,1351,1607],[97,143,226,1019,1020,1024,1030],[85,97,143,226,1147,1149,1161,1199,1351,1607],[97,143,226,1147,1149,1156],[85,97,143,226,1147,1149,1160,1199,1351,1607],[85,97,143,226,794,1019,1024,1147,1149],[85,97,143,226,1147,1149,1153,1159,1199,1351,1607],[85,97,143,226,1019,1020,1021,1024,1099,1147,1149,1158],[97,143,226,794,1020,1024,1147,1149],[97,143,226,1148,1149,1152,1153,1155,1157,1158,1159,1160],[85,97,143,226,1147,1149],[97,143,226,1168,1199,1351,1607],[85,97,143,226,518,1019,1024],[85,97,143,226,1021,1069,1199,1265,1351,1607,1906,2032],[85,97,143,226,1024,1035],[85,97,143,226,1019,1021,2008,2592],[97,143,226,1173,1199,1351,1607],[97,143,226,1032,1035,1166,1187],[97,143,226,1174,1199,1351],[85,97,143,226,851,1018,1019],[97,143,226,1076,1199,1351,1607],[97,143,226,1199,2749,2790],[85,97,143,226,2640],[85,97,143,226,1026,1199,1351,1607,2973],[85,97,143,226,1024,1025,1026,1647],[85,97,143,226,1026,1199,1351,1607,1648],[97,143,226,1199,1351,2881],[97,143,226,1020,1024,1908],[85,97,143,226,1023,1024],[85,97,143,226,1166,1285,1286,2756],[97,143,226,1026,1199,1351,1607],[97,143,226,1199,1351,1949],[85,97,143,226,744,1018,1019],[85,97,143,226,1024,1077,1954],[97,143,226,1162,1163,1199,1351],[85,97,143,226,1019,1024,1099,1162],[85,97,143,226,1035],[97,143,226,1165,1199,1351],[97,143,226,1164],[97,143,226,1166,1167,1199,1351,1607],[85,97,143,226,1019,1024,1164,1166],[97,143,226,1169,1199,1351,1607],[85,97,143,226,1019,1024,1168],[97,143,226,1163,1164,1165,1167,1169,1171,1172,1175,1176],[97,143,226,1171,1199,1351,1607],[97,143,226,1081,1099,1164,1170],[97,143,226,1172,1199,1351],[97,143,226,1175,1199,1351],[97,143,226,1166,1173,1174],[97,143,226,1176,1199,1351,1607],[85,97,143,226,1019,1099,1164],[97,143,226,1199,1351,2640],[97,143,226,1019,1028],[85,97,143,226,1414,1415],[97,143,226,1199,1370,1607,1955,2790],[85,97,143,226,1019,1020,1024,1028,1079,1094,1099,1370,1371,1372,1373,1375,1442,1951,1952,1953,1954],[85,97,143,226,1091,1187,1199,1351,1451,1607,1957],[97,143,226,1020,1024,1080,1091,1174,1187,1451,1956],[97,143,226,617,1187,1199,1351,1607,2799,2815],[85,97,143,226,617,1020,1029,1069,1095,1187,2768,2799],[97,143,226,1199,1351,3339],[85,97,143,226,623,1076,1187],[97,143,226,1187,1199,1351,1607,2790,3606,3608],[85,97,143,226,617,1187,3606,3607],[85,97,143,226,1024,1147,1149,1161,3606],[97,143,226,1019,1020,1024,1147,1149,1161,1177,1314],[85,97,143,226,1890],[97,143,226,1199,1351,2790,3234],[85,97,143,226,1020,1021,1029,1030,1076,1095,1265,1313,1652,1889,1906,2033,2223],[85,97,143,226,1024,1025],[85,97,143,155,164,226,1199,1607,1890,2790],[85,97,143,226,1020,1021,1023,1024,1028,1030,1035,1077,1099,1306,1884,1888,1889],[97,143,226,1187,1199,1351,2790,3236],[85,97,143,226,617,1020,1024,1077,1151,1156,1187,3235],[97,143,226,1199,2223],[97,143,226,1199,2229,2790,3237],[85,97,143,226,1024,1035,1077,1099,1166,2229,2310],[97,143,226,1199,3235],[97,143,226,1199,2225],[97,143,226,1162,1187,1199,1351,1367,1445,1501,1503,1514,1607,2790,3241],[85,97,143,226,617,625,1020,1021,1022,1024,1026,1029,1035,1069,1076,1077,1079,1080,1081,1087,1091,1094,1099,1166,1176,1187,1265,1301,1313,1367,1369,1439,1501,1649,1650,1651,1652,1884,1886,1887,1889,1902,1906,1915,1918,1920,2033,2225,2227,2230,2292,2578,2760,2762,2766,2772,2781,3228,3229,3230,3231,3232,3233,3234,3236,3237,3239,3240],[97,143,226,1087,1094,1199,1351,1506,1607,2790,3239,3241],[85,97,143,226,1024,1035,1087,1094,1166,1177,1187,1506,3238,3241],[97,143,226,1199,2227],[97,143,226,1032,1187,1199,1445,1607,2790,3240],[85,97,143,226,1021,1024,1032,1035,1081,1099,1147,1149,1161,1170,1177,1187,1414,1415,1445,1952,2751,2752,2774],[85,97,143,226,617,1081,1091,1187,1199,1351,1501,1607,1613,3612],[85,97,143,226,616,617,1020,1021,1022,1024,1026,1029,1032,1035,1079,1080,1081,1087,1091,1095,1187,1265,1301,1367,1369,1501,1503,1615,1649,1650,1651,1652,1886,1887,1889,1891,1902,1906,1915,1918,1920,2033,2227,2749,2760,2781,3229,3231,3233,3241,3608,3609,3611],[97,143,226,1032,1199,1503,1988],[97,143,226,1032,1187,1503,1969],[85,97,143,226,1032,1199,1351,1503,1607,2790,3611],[85,97,143,226,1020,1021,1024,1026,1032,1094,1147,1149,1161,1367,1414,1415,1503,1988,3610],[97,143,226,1019,1020,1024,1032,1147,1149,1150,1161,1166,1177,1187,1314],[85,97,143,226,617,1187,1199,1607,2790,3609],[85,97,143,226,617,1020,1021,1023,1024,1025,1077,1081,1099,1187,1313,1367,1652,1904,2781],[97,143,226,1199,2230],[85,97,143,226,1031,1032,1187,1199,1351,1607,2773,2790],[85,97,143,226,617,623,1020,1021,1022,1029,1030,1031,1032,1035,1075,1076,1079,1081,1087,1187,1313,1366,1367,1469,1506,1649,1650,1651,1652,1653,1887,1888,1889,1892,1902,1904,1906,1912,1913,1914,1915,1918,1920,1923,2016,2033,2051,2230,2232,2234,2769,2770,2771,2772],[97,143,226,1032,1094,1187,1199,1351,1367,1607,2750,2774,2790],[97,143,226,1032,1091,1094,1187,1199,1351,1460,1462,1607,2750,2774,2790],[85,97,143,226,617,1020,1024,1032,1077,1087,1091,1094,1095,1099,1166,1168,1173,1179,1187,1301,1367,1445,1448,1449,1460,1462,1469,1506,1888,2016,2054,2233,2578,2750,2754,2758,2759,2760,2761,2762,2766,2767,2768,2773],[97,143,226,1199,2234],[97,143,226,1032,1265,1888,2230,2232,2233],[97,143,226,1199,1351,1607,2754],[85,97,143,226,1020,1024,1028,1035,1099,1168,1179,1314,1952,2751,2752,2753],[85,97,143,226,1087,1091,1199,1351,2774],[97,143,226,1199,1285,1295,1351,2758],[85,97,143,226,1077,1087,1286,1295,1301,2222,2755,2757],[97,143,226,1199,1322,1351,1607,2647],[85,97,143,226,1020,1024,1099,1314,1322],[97,143,226,1091,1187,1199,1351,1607,3615],[85,97,143,226,1020,1024,1025,1091,1099,1187,1313,1903,1975,3064,3614],[97,143,226,1199,2790,3614],[85,97,143,226,1019,1030],[85,97,143,226,617,1091,1187,1199,1351,1607,2790,3618],[85,97,143,226,617,1091,1187,1369,1990,3065,3617],[85,97,143,226,1187,1199,1351,1607,2790,3617],[85,97,143,226,1024,1030,1147,1149,1161,1187,3614,3616],[97,143,226,1147,1149,1187,1199,1351,1607,3616],[97,143,226,1035,1147,1149,1161,1177,1187,3614],[85,97,143,226,1199,1351,1607,2790,3619],[85,97,143,226,1369,3615,3618],[97,143,226,1020,1199,1300,1351,1607],[85,97,143,226,704,1019,1020],[85,97,143,226,1018,1019],[97,143,226,1199,1351,1953],[85,97,143,226,742,1019],[97,143,226,1099,1199,1351],[97,143,226,844,1013,1018,1019],[97,143,226,1199,1351,2639],[97,143,226,844,1013,1018,1019,1028],[85,97,143,226,1020,1199,1351],[97,143,226,744,1018,1019],[85,97,143,226,1199,1351,2215],[85,97,143,226,1019,2214],[97,143,226,748,1019,1024],[97,143,226,754],[85,97,143,226,1014,1019,1020,1023,1024],[85,97,143,226,803,1019,1020,1024],[85,97,143,226,794,1019,1024],[85,97,143,226,1029,1199,1351],[85,97,143,226,1018,1019,1027,1028],[97,143,226,910,1019],[85,97,143,226,1018,1019,1020,1021,1022],[85,97,143,226,897,1019],[97,143,226,921,923,1019],[85,97,143,226,1020,1021,1022,1027,1028,1077,1099,1150,1151,1199,1313,1351,2215],[85,97,143,226,931,1019],[97,143,226,1030,1199,1351,1607],[85,97,143,226,951,1019,1024],[97,143,226,765,1019],[97,143,226,1019],[97,143,226,961,1019],[97,143,226,615,1024,1322],[97,143,226,965,1019],[97,143,226,972,1018,1019],[97,143,226,1035,1199,1351,1607],[85,97,143,226,1011,1019,1024],[97,143,226,1199,1313,1351],[85,97,143,226,1019,1312],[97,143,226,1187,1199,1351,1607,2816],[85,97,143,226,617,1020,1021,1024,1029,1030,1035,1069,1187,1265,1313,1906,2033],[97,143,226,1187,1199],[97,143,226,617,1187,1199,1351,1607,3222],[85,97,143,226,617,1020,1024,1029,1095,1187,1265,1313,1906,1908,2033,2798],[97,143,226,1032,1094,1187,1199,1351,1607,2048,3300],[85,97,143,226,1027,1035,1083,1094,1161,1166,1177,1187,1285,1884,2048,2222,2774],[97,143,226,1199,1285,1351,1607,3627],[85,97,143,226,1077,1147,1149,1161,1166,1177,1285,2222],[97,143,226,1199,1991],[97,143,226,1187,1199,1351,3633],[85,97,143,226,1025,1035,1077,1187,1301,1964,2222,3631,3632],[85,97,143,226,1187,1199,1351,2777,2790],[85,97,143,226,620,621,1032,1087,1187,1923,2748,2776],[97,143,226,1187,1199,1351,1512,2744,2790],[85,97,143,226,1020,1024,1187,1512,1908,2700,2743],[97,143,226,1076,1111,1199,1351,1920],[85,97,143,226,1076,1111,1187],[97,143,226,1199,1305,3674],[97,143,226,530,1305],[97,143,226,1199,1351,1607,2244,3101,3103],[85,97,143,226,1020,1024,1154,1176,1952,2244,2752,3101],[85,97,143,226,1091,1147,1149,1187,1304,3101,3102,3103],[97,143,226,1147,1149,1199,1351,1607,3101,3102],[85,97,143,226,1021,1024,1030,1147,1149,1161,3101],[97,143,226,1147,1149,1177,2752],[97,143,226,1199,1351,2253],[85,97,143,226,1199,1607,2252,2790],[85,97,143,226,1024,1080,1166],[85,97,143,226,1024,1035,1077,1099,1151],[97,143,226,2239],[85,97,143,226,1199,2239,2240,2790],[85,97,143,226,1035,1187],[85,97,143,226,1199,1607,2240,2250,2790],[85,97,143,226,1035,1166,2239,2247,2248,2249],[85,97,143,226,1199,1607,2240,2247,2790],[97,143,226,1199,1351,1607,2790,3113],[85,97,143,226,1301,1313,1369,3097,3100,3104,3112],[85,97,143,226,1032,1091,1147,1149,1187,1199,1351,2044,2244,3105],[97,143,226,1032,1087,1091,1147,1149,1187,2044,2242,2244,2795],[97,143,226,1199,1351,2243],[97,143,226,1019,1099],[85,97,143,226,1199,1351,1607,2272],[85,97,143,226,1024,1080],[85,97,143,226,1020,1024,1035,1099,1177,1305,2241,2242,2243,2244],[85,97,143,226,1199,1351,1607,2269,2275],[85,97,143,226,1024,1080,2269,2274],[97,143,226,2280,2281],[85,97,143,226,1199,1351,1607,2269,2276],[85,97,143,226,617,2269,2271,2272,2274,2275],[97,143,226,1199,1351,2259],[97,143,226,526,2241,2258],[97,143,226,1199,1351,1607,2242,2280],[85,97,143,226,1020,1024,1035,1077,1080,1099,1166,1183,1301,1313,1634,2241,2242,2244,2250,2251,2252,2253,2254,2255,2256,2259,2260,2268,2279],[97,143,226,1091,1177,1187,1199,1351,2242,2281],[85,97,143,226,1020,1024,1091,1154,1166,1177,1187,1301,1452,2236,2238,2241,2242,2243,2245,2246,2260,2280],[85,97,143,226,1199,1351,1607,2269,2277],[85,97,143,226,617,2241,2269,2271,2274],[97,143,226,2269],[85,97,143,226,1199,1351,2279],[97,143,226,2270,2276,2277,2278],[85,97,143,226,1199,1351,1607,2278],[85,97,143,226,1024,1035,1099,2271],[85,97,143,226,1183,1199,1351],[97,143,226,1019,1024,1099],[97,143,226,1199,1351,1607,2271],[97,143,226,1019,1020,1024,1035],[85,97,143,226,1199,1351,2274],[97,143,226,1019,2269,2273],[85,97,143,226,1199,1351,2273],[97,143,226,1019,2269],[97,143,226,1199,1351,2256],[97,143,226,1199,1351,2255],[97,143,226,1035,1952,2241],[85,97,143,226,2241,2242],[97,143,226,1199,2260],[97,143,226,1199,2244,3106],[97,143,226,2244],[85,97,143,226,1020,1021,1024,1079,1954,2236,2244,3106],[85,97,143,226,1162,1199,1351,1443,1488,1490,1607,2236,2790,3105,3108],[85,97,143,226,1021,1025,1026,1030,1032,1161,1162,1443,1488,1490,1648,2236,3105],[97,143,226,1091,1187,1199,1351,1607,1613,2242,2244,2790,3112],[85,97,143,226,1032,1091,1147,1149,1177,1187,2236,2237,2242,2244,2282,2774,3105,3107,3111],[85,97,143,226,1024,1032,1147,1149,1161,2242,3105,3108,3110],[97,143,226,1161,1199,1351,1607,2242,3110],[97,143,226,1147,1149,1161,1166,1177,1305,2236,2242,3109],[97,143,226,1151,2261],[97,143,226,2261,2262,2267],[97,143,226,2261],[85,97,143,226,1301,2261,2263,2264],[85,97,143,226,1024,1099,2261,2265],[97,143,226,1199,1351,1607,2242,2262,2267],[85,97,143,226,1024,1080,2242,2262,2266],[97,143,226,1199,2242,2262],[97,143,226,2242,2261],[97,143,226,1199,1351,3109],[97,143,226,2236],[85,97,143,226,1024,1080,1305],[85,97,143,226,1094,1166,1187],[97,143,226,1024,1032,1147,1149,1150,1161,1173,1177,1187,2751,2752],[85,97,143,226,1032,1187,1199,1351,1445,1446,1607,1613,2750,2776,2790],[85,97,143,226,1021,1024,1026,1032,1147,1149,1161,1367,1414,1415,1445,1446,1503,1615,2749,2774,2775],[85,97,143,226,620,621,622,1087,1187],[85,97,143,226,518,2003,2004],[97,143,226,1199,1351,2645],[85,97,143,226,616,1187],[97,143,226,1091],[85,97,143,226,1187],[97,143,226,2289],[97,143,226,2285,2286,2287,2288,2290],[85,97,143,226,617,1091,1187,1199,1351,2294],[97,143,226,617,1091,1187],[97,143,226,618,1187,1199,1351,3137],[85,97,143,226,617,618,1187,2315,2589],[85,97,143,226,1199,1321,1322,1323,1351],[85,97,143,226,1321,1322],[85,97,143,226,1073,1187],[97,143,226,1187,1199,1351,3154],[85,97,143,226,617,618,619,1187,2291,2315,2589],[85,97,143,226,617,618,1187,2291,2315,2589],[85,97,143,226,1093,1187],[97,143,226,1199,1304],[97,143,226,1184,1186],[97,143,226,1104,1105,1199,2297],[97,143,226,1078,1104,1105,1106,1108,1109,1110,2296],[97,143,226,1019,1199],[97,143,226,1015,1016,1018],[97,143,226,1069,1199,1351,2301],[97,143,226,1069],[97,143,226,1199,2303],[85,97,143,226,1199,1265,1351,1607,2033],[97,143,226,1069,1255,2032],[97,143,226,1185,1199,1293],[97,143,226,616,624,1184,1185,1291,1292],[97,143,226,616,1199],[97,143,226,1184,1199],[97,143,226,1185,1199],[97,143,226,1184],[97,143,226,616,617,1199],[85,97,143,226,615,616],[97,143,226,2008],[97,143,226,1087,1199,1366],[97,143,226,1087],[97,143,226,619,620,1199],[97,143,226,619],[97,143,226,617,1166,1199],[97,143,226,617],[97,143,226,1178],[97,143,226,1199,2315],[97,143,226,621,622,1199],[97,143,226,621],[97,143,226,1199,2576],[97,143,226,2575],[97,143,226,1199,2578],[97,143,226,1199,1956],[97,143,226,1199,1370],[97,143,226,1199,2583],[97,143,226,619,1199],[97,143,226,618],[97,143,226,1199,1916],[97,143,226,1178,1199],[97,143,226,1187,1199,1620],[97,143,226,1087,1187],[97,143,226,1199,1634],[97,143,226,1187,1199,1379],[97,143,226,1199,2008,2593],[97,143,226,2008,2592],[97,143,226,1199,2008,2592,2596],[97,143,226,2008,2593,2595],[97,143,226,1199,2595],[97,143,226,1086],[97,143,226,1087,1187,1199],[97,143,226,1199,1382],[97,143,226,1032,1199,1963],[97,143,226,1070,1199],[85,97,143,226,1086,1091,1199,1351,2635,2779],[97,143,226,2607,2618],[97,143,226,2607,2620],[97,143,226,2607,2622],[97,143,226,2607,2624],[97,143,226,2607,2626],[97,143,226,2607,2628],[97,143,226,1199,2607],[97,143,226,2609],[97,143,226,1199,2611],[97,143,226,1199],[97,143,226,1199,1351],[85,97,143,226,1091,1199,1351,1607,1613],[97,143,226,1094,1199,1285,2790,3300],[97,143,164,226,612]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"814d5c7384f3ca276e9dc4bcfde5545801a3ea0bfae09916b3336774e662fd1b","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"27fdb0da0daf3b337c5530c5f266efe046a6ceb606e395b346974e4360c36419","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"47ab634529c5955b6ad793474ae188fce3e6163e3a3fb5edd7e0e48f14435333","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"fad4e3c207fe23922d0b2d06b01acbfb9714c4f2685cf80fd384c8a100c82fd0","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"66be1299a7a3129ceb488b340c291cf575bebb0e337f92e169dec38231472e34","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"b838d4c72740eb0afd284bf7575b74c624b105eff2e8c7b4aeead57e7ac320ff","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"48dcc919f76c040a999c0d46d2bf25ab089645ca21b837f120b222f56a86cd76","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"cd9304972e6d616197fb44fce00540a904f38b54306a1951b5dbeaf3c01ab5bd","impliedFormat":1},{"version":"77438e2c397a3db78407621cfc57241a305b310ddea2c185f1d555248297f587","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"7715134a0cf07dd41a9da2895d708625a3a303a0385e355ecaaf0b8bfaef2550","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e5c939d896565dcac0f6fbdbada11284e7728ef26a069561c09aa5aa4a788393","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"2467b00d963828f540f4acd7910f4c04cfe4b489550e6bb682212f65583bca5b","impliedFormat":1},{"version":"854e50b93090b3f8fd6e355b074e1d24dce1ae0240f1ce46563e35fea210a6d5","impliedFormat":99},{"version":"5a16e93d5d53d987dddda1ec606c9821f6bd31d1bdf0635e05e3841312cefa8b","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"b55eb9f72166093b5460d34b34f5d8699c968de3bc3fc696e40f2c93f2ebf650","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"c30436b130b6218b7714314dc41d3f459590db4bdf099eecd51cb1bda32109a8","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"6ee598cdfdd0fa52039dca135b3dfff7b49035dc13292143e0a93843e3861967","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"eb5acb58487367e502d994b57e2c58255d8241f481ea8efa8e79af23af3f41c2","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"5757b78830c681b3124af568b94c269259ea5e8171a4316508ef67310c2ed1ed","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"db2d933d8101f90deeec6698e70f1e14729495c5daab3199f4cdf0ac78a87bdf","affectsGlobalScope":true},"7b550dda9686c16f36a17bf9051d5dbf31e98555b30d114ac49fc49a1e712651",{"version":"04471dc55f802c29791cc75edda8c4dd2a121f71c2401059da61eff83099e8ab","impliedFormat":99},{"version":"5c54a34e3d91727f7ae840bfe4d5d1c9a2f93c54cb7b6063d06ee4a6c3322656","impliedFormat":99},{"version":"db4da53b03596668cf6cc9484834e5de3833b9e7e64620cf08399fe069cd398d","impliedFormat":99},{"version":"ac7c28f153820c10850457994db1462d8c8e462f253b828ad942a979f726f2f9","impliedFormat":99},{"version":"f9b028d3c3891dd817e24d53102132b8f696269309605e6ed4f0db2c113bbd82","impliedFormat":99},{"version":"fb7c8d90e52e2884509166f96f3d591020c7b7977ab473b746954b0c8d100960","impliedFormat":99},{"version":"0bff51d6ed0c9093f6955b9d8258ce152ddb273359d50a897d8baabcb34de2c4","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"13918e2b81c4288695f9b1f3dcc2468caf0f848d5c1f3dc00071c619d34ff63a","impliedFormat":99},{"version":"120a80aa556732f684db3ed61aeff1d6671e1655bd6cba0aa88b22b88ac9a6b1","affectsGlobalScope":true,"impliedFormat":99},{"version":"a7ca8df4f2931bef2aa4118078584d84a0b16539598eaadf7dce9104dfaa381c","impliedFormat":1},{"version":"10073cdcf56982064c5337787cc59b79586131e1b28c106ede5bff362f912b70","impliedFormat":99},{"version":"72950913f4900b680f44d8cab6dd1ea0311698fc1eefb014eb9cdfc37ac4a734","impliedFormat":1},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"36977c14a7f7bfc8c0426ae4343875689949fb699f3f84ecbe5b300ebf9a2c55","impliedFormat":1},{"version":"ff0a83c9a0489a627e264ffcb63f2264b935b20a502afa3a018848139e3d8575","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fd8a5aefd8c3feb3936ca66f5aa89dff7bf6e6537b4158dbd0f6e0d65ed3b9e","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"41c41c6e90133bb2a14f7561f29944771886e5535945b2b372e2f6ed6987746e","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":1},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e4af494f7a14b226bbe732e9c130d8811f8c7025911d7c58dd97121a85519715","impliedFormat":1},{"version":"cbb1c5ba5dbabe42c19ca31b83e48fec95895484fe1d1a8fb649b69ea224c5b8","impliedFormat":99},{"version":"45cec9a1ba6549060552eead8959d47226048e0b71c7d0702ae58b7e16a28912","impliedFormat":99},{"version":"6907b09850f86610e7a528348c15484c1e1c09a18a9c1e98861399dfe4b18b46","impliedFormat":99},{"version":"12deea8eaa7a4fc1a2908e67da99831e5c5a6b46ad4f4f948fd4759314ea2b80","impliedFormat":99},{"version":"f0a8b376568a18f9a4976ecb0855187672b16b96c4df1c183a7e52dc1b5d98e8","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"092944a8c05f9b96579161e88c6f211d5304a76bd2c47f8d4c30053269146bc8","impliedFormat":99},{"version":"b34b5f6b506abb206b1ea73c6a332b9ee9c8c98be0f6d17cdbda9430ecc1efab","impliedFormat":99},{"version":"75d4c746c3d16af0df61e7b0afe9606475a23335d9f34fcc525d388c21e9058b","impliedFormat":99},{"version":"fa959bf357232201c32566f45d97e70538c75a093c940af594865d12f31d4912","impliedFormat":99},{"version":"d2c52abd76259fc39a30dfae70a2e5ce77fd23144457a7ff1b64b03de6e3aec7","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"f73e2335e568014e279927321770da6fe26facd4ac96cdc22a56687f1ecbb58e","impliedFormat":99},{"version":"317878f156f976d487e21fd1d58ad0461ee0a09185d5b0a43eedf2a56eb7e4ea","impliedFormat":99},{"version":"324ac98294dab54fbd580c7d0e707d94506d7b2c3d5efe981a8495f02cf9ad96","impliedFormat":99},{"version":"9ec72eb493ff209b470467e24264116b6a8616484bca438091433a545dfba17e","impliedFormat":99},{"version":"d6ee22aba183d5fc0c7b8617f77ee82ecadc2c14359cc51271c135e23f6ed51f","impliedFormat":99},{"version":"49747416f08b3ba50500a215e7a55d75268b84e31e896a40313c8053e8dec908","impliedFormat":99},{"version":"5e91172586d1be7d1508d1a40c8bf76161f6f4179d1c25158a20599f9fb26a66","impliedFormat":99},{"version":"09d215b379a93f8cbb6caf9e9f5d7b4d48042295ee697e7e4771288c7917a21e","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"891694d3694abd66f0b8872997b85fd8e52bc51632ce0f8128c96962b443189f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"971a2c327ff166c770c5fb35699575ba2d13bba1f6d2757309c9be4b30036c8e","impliedFormat":99},{"version":"4f45e8effab83434a78d17123b01124259fbd1e335732135c213955d85222234","impliedFormat":99},{"version":"7bd51996fb7717941cbe094b05adc0d80b9503b350a77b789bbb0fc786f28053","impliedFormat":99},{"version":"b62006bbc815fe8190c7aee262aad6bff993e3f9ade70d7057dfceab6de79d2f","impliedFormat":99},{"version":"ab80d2cb170a92c61ca8c822d0db0fc50eb15c7c6cf1a24cb01852a5a15a7db8","impliedFormat":99},{"version":"210955af8573671abebb6e1391d134d82fbcff130aa68922ea1e839f8f48a0ad","impliedFormat":99},{"version":"78a8f2e95e2904914c509e4bbc68e626f8b8ff8f30ca96cffc7a795fa17394f5","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"061446b67af18b541c723104f25aa94667dd438c050fc873f3c02a7b5a9a3ef0","signature":"b8ee70929b7bfa2ced6aded5f38945440e9ff6809c61d2972b59aaecf88c254c"},{"version":"8ef0b457802d1883c0c185f90610a8aaf33283250633a31853de01bb45565b7b","signature":"b730dbc27807d6a94494d69e0154827379b8ed4606f3dd3a4584a1e2242b1e53"},{"version":"64bc7684d633c835220935b80701168771e6ddc8c3d9145af8bb3a3ac7d0c59a","impliedFormat":99},{"version":"121fc7776751821e405243a0c188554d2749dd334482a1d311af61373072a89a","signature":"1c508f6403621b58f8d59e7eb61eb61788714be526c91dc3cad739330b6923b1"},{"version":"598c32af38ceddfaf9699b9013ecf2e0b2df7b5d76795c9de010d5ff92c52ad5","signature":"e064b7ccad9850f3a78ba58a45e43e4b3eaf126cd2bd2979896b5885dea07f57"},{"version":"618bb47ac74b0ab39b90743cfe920a6e670b2d3bfa24f37d106b535c1c18ae37","signature":"4c8a45f845c330caf5dc2bc33da6e8e4f317035d08a20fd5f1da986bea511d60"},{"version":"9f50731b7a6739ad4d5d0e00b5d0be3650535cd74d92bf86ba3b81cf57000269","signature":"64be38d2ab0fa005245ad20baf0fc7899f1db575a219b4428e0fc3e550d02410"},{"version":"1d0628911b56f83b654ca0ba826d503b3605926e73b985e754513832eeab5592","signature":"45b43c38bc20ca8c0edcee887ed49ed8b771dbe78e1cb5b4e3ba794e853fa887"},{"version":"6d575d93896c413b308c3726eed99ddd17e821a00bdd2cc5929510b46fe64de4","impliedFormat":99},{"version":"1beebd50610b0c9701d2de263e0183ec22aad6c051d0e15ce9e6cce295c6a40b","signature":"3c49b34b1c62e5d74c637b63276c9acacb605689334f5450cc3f67f560ac0ecf"},{"version":"310c820b803950d18c0ed9376df2cd73def2f56cfcc993f9012008403cdd4843","signature":"6fb95390f4022e0327e4a170917a06de5caad8c8c563c8b00be3cd40a71c759e"},"b7c5583a7e76b44bf7f987827c2793272b11c74d9f0a5c6b196f1035f8dfcf6a","47f5078d810ecb6e57eea5f0382dbfb9db641a35460fdb723e920c4898852e0b",{"version":"ddc62c8eb6b7fb8e8fd0f0f19809530b1e5ba5a131471a6eef65028d0d3b5a6e","impliedFormat":99},{"version":"802cbde8e06732ca0356927b4c9fbc39f5961df58a18b74fdc4e131269293a5d","impliedFormat":99},{"version":"480713ff75c24f445e3f159da28444406e1334730375c94d0aa24523c5e52e1c","impliedFormat":99},{"version":"3ac6eb2cafcb89a552a4923213c705f0fc3c2b50e466eae9dc1a540e3af18bc0","impliedFormat":99},{"version":"073f96a1cfddfedf8695401f8328a8e84a4d98fa5b08b4d894c3885069083cd6","impliedFormat":99},{"version":"66ba40fa928c2fada9a280a61c2b426dfbbfa69085f99913650ff72ddec75b1a","impliedFormat":99},{"version":"4d857105510df8011cfb5b3769dec55624a1df92e85d399cd03bc82bb89d090c","impliedFormat":99},{"version":"19a22f3446387435f13445a31e3d4eb65f132d8e6b7b060d249f0fb138cec698","impliedFormat":99},{"version":"ecc46b24349caeab20d889baf0a6f9d3beafa739a0f2c36afc107dceb15e7b2b","impliedFormat":99},{"version":"b887624859a2f03e78ae6018e96bd269b5318685f42adec4e93256ed7579c125","impliedFormat":99},{"version":"14191b461a91229ff4b388d15b9e15392a8a3af9bf11fa7d0d4cd31405178e75","impliedFormat":99},{"version":"23a564e852dc91b6e6f050584994b35156f6ee8a2d08c493dad04309046a8397","impliedFormat":99},{"version":"daf66c9de89f11011ef703af894970bb15985fd5a4156b8038e895ad4e4616a7","impliedFormat":99},{"version":"d59c3d0c3283c1878913fc2bc88d84160dbcdc69cf06f822ca7ffb39eefef13b","impliedFormat":99},{"version":"05128b72488ad970c2e30ae6b82c7ee232be49ce6def3b4dd56f62d8b7f7704c","impliedFormat":99},{"version":"9eb8e1320fc0ecbfba15c0f3452dfc1957543dfbd466aaf8b67ddb0f2ad0f217","impliedFormat":1},{"version":"4faca872dbd194a17b3ee267bd8ddc3daf3d16df96f4e43a02c7d9a862022c4f","impliedFormat":99},{"version":"87654de60b5cd8d91d59632ec576fa7e313b41c2540073d52814b6cf5bb739e6","impliedFormat":99},{"version":"144a4e5780b800c0553949169f50be285eccbdb0298afd83ef2ae03fef77e2d2","impliedFormat":99},{"version":"66aeb47bf8638d6767f7b4ff684c2d794391c981590073025e98f98e1afed499","impliedFormat":99},{"version":"cd5b0672c9699fe169d69efd65472a874de9d1e25fa8669a934f5f326bf0f025","impliedFormat":99},{"version":"4577621880c696b0aacec6ebd2dbf97ac178ee2e2bfaa0aa3a5260a798220ab4","impliedFormat":99},{"version":"26731910f98a56ed001d25d5167d85b1320def4ffbb76e1cc4b0c6484482a5e2","impliedFormat":99},{"version":"cbaadb95dcc68691900ffa857b3bd7eaa99eeb6c351afca15103560bc87f0d15","impliedFormat":99},{"version":"97b02501eb45f487174d5a0ff89b6a95690d50e9eae242e2162118edd5f2705c","impliedFormat":99},{"version":"bbea0619511648a92fe83d5c8eed6149106d7fbf3065310a1986d18598b83bbf","impliedFormat":99},{"version":"963ece6abb58542445eda863960cf053a98da8f4e8634b7a8826aa04f6f85a56","impliedFormat":99},{"version":"5ecea63968444d55f7c3cf677cbec9525db9229953b34f06be0386a24b0fffd2","impliedFormat":99},{"version":"1d226c1e6786584e97efede708d49f2dbd6f887905f16c785d5f09b300bc098d","impliedFormat":99},{"version":"07ff7d4360fbc945963d7a4a8105a5520d1681a00745c20a962fb36bf04452de","impliedFormat":99},{"version":"1b1c48c4d7cbe6f40616594c2a3f6f95bb1dcefd200a7e4167e47b67725b631a","impliedFormat":99},{"version":"62076be1e1e8b668a8ddcb803402f1aec725a31d592e8722ff39ad368d9cd472","impliedFormat":99},{"version":"2806e4d2a88e0461c3b0c8cd9e7bc8e927034690e33345aa0853439d67f801b4","impliedFormat":99},{"version":"b1d72bde8f54695b85883613af13295a615034b2829dde3a31bd3d2a40eb6bc4","impliedFormat":99},{"version":"26cfaec143443411bc7d5363f274f885ced430b8f4bee25a81f7827248848d7b","impliedFormat":99},{"version":"6870f32dc76ff6f8f6a419ce55add0a011909e8895252d7cca813835f431783f","impliedFormat":99},{"version":"29fcff21ff0ecbe700c7db7f719af2fb4822a08d6d703b6687822535e8bd3126","impliedFormat":99},{"version":"5597cbcd19e16f5c9148c76c914e158680de55849b625c2f6b69723f01f1007c","impliedFormat":99},{"version":"7864233f21a3bd04eb6dfa79103a6c1d0648cf17eb4c47cc7aef19d274dd639c","impliedFormat":99},{"version":"b27f7733758db8f462dadf0ee250056e370413028c99fc723c4a93baa54a7c1c","impliedFormat":99},{"version":"5bd7f6f573ac89ec20aaf326e79394da8a89fbff8a297aa864de9137ca045678","impliedFormat":99},{"version":"81b2bebeae6ec1e73b491fe22a82c7e2d3a8369271e622ed74b6e94ba108a475","impliedFormat":99},{"version":"d0e4a184f48eba140f30e8f770853b884e01694f6aff59b38d0be55b0410d397","impliedFormat":99},{"version":"1d34b3ef8e5926334d86d305477d3592d648adb41fa0110970a68059e13d45c0","impliedFormat":99},{"version":"791e26804cd328b19fc37f7903813e8e41892e70d5241dbe2c39fdb52fdd0c9a","impliedFormat":99},{"version":"02c2773eb8536a50f6e647483e78e8c2991fea8ac32ab69a37f9a24255401530","impliedFormat":99},{"version":"4ac6d584eada1621a7eaa4bfb3dd54e81c2c8a82c7ffdf421ae58d84c3a3490e","impliedFormat":99},{"version":"a522abad9b9b959a9c4bdb4e6bdad96e65d97da9385be13ffc8affc3669fd786","impliedFormat":99},{"version":"ae733b8a8fc9659e24821aa3797d25cfdc205bd31674227b49411ca4d54e510c","impliedFormat":99},{"version":"0aa9c5135c3a086d7c01d8d18409da6b01fd32f09a4a261048f8cb4653f22be1","impliedFormat":99},{"version":"1e185a3af4b4f3bd6fd52fde968f14dcf9a8cbbc4924237270e290e25d81fe40","impliedFormat":99},{"version":"ae42c6173cc8ad49d6ae21187d0bb7c7c65da10204f9e2614eeb83b29c58f4f7","impliedFormat":99},{"version":"050240464b97ffce2e353ccd5251660f5d3dcf9dc834f88504732ded7cfe926c","impliedFormat":99},{"version":"3f896952650454552b2584ef1e3dd072e97f8498908cd2ab25e6b0217e8bfeb2","impliedFormat":99},{"version":"3b3d0685f081f6a02cda029e4d1e1ba5f10690870c971e6697e0c2539501e835","impliedFormat":99},{"version":"5998b174ccb38a61393170f40448f80152ca5518f9d2048f5b5d3cbe0a9fbac2","impliedFormat":99},{"version":"6707d39d8afa069222d0674016d48c4772067eb671f9b62528a6cc8218fd5b40","impliedFormat":99},{"version":"481aab62f04afa6eab4e439fb4f39af392c5c51519f548897ff71e6bad0b6771","impliedFormat":99},{"version":"51de9d738596fcc085d13bdf86c0014f15d9b4e6986631c7be3df9d2f61590d8","impliedFormat":99},{"version":"8eca47167dadd486582ecd4e41f7fba6ae66cc4a4c5202f1f7acf34129a0dadf","impliedFormat":99},{"version":"29cc3322fd17fd1b55ea2150ad6f7cb37f0b587efca5696819cc5b6e95331bd4","impliedFormat":99},{"version":"d09b7414a64adc7cae660ecd6e8a222ad9fa58585dd2390eb0aaabfee812b354","impliedFormat":99},{"version":"769b6f9f1cd9471261d137513abc391a744a3c3a62f492491bcde520219fab53","impliedFormat":99},{"version":"49fdbd971a9b57df943498b37cf11c40fe09b2675493039a0b7841671f385108","impliedFormat":99},{"version":"faba6f3b673c89279d3b41a47e8ea2c850665eadfa1e2a56be4f50a6bf4356c6","impliedFormat":99},{"version":"5cbc3c3c6475704af132c35b095da392a03815baf2e9f2853178ff9b370b64d2","impliedFormat":99},{"version":"074209bc8fc6979cfc363d392a8babe62685adc61c62a8742ecdb86fb9b62ad0","impliedFormat":99},{"version":"6826e70645f65e77bcceb9230962687109301a4ad9d6dbb71a7785167d4a4b9e","impliedFormat":99},{"version":"3c8b637a833f97a085417e7d0024ac82f7fafc0834a4c61d5e48f8edd8da6c10","impliedFormat":99},{"version":"a3a4132d6c64f431b6d0cc890557c392f57eb43371bb73979ea38d27c86a1c4c","impliedFormat":99},{"version":"ec03be0777b98df75dcd97657ebfac0eb7a9153867aab050a591b6caadf1c2a2","impliedFormat":99},{"version":"c788aaea8be5712b40c3bd9cf589c9510930af7b2aa3d986125df0dedc569290","impliedFormat":99},{"version":"e7983072a038e512514c146b25e7e97a8a070ca3507950658ab1e96f6598957c","impliedFormat":99},{"version":"f2333bb4a221631fe506a0354fffb808507d4e5f6fe2c85b69890618226f7d9b","impliedFormat":99},{"version":"6c9114366ff07ee8f5c3cd4ba94ad189a098ce8368040909d605fb38e636d026","impliedFormat":99},{"version":"13e930c27d68ecfa906c24d599b10927b152030d07da0fa0889fd4fddc5b4115","impliedFormat":99},{"version":"b4e61f4f522304f7fce1038590ca1f6d091d58aff84833861848f8157732d8db","impliedFormat":99},{"version":"8c86f563e8bcefb0b5b1ac62e5a27ee6a2a9b775e72dea5793823edeb24d36e9","impliedFormat":99},{"version":"d9134c8daef2565f20b72171f634800efb204eba63b03142d5dae5f36088e95a","impliedFormat":99},{"version":"faf9a217d8d237b02ab6d95508d8736ae431bbeb38d98885eb5b8fb6dbe48cec","impliedFormat":99},{"version":"e702ed1fd1dcb24ec2634901441fd156449f75458359c771074cbe7675e86614","impliedFormat":99},{"version":"bbb044421875fc84b7d2f2aac4fb14499687cae5a5063da51bfa28c58239bcfb","impliedFormat":99},{"version":"74b564cd3da8f83d5e472a5b0cc53bf7e276b25576097cb89e6f67caf95b12dc","impliedFormat":99},{"version":"3705ba677801103461ff0a06d34b6b2149072952365e55d8266969978dd33154","impliedFormat":99},{"version":"cca68a7703ec3717b6d4c287884fc79ba811f894c472718126010418cd306aa7","impliedFormat":99},{"version":"590708a598f58b156518493c563df1d03040d3b2b7f75fe614e1ada06dbb44dc","impliedFormat":99},{"version":"dc2c32ad9c49a7c3e56a18f3f42933e91474bc26ecc2ea47cf533818a54e6471","impliedFormat":99},{"version":"e061e898ffe9970c067278f5a7462665e2706e7cc6ce2362276eca1c92c128f7","impliedFormat":99},{"version":"e5ee49966285e5afa0dd2db7f66acf1e8a9e1d0bc5724b03b67be92ea7819bfc","impliedFormat":99},{"version":"4db2be160aa80fecd367876f8cf1aa197cd1f296e5f82ed8d8b961d9ececb204","impliedFormat":99},{"version":"5a8e4a5e571755e265bd6a840d8ab48eeb1ca2e35487d96bbd601ed296e2d1b3","impliedFormat":99},{"version":"d93cac0bbb7e1fe241f4b0493cd47466df00d9f1c51a53b69e5442456cb4d102","impliedFormat":99},{"version":"9de94cfccea0da314e8554d6b2f1f01a1b63fa4c79dc24b54277e86b918e9d6f","impliedFormat":99},{"version":"b4f7f4e2e4d0e668ab7cfd94ae5b72b6c690eafeac0e7a6d2218b16afdf7432f","impliedFormat":99},{"version":"5e8d925c0b8f6f91ac0af131a83f72683f88d80a61f5eea37d8883afbe8f74fa","impliedFormat":99},{"version":"056e9235afb474b7b2ffb6df16ff331f5238027b185367ca745103eb228fe57b","impliedFormat":99},{"version":"2eb77a708b1d812a8b0a57a6a12cbdb659bf43acf839b21b8996dbbd511d6e53","impliedFormat":99},{"version":"9a2d7fe034d084982a18ed744a3e0748f4768fdc5b9f2cdbf5f190e5226b54a4","impliedFormat":99},{"version":"1cbf7a0290d370c2843e79344bd494a10d267b3e0323bb77cf1b34a36ecf4200","impliedFormat":99},{"version":"2dd580520217749fd86cd77b8e48075a6c2ff32339e2334aef676bd3800f345f","impliedFormat":99},{"version":"939fdf70427033c0a05d112c2b03e8e31f037b8f2ac4617df680107162ffb423","impliedFormat":99},{"version":"9bc7a3d724ff20d2429d94e087c276b9256946b2cd66c9f9bce79ab54ec9c115","impliedFormat":99},{"version":"aa813b5adf5ecf364ddcab7bc6652db73d5c4e43ee5f6ccfdc7737f6d3184667","impliedFormat":99},{"version":"e5fcc46e6fc608a77c7efea569e56e3cb02491a9fc0d74f49e784d0a4a6aee14","impliedFormat":99},{"version":"fd413d87e8bf7a8e523c70d194b2c3279016d1ec733a9db43640cc1e0cabde6f","impliedFormat":99},{"version":"126cab464ce86f9c155c0b79f9c38fa906c422ac02c856ff9874051ca35ceb14","impliedFormat":99},{"version":"4d84f055621f07107b6e882b0cb79848106d08899bde344eb6ad0c9bc3539eae","impliedFormat":99},{"version":"25ada8b073df8f9b669aa007ee66298095904b83236652ee940d827c2ed5fe9b","impliedFormat":99},{"version":"24bb5860d0b4310843a2ce164c113315db19861f3fae4f2a56727ca9b98dc4b4","impliedFormat":99},{"version":"b62d96002ec0c8710d0e99aa3175434e1df0f22f5a09291b19e5ec05e8a877e6","impliedFormat":99},{"version":"221c86478853bcca59d83ce0eb2832e575779f2244a9a0176971de55c45b9690","impliedFormat":99},{"version":"7b8940dddb145146d5e62f9d817d5cb9f54345cd17bb91a363c293dd5216a377","impliedFormat":99},{"version":"7d331ed732ddb23a5e04eb12716cff50491ba01b712f4810df496c174547403f","impliedFormat":99},{"version":"a444b1d18b18c90477babf60511e8348ab9d591698205ed1bf12f3a0bf5862e0","impliedFormat":99},{"version":"08ca4dca79ba1cc23d4610ddec493102d3fdef6bb57f025d99b1cba9759f71b3","impliedFormat":99},{"version":"39c2d0f3d8d82809c02668743fb19a50e66f05d4336d48765946e4a051d0579f","impliedFormat":99},{"version":"495e122ec7cd8b18150ec1191e48edda4e23b2587022e59805571ddf8a3b516a","impliedFormat":99},{"version":"9277cadea8fcd4c10616d7667f521274c5fc6cef385861f6962ef880db3e612d","impliedFormat":99},{"version":"20b20c535eb79b2a4a62229abf83f0fcdd3dc1f041fc3c588dbe01e3a7666ef9","impliedFormat":99},{"version":"e98970286b6514c67e3b0f916f23f8bb81ad6fbe3b5ef1f2bb013272e9ccb00a","impliedFormat":99},{"version":"09c1c46f10e01ae7399f2fb391178be7ddd42d70dc6a3abc41d80ccf73badad9","impliedFormat":99},{"version":"31c9882e1d08811f5821ea24554c0bd8a0d97fb7efc661ef76393a28d9a8eb26","impliedFormat":99},{"version":"7c9aecf4946da6395949f23bacaa6d7e9ce287f5aa65e50e69332ee5f4d1960f","impliedFormat":99},{"version":"6701adb65ce407ccabefbaf20862daf55d52dbdb2a663c899d163cc6cbb59192","impliedFormat":99},{"version":"f73df64d28c41e3bc777eca2fb49cb5cda69b52c3786b32d4dc473f855fca42b","impliedFormat":99},{"version":"b342ffdc48ee317927f88cb38871b984b7edf94634428fcad875c7a9fe5515ae","impliedFormat":99},{"version":"247fa787c809e9036079d3f4bf429f5c6e4d76a31647d5547e668fa25c46477e","impliedFormat":99},{"version":"f015d64096dbfde32ec9117706e6e1376e9ed0ea8534d17d1c4035262fb82ebe","impliedFormat":99},{"version":"0af38d2d00fc29764aead613ae52e263e235289ec9e2f365e909226e8b2df2a5","impliedFormat":99},{"version":"c610c569ccfdbcb03d9e531ac1be3ed944586e099bf4f756885fce2d5e1a680c","impliedFormat":99},{"version":"2062be175b1e4f6a0b6b21b4ad08c1e241833349fd82aae558000acb2a9c905b","impliedFormat":99},{"version":"32c98d5e98a05f108f4e405c853db481f83c5a1a9cd6c53870501d8248f9afad","impliedFormat":99},{"version":"c717d81d125641e3d95b30cb00d3c0179fdcb30c9e716c360aeb23c699e51321","impliedFormat":99},{"version":"9a96c65bc8d115c4cd1f6d61305013640593f0c0f869a2e6cebb7bbcdcd7313c","impliedFormat":99},{"version":"d5e101bf2eaafcf94b79c0a80a8b86e26ea0b24234f8f5b2c88b58cac0842a74","impliedFormat":99},{"version":"27caf95cace62037352d836d1c547a73363248289ba8b05205cb9eef146768ba","impliedFormat":99},{"version":"8ac1275f4eef836ace2b3779aa240cece0a7094cee65e3a56fc730a270695b0b","impliedFormat":99},{"version":"4dae97da440251bfb634edda1739b3cf39e66b56076e05d7b06bd3181a6fc500","impliedFormat":99},{"version":"66a183f89f492290d10baa4bc6840fac3a0212cd3e32f2230c1506eb6b1f84db","impliedFormat":99},{"version":"6576f83f333348274a02f3a9a048dfd9c0fbcc3515ec4e654def0ec5491a6261","impliedFormat":99},{"version":"f657e9bb81b35be0d298f305f4a6924c4b652692f9d48512038015e7eb79c7fa","impliedFormat":99},{"version":"5ea4c5fd9091e33b07825015ed1cce784854121cf42d337f0762f1d707ffacfa","impliedFormat":99},{"version":"0a6af3e7a2a63fec578ef9940ace9987eaa91450112efa665dd94cf26555463f","impliedFormat":99},{"version":"172423ba720956a2999c4e44a640d6b141c1c8646d96e8a88a333181eddb1eea","impliedFormat":99},{"version":"8d1ee20c4ca7a97ffb6c9b19049a1a9ebb34bfff32379261bc6295e82cb77abb","impliedFormat":99},{"version":"258436bc14be16b94eefec3da57b4eca7a3c1df633c79d4ccc35f18eaa9d8107","impliedFormat":99},{"version":"0ad0d843d93b5bf3fdaf79de4e159e28d6f9367a945970413205f345e9797cbc","impliedFormat":99},{"version":"f9a161a77ec523402d8d7dbaf9a04e9fc3d32d0b304dca4d7a86412bfdd1b1c9","impliedFormat":99},{"version":"ecaa337dd6eaa40a78934bc53a46455c969a9e2ec75e07da806552e5e1f5f575","impliedFormat":99},{"version":"747703dab2b5bfcb0f4372616373cbbe85a8a9e246bf4f2002252c54f79750a6","impliedFormat":99},{"version":"c09f4c7ec02ad3b5be269a3e220d69d3f16d43fe3843e2e75263344d3ce7981c","impliedFormat":99},{"version":"d351678cfdd7d86b5dbc0c75eaf66ada923f7ff1c76102508ac22f703cb9b927","impliedFormat":99},{"version":"b3d820765aa7672d9276e319e9a2b4d7a928b5dbfe34169e287bc2c0a03be70b","impliedFormat":99},{"version":"96ce9dcfef17a1945dbb4ec0ff2256f3847e813671bcba46381fa6673cf8b202","impliedFormat":99},{"version":"0d852b4958e9b9dee49676e33381e33280a0345bec8fde3f902b479bd0f69e37","impliedFormat":99},{"version":"1ee834bd1a5b21ee9f0f8e683ed8f46410f2548f5b81ae090c14fa41ebe3173e","impliedFormat":99},{"version":"fd875069349f1541cdbf2859ca8b0acdb81acaffeeb579f74dc08b332e8a2fc4","impliedFormat":99},{"version":"fb6994ae9a491ff440c5a78667f4d5783fb6c5827050db94f9ca7fb14f8ff260","impliedFormat":99},{"version":"3318f774e0fa8cd7decde2830e561c401e53057ea505c031f687966a16f4b32c","impliedFormat":99},{"version":"c130a5e599c565b49b02dfdaef22c5dc68bf648a9678339b44e8913d3d27ce71","impliedFormat":99},{"version":"06cb5fd4ff2e5cf532dfc6bbeae7b47ad7c2879909e6727ffdacc558115ebf0f","impliedFormat":99},{"version":"5cf2e81f262bee804fa9d50112c5288ed4224243b1837c653c9eec5a621a9b13","impliedFormat":99},{"version":"aeea67ef93786c8625e6c2840c5be41e6f6679f9890bc75628ac0a3cf8ea0c04","impliedFormat":99},{"version":"a37b86cc490287c9723338ed95965a938886313f0f912ba12f789462d8bad89c","impliedFormat":99},{"version":"41a073e65cbf693b4ca1f61f6847e16227d023cacfa75a84fb989efc3545cb19","impliedFormat":99},{"version":"e726badbad2c619272fe4fe528dd07cd5ef87bda456dc3656e4fd1bcc11976d0","impliedFormat":99},{"version":"60b0f3b27eed4652b4cf70ff359eecf92d1dadce962239812474436b4d608da6","impliedFormat":99},{"version":"4d7002dcc54793296ab4c4b1e28c00e99cdda63ef31b83ef616c58f8773c25bd","impliedFormat":99},{"version":"040dbae8a47533338afa394e6974e753b4bfc1895c322a3a715eb1be21eab5bf","impliedFormat":99},{"version":"a9d4d662f3494ab31e98c8193f20b0725a9488225df92bb4df2d9f96b5b7a166","impliedFormat":99},{"version":"6170c6827bcca40ead01d9a8e92e73049b82a0e595f1c11ef39bb98282781f7d","impliedFormat":99},{"version":"5dd074521b20eeb26c76fb3e1d0f85fb4bf26cd247c7dffbed08bd888a6d29d3","impliedFormat":99},{"version":"f3a8d4b406af14afba34488fec9b89859900a8df10510a23d9f1c2e8a116d3fd","impliedFormat":99},{"version":"b03aa91aef645f9856216a2223a47001a84954caf37b7ffb1d63d1327b4231fe","impliedFormat":99},{"version":"01b6435dae2508e231ded5ca79334075da7d6ca12d909765cb335211a90ba86e","impliedFormat":99},{"version":"75fc3992422a1d3b15788ee84656da98a10ce15ce5ba257a0df623a024a0d845","impliedFormat":99},{"version":"b6c5cede83853964b2f753d7e202613e1d461857cc3780a57a2a3d346c5afc0a","impliedFormat":99},{"version":"ffc4846043b7f71f310692e4bd38f349373981b832f907963e4bbdd4288f130e","impliedFormat":99},{"version":"54a730e06094b37f96436ccc8e736bb65b74d256439bf1663344e3fab16d2246","impliedFormat":99},{"version":"1389cb1ca8557f7380f983f00c337969542d6c932b1ba294b48f97f6fd1cb69e","impliedFormat":99},{"version":"85cfc4f1cd043b1df65ba7714d292ba7c6c79c9e288db0d4a9ea6a7b567a675b","impliedFormat":99},{"version":"74cc10ca21f4fc15188d7e7aafd66de5c34c82d011f0c9e02b05b9739e0fa31c","impliedFormat":99},{"version":"abe83f442f76121715241d0fc207d2c325510c6a4dfa6b07662f550c95c6a2a2","impliedFormat":99},{"version":"4b6de64797fd57745c2856f26b4c7de6be543f9335dfde7870a186c3541ff183","impliedFormat":99},{"version":"765122fafa15af14742c91619b7e30b36e5c38f01e6ad079d2c5ecd38a4fc45d","impliedFormat":99},{"version":"1194d3241ea56738d7d8e2b4908572a350cfe7a85b82ef89828ea32e20ab1803","impliedFormat":99},{"version":"6449789627c9555d2914c88498ab494cdc4f18e28a7426a1e74dcef3401f181d","impliedFormat":99},{"version":"17326f1b693cd3a0e89fdc1248097f0135adacbc072b0ed62cab9eecb1c21743","impliedFormat":99},{"version":"1b322be99b786ec951d3d14283aeddd32c3ab25033c4cb984b5224630317b232","impliedFormat":99},{"version":"c7523c0ac422da80b521031667dc06ca66d817ce5ac47f69db6fa98531febb26","impliedFormat":99},{"version":"19203771ab06e45e1524b7f608b332cad7143ba3ff473e302827a835ecd99dbc","impliedFormat":99},{"version":"3b7b9365174c24792ba2c762637b0bd5cbb8d88a72153e3f7f82d34e115d5647","impliedFormat":99},{"version":"7e3a2715195f927935488d7565bc30e7f540797776e1de208c64720d4ef87f77","impliedFormat":99},{"version":"5804ffbc65b78751fd510218b90827a7ca677ca34a45b4709a00783b658cbaba","impliedFormat":99},{"version":"0945a03ba41861ce8f75468e2bd1bfd424185418921fc2f55cac5eeeb5049c3d","impliedFormat":99},{"version":"246dc85745d220f0a1041d67bef89de1e02fabf49e6ce896bc1a345eab1fc507","impliedFormat":99},{"version":"616b74da95e0f9bca845458de4a8b25f12142b4a7b02e89882da05b4cc115802","impliedFormat":99},{"version":"b894722e4b4205a60154ee3d6fa8ecc3ffdfb92a7bd38936f666d3f00be6649c","impliedFormat":99},{"version":"1f7f05258c0992bd696cf00984e640011ae5477d7aac3b80fcf61bf27f42fe88","impliedFormat":99},{"version":"9129342b97e39ef2c9df4848dfe011329cef9b27e719c7913fd3859be5fc0cca","impliedFormat":99},{"version":"351edaf90b54a559e1759f7ceb54b7881079cba5f4d6dcf15bdb26f1877dd2c6","impliedFormat":99},{"version":"3f79205d951373afec1ca713cbda4be9816d97daa795a9e0a37fa3ae5429afbe","impliedFormat":99},{"version":"bcc4c8b5a39356915b8d366e3499a28adc89e2e0bffc02a108eaec1c4797a58e","impliedFormat":99},{"version":"5b2287eec9804a7fc7c6021ae0a7a92b0160750eb21604b77203589e2ad905f8","impliedFormat":99},{"version":"03870a19c7cbbad803b0ee2d69b777e12be7734e087ccfb0c862529a41cb493b","impliedFormat":99},{"version":"46a52d6ee42784826515dd6ab9f5afaab3a05dfb49ddd8298a2026b6c756b944","impliedFormat":99},{"version":"995f334b04df585cb2a77b74533441293ff1e1d4549c86dd5495494c1fc3969f","impliedFormat":99},{"version":"e83b7824f3d983e9b8c2785541579cd8d8c153e96959e71ab4f69bd83c71f953","impliedFormat":99},{"version":"7b0030262f3d2cc74ae1dd79f4990a7131c34935b2c177e6cfa17a88a6ea56ee","impliedFormat":99},{"version":"a923cde26c2e5431e455844ac5f31126d45976c85f347c7dfd2b9eba3e8ef63c","impliedFormat":99},{"version":"3b2738cfacb777ea1f53acdb26b4f4306fa3dbac7fc5d0f1c4750350d3f5741d","impliedFormat":99},{"version":"b95d11a17e57f0cd0ab04aa8148c8f0ca3a68f56c9a44ac9179cea8a6cccb546","impliedFormat":99},{"version":"7688f3196338007600eba7158240aaa15ad524ca42c204fdb3888446fd690086","impliedFormat":99},{"version":"514f33cfc8bf4a00d0603f6df438959657ce42f94e93e29df29fa9b58e7d54f9","impliedFormat":99},{"version":"7568cf2d6e505847c539e63406ddbde2ccc0f96f2e6c5f115a4b9774d0b55aad","impliedFormat":99},{"version":"d05bd9004c654c2583de473d77f047f03719e3e7bdbe62861371755208e36d59","impliedFormat":99},{"version":"74371225d6032ec7f73b46e736d9ff6ea3626be6fc7959e8b71fedff0bb75cf4","impliedFormat":99},{"version":"06dd247275efd44b3f91270763246700353f1add0945380bdbca8c90a517f9f1","impliedFormat":99},{"version":"0833e55be9920ff787cedb7ea623e97ac9bab28961e0e11aa4a56d36d6074dd2","impliedFormat":99},{"version":"92fbb2b6566fdefc6ba3f151299b2618bd1780cf26c2d0078dcd7f1bdc1c551e","impliedFormat":99},{"version":"3b5317db0574b276c1ecf6ebad9faa974f4e416786b682ed1f854cc85837c3df","impliedFormat":99},{"version":"5f27b1f1b03636451e90fc414bd8426a1db25ad438782354bea60f47d7efb9d6","impliedFormat":99},{"version":"53eb12cfe4c56afff32a3b8adec4fefefa12685c84202c8207351004d30c3b3a","impliedFormat":99},{"version":"05bc3698de467024d02654162f1eeb4edcb0ed9d855a96133572969a6f3675c4","impliedFormat":99},{"version":"a5ba4a306d8bc21ac2fef4e40e9076708dded0176aa21484f1f6da23a4d400e2","impliedFormat":99},{"version":"1c825d1f1bd9e70c306f6c16a0a6b76ccfe4be9350857831eba93e59b95fbb5b","impliedFormat":99},{"version":"b27224caf8db7ed9edf9b12368cedb963bbba3a9b5143c68dff53f5fb2351c96","impliedFormat":99},{"version":"eb3bfb8488f260946c5bbf5d9e730a6e23e0c4a568fbbbe782f3c365e0595dde","impliedFormat":99},{"version":"15de6ee96c8e0f6a78fed11e60c3a0f9b4535c1e6a802c55d65028d500e91e75","impliedFormat":99},{"version":"052f62cd94d56a5ca9d8ce7e68a2201fe8f399a12d7803be2619fd03dd36f1d9","impliedFormat":99},{"version":"06e98ec1e0428de740d985f3480b2e699826d5cd2fe2457f1265b32ff4797ae4","impliedFormat":99},{"version":"01b8daaa0be6124a730b7170c1bb1375f7ed6acf1b4b49c1389199b5ffb600e7","impliedFormat":99},{"version":"7b0d3cca9104d4d9f484ca0a64bf731ff1aea842c8a4bf93618814b1a8281992","impliedFormat":99},{"version":"e40aa12df628390fb3819a883c52c51ef94fd3998e74965fce6a38917a0530f4","impliedFormat":99},{"version":"0df397db19a2db183105dfe900d75798622677a5db73038608bd325f86a556ed","impliedFormat":99},{"version":"ab505b9c7ee7649920023b14384c71e3c542bc7535f51028dff27d70d2b1d6fd","impliedFormat":99},{"version":"29a9fb009bcc76c847dcf73d820d276d6353e5c6c4c016c847d51e42796f68f5","impliedFormat":99},{"version":"743751f2d8819fd7ac9d3ef6378614b6675d3101e42ddc18767901693621cb2f","impliedFormat":99},{"version":"e4a995fd487783122df0848df4c871dbb536e1636e8e7b6f6186d2993e9761e8","impliedFormat":99},{"version":"45da721f9a605485a439778c248dfbc6351341d87de448ec266b74185e090631","impliedFormat":99},{"version":"ec47a4e180f0cf61787ada2d4691a1cf4f7fd65482f6fa9e01444adff3cbd6eb","impliedFormat":99},{"version":"3209d42dcb86b35a13c127fc39981a644b61a1fb0e59524038d0f3bd7fe25768","impliedFormat":99},{"version":"c8c16f7fdc34f8bda36cd9827b11c065e94ae25473465b2b35aa71df336ecf63","impliedFormat":99},{"version":"db07a4e9f69cc9b58930c2d3a4ad1fd9f882794b92208d55ab057443081f649a","impliedFormat":99},{"version":"735a572ced293fa984b3675cce56091902a0529cef028fe016d9670e3a94dc8b","impliedFormat":99},{"version":"dcf0056dec8dc80fe76eac1e8c6fa778a2e4c094fe2d4b120e6f5bcabd820be8","impliedFormat":99},{"version":"6268a89f0ce2f857f6f7ada0045bf8dc990b449f648b51522c0a7d84d016fe85","impliedFormat":99},{"version":"efd3f26f59c3291a0998435ad54c67191b39b4cd0d451ac807afd8da86bc1996","impliedFormat":99},{"version":"19b70aecc85035f5faef7f3da8dcbf199af4ceccbce15a670950377b388c1d9c","impliedFormat":99},{"version":"d621382b4ad80cc27b2f670e44e0bb11a7e85cb0f6a0b043aa0c9b6b21b16a15","impliedFormat":99},{"version":"5c1f5f0c20f5171a182440cd0347dbb94e5c84f5976184f2f36dec92afbd9b9c","impliedFormat":99},{"version":"1a7e2345e3b20202800bc92adbf628d22a74902b8a5c87a6ce3c361d3ba314a9","impliedFormat":99},{"version":"eea9dc67b1bd75f72aad8483567241f5fdbe46436f018df7f0719e7ee5aa85da","impliedFormat":99},{"version":"5bf7ec4d84bfa8c29f32b7cde878e8ef4e11b1bbf0f4edbb9e851efbfdccbd2b","impliedFormat":99},{"version":"3a49927f72440d36c50e1b62f5cbc2f296253d151ea4e5484ecebc8bc461ad4f","impliedFormat":99},{"version":"ce293a2b914083388ff1de83875cc6e82792c5dc1e99c3be4b787f6b150516bf","impliedFormat":99},{"version":"f8d6e2784bb518d523898f614b8c0ae55341968c982d4617f08867b5d11cf354","impliedFormat":99},{"version":"1413f593b860e74f717f40bbf5c934fd77ee6cbbc630216954bc1a364d5d58a6","impliedFormat":99},{"version":"f7e358590496240e80dc08cd1b71ca492e4d27664bb403d3efbb9acef5075b40","impliedFormat":99},{"version":"656e30f229e3a05096b21a8d0b4a37cadda6201d74631fdfd6a6f52f0c158831","impliedFormat":99},{"version":"03206d1ab6b7f08b118786a903cf849768c8c927a21022df88fe63910ddc3433","impliedFormat":99},{"version":"702cb19c1b38ca1b2d158d765869b667b2c1e5ca0e62862b7792285055cb86d2","impliedFormat":99},{"version":"396f903b4d3bcc1d5a72580bb0a8d9f90c7dac5e481b81c2b58df80c968b64e5","impliedFormat":99},{"version":"0e8707f15586d91f92a120b4751048061e04fdee756246667158d4df0105dbe8","impliedFormat":99},{"version":"c37e7b3d6c0b5da08a46d028e980becdd8d48d7b32b7644209695d75d43f653c","impliedFormat":99},{"version":"fecc5365f9a1dd29cc8c582bc0427a7bf06a52c2a42cdb4b25012976628faa6e","impliedFormat":99},{"version":"0c073335c77c5ad0240a0303cae56c8be8da93e206591c5a5a8bd6a613d78d18","impliedFormat":99},{"version":"827c1178e5058f0aaa9047725b845d598f0f52871792441412059173f895597b","impliedFormat":99},{"version":"9e8ed20b5058a6f5f773f420c0efce5c8eb802c0af94cdb96b782cf2acf1b00b","impliedFormat":99},{"version":"8b0336c60458945b1fee185149fa4b5a512917aa171d4232b1f0805c3c12e31b","impliedFormat":99},{"version":"b15377bca02bd4d77f5d089fa0c7a13dc251b104de3b43f62dde6955cc8ef7e8","impliedFormat":99},{"version":"020409dbb29a4396e3c1c0732a0f8afa939e47c935182f6fd0603e21d5a6a8f2","impliedFormat":99},{"version":"bb259ffb75be8a11b1be05d135a561391f7123110d75074eeb5207be382ceb70","impliedFormat":99},{"version":"1f52c9be8dfb11cc31d9e2aa4f950ef56aa8eaef1b78949431882f70e10487de","impliedFormat":99},{"version":"1e685ffce849148fe9e9649189957078d9495608e9df42cfeab20367d2d70c75","impliedFormat":99},{"version":"97e30735672fbe25393231a53ab5e3b63d34e74d0697c59ffc034f9119c23d31","impliedFormat":99},{"version":"84ffde0a761e4b6cbf3cf90c97c4c01608962e8b55082f3705d29465a194f449","impliedFormat":99},{"version":"d874bdb89c1172b0eb109873d39175a5f210f5d853439e7eb250102622edb0d1","impliedFormat":99},{"version":"2b7e61a49cb27bbfc53fd5b888705290beb2d1fe78a8b433bac1ce7544113904","impliedFormat":99},{"version":"8b28a7039c2ccb5108bb3a3b771ca430db73c4ec9e47031303b8e87732a859a0","impliedFormat":99},{"version":"156eb4c6ef17eb61507364b320e2812cfc5afd862cb1baa251b2ab412384a2a9","impliedFormat":99},{"version":"9efc47a0e98346bfd4b386050634b4e150e6c41dd6d9b2bc1288e80a0f345390","impliedFormat":99},{"version":"2bd0a3ea02475382ae8e87d78e3be763dba251ddf9629664ce73c706b400dc94","impliedFormat":99},{"version":"20008d2327e19c4fd051a2c0ee88ea696d704bc6d7ad39988fc509d81c27a485","impliedFormat":99},{"version":"6cee28d40bc224e61f12e867140ab6d677a03a1defc9ade08b1bd60ab1c06524","impliedFormat":99},{"version":"3a0bb28315b2084f25a012275ef45e180ea80d9ca4bbc37665b9c67e912e998c","impliedFormat":99},{"version":"17662ae9763596c2ddaa833f9e326b3de9289098a71457ee18d2db9407cc681b","impliedFormat":99},{"version":"9442dcf95088615dd8ea58077ebed1f7d5dd662caca210b245a6b19f38984038","impliedFormat":99},{"version":"baf0ad4aa9df446c5b08370689dc08e23e112fdd1a022293676254fbb7897a47","impliedFormat":99},{"version":"e3a929f769e33c3001244a06d6a3e025083be64599c1e961aee31145d623e824","impliedFormat":99},{"version":"083493311f28114ab250a8f379798214e91f264dce121fa2140ae58376fc48c6","impliedFormat":99},{"version":"fd03b3ac929f2bcec6710176bbcdb34969d7f9810b01f65d19cbdac143a2c7d9","impliedFormat":99},{"version":"f3e2f84bdacbe962c856add41824ccfd66fba7b320753f6e9c6871cd6fd5133c","impliedFormat":99},{"version":"d70ae743099d2615ffab06760a3571a2beb01fcb27366cce4025544603a6081a","impliedFormat":99},{"version":"530fcba9474606ca2eca0b85f91b26d5e24c31431c27d20403928d51f9c1931f","impliedFormat":99},{"version":"c483babd94cb2effd09a918f5cacae5fbc8cdcb8b65b1a28cf07c2a9381f2a0d","impliedFormat":99},{"version":"c808470b50113d547da502f2380c6674fd41908d641663e5944a6070113469cd","impliedFormat":99},{"version":"f0568ac6f1c90cb01c4a2b3d14c0c6e734cfbfa34eebc57d789db55e7d0d34f1","impliedFormat":99},{"version":"0ae4ff7dd81505058a06f617152c94802f16fc7a8d2f768c8794f53f8be57178","impliedFormat":99},{"version":"3f61a28c42e990b337e084e92d7fa7df04f8a6b6699da3754dc59611d189b40e","impliedFormat":99},{"version":"73fbbf32113d791d019c474cf474344bb36d4c375f9622728163ad5640492a39","impliedFormat":99},{"version":"91b6fbc14c8a81bc1751cc033f55e0cb6f3b346653d51e30efb7995ecf969ed2","impliedFormat":99},{"version":"77e2fd9131fc81ffaffdc85a8ab553f869f2a67b236ceb95b85b9a1bd72b8823","impliedFormat":99},{"version":"330213ff23c7adbbb6f1b5ead11fb8dfb731c5c24f8c4a18586acaaa47e74077","impliedFormat":99},{"version":"a9f07992ccd51ff2a089628480d51364e19be7e5b22e04edd7e18a519c50e2fc","impliedFormat":99},{"version":"26f62f6b63fff6ad7abd3fc5d89d36f8c74f6ddb32d64795556d0ad3ac6b2d29","impliedFormat":99},{"version":"30c55932c3859c15cfb16c4cf3cda9c303588f3216f8b1ca205e2c41bf801402","impliedFormat":99},{"version":"b727fb19b28fdd8abf41b989f9ec0a6aae52cf07f3918386ad068b33d20c3468","impliedFormat":99},{"version":"d7fad08d42a437ea163bec1c3d08e5e4714a27636d89809602f04328a54a3fa4","impliedFormat":99},{"version":"22469dbd699381a169d6e02d5c080ba9d94b9d6567b7a5c41cb17f505e6a4ad7","impliedFormat":99},{"version":"44412e7238512522c472296f100c52c0accc20d3ee75db7aa503ad4d92b80754","impliedFormat":99},{"version":"27c4c4f9114b51cd89d2ba83e9fa60bacc6c29a1279f2f3b91d19c2f7b2c68ac","impliedFormat":99},{"version":"6acb809bb284648297faaefcb03e0e4500de5f78194a08b75512e13e5887829b","impliedFormat":99},{"version":"c31062874243eeb47ba70f53686f860d4c238bed5587af12ea4f73389ce2333c","impliedFormat":99},{"version":"b4f0992a1069bd5af311d02a49dae7aceb5e0400856449bd766b994267e2adba","impliedFormat":99},{"version":"adf2b0d2362e1b4c99336c56293ac3da8aa0d3ebbda67f963d4a0f3d3ef2a021","impliedFormat":99},{"version":"5fc3d9350eb34ad3cbcb1b69249161a33ffe19d7c0e72e6087c947046de6f756","impliedFormat":99},{"version":"b0418e08aab8aa9e4e406428964d2adf8187dd29f6cdaea32ede28fc36e86f56","impliedFormat":99},{"version":"be4147ddded6518b57942a23f89b50b772d841a97e22c93b70eddf901c7581d2","impliedFormat":99},{"version":"6b952ce628d71b1e1644cf8aea26a4de997596197158dc7b6e71ec356a8cf992","impliedFormat":99},{"version":"2f4dad0e02e51c0d630d46dd18b3a99a1d8c9f184af3e9d109027d8d11735f9f","impliedFormat":99},{"version":"41c5600e8662d67b2a149c2eebb422c80cc2337945f5b79dde92d41427499496","impliedFormat":99},{"version":"9c1e78acaead99ab9c612e54f5e16c0675cb6863627ec2dffa0c3d5651d53659","impliedFormat":99},{"version":"6df75e65602bbd54c977312ed62988e0c64423b046ed74ca126b529970233a2e","impliedFormat":99},{"version":"3ffc0815b3b1da65f6fc42a2a10aece2bda56d024cbcf7477b6380d4249ff8a1","impliedFormat":99},{"version":"7cb50c74ced03d93407f80f61840b52540cdc0ff7189ca603e6995306c2b25c2","impliedFormat":99},{"version":"a44dd85a5c1ba838eea01fd555504229ee74d97b1d237741598e7d97c0e857ca","impliedFormat":99},{"version":"aa7f2b3a9f4bb8a225b3a5e5c611b1a034ad76c3d870a1062e241485e3968e23","impliedFormat":99},{"version":"eb41d07bb7e2d527ac33c71146a3a4802a24d39defb6b8e4d707e5510074d076","impliedFormat":99},{"version":"405bf967f547561f6810f2903df5c5b3c7528d55917fcea0cf251951bedd879b","impliedFormat":99},{"version":"0060d5fcac50ed959be8765d1f5343eda5641109a62eba69696577e004b891d0","impliedFormat":99},{"version":"def4730fa85f358f1257bf2116242bec72080b1a0c70046d0c05ff7f90164707","impliedFormat":99},{"version":"5336f4657e6ffcc8bae26bd762b09b80ae6e3b67dce0a4b4aa99f5baab00c65a","impliedFormat":99},{"version":"8e728eefe8c7160465492dafb86f25085ede8c6b05e360dd2c7129955a155da8","impliedFormat":99},{"version":"c665cdd809976f388c82e21c47a040e5e19ba6cb953d0e0c1c38e1ce61f40922","impliedFormat":99},{"version":"c3bdb6cc2b1abe32815c4894c4d011d4ea80c79d0934d264b467cc6ec0051bcc","impliedFormat":99},{"version":"d40c02d227da200dd6be4e7d56ec2c560c08b9e24a4688a071f392b37953143a","impliedFormat":99},{"version":"01b9b0a56a739482aadb7da55886fa724bc2b557e9814ef4841d2262efb9846b","impliedFormat":99},{"version":"891117d566ab7e1a7798d83c58a20957c1703e92d5a351802081c643cf58faf0","impliedFormat":99},{"version":"6f925dbb5e83ba81d632287af1706945f435bfaec89258540eaae87817804c84","impliedFormat":99},{"version":"0fadf459265643344979f57c02e7ae5fdb5c70244fc9ccece5a1a977fe0b1fb8","impliedFormat":99},{"version":"0e77a1ed700a09eae143529750cc2eef65b8e28d76cf8a6eaf78b7f1afa24c63","impliedFormat":99},{"version":"5408800bf96b2cdd0d8d77e3d52f6848514efbf1590d96d9f8aa86c8ee95bbdf","impliedFormat":99},{"version":"b6e0ad0ba28715ae23a61b1192cdb24c06a909aa58b2048e64e56574aa4da7a8","impliedFormat":99},{"version":"c20b3e5d792dae26f5bbf8d1b73ddd16d9ddc336e32a301b9dd99c68a779f61e","impliedFormat":99},{"version":"d71713801d5419399f8edaaf0471dea5e578dd8b71eefde7abf387fb372feb1b","impliedFormat":99},{"version":"89b56bb82308d69d9ea109de95fff39ef64bcabd250688da972fcea05f50dad3","impliedFormat":99},{"version":"214f90578d41d0f5bf61b4d3de16b4671dc75fe893b803a483a4e7b96e80a1e7","impliedFormat":99},{"version":"32b6a2a6fc20f85513ffb0f35e455dfbaf058f65f063a10dda07ffe9592cd98f","impliedFormat":99},{"version":"8b2bdf89d903b856b52e4d416930701068a3522e9e8c2705602c6e7e2394e86d","impliedFormat":99},{"version":"c988c702e73a0ae03ff6d7868ebc2dd0497e921c3b7ea4fbde42aa781831b8a5","impliedFormat":99},{"version":"8409e2185704c03d12e1522dc4c7b137b6b7524e2fc1f9baee7581ee28fc3d86","impliedFormat":99},{"version":"95195cfacab74280a41490ca2c731fe499a37d7ffcaeac7dd2d9056cdc694623","impliedFormat":99},{"version":"9c8746b57866938dccd94775ccc3abe27e41d182b6f6d32ce82a1044084d3778","impliedFormat":99},{"version":"be71dce0024b565b17433b79dfb73c200bd087568e24e796d712cbd42eebf8cd","impliedFormat":99},{"version":"4d9081308548bde06c710ad7bc3af5e6d7e24538378a4c10eff2e769dec31bd5","impliedFormat":99},{"version":"09e693240afe609150a21882e64d8f34b664eea485d16ae78ac86cb3a47de3f9","impliedFormat":99},{"version":"89502a94ed72858e0018b65766f8deea38577994b7df9d406afc47224fc259c9","impliedFormat":99},{"version":"2c19973a0dad8e650d42349838ecc7bec9e181c28f7aacfc045eb3c0b8c7db19","impliedFormat":99},{"version":"851bba631a33a4413ce53ca3586b8a2d5799d0450207e8f7f9b594340e8d0af6","impliedFormat":99},{"version":"9772a1a4b6a4a8c16e2564c0d83848bc92c5410378710f9da8fb2d912ac32b57","impliedFormat":99},{"version":"ae66b8a49700f9b0e1e857eb7989a033392b92b5a19690c9ed7f8f403a1e219c","impliedFormat":99},{"version":"a095cd74b349b5c587c52343a00871d3a522d5d00614275a608e5c3ff690468f","impliedFormat":99},{"version":"ce2b17e7bb13676b9cfe8b9d71db509625851486b845475bf336e2c6f58a2cf7","impliedFormat":99},{"version":"68ff0025b0ff8a90165ae54d417191c8dddf93c794fd54fbfef6d4ea75f6ca82","impliedFormat":99},{"version":"6ff5a35137457c0c733501de9300f1801ae9abb33aea7bc9c6bf5e9d6d98cfc5","impliedFormat":99},{"version":"71128c986c2bd2554d203c724e897471277d96efae9d67721835e0174bb19a97","impliedFormat":99},{"version":"5118e5ed493b74299ca53eca1e5a422fb8f3207337285fe9206dd5d1f88785ad","impliedFormat":99},{"version":"aacbc0d9b6f47db9784a2193fcc7f4bfb1fc6cc711587a4bbac43e45432332ea","impliedFormat":99},{"version":"5be892a93003f44bc4420408ec0726322928020fe22f9a68264a176dc4eb8b96","impliedFormat":99},{"version":"9cf47cb5d151b9a09d0d2fed8b5858d726cbde497560ccb136557aa203364208","impliedFormat":99},{"version":"981feaf9d706617834eb318674966a8741ea35c93ce33e0ce155498e665d2593","impliedFormat":99},{"version":"8e661b24aed6caeef42e16eba111174c13ed178a660b41fd8f82401dfe129515","impliedFormat":99},{"version":"74606837f50a3a16d02993364c004db527b47cdb828edbd770595d5e4ee8dbde","impliedFormat":99},{"version":"d7e7588481cd78747b1d6a9439feede87c2e497df8448acc74d9803867cfdcc9","impliedFormat":99},{"version":"a46d60895edd2436d8927e02798c82975267d0b6fe3af28d7596177f23da3639","impliedFormat":99},{"version":"f93b561633fc4bf5005f34f0c2f96f48c3e548d1593136cfaa9331d7294ca417","impliedFormat":99},{"version":"d64b9ad5dc93f6dc86e1c13f5e483583597b35fa0ad3170c928e436253b1a252","impliedFormat":99},{"version":"23bbc076a14d01df086f77870c735b053cb1c9dc07c2c8b160f6a04db80c469e","impliedFormat":99},{"version":"7f1f69fdcac775d124fe626182219327b833a962de2c9751073d2643695ce2e0","impliedFormat":99},{"version":"ad774bd48cdebf2909e354cb24ed9ade7763306edda185b7692890a2aa96be4b","impliedFormat":99},{"version":"f53c345523d49bc3e6a11a5f6540ba145b441af3618efaf58d25b58db03d2922","impliedFormat":99},{"version":"164af37e5cde8d2d830b5a5f2aaa6be547b8004e4e98b33fd6977581f8be4d4a","impliedFormat":99},{"version":"2aa08243d9c596b3e993b558033dd391f39ba4d6525ccba992b11bb5be54c04e","impliedFormat":99},{"version":"208371c97acf811ef41ba4b217816aedb802a570129042463b198c7d72d1cca1","impliedFormat":99},{"version":"6311ecffa1680ff0f9587217df76d9556d4c8c623f12464b8beb44461d1d22af","impliedFormat":99},{"version":"16e2700613d061c8a3c21fd26bdff099948396954d5935ce913424d93c97815c","impliedFormat":99},{"version":"0890d6e6870d35b625590a98abc2bd3fa880fa46d0dc3de22dbf01628cfd34b7","impliedFormat":99},{"version":"193814fef68f60058efb9c02cffd20bcbf70eec1d32ea0fce4b5887aef746157","impliedFormat":99},{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"51954e948be6a5b728fcfaf561f12331b4f54f068934c77adfc8f70eea17d285","impliedFormat":1},{"version":"2fbe402f0ee5aa8ab55367f88030f79d46211c0a0f342becaa9f648bf8534e9d","impliedFormat":1},{"version":"b94258ef37e67474ac5522e9c519489a55dcb3d4a8f645e335fc68ea2215fe88","impliedFormat":1},{"version":"a9ff5614fec6e47cd306851cd39e2bb0bd1b939a9776cad032bc06753a24b105","signature":"2641cc270e66b5b412cf0f887ef90e12173ac7773390a8e0008f653358f66841"},{"version":"709504c4a347b021a9984ee3e65359992e9f0f172d22e63030207d0c604296d6","signature":"b0a30a6f3075e34a6a108ff4fb8c54e7714f964c0690db0b6e82bed93ef6568e"},{"version":"c91f4f63d6c2c84d2e0f7368a97dfb126ac74804e775e5aa5bcefd853917e69e","signature":"96d032d99c255b941936f513419610586f7e642f2abb57d1b8d2581f7d442eb8"},{"version":"a313760e9f66c6f819c3426e038acb9aa8f47a59be74062f51321caa88a688ea","signature":"439593d167651f2e1c0c439482dc3d5d5eb248ea221ecd8feb5c62cd0d60cd86"},{"version":"2c82ac3566fa4072c5cc6320a0a786afb9d27c061d41316411483f61353560eb","signature":"e0d9f1fd5544f50032be81792d9409f65c8ea46853ed0450b9934372d4255930"},{"version":"829b5cb87df9dfb327efb8a4e55644d809f3e03de209067122b99ffebf284f00","impliedFormat":1},{"version":"415510d38ea33f28cb571ef11ebd6ee777a377e0d1886b6771dcb15fdde7a02f","signature":"5a4e0d921d1c64c046a46838efd87367a659f8debed6c7f7801b8440576657de"},{"version":"6e9445b11a3d075d64853d8b32efd159b4a45f37b481bbbb7d3bd57f5a5d5f35","signature":"589cdbba6bdaf20ddef1fe78e3bdedfd4e7f6b6e08179a9d8197ded860ebaed0"},{"version":"80d23e36921e787529d3ccae753675b91180dc2326b4c1a3d8f270205b85af79","signature":"845a9728a8fd9284d40c63aaea7b11076866271659517e0ab1a1cbd041bf8588"},{"version":"1aadf3c39d08e4aeea1b9950040079b0fa8baa1d5f9644667cbbb6b9c8c0837a","signature":"730f18e9a86d7032845d6a326f8c5ec9469490304565e2a637f4dbdd8db08977"},{"version":"03a87f22d5567ad70a9761d76f0d16ca6ae32b6201d79c4946e751f2c4cb4e8a","signature":"daaa96af8feb9c538eac60042eb231ecb684bd361d5d7d5fccb0a614a41c365b"},{"version":"07e1a6c1c01468d20ce760a06f39c9e59c56103e63556c7240bbc9e8c2b8d24b","signature":"e6ec95dc819ab75e36c9e4492ba3e6bcf21507403a6afb5bbe8cdea76fd77fc7"},{"version":"e7205096e87497cb983cffe2ea271035dc0f7bae9db702859e5a2d0941d99597","signature":"1a85b0cd6837d60863844ad43f065863cd13b3cb956c369d493761bb603f4b63"},{"version":"18ca2e6d4cb671ed4530429e6b4886aa0792c79c4e5e74078dd290668b540599","signature":"c606b46f5784bed24dcbe2bec2d9ee535c29050ba160ea11fd41b6a173bbf25c"},{"version":"4ed96213860296593b569b425eec8dfac37cb5bdaffbce2206c000dc673007c7","signature":"0fbe920fa2bb3439dfa680647a4ea264b7a8ea9bfa75e4cdd9ff2507d69df783"},"4720053f6a578540743ee8f3c02278636ada05dfc7184b43433262c4aebbbff5",{"version":"21e365e7414b00e1dda3cb0e8c1ffe7eaf8f4cee8665857e7a4ab0051c694811","signature":"d36c6cc5adf1dd3c897e4bfe96cfc0506c9352c7413cd83da0d3032f820781b8"},{"version":"800de8bb8ea525980e16dd155bb6e6847e7fdeccaf816e5c2674e1a24c5bfc9a","impliedFormat":1},{"version":"88efe27bebddb62da9655a9f093e0c27719647e96747f16650489dc9671075d6","impliedFormat":1},{"version":"e348f128032c4807ad9359a1fff29fcbc5f551c81be807bfa86db5a45649b7ba","impliedFormat":1},{"version":"8ee6b07974528da39b7835556e12dd3198c0a13e4a9de321217cd2044f3de22e","impliedFormat":1},{"version":"deefd8c43b40f9797c3921d78d3f9243959621a17b817be7f5d95c149f23a9dd","impliedFormat":1},{"version":"5f12132800d430adbe59b49c2c0354d85a71ada7d756e34250a655baa8ad4ae5","impliedFormat":1},{"version":"ec27c0cee1436f58e785f621703d19d588ebbd489eca245e5198b4d6b715790d","impliedFormat":1},{"version":"b16e757e4c35434065120a2b3bf13a518fc9e621dc9c2ed668f91635a9dc4e75","impliedFormat":1},{"version":"efe2821496a760b9128309bb69ad43f1a99feb49d3fd004673c5e406de523da6","impliedFormat":1},{"version":"ea0e3c7d1347a549ac7ec32d3c61a30e473dbbbc901d458064db03f673128145","impliedFormat":1},{"version":"4374cefdde5c6e9bad52b0436e887b8325b8f407c12035194ad02c28f1553a3a","impliedFormat":1},{"version":"5f1ba0898eb0a54a644cb9c95c2240beaa961d87fd080cbb90807a6cc03daeb3","impliedFormat":1},{"version":"8e92ee8710ba85b158c5d91b0bbc9d0d033f5e062b6e70178063f01b20f63a14","impliedFormat":1},{"version":"ee933420aacba1f60aa70fb8ba47c5e69001b005073b71973114587089a13c7f","impliedFormat":1},{"version":"0a0714999d0a5bdfacd15c7b34cffbcc6f263f6cb0ccb42076cdc541c6987797","impliedFormat":1},{"version":"56584bfc655f9df64afc0f22f7d1122c29e5b74b342c203b891e19de9fa37de8","impliedFormat":1},{"version":"40ec58f0fadd0b3981b3d383e1c12fa0680115ae9f018387fc2cfc0bbcf23204","impliedFormat":1},{"version":"59709e26e08d4fd4c6a133552ad8f94c5b31463f295c4bf75fae1907738b8441","impliedFormat":1},{"version":"849b9e7283b7309a4556c9b90bb8e2dfc27751f157798065bbc513dcddb09a8c","impliedFormat":1},{"version":"76bba0c97594248c1be19af32d5799f7eff51cec2926d8e4dd59267d7636a0b4","impliedFormat":1},{"version":"10e109212c7be8a9f66e988e5d6c2a8900c9d14bf6beadf5fa70d32ada3425cf","impliedFormat":1},{"version":"f4558bcdc26690cc593cd59217cd17d8e00af0f5fbd0c4f1c0d71ba75029c42e","impliedFormat":1},{"version":"51d621c4e724720dd1b7ba6374d8a5b988beeda22d620ac84634a13691b631d9","impliedFormat":1},{"version":"f57a588d8f6b3ce5c8b494f2dc759a8885eaee18e80a4952df47de45403fedbe","impliedFormat":1},{"version":"34735727b3fe7a0ed0651a0f88d06449163d1989a2b2de7f047473adc7c1c383","impliedFormat":1},{"version":"a5b13abc88ab3186e713c445e59e2f6eee20c6167943517bc2f56985d89b8c55","impliedFormat":1},{"version":"8b29e3ed0c90b2ebc40b2bce5a518a0e86c0c417f7fe99a5e7658a61166bd9cd","impliedFormat":1},{"version":"7ae65fe95b18205e241e6695cb2c61c0828d660aca7d08f68781b439a800e6b8","impliedFormat":1},{"version":"c2c8c166199d3a7bd093152437d1f6399d05e458a9ca9364456feecba920cda4","impliedFormat":1},{"version":"369b7270eeeb37982203b2cb18c7302947b89bf5818c1d3d2e95a0418f02b74e","impliedFormat":1},{"version":"94f95d223e2783b0aef4d15d7f6990a6a550fe17d099c501395f690337f7105e","impliedFormat":1},{"version":"945be5a9505194381cfd4a8551a5f0ae48090847e454fecf834e054207c5a57b","impliedFormat":1},{"version":"d1e8b78a5ce49cee9ef4cd2565d4645d269c6fd0650e3592f85ba481f13da3a3","impliedFormat":1},{"version":"61be8f1d5345cf5750aed87af2869888ca1b675ffa481f1d4d80554e10084b4a","impliedFormat":1},{"version":"eeb6c806376b9c3464f29b6058aecf113328f9ce290af0375e520f1a844529cf","signature":"61f11ef9f7b473f14c872a139f0a329251738f177edfdcaefb3745adf8967036"},{"version":"216c830de5b7e1ff7336a1bf11dfe9c98ae2de2da56f616e7e4b4405aa14050d","signature":"f9653d5c0a8d7199894c3721eae87d898c8ce6668c3c28461dde2236367c94e6"},"ebfca49b6f505f572648960feb0bc5e131c9a6bea97f3f5883dfa9374ed4028d",{"version":"1ceb93a23603a978c37604ac8c0f3a5adb8a7bbd76a5769b950db644b972f0aa","signature":"0895d90edbc5d40218c073393554c18fa39a891461bfc44da8be225be27a6a37"},{"version":"b16d890b0ea02f67586f064f87af862c601884fd40ec000b3ec8dacdf1c4c7cf","signature":"c4bf08d84391225b229f7d67fe8f7b3ff511782f27e0d6f5f4680aab2cf451af"},{"version":"ed0300836934be9970c950c0d485e8276fac87cf1fec92f07e5f13fb7203604d","signature":"e3d48af43b4af0455edee6944467120f4272a8306e90d504935da490b053cafd"},{"version":"172445546b246f00923ce61b907837020174c84335bfa24cddc78b6a5d28d0a3","signature":"b34528c74b3ff693ae3d27488992d045d0da79151d70e3240ea701f4a8910b5e"},{"version":"a207d5278346c5ef6ea5ce0b34dcb377bf4cccbd7153ab83953cee72c59ab34a","signature":"1dd308df0c17f9580459e35f573f15a40609c032465913c8d86a10883edcda1a"},"f299ec29ad652a02319d39bcb58adf0803a2bb2387a025aec1a0a16f50519176",{"version":"b53aedd97ce9ebdcaf94f60bb120b172149f79045edf37b22858ff10bb61e09e","signature":"ec13261703b24c5ffb56fe30e3d7b64fb29d7ea5fbf548dbb3440646b65e1316"},{"version":"1251b05bdf0f9d8118139c3ed7bb0ea60466ee664c2d58e710be9b39032c6f6c","signature":"f2c6a00624f44434d49aef27eac8b74b150c4ad7ea531992cd5ec7b61cff698a"},"a7e4a0f02427c3e07643a6d5bb9bf0cc09f2ebe28b37a42189f923133c43c186","fe3c71661dd6c6d74c4bb196af4247d019f9308057fa1347f35877c4511c460d",{"version":"5bcca0c2e2f15929cfa8e0d91bad9f61ab85e7f256377ccc56d6b0f9f8552960","signature":"d77db17aa371d965761001b744dba64792f22b53c0a9ddd4d80d8c8b359c482b"},{"version":"641984c05f82a6e0b8dac973196b8ba146f1644b3706d318427096d844ac4f0d","signature":"eb5c97b219f68b8629c278d916c59c82b514b848ff10eb0db5d4196d69654147"},"064945c8a414c7a78b237a277403afd2b7ba4bb433d8cdc41fde3cddf09880f4",{"version":"5323f2f109370900f8d4f85c82ff47df76a7d63dbef322abf601217e4e677086","signature":"f59baba97905164ae2797a2a2869308ff3435aa1c66fd33034c0237abeababe1"},"1beb3dd4e06334a36673fbdf6df977bb28d28134285a21da8584cef98b0e7c46",{"version":"5a2cdf6adeec348bbc876221be4367e8adff0bb78a5680ebd7d71e5c3bad6cc0","impliedFormat":99},{"version":"e004826eac62081f867c66dabd92d3ef7d126d93a70430a2c88429228c3ecc50","impliedFormat":99},{"version":"38d6857b58d2ac42442e396311c542062d4f0dad40f2adb496dd5fd0756ee400","impliedFormat":99},{"version":"34b7d1e2d15845cf08bcf5e3c01adbb92cea1ec27564ee249ba486cdfb28526c","impliedFormat":99},{"version":"f13cc653015347688208b6a2817d6a024cae82cea1b2be422061ae1fb40e86a3","signature":"9d34eaf37fb26f7e3b5d52527e1cb097956ecec3deece2590b99f360ab4428a7"},"cc319ff8f06a331d4b359aa39f43dc18f8d4402f1f3c446b22a197389c4067a6","3c27fb3f66fa5c3798c663843ad30a16957d6cf39d4c4ee8c154dc03b777bd80",{"version":"0c50f7da7e287df66e69485e4e5b56c4a0fb9f8730571541873377e7ed45a2c8","signature":"987de9b3dd9352f138928040bd0776e179cdf67c235a18bd54580bc4163a2999"},{"version":"ae5f21d33e9cece1850a7223c30edff9bd2b842b05492b4d1f5c74891683186a","signature":"1bc767c3ecaad8c2a205ad502ee7c4f20cffc11447fadbd4ab3f573481073582"},"f45fce4f354b6059351eaea503203fe2661457390b5c443d299c90a690122d9e","721652119aa07fa7df69fec15bb05e7818c69f4798e424b2a444889f482d5118",{"version":"31f74c987ac1c8dd1bd2a84a270623b054c1fc4ce81a30eb788ce3d579d95e40","signature":"58a5ec371db12fd72d7b69a8c237fc87c5a131763b45d262a3d191c5d1356d6d"},{"version":"7efed9d38ce35662483150baaecb0eb98e400391ada29a436626063a3cd09be5","signature":"56e3f4727284e65c0f755411270bbf10da22e3fe5529baed216b93557b41276a"},"b3247c06acbd296275f69ae7aaa4572cfc9228e70de48b19ceb4584247fe05c8",{"version":"ced3338f3895b632082c71280b90c9425e3545064b1b595a2245930e353501e5","signature":"2abf126b8a0429351ec7cb3bd61efd7f4966a31641a2bef1339b78de215479ef"},"2cb5bcfcddafa73663cc7a0b9d07913ff00864af96cdf56ce809d55f80a1753b",{"version":"8605ab3907c8332a03b0fb2bb8ecb8259321c15adf6ec70b4032b85d771cf2f3","signature":"06ae795b9ca99a2466c46639c2ab809198e6c67d400165f05424a012b1bb817f"},"0b596ac641129a560bec8f495f52adda3c82d92b1a115434a46e9c48080c9157","393217dd0d9559eaec6303131eacb34df82e55bb8138da849896a108dc85151a","6515c88b44047c95ee046a13f74332deb2e8568f97aa6854d5d4a785ad05b84e","2b25605d3b717aec5daafbf2032723fda8ab4359aed0cb6e1585028b60b3f708","921a3cebbf89a24feeca9c194e89aab4fe3d19308ce4c13dad9efe3182df4459",{"version":"9e51643ad596e80ce30204003fbee1fb98f8006a5e383575efbdb0907951a0ee","signature":"fe75269529e2b297082168d9d65d1cc4a5f4b9d2649fdf474ea6ef7928b19729"},{"version":"bb23c7b441db38d447145cda42a252dd88d0ac4113dc27e43a3a7db35524bda9","signature":"c4e6581c0c2bf8d017173140969f491108dcd5784f12ddf140da8b0daf20ac83"},{"version":"e7c2f40dc99121500ad108a4f86541d29cac105ed018f994c7c5a2836e77b257","impliedFormat":1},{"version":"90e930283286ab117ab89f00589cf89ab5e9992bc57e79f303b36ee14649bdd9","impliedFormat":1},{"version":"6d48a6c907c668a6d6eda66acec4242e367c983e073100e35c1e234c424ad1a4","impliedFormat":1},{"version":"68a0e898d6c39160f1326ef922508914498c7a2d0b5a0d9222b7928d343214eb","impliedFormat":1},{"version":"69d96a8522b301a9e923ac4e42dd37fc942763740b183dffa3d51aca87f978d5","impliedFormat":1},{"version":"ff2fadad64868f1542a69edeadf5c5519e9c89e33bec267605298f8d172417c7","impliedFormat":1},{"version":"2866ae69517d6605a28d0c8d5dff4f15a0b876eeb8e5a1cbc51631d9c6793d3f","impliedFormat":1},{"version":"f8c4434aa8cbd4ede2a75cbc5532b6a12c9cac67c3095ed907e54f3f89d2e628","impliedFormat":1},{"version":"0b8adc0ae60a47acf65575952eee568b3d497f9975e3162f408052a99e65f488","impliedFormat":1},{"version":"ede9879d22f7ce68a8c99e455acab32fc45091c6eed9625549742b03e1f1ac1a","impliedFormat":1},{"version":"0e8c007c6e404da951c3d98a489ac0a3e9b6567648b997c03445ac69d7938c1c","impliedFormat":1},{"version":"f2a4866bed198a7c804b58ee39efe74c66ecdcf2dfebef0b9895d534a50790c4","impliedFormat":1},{"version":"ad72538d0c5e417ee6621e1b54691c274bcacaa1807c9895c5fa6d40b45fb631","impliedFormat":1},{"version":"4f851c59f3112702f6178e76204f839e3156daa98b5b7d7e3fc407a6c5764118","impliedFormat":1},{"version":"57511f723968d2f41dd2d55b9fbc5d0f3107af4e4227db0fb357c904bd34e690","impliedFormat":1},{"version":"9585df69c074d82dda33eadd6e5dccd164659f59b09bd5a0d25874770cf6042d","impliedFormat":1},{"version":"f6f6ce3e3718c2e7592e09d91c43b44318d47bca8ee353426252c694127f2dcb","impliedFormat":1},{"version":"4f70076586b8e194ef3d1b9679d626a9a61d449ba7e91dfc73cbe3904b538aa0","impliedFormat":1},{"version":"6d5838c172ff503ef37765b86019b80e3abe370105b2e1c4510d6098b0e84414","impliedFormat":1},{"version":"1876dac2baa902e2b7ebed5e03b95f338192dc03a6e4b0731733d675ba4048f3","impliedFormat":1},{"version":"8086407dd2a53ce700125037abf419bddcce43c14b3cf5ea3ac1ebded5cad011","impliedFormat":1},{"version":"c2501eb4c4e05c2d4de551a4bace9c28d06a0d89b228443f69eb3d7f9049fbd6","impliedFormat":1},{"version":"1829f790849d54ea3d736c61fdefd3237bede9c5784f4c15dfdafb7e0a9b8f63","impliedFormat":1},{"version":"5392feeda1bf0a1cc755f7339ea486b7a4d0d019774da8057ddc85347359ed63","impliedFormat":1},{"version":"c998117afca3af8432598c7e8d530d8376d0ca4871a34137db8caa1e94d94818","impliedFormat":1},{"version":"4e465f7e9a161a5a5248a18af79dbfbf06e8e1255bfdc8f63ab15475a2ba48bd","impliedFormat":1},{"version":"e0353c5070349846fe9835d782a8ce338d6d4172c603d14a6b364d6354957a4e","impliedFormat":1},{"version":"323133630008263f857a6d8350e36fb7f6e8d221ec0a425b075c20290570c020","impliedFormat":1},{"version":"c04e691d64b97e264ca4d000c287a53f2a75527556962cdbe3e8e2b301dac906","impliedFormat":1},{"version":"3733dba5107de9152f98da9bcb21bf6c91ac385f3b22f30ed08d0dc5e74c966f","impliedFormat":1},{"version":"d3ec922ddd9677696ee0552f10e95c4e59f85bb8c93fd76cd41b2dd93988ff39","impliedFormat":1},{"version":"0492c0d35e05c0fdd638980e02f3a7cdec18b311959fc730d85ed7e1d4ff38a7","impliedFormat":1},{"version":"c7122ba860d3497fa04a112d424ee88b50c482360042972bcf0917c5b82f4484","impliedFormat":1},{"version":"838f52090a0d39dce3c42e0ccb0db8db250c712c1fa2cd36799910c8f8a7f7bf","impliedFormat":1},{"version":"116ec624095373939de9edb03619916226f5e5b6e93cd761c4bda4efecb104fc","impliedFormat":1},{"version":"8e6b8259bfd8c8c3d6ed79349b7f2f69476d255aede2cd6c0acb0869ad8c6fdd","impliedFormat":1},{"version":"02b6d443cd64d2a7e8dba0f1d59944e55e91a16b21a7d7d4fb5a81724c832dc4","signature":"e66fec1c73ea068e8541b003c79af072b1b18910017d07c47ad151a438c709c1"},{"version":"a1cc7006ac0ac2dd2748f5b4a07092a330d175b16adbcd49c0eed365e9b4575f","signature":"6521410466cd5930d8f9db814cebdf1094def90f68293a3b330296a35ff2c1f6"},{"version":"d3984cd8c4d6cdf73a81ea0891dae87ae6a01c1895fd68df0b6d740006acb9d6","signature":"0ac76f72a94a13f3081c41c43b58679492c219ccada653f40801a89fcd5e9d04"},{"version":"64a1df79fbba93c3a1642f66057608a3d7e2fd24ba015b260f7014ddde542908","signature":"feb053fdd4dce7ad7c1ba7791bb6f65fb66d38bb9c1f0543012dab8f663e88b4"},{"version":"9b47bc8dd5a4d6b7f03a22a9ff4f46883813ae93554718511b93888e39ee58d2","signature":"32937206cdaee2551a23ce603292dd67e9606a27fc71a984eab852fbea3b9ad2"},{"version":"f748a5c971c789d58810273b596542811e7e49eea7a42b7fa3c42829dbf62a58","signature":"4dd7e1bfc2c138b564a1ff5bddcae96f4cefd39724115166bdbe071cd00b3cb6"},{"version":"c4528c70ebf1acf226f198422561ad4348ac9e35a8990b1fd15e17ad9268d60b","signature":"71f762a4ed63ccdd8a60c9930b445ab8e81bdf4b9919c5b94761511cd866f447"},{"version":"d6d3f9395cfd6f2ed3c9eaf572f882a03a1fecfc1e13acc4519df67833342bd5","signature":"5c597452991cbc579454bf8e1c5f549816d79f80ddd3514b52fbb26cc1cdeced"},{"version":"7c6ce84284a608e8ca9b7636cb5da89481d8c945d03ba511da6a2fd56bcdf78c","signature":"c169279b909f77b0c7b26ce990b20c6719869fd76be6f95f4eadf4f3befda363"},{"version":"83e1bfa7986a958fd6e069fc5df9dec6aa1e63f3dd81ddae889c19edf3a6c450","signature":"6efc188b6e1596f593cdcb356be53ede31fa87f972e5d2adc9377fa511e2685e"},{"version":"c463facc7d18f4c36823714a285903d1123cc38a9dc91a5d099c64145432f75c","signature":"2150afbdeb24336371088cf931c6081d224326f5c57580ee0b36925d1569ad5c"},{"version":"dc916450a7fe9f02ea4f2b015b836fb7d3e6291e59c93b47f711623ec4c62fe4","signature":"1609615e284b1a86bbaebd997d03c23cbe145012ba3b3d4376aa8a43a701e4e3"},{"version":"02a313eaacd1d0d97e7e1605737ac03e732648ba6d92fbf2c24716c1349c30bb","signature":"e42b8c3731c42dd2bdacdcbd0b7639df957c3f9b5fdc1edabac4a5e63772a4b2"},{"version":"af2d7b90a50168850a399d83b4e9afdc302a1025148194e2e94e1a31060b93c6","signature":"9836be02a489f0fb61392d0e3fe4127c72f079fcde9e9fed4c282bb070832fb6"},"9e06917a1e0918bc34f5e3cfc014c05c7cfdad0c98997d7047b4e7542aee1861","086d9066a9edc176d4baeb61d0075de9353ee4695c94ecfae51f293be8fefab9",{"version":"94fe52c96742b25429d30bc54d7ab2a2324f37025cbea99f819a77ec87bb1772","signature":"d570651c0a2c5e78e74c52a792b94ccc2cc9b2b927bfb3a5419acc0150942695"},{"version":"1897adbce3874a07180bb47daf0e8ebedd6d1793819143c63cbce290ca2ec80e","signature":"aaf435d6dc58d0a18a54421b3a622efedf9a7a996d8f75a06354219d91707650"},{"version":"68fe3c692ad2824bc811643cd5e239d872cba48006000dfe185146ad106066b3","signature":"0342e61cdf2eadde061c53e1c6fc7907ad69390beddb2aa50e656dc2a45a632b"},{"version":"b15c4ee8a756cf303d0efc482e861876a2e90b194c7cc393a8acbe7080fb186f","signature":"4a1ee69e5477f0d725306d7c9281d127f43ec0b40a23689a1a27b9430f030177"},{"version":"f6a08a8d8fa7acf45c3ba85e864da549befc88abeca247da5b6732a82685bf45","signature":"afc9b47eb28f4775396aacf528a98d207e4714ed7600c47bd33d01d4d6d3852b"},{"version":"bb0365d741d36b7f82832dbcf1b2e0025b6638516fda9ba3061d7d41f7f073c2","signature":"1e5e485956159fcc1eee2c73dcba5186c0a66f780ad21ec3760cd35a723930ca"},{"version":"431206d65e5858f0534c8be80eb4081627924a9d1cdd853982a3a4d811999681","signature":"e4b7681fdfe65ce81bcf251c1bcdd71b93740fde81479a2e3531a23fd347d951"},"7b5bae142db908800ba59fb353d7274e1f1ef0eb46074f1675af3ee77c4d789d",{"version":"058aa6a9383796a202fdc9eb0c5eac4cce8a19ba60bd8b551091beee197fe25d","signature":"b067ed3257c3d808d867d834eb5c1688ce5984ce6377ce6213f7fbea90bd6b58"},"c837dc2de1fef03dadb1fbe3ae46565ab80cbeea60c057acd5bf1e1d8df1b509",{"version":"e6c4a6a15416b28fac47309cf33fe8040115a2849ccd012bd098efa4ec4ce9ef","signature":"f3afc6db2c2172dfa631f271d2bc28e8dfddcb2807285ebd2ff547fb786e49bb"},"283ec3ae2b171cd28e6778d2bdba3f8b055e818dac238b8a2d27403c551974d2",{"version":"a76cce81c55f02fa760f7b994c9aca6f3878e566f2bc8dfc8ecb79950d04f354","signature":"26b17216456cfb72ed066ba09342f94a61d6ea42ad25b2f2f00285c728be628a"},"e0340f2e710b3caf03d7435335ed6441df684f5f416b3008077280a53bc0d195","92c285578eeb816b54f7042a5447e57b676d60becce977c9d4105b6565b1977b","04780775bbde0064d8134ab5c1f40f2a0cc6e8fb4d3bc8e8e2ac961c05bda871","c32feab5e5456978529c9eb1c2d8b56a04d9074f2f43e757edf680e132d37d00","be7bd88676ebb10c83d7fe1378c26122200f68085ea06524a4f0f8c66831b348","e111d7709868c64a5ec40c93a0831eff084f5f3747bb50300878504738e28c19",{"version":"be072d8f770e47c11f6ae1b77999dd40b0c32d7f710b8c2685a7725daeea9d19","signature":"e367993516c9f05fa87238bc5b53220f06b7f84b72629958930e2a7a37436c24"},{"version":"4d6792c606bdd2a9b2cddc4d24923ccc18f7f438cafa31e0e21285e97c58421f","signature":"2d8f81759b547e64f1b0e290fd4b0ac7316dc9c3e96f5ca93db1a1c790ec6038"},{"version":"b8b666a3d41df3b7cf4066283f67e72bc5e8e04ff4414695eb972ba7561ce133","signature":"171b8eafff7d0d126a6df4cb220dfdf7ae67c7c6687fbdc02bf4b791bca40091"},{"version":"d4e9258acb020b007d2a76f0d9870d5d10f0a2d6bc8d24bcaa0f65931892b736","signature":"345eb0a009f9b07377ff2e8bcbd390da1648e549914b3bd027bd6b4987f92481"},{"version":"7813cd7f20cdeb108fa5e7ca8c809d95738aef3d8aa514cf516fc6e5651e6357","signature":"b9210c8fdabb61d8f6f3aff9f643e99b8fd4ace26e523d81264ec5e5d23583ac"},"93c88804801702c2ebf4d7e282ff71d90f118253ee206e7f0ba03305cc581546",{"version":"447809300abe6967b66fe4226b18bcd8513258b0139a8fd1ac516767bc510372","signature":"6d31cb09b5e87e0588c937c73aff7673a15865e355903fe16ac3bdcb3d2894d6"},{"version":"d689a82dec12ec02e1c558870a50f71000e06f173151fcdff0458c587dbf016f","impliedFormat":99},{"version":"e58c0b5226aff07b63be6ac6e1bec9d55bc3d2bda3b11b9b68cccea8c24ae839","affectsGlobalScope":true,"impliedFormat":99},{"version":"5a88655bf852c8cc007d6bc874ab61d1d63fba97063020458177173c454e9b4a","impliedFormat":99},{"version":"7e4dfae2da12ec71ffd9f55f4641a6e05610ce0d6784838659490e259e4eb13c","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"c371ea3efec17691f019bca670c161bee2f4787b1f464bdbbb10c4117bf0a55d","impliedFormat":99},{"version":"309ebd217636d68cf8784cbc3272c16fb94fb8e969e18b6fe88c35200340aef1","impliedFormat":1},{"version":"91cf9887208be8641244827c18e620166edf7e1c53114930b54eaeaab588a5be","impliedFormat":1},{"version":"ef9b6279acc69002a779d0172916ef22e8be5de2d2469ff2f4bb019a21e89de2","impliedFormat":1},{"version":"71623b889c23a332292c85f9bf41469c3f2efa47f81f12c73e14edbcffa270d3","affectsGlobalScope":true,"impliedFormat":1},{"version":"88863d76039cc550f8b7688a213dd051ae80d94a883eb99389d6bc4ce21c8688","impliedFormat":1},{"version":"e9ce511dae7201b833936d13618dff01815a9db2e6c2cc28646e21520c452d6c","impliedFormat":1},{"version":"243649afb10d950e7e83ee4d53bd2fbd615bb579a74cf6c1ce10e64402cdf9bb","impliedFormat":1},{"version":"35575179030368798cbcd50da928a275234445c9a0df32d4a2c694b2b3d20439","impliedFormat":1},{"version":"c939cb12cb000b4ec9c3eca3fe7dee1fe373ccb801237631d9252bad10206d61","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"03268b4d02371bdf514f513797ed3c9eb0840b0724ff6778bda0ef74c35273be","impliedFormat":1},{"version":"3511847babb822e10715a18348d1cbb0dae73c4e4c0a1bcf7cbc12771b310d45","impliedFormat":1},{"version":"80e653fbbec818eecfe95d182dc65a1d107b343d970159a71922ac4491caa0af","impliedFormat":1},{"version":"53f00dc83ccceb8fad22eb3aade64e4bcdb082115f230c8ba3d40f79c835c30e","impliedFormat":1},{"version":"35475931e8b55c4d33bfe3abc79f5673924a0bd4224c7c6108a4e08f3521643c","impliedFormat":1},{"version":"9078205849121a5d37a642949d687565498da922508eacb0e5a0c3de427f0ae5","impliedFormat":1},{"version":"e8f8f095f137e96dc64b56e59556c02f3c31db4b354801d6ae3b90dceae60240","impliedFormat":1},{"version":"451abef2a26cebb6f54236e68de3c33691e3b47b548fd4c8fa05fd84ab2238ff","impliedFormat":1},{"version":"6042774c61ece4ba77b3bf375f15942eb054675b7957882a00c22c0e4fe5865c","impliedFormat":1},{"version":"41f185713d78f7af0253a339927dc04b485f46210d6bc0691cf908e3e8ded2a1","impliedFormat":1},{"version":"23ee410c645f68bd99717527de1586e3eb826f166d654b74250ad92b27311fde","impliedFormat":1},{"version":"ffc3e1064146c1cafda1b0686ae9679ba1fb706b2f415e057be01614bf918dba","impliedFormat":1},{"version":"995869b1ddf66bbcfdb417f7446f610198dcce3280a0ae5c8b332ed985c01855","impliedFormat":1},{"version":"58d65a2803c3b6629b0e18c8bf1bc883a686fcf0333230dd0151ab6e85b74307","impliedFormat":1},{"version":"e818471014c77c103330aee11f00a7a00b37b35500b53ea6f337aefacd6174c9","impliedFormat":1},{"version":"dca963a986285211cfa75b9bb57914538de29585d34217d03b538e6473ac4c44","impliedFormat":1},{"version":"d8bc0c5487582c6d887c32c92d8b4ffb23310146fcb1d82adf4b15c77f57c4ac","impliedFormat":1},{"version":"8cb31102790372bebfd78dd56d6752913b0f3e2cefbeb08375acd9f5ba737155","impliedFormat":1},{"version":"bb9b5a18147a0f927e0fffe91515a39610e2477b0d8a0d0b391c283013e0bfac","signature":"d373335450e0c74b3455541e03c0ff8fef26b51201c49ef145a0afb217a9f026"},"4bc5159b0bb1e303f1b662d485b7f9dcfaf785a29f8cd101ea85817fdb3a518e","25bb698c825c728521550bae3d4d8777520fea078d96529db79d3901278e084f",{"version":"d7382f620923bf13382b5aa1ed1d439617c8f6c916c1d7a645a6f5005dcddf8e","signature":"32159b615fba8ba0c76d071b40e35822660f8317b107f16b5d40ce3a8d6a5bbd"},"6fbedb59be020e7d349de8a1ffe8aaa52d16c78f9aea437249f14782b290aee9","1318f5ed0496352fb48c4c03ed01567db651e1794260df1b2da1e146a1aefab7","fcbc73a398e35777c583049d7a6315455a1c340d06a7ba06fd65a08a998576a3","ea963ab39dbed68f0cbfe8f7bebb09e3b9a98badb38164903aeda102ca62fe84",{"version":"70ac7fbe8555de02f7cb0fe42f479173ddb89a737908c560014d733348422046","signature":"d9b4f0fd652a60e8727bf295164c2d0a652cb6d79ac90e8b13c48d4230a47039"},{"version":"656ebe6a1e35fb1e45ace5b3d8975099fa82a7a42542c09ee1e1e975b4951722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c4f3d9c6228f744351b3f3d6ac2593edba9e7cf0a965cc6ccb1805595f44f275","signature":"96dacaf48c43f86fe63578c47b008b14f31ea3b26ba6604869be23386548a0af"},{"version":"ce80305706eb0c25efc5028968e9b4c6118a68c8987532ec9df246a8e7ecf993","signature":"b95e4b7b3523b6989a5d11cfd8722d821d0dae59e5016cc2fa69c6c3e7507a9d"},{"version":"256ac94c8da7010cbaacfb3e0f55cab2ce49beb7f21309659ab1e5c44b66cba3","signature":"4932a57ec8dc885c99967df2c08c4be4dcde303de1727465afc901bb526c9dce"},{"version":"054c188a756ddb383e1ccb176c09ab7f0894d89fdb9ed00f102af2a9f7ac0e3c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45d8c8b72f837a46ca63ef01ea3f4244112587c0abac142367982f443e31ea7d","signature":"eb8463d6df0ca2c38399823f1e38ff66180aaefb12e5b403155c2abe1eda8b5a"},{"version":"964e363030719b2e66f7eb64663b22f039bc64985dd1e75eae362e378608ad32","signature":"52b37759b4c21b0266e113f72e72db24ca11859fca9beaae88ac286fa508c5eb"},{"version":"2aca0bc14bc6a0e2ce70f410e002eb4aec77e7622afd0e40200a2d6c36542db3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d21bdd834776d159085df8f067883d3437dacf6eab3d356f7c3bb2bce9a9c98d","e541c3824271bf8af94ce64854e33b2434f4f619a75bbf7c9051b746d2c5b2a2",{"version":"c7fe6433c779a7bce07b3c90d85dbd397326047eb839680cb426b97d15b1af91","signature":"8ffce94f2622151e417ce42edf509f0890eaf9268f878c698e05d0bbe3df3159"},"4edd723b64a3e617fd8ffc3bbc1fcf757ea9e1eb9132d9d77525807656426e4b","fcf8bb50230d3b1973034c5f3d43b32ae889757e96c8f1bc574e4e229cac3855",{"version":"2ade88925e99f1feff0c33e971814e734e05f6f9e32c2fb7c6260247635417ac","signature":"06ca53e7c778e43262f44194db43a44dae84e02e9d9ae674f74a4039f043a39a"},{"version":"a5110f54ba5e9c7c7fdc029cd20e65f35a9ddab6830394949279d03f4baaa112","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e08859a0433654484c23ea7d2447e7da43768e228643cde336290e80359be015","impliedFormat":99},{"version":"427d5d08714a73f909d965ace2642ea9819e49245620483d47acb73b4eb922cf","impliedFormat":99},{"version":"242e53307a5705e9235ca1be47168a4c3155ea674e80f5a13f94d7938c23bb5d","impliedFormat":99},"2e0b9c5b9659b03cf5a40b73ebfe3b0c8de950f06308a61502a2722e2f418c18",{"version":"8ffa57b994af8cee7411cd7bfec0409118909a2a897b417d5ba378025b9b8eb3","signature":"66d21fb03c05d9e19a9e6328311f0e929ca450163fe3ad5a1f19b3e0563710df"},"50fdd772b1313709b583dd32561b52331a43b53d7aae0d6f3630a85d4871ad13","179f0303099722db250eb13fcd19349ee2fb24f33bf524e43d88f94a8a82aa95","80efb9a44eed9b0287c7811fa3b4418dd9a75a3c8c9d55bcd30ffbe3d72d8211","72d3fd192ffa0901a97fa17655ae18a1a4af3479f66348b17bfcafc42678e06a","849d186951b6fe08777eb595e7b5423a933404a59b255b15b3ef91eaa9e03e2b",{"version":"398322573c9e0ba2826eaea162b97f4987dd00caaa4dc29df93d7dccaff40c2f","signature":"dc1df284b2ecb2adb8124f0411490cbb6adc27d6b3f783cb98e4de022894c67c"},{"version":"0e614492dab5ee5f4418895293386b642203c8f1a3a9d14a8eca94a906c91c04","signature":"1ce004dab6fc4c13fe2a946bf541afc29f77e4b6d197bd7e078c216bf331c288"},{"version":"f22a38020548019be436f7d33fae243aecf68c9e068dd7fb5d88ec31fcfce2c5","signature":"990a86a4c51ffd7c3c146bc5b5e4f2a6eb31f8a08186da00d699ec62be38d14c"},{"version":"07a57ea6e42f784f7664053d917baf68d010a79f0df1fdb8fba87a6af92ddd7b","signature":"e57424bb8ca9fcb02c7b73c295bff56d7889e92610490ef4dbcf83dcf5006809"},{"version":"ba3f0e6512b7afdac714ac775b22777273fe0dd98096e1d1a7fa2f9aae83cec2","signature":"7546aea101d084a3039eec017b4629ef261e36c012a024daeb0f8170d86d192f"},{"version":"18f204ccd73154b6afaeb5c1672609aeaac9592c85183e75cf590a4bd70575e1","signature":"4b23394a9dda4737bec117daa9748cb9868e5be402b010881029c58b639c48b6"},{"version":"ed9e84f54b39f81bdc4e0520812489f40ea453de7a51d380a15b14d7bf02e683","signature":"e6cceaf655d91958114f0707a4d6c800cfd0d72ea8673f4f0face6b049c90ec3"},{"version":"9f2145479716604449381a636127459790f9e428a5c526cd0795223bb66dd9b3","signature":"d86c8b7a6c6edbeb6a14c73aad61eec9d13cec0040264ef13c78a8c2f7ecbf43"},{"version":"fa32219cc14042734452368d122b82bae2849be88d8941c5b363e3c47c9b651a","signature":"7c56fc0ecedef369430a6cc78f797e1f72ac6aa30cf3861bea222fc9efe93d49"},{"version":"f52646c7394ab792adfca993338d590f7d9030ae3269526d5dede9b131247717","signature":"d1d2efd128275df07279bf887018192c1b38c0cc2aea96243de78a8e92bc30ad"},{"version":"b1ffda5abe3874eaf7ee7e57cefd4c4ed1e85e00932b5da6847cbe0e22c7eedc","signature":"29afa7f4d2f64a222d590227109f01361ddb9c6588355096f6aaa036b9d67d05"},{"version":"0cdab5351929ad0e2d94ecea2b8d60d11fcdb153ff2355ab9c5109e84a697ddb","signature":"91537516c066b5bda3446b1dbd01a6b3ff342925cde014d5acb7b6f8b99ed12c"},{"version":"0172224d148517f7cf90527aa73f03cb436b3fe7f06ac70c039d6886b8f9dfdf","signature":"d01168279f1f9a417a578d3768c58eb5170a5b3445e7b2dedf880dbf0d1fe873"},{"version":"a6d5aa9d1ace7ec2a992729d02341737fc267a9d2d7f1467313bd170e4b26b15","signature":"a65ecfa05330aaeae23d23b899f0bd37c34e42fa5083d180b4a0bff3dc3ae25e"},{"version":"831b967c1911010eb3adbfe96d76340dce858803d80310236352a7b52de799c2","signature":"76d26c617c0a9f48d4e21938e684ae22166d2d3604d00cafab5212b0e15b57fc"},{"version":"d09b5dbb314f199a0d7ae3c2c2a9c7258b611f3f28d1cc83c2685d3c738aec3e","signature":"068d0597a17af822c2ec3af9b1c2a9b9a26c0a4387eb66f655a0f1d26e36ba84"},{"version":"35164c8ef06e7c366f6f45f993da6e0df0f7c2cc93e78198c199bec111da8fa4","signature":"942546eaf5ae2d0c5948c6d25a748fba25b6f4d760911ed595b40f043fbae102"},{"version":"ea484c456f3f9236d0b324d2c6563f6e77571c9414768590248a016b2e248a3a","signature":"65bb767048368601ad35597c54f6b112e3147dc84fc338199931af5d57b8fe95"},"1ac040d4f47be4c1b44b4a521c7bc1264c97371b3c51bc2170326827fc8a5406","f3d7aece0ac20c6911c50aa54b50c1ae6768a8793a72412d346aab2b66b4a7f7",{"version":"6a282a4b745d9ac9d04d759b34b9e51124a950ba33d83a1408f76742cab5d8a7","signature":"519c584866e4d804355422ce52d8088fda0124969a9668779f10b0d50e114153"},{"version":"3cef134032da5e1bfabba59a03a58d91ed59f302235034279bb25a5a5b65ca62","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c05d0fcee91437571513c404e62396ee798ff37a2d8bef2104accdc79deb9c0","impliedFormat":1},{"version":"373cf226ee7ddf9535231d4ea2c24d47e4262372e1c075aee7b48e0d2d38e759","signature":"3a700951382c62ca71c0a4fb951071e1a2692a3ddfc899ba0145c275ff12a006"},{"version":"e91484fa999daf133fc988973a12652f1f59f4e1e4e440e5f5e7aba9dc419e54","signature":"7cf5ac50b3def9f8df750c1e7ea9a102216484b4bba94f9e0bf68458bc77eacd"},{"version":"2d4b53789aab997f99121021686c05f5f54aae58fbb0525243fdd322c80d612d","signature":"5c975df906b720e560dc80cf99f12cb2763329a0d5c42ffe3039256137dc3a70"},{"version":"e43472b89b27f89f28fcb57260e48230b95baff6fa6c489c5710115cfa6c9506","signature":"2f9e549adb20bf7d44ab18efcdb5e7dab6bdf423d310f3df05e5ac78e3828990"},"91d7a64938101c27f0f5493074dd0ebc4f82ed6d58c42c8d148235de3f8978ed","d563b38c81c713a23b730e0e385c44442992d3b1dfad2424fd9c635e3eacf593","6d4b59d8a599531b5bd5cef904c5f8832f062b79eac5298805b9aade268d66b8","fee8eb73b4397c9d3fc50904fb4d93947f32879251345c687761a5ac20a76314","0d922edfe50c6ffb2c16d49dcdd160dc468b0f93f69fcb021d6464db95664a21",{"version":"df3a3dc2616be7db489fe6a853faa1e52a83dfde06d2f3214994ee7ef81f18e4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"3cfb7c0c642b19fb75132154040bb7cd840f0002f9955b14154e69611b9b3f81","impliedFormat":1},{"version":"8387ec1601cf6b8948672537cf8d430431ba0d87b1f9537b4597c1ab8d3ade5b","impliedFormat":1},{"version":"d16f1c460b1ca9158e030fdf3641e1de11135e0c7169d3e8cf17cc4cc35d5e64","impliedFormat":1},{"version":"a934063af84f8117b8ce51851c1af2b76efe960aa4c7b48d0343a1b15c01aedf","impliedFormat":1},{"version":"e3c5ad476eb2fca8505aee5bdfdf9bf11760df5d0f9545db23f12a5c4d72a718","impliedFormat":1},{"version":"462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","impliedFormat":1},{"version":"5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","impliedFormat":1},{"version":"d0570ce419fb38287e7b39c910b468becb5b2278cf33b1000a3d3e82a46ecae2","impliedFormat":1},{"version":"3aca7f4260dad9dcc0a0333654cb3cde6664d34a553ec06c953bce11151764d7","impliedFormat":1},{"version":"a0a6f0095f25f08a7129bc4d7cb8438039ec422dc341218d274e1e5131115988","impliedFormat":1},{"version":"b58f396fe4cfe5a0e4d594996bc8c1bfe25496fbc66cf169d41ac3c139418c77","impliedFormat":1},{"version":"45785e608b3d380c79e21957a6d1467e1206ac0281644e43e8ed6498808ace72","impliedFormat":1},{"version":"bece27602416508ba946868ad34d09997911016dbd6893fb884633017f74e2c5","impliedFormat":1},{"version":"2a90177ebaef25de89351de964c2c601ab54d6e3a157cba60d9cd3eaf5a5ee1a","impliedFormat":1},{"version":"82200e963d3c767976a5a9f41ecf8c65eca14a6b33dcbe00214fcbe959698c46","impliedFormat":1},{"version":"b4966c503c08bbd9e834037a8ab60e5f53c5fd1092e8873c4a1c344806acdab2","impliedFormat":1},{"version":"3d3208d0f061e4836dd5f144425781c172987c430f7eaee483fadaa3c5780f9f","impliedFormat":1},{"version":"34a8a5b4c21e7a6d07d3b6bce72371da300ec1aed58961067e13f1f4dc849712","impliedFormat":1},"6a534c594838029f9096b88db91c054e612ff951a57ed9d9efd92f19643a2753","6223f56cb79eac77e1211e76830da993ddcd9baea0dfe2d10a61a131d39f427a","20d8184cc9bf496dfd9415be762d5233809d005d149417d3c30a16084b0c3842",{"version":"7f72a954c349bccf89e393e243763fb141257a54d6647e369c79beda371378f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c6b47f0ea0108d741abf731f728b357a8370c238ffe73fcee007619484c24356",{"version":"882e8d0ba2abbb1b69de1964aa644932be0278f7ed640ddc904541ffda281fa8","signature":"f592c7e333a33b4e5dba58516b31a9ba2c3f5639c989fce88351556ba49606fc"},{"version":"316e4731cf6b5fa0f7200e020cc7264355bce4cd1c0a2556296dd7f4ba015b5c","signature":"e38a144b393c547e8f484fd4ee07f6790d350a3f1f1148211ba866434cda2648"},{"version":"214244e86df9709da19e41c83203eb228ab74388a8899c0cacdb856bcb9b2091","signature":"571d3448b7e5dbb700ec919745d70b84c0859909793935026a8331b7666d91ed"},{"version":"73f615ff0e9ff74f51982f4b09e85f2474c1e05a50a4c75f099061a3057094ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa4db26266d6f651c711350ddf671278179e6f59b28d3c390ae50a9b20a3aae4","signature":"921c81a312317ce376b3db64ec158a40d264b56c798653f7985b9361289d951a"},{"version":"973c5f132d06d0706025fb3d9a12e2aa50d9bda13557ffc7498cdbda3098fd58","signature":"03da542307af2869e7b2c1de8d1237d05b584cf0acf36c9d40f8e994f21adb2e"},"5f1ab4340b3a3f3d2c88167d0b98d1d8ae6c6d4b1ed845f25c3069d7d1b902d1","62fdb9ea1d1284dc72bae3338d2a20c737814b30d30c9d0ce40aec4fcbd51746","cab9185002bd5ebd154b6106ff93ae480bb26be2bc14bbf19180ae690449af28","b6ce8cc18189aa155b9d4386c03e3547f48121a7e3f37b66ed9ad43190b20dd3","81aa2ba3522bc10222837dca4556efb07a6628de274c94545a536c1955684ef4","fa1702a90530bc09b078bbfc9e98010c20333706f9d225c18558a146ac9e2219","b980df9c1d9398fb15cda202074eeb45eca1b733888708d0fb43c021b5411991",{"version":"2c4beeb10c123c02ff09c390cb92953e811cb0e846e2d040c65a8a0e73746894","signature":"5431108d0a4a15cc5f6d78abd5a358d13bcabb849877e09f3efc265eba21e6e2"},{"version":"5559d4fcad759cc07a71aca5a792755409db3b683788828abad2a84da3dcd7fc","signature":"c367bae6e0535dda7431e73df32e233511c1e9b1181082d551efc822fcbaae83"},{"version":"46ea0bba2f2e36762dae6bd527f342a19fe9f46c653dc4e8061d9c8530ba8dca","signature":"19467dd75b0a6bae42afc2ac103445b6ddc4a3e259599ac20b2ec70e75fff499"},{"version":"40f8a5ff101ec9d2a6a08af84db2d4865c35e2deb3da94075a482d94612ca24e","signature":"57e73f014bbd5a960cd0a3b39a240cddbf1842f7f06a09757be73de97a234a79"},{"version":"3d64c2914a71ab3af9fd253eda13ea735a784923284005d07af763636578b46e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5112478f7f7dcb622157981c8ac9a0fb3cad40c5eb87a19ff2e37674c75c0fd5","signature":"8ce6788984fbc5caf642946b8dc8a405629def762f166473ae6389aab4822034"},{"version":"178dc732f2d61a4fd094b6672b6c438d1b1d6cfd5489564206a84e3df486beff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6f815019b8b763503cecf9ac86f9de6bd8593180a0db3a624f98acf88dad162f","signature":"b5e89db47e4299930bf6020c3ac33fe228d590042b7dd4c5a3dc245027bd9a83"},"8f6aa64ab08524e8ee85ed63f8dffa377a7f4017680001a3669a963162f9ddef","c7aa0820877b8341f78e794fde60d464746eec82b0e2624982000ebb19fb8f8c","7866820b43b8c2b53cf5a1d2f4de019e059e681e9ec7cff7b5972800a8d78732","f38eb0a2421beba20ee66d42353732cf2ce6f343c1b1c322252842e0f22b8308","88d59e42faf36bf3fa832f1e69ed374efa2092ef1128f016701503413b9c44bc","9382ac249f4efbc0256803deafe838b51123955ca8b68c68a4be2b2c4a94027b","7f8e8b3d1d986e5be4c13b7a642a6f4899f1af03978448c01183a00e828c12bb","e1ae660c622a39b34c87f6dd244d59846a5d110d2c39bcf4316a499b166b6e7a","7606ef9eeff41c0616d32c7f6fc2086c38b34c3d7221598ed9291aaf126eb178","695e9a27d875aae3f404cbd6ff901fedf0d77c193cfd9552edba781658ed0e85","d0608ad5086ad006c7c2a10e4fce58cbdac946d73cda4c270717bbc11751afcb","42b18867a7543fec221e4f0321e077538e596cbacfec0595872df4876635dccb","c07f3037b31e0bd7e1384c41a6fd6524ac141e8f56b93a4a12ec63d75a704edf","6e925ea850d5c27ac93c1d0a26203779a347ded9a658643ac71febb557086d09",{"version":"25b749d6ada24514fd767c7212f8710ed80c3f54499a13642246913e678553a1","signature":"27bb3ddf3da26f0251f6fa1f7b1d888720e20fcb54f8513e691c7276c730e0c0"},{"version":"f77ebf90d0877e84d5f546d128be5e362554f93395f46ab6fe1fbf060b962765","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8efd52cc22298b4d6f0540b8c96ad97a563fd1a0effd9c245b9da300b5eef04","impliedFormat":99},{"version":"0d8cb9539485655600b329ddfcdb91d1b4b20f5d1b40a9e40c8017938fb68d5a","impliedFormat":99},{"version":"51aae950c97b61105064619e52f8b4e702ed9d88f6a38d8a6d461389be28dd0b","impliedFormat":99},{"version":"08a8feab6868367d5112474f9015e5b00c101012a639309e88fb105f94ced534","impliedFormat":99},{"version":"a15a870f6ab5a26a7eb91ddd8c47ff4e00bc23ece96ab48ff8aaf42450478a50","impliedFormat":99},{"version":"73390a82cbd5ea87d8bcdf183d66853207a111de00a8512c68ca17b47a11e65d","impliedFormat":99},{"version":"53755d0e8037d36720dc68e2e8c77512698befd0c0d48ac5d20c41986df91bc2","impliedFormat":99},{"version":"e0f44fe626dcd0026670f01dc0af34c40332729e8a1ce2dccc67e2ad5c96e3a5","impliedFormat":99},{"version":"2919501096a871a68fa6bda28480d7c237b232928215d6edec67e75dafc820c3","impliedFormat":99},{"version":"7f7f3dabb63cde6344d767f66379aa90b17c87362d999507724758247445d005","impliedFormat":99},{"version":"1d6f7095a9b7bfd7d035a4b07f23378ba7c44993d881b75593a252f186671e51","impliedFormat":99},{"version":"952f574aeb762b9927559c9d128dccf663352aa92736ebb856d2cec80fabceda","impliedFormat":99},{"version":"c06c1379c3f1bcf21007bd9a92e8c7a8e63611387392411bbb2399c0a4c5ae04","impliedFormat":99},{"version":"0fab16fa249312e20d9a96e0464c7ae63c841b17c02401a59ccd0bcdfa67bfcc","impliedFormat":99},{"version":"027464bfd5f5d3110b7b5303ee3a09d3bd74e630393c5caef2cbfe1bb6ca59d7","impliedFormat":99},{"version":"3e9aad7dd39dc61c41d0c249427d39b3548f7ac02f2fa2e4a813c38a8e1a2e01","impliedFormat":99},{"version":"3f28c2bdd8d3da9487f032bf85ea09bf9f24f6f02ae2336cb65e6988aa92de5b","impliedFormat":99},{"version":"6a437b4b58f8b3b220f3ae8af2230bf3bdd0fa4c17db62a9a2a03fd224a68a70","impliedFormat":99},{"version":"e16749a9377888735e5edcc765da4ac2f5a552de2ef46d930039b2d54f199fb6","impliedFormat":99},{"version":"bf973f547b27688728916b64a98fcfa836772e7382211a9692684947220ad550","impliedFormat":99},{"version":"507b0e93358d09b74a0caef2370175290a47c790dbf71fb63d1b4593b7e070ff","impliedFormat":99},{"version":"eb7e05259b0603e91365983fffb6e6dc1e574f1cbcf09c51bdad4f3717869a82","signature":"5679163e510a4314da81e928dfe7e72c6671b0377ebfa606c80e18db43ad402f"},"8269474f9aca3f56fe5ff007900aed4be90d6271a628d561d20cc29de0d5576e","88b5d609cf1c008e5d7926489df81bd606581dd083772e8ca735c1c0bc103093","dd0a4bfc93ee858cf6af173c428400652c01288761e7dc00b513652d005cd91f","f35ccdbcb49becc34f1c71a68ad0d843bf02fea572cb852884d0a96ac7169830","3d1ac90c29f450b8b90705d05264fd29f1034b5ebb6c2d2e9807489969e0a33f","6cdf25b7999cac1327ad91005d4aba65743aee71a2972eba11f5b1164e39fa4d","0d96088dc89a49f328fc47dd75713536556aca01e187da7dce124fbd2f395f09","ef05bfe2ed3c6fc7575cca3a8ee25f4a4aa28878b83c5d986ee47f4483f73b3f","dcdb66da2fdcdf0e80f084c8347e2114f573d97619a5e37f680a9ad5656f614b","d322344da57346fad32f22627e0028bdfe12501d7de9eb4a103d47c9a075a85b",{"version":"2d5bb04855694db19d13174de99509d4d4799d6bf3468318bb74b54ffe994129","signature":"9a6473983696c0401765d2ba2558ef9b0670592e8d74239b4d5623c42d686600"},"dbbdee1f403eb2a952f5e8ea724cb1a20a2b4dd63d8232e375a750d4929baa88","28c7612edce38076988a57695e970654fd09467b806a8e37b38a26946afafabc",{"version":"2a40fc7affa68ec88b30b2d2d19639d8fa8dadee567da499d460257372e04ab3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"7f13d1469822b3ce57bd440274f4a8d9c2d9925fa644d284fdeeb520f0ab43fa","0ed05108ce0f4538b5351bcf9d523912d200abe5b804951cf18b2e12feff6b70","fe55ccd61eb15dd0480443215f5556cd56d9a68075a79a1294e2d9cae200d70d","7fd1557a212fb2a671abe96de2e1b6a3e5110a07c49076705cdaf9c3e0f81f7c","b9eb4fbe039e06b65ba30bb786e50fa9b48e25d7eb26c4cc1cceba3a6c81615a","7ad085c66c15e665b43f1055b09ddc1d9c11a3d8f21174c9d3d9205d7dbc03c8","68dbb99a0ef2ffb046b1385fca8116795a7b4bf5700193b7bf2fe9cfb62e72e6","ded33ba85bd4f5491a76b8972dfb3104e8b7b4e1b256c44313d7ea9d21647d2c","1f260100362d7309e0cbae29fc09c4c36be2e4512013a3f6cd4706ada09c6675","4a96c42ed30bf77b8c127453fda697a212495a055f6dcafda400e42eb233d029","f911a52096fa219660557bc3c9ccb5745889d8a357674e8ae67585678891e106","3a2cadf5506cf6c268f65051dd63ddd5cdc82f5effcf658778af2b12e497726b","d5ebf3405d09e5eb9e3316e8b6a7329bba4fa306433222f109b9af077ec77525","2e1f498ba2e37492111a97c6048e07af342ff8df126eeaac6cb99f94372f8ca7","759a24229c02dafd2cd73ad6e3325c043d16ad85081d2e76296664bd96226ae2","2fb9eaa3ddcb8952e256d1537d6edc1593dd761fa12777b9ccb88370016463fd","9b7287bd51e848b323551afe464c4a91ef2b74bf1ed703dc7c7c5e35cd9073f4","26b692cceb67ab44563761e4c5701f66b58f7ee354393088e3b338aae9918ee3","89f0ba8c9a02ad55e23f296ff35f0d1d6361a190811401c3f0d767a1aeeefec1","5aa6936f80aaf206b952e46cb830f50e49e37862c6cc4fdca99000c797995a54","1f607599e3d2f94f8bc20f8f46a594132cd1b1b1004f0a4619dcfe84f792c774","827a3da73904af54ac6cad259cbde0bf0b811d253a3fc370664cf80cb65ad6ed","ca3da68186289d0f9bd93dc9a7e5c3eb30dae2526ca55be4b106af239b63d0fd","fe17ac85f4e0b305bf44f3a4d81cb75397d9d7776aa2649ead1caf2291f0d416","22c8ef3ac26c1ded1d40c6e1c48b6382fdf0d56a5a3e77f8bcb3bb198a1f769c","c7f6c31638211891b8f6ea106d4d9ca0cfe249adba1528b2d1ebd0f267fa324e","5d513a6a908bdec9f0c3a72c4fce232063a7365983847c1ade848a9970e97aa9","bcf829509dc2bdb8f3851efb4451010e917abcdd8a52e2ae302886045c0e38f1","10ebed90f6cf7c3e637d5595d4dedc3377575aad097fd0b28e15312572c09622","0ba02535f72f0cc1712b1a073897c522f3460eb86620046f1f3b250ea79fa567","2b4b00a99c93370ce060e954393ebac6d299d87a0400b9a69307c97e8021abcc","1f1d8292770ec1d17820eda3e3ca60550d3c356d4f4832e211e9ab2ae5d5c9f7","07e108a85e9c41db3bfca18812565985fe9c4185c09e44cbfd365364daf67e80","7236d482e2ca1a6307e2182466f18dc8ee373209e5588f156b019f949cd9ece0","da35015d12dac52832201a4900b07e4b0f4fc08f282eee6e7131d895db1930c5","25e39087798255a9189bdd829787ab8bd7854afeb8f8572586e73d47b3874412","24ac88c5cd809281eca45ae09b8b16dec4318221ea50d1b47888254798a046f9","63bb37a1d958427795e2e6ca7fb9451aff711e665b45bd4878ba693da9a140cc","d8f785b87f0a4f596648fb2e5fb351d34d4bffa5288980a8eb52ee82ed27180b","b581577a6affe98a790ea707971843ad284ae4cbd4bd1dc17642196e3c0de158","3b8a02a9dda0124bf30a030727c96e3d34272b55369bc55cf55102cc90ff4a41","0eee1242c13bce68990b788037aaecdb865d63943bc7b5681b8688cbc6d64e60","a3be9843036cf2c658821504c4931c7e1055db71db2a607b9bc5d8aa7be679a2","ae3566ec3d167f05176112f609460a77863016fec3216a87a15c36bef01d370d","c914982336f64f67076a86d5569fd1b878a42f415962a07d1d151a4d65d187d2","4d5caf53ba49b41ff378943f6b1dad1ea8e042426fa713b57c9524219bbbf573","bd703e7c27cc5dd4780eba552a51a698b459c9f12dbddfeafa9e0d216a4e66b2","c09c42c93bcd3ed631ea6902d069b5798db25862e47fdf4ba5f47ff0d36b2a51","bf82447aeb19b4df2e40900f920c15695a8557392588397ce359c51b133c00df","1e4dd6a5a91d2798fa4dd70d7e6f682475942bedb36b9f870cbb15e5c1f1a54b","0a5fe133b4ef41f0f2443b3cc82c4b99be11738a93d613fd12014e9d632c2fcd","cbf3758a6cc16ff397b8a2a27221d1f6d5f053265e353af0f37b356f0384b85b","e9dc912bf8a7678feb40d7d996c9263bad3b7fdab9ad9ac95d4e4ed023403d29","4b3f5e2463bca0844de6802fe4ee8e1e29a3c11d14fdd3fa7c96ae69fba8d74e","d878f9fb504fbde395cd7c61e48f2fc7bbc7d0cf14828004f95e5f9fe64f238c","12564f17d21c31e77240293e7434c0c933945a3a3a142130eaa166d0042f5529",{"version":"3e8a4a82fa1baa0281a0ccf309c22c954488a949c13a7ab6fd7c171b1bafb361","signature":"13428a7125f291070d1c531f233c9e8719c80160dd3587d51adf86a3e41bb62f"},"b51fb5ae439f311990f5a5c2fc4914fda89e5ebd585b33d22d0251afe382d391","deb873b1dff75e59633350db7fdd9e3c125d248ac7bf2c193a81e9665bbad9a1","d9628bca2f50c1a70ef77c452fe293c91380dccd76c285dd3aa988c0f93fed8b","59c0ddf46c0d1d17e34ceb4c253a3f3bc7654c450002d8f8080476a3baaf5755","7adf0dcdc081964a00a2235aa42fd757563b15038955013b98097c5731705a2f","d9df7df9ddd168648cb6e27223b325763738bccb1b57e965ba0d8443cd166fe4","99f67ae9774e4bb88839948649b26a55dfbe8b99ec80ecab4c548102d2ddcaf3","aae8eb9b4f313c457b2f82fde10a63f117333645b818b48d2ed26fb2333ca42c","53ed7ccce63fb30e129e73dd0abff74d68929d92fbe3b20f8ade965780b2353c","3abcb2afd6c10eadcd9e5cfcffe3f93172b891ac80b079ad1421105d1574b983","ade0a7d50f110afaed90ffd5f24f1617bf9b8a0d2f118530892d139ad67f29fe","4bc4b9b2e5a66597bed3af39f456ab78cc11601400c5adb4ad46a173bd03da41","a3311c2d4225d8eacfa5a9e662811aed57fdb4de78b824f1a702311b3f99b09a","d7e17a1b90344a6d9c26f1462f77d6350a6882706064a36e5d640f5726ce49a6","ea2d922ef7dd2904b092b91fbaa35be0af427504b9fc7e14ab5fbb6cd7c40846","5456720ba13d5a5037b07c10816207ca9a81cd79a370af608115c578d61146fb","ced670d0bf8913a58d8515d3b7ffc0e9215721c717efb9349c84d4710bfa7ce7","8d419ae38254b6ecf56946523964d6561fa3f8a677ed3c21b4b5d1176a3b5a51","6a073d10bc03e721d0b1ce2620253ae9b69daa32fbebb2f7a8c67e7e1579c6f2","a80fa136e8559ddb40afdfe957e7c625614d02ad4a72476a1dd758fc31ed2e54","e79d387b4470cc2ef4df34e09b4113eb85200a7c8c6508e4a2f418c63e29ae5a","56396e7c37789adc6f28a7c461ae904c01688c2836ac6278a9f9ee864078c7b6","4025a1efab8877af2ed8d8edda349736055a800404134c6b24ee95cdfb0c0ba4","0a46d4afc023db18a3c9d7816e3f90d960122319e2765c5d686f76da661864bb","4f649bd1b169333de666f079f7989d184c27109ce26544354957f5be00abd232","f84acbbf9c1536d22e69d354fae1dc2d43430ca0e524721df713722f9e26890f","b5da1cdeaf5fc3b53aab62bbdd5da7d9385fdb2839a18fad0e3b2c31c5d888da","0e30e16f547666851c4fe51505a9feeff6508b2d720b7b8c60691e5158db10d6","1c03c99ee1b5f5bcf0704dac9398e922805929cd23a7ad246ad0d67cfc3826db","a8adc0df2bc9038f9423e9947d03f490375a9f615cc8119055af8a695bb830a5","6096174ef99bb11f2656cd3f15a2fb649e504782c6ee27090448b681e33c2b40",{"version":"987a3bc405a132b704d415e99a6708c6ea54d0a70766ecf1ae59bd13034d848d","signature":"2d44dcdbe1d297af2ef6785176a9165f4feb886490712c82ab8578ca96ee0d10"},{"version":"187610881a6b1f7370788848d0a2af5a17e94b9b437727556ef3d2fe018a98f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0709632ce350e4970bc7fdb88e656ecea4b7579875a6f20ad20cdaaad26f369","signature":"f9a530c655221c9f5a24fc3421f341b21bd38da824f7612da7c87804306eca36"},{"version":"2b3793a5342b5d7ef5498271aa50c1fd31ce56b70f70d0dc5f9da4174eb1e5cc","signature":"9ae9233a7cd435509757e52ca1503b31fc923e1bf163d9bfba847b1a7dd89e51"},{"version":"1391eb93befc7b56fcc8fc9d4c37affcb37252ce6e91400da018023fac32c807","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17b39df68c9417376ab9d3f845ad45905499eec1ee9798fcdb980cbcacbf2f44","signature":"f8c081ad7f58588db5940385eaf280c202e6ce42c0117ad62f774ffc421712ad"},{"version":"ff7b4ff43ed91708cb770527c71b00da17728615a98d59f62ffc6760381a1987","signature":"883833dec7bf0238bfbbb33db50c709cc7ca3a1f6714d17992c0d7e82a964d00"},{"version":"f56d21cb2be8cc1ca29dc2ec7c48ab92fe41d38bac18bcad6eb20b33c07c1b8b","signature":"3d655def48973efb420a82a2e05119da3a2c45672bdfc7a695f6e569edaa417c"},{"version":"3f072b168376dc71baf99d36fea4aba49a269f5852826888a3c6c95e5c9cb202","signature":"7fb20dbe5a83b73a18118cafefc659b66c75e285f8f6100023eed6218035191e"},{"version":"98ba07f2f211272213e4201fa31bbe0de1f95049cb411f0c7dec9e9de1fc8232","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d9a8784de549deea41f12c4241eb87f77fe7b7f8222ddd7a6ea05085980d5c9","signature":"88af6abc2bcc060a798687a7bc8f8bc23f47f5bb2ea736e89666093f4e682a0c"},{"version":"7bc3f411c39c03e6ef2f245fabfa4bf821920e52e5c1083759cfb2c2dc264296","signature":"e18de9a7b62fac87db7bdfab03946f00b49de5cfc11b37f39d95c1f6d05b7dc4"},{"version":"b8747ecda57b04b458af6aa127d1e438878a6695def6c91ccb0820723f71bfb1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da0f84fcd93700b4a5fbf9c6f166a6cc19fc798231bff56dd1e3875bfc6966eb","impliedFormat":1},{"version":"634ff08e0143bec98401c737de7bfc6883bfec09200bd3806d2a4cfc79c62aaa","impliedFormat":1},{"version":"90a86863e3a57143c50fec5129d844ec12cef8fe44d120e56650ed51a6ce9867","impliedFormat":1},{"version":"472c0a98c5de98b8f5206132c941b052f5cc1ae78860cb8712ac4f1ebf4550ca","impliedFormat":1},{"version":"538c4903ef9f8df7d84c6cf2e065d589a2532d152fa44105c7093a606393b814","impliedFormat":1},{"version":"cfcb6acbb793a78b20899e6537c010bfbbf939c77471abcdc2a41faf9682ca1a","impliedFormat":1},{"version":"a7798e86de8e76844f774f8e0e338149893789cdc08970381f0ae78c86e8667f","impliedFormat":1},{"version":"eebc21bb922816f92302a1f9dcefc938e74d4af8c0a111b2a52519d7e25d4868","impliedFormat":1},{"version":"6b359d3c3138a9f4d3a9c9a8fda24be6fd15bd789e692252b53e68ce99db8edc","impliedFormat":1},{"version":"9488b648a6a4146b26c0fd4e85984f617056293092a89861f5259a69be16ca5c","impliedFormat":1},{"version":"e156513655462b5811a8f980e32ccd204c19042f8c9756430fe4e8d6f7c1326e","impliedFormat":1},{"version":"5679b694d138b8c4b3d56c9b1210f903c6b0ca2b5e7f1682a2dd41a6c955f094","impliedFormat":1},{"version":"ca8da035b76fb0136d2c1390dda650b7979202dbe0f5dc7eaefcde1c76dee4f4","impliedFormat":1},{"version":"4b1022a607444684abeee6537e4cace97263d1ef047c31b012c41fdc15838a79","impliedFormat":1},{"version":"dd0271250f1e4314e52d7e0da9f3b25a708827f8a43ceff847a2a5e3fd3283e8","affectsGlobalScope":true,"impliedFormat":1},{"version":"47971d8a8639a2a2dd684091c6e7660ec5909fed540c4479ca24e22ac237194e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e1075312b07671ef1cbf46409a0fa2eb2b90bb59c6215c94f0e530113013eeda","impliedFormat":1},{"version":"1bfd63c3f3749c5dc925bb0c05f229f9a376b8d3f8173d0e01901c08202caf6f","impliedFormat":1},{"version":"da850b4fdbabdd528f8b9c2784c5ba3b3bedc4e2e1e34dcd08b6407f9ec61a25","impliedFormat":1},{"version":"e61c918bb5f4a39b795a06e22bc4d44befcefd22f6a5c8a732c9ed0b565a6128","impliedFormat":1},{"version":"ee56351989b0e6f31fd35c9048e222146ced0aac68c64ce2e034f7c881327d6d","impliedFormat":1},{"version":"f58b2f1c8f4bcf519377d39f9555631b6507977ad2f4d8b73ac04622716dc925","impliedFormat":1},{"version":"4c805d3d1228c73877e7550afd8b881d89d9bc0c6b73c88940cffcdd2931b1f6","impliedFormat":1},{"version":"4aa74b4bc57c535815ae004550c59a953c8f8c3c61418ac47a7dcfefba76d1ba","impliedFormat":1},{"version":"78b17ceb133d95df989a1e073891259b54c968f71f416cd76185308af4f9a185","impliedFormat":1},{"version":"d76e5d04d111581b97e0aa35de3063022d20d572f22f388d3846a73f6ce0b788","impliedFormat":1},{"version":"0a53bb48eba6e9f5a56e3b85529fbbe786d96e84871579d10593d4f3ae0f9dba","impliedFormat":1},{"version":"d34fb8b0a66f0a406c7ce63a36f16dda7ff4500b11b0bd30a491aa0d59336d1f","impliedFormat":1},{"version":"282b31893b18a06114e5173f775dd085597ca220d183b8bd474d21846c048334","impliedFormat":1},{"version":"ed27d5ce258f069acf0036471d1fbb56b4cb3c16d7401b52a51297eca651db62","impliedFormat":1},{"version":"ec203a515afd88589bf1d384535024f5b90ebe6b5c416fb3dcca0abd428a8ba4","impliedFormat":1},{"version":"32a2a1374b57f0744d284ca93b477bd97825922513a24dfe262cbf3497377d96","impliedFormat":1},{"version":"a8b60d24dc1eb26c0e987f9461c893744339a7f48e4496f8077f258a644cffab","impliedFormat":1},{"version":"3f9df27a77a23d69088e369b42af5f95bcb3e605e6b5c2395f0bfcd82045e051","affectsGlobalScope":true,"impliedFormat":1},{"version":"9fd080a9458c6d6f3eb6d4e2b12a3ec498d7d219863e9dca0646bdee9acce875","impliedFormat":1},{"version":"e5d31928bee2ba0e72aeb858881891f8948326e4f91823028d0aea5c6f9e7564","affectsGlobalScope":true,"impliedFormat":1},{"version":"9a9ba9f6fd097bb2f57d68da8a39403bbe4dc818b8ccd155a780e4e23fa556f2","impliedFormat":1},{"version":"e50c4cd1f5cbce3e74c19a5bbf503c460e6ae86597e6d648a98c7f6c90b596dd","impliedFormat":1},{"version":"fa140f881e20591ce163039a7968b54c5e51c11228708b4f9147473d06471cf5","affectsGlobalScope":true,"impliedFormat":1},{"version":"295eca0c47be1191690fd2fe588195fff9d4dc43852aceb8b4cab2aa634579f0","impliedFormat":1},{"version":"59ee7346e19b0050508a592702871dc943083c6dcb69a47d52e888115d840781","impliedFormat":1},{"version":"067712491fb2094c212c733dd8e2d56e74c309a9ce9dac9e919286b7245a1eb4","impliedFormat":1},{"version":"a5eae58ac55bd30c42359e4b01fb2be5eddac336869d3f04ffb4daa54b58f009","impliedFormat":1},{"version":"d12d691ef8933e8db39f2ca81d6973940ff5e37bb421752f5b6e7bc15dea3abf","impliedFormat":1},{"version":"4c5f8bd9b3a1aae4e4fddfee41667e495a045f73ed603993038fa6a8ba92fa14","impliedFormat":1},{"version":"dfb274ab0f319cf18ce7152067c25f984c7fd1924fc72b3f66734588444c934a","impliedFormat":1},{"version":"108c8c05cbc3fbbbd4ff4fc0779c9bef55655c28528eb0f77829795dc9f0b484","impliedFormat":1},{"version":"a7e5444d24cdec45f113f4fb8a687e1c83a5d30c55d2da19a04be71108ad77bd","impliedFormat":1},{"version":"41ec17e218b7358fcff25c719bc419fec8ec98f13e561b9a33b07392d4fec24c","impliedFormat":1},{"version":"23c204326746e981e02d7f0a15ab6f8015f9035998cb3766c9ddbf8ea247aea2","impliedFormat":1},{"version":"25f994b5d76ce6a3186a3319555bbba79706dac2174019915c39ac6080e98c7e","impliedFormat":1},{"version":"dfa4e2c6a612d43851ccbc499598cb006a3a78bc8c7f972c52078f862fa84e47","impliedFormat":1},{"version":"02c1705fa902f172be6e9020d74bcd92ce5db8d2ef3e1b03aabc2ac8eb46c3db","impliedFormat":1},{"version":"99d2d8a0c7bb3dd77459552269a7b5865fa912cedab69db686d40d2586b551f7","impliedFormat":1},{"version":"b47abe58626d76d258472b1d5f76752dd29efe681545f32698db84e7f83517df","impliedFormat":1},{"version":"3a99bbbbbf42e45c3d203e7c74f1319b79f9821c5e5f3cdd03249184d3e003ce","impliedFormat":1},{"version":"aaacc0e12ab4de27bdf131f666e315d8e60abec26c7f87501e0a7806fc824ae6","impliedFormat":1},{"version":"3b4195afd41a9215afc7be0820f8083f6bd2e85e5e0b45bb0061fb041944711e","impliedFormat":1},{"version":"108df8095f5e25d7189dd0d1433ac2df75ec40c779d8faf7d2670f1485beb643","impliedFormat":1},{"version":"ddd3c1d3c9ff67140191a3cf49b09875e20f28f2fc5535ae5ea16e14293a989b","impliedFormat":1},{"version":"7b496e53d5f7e1737adcb5610516476ee055bf547918797348f245c68e7418fe","impliedFormat":1},{"version":"577f44389d7faedd7fc9c0330caf73140e5d0d5f6c968210bff78be569f398a7","impliedFormat":1},{"version":"3046c57724587a59bceefadd30040d418e9df81b9f3cfd680618a3511302ed7a","impliedFormat":1},{"version":"15ccc911ed15397e838471bfe6d476c28deffe976c05cb057e6b1ea7491242c2","impliedFormat":1},{"version":"64b5a5ebdaead77a9a564aa938f4fb7a45e27cda7441d3bee8c9de8a4df5a04f","impliedFormat":1},{"version":"a48037f7af5f80df8973db5e562e17566407541de284b8dadf1879ea3aed8a2f","impliedFormat":1},{"version":"dab97d96ce986857150db03f0d435b44c060d126b4a387c7807f4e9f6c92e531","impliedFormat":1},{"version":"85f39366ea7bc5e34b596fc97de18a7e377856755e789d8e931054f2191d9b8b","impliedFormat":1},{"version":"daf3ea3d49f6e8a2fa70b7ca1f21bd97f1b65021b31fbfccb73dd55f86abb792","impliedFormat":1},{"version":"b15bd260805f9dd06cd4b2b741057209994823942c5696fd835e8a04fb4aab6b","impliedFormat":1},{"version":"6635a824edf99ed52dbd3502d5bce35990c3ed5e2ec5cef88229df8ac0c52b06","impliedFormat":1},{"version":"d6577effa37aae713c34363b7cc4c84851cbabe399882c60e2b70bcbb02bfa01","impliedFormat":1},{"version":"8eaf80ad438890fe5880c39a7bbf2c998ce7d29d4c14dd56d82db63bd871eefb","impliedFormat":1},{"version":"9b3e7f776f312c76ac67e1060e5398d7ac2c69d6a3a928a9daaae2eb05b15f56","impliedFormat":1},{"version":"202042eccb4789b7dee51ba9ecab0b854834ea5c1d6a3946504bfc733d4468c3","impliedFormat":1},{"version":"2b2ef76a9f36094b07ee6f76a5ac6903f2f65c0a20283201814a8d1e752cb592","impliedFormat":1},{"version":"8882e4e087d0bc8cc713cb3d8090c45d33e373e6f5c83e0f8d00fe6a950ef875","impliedFormat":1},{"version":"baab78e9401b9a82e8fb3634de0d3750cbf4d3d6afb59ba28472ab15afd3a749","signature":"3d01773dca02fddc18139d243b8adbbe1c6c6447b8235bdc3cbbd4b493e7ffbd"},{"version":"1cce0ed0784dfa68a0572c20ceb1a173664dbb3ac59eead22d55be246fdf17d9","signature":"5de2fd3f978ef1724ed1d72271f8d9bd911d19d80a709137225e173127e3c615"},{"version":"57e3b4916970da260c692cda82bc670552fa93563710da8485ef3f1a40fc0cd8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"24772aec0e0f59dd17a2a1c4a924fa4fc228f24c64bee4fee8c5c08965f05925","impliedFormat":99},{"version":"e636cb7d61143bd3901daa91d1c2c3d8a53b677f6bfe51fafbd2d14a51efdfd0","affectsGlobalScope":true,"impliedFormat":99},{"version":"d33b19c5e9b2f8b26b1875c7aa12229cdd1e3ff0e809c89189805a05c626dc3f","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"077cd7acbb4a3b50b4a01690d6a7d2583ebb39335f612763442a4d33dde01c36","impliedFormat":99},{"version":"8b9ab1d118cd0092e03b36d26b83192c6374c30e16abb7cbd0ad33979fa0c2a7","signature":"a3a467223e1b0d6dafe7ba2a535de44efc5aa9438c3b277566336031e5cd3f4a"},{"version":"db1d1a51416710f03d5b33f8ba166c677f7a372d7236d0d75857abcf2c46d869","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"cf0f4d9e5edafe3f777e151b9719afb37ca6abaa904fb8289367fe99913f0ad1",{"version":"03493681f3175378f73cc1994441b55eb2f178585c613377853cdf5dfb39ecc2","signature":"4540e50e72fce7b0cbb91e773d9c3ace94268cad237d1a032f294b9786a348b1"},"59e8ed7fe97a22a7e83c915d37eb2494f0eb416d7a52d0050824d718d0ed8cdd",{"version":"360a32be2efd3b572bbe4f092987fc0c27de1edbcba3a5a99b9eeb67efa91d62","signature":"bb97d8ce68b8061ea36f60acf43e82f3560c4e53522b05949721c6b7d014d89a"},"755907e327ad953500fb7ae52e0dc7dedceb54626942f3af04eaf1cbf20526b5",{"version":"8d096c0b73874d64e56e5fe99fb94f14b5a6fc0824a4a1cc0251147a823c25a3","signature":"06980b548bb6ff2b15c92032296a46d7f80d3e8ad9af172f5c2ccdefa86b3fb9"},{"version":"d330961532fa59192f0330dd430076475d3a3f5cbbb60c2ba196351c069243ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34ef539c1384da9fe858fb9c16368ddf1624a4914dc3f0fabc7a39811bcd2668","signature":"ed6cfc1cf330cbbc602b7a0305aebcef220219fcd5cd3e4493e5afe79058fcee"},{"version":"f7409e1093e57b3f7be327a71c71087e1f7a767333fc10cff7c2504af7222f88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b203b12c30dae8bb84ff0f770a857445942d8c9ec8d6ee472c3ca6b0b5b261d0","signature":"9fc50c5741ded49943ed4b81fc428d0aaa18cefc596400fdb71fdd11a21d8d8c"},{"version":"24803211766a60a0fca7ca541d6a158d376c65f7ae4827a8661063c2e70c06e5","signature":"6e2681371e356de1c4fa982616bd1d26b7c525b5ca9f75e1f688b83332e8a81f"},{"version":"1453393d564bcb47dd35ada6b469c661ef5d6c98f9dcbd7bc0f9eee3470ac944","signature":"22462cc125563699336669ccb959793d6c462626957a1da4ec4a639d4341fb3c"},{"version":"7c56faad4a628f9671b73a1227c941f930b55649699ac62931e360389775edff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"579a472a70260bf6b40d68aeffa65bf00fa5c59a44bf214f2385dce399f5898c","3fa0c656d89d31fa67f25c8b3eb9974b1aab6fd8fe5ae0fafd521e7d6808f71c","ad5ab4bc064063efa662328e47c8eb39c80888a25467fefd231a32e874162d6f",{"version":"f0e29314c7c3239de9961d460d5347081cc6e49bf065dd0a6cca6e7132a99ee9","signature":"ccb2b3ebb1fa7bd3fa3e02c7a23ecfb2ebad06df9c3c8a9e685113d81026d0bb"},{"version":"614e5cdc3c5f89f035510a0c61652fdeba39a62c9904acde7de79fce2d60bfe3","signature":"bfe40cd3dd4d0d35754643dbf07ecb96362953f4fdb490803e122593e679db64"},{"version":"36e5bb11081348bd0869d683fadc9a4115fb28720594bdf185a13ff19faac88d","signature":"d356e9c1bad769f9e8d358a35c420cc37a0ad01ea4f865d968f3b7fe10c9c3de"},{"version":"4d46dcb6027f62db92924103d77c199455ed38d1dc1c6c29bb65a707c25f847e","signature":"14084429a03e8974f38aaa1890d6a80ea62d5c8f0b627e0f824afb0ac621a65c"},{"version":"1d5a2531ab660f7a4f8b4572b7a19c23fc5a431299ecb8c1846cf8e279b97851","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d6a819db1ca73bc3e3b29597b18ca5c36e2dafa02b171e8bb92208b2d50b353","signature":"1571b1b7546d0267d42d0c0b3e1e4593b2ef990541b260a9652427dd82758bb7"},{"version":"a62a7e45347ca1dc80dd4df8d2094790870c03b1c890960cce9628934f695295","signature":"af0b9205baeb5b5d2e4470da33f26673b5c363db0ccfe761276f8af34cfe3e9b"},{"version":"4217680981bab6d62ee8fe0cbac591599bf18f30cf2be39170d344eca5f7885f","signature":"5d3ccf27d7ce9e5f390fa882da69e253103b64bcc4e1af716d4e385d1f7dea5f"},{"version":"0571fa29dd502778997d9453169040a83607ae311f6ab6a7ce90fdaa83f86a72","signature":"6abb8469a763dfe1299c79302eb5559ecc978df41c92c0444a30c1b55710860b"},{"version":"afaea598c95b1ad2bd0ed30bfb6ad867d78bf021b73a048b8fdaeb90cff22d88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1bddf61aee12dc67fa70b5e40a9124f1f71f960b6bfe006fe4078273a7f77e5","signature":"77307295274cc402aca163afe863f0ae8a1d2e94588f2acd35ec24d77af97b75"},{"version":"d579bdba0db63d615c541195af19b783527c13bbcc870e83ea61d198e3be56c6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10412b70545a4b21b51229be0a35ddb2bdff35e164c35e214f5f56baf863f12a","signature":"8aa83e11d68ccfd7360e5de9cb82c18ce6dd67f9c7dd89c81dc67e55dab60864"},{"version":"ae6af43f3746a31699410d070400739e19ef2281ccaba80b5e4b1c4dd4ce644b","signature":"1fd318722bb7bd17560dd18e11b824febc7904ec355636c7e042efcf18859c12"},{"version":"4e92e1739b0c23ae5b37b5dd14b72caf2adf6d0abc18d73596b627d63efd0f3e","signature":"69db250a4e6aaf0f4ed86907855c93327ae8e663b3b1fdb429fa0405ee768215"},{"version":"7aa764a1146707f1b9e18292969e24f394ef3c347d4f396cc6f90d39c3f3b6da","signature":"7c52c6c55104753b3519528829004136bfbe6e76535ec0a19430668fabd41269"},"688c5e58ff9137a2c5d6eb1a79475ec4c9d61c34bb10080e21d09babaa30ae1c","c51f1961b6b22a86183d4e8e166a4b08df4cf3537533f9249a79d3b460efe6ab",{"version":"13cc0e63b3212f43a760f9618ac9a5a26a3123954baa408ada44dd9744d060f4","signature":"6d2de774f7f1930f5a1a0061d45b779777be9dc7e6125661388a11a43f386636"},{"version":"693212d0a67ee305c09bfdd670455ad335e448c9fd52fb8c69ecbcda23eb2b93","signature":"225d95d4c8f9caffa003ab70fa3ac2d8b66e4bca291dc775b2d1ad4b676b660d"},{"version":"41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","impliedFormat":1},{"version":"e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","impliedFormat":1},{"version":"e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","impliedFormat":1},{"version":"a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","impliedFormat":1},{"version":"2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","impliedFormat":1},{"version":"6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","impliedFormat":1},{"version":"a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","impliedFormat":1},{"version":"cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","impliedFormat":1},{"version":"33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","impliedFormat":1},{"version":"cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","impliedFormat":1},{"version":"f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","impliedFormat":1},{"version":"f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","impliedFormat":1},{"version":"261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","impliedFormat":1},{"version":"e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","impliedFormat":1},{"version":"9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","impliedFormat":1},{"version":"d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","impliedFormat":1},{"version":"69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","impliedFormat":1},{"version":"fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","impliedFormat":1},{"version":"1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","impliedFormat":1},{"version":"4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","impliedFormat":1},{"version":"dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","impliedFormat":1},{"version":"bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","impliedFormat":1},{"version":"7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","impliedFormat":1},{"version":"54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","impliedFormat":1},{"version":"7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","impliedFormat":1},{"version":"b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","impliedFormat":1},{"version":"a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","impliedFormat":1},{"version":"6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","impliedFormat":1},{"version":"f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","impliedFormat":1},{"version":"325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","impliedFormat":1},{"version":"79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","impliedFormat":1},{"version":"e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","impliedFormat":1},{"version":"edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","impliedFormat":1},{"version":"511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","impliedFormat":1},{"version":"496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","impliedFormat":1},{"version":"d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","impliedFormat":1},{"version":"67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","impliedFormat":1},{"version":"e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","impliedFormat":1},{"version":"2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","impliedFormat":1},{"version":"1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","impliedFormat":1},{"version":"3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","impliedFormat":1},{"version":"d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","impliedFormat":1},{"version":"8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","impliedFormat":1},{"version":"e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","impliedFormat":1},{"version":"ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","impliedFormat":1},{"version":"7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","impliedFormat":1},{"version":"fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","impliedFormat":1},{"version":"652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","impliedFormat":1},{"version":"790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","impliedFormat":1},{"version":"89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","impliedFormat":1},{"version":"9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","impliedFormat":1},{"version":"8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","impliedFormat":1},{"version":"d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","impliedFormat":1},{"version":"d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","impliedFormat":1},{"version":"fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","impliedFormat":1},{"version":"af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","impliedFormat":1},{"version":"784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","impliedFormat":1},{"version":"e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","impliedFormat":1},{"version":"a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","impliedFormat":1},{"version":"96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","impliedFormat":1},{"version":"0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","impliedFormat":1},{"version":"72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","impliedFormat":1},{"version":"cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","impliedFormat":1},{"version":"bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","impliedFormat":1},{"version":"b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","impliedFormat":1},{"version":"2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","impliedFormat":1},{"version":"01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","impliedFormat":1},{"version":"bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","impliedFormat":1},{"version":"a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","impliedFormat":1},{"version":"affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","impliedFormat":1},{"version":"3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","impliedFormat":1},{"version":"a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","impliedFormat":1},{"version":"348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","impliedFormat":1},{"version":"235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","impliedFormat":1},{"version":"ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","impliedFormat":1},{"version":"15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","impliedFormat":1},{"version":"d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","impliedFormat":1},{"version":"7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","impliedFormat":1},{"version":"96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","impliedFormat":1},{"version":"5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","impliedFormat":1},{"version":"d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","impliedFormat":1},{"version":"fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","impliedFormat":1},{"version":"cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","impliedFormat":1},{"version":"24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","impliedFormat":1},{"version":"7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","impliedFormat":1},{"version":"461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","impliedFormat":1},{"version":"829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","impliedFormat":1},{"version":"162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","impliedFormat":1},{"version":"4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","impliedFormat":1},{"version":"3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","impliedFormat":1},{"version":"5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","impliedFormat":1},{"version":"96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","impliedFormat":1},{"version":"e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","impliedFormat":1},{"version":"63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","impliedFormat":1},{"version":"adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","impliedFormat":1},{"version":"fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","impliedFormat":1},{"version":"e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","impliedFormat":1},{"version":"8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","impliedFormat":1},{"version":"811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","impliedFormat":1},{"version":"f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","impliedFormat":1},{"version":"c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","impliedFormat":1},{"version":"e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","impliedFormat":1},{"version":"dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","impliedFormat":1},{"version":"41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","impliedFormat":1},{"version":"f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","impliedFormat":1},{"version":"970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","impliedFormat":1},{"version":"8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","impliedFormat":1},{"version":"e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","impliedFormat":1},{"version":"cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","impliedFormat":1},{"version":"6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","impliedFormat":1},{"version":"cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","impliedFormat":1},{"version":"db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","impliedFormat":1},{"version":"aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","impliedFormat":1},{"version":"298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","impliedFormat":1},{"version":"a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","impliedFormat":1},{"version":"341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","impliedFormat":1},{"version":"238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","impliedFormat":1},{"version":"8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","impliedFormat":1},{"version":"bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","impliedFormat":1},{"version":"e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","impliedFormat":1},{"version":"3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","impliedFormat":1},{"version":"f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","impliedFormat":1},{"version":"e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","impliedFormat":1},{"version":"ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","impliedFormat":1},{"version":"69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","impliedFormat":1},{"version":"d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","impliedFormat":1},{"version":"eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","impliedFormat":1},{"version":"a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","impliedFormat":1},{"version":"76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","impliedFormat":1},{"version":"815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","impliedFormat":1},{"version":"afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","impliedFormat":1},{"version":"b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","impliedFormat":1},{"version":"54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","impliedFormat":1},{"version":"65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","impliedFormat":1},{"version":"969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","impliedFormat":1},{"version":"b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","impliedFormat":1},{"version":"3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","impliedFormat":1},{"version":"4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","impliedFormat":1},{"version":"5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","impliedFormat":1},{"version":"98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","impliedFormat":1},{"version":"355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","impliedFormat":1},{"version":"8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","impliedFormat":1},{"version":"dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","impliedFormat":1},{"version":"71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","impliedFormat":1},{"version":"49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","impliedFormat":1},{"version":"d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","impliedFormat":1},{"version":"4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","impliedFormat":1},{"version":"5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","impliedFormat":1},{"version":"149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","impliedFormat":1},{"version":"3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","impliedFormat":1},{"version":"ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","impliedFormat":1},{"version":"394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","impliedFormat":1},{"version":"19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","impliedFormat":1},{"version":"728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","impliedFormat":1},{"version":"8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","impliedFormat":1},{"version":"de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","impliedFormat":1},{"version":"8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","impliedFormat":1},{"version":"4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","impliedFormat":1},{"version":"ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","impliedFormat":1},{"version":"de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","impliedFormat":1},{"version":"c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","impliedFormat":1},{"version":"fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","impliedFormat":1},{"version":"7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","impliedFormat":1},{"version":"29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","impliedFormat":1},{"version":"7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","impliedFormat":1},{"version":"4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","impliedFormat":1},{"version":"d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","impliedFormat":1},{"version":"665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","impliedFormat":1},{"version":"3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","impliedFormat":1},{"version":"4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","impliedFormat":1},{"version":"7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","impliedFormat":1},{"version":"9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","impliedFormat":1},{"version":"726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","impliedFormat":1},{"version":"05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","impliedFormat":1},{"version":"134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","impliedFormat":1},{"version":"264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","impliedFormat":1},{"version":"98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","impliedFormat":1},{"version":"1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","impliedFormat":1},{"version":"54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","impliedFormat":1},{"version":"60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","impliedFormat":1},{"version":"236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","impliedFormat":1},{"version":"e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","impliedFormat":1},{"version":"4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","impliedFormat":1},{"version":"4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","impliedFormat":1},{"version":"7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","impliedFormat":1},{"version":"b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","impliedFormat":1},{"version":"a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","impliedFormat":1},{"version":"724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","impliedFormat":1},{"version":"b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","impliedFormat":1},{"version":"3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","impliedFormat":1},{"version":"62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","impliedFormat":1},{"version":"656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","impliedFormat":1},{"version":"b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","impliedFormat":1},{"version":"2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","impliedFormat":1},{"version":"2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","impliedFormat":1},{"version":"080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","impliedFormat":1},{"version":"7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","impliedFormat":1},{"version":"07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","impliedFormat":1},{"version":"79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","impliedFormat":1},{"version":"8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","impliedFormat":1},{"version":"4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","impliedFormat":1},{"version":"4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","impliedFormat":1},{"version":"87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","impliedFormat":1},{"version":"4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","impliedFormat":1},{"version":"e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","impliedFormat":1},{"version":"1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","impliedFormat":1},{"version":"c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","impliedFormat":1},{"version":"e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","impliedFormat":1},{"version":"973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","impliedFormat":1},{"version":"5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","impliedFormat":1},{"version":"85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","impliedFormat":1},{"version":"88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","impliedFormat":1},{"version":"8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","impliedFormat":1},{"version":"ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","impliedFormat":1},{"version":"997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","impliedFormat":1},{"version":"aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","impliedFormat":1},{"version":"1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","impliedFormat":1},{"version":"25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","impliedFormat":1},{"version":"87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","impliedFormat":1},{"version":"56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","impliedFormat":1},{"version":"ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","impliedFormat":1},{"version":"d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","impliedFormat":1},{"version":"fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","impliedFormat":1},{"version":"35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","impliedFormat":1},{"version":"b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","impliedFormat":1},{"version":"beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","impliedFormat":1},{"version":"2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","impliedFormat":1},{"version":"da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","impliedFormat":1},{"version":"17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","impliedFormat":1},{"version":"a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","impliedFormat":1},{"version":"bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","impliedFormat":1},"6bc9832d675edd15ca0c8e096cc4008e2791d822cddbe218e7fe65d33de8fa2e","baad5518e27c0ff3bc6192606a3c70d64e52338ecf1a1492a3582c9e8827a7bf","a418d3e5729d2bc1f21789a3926a6e5db364e9f80410207f4eb28b55a5c70cff",{"version":"71dde8ef5faa2b2f5f4a8f56944429ff768600489ba021017b68473c93660eab","signature":"b3a61d1bb2c4eff882c25e5284189e1934aeb4af535fdb36694fc461cf4b7068"},{"version":"0b791c213954a91e7d80daceb4b7d7b53600a731e2227d3541d88a09fcea1621","signature":"b6e882b417c55fc40bb0b42ad061d8f97bd0b2fdbd2aec5aa2aa257420c7c2ec"},{"version":"c041ad3802a420609f6fbb3200a946b897838cb21b76e176e78b0cafa83698bf","signature":"63b2f936da9faea8c52723d6b78ca09ee0bb769833160bbcb000be8b7cb456ac"},{"version":"9d0c46e2b8776a71972db76904d933f54d190601cbd57b82438254c275808ccf","signature":"95bee50322d4d787b4a886030c691a2317aca49f557c115e52f95938343f65cc"},{"version":"df804257d254a2e00d640a55eefb2ae628da95dba0085ca824a04ea3ff69ac99","signature":"2302a6d37e153539b259b1f3bda1c10d344984b15efa30ea39ff5c83b5825977"},{"version":"74226e280a2991fdeba3808665dcce17f87736137ca79404c5d8d7c668eec8df","signature":"b4e98fe21b2b7cca7ccabb5169df346479a0e3bb6bf46c25946174977917d316"},{"version":"bb520dd5abb511ac234e88f420dfbfba03a6ef74a9c783850bddd833b8235b23","signature":"80923ea73f37baf4c06eb870a02060c68149fc8a8e47daddf6b55af1047d2b0c"},{"version":"372db055c8310930dcb90ffa00df06b44ac8e725c75e0c172786676ea6a11794","signature":"6179a86622a28cdafe5d99fb99e1ab06b1a06011bb9a8ae9d65d4697e61d5316"},{"version":"0d7213f8b71376061118e4f91a6faac51b38b372ad171b4df50bf3559ac2b3c0","signature":"37a718acb4d240ce0d45b7082a821b9bc8d9c523df47980aed0547887beafcb0"},{"version":"b858c7849e256828563264a2345354ac829be7d7afc77e2c04f7683b81ccc79d","signature":"e724140889de1a68b6fac45652942a37dbe94d0951da3249196817e9004181c8"},{"version":"a4c991c3fa2bc9437a6d84cd1b2557b904adf57f6e7098943d65a01b8c57acc7","signature":"4fa89a082213215027fc85892fd3a42bf898e652eded33469c2c31a75cc7db12"},{"version":"9da149c4fd78a4ccab4e68a54c0cec7c3bbe48163c7e4cb550569bbca603ddd4","signature":"f50138c9b21bb7d4b52c5bcb99ff08cab9112b3fa3eb67ba583c1c033f5658fc"},{"version":"baa1b838cd0e200f302fa49ee523d8f74fdf7a16c6d14a121621aec564cc92a1","signature":"fe0fcdbbfc40a17d638651589c6fdae7c4d56ed10a0bf9e04dc47fa42b94ead4"},"ea99af3c9a22cee8ea6b5754cb9d29c7076588361543519e0a95675747e2c17b","9bd02ddf990e7c7a97c5b70d4357ed3d8ca8f7bc5061615ea7b6d36f2e469ab4","0bdaf3b9ac7dea3986c57c39de9ded3d5d4508776b840dc0764ae0dae7fec9cf","36817a296ae92afafd90b250316bb568a39791e1fbcc47b0ceda39b7c19cf358","eb3f998132c1ee368d9196be6771f374f6b809b6693f1f6a75be7118cca56145",{"version":"5a11dbf49dfc0bacac057085c4c818507b8613630a6080846a330caa09f40a1e","signature":"887a929e952df6c08de135d3c73360dd80e833b99706ce3aef0c8b64b26ce68b"},{"version":"a128283ceced70086ed7a99436e55575c7d385f95ec1937b86e2d7c725c6e532","signature":"bb7d350c5b0c764dc29222248163f61f8540997db099b636c993ce1ec6981018"},{"version":"954eafdd8e119ae7fd13c652d092ac62f95a3c450127f9bf2c4235b9a5550f9a","signature":"7515a48dc017014e10b59b93449b24053cfe2f6cbec7424292ff04fb29f14569"},{"version":"6d9e1b7a1fa967fb8505a5fa33073efb38aec5e7b75f2dc6383c9f84f3b5c0ba","impliedFormat":1},{"version":"0462956c97fcc2f9a0f7a498600008751aae2b004f8ab4da34af41eb2fb5317d","signature":"8c23d09073975011bf5b8adde26ee58c4c5c27b5c4cc656a32313963f3388846"},"8e03d5a09d01f13dd41c86a4930ad9562cc270cfff625eebc6a261b27cfbeed2",{"version":"40c9ed5a63b54bd64ea351b5d853e67c373d730a8243e2fad4757eab3ec5ab8f","signature":"26ab3593d88f84d8250fde332b61ef8e6c9331bf4da6e89698ed83e95c57f7ee"},{"version":"42858b5e9f40b8a0b2f860a6304d779419ecf0c8773f6cf498c882bcd9aae1fb","signature":"2f55fd6804783792ef44c4afb78fd8a5d6a2810a4c02007e53ded6f01e24b521"},{"version":"819a9152da954b548e16204dfbcd75208938e5e1a21464998d2d155c14f08f64","signature":"b6ea2388d7e17effc8c7a702bd5e736213f77468d04bda4d8871a07ff6b191a0"},"7d57f62963f7f76d3e4604f86fa9e7fd005e3e11bc81490b32193dd9b3f019e4",{"version":"d6f4b9a418add129da3898008b6036a68dab54ec3997b04dd0bee2534d59a2fc","signature":"2bde30c5f8e10d5820a401c68e63e2b81a23077352c01977a10cc10a15f266f9"},{"version":"fc381b272ffe38fb6844f6885fb858ac719c2ef6e7bdd79f0b18d6fa4b708850","signature":"4b3f4c9228ab5427308c651bd192f9f627d6e7465d7e3eaf63422d09e1a2e187"},"1d4e5a6bac0b4de345e9261395c7dde4f5788f2bf2a96734b0fcd153d83284bc",{"version":"a67823a8d4a16991b3653dda2eb722a15efb2762dec299662a23322bf2394e43","signature":"419996c73365008124de6ed63224ae81323de45079174bcac6b0dbdfc0108b44"},"2076d2cf1cdaeaeb896e27ec77082c91b5e485d297935597e76c8fec1c08e39b","a84795af5152dc3fc5782eedd4031079b9753301847f158ca3c979e551a4ad34",{"version":"92dc4e8b3d0e8dea1f5abbe30adfd3910a7be441c12ed6fadc738adb59f9bb2c","signature":"322baceb1c9f45aedb9e5100ebbbedd07a164fc03ab9e98c462e32b41cbdc90a"},{"version":"7da7e22f14ac9b7b4eb1f6628c33c7131d508ab0483dc146032aacbd35670b02","signature":"fe0cea76d1c6a718447bcd594059ac0a4c7f60b9452227e9bd6af7d564519f45"},"673b4a9dc7a23138c3dcb75f1a77cccf8d2a11167df9587286d6140b2c60499c","4ccc411eab7ab26ee65e6796dc137a43eb6d3145e6c616cc3fba32bd3901c240","c37155416601c041802206333c2537b309d8031770da2717d9ebbc0fbc0f1527",{"version":"6d870a6ebc4af392ccd2875f99d1dd4c90db167636e4b267de2e1d3ff14a6104","signature":"d02c70b928a2b77ba435d387595ed3b27e4466e3115d7b71a79c36c592238798"},{"version":"2f79a63626adba271a461cbaa34c976c26be8e474095b1bc8a80ce6e4fc68536","signature":"019c10e3a4d1413779d87a055ffea70d5dfd127b4b65a51cb2e20fca9e8f1f66"},{"version":"ef68d70baf9635137535142e9df63e515a9b8bdcf9906d6edaaf0e93313ac3aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1d17705658519c7d57ce6c5cf95f0c894c2dd754ad1fcaeb1e2c4207d54e6e5","signature":"0bdd8b0d06cc0910bf6a36f2bdf87794634ab7014e178c7a7eb928437c6e5b76"},"688842a137a6cf51df9153af0452d1099fa559ac47504178827954a0957f4d1b",{"version":"bc23a9eba2c69e497917dca9118a1a1169c27b9c527693802899955e9874789c","signature":"077309ec211d24c291b6f2483550990121454d5ee75109b3802b3c82d966557f"},{"version":"3fd930ef5d29ec40a3b52a43571be8356a95cdc215ea8da402feb0e67daf57c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"697256913297b09bb0b83b40de56ad875baa198a0d1c03b8bd9a8f8f77c2e500","signature":"bce98e573080ea97bd3c360011d50db0affee86bc74443866897c0061708072b"},{"version":"3b455aef3a8f1084aec20cf655ea99e1f68620df4c3f6071e8eed404a1c379f7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"54dc720078a1e4b9dee72d9535dfc939609e8bd23bc5d58201980f5a302cd7e6","signature":"76964f1fd067c7ecd79d2dc18affd81fb2f0148dce268546b64bb6cb0cad859b"},{"version":"2c17c6e842123c5c921ba98cee5bd3886f3eeffd42eb3011819cf99cb5b02ebb","signature":"fad4e252103942053bbd84c183603d06e19da7332de7d295294f368a69af0752"},{"version":"47762a84ce21afc46f46c000e87fbf6b3035b5944da16ca8ac62de576d877fdb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"937a3521bff8fc032a57777777feb260c9ab218d266ac3d7723f7de32a48a430","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3858cf95d68efd835700eb41b1fdf881906eaebd35b07596bbd5b7c1c6fec6c","signature":"1671fc31a114078bc9cb71989c1919c504f1af4e0690995b055181a1932bc74a"},"339fbca5cf5752f3fa77eeef5ec37c42010f1549655b3796eff5f4747e419488","357afcfd45b1bbdf4029dc5107fbf70fbfb519eb1f7cce5c9d9e5dfceed98efb",{"version":"c9d2066069488cecf420d111d0201193958022a2905ac6c66689f50ccecda6b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5452448d44362f60de4ad50c0c5eff76066ef5b1b9f2b4921e83fb50a0c568d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1027c4c5e64d6a6f29226a6d46ba61ca97f539f7b78822e3f8da3937b867fa5b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5a7b42900c17657e4fecb9034c6bbd87a02fc402ee49415ae9cafdbe6f9d1dc","signature":"12ee1ad5a651c4484cbbdc6ea7d594fe1f8adc9684006988275bd671f510f581"},"f7409ccc7875d9cebabc5e27d9df8f3aca19ba959f30fa1b486418ae9c3058e9",{"version":"f51d0b8cfa092a592caf543d772238f191037278e2004d58e7354447d830863f","signature":"986bf1e9bc3d1b0b157927aafcfbf9e94478b28eda319c209ed8e9e613e14827"},{"version":"3b9d0ef4847a6525e297172e340c0dc383c8ab6c58a27aee0a27b2df991ecef1","signature":"57069ea736148610272f87e767f23439015d900f230c3060afa193d6b9029cf2"},{"version":"812fbe241e51f1fb745bfdb0cf447cff8a9802beeac16df1980f14499990900f","signature":"a4c0f47a1176dc8ca692834c31a2f1c95994955eb191e76cbf3e58dbd16ec08c"},{"version":"3cdbef5d6bb0c6201d2edacc2f930322c36b9798b90131787398324b67bd2130","signature":"b84e31232011f6ac9ded7e727871f7c1ece606d8179950f791bcc03ac3c03b6e"},{"version":"d6c2a85159f32ddee646895592b5c53e04b18cfb5346de79c2d003b814b601e4","signature":"2bc381b2105d5a05c2724fa4ae393e83f0adefda1e390db743e55a0cb949c099"},{"version":"9fda6fefc6a936e326113a3d3dcb56da7dc7c2a7a064bf451429b51e7b645d8f","signature":"f668ac39f924b2946f0e323d23da14308c0d996f579dce2b5fe5c9f2085c9ad2"},{"version":"182221370b4c51b9fdd08f71c259596d747b565fcebeed2875832d1f2f556c8a","signature":"00ec18666782d50d3be062bceb46231a3e2c4abae3128f6638529e9fdabefab0"},"e1d7527f3d057bd92e487081450d9037a1dc9dd5e2f8e84e1fb2f6c09903db4c",{"version":"41d344efc8e2dcfc00c0cd0d7bc8f5dabcc6bb0062766fd17aaf85deb4d60ecf","signature":"83df5dd9f98fa4184cd1227ae312c09558f5a00b35243e263069a3a545e7f6b9"},"d6452b09863385bd57e48e1fb836f95c3a6f36ebe690e342d834fd2868d6ba74","013138b404f25c507cc7dcd1e2ec3b0f7e7e7abbd42dc14003000066fd6b230c","e1f90140453b0fd52c46cc6ebfec2ebe754483c05e50dc74caae321d41e7a9ac","5563052849dd3f93ba63a20568ac06acf6b8e15c0b57aee1bd452e7e4b1d5d95",{"version":"bb2cef14d223750bf32f070eea09b2b2e176e10811d5c34f7a824628bad9dbc6","signature":"cc4068562a009b8285b75a2c53ea7b7323cc91785c59635e98b38256e80a2514"},{"version":"c5e286949fb1b24d3395196df616ec5f9090c2569534e48d1aa86e14308f6f2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"908b7b3f2c71140accb3333fa49485ce1ad10ac4e14faab10ed51c0c28c68782",{"version":"637c70ab565be71168064142fdc7fde5a58ab95425066d3c8a6c3c592ca7167d","signature":"5baac7ee5e50c4c52bf4905d5cf4f735c939053555b99a56d1b743630788f665"},"2178789bd22566bcaa973006fa541e2c70d5698b5c099831828c9a1ec141802d","6fa0ea6916329d3aa5c6056e13512e1757edecbce26dae1e8e5a3334e81fbf93",{"version":"dae66bf6a17992ce4aaa4a16b8d8c590e84c396a341cb70ddf61ac2fe710e089","signature":"173c629dcaca1da42db9c0a508d079657fcc0cc56db24103f8a8171294902ff1"},"e7a672c4cf7f2314673b2fded201b122b6b4eda779709e2cb235531e8fac004f",{"version":"fa8dbed00530fb4114906cd93f7fb55512c8eb9551d2f2e9796c69a4da4b594f","impliedFormat":1},"3871ce03ddb2e068e59178c70a88d8830c1cf28ac243d585e267aee1fb5f0bc7","ad5ea69c890012a5b61d4cad41a2d1c2bf581a023eb58290c5ea86554184bae3","da11ab55563abce966b549bc9121f74f71d0aa0f0ad86f74c93d7304634c7007","6958e241f880588015372a690454d0f7c0727b78e0a9882f493e2ac0fada857e","fceca4d896e6fd11de25ba760ff482c087c3a2150da1d841b8092bf8e1dd812c",{"version":"7a732b03d5a0bd9c071d5887794887796b8a5cc30decef607a565735d8cdfc0d","signature":"f1983b8ef21692b453a2ef5ee21b5f8e5c32fea1e87bc833c55c781c109e45bf"},{"version":"c2d4dfa9bb5bbafa31b4423a78c2df02ccb51ad3f4abe7dcbbfaeb8dcf2cb82f","signature":"90b39c231c33d05240cbaabcfc21d94f68e05b5d9d2e972b644363be2133bebe"},{"version":"94adbb305113a8e6572989713200d1eba425e7a01443f1d02f4bd9a66f7f4fa3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3f8860ee39a7c1ca7824f29d948eb3f2160caa4cf4b1df9380a9b9b0c1ea184f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a13bfc666d310b2b552c8a2af2d17c2f8ac8c4ad431eae9f9e961a1fb988ca04","signature":"a7c717bceaf09367324737ee4b73cf87e7c45ef1447547eca4853e516478c7cb"},{"version":"cbcd7e3639eae69860983466b671c160570ff8bef3295eb9d9cf3a918f7c3924","signature":"22039e2144d09a04262d19b5f0e4237ee40bec8322b5c9574403a9103af52044"},"2b7bd4c530f8df99a7c513289d15cc3d919182a3e47a509f7dd66f7c0c618c64","700a699bc316498b27b98820c837965a737debebb4fee5d0a027e95d3c4a1925",{"version":"b61da04f747568084ac75ba893c009197a7a0bb511ce6e8ea11ec3727b1e0bff","signature":"659c6cddd4e661edcbf460b40c7b690f346714057fd0faf27d1400d95cb6a398"},{"version":"5e92985539c56d5b665b392fd3883c103e0a83b63a79955d940547f494a87f27","signature":"d273813f1a71341c5f482788561acb719f12c65fcafb4f36423a6d409856d472"},"1e36aa6fd246d7240a3598e917647e1d2ca0380a1b7bb3b8e3945cb26941b031","205f7ac530c6e5712a640fe3b0dd9f29296ace25043f7179ec1adb56882a1c37",{"version":"5e9142cb5f88305d5b5db601df9fe78244144630b03316f24b123c2bdaec5cd8","signature":"72a4b4bcd25bb33acac0c8d83f0d4d198714a06e03c49046690f380b9736998f"},{"version":"735334eab2d97fbdb987695b8ce10bb987dc318c3dd3f76f65a2f955bc4dce45","signature":"eea48c3b3a4a380ccfec9ac95ea1f6535cc57b17efa2d13009895942af52555c"},{"version":"b6fa86562861ef430157dbc9d6913461f6bb416c58b7052efa28a7fd503a2e59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"34817c134a9a64cb3564c424f056a13554d6c40d31af05be7ea6b28cd9d0ac53",{"version":"90453ef456c9284945d132c4d1c29753bcc06b0b7b1df7910768f748d4630160","signature":"d4d3b854dee0def611af8377422b5caef70f3b8c2c2d10ee3bb9ffb97d51cd45"},{"version":"404d9825e0fe3cc10db20060d068fd4f33c85c245ec9d2f99a20d05b02291b20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"354afe485d131f817329e133ea768376707f9e4041d68975ba6f8b6eb2deca05","0d0fb8169becb3c35ffb1069d105e59d36e1152bfac10d47d122129c8b6ac89a",{"version":"af2b2a730449f22a36e31020631b3f73fb89a0f41fcec38cae6026bc91ffa8f2","signature":"a93511a9ba3c3a239d6d17527c51c2b2a75c994c354029b2d3512e321980e4a9"},{"version":"cf9666636c6b695a0188d6fe4e8441f685cf76f4639552360a084ae53ebf8eb3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8a77be0564f98c1e1f8f1c0ca31bd9ec2d4cf20b1c780dbfdf4cd81d954148c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc4ffeecc189d198af9ce492abed824b47bff7e7e6f8ec739a0eccc849836e4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"598e1063a09bc7bbf1bc527cd19769aadf213b151d89921fafd9eb6c74121fc7","39ca1c24e657b1083a1798a005f0b4c498c547d400b627ef388ed8498d334e22","9b05dbef22d051726098dcbc6490886790bf7bdb93aa9f8a46403fabd59128cd","3f4784aa9fcc39fc0986a29e1066a510ba747012e13a944828b737a0ac9d890c",{"version":"2d0006e2c2a094ea0fabc4465b2cab0d7e8f5e785b3dda2961c2242257908b6e","signature":"a82c92852eb3872216a45757430fb88588440285e6f17c1bb864abe9f209fcd9"},{"version":"2f1a45e754761c4c17af1ecbd707a35ef9421ddb2daf244d1237aa929f919ba1","signature":"a76cde90a90b5582bffaa8aecdbdef0ee7d82667c57cad2c076404a3bcb741b8"},{"version":"5ebf1bcfa735477bf05c2a72f05efa171db37d28e39a690cc57d28447e09b070","signature":"ff96e4d1e720fdea29de66b9f495391d4c8c6b20fa4db88964df688d5a8538d4"},{"version":"73a0ee6395819b063df4b148211985f2e1442945c1a057204cf4cf6281760dc3","affectsGlobalScope":true,"impliedFormat":1},{"version":"d05d8c67116dceafc62e691c47ac89f8f10cf7313cd1b2fb4fe801c2bf1bb1a7","impliedFormat":1},{"version":"3c5bb5207df7095882400323d692957e90ec17323ccff5fd5f29a1ecf3b165d0","impliedFormat":1},{"version":"ed79d26639d1d98ab19d6f419180e5abe2f7fc6c194877d809282813888c98b5","signature":"46713144a8e07e24962b43c73b40a4f4b16e696eb52b8b519876fcd1f5e6eaf3"},"31152f7b9d390e7fc7d92db8ac3934a2f189432dd8cefa237ceb51667511535a","26a7fc2c9efa90591c196a780ebb5940a3cfd7a74245698b2c0e648986755e76",{"version":"ef20d9124a12e824c20d04362452ecbbdc6c9f3de2c3c4b231222870b9586e27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"02ae185acd25001f4af91e9f275661d7d284ca994374867cc564ddf22f8a6082","535fb697e71bce5739129ef269f852ba83a2eea358ce8ca090f4b1cc905af9bb",{"version":"27cefa9a8df763b7c4e3abc76cb9867d1ddad908ac8c8d1e2bb32c3838616d4a","signature":"ea71b9399fcc1d3c46ec554ae15f397c40bf146ca2d9a58374cfb7116c343ab1"},"d1520fdce7489a3ad57359fab13c79ddc0a2a6d743940358a4dd3ad8d959fb38","c65bec5967ebb52be456a4fb70ac4cd92ffd671aaae4661cde2062fe3117fb7f",{"version":"d3cfde44f8089768ebb08098c96d01ca260b88bccf238d55eee93f1c620ff5a5","impliedFormat":1},{"version":"293eadad9dead44c6fd1db6de552663c33f215c55a1bfa2802a1bceed88ff0ec","impliedFormat":1},{"version":"08b2fae7b0f553ad9f79faec864b179fc58bc172e295a70943e8585dd85f600c","impliedFormat":1},{"version":"f12edf1672a94c578eca32216839604f1e1c16b40a1896198deabf99c882b340","impliedFormat":1},{"version":"e3498cf5e428e6c6b9e97bd88736f26d6cf147dedbfa5a8ad3ed8e05e059af8a","impliedFormat":1},{"version":"dba3f34531fd9b1b6e072928b6f885aa4d28dd6789cbd0e93563d43f4b62da53","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"e4b03ddcf8563b1c0aee782a185286ed85a255ce8a30df8453aade2188bbc904","impliedFormat":1},{"version":"2329d90062487e1eaca87b5e06abcbbeeecf80a82f65f949fd332cfcf824b87b","impliedFormat":1},{"version":"25b3f581e12ede11e5739f57a86e8668fbc0124f6649506def306cad2c59d262","impliedFormat":1},{"version":"4fdb529707247a1a917a4626bfb6a293d52cd8ee57ccf03830ec91d39d606d6d","impliedFormat":1},{"version":"a9ebb67d6bbead6044b43714b50dcb77b8f7541ffe803046fdec1714c1eba206","impliedFormat":1},{"version":"833e92c058d033cde3f29a6c7603f517001d1ddd8020bc94d2067a3bc69b2a8e","impliedFormat":1},{"version":"8e6427dd1a4321b0857499739c641b98657ea6dc7cc9a02c9b2c25a845c3c8e6","impliedFormat":1},{"version":"58da08d1fe876c79c47dcf88be37c5c3fab55d97b34c8c09a666599a2191208d","impliedFormat":1},{"version":"e770447d49d5c7ee25f80ccfff0f95003e08bf1147d039f0e8320d95d882c76b","signature":"399eb8b682bd93241cc96cb483306f8634ba94bc17ddb123e9106088240e9c7c"},{"version":"15ba1669f8cb8433a7a7b40422f81fed4f7e037e3cd4ca65b7b4af0434a43560","signature":"4f83f97fe204009c8bbad58d06e956970062930bd694b7ecd88d13a6f85f7e3a"},{"version":"a18970969188e47a48af09738dde83579f9c85bfd731675b671c1f32c5bdc134","signature":"f6c3f2c52494a1c44f58bc28dc1f8f89c7e3b0d005a5c3bb8789f82131996dd5"},"68ec8a37a3f7ce830a6be8e0ed448f8907f638e02a22a12a0f76a900d9f7b258",{"version":"ca74e09cccdadf9ed4a596b473809cf922455e3ce70bdb5d8f20a6acd4a83209","signature":"5b1d834d8c16cc056da6e18683b1fa5bc036290b0a0cdeba3b315826c73eab88"},{"version":"93df3c7e9e01564c615aa025fd2ea367cf2371731eb04f531a5f2c040bde1748","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d9380c6a61635f8a7019cd889f3c9edbb47a2664847d029f935e632f35fa7b09","e70993be79de2ffc2132f91126903db8573e68b0f5318ec48eec97a5e09c5f8c","9eed204f26aed45ba513a001aaa78dffd4bf0194ed42fb59fa4a5b48dc382767",{"version":"bbed8132ccf8ed24e09b7a0c103afe746ec74c3f6d497676ce9a2b09a8e0e4ad","signature":"e9805c8a045ade45cf5dda8406be734ed77bce51fe25e6a431345e403964f502"},{"version":"1b046683cc56fca31919c8cfc9a7b47796d986b2df18c1e55615f7f67a464c0a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"954e64b65c8632c8e6c602f86ddb7a855b541f719153c52586da47df81740592","8e67d08427faa2cd614ffde8279aca632928a75610fab7f0e80eea0481c3ffa0",{"version":"4dd230e5ffb901e4d715a7507fb671f3510cbcb3781701177e09efce8cf30c6f","signature":"7a3b7f911a6906b2fd8d38f7347bc751ff290914c35f2998438f2985dcea418b"},{"version":"d79917970e2012fea644dd1c3d00e7499579d4adfdd3628bc4d4153c2fa38d2e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"eaf4a58ef586c168ad73bf0bf2e4fc7b50b0207046cb03e70f064e23cc7410a2","08e69d30c063db86ea2221adf2282b1c118723cec16131cbd40024f3f43e5a90",{"version":"80ccde47e35b2546400135ffc69d55feb65a4a473964a40cdeda39bcfa10aef9","signature":"2cc35ee4dd1c4f9d97475451cc25f443a692f68f9bc47fb0044f009e356da599"},{"version":"a2d8e5740b1d7e274651ad4e68fd99942d7b33d67adce2d3ff8b976d12327840","signature":"d14e729f535d0e6d801090b439ff6f73f8ae7d713de7468a36d5989f0f10f19a"},{"version":"1d57813ffd927563821c58c16a5a7c35d350415b2b8de5978b370c78b8a750ff","signature":"d5d64072f36683f1af5cdbc66e7ac58d839b6b2d99cee1b0e96df9f4413640a2"},{"version":"70fe6d07a4bad7a73b493a4bfbe2c5b501167449f0e95e3a896261e08d647b67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7d2792a15bf4bcd948e330c3cb747a075d137db57ac53adc6900f69009dd8978","signature":"fb6fdfe7ee4e1c16d6bc8b3c8da0d22ebd365981b6c4dfe881b391328d68f220"},{"version":"75e64a7fcef4db0c9ff13acc31c53cce109194012351733ce9833347e0a8e518","signature":"a97e6b4712135857efbdd73004c551d3a71d65d6b8a9d8f661f608a47b607cf3"},{"version":"a24154a3954030448c58433c23ca4f6d78e763a3af035de3d9633cc9158d7038","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"72fc4be3f73ed954fa04a52ebc5975c56b5f13c4265392191c95028ec27daab5","signature":"13a8b4fdf45f95814460c5001fc04194f85ca7055d460a9f852eed3fbd5c2293"},"d3a3a8fa4cac4860d3fabd83dbbe072bd0db08b6dfc5447fbc3f65a480bbb896",{"version":"773df341514640879d77b0b24b636e6a8ccae2e88bbb09cee7383274046eab2e","signature":"3f3e3ab94baceade05836e0805fd32550fc1cad12d3d31a2fcae6d56882ac2f8"},{"version":"2d96663076cc7fea06c11a0165be63c11b533672c6d02ef361bd86f8394ecdb2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"95f053ad6f9e8f22fb9a0309e14a768302ff5f8072b9bc24b8decdcbdaaad0ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc4cc5cacfa347d15035093ecc8a2c650968fd9208de260c8c141749d1797d23","signature":"8efda6ec7129eb4762df1d2b2a593fe59c69f1a2d5696d6d7bddeff50c24b17d"},"de7f6eb89010bc7d22b76bfd8d01ebdf803df6bdf7e7b7528d2705f74c401e58","745615f591324c1ce4fd8a905b5af838474e781548807dff21154e64b51e945d","a1a2e508046cbc9709255c938bf9935cbffa6cfe006cb5bb7f36b9f4c5a3a2db","d09eaa9c4d651a351d0ed84a88a22b35bd41f307ff7aa0fc356a2b7ac41ccf25",{"version":"005ce56f0d10ed61656324f72713a4e920f12a8656def94bb1735e9cd8392ad5","signature":"484482bbf35c97458c170dd84777adcd87d6e9fcbeac3ed86ba79eaeb8cc7968"},{"version":"265c9ae2b7a62781e57de439be00ccb1b8693156cfb98a0618ba6c5c54596e42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34330c5f52442c69dd7c50d7a95912d87b85cc01be135026a5d7ac060b184464","signature":"720e771373458011bd56c0c6bbeea34302eea42ccf08c8a6b5840a338e7e93b9"},{"version":"e7f0547a22cdcb3e5d9b0fd91191cc2dba8f75a2694eeb4d45a9ddf2a8352960","signature":"c1f5f74ae95ba44d64781ed79486fe7192478040d82d787876f44bc7e77418b2"},{"version":"56208c500dcb5f42be7e18e8cb578f257a1a89b94b3280c506818fed06391805","impliedFormat":1},{"version":"0c94c2e497e1b9bcfda66aea239d5d36cd980d12a6d9d59e66f4be1fa3da5d5a","impliedFormat":1},{"version":"eb9271b3c585ea9dc7b19b906a921bf93f30f22330408ffec6df6a22057f3296","impliedFormat":1},{"version":"aa4a927d0c7239dff845a64e676c71aeed2bbda89a7fb486baab22eb7688ba1d","impliedFormat":1},{"version":"340a990742a00862049b378aaa482b5bb8323d443c799dded51ce711f4f8eb51","impliedFormat":1},{"version":"89eeeebbc612a079c6e7ebe0bde08e06fbc46cfeaebf6157ea3051ed55967b10","impliedFormat":1},{"version":"4c72f66622e266b542fb097f4d1fe88eb858b88b98414a13ef3dd901109e03a1","impliedFormat":1},{"version":"15d8dcd70d6cc6c75476a75ea83c53df1115bdd551c73ef2168a9b4a4bd55a51","impliedFormat":1},{"version":"2acad3ae616a9fb5a8c3d4d7bb5edb11d1d0102372ee939e7fc64359fec4046e","impliedFormat":1},{"version":"c812eabb7d2e13c8e72e216208448f92341a4094dd107cbb0bdb2cb23d1a83e7","impliedFormat":1},{"version":"f734b58ea162765ff4d4a36f671ee06da898921e985a2064510f4925ec1ed062","affectsGlobalScope":true,"impliedFormat":1},{"version":"9619b4a3db123eee6912ce9cbeae535739a1b1736dbbc224a697a2a98fee560c","affectsGlobalScope":true,"impliedFormat":1},{"version":"9c5c84c449a3d74e417343410ba9f1bd8bfeb32abd16945a1b3d0592ded31bc8","impliedFormat":1},{"version":"a7f09d2aaf994dbfd872eda4f2411d619217b04dbe0916202304e7a3d4b0f5f8","impliedFormat":1},{"version":"86ac756569f83cf0571646c916b546634652e92a775e964304912ecafa81dc42","impliedFormat":99},{"version":"a7f23fecdccf1504dae27c359db676d0a1fbaaeb400b55959078924e4c3a4992","impliedFormat":1},{"version":"bee66a62aa1da254412bb2c3c8c1a0dd12efea0722d35cc6ea7b5fdaa6778fd1","impliedFormat":1},{"version":"05d80364872e31465f8a1eaf2697e4fc418f78aa336f4cea68620a23f1379f6f","impliedFormat":1},{"version":"7345ba3b9eb2182d8cdc4c961b62847c3c9918985179ddefd5ca58a80d8b9e6a","impliedFormat":1},{"version":"81c4a0e6de3d5674ec3a721e04b3eb3244180bda86a22c4185ecac0e3f051cd8","impliedFormat":1},{"version":"39975a01d837394bcac2559639e88ecdc4cfd22433327b46ea6f78eb2c584813","impliedFormat":1},{"version":"7261cabedede09ebfd50e135af40be34f76fb9dbc617e129eaec21b00161ae86","impliedFormat":1},{"version":"ea554794a0d4136c5c6ea8f59ae894c3c0848b17848468a63ed5d3a307e148ae","impliedFormat":1},{"version":"2c378d9368abcd2eba8c29b294d40909845f68557bc0b38117e4f04fc56e5f9c","impliedFormat":1},{"version":"9b048390bcffe88c023a4cd742a720b41d4cd7df83bc9270e6f2339bf38de278","affectsGlobalScope":true,"impliedFormat":1},{"version":"c60b14c297cc569c648ddaea70bc1540903b7f4da416edd46687e88a543515a1","impliedFormat":1},{"version":"efcdea26e9115d5c05b3f4c5827fe3b32b4fef1b59dbd67f529c6cb685c7d9c4","impliedFormat":1},{"version":"bb0c361fd2b4bdabbf1307f1a61fd14c953f2692fa642391f93276f2df41de50","impliedFormat":1},{"version":"90588fb5ef85f4a8a4234e8062eb97bd3c8114dfb86a0c67f62685969222da8b","impliedFormat":1},{"version":"6ce50ada4bc9d2ad69927dce35cead36da337a618de0a2daaaeeafe38c692597","impliedFormat":1},{"version":"5fbc333346d28f290d42ac81cf16e454fd3947c6e524384dfd3ce59d4ac3af04","impliedFormat":1},{"version":"072163fdea42ece03bd323b907f5d6acf575a34a9dac4620e517e4378d773d0d","impliedFormat":1},{"version":"df6251bd4b5fad52759bfe96e8ab8f2ce625d0b6739b825209b263729a9c321e","impliedFormat":1},{"version":"846068dbe466864be6e2cae9993a4e3ac492a5cb05a36d5ce36e98690fde41f4","impliedFormat":1},{"version":"94c8c60f751015c8f38923e0d1ae32dd4780b572660123fa087b0cf9884a68a8","impliedFormat":1},{"version":"db8747c785df161ef65237bac36a7716168e5ebf18976ab16fd2fff69cf9c6ce","impliedFormat":1},{"version":"3085abdf921a6d225ad037c89eb2ba26a4c3b2c262f842dd3061949d1969b784","impliedFormat":1},{"version":"8e8f7b36675be31c4e9538529c30a552538c42ff866ba59fe70f23ba18479c5a","impliedFormat":1},{"version":"1fe8b45c1564eca8b1bd27d427d193ea8c1a5d64f7144a5a64665d5d0f27a9e4","impliedFormat":1},{"version":"a03c6f93651e458531f223d52eac1a12f2aee8adc2cbc4b4154a3fe515984e5c","impliedFormat":1},{"version":"8d05dbd747569cb1b0cc2ec1018a3378c47d803de0e7d34f7e12909ff48bb437","impliedFormat":1},{"version":"1afb31819f4b7d04f4089d575acd30854a4cc614baea960066f7cc5755e9efcd","impliedFormat":1},{"version":"35cc30df63b9fa7c9d3637ef315eb5f21f5b0dc0f982c736cad20d39e29b579c","impliedFormat":1},{"version":"c5b47653a15ec7c0bde956e77e5ca103ddc180d40eb4b311e4a024ef7c668fb0","impliedFormat":1},{"version":"0528a80462b04f2f2ad8bee604fe9db235db6a359d1208f370a236e23fc0b1e0","impliedFormat":1},{"version":"94153ca0b430f575f45a5e07d66771dc5ab331af7791691855ba3499958c4e49","impliedFormat":1},{"version":"dd361fe00d3033451e4a43c9eaeafcd1b9b6777adfbc8b8f91d63ea56818c31c","impliedFormat":1},{"version":"b86720947f763bbb869c2b183f8e58bca9fa089ed8f9c5a1574b2bea18cfbc02","impliedFormat":1},{"version":"fb7e20b94d23d989fa7c7d20fccebef31c1ef2d3d9ca179cadba6516e4e918ad","impliedFormat":1},{"version":"8326f735a1f0d2b4ad20539cda4e0d2e7c5fc0b534e3c0d503d5ed20a5711009","impliedFormat":1},{"version":"8d720cd4ee809af1d81f4ce88f02168568d5fded574d89875afd8fe7afd9549e","impliedFormat":1},{"version":"df87c2628c5567fd71dc0b765c845b0cbfef61e7c2e56961ac527bfb615ea639","impliedFormat":1},{"version":"659a83f1dd901de4198c9c2aa70e4a46a9bd0c41ce8a42ee26f2dbff5e86b1f3","impliedFormat":1},{"version":"1db5c2491eebd894eb9be03408601cddfe1b08357d021aeb86c3fb6c329a7843","impliedFormat":1},{"version":"224f85b48786de61fb0b018fbea89620ebec6289179daa78ed33c0f83014fc75","impliedFormat":1},{"version":"05fbfcb5c5c247a8b8a1d97dd8557c78ead2fff524f0b6380b4ac9d3e35249fb","impliedFormat":1},{"version":"322f70408b4e1f550ecc411869707764d8b28da3608e4422587630b366daf9de","impliedFormat":1},{"version":"c4ef9e9e0fcb14b52c97ce847fb26a446b7d668d9db98a7de915a22c46f44c37","impliedFormat":1},{"version":"0e447b14e81b5b3e5d83cbea58b734850f78fb883f810e46d3dedba1a5124658","impliedFormat":1},{"version":"b16a6680ef4108fb2982b1d47e7ce36a8b2c382cf76b3e1b500de70f0a62fdff","impliedFormat":1},{"version":"929939785efdef0b6781b7d3a7098238ea3af41be010f18d6627fd061b6c9edf","impliedFormat":1},{"version":"fca68ac3b92725dbf3dac3f9fbc80775b66d2a9c642e75595a4a11a2095b3c9a","impliedFormat":1},{"version":"245d13141d7f9ec6edd36b14844b247e0680950c1c3289774d431cbbd47e714e","impliedFormat":1},{"version":"4326dc453ff5bf36ad778e93b7021cdd9abcfc4efe75a5c04032324f404af558","impliedFormat":1},{"version":"27b47fbd2f2d0d3cd44b8c7231c800f8528949cc56f421093e2b829d6976f173","impliedFormat":1},{"version":"d5426c0e36296daf07cf2f38227907469c33a53473d9c2721d21dc515c5724df","impliedFormat":1},{"version":"cc03a3e284393b02fdb646931e8576f6dbe839a249d172eb3397adec80559450","impliedFormat":1},{"version":"3d94a259051acf8acd2108cee57ad58fee7f7b278de76a7a5746f0656eecbff6","impliedFormat":1},{"version":"fc745bebefc96e2a518a2d559af6850626cada22a75f794fd40a17aae11e2d54","impliedFormat":1},{"version":"199d42a358b3b73312d499dd04d9f855bf8ad492452765de4ef80b8cb6871cd3","impliedFormat":1},{"version":"eafa048ffaf72cdb64fa1d0dae49aa91280a7bb94e0b034883ae48cec27a04d7","impliedFormat":1},{"version":"593bcf66433eff881c9abb75d2e55a7403c57905aa61d818a616bb3c7f076b49","impliedFormat":1},{"version":"3f3526aea8d29f0c53f8fb99201c770c87c357b5e87349aca8494bfd0c145c26","impliedFormat":1},{"version":"6ee92d844e5a1c0eb562d110676a3a17f00d2cd2ea2aaaff0a98d7881b9a4041","impliedFormat":1},{"version":"b9dc36d1f7c5c2350feafb55c090127104e59b7d2a20729b286dab00d70e283d","impliedFormat":1},{"version":"45d3f1d53fa99783a5e3c29debb065d6060d0db650a6a1055308a8619bd6b263","impliedFormat":1},{"version":"a14febaf38fd75a88620a0808732cf9841afc403da2dc3de7a6fc9a49d36bdbc","impliedFormat":1},{"version":"6052522a593f094cfee0e99c76312a229cf2d49ac2e75095af83813ec9f4b109","impliedFormat":1},{"version":"a0ceb6ce93981581494bae078b971b17e36b67502a36a056966940377517091d","impliedFormat":1},{"version":"a63ce903dd08c662702e33700a3d28ca66ed21ac0591e1dbf4a0b309ae80e690","impliedFormat":1},{"version":"2b63d2725550866e0f2b56b2394ce001ebf1145cb4b04dc9daa29d73867b878c","impliedFormat":1},{"version":"e885933b92f26fa3204403999eddc61651cd3109faf8bffa4f6b6e558b0ab2fa","impliedFormat":1},{"version":"22338cc18afb909a95a6c55417f1a67db99badecbac1710a963f0bf63c952124","impliedFormat":1},{"version":"e61b31fd5fd627c73da6041d201c0bbd721170288381f09055cad4fcb2ad327b","impliedFormat":1},{"version":"6e2d2b63c278fd1c8dd54da2328622c964f50afa62978ed1a73ccd85e99a4fc7","impliedFormat":1},{"version":"e151e41c82004cf09b7ea863f591348c9035e0f7a69d4189cbac89cc9611b89d","impliedFormat":1},{"version":"dedf4655c327e9c5294a63d75764946308700825e8d8c1d4318a10602581cd6c","impliedFormat":1},{"version":"b83ffe71adbac91c5596133251e5ec0c9e6664017ee5b776841effe93de8f466","impliedFormat":1},{"version":"61ecf051972c69e7c992bab9cf74c511ecba51b273c4e1590574d97a542bd4ea","impliedFormat":1},{"version":"068f5afbae92a20a5fcd9cfce76f7b90de2c59a952396b5da225b61f95a1d60a","impliedFormat":1},{"version":"18d97e6b17d1196d88d4deb0e37d8edb7fbdd102ae8a5681f03e15030b6b2fd4","impliedFormat":1},{"version":"d7ded5d2060ac6a4404e6001a46d5a704e3f325f95e2cb0dc055ea05404c9cf6","impliedFormat":1},{"version":"99c88ea4f93e883d10c04961dbf37c403c4f3c8444948b86effec0bf52176d0e","impliedFormat":1},{"version":"e88f3729fcc3d38d2a1b3cdcbd773d13d72ea3bdf4d0c0c784818e3bfbe7998d","impliedFormat":1},{"version":"f25b1264b694a647593b0a9a044a267098aaf249d646981a7f0503b8bb185352","impliedFormat":1},{"version":"964d0862660f8e46675c83793f42ab2af336f3d6106dee966a4053d5dc433063","impliedFormat":1},{"version":"292ad4203c181f33beb9eb8fe7c6aaae29f62163793278a7ffc2fcc0d0dbed19","impliedFormat":1},{"version":"aa8e5ac3f73eede931d5da74ef1797c174b00854ac701ead5c4a7d6ce4a49029","impliedFormat":1},{"version":"f1a4ca3688d951daa2d7740da5a0827fa34d4a7709eed7b8225215986ee87108","impliedFormat":1},{"version":"08e159b5ef9d14bdd329457c5cbe181e84f13c4ff2546a24b9eb9129b0c71c46","impliedFormat":1},{"version":"f8453a3fe0fe49ab718357120bec2b8205e15eb91ff62eada60a4780458fa91e","impliedFormat":1},{"version":"06f186bb9a6408ef8563dbf17d53cbe23e68422518b49b96afac732844ddbaa1","impliedFormat":1},{"version":"525f9c06245b5b43b1237cfd757396fd7fd8090e5d6a4ded758c7ce17a04bf42","impliedFormat":1},{"version":"e46b752c48b3aec77516d23b5cbc0b85df78c740c058a822b43a32c958e468f0","impliedFormat":1},{"version":"f693b1fce39951823f128590c6c837b70f844b6d3746ef778b7fae7f1340338a","impliedFormat":1},{"version":"bc264419318f0b174b5dabdd465e1eddb82f872e899b6c696c67217b346e958c","impliedFormat":1},{"version":"6046bffaa17bbb55ffd62926a966a7badce21b27d6239ba0b569b8266bedaf19","impliedFormat":1},{"version":"9376cce4d849f1d6ad2cb0048807c77cfeb78cee6e29b61dcfe74c7ab2980e18","impliedFormat":1},{"version":"2e0dc55ea1ade444d285576a4ed7915834d4a87f71b147c38afdb877ebb0ad2d","impliedFormat":1},{"version":"4edfc4848068bf58016856dfeb27341c15679884575e1a501e2389a1fea5c579","impliedFormat":1},{"version":"0c3d7a094ef401b3c36c8e3d88382a7e7a8b1e4f702769eba861d03db559876b","impliedFormat":1},{"version":"1a3b915d24b2a26df000ef55ed356028dec11ff54f7e93a5c095c313d7016e1c","impliedFormat":1},{"version":"7e3a4800683a39375bc99f0d53b21328b0a0377ab7cbb732c564ca7ca04d9b37","impliedFormat":1},{"version":"b1b5e35575486918e155ef02d995598be2b5d8e729f857f8309ba0b76e14d833","impliedFormat":1},{"version":"8d87de8b839a017541ec1baec68292ddbcdfad0f2f3b5f2ae8abd61f06105cbd","impliedFormat":1},{"version":"7cb0d946957daea11f78a31b85de435e00bcd8964eba66d3e8056ba9d14b9c55","impliedFormat":1},{"version":"b3e441cdb9d9e55e6e120052fe8bf2a8b5e5a46287f21d5bc39561594574e1a9","impliedFormat":1},{"version":"0870e8eb0527c044e844a1d83127f020aa7f79048218a62b2875e818355f8cb2","impliedFormat":1},{"version":"38400b70ac70600c632ad498df2d956ed8ca6c6774dfb0ef69a2d35a9450df7a","impliedFormat":1},{"version":"abab86c01d001d0cc410c7aee59168eb09bdb7d6d9d39d3c0081b36235e2824f","impliedFormat":1},{"version":"7ae39872b4f4d38b9df079cce4223e999754eb3b3f90e4e46b978b29e72c419e","impliedFormat":1},{"version":"dc0f3099379383bf14f2263c7987584e81b6d9b60259c9e31390455ca0619dba","impliedFormat":1},{"version":"6dd704b0ba0131eb9e707aeedc39be6a224b4669544e518217a75eb7f5dd65c2","impliedFormat":1},{"version":"6effa89f483e5c83c0e0063df5f1d8b006d9d0f1de7eed2233886642424dc8fb","impliedFormat":1},{"version":"5c6dc17513298b4daac99bf8e88ad4e4a504310cf69a0cf3cffefa5912b85234","impliedFormat":1},{"version":"d43130c35762a80da2299f8b59a4321b6e64acfb0b11a36183379b4c7b83314b","impliedFormat":1},{"version":"6bf44b890824799af8e20c0387ffa987e890fac5c5954a3a7352351eefe55d5d","impliedFormat":1},{"version":"e61999c06ae79ec587c2e7db514a024d85732b32ee2c997bf4a1ceb2b561c611","impliedFormat":1},{"version":"aecd29a5bc49b1de6b933344e9c96384cd098162c46873673ffa1408e6195c52","impliedFormat":1},{"version":"f83afa274e0f11860c6609198ecca220f5df60690923b990ca06cae21771016e","impliedFormat":1},{"version":"af31f37264ea5d5349eec50786ceca75c572ed3be91bdd7cb428fdd8cd14b17c","impliedFormat":1},{"version":"86d01647c3c215e53729aa2cb15d7bcc2b049088bc76bdb5e04a0bf25f97c386","impliedFormat":1},{"version":"9d3173cf740b742d1048d8ab20469060a2b5e2d426f8ff7df36042e6829c4aa8","impliedFormat":1},{"version":"f1063f0e6ca22a9fae0c0338768b03911c954b8e6ad4fff5381cc6a964b34324","impliedFormat":1},{"version":"4f85d12a28937e950b123e5385448a3bce0f04dccbca7ceb8aef351ffeccb228","impliedFormat":1},{"version":"85e4673ec8507aef18afd4a9acfae0294bdfaac29458ede0b8b56f5a63738486","impliedFormat":1},{"version":"40683566071340b03c74d0a4ffa84d49fedb181a691ce04c97e11b231a7deee4","impliedFormat":1},{"version":"81c8ab81daa2286241ad27468d6fc7ad3ecc62da04b18b77ce9b9b437f6b0863","impliedFormat":1},{"version":"268755fe3b7dd5ca84fc043de1502c56c5cf8ef70271c017964a9dff8af94a7f","impliedFormat":1},{"version":"8e56db8febfe127a9142435940c9a5a1ad17ddb2b2a6d8e9e8984785a76db1fd","impliedFormat":1},{"version":"f1efa458a3f630de51e30c823a8e1109eadf8562b8b90c764ef1fd989329bcaf","impliedFormat":1},{"version":"1ea64554b23db011171f7e0dd59d006b239fd4ec2e7e8b31ecf995528de79423","impliedFormat":1},{"version":"46788ee6b4670d904a54d56fe9e3bb308ab4c4ba01a435d39d8beaebf85f1a54","impliedFormat":1},{"version":"f4f6e61f620861b576f466e8af34e6064a997aa93ad62593c0c3f51489784e5c","impliedFormat":1},{"version":"f92fe945f94fee5c2811d6ee81b1751a1f1970b29063907d48067f1c2389bc3b","signature":"2825a8ae716e344c54428f3916a5fb98e4f7b7d4f521e0aa40a6781766e2b2a5"},{"version":"3974befffa7647e5d975081c15016cda5f16062c8957af8d5b93b85bd6b57b21","signature":"8364a4867aade4b7b8e12b3116edc4c0cc374833476df15a0cbbe7b147bb1387"},{"version":"da094bbe2ba0c875d680fa8957a0b4056d806ed8093c4eb84f1d1319bc148924","signature":"2ecfec679572556d5739697241ee12faf6d1c088a64eb646f358d6b908201893"},{"version":"884b3c4b6de733bea0363994edfdbc08f23168c3819ee92eacf9ee2ff38b9e31","signature":"8b18201daa2caa4d6dad664291f923d8607cf8211ebd0dec3986e400f02376b4"},{"version":"d61b3b8b5d54ffbc1159015019c05472841f9b12287ad1eb0febb9d50b3fcf2b","signature":"7bd1aae3ca5e15b45dc603fad958b8d228f09e8c43ad9a4efdc70c7b3f96fc35"},{"version":"4b9b77c14bfa8102fcb57b14ffe92dbff3b513a8c4ba62893ab009fbd4c73647","signature":"9d2c9cbb279702e44a3ea7fe24bfe19cf27352d4cbe4882bbe5d521d27c9741e"},{"version":"aee88de82317641d6391f0686ca4acceedfaae5ade43d00dfdbb2e32e83870b1","signature":"979a61915ecd6734d45f9ab06a423a5b75cac28c23c512c838c10e333ff88a02"},{"version":"9a889402f27da6ba13bcaf7e0731fa06758e971c0d4ed730d6b46f08d9a05f34","signature":"f9d6f6e5c3e8a1dbf9499c426fb4d97386c7aa5b205662a4777f9289ef9152ab"},{"version":"f6534bed93400a60ab02368c4a698062e31ce5ad4eefd0f4994c2385ae83c54b","signature":"9c2f866be60bdff85a59bf2cd9b85041d63bfc369560cf59b88d7a95c6072f28"},{"version":"b6ced0b0b07feec87098d3eb446bdf772cc268ee3ac4230a4069e61dbf75cfe4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb040b86775b25fc0848d43e3cc03e1b5cf4f00c40cbd350dbce010c06404df1","signature":"6d46cfeb3c048590784e9c099f0dfb8de954141c9fbf25f6d9ec87981ebc6fe7"},{"version":"d20eaead87726a364899203678c3e81614bfc368630e7a38ed0008acf7f7c1f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e892e40c4a0fc631c78d3480f7edc5c1cd469ea0b8edd5e21951ba39c996b889","signature":"703090444b11f1b3ff7c9d90d1f20f336bdd927ab54747e57150d42e86e1f62a"},{"version":"5a2958fdf63b7d83f8d734d08ab6975b2a66defa7d7ee4988c0abfec0881b3a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"7859b822b52969cf5421d8d07df464fabd68c67579733fd51cc994c6f3b9452e",{"version":"05a0701aab09b3b50154c4469670a6af40d716c1bd84258ab88c4486efccc2de","signature":"cd7eee6f9641bca037731468d9b1012d11858efb65ccb7a23e35377d824b2a4b"},{"version":"cef9a872724202d022975121422e03878a38b6c4a78977b7e277733a2ed5151f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2776443e3c5ce498f62ac5661d0e35884afea55b0d3f6f9306f8ffb97b35e9fc","signature":"c049b08ee071ee35f8623f69360d9b11a4e78f6f903a9601e9f76346ff07ffc4"},{"version":"18981392c502332d353be793e0eee6b4b71b92c4cc159879c76c0e412b50166f","signature":"85a5f8ec84196d475ea68d0239a7ee678d96c40274f16d0e940937166e2f9fa7"},"091c011d67fce1f188bb8c7474775ffe3275ddbc9837bd5fb5ffa26fd70a1cd8","ede33324139612cc144cb9ab0658d31f633fbbf6e5654b4867ad17964e494463",{"version":"90bdcbca0e0bf6c2b0bbb00673a340a12647ee9e9c0b7ffa08c1cbee51683f6a","signature":"081dc84b2cfc91d910771f621446c86f99ef16cba361a195bc5b6415671e3940"},{"version":"c3a6e46122a15e372681357101c151aefc21040e65611ab4edb7366b6694b2ea","signature":"84a5f8d870d0e3a83ea81b7fdd41940ea8af6ad244f7b5a41347a696ce8ee863"},{"version":"5c6fad27889b29555ec4270fa34f6d318520fc36e3c368efef6988c2240f943d","signature":"e569b4fa591b1a8f2e8bc65df628ea7b8314ea9e9fc4de877bae1db7073f82d3"},{"version":"75d850624f64a90b0709ea1dc2742d4b189c106098f94125af7cdfcbc9db0852","signature":"2bb5816f801dc67b86549dcb0c662be56b248c5dd1662c1042691c7329148f98"},{"version":"4e127bfcc9d5d93ab906153cc602cfcc13188847639ded607d4e83fd57838e12","signature":"f91e8b73b979d76dc70cf739261a03907357f4b464c2c2b57aa1da3ef60840c4"},{"version":"43fbe80cee30066d6ade0e64b13f0987cd6b23946ec6265728fa2adb27146000","signature":"017bbf6636858e6e607294afca49a452e39c06854a04aa20ad3850defd0025b2"},{"version":"f527325efcfb6f6a0d9253f1af0e0a32ada4f9c5cac06ca5689927515225c440","signature":"4105893a2351efe282a947f23f959ba55f8f46aa72d55829d362261b1429b42f"},{"version":"753dc412c871f3fdc65bfea46ee79b435fabb41509238f866f6249d44f7c1dcd","signature":"c286b503f750f73cbf22d1031c189fb27e7d8a93ef017dc18d17bbe37fd5dd9b"},{"version":"4051f6311deb0ce6052329eeb1cd4b1b104378fe52f882f483130bea75f92197","impliedFormat":1},"324a1e17354169e427d4c5b39f9fa33866c2474b364fb66bcfe0c4e46dd0de08",{"version":"d6ad5279f8d296ebb50264aabcbd50a5296edfef7f23ae4c159579028935d119","signature":"48cf5a32fd77ba27774c29824c8d1bf27cfe16081169b1a013e0874292c3709b"},{"version":"6f2d007923eb835494e65dcc1034da47cf8e60aef0554d323273a59b8b8c2f86","signature":"bcb9686b97930d312e851d879aa0ceb39656e4e49b07b8aef72ec0eae03cb376"},{"version":"406af28178f025030a57332cb2a36516048ecab7acf102b84f1c1a84f09d77fa","signature":"aaeb521b6f9317f1358efeed044f7b8c9da2de643c9c444c86efa4b5974707ac"},"00e2552694e9ca66c48d911ae3a46b5ec592ceaf1aa11fc892a11ea68e8f61b4",{"version":"4e028b7fdb99d3de652792b19e13bca74e46bc452c80b9272d77b1ac2f138aaa","signature":"385f9da32da6c7c156c4c273f1e08ab2fbe264bb424469850b65c9e6ffa9c9ed"},{"version":"fa8896708f7c899af3f718f77f46489b8d3efd15204184f74b878992dd516270","signature":"e89614e458edec1676ac424f0a893a6e87bf5bf38d34a8758b3e4823f0d2b48f"},{"version":"85b1d0061b1268cbaa7efeba177d96bac002d38d7acdffd7a023decbaab2ef7f","signature":"0fafd7d6cde3e37c7c4045f298b00355745dcb2147c0e8d0f1240fba867274de"},{"version":"bb5660a80ad6edc1e4a7831bdc38cb4f70adbf718846aa3bb936a27b62d742d6","signature":"e017494fbef6d93da248b22d25416f95d8412cb4a4e8cb12c7e64495f16de3ec"},{"version":"91d4ebbf20c7ce05ce56b901d34ac84f18c5de49cdcc8b4e2e79416bf5863a52","signature":"00aed0049902c591b92c49af96b5a8d1b3e202604017f34241bf72cf89f80756"},{"version":"f7cde16e51986d5a1361c4d7e36cb8f8089acd60e7b43b7c0cb7ec9d3c58bbb8","signature":"fbfd3cb405fce3aab2cc8b6c68371f03f340b5bedfb22d1a0b46408ca184aa4b"},{"version":"0f55704e7fce1025a74958ce04d7d099a3605ab1ba105c63b7fde02139a17eef","signature":"69652f240dac09436bdaa4cedabd63700a279aaa035b43ade48742fbe5b37d08"},{"version":"4d8d7e049c7a369a07b41963903b7041bd8c88560b55af2b4b6c4fd7be645cd5","impliedFormat":1},{"version":"83f6b233e11c9f2855f7f318f608570e9a45db007ae924278e7a581d7ef99b35","impliedFormat":1},{"version":"7296b6e2f2accbd8ed583ac9fa90c88d7d50ca2ff95a04ce2959d46e6cf7696c","signature":"1fc7a196b7cb9628c96283d1c55177082524e81f5e607404a5ca9a1ff53e45e4"},{"version":"231a843f95abff5b70bf76ded015c4d7c0ff006544d27c9747471a495743c2ed","signature":"1507e471793e1215912dd1ab92c0797ae9259ebf7fd0f3146e2bcee42b776bc8"},{"version":"deb4df42f640706245617d22c38500d0d24e34689f405224004477c47b30a287","signature":"84225d531b0d673c7dee0a7abb7592e937c207fb0393e85d8e9808505e415642"},{"version":"05196723f295c088f92bd93f9edf2f9d72a0e6fb7a468f55aa270214519857a5","signature":"1c7c3f06b140f7f31f69c3f2a6f87659c1a487c552bc733f9de6ed963779a17a"},{"version":"0684c0f5805f8c75a0613dbf6e8d386e93721218828baed8a419dad06db0266d","signature":"a0b2ed7ed78ffb63bdb8c45c49596bf2792676cc3c527c599be027c2d772c840"},{"version":"60baecf2ee0b36e0b6f81536d77774d964bde3e3975c00328874e3b564a97e9e","signature":"9813abe08f8dc60f627701e1576bfbbe8498fa01840b3500ef120ffbe3ece69b"},{"version":"babf8f17c539cd8e5309393275eb17fa2a6790a848f9b6736e3e75b69ca12ae6","signature":"ee79b4e030d4b005413044e47295b78001ccb4849995c4dc59e42e65c509f21a"},{"version":"fea6e19848834ac2c8fa97416625b380176f0fda1396eef00f84d136af989050","signature":"d703ffb3cf86f2e1cf7460554b6fc0a3a0eada0040fc48aafeacca14bffb7ebc"},{"version":"cb262ae73b7b864a9cc5e62142dc12600f5afddafa458e6c26218259d5ff67d7","signature":"433e57f0df48dbb4612309330aee7b075651c0ba5d29c483b17bd92e81cad910"},{"version":"f060e1946eb32ff62b101bbac21a6cd02835440c0892554566d0dde5d4838cec","signature":"036240f98ae8d5e07ad6f648996ff0630d6112eaee5b53fc3b309a1acd7c0721"},{"version":"a8f8ddbbd5a595a3a45b89108072fd7c11afcc5df839f3b2d234ce93bf5ba511","signature":"621d7479105eaf0b7002459dd4a7746134df8f621a6d9e62ac5c69f4b73902af"},{"version":"25a9445362108d35961825c730d1385aa52655c253523603fe3a514699a08308","signature":"0a82088daf1f69f93a6f03b7ba430d6605a8b48febb578e7ecd2c3564b8d235a"},{"version":"9b015283fd545bc487ff27b205f5f87ad9257e30df4e0137deb8260228fd97c6","signature":"6e0962e848047bf57be651109d9ee3d4e499277d114392e6efdbf60a3863fec6"},{"version":"b92e316d7caef01a7d96aae2fc81bac3411d81aa08c08369fdd79b79052d0804","signature":"a15f6b3477115f885bb24033267a6e06e889bbc393c5ae977513f0ef2c29efdc"},{"version":"883e6a16350e6a237822deb193859ba6f80f68b5bc63d37932eb5a222afabcfb","signature":"4e4f390cf28f71013350ead1ba25290872b936b31244feb495c7da040c655c54"},{"version":"20eeac8a87d7e85f13f2ce118073cec7275054be646bd47823f1e9cc8951ed4d","signature":"9f2d02e65e22f5bc32f727fb091f17315fe58a8792d8280ec59ab072272e3376"},{"version":"88f2985b43e7af3d4dbcba54e609861fcd28cef3ee74ca4d54e82917a9165b30","signature":"2e54daabbe58c730286e014d2bfe4a80b6d533a2bc9c5ab6fb1e3e654d3a4872"},{"version":"417c3d98d4efb99cd7f3c683c2caf02ae28758f18fed72ae0389aecfdab29878","signature":"2a39da52aed89ee43bf5dcadf72fc7ab5d16b8dee17ff890bf0ad3b72a0320c0"},{"version":"15e9ece6b9f5f2ce89f2ec8a96bc9303b35f07374b94005eb2443efaa0c6a49a","signature":"46676fa7ca6a5b6552a61d40d41f41eebc81cf838c14933cddd35203d298b874"},{"version":"0aaaaf9e39d6225f0fcce6949faf7254a473de642dd96f1b6cf5501b87347546","signature":"1f8e872ea16e6ef3029e47f25725a22c286734fcb4a88ea2e13c437e905f0c21"},{"version":"735af8f14d6e3866b5863742bec289903bdde848ba8754fb628afb54af74d5bf","signature":"6058d5942879388147f8aa5e9c2713af05d0f1680d7ba91d1999b97dc6b5ca01"},"2e4dabbebd31ee7206edd6a4ae429f487df1734e92df23dd037839f212c3e9d1","b27d139dd9c71966306fabe2f545b928f610ffa6b0c75d84a9dded090f66a422","98ebfeb0805807ae08415404af1b664e76353e70e5e71e6f086c7ba264b76fba",{"version":"fd79331caf4d1c981f82d179a8f8ee1f5f9db5485b5960d2ac5252ff91ba195f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec213e4001fcd5e8446fea02e4a123873df592e8205987405c0e6664886104fd","signature":"f5ff573f4a6451fc2e8c7e1e6ceb8537cd0f25338065baae8a5735c24f22d431"},{"version":"c38e3b9be1619f15bec612db3c9867c9e3435bcc9798dd0d8f6342ab0d8ae946","signature":"e8f50d40694344b6b22ef6d4c3d5fe9347601c7a435fb7e60f820bf37d881d0a"},{"version":"1d25e7af5af3830d0cbc77493b047838dc152a775920710be72c3c8472b980a6","signature":"3560dc1bdf53f078348f0e499ab1e3339be7659310d92861fba7a9277024ffa7"},{"version":"cb83a4611b876ddcf649c18d80609654bb2c80fc160da1e7539cd3be15811a60","signature":"4220ee655e09296874f3ae3d3145efdd76398677bfa27fbef8e133c1b09cf50d"},{"version":"51a8c66060d8e08fa1607e33758afebaa47c542b40cf7091a791218255e7f769","signature":"475b03ccd54597a3115e13c07c1fe8ac417f6f6857d485114a00039d5d1ea179"},{"version":"817f663192fcc81412090c3e7167fa81c63eab9ea977e6a6ae8eeafb8742001e","signature":"77ce1243fed91f1a9f7be9c18192af18f4ea1603611a3dac2ba4f726c298de4c"},{"version":"4c31fd07fd4e530b6da7febec131d259907d5e179ef3bdac0a4a43cfa56b6935","signature":"b1eb5e11871638368fc4cab710e40fe4b23e0f7e9d4a6e22052edfa50d185fa9"},"9c5d15efba9e25be56ae6fe11057225c74316fb0c90e7d34b81ec8f633a9810e",{"version":"b02c8a9ebc617e95159a1d928fce2fbc345f3e9ccc9f7f6684195d8f8d9bab5e","signature":"45e169847975d5baedaaa5fbe3da4bc92db0b90a305f2536491b7a4a2d262341"},"963c32aa76aee7050bf4b5749bec7c775046f03bcd791d54992b3345f7b80e2e","9e473cec8b5dbb77baf8db593da0a943701f1edca3b3b1ac81af9ce178dac9cd",{"version":"10ea972b401fc77b7e35429345f02bb02dde34fc9d7d1fc3232a187f5b52facf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"38cac01302000992d41adce1e4ea4a985de9d38fc126618de525a524eb000fef","signature":"a7c0fbde9afd282c949332844e31c9d8af5b35f443e19d2237afbc862e74fe86"},{"version":"520a429b325443370aa841a2ba8ed537c6b43385fac88a7eb0a542cc1d48af55","signature":"bf343a08f58ac65036d01c5f5fdf07b339340f9afbb4cda14c89aa6582c3c2d7"},{"version":"90ec5f47910a26321c68ec7e45c5912b61aa8c0b6bae4e36dfd35c6274376ea9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1ab7dfe2e40a14457c44447646438563ffbf187e60a175f258af4189bb414e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0d29ff571f05d9f1c9ecabd53c10cb9bfcaa313b3b64612593bec64745c4d224","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cd5944f91eaf3e04d8c66d1c7c44508f932ae86fc033403193a81a0e3a95e53b","signature":"2630a9fc3e9a2205f1df08e9d39ac89290da5a35ab782d1504364baa70c67104"},{"version":"3ab2b6455439badb3d984aef6d2519029dd8595f19f614654072798269598876","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8f3b78fbcee37a708acd2a86f1c22645cf34b444cfd7459be341415228f4b63","signature":"14fe776ea9f72086fe119d5df096c39513d6bdd3ba1615b8d9f5cbce35933f54"},{"version":"f14799e6e43275054eb876159fdcb6c55b4e76808911ffdc9f81a2e3e5baa564","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"aeabfd5da8290656189b20d20600d0df6381dc3881c381b815807e9fb745f5d7","3dc95ec98d2db484ccbaa31a47c2633bd619a4d86fd655739ed248f081f49f07",{"version":"6b32764a0410770ea2d05907024bd8ef5044fcc5ee257ddaac24e5a09de8ac91","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9ac8c2249f0a97698a155031023e87eaa74c871229e36b51c3c83fd1a0bc92d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35d7cacd428e674f89a928ed99f34ddc7c36958b395627a9196a8ba22618a29a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69a81ee2b65949687067f23e7d5ca0fec2110620ff93ca8b92bc56740340b760","signature":"bc160a1badf1d1e0e9b92666b4848dfdc428b553d00ac5d2304b7ad80ac4cbea"},"5f44765c75000e8fea925fba6c2ba696386103cab9d813e72070cdcf45e1f804",{"version":"b11dcc6b3a1e92851fa7626c01c543833b96a9f37a29d80de6f11b320b626c9a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63e34e9210807a4d1af057003031a6689dd3295f8f2524ae7597ab27f326335c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad7e4e17c67808e01a84f46591f2d4b763173ecc84a4863a727b7058b5786945","signature":"b7902bfba5bc8b901152f1ed5f8d9c2cbf2ba2790d351e5e5d61e00bdebbc624"},{"version":"3e5b494cda4d0ac2bce024928f0eacc77f8d9e4ac3d51eadde967ce542efc27e","signature":"b94b0bc72e5a26387c9314dc4f72106bddc0e5056469dcf4a43a351c1523c49c"},{"version":"1c230948c8120cd778cb258a0b70eec899b458db58229de16a2951a7ebdc57b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0fef5a24cad5c4948eb776b00fa114e2560ce3dded3abc27db2e2b2b66832331","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2cef84bf00cbdb452fdc5d8ecfe7b8c0aa3fa788bdc4ad8961e2e636530dbb60","impliedFormat":99},{"version":"9e2f5dc3da9d83bf4a0a9e5d39d8c9918482d586e0c403a44021e4ae7662697e","impliedFormat":99},{"version":"799003c0ab928582fca04977f47b8d85b43a8de610f4eef0ad2d069fbb9f9399","impliedFormat":99},{"version":"a62e448d3f09fee63ec1230acb23fb54f8f6ccf8d6f0001c7b94fd51594b7c9b","impliedFormat":99},{"version":"5366549884acc57185eeeb64561c2060af008230a8ea645f048f747cfac6549c","impliedFormat":99},{"version":"cd3229a2e4ca10207178e22f215c8e196c837254dd34ee440612a2a14993ffc2","impliedFormat":99},{"version":"73b7e3d5300ad64f9231f5bb145fca4892574d85e2d1a015ce095628f16915ca","impliedFormat":99},{"version":"42944c2dd3115e25cb0aa77aa05fe9e3d0f8a3b4ac251896cc680b7be41ad60c","impliedFormat":99},{"version":"7ed8ed496092801dd5f25f39af223ebeddc97bd64a7d9a5621f790dbc836eebe","impliedFormat":99},{"version":"abc549dfea982be25e0379cdcb6ef2aa6716b0013c11d8d6b14c814cc955d7c8","impliedFormat":99},{"version":"0e6ba4003cfaa90748b69ed0dcc9f99299d1af70f4bd835a872e52705b0c850c","impliedFormat":99},{"version":"3decd4c8e355126e76c9a43cc7ae08017fbcf1d766b204d696ccdfa5128de1ac","impliedFormat":99},{"version":"40410f51558d0b3d635584333fbba6b58b4b7f74037f59a08c0577828539637e","impliedFormat":99},{"version":"096350f9446ef08832b935d4a97c66f74a9133faebd90a40a12abd5f8bc7eab2","impliedFormat":99},{"version":"26baad6aa356ef75b2e1ee150ef6325988be9700bba14249f9e8d0f66bb36087","impliedFormat":99},{"version":"237dd4f246265a3efb18c3d40f54f98336ba2c329a9f9e30b4bb0f1a27baf324","impliedFormat":99},{"version":"3fa62b954262157916a65b3dd57faf6cfec7544579e673204da30eba00852543","impliedFormat":99},{"version":"8ab0b13972c8018bd18d49236b8c08448a38c823e28f5620b3ef0b43ff521589","impliedFormat":99},{"version":"065dff95b2b9cd6f5f7404222ecdbd371f15c22b844b731eb32286540f499d2a","impliedFormat":99},{"version":"d1c03f0339b8514b7d5420e075684e6b1dfb9d6c27a7fc6fbb09bc3f25fc7764","impliedFormat":99},{"version":"f8900ddf4a4944cad4a81de965c4761094758ee39bfe24198668c397caf5db3a","impliedFormat":99},{"version":"b2b8376bb1ac24155cde89574c32edfdefdb926845d9426ab52815421b3d19a1","impliedFormat":99},{"version":"4e805f78a8acff48feea70836df232a6db887b2e376492666f6b70985fb706fd","impliedFormat":99},{"version":"cfc5a66408fb9a7dd136ec2afd50a3eced54baa3321c473ee4d29046e761a3a2","impliedFormat":99},{"version":"594201c616c318b7f3149a912abd8d6bdf338d765b7bcbde86bca2e66b144606","impliedFormat":99},{"version":"35c190fc184fc2fdca132bb8aad00ac84819f135428b0e906c3e599c74125d24","impliedFormat":99},{"version":"8f567d63ab28f074ab2be3ddc2da27107de8022488f4a3bd91609752045bb612","impliedFormat":99},{"version":"f6c7ae690e2d224a310c8f967cdb415d8c7c55791a30c00da30e0c19ef4def49","impliedFormat":99},{"version":"f0ee7287284a844f4d04b80ae7a955b12cb50f85fb0021a78cc7f20a90459823","impliedFormat":99},{"version":"956e7dae5b888d02ec65dfe4113b541042cd2c70f96f6b9de0a5465bdf9565fe","impliedFormat":99},{"version":"75722639ade81b4d9a9a7f67f9cee2abbb68c52367322fe4fcd51949dbf60706","impliedFormat":99},{"version":"f90d3104f554535c4bfcf9d429e41318563c40b3b7e0827c0975624722546514","impliedFormat":99},{"version":"c61b9b3f161eb34fe5ed7fd3bb84f0774d74445928a06e9089ddc0a152f2a016","impliedFormat":99},{"version":"17268b7c5aed233ecafd22ac3e751c3aafc101b7ff982de8617bf19fafb7058e","impliedFormat":99},{"version":"bf6060c0585e76d2670629cc4c592e1dd938ac356e974916ced7f46587ba8181","impliedFormat":99},{"version":"dfe68566e870382e203fbf082e3e094b3d3d6712a3b6bf56fe66f69271d27cce","impliedFormat":99},{"version":"f230e4b9b3a7c27975a8af6131b08f6b17505e829073a3faa6ebff4a163090aa","impliedFormat":99},{"version":"e89ae5ee53771a98d89105723fc4dc73205bd96bfd2a784597b5ec6c2ed35abb","impliedFormat":99},{"version":"7f79b823d4b2a1fdee3a799a6a46792a21e4400ed0c2f45f1e1a9bb8de21d18c","impliedFormat":99},{"version":"42b828f21d7b672495a1f538ac49e93ea12da980d07d28999c7eb8dc55f297e5","impliedFormat":99},{"version":"35e7486045f8a29b25ec8adad02823bb82e0876fcce76228bc683e0da0726e98","impliedFormat":99},{"version":"a43f4964d97d0feeb6b33944f750707dbdb539e1c9c3a0496c40789a90d7e0d9","impliedFormat":99},{"version":"8dc6b9b1f772053689d3b298f089ffedf29ee93be2eead0d9c07d77e68aad9e4","impliedFormat":99},{"version":"b44e0ca6cba9c3f98a1b277e93dedcc31990c57c08f0fb37c29eb929afae3a49","impliedFormat":99},{"version":"e236485fde7c092508a177ccfef03ba15ec72ac697b50e241802d68dd99c5f73","impliedFormat":99},{"version":"b7ea66bd111288844e2d0cfb12abc02242af0786a83ddde14abc156a7f80d500","impliedFormat":99},{"version":"83fd9ba9e82b881f410b69b30d4fa9e41b1b6e445e4d7c7eaec836d4cc5a5712","impliedFormat":99},{"version":"ba2733b454a9756b8207e110896e4889859d4a23581e54fdd659f09267a63ecd","impliedFormat":99},{"version":"d94b9c4da700bf7e011fbd442c54b5c88a52db58bc71bb69db67f46a1c525320","impliedFormat":99},{"version":"bb19a13fddc505d633b9d08340c851a16638a3a2c6ba4971d538908b0cce8671","impliedFormat":99},{"version":"da588a0328ea4fa648563415d1ee4cad0587e3d1e1d29cf54d761fbe83ed9670","impliedFormat":99},{"version":"673f71885a78cdf431dd29b801ef2f811a2c793b415c44e64a55489ad010f6e2","impliedFormat":99},{"version":"4c0c16f5d60671e0654e560a94cc549a858a5fb9397a072d3f9935b3068be740","impliedFormat":99},{"version":"2cb58371baa22dbaa02e2abfc40b5640f00e7ed203e70e97fa20226a776b2e16","impliedFormat":99},{"version":"29db777661a60ea3a85cd21ce29b0bd877bb44f52cd583f9f3f7580ee08d4fd1","impliedFormat":99},{"version":"cdf79d50d5ca102a6ccd1ead392b0f5ebcb9b6c8b230e4f4931f0fab8b6ff3c4","impliedFormat":99},{"version":"6e21729eb1f94c93f99d1c13492b6e835e5c2d2ba552693c1c699f0e34d1fa1d","impliedFormat":99},{"version":"8267fbe09febe68384466808d3feaf055ebb7b15903728d23e7fb4c01949148b","impliedFormat":99},{"version":"54e45f5f4f7684c5c49d3e6367ba73c55c69f82973ecf7aca793a86bea5a99af","impliedFormat":99},{"version":"55a9664e49c8e8db27d8eb413749957eb222485b91b1148840a73e065ef6c028","impliedFormat":99},{"version":"af7945629e88f161817436aeab27906b947cea60102066575eb31071b4f84168","impliedFormat":99},{"version":"847b7eec4ffc81b7eaa1bcb473fd5da4aa73ab7e56944df3caf7d284317e95f3","impliedFormat":99},{"version":"5dd262cbb746c2a4d0a26f09369b3ede4a1a36e15c272adfd0289c47cef81ad7","impliedFormat":99},{"version":"677e4d55a1353f1b83ad68faffbdd91ffa7dbc34d67b1e91e88d3ac71b88be0b","impliedFormat":99},{"version":"21ba9b6a4c6dfc6dc403884d34dec961eeb965a4e0c99521ba2b3f9929e26b75","impliedFormat":99},{"version":"452a373c93cae3a20fb8f8309ac48b40cb2a33f05c3d54b090582ce3b8ae96c1","impliedFormat":99},{"version":"112f147e1f4b44b4a4f186cefcae4e58c49d6a0a61faacf7a12f55694b9f2232","impliedFormat":99},{"version":"c293793b601177e19a4230a9ecdaa167e6a44c93147da549941eb8e154510f4f","impliedFormat":99},{"version":"82ece43251947dd304e6f5dbfaf8b97588e5676ddf0bc0fc1a6a861aaa3eaf7c","impliedFormat":99},{"version":"f67c58823afbf2590f2c239d09a46aba9d3456327eee05b593c48ee248758ce0","impliedFormat":99},{"version":"e2647503f56e5c6d41b256af0b17ad3b98455cd8b852ba7336221af5fe99d805","impliedFormat":99},{"version":"c2d12e71e905f9ae80895201ae4b52b0082716d3177d794799f0140c3bbdb65c","impliedFormat":99},{"version":"668eaa98e8d54dc5a22d7a66d659a47f0b152e7b109f798cb295a3c3dd817dbb","impliedFormat":99},{"version":"81d447a1f248a2345a89673774ca673e79da5df8e25c6fd6bffb495d3b704362","impliedFormat":99},{"version":"900f1f5341752c6c2824ea871ae941d60be1359793a0284e56abcf277955a511","impliedFormat":99},{"version":"00c8b548f04329a012af189dfd8e3f3ddd8d4fb187f4fd22fdeba5e1eb740d92","impliedFormat":99},{"version":"919ea552c5b52ac5c8303a96dd7357986a2597de5760416468550b659113bad6","impliedFormat":99},{"version":"8255114fec0d6189524bf52d90580a2fce40bdac621215e562aa5f5b058fea33","impliedFormat":99},{"version":"6020d3e324725ee474aa4637005d2449eb8bce66e8aaf85163d683df86384dd0","impliedFormat":99},{"version":"d95e4069a535a118c22ac66a8b018818f9f74ca7000c8eac977dceaf752d0f95","impliedFormat":99},{"version":"5d8097f4e2588d7912d82772ac6f05ee6def5b738f5e4605f2e9bb24d26b4e86","impliedFormat":99},{"version":"6703ee0cb2405fc9e98a8835e4266ed4131fd25c31bcc0c302e66e9b05271eee","impliedFormat":99},{"version":"4b83d4ffdcb29aa6562749ca797b76a3b914d80f54819c6a08f1014fb6841623","impliedFormat":99},{"version":"41ecfbc96066dc0d03f1a8139e28b4b3297bc231257d27a7c5796d017962a438","impliedFormat":99},{"version":"46e060979c9bb359578744342c37b843529c284e20ebc219bd71d5fbc04b3704","impliedFormat":99},{"version":"720b258293ffe0939688db7b4729d24f64809718157b14ae50fb9e2397c69fbc","impliedFormat":99},{"version":"1273795a90591a538b11c91a7840b1facbb5b6d500146cc055324a16f58c0346","impliedFormat":99},{"version":"a06814aa3f18bf501a7bbd1cf3ad9b1fb090cb89b19375debf6ac3b906ad9090","impliedFormat":99},{"version":"785afd3f604c75ef24a65c0f2ce4b3ce2137f941773c201842abaa7385b12e3b","impliedFormat":99},{"version":"ee3bfff84df83f9e3cf0ec85aff97df52fc57e740e41fd4780de1cb3f9e73780","impliedFormat":99},{"version":"2025d7779d9356a37ed4142da93898d39f811d9c5937f8c107f44ab2344e87b7","impliedFormat":99},{"version":"471b3d02d1af08c6b58a9a2ff5c85da205910f782a7783d7a1f59dcb681ee8ea","impliedFormat":99},{"version":"e1902decb3f07a58e9be70b5136e3d715997025e0f0f20cf7e2610363f38ee04","impliedFormat":99},{"version":"323156c80e3ac6175f4b75952ed871ead30b58b9ec463131e368e572d89777be","impliedFormat":99},{"version":"7c54717447fdfa134e43c6f1a71f8ae4e955538f9e59a8bbd60eb65f5bb965e6","impliedFormat":99},{"version":"0d153b01d0b1e33ad2b8c778765c3f3539a3ffaa595dc3e9d53d91cfe5615f11","impliedFormat":99},{"version":"0ef8dbf7f717c2d8912df768687073cda1d7ec73ce2861fa8ee30ea8c15455e7","impliedFormat":99},{"version":"355ae3751ad1378804c850b212bbfed1bb68af9e4cde0cde857b86c6cbbe2140","impliedFormat":99},{"version":"06b02b230ad18789680a5d286d55d566451973456fa33b63ddff6c9b2c2ab41c","impliedFormat":99},{"version":"971f0be2884711cdbd2dc522224ba68db24abea620e9089b5432a9ed73dd406c","impliedFormat":99},{"version":"1e45c92c3241e189027db53310d5b3b8d713fad08ca6ec5f8e0734b275b6dd76","impliedFormat":99},{"version":"a374180a9dc60b15b4fea69423ae9d8e3cdfdf604e8cb314325db23a2a8e3cf9","impliedFormat":99},{"version":"acc82e49137ccc0be7e523164613032cd0a35a08b38721138a228926539a33f8","impliedFormat":99},{"version":"990951a94433c2efe6e42266ebd096f63154115a37c1f4e5bd37bee57bbd3563","impliedFormat":99},{"version":"b65b675fe2b1ad0d621ce5ad94e9fbdbd16b17e8afebe2863361e0d028dc73fc","impliedFormat":99},{"version":"1bc87b80ef30a78d0cec6f6c56ad41b68a8f03d30a7052d1a0f1e946f5eb5150","impliedFormat":99},{"version":"79ace3491ac2d2585e2e3748827466f99d0fe06acfb8cfd7bb5ac6e272d9b742","impliedFormat":99},{"version":"f51bf6581de40babf85946efb37bf4bab0a5357b46b4a0cf904278f3b8234350","impliedFormat":99},{"version":"c728002a759d8ec6bccb10eed56184e86aeff0a762c1555b62b5d0fa9d1f7d64","impliedFormat":99},{"version":"586f94e07a295f3d02f847f9e0e47dbf14c16e04ccc172b011b3f4774a28aaea","impliedFormat":99},{"version":"cfe1a0f4ed2df36a2c65ea6bc235dbb8cf6e6c25feb6629989f1fa51210b32e7","impliedFormat":99},{"version":"d94d06e50f58be0a417ebc0336be0c51e5aeb06cbb59ae7d5d4cba95e4948418","impliedFormat":99},{"version":"02246d22f0fc51c76534d953f606aab7c012d1acdb182f822c8ac8a37926a72c","impliedFormat":99},{"version":"0166e0f095473027f6f8744378f5ac5cb6557e788540fdad76e0abca9eef2567","impliedFormat":99},{"version":"f950a4cec73ccf53ee3c56f117e5c585872bd13328c487cdf7a614246feb075e","impliedFormat":99},{"version":"f325583644b63525d1c4d22825633c220e478411d813f134d5930207cdf8aab3","impliedFormat":99},{"version":"e25a05c0fd866cf73c00a281ea11bb51fa8d2a9955f2edf8a7b8f3081b37c165","impliedFormat":99},{"version":"2c86f279d7db3c024de0f21cd9c8c2c972972f842357016bfbbd86955723b223","impliedFormat":99},{"version":"df6ccc0d7f7324035b05a6294404b310a23b2f07fbbebe1cd298f88647ab8b6d","impliedFormat":99},{"version":"8cfc293b33082003cacbf7856b8b5e2d6dd3bde46abbd575b0c935dc83af4844","impliedFormat":99},{"version":"62391e62217e8a22a4d5f3ff123912bb4d182598e051f31b287096d187cbaea9","impliedFormat":99},{"version":"81895ab68da9cb1656eca90934f01924181d57439e980aa3df8c788488363272","impliedFormat":99},{"version":"cb1ee5692cfe21d8865ab74cc64aeb2f3319f2c2ea2f63cf2662b6319160beee","impliedFormat":99},{"version":"ef6ed27ceed062efa353f3c108dd31d3e4e83e222ed9e18566fa85ed4600e366","impliedFormat":99},{"version":"faa076baad26c7c20856aa86a22c8afd9113f0bc47feeacc680a9e6d4493ea5e","impliedFormat":99},{"version":"782d76ae47ae31c1169c04d93f11e6e13b50c704833517ffeb933516abc4dc12","impliedFormat":99},{"version":"9036dd2d0b09989692fb0eb69b5142647a709aae1f2bfea464701df33758345f","impliedFormat":99},{"version":"14eeb7d5737bc074d1020b7648358ad0488dfb576aa82937e8447586c1b02bd8","impliedFormat":99},{"version":"73b252fae9083ac46b9f2fa376c3a4f5d2c98f5fc0d31922e6d74e7a416f1034","impliedFormat":99},{"version":"0c35ff99747044453c64a4fe3e0e813adc45e67c16d6c47a39cda7d5b2c45764","impliedFormat":99},{"version":"a127363b7f50b5ce89ee98b2faa52a3e7247af32785f937e827e9ee32578d803","impliedFormat":99},{"version":"ae9e8befa5a81361fda14b5c44953b69a6b32abed1e9c62c533230796ba2b39f","impliedFormat":99},{"version":"f3eeac608cb47badfaf2218914776864558c08a392fa626d3a0ad678b0fbfe38","impliedFormat":99},{"version":"2c05477216d0da559ce805e5b5cb8f3c72e1897110886a0fe22808ac37a2f5f6","impliedFormat":99},{"version":"6307d21b4a02a9de0ec25ad7c8a36bfb3a25d38adb1dbe877f7e73595a4a924c","impliedFormat":99},{"version":"cc78721e9ec12b7b62352b8bfa1e37abe055c17965d5fc956d4edccb1bf4f673","impliedFormat":99},{"version":"53565e07ff42ff137d862dd402cd1799785904a50cbd75fbf8402d7ae76fb6b8","impliedFormat":99},{"version":"c8c4e8de61ce90831b7342b6e4800a3e70f4c06eadb17dd743e652ece3562ebd","impliedFormat":99},{"version":"37ed869a9de36bb1ddf343286b5cd0e0afaddd892ba28e82fd652b7ee2c46dec","impliedFormat":99},{"version":"5dccac21bdd7a3a3f399f2a0110bb1bb22a7bb002e3c4a3403f781299faf3f53","impliedFormat":99},{"version":"976ff2cb836f3b64382f2090462966b6b82a059b8d90c4eba54ffa2021e5c150","impliedFormat":99},{"version":"9432e9ba2ed3ef0169d133a2fdb113002be901691ec78ec9d2329c12c16d5065","impliedFormat":99},{"version":"f7e369493bd11921421f51025608f6450675e5d5fba73a1f5617c96072449ab9","impliedFormat":99},{"version":"0919c74e404e0f876c1687425547263ceffe5cc184404492ed2f8deb8a13cbcd","impliedFormat":99},{"version":"7df13a374704470d39a931dd1fa3602a3bd1cadf064115784e4acc3b25e6c24f","impliedFormat":99},{"version":"36944fe70fea641703d40efab3585844c0ed20ce7e783fdcde90bad50bf77f5d","impliedFormat":99},{"version":"cde65d40e64bf0aedba644d8841fba8fecc6f4793d7e4a4364be954bf273ec0c","impliedFormat":99},{"version":"ab9a48af27d31f50da02f40b83b2e8695c4ac28bd446f37d34d5ded0443aed3e","impliedFormat":99},{"version":"0b1a50c36805a5f3be773ea73339750c3619a7ac53c0f441f5e9f1cdfbddc695","impliedFormat":99},{"version":"b85424e3eeb4843556cc1838289e1d3aafc8907b44fad864f228e2abf1af55d4","impliedFormat":99},{"version":"0bdeb9f8d6472b196355591ea4a4313cef5434d24bc79c6e5e733132380b87ea","impliedFormat":99},{"version":"91fe1b91f77a6080c156f0f6af3f6b12524f04604b6e0925432c48f7ef58cfd9","impliedFormat":99},{"version":"9866369eb72b6e77be2a92589c9df9be1232a1a66e96736170819e8a1297b61f","impliedFormat":99},{"version":"e84281e45703810be96251405f8051317362e453f39f26e078cde8967fd2945f","impliedFormat":99},{"version":"0bcb04a160a2a2a934480e3b899b1d2255970b25ffc7408a5d07aaa07baf2878","impliedFormat":99},{"version":"8e3a9c17439b657424fc7e311943dcf9444fbcac73f3b9b72aec2f449a11e203","impliedFormat":99},{"version":"a6c3df80c7c5e8a15e302df97c8a35b1deec48f6a8639110663d6c85ea562fff","impliedFormat":99},{"version":"4c69a93a4645185c445f0050939645592d49f2b8dbc999ff63176c607f3dc319","impliedFormat":99},{"version":"0e2d2919246a4491005fba1612d101a68dad27a5592a77baab1523b2de335cc2","impliedFormat":99},{"version":"c32be5821ff157b2845dacfb257531e932a1161b933e6cd1cd0a4de9e057bdea","impliedFormat":99},{"version":"eb14bc57e220517c752f74ab7c810b72a80632c26eccbd7af690ed9ea7b5ee03","impliedFormat":99},{"version":"ee0de1f85e4fcafe9019c89085cedbde41a22d4492bab87623eed5afb91065ec","impliedFormat":99},{"version":"588b99d933490c59f0ac74e43491ec1b71348b049b1a391f24318b84bdc17b97","impliedFormat":99},{"version":"d78f57a7b922e855a90900275fc93805e07f8cfc7689039840118eb6bf6f0057","impliedFormat":99},{"version":"3c20a3bb0c50c819419f44aa55acc58476dad4754a16884cef06012d02b0722f","impliedFormat":99},{"version":"82c69793fa09d8b58a3589f08c7d16163c566cca5657dcc45deaf5160f2c0e95","impliedFormat":99},{"version":"b89268c927a997e32030d8d8daeb0ee65a7c7db40b167a39296459e114ba7511","impliedFormat":99},{"version":"fb8bc4e79a3b9442dd3e8b1bea89b3e0ad93dd154f94fcb7ca81f511c7c06b65","impliedFormat":99},{"version":"b6c9a2deefb6a57ff68d2a38d33c34407b9939487fc9ee9f32ba3ecf2987a88a","impliedFormat":99},{"version":"f6b371377bab3018dac2bca63e27502ecbd5d06f708ad7e312658d3b5315d948","impliedFormat":99},{"version":"31947dd8f1c8eeb7841e1f139a493a73bd520f90e59a6415375d0d8e6a031f01","impliedFormat":99},{"version":"3a4b1b3e62543a3955e1ad5cddfcc59b25074f722d5dbf7aee1971a43de8acd2","impliedFormat":99},{"version":"19287d6b76288c2814f1633bdd68d2b76748757ffd355e73e41151644e4773d6","impliedFormat":99},{"version":"fc4e6ec7dade5f9d422b153c5d8f6ad074bd9cc4e280415b7dc58fb5c52b5df1","impliedFormat":99},{"version":"3aea973106e1184db82d8880f0ca134388b6cbc420f7309d1c8947b842886349","impliedFormat":99},{"version":"765e278c464923da94dda7c2b281ece92f58981642421ae097862effe2bd30fa","impliedFormat":99},{"version":"de260bed7f7d25593f59e859bd7c7f8c6e6bb87e8686a0fcafa3774cb5ca02d8","impliedFormat":99},{"version":"f9d8848e3c6d82c1e348a9e5cc531e433be58c4ba233a6683a4e9bf6d923a462","impliedFormat":99},{"version":"48a3ae8b6325c87135a210f6d6a7ce15d58417870a2ad78d70858313c47eee99","impliedFormat":99},{"version":"9822da8046d00ef9b8a230345cc163599e58629112081ba55cf4f8d88ba5bd93","impliedFormat":99},{"version":"260a0f4a8a6dc69a2dec8ea672d702629ff7624d5684b29be55cca02a3e42e7e","impliedFormat":99},{"version":"9789d7263d261044cf33f0bb5fd31a2f4ae3a4cc2a010aa45db6f7d01fb019fa","impliedFormat":99},{"version":"d81f0485800e8813d917c2edf184ca3a7fdeada1472cad6dc41e43c37e240800","impliedFormat":99},{"version":"f59c2a64fc652509e0cc56fffb59d7b81f4c7950c7dcfc2da44b681637604797","impliedFormat":99},{"version":"ac0d6f9d09ee9ec076ec3045d20f0d6f5b32300d5a2fa05b5c5a9b6492c0de1f","impliedFormat":99},{"version":"1d8a6497f663251332519c392c6053d5b5e93e5a2189e2669620851b93fbab65","impliedFormat":99},{"version":"52f2d4cea9e3b8e4821b6ca71077ec5f41316d1b3c7d599ef10fd7c8c839ee09","impliedFormat":99},{"version":"013d7f1c5798ac843bcf24e6f3d97efa42c79f038da9cae4fc95ec686b3087ce","impliedFormat":99},{"version":"83b28136beeebb45a635f0179b828e0d0ec9c59330db43060c5958d796e35ddd","impliedFormat":99},{"version":"81c1ea7f9b00460828ef1c92fbbcfa9ff0a7bfcfb2dbfe2510bf7916c914fa75","impliedFormat":99},{"version":"6cf0bf08cc2ffa6d25c7a9852e58f7de9b26122a42380a89105c201e8bde13c8","impliedFormat":99},{"version":"07350c1be768f0446138cf700b47a8aae8e2f6d828310e519bc500200d519a92","impliedFormat":99},{"version":"4253e0bc9530f4c0eec62d1c566350dffef04ab26d0f72befd2ddc08ccb61925","impliedFormat":99},{"version":"9237ce9c67ba997f8cdbc795be7628c1eafefc3317260c38c1e2df4ebd63a62b","impliedFormat":99},{"version":"1ae2b7f6a1352e73754401f16a7894c1335f3fd199acf4c473274243f89c3230","impliedFormat":99},{"version":"ecfe3af749f3c44ab0fa260d7027b067332f0841bcdca1c8db75eb9b1890bbb5","impliedFormat":99},{"version":"94899ca690be8b491a49004460b79426162b218ef26948625fc025cb40a092e9","impliedFormat":99},{"version":"7fd2e48e2ebd92a381e745c7cfe58003969296f7d0cb0109808e6e867bef6a4d","impliedFormat":99},{"version":"98d7fcdd7c0c682528a70f6781f7a00cc0f314b720d1b15996f223c74dc0cf69","impliedFormat":99},{"version":"155e18326afb2fb26a380b480e0c892cc85cc9449537b3346fcf5aaceeb953a8","impliedFormat":99},{"version":"523d1775135260f53f672264937ee0f3dc42a92a39de8bee6c48c7ea60b50b5a","impliedFormat":99},{"version":"e441b9eebbc1284e5d995d99b53ed520b76a87cab512286651c4612d86cd408e","impliedFormat":99},{"version":"f67db9e9b24275680e88888b618e0d6514a40cef9aec2b6ea8eb1de899f97933","impliedFormat":99},{"version":"0968374af7bf8bf67301b89a4fd4bc8594dcb90b16b4be06ee57d26a708bb776","impliedFormat":99},{"version":"f57e6707d035ab89a03797d34faef37deefd3dd90aa17d90de2f33dce46a2c56","impliedFormat":99},{"version":"cc8b559b2cf9380ca72922c64576a43f000275c72042b2af2415ce0fb88d7077","impliedFormat":99},{"version":"1a337ca294c428ba8f2eb01e887b28d080ee4a4307ae87e02e468b1d26af4a74","impliedFormat":99},{"version":"98a667d4585d5b040af90fb5062e31da7c215abcb47521ff57e33f62755fdc17","impliedFormat":99},{"version":"c29e02568f8b68e62b83db2243e4bbacb5ced2d6c8d120e322b56a018d1070b8","impliedFormat":99},{"version":"53c8e58bcea418aa22f5ee013774c08dfe15f0df9625868b7ce5a7201de29785","impliedFormat":99},{"version":"d56ca5a8aa5dd4937a82df98dd930ef154f340d259bcb2980d36c28a47ff2901","impliedFormat":99},{"version":"3d421ded6ae2260cfd45b230eabe38b6c8498a1a35db809384c85ff2bc3ba822","impliedFormat":99},{"version":"ead83f43dcb956f13b924b9e43e7a64380f830efc67439a9e9e479bc985df8f8","impliedFormat":99},{"version":"d04ba54e15442a067fd28679bf18d11ca2162d64f3b0695b9ddfba2b8e1c3b59","impliedFormat":99},{"version":"6b6cced9b26444d621bb62a1b8cb65911c22505fd559411b5a57e699c7aa519e","impliedFormat":99},{"version":"dafc53212e800bc9bbfed7f3a7732ba8f401516b2bc0c71b2f601ae2583a007f","impliedFormat":99},{"version":"940e51654c3c1967f34160a9674e3bf1dc436a5d36c5d7833718aea235e52fda","impliedFormat":99},{"version":"16f2023402fd0a4eeac99edb5d75d3dd8cb4b2f25f46e9bcdaf0c0bd9670e77b","impliedFormat":99},{"version":"9d7765384806b08a522ff85c20184667eec635fdb809736184da23e89533dabd","impliedFormat":99},{"version":"d53593a008e289638eac5b0a0dfbd4296233e395205831367992a87e81eda13b","impliedFormat":99},{"version":"aa0981acabb92a87323aa1579664c293a968138d9377310fde29429e92febbc6","impliedFormat":99},{"version":"796d8fd55590f854e79d3f4181b54f28108e90118314c858726163bb9961e7ae","impliedFormat":99},{"version":"b37e4e4f8f34745d839c334991d9cf227c34c2ed7fb3b297011ddfddf3ac7d68","impliedFormat":99},{"version":"ef55aaa329259ffcb1694dc5d0d688f05e5a37eec2ae34510ef751e9d608b90d","impliedFormat":99},{"version":"264b53b60d27b252258cca58f80b81e143b6299a866402819d5524fd20febd0c","impliedFormat":99},{"version":"714456dfe665ce8b398af312b56b68a927a8a182f8e78dd7c1ef5cfb596ade25","impliedFormat":99},{"version":"27c9ce7c539db9b37ec0d7476b4e9d9ba7439dc41549e466aeadde43746e8390","impliedFormat":99},{"version":"903e813fb2d906d278ab54626f4ade4f43f96f4e636dc66f5aced69d1afb871b","impliedFormat":99},{"version":"c80bc9ee4fa024302308d14084c0f6c3026301db9abbf6789e6b1caf686ce35c","impliedFormat":99},{"version":"8cd470e7936934cb17c70c18a2e03282980d8d047ec08467925a31bf99ec1bf1","impliedFormat":99},{"version":"c09f5d7d8cdee279972790105f90d6adbfb18efb905cf04815ac59d033f7bb7f","impliedFormat":99},{"version":"e92673d9d3c39fff66b14270f144fd32d2ec6fe92e8b2c51e65bd7b4a0e5f355","impliedFormat":99},{"version":"d465455e9f29288b7c879ecd390256571ba306f8b947698f03b1429d6300ff67","impliedFormat":99},{"version":"108b9e022f7dddd5e5ed8165170d65b752fa7b21ced5dd1005ffad3c36242c57","impliedFormat":99},{"version":"1890b77d7c36efdd18174e345b295ece38e66179dae192fad21e8c3642b993a1","impliedFormat":99},{"version":"636f9c9b34b3f33b2258704da1187e271fbf36081a8e22da97be5b53488a9863","impliedFormat":99},{"version":"fa6693c8ad74ce099f2a93ca8d1b0a643dd7f6026f41ba4b244d440d8dd07f03","impliedFormat":99},{"version":"fd76be177303d35dbd29c11de5f935f5d21ad605d34aa4aad9e309ec494b51a2","impliedFormat":99},{"version":"f31af014cf064d7cea0392f02595f09d8cd4b9d06c7794397cf3ddce13111d81","impliedFormat":99},{"version":"d15de8944d6dfb1c8fab88ed1d56947c4ae438b9fcbd9be18f7840b78c9c3bbd","impliedFormat":99},{"version":"040fd90833b34b59436ca6545a00a3b5988f5a95e6cce0a378ddd66bd2cf44f2","impliedFormat":99},{"version":"f332d07979b46f12410417a97153271e1bf5ea11677423718c59010df71a3f2d","impliedFormat":99},{"version":"06911ddbb7160760c75015d2d6fa0f1c0f94d9f0d61265b2d211238b571a3ff2","impliedFormat":99},{"version":"af0612a0e9b7efc543168628fe60a8d3f4d7ae8d97fe257788cb60bdac2459c3","impliedFormat":99},{"version":"9dd05d844e6b99e0a3c8ab8e37bac8f6297d531a844af0738f9b1eaf4aead087","impliedFormat":99},{"version":"5f7be41a9ceed0632c19b7cdb5ad9e07ac19093cbe23a738fe0f1c8c2f27b036","impliedFormat":99},{"version":"b33e84f2148cc81a9afa6d4177a27a1d246fabea3c0cf391aebd3e62eec04f4f","impliedFormat":99},{"version":"5dd273430ddfd576316532f118feafc41f18d5128d7d84e674d98f4a57107384","impliedFormat":99},{"version":"a9c40d74fab8e810c62cfea99a21d09f529fe6a0e60c39353510974c33df980d","impliedFormat":99},{"version":"2665ad2e88b3633b417e176af058b1c20bf5645327a8c4fd4f08e35636b72f9d","impliedFormat":99},{"version":"2321ad799e7ff9c6c6a886dea5ab208d08072a8d33da312f1b9a10ebc888765d","impliedFormat":99},{"version":"8e2f56264cfd71093034fadc1c788d6f46d58036a57e7189e8eda9a7f87eb9d9","impliedFormat":99},{"version":"06deb0a45f5a6dd23244cae8f1ebfa2400ec7de804980f044316d2d9d35a6ce5","impliedFormat":99},{"version":"b27242dd3af2a5548d0c7231db7da63d6373636d6c4e72d9b616adaa2acef7e1","impliedFormat":99},{"version":"e0ee7ba0571b83c53a3d6ec761cf391e7128d8f8f590f8832c28661b73c21b68","impliedFormat":99},{"version":"072bfd97fc61c894ef260723f43a416d49ebd8b703696f647c8322671c598873","impliedFormat":99},{"version":"e70875232f5d5528f1650dd6f5c94a5bed344ecf04bdbb998f7f78a3c1317d02","impliedFormat":99},{"version":"09b103d94e6bf3723cc3642b164dcae50bea1d1f0ab1f5cccc38dfed3fb2beda","impliedFormat":99},{"version":"e3c2cc25f470bea78afb3af5ca6f30759cfd44e4b1212e84657fb36e45a3a1d3","signature":"6e0fa1db91df6484a033340dfafa435f2e98844b66e876a01f963ff84a878646"},{"version":"16bed64f9c23abdf4e18425ab522343d5c8f180214832e5b6d40135d838f7841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5e6e6f01cb776da72cb3ca11adf3fa20c16d0f68841c2f550e485376491fe584","signature":"981a1e9bb280cbad4485d10bfb76e890079fcc75cdf11f502bd995e1065d2616"},{"version":"2e0902468e1a220489a3f33dc82d5eff8f70521cc4ad8eb35abee7a792a14a6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca55e9c482d5da0295fc69d21ed6822af32439b9fc3b1fc55ab593deb4a83880","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a0395b4c83044d52eb3954c29d53ccba5aab9acf9765dbe663f8f95783629609","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abb13a376d731db2984464da46235b3dd198602a97e200bf687c9a9a2bb43593","signature":"909a9f6b4a08c0af15d0c0e3cb1f290ccda985ee205dadc0c735d3bd1467d5bf"},{"version":"1652f9e9c84699da6c0050e0818ffbd6093fb9dc67e082bfb5d3df1fc92012b2","signature":"e7ed13b72a29fe7f0c628bb06ed80ba591e327268a8acf0cb66fa1725ce5e930"},{"version":"ccb92614f79729906fc8a9f5c9c18f163c1352d2e549cc49b42c4302fa591f30","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec90498fea3cfaefc1dc5badcfa5d2c8f05a73f96abb856d63707c0cd25351eb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eedbc489481913977cc00e055bd0f493c8ed3115928ca006929d1b353411e722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"9344e6e424dfd647c27be85b5ea478753830f7fb31a74747ce6a373b479d51b2","ba98ffac19abe3f9aa945abea3b81b3ecb435ab243502108b61d6af1a31c00b1",{"version":"73a98325658b7d7509ec766ae0f0b34b9a4d9819b388d4e5b3c74ef8af5af185","signature":"211206a1cc64d3623a54c919fe8e7e8cb70032936ce7dd2625ba2f185580334a"},{"version":"8072581b3b7e9ce43d9553465431ebc422579042d0a644394d018c6803c45918","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"938165e53c76725415bd8152fc8363d628ac4a95e04d4c2884f75349e3d337a9",{"version":"c868f50837eedd81fa9f61bd42de6665f74e7eb7a459135c6a14ac33ddc86798","impliedFormat":1},{"version":"42cf6b642a67b27545981d06932f7e5ef948a68dadf5779cdfa9e052e3a13d76","signature":"41302973852bac2a0d545eb886ea0b819803722d9d6344a011477d235854894a"},{"version":"cb61a5aafcdee23a7ccf20343670924ee6cf6ec6f631b65a3ab249e27d9db542","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d2e1e7f9ea69da6734503f8b7077edde2e9fc91596141725e2beffba76ea2ec3","signature":"0f87709207a3c70d4c4dd8ca7a866e5114b412c6629abcd9f4bac4a7b91495e1"},{"version":"1e3bd35220cea102b5a84d579f9bb1adf4dc20dea714829473bb3aa87499a64d","signature":"230d47db97c6f501ec507c267dbecfcf25a3a8c8c13854734008f4294a0da41e"},{"version":"ad3e839b384c5231de4906ea0d62e778d95f1e46937d9c005487b0897ffc48f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebe9b7b5b1909551f7fe8a5aedab9f4c713b928f5ffeb7b83c9ac876861a74fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dae983fc2e940a628dd197d10e67ca9cdaa071d87d7018ceb8fa5c8a690eccf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"eeb5551958a1e9c5493e02cc7a0eaa112e946b7590a018f1bec0e29de91a64de","29e02239f94241d9f26f19f570a5eb688c86873d1e77e43868fd69f6a38e771d","681ab80103a45e835b91035d733228aa210d75cb0cd45355dbe6e72fcbe1806a",{"version":"cfc4d1e3e0f3fa0a4a3de8483598fe4ca1f9677de760b092b4919748ca383fff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"ac450542cbfd50a4d7bf0f3ec8aeedb9e95791ecc6f2b2b19367696bd303e8c6","impliedFormat":1},{"version":"8a190298d0ff502ad1c7294ba6b0abb3a290fc905b3a00603016a97c363a4c7a","impliedFormat":1},{"version":"5ba4a4a1f9fae0550de86889fb06cd997c8406795d85647cbcd992245625680c","impliedFormat":1},{"version":"57c98d540df72bdc219a9d4e23e7c1f844459414e4e62eda3439067fadf743ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"723d1d05be7e263d358580c9bba607944fdf6e5093e7bf62a2f578754b779390","impliedFormat":99},{"version":"7a59476a46fd4b3e1522e9c6ec6cf436b6d5ab8ac97a17ae867aeb9cdf0371ff","signature":"57f1ad6cd433ecc0e78e4616e780d4db68642604162b65747c70a6142d28e49b"},{"version":"ff3e228e751934dd42a9f05cfd75bccfedfb529eda504ee0c4f0d184da345050","signature":"4a1201a691800bf407a2703017b769c5ce1a53418279b7682e4cde1afc7dc6d9","impliedFormat":99},{"version":"ee70ae40394baf9312c35363c42fa429ba3e037ab10cf767a184ec38d24b5427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b5b7c87e72f384980ca1d92c4f54d6c30b2f099556e3843588073cfe0a0a893f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f329dfad7970297cbf07ddc8fce2ad4a24e2a3855917c661922ef86eb24dd1f1","impliedFormat":1},{"version":"841784cfa9046a2b3e453d638ea5c3e53680eb8225a45db1c13813f6ea4095e5","affectsGlobalScope":true,"impliedFormat":1},{"version":"646ef1cff0ec3cf8e96adb1848357788f244b217345944c2be2942a62764b771","impliedFormat":1},{"version":"c864801e02e8547ed49024b3a469d6fbf600ee240be6bf413bd6149f26241348","signature":"90ec9100c29e008c3d9194acd818e2cfa6dc6e177154bc8e10c5959aa35619ed"},{"version":"6c8c958cc35f90494284a36edeedf503f3a56a93960016a618a1e587d19d86c4","signature":"2b02e2635e94d92d8a4c1fb05177aa1f9bee04c362dc8600559080aafe963e14","impliedFormat":99},{"version":"9c947051913ac9feed2de4ec57656a9f38ef4bccd22518b765f5877c69894082","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0260838d2473bd7872f0fcef24bdfebc247cdf5c95217670ef50931bf93f2e91","signature":"2affb08b140b8e89210e4b39ed75b00cd5e5ccc3553a80bb3e83514fd2461e7b","impliedFormat":99},{"version":"e9e4ac4ee6a2c612f408e17bfd9bd5398bab08053196d8e8c6cf64d8a7335a51","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15128feed70d09b1e4f994cee399f093af7c7c42e224db77f3ace502a457a2f2","signature":"c7108b0b3c30b5aa5fe1fb0c2399dbe7da3e7730cfdd42e7403a0402394bf466","impliedFormat":99},{"version":"3bbf19210a7e08f50ce1518710ba0ffa8e13a0d55d78fdf3cb62cbad44d30e1b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca273ec7d7789662ab7ae9e00a4556a0e42416d6f8a13702a5746b5ea6862061","signature":"dc89f83d1e61d147d010a811cad4539c273b3ed227aabfa8a9a130b4180d2cd0","impliedFormat":99},{"version":"2e71945b350a81ff50fd4e21a3660e7e6055a5cd5691d6d8d867d0e6f10cf313","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5d0e499d96f070dc2607f23d55fe54ac074d1a840b6505e1978f70f57232cf7b","signature":"9e4d212471d83031de81b7c76834be81b4d32b5eb573cda6c61023d1cd5f326f","impliedFormat":99},{"version":"f20e59aa1f8ad6e7dbfb10f7c7147773dab8b5d8e4d59eeeca34944b51e4dd14","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"84daf1ace1a44a36500dd4dbedc8a92e50c4a1c1e935ef732dd18b7e2fb0aaf7","signature":"5cbfab9a555788720d027df70fa580bd727ad40aa2d325eb0b04ec4642f9faf8","impliedFormat":99},{"version":"1784d27f3095418bde9b61739c7ca7bd30b1bf05c95bde803514bfe48ce23f57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"e62fecd6655ce82858142ac7225caded25ac9b7da81632bec4c7c054983bfc68",{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"3255b97f3f24af29c79cc1aa88004efb13b6285ebdde0a567bf32e19bb65250d","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"8589487932fd916840218fdedbb143741d22216ddf630c70d401ee674c448e1e","impliedFormat":99},"c0fc68a185e7479c68bb3304bf208d87e9d8bbe9a684302d06c40245670cabf1",{"version":"31caf28f1dc08edfe3cb8e7c4011ced09f4bf3f5f725c50e5dc0b5ae573b498a","signature":"04a996928d0f8d5efd87a2990c4f4ce70e00fd0c975971fcbc570df7961daee5"},{"version":"502c011687aee1a48fa221d356f8f2d8eeb035c0706e8f8e9ef0104660cfc51d","signature":"1a734856e43cee0599e8a537f131cbaa1e9290b47f2b496fb504f95e252b8495"},"1a7163e59864fbaa14672752a70c8b38086117e5a14afa00893611dfc2fa803c",{"version":"348b8169a6c19556863ffe85bf1fe1ddb0006affc951bee6eeb7dcb3a2d6eb30","signature":"f12359b22cbaca86f938ddee38c0c33924e768a93042ad939fc2288f2471e5e9"},{"version":"43ee1831235987ca593e76b22b4116009f1ff6fb0e7a3fa6bf1e5df1420fd6dc","signature":"a05af3719b211bbf59b553f0760633dc3095778bb0171502d7bb7342a54d3b15"},"dc4085267e01a46acdc4e014d59e60d40d6acfe0806a041e857ed5b91c688c5f",{"version":"45311c218ffe1c8393be29ebab04527a9167c2e48a5fdb15adc0f22cd541614f","signature":"dfb3bb27e47ca92752033b3171dbe6a1f8e9404b34577d1b16eac221e1745a2a"},{"version":"093616375ac2af574eac9fdfcd18193c3f9394e1b1d4d8c79d2e6068790ac100","signature":"335f66607c072668fef3e0563ad5619a7632e75eef9f85740e84e35523d1ee82"},{"version":"383fc1e3823bc2d2cccdbf51be644b7f2297d6d04190008c1ef7ccf82eed9b76","signature":"77658513755ac8d8ad639e6f969539b6d98cdc9ea85a2eabeb33fc94a839f395"},"eb2ca2e825628da3e61a98cbf8338f9af1da351244346ee4955b09972d60e7c3","63d36b8af9723f5416b3c0c7270f4094ea417909c8196a01775da5ecab082c9c",{"version":"622276611290ba3952b0585e626d99e70ae18719e1eac03fcfd026e8a44cffe3","signature":"257d9dcd4c3e61e552ab3f34ead65de21367da6f92f9d62979a26fd748982849"},"6b43dfa5e9c9d89bcaca0ffe7da88f34e20d760ca158398a3276cef61f738c4c","83a3f59d023ec0384755cb114026dd5bf1bdb12fa59e166330486c05fd6007c2","63d9dc36da9bc05dfdb5ccf23b5738648c073c545320dbb619c6b0b27ce304b3","c01e6f5f2acfc5e3a04850fc1a502350f37b59192124965753a18b8c8c0a3d6a",{"version":"bc7bc237e289f8d435d34601a22322d303d64d497e25d80d555f06f7acc34e4b","signature":"7da246bb1c2b2ce4879114715c5bd7714bef80824031c70e814efa143acfdd51"},"ea148617618060b428a28a47935b7d220bd76a20c909c3f55b15dcc94fee0b89","9a7b469bc32fae75951dc069e760b7945d91829873247f00a5ede47eddfc5d2d","5b8eb6e16859a5d0b869e2607f6510cbdb93ff3b24942edfb5098f2e6b07e773","983793b81b9d3f63b32a2b4aed4cbecdd215d0c00487729c5ee788f9d8a77c13",{"version":"89121c1bf2990f5219bfd802a3e7fc557de447c62058d6af68d6b6348d64499a","impliedFormat":1},{"version":"79b4369233a12c6fa4a07301ecb7085802c98f3a77cf9ab97eee27e1656f82e6","impliedFormat":1},{"version":"2b37ba54ec067598bf912d56fcb81f6d8ad86a045c757e79440bdef97b52fe1b","impliedFormat":99},{"version":"1bc9dd465634109668661f998485a32da369755d9f32b5a55ed64a525566c94b","impliedFormat":99},{"version":"5702b3c2f5d248290ed99419d77ca1cc3e6c29db5847172377659c50e6303768","impliedFormat":99},{"version":"9764b2eb5b4fc0b8951468fb3dbd6cd922d7752343ef5fbf1a7cd3dfcd54a75e","impliedFormat":99},{"version":"1fc2d3fe8f31c52c802c4dee6c0157c5a1d1f6be44ece83c49174e316cf931ad","impliedFormat":99},{"version":"dc4aae103a0c812121d9db1f7a5ea98231801ed405bf577d1c9c46a893177e36","impliedFormat":99},{"version":"106d3f40907ba68d2ad8ce143a68358bad476e1cc4a5c710c11c7dbaac878308","impliedFormat":99},{"version":"42ad582d92b058b88570d5be95393cf0a6c09a29ba9aa44609465b41d39d2534","impliedFormat":99},{"version":"36e051a1e0d2f2a808dbb164d846be09b5d98e8b782b37922a3b75f57ee66698","impliedFormat":99},{"version":"d4a22007b481fe2a2e6bfd3a42c00cd62d41edb36d30fc4697df2692e9891fc8","impliedFormat":1},{"version":"9d62e577adb05f5aafed137e747b3a1b26f8dce7b20f350d22f6fb3255a3c0ed","impliedFormat":99},{"version":"7ed92bcef308af6e3925b3b61c83ad6157a03ff15c7412cf325f24042fe5d363","impliedFormat":99},{"version":"3da9062d0c762c002b7ab88187d72e1978c0224db61832221edc8f4eb0b54414","impliedFormat":99},{"version":"84dbf6af43b0b5ad42c01e332fddf4c690038248140d7c4ccb74a424e9226d4d","impliedFormat":99},{"version":"00884fc0ea3731a9ffecffcde8b32e181b20e1039977a8ae93ae5bce3ab3d245","impliedFormat":99},{"version":"0bd8b6493d9bf244afe133ccb52d32d293de8d08d15437cca2089beed5f5a6b5","impliedFormat":99},{"version":"7fc3099c95752c6e7b0ea215915464c7203e835fcd6878210f2ce4f0dcbbfe67","impliedFormat":99},{"version":"83b5499dbc74ee1add93aef162f7d44b769dcef3a74afb5f80c70f9a5ce77cc0","impliedFormat":99},{"version":"8bf8b772b38fc4da471248320f49a2219c363a9669938c720e0e0a5a2531eabf","impliedFormat":99},{"version":"7da6e8c98eacf084c961e039255f7ebb9d97a43377e7eee2695cb77fec640c66","impliedFormat":99},{"version":"0b5b064c5145a48cd3e2a5d9528c63f49bac55aa4bc5f5b4e68a160066401375","impliedFormat":99},{"version":"702ff40d28906c05d9d60b23e646c2577ad1cc7cd177d5c0791255a2eab13c07","impliedFormat":99},{"version":"49ff0f30d6e757d865ae0b422103f42737234e624815eee2b7f523240aa0c8f8","impliedFormat":99},{"version":"0389aacf0ffd49a877a46814a21a4770f33fc33e99951a1584de866c8e971993","impliedFormat":99},{"version":"5cb7a51cf151c1056b61f078cf80b811e19787d1f29a33a2a6e4bf00334bbc10","impliedFormat":99},{"version":"215aa8915d707f97ad511b7abbf7eda51d3a7048e9a656955cf0dda767ae7db0","impliedFormat":99},{"version":"0d689a717fbef83da07ab4de33f83db5cbcec9bc4e3b04edb106c538a50a0210","impliedFormat":99},{"version":"d00bc73e8d1f4137f2f6238bb3aa2bbdad8573658cc95920e2cdfa7ad491a8d8","impliedFormat":99},{"version":"e3667aa9f5245d1a99fb4a2a1ac48daf1429040c29cc0d262e3843f9ae3b9d65","impliedFormat":99},{"version":"08c0f3222b50ec2b534be1a59392660102549129246425d33ec43f35aa051dc6","impliedFormat":99},{"version":"612fb780f312e6bb3c40f3cb2b827ea7455b922198f651c799d844fdd44cf2e9","impliedFormat":99},{"version":"bcd98e8f44bc76e4fcb41e4b1a8bab648161a942653a3d1f261775a891d258de","impliedFormat":99},{"version":"5abaa19aa91bb4f63ea58154ada5d021e33b1f39aa026ca56eb95f13b12c497a","impliedFormat":99},{"version":"356a18b0c50f297fee148f4a2c64b0affd352cbd6f21c7b6bfa569d30622c693","impliedFormat":99},{"version":"5876027679fd5257b92eb55d62efee634358012b9f25c5711ad02b918e52c837","impliedFormat":99},{"version":"f5622423ee5642dcf2b92d71b37967b458e8df3cf90b468675ff9fddaa532a0f","impliedFormat":99},{"version":"70265bc75baf24ec0d61f12517b91ea711732b9c349fceef71a446c4ff4a247a","impliedFormat":99},{"version":"41a4b2454b2d3a13b4fc4ec57d6a0a639127369f87da8f28037943019705d619","impliedFormat":99},{"version":"e9b82ac7186490d18dffaafda695f5d975dfee549096c0bf883387a8b6c3ab5a","impliedFormat":99},{"version":"eed9b5f5a6998abe0b408db4b8847a46eb401c9924ddc5b24b1cede3ebf4ee8c","impliedFormat":99},{"version":"dc61004e63576b5e75a20c5511be2cdbddfdbcdff51412a4e7ffe03f04d17319","impliedFormat":99},{"version":"323b34e5a8d37116883230d26bc7bc09d42417038fc35244660d3b008292577b","impliedFormat":99},{"version":"a5dbd4c9941b614526619bad31047ddd5f504ec4cdad88d6117b549faef34dd3","impliedFormat":99},{"version":"e87873f06fa094e76ac439c7756b264f3c76a41deb8bc7d39c1d30e0f03ef547","impliedFormat":99},{"version":"488861dc4f870c77c2f2f72c1f27a63fa2e81106f308e3fc345581938928f925","impliedFormat":99},{"version":"eff73acfacda1d3e62bb3cb5bc7200bb0257ea0c8857ce45b3fee5bfec38ad12","impliedFormat":99},{"version":"aff4ac6e11917a051b91edbb9a18735fe56bcfd8b1802ea9dbfb394ad8f6ce8e","impliedFormat":99},{"version":"1f68aed2648740ac69c6634c112fcaae4252fbae11379d6eabee09c0fbf00286","impliedFormat":99},{"version":"5e7c2eff249b4a86fb31e6b15e4353c3ddd5c8aefc253f4c3e4d9caeb4a739d4","impliedFormat":99},{"version":"14c8d1819e24a0ccb0aa64f85c61a6436c403eaf44c0e733cdaf1780fed5ec9f","impliedFormat":99},{"version":"d36518bd617ff673c7d9f372706f241932a43f27673187f2a8472e93c40041c6","impliedFormat":99},{"version":"f8eb2909590ec619643841ead2fc4b4b183fbd859848ef051295d35fef9d8469","impliedFormat":99},{"version":"fe784567dd721417e2c4c7c1d7306f4b8611a4f232f5b7ce734382cf34b417d2","impliedFormat":99},{"version":"45d1e8fb4fd3e265b15f5a77866a8e21870eae4c69c473c33289a4b971e93704","impliedFormat":99},{"version":"cd40919f70c875ca07ecc5431cc740e366c008bcbe08ba14b8c78353fb4680df","impliedFormat":99},{"version":"ddfd9196f1f83997873bbe958ce99123f11b062f8309fc09d9c9667b2c284391","impliedFormat":99},{"version":"2999ba314a310f6a333199848166d008d088c6e36d090cbdcc69db67d8ae3154","impliedFormat":99},{"version":"62c1e573cd595d3204dfc02b96eba623020b181d2aa3ce6a33e030bc83bebb41","impliedFormat":99},{"version":"ca1616999d6ded0160fea978088a57df492b6c3f8c457a5879837a7e68d69033","impliedFormat":99},{"version":"835e3d95251bbc48918bb874768c13b8986b87ea60471ad8eceb6e38ddd8845e","impliedFormat":99},{"version":"de54e18f04dbcc892a4b4241b9e4c233cfce9be02ac5f43a631bbc25f479cd84","impliedFormat":99},{"version":"453fb9934e71eb8b52347e581b36c01d7751121a75a5cd1a96e3237e3fd9fc7e","impliedFormat":99},{"version":"bc1a1d0eba489e3eb5c2a4aa8cd986c700692b07a76a60b73a3c31e52c7ef983","impliedFormat":99},{"version":"4098e612efd242b5e203c5c0b9afbf7473209905ab2830598be5c7b3942643d0","impliedFormat":99},{"version":"28410cfb9a798bd7d0327fbf0afd4c4038799b1d6a3f86116dc972e31156b6d2","impliedFormat":99},{"version":"514ae9be6724e2164eb38f2a903ef56cf1d0e6ddb62d0d40f155f32d1317c116","impliedFormat":99},{"version":"970e5e94a9071fd5b5c41e2710c0ef7d73e7f7732911681592669e3f7bd06308","impliedFormat":99},{"version":"491fb8b0e0aef777cec1339cb8f5a1a599ed4973ee22a2f02812dd0f48bd78c1","impliedFormat":99},{"version":"6acf0b3018881977d2cfe4382ac3e3db7e103904c4b634be908f1ade06eb302d","impliedFormat":99},{"version":"2dbb2e03b4b7f6524ad5683e7b5aa2e6aef9c83cab1678afd8467fde6d5a3a92","impliedFormat":99},{"version":"135b12824cd5e495ea0a8f7e29aba52e1adb4581bb1e279fb179304ba60c0a44","impliedFormat":99},{"version":"e4c784392051f4bbb80304d3a909da18c98bc58b093456a09b3e3a1b7b10937f","impliedFormat":99},{"version":"2e87c3480512f057f2e7f44f6498b7e3677196e84e0884618fc9e8b6d6228bed","impliedFormat":99},{"version":"66984309d771b6b085e3369227077da237b40e798570f0a2ddbfea383db39812","impliedFormat":99},{"version":"e41be8943835ad083a4f8a558bd2a89b7fe39619ed99f1880187c75e231d033e","impliedFormat":99},{"version":"260558fff7344e4985cfc78472ae58cbc2487e406d23c1ddaf4d484618ce4cfd","impliedFormat":99},{"version":"413d50bc66826f899c842524e5f50f42d45c8cb3b26fd478a62f26ac8da3d90e","impliedFormat":99},{"version":"d9083e10a491b6f8291c7265555ba0e9d599d1f76282812c399ab7639019f365","impliedFormat":99},{"version":"09de774ebab62974edad71cb3c7c6fa786a3fda2644e6473392bd4b600a9c79c","impliedFormat":99},{"version":"e8bcc823792be321f581fcdd8d0f2639d417894e67604d884c38b699284a1a2a","impliedFormat":99},{"version":"7c99839c518dcf5ab8a741a97c190f0703c0a71e30c6d44f0b7921b0deec9f67","impliedFormat":99},{"version":"44c14e4da99cd71f9fe4e415756585cec74b9e7dc47478a837d5bedfb7db1e04","impliedFormat":99},{"version":"1f46ee2b76d9ae1159deb43d14279d04bcebcb9b75de4012b14b1f7486e36f82","impliedFormat":99},{"version":"2838028b54b421306639f4419606306b940a5c5fcc5bc485954cbb0ab84d90f4","impliedFormat":99},{"version":"7116e0399952e03afe9749a77ceaca29b0e1950989375066a9ddc9cb0b7dd252","impliedFormat":99},"3654ba818fbf4ac2c49aa3dbb050b912277acc71b6d5e4f434720c27a1a68f3d","82783f40f1fb9a547a1c74622a4cf4c671fb927c57165ebcece5cb133a68f4fb","2a7a18a2cc9b4656d9eb1d5f4fd0e3f3466f600c32ea8148643dd8c909bb3476","d9cb0facf05859f0f35707063253d8b55d8fbb565afb642c0edbd72ce77817e1","c0d0f32efeb6b747b535605fbc150723df43935937ad768694508546bb05cfd1",{"version":"c8924b198de81de4222b2f0b171e9262f80bdf62beaabdf8ee7aa13b27245871","signature":"2f5adff38c8a75301b364bad4bd26f79cd3a86bbdd3cbba4541673d903d47b4f"},"0702499aa6384244a89e13df162163ed41949de76518a8580c8044e783876dea",{"version":"9f4f376e778fd1560de1f3afa4b8ba1971bb8bc5f272324ea61e65e15b685f1c","signature":"6b211c08718dabbbcb8d48382a8416b20d9c90e3a7a3a9f8dbb192baa29018dc"},{"version":"b16a9573271f151e37a10543a8faffe67811ac8570d87054108ba01799b73ba9","signature":"c274af3f97f26f9143c42701bf431c06ff0af56cd5b14e86c661a294f335d8db"},{"version":"48f96604f28e1d321ea8c94e7e5cc889f4ab3720d92ed9f412ac7dbc2931a1d9","signature":"0d095606a67e17da85041e7a56c4d15c377ff643b56ca69eef8b42d670748bb2"},"1d899a3b3c762069c87a2363e38fc467d3fc0c17f6d22f98de3a98e1a691540d",{"version":"3f56d8959b17508732d17ec607714398e73009d9eaec652c8fb9d5891d1c7c7e","signature":"b4f0b3be4ce1aab443b18ffd19432c63b332180881e573f620cdf4d257b5426c"},{"version":"61c5a30df40ffd1e5917e3486964faf57b3247e4727ded537fdb0a37ff8a3050","signature":"9a4fce133a99a8e4f1ae6d4d95d1b5d86491c18fc13888f5ee534823e1f1f830"},"0be20053ed11b126b77183542e054ff77548fa8e5910baa789512abd13be724a","fbfef1742d67c1f5379b4cc569959b96ef446e074d63ca93921a4a86ed3dfd18","aa40d71dd57a81028c76d4080716d6dde78ff51e92ad1460e5f973adbfaa193b",{"version":"955ae27fdc755f32aabee0f82c2db6b3d8505f99551cc8376df389eb90e7c84b","signature":"4675797b0de56fe3c5a6e468df193709c7f066a244e2da0d02690f193eed5345"},"4eb900416055b66a7063f285dc36561ccd1d276de8a637165beef04a3b3aa162",{"version":"7abfcb37b73f3b4fcca65caa3cfe40a12b8a89fafcfa783f93605acdddb0cc25","signature":"1a574fe33afec63182b358ca9e29944cbdd13c69413c53fcbb8e924018e33b8c"},"4bbc8169c9196d2768927f96e35712502c0445e653a5d427f670aac13452f77b","e712c5b04b15a0dcfde9b382f466dece347f88369386bde440848fe8e2501a21","0961df49eea10f9fe072e10c83c8bd96505bf9b93cb0ca6fa1d10dd3d68e506e","c9835b14ddc4e4115f493b814c646b64cc592bd18a8168c0b94fad83406aefd5","e99bce1c616138462e9ad01d669d9667759a66a549795aad43cbd8d3829eabd2",{"version":"88cdbc3bcb4689a70130597de7c941b2450bb2760674a02ae816e0667a1958f6","signature":"6dbaf13dab6dc2db0cb7312fba7996ca7f548c7929bb627315cc89b43bf93ada"},"93fdaea06f53eda94b236d54909091dbd7046bc96315b59d224962d4a95299da","5437c086fa05daccd0b205f10e71c34f7a5c65a60b70c449a77d71c547777399",{"version":"7b4bcd71a2ca99183c38b93f34926a94615833826ef27f05dcf62494e196325c","signature":"0646934539246310c9949fff3507ffa197e60e50821f7ba77b5518241bbfd7af"},{"version":"55a9358103d4d3fc812e7226cbf54ec885ab5d274c3f0fd7c7b89138761420fe","signature":"09ac449d6431aaeea979c72f664e0179ac698d66b9dd695199bcc985f21b10b7"},"332a923c1b65c0e3342254ffa34852cb772db010cdf62e81fc77b9acdab179af","151fe72ab1a9917c0822dcc922ed5d1ab5999e4cc39490a329d5bfd088223de7","33289c6ff4c33c404bf9f1b11602158811e4a758d02f7ac67d459c72043c6a4c","e991b473ae7d3407efad7d94f21cdbbf38e0edceb0ca77a0937896db1a69a432","139ab031d84be958f97af2a882ea123ea54c99cc05f4a4f2c3afebccc1f76059","4e3cef7add4741ef800199b5d9f6f45f3b05b4cbd7b4f9713b680370488856b2","83633eaca29decaf169278318269ef988fc92d0b9a47531dd5301ba069652fa5","d100a3684e4d3e61492477eafe8fb250d6463f83e66b6739ca270b99ed9ccd52","2564e83977f854fbd3ce140f2d86f6992c1332945634a2f803306596ee0bf69c","b66cadd5b2da034134f0112a8d584d76eec9e8025f23eb6b556dea5aa74fe3a3","3402b3070b7f9a2c6ea7f3082c2ed7f2f0d8c589badb6f3ec62044c3b7f0184c","3db03edfb97a8c0b482a94fc0280ae10207fc529842dc268fb0cad92148a638f","913dd719a5b5a91dfc16f29bbb6af8d1f8aa0f2b141eb4320cb2ff4f973bec35","a4150749c6aa9db1224cefcb07931a35d19f1f8f00f4b79674f6d25c5423f181","5eb87fa9a117af0d5672f63271c070d9294dc2592a8a71c7f67dda52635bb8d6","132f976bdb7a85c0fc4a180cb4673d199394e4b38feaddeae1d0939c90df34b1","2fa9260ba8c9c073651025b09d81b2da143ed8a4d7334d20a0f7f7eaca3c3ec3",{"version":"2023aebac248da544760947901d5fe7aaa214eddb7c2d7a92d33bee0650ffc2b","signature":"8490d17f8c61b6b1b705fb66b5d5e12f22aa29bf3b5ac54718fb95a75513d46f"},"4658529914e4785a4ce0213e8cc9af4f2cc86adbc3bef7cb6e9836ca17d8f0fb","83b0843643676904927c595db1a32660cf4eff0ce34cf374082588566851e37f","ba42a3ea09e763f637b9f8b040704c66d052c7e0a4c3526fa084516fb34cac0c","c742723bb689a361dc0e32cdacf7f4160145254716deb013292a2f45e6f5e1ab",{"version":"7b6e8d32728e05107c573c5dc2b6fe9cb14332dd7c82fb530093a840d6b59dc7","signature":"22146890ab30bea45bc289ccc48192249fe1cead53510eda7d9af2b09e065189"},"7a81b4127658262d3b44f32ca2fb5589bdd370f3c971b7023ec2bf0fa80208f3","0471574e07ea402de091b23741e7759f0293fc476645407c75e8807fce4d508d",{"version":"54a60dfbef03a8f34a21a1b21e6f8c6b991390b6bdca741071f0e9aa378b4610","signature":"2e695038b1f0a6040ae88a1a869e11cc466e03a7b13b526efa90b3ebfcb0068c"},{"version":"8fce03e56480ecfdf0458b4f97596020c7740577638075be2337984e2f7e0c27","signature":"b86d5d8bd5104f1ab29d23cd5be61bc514b8146a091257366678f5d99000a957"},"503b83a8c33ffdf3a4fae4b560df55b7e98c0722c4ea69e32b7e71427888f440","ea6437c6eda871607d5a01adf7cc5afdcd66f674509289cf2c226cc8b9734773","61a5d2a3dd261b3c2b751c713d088f6548be6199705c2c9c5775d12bba1b8fcc",{"version":"24863e2f4b2b1bb3a3450294a76b5e0eea7b3a2e295f2225745da9c8592ee216","signature":"861096a3a6ca8f6ad72022664dd68b02ce3c37ff2d8f05354e1cd3fb3342b366"},"ca5c0df4cf20a1e1a7b2961248f35767785a03058ed250be5afa76c2713b202e",{"version":"9859454fa6df442ae16cb0ac31d0c02a0a85bac28c82b9783e8f370adb33b245","signature":"f6b87832d9447b2e9d26a9676efe78dc75cff9279ee64a499f3e4360d22f2730"},{"version":"583137ad8d520191737844c217f6e5d839105c7ec976abbccd46060ed8cf928b","signature":"213e8f64d2aee549df8047a587e27018fee7674c72407c2a191a634d8e05ae4f"},"92f78731c5130df45847dfa1a46a00a27686891e38ba51f116c586e520498ee7","25cc87856525e88d4007f5f84251a00b6c47b90fb435ad8459037f18ba6b8a11","ce0d61b977618ef61cee89091bf0bc0ac139c64da5b41080486c84f0002e755a","be2f617d92b80f8cc4e567b59cae553cecfa618a81b93ffd974ee7f2a94ecdfe","f264d234b8645ae1bdd723fdeb71a0314d77e06d8e7f6aabeef77c6607acd56a",{"version":"a47d50bd2f57719021eb5184bc1314ce3f5837f2f78c4d25858e15c721e07ad8","signature":"516fbe6606f98a2736d92faf0b928b6f1084ed15368ba3cc8f055ebec38fb818"},"b26e8bf9c6f7701c5fb76c46235e05380573408867c4d57f68000bb3f543937a","8b726542035580da854bccfbea23223e0fdac7df070292db0856bc04cc3989bd","96164479311e65dfb12975f7cb97fa997328e7f94a0174377f4d6b8884e9ff83","7ac8e07828dcc1a5e01fee4cc13c788dcbdce430795ff0eab6d39e7b3c095254","87a05689f17c2271a7e63a0c5dfb6734c823f69bb03a6e89cb558ddca0db79fb","f71d2d69ba2857a6ba490861f2ee808e7c362499c19e5f2fd350d2e48b990d93","3124c0b40c96a6ec3df9a6053d4107ef952b5353c72ff85ddfea0c56cfcb56ff",{"version":"c09242a97ddc30a0ed86ad6e481998869972d43d60027ea2dea569dfc4ff79d6","signature":"0fe3236fcc755ecae3aea84e78a420d59c851fc19f1623254decd6408be9747e"},{"version":"15f6b22a1a9dcb5d6ae6b4cb465b0c628f5d065489e0250ce46921de4c343df6","signature":"80366674fad0d2eb8bac45ad76aacdf3112cabf2e032fee7755a61ee0fd9914c"},{"version":"ed078b6e6e7eea82b93d3e16aecf4e5264db34569ccecf89ea244e130a0fcaed","signature":"92c9c93878f36fe51e3431455c359340aeacd788cd1f7dc1ba24faeb4fa87d3d"},"1d6bf45b076d03144b3058c0df777f1efec117c18e32e691f41bd9787514eea5","d86ab7f08858c5b466e689581092e41d03390d1b527a476cae72331305dcec24","f3c042ee7810ec25d7db134620b13c2610c73f55882f6ab8be13e27252117d40","7c7e71e5e39435b48e0271eec28ab242ed6f1a65e740a29932cb83b9e617c83e","cc60fd980e5701b006200ca499fcfc09b7ac317785fe53307bc9a50fc4bec464","852c7b0aba9aeccc21161dd2e0fbf11250730018343d88986bae2f905caa3b40","d0b7e2a5548f56597acc899917e354c348407549ded42ee13332c83b5c045bfa","a030ccf7a13e613b354dcdbe5f197a9b7fa0819a4d0d8ce7d1ed0aafdaae48ae","29228a2fd8fa9e03243e2af185473f8abfeb407cdbe4f72ed329bdadbdc484b8","84ee6c19db9aebc0f267dad9f38b59769a344e20ae762030ba2d8db629f925ce","1314a35a2551c127f4844fb29fd49321ffaf3701afc6ed7131c90833121593aa","9d764bbb43ce204d8fada7418d0681720eb5fe4cc2bc14018a1ad6cff876aa56","bd49ae74cc4c2def51418f9bfb393a8b303c05972c2fd8bdbc0a7d9c88d2bbd2","e12eab448b2741fbc58fd99df25cc662d647313a3f5f6ad7cb0d168b35c512bc","ac37a6d8ed49983b7045356b04ad84f58799843ea2afdc53a08f2614c11b662e","d1fa13f317d3637fabb663edd46b39ccdc420e0c5a3913b7fa4e906d99497cb2","653f388cac26465dea74d7a695412dc4285bff051db33e18a576e941c79842a3","9ae477f6e996170dcc13a79cdfa0a2b709f3eb50b6de974c1ed15fb2e32eb98c","62fe41879cae66d14c865c973556b0e24a904d9c6445557f5414007a236ea56b","071f3deb2c96ba5dd81668fcf4f909d6402b64c0c053846ac9d2aa561a136b03","cc39c13ff8d9df5c8ef59f0e717e3b5b4e96f7dc05859a531f1f703718d4192f",{"version":"10acfb644142d4c7da056485bd721efacd6ee61c0543c5762862a88f4ec9be94","signature":"1857ecaad23982cebb7ec28e547ecdb341d40713e95988b7f7d9da4c20f9646b"},{"version":"d337b2b575efa0ae09ab5b8bb94ca907728beb48ad4f9a43c653c247ebdf871b","signature":"be1b859329d61b0f74fe1393fd56c1542d1807ddf5a035b27a7153c471f53e32"},{"version":"9af90dcfb3df248fa3f8abf701c073fa30d6ee7b5758ba4de460594c56e4af8f","signature":"5b09eaef203954c253a646fea5d827882c557488a4ec3fd8cc50493e9ac5ef4b"},"c44bd1c97aec9b3731f94e4ca33797b718f355040fd1a3531cd1cdf72a092f98",{"version":"c7082c44bffd6cbe3c72aef8e57431fbc1d554a0db75d11b0c38fe4e213545ee","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"9d116a47a60bd0dfe34a66d2a4857a9a73bf2406915bd5b19bab3d1f42b8115f","a0460c3775eae1effe1641d510f8cbd74a3b430951edbddf3b8ca9cddf732bce","6aba6fd003ec6b75e94e40335a2315213295714f33e38b4164aaf7bdb2a3aae0","b72a531a79d4cb645c43c6782dcccedaa609b2c7efd71547a56ee74fad0c3dd0","53cc94938d41698f1994b5de600edb7e89aa936944ce1d2955720f69be6d460b","259cc7fcae5804316e63f5d00416e69fe28d9a3bae59dd20c767d714626dcd5d","f5c2a1cb2d8619642ba9bd687227fe3ed43787235c8e980c34aa844645728465","295e589e5b8aa32d6997d6c604fe50ee40f25b42ff0134c5167c651e27c332cd","0f4d43d34056d61a57ff787c29fbe5b2ef301a333ba157449ba3df4f0a45649b","254a9df28b54e73e3fae641287cf5e938315c436c42554e7f39970a5f41c8f9b",{"version":"f4edf0a9027ff9279ede897f9c304c9f7e42c93170d2b2f66570698048e887ec","signature":"abe72455f516e18ed06bbb7e01ea1450572ff48cd86d4153c5853474dae5e8c1"},{"version":"d45ebed0a7af7351812afbdfe2cbfc7f88163d72bd79807532bce53cea6e9cb4","signature":"4ae59f31cbb1d8f65520a2852714b43fa800e651ddf50dc1a3e68c56d6537f9c"},{"version":"13bf5a8573fc1891a43ebea36a1ef5517d59f06c22f6e3bcacd8c4fbfdc0be76","signature":"654865d2998e7e7aa50e64fba9f1dcd717a7f378ee65b7e40311035c011e91ae"},{"version":"452510133c135fc44ee7c3ca38c2169280ba85989504826040196555e2b03c92","signature":"0e6ee02a5692f58fae9680a1c9b1dc94d3af9a97456ec14bea39bf4a9e5931ad"},{"version":"2f33f28160bfb02bedb63ddf4f6a8241cb2ff6967041643a0d7ee0909f75c3e6","signature":"e7d315801dfb219e04a94c847f0ae759b7d2b451783d38974a72e7b695436803"},"04fd50ba4fdfc24324446f14648d1c95fd08fb7c3f91b6de6a17ef503f052e36",{"version":"fa160d0c5713d8259b2648497fd70ba7c7b7a6602a840c574eb1c0a6f46e0454","signature":"4951a5459b063778e07d022547e89168c941ebe6bf458f07ea66f68b5f2e8de2"},{"version":"e74268ffc9270115d1d343bcbba879e819fb149e693a0e0524e1f321bd55362e","signature":"72dcdb99ca1e3ca76a476fa8bc73a89768a7404721c1ff2266d2c649bfb9e11a"},{"version":"ee82aa0ef404999ad87bb7a2baa1d75b0fd94aa2a0ff93bd673b39f7901fc37d","signature":"080b3addbb0d6625d7af627d88f46c15af2dcb962ca35a4715510d924cd470db"},"9267691f6b1c001d1ad417d316eb19e3448db243cd5eccd9e7fe1933dc80303d","c256e102702b489676e3738666b34d985b2bed2835c1c6a7da638a2442ac8d88","1fbf86d5c06434863bf58d1e0b464481274e989244d9553ff867d4f742ab0832","5bb0181380d7d4f24d5b59efd31845ed2835be7a0e6ee2fa113735e2d14f7be7","42f84fb7fb1bdea79ffd6b67b36c9906b21f0457783277abd39c047f053b3e42","65c5c1e3cfa7e96ddf00b29103d558810220aeec2c5e15bb281ff6bfb7e61148",{"version":"279e1abd50429cfe84b8dd7cb57e9684d8ca7864af5c3fcf853efcacf680830c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"578015da0ecf6fb49aaf4d86e90e8ce9f46a7b6ac293ca9d810a291d42501841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"e71fe8dc39bd428a96ca05a044b5a87e7fdb21043102d1eb4fe32f758e88092d","9ee3a3696c5ab964b6ba7d41121d5b4d91ed7d70d2ba7cf0dbdcfaa617d19735",{"version":"19cbe4c67f1b32b90b7ef46d4bc60f25d42dbb6cb95f35da6d41c72ede463d4e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c22f3ec19a761c9989950f01e38fc127ef63f2c0a3300cdd0b3b54cc28dc75c1","a31abfd6a1707f3d3fa8fcd6380a7cabf458285d7190030215d8a92b0c360827",{"version":"bc5ea5422be0834017b7ea3550c58d61ed1f7f976feaa321634d7fe60a0f26e3","signature":"117ec0eed14f00ef3524ba8069fbda8cbb45fde70d22b16ed255473b2108f1ce"},"f32c35930719a4f9920de8c496365ec008e8cdeaa312c8902b6ee6eb8167da17","22d768ed04ecd7cea3fc40851466b04fad6078e979dc2ea835646413b2a05acf","0ef3b705c81fb51f3b20c828fc50e9d2902644ce8343281c7a5c057da23c5f86",{"version":"b1535397a73ca6046ca08957788a4c9a745730c7b2b887e9b9bc784214f3abac","impliedFormat":1},{"version":"1dab12d45a7ab2b167b489150cc7d10043d97eadc4255bfee8d9e07697073c61","impliedFormat":1},{"version":"611c4448eee5289fb486356d96a8049ce8e10e58885608b1d218ab6000c489b3","impliedFormat":1},{"version":"5de017dece7444a2041f5f729fe5035c3e8a94065910fbd235949a25c0c5b035","impliedFormat":1},{"version":"d47961927fe421b16a444286485165f10f18c2ef7b2b32a599c6f22106cd223b","impliedFormat":1},{"version":"341672ca9475e1625c105a6a99f46e8b4f14dff977e53a828deef7b5e932638f","impliedFormat":1},{"version":"d3b5d359e0523d0b9f85016266c9a50ce9cda399aeac1b9eeecb63ba577e4d27","impliedFormat":1},{"version":"5b9f65234e953177fcc9088e69d363706ccd0696a15d254ac5787b28bdfb7cb0","impliedFormat":1},{"version":"510a5373df4110d355b3fb5c72dfd3906782aeacbb44de71ceee0f0dece36352","impliedFormat":1},{"version":"eb76f85d8a8893360da026a53b39152237aaa7f033a267009b8e590139afd7de","impliedFormat":1},{"version":"1c19f268e0f1ed1a6485ca80e0cfd4e21bdc71cb974e2ac7b04b5fce0a91482b","impliedFormat":1},{"version":"84a28d684e49bae482c89c996e8aeaabf44c0355237a3a1303749da2161a90c1","impliedFormat":1},{"version":"89c36d61bae1591a26b3c08db2af6fdd43ffaab0f96646dead5af39ff0cf44d3","impliedFormat":1},{"version":"fcd615891bdf6421c708b42a6006ed8b0cf50ca0ac2b37d66a5777d8222893ce","impliedFormat":1},{"version":"1c87dfe5efcac5c2cd5fc454fe5df66116d7dc284b6e7b70bd30c07375176b36","impliedFormat":1},{"version":"6362fcd24c5b52eb88e9cf33876abd9b066d520fc9d4c24173e58dcddcfe12d5","impliedFormat":1},{"version":"aa064f60b7e64c04a759f5806a0d82a954452300ee27566232b0cf5dad5b6ba6","impliedFormat":1},{"version":"7ffb4e58ca1b9ed5f26bed3dc0287c4abd7a2ba301ca55e2546d01a7f7f73de7","impliedFormat":1},{"version":"65a6307cc74644b8813e553b468ea7cc7a1e5c4b241db255098b35f308bfc4b5","impliedFormat":1},{"version":"bd8e8f02d1b0ebfa518f7d8b5f0db06ae260c192e211a1ef86397f4b49ee198f","impliedFormat":1},{"version":"71b32ccf8c508c2f7445b1b2c144dd7eef9434f7bfa6a92a9ebd0253a75cb54a","impliedFormat":1},{"version":"4fd8e7e446c8379cfb1f165961b1d2f984b40d73f5ad343d93e33962292ec2e0","impliedFormat":1},{"version":"45079ac211d6cfda93dd7d0e7fc1cf2e510dad5610048ef71e47328b765515be","impliedFormat":1},{"version":"7ae8f8b4f56ba486dc9561d873aae5b3ad263ffb9683c8f9ffc18d25a7fd09a4","impliedFormat":1},{"version":"e0ab56e00ef473df66b345c9d64e42823c03e84d9a679020746d23710c2f9fce","impliedFormat":1},{"version":"d99deead63d250c60b647620d1ddaf497779aef1084f85d3d0a353cbc4ea8a60","impliedFormat":1},{"version":"ba64b14db9d08613474dc7c06d8ffbcb22a00a4f9d2641b2dcf97bc91da14275","impliedFormat":1},{"version":"530197974beb0a02c5a9eb7223f03e27651422345c8c35e1a13ddc67e6365af5","impliedFormat":1},{"version":"512c43b21074254148f89bd80ae00f7126db68b4d0bd1583b77b9c8af91cc0d3","impliedFormat":1},{"version":"0bfacd36c923f059779049c6c74c00823c56386397a541fefc8d8672d26e0c42","impliedFormat":1},{"version":"19d04b82ed0dc5ba742521b6da97f22362fe40d6efa5ca5650f08381e5c939b2","impliedFormat":1},{"version":"f02ac71075b54b5c0a384dddbd773c9852dba14b4bf61ca9f1c8ba6b09101d3e","impliedFormat":1},{"version":"bbf0ae18efd0b886897a23141532d9695435c279921c24bcb86090f2466d0727","impliedFormat":1},{"version":"067670de65606b4aa07964b0269b788a7fe48026864326cd3ab5db9fc5e93120","impliedFormat":1},{"version":"7a094146e95764e687120cdb840d7e92fe9960c2168d697639ad51af7230ef5e","impliedFormat":1},{"version":"21290aaea56895f836a0f1da5e1ef89285f8c0e85dc85fd59e2b887255484a6f","impliedFormat":1},{"version":"a07254fded28555a750750f3016aa44ec8b41fbf3664b380829ed8948124bafe","impliedFormat":1},{"version":"f14fbd9ec19692009e5f2727a662f841bbe65ac098e3371eb9a4d9e6ac05bca7","impliedFormat":1},{"version":"46f640a5efe8e5d464ced887797e7855c60581c27575971493998f253931b9a3","impliedFormat":1},{"version":"cdf62cebf884c6fde74f733d7993b7e255e513d6bc1d0e76c5c745ac8df98453","impliedFormat":1},{"version":"e6dd8526d318cce4cb3e83bef3cb4bf3aa08186ddc984c4663cf7dee221d430e","impliedFormat":1},{"version":"bc79e5e54981d32d02e32014b0279f1577055b2ebee12f4d2dc6451efd823a19","impliedFormat":1},{"version":"ce9f76eceb4f35c5ecd9bf7a1a22774c8b4962c2c52e5d56a8d3581a07b392f9","impliedFormat":1},{"version":"7d390f34038ca66aef27575cffb5a25a1034df470a8f7789a9079397a359bf8b","impliedFormat":1},{"version":"18084f07f6e85e59ce11b7118163dff2e452694fffb167d9973617699405fbd1","impliedFormat":1},{"version":"6af607dd78a033679e46c1c69c126313a1485069bdec46036f0fbfe64e393979","impliedFormat":1},{"version":"44c556b0d0ede234f633da4fb95df7d6e9780007003e108e88b4969541373db1","impliedFormat":1},{"version":"ef1491fb98f7a8837af94bfff14351b28485d8b8f490987820695cedac76dc99","impliedFormat":1},{"version":"0d4ba4ad7632e46bab669c1261452a1b35b58c3b1f6a64fb456440488f9008cf","impliedFormat":1},{"version":"74a0fa488591d372a544454d6cd93bbadd09c26474595ea8afed7125692e0859","impliedFormat":1},{"version":"0a9ae72be840cc5be5b0af985997029c74e3f5bcd4237b0055096bb01241d723","impliedFormat":1},{"version":"920004608418d82d0aad39134e275a427255aaf1dafe44dca10cc432ef5ca72a","impliedFormat":1},{"version":"3ac2bd86af2bab352d126ccdde1381cd4db82e3d09a887391c5c1254790727a1","impliedFormat":1},{"version":"2efc9ad74a84d3af0e00c12769a1032b2c349430d49aadebdf710f57857c9647","impliedFormat":1},{"version":"f18cc4e4728203a0282b94fc542523dfd78967a8f160fabc920faa120688151f","impliedFormat":1},{"version":"cc609a30a3dd07d6074290dadfb49b9f0f2c09d0ae7f2fa6b41e2dae2432417b","impliedFormat":1},{"version":"c473f6bd005279b9f3a08c38986f1f0eaf1b0f9d094fec6bc66309e7504b6460","impliedFormat":1},{"version":"0043ff78e9f07cbbbb934dd80d0f5fe190437715446ec9550d1f97b74ec951ac","impliedFormat":1},{"version":"bdc013746db3189a2525e87e2da9a6681f78352ef25ae513aa5f9a75f541e0ae","impliedFormat":1},{"version":"4f567b8360c2be77e609f98efc15de3ffcdbe2a806f34a3eba1ee607c04abab6","impliedFormat":1},{"version":"615bf0ac5606a0e79312d70d4b978ac4a39b3add886b555b1b1a35472327034e","impliedFormat":1},{"version":"818e96d8e24d98dfd8fd6d9d1bbabcac082bcf5fbbe64ca2a32d006209a8ee54","impliedFormat":1},{"version":"18b0b9a38fe92aa95a40431676b2102139c5257e5635fe6a48b197e9dcb660f1","impliedFormat":1},{"version":"86b382f98cb678ff23a74fe1d940cbbf67bcd3162259e8924590ecf8ee24701e","impliedFormat":1},{"version":"aeea2c497f27ce34df29448cbe66adb0f07d3a5d210c24943d38b8026ffa6d3c","impliedFormat":1},{"version":"0fbe1a754e3da007cc2726f61bc8f89b34b466fe205b20c1e316eb240bebe9e8","impliedFormat":1},{"version":"aa2f3c289c7a3403633e411985025b79af473c0bf0fdd980b9712bd6a1705d59","impliedFormat":1},{"version":"e140d9fa025dadc4b098c54278271a032d170d09f85f16f372e4879765277af8","impliedFormat":1},{"version":"70d9e5189fd4dabc81b82cf7691d80e0abf55df5030cc7f12d57df62c72b5076","impliedFormat":1},{"version":"a96be3ed573c2a6d4c7d4e7540f1738a6e90c92f05f684f5ee2533929dd8c6b2","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":1},{"version":"137272a656222e83280287c3b6b6d949d38e6c125b48aff9e987cf584ff8eb42","impliedFormat":1},{"version":"5277b2beeb856b348af1c23ffdaccde1ec447abede6f017a0ab0362613309587","impliedFormat":1},{"version":"d4b6804b4c4cb3d65efd5dc8a672825cea7b39db98363d2d9c2608078adce5f8","impliedFormat":1},{"version":"929f67e0e7f3b3a3bcd4e17074e2e60c94b1e27a8135472a7d002a36cd640629","impliedFormat":1},{"version":"0c73536b65135298d43d1ef51dd81a6eba3b69ef0ce005db3de11365fda30a55","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":99},{"version":"4e49c2a5cd5b413d6f345797cf2db9b1de533863cc4ab32c4de16d4866480867","signature":"4ea82f415bb35563eae553ebdd9cfc541d18967c536d1bd821f38ddf50836ec5"},"b49285ffdee55942615f0dbefbad0034203e214cb288d2cee09d3e7b011c92ac","d46d48d5ccca19b55042e2d48a773fb97d0bb9769d9f457112c8273851b84d0c",{"version":"565e7c8592a98903a22c5caa7be9df48b5defeb0f9dd5c95cff6cc02db46add9","signature":"19f13e301afd7de9e6c815b06b16029cb6ba524d50bebd2b381b4b5009521f72"},"6e3cc8174feee7c91df7b15357a2a608ed4389ba83455b70278f0ca5630cdfe7","259042b0a833022120c295f2e44f95bd7acece59830d6490ce6ed9b2f9ceee52",{"version":"f721d57981e266030ba4406ce641861f72fcc09ab59462db608ef66a5ebe4e6b","signature":"8eb5d3569824aba69bac738c173d655d8fb61b8585dc4417a04c591d8e1b26bb"},"954ba07c66ad24d7d4bb222993578083a4423c0f92a9bac4fb9e736a3d4eb813","bae8f47ccab731836cb7117c0a8609e8c02d0addd4fd4c7009e8cecd476e818e","14a5129ed9a94b8e4a84095dfbc088e5a713ecdb391ee5bd7b0a733e64d69301","8166c477f254219baa01afacf9e1c7f90a4afc2efde83183553b666f582fd1cc",{"version":"978ac9ab1977c957fe99662e28e319fd04ebeaf373bc16fa6adfbed404f61b75","signature":"be1719b0c3e5f1de72217c7107e2106c1e3762ce3ca52d3d26db16dc36c52150"},"6cd466c69267ba1eb5e573879aa16f6ef4cf9547ef136f7a9302519e63d76d0b","5e1f26611ca7da9b91ade8b167414353ecec33bc80baf6221e5380caffee6d77",{"version":"5a34bd0c56b037d93c424ecd406abe72f1b269601bf41192361b141adb9074fc","signature":"02808a98b0a41f297bba68b200e2b9d820bda512785431e9e024b23187a17c73"},"af0cf510af3d03a0b9fe72d343822474a7fb9d983a5055e6ff3230b7b5be14af","014f38ff04103744e6afef3477513156f3764c2b716e29dc06654ab68ee9b20e",{"version":"a234d8736b9199989054d2b41cfdac2bbdd5614d29199ace0392d74b90125ca9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"561b834108e87bb7d1af50d2bd2abc639ab2b500127194847f60dfc8f773262b","5515ce465ee3acb15a149737e6c68ccc5471ff3af40f734bbcebde04843218e7","a6e71ca46fb789195d1a5be98c7736ebda95ca1aa8ee682407357f51de94126b","597350c994dd8612fb904fcd1a29aa30bc85c98a2af98c26a1cd5c6bce9f9d94","d0fa6fb03d1d5584fcb167aad7269de2625bba64cc45c92d023558309bfe6552","153915f06dad4aaac530cd789440038545c7634f5b4fba7ee5a7df597891b26a","4f088bd2a2b33a24314e7d751ddb7f1b223459ed170ee2b149ac5fd9a2113c06","f54b584ec4aa7e62786b850734101d7a26ae631c71d2dd0be082f13c722d4cd1","878afe3cfbea7f16b757d79b58604bfa14e483ebcf672c9fe7eecb1425c2dded","26ea4b6af6742a924b625e49614863deef40b7ee5aed16af861589c265bbeb28","5b228259001804ec6228a9a0ebd02b8b549529319be68558801a8d93cd50a5ea",{"version":"662f4f9aaef37a862d00552a59d1aa314f681e424eebf9576b16df78419903bb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b24944dbb9cae7dcc4282a42546e31fb53bd8a2f2cc7f8ae6c272d5924a2ba55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b1cb47f4d87126cf3f2973da87105edb404a1c98c0aef3a2a289b98fc879029","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"1856c2b5c7e6167bd7869d46273e730aedb23f80c1fc013f9f018cde1ac508c9","0387f0bfbd708bda5035a03775563836aa22508d2459e017f75b415c5f6b3452",{"version":"dde98beb8bef53cee95b020cbfddc90d0012e9d98fa19595035191cd7d2cc1ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d06d3fee2b986f19cca9483a4420497ff3909f6487e229467e75e62e283161d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f78fab13e0f5ee19bf3e2ef18b5ab38a47dc60899def7a82dc05860915155308","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c3eba367c1921c8f9e7f231a941cac022824cb666e652cbe754ac1e50804cb11",{"version":"2e0c7c56fd6742b25af440e2a83916cff12be55ca6c91f899f1b4fea9827a69a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0257b9ac5edeb935209106b79f9b4565fc6bdef9f2b4c5be6bed787a60ffdf1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"982ce1b9e222732ba63b4312ad1702dc81cdf4e277bea0040898e35fde086058","signature":"1af3e359f2c3a3b25e6cf0532c9b12b27c8ade0e7eb582007452ce06db289d9b"},{"version":"86e3ae5677e1c3559ed9078231e1e54111f3bff63b1adea3d14ff48d76e2ef0d","signature":"bf3ca96bc59503b3214f0618af79741f5a28de7d7ac663c13578af8a12fdc385"},{"version":"169351f1ba2dc3837650208074687baa7b7abdb18e43d3547565ac0ca05918d1","signature":"dbd0c498b5edc07924a5f7ebf0ae90efb9436dc2229792eaf175e16c03248f98"},{"version":"87bebd1c58e39741b573ee8d32b69b74d318782eace1a9a6d9f80586324b2d3a","signature":"800a7b9dc46ee24c46db2afa893e0bcf0b8c8bb1bd9b8db5361e34fa3c2aad18"},{"version":"06350267a4b707023301d6af8e0011c9403dcfc5eff31ef7cedb13d8f97c22e2","signature":"b7b254f81d9a367bcf98769a957c2f8dfef2267573a862b52ee2748647af39e1"},{"version":"f1844ef5a7fe33fe520fb2ea00ee03f96e3ed528392ca36de5f42964490a70cb","signature":"d949e121e0b71673df6140eda41341316408ac47555ba72b02b4abd3f0b6bc3e"},{"version":"eab55adf55f35c0e404ef2ed03340e5bfbfcc9f8e631c1ccb99d28686b79c60a","signature":"dd569f5b0cf0ca74aa2b1b5f2559d99655fdb41881b534f6d27e226903a24880"},{"version":"013936b36694b71563cf2eeeb8a159cfe7832432802c33a2d991b0932c81a36a","signature":"7bb19ff78f5dad94999cc2e0debb65a5ef8812b4507a7652b0b3fc455a9e8ddb"},"80ea52c65ce80ac3d8d81821de8e8675a7497210ee37b23efa79f21bd57fc86a",{"version":"a384e31103a21be8505d837fc43ff1a3653f70ae795b4f46d1484bd9e2623301","signature":"214644d2fea678926fe214494d5b88720514df481a09f665137efc5ae653499f"},{"version":"bc2c8875db5a1430437c82f060faae49d0eab2295f7ff81c5b82279fafa8394d","signature":"5cd36275e5e2e7c71e522a445740890253664d68f28df4d62a4a13c21e3bf45b"},"1c789f799f0a7e4ec25f87d0d72d21a19b416abed2674a3afdab31ad48f3bcf5",{"version":"414844c14d31371280f1024fdc10ff268455384385eea30dc5ba252f3e4fbeb3","signature":"da8aa5942188ad3147f0afacf4c3f11b24942ed40114ac1a2fb9444119d69e17"},{"version":"ac002c49c6dc6a9a524d074a6f4c324cbd4c320e222eda80415d53150d3b10a3","signature":"ac68bf7e24525499431c6bf39d62b264a7708d2393d1aca05a3b8d153657b2c3"},{"version":"7516f8012b8b4fceff405a25b09facf3eea5aa640fd6bbd91c169ef0ba7119cf","signature":"49dbff2eb0425c00c48128b4ff64bc5c8ec07f8aa6fda343bfb9302a2398392a"},{"version":"61df7340e77582676d6a10c309862970af30ffbb6cb10e86b49005764fea89db","signature":"9a1fa87e956dd9e945a728ecbf0bcdf59b5bc35bd4ab98618c8f51fe319ae756"},"547ea3ada84754869bc28f5822c247c0525383c3d8805f342a512ac2ed139f0f","8758d5e30c12540491d40282af28875a47bca5b8bd5e7f3136ebffb4d57a86c7","a57ee60e0e362aa6d65e1fa853b4521c967a31485d2ddd5037212f09910c0dd8",{"version":"43ff1f43dfaad43d87026e3a953b74f70368ec1ba49f67eb8df40164a4ba3056","signature":"4a26aafff5702c778bf7349914063554cabacbf4b74ba530aea9fd2b5c060e1b"},{"version":"c954faf290c5251991902e64a51f18bf0a99836430e50c38126a7ec753629bec","signature":"e5d7539a72d07ef9c3d686776f885111a063fc63c60c975d027b6b643970a358"},"be035cd5d01eb15b85322a205f090f64d333dc047ca1082de84837dc31c31d97","49c25190f11126bf668831364bfc03a136ee59e33b3ee7a7f1a214cadedc2bb3",{"version":"5dbd0527243a6d622ede33b461f27551614d1d4071c9dc1b246a7cc9db850cab","signature":"5136f18880c11778e02967105e9fae9a0482deaad8f1583230676bdb59fb7ab7"},{"version":"3ad1bdb57b05fd29dc468a42e71c4ec8f12781a647edf5029bb60f5a8afee701","signature":"5ac419d5eeb2a884c1d260bf31248fb2a853d3628aa0d7c3a99757ef99fd6c2c"},"678dd9537cd28a491bd13f7f3177c851120fdf39f27e9a93b349979bb21641af",{"version":"0d010c0b5a9166166771c8c48bf48e48d9d037de37903d2b2aba860d1108a2a8","signature":"137de1e22724e42c5f197f61b17ec1264467852dd37b27811c311c97d705c138"},"41dc35af0efbda57ae462f8791b8fe355cdbd57d7414e238e2622af12f83b52b","aba095f915652dc697979c0b9ca5a3111b7160144f9a1e18efc81fd485ec9c3f",{"version":"fd1383591235471dc0499e38fa8f0be6bf354c4ca3aef2bd052f93e34c821f38","signature":"2ea178cccad298208dd3300fecfc1e882484d9fdfca4a8c473cc345f0a34eed6"},"5e7c8ec6ecf3ed122b8723973c85fb4db2d0aca907a4854a1a800140e8bac53e","464597875def0d40d6a0ea4bc5be50373eeb35af3023b4901fc539c19fb088c4",{"version":"560ec4980f9fdf84e4df149a31c676fbc624df75f33af2159b30aaad1d624506","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5a31e889fd61c82203d9d046edf845eb54c12d63b13d2028e5c1f16c26c5a535",{"version":"e0875a51dcc08220ab37ee44e0d604789fb0c24c6435826eff6522f9af79faf6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"3494b784dd3988b30529cc0f271d5750b85f3d241eb612e4bec87d99f3a79de5","2f9d111343117be248f5860e96c68b5c55e402894408fbbaa4b031ab12572474","e8252a1e88c45e4b76044e3ced48484fa04faf5873eeb2a15e88813fcae79808","bae7d0911c58609a404bcd7255d5c80cdda6d568c3b95fb189620ed7bad20843","23d90e3d7b8e5a17f760fff35617a57ecd7b7f042602b3f9dbe314e938c77330","00ae1a801699b73d425782db51a2eba53741776741421dc8446480d09091377a",{"version":"f89a50a4a14e6ef1a1c81b263997c2d728ce5b56bd1d93dcb907d57114ccf955","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"830b21dc28f068d3d362d407c17d010f37a9a29cc412527c274b8254c448dbde","4492c3ccf40d889bf6eb454af8a7fba4199af810c53d10ef8d0bcc16156e72ff","3c21f5bee2f1186f62b01fc606780ad26dfc12ce34fee2032d2c1e35ec2e5334",{"version":"6fb49bb8359a76bbd80e39616d5cc6de09d3a9ff938cf58be22c155e8ff42916","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"effed161b9f183f637fba8f96864ffa67bbad3a3339b18d9d368438fbfc00bd7",{"version":"a681431952e1348dc231f334ee2f4818b4be12d2a720c06f52c842d0a577aa9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35707dbd962f597cc72a0ccefdcbda1c0cbbddaa11bf9a072fa9dc2f2b40eac5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"6d872c4f980d7e6288d80742c84f1dc087a0ec7531e18cdadfb47448a669c2f2","179703b328f92994e719755b197ff2310945583fded682cb02b88aaaec0b3d33",{"version":"d2e752aeb02ae1be73703cc7834f9bf1de14b84d32121fef58982b29bb138019","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"954fa93b29dc7267fecdb55b80d28bc943cf370e0165963ca051c0cc6899e114","bc8201479e29d49966186df4e5c359d507dbbcd4f772499b365e6836e500bde1",{"version":"ce4cf241091329ede4bf94c365874f20cb8309b02ec32980d9bb47f6527e86c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"522ac15e66211cad975f897a7eb70e77ba20b34ba8f9c4babb8f75f37e19c24d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9bbbbd6f4a35a22eefdd4d13b639ad27d2b1316a6e833e262a126d4310d904ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80c14a78262fb095d375cbeffe6a6b53a300098928410181ee1140a3a8869a47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"66cce2ff73442b6f95408d5847e2c8748bb4e47e44334546e94e52be58c0d163","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"ad628be53a44b47262b560ab15866282ad4d257f2f214369e5f8579c84d503d2",{"version":"98021cc3dddc6c8ce5498e301424f2314a9f17deb130c609269c900716a6c129","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d4011e0ecac2f22d7f639baa671cb23d19be79b0dc64c1cedcfd026469e5dd46","563fa16b249fb0bf5ed14f72e40b6ead283ccb254dc1ecf0304c0165ffd4dc6c",{"version":"66be3a972b4e3ca0b0264b6d5de8436ed29f06e9737bb73d355e1cbaa8aed008","signature":"5284727f6ff23b3af566b99ce979915c2adcc7603f6d73c1155afc7860b7bced"},"d0fce09b7c0187f24c9b0be74c938a6c39b6275b2a648df401219a79911105ee",{"version":"92d32911e086e087141b1aac3b7876089e26ada9ca8758a91280a05b4efd3a7c","signature":"6d53e68963aec64794baff110983e875c60a42a3e3d1bf17ea385752c914c1ec"},"0409151083cb223c8bbb1c13940f1aef4c1cb2078e750e3e1b6dac6403a11848","7f0c36e389b38fe05922db66efe56eee73475c748275e5d2b412bd4c4b495b86",{"version":"fd3e19108e40b4bd6502bcd08768a75693473f1ac31649f1f4ef6ffd7c88d36f","signature":"0b2eefc3650c7cb2c277d27ea3a3290f5835e2ad871b17041ff92843b06bf99a"},"b14453b02122266e37e186d1935cd337dde89929a1417cb87c6b962b39af0d36","af627ecf76e60d85bfe1697aac2044ee9a1b4f0ee8439eb51d351db84cb56654","20a066d0baec26f8ee4902ff7cc7afdec57496053b60b5d3bc5c85732a14597b",{"version":"c216d4cd926b1cd512c039ee12dfdca10a292a08b76ab11198dc2293eec74ed5","signature":"64718cf0d577ae9ed2926faff603162ccee149cabf0f7d6c3d2eff8bab3f54fd"},{"version":"c8fe61044fac5d42706c4c8854e03e5eb073792202ec4e7180f7397155e34f9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"ffc13d84cf2d63a59bed820986ff22900e8605703847e99fd0689f513278c8ef","48c2e723a61bfb7e205ee843adba993f04a9764b6a9d11f0abce43a08bf64c4e","6207173cb052cb38b9660453164db77c4c677e0901e1950be658ba41c01cb250",{"version":"65db26f870db2f36af509737119f27bf6fbcfe7aa413b169dab0f6215758243f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"52977bca0c3c391efb84678029b1816a997a5069d91f8de7277079ad39b00c53","d15dbc0f96e7b8141a77749e01a4e920a5381ca32a2aa58132bc5f7223f291d2","544ad2754ea5eb052e793f75425624b7522f638801fbfe50cc252e8bda11e0ba",{"version":"75477456333eb2c8c6de6021163fb889ce42464239529345f7bd77a77414a743","signature":"1416dec78fa5f6be6be2e406d0cc50d6f98ce0c77dad79080d5e5a80be18084e"},"0debcbdd5a9e7131d85401b18ef4a9e4dc73a0e08641b30d08004be0d54e3ebb",{"version":"b7277bf592b4832b905af6bdf6120ea14f1b9a9210efb7a4ae3803a87287150f","signature":"3922dbc2ce29d177e9d0c1abe636860f8a1559bc545abd790c0060803fe2e1ea"},{"version":"9a106f225ee7bf695ed69744df6cb6982083b3f9c2fec9610d5ea74e2e49f6d6","signature":"9dab80bdc4cbca67c3eddb3cd102b87f111b1e4d1ba1b3a0e27a38258e31e426"},"d1df8b7eb29b69426f6328b503a12b4408d4ba4a3a305ada40af859fd0d1542f","71590a10d662a3f420f700c10793764811c558938e36311c61eefb13033a21ba","ef07c47a9f22bffbb585da6ab96f379d97e6b72fbb658e78c03b11702cd1dc6a","e561876a844b5d66796e60c5374a55e3666d17b8026012a3de1e78dc03e045a5","2bba20822fb6a665abb0944bd00e587f93272c5aa1b2a513eeb9f2fed00a7e7b","fc9258c7768321dff71ae7ff240ad1b5a6b204acaaaf8c088d37f1c4d644f20e","fedfde2b5b28d1a1ef04e2180aa4872b9d6fae211c9c2dae739c58eb7c24264a","c78318850aa730e4e4d4900d62112d87546cf76bd8c0f8a5389de62a8fab97e9","e21face6fb69732353a584c8ca54d4c4f32840b9d976bd2ed16e6e04ddb1b689","cb29061301b3205ef763d9159d82d81dced1528068d82d9248aa828b293473b2","47049411d18af8a4afd89d6507e87fa1e1f761cfadfc49db94ff0fda85e2db4b","ac87cb8a327e1a7e15b3597bf1ea9207128645094941d34c91a4f4294cb50c38","a580c25f701be8158ca4a6031e21954544e71edb470a31fd4a572b6aaf3c7064","4e5ac226a5b7a72d76155e280d38d71077346ad9a60eebbbdf0b02b8c34a8512","b552dd1e51bbd18d0d9b4904dee01cad165d7f0d3471b42f00ee36ca78cb81d1","a6ea6a6419bf0d19369d82a80852c63bf4c4585648584fbb65c3d7cfc2aa688e","b83570a2939d33a6ecfdd2766a3e416ba612d0e8d6f83ad11156a004fa033c77","10a08fede9729e6432dd4a751e6d512f298fbfb9d361104ac97a2f4eeb2a0625",{"version":"256fdacde97c7484ce9e383899d94781672799f45a45c16cd6a079ef5f9f8a5d","signature":"5f5d48b01cdeac8202e38db3454ce0904a00a64bb7b521fcf30e5376084d9de3"},{"version":"a6bc0506fd785d58fc01916eed093992884c77047de6ef24a1984e870958f3b9","signature":"6c67fa30e0db9490403ce70c9bd112dc16256f74e793f48501b0af56cf31c2f5"},{"version":"96a0fcac817c0a1d7c901beab6ed6c88071511191fa3abca730407a905361481","signature":"37d3ec125450ce03a992226a8a4952bdacacfa88aee5d09f821236d9c63b20a0"},{"version":"419ca4ab657409f45b8db6cee2d5d6888a2b08a2fba70ddda952d968c69a16e2","signature":"072d63362c70c19e5647e1dd12ada4492213157c48c17ccd13a008f9c6b4a12d"},{"version":"8231663779bfba7f580018479f74d02df7c9160b3e8dade1940a569ed9d80ac8","signature":"b19e055eff7a9ba8d3416874c9a679d800a5df0a0c5219cdd5aa5335c6b8b072"},"cd9961e19450bde1798e94855447fcea0f9483ba8bf4bf4624951e42a2bdcfcb","778019a2b3ecf4e408cb6b4c19fe86bb89ac9af1420d4564adb23bb7a8d499cd",{"version":"e8ec03a7a1c38bad33a45b08e9087012dc1a17274515d72f7f99c8e591c3b2a0","signature":"0e094d3f18ed4a44baa44ef3264239439eadb03b3f8e2ae278d766c852fa0754"},"2bcdf74ea61885bc9a5da25620364899a0e8cc6a2f6bc0bdb44d7698152d4d22","889c6cf5d2f17ebbce07ec946521f4f1a8fc55d9530b4959a7ae954ce7f0300a",{"version":"bbc45438a3de93d2d44f46fd0cbded993b3f8afc82779a42d0a6819a10898fcc","signature":"f376da706da2e3ce62334b6d086d2d91040531879603f039d3cb7682d8d889aa"},"c15fd275a051a6770515950834e07dc22b7ebce6a9e8a93bce69d67d92f39e40","f2733e4721a9ff2047d46ddf9f771aa053a8f482f93d4820401d0be58dde660b","fb742dd0eee88f661ddde482049aeda9648bf9997a53db2411360517e1e81549",{"version":"7db8deb452f9faf63b51a33fc3a09dea5a305e4dc231b770aced708f902dc7ba","signature":"2fe7ae68eac160827cc1ef3f71109e12ae1ba4c407fcf41d653877c7a3008970"},{"version":"8dab908f81bf0eeb9611fbbccb2508c2b4a8e1d57622968cf98993e878a972fe","signature":"b01970e81b7e682cd2d51def6b76c7bffa451a1b58fc54b528629c35dd89c9f5"},{"version":"9367e99e6028dfce0d37891b19a17bf1a3b04fb2649a89ae7ea832ffc7507b99","signature":"ba000331a8a0915160cf82ffd04d583bed6ea5547a117b8d88ed7d3ff6eece7a"},{"version":"456eebe80c579a1f7462b21134feb2bcee727f99966435c3fc7cded50fc80e3d","signature":"525d97773ff298b2f2fbdf14a791f0587ea0172827b6ac8821f3eb70b20aaf1e"},{"version":"24892b8255b88ef0102847ef8b231c6bfc0ee618a69b17e40ff1438f9997f2a7","signature":"59203658389170eec22beaac1509a33cbbdb6dff49b69e34593aa96c90c7de1d"},{"version":"d644c33e3d80969acb5b187976c8cf99eb0a259f63bcef80a6ee38da18e83247","signature":"2769f26e263572cb6b16ff1b24f373ded17e74e710e33725c98a22a0b7ae79b6"},{"version":"314650281c03451fe80bb91889aec0b247946fd5b52a318d51c5faf64cdc57ef","signature":"5fbeb568fafddc09e602cdbfda7df5cd0e561ba1dd8443318f1bb3b586066c9a"},{"version":"390527c96f2bc590de934f2ef5bb5bb3d6905d6171bda8835c197e0abed15b08","signature":"0ce70420a3f859e7ada24e14d433bf75f91954ea538e9f25cf32a9afe7e86539"},{"version":"6cf137bc48f40ebfe5138b9005c22a2a36c6d0eae90e27f7bc5dd58a04faf07f","signature":"e3575536a31286b081d4db3ae027a171f9567fb73765c91c67550cd330650e49"},{"version":"656330b9d0697dbe04cb1d8b8402b3ba3953dcf48e4dea01887c992036bb173c","signature":"24733ebd4c83b4d7b05b39d79f1eaf60c6edfc8f0da5c2f848b01517947697f7"},{"version":"8c4c8c4467f9519b0878333232f88ea38920588b21fad94d09e7d191c1fac691","signature":"5bbc828ff668bd2cad6f88b3f8bc1e85e3ab4a84af3eae83b3931bddb79d5d5f"},{"version":"b7567dec5ce2d27ed70feca5c5a53b033bbda727b3d65c1eac4d5256adf09315","signature":"31f22cee584992be54d06fdaa9dec55d060c358cf67c4d162fb2f5fc0c98283f"},{"version":"7354576cc5cd9410252734f2a40c4fff01428a753a672f354975a958e7c63329","signature":"b4444c70cf4bbb122a1983ec33c297027a05fc1dfc23376d3b140745a58a2ed6"},{"version":"3e8f5153df2b58ffc421a7d8440d3f92fe8ed9bade9a7b18bb0ed161998b40f4","signature":"15c69a20c8c5420b76b7c62d82cb284a1608ad67c2e0d1a71e3e3caf90bc4201"},"4d87ee3b202e0f2f91804622d86dc5cacdf3596c0fd62e4debf04d02ae25bfed","6788a1deef524d1bb463645a178f02627169ebb47346eafb1a61faa5cb144333",{"version":"2ddeb4d8ce27590153aa6ee84b36bf9764700d7260124167167a2d2a32166bee","signature":"68597f354e4595d3f1f99cfed58a445a66dd9b5d4ed5d8133e445ee2a3de6cfb"},{"version":"f7f56d7774204ea550efee0d9e05494e8df297bdf32634dd601fef7fe45f54a6","signature":"1aaeb72b56fbfc7fe35e5c5195bc4b102fdcb25a30fa39a8dcf26e4e03afe4f0"},"2ac12549f1ae0aa1775782876baa9c06e9d845be26d99ce56a36276a8831a395","79e36ac740e122de9323550df934df06c908a26820f221c758dcc16beade6618","bbe55ae5cb40ed5f38ecbfe673ec070dacc7e0d55dd02472263f7903b3c5ffae","c10b1247cc334d64f4740702063dc4dc4251b96427e0d846b5eb9a7d0379bb1f",{"version":"672ec17aebc02c37f3bd6a75778652f5cfcc450b0b2f4dbd8d5821ccc7909af4","signature":"0799f99f4e37567f2fe31840ff206efb30c29b21bdb0af72d55aeae15c70760d"},"6c6bce5fd86564171cf1bfc4122e6b4906a820790b4097c87723c2fb92eca8a1",{"version":"7bd42610639a14bb0c854bde2d3bab07cce272b4f9699027258eee83d5c11a73","signature":"cbe7252f19d4397211500df1c2861e7c4ed9218b8d1614ae2d11ca03679f9551"},"b7b303f6ccc15e4db96956737e538d893c25b7092a159a39c0aa8ad932d3c636","8b2eedc0f7bacc05c6f0b56dc41f46d1b06ba1b9868fe0fe77e8cb22bef6f2a9","0c7459b35e2665327b17a7693b824fa83a3cc5647510a2cdf09a6635b4561c60","e0c10ae5e38df160cb240dc9e46ac464dd22ca7432f783f75d77b1b0e1aabf46","ae6c80232b4c2c4a00fa2f7dc51552a73683afd6acd88dc6c8a745cd39a823ef","2a4f83a64245f53cbd1eecade0cd429c73f5b4e992439b773fbf2e8680ca4572","3e0238000be6f6ecf94fff98c1c71072caa73ccf6c318c7b8fb324ff2903103b","d80b042bd5c32bd812910229cd6176855852fdbec7afca06d4bec3e1af8e1446",{"version":"622cd7a5b9b304ca18aa1723952f21ec5c939f7c70431c1063bc25e59a912dc7","signature":"90c7406dcc6fe0fd8b0fa3e23b8b1440b2506d841d8e629a6b1df0283c8fd1b6"},{"version":"83837a404834ce7ba3f2498e3faf5dc31ae0a5859cec3101d684f824f8cbe3f1","signature":"3eca308a8adead7d78f165d89c01c30c4dbf141cfc5900a563ef47bd2b652a27"},"64cd8e7ebad2b8827d66171a80c2b516c5a57a91eddfe3f9b317faf8879dad26","68ee63044e87286b7a2100c05437babf550d647e748e3ee66ea6ad4cb268d52f","ff60cb0d4b987911a9db25c4e372a81da6211e9248bf9eb336d2070b77771bfb","187b3e36e643d6484ba0286c361eadcf4d3f4174865c8bb2e92ede5cfc0206c7","b5cc0ba3df9e33e2fa3849d474b4c558b77c854ec3addb719919720786f2462b","996c05dee2488fcd52dea0baa6bb03cbcbbd451bf22ca0982ffc1bb412ee5dc3","e83b7266b4bc5653f60004a5a07e2dd1484a92b256fded2dc1fc65e828b4bb57",{"version":"2853d5c65a6ad064deedce24ab8dbf06aaa5ce9542a47f078fe02f03ac7cdd03","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3383d105a4eb14ea4ed618769f30b75f90188e7935364332f7082793d1196b9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d96eb2f5d3802c4a877dde7cf5c19f3e938d792a6c623e806c9cb3d64f134d19",{"version":"df1cb31463ecfb08b80ee1fb021dc44fe79934972679382b951fd13eded5d250","signature":"2af67711c0b92f1ec7bfe590266fb550a2a274b8e60fdf1a37d57af36b0bed07"},{"version":"40b19636fdea5f4ff717e2b8c783e06978d56ddf2e56cadc547203802f3ac0ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc0e638cb5f96071486da3fcc349b7f938455220ad96d4e80a1afb444b7fe0f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b65293780e6f9a13e12fd16c069c51294f40e1e12a28add6a26d8205c74b17fb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"82a1830c87e3d98fdd26f717ed49b8781d7fc773c0de5264e05e62640da8987a","e7b9731ae386cc1518aaa4172cab2116a0b1a791cd8ad34ffe09459f4574a415",{"version":"1984ac4245d99924a641f9d1833899c51fce20f0c51b713d21f2d386c87f4492","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"ab754ad0ec19423ea27bc7313015d6cf738360f4631148d2d49b9b87c0a46929",{"version":"1395fd01fc1397b94c1c12676294a680c57d649db4b3b28e1c260f0ebb541e6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc40a65405b276cba2de5d724820da75d7e30c9e7d10e405719a7be5b3e31a5a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4156d5f13cb167807cd3b50f1ab673c57ff0051d3c5ba40aa2dcf95f310465f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"1cc522ad210d2c7dffe081392e099776aea1a12b7341387bcceae1546565fdc6",{"version":"0eeae146f6113ee176a29b1625a3e63bf9e84e3d15c25b672653db26e45d8ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"85251bb0af0b000acc3eddaabb09b481db2d5b09be42f25d52b056b966ddc6c8",{"version":"a63ab84a834b223bc3cd8224f1e39c2ff0f906f3c29375f1dbca5a34ea1b4005","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"093fd001ca343779855f7f386b448d526e734c0d8c707eb3b979eddb84d40161",{"version":"9598e8f2c6880331c2f57e6fe39fe65d279d5fcee0879cdfcb10f676f2af9ae0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2deaf139a18640875564d069b8df011081214018c145526504bf2e378c716a3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"8e7a92c6c568e3073f67648be4f0ab0e8d77e36fcfad8aa97bbb268ffce6cae5","be1f6a316e168ee956b44f0e9587e97a5989614d65651278328e6de12800fe42","1c26429a84968da0f9f6818874208d5395d5d681789eb62d1e97874afbe55156","831663ae7da68955e3dcac239c6f0c4b4ba951287903427713c0e5434b268c3b",{"version":"a5032259dba8af052f8650efd4b8290d61a0213e30c43255cbae505fcf6d1581","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"a61e00af63164e828f24a3b5abb6c76837562fb18ad83607d245762b92566c22",{"version":"ec80f7ad05865aa145a03395e48710a388452c5573da23b8680f9fc4c40f7023","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"0c2cca1042ac885087aefc61dd07c98d4d763e1b93da979e9cffefba1535103e","90f918fa4bfc8a1ca28e5ee6c726fb4314e3dd5e4e6e5c138d3722a4406dec1c","c1acbe64c0dffe2769da1455ffa7a7a54c630553e711ed9ce686509d5a7ca22e","50dc2f59a00d680eeabc050af25b1e67047756935d858c7f1b11bfa25064f92a","1418e41691be1d8e5b6c0ba32ba0279999a75c035c1826b88ae914d8799ee8a4","68a712c8150b2351406c2564d71be4e6bcf2ca9d5d5a241e99421ecd917043d1","75b3fb36bd172a0191b3540170778693e0d098328f7f6b783d0155848717a104","c360f159bf7cc50cdbf9fd68912ac63bf5889b7220045435cc681b4fbe0b8f99","a93c39a33bbf74c81cd249032ab84d98f1bb5b86a5d557111792af7fa51fd3b3","a08f9d6a255a986f0709ad01ed4719d10a78c911442258e3fa586511e54a68db","0d74771d5bfa09d8ef0f129e8f5d5f64fc0fa44ca6e2319d711a301544095623","aaa89e91914fae87683fc9b47537b7561fe705e3a19ebe436209a8553a9504df","6d55514cfe052291428316f8b5ddd2620f161abcf9d180a39ad04f2572852a5d","e34138d79ebd653b7302f054d2f5b086c40075dd387ea3c02b9cff1cafbf66c2","6c6f6ae7a61d464a58070d9204181c34f88def3da2364ab213b2769fe1da314b",{"version":"40d866aa997f590716d7630f06025a53e7efe1982140182fef03d1971594672a","signature":"aa43218ec3abf932ac6aa3fc859581b8f2fd8c9469e3c919408062e57d232bda"},"31c12db46e3320bb3d198856123b8875d4c18a00a9e8e8e6aa4c87153954a24f","442f6f1df0e9859c783e9c1260324833376cf2ef977b0a8e7c5fe85ac45b2b4d",{"version":"727b5a06aa2c2d16692b1ff55cac347033ee492fb0132f3843133567175c5926","signature":"edcc0d9e675c37f8b8345ef683965422335c183997a5abae692e03fae3b476d6"},{"version":"104321bbbae499a49b02b529e4e5176eeb094395ccabb51475b94ee7ec3fac31","signature":"cd789dd692d4dd223dfd8938a1dfe00325b137c3852e6a85bfa9ace8ed00a10b"},"8d1549cef4bfbd34d863903405a6f4146fca4310628edf97cca7f43eb1b5b70f","90ddbf56e752ea3aac5906c0bf372a35361bfdd05e37674295b930d1a6145676","a312b9b01b548d6a5c198fba7ebba16e890e181b09441f9b358f082f927c7f84","db89abf280f68499f246e5e7aef6fb38059f8d9ebfc4d485e89441d72cffcda8","1ea8cf150ecfa2e7100ccb91fb039af6b12d8f5f022266a716f6c6d3d0564280","de3c3ede735330a69dfea482cc4d40bb5ccc96bca1ce3e0255cdc07e96cc93ce",{"version":"1d75e713898af44896feaec991b57d2e9a23e8790c7715eab5617b37c81b1304","signature":"c44cc8c4930b76d1fbf935484045f099a078a0e56218439da28da5d829065a89"},"a1bb76d514e93f8f24cb98a136465f860a25d9b413d9ee1af1016d703e515a62","3147101b0718a86a739558fa3218ff29d597b7c7b706ad8c4169c8c80c4daa34","73179d9bed7d28d279c73fdf5c7ec4dbc78493af676d383b3f3c5c35d839e7c7",{"version":"cfdec451e6198722f6f1a470ae1d702e91aba34c5a82ddc8ca2c46eb2841b25d","signature":"51e03b177ae1693a016731d78123a9375e88191258438907cfb8c28289ccb8bd"},"f73cf056e756688b97c5e8b366b8516a5cbc18aaf1d5278cfd567db66b4c77f0","2e818b0de54379a805ff642430dd2ebf684b6cf4d3ae133f12fe826a47eaebd9","fe83b119da0f5ad1d6de35dd8a8ff11c6d3b4f430d2ded235430d4cb84bf32e0",{"version":"993f4c89fd25bd6aa86e3329183f0ebcf30123d243bc6505e945c7d23213fbd1","signature":"076becc81584aedfa7349ab56ec3058a2c48e51aecc5ecdabc2fd4aee654cdb3"},{"version":"5c0d0bc099cf3cde30d02b2d11f7fbbb934c2434cbfb69d8d67595bb2ccc1d95","signature":"43b7a0a2b2def00095492d167729428073dedf1d85c28159254d3b64e77eb0b6"},"70b90a13137fcb5ceaefaec6c636bdf5ee4fec1b03803f5bf1d93d3443231741",{"version":"d9aba09758928ec2439f08c4736980c6e52ddf6a0cab476c161b3592a34d44e5","signature":"ffaea7fcaed416769800cd74682a38d1335953e1eb903bb59c22e45cced12b52"},"cabf742a4ea4a60f70f88efcc2d320b8e1560f54d7ae90fcb8c79af7a75cd920",{"version":"5b42e9d056ba435957700d0585861287052c08afde82e5349a21a43ae7113457","signature":"c892b55f40a8f35ede8ef7f1e0cdd1dfa70b22bee55d10674222bffbb703ef02"},{"version":"fe2e6c470b89b10fdc90714c6c734713dd0809189913130fd31fde1c152dd96d","signature":"d5dfca986d325fb72b02cb63065520cc0128b46d77c1c68441ca2241ce17113b"},{"version":"e221838e10f6d2c4b1fe86acc4066491685a9c6e878ce39480638cadd1fa2650","signature":"b98c55bf5fe063f227004fe751cd334ce9690aa25afe7be7570d66c48cf86e56"},"742bbb2ee54b65f16f77094b8444fff1e3f1c4aea3a2bd44cae5de3fcc369411",{"version":"dac20589f2919a63805df5e02ca738dc974c5363fae35c526eccf6b7f9dacca1","signature":"a6d58c8a4ac0a18d66afe6789e372e54e3663f5198753e6e94481cff20b7452a"},"be476947bb48a7e4e2a2bf100c43026c646e115085d076247f276f88111d254d",{"version":"a1c585659ad6a50677fc7ac3252133c90ec1d60d2f44a716b6ed4f945c0c337e","signature":"092f1a685f107b5dcb94b5d54e07eaa58894ea17312c10bcbf11921448776f41"},{"version":"0c0f21b2b2173e3f325fb91099b32f3e19843b80a92f4ff2ff5a3d4235afa72a","signature":"e5861628b3c26867cdd721803b8fd63ac2568b1a32ece964adb39bcc895a1ce2"},"f8bd37d0e25c4048cb2f19e6039b8ebfa0bac6d24ac8ba58aa0fa4efeaa571cf","09256332eb93d63b5de0c4c87a64562486589143068b03be359bbaf2038601e7","2dbb440d516e8a8107ae311ca6371d7808832cab07de9c432b60d3e3a7e89d5b","e46586b8eff1754102c56c3132d0e4622535a474a8a6b82f001baadaffe33779","d48210a6d909980fbe83eb6580fe3a2642fe743539c17cfcf8f89dbf7e8b9c36","b948cfba8edcc72a86f90f7ca9f7de41fe1777dc92405022eba2163d92728e95","268d9444d7e21783addb24299011460c065a57101057d5ce904524742f7fd5a7","b820c9c3de6cb1040413353cacbee04c9b8bc8dfa653a4aab2c25aa6c7c65120","5ec9b5391ad2f1fc9329b4d7f8684642d34f6c7fd339fbf5074ea3115ce9b5dd","615e50151e5cc86eb9c7220aed8849841dd2fd2b2cd6191cdd12943c5b95cecd","fac01cc464ca9dce1a0a9480945abd88a0098d2e8787cde86a848253cb20ff56","21c8e068769517198fb91373415bb22206cbc7b95021c213c4b70d9d7e5cfe78","f7956442417275691905a10a694cf23e778b1d4650fc39f23e4ea91435e92cfc",{"version":"f2b1ddea19be18b09439bc8c694feb590c9cf94ef579bed1e5d68de99fa895aa","signature":"ff90d881a61926ab079e751164b0087197a1a1a3b234431af5abc14235e438e9"},{"version":"9cf58515da90a71a9353678a765a8b6c94b63b625fc05bf5f9d691992d0c5ef1","signature":"0646461331d1a1e9f1dd6b22fb002a043259f5210cd693c5959bb6d1737415b6"},{"version":"b667b78d8db3056c14aba18e2138fd530838de4e44437153b8c338d97eb7944f","signature":"67cd5d46643ba488aeb104da791a837e1c564361ce47f8bf02f18a49b1ff1eff"},"9eb659b8534f4f030c58515fb79baff9b1f513df9a8c9916fb0e5a2023b9c6a0","21366491057467278d3243b28f9065797bb996a4e4919f1086e4e2710c9350dc","070a5b980cf70e9e54d6291f31a634d1346707662aa0b906ebb47695316d94f5",{"version":"cfca522a29f53430f1d0447baa732bf2fbfa5bebfe68d2e475432b228a496110","signature":"f8ac07e911fd9a3bff7d0a2cb3b8589e14a3b52a3d26a2fad493348f494edfac"},{"version":"431b70b424860910a8ba2560f83bd864a2939b87109f11ce22873964f1823b62","signature":"24cf0f162a2bcae8f5ae4b678da2ca97a91699cfc30898c606cda9ebec4d9f69"},"c56c127a7dc75963ac6a68383a14f81da3c0c9e3d8e86c24ef9e37ae0ed777b6","703e7b32062955f1941d78af2bf1a972cee1905d9f64c8c945e0307b71c6c8f2","5320b2c2ba15fc1d0250cfacd6deec6a1b242c1d03f5d5bcc2d7ea8186fb8787",{"version":"7b7a0b908bf6dbaee29816d012f9ef2ff0b0745ebe5977484e12ff0d2a1d4fd3","signature":"7b90b0d17d565ad2bcb84936503634ee9f77cf9f40b2353797af5c1dd26b6161"},"4d1f600d2d153c7c44e2ac25f5d64776f9eb7a52f1e92bff302e50a7efd08a36","043caefcacde199496905b469f7251c08948c4921f91e5a0f5c0df4f03cd2d55","d33e44d9c563ba82cceb0c3fd5a20de58d19a4ad63160482de55bd9c50c3ad2a","58cca2c47d5dd00d8585348eec7067b9e45f9fc89c9c430cbac18e2846c4bb80","a3d3ea65ca56bcadb960f2e884fc5a2b3ca80ca7949dd540ff63d00719711fc6","920099117da73b53caf5e84b81cc4d2200bec4f82e818bc23b7d079a2a56907c","7444ab226ecde90756e4e31ca68280797132b5c3b38348dfafbb101346ff9c4a",{"version":"4674b23baba8d8d1145d47b4d8db58a1161a0f0327cc5e05aaa3c70dea3aa4f2","signature":"597635cd2982b768c8075e33902d4bcad6b823ad6837b83bdd5df1108a8b5ef1"},{"version":"dbdf5c99dd4d0362a790a664fda2f7d80f0b90ec20d2dcf9f4e71e5d859ee247","signature":"7b9aa1a8a9728abd8faf699093ec32552e44ceb2e3e4eea7fd39fd4a105abc61"},"7007c577d3881953fee9f301de570abe4ba1f6a54fbe2873968dc002ab5e5629",{"version":"cfad3779365697cae7b23669d57fdb286de38dcd3c9b1fd53689f9ca3a91a2f0","signature":"51838b26378f28235d88da3177a2f581d6325b1c546464f2f5bcec82149eda0e"},"a6e432450d84b15cefce91791dc06498a08e357e05526537db4bc137807316d5","2cdc33138be52678761d11065245a401d3499f110f502a2cb34cc2632e9c5e61","236b3b7d3b7a86bfa27e5bbf1998dd7a09b9b6ae3ebcaf1040be305324dcb5bd","e22bf0ed5f47dd3f92ca02adde6e4459657a649c00c57071c4b401d6883009bc","e420009e6a6660fb935064b5233cf09d28f28386810a62ecbc0c42044d5e97a5","d592e7b22c830bdf0c8da2ee4c4d5d3587675ef62db03bdc0c78df8e7f7b7c80","19edabca93b6826a91c26832d55037e487218d8f29f2172917ef87ff08f8f380","fae8d4ba3bdfd3f087c40507d8748236b0c08aae3d74c706651a15c0e27ba16f","9041eb411777fc80385d1b639173fbc6675ad3d7cbce257f52605d4b18616543","3aaac2c7f4e18c47e5197948b4f1c4d1d569257499c1dcd2395bcb15849fdae4","ced161d675dae30f24f7d001a14ad62504d69069b971753e9a8003b2200e7cc4",{"version":"6b76f984a9fee625d81fe94eaa63de765b85b492e2936255608e9935f577df97","signature":"dd0fdb6f0c71a53e434d39a22ad54d9d196de67178d156fa5b13df073c527f19"},"22772822785aea051e4454632aa2bb73baab3d08d48cf7366cafd8f19e1e0c4b","08069afec7cba0f29e89b4fab6af533440854664607e1a6381781df96676115d","cc5667037f805a2921883f7fa5e091aadf2ee8907c46d61a7d7992911965eb66","82dbb5baa7af6aca1b1392a81acc3bbbc07f50ccd8cff2b3eff2ceb1c5db2182","fd3444a7b0304c83565c7d69296748987a09c2a55377bc9e6c4d32961f8c99cc","65f0fb9a264a44649bc963291e7d6e810c6c06350748007de4e1575eaba8d319","f40778d511004eb579d576fd71059f3ae2bad589d17872de170e632e0632f4a3","a1e7d896074ea540edefa896e31c66fc75904a26c4b2ef701a93d87a83376ad9","8bdbddad53b0b942e2bc6c3f2d63a6a3d560dd239e8b30c69805367eadb090e0","8e27dfd3b35176a3a2e4307206a9ec3909995b23657330dc835e7b5fd50ae89a",{"version":"72ac7a0ae5374dad1652ef8e41ef145bb371e9b8af2394b91a3b6e0220b5f39e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"8a9333c31386954706e26b45e586e7e05f604d04bb65a345ce2f47e56b9352b1",{"version":"5900e81e3e81b42e25cb9d29df5a19a255d5b3609ad86f3f6da40c3c5f895a52","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"ea3738176afff87d2d326835927fcc4c4e3b561cf56da5dee6959a08458862e7","90e42071689c6160951272f117347d7859a2ef54dc83f3879eacbffcbfee1868","faee2c92e93b04bcf0bc3cf951a6ab15c80022850773ba6721dba52e84a5ba41","e353b7b008a1c093f02f51d6c46b2c1ef2c28fddbbd38889a6a7e4c224916779","e60dd86cf0c509da27cdedc9fbd456d02f145c04af44fc026d67f1f3ad4d4d79","b858241a65512e697bd928eb1675541fa5fcf2b516aa837f90cca9ccf23162f3","045b680cd4cc18bf4d40193feb610f9692e31e055ea84a89bac2f417831c7ed2",{"version":"e6107e7fa315d770a69b9edc7dd077036f115479e102f2380306a7a92629329e","signature":"0717bfe8ecec022eb7f964ce697b1b0d749e217864e7a11b438c462ca1e55412"},{"version":"b26bd1398c6f71549fa23175f3c8b8245fdd2d2092a6380794cf8ae8ee67666e","signature":"99173fef2fa963dad3c3a06cef6bff8e4a9e4fea656ec6414aea2f84126ffc23"},{"version":"002de79e07e5851f180fd44a17d1f855b5a17fb9a00f9a17cd53ca055d27ab8f","signature":"08e14aa9343103c4781efad6b4d287b2d577a8266f51f389b2ed4db8957cb5ed"},{"version":"ca319732ee32ab8064188cb5e284f7d8994c1e05e67bd2f7f55203eaf67b33de","signature":"0de5e5a2fd2db15c16147aff67475c913395e62c14bd7c5313880b001a88e009"},{"version":"04ff795f13235dcc2df104c2363bc370338976c37fd408129eee133fd481b1b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d043ae7012e9b61fc3a47946b043e6feabd01d8a1d43f5613bcfe1fb1d144fd0","e48cbc2646a758124caa3c87b05a722e8da250afbc6fd5f4eefda072772b4616",{"version":"534cced4db5dcc639cd555583be09c6891c0633dc395308c87f60b47dd54a6b2","signature":"33ecf206edccc488e96cfb5177f19809e8bbb549ed0e94ff66d1cd1ff1a1fcb3"},"056fc04ab05389b453bdac4ec2e3c1eedd8bb661c20c9fe2e184125c8d69dfb0","cf4d4045a6ef47b776863026fea118f50fefbf94bfaca15b330d5c939ebeae61","ccafa0cc21d137d4d29093eae284e4f38dd4c43524f9711d9976b29a4a709b99","2d3dd03df960f48735d9ea246405ce7f2f6501675599c7342965217e6873ac28","dc2a05a4d3db8795c9c161d8e05a72d0548cf61e5b5a1e992b958d287d148f20","2c008ab0e0b102c7d0752086dd258c08086879c4ee036b40eb909f53cd444f76","9c5cf595d6470b5fe3b37de5815b5a13b155e3e100313f9e30f5e8728dd9b055","3e5cd51e11096be95c01de7fee203750f0b365f46dad987f3afe9fb535b99122","e50fe642408753de3208274d3a7c83c42bf821821740b79012eb581ccb425bd9",{"version":"6ada175c0c585e89569e8feb8ff6fc9fc443d7f9ca6340b456e0f94cbef559bf","impliedFormat":99},{"version":"e56e4d95fad615c97eb0ae39c329a4cda9c0af178273a9173676cc9b14b58520","impliedFormat":99},{"version":"bd821b87e2c0fb5f509cedf47da465c447451835ce0fe2a752c4fc53a9f95a5b","impliedFormat":99},{"version":"f1d7352c0f7041abb43e1054abb14fb8c53a13dd54bcc1d67b97d2c02bb5028c","impliedFormat":99},{"version":"fc820b2f0c21501f51f79b58a21d3fa7ae5659fc1812784dbfbb72af147659ee","impliedFormat":99},{"version":"08f88f75fc2f516413477606a4122b6d3f6eb6680e8eb79f3fda5a5d2ed306df","impliedFormat":99},{"version":"6ab9821afd2a06879620eb4e041b9492a90f294e9b733ae5eb022edaa3964a45","impliedFormat":99},{"version":"003533cc3fa10cc457668d4256d21a65706a67a04251962cfe85d240502f8d67","impliedFormat":99},{"version":"6c00cb8a4b187505dfe21aff242b07f69f84f5c832e8ab4357af69daaee1b0df","impliedFormat":99},{"version":"de14ddf9d780367c6a117bd8a1718d491aff66094186523b3eea680ea7035a7c","impliedFormat":99},{"version":"ee06b94d0521cfaf91e4b003518eeefc45bbd594b0c22955fe35be282958252b","impliedFormat":99},{"version":"9e5f8fdaeb03f1699392b4724a58ca7b47c5cbb6762920d2bfc722c265495ede","impliedFormat":99},{"version":"d05bd4d28c12545827349b0ac3a79c50658d68147dad38d13e97e22353544496","impliedFormat":99},{"version":"a1597b0039f39e9f3eeaf120f02d0c94a826fad30b027a2abfdb8d580c89be70","impliedFormat":99},{"version":"04ace6bedd6f59c30ea6df1f0f8d432c728c8bc5c5fd0c5c1c80242d3ab51977","impliedFormat":99},{"version":"57a8a7772769c35ba7b4b1ba125f0812deec5c7102a0d04d9e15b1d22880c9e8","impliedFormat":99},{"version":"1de82ba3718b2b3bc5333c5bc35da5cfc46d1b654edc012de46bbce48126fcce","impliedFormat":99},{"version":"36eafdce35542335372c9104a44db2597b5ecbdb11af1177da13d457efc94fb3","signature":"d6ab9d3f4bfde85a62fea7182d4e68ba2104946541f47eab58799009a38ba2db"},"d153e1cec75d95055701de32dec8d0ba9c9a89ce85bd371b7d51fa15e495137c","fbe6ac1edabe62f9565a5dcf644865c3b839c759c6b81a0991e7314186c77c14",{"version":"86d4ff8ba66b5ea1df375fe6092d2b167682ccd5dd0d9b003a7d30d95a0cda32","impliedFormat":99},{"version":"736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","impliedFormat":1},{"version":"4340936f4e937c452ae783514e7c7bbb7fc06d0c97993ff4865370d0962bb9cf","impliedFormat":1},{"version":"b70c7ea83a7d0de17a791d9b5283f664033a96362c42cc4d2b2e0bdaa65ef7d1","impliedFormat":1},{"version":"eb541103f61ea69ee1795085a473b727d46fe49ebc7091c721623b5ecd87c0f7","impliedFormat":99},{"version":"014ba72e2add59d6d2d2e82166647982c824639e2902ccd7b3103cf720a0cb65","impliedFormat":99},{"version":"e22273698b7aad4352f0eb3c981d510b5cf6b17fde2eeaa5c018bb065d15558f","impliedFormat":99},{"version":"b78c801c3c21015ee487f6494448bcff55bb6b61f41172dfc2c26f2218d99138","impliedFormat":99},{"version":"de97e016d8dd4869febd5bccce02eb96957089d04b74ea5d1dc0e66112493b64","impliedFormat":99},{"version":"671ccab2e6a253d2516c0e4699b3077fc30cdb70b4436d8c79d76c91266a1a94","impliedFormat":99},{"version":"a11fbd8ffbee6e5a7fe4c7c23e6a391be615de2e710a6946d7d1f947a85a1374","impliedFormat":99},{"version":"2d383c515b9b606aefcde23da9c312a69bc7976b75abb85c02592f7a8589a343","impliedFormat":99},{"version":"e760f7860d08e9d42b6ecd7dd341602fbc0c13d60eb30beaf1153f1c7c44d66d","impliedFormat":99},{"version":"fb04e1ca667399e7302c033656cc285e6c1cff9c29f264cf229dd25e3962a762","impliedFormat":99},{"version":"ca6fb77e3480af8f2287ccb756ac88d047ba8a8bcc0512f6720ac1216e274ea2","impliedFormat":99},{"version":"c0cc44b0ad2fd65c933d187c4faad6157efbed33c3c21023802aa6a89d9b9d13","impliedFormat":99},{"version":"ddaf5d3ddc45282b19fb0fecec91c87fc9b4d1f45c2ee611677345c81383c5c5","impliedFormat":99},{"version":"5668033966c8247576fc316629df131d6175d24ccf22940324c19c159671e1c1","impliedFormat":99},{"version":"d76df1670eeb97afbab6c87b8cd31bbd09dbf9026ff0ca533b5d7d3fc0291f79","impliedFormat":99},{"version":"e9b947b944887cf2cdea2d6188f412a25125eade82f2fe2334658af86f14cded","impliedFormat":99},{"version":"bc05fb9d657d30e61d50d690615f379b0d0415b8f29e69196e1dc6bfc664dc57","impliedFormat":99},{"version":"e315bab2f28d53f9ab473d9de610c455b6c414757bb19589b31ec8f490cebd4d","impliedFormat":99},{"version":"d999dd5abf4befbdab5f1248193cbea69b323b71131a02bb120f9462807fcd5a","impliedFormat":99},{"version":"031f1805f87171e8a9125cd99105bea4a869018ab2356c2e29dca7c86925510c","impliedFormat":99},{"version":"bdcb070ed484b40b84dad668b58e4861f7c3d36f38632072dad5f905bd8cc0cb","impliedFormat":99},{"version":"54a97fd1e2f33041d7b4cbbe8fe3235f97fdf1a05e9fa41e78417851bb8f1c78","impliedFormat":99},{"version":"f63e0743e2ac9f7eb4d3b8bc110834465f164eb9e75e4f531046e4ff9822f3a4","impliedFormat":99},{"version":"0372d6d6a41ae89f9aa86eef998b44bc0ba035d9d09c9226e0a150ef4578fc3d","impliedFormat":99},{"version":"cd8a4297d0ab56dc571dadd2845e558c9d979fe1e120a0dec537935bc8a36dd2","impliedFormat":99},{"version":"079a12cb0e0c42655d77da5185e882b4cc94bd5c6c2131171a9289fc1f4287fc","impliedFormat":99},{"version":"50cf14b8f0fc2722c11794ca2a06565b1f29e266491da75c745894960ebbce06","impliedFormat":99},{"version":"d4ce52c42c23981d958206037138e05f7b48d41faa1cfaba7e9eecce8c2e5489","impliedFormat":99},{"version":"8a3be5afe0275ce84a6a6298010e66d54d2d2f8e927df6bcde0ac326b5e81792","impliedFormat":99},{"version":"167edfac7664bec77aa2efb2ce9d515c41b5cc4269091a946b3fa6ec4e7e8738","impliedFormat":99},{"version":"218997a627f0efc4b8be5e6bc0b58e0c9edf250baa3674b661d3f7e6a7ef21e8","impliedFormat":99},{"version":"c3f1acbd39f587a7539d435d6c78ce8647b3bfdc5435df153a70cb2656b52b80","impliedFormat":99},{"version":"a1f568855887e2b5121e8b5c4cacd66dc5428259981c47026d95be1d844adf71","impliedFormat":99},{"version":"1db6f2cf99c83598669df0c7166405b0e83c153a03b59115000df3cb1afba79c","impliedFormat":99},{"version":"49af73d71b88a99b1a211ec02bacd321c21397062d253c605bb8d140082eb7b0","impliedFormat":99},{"version":"a50de7f1a7eadab7732d80dcf9c8a0c0d7d00e33315425316757006b5bec6e46","impliedFormat":99},{"version":"4c6fe268c2a984b3f14031a4b09ae7b2d9e51673258f4b4352e48d0c6ebed679","impliedFormat":99},{"version":"3bc3d81dbfb842bcf15454aacbe37cfeac57f1d15e829812ad02a05cc42be873","impliedFormat":99},{"version":"016952415e1ad35f9070b4d454946e79cd3881f3c76c0978d759b168fe033018","impliedFormat":99},{"version":"6bf5dc0c9f6b6c79fce77b56c985dadca4d4d474c9abf9139ae0785cb5c01992","impliedFormat":99},{"version":"d507276467f554e383b4fa058f42b8fdf5c15f1d1db84a2372bb569ce9d57a66","impliedFormat":99},{"version":"b3af5d182c9ef267d58cf43f3e51ab73986862436790a4dbf076b5994758d53f","impliedFormat":99},{"version":"1c7f26a88f861afdccd8f9d9e793f1affef4635d16d2609c487480c41c42f253","impliedFormat":99},{"version":"4d4551dcb3fd19a4f22aaa63c6c391d42ce44a15602a6f6a19d582709edb24d9","impliedFormat":99},{"version":"a76075b5aba8187b1fc5c8f565745daed6e4341e64b44e6ec41412a16d575d62","impliedFormat":99},{"version":"f836bf3653e31c3bba120071196c95d416b83c5d860ce27549975f8785cd670a","impliedFormat":99},{"version":"058d970583137cface729371715449aac0c1388bf7a5ba15e0be952677485fe3","impliedFormat":99},{"version":"31c45c074a9acb94dbe340d9336d3c915635eac2df3308916fcf41f2ba6ab84c","impliedFormat":99},{"version":"774256d456ca1d8266f6e2170a51bad2659cb7116334d1e7977595999533a5d0","impliedFormat":99},{"version":"07916d3ee50b94b3217c0bac71fa9d70d48f51586481c7cf61af2a8ebc2a9db5","impliedFormat":99},{"version":"f687f35c2206a319dc7d8f0b751e182638c912838ff54034fb782beae50f7cac","impliedFormat":99},{"version":"1ea2d362005804d980325c2fe6ca0abbb145197b856a64d80016554129966c97","impliedFormat":99},{"version":"7a81f15892b1c8d0cbfb35605038ce5c6d0cf93542946aa0b8c415dbefdea1cd","impliedFormat":99},{"version":"22ef1a1604bed6e226888a2414676ef477a7ad5d6ed907a62d6e40c831797366","impliedFormat":99},{"version":"2ab500573da35083b48fa8f4fe719860099d1502df3384f977eb22ab6b14caf7","impliedFormat":99},{"version":"9fd7d60e314f01c950ba31932c150dcec5db2c82de3c7fe0d0d24ee8b54f1fca","impliedFormat":99},{"version":"841d1f32f66723468772802e1558eb36ae0226c95d0527a3ebfcfa2c75b6f6d0","impliedFormat":99},{"version":"d3327c9f7dce1e11f5ce85b8ca921668a13068980f7f4ea4daedbc2c81589e9b","impliedFormat":99},{"version":"a698ef4e27ad6053ad8c189b53c468f857501046aacb554eb52be0403ba7f262","impliedFormat":99},{"version":"94b576c860480aac3eafdf904cd81755f5c9b16c3e0ef3253953a8f4fd8cecce","impliedFormat":99},{"version":"8dc7c54b72cb2a49a7639dccd99a559c243667a74abfb09545cf8afaecc58056","impliedFormat":99},{"version":"d2166d3793936235216ee5d014bcf0d8695f3a954ad54c01b1976c05f544ceea","impliedFormat":99},{"version":"41bf8c3193b575946682ca243de53370f61917035c3ff3fb747067bc680f2509","impliedFormat":99},"916fafd410b9c3a04bb3720774b0cca93d1dee94bc88b4ecb6edf56cd5585abb","ef82bdd9d674d855785bbfcbec2181e8d602c430bf73b4b65ef581d78ecdc64a","846affbec83fefdf905e16b3fbdf845edaa248b5895279498aa6ac733ff2a4b8","227f3a03b267191752ed1a2381855cd73b0915794ed51151e5ce82ffd786dbde","4a700720ced7ebe4c0c973bfc450c6a7ae31f82fd447e0f464c7171562e8aa53","b69c8778c50bf0caee3dd1d2da2fc7d5f6157498b51cdf51fac81476850c715f","39bc5068f51c657236fa9a763dde5bdae05b46bdd49d0390c4a72fa9dcb45dbe","2a6012a4f4a4695bc0a97d29f47861bda054359a9a60d295ab26f416f95e8940",{"version":"094e1a72a14a0f38f950e388d9a4e8f6118b493a5918235de9781d5c47f327c6","signature":"d7cd6120b5ccddff937be1aa22a538829f8a93ffc9b4715519f67bd21da26689"},{"version":"60523c590e7ec5b89c49c0728f8b64ec2132482709ea5c5909752d6d68c93401","signature":"2315efae7ec760b18fa4c15f987003721972b75388eb00f80f3a419e91159751"},{"version":"96118b858afcbe20db893025dcd75e19fd530bd5540c65029500e8cc251c34e1","signature":"bad3fb3837da6b89c49e110430e58827c321031273ba09aeb1c83a1e0e9dec70"},{"version":"17ae4d2c0c3487e077a7f6db647df1ebc56194a135262b8af15b19a3a1072452","signature":"7d5919c7ef0b53f308a78754ac25e352473a2de6bf6e25e109cd03dd493aab54"},{"version":"a389c1e6da14dc436285d19455229c3ecb445f0d26b4de5e4df0c223e43545c4","signature":"7e2734061c31bb7fcc162aa37af53a181cb8db1bc2ea1168ecf1c816cf52e045"},{"version":"c40cca5deab8288e95cacc2e5f8d1d2717f9b49e3617cb3ac992847d5a143fc3","signature":"d23b1f070ca79bf4cececb66c23b78eb3f35e10b3ef7d0119549521c9fd2ccbf"},"be28318b8f96ff27ce32fc1882bf6f18e306dc8ae65ca3361d7769ff98c933b7",{"version":"f8fcc667b4a4cc586bfbf3d76cc17e91bad6749ee634f736ca957ea7377cab3f","signature":"4f3963b6ccad89bd71ea9c5e491a83c9b448df7d36a01ec887aea29400c52cdc"},{"version":"fee446e0178c52a271d63c9d12598620eeba7a0a0178def71ab7eb70837d7f26","signature":"5eccb4db63e70774c70de6e6e6f67f3f4b26f2801767073541a772077c2b8458"},{"version":"dc1d17e168090dd4df773ba839410a2e106ff0f43163a994f681a0044b4de578","signature":"6113e636ad4fffa72648f2a41eb42ef83262089973d54e080d130cb4219dab4c"},{"version":"01ba304e845f2081cf6ce244153824e727d70d9acbb973de8e2b6340e4355185","signature":"9adbd23317b76a09a9364daa02f7ad358ef30c277dca1b05e8df356f7751702e"},"e8ac8a0a426c433de3f592188e1fabc47b43cc63be440be87f36f5f90980fc56",{"version":"5e8cfa753701ab1bdd8545e9436da3c53f24c179978efab36a5d919484516735","signature":"03f8247a05f82c8f96aac14f06944d2dab5061d08fe71d98e429251144b7d9d8"},{"version":"e9ff70874950ac2fa288fe64a6fd622a06d3579bfd781be96bea79fee7fd1381","signature":"1b758ade259220a7723152591ae4997ead9ed62664cd36c08289f94fbaaa5511"},{"version":"574fede341569986c736fd5468e28194913dfe16685e804a5d5fb0a24e71384c","signature":"cb0d718f6419c7cf0ae1316734875540e46cc33e10e9aeecdf79e2f29f6eab4f"},{"version":"2a02d329b308ce5a74632fe2062c72c049e672e5941c5a8204bd14408859c3b3","signature":"291d5759f6ff0d7180456b40f53ac4ec52d6fb91b6688aa2748e70feabcc8124"},{"version":"3970978fc2242f1ee735a0b0f0dfa8a42e17bf5bc8d9200e4e7059c63877f4d9","signature":"f8328683d5b3f602c387cf200cb1726c422dc90197a7fdb0d578fcb7c9bc6786"},"5d420f3f67f2c448b28bfc8a6aaaddfdd5e3fac96381e3764c75cbb0540c3211",{"version":"9607e2d3418c1e50af1dac762f78b031f5f9c24f13ca4990b062f27c4f09a340","signature":"4cb3d1e907efe7537c8b4603e87bba3e9afd8e3294a436401b5b95fd2bdebdfd"},{"version":"3f9ae10a4a447dc5fd8d079cdac3d973bfbfb61149d6d34421ca8ccb9fc25a8c","signature":"6fb16d7f85050f01ba2e8248d33306db324277a87040aed2ac58e20343a0c2ac"},"d8a627c1f6473ead38c4a7fc6c22a1718e4f4b83855f85eea45cf645aee63cb8","7b37c073829fc3fa3f22a6252c214e944b7c306e8dc1a4fbfbc5d6f2a2f95c5d","7ebcbdfd5763421e021e5472bdcde0bb7dadd2fc6bb2d81f70309a89362155a2",{"version":"826e0ef6771e8bdb186b153dfac8f181926f29570c7443560bbb665099eee80b","signature":"39dcbe7a573f3d3df729c6028108cea477260aba94ca082a95de9d02a267ef27"},"f955a769066260ff6a27a22deed2c93ed071342093caf86e7a6309d35eaaa480","60ba574b03771c2da031380bda16f8ebe86e64be2a04f31c53d572edf987d8d4",{"version":"316b866c3bfbe957ec585f572fab4b2f7a35e8d9cb266dffe597e57927a5d66a","signature":"19d7ddc11ff468813dcf97fb05f4e51d6f78e16a0030933a608aa0fb9f2ff9ad"},"a17a4fdad4f5f7be2b342254233644413c8ef984661db43c951953933083d8e9","db6562108a47f4a746b4bea1694912ec1ac7ec51b48e3a31b274b4c8102ab772","85592302683b0f3d636e53a571bee7fe59803339b8b3eeaa9a5f3e43717bf81b",{"version":"77e9654c2e90c0915a4894800e66a9c269ffd3f0fe06bb17c14bdc23ef7f5d1e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"861c7b678c64d3cdfa0ad2a3f529dc1f57ad0252f6bf7db739be18e14c79c617",{"version":"8ab61002066e314d8b266725a4ed3db857e41c7aa11927aa4a38386370204af6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"62845c5b09ae355ab3bc4c4745dc5585b77b447706ebffb09ea3641e5c963da0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca973ee48e138941af5747d17cc31073d1d08f57241a81b8a6370fba035c57c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"56b2d36623f14185ef2134637e9da591a86b6faf40b78ebcde2a390b6cbd5b54","a2cb0d579cfd7ee8015c6adea94ddfeb2d7e79c040ae9ea9b57275096512bf0d","1d95bd896b6216d08fbd7ec10a33b40d09d711e3fa102786292ceb82e4b8193f",{"version":"6dbc20023316e17c6ae6382458fa64ee65049a6367dd648e89ec443cd59ca18a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5c6eff88e726a6c9ccb73bd9f6b02dd0e248fba87dc47e7b3e211f3e9680b24c",{"version":"31cf5e25aaf868c1ba0e195b7b62f5cb516b70a5bd6e2ffd8eebad1bb011c24b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"1896da12486c6e51bb02c20eaf22d1826fe48349e584f2c59c8506e925172b44","8724ccf801c593b9a763cf5949039e650f4c7ca57fcfa045d295e911d03f541d","5903b40fa3676f924372e37bbdca65ba67e3191a92f52852d5b70a2153f664c2",{"version":"9b80069148c23348d866893c51792242c00832e28ee92964dff0c305ad1ba8b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ac0bb5930db453fdd87419f223d44c23e8852223300428032ca09c4a497d9ade","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3acc54979130b5a6786b3cb3a1f47f0330b6acecf6c509a09c071a1760e3f09","signature":"73351372b4295fa8b882bc93e30276d7a911cadee0f013b17f66d50ae3de6a29"},{"version":"c7b6e3a82a16fd54330388cc5023d8686071c102d3a4cb1899a74064910e7704","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"7739faa1d7e4719d14729d52aef996a6d8c8b1b1447dd9441728c642f46d4f79","d1e9b0eaec1fc9821665f79d5cb10b16f5aedda997b77f67cfc634c219be45cf","eddd32b79454e4df90e6e3bd8d43a997c8813aa61e2be0c79875c87a5d9f7b2a","2c217dd4af49f75ea5671d76d7129d3d3154589fd0193c323ec2c687ed13ca62","70879fcfe03c15515033be18baa3afa57f4f4a6d6bce8801e87050b02e04df55","811af3b90fea77ad0ddc26b1a7f884d9366a44b20efff4c2a15de5bc9b35bb2e","fe3c52844859ce7b95eba27362fae54be53773604213757448c2bc92760f4c49","e292ff26b159b2acb49baf29c18d233486c78afdd409e727b71ef6cffd21378f","4ed889a15e24a9e0f20d3642768a080cbf47254ba81997f4c88a37d1a7d0a7d0","a161552e025ab65f8b854f9a3338f8c69229c0493b13c323252d07407ecbb1c1","154af56b732ad2cf00fb80508d1f3158f0497507c9309670b66758fdc0461bd1","70e051b3ac6969f054669d0eec72f57662efbeeebaec77174e96cb91dd3d7b9f","c4ece3fe232b07819dab6dcb382d611f3b1c06a6b93cd924ef7d9abd8d090d10",{"version":"1747682b50a243bbda982e8ef09306e5dc2bf9b0a0def44da795c851ef31d6e1","signature":"fb891c7c97af75b1638fcbf3b1cf51815a1f7aa9192f202bc4fbe295827537c6"},{"version":"8acf6816ce4505a5ef68bb1ccd8d4fc30815a83e00da1353b90102ae5160da81","signature":"997cba142aed5347c9d15f4e15f6daef2889c2fd037587841778b8ba476ea168"},"0c82b7ea29dce9d9c5ad81687769d14ff730a377fb9bc3c03cb16fb8ebdfdcb4","80c8854964c4a39f42fdcf47a985104612a776c8de5b7e08e929c4389331a06a","ff619d9cbb2254ea51e7d71384abbbd5d72f2c93c071fea9c32b64ec3342888d",{"version":"5c0d00b05aedbf7b0bc483dcbb388e94b5948cfb1fbff930af60dfd9298dfc50","signature":"064b44fe73f0f7a084ed8e01bfb2ccbf0a6203b191fd521fc2f9c76d21e652c2"},"e89aeb89eb7cf6060c0af880e07093c91b08938d8b3a82a8a9b8fd5ae1d056f5","e8373b5d06c8923b34324f9df29eb35bea64a6a995b607a0b0fb2fb8c3a3a140","6be1b5921e30052b789c02a63eda3517e0686c0d8e359d9ba5bbbee4e738d1b0","0b208ce8494b358652ba9030cf0e14619451807547d25b3b5b720aa57bb940cf","d985bff3e70be34ddba319f5e9209e8eb799e392218201acb3afbd77b6ad4d5f","5f45030e7b52dbd77b0e101bccf5bbc08537605f8fb10927b0281a51fb2abbd6","6897d0d0498030dd4d7b6190a78010c071e924f62811f51897f63268faca2248","cbc7b28d500a1738964097922cd6c6db2adb129dadcfdba9c1d56b77697afbfc","a219c1949667d439c27329b94cfdc416e2839e8214497fb621c491eb24cf3bc1",{"version":"20769f36dc2e033c8fcc237bc2d7a75682dfba17022efbe30ae06f22767869b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"05a2797ae1b679bba91ebd96c9fee9bcfeee3b3dd3e400ebb3ddbedbba606306",{"version":"ae2e40a1fdc7fd8cdab6be243e4541f50b54445387834299471885785e3b2489","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"96fa384dc9c129874b902106257491e15eb6cc80bf921cbf2906a779ac96e60d","db3b9f398ad210cb961c2b5d638e28f99792cefe9c50c81ed383d2942aa226e1","b3277a8f5c5b9d50cdda98e93cc145820c3983f5e8aaffd31f4316eeb0ce465c","bb6288a9c750095a16037444e6026de8bbdee3e77af676ceb41d4ab7a8aa465d","73ee1c42b6c6c78c5d03a0c111e53496a54aa3505a78e452f5e306b84f769812","ab9cef431c5ba3ad0da377558211af661ac8ef1b0e3bc5c66bb36f4cfc3ad177","13d80d19b3c6cf01d42d51623f934ba1ce71c75aadba05d91d0d67860d86e629","f2b455abf7da931e2c6af9e90e22ace14c7f357bd2cffbddd865d15b442030b5","70b399f171d822aca03548b1644217d752bbfde4d4ec2cfed1a214d7fc79840f","d02be3bb5d64b49b5ba9e768fa8448b5c127d09e2b8ba14026773c1fabde1592","a88dcc474c044ec5c3ac8536ae40771d408085bba71d322d73bf2204ea023dc1","b1d512503e816355be4952330e0a427949fafd8cb3ee124017b7a535dbb26209","842955471f601e4c1d21afb2cbb3d250ef424ced61416e8d7ffda5addbda9eb2","493e9f05ac502360eeea2d5c72d28984c6bb2e03dd0c1bff35e2e5265cd8a6ce","5dc03bbe2c52976d8b054be1fdfafa1b7e43f328bf48a19d5f62f0563dfee905","3e06d8650c98a672c6d811bc035a7fa2561bc2c87ad01172e5df460a8629d489","feb5cba45f6c40b8b4601f40eb48697fa7e2f7e3db51337f15c308cf2800da36","c0638b8d32100f3827e3535c4307c24b5ea5e7ae6b33476db318a8d706386626","0fb10fe09a6f0c5fb2f5f7bfc0855cbabc27cc4fb9fa3c56e5956f0673746ddb","7b0daa318777bc57de0c9198d4ca71d7f1ee1e3f02c5bd860ec5bf390e08fce2","4c5ca16923d08d3df7ce8003095ee7cf136956b93a3a87d06e046c967a07d379","6ddec7fe4e03cf6c98a431bd4b7998cc9a11ad1f5aace1c73f6a0784c7c9d503","048955e8ea4fff7090afeacc7a8d9a0d843199be15fb1ab402679f63d2a18a54","ca2ccf002342ee1f87be1682f2aab080fab7316eb8f37aaa6a76a58859b3de76","e85d9d5252dfb1dde90672424170e3b89cd14b07086f790d3c45aa3f023a93a1","789e5210de191f9b2c090a2acc40b4d8a1e86e02626cf05cbc6b60079b132f3c","3fbbbf2353edb06efd1d6d25286c1cc267f3346dd7a73424e35606ef0fc04eb9","f8bd8ba1c9d155a5a5543a28f8b483a2a66718ed4320402a5a4c4441628ca0c6","12f01407b6072b7e3a195c5c8e6148a2ac2bb0b355e78c6c5aa6284d99c4fa11",{"version":"e79eab381e519df4a338ac92944482de36fbd094b1ca674b8934bc55c92b25b2","signature":"78fd9f116c4a198c60620ad7374fbf67fdc41baa66b8da57db3b80cb6b23098a"},"acb0f18b8895dc2544741426df1c542d8441deea13b0fa5445d83a423dfcc4de","a50db966163020665ec8a68d0ecd79d8a9fb0d059c0f4d25ba53bdcd7e43cd75",{"version":"5880d909fcd7aa478c019c0916f68012f10427b2d90d203a9060517bb9ce4de5","signature":"109a47009ff1ea87255fbb9bd75f5f3a918ac7ab44ed123b521028f580aff53b"},{"version":"707a9214ca48e106978cf001b80c3f53e77ce04dd6b447dbc0b9c3b53faea3e0","signature":"8146af3e7bf09f628b043910cfa6b72b86f8109aa7b06167b9eb51a3f0a75da4"},"d97193507f74ca55d696adf4c7bf4dcaa581cc38da8993320385450a4837b988",{"version":"b7426a25a7942fd04027ef39d6e57d3652de5850a59c04b7a3b74ad2f335db99","signature":"ed09ce0bd7cf961caa2bbaa0265743b1a22acb59fe82fc227698550a7f0b1e14"},{"version":"d7e15183b1073666220cad96a18914084528dc05dc1e2af175c863afa3023e07","signature":"7afb481364c9e976ea5c55b9b02006f2496e68cc009eedc57f38264121a77836"},{"version":"b56e4b6d8d241dc9428b20e7be5d13487de4d263c5999f91d547983fffd8bed9","signature":"91ee1b220ead097d3cc5b596db9be622f0dccd9c05e4f4cf069f2e1db077511a"},{"version":"c5391ca708a239529f9f132919def5d73d4cd67786f87536da7e539d247bf149","signature":"44248c8a13f35779d07d3168c64fe9a1040ea2e66bfc4ff92567095c5b243e55"},{"version":"7fd84794a97f879f03f3067cc042ac622063d821e7b60b27100ce300bc65d833","signature":"78361a8f013fc8aea9c04034475febbc49998b63c3fe09f56dacba1e1c73f8fe"},{"version":"eaae5968ffd536215d143ee0c4a295cc4ab730c6306c0ff39da500a259fffe48","signature":"11fa086538a611fe1a99a34d1378e2579a4de6eac405ad7fb9eeaa51836977c0"},{"version":"169d8b256bea5a05efb2049b4bf5b8d916d986a97fe9000ad3af60c1804deb62","signature":"6840721f787baca46b15289facff041cf00967e6d746b6e2fcf657881b6e6c5d"},{"version":"0ad029b491ecca9c3bc7994015f376562f9fe7196e2c7815a7e7914545fcdb65","signature":"87d3a353f4a5033a14c02bebecb39e225f521c82a998c294c33481b9c5198271"},{"version":"505f10cf78d9caaf7df503e3c495055785de4c93e0286843574106d787d9f97a","signature":"4929cd61e267755bf505ff0a66adda55af5d318b84798ab1b46ead808203bc59"},{"version":"cb431697c9e94cf9faf8cb15dc79c36f21d951f2ae68a6cfa106b93edc373044","signature":"d21e563fc29f32dab8756bf5797d4c39b98ff0828fe02f8b766a8cd0f2130729"},"3c5a7c91728aa49db1d5eadc0e9f0d724dbb50b01ac203b8c577781846962d23",{"version":"c5e33bc47d97c9161f3cf286f89238e4097589e4dc86632a8a575135353883d7","signature":"bc3dc9e5cb7a7493571d35b9b2fa5a1f39cc7ad76f998ad62e7a98b56fb8df6c"},{"version":"a422e96804615648c7cfcaf2e23d5353ce5dbd305ef5f5467c7fff7ab39f5bdf","signature":"063c721b1237aa52f454a374210ba793cc38a5267af12e5f937c7f36bb33b6c6"},"0ef1d6c0063b12f4dea951dc976267bd8e11aca63fddb3aa10213ebd2abedf04","a1d10e7fa181933ae7eeb34361f76d99ad2872cf6da8542528df84e4311da86d","db4c881c4d0036d8676e76f60ff17c6fcf240dbea3e48b5961e17d9a3b73831c","938a30c9758bf74e9cc7471ce79996502c99446ad8c1c06d1c86634584ba939f","c82897568bbe3658edfc608ed84b0615593c444b3dfe66fb884fe1f6f9ea0254","163c58b665bd8dd47661e39af68de9f625b3fdfe912b4d3dfb9eb55012a6ab92","cc1c0d6d5a958523960410c45f1e15874e8d8091120d3d7ef90f6d510b00438f","47670cf66cb61194eb75ce6154b416f839364bced965df413b466ddfd00d099e","e4446e6fb84112ca5eb1da220fa4a2b59fc834a162499e81e1f016a9f3e64707",{"version":"20fdd22451018bcdf123b42bcf8f3607b54ec5bfc1a40ce6f3aa195114fee50d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77aa32ef7822978656a1cf7a8955056e16072d0b6b3c71c8fe81998678532695","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2bd3da8d18b5839c651f5dfffc391a3f583de5e4a3d7f856d908a60f47b04ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c840fa95d2134e19a21130e67e75c7d75715d95f35921d62b1d50262d7e34cf0",{"version":"28e48ac60dcc1bacd1d2ff442848e81673dc6e93012853ca87f3ab0784ec1ab6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"19f9e9dd7641c80df2f21391d85a5aeee1d5d729dcb599f89034977bedc50b3a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c8bb1a6ad7f07b0c3af284d80c5b76724ec9b6c2dbc1720d1af4018b571cabe7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5585e289b764f82b17802e044380f2f72b584c02f0a9e5e5f9994fa14079179","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc40f72f1c03ad660c6ad52cc2ec092594bd05e49bc5c960a4b0d30620dc55c7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"968339f16a5177a5ee35cf9b77108d92938ec1da02bd41e361585030b4f00da4",{"version":"bdcb2c9e692ee3ee605a7704fdb479fa10ef6d4271ff6b9ff995d355d40e2206","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"044ad589837559611012aad8bd6a946acdc485aef131a351c8a01c1bcfad9db4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"53a1989193c0f9f558c62b7eee59b3ecf57cc7c3bea2fdd469ed4fa2aafeb0fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5af67615072b85cf169a9b15a5bc2f54f874f32ff594fc80135b0229d46ed148","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b3d3b1134cd416e62ad730ba82293706888320b0ab860aa34a61c02aa48789","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"eef952dea22ec228c085f41b939f8824d7a8a9d5d53edf570d0fd162be862e8b","41c362e66d4ade6b4727a2d3dcf1a3249ab336e6812cd51cad747284736a3610","c83c8f01896aed99315ae67c6fb0a5c948bada628c8f7b19665a228711c2d340","03c10706dc050b16e0ab8f3c5adde2d44fd9c4510394ded88c1254b29614bcf4",{"version":"14b18c1e8cf5b7d1f6209fec9b448effce1cb2b878b6f9a818fe26276a315778","signature":"cce85e3b51a75019f9eba99a92879e5f990efeffcaa2706e38b8d56d9efd0a0a"},"0fc0b0f4d12ca9e700c29966067ae7625216994587ef69f173c56df4e531e166","9d2e9036f50ec7b8066dc9536bd50a76f1a2e503c4fa7ee1c92725b694600d94","6f13ff7ba32304eb4b4bd18abf9374b3b25a49146bb8b4b2ad801712dc384708","cf6a54f50ebe9b1fa179e3ae972e17bb5132bc1dddc612dfc2d868ca309999d5","6b3d4163477739b98bbda0e3722c1df15427f4fcdbcc044d4ae093622fc07691","c4af4eaa49b5afdd70def3eb9ee71b509fa90dec11ea33591f7a1b1822400fd1","920205f073d9e7a7cec8b42adeda1b66e3287253232703d75676ae0ee280ce5b","8e209754fc98efbd0db41c1da78eb4f46d55d6da176ad9c6988b5947c02ea59c","0bd708369bc7263c061b5ad5ae31194cc55010bb069d87ece21a0d54d2ec4e73","cc0c38dbb4436cef6d4ad0462c0b9230363a23303589e36042685c1132f33696","9a999f9be568ad3a72ecf729bcd348b4bcee26719790f21290a16b5bc7dfe839","fb37ccdae5fa6b7325f5aaf5d1b28caaea4c148957839827fc5b1f7ab2b2e2d1","177cba3134e2dee9afd65d1d508127f10141c81769cef693f3493e5f691892b3","b54e6b18ed48a74d2d6129ca2ddda0aff1c30d2c46e7640113d2fe6669a5974f","ab95e9dce490b100c486fcc8da962a6155125f7f98f2b8fe34e53e68cea378f8","6b34a6f3217920c7cab89d81ea67dc64b48ebca1b4fe06e38852af49ea78068b","8a354ab401139f416005ba61675a503152089ecad7ac237da3d508779c29957b","76b797eb5bc8fe7158378f9ae1a16f98a76f4963b2a6eb40e1afce5f7574dc6f","32b69c9d97c045cde841e4cc73b29d8a79076b995f19dacd96d0525a1c46a35d","fdc0244b111f72144b4b5ffeb4be73d77985a2c9839d87630366739702a7d069","2c7d3b8e7fac27fcfcce5e3b5a0043f58baf786e20e951be19087e05956faf52","feec6c48848e9e9fd2cc1dce253451511a02574223035461557a4bb97f173c2d","213c42f7d5367619fc1f7a022520c0cfaf8828c3dd910d8abb68b229c44f97ed","fe4805a16fec6d9ceaa3834ce1ab4d8d3ec80c3c41ad093c2d09e7d7a00fe81b","ebc3418849ce69c3e4935c9c8ae98abd05c1bda372d9ad08cd259635d6bcf475","29775ef79bb6d19d569a24c59922476271b093a1afffb1678254d66e938e6980","9e44fa125a873ec1319bf8efe11fc6c79ea5d692b7fb5d628f79bbb14dc03e0a",{"version":"bbffefcf2d2194e3c9cae686f981935765cee13a5f390c97363fed32cad90d63","signature":"c10afa01e312d1ec1d2e455117340bd869610913a3ddee3e1903060237b2d330"},"57771e45f6bcbfb36dac19742e8984372065cb0ca9d5339ea982668171da36f1","754e504a4f7e802cc110ec7cfab158438903677b6d1e5320e3d06b4215f42d7c","a3e35d26f2d2ba764a55bf9af3fb0c22806c238a222679a83b40c838c30c7499","4d1729c06dd03ddd24e92985fec6aa5863373fdce658884e07eb4827df021f67","b80c68e22c6ef3a8c82b3e48dece693fd7b4e628542ac28b02dff88b31385882",{"version":"22f5bdac2994c065f821a3c19074445873b02b4c89c5c4d26f95fb7319bd7298","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"da3a4a4651c78a13301492b76a30f6b89aff6919a14ce20dc48fab5473b99bd0","516af411d9621dcbf6547314236500360c2076b4b2fc61a593b09bebe1ba6e1a","9ffd818baa22a5a4a3494bda2daf646849c2635ad622ea25e34f4ee2c9a8f400","3efe902a8539920b21bd44d2d0bed08ef8a95d3c4601ede6848a192af8563536","0bbcbcd6dd929d9dad0cf660bb39c2c578888071f5a7d80db51857ebc1c57923",{"version":"b82cecf3c9b8434c1ecc9158fa74592c9429a053696756ca6b63f53b042b98cd","signature":"7199bac5eac9213b52fe3a6d9481a0d20ab76d2bb99cbdafaf6ead4e5914e7a1"},"001ec942abc451470202c4baf55abb69d0ba41c1e6f4cbcf39aee73608dc16d7","1ff67eb52f40826c7d5512f924be11f6bec373c92896df0df557a13a8658f693",{"version":"7025dad7d78fd9ad96f064ff669d353f930ddddbb39aa3c4984144fc6760118a","signature":"7d3b48b39ec46eacc882956538307aeec6db56edc31f31be7d6289ec2c92a385"},"8a9404494ea982c2bff41003f8de1daf258f83a54e917a6df73e6a6201862cbd","b1f167490ed130cf9c920ee60fb21e9dd2ea9e601e9567e457f609a61f2f062d","786aa97ed22b1c1aadb445ee997a12785c863377f4dd4a45365a1a90e1bdfe98","01f31174c59202f69635b2957a7a556a01c2ea194906befae45997b6d3c470b2","6abbb171efa9fad3d88c9320ec5eccb199b726f832482379414fd55bdd485a66","08bcfa6546d768789b5134c6344f18ae851abd4513e3431e91b5e955f07d7eb9","d9bfe44b7126fd3ce4741db90af68d24cf8a56104826770276ee19f133496d37","ae6be9a07e940a6f4b0743220077f33259542ae908744ab349a1a70d22f723f5","70442efc456b60235ae408e157a6f0caa8ceacfac13f5af9d27265a6a3f57dc8","31409dc7d6946f1566b501934ee84e4d61916cb6893791c2da2731b12ef24b89","36c6b6a3bbee15a10e445a9aad4f1287d7d1039b6b58224a01524fc62446e533","11fe25d3baf912c908f4a63d2e05da668fd9a9838f258130cca57cc5be4744d2","ebaecc11e0bd3f3451f11514cc0ca76bb2c763d10240b59cb187e587b9e01f66","035e2335298c2061e077d0ce080b69b4047792fb02338310ac848c334c590b5e","635713a99868407271583323a9aaca2958b2abe2ddd43d7f2ea987160f6ff89f","7c3c3e194f59da1a744d6d5c1090d144769d025c068b2f84524dbae0fd481d97",{"version":"fb888ca2d1491a87202204b095f2816e2e8041f8a0dc67718d21e3e963afeaf2","signature":"f6eb9961bbb1fc5507f52b8081d6e82eb3ef5eab264a5d926518296dc4244127"},"1d34e8a8581252ad585019e14595e44c1100a88d1b586cafedf89153b71177e5","63b2318993b6e0dcf67bc21cc8aa94e41c7de4936bc0d33feed3828d589d33ac",{"version":"454b346ab7c6e6fea1963daa8abc997bf20a61e172018cc3fb4f3da99adc3ec1","signature":"29c3c744e646ac31f51cb4ae4b0cf912d8d251972c6a958b100df797025a94ac"},{"version":"fc357aeb6dafb0b0088062750d702118459f2385d31434c8ce94ed1c1e7914be","signature":"995cd4a56687721b9ebcde8c6499921201e7bdae56f437f08f6a2ec2b1e1ca0a"},"6ba0e711d73e317b739a4b0b083a109fc3fd294985c81e7f3c284ce4bc6427d4","bdbdf92aecef77ec1ce77d842bad71821d8c11bb84335c99cbab6e6519885583",{"version":"33c2eaf9f2216da640acf1814500bdbc10e08241fd17d8e19eaa203c50152815","signature":"155a0ce1ae5dea70b7c62b88bad1d252f860edbe6973cfb381851ae84fbc57b0"},{"version":"f46250a3d3cd3bd34994208caa7d245088a529540bdf459e7225f4752509085b","signature":"4c117079aad8524348f5b782625f9663c24202732204ab321cc56f0913c99318"},"2fc8086bb1e429d2786b7d38419c2dd195328c42631f4c03800ba8b7d691fc6a","d68e4544ac349d3775adff756e88da503e34879d7993769da9b7c93f90f3a1ef","c15e4b4deaf1fb4877793b7cf7d89f6254a54419ce5357ef98a2f800c97825c4","df70517f2532151afcebc39b9984bfa3c5ee4677c6e9938df86d17dcbe6a8222","b6bcd22d528966ac3b3226ce4368fa2548b9d27086496f200acb6778b4be9e37","c5dd07118defee6b0126f06b654506b726f4c3ee059fd5373f474c2364f0002a","d99e261147a8ca0295f772e82edaae16711db8d30e2511b98c59131f6583c216","caa900f1d326dfd6bc47d123e685680bcc21d4462bcda44c92ca7cb4318efcbe","80f6f4419b10ac52e19081d625d5c87e296a4911d9079bc92b46eb68f39dcd94","963e29c03860a04f42f2ca7723bf2f6c8aabcce3c2aed54a011716e20c1d65df",{"version":"3571219af6edabae2c952146660e1734804bad8169857f4b8ebe6433463ec3a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f610e2b5a184bf4fa504123d543d6c34a35afa82f6a58cde23d70942c8d77d9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"b6a64100f55b037a2788401e6a59d3850ce656c85f3e4a0a8eaf66a750c6ed0d","f371e54f31a872850cd31df8f6580dd22e8a08a6ae55fbc1647fb650384550f6","16ed5d4e6d9bf022f732e27adf9081604d593b3ec37e9c7a2094c67d115d6e51","ba2e7bb3085f0acf77f6e173b0318d0db592580a32aed9c1d9a4bee49693996c","dc529f36460fcb82d608cbf7dfca17bf60caa2efcfa2ffc62dae265cf1eedc81","0cded9960cabc4a947c3bf0036b1e4cb71f157413ff8ff3955b38e1f1ea3a310","7dafd83200a4776fbc6fd2bbda38b6bf4743cd754535adc2d0ac4a5cae258aca","b3f296dacd56947df11418f474b12eb09c180449cc833fbbb203c13e657b96bb","8ca12e1da31b750904a7e9c542da66d01d735bbe9b798bcfbf9753bc9451aa66","4e64be35164e01cacc75bdc277a3412e76636435de76c0b193b3f3c5d4290d48","90faa6c0944d21ee0222ae9b66b39c9f18e1225f9acaf9c4b83b1d4a43b26769","2d4000626b78819a6a26c46ab8fd01ea13296c078a8ac19ca144933e47826a28","2bdab51bbfcea17d53fdf5cc1ed29d56e98a64a9f54f568dbaf327c25d2677b0","1cf312f6363cab7b3a2e6c67480fa5a5924eb6488bb43b766244327a6ab75db3","dcbd3305da0fa9f9bfa2b6775179a16b18185d820810dd243be17ca852b7bd99","da3e0ab10454bff69d784689a6017755f62f51f9270bc5ca33a780d8f1effed6","ab5b2fe21848a9ebf39408f8e5faa4a3fcce9eb6580fe9b2919990f573f70591","3370266542d151b96946fe6b140f046a0f1c98a99c2ee2f74b9c7f8e6c7c56a5","402e78c9fc8f2d232f0ba377e70c2ebba520dfde76cdf4cf3d71e28515c8f33c","9279672d35d72514a5d65cb870ae38fc12b87f6e814f1c8f60769021d49629be","77d640a224467919d1eaecefed3e3bddbcdd6ed34ae045f4c6c879b03ea8552c","35751d934bda8baf8801ff32ba94d394350eabdfaede494dd1651a99cace6f90",{"version":"6e9a6151390e4f86224464e69c92b3caf0f5af8dfc53cc5c93abbabf638a2592","signature":"570c73d45cc72509d98f043168119c5ad36e6b716e2441189f8f875b78b7d309"},"83d32bb6c68c36dc2c27d16caf470e429254d3de8c5c8f9ef91134d33299aae5","bacdd6d5210d35dc960527ea72f595feb0bf54996c092239a22b1e443f419a00",{"version":"f2e84d431acea5d959cec667fbc5a2410ed31a23a0e104f5d442db92d11b2492","signature":"8490537159f5b3a3fd14f628b32e977a351e70a3bd09b890fae5616aaf894cca"},"68644ec645837f18a23be76bb3f4a66f5812bb9c347e23f6c25fe93e7ce8d7c9",{"version":"b26b234c627799e3b90925ea36bd4cec5e57e26e3eb4b95d598b0ee19d1fdedc","signature":"a476746c1a3430e74dc7d6764252eb4efaab3f931bb8ed285d82185b2efb30ba"},"ae3c82b6b88fe1315f8e92eb498df792923d5da97e77376a8e6434ac660bbc62","5d1a09f5ab37a76ea0dbcc20cd08dd4dc224e263389aeac1c61ff592cc825690","d89ff4c66bb8ce9ecce1e47d62c2a11000e5cb57d27604af1ee22374cc7d6a32",{"version":"7e488d2c1064204830ff271a055a09dc04d15aa208f1c2aa19be88ba19f57bde","signature":"2a346de3340c2b612e54eaf8f283d10e06620b02d5ad511a26faa5178f473b06"},"6a52477ffa08adc8d4bd84879ac20a5436f46333996cde7ca4a2e53e4e2f1776","fdfee9e401a2707036f47501a7759d3e3d9ef181ee6efda7a9cc9539c17e8638","c2932a793359f3b09586284f89843b49ba29859791693df7e3713f5c169ada20","c11954f6c73d0bfdcafe0036d47648d71e8ca4f1a70b1ae88c815a703fa9ab80","fc9adafb376ae31c4ea9501ef266f0faaf29de7d76aefde50ec9c6ceb67655fe","af03f35fd7d1a7a1b59e8fefab3d87aa8fc45501cde4d5f42657a0af2dbd3b85","765ccc4e3f7042c4bf9a0288838c93f3841d85e2c3fd10e15a17ef5da7e348a3","bc705423f5e581231d5ab8472659fb37060aa3ade1a052f301ccfaa5eb836748","23f751ecd2c2b9ce4449c843400093a3359bd77b541c50c815b3f3bb234ddbcc","c1f283a5f8e29c8def3e16de0233029b469cb0c493d586c737e4d9c373e7cffa","301400d715a0763a26cde374da3440a1d4269254f6438f90f63b92e2ecb904f0","566e62da419b55e2c0504baa8b1e36b7af570e68ff1efecd7db3fdbd67d75984","f00ab38948981d4a7ee14b6d84a96edc3d50d3ac4412e4fa879210a4f34d251b","7fdf1eb6c97f1f98cc7cbbc310c8ed4ac840346236053e0453ba33a58b141735","ff310bdb1d2c5121653e826dd2e72cd137c909bb92fbbcaa12d612e6008eca9d","ca5e466b1cd54a780167ecb1b23e6be6ebb99ccd3e500bdb6909343f4eb08e70","86bebba5823cd4c0c8c264ab1e5ca89532125029e01b3701365d7c8b57ff7b03","15c6a3bcc2ccaba6a79ea23cc968005bd86ae7c98e1851abbddacda91561027f","ba697cec2494efd4491a9b92cf45a2b453b381938429db999e6b0ad8eb91b607","3b9f374fb01fb21e7d3dc1ac1bda5a6ca485e8a42d80c5857c0a907fb1d56d9e","4056fa415788fb428681ff6d118600c813bae18a8939c0997e3a3a0eebbd462b","701d18960c7fdb3d53f81c7081a871759da5846297845a6df470e448c1ee46ff",{"version":"178b68ad3da8447deb3fa36b903515c68a878693390dce2c3c51887138a4d358","signature":"d80742a6a41d9f569db06f2a6f1ce341e38a8023e1f172dfc596ebf59b320d89"},"2fe04834987c803287fedee95429f29ed93194477634301a80acea18732b0584","64e96839e33ffc472581904d3d5f5101ea95a39fdac17d42bf3b8080ed452416","c632abd896e5fc858119334bc27fe15d828dd2ecb2efa72b19ade831564e4a56","ad56f09ec02b513928021933ba8ccb5322184a5f145211adbb54bec8ab7c939e","4d9ac1ae59eaa55088de16aa37191db82e512889ea2e3475c923acb0fb5dd1ec","2b207ad5750863999cb3b248b98e29d8cf15b832e77bee46c23dd5c712094bcc","8a5547b3ef575d99d9981c3f2cec446e2b348ed27d728e3758ce04518afe2236","04aa306d9eee3d2db5ee5663ba1503459ebf0895272569c8b85b9ac10947c453","4219b873be82b7bea21e2c107b5a377780307fb4ff00dc949d086f13a2f0866b","a2709ecb4b779ee385bdfbe5ca4d5d7d6a77527d0d7cab0d286f18d935e8b4f8","3b40021cf5c4b492aa5cd8fa0871ab438f0da413ca344de421849513e4332ba7","9a6a75a9d4cbcfe725e96855f3af3803559790aa6b7e48a6314be4497e3aeb8c","579925bdfaa8ffdf328f0aaf7a2b98a43acd6c7e56f4902c31f81cb93597fb98","f749d4843e5fa4533b0e8bae2784314231b4404e332e8d56abc2692272965212","71c8ad895db3c65dfbefa63d75e779b2ce821e8badb100fdcdc6bc241f2f4544","25ff64eed6d319715fece8d041173a27719a7616837f57626e812d1ec3c6faa1","4e66ff6829096c09cab4b63dd3b1963525319b75bc885f40a82980d024253c88","90b2c1b62ad1584dc7a33d91850fc92996bcaec77e8dd5f582c4906f6039a7cf","2006492de4323a0166b032c02a1f8f5f6433b5e9756bfde1d98f7902aad7643a","38dfac0e60c6379a3276ffe33739a19e2c81f3359a73f80370b7dbd615239da2","7ecfb0fe515e7462466e1fffddf551ab3cbbb0120b9c60d8b0882da1184d719b","2d6e05af866b9a2fba0ca03edd8461339787f298085067eac7179ff66826fbf6",{"version":"c7a40c6af045ffba5250fd4b2805c5e57e5f7ce518690f180c83b65018840f3a","signature":"cf231aee194a0a458e33d6b2a8017c04c869079c965b00b9d294016e5f331617"},"fa4fd1a6c106daad4d2048e50518a1707039d574d96f2282addc0157b2143b29","22c9076e33fb7efff1af1d8c1a9c3e38eb017a1a07e62d78b8b64ab33664f2d3",{"version":"78baac76996d1d214302749ad18c6424d1952fc441004bc8b1ff78e16ae94f2a","signature":"e0fa0f834bef15145ff38c4f94b555e406815bff1d72c3cc4b911bed38024c17"},{"version":"a41f813b81e3ee6f2fe6051c05f77671ef035853004832795377479c61cbcb81","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e374ce00606b66ae99a8dd321694504f11749fa9f407bcc445dd4eb6c6b3b5f4","signature":"6a2a0e9055a691ef8a292a143dd336005e96f4cfed93373adb6d1fb2f7d67cee"},{"version":"bc8339d6590cff26e515017178e6a430e53c0fe8f4f858355180bc22278a1bcb","signature":"0c25e09a2b6916bfd4fb6138feb16d394bfedda3d5fce6464478918e2f3a32ef"},"50a97612fd2557f947fff7bc53118552d9eeae0f349814c410151cc6dc305f81","93b01da13427e2d00a8d73767869b61102d6ced5b8e6ef297006047e7a36b63a",{"version":"4ed34dcbd916c8746407bbe31966464ba2a40992a7d3eafc7b89fe9487322e0f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"980ff563c04a7ee054838de6d5581a1c74f879aa573e49083b767661eb497b06","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"073d7d72dada0f47cf563f302854c2f4a56a0fbdb4ca0bb02878abb996b14c71","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"81f5e70b2efef928bafed1f1ed517b5f3d7dbe1bbae734620b1680b8cdd0bc66",{"version":"2f9876fe775220881f9a1dc662c4d45a1fc6c69dcbdf3394d4dfa7d38e7abf08","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"957c4489f92b096c32fbd8a1ff11729f1dbe37174d0e02792a253a195a2a8ba8","86c4fb8a79f66576d0dbe6189315842ca38029afe2c6ebe5b69d720ae7204d6d","23a5597b552c4fa9218e4ff75c6fde15c735a495a7eba796b21a28102ab16307",{"version":"23fa3382c09d278365b7a211300808076300a0d16e6b7a7aceb22bbd6a5e2850","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32890338fb3db8ba265d19c7192bfa9a11bc5ee4c15154a4db81a4ddf1c8b38a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebefa20d8e7844bf717e29dea823d72e0e3851abec67bd7442f18d5e1c929979","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"4cfb9e24d12ee634464b2e685f0e830f3871b28e0173cc89558416f194d49f73","ef7f54e0c441529398e2666a264256395d244f143f2f97ce5737b8ba12f9dfb3","9a2f242d01ce2d89d7afdfd1fd83653b8d751731fe8484472e55caff6fca829c","dec2391eb73f6d626e7679f9c1a15a5a3939f799b408ee2ace519ebb16802d9a","6c8520dc79618d2ef97bd41bd2d9f9615e8d7c31289ad6ff40202de2520d8a0d","d7c9c2e7dc4c35e0a79a12add067b79cb96493da0593a7e063db435257c7ece0","125a82f0749289343dae5c1ebf6a992bd166e0eaf1c885f53cb8224734877a97","ed8ce303eb9c07bea6cfa724060c049d83421b0a03c671040208438adcc1ddd0","bc2358aa66dfb3288e71f8568e09cbf493eb412a7ec67ffa33cdc24b0eac922a","71c4bc806bfef481e0a6ad07ad37d0be53ac5d8b0d19fb843e6a9549080dcefb","3e133c38d7312361e47e684f52022933865ca28b6d5d1bac3fa6e306c64e54e6","bf92a5c54601a670a6c8b9c02336b7a63a05b0cb9a05cf290d1cfaa95f28f284","e6160352cc574ab341489fcec7150515a9565817b60ab0a003d6c1444fca17b9","15601602390502326a32314bdea6c1331b340ccc19d41e82a71e69e7521f9b2d","feabcc3b9de397321d2fbbecfe8069975c10ca8f7d210bdb8fb1fb2ca06a2996","6b55ad93c7c4c1b77f78a46e1d78564d3dae464706a767f3d25ffa5e3dcec0cd","ee249a2e5c93e9110ec235c1e89cfde32b81e509c667abb08fe9c1f2e324a810",{"version":"8f33490c1ff132d5483cf4e1eb80e6e6495eeee76803ad9e0bf039c16f6214f0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9530414f935f2d4311ff2b25d6d8fe9b119e40eb052183336306fc8be3c84e88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"1ec05ea95ea33b0711f491a431916c731d2791aa389add26b4b0ae1fae5de7b2",{"version":"6ad131fba9f64b1c6efecc01403b93c63b294fca637e29d8d515eef286d78348","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"f69bf166c44feb49a246356afb2fe5b9ef6eef32567ba98fdef5572be707ed11","a376d7cc82fea71186921ef0f2779295f1ae28d8685f2dcf5aecebd6ed897e7a",{"version":"0c9b5c4082f748ded869361cbb3f97d405998ff5512bff4ec98ea95213085ae9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"b3f09f17c91d57b6a841936dd215929d1ddb25b6cc36e2d5af8c2ad22efaea57","c61a278f15af8373e1c5dc59fcef735e0a67d0ec68e0bb39993cf421922d79f7","4ffba69cef9d354ab21efcc26daafa01e3426d6ce70629064bc121269544e2f0",{"version":"7b62e0df27e53f8b9b32da0dcd5b818882e5952125b5d0e4fcf618cf2e3231d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"3ccafcfd1f83fa4242ada464cd0cce589e03570b8d32806ea0ee8f66bbc75ee4",{"version":"83780a3b4577d40f2094e631b3929043444b0bb16097fcb8c7eca08dcb3c1427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c0acf3d7f2a5d62332da4fc79bcf475ec142934b00b1b0c8bfd3893f64bd1c24","70adbc536de0f2152a13491e0c1e76777e59ea9abd4217cb54cc7084f8574cb9","d767afd9e2f82e7e899edc3775e1d86e5acb4c7e6268acfa95c551fc7c02d676","34a2803e9127b665802f3808b668a5474c0e95e2efa58720312bed19f4461187","f1669908a2919eaaca00a2d247943b171e70beedf9ebcc743ccf6572392a26c3",{"version":"c582809c6b259123d3e999f8fc54040732e9047ad51e968d35de9c9e7b23475f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c6c4b346b7396de0a88562c85f142a3e6c71f0f0c3a51d8956d9d3d656bece75","17428ad5e6272b4958e99bad33e44b2c65c554fc5a4511c5ca18f6ee88277296","443177f481983e2dc6ed086301cafca403fec7d0b5f97d65658b79b7b37e11a0","87525aac3b68b128ede1c21fe4f43b896ffb651c5507ec5bf554021789f0ec68","2b52485c59bb8f5f8ecebc36f9eebd5bb9e839006267e67f14f40bf57c21e545","6230518eb3bb41f00853f984b9208154c9180a11639ac532d115aa34daf08a4c","63e54be11fb7b740bfdeadd63e8f451830470fb4add677af84ca53813253f593","41222d3dac2c14b6a1f71e0b5105f2e3f860186aa3db1aff6ec4d95f833bf6ba","59d51e5e8361f7051ead0c29c8a03483e6929dbb6cefc3b77c2c497f2d895762","34bd75b6379933f0a0371170d95905d43f72c8a3a2ee431fba5129470947bc84","4ac0ebe63335c8cf5fd698cefa7904ccccca2f9e5d27dc9e0e18ae1cbb5ba066","44da1db5f81f80f935eb95e20e3c925d71d68ab43379c478ef6aea748a3a0b92",{"version":"bd6074dbcef6177b94d63a539d60447a71cc249f93982528095f888e24d1fd9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"405ab3515b5d2f07531943438c5ecf082bd61434adbf4860e3f83cea145175dd","7356415ae2693e3f94e126d3fb31d42990d0efd882d063661d8a588124fecb67","d0dd8957db84d11780ab6f4fa208bc3827c49b5986f0b5efd5bb98171bb5a944","25028aa767cb234fb49871cb5dd6784ad018d94609a519cdc5334f590085d21a","28f03986bd300c037d0dfaa877d4e1ec84e84f56f87e6a28354988c4dd313325","94af03723f5fc0766c58bed116f2d53102c1f48eaebed8a5f0d8af8d6f38682b","6dd4c595d7c2e50ef87da5a03626aa375407f05af9a1edfee1556ff27eb68ccf","871e55ec9de2b9c46582e36d93f3ae0b8f9414bce0438125de318a235d0293e1","ad3602cf898d906862e748a847deed9392213387659382a47bda93090c24d2e7",{"version":"47beeb485aff1ad4e1fbd8ac6e8d36a24e150de10a30b6899840faa301655414","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"487344aba34994c6bea6db3099ad923d847a27e7c900106a17d5273f8cccaa06","a8a2e0a62736c86aafb6bdfb9d640a79dcab172ad24a4ea1c0032e28a44359fe",{"version":"0568baac3cdb203dd85282b137b649bbf8171ae2f1a61264cfdfdc1dd5f6c1ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"2d05701eea88d40fa2962c3e988e6e8c751892445eeceaebb8f76bf10d8fb47e","fadf95731f4678454817d68abb0951550e2873b96d0e549fc0e46e8b9ca303a6","cbe51fafda1456e3f033e37684ff3dec49b3c11097453e460cf494d612abbf36",{"version":"a5f1a3661ab26f668a8195833f02b4085991113b44a0bc7f790e9c9af257f07a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8fbf898b003bf3d70416df534552735d946ee7c578766469039551b5b5989a16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d43aea7abe28c92b8494f0fdd74762bc1d3ec18b972711d2a883ede1ca8ae628","598384c7786700c7d6208cac6007b37f123131de52f69441e496d3086f01599d","8237ed9ac77a0e6c957c04ae1939d077b1eb214150e0f2ee2330dcc698ebdb6e","9db5db65827dce6c3005c0ab5feb8dbc60776a2767d1f3779e4e56b6ac0eee26","750e7f25638270d4fba9ee9fa59e79d2d97cc88e655bc8bf27573dce9ecf52d1","745001d456418763f9801cd2f8e00a519d597d29efac153f41db8ca2b4cb5cbe",{"version":"32552e3b687bed279decc2ff81ba12b6d194998a102b5592537ef0a7bc246ed0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"17000b2a7cbc8febc1c38e79ca4aff5a824bca523973aa7b5c4be0313c10278c","ae3187226d80dbd2906f54a87fe586f0b33961a92b99f74baddf23943ddf197b","6290d7ae201e2cb37a3462e8f0474823749c74478df2c024483ba0f66b9201b7","c3ffb3ab371ef4a1c49f3d70e6cf58152abbcf97f79b87b81fcecf0e349c9e47",{"version":"43c903a3a3e6bd110c6e1e0edf3f119bc3863e25f534de171957fceb9373b791","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"6060795a11dbdc1b053619d909275140681f310638413d7f75dae71c0698a0fc","9b536f09a14585f7a60c6198eb73475cfda55bdb6eb7982562b14d9745ab3f58",{"version":"7f21b2e5f827fd2bfa35959f943d5f7c38bd76195247f63a7e00c35c869583c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c93a6690c5ddf530c35ab275c70a4a15ac6ca4a74275d3a0205d1acdc8f99d2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5a1d4f7b55a0f4585ce971998ad5602b25f56fa82c105750c8f770fd89f61fdb",{"version":"f9f0996b794816a3dbaee1dc3e8d20e19845f48e94a28b86ba71cd7dfd7bd4c2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"71cf715fe7ac9cbda3398c62715e9e41e205bb9f66c14db522c05d33d9bed871","c170d6a07e32644b63485cb4fec95a7b4210c95b0106bf604f77f60be4590609","7e8ad08f464b4d38665019d1a2e7abcf8431a2fafd4af65bcd93e71e9defe276","284afc03d292b1476a7abafc7a199b1374eece1304d742dfa2fffe29d1ef0c25","724ba566f050f9a5c9d59f094d43c5986a190bc913ea545fadd79e99201c1cb7","63c289c6931d3546d36c0cb59ea38f2d22ce5df282547200bf86dadb4cf442aa","e9a831e721e46b46f371bea50434b366775486045b009ed500a273a0c87cbc6f","220c41cb6d922f9df023fc9633b25d3f277be8ca0b6959d35510aa0ce0d7f435",{"version":"fcf4e15889b48532a4fab0550d27792e595ac7e53b656745561bbf0499175c37","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"9f93119e73d9aae89eb4897d9fcacebfc8131e4fd6add6bd0af2f085efbc1b5d",{"version":"bed5a24b28678ac3060e6247e7f1028d52c3cd0a5da6f8de620813357bef52ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"8e8323f9bf61781a5c665b85254c338ac0bc879cf252c408a9155fcde6d3926d","58da354bb341bfae058822830e25841c7d4e322f2c01b523533d976788288a79","7cd11c824b38c56c3331454c55bce8d8c965e483bef9c7889d44f06fd0a3778b",{"version":"9657559845b4561279a2fbfcbfd17fb71629ef81d05c3faf1856ddd14977c8bc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"de10d6cc7a07ce5c5d961316be25ee61e38b528aefc5b78bf4890f24c0749f6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"569beb54f189db6412e1bd14225b3c003cb7ea7a8b8ac9d2bb4a98d443a1202a","2bcf3b15c705b78d2624ca829055672f638ce38a4ec0bb25d7f776265ac833c6","d7a0f90adedfb247320507bc1f490cbff7e5c0236bf52363e4dcfae1219bb9d4","b2a4650779610aa8626f855bdead2a9ee445074ac77f0df56d4c3d74d471ac27","1a138b16a062718039f7b4a0189c173d4612c918f1391c15a13ff9d74d76c0cb","2177c2f515fe8ca0aff425dee0fa1300d9f8012e341a74dd4923378a60175136","d6e42e08867a127f7976389a59ccddd411a8a00653ebbc5d4f4d7a7cbf36dc36","570d900e54c02bb666819963695f97ab355d3a10137e4c90d48647fbef5a8bf1","c51737d123042bf7b78e65abf4f684cf71693261300d9689a5e35906b94f9120",{"version":"0f84ee74cf2a11b56a047e162e43e73678cca0e17a2a7c750939d80964fbaceb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"eb45916ddfa4b3ca5ef6dafdfc7ed7923ce2da5b6716632275ad31ebc4e628b7","6c3b470578a5bd66ef16829c185d96ccefc3d2a3377d9976410f500610ab9628","f13b437a12555b0ec040c3d4f6d3aed3eda3ac447ef37cdfb0e458b697a97b8f","972a8cd8b3335703b18119089e6d0ea65460a6b0502350734fdb77941bb0762d",{"version":"b5700efc19c70a9da0041f25525ecbf96491839b2e4dc8dab32f8e662721ab4a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27a14d107fa2f36104dddfe0d0f3ad259d6a5a8cf3ff91cce99b5e493f9395c6","signature":"c754e6829c741e6b805b1868f57d8dccbecec8f04c2bea49c8fd3906a9b4bb9c"},"931a84417d61b614170fb2398ce6996a3413ce2e44b8e8778f68944f2e90cd87","adb0c4e652e7e2fe0de47ad7ff507a8d633122926d15e2196cc45ee94ea1c574",{"version":"b03d1c836d3624a6ab8fd8395bcd1df2106a4c7da12ad82bbc7fe448968e7f41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20d1bd40bc36713f75dde61ff02bdda74cee057be3c13af6ee23fecdae565d53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"02cc4901ed1607eec674547e981beef06f1af8120dae3797ba9f19220246bc63",{"version":"5f26bbb408f078078d1bbca7f13884b9b9849023484395cd135394d4fa8e62e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"26085d6a7b985e91fad21164ed5cba66427dbeded7e0a672532ecff63d2e7c4c","87dbf0346d5746894eca4b429e98201f34a03e11331cf456d13e71c81212e426","dbee469d488b262f97f892153e62cd20ee4724dd8b7d253ba771770ac8114c67",{"version":"a3b5b2202cdedc66781da6676815f67ed036e5ae1ba2218dd9935a70e5b1db41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"627f1ed82ab6a133fab304b936ad760a3e3099352c8aa96e0560e3417f063909","3a6dffbdf23ab002e31cedb2a1ab916c66a51a78a87771cec3ac596f12d82fa7","95658c91b67a72ec6af1ede02ccb5802d685bf848391710bb006f1ff1de9cc67","66db29e3c77173b1a53f6d0f07474d50b9b21bd20e5427bf4a70015fdd2df3ac","baa9f93cd885deed2211a1f17e2b64074d45217f6f95784d9d7db3b9adf39f7b",{"version":"cdd21ddbcdf8e5073e31fe7f730fe3c4023c66309625b87b5b9785fe140190db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cac72a71dd33cd4dcf93a4c06a34590d661d2ce406b9734cca33f567ddcc7208","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"508859ba82d5f926349e4a9d51add2f33fec2eb154fed40a6a80f12df4d99bec","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9fa26168f88bfa67f9b9f82b7cdc70c643822adc48535a76c320bc7d262ad78c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9f11d83ac8f4908d460984e703c13f43b69aca1572d2949292bc9b95ecb7a2b0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f98e81010dd1f0a168ccf0c28c53950048dab88a9aed8cd5cb1cc7790f883ac0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5458b79d513a3c28249bd399e109764da57de09097034437d65d13753035ec7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abd6d932a4f5ffeb10ea89ddc43d53467aabd67f1424f03634d2bdb9e91bb39f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"e7484d772180a4fe32c9d12b3701087ec6479a1fb4027d02443b362d6748f265","d22df3d0d4a171faea1356d2ed06746654b7b54a6f134ad5ea64f2bbffbe282c","3b9a5b677b8ac9cfeaba131842398608331bd99d1b9a939cbcffa96c77b05f70",{"version":"6256390bc79dff5190177864fca522b99f1ff8c690ab411abb268d2660660479","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c871c193395edb4f0bc64f8dedd55c8d15a51a9519046dec95c4904242d7b2c6","signature":"3b5031a79ad3b873f4979dd714732927534e3a6d3ae7a9ec689c5725ca791ea6"},{"version":"3cd49322854ce1d737e709347cfd3aea195ff6e1b262d5958bb256c8beecfa0b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"41950adfe5fb33897a55572728896c2f93444277a234d432edadac80a0fa4e84","2c144fb7b835575d4eb400187da6e88cb37e0e58c7f2d430bfaa511f7f471fda",{"version":"ec563dab247f022b8527fe82436349f3792b975c4e939886ce128d095583abf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f815436168a53475078bbd0aa903c756c66bca0ec8c468ff534ca4312eca4bb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"fa2aa9a0b964ca9bd71c8f1b2554010f338e89979fc0581e1f273a56897086f8","01655680390019da612e557fa6c87313dd411791e200ec4a960546fa1c73860b","79bdfee706e5a2f5afc91eff7c3a186da1c451fc3827038d6bcead0160ead42e","4f719002191cbec9176949717a9a57b621e3a1d307a74ede4cc94dcb78c249c8",{"version":"9cd6b43d528855022541993f9db3ff28d73c7183ab1043df00fc36073d6555d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc96489dddaa0e3e3c918fea22584a4823f0f5b0ea27df04311ca25224969acc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9e68ae5f6432e9c50c43e9d2835536cfa152255e680aceddeb3ba2c13b5a24b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fb4e3677e240e9cbc6268542e651d8d7142cafc9b716002ecb94db2923231f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4fab0835f3f0569611b4185f74019264c8ac4338acdae9786c36fc5e73165f72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"095cc9d0709e52c5869e31a60f719773a43b60940f726480b689e301eb661d9c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6e4a5152f75e9d77ddeec6158887b08565816164545f301243fb653d7c57c47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"b850436d0a9d744cd3fb92eb3be65206791e3b4c21cd66fa1af395074ccf9520","5ae00f9eb7202bad96cf17277b37f8eb7ea8dea3e1d29766ca904c2b81f54043",{"version":"33195f2e0363a39a049cb3839f69891f3e92cdef82661f683823b7d4f2f3d3cc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1a91b7d838f633711b64538b4a4fdfa77eb8ffc9e1a5cad23d66a43cc9d1bbf5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e30d7ffd5f108b4f12429dca91377297ac7b070fa87b5680201b4c3da07ff6db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"6eaea317fe5b6bbceeddd6440306eb6dfe56c86796b5e90b0c86f03d98fff955","539ea0ca2bc54254c4751432472c80d8a6336e592b9701695ac473aa6c9b4001","0e7a4c03bedb6d5fe845344a08f7a3fbedc0831109d5b36facced86d3fd95d90","ce30fa93f285427c6251e073491cecdaf1e80751e13ebb7da419092fce4393ae","1eed9281109c026b9f052241336e80589c39df225980919ec591a01ae388f11b","c45bd057a7310603766ccd2d367916500bdc549f46285ba074bdaacc5b6d05e6","a0f194912ffe562a67d5570ee74538fc74e5b9ac3eda0c8188b314e72bc0b1a4",{"version":"9c63668af53291cbd777acdc086a76266b1f9c51e354ea2787619ffc3c10cd24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eafd0838df9188d3b117c9fe53e0c77b707f5a985d3b8af99f664de7a4bbed33","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"83e6f2be1beac83f30dd5f1e56d42e907c7ce21c05ac72970b6ebd370e5432d7","4f59b8b9f9609eced7551d65f5a9d36c47c3e8e8f946304c4a9202d8748c87e4","ccc6e094800a0ec7e18a71109ed4efc28f2070b608838fb625afe4ccf0dc9b87","313a574dfe32f592b23877fc0677f33c8656ea9970e4af30ef78b96e17e0a032","3fcdf7901f8d9f9e77895e5b0743e77242c2710c17d8ac73beba8a79e433b57c","5a991ecc505e086074ced217226118fbee9bd97d37d94e9a73cbe73cefc82b23","3c91bf1628b9af6723816e7f06ec22cbf5627ea3c793e802eee02aea37406231",{"version":"ffdd7e9f674d0ec87a1da0853cde6df604b57b86982b95351262e9b2aa5cc88a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"09fd6ecf716a64bbd71674daee9e81ed726a6e716a66786508e12f95d4d47623","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca5a2320c781052b195b38bd95a7424e01468dc5e78ef946d8eae4d218437eb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"0dbc717d091c928ea25aac5b7118713c489b0b07f74b6ae3a57803d4d704c841","7fa3cf6e959f107cb2e099dbdd80e4d78f6aab3a0c012a77a7b0d1288917c2b6",{"version":"350701118c382c3cfe18b010184b8f3af5ce366f7f27a48c1892451ba12d3dcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"2f0f520b1cab36fdc9f80c54b74f17e7189921be14e5e6384c9e76dd694c5df1","5f75a5464eb3481ed47aa72c7386a0bfa9e306ff570303a1ca2067a137e3cd15","9210170f2fa566053f02e2c5c3a77faed4e7e51d8366ec02adcce7953297fa56","b01dbe7929b0a92420ded501af329eacee87e3465038b6b1a0950bc7c8f90421","a09f8f187f3b0a161b4ac047191bfb07e8ef61816872267b882b311ebea87b2d","803ce94cdd49ca8ed653e63004ed3fcb16ef302b983ede0d5291257babef6bcd",{"version":"ebd54d09bcb969873eff5f50835348d41ed084785a1a53110b155cfe0875b7f1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1d57277c40e6512296d6635e280c6228aeb08d789aff2eb4ef865eecf86cc78","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d71d12f9748990e5a21ef6fae3483650f1da187533e520785ba561f8e8f177af","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3c1c6e2d0c647263a8aee2d16655f525c930d6b9784eb6080c93ccac28a7c9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a93fe9b1bfbc124b8f4777276537084f37469f91fb5ea6ba8637f62222f9d378","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"89e0c0b9430b0635a17c439eb81fe536ac9ad69c9229a832c1a661dab780a362",{"version":"81206a45e70c1954a56f3f56081b7161b80abd9048c0a6806deeb279b05b248d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"b57719fe1738f95cf28675ad0e55eb81a991bf372a7a5dda6c45b162bd094d96","fee02d6d186cd9b1dc4824242b05768bb2edc61614f01ad6207145744366a731","a83e4dd75c54300e6314ea2c0c5813b418d1a2244391acb001f263c9b1b37521",{"version":"27c25a73ab8e8e6ea25f0679d1ef24c446a929a7b9da8fc842af72349beb9ef1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0eef830593e6ca3e34d7c4af265fcbcd5d7ec2a6c980a442a8a395c98b7d872","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"752bc0bd543fc478323820a8595d137ea1fb8fd0d8ceb0ea05c3ded4bf1d3729","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4410ddaa3d6e3c1441fc5f669ea5c3e3390fd75f6127f06b1240625558160a9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"36919106e6e5c86f0628d2542222b4f6a09cf7955bd96c53a9f17a09b62f3903","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"f6be390877d224b02106db41d582eca38b6d52215c0843d3e6d78d210c956f95",{"version":"477bc6781d39427cdfb58b00ac6744fd72a76ddb9add5ee2b6fd7c0123e8c133","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d77a4e85ac4e7465ee559c7aa33e9b67794fb42eb006094e41de859e0f574567","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"efaba8801c46c71114040269fdbc963f3496d01a5b185ef05612d3d71f6c1fbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b4f65e7dc37a2e488233381af3c9fdd7ca6e0171adc8a9cb2d49d2fa17cd7d67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3d6c9ea6ee8d52f0c4b758310bfa00f87f3a32eda7ac46b4f0b94f8f14038a3e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c293a5c9fca11fdc9f025e3a12b767b2eb7af7e2ee0c0bf815015a355d2d36d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c120a6708c0c275899bcc98083090a85487ec866f85b6be29a50714dacdf73bc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4733417da9fe7eeed82209b53ddbf53bb76c0a7706f747945278a2c037ba2bcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b78fa0476aae9c90f1ba345f48912c46ff37b4c95cd6242b75cb57efd8f2bc4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9959681b8cffae14e821fbfdf3daac7759ccd92bd04413f45100301d8d08d20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"57ebd17c1bf0e09d222252bb67deb0cef31476b2476c680ea5ceeca29cfa3efe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2813a4305e2d3d23e4997d9a2f482fde783962eec4c66dcd112a3348a1b1f6a8","signature":"b84cea73e43cd5d152e01d2870e7736075b6c5ffd9355dfe2660b98078c17e9d"},{"version":"119ae1f4c43b80a86564573e397d49f6e19dcc54b96b3a513066a2e108e89c6a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"48392b4f5473115f4cbd2da11efb0fda7bb0610c15185a5838260c9c2b2e5745","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"931e404359129b88ae22b707a39298f2d8351f150a5ad6feabb975603272beeb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b522f77d275268e582dd53f3dc4f93082eb2f79a0022d066bcadb94a59b6c88b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a9c542d03e88c557c60e7dda6aaa2d71a05687764720b66dadf6cfe080888982","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c77cad2e3e80373964256a967f064b23ff95f5fc46788636eac8b765b2fea524","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fa264b7613bfa8f9e9f8b06198322e50d8e14692e44618cdb6feb7579e016919","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"02d2e27ee8ad6bff557165700f7a20e6dbb7816cfc60bca8c2613cfbd211bbe9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69b3922251aa575049849afbf72d429c17965f5827fcfc0b6636263d0a261779","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a23b58c3087d419c5d21fba70096b8a9eb42977ad61f22f6f7fba5e09e0e6ae3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"055e5ac1ac33ae174595f55c76aa1e371ca8819456cc5b97a69872037139ac72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45f9d9bf9998e2ec7147ad27c7edc2e5ed387302c4018c9f4f7ff088eb22af8a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90e9e2b93e5bae19a5e66972efb5e6ec11dc1b50b9e8259f882055ccdb3d4aac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"f397adc5718aa5b8ea60ec16afed311eafe510111cdc0de0378994c629ff4eff",{"version":"7a83e8d50eecbdb731f8da23ee08494be2d247cb9e8e5c3857da7cd9e07fdc50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"671265f64fc5c31cd317267ead0afc5c6c4634fb51204bfb54e3bac5d19d4db7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f1f4a3bf8d46ac603eaefa297ebfafb18a111a4854577d169bc3c0358bb373aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"89a4895e643533b7b76f782b52aa9b695d0961f2695ca7720dc50b48e9e55215",{"version":"dd12c9b4822755161ebb4ba65818948561a5982f5f493eca9f6f0db242a468b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"114445bc0794c2c9a3f03a42134748f545ea788a004e4667d7b9eff39211a61f",{"version":"8efdfaa6427be4a0852ac62cc450946e95bd551cb7c5b55dcc99675352e15362","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"09b4d72988c4682aa5713bc6a6df7892a7b8e2f10e1af3dc02985c6d4aef84ad","baefb08b615c9fc53a31c27981bf8899af8e01a0c9e2ab60c23cf0d324d77274",{"version":"465069c75ef1e4b084bce885c0a2ee70520c5ebb8f201fe6f85090a28fb34703","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1e1d5c76c59c49b2b6f32b7065d0a95bfd229908da2db72526ebdb9312ed2cdd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"99eedc96ba7fa339e3ac82727c73628382af56287cc1219589004ea36e1b0c64","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"40510866633ba6c635e0495e40994f4e3f30d9378f23cc26887b3ff5e56391a3","291678a2a42b52881173274b55c7583807a9e94fe535b9bc84458d1fea33146e","288e930c1a2661f6345d07635585f1fe13c2deda86e2ccfc349413716c420555","b8e67d1c4879855a82071fc676a117355eec33a97bac9b727c13b728ebca825c","beabd1db71dc8e0911944d9400ced2cd02de425ffeb61c6ca0d2124cbe64d785",{"version":"945b34137fa3efb0ae22928275c1c42a722dbb81a26c57fb02f64f71e5daa3ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"294041d51d1910e6986cfe979cd3732a5f7eae7f329589ca4f2799248e5a7265","8f270a39bc647847500a6173ebd429406421cd10b2410d8cc0aed908f2bc47a0","8718ff0ade9fab90f46bcf4f55402132998e3c7b2b3f92154d7b85ccd91ac76d","322dc7275e83a2b413717c27f9dbb39f36372a09d4b694d8e0d18034765f8ed6","3828c70cef320027121c2ae0386e44385b937709ad0a1cfa4744a0a270b5b270","a7c931d7405b9910835cda95a3cd42684ebf92eb7bcc0d3649f90aa32a2d166b",{"version":"78228dfdc1a7024c9e4eabee22c057409886b9014ebd22319ee8a0a9ed31e799","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"18beabc03110b8f4e1d1eb5a556e6de09834d365995b2f10b17d26a574dba141","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70e8e8b44c2fca9791db4f3c06c3cb310556b19335d656bfe3cfe6aee8d65622","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"995394b9345e8eae0c2413b22ea07faa239769d45a58bb219b9222d86bf2d9b7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"04b5ec07def664c916b3a73a5b4b31f3930a626739ddb528569bdd33f0300456","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa82aeaf9be0588a652ec636bca8e6d7be86a81f85bb22e857b02a469e8ab2b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e4c50fe5f17db1977becce308656eff49a36eb1010b46ca295c27a77ee66a10d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4b54f4c0273db6878a1823ac888998ad7a0dd816f1c45a2fa24e0417702fc7c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32b1d470365f006b5fb2ad91d9097eefce0fad34bba93764e4501f2611104482","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41b4c577be164a41be457fa1eff74c8923c8f08e8ba7e5e57d894424f48de2a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15c805dfe9b0eedb507e5a9d32ae6e321327d77673ba6181de4710f2c2634cc2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5cc1c46b57a52eba565ad27fa54cf2e09d763de1f3412354357e6085e0d89ec4","b679abde8e957cce28fd0a30fda80cd7b9042fe9a9bb5a9369af5046d043fb2f",{"version":"47c0e01bceee2d7e95b691b2417954d55251167544413855e8440495dd67a5a8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b1d972a2b83eb6f85a7d894c57331aa5be4e9b93d1b9b16d697112b52069bc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b98d50f326e42519bc139f429f5699b4049056f5a9eec6d276cd70c4dc7f7a27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"db11d9efd635982368df0b9378618005c6a8f2a32029b66723870bca3bf6c860","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"670eb13bccc2eb7b1754301c59f8fb33f5e30de44f17835fc8e1c741aa3f68ba",{"version":"35bd7fa89a59ca3b136c221ef604ba3a8e30cc88bb03cbc4d26fee0659d02ce7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d596725c15eee936539fe4bcb0ec9f08b2d8392f0e9bce03effb76ed734910ed","b08b6177c9234876e6836895b0bbf4465e14c9b64bbb7467da5b89b9b5b11d89",{"version":"90658f66263e4b168ed803e40c3ffe4ff81df88ce4c044dd96bf1c4ef278c33b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a85610ba8978234a59d37629fafe05c27fb2154cb62b6b775eb8119802e0a38e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"adfa78b8af8a8be5116202f634a2f113d7801ed20c47767339f1505f952ebcc1","f328b3203d26b5f709e5a082bc956c2e95fbd9fbdca6abee0802b59b633e0dc0","34fc137aa6085dd4f915e8b4a8c0d48d6b07d4fda84716ba91619d5ba39566bb","d54083855fcc2ed66dedb389bf2efc33b892dcf572829e43758309fe7bcaabb3",{"version":"f44df634432426f2b1249398b735f171a84c3902b4e0452ea2f7cc3d02568bd1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7952ed742b48930403868cdd2e09a9b5aa543c9adbed9f012618d6b58c289dff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba0d14c18aebe4a5cba52b4a7b902247dd5a91106737e06d6e2112b1b4cbcacf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b8983ef3b44f8779b91c7604f70379f8c40f88da3d6863e4bb7a5d7f95b2c98","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a7b89eb149bb46fafba5e3eb85d5a9fa76013cfe937ed5c0b8898636a4eee533","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"23ccd9a0a59c200893dfa0ac3c539ac8f4416d0f43bce55501603f949ad1939c","ecaaff298281e8bd8bc234e03d4bc1ba565a804edb846005ea6566cbcc47fc73",{"version":"ceded34bed1c475b90671a320a8fd84a6a4a4d7c56c3f3f88d9a6804e933eba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6679ae1bd78dea53dc058ae235a3708f27ac7f87da929ddd38f7d4c222c18f9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a40eaa7da4b2085746448671fad7ca6da6a84c58cb1d0e2ebfba17888d040a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"df815a5a142bc0b6b160b0735938321d8454a4a5fec0923bb6d7dea3f6c068ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"49d82eb2dfa6a10a2a6b59d85b09baec0b700ed3c9f43fcdc0b1ec58ab35a8fd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9815b675507e394e469b6bc395afbe8c63d6736cc7290a73f56cfaaca549b027","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0248daeeaac242de0ea72ac0f093a31b55e70b43020d40380dbf609803a45e7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a31a79a9691f6153276381e906dd27e985f53c6920adab35199527cbfaeace8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3414d416e51f2846b21f4aa1492fd44bf9ffeaece26743ae1527154e3da6f7fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6309b32da582c7b3e5afdf30678bd7d456cd9a1118ea1c660dd73ee32770d683","signature":"4c372df16f354b44e6e653a4442eb9f26b95f2d43efcbaa75b59506276b92df7"},"26f213bee14ac8092e7a36473db58d1955fa4867bf5b091950ad8dfd31956809","4b332cfe58c80b9e5abef88dfe157a88f9170f64035fd2a83dc395b334c440fc",{"version":"262dc2495f719b674acd7919e678de874580311f4a0cb71f04c69995bf61650e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"c1161b186fb7ef72c0dbd14af1652937e6cb3453231dd6f56d396f43d46d638f","89ac6a7385062683575fc5ad85a18f77e6c9617a3786f49aba644d55ae277f4e",{"version":"3e33a62342fe8bc07fd5ffb6e870ed8f0d906f8021115bea5b4ef5cbd3632d04","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"dbbe19275d7ea098ce95e9ea65e45380eb8f80179cb14d0f2fb1196ffd9b98dd","b247732a1ae37a5e0307d4333ef15e3d5393951e3236be0287adeaa48d553b35","d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7","adf3ce64a58ecd81745769e79d346a3e6a827bb14dc6f81689449bfe4a97eb58",{"version":"b1538a92b9bae8d230267210c5db38c2eb6bdb352128a3ce3aa8c6acf9fc9622","impliedFormat":1},{"version":"6fc1a4f64372593767a9b7b774e9b3b92bf04e8785c3f9ea98973aa9f4bbe490","impliedFormat":1},{"version":"ff09b6fbdcf74d8af4e131b8866925c5e18d225540b9b19ce9485ca93e574d84","impliedFormat":1},{"version":"d5895252efa27a50f134a9b580aa61f7def5ab73d0a8071f9b5bf9a317c01c2d","impliedFormat":1},{"version":"1f366bde16e0513fa7b64f87f86689c4d36efd85afce7eb24753e9c99b91c319","impliedFormat":1},{"version":"fb893a0dfc3c9fb0f9ca93d0648694dd95f33cbad2c0f2c629f842981dfd4e2e","impliedFormat":1},{"version":"89e326922cadcc2331d7e851011cf9f0456a681aaf3c95b48b81f8d80e8cdfba","impliedFormat":1},{"version":"5d08a179b846f5ee674624b349ebebe2121c455e3a265dc93da4e8d9e89722b4","impliedFormat":1},{"version":"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","impliedFormat":1},{"version":"7fa8d75d229eeaee235a801758d9c694e94405013fe77d5d1dd8e3201fc414f1","impliedFormat":1}],"root":[531,532,613,614,[616,620],[622,625],[1019,1023],[1025,1035],[1070,1087],[1092,1111],[1148,1189],[1266,1289],[1293,1320],[1323,1332],[1352,1393],[1415,1530],[1608,1610],[1616,1653],[1885,1908],[1910,1968],[1970,2005],[2009,2017],[2033,2070],[2215,2243],[2245,2256],[2259,2295],[2297,2317],[2576,2591],[2593,2603],2608,2610,2612,2613,2617,2619,2621,2623,2625,2627,2629,2630,[2635,2656],[2744,2884],[2962,3320],[3338,3340],[3408,4062]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[4061,1],[531,2],[4062,3],[532,4],[3406,5],[3354,6],[3352,7],[3355,8],[3359,9],[3348,10],[3358,11],[3371,12],[3407,13],[3341,2],[3370,14],[3369,2],[3346,2],[3353,15],[3349,16],[3347,17],[3357,18],[3345,19],[3356,20],[3350,21],[3379,22],[3380,23],[3376,24],[3375,25],[3396,26],[3399,27],[3398,28],[3400,26],[3397,29],[3395,30],[3365,31],[3381,32],[3364,33],[3402,34],[3360,35],[3361,36],[3394,37],[3382,38],[3366,35],[3368,39],[3367,40],[3378,41],[3383,42],[3401,43],[3362,35],[3384,44],[3387,45],[3386,46],[3385,47],[3390,48],[3389,49],[3388,36],[3363,35],[3391,35],[3393,50],[3392,51],[3403,52],[3405,53],[3374,54],[3372,55],[3373,56],[3377,57],[3404,35],[3351,2],[636,58],[640,59],[639,60],[635,61],[638,62],[631,63],[637,58],[692,64],[704,65],[703,66],[693,67],[701,68],[737,69],[736,70],[716,71],[728,72],[707,73],[714,71],[708,74],[740,75],[739,76],[742,77],[741,78],[738,72],[743,72],[744,79],[749,80],[750,81],[748,82],[747,83],[746,84],[745,80],[754,85],[753,86],[752,87],[633,88],[634,89],[751,90],[725,91],[722,92],[764,72],[763,72],[762,72],[718,92],[730,74],[731,72],[727,72],[726,72],[717,72],[767,93],[766,94],[758,71],[715,71],[761,92],[760,72],[756,95],[719,72],[724,96],[721,97],[723,91],[706,98],[755,73],[734,99],[735,2],[729,72],[720,72],[759,71],[757,74],[798,100],[797,101],[795,102],[773,103],[796,72],[799,104],[801,105],[800,106],[694,92],[695,72],[696,72],[803,107],[802,108],[697,109],[698,97],[691,110],[690,111],[689,112],[699,72],[700,113],[702,92],[805,114],[807,115],[806,116],[808,92],[809,72],[810,72],[811,72],[813,72],[812,72],[826,117],[825,118],[817,119],[818,97],[819,104],[815,120],[816,121],[820,122],[821,72],[822,113],[823,92],[824,104],[830,80],[829,95],[828,123],[834,124],[833,125],[832,95],[827,95],[712,126],[831,127],[838,128],[837,129],[836,72],[835,72],[680,130],[659,131],[662,132],[658,133],[678,134],[657,135],[673,136],[681,137],[663,135],[664,138],[682,135],[676,139],[665,135],[669,140],[670,135],[671,141],[668,142],[674,143],[683,144],[675,145],[684,146],[677,147],[679,148],[672,135],[667,149],[710,150],[711,151],[1014,152],[840,153],[839,154],[628,155],[804,74],[733,2],[709,156],[660,2],[957,72],[626,2],[627,157],[705,74],[666,2],[630,158],[661,159],[632,74],[768,91],[769,92],[777,92],[776,160],[779,72],[778,72],[794,161],[793,162],[780,72],[781,72],[782,96],[783,97],[784,91],[785,160],[787,92],[786,72],[775,163],[771,164],[774,165],[770,166],[789,167],[788,168],[792,72],[790,169],[791,72],[842,170],[841,160],[772,171],[844,172],[843,72],[851,173],[850,174],[847,175],[849,175],[845,72],[846,175],[848,175],[862,91],[860,92],[855,92],[864,72],[866,176],[865,177],[854,72],[863,72],[853,72],[861,178],[857,97],[858,91],[852,63],[856,72],[859,72],[871,179],[869,179],[870,179],[876,180],[875,181],[872,179],[868,182],[874,179],[873,179],[867,2],[881,183],[880,184],[879,185],[878,186],[877,2],[890,91],[891,92],[894,72],[893,72],[897,187],[896,188],[889,96],[887,97],[888,91],[885,189],[884,190],[883,191],[892,72],[886,192],[895,72],[906,91],[907,92],[910,193],[909,194],[905,178],[902,195],[904,91],[900,196],[899,197],[898,198],[903,199],[908,72],[917,200],[916,201],[913,202],[915,202],[911,72],[912,202],[914,202],[923,203],[922,80],[921,204],[920,205],[919,206],[918,95],[927,207],[929,72],[931,208],[930,209],[924,72],[926,207],[928,72],[925,207],[945,91],[938,92],[949,72],[948,72],[936,72],[951,210],[950,211],[943,92],[944,72],[942,72],[933,95],[941,72],[940,96],[937,97],[939,91],[932,98],[946,72],[947,72],[934,71],[935,72],[765,212],[732,72],[955,213],[961,214],[960,215],[959,213],[953,213],[952,80],[958,216],[956,213],[954,213],[965,217],[964,218],[962,219],[963,220],[972,221],[971,222],[968,223],[970,224],[969,225],[967,226],[966,224],[983,72],[985,91],[982,72],[979,72],[975,227],[980,72],[987,228],[986,229],[984,195],[973,230],[976,231],[978,232],[981,72],[974,233],[977,72],[991,234],[990,63],[989,235],[988,63],[995,236],[994,236],[999,237],[998,238],[997,236],[996,236],[993,72],[992,239],[1007,91],[1011,240],[1010,241],[1006,178],[1004,195],[1005,91],[1008,74],[1002,242],[1001,243],[1000,244],[1003,245],[1009,72],[629,246],[1013,247],[1012,159],[901,97],[688,248],[656,249],[687,250],[685,2],[686,251],[713,252],[814,74],[644,74],[642,253],[643,254],[649,255],[647,256],[645,2],[648,257],[646,258],[650,74],[882,2],[2605,259],[652,260],[654,261],[655,262],[651,2],[653,2],[1654,74],[1655,74],[1656,74],[1657,74],[1658,74],[1659,74],[1660,74],[1661,74],[1662,74],[1663,74],[1664,74],[1665,74],[1666,74],[1667,74],[1668,74],[1674,74],[1669,74],[1670,74],[1671,74],[1672,74],[1673,74],[1675,74],[1676,74],[1677,74],[1678,74],[1679,74],[1680,74],[1682,74],[1683,74],[1681,74],[1684,74],[1685,74],[1686,74],[1687,74],[1688,74],[1689,74],[1690,74],[1691,74],[1692,74],[1693,74],[1694,74],[1695,74],[1696,74],[1697,74],[1698,74],[1699,74],[1700,74],[1701,74],[1702,74],[1703,74],[1704,74],[1705,74],[1706,74],[1707,74],[1708,74],[1710,74],[1709,74],[1711,74],[1712,74],[1714,74],[1713,74],[1715,74],[1716,74],[1717,74],[1718,74],[1719,74],[1721,74],[1720,74],[1722,74],[1723,74],[1724,74],[1725,74],[1726,74],[1727,74],[1728,74],[1729,74],[1730,74],[1731,74],[1732,74],[1733,74],[1734,74],[1735,74],[1740,74],[1736,74],[1737,74],[1738,74],[1739,74],[1741,74],[1742,74],[1743,74],[1744,74],[1745,74],[1746,74],[1747,74],[1748,74],[1749,74],[1750,74],[1752,74],[1751,74],[1753,74],[1754,74],[1755,74],[1756,74],[1757,74],[1758,74],[1759,74],[1760,74],[1763,74],[1761,74],[1762,74],[1764,74],[1765,74],[1766,74],[1767,74],[1768,74],[1769,74],[1770,74],[1771,74],[1773,74],[1772,74],[1884,263],[1774,74],[1775,74],[1776,74],[1777,74],[1778,74],[1779,74],[1780,74],[1781,74],[1782,74],[1783,74],[1784,74],[1786,74],[1785,74],[1787,74],[1788,74],[1789,74],[1790,74],[1791,74],[1792,74],[1793,74],[1794,74],[1796,74],[1795,74],[1797,74],[1798,74],[1799,74],[1800,74],[1801,74],[1802,74],[1803,74],[1804,74],[1805,74],[1809,74],[1806,74],[1807,74],[1808,74],[1810,74],[1811,74],[1812,74],[1814,74],[1813,74],[1815,74],[1816,74],[1817,74],[1818,74],[1819,74],[1820,74],[1821,74],[1822,74],[1823,74],[1824,74],[1825,74],[1826,74],[1827,74],[1828,74],[1829,74],[1830,74],[1831,74],[1832,74],[1833,74],[1834,74],[1835,74],[1836,74],[1837,74],[1838,74],[1839,74],[1840,74],[1841,74],[1842,74],[1843,74],[1844,74],[1845,74],[1846,74],[1847,74],[1848,74],[1849,74],[1850,74],[1851,74],[1852,74],[1853,74],[1854,74],[1855,74],[1856,74],[1857,74],[1858,74],[1859,74],[1860,74],[1861,74],[1862,74],[1863,74],[1864,74],[1865,74],[1866,74],[1867,74],[1869,74],[1868,74],[1870,74],[1871,74],[1872,74],[1873,74],[1874,74],[1875,74],[1876,74],[1877,74],[1878,74],[1879,74],[1880,74],[1881,74],[1882,74],[1883,74],[2032,264],[2031,265],[405,2],[374,2],[2085,266],[2084,267],[1614,2],[1409,268],[1408,2],[1088,2],[1089,269],[1414,270],[1411,271],[1412,272],[1413,272],[1410,273],[1090,274],[1091,275],[1405,276],[1394,74],[1407,277],[1404,276],[1401,278],[1402,278],[1403,2],[1406,2],[1147,279],[1395,2],[1397,280],[1400,281],[1399,2],[1398,280],[1396,282],[1126,283],[1136,284],[1133,284],[1134,285],[1118,285],[1132,285],[1113,284],[1119,286],[1122,287],[1127,288],[1115,286],[1116,285],[1129,289],[1114,286],[1120,286],[1123,286],[1128,286],[1130,285],[1117,285],[1131,285],[1125,290],[1121,291],[1146,292],[1124,293],[1135,294],[1112,285],[1137,285],[1138,285],[1139,285],[1140,285],[1141,285],[1142,285],[1143,285],[1144,285],[1145,285],[1347,2],[1344,2],[1343,2],[1338,295],[1349,296],[1334,297],[1345,298],[1337,299],[1336,300],[1346,2],[1341,301],[1348,2],[1342,302],[1335,2],[2616,303],[2615,304],[2614,297],[1351,305],[1593,306],[1594,306],[1596,307],[1595,306],[1588,306],[1589,306],[1591,308],[1590,306],[1568,2],[1567,2],[1570,309],[1569,2],[1566,2],[1533,310],[1531,311],[1534,2],[1581,312],[1535,306],[1571,313],[1580,314],[1572,2],[1575,315],[1573,2],[1576,2],[1578,2],[1574,315],[1577,2],[1579,2],[1532,316],[1607,317],[1592,306],[1587,318],[1597,319],[1603,320],[1604,321],[1606,322],[1605,323],[1585,318],[1586,324],[1582,325],[1584,326],[1583,327],[1598,306],[1602,328],[1599,306],[1600,329],[1601,306],[1536,2],[1537,2],[1540,2],[1538,2],[1539,2],[1542,2],[1543,330],[1544,2],[1545,2],[1541,2],[1546,2],[1547,2],[1548,2],[1549,2],[1550,331],[1551,2],[1565,332],[1552,2],[1553,2],[1554,2],[1555,2],[1556,2],[1557,2],[1558,2],[1561,2],[1559,2],[1560,2],[1562,306],[1563,306],[1564,333],[1333,2],[602,334],[4063,2],[4064,2],[4065,2],[4066,335],[2094,2],[2072,336],[2095,337],[2071,2],[4067,2],[4069,338],[600,2],[4070,339],[546,2],[2658,340],[2604,2],[4071,2],[2668,340],[4068,2],[3343,2],[3344,341],[140,342],[141,342],[142,343],[97,344],[143,345],[144,346],[145,347],[92,2],[95,348],[93,2],[94,2],[146,349],[147,350],[148,351],[149,352],[150,353],[151,354],[152,354],[153,355],[154,356],[155,357],[156,358],[98,2],[96,2],[157,359],[158,360],[159,361],[191,362],[160,363],[161,364],[162,365],[163,366],[164,367],[165,368],[166,369],[167,370],[168,371],[169,372],[170,372],[171,373],[172,2],[173,374],[175,375],[174,376],[176,17],[177,377],[178,378],[179,379],[180,380],[181,381],[182,382],[183,383],[184,384],[185,385],[186,386],[187,387],[188,388],[99,2],[100,2],[101,2],[139,389],[189,390],[190,391],[1969,392],[1909,74],[195,393],[460,74],[196,394],[194,395],[462,396],[461,397],[1350,74],[1321,398],[192,399],[458,2],[193,400],[83,2],[85,401],[457,74],[226,74],[2657,2],[4072,2],[542,402],[589,403],[587,2],[588,2],[534,2],[584,404],[581,405],[582,406],[603,407],[594,2],[597,408],[596,409],[608,409],[595,410],[533,2],[541,411],[583,411],[536,412],[539,413],[590,412],[540,414],[535,2],[601,2],[1018,415],[1017,416],[1015,2],[84,2],[2406,417],[2385,418],[2482,2],[2386,419],[2322,417],[2323,417],[2324,417],[2325,417],[2326,417],[2327,417],[2328,417],[2329,417],[2330,417],[2331,417],[2332,417],[2333,417],[2334,417],[2335,417],[2336,417],[2337,417],[2338,417],[2339,417],[2318,2],[2340,417],[2341,417],[2342,2],[2343,417],[2344,417],[2346,417],[2345,417],[2347,417],[2348,417],[2349,417],[2350,417],[2351,417],[2352,417],[2353,417],[2354,417],[2355,417],[2356,417],[2357,417],[2358,417],[2359,417],[2360,417],[2361,417],[2362,417],[2363,417],[2364,417],[2365,417],[2367,417],[2368,417],[2369,417],[2366,417],[2370,417],[2371,417],[2372,417],[2373,417],[2374,417],[2375,417],[2376,417],[2377,417],[2378,417],[2379,417],[2380,417],[2381,417],[2382,417],[2383,417],[2384,417],[2387,420],[2388,417],[2389,417],[2390,421],[2391,422],[2392,417],[2393,417],[2394,417],[2395,417],[2398,417],[2396,417],[2397,417],[2320,2],[2399,417],[2400,417],[2401,417],[2402,417],[2403,417],[2404,417],[2405,417],[2407,423],[2408,417],[2409,417],[2410,417],[2412,417],[2411,417],[2413,417],[2414,417],[2415,417],[2416,417],[2417,417],[2418,417],[2419,417],[2420,417],[2421,417],[2422,417],[2424,417],[2423,417],[2425,417],[2426,2],[2427,2],[2428,2],[2575,424],[2429,417],[2430,417],[2431,417],[2432,417],[2433,417],[2434,417],[2435,2],[2436,417],[2437,2],[2438,417],[2439,417],[2440,417],[2441,417],[2442,417],[2443,417],[2444,417],[2445,417],[2446,417],[2447,417],[2448,417],[2449,417],[2450,417],[2451,417],[2452,417],[2453,417],[2454,417],[2455,417],[2456,417],[2457,417],[2458,417],[2459,417],[2460,417],[2461,417],[2462,417],[2463,417],[2464,417],[2465,417],[2466,417],[2467,417],[2468,417],[2469,417],[2470,2],[2471,417],[2472,417],[2473,417],[2474,417],[2475,417],[2476,417],[2477,417],[2478,417],[2479,417],[2480,417],[2481,417],[2483,425],[2319,417],[2484,417],[2485,417],[2486,2],[2487,2],[2488,2],[2489,417],[2490,2],[2491,2],[2492,2],[2493,2],[2494,2],[2495,417],[2496,417],[2497,417],[2498,417],[2499,417],[2500,417],[2501,417],[2502,417],[2507,426],[2505,427],[2506,428],[2504,429],[2503,417],[2508,417],[2509,417],[2510,417],[2511,417],[2512,417],[2513,417],[2514,417],[2515,417],[2516,417],[2517,417],[2518,2],[2519,2],[2520,417],[2521,417],[2522,2],[2523,2],[2524,2],[2525,417],[2526,417],[2527,417],[2528,417],[2529,423],[2530,417],[2531,417],[2532,417],[2533,417],[2534,417],[2535,417],[2536,417],[2537,417],[2538,417],[2539,417],[2540,417],[2541,417],[2542,417],[2543,417],[2544,417],[2545,417],[2546,417],[2547,417],[2548,417],[2549,417],[2550,417],[2551,417],[2552,417],[2553,417],[2554,417],[2555,417],[2556,417],[2557,417],[2558,417],[2559,417],[2560,417],[2561,417],[2562,417],[2563,417],[2564,417],[2565,417],[2566,417],[2567,417],[2568,417],[2569,417],[2570,417],[2321,430],[2571,2],[2572,2],[2573,2],[2574,2],[2008,431],[2007,432],[2006,2],[2592,433],[2207,2],[551,2],[2607,434],[2606,435],[1196,436],[1198,437],[1197,438],[1195,439],[1194,2],[3342,440],[2082,2],[621,2],[574,2],[576,441],[575,2],[1024,74],[2737,2],[2711,442],[2710,443],[2709,444],[2736,445],[2735,446],[2739,447],[2738,448],[2741,449],[2740,450],[2696,451],[2670,452],[2671,453],[2672,453],[2673,453],[2674,453],[2675,453],[2676,453],[2677,453],[2678,453],[2679,453],[2680,453],[2694,454],[2681,453],[2682,453],[2683,453],[2684,453],[2685,453],[2686,453],[2687,453],[2688,453],[2690,453],[2691,453],[2689,453],[2692,453],[2693,453],[2695,453],[2669,455],[2734,456],[2714,457],[2715,457],[2716,457],[2717,457],[2718,457],[2719,457],[2720,458],[2722,457],[2721,457],[2733,459],[2723,457],[2725,457],[2724,457],[2727,457],[2726,457],[2728,457],[2729,457],[2730,457],[2731,457],[2732,457],[2713,457],[2712,460],[2704,461],[2702,462],[2703,462],[2707,463],[2705,462],[2706,462],[2708,462],[2701,2],[2244,2],[1322,74],[483,464],[488,1],[495,465],[478,466],[230,2],[238,467],[378,468],[381,469],[353,2],[366,470],[373,471],[255,2],[355,2],[236,2],[352,472],[398,473],[237,2],[228,474],[380,475],[382,476],[383,477],[455,478],[347,479],[300,480],[360,481],[361,482],[359,483],[358,2],[354,484],[379,485],[239,486],[425,2],[426,487],[266,488],[240,489],[267,488],[303,488],[206,488],[376,490],[375,2],[365,491],[473,2],[215,2],[494,492],[433,493],[434,494],[430,495],[512,2],[330,2],[435,104],[431,496],[517,497],[516,498],[511,2],[281,2],[333,499],[332,2],[510,500],[432,74],[286,501],[293,502],[295,503],[285,2],[290,504],[292,505],[294,506],[289,507],[287,2],[291,508],[513,2],[509,2],[515,509],[514,2],[284,510],[504,511],[507,512],[274,513],[273,514],[272,515],[520,74],[271,516],[260,2],[522,2],[2632,517],[2631,2],[523,74],[524,518],[198,2],[362,519],[363,520],[364,521],[202,2],[367,2],[222,522],[197,2],[447,74],[204,523],[446,524],[445,525],[436,2],[437,2],[444,2],[439,2],[442,526],[438,2],[440,527],[443,528],[441,527],[235,2],[232,2],[233,488],[387,2],[392,529],[393,530],[391,531],[389,532],[390,533],[385,2],[453,104],[227,104],[482,534],[489,535],[493,536],[321,537],[320,2],[315,2],[469,538],[477,539],[348,540],[349,541],[428,542],[337,2],[451,543],[325,74],[342,544],[454,545],[338,2],[341,546],[339,2],[452,547],[449,548],[448,2],[450,2],[345,2],[424,549],[210,550],[323,551],[327,552],[343,553],[346,554],[335,555],[328,556],[476,557],[401,558],[319,559],[207,560],[475,561],[203,562],[394,563],[386,2],[395,564],[413,565],[384,2],[412,566],[91,2],[407,567],[231,2],[427,568],[402,2],[216,2],[218,2],[357,2],[411,569],[234,2],[258,570],[344,571],[264,572],[324,2],[410,2],[388,2],[415,573],[416,574],[356,2],[418,575],[420,576],[419,577],[368,2],[409,560],[422,578],[318,579],[408,580],[414,581],[243,2],[247,2],[246,2],[245,2],[250,2],[244,2],[253,2],[252,2],[249,2],[248,2],[251,2],[254,582],[242,2],[310,583],[309,2],[314,584],[311,585],[313,586],[316,584],[312,585],[223,587],[302,588],[472,589],[470,2],[499,590],[501,591],[465,592],[500,593],[211,594],[208,594],[241,2],[225,595],[224,596],[220,597],[221,598],[229,599],[257,599],[268,599],[304,600],[269,600],[213,601],[212,2],[308,602],[307,603],[306,604],[305,605],[214,606],[456,607],[256,608],[464,609],[429,610],[459,611],[463,612],[351,613],[350,614],[331,615],[317,616],[299,617],[301,618],[298,619],[421,620],[322,2],[487,2],[219,621],[423,622],[471,623],[329,2],[259,624],[336,625],[334,626],[261,627],[396,628],[466,2],[262,629],[397,629],[485,2],[484,2],[486,2],[468,2],[467,2],[399,630],[326,2],[296,631],[217,632],[275,2],[201,633],[263,2],[491,74],[200,2],[503,634],[283,74],[497,104],[282,635],[480,636],[280,634],[205,2],[505,637],[278,74],[279,74],[270,2],[199,2],[277,638],[276,639],[265,640],[340,371],[400,371],[417,2],[404,641],[403,2],[288,510],[209,2],[297,74],[474,522],[481,642],[86,74],[89,643],[90,644],[87,74],[88,2],[377,645],[372,646],[371,2],[370,647],[369,2],[479,648],[490,649],[492,650],[496,651],[2633,652],[498,653],[502,654],[530,655],[506,655],[529,656],[508,657],[518,658],[519,659],[521,660],[525,661],[528,522],[527,2],[526,662],[2634,663],[1613,663],[1612,664],[1611,74],[1615,665],[2886,2],[2892,666],[2885,2],[2889,2],[2891,667],[2888,668],[2961,669],[2955,669],[2916,670],[2912,671],[2927,672],[2917,673],[2924,674],[2911,675],[2925,2],[2923,676],[2920,677],[2921,678],[2918,679],[2926,680],[2893,668],[2956,681],[2907,682],[2904,683],[2905,684],[2906,685],[2895,686],[2914,687],[2933,688],[2929,689],[2928,690],[2932,691],[2930,692],[2931,692],[2908,693],[2910,694],[2909,695],[2913,696],[2957,697],[2915,698],[2897,699],[2958,700],[2896,701],[2959,702],[2898,703],[2936,704],[2934,683],[2935,705],[2899,692],[2940,706],[2938,707],[2939,708],[2900,709],[2943,710],[2942,711],[2945,712],[2944,713],[2948,714],[2946,713],[2947,715],[2941,716],[2937,717],[2949,716],[2901,692],[2960,718],[2902,713],[2903,692],[2919,719],[2922,720],[2894,2],[2950,692],[2951,721],[2953,722],[2952,723],[2954,724],[2887,725],[2890,726],[1291,727],[1292,728],[1290,2],[569,729],[567,730],[568,731],[556,732],[557,730],[564,733],[555,734],[560,735],[570,2],[561,736],[566,737],[572,738],[571,739],[554,740],[562,741],[563,742],[558,743],[565,729],[559,744],[1340,745],[1339,2],[1036,2],[1052,746],[1053,746],[1054,746],[1055,746],[1069,747],[1056,748],[1057,748],[1058,749],[1049,750],[1047,751],[1038,2],[1042,752],[1046,753],[1044,754],[1051,755],[1039,756],[1040,757],[1041,758],[1043,759],[1045,760],[1048,761],[1050,762],[1059,748],[1060,748],[1061,748],[1062,746],[1063,748],[1064,748],[1037,748],[1065,2],[1067,763],[1066,748],[1068,746],[2257,764],[2258,765],[2700,766],[2699,767],[2111,768],[2204,769],[2202,770],[2109,2],[2110,771],[2203,2],[2205,772],[2113,773],[2112,774],[2116,775],[2183,776],[2178,777],[2079,778],[2149,779],[2142,780],[2199,781],[2077,782],[2148,783],[2137,784],[2136,774],[2182,785],[2179,786],[2130,787],[2141,788],[2184,789],[2185,789],[2186,790],[2194,791],[2188,791],[2196,791],[2200,791],[2187,791],[2189,792],[2192,792],[2195,792],[2191,793],[2193,791],[2197,794],[2190,795],[2088,796],[2163,74],[2160,797],[2164,74],[2099,791],[2089,791],[2155,798],[2078,799],[2098,800],[2102,801],[2162,791],[2075,74],[2161,802],[2159,74],[2158,791],[2090,74],[2209,803],[2173,795],[2153,804],[2214,805],[2171,2],[2169,2],[2174,806],[2172,807],[2168,808],[2170,809],[2175,810],[2177,811],[2167,74],[2097,812],[2074,791],[2166,791],[2115,813],[2165,74],[2138,812],[2198,791],[2132,814],[2086,815],[2091,816],[2143,817],[2145,814],[2124,818],[2127,814],[2103,819],[2126,820],[2134,821],[2135,822],[2131,823],[2146,824],[2133,825],[2108,826],[2154,827],[2150,828],[2151,829],[2147,830],[2125,831],[2114,832],[2118,833],[2092,834],[2122,835],[2123,836],[2119,837],[2093,838],[2104,839],[2144,822],[2087,840],[2152,2],[2117,841],[2107,842],[2139,2],[2211,843],[2212,844],[2213,771],[2180,2],[2210,771],[2201,2],[2128,2],[2100,2],[2176,845],[2129,2],[2080,771],[2208,846],[2106,847],[2140,848],[2105,849],[2181,850],[2120,2],[2156,2],[2157,851],[2101,2],[2121,2],[2206,2],[2076,74],[2083,852],[2081,2],[2743,853],[2742,854],[2698,855],[2697,856],[641,2],[548,857],[547,339],[406,858],[615,74],[553,2],[1016,2],[604,2],[537,2],[538,859],[2665,860],[2664,2],[81,2],[82,2],[13,2],[14,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[57,2],[58,2],[60,2],[59,2],[61,2],[62,2],[10,2],[63,2],[64,2],[65,2],[11,2],[66,2],[67,2],[68,2],[69,2],[70,2],[1,2],[71,2],[72,2],[12,2],[76,2],[74,2],[79,2],[78,2],[73,2],[77,2],[75,2],[80,2],[117,861],[127,862],[116,861],[137,863],[108,864],[107,865],[136,662],[130,866],[135,867],[110,868],[124,869],[109,870],[133,871],[105,872],[104,662],[134,873],[106,874],[111,875],[112,2],[115,875],[102,2],[138,876],[128,877],[119,878],[120,879],[122,880],[118,881],[121,882],[131,662],[113,883],[114,884],[123,885],[103,886],[126,877],[125,875],[129,2],[132,887],[2667,888],[2663,2],[2666,889],[3337,890],[3321,2],[3322,2],[3324,891],[3325,2],[3323,2],[3326,891],[3327,891],[3329,892],[3328,891],[3330,891],[3331,892],[3332,891],[3333,2],[3334,891],[3335,2],[3336,2],[2660,893],[2659,340],[2662,894],[2661,895],[2073,896],[2096,897],[606,898],[592,899],[593,898],[591,2],[544,900],[580,901],[550,902],[545,900],[543,2],[549,903],[578,2],[573,2],[577,904],[552,2],[579,905],[612,906],[605,907],[598,908],[607,909],[586,910],[1191,911],[1192,912],[609,913],[1193,914],[610,915],[599,916],[1190,917],[611,918],[2609,919],[1199,920],[585,2],[2022,921],[2029,922],[2024,2],[2025,2],[2023,923],[2026,924],[2018,2],[2019,2],[2030,925],[2021,926],[2027,2],[2028,927],[2020,928],[1259,929],[1262,930],[1260,930],[1256,929],[1263,931],[1264,932],[1261,930],[1257,933],[1258,934],[1252,935],[1204,936],[1206,937],[1250,2],[1205,938],[1251,939],[1255,940],[1253,2],[1207,936],[1208,2],[1249,941],[1203,942],[1200,2],[1254,943],[1201,944],[1202,2],[1265,945],[1209,946],[1210,946],[1211,946],[1212,946],[1213,946],[1214,946],[1215,946],[1216,946],[1217,946],[1218,946],[1219,946],[1221,946],[1220,946],[1222,946],[1223,946],[1224,946],[1248,947],[1225,946],[1226,946],[1227,946],[1228,946],[1229,946],[1230,946],[1231,946],[1232,946],[1233,946],[1235,946],[1234,946],[1236,946],[1237,946],[1238,946],[1239,946],[1240,946],[1241,946],[1242,946],[1243,946],[1244,946],[1245,946],[1246,946],[1247,946],[2618,948],[2620,267],[2622,267],[2624,267],[2626,267],[2628,267],[2611,267],[2794,949],[2785,950],[1268,951],[1267,952],[1266,953],[2791,954],[2784,955],[2782,956],[2793,957],[2783,958],[2792,959],[2788,960],[2787,961],[2786,962],[1189,267],[2789,963],[2819,964],[2817,965],[2818,966],[2746,967],[2835,968],[2836,968],[2825,969],[2837,970],[2823,971],[1269,267],[2838,972],[2827,973],[1271,974],[1270,975],[2822,976],[2839,977],[2840,978],[2828,979],[1273,980],[2841,981],[2826,982],[2820,983],[2833,984],[2831,985],[2834,986],[2830,987],[2829,988],[2821,989],[2824,990],[2832,991],[2842,992],[2778,993],[2843,994],[2848,995],[2845,996],[2844,997],[2847,998],[2856,999],[2849,1000],[2857,1001],[2853,1002],[1275,1003],[1274,267],[2855,1004],[2851,1005],[2850,1006],[1276,267],[2858,1007],[2852,1008],[2854,1009],[2873,1010],[2870,1011],[2874,1012],[2860,1013],[2863,1014],[2862,1015],[1277,267],[1279,1016],[1278,1017],[2876,1018],[2877,1018],[2864,1019],[2875,1020],[2861,1021],[1280,267],[2866,1022],[2865,1023],[2878,1024],[2867,1025],[1282,1026],[1281,1027],[2879,1028],[2880,1029],[2868,1030],[1074,267],[2872,1031],[2869,1032],[2859,104],[2871,1033],[2653,1034],[1284,1035],[1283,1036],[2979,1037],[2976,1038],[2980,1039],[2971,1040],[1287,1041],[1286,1042],[2981,1043],[2982,1043],[2977,1044],[1289,1045],[1288,267],[2983,1046],[2972,1047],[2984,1048],[2883,1049],[2985,1050],[2974,1051],[2986,1052],[2975,1053],[2987,1054],[2882,1055],[1297,1056],[1296,1057],[2988,1058],[2989,1059],[1295,1060],[1299,1061],[1298,1062],[2978,1063],[2991,1064],[1310,1065],[2992,1066],[2993,1067],[1308,1068],[2994,1069],[2995,1069],[1330,1070],[2996,1071],[1325,1072],[1331,1073],[2999,1074],[1319,1075],[3000,1076],[1317,1077],[3001,1078],[1316,1079],[1355,1080],[1315,1081],[1311,1082],[1356,1083],[1318,1084],[2997,1085],[1307,1086],[1332,1087],[1326,1088],[2998,1089],[1309,1086],[1302,267],[1352,1090],[1329,1091],[1353,1092],[1327,1093],[1354,1094],[1328,1093],[2990,1095],[3073,1096],[3063,1097],[3075,1098],[3074,1099],[3076,1100],[3066,1101],[3077,1102],[3069,1103],[3078,1104],[3068,1105],[3079,1106],[3067,1107],[3072,1108],[3071,1109],[3040,1110],[3041,1111],[3018,1112],[1361,267],[3021,1113],[3051,1114],[3009,1115],[3007,1116],[3052,1117],[3010,1118],[3053,1119],[3022,1120],[3054,1121],[3023,1122],[3055,1123],[3056,1124],[3003,1125],[3057,1126],[3004,1127],[3006,1128],[3058,1129],[3002,1130],[3005,1113],[3059,1131],[1649,1132],[3060,1133],[3008,1134],[3061,1135],[1362,1136],[1363,1137],[3042,1138],[3030,1139],[3043,1140],[3028,1141],[1357,267],[1360,1142],[1359,1143],[3044,1144],[3029,1145],[3045,1146],[3046,1147],[3024,1148],[3047,1149],[1358,1150],[3012,1151],[3013,1152],[3048,1153],[3020,1154],[3011,1155],[3037,1156],[3032,1157],[3019,1158],[3034,1159],[3026,1160],[3035,1161],[3027,1162],[3036,1163],[3025,1164],[3014,1165],[3049,1166],[3015,1167],[3050,1168],[3016,1169],[3038,1170],[3039,1171],[3031,1172],[3062,1173],[3017,1174],[3033,1175],[1385,1176],[1386,1177],[1384,1178],[1387,1179],[1388,1179],[1390,1180],[1389,1181],[1098,1182],[1391,1183],[1393,1184],[1392,1185],[1417,1186],[1419,1187],[1418,1188],[1421,1189],[1420,1183],[1423,1190],[1422,1183],[1425,1191],[1424,1183],[1428,1192],[1427,1193],[1429,1194],[1092,267],[3081,1195],[1416,1196],[1430,975],[1432,1197],[1431,1198],[1433,1197],[1434,1199],[1436,1200],[1435,1201],[1438,1202],[1437,1203],[1440,1204],[1439,1201],[1441,1201],[1442,1182],[1444,1205],[1443,1201],[1446,1206],[1447,1207],[1445,1208],[1448,1209],[1450,1210],[1449,1209],[1451,1182],[1452,1211],[1453,1183],[1454,1201],[1455,1182],[1457,1212],[1456,1201],[1459,1213],[1458,1214],[1461,1215],[1460,1216],[1462,1216],[1464,1217],[1463,1182],[1465,1218],[1162,1201],[1467,1219],[1466,1220],[1468,1221],[1367,1201],[1471,1222],[1470,1223],[1473,1224],[1472,1223],[1475,1225],[1474,1226],[1476,1227],[1469,1178],[1478,1228],[1477,1223],[1480,1229],[1479,1182],[1482,1230],[1481,1201],[1380,1231],[1484,1232],[1483,1201],[1485,1183],[1487,1233],[1489,1234],[1488,1188],[1491,1235],[1490,1236],[1493,1237],[1492,1211],[1495,1238],[1494,1201],[1497,1239],[1496,1211],[1498,1240],[1500,1241],[1499,1242],[1502,1243],[1501,1244],[1504,1245],[1503,1246],[1505,1247],[1093,1182],[1508,1248],[1507,1249],[1509,1250],[1506,1182],[1511,1251],[1510,1182],[1364,1252],[1365,1253],[1094,1254],[1369,1255],[1371,1256],[1372,1256],[1374,1257],[1373,1256],[1376,1258],[1375,1256],[1377,1256],[1378,1259],[1368,1260],[1381,1261],[1513,1262],[1512,1182],[1515,1263],[1514,1201],[1517,1264],[1516,1178],[3080,1265],[1383,1266],[2750,1267],[2747,1268],[2745,1269],[3094,1270],[3114,1271],[3119,1272],[3159,1273],[3160,1274],[3139,1275],[1519,1276],[1518,1277],[1522,1278],[1521,1279],[3124,1280],[1524,1281],[1525,1282],[1523,1283],[3161,1284],[3136,1285],[3127,1286],[3157,1287],[3177,1288],[3140,1289],[3178,1290],[3129,1291],[3179,1292],[3148,1293],[3180,1294],[3128,1295],[3181,1296],[3143,1297],[3182,1298],[3183,1299],[3142,1300],[3184,1301],[3144,1302],[3185,1303],[3151,1304],[3186,1305],[3130,1306],[3187,1307],[3156,1308],[1527,1309],[1526,1310],[3176,1311],[1528,1312],[3164,1313],[3162,1314],[3135,1315],[3163,1316],[3147,1317],[3165,1318],[3132,1319],[3166,1320],[3141,1321],[3167,1322],[3115,1323],[3116,1324],[3169,1325],[3118,1326],[3168,1327],[3117,1328],[1530,1329],[1529,1330],[3170,1331],[3122,1332],[3120,1333],[3134,1334],[3171,1335],[3133,1336],[3172,1337],[3125,1338],[3131,1339],[1608,1340],[3121,1341],[3126,1342],[3152,1343],[1610,1344],[1609,1345],[3173,1346],[3153,1347],[3174,1348],[3123,1349],[3175,1350],[3150,1351],[3188,1352],[1520,1323],[3158,1353],[3196,1354],[3189,1355],[3197,1356],[3190,1357],[3198,1358],[3192,1359],[3191,1360],[3199,1361],[3193,1362],[3195,1363],[3194,1364],[3218,1365],[3292,1366],[3245,1367],[3293,1368],[3244,1369],[1622,1370],[1621,1371],[3296,1372],[3252,1373],[3251,1374],[3250,1375],[1624,1376],[1623,267],[3294,1377],[3283,1378],[3243,1379],[3295,1380],[3288,1381],[1617,1382],[1616,1383],[3291,1384],[3290,1385],[3297,1386],[3262,1387],[3246,1388],[3253,1389],[3298,1390],[3282,1391],[3267,1392],[3286,1393],[3284,1394],[3278,1395],[3289,1396],[1618,1397],[1626,1398],[1625,267],[1188,975],[3303,1399],[3301,1400],[3302,1401],[3317,1402],[3315,1403],[3318,1404],[3314,1405],[3313,1406],[3308,1407],[3307,1408],[3316,1409],[2780,1410],[2779,1411],[3424,1412],[3446,1413],[3416,1414],[3447,1415],[3438,1416],[3448,1417],[3425,1418],[3449,1419],[3417,1420],[1628,1421],[3426,1422],[3418,1423],[3450,1424],[3419,1425],[3451,1426],[3433,1427],[3452,1428],[3437,1429],[3453,1430],[3427,1431],[3420,1432],[3454,1433],[3421,1434],[3455,1435],[3422,1436],[3456,1437],[3423,1438],[3457,1439],[3436,1440],[3431,1441],[3434,1423],[3430,1425],[3432,1442],[3435,1443],[1630,1444],[1629,267],[3458,1445],[3443,1446],[3459,1447],[3441,1448],[3460,1449],[3439,1450],[3461,1451],[3442,1452],[3463,1453],[3462,1454],[3464,1455],[3440,1456],[1633,1457],[1632,1458],[3320,1459],[1638,1460],[1637,1461],[1640,1462],[3340,1463],[3465,1464],[3408,1465],[3466,1466],[3409,1467],[3467,1468],[3410,1469],[3468,1470],[3411,1471],[1631,975],[3412,1469],[3413,1469],[3415,1471],[3445,1472],[3444,1473],[3489,1474],[3479,1475],[3490,1476],[3473,1477],[3491,1478],[3484,1479],[3487,1480],[3476,1481],[3475,1482],[1643,1483],[1642,1484],[3492,1485],[3482,1486],[3493,1487],[3474,1488],[3494,1489],[3477,1490],[3495,1491],[3485,1492],[3496,1493],[3471,1494],[3497,1495],[3472,1496],[3498,1497],[3481,1498],[3499,1499],[3480,1500],[3488,1501],[3470,1502],[3469,1503],[1645,1504],[1644,267],[3500,1505],[3483,1506],[3478,1132],[3486,1507],[3511,1508],[3506,1509],[3512,1510],[3505,1511],[3513,1512],[3504,1513],[3503,1514],[3516,1515],[3517,1516],[3501,1517],[3518,1518],[3519,1519],[3502,1520],[3520,1521],[1924,1522],[1646,953],[1926,1523],[1925,1524],[3514,1525],[3509,1526],[3515,1527],[3508,1528],[3507,1529],[3510,1530],[3549,1531],[3526,1532],[3550,1533],[3546,1534],[3545,1535],[3562,1536],[3535,1537],[3567,1538],[3540,1539],[3563,1540],[3536,1541],[3564,1542],[3539,1452],[3565,1543],[3537,1544],[1930,1545],[1931,1546],[3566,1547],[3534,1134],[3538,104],[3554,1548],[3532,1549],[3542,1550],[3544,1551],[3555,1552],[3529,1553],[3556,1554],[3524,1555],[3557,1556],[3528,1557],[3558,1558],[3533,1559],[3559,1560],[3541,1561],[3560,1562],[3530,1563],[1927,267],[1929,1564],[1928,1565],[3561,1566],[3543,1567],[3551,1568],[3525,1569],[3521,1570],[3548,1571],[3523,1572],[3522,1573],[3552,1574],[3527,1575],[3553,1576],[3531,1577],[3547,1578],[3569,1579],[2970,1580],[3568,1581],[3580,1582],[3581,1583],[3572,1584],[3578,1585],[3582,1586],[3570,1587],[1933,1588],[1932,267],[3586,1589],[3587,1589],[3577,1590],[3583,1591],[3574,1592],[3573,1593],[3584,1594],[3575,1595],[3585,1596],[3576,1597],[3571,267],[3579,1598],[3595,1599],[3588,1600],[3593,1601],[3591,1602],[3594,1603],[3590,1604],[3589,1605],[3592,1606],[3605,1607],[3599,1608],[3603,1609],[3600,1610],[3604,1611],[3596,1612],[3602,1613],[3598,1614],[3597,1615],[3601,1616],[3613,1617],[3620,1618],[3623,1619],[3622,1620],[3621,1621],[3626,1622],[3625,1623],[3624,1624],[3650,1625],[3634,1626],[3651,1627],[3635,1626],[3652,1628],[3636,1629],[3649,1630],[3637,1631],[3653,1632],[3641,1633],[1936,1634],[1938,1635],[1937,1636],[3654,1637],[3642,1638],[3655,1639],[3640,1640],[1935,1641],[1934,267],[3639,267],[3647,1642],[3643,1643],[3648,1644],[3645,1645],[3656,1646],[3644,1647],[1939,1648],[1294,1649],[3646,1650],[3667,1651],[3658,1652],[3670,1653],[3660,1654],[1942,1655],[1941,1656],[1943,1657],[1940,953],[3665,1658],[3668,1659],[3657,1660],[3669,1661],[3664,1662],[3672,1663],[3673,1664],[3663,1665],[3671,1666],[3662,1667],[3661,1668],[3666,1669],[3687,1670],[3688,1671],[3683,1672],[3689,1673],[3681,1674],[3680,1675],[3697,1676],[3685,1677],[1182,1678],[3690,1679],[1181,1680],[1180,1681],[3691,1682],[3682,1683],[3692,1684],[3684,1685],[3698,1686],[3699,1687],[3679,1688],[3693,1689],[3694,1690],[3677,1691],[3695,1692],[3676,1693],[3675,1694],[3696,1695],[3678,1696],[3686,1697],[3703,1698],[3702,1699],[3701,1700],[3700,1701],[3711,1702],[3713,1703],[3716,1704],[3705,1705],[3704,1706],[3718,1707],[3709,1708],[3708,1709],[3720,1710],[3722,1711],[3721,1712],[3724,1713],[3723,1714],[2638,1715],[3726,1716],[3727,1717],[3725,1718],[3728,1719],[3729,1720],[3730,1721],[3731,1722],[3733,1723],[3732,1724],[3737,1725],[3736,1726],[3738,1727],[3739,1728],[3735,1729],[3740,1730],[3734,1731],[3741,1732],[2296,267],[3761,1733],[3628,1734],[2034,1132],[1085,1735],[3864,1736],[3249,1737],[3260,267],[3856,1738],[3261,1739],[3866,1740],[3254,1741],[3867,1742],[3220,1743],[1619,267],[3857,1744],[3248,1745],[1994,1746],[1993,1747],[1996,1748],[1995,267],[1997,1749],[1110,1750],[3868,1751],[3224,1752],[1102,1753],[3858,1754],[1097,1755],[1998,1756],[1096,267],[1999,1757],[1078,1758],[2000,1759],[1108,1760],[3859,1761],[1106,1762],[3869,1763],[3255,1764],[1104,1765],[3247,1766],[3870,1767],[3257,1768],[2001,1769],[1100,267],[3860,1770],[1101,1771],[1109,1772],[3871,1773],[3256,1774],[3872,1775],[3258,1776],[2035,1132],[3873,1777],[3259,1778],[3861,1779],[2036,1780],[3862,1781],[1105,1782],[2002,1783],[1107,1784],[3863,1785],[1103,1786],[3762,1787],[3273,1788],[3874,1789],[1650,1790],[1272,1036],[3785,1791],[3200,1792],[3791,1793],[3201,1794],[3792,1795],[3203,1796],[3793,1797],[3205,1798],[3786,1799],[3202,1792],[3787,1800],[3217,1801],[3788,1802],[3206,1792],[3212,1803],[3789,1804],[3210,1805],[3790,1806],[3209,1807],[3084,1808],[3875,1809],[3083,1810],[3742,1811],[1950,1812],[3763,1813],[3659,1814],[1888,1150],[3706,1815],[2011,1816],[3876,1817],[2010,1818],[3877,1819],[3715,1820],[2009,1821],[3710,1822],[3878,1823],[3717,1824],[3879,1825],[3714,1826],[3880,1827],[3707,1828],[3712,1829],[2003,1830],[3719,1831],[2012,1832],[2004,1833],[3881,1834],[3215,1835],[3428,1836],[1627,267],[3882,1837],[3429,1838],[3883,1839],[1635,1840],[1636,1545],[2014,1841],[2013,1842],[3213,1843],[3211,1844],[1034,1036],[3764,1845],[3629,1846],[3794,1847],[3090,1848],[3795,1849],[3796,1850],[3087,1851],[3797,1852],[3085,1438],[3086,1853],[3798,1854],[3089,1855],[1962,1856],[1961,267],[3799,1857],[3800,1858],[3088,1859],[1426,267],[1324,1860],[1651,1861],[2759,1862],[1652,1021],[1072,1863],[3884,1864],[2752,1865],[3885,1866],[2760,1867],[2748,975],[3905,1868],[3304,1869],[3906,1870],[3305,1871],[3907,1872],[3306,1873],[2015,1312],[3908,1874],[3207,1875],[3909,1876],[3208,1877],[3886,1878],[1653,1879],[3887,1880],[2753,1881],[3888,1882],[2652,1883],[3238,1884],[3889,1885],[3231,1886],[3890,1887],[1886,1888],[3891,1889],[1885,1890],[3892,1891],[1071,1892],[3894,1893],[3893,1812],[3895,1894],[1904,1895],[3896,1896],[3272,1897],[1887,1790],[3271,1898],[3897,1899],[1891,1900],[1905,1901],[3898,1902],[1892,1903],[3899,1904],[1902,1905],[2017,1906],[2016,1907],[3900,1908],[2761,1909],[3902,1910],[1303,1911],[1903,1912],[3903,1913],[3638,1914],[3904,1915],[3228,1916],[3901,1917],[3630,1918],[2795,104],[3743,1919],[1911,1920],[3744,1921],[2649,1922],[3745,1923],[2654,1924],[3801,1925],[3097,1926],[3802,1927],[3096,1928],[3095,1929],[3803,1930],[3100,1931],[3804,1932],[3099,1933],[3098,1934],[3746,1935],[2846,1936],[2038,1937],[2039,1938],[2037,1939],[3910,1940],[2040,1941],[2041,1942],[1033,1943],[3765,1944],[3082,1945],[3805,1946],[1971,1947],[3806,1948],[1966,1949],[3807,1950],[1967,1951],[3808,1952],[1968,1953],[1973,1954],[1965,1955],[3809,1956],[1972,1957],[1974,1958],[1970,1959],[3911,1960],[2769,1961],[2042,267],[3747,1962],[3232,1963],[3064,1964],[3810,1965],[3065,104],[1975,267],[3748,1966],[1320,1537],[3766,1967],[2233,267],[1944,1968],[1170,267],[3912,1969],[1912,1970],[1913,1971],[3915,1972],[1081,975],[2045,1973],[2044,1974],[1032,1975],[3913,1976],[2043,1977],[1031,1978],[2047,1979],[2046,1980],[3914,1981],[1914,1982],[2049,1983],[2048,1984],[2051,1985],[2050,104],[3767,1986],[3268,1987],[3768,1988],[1958,1989],[3749,1990],[2656,1991],[3916,1992],[3319,1993],[1639,267],[3917,1994],[1082,1995],[2053,1996],[2052,1323],[3918,1997],[3414,1998],[3769,1999],[2762,2000],[2054,2001],[3919,2002],[1915,2003],[3920,2004],[1918,2005],[3921,2006],[3149,2007],[1075,267],[1917,2008],[3922,2009],[3338,2010],[3923,2011],[1073,267],[2056,2012],[2055,1088],[3924,2013],[3263,2014],[3925,2015],[3266,2016],[3926,2017],[3265,2018],[3264,2019],[3927,2020],[3221,2021],[3928,2022],[3281,2023],[3929,2024],[3280,2025],[3279,2026],[3930,2027],[3242,2028],[2057,267],[3204,2029],[3285,2030],[3770,2031],[3227,2032],[3225,2033],[3811,2034],[2781,2035],[1977,2036],[1976,267],[3931,2037],[3219,1873],[3932,2038],[1306,2039],[3933,2040],[2962,2041],[3771,2042],[2651,2043],[3813,2044],[2641,2045],[3814,2046],[2643,2047],[1978,2048],[1951,267],[1979,267],[3815,2049],[2644,2050],[3816,2051],[2650,2052],[3812,2053],[2646,2054],[3817,2055],[2648,2056],[1945,2057],[1187,2058],[3750,2059],[2655,2060],[625,1036],[2766,2061],[3772,2062],[1910,2063],[3936,2064],[3937,2065],[1923,2066],[2058,2067],[1921,2068],[3934,2069],[3935,2070],[2767,2071],[2060,2072],[2059,267],[2061,2073],[1922,267],[2064,2074],[2063,2075],[3939,2076],[3310,2077],[2066,2078],[2065,2079],[3940,2080],[3309,2081],[2062,953],[3938,2082],[3312,2083],[1946,267],[1960,2084],[1959,2085],[3773,2086],[3274,2087],[3818,2088],[3276,2089],[3275,2090],[3819,2091],[3277,2092],[3774,2093],[3632,2094],[2765,2095],[3941,2096],[2764,2097],[2763,2098],[3942,2099],[2770,2100],[1641,267],[3775,2101],[3287,2102],[3776,2103],[1305,2104],[3777,2105],[3216,2106],[3214,2107],[3778,2108],[3269,2109],[3779,2110],[3270,2111],[3948,2112],[2884,2113],[3943,2114],[1893,1134],[3944,2115],[1894,1134],[3945,2116],[1897,2117],[3946,2118],[1895,1021],[3947,2119],[1896,2120],[3951,2121],[2969,2122],[3949,2123],[2968,2124],[2068,2125],[2067,2126],[3950,2127],[2967,2128],[2966,2129],[2965,2130],[2069,267],[1486,267],[3751,2131],[2796,2132],[3952,2133],[3233,2134],[3780,2135],[3093,2136],[1980,267],[3820,2137],[2811,2138],[3821,2139],[2813,2140],[3822,2141],[2812,1438],[3823,2142],[2797,2143],[3824,2144],[3146,2145],[3825,2146],[3145,2147],[1982,2148],[1981,1471],[3826,2149],[2814,2150],[1983,953],[1984,1150],[3832,2151],[2800,2152],[3833,2153],[2799,2154],[3834,2155],[2801,2156],[3835,2157],[3836,2158],[2802,2159],[3827,2160],[2803,1873],[3828,2161],[2804,2162],[3829,2163],[2807,2164],[3830,2165],[2805,1438],[3831,2166],[2806,2167],[1986,2168],[1985,2169],[3837,2170],[2808,2171],[3838,2172],[2809,2173],[3839,2174],[2810,2175],[3840,2176],[3092,2177],[3091,2178],[1987,267],[3841,2179],[1901,2180],[3842,2181],[1898,2182],[3843,2183],[2963,2184],[1899,2185],[3845,2186],[2964,2187],[3844,2188],[1900,2189],[3070,104],[3968,2190],[2755,2191],[3953,2192],[1908,2193],[3954,2194],[3311,2195],[3969,2196],[3631,1731],[3977,2197],[2217,2198],[3978,2199],[2218,2198],[3979,2200],[2219,2201],[3980,2202],[2216,2203],[2070,267],[3981,2204],[2220,2198],[2222,2205],[3982,2206],[2221,2198],[3955,2207],[1952,1871],[3956,2208],[1919,2209],[1149,2210],[3970,2211],[3971,2212],[1153,2213],[3972,2214],[1155,2215],[3973,2216],[1152,2217],[3974,2218],[1157,2219],[3975,2220],[1160,2221],[3976,2222],[1159,2223],[1158,2224],[1161,2225],[1148,2226],[1964,267],[3957,2227],[1168,2228],[2768,267],[3983,2229],[1906,1892],[3229,2230],[3223,2231],[3958,2232],[1173,2233],[3959,2234],[1174,2235],[3960,2236],[1076,1132],[1889,1134],[3961,2237],[2749,2238],[3962,2239],[2973,2240],[3963,2241],[1648,2240],[3964,2242],[2881,2243],[2798,2244],[2757,2245],[3965,2246],[1026,1132],[3966,2247],[1949,2248],[2756,2249],[3984,2250],[1163,2251],[1164,2252],[3985,2253],[1165,2254],[3986,2255],[1167,2256],[3987,2257],[1169,2258],[1177,2259],[3988,2260],[1171,2261],[3989,2262],[1172,1636],[3990,2263],[1175,2264],[3991,2265],[1176,2266],[3967,2267],[2640,2268],[1647,2269],[3846,2270],[1955,2271],[3753,2272],[1957,2273],[3752,2274],[2815,2275],[3992,2276],[3339,2277],[623,267],[3993,2278],[3608,2279],[3607,2280],[3606,2281],[2772,2282],[3994,2283],[3995,2283],[3234,2284],[3230,2285],[3996,2286],[1890,2287],[4001,2288],[3236,2289],[2224,2290],[2223,267],[3997,2291],[3237,2292],[4002,2293],[3235,267],[2226,2294],[2225,267],[3998,2295],[3241,2296],[3999,2297],[3239,2298],[2228,2299],[2227,267],[4000,2300],[3240,2301],[2229,1211],[3755,2302],[3612,2303],[1989,2304],[1988,2305],[3847,2306],[3611,2307],[3610,2308],[3754,2309],[3609,2310],[2231,2311],[2230,267],[4006,2312],[2773,2313],[4007,2314],[4008,2315],[2774,2316],[2232,267],[2235,2317],[2234,2318],[2771,1903],[4003,2319],[2754,2320],[4004,2321],[4005,2322],[2758,2323],[3848,2324],[2647,2325],[3756,2326],[3615,2327],[3849,2328],[3614,2329],[3850,2330],[3618,2331],[1990,1183],[3851,2332],[3617,2333],[3852,2334],[3616,2335],[3757,2336],[3619,2337],[4009,2338],[1300,2339],[1907,2340],[4010,2341],[1953,2342],[4011,2343],[1099,2344],[4012,2345],[2639,997],[2642,2346],[4013,2347],[1020,2348],[1077,1875],[4014,2349],[2215,2350],[1156,2351],[1080,2352],[1025,2353],[1095,2354],[1314,2355],[4015,2356],[1029,2357],[2751,2358],[1023,2359],[1021,1875],[1027,1875],[1954,2360],[1083,2361],[4016,2362],[1948,2363],[4017,2364],[1030,2365],[1028,2366],[1154,2354],[1150,2367],[1084,2368],[2637,2369],[1079,2370],[1151,1875],[1301,2371],[1022,1875],[4018,2372],[1035,2373],[4019,2374],[1313,2375],[3758,2376],[2816,2377],[3759,2378],[3781,2379],[3222,2380],[3854,2381],[3300,2382],[3853,2383],[3627,2384],[1285,267],[1992,2385],[1991,267],[3782,2386],[3633,2387],[3783,2388],[2777,2389],[3760,2390],[2744,2391],[1111,267],[4020,2392],[1920,2393],[3784,2394],[3674,2395],[4032,2396],[3103,2397],[3104,2398],[4021,2399],[3102,2400],[3101,2401],[2242,267],[4022,2402],[2253,104],[2236,267],[4023,2403],[2252,2404],[2251,2405],[2240,2406],[4033,2407],[2239,104],[2249,2408],[2248,104],[4034,2409],[2250,2410],[4035,2411],[2247,104],[4028,2412],[4029,2412],[3113,2413],[4030,2414],[3105,2415],[2237,1383],[4036,2416],[2243,2417],[4037,2418],[2272,2419],[2241,267],[2245,2420],[4038,2421],[2275,2422],[2282,2423],[4039,2424],[2276,2425],[4040,2426],[2259,2427],[4041,2428],[2280,2429],[4042,2430],[2281,2431],[4043,2432],[2277,2433],[2269,267],[2270,2434],[4044,2435],[2279,2436],[4045,2437],[2278,2438],[4046,2439],[1183,2440],[4047,2441],[2271,2442],[4048,2443],[2274,2444],[4049,2445],[2273,2446],[4050,2447],[2256,267],[4051,2448],[2255,2449],[2246,2450],[2283,2451],[2260,267],[4031,2452],[3106,2453],[3107,2454],[4024,2455],[3108,2456],[4025,2457],[3112,2458],[3111,2459],[4026,2460],[3110,2461],[2263,2462],[2268,2463],[2264,2464],[2265,2465],[2266,2466],[4052,2467],[2267,2468],[2261,267],[2284,2469],[2262,2470],[4027,2471],[3109,267],[2238,2472],[2254,2473],[3226,267],[3299,2474],[2775,2475],[3855,2476],[2776,2477],[2635,2478],[2005,2479],[4053,2480],[2645,2481],[2636,2482],[1947,2483],[2290,2484],[2288,2484],[2287,2484],[2289,2485],[2286,2484],[2285,2484],[2291,975],[4057,2486],[2294,2487],[1312,104],[4054,2488],[3137,2489],[4055,2490],[1323,2491],[3138,2492],[4056,2493],[3154,2494],[3155,2495],[2292,104],[2293,2496],[2295,2497],[1304,2498],[2298,2499],[2297,2500],[2299,2501],[1019,2502],[2302,2503],[2301,2504],[2304,2505],[2303,267],[4058,2506],[2033,2507],[2305,2508],[2306,2508],[1293,2509],[2307,2510],[616,267],[2308,2511],[1184,267],[2309,2512],[1185,2513],[624,2],[1186,267],[2300,2514],[617,2515],[614,267],[2310,2516],[2311,2517],[1366,2518],[2312,2519],[620,2520],[2313,2521],[1166,2522],[1415,267],[1179,2523],[2314,267],[2316,2524],[2315,267],[2317,2525],[622,2526],[2577,2527],[2576,2528],[2579,2529],[2578,267],[2580,2530],[1956,267],[2581,2531],[1370,267],[2582,267],[2584,2532],[2583,267],[2585,2533],[619,2534],[2586,2535],[1916,267],[2587,2536],[1178,975],[2588,2537],[1620,2538],[2589,267],[2590,2539],[1634,267],[2591,2540],[1379,975],[2594,2541],[2593,2542],[2597,2543],[2596,2544],[2598,2545],[2595,267],[2599,2546],[1086,267],[2600,2547],[1087,975],[618,267],[2601,2548],[1382,2523],[2602,2549],[1963,1984],[2603,2550],[1070,267],[4059,2551],[2619,2552],[2621,2553],[2623,2554],[2625,2555],[2627,2556],[2629,2557],[2608,2558],[2610,2559],[2612,2560],[2630,2378],[3865,1310],[2613,2561],[2617,2562],[2790,2563],[4060,2564],[613,2565]],"semanticDiagnosticsPerFile":[[1444,[{"start":5247,"length":6,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'undefined'."}]],[1447,[{"start":1996,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3425,"length":10,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[1495,[{"start":643,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}},{"start":7416,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}}]],[1517,[{"start":1402,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }[]' is not assignable to type 'UserInfo[]'.","category":1,"code":2322,"next":[{"messageText":"Property 'models' is missing in type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' but required in type 'UserInfo'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' is not assignable to type 'UserInfo'."}}]},"relatedInformation":[{"file":"./src/components/networking.tsx","start":30475,"length":6,"messageText":"'models' is declared here.","category":3,"code":2728},{"file":"./src/components/networking.tsx","start":30782,"length":5,"messageText":"The expected type comes from property 'users' which is declared here on type 'UserListResponse'","category":3,"code":6500}]}]],[1929,[{"start":328,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":784,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1182,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1684,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2044,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2529,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3149,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3683,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4135,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4538,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":5174,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}}]],[1939,[{"start":4983,"length":43,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type 'SpendMetrics' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Index signature for type 'string' is missing in type 'SpendMetrics'.","category":1,"code":2329}]}}]],[1994,[{"start":497,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":729,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":835,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":935,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1135,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1374,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1431,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1486,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[1996,[{"start":425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":690,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1694,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1795,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1851,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2039,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2311,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2503,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2770,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2871,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3139,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4094,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4354,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4619,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4691,"length":12,"messageText":"Parameter 'defaultModel' implicitly has an 'any' type.","category":1,"code":7006},{"start":4905,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[1997,[{"start":507,"length":10,"code":2739,"category":1,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 15 more ...; returnRawModelName: false; }' is missing the following properties from type 'BuildComplexityRouterConfigParams': defaultModel, planModeMinTier, heuristicFirstMaxTier","canonicalHead":{"code":2322,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 15 more ...; returnRawModelName: false; }' is not assignable to type 'BuildComplexityRouterConfigParams'."}},{"start":1221,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1271,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1639,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1863,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1956,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2147,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2204,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2450,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2543,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2803,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2851,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2950,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3246,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3309,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3756,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3815,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3883,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4294,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4361,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4434,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4780,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4920,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5311,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5375,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6087,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6150,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6217,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6637,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6694,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6820,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7328,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7380,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7437,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7542,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7604,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7664,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8384,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8908,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8953,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9123,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9277,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9340,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9541,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9594,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9805,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9859,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10014,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10072,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10382,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10422,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10496,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10549,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10949,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10989,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11051,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11116,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11223,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11307,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11380,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11548,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11636,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11824,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11960,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12133,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12200,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12328,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12450,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12533,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12683,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12748,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12918,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13418,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13465,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13530,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13797,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14140,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14214,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14399,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14588,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":14596,"length":8,"messageText":"Parameter 'keywords' implicitly has an 'any' type.","category":1,"code":7006},{"start":14615,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14868,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14957,"length":24,"messageText":"Expected 2 arguments, but got 1.","category":1,"code":2554,"relatedInformation":[{"file":"./src/components/add_model/build_complexity_router_config.ts","start":8516,"length":24,"messageText":"An argument for 'rows' was not provided.","category":3,"code":6210}]},{"start":15302,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15391,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15546,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15790,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15972,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16051,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16277,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16357,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16700,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16826,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16912,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17147,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17556,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17654,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17737,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18148,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18528,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18614,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18713,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18956,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19480,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19571,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19966,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20052,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20165,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20267,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20442,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20597,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20631,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20710,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20796,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21145,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21377,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21457,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21555,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21763,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21818,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21859,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21905,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21964,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22176,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22265,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22359,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22458,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22722,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22794,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22886,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22977,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23049,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23089,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23160,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23223,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23265,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23389,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23559,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23638,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23688,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23760,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23951,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24432,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24575,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24633,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24698,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24824,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24927,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25035,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25140,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25249,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25414,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25744,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25806,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25882,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26055,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26100,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26292,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26358,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26496,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26556,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26747,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26815,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27005,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27116,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27282,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27383,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27506,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27574,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27656,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27755,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28047,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28208,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28254,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28340,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28427,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28523,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28676,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28729,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28882,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28987,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29049,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29237,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29415,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29700,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29805,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29952,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30345,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30464,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30524,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30589,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30771,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30865,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30987,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31054,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31330,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[1998,[{"start":196,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":501,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":595,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":679,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":786,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":976,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1134,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1208,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1349,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1587,"length":12,"messageText":"Parameter 'systemPrompt' implicitly has an 'any' type.","category":1,"code":7006},{"start":1601,"length":8,"messageText":"Parameter 'expected' implicitly has an 'any' type.","category":1,"code":7006},{"start":1620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1707,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1746,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1920,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2040,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2039,[{"start":2106,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2163,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2357,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2427,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2615,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2687,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2905,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2970,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3155,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3235,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3411,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3485,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3799,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2043,[{"start":1600,"length":17,"code":2322,"category":1,"messageText":{"messageText":"Type '{ budget_limit: number; time_period: string; } | { max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'.","category":1,"code":2322,"next":[{"messageText":"Type '{ max_budget: number; budget_duration: string; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'."}}]}},{"start":2144,"length":12,"code":2322,"category":1,"messageText":{"messageText":"Type 'string | number' is not assignable to type 'number'.","category":1,"code":2322,"next":[{"messageText":"Type 'string' is not assignable to type 'number'.","category":1,"code":2322}]},"relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":2388,"length":12,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'number'.","relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":3742,"length":8,"code":2739,"category":1,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is not assignable to type 'ModelBudgetConfig'."}}]],[2298,[{"start":31656,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; }' is missing the following properties from type 'ComplexityRouterConfigPayload': session_affinity, deployment_affinity","category":1,"code":2739}]}}]],[2306,[{"start":3271,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]},{"start":3928,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]}]],[2307,[{"start":1322,"length":3,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":1327,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":1491,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1616,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1943,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":1987,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":2025,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":4549,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":4593,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048}]],[2599,[{"start":272,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":354,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":907,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":1076,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1114,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1276,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1338,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1481,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1665,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1757,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1836,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1976,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2023,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2370,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2447,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2802,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2838,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2914,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2969,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3051,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3148,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3523,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3642,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3690,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4031,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4484,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4533,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4878,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4940,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4977,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6141,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6485,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6532,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6573,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6703,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6766,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6888,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7012,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7255,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7452,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7497,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7663,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7736,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7807,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8020,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8403,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8481,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8933,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9013,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9490,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9757,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9835,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9876,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10661,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10785,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11070,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11970,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12047,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12318,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2600,[{"start":3595,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4010,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4616,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":5031,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}}]],[2746,[{"start":3077,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":3083,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":3175,"length":4,"messageText":"'opts' is possibly 'undefined'.","category":1,"code":18048}]],[2979,[{"start":6376,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}},{"start":6442,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}}]],[3039,[{"start":2067,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2572,"length":41,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3058,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3554,"length":8,"code":2322,"category":1,"messageText":"Type 'undefined' is not assignable to type 'string'.","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/useauthorized.ts","start":1740,"length":50,"messageText":"The expected type comes from property 'userRole' which is declared here on type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'","category":3,"code":6500}]},{"start":4033,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5026,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3047,[{"start":3309,"length":15,"code":2339,"category":1,"messageText":{"messageText":"Property 'CustomGuardrail' does not exist on type 'Record | typeof GuardrailProviders'.","category":1,"code":2339,"next":[{"messageText":"Property 'CustomGuardrail' does not exist on type 'typeof GuardrailProviders'.","category":1,"code":2339}]}}]],[3075,[{"start":198,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":366,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":417,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":500,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":702,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1191,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1353,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1621,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1713,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2069,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2354,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2908,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2988,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3401,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3592,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3750,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3076,[{"start":5828,"length":11,"code":2322,"category":1,"messageText":"Type 'null' is not assignable to type 'string | undefined'."}]],[3173,[{"start":2696,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":2826,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":3842,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."}]],[3182,[{"start":10763,"length":423,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '{ status: \"healthy\"; last_health_check: string; health_check_error: null; teams: { team_id: string; }[]; allowed_tools: string[]; has_user_credential: true; approval_status: \"approved\"; submitted_by: string; ... 47 more ...; env_vars?: MCPEnvVar[] | null; }' to type 'MCPServer' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Types of property 'approval_status' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '\"approved\"' is not comparable to type '\"active\" | \"rejected\" | \"pending_review\" | null | undefined'.","category":1,"code":2678}]}]}}]],[3292,[{"start":4242,"length":15,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3296,[{"start":4842,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":11747,"length":24,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }[]' is not assignable to parameter of type '({ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; })[]'.","category":1,"code":2345,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; }'.","category":1,"code":2322,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'litellm_params' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }' is missing the following properties from type '{ model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }': auto_router_config, auto_router_default_model","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'."}}]}]}]}]}}]],[3569,[{"start":2185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2365,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2415,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":2652,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3085,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3188,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3674,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3998,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4041,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":4130,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4412,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3603,[{"start":2516,"length":2,"code":2345,"category":1,"messageText":"Argument of type '{}' is not assignable to parameter of type 'void'."}]],[3648,[{"start":11320,"length":300,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":20146,"length":308,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":30967,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":31850,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3701,[{"start":3053,"length":46,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '[url: string][]' to type '[string, RequestInit][]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Type '[url: string]' is not comparable to type '[string, RequestInit]'.","category":1,"code":2678,"next":[{"messageText":"Source has 1 element(s) but target requires 2.","category":1,"code":2618}]}]}}]],[3727,[{"start":9097,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304}]],[3745,[{"start":401,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":442,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":647,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":711,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1064,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1307,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1383,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1651,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1701,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1948,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2297,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3750,[{"start":792,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":835,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1122,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1226,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1306,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1527,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1913,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2009,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2261,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2311,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2510,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3760,[{"start":780,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":813,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":891,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1371,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1570,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1620,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1789,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2053,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2107,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2176,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2342,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":2352,"length":9,"messageText":"Parameter 'iconClass' implicitly has an 'any' type.","category":1,"code":7006},{"start":2505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2857,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3063,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3571,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3661,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4075,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3798,[{"start":2005,"length":27,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]},{"start":2084,"length":26,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]}]],[3806,[{"start":162,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":205,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":602,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":751,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3807,[{"start":119,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":155,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":397,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":451,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1165,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1233,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1820,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3808,[{"start":211,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":252,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":384,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":615,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":875,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1570,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3809,[{"start":877,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":917,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1106,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1769,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2010,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2940,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3810,[{"start":144,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":359,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":488,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":554,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":618,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":732,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":793,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":961,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1031,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1248,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1396,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1468,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3815,[{"start":236,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":276,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":583,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":859,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1267,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1590,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1854,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2168,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3839,[{"start":1201,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1242,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1434,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1627,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1963,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2391,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2426,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2876,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2924,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3198,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3285,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3314,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3448,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":3458,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":3609,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3847,[{"start":5180,"length":36,"messageText":"Object is possibly 'null'.","category":1,"code":2531}]],[3849,[{"start":208,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":242,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":387,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":451,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":525,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":605,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":681,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":864,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":932,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1101,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1167,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1423,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1485,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1663,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1726,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1772,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1825,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1947,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3854,[{"start":1780,"length":8,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15138,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3855,[{"start":3323,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3344,"length":7,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[3856,[{"start":3533,"length":8,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' is not assignable to type 'Team'."}},{"start":5267,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5784,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6718,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":7666,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8613,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9408,"length":43,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10172,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10857,"length":56,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12152,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3857,[{"start":1457,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1501,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1638,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2186,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2372,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2539,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3054,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3858,[{"start":837,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1657,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1766,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1811,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2179,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2275,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2634,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3206,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3312,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3386,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3836,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3910,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4103,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4162,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4476,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4653,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4712,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3859,[{"start":1381,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1426,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1614,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1736,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1801,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1866,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1932,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2133,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2193,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2362,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2439,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2651,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2734,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2953,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3019,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3090,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3161,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3232,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3438,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3523,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3946,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4061,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4799,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4862,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5432,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5502,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5568,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5633,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5706,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5769,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5866,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6429,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6614,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6700,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7224,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7307,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8037,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8581,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8658,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8754,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9214,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9314,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9598,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9686,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10263,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10308,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10403,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10686,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10765,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10862,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11597,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11714,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12008,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12354,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12411,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12475,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13196,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13276,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14029,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14131,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14357,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14433,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14528,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14937,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15019,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15143,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15231,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15704,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15831,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15869,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15948,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16636,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16760,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17347,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17494,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17926,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18085,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18365,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18434,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18512,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19051,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19118,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19147,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19592,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19677,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19759,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19893,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19979,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20448,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20537,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21096,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21415,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21499,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21573,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21944,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22017,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22092,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22244,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22340,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22583,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22865,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22961,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23334,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23372,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23449,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24046,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24171,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24474,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25250,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25818,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25912,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26391,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26429,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26502,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27053,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27458,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27533,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27630,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28208,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28253,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28302,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28621,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28682,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28783,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29078,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29123,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29251,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29494,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29580,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29928,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29985,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30075,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30326,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30419,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30780,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30896,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31033,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31143,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31378,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31556,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31833,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32005,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32187,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32472,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32556,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32913,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32986,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33150,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33249,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33390,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33723,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33782,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33845,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34207,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34324,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34704,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34813,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35194,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35291,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35558,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35684,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35839,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36010,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36120,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36896,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36934,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37453,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37491,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37556,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37828,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37899,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38459,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38580,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39187,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39389,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39768,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40176,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40294,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40355,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40833,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40920,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41014,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41296,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41413,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41485,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41748,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41802,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42193,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42244,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42687,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42848,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43404,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43505,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43574,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43728,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44012,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44318,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44472,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44544,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44929,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45003,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45436,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45738,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46146,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46461,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46740,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46887,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47293,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47893,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":47901,"length":5,"messageText":"Parameter 'value' implicitly has an 'any' type.","category":1,"code":7006},{"start":47953,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48037,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48323,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3860,[{"start":10021,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}},{"start":11180,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}}]],[3862,[{"start":670,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":995,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1089,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1162,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1454,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1549,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1753,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1842,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2056,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3864,[{"start":2791,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5175,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5214,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":5831,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5951,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6355,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6471,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6561,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6961,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7052,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7265,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7479,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7893,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8489,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9452,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9618,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9711,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9778,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10272,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10460,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10544,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10641,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11328,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11420,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11510,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12247,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12306,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12509,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13010,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13103,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13170,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13594,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13807,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13866,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14915,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14974,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15139,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15751,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15966,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16392,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16629,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16688,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16847,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17537,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17821,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18185,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18222,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18593,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18681,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19959,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20106,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20147,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20208,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20413,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20530,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20601,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21407,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21666,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21758,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21864,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21905,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22367,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22427,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22775,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22872,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23093,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23284,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23344,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23848,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24186,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24312,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24398,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24703,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25025,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25122,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25433,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25650,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25748,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26242,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26599,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27132,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27193,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27512,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28056,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28891,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29352,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30027,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30195,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30240,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30296,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30371,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31051,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":31202,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31595,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32108,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32179,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32514,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32856,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33024,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33069,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33191,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33234,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":33335,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33793,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33854,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34019,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34760,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34937,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35734,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36114,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36204,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36307,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36717,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36856,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37138,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37653,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38263,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38388,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38592,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":38600,"length":9,"messageText":"Parameter 'modelName' implicitly has an 'any' type.","category":1,"code":7006},{"start":38950,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39037,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39124,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39677,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40041,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40131,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40234,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40597,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40927,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40988,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41452,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41922,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42203,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42368,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3867,[{"start":793,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":840,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":892,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1224,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1269,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1342,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1419,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1501,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1794,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1869,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1948,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2022,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2531,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2612,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2703,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2889,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3304,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3889,[{"start":3670,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ metadata: { key: string; value?: string | undefined; }[]; }' is not assignable to parameter of type '{ metadata?: MetadataPair[] | undefined; }'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'metadata' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }[]' is not assignable to type 'MetadataPair[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'value' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string | undefined' is not assignable to type 'string'.","category":1,"code":2322,"next":[{"messageText":"Type 'undefined' is not assignable to type 'string'.","category":1,"code":2322}],"canonicalHead":{"code":2322,"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'."}}]}]}]}]}]}}]],[3895,[{"start":806,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1045,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1459,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1865,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":2328,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]}]],[3898,[{"start":221,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":265,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":376,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":667,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3999,[{"start":2930,"length":304,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4006,[{"start":5233,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': project_id, key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}}]],[4007,[{"start":5009,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10433,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4008,[{"start":3100,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}},{"start":5501,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6874,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":7548,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":7993,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8654,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9411,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10043,"length":104,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":11330,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12106,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12901,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":13663,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15005,"length":96,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":16135,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":18669,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":19912,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":20358,"length":111,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":20814,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":21298,"length":113,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":22406,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":22827,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":23458,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24088,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24671,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":25867,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":26622,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":27508,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":28369,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":29570,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":32974,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":40532,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4060,[{"start":1980,"length":293,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2424,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2638,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2857,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":5736,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6118,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6926,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7351,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7757,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":8309,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9294,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9836,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10256,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10874,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}}]]],"affectedFilesPendingEmit":[4062,2618,2620,2622,2624,2626,2628,2611,2794,2785,1268,1267,1266,2791,2784,2782,2793,2783,2792,2788,2787,2786,1189,2789,2819,2817,2818,2746,2835,2836,2825,2837,2823,1269,2838,2827,1271,1270,2822,2839,2840,2828,1273,2841,2826,2820,2833,2831,2834,2830,2829,2821,2824,2832,2842,2778,2843,2848,2845,2844,2847,2856,2849,2857,2853,1275,1274,2855,2851,2850,1276,2858,2852,2854,2873,2870,2874,2860,2863,2862,1277,1279,1278,2876,2877,2864,2875,2861,1280,2866,2865,2878,2867,1282,1281,2879,2880,2868,1074,2872,2869,2859,2871,2653,1284,1283,2979,2976,2980,2971,1287,1286,2981,2982,2977,1289,1288,2983,2972,2984,2883,2985,2974,2986,2975,2987,2882,1297,1296,2988,2989,1295,1299,1298,2978,2991,1310,2992,2993,1308,2994,2995,1330,2996,1325,1331,2999,1319,3000,1317,3001,1316,1355,1315,1311,1356,1318,2997,1307,1332,1326,2998,1309,1302,1352,1329,1353,1327,1354,1328,2990,3073,3063,3075,3074,3076,3066,3077,3069,3078,3068,3079,3067,3072,3071,3040,3041,3018,1361,3021,3051,3009,3007,3052,3010,3053,3022,3054,3023,3055,3056,3003,3057,3004,3006,3058,3002,3005,3059,1649,3060,3008,3061,1362,1363,3042,3030,3043,3028,1357,1360,1359,3044,3029,3045,3046,3024,3047,1358,3012,3013,3048,3020,3011,3037,3032,3019,3034,3026,3035,3027,3036,3025,3014,3049,3015,3050,3016,3038,3039,3031,3062,3017,3033,1385,1386,1384,1387,1388,1390,1389,1098,1391,1393,1392,1417,1419,1418,1421,1420,1423,1422,1425,1424,1428,1427,1429,1092,3081,1416,1430,1432,1431,1433,1434,1436,1435,1438,1437,1440,1439,1441,1442,1444,1443,1446,1447,1445,1448,1450,1449,1451,1452,1453,1454,1455,1457,1456,1459,1458,1461,1460,1462,1464,1463,1465,1162,1467,1466,1468,1367,1471,1470,1473,1472,1475,1474,1476,1469,1478,1477,1480,1479,1482,1481,1380,1484,1483,1485,1487,1489,1488,1491,1490,1493,1492,1495,1494,1497,1496,1498,1500,1499,1502,1501,1504,1503,1505,1093,1508,1507,1509,1506,1511,1510,1364,1365,1094,1369,1371,1372,1374,1373,1376,1375,1377,1378,1368,1381,1513,1512,1515,1514,1517,1516,3080,1383,2750,2747,2745,3094,3114,3119,3159,3160,3139,1519,1518,1522,1521,3124,1524,1525,1523,3161,3136,3127,3157,3177,3140,3178,3129,3179,3148,3180,3128,3181,3143,3182,3183,3142,3184,3144,3185,3151,3186,3130,3187,3156,1527,1526,3176,1528,3164,3162,3135,3163,3147,3165,3132,3166,3141,3167,3115,3116,3169,3118,3168,3117,1530,1529,3170,3122,3120,3134,3171,3133,3172,3125,3131,1608,3121,3126,3152,1610,1609,3173,3153,3174,3123,3175,3150,3188,1520,3158,3196,3189,3197,3190,3198,3192,3191,3199,3193,3195,3194,3218,3292,3245,3293,3244,1622,1621,3296,3252,3251,3250,1624,1623,3294,3283,3243,3295,3288,1617,1616,3291,3290,3297,3262,3246,3253,3298,3282,3267,3286,3284,3278,3289,1618,1626,1625,1188,3303,3301,3302,3317,3315,3318,3314,3313,3308,3307,3316,2780,2779,3424,3446,3416,3447,3438,3448,3425,3449,3417,1628,3426,3418,3450,3419,3451,3433,3452,3437,3453,3427,3420,3454,3421,3455,3422,3456,3423,3457,3436,3431,3434,3430,3432,3435,1630,1629,3458,3443,3459,3441,3460,3439,3461,3442,3463,3462,3464,3440,1633,1632,3320,1638,1637,1640,3340,3465,3408,3466,3409,3467,3410,3468,3411,1631,3412,3413,3415,3445,3444,3489,3479,3490,3473,3491,3484,3487,3476,3475,1643,1642,3492,3482,3493,3474,3494,3477,3495,3485,3496,3471,3497,3472,3498,3481,3499,3480,3488,3470,3469,1645,1644,3500,3483,3478,3486,3511,3506,3512,3505,3513,3504,3503,3516,3517,3501,3518,3519,3502,3520,1924,1646,1926,1925,3514,3509,3515,3508,3507,3510,3549,3526,3550,3546,3545,3562,3535,3567,3540,3563,3536,3564,3539,3565,3537,1930,1931,3566,3534,3538,3554,3532,3542,3544,3555,3529,3556,3524,3557,3528,3558,3533,3559,3541,3560,3530,1927,1929,1928,3561,3543,3551,3525,3521,3548,3523,3522,3552,3527,3553,3531,3547,3569,2970,3568,3580,3581,3572,3578,3582,3570,1933,1932,3586,3587,3577,3583,3574,3573,3584,3575,3585,3576,3571,3579,3595,3588,3593,3591,3594,3590,3589,3592,3605,3599,3603,3600,3604,3596,3602,3598,3597,3601,3613,3620,3623,3622,3621,3626,3625,3624,3650,3634,3651,3635,3652,3636,3649,3637,3653,3641,1936,1938,1937,3654,3642,3655,3640,1935,1934,3639,3647,3643,3648,3645,3656,3644,1939,1294,3646,3667,3658,3670,3660,1942,1941,1943,1940,3665,3668,3657,3669,3664,3672,3673,3663,3671,3662,3661,3666,3687,3688,3683,3689,3681,3680,3697,3685,1182,3690,1181,1180,3691,3682,3692,3684,3698,3699,3679,3693,3694,3677,3695,3676,3675,3696,3678,3686,3703,3702,3701,3700,3711,3713,3716,3705,3704,3718,3709,3708,3720,3722,3721,3724,3723,2638,3726,3727,3725,3728,3729,3730,3731,3733,3732,3737,3736,3738,3739,3735,3740,3734,3741,3761,3628,2034,1085,3864,3249,3260,3856,3261,3866,3254,3867,3220,1619,3857,3248,1994,1993,1996,1995,1997,1110,3868,3224,1102,3858,1097,1998,1096,1999,1078,2000,1108,3859,1106,3869,3255,1104,3247,3870,3257,2001,1100,3860,1101,1109,3871,3256,3872,3258,2035,3873,3259,3861,2036,3862,1105,2002,1107,3863,1103,3762,3273,3874,1650,1272,3785,3200,3791,3201,3792,3203,3793,3205,3786,3202,3787,3217,3788,3206,3212,3789,3210,3790,3209,3084,3875,3083,3742,1950,3763,3659,1888,3706,2011,3876,2010,3877,3715,2009,3710,3878,3717,3879,3714,3880,3707,3712,2003,3719,2012,2004,3881,3215,3428,1627,3882,3429,3883,1635,1636,2014,2013,3213,3211,1034,3764,3629,3794,3090,3795,3796,3087,3797,3085,3086,3798,3089,1962,1961,3799,3800,3088,1426,1324,1651,2759,1652,1072,3884,2752,3885,2760,2748,3905,3304,3906,3305,3907,3306,2015,3908,3207,3909,3208,3886,1653,3887,2753,3888,2652,3238,3889,3231,3890,1886,3891,1885,3892,1071,3894,3893,3895,1904,3896,3272,1887,3271,3897,1891,1905,3898,1892,3899,1902,2017,2016,3900,2761,3902,1303,1903,3903,3638,3904,3228,3901,3630,2795,3743,1911,3744,2649,3745,2654,3801,3097,3802,3096,3095,3803,3100,3804,3099,3098,3746,2846,2038,2039,2037,3910,2040,2041,1033,3765,3082,3805,1971,3806,1966,3807,1967,3808,1968,1973,1965,3809,1972,1974,1970,3911,2769,2042,3747,3232,3064,3810,3065,1975,3748,1320,3766,2233,1944,1170,3912,1912,1913,3915,1081,2045,2044,1032,3913,2043,1031,2047,2046,3914,1914,2049,2048,2051,2050,3767,3268,3768,1958,3749,2656,3916,3319,1639,3917,1082,2053,2052,3918,3414,3769,2762,2054,3919,1915,3920,1918,3921,3149,1075,1917,3922,3338,3923,1073,2056,2055,3924,3263,3925,3266,3926,3265,3264,3927,3221,3928,3281,3929,3280,3279,3930,3242,2057,3204,3285,3770,3227,3225,3811,2781,1977,1976,3931,3219,3932,1306,3933,2962,3771,2651,3813,2641,3814,2643,1978,1951,1979,3815,2644,3816,2650,3812,2646,3817,2648,1945,1187,3750,2655,625,2766,3772,1910,3936,3937,1923,2058,1921,3934,3935,2767,2060,2059,2061,1922,2064,2063,3939,3310,2066,2065,3940,3309,2062,3938,3312,1946,1960,1959,3773,3274,3818,3276,3275,3819,3277,3774,3632,2765,3941,2764,2763,3942,2770,1641,3775,3287,3776,1305,3777,3216,3214,3778,3269,3779,3270,3948,2884,3943,1893,3944,1894,3945,1897,3946,1895,3947,1896,3951,2969,3949,2968,2068,2067,3950,2967,2966,2965,2069,1486,3751,2796,3952,3233,3780,3093,1980,3820,2811,3821,2813,3822,2812,3823,2797,3824,3146,3825,3145,1982,1981,3826,2814,1983,1984,3832,2800,3833,2799,3834,2801,3835,3836,2802,3827,2803,3828,2804,3829,2807,3830,2805,3831,2806,1986,1985,3837,2808,3838,2809,3839,2810,3840,3092,3091,1987,3841,1901,3842,1898,3843,2963,1899,3845,2964,3844,1900,3070,3968,2755,3953,1908,3954,3311,3969,3631,3977,2217,3978,2218,3979,2219,3980,2216,2070,3981,2220,2222,3982,2221,3955,1952,3956,1919,1149,3970,3971,1153,3972,1155,3973,1152,3974,1157,3975,1160,3976,1159,1158,1161,1148,1964,3957,1168,2768,3983,1906,3229,3223,3958,1173,3959,1174,3960,1076,1889,3961,2749,3962,2973,3963,1648,3964,2881,2798,2757,3965,1026,3966,1949,2756,3984,1163,1164,3985,1165,3986,1167,3987,1169,1177,3988,1171,3989,1172,3990,1175,3991,1176,3967,2640,1647,3846,1955,3753,1957,3752,2815,3992,3339,623,3993,3608,3607,3606,2772,3994,3995,3234,3230,3996,1890,4001,3236,2224,2223,3997,3237,4002,3235,2226,2225,3998,3241,3999,3239,2228,2227,4000,3240,2229,3755,3612,1989,1988,3847,3611,3610,3754,3609,2231,2230,4006,2773,4007,4008,2774,2232,2235,2234,2771,4003,2754,4004,4005,2758,3848,2647,3756,3615,3849,3614,3850,3618,1990,3851,3617,3852,3616,3757,3619,4009,1300,1907,4010,1953,4011,1099,4012,2639,2642,4013,1020,1077,4014,2215,1156,1080,1025,1095,1314,4015,1029,2751,1023,1021,1027,1954,1083,4016,1948,4017,1030,1028,1154,1150,1084,2637,1079,1151,1301,1022,4018,1035,4019,1313,3758,2816,3759,3781,3222,3854,3300,3853,3627,1285,1992,1991,3782,3633,3783,2777,3760,2744,1111,4020,1920,3784,3674,4032,3103,3104,4021,3102,3101,2242,4022,2253,2236,4023,2252,2251,2240,4033,2239,2249,2248,4034,2250,4035,2247,4028,4029,3113,4030,3105,2237,4036,2243,4037,2272,2241,2245,4038,2275,2282,4039,2276,4040,2259,4041,2280,4042,2281,4043,2277,2269,2270,4044,2279,4045,2278,4046,1183,4047,2271,4048,2274,4049,2273,4050,2256,4051,2255,2246,2283,2260,4031,3106,3107,4024,3108,4025,3112,3111,4026,3110,2263,2268,2264,2265,2266,4052,2267,2261,2284,2262,4027,3109,2238,2254,3226,3299,2775,3855,2776,2635,2005,4053,2645,2636,1947,2290,2288,2287,2289,2286,2285,2291,4057,2294,1312,4054,3137,4055,1323,3138,4056,3154,3155,2292,2293,2295,1304,2298,2297,2299,1019,2302,2301,2304,2303,4058,2033,2305,2306,1293,2307,616,2308,1184,2309,1185,1186,2300,617,614,2310,2311,1366,2312,620,2313,1166,1415,1179,2314,2316,2315,2317,622,2577,2576,2579,2578,2580,1956,2581,1370,2582,2584,2583,2585,619,2586,1916,2587,1178,2588,1620,2589,2590,1634,2591,1379,2594,2593,2597,2596,2598,2595,2599,1086,2600,1087,618,2601,1382,2602,1963,2603,1070,4059,2619,2621,2623,2625,2627,2629,2608,2610,2612,2630,3865,2613,2617,2790,4060,613],"version":"5.9.3"} \ No newline at end of file +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/@img/colour/index.d.ts","./node_modules/sharp/dist/index.d.mts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/dev/types/routes.d.ts","./next-env.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/tinyrainbow/dist/index-8b61d5bc.d.ts","./node_modules/tinyrainbow/dist/node.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/rollup/dist/rollup.d.ts","./node_modules/rollup/dist/parseast.d.ts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/esbuild/lib/main.d.ts","./node_modules/vite/types/internal/terseroptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/vite/types/internal/csspreprocessoroptions.d.ts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-cksck4of.d.ts","./node_modules/@vitest/runner/dist/types.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/vitest/dist/chunks/environment.d.cl3nlxbe.d.ts","./node_modules/@vitest/mocker/dist/registry.d-d765pazg.d.ts","./node_modules/@vitest/mocker/dist/types.d-d_arzrdy.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/vite-node/dist/index.d-dgmxd2u7.d.ts","./node_modules/vite-node/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-dhdq1csl.d.ts","./node_modules/@vitest/snapshot/dist/rawsnapshot.d-lfsmjfud.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/vitest/dist/chunks/config.d.bkdhh7zx.d.ts","./node_modules/vitest/dist/chunks/worker.d.cugipz9v.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.bwvbvtda.d.ts","./node_modules/vite-node/dist/client.d.ts","./node_modules/vitest/dist/chunks/coverage.d.s9rmnxie.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.buron0i0.d.ts","./node_modules/vitest/dist/chunks/vite.d.bnoppc46.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./src/types.ts","./node_modules/vitest/dist/chunks/worker.d.uzwscv9x.d.ts","./node_modules/vitest/dist/chunks/global.d.mamajcmj.d.ts","./node_modules/vitest/dist/chunks/mocker.d.be_2ls6u.d.ts","./node_modules/vitest/dist/chunks/suite.d.fvehnv49.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./node_modules/@tanstack/query-core/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/query-core/build/modern/index.d.ts","./node_modules/@tanstack/react-query/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/react-query/build/modern/index.d.ts","./node_modules/sonner/dist/index.d.mts","./src/lib/http/client.ts","./src/lib/toast.ts","./src/utils/securestorage.ts","./src/utils/mcptokenstore.ts","./src/utils/cookieutils.ts","./node_modules/jwt-decode/build/esm/index.d.ts","./src/utils/jwtutils.ts","./src/components/tag_management/types.tsx","./src/components/email_events/types.ts","./src/lib/http/schema.d.ts","./src/components/claude_code_plugins/types.ts","./node_modules/@base-ui/react/internals/reason-parts.d.mts","./node_modules/@base-ui/react/internals/reasons.d.mts","./node_modules/@base-ui/react/internals/createbaseuieventdetails.d.mts","./node_modules/@base-ui/react/types/index.d.mts","./node_modules/@base-ui/react/internals/types.d.mts","./node_modules/@base-ui/react/accordion/root/accordionroot.d.mts","./node_modules/@base-ui/react/internals/usetransitionstatus.d.mts","./node_modules/@base-ui/react/collapsible/root/collapsibleroot.d.mts","./node_modules/@base-ui/react/collapsible/root/usecollapsibleroot.d.mts","./node_modules/@base-ui/react/accordion/item/accordionitem.d.mts","./node_modules/@base-ui/react/accordion/header/accordionheader.d.mts","./node_modules/@base-ui/react/accordion/trigger/accordiontrigger.d.mts","./node_modules/@base-ui/react/accordion/panel/accordionpanel.d.mts","./node_modules/@base-ui/react/accordion/index.parts.d.mts","./node_modules/@base-ui/react/accordion/index.d.mts","./node_modules/reselect/dist/reselect.d.ts","./node_modules/@base-ui/utils/store/createselector.d.mts","./node_modules/@base-ui/utils/store/createselectormemoized.d.mts","./node_modules/@base-ui/utils/fasthooks.d.mts","./node_modules/@base-ui/utils/store/store.d.mts","./node_modules/@base-ui/utils/store/usestore.d.mts","./node_modules/@base-ui/utils/store/reactstore.d.mts","./node_modules/@base-ui/utils/store/storeinspector.d.mts","./node_modules/@base-ui/utils/store/index.d.mts","./node_modules/@base-ui/utils/useenhancedclickhandler.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.d.mts","./node_modules/@floating-ui/core/dist/floating-ui.core.d.mts","./node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.d.mts","./node_modules/@floating-ui/dom/dist/floating-ui.dom.d.mts","./node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.d.mts","./node_modules/@base-ui/react/utils/popups/inlinerect.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtreestore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingrootstore.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingfocusmanager.d.mts","./node_modules/@base-ui/react/internals/getstateattributesprops.d.mts","./node_modules/@base-ui/react/internals/userenderelement.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingportal.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclientpoint.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usedismiss.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefocus.d.mts","./node_modules/@base-ui/react/internals/shadowdom.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/element.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehovershared.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehover.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverfloatinginteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usehoverreferenceinteraction.d.mts","./node_modules/@base-ui/react/floating-ui-react/utils/composite.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/gridnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/uselistnavigation.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usetypeahead.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/safepolygon.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingtree.d.mts","./node_modules/@base-ui/react/floating-ui-react/types.d.mts","./node_modules/@base-ui/react/floating-ui-react/components/floatingdelaygroup.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/useclick.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usefloating.d.mts","./node_modules/@base-ui/react/floating-ui-react/hooks/usesyncedfloatingrootcontext.d.mts","./node_modules/@base-ui/react/floating-ui-react/index.d.mts","./node_modules/@base-ui/react/utils/popups/popuptriggermap.d.mts","./node_modules/@base-ui/react/utils/popups/store.d.mts","./node_modules/@base-ui/react/utils/popups/popupstoreutils.d.mts","./node_modules/@base-ui/react/utils/popups/index.d.mts","./node_modules/@base-ui/react/dialog/store/dialogstore.d.mts","./node_modules/@base-ui/react/dialog/store/dialoghandle.d.mts","./node_modules/@base-ui/react/dialog/root/dialogroot.d.mts","./node_modules/@base-ui/react/alert-dialog/handle.d.mts","./node_modules/@base-ui/react/alert-dialog/root/alertdialogroot.d.mts","./node_modules/@base-ui/react/dialog/backdrop/dialogbackdrop.d.mts","./node_modules/@base-ui/react/dialog/close/dialogclose.d.mts","./node_modules/@base-ui/react/dialog/description/dialogdescription.d.mts","./node_modules/@base-ui/react/dialog/popup/dialogpopup.d.mts","./node_modules/@base-ui/react/dialog/portal/dialogportal.d.mts","./node_modules/@base-ui/react/dialog/title/dialogtitle.d.mts","./node_modules/@base-ui/react/dialog/trigger/dialogtrigger.d.mts","./node_modules/@base-ui/react/alert-dialog/trigger/alertdialogtrigger.d.mts","./node_modules/@base-ui/react/dialog/viewport/dialogviewport.d.mts","./node_modules/@base-ui/react/alert-dialog/index.parts.d.mts","./node_modules/@base-ui/react/alert-dialog/index.d.mts","./node_modules/@base-ui/react/internals/resolvevaluelabel.d.mts","./node_modules/@base-ui/react/combobox/root/ariacombobox.d.mts","./node_modules/@base-ui/react/autocomplete/root/autocompleteroot.d.mts","./node_modules/@base-ui/react/autocomplete/value/autocompletevalue.d.mts","./node_modules/@base-ui/react/internals/form-context/formcontext.d.mts","./node_modules/@base-ui/react/form/form.d.mts","./node_modules/@base-ui/react/form/index.d.mts","./node_modules/@base-ui/react/field/root/fieldroot.d.mts","./node_modules/@base-ui/react/utils/useanchorpositioning.d.mts","./node_modules/@base-ui/react/autocomplete/trigger/autocompletetrigger.d.mts","./node_modules/@base-ui/react/combobox/input/comboboxinput.d.mts","./node_modules/@base-ui/react/autocomplete/input-group/autocompleteinputgroup.d.mts","./node_modules/@base-ui/react/combobox/icon/comboboxicon.d.mts","./node_modules/@base-ui/react/combobox/clear/comboboxclear.d.mts","./node_modules/@base-ui/react/combobox/list/comboboxlist.d.mts","./node_modules/@base-ui/react/combobox/status/comboboxstatus.d.mts","./node_modules/@base-ui/react/combobox/portal/comboboxportal.d.mts","./node_modules/@base-ui/react/combobox/backdrop/comboboxbackdrop.d.mts","./node_modules/@base-ui/react/combobox/positioner/comboboxpositioner.d.mts","./node_modules/@base-ui/react/combobox/popup/comboboxpopup.d.mts","./node_modules/@base-ui/react/combobox/arrow/comboboxarrow.d.mts","./node_modules/@base-ui/react/combobox/group/comboboxgroup.d.mts","./node_modules/@base-ui/react/combobox/group-label/comboboxgrouplabel.d.mts","./node_modules/@base-ui/react/autocomplete/item/autocompleteitem.d.mts","./node_modules/@base-ui/react/combobox/row/comboboxrow.d.mts","./node_modules/@base-ui/react/combobox/collection/comboboxcollection.d.mts","./node_modules/@base-ui/react/combobox/empty/comboboxempty.d.mts","./node_modules/@base-ui/react/separator/separator.d.mts","./node_modules/@base-ui/react/internals/filter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefilter.d.mts","./node_modules/@base-ui/react/combobox/root/utils/usefiltereditems.d.mts","./node_modules/@base-ui/react/autocomplete/index.parts.d.mts","./node_modules/@base-ui/react/autocomplete/index.d.mts","./node_modules/@base-ui/react/avatar/root/avatarroot.d.mts","./node_modules/@base-ui/react/avatar/image/avatarimage.d.mts","./node_modules/@base-ui/react/avatar/fallback/avatarfallback.d.mts","./node_modules/@base-ui/react/avatar/index.parts.d.mts","./node_modules/@base-ui/react/avatar/index.d.mts","./node_modules/@base-ui/react/button/button.d.mts","./node_modules/@base-ui/react/button/index.d.mts","./node_modules/@base-ui/react/checkbox/root/checkboxroot.d.mts","./node_modules/@base-ui/react/checkbox/indicator/checkboxindicator.d.mts","./node_modules/@base-ui/react/checkbox/index.parts.d.mts","./node_modules/@base-ui/react/checkbox/index.d.mts","./node_modules/@base-ui/react/checkbox-group/checkboxgroup.d.mts","./node_modules/@base-ui/react/checkbox-group/index.d.mts","./node_modules/@base-ui/react/collapsible/trigger/collapsibletrigger.d.mts","./node_modules/@base-ui/react/collapsible/panel/collapsiblepanel.d.mts","./node_modules/@base-ui/react/collapsible/index.parts.d.mts","./node_modules/@base-ui/react/collapsible/index.d.mts","./node_modules/@base-ui/react/combobox/root/comboboxroot.d.mts","./node_modules/@base-ui/react/combobox/label/comboboxlabel.d.mts","./node_modules/@base-ui/react/combobox/value/comboboxvalue.d.mts","./node_modules/@base-ui/react/combobox/input-group/comboboxinputgroup.d.mts","./node_modules/@base-ui/react/combobox/trigger/comboboxtrigger.d.mts","./node_modules/@base-ui/react/combobox/item/comboboxitem.d.mts","./node_modules/@base-ui/react/combobox/item-indicator/comboboxitemindicator.d.mts","./node_modules/@base-ui/react/combobox/chips/comboboxchips.d.mts","./node_modules/@base-ui/react/combobox/chip/comboboxchip.d.mts","./node_modules/@base-ui/react/combobox/chip-remove/comboboxchipremove.d.mts","./node_modules/@base-ui/react/separator/index.d.mts","./node_modules/@base-ui/react/combobox/index.parts.d.mts","./node_modules/@base-ui/react/combobox/index.d.mts","./node_modules/@base-ui/react/menu/arrow/menuarrow.d.mts","./node_modules/@base-ui/react/menu/backdrop/menubackdrop.d.mts","./node_modules/@base-ui/react/menu/store/menustore.d.mts","./node_modules/@base-ui/react/menu/root/menurootcontext.d.mts","./node_modules/@base-ui/react/menubar/menubarcontext.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/store/menuhandle.d.mts","./node_modules/@base-ui/react/menu/root/menuroot.d.mts","./node_modules/@base-ui/react/menu/checkbox-item/menucheckboxitem.d.mts","./node_modules/@base-ui/react/menu/checkbox-item-indicator/menucheckboxitemindicator.d.mts","./node_modules/@base-ui/react/menu/group/menugroup.d.mts","./node_modules/@base-ui/react/menu/group-label/menugrouplabel.d.mts","./node_modules/@base-ui/react/menu/item/menuitem.d.mts","./node_modules/@base-ui/react/menu/link-item/menulinkitem.d.mts","./node_modules/@base-ui/react/menu/popup/menupopup.d.mts","./node_modules/@base-ui/react/menu/portal/menuportal.d.mts","./node_modules/@base-ui/react/menu/positioner/menupositioner.d.mts","./node_modules/@base-ui/react/menu/radio-group/menuradiogroup.d.mts","./node_modules/@base-ui/react/menu/radio-item/menuradioitem.d.mts","./node_modules/@base-ui/react/menu/radio-item-indicator/menuradioitemindicator.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenurootcontext.d.mts","./node_modules/@base-ui/react/menu/submenu-root/menusubmenuroot.d.mts","./node_modules/@base-ui/react/menu/trigger/menutrigger.d.mts","./node_modules/@base-ui/react/menu/viewport/menuviewport.d.mts","./node_modules/@base-ui/react/menu/submenu-trigger/menusubmenutrigger.d.mts","./node_modules/@base-ui/react/menu/index.parts.d.mts","./node_modules/@base-ui/react/menu/index.d.mts","./node_modules/@base-ui/react/context-menu/root/contextmenuroot.d.mts","./node_modules/@base-ui/react/context-menu/trigger/contextmenutrigger.d.mts","./node_modules/@base-ui/react/context-menu/index.parts.d.mts","./node_modules/@base-ui/react/context-menu/index.d.mts","./node_modules/@base-ui/react/csp-provider/cspprovider.d.mts","./node_modules/@base-ui/react/csp-provider/index.parts.d.mts","./node_modules/@base-ui/react/csp-provider/index.d.mts","./node_modules/@base-ui/react/dialog/index.parts.d.mts","./node_modules/@base-ui/react/dialog/index.d.mts","./node_modules/@base-ui/react/internals/direction-context/directioncontext.d.mts","./node_modules/@base-ui/react/direction-provider/directionprovider.d.mts","./node_modules/@base-ui/react/direction-provider/index.parts.d.mts","./node_modules/@base-ui/react/direction-provider/index.d.mts","./node_modules/@base-ui/react/drawer/backdrop/drawerbackdrop.d.mts","./node_modules/@base-ui/react/drawer/close/drawerclose.d.mts","./node_modules/@base-ui/react/drawer/content/drawercontent.d.mts","./node_modules/@base-ui/react/drawer/description/drawerdescription.d.mts","./node_modules/@base-ui/react/drawer/indent/drawerindent.d.mts","./node_modules/@base-ui/react/drawer/indent-background/drawerindentbackground.d.mts","./node_modules/@base-ui/react/utils/useswipedismiss.d.mts","./node_modules/@base-ui/react/drawer/root/drawerroot.d.mts","./node_modules/@base-ui/react/drawer/root/drawerrootcontext.d.mts","./node_modules/@base-ui/react/drawer/popup/drawerpopup.d.mts","./node_modules/@base-ui/react/drawer/portal/drawerportal.d.mts","./node_modules/@base-ui/react/drawer/provider/drawerprovider.d.mts","./node_modules/@base-ui/react/drawer/swipe-area/drawerswipearea.d.mts","./node_modules/@base-ui/react/drawer/title/drawertitle.d.mts","./node_modules/@base-ui/react/drawer/trigger/drawertrigger.d.mts","./node_modules/@base-ui/react/drawer/viewport/drawerviewport.d.mts","./node_modules/@base-ui/react/drawer/virtual-keyboard-provider/drawervirtualkeyboardprovider.d.mts","./node_modules/@base-ui/react/drawer/index.parts.d.mts","./node_modules/@base-ui/react/drawer/index.d.mts","./node_modules/@base-ui/react/field/label/fieldlabel.d.mts","./node_modules/@base-ui/react/field/error/fielderror.d.mts","./node_modules/@base-ui/react/field/description/fielddescription.d.mts","./node_modules/@base-ui/react/field/control/fieldcontrol.d.mts","./node_modules/@base-ui/react/field/validity/fieldvalidity.d.mts","./node_modules/@base-ui/react/field/item/fielditem.d.mts","./node_modules/@base-ui/react/field/index.parts.d.mts","./node_modules/@base-ui/react/field/index.d.mts","./node_modules/@base-ui/react/fieldset/root/fieldsetroot.d.mts","./node_modules/@base-ui/react/fieldset/legend/fieldsetlegend.d.mts","./node_modules/@base-ui/react/fieldset/index.parts.d.mts","./node_modules/@base-ui/react/fieldset/index.d.mts","./node_modules/@base-ui/react/input/input.d.mts","./node_modules/@base-ui/react/input/index.d.mts","./node_modules/@base-ui/react/menubar/menubar.d.mts","./node_modules/@base-ui/react/menubar/index.d.mts","./node_modules/@base-ui/react/merge-props/mergeprops.d.mts","./node_modules/@base-ui/react/merge-props/index.d.mts","./node_modules/@base-ui/react/meter/root/meterroot.d.mts","./node_modules/@base-ui/react/meter/track/metertrack.d.mts","./node_modules/@base-ui/react/meter/indicator/meterindicator.d.mts","./node_modules/@base-ui/react/meter/value/metervalue.d.mts","./node_modules/@base-ui/react/meter/label/meterlabel.d.mts","./node_modules/@base-ui/react/meter/index.parts.d.mts","./node_modules/@base-ui/react/meter/index.d.mts","./node_modules/@base-ui/react/navigation-menu/root/navigationmenuroot.d.mts","./node_modules/@base-ui/react/navigation-menu/list/navigationmenulist.d.mts","./node_modules/@base-ui/react/navigation-menu/item/navigationmenuitem.d.mts","./node_modules/@base-ui/react/navigation-menu/content/navigationmenucontent.d.mts","./node_modules/@base-ui/react/navigation-menu/trigger/navigationmenutrigger.d.mts","./node_modules/@base-ui/react/navigation-menu/portal/navigationmenuportal.d.mts","./node_modules/@base-ui/react/navigation-menu/positioner/navigationmenupositioner.d.mts","./node_modules/@base-ui/react/navigation-menu/viewport/navigationmenuviewport.d.mts","./node_modules/@base-ui/react/navigation-menu/backdrop/navigationmenubackdrop.d.mts","./node_modules/@base-ui/react/navigation-menu/popup/navigationmenupopup.d.mts","./node_modules/@base-ui/react/navigation-menu/arrow/navigationmenuarrow.d.mts","./node_modules/@base-ui/react/navigation-menu/link/navigationmenulink.d.mts","./node_modules/@base-ui/react/navigation-menu/icon/navigationmenuicon.d.mts","./node_modules/@base-ui/react/navigation-menu/index.parts.d.mts","./node_modules/@base-ui/react/navigation-menu/index.d.mts","./node_modules/@base-ui/react/number-field/utils/types.d.mts","./node_modules/@base-ui/react/number-field/root/numberfieldroot.d.mts","./node_modules/@base-ui/react/number-field/group/numberfieldgroup.d.mts","./node_modules/@base-ui/react/number-field/increment/numberfieldincrement.d.mts","./node_modules/@base-ui/react/number-field/decrement/numberfielddecrement.d.mts","./node_modules/@base-ui/react/number-field/input/numberfieldinput.d.mts","./node_modules/@base-ui/react/number-field/scrub-area/numberfieldscrubarea.d.mts","./node_modules/@base-ui/react/number-field/scrub-area-cursor/numberfieldscrubareacursor.d.mts","./node_modules/@base-ui/react/number-field/index.parts.d.mts","./node_modules/@base-ui/react/number-field/index.d.mts","./node_modules/@base-ui/react/otp-field/utils/otp.d.mts","./node_modules/@base-ui/react/otp-field/root/otpfieldroot.d.mts","./node_modules/@base-ui/react/otp-field/input/otpfieldinput.d.mts","./node_modules/@base-ui/react/otp-field/index.parts.d.mts","./node_modules/@base-ui/react/otp-field/index.d.mts","./node_modules/@base-ui/utils/usetimeout.d.mts","./node_modules/@base-ui/react/popover/store/popoverstore.d.mts","./node_modules/@base-ui/react/popover/store/popoverhandle.d.mts","./node_modules/@base-ui/react/popover/root/popoverroot.d.mts","./node_modules/@base-ui/react/popover/trigger/popovertrigger.d.mts","./node_modules/@base-ui/react/popover/portal/popoverportal.d.mts","./node_modules/@base-ui/react/popover/positioner/popoverpositioner.d.mts","./node_modules/@base-ui/react/popover/popup/popoverpopup.d.mts","./node_modules/@base-ui/react/popover/arrow/popoverarrow.d.mts","./node_modules/@base-ui/react/popover/backdrop/popoverbackdrop.d.mts","./node_modules/@base-ui/react/popover/title/popovertitle.d.mts","./node_modules/@base-ui/react/popover/description/popoverdescription.d.mts","./node_modules/@base-ui/react/popover/close/popoverclose.d.mts","./node_modules/@base-ui/react/popover/viewport/popoverviewport.d.mts","./node_modules/@base-ui/react/popover/index.parts.d.mts","./node_modules/@base-ui/react/popover/index.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardstore.d.mts","./node_modules/@base-ui/react/preview-card/store/previewcardhandle.d.mts","./node_modules/@base-ui/react/preview-card/root/previewcardroot.d.mts","./node_modules/@base-ui/react/utils/floatingportallite.d.mts","./node_modules/@base-ui/react/preview-card/portal/previewcardportal.d.mts","./node_modules/@base-ui/react/preview-card/trigger/previewcardtrigger.d.mts","./node_modules/@base-ui/react/preview-card/positioner/previewcardpositioner.d.mts","./node_modules/@base-ui/react/preview-card/popup/previewcardpopup.d.mts","./node_modules/@base-ui/react/preview-card/arrow/previewcardarrow.d.mts","./node_modules/@base-ui/react/preview-card/backdrop/previewcardbackdrop.d.mts","./node_modules/@base-ui/react/preview-card/viewport/previewcardviewport.d.mts","./node_modules/@base-ui/react/preview-card/index.parts.d.mts","./node_modules/@base-ui/react/preview-card/index.d.mts","./node_modules/@base-ui/react/progress/root/progressroot.d.mts","./node_modules/@base-ui/react/progress/track/progresstrack.d.mts","./node_modules/@base-ui/react/progress/indicator/progressindicator.d.mts","./node_modules/@base-ui/react/progress/value/progressvalue.d.mts","./node_modules/@base-ui/react/progress/label/progresslabel.d.mts","./node_modules/@base-ui/react/progress/index.parts.d.mts","./node_modules/@base-ui/react/progress/index.d.mts","./node_modules/@base-ui/react/radio/root/radioroot.d.mts","./node_modules/@base-ui/react/radio/indicator/radioindicator.d.mts","./node_modules/@base-ui/react/radio/index.parts.d.mts","./node_modules/@base-ui/react/radio/index.d.mts","./node_modules/@base-ui/react/radio-group/radiogroup.d.mts","./node_modules/@base-ui/react/radio-group/index.d.mts","./node_modules/@base-ui/react/scroll-area/root/scrollarearoot.d.mts","./node_modules/@base-ui/react/scroll-area/viewport/scrollareaviewport.d.mts","./node_modules/@base-ui/react/scroll-area/scrollbar/scrollareascrollbar.d.mts","./node_modules/@base-ui/react/scroll-area/content/scrollareacontent.d.mts","./node_modules/@base-ui/react/scroll-area/thumb/scrollareathumb.d.mts","./node_modules/@base-ui/react/scroll-area/corner/scrollareacorner.d.mts","./node_modules/@base-ui/react/scroll-area/index.parts.d.mts","./node_modules/@base-ui/react/scroll-area/index.d.mts","./node_modules/@base-ui/react/select/root/selectroot.d.mts","./node_modules/@base-ui/react/select/label/selectlabel.d.mts","./node_modules/@base-ui/react/select/trigger/selecttrigger.d.mts","./node_modules/@base-ui/react/select/value/selectvalue.d.mts","./node_modules/@base-ui/react/select/icon/selecticon.d.mts","./node_modules/@base-ui/react/select/portal/selectportal.d.mts","./node_modules/@base-ui/react/select/backdrop/selectbackdrop.d.mts","./node_modules/@base-ui/react/select/positioner/selectpositioner.d.mts","./node_modules/@base-ui/react/select/popup/selectpopup.d.mts","./node_modules/@base-ui/react/select/list/selectlist.d.mts","./node_modules/@base-ui/react/select/item/selectitem.d.mts","./node_modules/@base-ui/react/select/item-indicator/selectitemindicator.d.mts","./node_modules/@base-ui/react/select/item-text/selectitemtext.d.mts","./node_modules/@base-ui/react/select/arrow/selectarrow.d.mts","./node_modules/@base-ui/react/select/scroll-down-arrow/selectscrolldownarrow.d.mts","./node_modules/@base-ui/react/select/scroll-up-arrow/selectscrolluparrow.d.mts","./node_modules/@base-ui/react/select/group/selectgroup.d.mts","./node_modules/@base-ui/react/select/group-label/selectgrouplabel.d.mts","./node_modules/@base-ui/react/select/index.parts.d.mts","./node_modules/@base-ui/react/select/index.d.mts","./node_modules/@base-ui/react/slider/root/sliderroot.d.mts","./node_modules/@base-ui/react/slider/label/sliderlabel.d.mts","./node_modules/@base-ui/react/slider/value/slidervalue.d.mts","./node_modules/@base-ui/react/slider/control/slidercontrol.d.mts","./node_modules/@base-ui/react/slider/track/slidertrack.d.mts","./node_modules/@base-ui/react/internals/labelable-provider/labelablecontext.d.mts","./node_modules/@base-ui/react/slider/thumb/sliderthumb.d.mts","./node_modules/@base-ui/react/slider/indicator/sliderindicator.d.mts","./node_modules/@base-ui/react/slider/index.parts.d.mts","./node_modules/@base-ui/react/slider/index.d.mts","./node_modules/@base-ui/react/switch/root/switchroot.d.mts","./node_modules/@base-ui/react/switch/thumb/switchthumb.d.mts","./node_modules/@base-ui/react/switch/index.parts.d.mts","./node_modules/@base-ui/react/switch/index.d.mts","./node_modules/@base-ui/react/tabs/tab/tabstab.d.mts","./node_modules/@base-ui/react/tabs/root/tabsroot.d.mts","./node_modules/@base-ui/react/tabs/indicator/tabsindicator.d.mts","./node_modules/@base-ui/react/tabs/panel/tabspanel.d.mts","./node_modules/@base-ui/react/tabs/list/tabslist.d.mts","./node_modules/@base-ui/react/tabs/index.parts.d.mts","./node_modules/@base-ui/react/tabs/index.d.mts","./node_modules/@base-ui/react/toast/positioner/toastpositioner.d.mts","./node_modules/@base-ui/react/toast/usetoastmanager.d.mts","./node_modules/@base-ui/react/toast/createtoastmanager.d.mts","./node_modules/@base-ui/react/toast/provider/toastprovider.d.mts","./node_modules/@base-ui/react/toast/viewport/toastviewport.d.mts","./node_modules/@base-ui/react/toast/root/toastroot.d.mts","./node_modules/@base-ui/react/toast/content/toastcontent.d.mts","./node_modules/@base-ui/react/toast/description/toastdescription.d.mts","./node_modules/@base-ui/react/toast/title/toasttitle.d.mts","./node_modules/@base-ui/react/toast/close/toastclose.d.mts","./node_modules/@base-ui/react/toast/action/toastaction.d.mts","./node_modules/@base-ui/react/toast/portal/toastportal.d.mts","./node_modules/@base-ui/react/toast/arrow/toastarrow.d.mts","./node_modules/@base-ui/react/toast/index.parts.d.mts","./node_modules/@base-ui/react/toast/index.d.mts","./node_modules/@base-ui/react/toggle/toggle.d.mts","./node_modules/@base-ui/react/toggle/index.d.mts","./node_modules/@base-ui/react/toggle-group/togglegroup.d.mts","./node_modules/@base-ui/react/toggle-group/index.d.mts","./node_modules/@base-ui/react/toolbar/separator/toolbarseparator.d.mts","./node_modules/@base-ui/react/toolbar/root/toolbarroot.d.mts","./node_modules/@base-ui/react/toolbar/group/toolbargroup.d.mts","./node_modules/@base-ui/react/toolbar/button/toolbarbutton.d.mts","./node_modules/@base-ui/react/toolbar/link/toolbarlink.d.mts","./node_modules/@base-ui/react/toolbar/input/toolbarinput.d.mts","./node_modules/@base-ui/react/toolbar/index.parts.d.mts","./node_modules/@base-ui/react/toolbar/index.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltipstore.d.mts","./node_modules/@base-ui/react/tooltip/store/tooltiphandle.d.mts","./node_modules/@base-ui/react/tooltip/root/tooltiproot.d.mts","./node_modules/@base-ui/react/tooltip/trigger/tooltiptrigger.d.mts","./node_modules/@base-ui/react/tooltip/portal/tooltipportal.d.mts","./node_modules/@base-ui/react/tooltip/positioner/tooltippositioner.d.mts","./node_modules/@base-ui/react/tooltip/popup/tooltippopup.d.mts","./node_modules/@base-ui/react/tooltip/arrow/tooltiparrow.d.mts","./node_modules/@base-ui/react/tooltip/provider/tooltipprovider.d.mts","./node_modules/@base-ui/react/tooltip/viewport/tooltipviewport.d.mts","./node_modules/@base-ui/react/tooltip/index.parts.d.mts","./node_modules/@base-ui/react/tooltip/index.d.mts","./node_modules/@base-ui/react/use-render/userender.d.mts","./node_modules/@base-ui/react/use-render/index.d.mts","./node_modules/@base-ui/react/index.d.mts","./node_modules/clsx/clsx.d.mts","./node_modules/tailwind-merge/dist/types.d.ts","./node_modules/class-variance-authority/dist/types.d.ts","./node_modules/class-variance-authority/dist/index.d.ts","./src/lib/cva.config.ts","./src/components/ui/button.tsx","./src/components/ui/input.tsx","./src/components/ui/textarea.tsx","./src/components/ui/input-group.tsx","./node_modules/lucide-react/dist/lucide-react.d.ts","./src/components/ui/combobox.tsx","./src/components/shared/searchselect.tsx","./src/components/ui/label.tsx","./src/components/ui/separator.tsx","./src/components/ui/field.tsx","./src/components/ui/select.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","./src/components/object_permission_types.ts","./src/components/ui/tooltip.tsx","./node_modules/react-hook-form/dist/constants.d.ts","./node_modules/react-hook-form/dist/utils/createsubject.d.ts","./node_modules/react-hook-form/dist/types/events.d.ts","./node_modules/react-hook-form/dist/types/path/common.d.ts","./node_modules/react-hook-form/dist/types/path/eager.d.ts","./node_modules/react-hook-form/dist/types/path/index.d.ts","./node_modules/react-hook-form/dist/types/fieldarray.d.ts","./node_modules/react-hook-form/dist/types/resolvers.d.ts","./node_modules/react-hook-form/dist/types/form.d.ts","./node_modules/react-hook-form/dist/types/utils.d.ts","./node_modules/react-hook-form/dist/types/fields.d.ts","./node_modules/react-hook-form/dist/types/errors.d.ts","./node_modules/react-hook-form/dist/types/validator.d.ts","./node_modules/react-hook-form/dist/types/controller.d.ts","./node_modules/react-hook-form/dist/types/watch.d.ts","./node_modules/react-hook-form/dist/types/index.d.ts","./node_modules/react-hook-form/dist/controller.d.ts","./node_modules/react-hook-form/dist/fieldarray.d.ts","./node_modules/react-hook-form/dist/form.d.ts","./node_modules/react-hook-form/dist/formstatesubscribe.d.ts","./node_modules/react-hook-form/dist/logic/appenderrors.d.ts","./node_modules/react-hook-form/dist/logic/createformcontrol.d.ts","./node_modules/react-hook-form/dist/logic/index.d.ts","./node_modules/react-hook-form/dist/usecontroller.d.ts","./node_modules/react-hook-form/dist/usefieldarray.d.ts","./node_modules/react-hook-form/dist/useform.d.ts","./node_modules/react-hook-form/dist/useformcontext.d.ts","./node_modules/react-hook-form/dist/useformstate.d.ts","./node_modules/react-hook-form/dist/usewatch.d.ts","./node_modules/react-hook-form/dist/utils/get.d.ts","./node_modules/react-hook-form/dist/utils/set.d.ts","./node_modules/react-hook-form/dist/utils/index.d.ts","./node_modules/react-hook-form/dist/watch.d.ts","./node_modules/react-hook-form/dist/index.d.ts","./src/utils/textutils.ts","./src/components/common_components/mountedformfield.tsx","./src/components/common_components/check_openapi_schema.tsx","./src/components/mcp_tools/types.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/types.ts","./src/components/mcp_tools/constants.ts","./src/components/key_team_helpers/fetch_available_models_team_key.tsx","./src/components/llm_calls/fetch_models.tsx","./src/components/shared/multiselect.tsx","./src/components/ui/card.tsx","./src/components/add_model/complexity_router_keywords.ts","./src/components/ui/switch.tsx","./src/components/add_model/affinitycontrols.tsx","./src/components/add_model/nonreasoningtiertoggle.tsx","./src/components/add_model/tierrestrictions.tsx","./src/components/add_model/tierconfigintro.tsx","./src/components/add_model/tierrowselect.tsx","./src/components/add_model/modalityroutingcontrols.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/slider.tsx","./src/components/add_model/adaptiveroutingconfig.tsx","./src/utils/returnurlutils.ts","./src/utils/roles.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.ts","./src/app/(dashboard)/hooks/useauthorized.ts","./src/components/ui/dialog.tsx","./src/components/add_model/classifierprompteditorstate.ts","./src/components/add_model/classifierprompteditor.tsx","./src/components/add_model/openingprompteditor.tsx","./src/app/(dashboard)/hooks/autorouter/usecomplexityscorerdefaults.ts","./src/components/ui/badge.tsx","./src/components/add_model/heuristic_scoring_knobs.ts","./src/components/add_model/heuristicscoringconfig.tsx","./src/components/add_model/classifierreasoningeffortselect.tsx","./src/components/add_model/classifiercircuitbreakerconfig.tsx","./src/components/add_model/classifiervisionconfig.tsx","./src/components/add_model/classificationmethodconfig.tsx","./src/components/add_model/contextwindowescalationconfig.tsx","./src/components/add_model/responseformatcontrols.tsx","./src/components/add_model/stallescalationconfig.tsx","./src/components/add_model/tier_set_actions.ts","./src/components/add_model/tiermodeleffortrows.tsx","./src/components/add_model/escalationkeywords.tsx","./src/components/add_model/semantickeywordmatching.tsx","./src/app/(dashboard)/hooks/guardrails/useguardrails.ts","./src/components/add_model/buildautoroutercompression.ts","./src/components/add_model/compressioncontrols.tsx","./src/components/add_model/complexityrouterconfig.tsx","./src/components/add_model/tier_rows.ts","./src/components/add_model/complexity_router_tiers.ts","./src/components/add_model/keywordtierrules.tsx","./src/components/add_model/build_complexity_router_config.ts","./src/lib/autorouter_presets.ts","./src/components/vector_store_management/types.tsx","./node_modules/@tanstack/table-core/build/lib/utils.d.ts","./node_modules/@tanstack/table-core/build/lib/core/table.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnvisibility.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnordering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpinning.d.ts","./node_modules/@tanstack/table-core/build/lib/core/headers.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfaceting.d.ts","./node_modules/@tanstack/table-core/build/lib/filterfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/features/globalfiltering.d.ts","./node_modules/@tanstack/table-core/build/lib/sortingfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowsorting.d.ts","./node_modules/@tanstack/table-core/build/lib/aggregationfns.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columngrouping.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowexpanding.d.ts","./node_modules/@tanstack/table-core/build/lib/features/columnsizing.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowpagination.d.ts","./node_modules/@tanstack/table-core/build/lib/features/rowselection.d.ts","./node_modules/@tanstack/table-core/build/lib/core/row.d.ts","./node_modules/@tanstack/table-core/build/lib/core/cell.d.ts","./node_modules/@tanstack/table-core/build/lib/core/column.d.ts","./node_modules/@tanstack/table-core/build/lib/types.d.ts","./node_modules/@tanstack/table-core/build/lib/columnhelper.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getcorerowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getexpandedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedminmaxvalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfacetedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfaceteduniquevalues.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getfilteredrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getgroupedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getpaginationrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/utils/getsortedrowmodel.d.ts","./node_modules/@tanstack/table-core/build/lib/index.d.ts","./node_modules/@tanstack/react-table/build/lib/index.d.ts","./src/components/shared/datatable/types.ts","./src/components/shared/datatable/columnmeta.ts","./src/components/ui/skeleton.tsx","./src/components/ui/table.tsx","./src/components/shared/datatable/datatablepagination.tsx","./src/components/shared/datatable/datatable.tsx","./src/components/ui/sheet.tsx","./src/components/shared/datatable/datatablefilterdrawer.tsx","./src/components/ui/checkbox.tsx","./src/components/shared/datatable/datatableselectioncolumn.tsx","./src/components/shared/datatable/datatableviewoptions.tsx","./src/components/shared/datatable/datatabletoolbar.tsx","./src/components/shared/datatable/datatablesortheader.tsx","./src/components/shared/datatable/index.ts","./src/app/(dashboard)/hooks/models/usemodels.ts","./src/components/shared/table_cells/autoroutertag.tsx","./src/components/shared/table_cells/cell_tooltip.tsx","./src/components/shared/table_cells/date_cell.tsx","./src/utils/datautils.ts","./src/components/shared/table_cells/id_cell.tsx","./src/components/shared/entitylink.tsx","./src/components/shared/table_cells/identity_cell.tsx","./src/components/key_scope.ts","./src/components/shared/table_cells/models_cell.tsx","./src/components/shared/table_cells/money_cell.tsx","./src/components/shared/inheritedbudgethint.tsx","./src/components/shared/meter.tsx","./src/components/shared/table_cells/spend_budget_cell.tsx","./src/components/shared/table_cells/status_badge.tsx","./src/components/shared/table_cells/index.ts","./src/lib/serverrootpath.ts","./src/utils/uihref.ts","./src/utils/entitylinks.ts","./src/app/(dashboard)/vector-stores/_components/indexestablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.tsx","./src/app/(dashboard)/vector-stores/_components/indexestab.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.tsx","./src/lib/http/resolveapibase.ts","./src/lib/http/runtime.ts","./src/components/networking.tsx","./src/components/key_team_helpers/key_list.tsx","./src/app/(dashboard)/networking.ts","./src/app/(dashboard)/hooks/teams/useteams.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.ts","./src/app/(dashboard)/hooks/useisorgadmin.ts","./src/app/(dashboard)/hooks/healthreadiness/usehealthreadinessdetails.ts","./src/utils/proxyutils.ts","./src/app/(dashboard)/hooks/proxysettings/useproxysettings.ts","./src/app/(dashboard)/hooks/uselogout.ts","./src/contexts/themecontext.tsx","./src/components/ui/scroll-area.tsx","./src/components/shared/sidebar.tsx","./src/utils/capabilities.ts","./src/utils/localstorageutils.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.ts","./src/components/betabadge.tsx","./src/app/(dashboard)/hooks/usedisableblogposts.ts","./src/app/(dashboard)/hooks/usedisablebouncingicon.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.ts","./src/components/navbar/navdisplayname.ts","./src/components/shared/copybutton.tsx","./src/components/ui/avatar.tsx","./src/components/ui/popover.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.tsx","./src/app/(dashboard)/hooks/license/uselicenseinfo.ts","./src/utils/licenseutils.ts","./src/components/sidebarusagecard.tsx","./src/components/leftnav.tsx","./src/app/(dashboard)/legacypageroutes.ts","./src/app/(dashboard)/legacypageroutes.test.ts","./src/app/(dashboard)/access-groups/_components/types.ts","./node_modules/zod/v4/core/standard-schema.d.cts","./node_modules/zod/v4/core/util.d.cts","./node_modules/zod/v4/core/versions.d.cts","./node_modules/zod/v4/core/schemas.d.cts","./node_modules/zod/v4/core/checks.d.cts","./node_modules/zod/v4/core/errors.d.cts","./node_modules/zod/v4/core/core.d.cts","./node_modules/zod/v4/core/parse.d.cts","./node_modules/zod/v4/core/regexes.d.cts","./node_modules/zod/v4/locales/ar.d.cts","./node_modules/zod/v4/locales/az.d.cts","./node_modules/zod/v4/locales/be.d.cts","./node_modules/zod/v4/locales/ca.d.cts","./node_modules/zod/v4/locales/cs.d.cts","./node_modules/zod/v4/locales/de.d.cts","./node_modules/zod/v4/locales/en.d.cts","./node_modules/zod/v4/locales/eo.d.cts","./node_modules/zod/v4/locales/es.d.cts","./node_modules/zod/v4/locales/fa.d.cts","./node_modules/zod/v4/locales/fi.d.cts","./node_modules/zod/v4/locales/fr.d.cts","./node_modules/zod/v4/locales/fr-ca.d.cts","./node_modules/zod/v4/locales/he.d.cts","./node_modules/zod/v4/locales/hu.d.cts","./node_modules/zod/v4/locales/id.d.cts","./node_modules/zod/v4/locales/it.d.cts","./node_modules/zod/v4/locales/ja.d.cts","./node_modules/zod/v4/locales/kh.d.cts","./node_modules/zod/v4/locales/ko.d.cts","./node_modules/zod/v4/locales/mk.d.cts","./node_modules/zod/v4/locales/ms.d.cts","./node_modules/zod/v4/locales/nl.d.cts","./node_modules/zod/v4/locales/no.d.cts","./node_modules/zod/v4/locales/ota.d.cts","./node_modules/zod/v4/locales/ps.d.cts","./node_modules/zod/v4/locales/pl.d.cts","./node_modules/zod/v4/locales/pt.d.cts","./node_modules/zod/v4/locales/ru.d.cts","./node_modules/zod/v4/locales/sl.d.cts","./node_modules/zod/v4/locales/sv.d.cts","./node_modules/zod/v4/locales/ta.d.cts","./node_modules/zod/v4/locales/th.d.cts","./node_modules/zod/v4/locales/tr.d.cts","./node_modules/zod/v4/locales/ua.d.cts","./node_modules/zod/v4/locales/ur.d.cts","./node_modules/zod/v4/locales/vi.d.cts","./node_modules/zod/v4/locales/zh-cn.d.cts","./node_modules/zod/v4/locales/zh-tw.d.cts","./node_modules/zod/v4/locales/index.d.cts","./node_modules/zod/v4/core/registries.d.cts","./node_modules/zod/v4/core/doc.d.cts","./node_modules/zod/v4/core/function.d.cts","./node_modules/zod/v4/core/api.d.cts","./node_modules/zod/v4/core/json-schema.d.cts","./node_modules/zod/v4/core/to-json-schema.d.cts","./node_modules/zod/v4/core/index.d.cts","./node_modules/zod/v4/classic/errors.d.cts","./node_modules/zod/v4/classic/parse.d.cts","./node_modules/zod/v4/classic/schemas.d.cts","./node_modules/zod/v4/classic/checks.d.cts","./node_modules/zod/v4/classic/compat.d.cts","./node_modules/zod/v4/classic/iso.d.cts","./node_modules/zod/v4/classic/coerce.d.cts","./node_modules/zod/v4/classic/external.d.cts","./node_modules/zod/v4/classic/index.d.cts","./node_modules/zod/v4/index.d.cts","./src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.ts","./src/app/(dashboard)/access-groups/_components/access-group-create/mapper.test.ts","./src/app/(dashboard)/agents/_components/agent_config.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.ts","./src/app/(dashboard)/agents/_components/agent_discovery_utils.test.ts","./src/components/agents/types.ts","./src/app/(dashboard)/agents/_components/agent_type_utils.ts","./src/app/(dashboard)/agents/_components/agent_type_utils.test.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.ts","./src/app/(dashboard)/budgets/_components/budgetprecision.test.ts","./src/app/(dashboard)/budgets/_components/constants.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsfields.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.ts","./src/app/(dashboard)/caching/_components/cache_settings/cachesettingsutils.test.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfields.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.ts","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.test.ts","./src/components/usagepage/types.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.ts","./src/app/(dashboard)/cost-optimization/_components/costoptimizationutils.test.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.ts","./src/app/(dashboard)/cost-optimization/_components/helpers.test.ts","./node_modules/openapi-typescript-helpers/dist/index.d.mts","./node_modules/openapi-fetch/dist/index.d.mts","./node_modules/openapi-react-query/dist/index.d.mts","./src/lib/http/api.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.ts","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.ts","./src/app/(dashboard)/cost-optimization/_components/useautorouterbenchmarks.test.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.ts","./src/app/(dashboard)/cost-optimization/_components/useshadoweval.test.ts","./src/components/ui/alert-dialog.tsx","./src/components/ui/tabs.tsx","./src/app/(dashboard)/cost-tracking/_components/types.ts","./src/components/common_components/simple_table.tsx","./src/lib/assetpaths.ts","./src/components/provider_info_helpers.tsx","./src/lib/logotreatments.ts","./src/components/molecules/logo/logo.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/types.ts","./src/hooks/use-safe-layout-effect.ts","./src/components/ui/ui-loading-spinner.tsx","./src/components/ui/dropdown-menu.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.tsx","./src/components/helplink.tsx","./node_modules/@types/react-syntax-highlighter/index.d.ts","./node_modules/next-themes/dist/index.d.ts","./src/hooks/usesyntaxtheme.ts","./src/components/codeblock.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.ts","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.tsx","./src/app/(dashboard)/cost-tracking/_components/index.ts","./src/app/(dashboard)/cost-tracking/_components/provider_display_helpers.test.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@types/react-dom/test-utils/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./src/app/(dashboard)/cost-tracking/_components/use_block_unpriced_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_discount_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/use_margin_config.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_utils.test.ts","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/use_multi_cost_estimate.test.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_configs.ts","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.ts","./src/app/(dashboard)/guardrails/_components/guardrail_garden_data.test.ts","./src/app/(dashboard)/guardrails/_components/content_filter/action_options.ts","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/index.ts","./src/app/(dashboard)/hooks/useauthorized.serverrootpath.test.ts","./src/app/(dashboard)/hooks/useauthorized.test.ts","./src/app/(dashboard)/hooks/usecan.ts","./src/app/(dashboard)/hooks/usedisableshownewbadge.test.ts","./src/app/(dashboard)/hooks/usedisableshowprompts.test.ts","./src/app/(dashboard)/hooks/usehideautorouterannouncement.ts","./src/app/(dashboard)/hooks/useisorgadmin.test.ts","./src/utils/tabroutes.ts","./src/app/(dashboard)/hooks/usetabrouting.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroupdetails.ts","./src/app/(dashboard)/hooks/accessgroups/useaccessgroups.test.ts","./src/app/(dashboard)/hooks/accessgroups/usedeleteaccessgroup.ts","./src/app/(dashboard)/hooks/accessgroups/useeditaccessgroup.ts","./src/app/(dashboard)/hooks/agents/useagents.ts","./src/app/(dashboard)/hooks/agents/useagents.test.ts","./src/app/(dashboard)/hooks/autorouter/useautorouterpresets.ts","./src/app/(dashboard)/hooks/blogposts/useblogposts.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.ts","./src/app/(dashboard)/hooks/budgets/budgetfilters.test.ts","./node_modules/@tanstack/react-store/dist/createstorecontext.d.ts","./node_modules/@tanstack/store/dist/alien.d.ts","./node_modules/@tanstack/store/dist/types.d.ts","./node_modules/@tanstack/store/dist/atom.d.ts","./node_modules/@tanstack/store/dist/store.d.ts","./node_modules/@tanstack/store/dist/shallow.d.ts","./node_modules/@tanstack/store/dist/index.d.ts","./node_modules/@tanstack/react-store/dist/usecreateatom.d.ts","./node_modules/@tanstack/react-store/dist/usecreatestore.d.ts","./node_modules/@tanstack/react-store/dist/useselector.d.ts","./node_modules/@tanstack/react-store/dist/useatom.d.ts","./node_modules/@tanstack/react-store/dist/_usestore.d.ts","./node_modules/@tanstack/react-store/dist/usestore.d.ts","./node_modules/@tanstack/react-store/dist/index.d.ts","./node_modules/@tanstack/pacer/dist/types.d.ts","./node_modules/@tanstack/pacer/dist/debouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncer.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedcallback.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedstate.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/usedebouncedvalue.d.ts","./node_modules/@tanstack/react-pacer/dist/debouncer/index.d.ts","./src/utils/debounceconstants.ts","./src/app/(dashboard)/hooks/common/useresourcelist.ts","./src/app/(dashboard)/hooks/budgets/usebudgets.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.ts","./src/app/(dashboard)/hooks/caching/usecacheactivity.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerocreate.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerodryrun.test.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzeroexport.test.ts","./src/components/cloudzerocosttracking/types.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.ts","./src/app/(dashboard)/hooks/cloudzero/usecloudzerosettings.test.ts","./src/app/(dashboard)/hooks/common/querykeysfactory.test.ts","./src/app/(dashboard)/hooks/configoverrides/cyberarkapi.ts","./src/app/(dashboard)/hooks/configoverrides/hashicorpvaultapi.ts","./src/app/(dashboard)/hooks/configoverrides/usecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usedeletecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/usedeletehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/configoverrides/useupdatecyberarkconfig.ts","./src/app/(dashboard)/hooks/configoverrides/useupdatehashicorpvaultconfig.ts","./src/app/(dashboard)/hooks/coordinationredis/usecoordinationredissettings.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.ts","./src/app/(dashboard)/hooks/credentials/usecredentials.test.ts","./src/app/(dashboard)/hooks/customers/usecustomers.ts","./src/app/(dashboard)/hooks/customers/usecustomers.test.ts","./src/app/(dashboard)/hooks/guardrails/useguardrails.test.ts","./src/app/(dashboard)/hooks/guardrails/useguardrailsusage.ts","./src/app/(dashboard)/hooks/guardrails/useguardrailsusage.test.ts","./src/app/(dashboard)/hooks/guardrails/useregisterguardrail.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.ts","./src/app/(dashboard)/hooks/keys/usekeyaliases.test.ts","./src/app/(dashboard)/hooks/keys/usekeys.ts","./src/app/(dashboard)/hooks/keys/usekeyinfo.ts","./src/app/(dashboard)/hooks/keys/usekeys.test.ts","./src/app/(dashboard)/hooks/keys/useresetkeyspend.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.ts","./src/app/(dashboard)/hooks/keys/usesetkeyblockedstate.test.ts","./src/app/(dashboard)/hooks/logdetails/uselogdetails.ts","./src/app/(dashboard)/hooks/login/uselogin.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/usemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpsemanticfiltersettings/useupdatemcpsemanticfiltersettings.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpaccessgroups.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpserverhealth.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.ts","./src/app/(dashboard)/hooks/mcpservers/usemcpservers.test.ts","./src/app/(dashboard)/hooks/mcpservers/usemcptoolsets.ts","./src/app/(dashboard)/hooks/mcptoolsearchsettings/usemcptoolsearchsettings.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usemodelaccessgroups.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usedeletemodelaccessgroupbudget.ts","./src/app/(dashboard)/hooks/modelaccessgroups/usesetmodelaccessgroupbudget.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.ts","./src/app/(dashboard)/hooks/models/usemodelcostmap.test.ts","./src/app/(dashboard)/hooks/models/usemodels.test.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.ts","./src/app/(dashboard)/hooks/onboarding/useonboarding.test.ts","./src/app/(dashboard)/hooks/organizations/useorganizations.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.ts","./src/app/(dashboard)/hooks/projects/usecreateproject.test.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.ts","./src/app/(dashboard)/hooks/projects/usedeleteproject.test.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.ts","./src/app/(dashboard)/hooks/projects/useprojectdetails.test.ts","./src/app/(dashboard)/hooks/projects/useprojects.test.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.ts","./src/app/(dashboard)/hooks/projects/useupdateproject.test.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.ts","./src/app/(dashboard)/hooks/providers/useproviderfields.test.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.ts","./src/app/(dashboard)/hooks/proxyconfig/useproxyconfig.test.ts","./src/app/(dashboard)/hooks/router/userouterfields.ts","./src/app/(dashboard)/hooks/router/userouterfields.test.ts","./src/app/(dashboard)/hooks/routersettings/useupdateretrypolicy.ts","./src/components/routing_groups/types.ts","./src/app/(dashboard)/hooks/routinggroups/useroutinggroups.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogendusers.test.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.ts","./src/app/(dashboard)/hooks/spendlogs/usespendlogusers.test.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.ts","./src/app/(dashboard)/hooks/sso/useeditssosettings.test.ts","./src/app/(dashboard)/hooks/sso/usessosettings.ts","./src/app/(dashboard)/hooks/sso/usessosettings.test.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.ts","./src/app/(dashboard)/hooks/storemodelindb/usestoremodelindb.test.ts","./src/app/(dashboard)/hooks/storerequestinspendlogs/usestorerequestinspendlogs.ts","./src/app/(dashboard)/hooks/tags/usetags.ts","./src/app/(dashboard)/hooks/tags/usetags.test.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.ts","./src/app/(dashboard)/hooks/teams/useteammetadataschema.test.ts","./src/app/(dashboard)/hooks/teams/useteams.test.ts","./src/app/(dashboard)/hooks/uiconfig/useuiconfig.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.ts","./src/app/(dashboard)/hooks/uisettings/useptucostattributionenabled.test.ts","./src/app/(dashboard)/hooks/uisettings/useuisettings.test.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.ts","./src/app/(dashboard)/hooks/uisettings/useupdateuisettings.test.ts","./src/app/(dashboard)/hooks/userbanner/useuserbanner.ts","./src/app/(dashboard)/hooks/userbanner/useupdateuserbanner.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.ts","./src/app/(dashboard)/hooks/users/usecurrentuser.test.ts","./src/app/(dashboard)/hooks/users/useusers.ts","./src/app/(dashboard)/hooks/users/useusers.test.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.ts","./src/app/(dashboard)/mcp-servers/_components/createoauthuistate.test.ts","./src/app/(dashboard)/mcp-servers/_components/utils.tsx","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/createserverpayload.test.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.cases.ts","./src/app/(dashboard)/mcp-servers/_components/editserverpayload.differential.test.ts","./src/app/(dashboard)/mcp-servers/_components/importconnectorconfig.ts","./src/app/(dashboard)/mcp-servers/_components/importconnectorconfig.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.ts","./src/app/(dashboard)/mcp-servers/_components/mcpfieldrules.test.ts","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.ts","./src/app/(dashboard)/mcp-servers/_components/mountedserverfields.test.ts","./node_modules/@testing-library/user-event/dist/types/event/eventmap.d.ts","./node_modules/@testing-library/user-event/dist/types/event/types.d.ts","./node_modules/@testing-library/user-event/dist/types/event/dispatchevent.d.ts","./node_modules/@testing-library/user-event/dist/types/event/focus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/input.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/click/isclickableinput.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/blob.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/datatransfer.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/filelist.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/datatransfer/clipboard.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/timevalue.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iscontenteditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/iseditable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/maxlength.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/edit/setfiles.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/cursor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/getactiveelement.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/gettabdestination.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/isfocusable.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selection.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/focus/selector.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/keydef/readnextdescriptor.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/cloneevent.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/findclosest.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getdocumentfromnode.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/gettreediff.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/getwindow.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdescendantorself.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/iselementtype.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isvisible.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/isdisabled.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/level.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/misc/wait.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/pointer/csspointerevents.d.ts","./node_modules/@testing-library/user-event/dist/types/utils/index.d.ts","./node_modules/@testing-library/user-event/dist/types/document/ui.d.ts","./node_modules/@testing-library/user-event/dist/types/document/getvalueortextcontent.d.ts","./node_modules/@testing-library/user-event/dist/types/document/copyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/document/trackvalue.d.ts","./node_modules/@testing-library/user-event/dist/types/document/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/getinputrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/moveselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/modifyselectionpermouse.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/selectall.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselectionrange.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/setselection.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/updateselectiononfocus.d.ts","./node_modules/@testing-library/user-event/dist/types/event/selection/index.d.ts","./node_modules/@testing-library/user-event/dist/types/event/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/buttons.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/shared.d.ts","./node_modules/@testing-library/user-event/dist/types/system/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/index.d.ts","./node_modules/@testing-library/user-event/dist/types/system/keyboard.d.ts","./node_modules/@testing-library/user-event/dist/types/options.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/click.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/hover.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/tab.d.ts","./node_modules/@testing-library/user-event/dist/types/convenience/index.d.ts","./node_modules/@testing-library/user-event/dist/types/keyboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/copy.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/cut.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/paste.d.ts","./node_modules/@testing-library/user-event/dist/types/clipboard/index.d.ts","./node_modules/@testing-library/user-event/dist/types/pointer/index.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/clear.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/selectoptions.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/type.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/upload.d.ts","./node_modules/@testing-library/user-event/dist/types/utility/index.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/api.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/directapi.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/setup.d.ts","./node_modules/@testing-library/user-event/dist/types/setup/index.d.ts","./node_modules/@testing-library/user-event/dist/types/index.d.ts","./src/app/(dashboard)/mcp-servers/_components/testutils.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.ts","./src/app/(dashboard)/mcp-servers/_components/toolcallarguments.test.ts","./node_modules/nuqs/dist/defs-butbdnwx.d.ts","./node_modules/nuqs/dist/context-3xask51n.d.ts","./node_modules/nuqs/dist/adapters/testing.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/nuqs/dist/index.d.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.ts","./src/app/(dashboard)/models-and-endpoints/detailnavigation.test.ts","./src/app/(dashboard)/models-and-endpoints/usemodeldashboarddata.ts","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetpayload.ts","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetpayload.test.ts","./src/components/add_model/auto_router_strategies.ts","./src/utils/modelpermissions.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterrows.test.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.ts","./src/app/(dashboard)/models-and-endpoints/components/autorouters/fitpills.test.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.ts","./src/app/(dashboard)/models-and-endpoints/utils/modeldatatransformer.test.ts","./src/components/chat_ui/mode_endpoint_mapping.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatconstants.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.ts","./src/app/(dashboard)/playground/components/chat_ui/uploadvalidation.test.ts","./src/app/(dashboard)/playground/llm_calls/fetch_agents.tsx","./src/app/(dashboard)/playground/components/compareui/endpoint_config.ts","./src/app/(dashboard)/playground/components/compareui/endpoint_config.test.ts","./src/utils/promptcacheusage.ts","./src/components/chat_ui/responsemetrics.tsx","./src/components/chat_ui/types.ts","./src/app/(dashboard)/playground/hooks/usechathistory.ts","./src/app/(dashboard)/playground/hooks/usechathistory.test.ts","./src/components/llm_calls/code_interpreter_handler.ts","./src/app/(dashboard)/playground/hooks/usecodeinterpreter.ts","./src/components/policies/types.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.ts","./src/app/(dashboard)/policies/_components/build_attachment_data.test.ts","./src/app/(dashboard)/policies/_components/scope_validation.ts","./src/app/(dashboard)/policies/_components/scope_validation.test.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformschema.ts","./src/components/shared/usepaginatedcombobox.ts","./src/components/shared/paginatedsearchselect.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.tsx","./src/components/agent_management/agentselector.tsx","./src/components/common_components/accessgroupselector.tsx","./src/components/common_components/budget_duration_dropdown.tsx","./src/components/common_components/keylifecyclesettings.tsx","./node_modules/@heroicons/react/outline/academiccapicon.d.ts","./node_modules/@heroicons/react/outline/adjustmentsicon.d.ts","./node_modules/@heroicons/react/outline/annotationicon.d.ts","./node_modules/@heroicons/react/outline/archiveicon.d.ts","./node_modules/@heroicons/react/outline/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/outline/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/outline/arrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmupicon.d.ts","./node_modules/@heroicons/react/outline/arrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/outline/atsymbolicon.d.ts","./node_modules/@heroicons/react/outline/backspaceicon.d.ts","./node_modules/@heroicons/react/outline/badgecheckicon.d.ts","./node_modules/@heroicons/react/outline/banicon.d.ts","./node_modules/@heroicons/react/outline/beakericon.d.ts","./node_modules/@heroicons/react/outline/bellicon.d.ts","./node_modules/@heroicons/react/outline/bookopenicon.d.ts","./node_modules/@heroicons/react/outline/bookmarkalticon.d.ts","./node_modules/@heroicons/react/outline/bookmarkicon.d.ts","./node_modules/@heroicons/react/outline/briefcaseicon.d.ts","./node_modules/@heroicons/react/outline/cakeicon.d.ts","./node_modules/@heroicons/react/outline/calculatoricon.d.ts","./node_modules/@heroicons/react/outline/calendaricon.d.ts","./node_modules/@heroicons/react/outline/cameraicon.d.ts","./node_modules/@heroicons/react/outline/cashicon.d.ts","./node_modules/@heroicons/react/outline/chartbaricon.d.ts","./node_modules/@heroicons/react/outline/chartpieicon.d.ts","./node_modules/@heroicons/react/outline/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/outline/chatalt2icon.d.ts","./node_modules/@heroicons/react/outline/chatalticon.d.ts","./node_modules/@heroicons/react/outline/chaticon.d.ts","./node_modules/@heroicons/react/outline/checkcircleicon.d.ts","./node_modules/@heroicons/react/outline/checkicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/outline/chevrondownicon.d.ts","./node_modules/@heroicons/react/outline/chevronlefticon.d.ts","./node_modules/@heroicons/react/outline/chevronrighticon.d.ts","./node_modules/@heroicons/react/outline/chevronupicon.d.ts","./node_modules/@heroicons/react/outline/chipicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/outline/clipboardlisticon.d.ts","./node_modules/@heroicons/react/outline/clipboardicon.d.ts","./node_modules/@heroicons/react/outline/clockicon.d.ts","./node_modules/@heroicons/react/outline/clouddownloadicon.d.ts","./node_modules/@heroicons/react/outline/clouduploadicon.d.ts","./node_modules/@heroicons/react/outline/cloudicon.d.ts","./node_modules/@heroicons/react/outline/codeicon.d.ts","./node_modules/@heroicons/react/outline/cogicon.d.ts","./node_modules/@heroicons/react/outline/collectionicon.d.ts","./node_modules/@heroicons/react/outline/colorswatchicon.d.ts","./node_modules/@heroicons/react/outline/creditcardicon.d.ts","./node_modules/@heroicons/react/outline/cubetransparenticon.d.ts","./node_modules/@heroicons/react/outline/cubeicon.d.ts","./node_modules/@heroicons/react/outline/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/outline/currencydollaricon.d.ts","./node_modules/@heroicons/react/outline/currencyeuroicon.d.ts","./node_modules/@heroicons/react/outline/currencypoundicon.d.ts","./node_modules/@heroicons/react/outline/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/outline/currencyyenicon.d.ts","./node_modules/@heroicons/react/outline/cursorclickicon.d.ts","./node_modules/@heroicons/react/outline/databaseicon.d.ts","./node_modules/@heroicons/react/outline/desktopcomputericon.d.ts","./node_modules/@heroicons/react/outline/devicemobileicon.d.ts","./node_modules/@heroicons/react/outline/devicetableticon.d.ts","./node_modules/@heroicons/react/outline/documentaddicon.d.ts","./node_modules/@heroicons/react/outline/documentdownloadicon.d.ts","./node_modules/@heroicons/react/outline/documentduplicateicon.d.ts","./node_modules/@heroicons/react/outline/documentremoveicon.d.ts","./node_modules/@heroicons/react/outline/documentreporticon.d.ts","./node_modules/@heroicons/react/outline/documentsearchicon.d.ts","./node_modules/@heroicons/react/outline/documenttexticon.d.ts","./node_modules/@heroicons/react/outline/documenticon.d.ts","./node_modules/@heroicons/react/outline/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotsverticalicon.d.ts","./node_modules/@heroicons/react/outline/downloadicon.d.ts","./node_modules/@heroicons/react/outline/duplicateicon.d.ts","./node_modules/@heroicons/react/outline/emojihappyicon.d.ts","./node_modules/@heroicons/react/outline/emojisadicon.d.ts","./node_modules/@heroicons/react/outline/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/outline/exclamationicon.d.ts","./node_modules/@heroicons/react/outline/externallinkicon.d.ts","./node_modules/@heroicons/react/outline/eyeofficon.d.ts","./node_modules/@heroicons/react/outline/eyeicon.d.ts","./node_modules/@heroicons/react/outline/fastforwardicon.d.ts","./node_modules/@heroicons/react/outline/filmicon.d.ts","./node_modules/@heroicons/react/outline/filtericon.d.ts","./node_modules/@heroicons/react/outline/fingerprinticon.d.ts","./node_modules/@heroicons/react/outline/fireicon.d.ts","./node_modules/@heroicons/react/outline/flagicon.d.ts","./node_modules/@heroicons/react/outline/folderaddicon.d.ts","./node_modules/@heroicons/react/outline/folderdownloadicon.d.ts","./node_modules/@heroicons/react/outline/folderopenicon.d.ts","./node_modules/@heroicons/react/outline/folderremoveicon.d.ts","./node_modules/@heroicons/react/outline/foldericon.d.ts","./node_modules/@heroicons/react/outline/gifticon.d.ts","./node_modules/@heroicons/react/outline/globealticon.d.ts","./node_modules/@heroicons/react/outline/globeicon.d.ts","./node_modules/@heroicons/react/outline/handicon.d.ts","./node_modules/@heroicons/react/outline/hashtagicon.d.ts","./node_modules/@heroicons/react/outline/hearticon.d.ts","./node_modules/@heroicons/react/outline/homeicon.d.ts","./node_modules/@heroicons/react/outline/identificationicon.d.ts","./node_modules/@heroicons/react/outline/inboxinicon.d.ts","./node_modules/@heroicons/react/outline/inboxicon.d.ts","./node_modules/@heroicons/react/outline/informationcircleicon.d.ts","./node_modules/@heroicons/react/outline/keyicon.d.ts","./node_modules/@heroicons/react/outline/libraryicon.d.ts","./node_modules/@heroicons/react/outline/lightbulbicon.d.ts","./node_modules/@heroicons/react/outline/lightningbolticon.d.ts","./node_modules/@heroicons/react/outline/linkicon.d.ts","./node_modules/@heroicons/react/outline/locationmarkericon.d.ts","./node_modules/@heroicons/react/outline/lockclosedicon.d.ts","./node_modules/@heroicons/react/outline/lockopenicon.d.ts","./node_modules/@heroicons/react/outline/loginicon.d.ts","./node_modules/@heroicons/react/outline/logouticon.d.ts","./node_modules/@heroicons/react/outline/mailopenicon.d.ts","./node_modules/@heroicons/react/outline/mailicon.d.ts","./node_modules/@heroicons/react/outline/mapicon.d.ts","./node_modules/@heroicons/react/outline/menualt1icon.d.ts","./node_modules/@heroicons/react/outline/menualt2icon.d.ts","./node_modules/@heroicons/react/outline/menualt3icon.d.ts","./node_modules/@heroicons/react/outline/menualt4icon.d.ts","./node_modules/@heroicons/react/outline/menuicon.d.ts","./node_modules/@heroicons/react/outline/microphoneicon.d.ts","./node_modules/@heroicons/react/outline/minuscircleicon.d.ts","./node_modules/@heroicons/react/outline/minussmicon.d.ts","./node_modules/@heroicons/react/outline/minusicon.d.ts","./node_modules/@heroicons/react/outline/moonicon.d.ts","./node_modules/@heroicons/react/outline/musicnoteicon.d.ts","./node_modules/@heroicons/react/outline/newspapericon.d.ts","./node_modules/@heroicons/react/outline/officebuildingicon.d.ts","./node_modules/@heroicons/react/outline/paperairplaneicon.d.ts","./node_modules/@heroicons/react/outline/paperclipicon.d.ts","./node_modules/@heroicons/react/outline/pauseicon.d.ts","./node_modules/@heroicons/react/outline/pencilalticon.d.ts","./node_modules/@heroicons/react/outline/pencilicon.d.ts","./node_modules/@heroicons/react/outline/phoneincomingicon.d.ts","./node_modules/@heroicons/react/outline/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/outline/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/outline/phoneicon.d.ts","./node_modules/@heroicons/react/outline/photographicon.d.ts","./node_modules/@heroicons/react/outline/playicon.d.ts","./node_modules/@heroicons/react/outline/pluscircleicon.d.ts","./node_modules/@heroicons/react/outline/plussmicon.d.ts","./node_modules/@heroicons/react/outline/plusicon.d.ts","./node_modules/@heroicons/react/outline/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/outline/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/outline/printericon.d.ts","./node_modules/@heroicons/react/outline/puzzleicon.d.ts","./node_modules/@heroicons/react/outline/qrcodeicon.d.ts","./node_modules/@heroicons/react/outline/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/outline/receiptrefundicon.d.ts","./node_modules/@heroicons/react/outline/receipttaxicon.d.ts","./node_modules/@heroicons/react/outline/refreshicon.d.ts","./node_modules/@heroicons/react/outline/replyicon.d.ts","./node_modules/@heroicons/react/outline/rewindicon.d.ts","./node_modules/@heroicons/react/outline/rssicon.d.ts","./node_modules/@heroicons/react/outline/saveasicon.d.ts","./node_modules/@heroicons/react/outline/saveicon.d.ts","./node_modules/@heroicons/react/outline/scaleicon.d.ts","./node_modules/@heroicons/react/outline/scissorsicon.d.ts","./node_modules/@heroicons/react/outline/searchcircleicon.d.ts","./node_modules/@heroicons/react/outline/searchicon.d.ts","./node_modules/@heroicons/react/outline/selectoricon.d.ts","./node_modules/@heroicons/react/outline/servericon.d.ts","./node_modules/@heroicons/react/outline/shareicon.d.ts","./node_modules/@heroicons/react/outline/shieldcheckicon.d.ts","./node_modules/@heroicons/react/outline/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/outline/shoppingbagicon.d.ts","./node_modules/@heroicons/react/outline/shoppingcarticon.d.ts","./node_modules/@heroicons/react/outline/sortascendingicon.d.ts","./node_modules/@heroicons/react/outline/sortdescendingicon.d.ts","./node_modules/@heroicons/react/outline/sparklesicon.d.ts","./node_modules/@heroicons/react/outline/speakerphoneicon.d.ts","./node_modules/@heroicons/react/outline/staricon.d.ts","./node_modules/@heroicons/react/outline/statusofflineicon.d.ts","./node_modules/@heroicons/react/outline/statusonlineicon.d.ts","./node_modules/@heroicons/react/outline/stopicon.d.ts","./node_modules/@heroicons/react/outline/sunicon.d.ts","./node_modules/@heroicons/react/outline/supporticon.d.ts","./node_modules/@heroicons/react/outline/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/outline/switchverticalicon.d.ts","./node_modules/@heroicons/react/outline/tableicon.d.ts","./node_modules/@heroicons/react/outline/tagicon.d.ts","./node_modules/@heroicons/react/outline/templateicon.d.ts","./node_modules/@heroicons/react/outline/terminalicon.d.ts","./node_modules/@heroicons/react/outline/thumbdownicon.d.ts","./node_modules/@heroicons/react/outline/thumbupicon.d.ts","./node_modules/@heroicons/react/outline/ticketicon.d.ts","./node_modules/@heroicons/react/outline/translateicon.d.ts","./node_modules/@heroicons/react/outline/trashicon.d.ts","./node_modules/@heroicons/react/outline/trendingdownicon.d.ts","./node_modules/@heroicons/react/outline/trendingupicon.d.ts","./node_modules/@heroicons/react/outline/truckicon.d.ts","./node_modules/@heroicons/react/outline/uploadicon.d.ts","./node_modules/@heroicons/react/outline/useraddicon.d.ts","./node_modules/@heroicons/react/outline/usercircleicon.d.ts","./node_modules/@heroicons/react/outline/usergroupicon.d.ts","./node_modules/@heroicons/react/outline/userremoveicon.d.ts","./node_modules/@heroicons/react/outline/usericon.d.ts","./node_modules/@heroicons/react/outline/usersicon.d.ts","./node_modules/@heroicons/react/outline/variableicon.d.ts","./node_modules/@heroicons/react/outline/videocameraicon.d.ts","./node_modules/@heroicons/react/outline/viewboardsicon.d.ts","./node_modules/@heroicons/react/outline/viewgridaddicon.d.ts","./node_modules/@heroicons/react/outline/viewgridicon.d.ts","./node_modules/@heroicons/react/outline/viewlisticon.d.ts","./node_modules/@heroicons/react/outline/volumeofficon.d.ts","./node_modules/@heroicons/react/outline/volumeupicon.d.ts","./node_modules/@heroicons/react/outline/wifiicon.d.ts","./node_modules/@heroicons/react/outline/xcircleicon.d.ts","./node_modules/@heroicons/react/outline/xicon.d.ts","./node_modules/@heroicons/react/outline/zoominicon.d.ts","./node_modules/@heroicons/react/outline/zoomouticon.d.ts","./node_modules/@heroicons/react/outline/index.d.ts","./src/components/common_components/modelselector.tsx","./src/components/common_components/modelaliasmanager.tsx","./src/components/common_components/passthroughroutesselector.tsx","./src/components/callback_info_helpers.tsx","./src/components/shared/numerical_input.tsx","./src/components/team/loggingsettings.tsx","./src/components/common_components/premiumloggingsettings.tsx","./src/components/common_components/ratelimittypeformitem.tsx","./src/components/router_settings/latencybasedconfiguration.tsx","./src/components/router_settings/reliabilityretriessection.tsx","./src/components/router_settings/routingstrategyselector.tsx","./src/components/router_settings/tagfilteringtoggle.tsx","./src/components/router_settings/routersettingsform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.tsx","./src/components/settings/routersettings/fallbacks/fallbackgroupconfig.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.tsx","./src/components/common_components/routersettingsaccordion.tsx","./src/components/common_components/team_dropdown.tsx","./src/components/common_components/organizationdropdown.tsx","./src/components/common_components/projectdropdown.tsx","./src/components/shared/form/formfield.tsx","./src/components/ui/alert.tsx","./src/components/shared/alert.tsx","./node_modules/@types/react-copy-to-clipboard/index.d.ts","./src/components/onboarding_link.tsx","./src/components/createuserbutton.tsx","./src/components/key_team_helpers/budgetfallbackseditor.tsx","./src/components/key_team_helpers/budgetwindowseditor.tsx","./src/components/key_team_helpers/tagratelimiteditor.tsx","./src/components/mcp_server_management/mcpserverselector.tsx","./src/utils/mcptoolcrudclassification.ts","./src/components/mcp_tools/mcpcrudpermissionpanel.tsx","./src/components/mcp_server_management/effectivemcpservers.ts","./src/components/mcp_server_management/mcptoolpermissions.tsx","./src/components/shared/createdkeydisplay.tsx","./src/components/vector_store_management/vectorstoreselector.tsx","./src/components/organisms/createkeypayload.ts","./src/components/organisms/utils.ts","./src/components/organisms/create_key_button.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.ts","./src/app/(dashboard)/projects/_components/projectmodals/projectformutils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/utils.test.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/types.ts","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/useconversation.ts","./src/app/(dashboard)/search-tools/_components/types.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.ts","./src/app/(dashboard)/search-tools/_components/searchtoolpayload.test.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.ts","./src/app/(dashboard)/usage/_components/components/gatewayactivity.test.ts","./src/components/usagepage/keyactivitylabel.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusageaggregations.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.ts","./src/app/(dashboard)/usage/_components/components/entityusage/entityusagesummary.test.ts","./node_modules/@types/papaparse/index.d.ts","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspend.ts","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspend.test.ts","./src/app/(dashboard)/usage/_components/hooks/usepaginateddailyactivity.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.ts","./src/app/(dashboard)/users/_components/default-user-settings/mapper.test.ts","./src/app/(dashboard)/users/_components/default-user-settings/schema.test.ts","./src/components/key_scope.test.ts","./src/components/networking.test.ts","./src/components/page_metadata.ts","./src/components/page_utils.ts","./src/components/page_utils.test.ts","./src/components/cloudzerocosttracking/cloudzeropayload.ts","./src/components/cloudzerocosttracking/cloudzeropayload.test.ts","./src/utils/teamutils.ts","./src/components/shared/date_picker_types.ts","./src/components/entityusageexport/types.ts","./src/components/entityusageexport/exportformatselector.tsx","./src/components/entityusageexport/exportsummary.tsx","./src/components/entityusageexport/exporttypeselector.tsx","./src/components/entityusageexport/utils.ts","./src/components/entityusageexport/entityusageexportmodal.tsx","./src/components/entityusageexport/usageexportheader.tsx","./src/components/entityusageexport/index.ts","./src/components/entityusageexport/utils.test.ts","./src/components/guardrailsmonitor/mockdata.ts","./src/components/guardrailsmonitor/usageunits.ts","./src/components/guardrailsmonitor/usageunits.test.ts","./src/components/modelselect/modelutils.ts","./src/components/modelselect/modelutils.test.ts","./src/components/navbar/navdisplayname.test.ts","./src/components/navbar/navproductlinkclass.ts","./src/components/settings/adminsettings/cyberark/constants.ts","./src/components/settings/adminsettings/hashicorpvault/constants.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.ts","./src/components/settings/adminsettings/mcpsemanticfiltersettings/semanticfiltertestutils.test.ts","./src/components/settings/adminsettings/mcptoolsearchsettings/toolsearchform.ts","./src/components/settings/adminsettings/mcptoolsearchsettings/toolsearchform.test.ts","./src/components/settings/adminsettings/pluginsettings/schema.ts","./src/components/settings/adminsettings/ssosettings/constants.ts","./src/components/settings/adminsettings/ssosettings/utils.ts","./src/components/settings/adminsettings/ssosettings/utils.test.ts","./src/components/settings/loggingandalerts/loggingcallbacks/types.ts","./src/components/teamspage/teamscsvexport.ts","./src/components/teamspage/teamscsvexport.test.ts","./src/components/toolpolicies/toolpoliciesqueries.ts","./src/components/usagepage/keyactivitylabel.test.ts","./src/components/usagepage/utils/value_formatters.tsx","./src/components/usagepage/utils/value_formatters.test.ts","./src/components/add_model/auto_setup.ts","./src/components/add_model/auto_setup.test.ts","./src/components/add_model/buildautoroutercompression.test.ts","./src/components/add_model/build_auto_router_routing_test_request.ts","./src/components/add_model/build_auto_router_routing_test_request.test.ts","./src/components/add_model/build_auto_router_test_targets.ts","./src/components/add_model/build_auto_router_test_targets.test.ts","./src/components/add_model/build_complexity_router_config.test.ts","./src/components/add_model/classifierprompteditorstate.test.ts","./src/components/add_model/complexity_router_keywords.test.ts","./src/components/add_model/complexity_router_tiers.test.ts","./src/components/add_model/heuristic_scoring_knobs.test.ts","./src/components/add_model/tier_rows.test.ts","./src/components/add_model/tier_set_actions.test.ts","./src/components/chat/types.ts","./src/components/chat/usechathistory.ts","./src/contexts/chatshellcontext.tsx","./node_modules/dayjs/locale/types.d.ts","./node_modules/dayjs/locale/index.d.ts","./node_modules/dayjs/index.d.ts","./src/components/chat/conversationlist.tsx","./src/components/chat/chatshell.tsx","./src/components/chat/chatshell.serverrootpath.test.ts","./src/components/chat/usechathistory.test.ts","./src/components/claude_code_plugins/helpers.ts","./src/components/claude_code_plugins/helpers.test.ts","./src/components/common_components/formrules.ts","./src/components/common_components/routersettingspayload.ts","./src/components/common_components/routersettingspayload.test.ts","./node_modules/zod/v3/helpers/typealiases.d.cts","./node_modules/zod/v3/helpers/util.d.cts","./node_modules/zod/v3/zoderror.d.cts","./node_modules/zod/v3/locales/en.d.cts","./node_modules/zod/v3/errors.d.cts","./node_modules/zod/v3/helpers/parseutil.d.cts","./node_modules/zod/v3/helpers/enumutil.d.cts","./node_modules/zod/v3/helpers/errorutil.d.cts","./node_modules/zod/v3/helpers/partialutil.d.cts","./node_modules/zod/v3/standard-schema.d.cts","./node_modules/zod/v3/types.d.cts","./node_modules/zod/v3/external.d.cts","./node_modules/zod/v3/index.d.cts","./node_modules/@hookform/resolvers/zod/dist/zod.d.ts","./node_modules/@hookform/resolvers/zod/dist/index.d.ts","./src/lib/forms/usezodform.ts","./src/components/add_model/accessgrouptagscombobox.tsx","./src/components/add_model/modelchoicecombobox.tsx","./src/components/add_model/routerconfigbuilder.tsx","./src/components/edit_auto_router/edit_auto_router_modal.tsx","./src/components/edit_auto_router/build_updated_complexity_router_config.test.ts","./src/components/edit_auto_router/edit_auto_router_modal.test.ts","./src/components/email_events/email_event_settings.tsx","./src/components/email_events/index.ts","./src/components/guardrails/types.ts","./src/components/key_team_helpers/modelmaxbudgeteditor.test.ts","./src/components/key_team_helpers/filter_helpers.ts","./src/components/key_team_helpers/filter_helpers.test.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.ts","./src/components/key_team_helpers/modelmaxbudgetpayload.test.ts","./src/components/key_team_helpers/transform_key_info.tsx","./src/components/key_team_helpers/transform_key_info.test.ts","./src/components/key_team_helpers/useseededstate.ts","./src/components/key_team_helpers/usemodelmaxbudgetfield.ts","./src/components/llm_calls/mcp_tool_blocks.ts","./src/components/llm_calls/mcp_tool_blocks.test.ts","./src/components/mcp_server_management/effectivemcpservers.test.ts","./src/components/mcp_server_management/mcpentitlement.ts","./src/components/model_add/credential_form_helpers.ts","./src/components/model_add/credential_form_helpers.test.ts","./src/components/model_dashboard/types.ts","./src/components/organisms/createkeypayload.test.ts","./src/components/organisms/regeneratekeypayload.ts","./src/components/organisms/regeneratekeypayload.test.ts","./src/components/organisms/utils.test.ts","./src/components/organization/org-settings/schema.ts","./src/components/organization/org-create/mapper.ts","./src/components/organization/org-create/mapper.test.ts","./src/components/organization/org-settings/mapper.ts","./src/components/organization/org-settings/mapper.test.ts","./src/components/team/teammodelaccess.ts","./src/components/permissions/inheritedgrants.ts","./src/components/permissions/inheritedgrants.test.ts","./src/components/publicmodelhub/publicmodelhubfilters.ts","./src/components/publicmodelhub/publicmodelhubfilters.test.ts","./src/components/publicmodelhubtablecolumns.tsx","./src/components/publicmodelhub/usepublicmodelhublist.ts","./src/components/publicmodelhub/usepublicmodelhubfacets.ts","./src/components/routing_groups/routinggrouppayload.ts","./src/components/routing_groups/routinggrouppayload.test.ts","./src/components/routing_groups/strategy.ts","./src/components/shared/charts/colors.ts","./node_modules/@types/d3-time/index.d.ts","./node_modules/@types/d3-scale/index.d.ts","./node_modules/victory-vendor/d3-scale.d.ts","./node_modules/recharts/types/shape/dot.d.ts","./node_modules/recharts/types/component/text.d.ts","./node_modules/recharts/types/zindex/zindexlayer.d.ts","./node_modules/recharts/types/cartesian/getcartesianposition.d.ts","./node_modules/recharts/types/component/label.d.ts","./node_modules/recharts/types/cartesian/cartesianaxis.d.ts","./node_modules/recharts/types/util/scale/customscaledefinition.d.ts","./node_modules/redux/dist/redux.d.ts","./node_modules/immer/dist/immer.d.ts","./node_modules/redux-thunk/dist/redux-thunk.d.ts","./node_modules/@reduxjs/toolkit/dist/uncheckedindexed.ts","./node_modules/@reduxjs/toolkit/dist/index.d.mts","./node_modules/recharts/types/state/cartesianaxisslice.d.ts","./node_modules/recharts/types/synchronisation/types.d.ts","./node_modules/recharts/types/chart/types.d.ts","./node_modules/recharts/types/component/defaulttooltipcontent.d.ts","./node_modules/recharts/types/context/brushupdatecontext.d.ts","./node_modules/recharts/types/state/chartdataslice.d.ts","./node_modules/recharts/types/state/types/linesettings.d.ts","./node_modules/recharts/types/state/types/scattersettings.d.ts","./node_modules/@types/d3-path/index.d.ts","./node_modules/@types/d3-shape/index.d.ts","./node_modules/victory-vendor/d3-shape.d.ts","./node_modules/recharts/types/shape/curve.d.ts","./node_modules/recharts/types/component/labellist.d.ts","./node_modules/recharts/types/component/defaultlegendcontent.d.ts","./node_modules/recharts/types/util/payload/getuniqpayload.d.ts","./node_modules/recharts/types/util/useelementoffset.d.ts","./node_modules/recharts/types/component/legend.d.ts","./node_modules/recharts/types/state/legendslice.d.ts","./node_modules/recharts/types/state/types/stackedgraphicalitem.d.ts","./node_modules/recharts/types/util/stacks/stacktypes.d.ts","./node_modules/recharts/types/util/scale/rechartsscale.d.ts","./node_modules/recharts/types/util/chartutils.d.ts","./node_modules/recharts/types/state/selectors/areaselectors.d.ts","./node_modules/recharts/types/animation/easing.d.ts","./node_modules/recharts/types/animation/matchby.d.ts","./node_modules/recharts/types/animation/animateditems.d.ts","./node_modules/recharts/types/cartesian/arearevealshape.d.ts","./node_modules/recharts/types/cartesian/area.d.ts","./node_modules/recharts/types/state/types/areasettings.d.ts","./node_modules/recharts/types/shape/rectangle.d.ts","./node_modules/recharts/types/cartesian/bar.d.ts","./node_modules/recharts/types/util/barutils.d.ts","./node_modules/recharts/types/state/types/barsettings.d.ts","./node_modules/recharts/types/state/types/radialbarsettings.d.ts","./node_modules/recharts/types/util/svgpropertiesnoevents.d.ts","./node_modules/recharts/types/util/useuniqueid.d.ts","./node_modules/recharts/types/state/types/piesettings.d.ts","./node_modules/recharts/types/state/types/radarsettings.d.ts","./node_modules/recharts/types/state/graphicalitemsslice.d.ts","./node_modules/recharts/types/state/tooltipslice.d.ts","./node_modules/recharts/types/state/optionsslice.d.ts","./node_modules/recharts/types/state/layoutslice.d.ts","./node_modules/recharts/types/util/ifoverflow.d.ts","./node_modules/recharts/types/util/resolvedefaultprops.d.ts","./node_modules/recharts/types/cartesian/referenceline.d.ts","./node_modules/recharts/types/state/referenceelementsslice.d.ts","./node_modules/recharts/types/state/brushslice.d.ts","./node_modules/recharts/types/state/rootpropsslice.d.ts","./node_modules/recharts/types/state/polaraxisslice.d.ts","./node_modules/recharts/types/state/polaroptionsslice.d.ts","./node_modules/recharts/types/cartesian/linedrawshape.d.ts","./node_modules/recharts/types/cartesian/line.d.ts","./node_modules/recharts/types/shape/symbols.d.ts","./node_modules/recharts/types/util/constants.d.ts","./node_modules/recharts/types/util/scatterutils.d.ts","./node_modules/recharts/types/cartesian/scatter.d.ts","./node_modules/recharts/types/cartesian/errorbar.d.ts","./node_modules/recharts/types/state/errorbarslice.d.ts","./node_modules/recharts/types/state/zindexslice.d.ts","./node_modules/recharts/types/state/eventsettingsslice.d.ts","./node_modules/recharts/types/state/renderedticksslice.d.ts","./node_modules/recharts/types/state/store.d.ts","./node_modules/recharts/types/cartesian/getticks.d.ts","./node_modules/recharts/types/cartesian/cartesiangrid.d.ts","./node_modules/recharts/types/state/selectors/combiners/combinedisplayedstackeddata.d.ts","./node_modules/recharts/types/state/selectors/selecttooltipaxistype.d.ts","./node_modules/recharts/types/types.d.ts","./node_modules/recharts/types/hooks.d.ts","./node_modules/recharts/types/state/selectors/axisselectors.d.ts","./node_modules/recharts/types/component/dots.d.ts","./node_modules/recharts/types/util/typeddatakey.d.ts","./node_modules/recharts/types/util/types.d.ts","./node_modules/recharts/types/container/surface.d.ts","./node_modules/recharts/types/container/layer.d.ts","./node_modules/recharts/types/component/cursor.d.ts","./node_modules/recharts/types/component/tooltip.d.ts","./node_modules/recharts/types/component/responsivecontainer.d.ts","./node_modules/recharts/types/component/cell.d.ts","./node_modules/recharts/types/component/customized.d.ts","./node_modules/recharts/types/shape/sector.d.ts","./node_modules/recharts/types/shape/polygon.d.ts","./node_modules/recharts/types/shape/cross.d.ts","./node_modules/recharts/types/polar/polargrid.d.ts","./node_modules/recharts/types/polar/defaultpolarradiusaxisprops.d.ts","./node_modules/recharts/types/polar/polarradiusaxis.d.ts","./node_modules/recharts/types/polar/defaultpolarangleaxisprops.d.ts","./node_modules/recharts/types/polar/polarangleaxis.d.ts","./node_modules/recharts/types/context/tooltipcontext.d.ts","./node_modules/recharts/types/polar/pie.d.ts","./node_modules/recharts/types/polar/radar.d.ts","./node_modules/recharts/types/util/radialbarutils.d.ts","./node_modules/recharts/types/polar/radialbar.d.ts","./node_modules/recharts/types/cartesian/brush.d.ts","./node_modules/recharts/types/cartesian/referencedot.d.ts","./node_modules/recharts/types/util/excludeeventprops.d.ts","./node_modules/recharts/types/util/svgpropertiesandevents.d.ts","./node_modules/recharts/types/cartesian/referencearea.d.ts","./node_modules/recharts/types/cartesian/barstack.d.ts","./node_modules/recharts/types/cartesian/xaxis.d.ts","./node_modules/recharts/types/cartesian/yaxis.d.ts","./node_modules/recharts/types/cartesian/zaxis.d.ts","./node_modules/recharts/types/chart/linechart.d.ts","./node_modules/recharts/types/chart/barchart.d.ts","./node_modules/recharts/types/chart/piechart.d.ts","./node_modules/recharts/types/chart/treemap.d.ts","./node_modules/recharts/types/chart/sankey.d.ts","./node_modules/recharts/types/chart/radarchart.d.ts","./node_modules/recharts/types/chart/scatterchart.d.ts","./node_modules/recharts/types/chart/areachart.d.ts","./node_modules/recharts/types/chart/radialbarchart.d.ts","./node_modules/recharts/types/chart/composedchart.d.ts","./node_modules/recharts/types/chart/sunburstchart.d.ts","./node_modules/recharts/types/shape/trapezoid.d.ts","./node_modules/recharts/types/cartesian/funnel.d.ts","./node_modules/recharts/types/chart/funnelchart.d.ts","./node_modules/recharts/types/util/global.d.ts","./node_modules/recharts/types/animation/animationhandle.d.ts","./node_modules/recharts/types/animation/timeoutcontroller.d.ts","./node_modules/recharts/types/animation/animationcontroller.d.ts","./node_modules/recharts/types/animation/useanimationcontroller.d.ts","./node_modules/recharts/types/zindex/defaultzindexes.d.ts","./node_modules/decimal.js-light/decimal.d.ts","./node_modules/recharts/types/util/scale/getnicetickvalues.d.ts","./node_modules/recharts/types/context/chartlayoutcontext.d.ts","./node_modules/recharts/types/util/getrelativecoordinate.d.ts","./node_modules/recharts/types/util/createcartesiancharts.d.ts","./node_modules/recharts/types/util/createpolarcharts.d.ts","./node_modules/recharts/types/util/datautils.d.ts","./node_modules/recharts/types/index.d.ts","./src/components/ui/chart.tsx","./src/components/shared/charts/chart_tooltip.tsx","./src/components/shared/charts/area_chart.tsx","./src/components/shared/charts/bar_chart.tsx","./src/components/shared/charts/chart_legend.tsx","./src/components/shared/charts/donut_chart.tsx","./src/components/shared/charts/line_chart.tsx","./src/components/shared/charts/index.ts","./src/components/team/memberformvalues.ts","./src/components/team/memberformvalues.test.ts","./src/components/team/tabvisibilityutils.ts","./src/components/team/tabvisibilityutils.test.ts","./src/components/team/teammodelaccess.test.ts","./src/components/team/usemyteammember.ts","./src/components/templates/estimatedoutputtokens.ts","./src/components/templates/estimatedoutputtokens.test.ts","./src/components/templates/keyeditfieldnormalizers.ts","./src/components/key_info_utils.tsx","./src/components/templates/keyeditformvalues.ts","./src/components/templates/keyeditformvalues.test.ts","./src/components/view_logs/constants.ts","./src/components/view_logs/logdetailrouting.ts","./src/components/view_logs/utils.ts","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.tsx","./src/components/view_logs/guardrailviewer/__tests__/fixtures.ts","./src/components/view_logs/logdetailsdrawer/constants.ts","./src/components/view_logs/columns.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.tsx","./node_modules/moment/ts3.1-typings/moment.d.ts","./src/components/view_logs/logdetailsdrawer/sidebartoggle.tsx","./src/components/view_logs/logdetailsdrawer/drawerheader.tsx","./src/components/view_logs/logdetailsdrawer/usekeyboardnavigation.ts","./src/components/view_logs/guardrailviewer/presidiodetectedentities.tsx","./src/components/view_logs/guardrailviewer/contentfilterdetails.tsx","./src/components/view_logs/guardrailviewer/compliancepanel.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.tsx","./src/components/view_logs/evalviewer/evalviewer.tsx","./src/components/view_logs/costbreakdownviewer.tsx","./src/components/view_logs/configinfomessage.tsx","./src/components/view_logs/vectorstoreviewer.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.tsx","./node_modules/react-json-view-lite/dist/datarenderer.d.ts","./node_modules/react-json-view-lite/dist/index.d.ts","./src/components/view_logs/logdetailsdrawer/jsonviewer.tsx","./src/components/view_logs/logdetailsdrawer/utils.ts","./src/components/view_logs/toolssection/types.ts","./src/components/view_logs/toolssection/utils.ts","./src/components/view_logs/toolssection/formattedtoolview.tsx","./src/components/view_logs/toolssection/jsontoolview.tsx","./src/components/view_logs/toolssection/toolexpandedcontent.tsx","./src/components/view_logs/toolssection/toolitem.tsx","./src/components/view_logs/toolssection/toolssection.tsx","./src/components/view_logs/toolssection/index.ts","./src/components/view_logs/logdetailsdrawer/prettymessagestypes.ts","./src/components/view_logs/logdetailsdrawer/prettymessagesutils.ts","./src/components/view_logs/logdetailsdrawer/sectionheader.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.tsx","./src/components/view_logs/logdetailsdrawer/historytree.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.tsx","./src/components/view_logs/logdetailsdrawer/index.ts","./src/components/view_logs/logdetailsdrawer/utils.test.ts","./src/components/view_logs/toolssection/utils.test.ts","./src/data/insultscomplianceprompts.ts","./src/data/financialcomplianceprompts.ts","./src/data/codeexecutioncomplianceprompts.ts","./src/data/claimscomplianceprompts.ts","./src/data/complianceprompts.ts","./src/data/canadianpiicomplianceprompts.ts","./src/hooks/mcpoauthutils.ts","./src/hooks/usevisitedtabs.ts","./src/hooks/useworker.ts","./src/hooks/policies/usedeletepolicyattachment.ts","./src/lib/assetpaths.test.ts","./tests/mocks/autorouterpresets.ts","./src/lib/autorouter_presets.test.ts","./src/lib/cva.config.test.ts","./src/lib/logotreatments.test.ts","./src/lib/toast.test.ts","./src/lib/forms/pickdirty.ts","./src/lib/forms/pickdirty.test.ts","./src/lib/forms/urlvalidation.ts","./src/lib/forms/urlvalidation.test.ts","./src/lib/http/api.sameorigin.test.ts","./src/lib/http/api.test.ts","./src/lib/http/client.test.ts","./src/lib/http/resolveapibase.test.ts","./src/lib/http/runtime.test.ts","./src/lib/http/searchtooltypes.test-d.ts","./src/utils/budgetutils.ts","./src/utils/capabilities.test.ts","./src/utils/cookieutils.test.ts","./src/utils/datautils.test.ts","./src/utils/entitylinks.test.ts","./src/utils/errorpatterns.ts","./src/utils/errorutils.ts","./src/utils/errorutils.test.ts","./src/utils/jwtutils.test.ts","./node_modules/date-fns/constants.d.ts","./node_modules/date-fns/locale/types.d.ts","./node_modules/date-fns/fp/types.d.ts","./node_modules/date-fns/types.d.ts","./node_modules/date-fns/add.d.ts","./node_modules/date-fns/addbusinessdays.d.ts","./node_modules/date-fns/adddays.d.ts","./node_modules/date-fns/addhours.d.ts","./node_modules/date-fns/addisoweekyears.d.ts","./node_modules/date-fns/addmilliseconds.d.ts","./node_modules/date-fns/addminutes.d.ts","./node_modules/date-fns/addmonths.d.ts","./node_modules/date-fns/addquarters.d.ts","./node_modules/date-fns/addseconds.d.ts","./node_modules/date-fns/addweeks.d.ts","./node_modules/date-fns/addyears.d.ts","./node_modules/date-fns/areintervalsoverlapping.d.ts","./node_modules/date-fns/clamp.d.ts","./node_modules/date-fns/closestindexto.d.ts","./node_modules/date-fns/closestto.d.ts","./node_modules/date-fns/compareasc.d.ts","./node_modules/date-fns/comparedesc.d.ts","./node_modules/date-fns/constructfrom.d.ts","./node_modules/date-fns/constructnow.d.ts","./node_modules/date-fns/daystoweeks.d.ts","./node_modules/date-fns/differenceinbusinessdays.d.ts","./node_modules/date-fns/differenceincalendardays.d.ts","./node_modules/date-fns/differenceincalendarisoweekyears.d.ts","./node_modules/date-fns/differenceincalendarisoweeks.d.ts","./node_modules/date-fns/differenceincalendarmonths.d.ts","./node_modules/date-fns/differenceincalendarquarters.d.ts","./node_modules/date-fns/differenceincalendarweeks.d.ts","./node_modules/date-fns/differenceincalendaryears.d.ts","./node_modules/date-fns/differenceindays.d.ts","./node_modules/date-fns/differenceinhours.d.ts","./node_modules/date-fns/differenceinisoweekyears.d.ts","./node_modules/date-fns/differenceinmilliseconds.d.ts","./node_modules/date-fns/differenceinminutes.d.ts","./node_modules/date-fns/differenceinmonths.d.ts","./node_modules/date-fns/differenceinquarters.d.ts","./node_modules/date-fns/differenceinseconds.d.ts","./node_modules/date-fns/differenceinweeks.d.ts","./node_modules/date-fns/differenceinyears.d.ts","./node_modules/date-fns/eachdayofinterval.d.ts","./node_modules/date-fns/eachhourofinterval.d.ts","./node_modules/date-fns/eachminuteofinterval.d.ts","./node_modules/date-fns/eachmonthofinterval.d.ts","./node_modules/date-fns/eachquarterofinterval.d.ts","./node_modules/date-fns/eachweekofinterval.d.ts","./node_modules/date-fns/eachweekendofinterval.d.ts","./node_modules/date-fns/eachweekendofmonth.d.ts","./node_modules/date-fns/eachweekendofyear.d.ts","./node_modules/date-fns/eachyearofinterval.d.ts","./node_modules/date-fns/endofday.d.ts","./node_modules/date-fns/endofdecade.d.ts","./node_modules/date-fns/endofhour.d.ts","./node_modules/date-fns/endofisoweek.d.ts","./node_modules/date-fns/endofisoweekyear.d.ts","./node_modules/date-fns/endofminute.d.ts","./node_modules/date-fns/endofmonth.d.ts","./node_modules/date-fns/endofquarter.d.ts","./node_modules/date-fns/endofsecond.d.ts","./node_modules/date-fns/endoftoday.d.ts","./node_modules/date-fns/endoftomorrow.d.ts","./node_modules/date-fns/endofweek.d.ts","./node_modules/date-fns/endofyear.d.ts","./node_modules/date-fns/endofyesterday.d.ts","./node_modules/date-fns/_lib/format/formatters.d.ts","./node_modules/date-fns/_lib/format/longformatters.d.ts","./node_modules/date-fns/format.d.ts","./node_modules/date-fns/formatdistance.d.ts","./node_modules/date-fns/formatdistancestrict.d.ts","./node_modules/date-fns/formatdistancetonow.d.ts","./node_modules/date-fns/formatdistancetonowstrict.d.ts","./node_modules/date-fns/formatduration.d.ts","./node_modules/date-fns/formatiso.d.ts","./node_modules/date-fns/formatiso9075.d.ts","./node_modules/date-fns/formatisoduration.d.ts","./node_modules/date-fns/formatrfc3339.d.ts","./node_modules/date-fns/formatrfc7231.d.ts","./node_modules/date-fns/formatrelative.d.ts","./node_modules/date-fns/fromunixtime.d.ts","./node_modules/date-fns/getdate.d.ts","./node_modules/date-fns/getday.d.ts","./node_modules/date-fns/getdayofyear.d.ts","./node_modules/date-fns/getdaysinmonth.d.ts","./node_modules/date-fns/getdaysinyear.d.ts","./node_modules/date-fns/getdecade.d.ts","./node_modules/date-fns/_lib/defaultoptions.d.ts","./node_modules/date-fns/getdefaultoptions.d.ts","./node_modules/date-fns/gethours.d.ts","./node_modules/date-fns/getisoday.d.ts","./node_modules/date-fns/getisoweek.d.ts","./node_modules/date-fns/getisoweekyear.d.ts","./node_modules/date-fns/getisoweeksinyear.d.ts","./node_modules/date-fns/getmilliseconds.d.ts","./node_modules/date-fns/getminutes.d.ts","./node_modules/date-fns/getmonth.d.ts","./node_modules/date-fns/getoverlappingdaysinintervals.d.ts","./node_modules/date-fns/getquarter.d.ts","./node_modules/date-fns/getseconds.d.ts","./node_modules/date-fns/gettime.d.ts","./node_modules/date-fns/getunixtime.d.ts","./node_modules/date-fns/getweek.d.ts","./node_modules/date-fns/getweekofmonth.d.ts","./node_modules/date-fns/getweekyear.d.ts","./node_modules/date-fns/getweeksinmonth.d.ts","./node_modules/date-fns/getyear.d.ts","./node_modules/date-fns/hourstomilliseconds.d.ts","./node_modules/date-fns/hourstominutes.d.ts","./node_modules/date-fns/hourstoseconds.d.ts","./node_modules/date-fns/interval.d.ts","./node_modules/date-fns/intervaltoduration.d.ts","./node_modules/date-fns/intlformat.d.ts","./node_modules/date-fns/intlformatdistance.d.ts","./node_modules/date-fns/isafter.d.ts","./node_modules/date-fns/isbefore.d.ts","./node_modules/date-fns/isdate.d.ts","./node_modules/date-fns/isequal.d.ts","./node_modules/date-fns/isexists.d.ts","./node_modules/date-fns/isfirstdayofmonth.d.ts","./node_modules/date-fns/isfriday.d.ts","./node_modules/date-fns/isfuture.d.ts","./node_modules/date-fns/islastdayofmonth.d.ts","./node_modules/date-fns/isleapyear.d.ts","./node_modules/date-fns/ismatch.d.ts","./node_modules/date-fns/ismonday.d.ts","./node_modules/date-fns/ispast.d.ts","./node_modules/date-fns/issameday.d.ts","./node_modules/date-fns/issamehour.d.ts","./node_modules/date-fns/issameisoweek.d.ts","./node_modules/date-fns/issameisoweekyear.d.ts","./node_modules/date-fns/issameminute.d.ts","./node_modules/date-fns/issamemonth.d.ts","./node_modules/date-fns/issamequarter.d.ts","./node_modules/date-fns/issamesecond.d.ts","./node_modules/date-fns/issameweek.d.ts","./node_modules/date-fns/issameyear.d.ts","./node_modules/date-fns/issaturday.d.ts","./node_modules/date-fns/issunday.d.ts","./node_modules/date-fns/isthishour.d.ts","./node_modules/date-fns/isthisisoweek.d.ts","./node_modules/date-fns/isthisminute.d.ts","./node_modules/date-fns/isthismonth.d.ts","./node_modules/date-fns/isthisquarter.d.ts","./node_modules/date-fns/isthissecond.d.ts","./node_modules/date-fns/isthisweek.d.ts","./node_modules/date-fns/isthisyear.d.ts","./node_modules/date-fns/isthursday.d.ts","./node_modules/date-fns/istoday.d.ts","./node_modules/date-fns/istomorrow.d.ts","./node_modules/date-fns/istuesday.d.ts","./node_modules/date-fns/isvalid.d.ts","./node_modules/date-fns/iswednesday.d.ts","./node_modules/date-fns/isweekend.d.ts","./node_modules/date-fns/iswithininterval.d.ts","./node_modules/date-fns/isyesterday.d.ts","./node_modules/date-fns/lastdayofdecade.d.ts","./node_modules/date-fns/lastdayofisoweek.d.ts","./node_modules/date-fns/lastdayofisoweekyear.d.ts","./node_modules/date-fns/lastdayofmonth.d.ts","./node_modules/date-fns/lastdayofquarter.d.ts","./node_modules/date-fns/lastdayofweek.d.ts","./node_modules/date-fns/lastdayofyear.d.ts","./node_modules/date-fns/_lib/format/lightformatters.d.ts","./node_modules/date-fns/lightformat.d.ts","./node_modules/date-fns/max.d.ts","./node_modules/date-fns/milliseconds.d.ts","./node_modules/date-fns/millisecondstohours.d.ts","./node_modules/date-fns/millisecondstominutes.d.ts","./node_modules/date-fns/millisecondstoseconds.d.ts","./node_modules/date-fns/min.d.ts","./node_modules/date-fns/minutestohours.d.ts","./node_modules/date-fns/minutestomilliseconds.d.ts","./node_modules/date-fns/minutestoseconds.d.ts","./node_modules/date-fns/monthstoquarters.d.ts","./node_modules/date-fns/monthstoyears.d.ts","./node_modules/date-fns/nextday.d.ts","./node_modules/date-fns/nextfriday.d.ts","./node_modules/date-fns/nextmonday.d.ts","./node_modules/date-fns/nextsaturday.d.ts","./node_modules/date-fns/nextsunday.d.ts","./node_modules/date-fns/nextthursday.d.ts","./node_modules/date-fns/nexttuesday.d.ts","./node_modules/date-fns/nextwednesday.d.ts","./node_modules/date-fns/parse/_lib/types.d.ts","./node_modules/date-fns/parse/_lib/setter.d.ts","./node_modules/date-fns/parse/_lib/parser.d.ts","./node_modules/date-fns/parse/_lib/parsers.d.ts","./node_modules/date-fns/parse.d.ts","./node_modules/date-fns/parseiso.d.ts","./node_modules/date-fns/parsejson.d.ts","./node_modules/date-fns/previousday.d.ts","./node_modules/date-fns/previousfriday.d.ts","./node_modules/date-fns/previousmonday.d.ts","./node_modules/date-fns/previoussaturday.d.ts","./node_modules/date-fns/previoussunday.d.ts","./node_modules/date-fns/previousthursday.d.ts","./node_modules/date-fns/previoustuesday.d.ts","./node_modules/date-fns/previouswednesday.d.ts","./node_modules/date-fns/quarterstomonths.d.ts","./node_modules/date-fns/quarterstoyears.d.ts","./node_modules/date-fns/roundtonearesthours.d.ts","./node_modules/date-fns/roundtonearestminutes.d.ts","./node_modules/date-fns/secondstohours.d.ts","./node_modules/date-fns/secondstomilliseconds.d.ts","./node_modules/date-fns/secondstominutes.d.ts","./node_modules/date-fns/set.d.ts","./node_modules/date-fns/setdate.d.ts","./node_modules/date-fns/setday.d.ts","./node_modules/date-fns/setdayofyear.d.ts","./node_modules/date-fns/setdefaultoptions.d.ts","./node_modules/date-fns/sethours.d.ts","./node_modules/date-fns/setisoday.d.ts","./node_modules/date-fns/setisoweek.d.ts","./node_modules/date-fns/setisoweekyear.d.ts","./node_modules/date-fns/setmilliseconds.d.ts","./node_modules/date-fns/setminutes.d.ts","./node_modules/date-fns/setmonth.d.ts","./node_modules/date-fns/setquarter.d.ts","./node_modules/date-fns/setseconds.d.ts","./node_modules/date-fns/setweek.d.ts","./node_modules/date-fns/setweekyear.d.ts","./node_modules/date-fns/setyear.d.ts","./node_modules/date-fns/startofday.d.ts","./node_modules/date-fns/startofdecade.d.ts","./node_modules/date-fns/startofhour.d.ts","./node_modules/date-fns/startofisoweek.d.ts","./node_modules/date-fns/startofisoweekyear.d.ts","./node_modules/date-fns/startofminute.d.ts","./node_modules/date-fns/startofmonth.d.ts","./node_modules/date-fns/startofquarter.d.ts","./node_modules/date-fns/startofsecond.d.ts","./node_modules/date-fns/startoftoday.d.ts","./node_modules/date-fns/startoftomorrow.d.ts","./node_modules/date-fns/startofweek.d.ts","./node_modules/date-fns/startofweekyear.d.ts","./node_modules/date-fns/startofyear.d.ts","./node_modules/date-fns/startofyesterday.d.ts","./node_modules/date-fns/sub.d.ts","./node_modules/date-fns/subbusinessdays.d.ts","./node_modules/date-fns/subdays.d.ts","./node_modules/date-fns/subhours.d.ts","./node_modules/date-fns/subisoweekyears.d.ts","./node_modules/date-fns/submilliseconds.d.ts","./node_modules/date-fns/subminutes.d.ts","./node_modules/date-fns/submonths.d.ts","./node_modules/date-fns/subquarters.d.ts","./node_modules/date-fns/subseconds.d.ts","./node_modules/date-fns/subweeks.d.ts","./node_modules/date-fns/subyears.d.ts","./node_modules/date-fns/todate.d.ts","./node_modules/date-fns/transpose.d.ts","./node_modules/date-fns/weekstodays.d.ts","./node_modules/date-fns/yearstodays.d.ts","./node_modules/date-fns/yearstomonths.d.ts","./node_modules/date-fns/yearstoquarters.d.ts","./node_modules/date-fns/index.d.ts","./src/utils/keyexpiryutils.ts","./src/utils/keyexpiryutils.test.ts","./src/utils/keyupdateutils.ts","./src/utils/keyupdateutils.test.ts","./src/utils/licenseutils.test.ts","./src/utils/localstorageutils.test.ts","./src/utils/maskedsecretutils.ts","./src/utils/mcpheaderutils.ts","./src/utils/mcpheaderutils.test.ts","./src/utils/mcptokenstore.test.ts","./src/utils/mcptoolcrudclassification.test.ts","./src/utils/modelpermissions.test.ts","./src/utils/pkce.ts","./src/utils/promptcacheusage.test.ts","./src/utils/proxyutils.test.ts","./node_modules/dayjs/plugin/utc.d.ts","./src/utils/ptudatetime.ts","./src/utils/ptudatetime.test.ts","./src/utils/ptuvalidation.ts","./src/utils/ptumodelinfo.ts","./src/utils/ptumodelinfo.test.ts","./src/utils/ptuvalidation.test.ts","./src/utils/returnurlutils.test.ts","./src/utils/roles.test.ts","./src/utils/searchutils.ts","./src/utils/searchutils.test.ts","./src/utils/tabroutes.test.ts","./src/utils/teamutils.test.ts","./src/utils/textutils.test.ts","./src/utils/uihref.test.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@eslint/core/dist/cjs/types.d.cts","./node_modules/eslint/lib/types/use-at-your-own-risk.d.ts","./node_modules/eslint/lib/types/index.d.ts","./tests/fetch-location-rule.test.ts","./tests/fieldorientation.ts","./node_modules/vitest/dist/environments.d.ts","./tests/jsdomfetchenv.ts","./scripts/lint-budget-lib.mjs","./tests/lint-budget-lib.test.ts","./tests/setup.unit.ts","./node_modules/@testing-library/jest-dom/types/matchers.d.ts","./node_modules/@testing-library/jest-dom/types/jest.d.ts","./node_modules/@testing-library/jest-dom/types/index.d.ts","./tests/setuptests.ts","./scripts/eslint-rules/filename-pascal-case.mjs","./tests/eslint-rules/filename-pascal-case.test.ts","./scripts/eslint-rules/no-ad-hoc-z-index.mjs","./tests/eslint-rules/no-ad-hoc-z-index.test.ts","./scripts/eslint-rules/no-complex-jsx-arrow.mjs","./tests/eslint-rules/no-complex-jsx-arrow.test.ts","./scripts/eslint-rules/no-large-inline-object-arg.mjs","./tests/eslint-rules/no-large-inline-object-arg.test.ts","./scripts/eslint-rules/no-long-condition-chain.mjs","./tests/eslint-rules/no-long-condition-chain.test.ts","./scripts/eslint-rules/no-noop-hover-variant.mjs","./tests/eslint-rules/no-noop-hover-variant.test.ts","./tests/mocks/complexityscorerdefaults.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./node_modules/nuqs/dist/adapters/next/app.d.ts","./src/contexts/authcontext.tsx","./src/contexts/reactqueryprovider.tsx","./src/components/ui/sonner.tsx","./src/app/layout.tsx","./src/components/ui/breadcrumb.tsx","./src/components/shared/toolbarseparator.tsx","./src/components/navbar/blogdropdown/blogdropdown.tsx","./src/components/navbar/docslink/docslink.tsx","./src/components/ui/button-group.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.tsx","./src/components/navbar/notificationsbell/notificationsbell.tsx","./src/contexts/pluginmodecontext.tsx","./src/components/navbar/viewswitcher.tsx","./src/components/themetoggle/themetoggle.tsx","./src/components/navbar/workerdropdown/workerdropdown.tsx","./src/components/dashboardheader.tsx","./src/components/navbar/userdropdown/userdropdown.tsx","./src/components/navbar.tsx","./src/components/common_components/loadingscreen.tsx","./src/app/(dashboard)/components/sidebarprovider.tsx","./src/components/debugwarningbanner.tsx","./src/components/norediswarningbanner.tsx","./src/components/licenseexpirybanner.tsx","./node_modules/@types/unist/index.d.ts","./node_modules/@types/hast/index.d.ts","./node_modules/vfile-message/lib/index.d.ts","./node_modules/vfile-message/index.d.ts","./node_modules/vfile/lib/index.d.ts","./node_modules/vfile/index.d.ts","./node_modules/unified/lib/callable-instance.d.ts","./node_modules/trough/lib/index.d.ts","./node_modules/trough/index.d.ts","./node_modules/unified/lib/index.d.ts","./node_modules/unified/index.d.ts","./node_modules/@types/mdast/index.d.ts","./node_modules/mdast-util-to-hast/lib/state.d.ts","./node_modules/mdast-util-to-hast/lib/footer.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/blockquote.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/delete.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/emphasis.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/footnote-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/heading.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/html.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/image.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/inline-code.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link-reference.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/link.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list-item.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/list.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/paragraph.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/root.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/strong.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-cell.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/table-row.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/text.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/thematic-break.d.ts","./node_modules/mdast-util-to-hast/lib/handlers/index.d.ts","./node_modules/mdast-util-to-hast/lib/index.d.ts","./node_modules/mdast-util-to-hast/index.d.ts","./node_modules/remark-rehype/lib/index.d.ts","./node_modules/remark-rehype/index.d.ts","./node_modules/react-markdown/lib/index.d.ts","./node_modules/react-markdown/index.d.ts","./node_modules/micromark-util-types/index.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/html.d.ts","./node_modules/micromark-extension-gfm-footnote/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-footnote/index.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/html.d.ts","./node_modules/micromark-extension-gfm-strikethrough/lib/syntax.d.ts","./node_modules/micromark-extension-gfm-strikethrough/index.d.ts","./node_modules/micromark-extension-gfm/index.d.ts","./node_modules/mdast-util-from-markdown/lib/types.d.ts","./node_modules/mdast-util-from-markdown/lib/index.d.ts","./node_modules/mdast-util-from-markdown/index.d.ts","./node_modules/mdast-util-to-markdown/lib/types.d.ts","./node_modules/mdast-util-to-markdown/lib/index.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/blockquote.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/definition.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/emphasis.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/heading.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/html.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/image-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/inline-code.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/link-reference.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/list-item.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/paragraph.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/root.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/strong.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/text.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/thematic-break.d.ts","./node_modules/mdast-util-to-markdown/lib/handle/index.d.ts","./node_modules/mdast-util-to-markdown/index.d.ts","./node_modules/mdast-util-gfm-footnote/lib/index.d.ts","./node_modules/mdast-util-gfm-footnote/index.d.ts","./node_modules/markdown-table/index.d.ts","./node_modules/mdast-util-gfm-table/lib/index.d.ts","./node_modules/mdast-util-gfm-table/index.d.ts","./node_modules/mdast-util-gfm/lib/index.d.ts","./node_modules/mdast-util-gfm/index.d.ts","./node_modules/remark-gfm/lib/index.d.ts","./node_modules/remark-gfm/index.d.ts","./src/components/userbanner.tsx","./src/app/(dashboard)/layout.tsx","./src/app/(dashboard)/agentcontrolplaneview.test.tsx","./src/app/(dashboard)/layout.test.tsx","./src/components/shared/pageheader.tsx","./src/app/(dashboard)/hooks/useteams.tsx","./src/components/shared/badgelink.tsx","./src/components/ui/hover-card.tsx","./src/components/common_components/defaultproxyadmintag.tsx","./src/components/common_components/labeledfield.tsx","./src/components/templates/keyinfoheader.tsx","./src/components/shared/advanced_date_picker.tsx","./src/components/shared/summarycard.tsx","./src/components/shared/savingstiles.tsx","./src/components/templates/keysavingstab.tsx","./src/components/shared/paginatedmultiselect.tsx","./src/components/common_components/team_multi_select.tsx","./src/components/common_components/userdropdown.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalstartform.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.tsx","./src/components/templates/keyautorouterusagetab.tsx","./src/components/common_components/autorotationview.tsx","./src/components/common_components/deleteresourcemodal.tsx","./src/components/common_components/routersettingssummary.tsx","./src/components/logging_settings_view.tsx","./src/components/permissions/vectorstorepermissions.tsx","./src/components/permissions/mcpserverpermissions.tsx","./src/components/permissions/agentpermissions.tsx","./src/components/object_permissions_view.tsx","./src/components/organisms/regeneratekeymodal.tsx","./src/components/shared/errorutils.tsx","./src/components/guardrails/guardrailselector.tsx","./src/components/policies/policyselector.tsx","./src/components/templates/keyeditviewcontrols.tsx","./src/components/team/editloggingsettings.tsx","./src/components/templates/key_edit_view.tsx","./src/components/templates/key_info_view.tsx","./src/components/virtualkeyspage/keytablecolumns.tsx","./src/components/virtualkeyspage/virtualkeystable.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.tsx","./src/app/(dashboard)/page.tsx","./src/app/(dashboard)/page.test.tsx","./src/components/modelselect/modelselect.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupbaseform.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstablecolumns.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupstable.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.tsx","./src/app/(dashboard)/access-groups/page.tsx","./tests/test-utils.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsdetailspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupspage.test.tsx","./src/app/(dashboard)/access-groups/_components/accessgroupsmodal/accessgroupeditmodal.integration.test.tsx","./src/app/(dashboard)/access-groups/_components/access-group-create/accessgroupcreatedialog.test.tsx","./src/components/constants.tsx","./src/components/scim.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.tsx","./src/components/shared/passwordinput.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.tsx","./src/components/settings/adminsettings/uisettings/uisettings.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.tsx","./src/components/settings/adminsettings/cyberark/cyberarkemptyplaceholder.tsx","./src/components/settings/adminsettings/cyberark/editcyberarkmodal.tsx","./src/components/settings/adminsettings/cyberark/cyberark.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.tsx","./src/components/ssomodals.tsx","./src/components/uiaccesscontrolform.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.tsx","./src/app/(dashboard)/admin-panel/page.tsx","./src/app/(dashboard)/admin-panel/_components/adminpanel.test.tsx","./src/app/(dashboard)/agents/_components/agentformkit.tsx","./src/app/(dashboard)/agents/_components/cost_config_fields.tsx","./src/app/(dashboard)/agents/_components/agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.tsx","./src/app/(dashboard)/agents/_components/dynamic_agent_form_fields.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.tsx","./src/app/(dashboard)/agents/_components/agent_info.tsx","./src/app/(dashboard)/agents/_components/agentstablecolumns.tsx","./src/app/(dashboard)/agents/_components/agentstable.tsx","./src/app/(dashboard)/agents/_components/agentspanel.tsx","./src/app/(dashboard)/agents/page.tsx","./src/app/(dashboard)/agents/_components/agentspanel.test.tsx","./src/app/(dashboard)/agents/_components/agentstable.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.integration.test.tsx","./src/app/(dashboard)/agents/_components/add_agent_form.test.tsx","./src/app/(dashboard)/agents/_components/agent_card_discovery.test.tsx","./src/app/(dashboard)/agents/_components/agent_cost_view.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.integration.test.tsx","./src/app/(dashboard)/agents/_components/agent_info.test.tsx","./src/app/(dashboard)/agents/_components/agent_virtual_keys.test.tsx","./src/app/(dashboard)/api-keys/apikeysdashboard.test.tsx","./src/app/(dashboard)/api-keys/page.tsx","./src/app/(dashboard)/api-reference/_components/doclink.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.tsx","./src/components/deprecationbanner.tsx","./src/app/(dashboard)/api-reference/page.tsx","./src/app/(dashboard)/api-reference/_components/apireferenceview.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budgettablecolumns.tsx","./src/app/(dashboard)/budgets/_components/budgettable.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.tsx","./src/app/(dashboard)/budgets/page.tsx","./src/app/(dashboard)/budgets/_components/budgettable.test.tsx","./src/app/(dashboard)/budgets/_components/budget_modal.integration.test.tsx","./src/app/(dashboard)/budgets/_components/budget_panel.test.tsx","./src/app/(dashboard)/budgets/_components/edit_budget_modal.integration.test.tsx","./src/app/(dashboard)/caching/_components/response_time_indicator.tsx","./src/app/(dashboard)/caching/_components/cache_health.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cacheformfield.tsx","./src/app/(dashboard)/caching/_components/cache_settings/cachefieldsection.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisformfield.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredisfieldsection.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.tsx","./src/app/(dashboard)/caching/page.tsx","./src/app/(dashboard)/caching/_components/errordrilldown.test.tsx","./src/app/(dashboard)/caching/_components/cache_dashboard.test.tsx","./src/app/(dashboard)/caching/_components/cache_health.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/redistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/cache_settings/index.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationredistypeselector.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.integration.test.tsx","./src/app/(dashboard)/caching/_components/coordination_redis_settings/index.test.tsx","./src/components/shared/paginationstatusalerts.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.tsx","./src/components/router_settings/index.tsx","./node_modules/openai/_shims/manual-types.d.ts","./node_modules/openai/_shims/auto/types.d.ts","./node_modules/openai/streaming.d.ts","./node_modules/openai/error.d.ts","./node_modules/openai/_shims/multipartbody.d.ts","./node_modules/openai/uploads.d.ts","./node_modules/openai/core.d.ts","./node_modules/openai/_shims/index.d.ts","./node_modules/openai/pagination.d.ts","./node_modules/openai/resources/shared.d.ts","./node_modules/openai/resources/batches.d.ts","./node_modules/openai/resources/chat/completions/messages.d.ts","./node_modules/openai/resources/chat/completions/completions.d.ts","./node_modules/openai/resources/completions.d.ts","./node_modules/openai/resources/embeddings.d.ts","./node_modules/openai/resources/files.d.ts","./node_modules/openai/resources/images.d.ts","./node_modules/openai/resources/models.d.ts","./node_modules/openai/resources/moderations.d.ts","./node_modules/openai/resources/audio/speech.d.ts","./node_modules/openai/resources/audio/transcriptions.d.ts","./node_modules/openai/resources/audio/translations.d.ts","./node_modules/openai/resources/audio/audio.d.ts","./node_modules/openai/resources/beta/threads/messages.d.ts","./node_modules/openai/resources/beta/threads/runs/steps.d.ts","./node_modules/openai/resources/beta/threads/runs/runs.d.ts","./node_modules/openai/lib/eventstream.d.ts","./node_modules/openai/lib/assistantstream.d.ts","./node_modules/openai/resources/beta/threads/threads.d.ts","./node_modules/openai/resources/beta/assistants.d.ts","./node_modules/openai/resources/chat/completions.d.ts","./node_modules/openai/lib/abstractchatcompletionrunner.d.ts","./node_modules/openai/lib/chatcompletionstream.d.ts","./node_modules/openai/lib/responsesparser.d.ts","./node_modules/openai/resources/responses/input-items.d.ts","./node_modules/openai/lib/responses/eventtypes.d.ts","./node_modules/openai/lib/responses/responsestream.d.ts","./node_modules/openai/resources/responses/responses.d.ts","./node_modules/openai/lib/parser.d.ts","./node_modules/openai/lib/chatcompletionstreamingrunner.d.ts","./node_modules/openai/lib/jsonschema.d.ts","./node_modules/openai/lib/runnablefunction.d.ts","./node_modules/openai/lib/chatcompletionrunner.d.ts","./node_modules/openai/resources/beta/chat/completions.d.ts","./node_modules/openai/resources/beta/chat/chat.d.ts","./node_modules/openai/resources/beta/realtime/sessions.d.ts","./node_modules/openai/resources/beta/realtime/transcription-sessions.d.ts","./node_modules/openai/resources/beta/realtime/realtime.d.ts","./node_modules/openai/resources/beta/beta.d.ts","./node_modules/openai/resources/containers/files/content.d.ts","./node_modules/openai/resources/containers/files/files.d.ts","./node_modules/openai/resources/containers/containers.d.ts","./node_modules/openai/resources/graders/grader-models.d.ts","./node_modules/openai/resources/evals/runs/output-items.d.ts","./node_modules/openai/resources/evals/runs/runs.d.ts","./node_modules/openai/resources/evals/evals.d.ts","./node_modules/openai/resources/fine-tuning/methods.d.ts","./node_modules/openai/resources/fine-tuning/alpha/graders.d.ts","./node_modules/openai/resources/fine-tuning/alpha/alpha.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/permissions.d.ts","./node_modules/openai/resources/fine-tuning/checkpoints/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/checkpoints.d.ts","./node_modules/openai/resources/fine-tuning/jobs/jobs.d.ts","./node_modules/openai/resources/fine-tuning/fine-tuning.d.ts","./node_modules/openai/resources/graders/graders.d.ts","./node_modules/openai/resources/uploads/parts.d.ts","./node_modules/openai/resources/uploads/uploads.d.ts","./node_modules/openai/resources/vector-stores/files.d.ts","./node_modules/openai/resources/vector-stores/file-batches.d.ts","./node_modules/openai/resources/vector-stores/vector-stores.d.ts","./node_modules/openai/index.d.ts","./node_modules/openai/resource.d.ts","./node_modules/openai/resources/chat/chat.d.ts","./node_modules/openai/resources/chat/completions/index.d.ts","./node_modules/openai/resources/chat/index.d.ts","./node_modules/openai/resources/index.d.ts","./node_modules/openai/index.d.mts","./src/components/molecules/models/providerlogo.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.tsx","./src/components/routing_groups/routinggroupusagepanel.tsx","./src/components/routing_groups/routinggroupstablecolumns.tsx","./src/components/routing_groups/routinggroupstable.tsx","./src/components/routing_groups/routinggroupmodal.tsx","./src/components/routing_groups/index.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.tsx","./src/app/(dashboard)/cost-optimization/page.tsx","./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarkstab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/cacheleakagecard.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.activity.test.tsx","./src/app/(dashboard)/cost-optimization/_components/costoptimizationview.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcachingtab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/promptcompressiontab.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/shadowevalsection.test.tsx","./src/app/(dashboard)/cost-optimization/_components/tierturnschart.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usagetab.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.integration.test.tsx","./src/app/(dashboard)/cost-optimization/_components/usedailyactivityrange.test.tsx","./src/app/(dashboard)/cost-tracking/page.tsx","./src/app/(dashboard)/cost-tracking/_components/add_margin_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/add_provider_form.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.integration.test.tsx","./src/app/(dashboard)/cost-tracking/_components/cost_tracking_settings.test.tsx","./src/app/(dashboard)/cost-tracking/_components/how_it_works.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_discount_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/provider_margin_table.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/index.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.test.tsx","./src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patterntable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordtable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentcategoryconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.tsx","./src/app/(dashboard)/guardrails/_components/guardrailformfield.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_optional_params.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx","./src/app/(dashboard)/guardrails/_components/llm_judge/llmjudgefields.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtablecolumns.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/categorytable.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.tsx","./src/app/(dashboard)/guardrails/page.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestpanel.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestplayground.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailtestresults.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrailspanel.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.integration.test.tsx","./src/app/(dashboard)/guardrails/_components/teamguardrailstab.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/add_guardrail_form.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_card.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_garden_detail.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.characterization.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_info_helpers.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.test.tsx","./src/app/(dashboard)/guardrails/_components/guardrail_table.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_components.test.tsx","./src/app/(dashboard)/guardrails/_components/pii_configuration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/competitorintentconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterconfiguration.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfilterdisplay.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltermanager.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/contentfiltertables.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/custompatternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/keywordmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/patternmodal.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/tagsinput.test.tsx","./src/app/(dashboard)/guardrails/_components/content_filter/thresholdinput.test.tsx","./src/app/(dashboard)/guardrails/_components/custom_code/customcodemodal.test.tsx","./src/app/(dashboard)/guardrails/_components/tool_permission/toolpermissionruleseditor.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.tsx","./src/components/guardrailsmonitor/calcpopover.tsx","./src/components/guardrailsmonitor/metriccard.tsx","./src/components/guardrailsmonitor/unpricednote.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailusagebreakdown.tsx","./src/components/guardrailsmonitor/logviewer.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.tsx","./src/components/shared/adminonlynotice.tsx","./src/app/(dashboard)/guardrails-monitor/page.tsx","./src/app/(dashboard)/guardrails-monitor/page.integration.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/evaluationsettingsmodal.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailconfig.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardraildetail.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailusagebreakdown.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsmonitorview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/guardrailsoverview.test.tsx","./src/app/(dashboard)/guardrails-monitor/_components/scorechart.test.tsx","./src/app/(dashboard)/hooks/usetabrouting.test.tsx","./src/app/(dashboard)/hooks/common/useresourcelist.test.tsx","./src/components/email_settings.tsx","./src/components/msteamssettings.tsx","./src/components/alerting/dynamic_form.tsx","./src/components/alerting/alerting_settings.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.tsx","./src/components/cloudzerocosttracking/cloudzeroformcontrols.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstablecolumns.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.tsx","./src/components/settings.tsx","./src/app/(dashboard)/logging-and-alerts/page.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystablecolumns.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.tsx","./src/components/deletedkeyspage/deletedkeyspage.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstablecolumns.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.tsx","./src/components/deletedteamspage/deletedteamspage.tsx","./src/components/view_logs/auditlogstablecolumns.tsx","./src/components/view_logs/auditlogstable.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.tsx","./src/components/view_logs/auditlogspanel.tsx","./src/components/view_logs/log_filter_logic.tsx","./src/components/view_logs/logs_utils.tsx","./src/components/view_logs/logstabletoolbar.tsx","./src/components/view_logs/requestlogsfilters.tsx","./src/components/view_logs/typebadges.tsx","./src/components/view_logs/requestlogstablecolumns.tsx","./src/components/view_logs/requestlogstable.tsx","./src/components/view_logs/requestlogspanel.tsx","./src/components/view_logs/index.tsx","./src/app/(dashboard)/logs/page.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpsubmissionstab.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.tsx","./src/app/(dashboard)/mcp-servers/_components/awssigv4fields.tsx","./src/app/(dashboard)/mcp-servers/_components/openapibyokfields.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenendpointauthmethodfield.tsx","./src/app/(dashboard)/mcp-servers/_components/upstreamtokenheaderfield.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.tsx","./src/app/(dashboard)/mcp-servers/_components/dcrbridgetoggle.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.tsx","./src/app/(dashboard)/mcp-servers/_components/tokenexchangeformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/idjagformfields.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx","./src/app/(dashboard)/mcp-servers/_components/stdioconfiguration.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiformsection.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.tsx","./src/hooks/usemcpoauthflow.tsx","./src/hooks/usetestmcpconnection.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.tsx","./src/app/(dashboard)/mcp-servers/_components/importmcpservers.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.tsx","./src/components/settings/adminsettings/mcptoolsearchsettings/mcptoolsearchsettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx","./src/components/mcp_tools/byokcredentialmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx","./src/app/(dashboard)/mcp-servers/_components/toolargumentsform.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.tsx","./src/hooks/usetoolsoauthflow.tsx","./src/hooks/useusermcpoauthflow.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx","./src/app/(dashboard)/mcp-servers/_components/index.tsx","./src/app/(dashboard)/mcp-servers/page.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/createmcpserver.permissions.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/envvarssection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcplogoselector.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpnetworksettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformtestharness.tsx","./src/app/(dashboard)/mcp-servers/_components/mcppermissionmanagement.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpservercard.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpstandardssettings.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsettablecolumns.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcptoolsetstab.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/oauthformfields.test.tsx","./src/app/(dashboard)/mcp-servers/_components/openapiquickpicker.test.tsx","./src/app/(dashboard)/mcp-servers/_components/passthroughauthorizesection.test.tsx","./src/app/(dashboard)/mcp-servers/_components/tooltestpanel.test.tsx","./src/app/(dashboard)/mcp-servers/_components/truepassthroughwarning.test.tsx","./src/app/(dashboard)/mcp-servers/_components/userenvvarsmodal.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcpformstore.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connect.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_connection_status.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_discovery.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.integration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_server_view.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_servers.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.test.tsx","./src/app/(dashboard)/mcp-servers/_components/mcp_tools.test.tsx","./src/app/(dashboard)/mcp-servers/_components/utils.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.tsx","./src/app/(dashboard)/memory/_components/memorytablecolumns.tsx","./src/app/(dashboard)/memory/_components/memorytable.tsx","./src/app/(dashboard)/memory/_components/memoryview.tsx","./src/app/(dashboard)/memory/page.tsx","./src/app/(dashboard)/memory/page.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorydetaildrawer.test.tsx","./src/app/(dashboard)/memory/_components/memoryeditmodal.integration.test.tsx","./src/app/(dashboard)/memory/_components/memorytable.test.tsx","./src/app/(dashboard)/memory/_components/memoryview.test.tsx","./src/components/aihub/agenthubtablecolumns.tsx","./src/components/aihub/forms/makeagentpublicform.tsx","./src/components/aihub/mcphubtablecolumns.tsx","./src/components/aihub/forms/makemcppublicform.tsx","./src/components/model_filters.tsx","./src/components/aihub/forms/makemodelpublicform.tsx","./src/components/aihub/modelhubtablecolumns.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.tsx","./src/components/aihub/usefullinksmanagement.tsx","./src/components/aihub/skillhubtablecolumns.tsx","./src/components/claude_code_plugins/skill_detail.tsx","./src/components/aihub/skillhubdashboard.tsx","./src/components/claude_code_plugins/makeskillpublicform.tsx","./src/components/chat_ui/codesnippets.tsx","./src/components/public_model_hub.tsx","./src/components/aihub/modelhubtable.tsx","./src/app/(dashboard)/model-hub-table/page.tsx","./src/components/molecules/cost_optimization_feedback_banner.tsx","./src/components/add_model/auto_router_connection_test.tsx","./src/components/model_add/reuse_credentials.tsx","./src/components/update_model_credentials_modal.tsx","./src/components/shared/form/utcdatetimeinput.tsx","./src/components/add_model/cache_control_settings.tsx","./src/components/modelinfoeditform.tsx","./src/components/view_model/model_name_display.tsx","./src/components/model_info_view.tsx","./src/components/common_components/user_search_modal.tsx","./src/components/shared/form/labelwithhint.tsx","./src/components/team/guardrailsselect.tsx","./src/components/common_components/metadatakeyvaluefields.tsx","./src/components/guardrailsettingsview.tsx","./src/components/search_tools/searchtoolselector.tsx","./src/components/team/editmembership.tsx","./src/components/team/permission_definitions.tsx","./src/components/team/member_permissions.tsx","./src/components/team/myusertab.tsx","./src/components/common_components/membertable.tsx","./src/components/team/teammembertab.tsx","./src/components/team/teamvirtualkeystable.tsx","./src/components/team/teaminfo.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/allmodelspanel.tsx","./src/components/add_model/handle_add_auto_router_submit.tsx","./src/components/add_model/autorouterroutingtest.tsx","./src/components/add_model/add_auto_router_tab.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstablecolumns.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterstable.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/autorouterstabpanel.tsx","./src/components/add_model/advanced_settings.tsx","./src/components/add_model/conditional_public_model_name.tsx","./src/components/add_model/litellm_model_name.tsx","./src/components/add_model/handle_add_model_submit.tsx","./src/components/add_model/model_connection_test.tsx","./src/components/add_model/provider_specific_fields.tsx","./src/components/add_model/add_model_modes.tsx","./src/components/add_model/addmodelform.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.tsx","./src/components/model_add/credentialmodal.tsx","./src/components/model_add/credentialstablecolumns.tsx","./src/components/model_add/credentialstable.tsx","./src/components/model_add/credentialspanel.tsx","./src/app/(dashboard)/models-and-endpoints/panels/llmcredentialspanel.tsx","./src/components/key_value_input.tsx","./src/components/query_param_input.tsx","./src/components/route_preview.tsx","./src/components/common_components/passthroughsecuritysection.tsx","./src/components/common_components/passthroughguardrailssection.tsx","./src/components/add_pass_through.tsx","./src/components/pass_through_info.tsx","./src/components/passthroughsettings/passthroughendpointstablecolumns.tsx","./src/components/passthroughsettings/passthroughendpointstable.tsx","./src/components/passthroughsettings/passthroughsettings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/passthroughpanel.tsx","./src/components/model_dashboard/healthcheckstablecolumns.tsx","./src/components/model_dashboard/healthcheckstable.tsx","./src/components/model_dashboard/healthcheckcomponent.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelretrysettingspanel.tsx","./src/components/model_group_alias_settings.tsx","./src/app/(dashboard)/models-and-endpoints/panels/modelgroupaliaspanel.tsx","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetmodal.tsx","./src/app/(dashboard)/models-and-endpoints/components/accessgroupbudgetcolumns.tsx","./src/app/(dashboard)/models-and-endpoints/panels/accessgroupbudgetspanel.tsx","./src/components/price_data_reload.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.tsx","./src/app/(dashboard)/models-and-endpoints/panels/pricedatapanel.tsx","./src/app/(dashboard)/models-and-endpoints/page.tsx","./src/app/(dashboard)/models-and-endpoints/page.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/allmodelstable.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/modelretrysettingstab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/pricedatamanagementtab.test.tsx","./src/app/(dashboard)/models-and-endpoints/components/autorouters/autorouterspanel.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/accessgroupbudgetspanel.integration.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/addmodelpanel.integration.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/autorouterstabpanel.test.tsx","./src/app/(dashboard)/models-and-endpoints/panels/healthstatuspanel.test.tsx","./src/components/view_user_spend.tsx","./src/components/usagepage/components/entityusage/topkeyview.tsx","./src/app/(dashboard)/old-usage/_components/usage.tsx","./src/app/(dashboard)/old-usage/page.tsx","./src/app/(dashboard)/old-usage/_components/usage.test.tsx","./src/components/common_components/filters/filterinput.tsx","./src/components/common_components/filters/filtersbutton.tsx","./src/components/common_components/filters/resetfiltersbutton.tsx","./src/app/(dashboard)/organizations/organizationfilters.tsx","./src/app/(dashboard)/organizations/organizationfilters.test.tsx","./src/components/organization/org-settings/orgsettingsform.tsx","./src/components/organization/org-create/orgcreatedialog.tsx","./src/components/organization/organization_view.tsx","./src/app/(dashboard)/organizations/_components/organizationstablecolumns.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.tsx","./src/app/(dashboard)/organizations/page.tsx","./src/app/(dashboard)/organizations/_components/organizationspanel.test.tsx","./src/app/(dashboard)/organizations/_components/organizationstable.test.tsx","./src/components/llm_calls/chat_completion.tsx","./src/app/(dashboard)/playground/components/complianceui/complianceui.tsx","./node_modules/uuid/dist/max.d.ts","./node_modules/uuid/dist/nil.d.ts","./node_modules/uuid/dist/types.d.ts","./node_modules/uuid/dist/parse.d.ts","./node_modules/uuid/dist/stringify.d.ts","./node_modules/uuid/dist/v1.d.ts","./node_modules/uuid/dist/v1tov6.d.ts","./node_modules/uuid/dist/v35.d.ts","./node_modules/uuid/dist/v3.d.ts","./node_modules/uuid/dist/v4.d.ts","./node_modules/uuid/dist/v5.d.ts","./node_modules/uuid/dist/v6.d.ts","./node_modules/uuid/dist/v6tov1.d.ts","./node_modules/uuid/dist/v7.d.ts","./node_modules/uuid/dist/validate.d.ts","./node_modules/uuid/dist/version.d.ts","./node_modules/uuid/dist/index.d.ts","./src/components/mcp_tools/mcptoolargumentsform.tsx","./src/components/tag_management/tagselector.tsx","./src/app/(dashboard)/playground/llm_calls/a2a_send_message.tsx","./node_modules/@anthropic-ai/sdk/internal/builtin-types.d.mts","./node_modules/form-data/index.d.ts","./node_modules/@types/node-fetch/externals.d.ts","./node_modules/@types/node-fetch/index.d.ts","./node_modules/@anthropic-ai/sdk/internal/types.d.mts","./node_modules/@anthropic-ai/sdk/internal/headers.d.mts","./node_modules/@anthropic-ai/sdk/internal/shim-types.d.mts","./node_modules/@anthropic-ai/sdk/core/streaming.d.mts","./node_modules/@anthropic-ai/sdk/internal/request-options.d.mts","./node_modules/@anthropic-ai/sdk/internal/utils/log.d.mts","./node_modules/@anthropic-ai/sdk/resources/shared.d.mts","./node_modules/@anthropic-ai/sdk/core/error.d.mts","./node_modules/@anthropic-ai/sdk/internal/parse.d.mts","./node_modules/@anthropic-ai/sdk/core/api-promise.d.mts","./node_modules/@anthropic-ai/sdk/core/pagination.d.mts","./node_modules/@anthropic-ai/sdk/internal/uploads.d.mts","./node_modules/@anthropic-ai/sdk/internal/to-file.d.mts","./node_modules/@anthropic-ai/sdk/core/uploads.d.mts","./node_modules/@anthropic-ai/sdk/core/resource.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/environments.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/files.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/user-profiles.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/agents.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/line.d.mts","./node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.mts","./node_modules/@anthropic-ai/sdk/error.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages.d.mts","./node_modules/@anthropic-ai/sdk/lib/parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/messagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.mts","./node_modules/@anthropic-ai/sdk/lib/beta-parser.d.mts","./node_modules/@anthropic-ai/sdk/lib/betamessagestream.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/agents/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/events.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/sessions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/resources.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/sessions/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/versions.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/skills.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/skills/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/credentials.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/vaults.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/vaults/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/index.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betarunnabletool.d.mts","./node_modules/@anthropic-ai/sdk/resources.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/compactioncontrol.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/betatoolrunner.d.mts","./node_modules/@anthropic-ai/sdk/lib/tools/toolerror.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.mts","./node_modules/@anthropic-ai/sdk/resources/beta/beta.d.mts","./node_modules/@anthropic-ai/sdk/resources/completions.d.mts","./node_modules/@anthropic-ai/sdk/resources/models.d.mts","./node_modules/@anthropic-ai/sdk/resources/index.d.mts","./node_modules/@anthropic-ai/sdk/client.d.mts","./node_modules/@anthropic-ai/sdk/index.d.mts","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.tsx","./src/app/(dashboard)/playground/llm_calls/image_edits.tsx","./src/app/(dashboard)/playground/llm_calls/image_generation.tsx","./src/components/llm_calls/responses_api.tsx","./src/app/(dashboard)/playground/llm_calls/interactions_api.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpretertool.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.tsx","./src/app/(dashboard)/playground/components/chat_ui/a2ametrics.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.tsx","./src/components/chat_ui/mcpeventsdisplay.tsx","./src/components/chat_ui/reasoningcontent.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageutils.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimagerenderer.tsx","./src/app/(dashboard)/playground/components/chat_ui/searchresultsdisplay.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.tsx","./src/app/(dashboard)/playground/components/chat_ui/responsesimageupload.tsx","./src/app/(dashboard)/playground/components/chat_ui/sessionmanagement.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.tsx","./src/app/(dashboard)/playground/page.tsx","./src/app/(dashboard)/playground/page.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/additionalmodelsettings.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/agentbuilderview.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/audiorenderer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatcomposer.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatimageutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatmessagebubble.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/chatui.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/codeinterpreteroutput.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointselector.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/endpointutils.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/filepreviewcard.test.tsx","./src/app/(dashboard)/playground/components/chat_ui/realtimeplayground.test.tsx","./src/app/(dashboard)/playground/components/compareui/compareui.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/comparisonpanel.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messagedisplay.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/messageinput.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.tsx","./src/app/(dashboard)/playground/components/compareui/components/modelselector.test.tsx","./src/app/(dashboard)/playground/components/compareui/components/unifiedselector.test.tsx","./src/app/(dashboard)/playground/llm_calls/anthropic_messages.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_speech.test.tsx","./src/app/(dashboard)/playground/llm_calls/audio_transcriptions.test.tsx","./src/app/(dashboard)/playground/llm_calls/embeddings_api.test.tsx","./src/app/(dashboard)/policies/_components/policytablecolumns.tsx","./src/app/(dashboard)/policies/_components/policytable.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx","./src/app/(dashboard)/policies/_components/policy_info.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.tsx","./src/app/(dashboard)/policies/_components/impact_popover.tsx","./src/app/(dashboard)/policies/_components/attachmenttablecolumns.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.tsx","./src/app/(dashboard)/policies/_components/tokenselect.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.tsx","./src/app/(dashboard)/policies/_components/policy_templates.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx","./src/app/(dashboard)/policies/_components/index.tsx","./src/app/(dashboard)/policies/page.tsx","./src/app/(dashboard)/policies/_components/attachmenttable.test.tsx","./src/app/(dashboard)/policies/_components/policytable.test.tsx","./src/app/(dashboard)/policies/_components/add_attachment_form.test.tsx","./src/app/(dashboard)/policies/_components/add_policy_form.test.tsx","./src/app/(dashboard)/policies/_components/ai_suggestion_modal.test.tsx","./src/app/(dashboard)/policies/_components/guardrail_selection_modal.test.tsx","./src/app/(dashboard)/policies/_components/impact_popover.test.tsx","./src/app/(dashboard)/policies/_components/impact_preview_alert.test.tsx","./src/app/(dashboard)/policies/_components/index.test.tsx","./src/app/(dashboard)/policies/_components/pipeline_flow_builder.test.tsx","./src/app/(dashboard)/policies/_components/policy_info.test.tsx","./src/app/(dashboard)/policies/_components/policy_templates.test.tsx","./src/app/(dashboard)/policies/_components/policy_test_panel.test.tsx","./src/app/(dashboard)/policies/_components/template_parameter_modal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.tsx","./src/app/(dashboard)/projects/_components/projectkeystablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.tsx","./src/app/(dashboard)/projects/_components/projectstablecolumns.tsx","./src/app/(dashboard)/projects/_components/projectstable.tsx","./src/app/(dashboard)/projects/_components/projectspage.tsx","./src/app/(dashboard)/projects/page.tsx","./src/app/(dashboard)/projects/_components/projectdetailspage.test.tsx","./src/app/(dashboard)/projects/_components/projectkeyssection.test.tsx","./src/app/(dashboard)/projects/_components/projectkeystable.test.tsx","./src/app/(dashboard)/projects/_components/projectspage.test.tsx","./src/app/(dashboard)/projects/_components/projectstable.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/createprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.integration.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/editprojectmodal.test.tsx","./src/app/(dashboard)/projects/_components/projectmodals/projectbaseform.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_utils.tsx","./src/app/(dashboard)/prompts/_components/prompttablecolumns.tsx","./src/app/(dashboard)/prompts/_components/prompttable.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variablewarning.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/dotpromptviewtab.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/index.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view.tsx","./src/app/(dashboard)/prompts/_components/index.tsx","./src/app/(dashboard)/prompts/page.tsx","./src/app/(dashboard)/prompts/_components/prompttable.test.tsx","./src/app/(dashboard)/prompts/_components/add_prompt_form.integration.test.tsx","./src/app/(dashboard)/prompts/_components/index.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_info.test.tsx","./src/app/(dashboard)/prompts/_components/tool_modal.test.tsx","./src/app/(dashboard)/prompts/_components/variable_textarea.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/developermessagecard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/modelconfigcard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptcodesnippets.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/prompteditorheader.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/promptmessagescard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/publishmodal.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/toolscard.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/versionhistorysidepanel.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/emptystate.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagebubble.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messageinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/messagelist.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/variableinput.test.tsx","./src/app/(dashboard)/prompts/_components/prompt_editor_view/conversation_panel/index.test.tsx","./src/app/(dashboard)/router-settings/page.tsx","./src/app/(dashboard)/router-settings/_components/general_settings.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltablecolumns.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.tsx","./src/app/(dashboard)/search-tools/_components/index.tsx","./src/app/(dashboard)/search-tools/page.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/createsearchtools.test.tsx","./src/app/(dashboard)/search-tools/_components/searchconnectiontest.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltable.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtooltester.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtoolview.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.integration.test.tsx","./src/app/(dashboard)/search-tools/_components/searchtools.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.tsx","./src/app/(dashboard)/skills/_components/plugintablecolumns.tsx","./src/app/(dashboard)/skills/_components/plugintable.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.tsx","./src/app/(dashboard)/skills/page.tsx","./src/app/(dashboard)/skills/_components/claudecodepluginspanel.test.tsx","./src/app/(dashboard)/skills/_components/plugintable.test.tsx","./src/app/(dashboard)/skills/_components/add_plugin_form.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.tsx","./src/app/(dashboard)/tag-management/_components/tagtablecolumns.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.tsx","./src/app/(dashboard)/tag-management/_components/index.tsx","./src/app/(dashboard)/tag-management/page.tsx","./src/app/(dashboard)/tag-management/_components/tagtable.test.tsx","./src/app/(dashboard)/tag-management/_components/index.test.tsx","./src/app/(dashboard)/tag-management/_components/tag_info.integration.test.tsx","./src/app/(dashboard)/tag-management/_components/components/createtagmodal.test.tsx","./src/components/team/availableteamstablecolumns.tsx","./src/components/team/availableteamstable.tsx","./src/components/team/availableteamspanel.tsx","./src/components/teamssosettings.tsx","./src/components/teamspage/teamtablecolumns.tsx","./src/components/teamspage/teamstable.tsx","./src/components/teams.tsx","./src/app/(dashboard)/teams/page.tsx","./src/components/toolpolicies/policyselect.tsx","./src/components/tooldetail.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.tsx","./src/components/toolpolicies/toolpoliciestable.tsx","./src/components/toolpolicies/toolpoliciespanel.tsx","./src/components/toolpoliciesview.tsx","./src/app/(dashboard)/tool-policies/page.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.tsx","./src/app/(dashboard)/transform-request/transformrequestpanel.test.tsx","./src/app/(dashboard)/transform-request/page.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.tsx","./src/app/(dashboard)/ui-theme/uithemesettings.test.tsx","./src/app/(dashboard)/ui-theme/page.tsx","./src/components/usagepage/components/keymodelusageview.tsx","./src/components/activity_metrics.tsx","./src/components/cloudzero_export_modal.tsx","./src/components/shared/chart_loader.tsx","./src/components/per_user_usage.tsx","./src/components/user_agent_activity.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.tsx","./src/app/(dashboard)/usage/_components/components/modelviewtoggle.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/teamuserspendcard.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.tsx","./src/app/(dashboard)/usage/page.tsx","./src/app/(dashboard)/usage/_components/components/usageaichatpanel.test.tsx","./src/app/(dashboard)/usage/_components/components/usagepageview.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/endpointusage.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagebarchart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagelinechart.test.tsx","./src/app/(dashboard)/usage/_components/components/endpointusage/components/endpointusagetable.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/entityusage.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/spendbyprovider.test.tsx","./src/app/(dashboard)/usage/_components/components/entityusage/topmodelview.test.tsx","./src/app/(dashboard)/usage/_components/components/usageviewselect/usageviewselect.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.tsx","./src/components/bulk_create_users_button.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.tsx","./src/app/(dashboard)/users/_components/view_users/userstablecolumns.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.tsx","./src/app/(dashboard)/users/_components/view_users.tsx","./src/app/(dashboard)/users/_components/index.tsx","./src/app/(dashboard)/users/page.tsx","./src/app/(dashboard)/users/_components/bulkeditusers.test.tsx","./src/app/(dashboard)/users/_components/user_edit_view.test.tsx","./src/app/(dashboard)/users/_components/view_users.test.tsx","./src/app/(dashboard)/users/_components/default-user-settings/defaultusersettingsform.test.tsx","./src/app/(dashboard)/users/_components/view_users/userstable.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.integration.test.tsx","./src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx","./src/components/vector_store_providers.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx","./src/app/(dashboard)/vector-stores/_components/documentstablecolumns.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.tsx","./src/app/(dashboard)/vector-stores/_components/index.tsx","./src/app/(dashboard)/vector-stores/page.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.characterization.test.tsx","./src/app/(dashboard)/vector-stores/_components/createvectorstore.test.tsx","./src/app/(dashboard)/vector-stores/_components/documentstable.test.tsx","./src/app/(dashboard)/vector-stores/_components/indexestable.test.tsx","./src/app/(dashboard)/vector-stores/_components/s3vectorsconfig.test.tsx","./src/app/(dashboard)/vector-stores/_components/testvectorstoretab.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoreform.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretable.test.tsx","./src/app/(dashboard)/vector-stores/_components/vectorstoretester.test.tsx","./src/app/(dashboard)/vector-stores/_components/index.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.integration.test.tsx","./src/app/(dashboard)/vector-stores/_components/vector_store_info.test.tsx","./src/app/(dashboard)/workflows/workflowruns.tsx","./src/app/(dashboard)/workflows/workflowruns.test.tsx","./src/app/(dashboard)/workflows/page.tsx","./src/app/(dashboard)/workflows/page.integration.test.tsx","./src/app/chat/layout.tsx","./src/app/chat/layout.test.tsx","./src/components/chat/chatmessages.tsx","./src/components/chat/mcpconnectpicker.tsx","./src/app/chat/page.tsx","./src/app/chat/page.integration.test.tsx","./src/components/chat/keyspanel.tsx","./src/app/chat/api-keys/page.tsx","./src/components/chat/mcpcredentialstab.tsx","./src/app/chat/credentials/page.tsx","./src/components/chat/mcpappspanel.tsx","./src/components/chat/connectflowbanner.tsx","./src/components/chat/connectflowsurface.tsx","./src/app/chat/integrations/page.tsx","./src/components/chat/logspanel.tsx","./src/app/chat/logs/page.tsx","./src/components/chat/usagepanel.tsx","./src/app/chat/usage/page.tsx","./src/app/connect/layout.tsx","./src/app/connect/layout.test.tsx","./src/app/connect/page.tsx","./src/app/connect/page.test.tsx","./src/app/login/loginpage.tsx","./src/app/login/loginpage.integration.test.tsx","./src/app/login/loginpage.test.tsx","./src/app/login/page.tsx","./src/app/mcp/oauth/callback/page.tsx","./src/app/model_hub/page.tsx","./src/app/model_hub_table/page.tsx","./src/app/onboarding/onboardingerrorview.tsx","./src/app/onboarding/onboardingerrorview.test.tsx","./src/app/onboarding/onboardingloadingview.tsx","./src/app/onboarding/onboardingformbody.tsx","./src/app/onboarding/onboardingform.tsx","./src/app/onboarding/onboardingform.test.tsx","./src/app/onboarding/onboardingformbody.integration.test.tsx","./src/app/onboarding/onboardingformbody.test.tsx","./src/app/onboarding/onboardingloadingview.test.tsx","./src/app/onboarding/page.tsx","./src/components/betabadge.test.tsx","./src/components/createuserbutton.test.tsx","./src/components/dashboardheader.test.tsx","./src/components/debugwarningbanner.test.tsx","./src/components/deprecationbanner.test.tsx","./src/components/guardrailsettingsview.test.tsx","./src/components/helplink.test.tsx","./src/components/licenseexpirybanner.test.tsx","./src/components/norediswarningbanner.test.tsx","./src/components/scim.test.tsx","./src/components/ssomodals.test.tsx","./src/components/sidebarusagecard.test.tsx","./src/components/teamssosettings.test.tsx","./src/components/teams.test.tsx","./src/components/tooldetail.test.tsx","./src/components/toolpoliciesview.test.tsx","./src/components/uiaccesscontrolform.integration.test.tsx","./src/components/uiaccesscontrolform.unit.test.tsx","./src/components/userbanner.test.tsx","./src/components/activity_metrics.test.tsx","./src/components/add_pass_through.integration.test.tsx","./src/components/bulk_create_users_button.test.tsx","./src/components/cloudzero_export_modal.integration.test.tsx","./src/components/email_settings.test.tsx","./src/components/key_info_utils.test.tsx","./src/components/key_value_input.test.tsx","./src/components/leftnav.test.tsx","./src/components/logging_settings_view.test.tsx","./src/components/model_info_view.test.tsx","./src/components/navbar.test.tsx","./src/components/onboarding_link.test.tsx","./src/components/pass_through_info.integration.test.tsx","./src/components/per_user_usage.test.tsx","./src/components/price_data_reload.test.tsx","./src/components/provider_info_helpers.test.tsx","./src/components/public_model_hub.test.tsx","./src/components/query_param_input.test.tsx","./src/components/route_preview.test.tsx","./src/components/settings.test.tsx","./src/components/update_model_credentials_modal.test.tsx","./src/components/user_agent_activity.test.tsx","./src/components/vector_store_providers.test.tsx","./src/components/aihub/agenthubtablecolumns.test.tsx","./src/components/aihub/mcphubtablecolumns.test.tsx","./src/components/aihub/modelhubtable.test.tsx","./src/components/aihub/modelhubtablecolumns.test.tsx","./src/components/aihub/skillhubtablecolumns.test.tsx","./src/components/aihub/usefullinksmanagement.test.tsx","./src/components/aihub/forms/makeagentpublicform.test.tsx","./src/components/aihub/forms/makemcppublicform.test.tsx","./src/components/aihub/forms/makemodelpublicform.test.tsx","./src/components/cloudzerocosttracking/cloudzerocosttracking.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzerocreatemodal.test.tsx","./src/components/cloudzerocosttracking/cloudzeroemptyplaceholder.test.tsx","./src/components/cloudzerocosttracking/cloudzerointegrationsettings.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.integration.test.tsx","./src/components/cloudzerocosttracking/cloudzeroupdatemodal.test.tsx","./src/components/deletedkeyspage/deletedkeyspage.test.tsx","./src/components/deletedkeyspage/deletedkeystable/deletedkeystable.test.tsx","./src/components/deletedteamspage/deletedteamspage.test.tsx","./src/components/deletedteamspage/deletedteamstable/deletedteamstable.test.tsx","./src/components/entityusageexport/entityusageexportmodal.test.tsx","./src/components/entityusageexport/exportformatselector.test.tsx","./src/components/entityusageexport/exportsummary.test.tsx","./src/components/entityusageexport/exporttypeselector.test.tsx","./src/components/entityusageexport/usageexportheader.test.tsx","./src/components/guardrailsmonitor/metriccard.test.tsx","./src/components/modelselect/modelselect.test.tsx","./src/components/navbar/viewswitcher.test.tsx","./src/components/navbar/blogdropdown/blogdropdown.test.tsx","./src/components/navbar/communityengagementbuttons/communityengagementbuttons.test.tsx","./src/components/navbar/docslink/docslink.test.tsx","./src/components/navbar/notificationsbell/notificationsbell.test.tsx","./src/components/navbar/userdropdown/userdropdown.test.tsx","./src/components/navbar/workerdropdown/workerdropdown.test.tsx","./src/components/passthroughsettings/passthroughendpointstable.test.tsx","./src/components/passthroughsettings/passthroughsettings.test.tsx","./src/components/settings/adminsettings/cyberark/cyberark.test.tsx","./src/components/settings/adminsettings/cyberark/editcyberarkmodal.test.tsx","./src/components/settings/adminsettings/hashicorpvault/edithashicorpvaultmodal.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvault.test.tsx","./src/components/settings/adminsettings/hashicorpvault/hashicorpvaultemptyplaceholder.test.tsx","./src/components/settings/adminsettings/loggingsettings/loggingsettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltersettings.test.tsx","./src/components/settings/adminsettings/mcpsemanticfiltersettings/mcpsemanticfiltertestpanel.test.tsx","./src/components/settings/adminsettings/mcptoolsearchsettings/mcptoolsearchsettings.test.tsx","./src/components/settings/adminsettings/pluginsettings/pluginsettings.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/redactablefield.test.tsx","./src/components/settings/adminsettings/ssosettings/rolemappings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettings.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsemptyplaceholder.test.tsx","./src/components/settings/adminsettings/ssosettings/ssosettingsloadingskeleton.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/addssosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/basessosettingsform.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/deletessosettingsmodal.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.integration.test.tsx","./src/components/settings/adminsettings/ssosettings/modals/editssosettingsmodal.test.tsx","./src/components/settings/adminsettings/uisettings/pagevisibilitysettings.test.tsx","./src/components/settings/adminsettings/uisettings/uisettings.test.tsx","./src/components/settings/adminsettings/userbannersettings/userbannersettings.test.tsx","./src/components/settings/loggingandalerts/loggingcallbacks/loggingcallbackstable.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/addfallbacksmodal.test.tsx","./src/components/settings/routersettings/fallbacks/editfallbacks.test.tsx","./src/components/settings/routersettings/fallbacks/fallbackselectionform.test.tsx","./src/components/settings/routersettings/fallbacks/fallbacks.test.tsx","./src/components/sidebaraccountmenu/sidebaraccountmenu.test.tsx","./src/components/teamspage/teamstable.test.tsx","./src/components/themetoggle/themetoggle.test.tsx","./src/components/toolpolicies/policyselect.test.tsx","./src/components/toolpolicies/toolpoliciespanel.test.tsx","./src/components/toolpolicies/toolpoliciestable.test.tsx","./src/components/toolpolicies/toolpoliciestablecolumns.test.tsx","./src/components/usagepage/components/keymodelusageview.test.tsx","./src/components/usagepage/components/entityusage/topkeyview.test.tsx","./src/components/virtualkeyspage/virtualkeystable.test.tsx","./src/components/add_model/addmodelform.test.tsx","./src/components/add_model/autorouterroutingtest.test.tsx","./src/components/add_model/classifierprompteditor.integration.test.tsx","./src/components/add_model/complexityrouterconfig.test.tsx","./src/components/add_model/heuristicscoringconfig.test.tsx","./src/components/add_model/openingprompteditor.test.tsx","./src/components/add_model/routerconfigbuilder.test.tsx","./src/components/add_model/semantickeywordmatching.test.tsx","./src/components/add_model/stallescalationconfig.test.tsx","./src/components/add_model/tiermodeleffortrows.test.tsx","./src/components/add_model/add_auto_router_tab.test.tsx","./tests/mounted-form-host.tsx","./src/components/add_model/advanced_settings.test.tsx","./src/components/add_model/auto_router_connection_test.test.tsx","./src/components/add_model/cache_control_settings.test.tsx","./src/components/add_model/conditional_public_model_name.test.tsx","./src/components/add_model/handle_add_model_submit.test.tsx","./src/components/add_model/litellm_model_name.test.tsx","./src/components/add_model/model_connection_test.test.tsx","./src/components/add_model/provider_specific_fields.test.tsx","./src/components/agent_management/agentselector.test.tsx","./src/components/alerting/dynamic_form.integration.test.tsx","./src/components/chat/chatshell.test.tsx","./src/components/chat/connectflowbanner.test.tsx","./src/components/chat/connectflowsurface.test.tsx","./src/components/chat/logspanel.test.tsx","./src/components/chat/mcpappspanel.test.tsx","./src/components/chat/mcpconnectpicker.test.tsx","./src/components/chat_ui/codesnippets.test.tsx","./src/components/chat_ui/reasoningcontent.test.tsx","./src/components/chat_ui/responsemetrics.test.tsx","./src/components/common_components/defaultproxyadmintag.test.tsx","./src/components/common_components/deleteresourcemodal.test.tsx","./src/components/common_components/keylifecyclesettings.test.tsx","./src/components/common_components/labeledfield.test.tsx","./src/components/common_components/loadingscreen.test.tsx","./src/components/common_components/metadatakeyvaluefields.test.tsx","./src/components/common_components/modelaliasmanager.test.tsx","./src/components/common_components/modelselector.test.tsx","./src/components/common_components/mountedformfield.test.tsx","./src/components/common_components/newbadge.tsx","./src/components/common_components/newbadge.test.tsx","./src/components/common_components/organizationdropdown.test.tsx","./src/components/common_components/passthroughguardrailssection.test.tsx","./src/components/common_components/premiumloggingsettings.test.tsx","./src/components/common_components/ratelimittypeformitem.test.tsx","./src/components/common_components/routersettingsaccordion.test.tsx","./src/components/common_components/routersettingssummary.test.tsx","./src/components/common_components/userdropdown.test.tsx","./src/components/common_components/routersettingswiring.test.tsx","./src/components/common_components/team_dropdown.test.tsx","./src/components/common_components/team_multi_select.test.tsx","./src/components/common_components/user_search_modal.test.tsx","./src/components/common_components/filters/filterinput.test.tsx","./src/components/common_components/filters/filtersbutton.test.tsx","./src/components/common_components/filters/resetfiltersbutton.test.tsx","./src/components/common_components/iconactionbutton/baseactionbutton.test.tsx","./src/components/common_components/iconactionbutton/tableiconactionbuttons/tableiconactionbutton.test.tsx","./src/components/email_events/email_event_settings.test.tsx","./src/components/guardrails/guardrailselector.test.tsx","./src/components/key_team_helpers/budgetfallbackseditor.test.tsx","./src/components/key_team_helpers/modelmaxbudgeteditor.integration.test.tsx","./src/components/key_team_helpers/tagratelimiteditor.test.tsx","./src/components/key_team_helpers/fetch_available_models_team_key.test.tsx","./src/components/llm_calls/chat_completion.test.tsx","./src/components/llm_calls/fetch_models.test.tsx","./src/components/llm_calls/responses_api.test.tsx","./src/components/mcp_server_management/mcpserverselector.test.tsx","./src/components/mcp_server_management/mcptoolpermissions.test.tsx","./src/components/mcp_tools/byokcredentialmodal.test.tsx","./src/components/mcp_tools/mcptoolargumentsform.test.tsx","./src/components/mcp_tools/types.test.tsx","./src/components/model_add/credentialmodal.test.tsx","./src/components/model_add/credentialspanel.test.tsx","./src/components/model_add/credentialstable.test.tsx","./src/components/model_add/reuse_credentials.test.tsx","./src/components/model_dashboard/healthcheckcomponent.test.tsx","./src/components/model_dashboard/healthcheckstable.test.tsx","./src/components/model_dashboard/modelsettingsmodal/modelsettingsmodal.test.tsx","./src/components/molecules/cost_optimization_feedback_banner.test.tsx","./src/components/molecules/logo/logo.test.tsx","./src/components/molecules/models/providerlogo.test.tsx","./src/components/organisms/regeneratekeymodal.integration.test.tsx","./src/components/organisms/regeneratekeymodal.test.tsx","./src/components/organisms/create_key_button.integration.test.tsx","./src/components/organisms/create_key_button.test.tsx","./src/components/organization/organization_view.test.tsx","./src/components/organization/org-create/orgcreatedialog.test.tsx","./src/components/organization/org-settings/orgsettingsform.test.tsx","./src/components/permissions/agentpermissions.test.tsx","./src/components/permissions/mcpserverpermissions.test.tsx","./src/components/policies/policyselector.test.tsx","./src/components/router_settings/latencybasedconfiguration.test.tsx","./src/components/router_settings/reliabilityretriessection.test.tsx","./src/components/router_settings/routersettingsform.test.tsx","./src/components/router_settings/routingstrategyselector.test.tsx","./src/components/router_settings/tagfilteringtoggle.test.tsx","./src/components/router_settings/index.test.tsx","./src/components/routing_groups/routinggroupmodal.test.tsx","./src/components/routing_groups/routinggroupstable.test.tsx","./src/components/routing_groups/index.integration.test.tsx","./src/components/search_tools/searchtoolselector.test.tsx","./src/components/shared/alert.test.tsx","./src/components/shared/badgelink.test.tsx","./src/components/shared/copybutton.test.tsx","./src/components/shared/createdkeydisplay.test.tsx","./src/components/shared/entitylink.test.tsx","./src/components/shared/inheritedbudgethint.test.tsx","./src/components/shared/meter.test.tsx","./src/components/shared/multiselect.test.tsx","./src/components/shared/pageheader.test.tsx","./src/components/shared/paginatedmultiselect.test.tsx","./src/components/shared/paginatedsearchselect.test.tsx","./src/components/shared/paginationstatusalerts.test.tsx","./src/components/shared/searchselect.test.tsx","./src/components/shared/sidebar.test.tsx","./src/components/shared/toolbarseparator.test.tsx","./src/components/shared/advanced_date_picker.test.tsx","./src/components/shared/chart_loader.test.tsx","./src/components/shared/datatable/datatable.test-d.tsx","./src/components/shared/datatable/datatable.test.tsx","./src/components/shared/datatable/datatablefilterdrawer.test.tsx","./src/components/shared/datatable/datatablepagination.test.tsx","./src/components/shared/datatable/datatablerowselection.test.tsx","./src/components/shared/datatable/datatablesortheader.test.tsx","./src/components/shared/datatable/datatabletoolbar.test.tsx","./src/components/shared/charts/area_chart.test.tsx","./src/components/shared/charts/bar_chart.test.tsx","./src/components/shared/charts/chart_legend.test.tsx","./src/components/shared/charts/chart_tooltip.test.tsx","./src/components/shared/charts/donut_chart.test.tsx","./src/components/shared/charts/line_chart.test.tsx","./src/components/shared/form/formfield.test.tsx","./src/components/shared/table_cells/autoroutertag.test.tsx","./src/components/shared/table_cells/date_cell.test.tsx","./src/components/shared/table_cells/id_cell.test.tsx","./src/components/shared/table_cells/identity_cell.test.tsx","./src/components/shared/table_cells/models_cell.test.tsx","./src/components/shared/table_cells/money_cell.test.tsx","./src/components/shared/table_cells/spend_budget_cell.test.tsx","./src/components/shared/table_cells/status_badge.test.tsx","./src/components/tag_management/tagselector.test.tsx","./src/components/team/availableteamspanel.test.tsx","./src/components/team/editmembership.integration.test.tsx","./src/components/team/editmembership.test.tsx","./src/components/team/loggingsettings.test.tsx","./src/components/team/myusertab.test.tsx","./src/components/team/teaminfo.test.tsx","./src/components/team/teammembertab.test.tsx","./src/components/team/teamvirtualkeystable.test.tsx","./src/components/team/member_permissions.test.tsx","./src/components/team/permission_definitions.test.tsx","./src/components/templates/keyautorouterusagetab.integration.test.tsx","./src/components/templates/keyinfoheader.test.tsx","./src/components/templates/keyinfoview.handlekeyupdate.test.tsx","./src/components/templates/keysavingstab.integration.test.tsx","./src/components/templates/key_edit_view.test.tsx","./src/components/templates/key_info_view.budget_display.test.tsx","./src/components/templates/key_info_view.test.tsx","./src/components/ui/alert-dialog.test.tsx","./src/components/ui/avatar.test.tsx","./src/components/ui/badge.test.tsx","./src/components/ui/breadcrumb.test.tsx","./src/components/ui/button.test.tsx","./src/components/ui/chart.test.tsx","./src/components/ui/field.test.tsx","./src/components/ui/ref-forwarding.test.tsx","./src/components/ui/select.test.tsx","./src/components/ui/tooltip.test.tsx","./src/components/ui/ui-loading-spinner.test.tsx","./src/components/vector_store_management/vectorstoreselector.test.tsx","./src/components/view_logs/auditlogspanel.test.tsx","./src/components/view_logs/auditlogstable.test.tsx","./src/components/view_logs/configinfomessage.test.tsx","./src/components/view_logs/costbreakdownviewer.test.tsx","./src/components/view_logs/requestlogsfilters.test.tsx","./src/components/view_logs/requestlogspanel.test.tsx","./src/components/view_logs/requestlogstablecolumns.test.tsx","./src/components/view_logs/typebadges.test.tsx","./src/components/view_logs/index.integration.test.tsx","./src/components/view_logs/index.test.tsx","./src/components/view_logs/log_filter_logic.test.tsx","./src/components/view_logs/logs_utils.test.tsx","./src/components/view_logs/auditlogdrawer/auditlogdrawer.test.tsx","./src/components/view_logs/guardrailviewer/bedrockguardraildetails.test.tsx","./src/components/view_logs/guardrailviewer/guardrailviewer.test.tsx","./src/components/view_logs/guardrailviewer/presidiodetectedentities.test.tsx","./src/components/view_logs/logdetailsdrawer/classifytag.test.tsx","./src/components/view_logs/logdetailsdrawer/collapsiblemessage.test.tsx","./src/components/view_logs/logdetailsdrawer/drawerheader.test.tsx","./src/components/view_logs/logdetailsdrawer/historytree.test.tsx","./src/components/view_logs/logdetailsdrawer/inputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/jsonviewer.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailcontent.test.tsx","./src/components/view_logs/logdetailsdrawer/logdetailsdrawer.test.tsx","./src/components/view_logs/logdetailsdrawer/outputcard.test.tsx","./src/components/view_logs/logdetailsdrawer/prettymessagesview.test.tsx","./src/components/view_logs/logdetailsdrawer/realtimeprettyview.test.tsx","./src/components/view_logs/logdetailsdrawer/routingdecisioncard.test.tsx","./src/components/view_logs/logdetailsdrawer/sectionheader.test.tsx","./src/components/view_logs/logdetailsdrawer/simplemessageblock.test.tsx","./src/components/view_logs/logdetailsdrawer/simpletoolcallblock.test.tsx","./src/components/view_logs/logdetailsdrawer/tokenflow.test.tsx","./src/components/view_logs/logdetailsdrawer/truncatedvalue.test.tsx","./src/components/view_logs/toolssection/toolssection.test.tsx","./src/contexts/pluginmodecontext.test.tsx","./src/hooks/usemcpoauthflow.test.tsx","./src/hooks/usesyntaxtheme.test.tsx","./src/hooks/usetoolsoauthflow.test.tsx","./src/hooks/policies/usedeletepolicyattachment.test.tsx","./src/lib/forms/usezodform.test.tsx","./tests/createkeypage.expiredtoken.test.tsx","./tests/top_key_view.test.tsx","./.next/dev/types/cache-life.d.ts","./.next/dev/types/validator.ts","./node_modules/@types/d3-array/index.d.ts","./node_modules/@types/d3-color/index.d.ts","./node_modules/@types/d3-ease/index.d.ts","./node_modules/@types/d3-interpolate/index.d.ts","./node_modules/@types/d3-timer/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/debug/index.d.ts","./node_modules/@types/estree-jsx/index.d.ts","./node_modules/@types/json5/index.d.ts","./node_modules/@types/use-sync-external-store/index.d.ts"],"fileIdsList":[[97,143,484,485,486,487],[97,143],[97,143,226,528,531,2703,2811,2852,2862,2894,2908,2919,2923,2930,2947,3050,3062,3105,3147,3172,3192,3239,3275,3298,3373,3387,3400,3528,3570,3594,3631,3652,3662,3675,3684,3696,3703,3706,3709,3728,3748,3768,3784,3786,3790,3793,3795,3799,3801,3803,3804,3806,3811,3812,3813,3814,3824],[97,143,529,530,531],[97,143,3425,3429,3430,3433,3434,3436,3438,3439,3442,3461,3486,3487,3488,3489],[97,143,3429,3437,3490],[97,143,3435],[97,143,3433,3437,3438,3490],[97,143,3490],[97,143,3431,3490],[97,143,3440,3441],[97,143,3436],[97,143,3436,3438,3439,3442,3459,3490],[97,143,3453],[97,143,3433,3439,3490],[97,143,3425,3429,3430,3432],[97,143,176],[97,143,3425],[97,138,143,3428],[97,143,3425,3433,3490],[97,143,3433,3490],[97,143,3485,3490],[97,143,3433,3455,3463,3485,3490],[97,143,3433,3455,3458,3459,3490],[97,143,3461,3490],[97,143,3479],[97,143,3433,3464,3479,3480,3482,3491],[97,143,3481],[97,143,3489],[97,143,3478],[97,143,3433,3438,3439,3443,3448,3486],[97,143,3448,3449],[97,143,3433,3439,3443,3449,3486],[97,143,3443,3444,3445,3446,3447,3449,3452,3469,3473,3476,3485],[97,143,3433,3438,3439,3443,3486],[97,143,3433,3438,3439,3442,3443,3486],[97,143,3444,3445,3446,3447,3465,3466,3467,3471,3474,3477,3486],[97,143,3450,3451,3452],[97,143,3433,3438,3439,3443,3450,3451,3486],[97,143,3433,3438,3439,3443,3450,3486],[97,143,3433,3438,3439,3443,3454,3461,3485,3486],[97,143,3462,3485],[97,143,3432,3433,3438,3443,3461,3462,3463,3464,3483,3484,3485,3486],[97,143,3432,3433,3438,3439,3443,3486],[97,143,3468,3469,3470],[97,143,3433,3438,3439,3443,3469,3486],[97,143,3433,3438,3439,3443,3449,3468,3470,3486],[97,143,3472,3473],[97,143,3433,3438,3439,3442,3443,3472,3486],[97,143,3475,3476],[97,143,3433,3438,3439,3443,3475,3486],[97,143,3432,3433,3438,3443,3461,3486,3487],[97,143,3435,3461,3486,3487,3488],[97,143,3457],[97,143,3433,3435,3438,3439,3443,3454,3461],[97,143,3456,3461],[97,143,3432,3433,3438,3443,3456,3459,3460,3461],[85,97,143,645,650],[97,143,646,650,651,652,653,654],[97,143,646,650,651,652,653],[85,97,143,642,643,645,646,649],[85,97,143,645,646,647,650],[85,97,143,642,643,645],[97,143,704,705],[97,143,708,709,710,711,712,713,714,716,717,718],[97,143,707,708,709,710,711,712,713,714,716,717],[85,97,143,226,643,706,707],[85,97,143,707,715],[97,143,722,723,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,749,751],[97,143,722,723,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,749,750],[85,97,143,645,727,728],[85,97,143,645],[85,97,143,721],[85,97,143],[85,97,143,645,753],[85,97,143,645,647,753],[97,143,753,754,755,756],[97,143,753,754,755],[97,143,758],[85,97,143,642,643,645,727],[97,143,764],[97,143,760,761,762],[97,143,760,761],[85,97,143,645,647,760],[97,143,648,766,767,768],[97,143,648,766,767],[85,97,143,645,647,648],[85,97,143,642,643,645,649],[85,97,143,647,648],[85,97,143,645,648],[85,97,143,645,728],[85,97,143,645,647],[97,143,730,732,733,734,735,736,737,738,739,740,741,742,744,745,746,749,770,771,772,773,774,775,776,777,778,779,781],[97,143,730,732,733,734,735,736,737,738,739,740,741,742,744,745,746,749,750,770,771,772,773,774,775,776,777,778,779,780],[85,97,143,645,727],[85,97,143,645,647,665,728],[85,97,143,699],[85,97,143,642,643,720],[97,143,748],[97,143,783,784,791,792,793,794,795,796,797,798,799,800,801,802,804,807,810,811,812],[97,143,747,783,784,791,792,793,794,795,796,797,798,799,800,801,802,804,807,810,811],[97,143,226,644,790,809],[85,97,143,810],[85,97,143,226],[97,143,814,815],[97,143,814],[97,143,706,709,710,711,712,713,714,715,717,817],[97,143,705,706,709,710,711,712,713,714,715,717],[85,97,143,645,647,665],[85,97,143,226,642,643,703,705],[97,143,704],[85,97,143,647,664,665,673,699,703,706,1029],[85,97,143,645,705],[85,97,143,819],[97,143,820,821],[97,143,819,820],[97,143,823,824,825,826,827,828,830,832,833,834,835,836,837,838,839,840],[97,143,705,823,824,825,826,827,828,830,832,833,834,835,836,837,838,839],[85,97,143,645,647,665,831],[85,97,143,226,642,643,703,705,831],[85,97,143,829,830],[85,97,143,645,829,831],[85,97,143,645,647,727],[97,143,727,842,843,844,845,846,847,848],[97,143,727,842,843,844,845,846,847],[85,97,143,645,726],[85,97,143,647,727],[97,143,850,851,852],[97,143,850,851],[85,97,143,694],[85,97,143,665,672,694],[85,97,143,645,676],[85,97,143,643,647,664,694,703],[85,97,143,672,694],[97,143,694],[85,97,143,687],[97,143,642,694],[97,143,672,694],[97,143,643,673,694],[97,143,683,694],[85,97,143,645,672,683,694],[97,143,682,694],[85,97,143,672,688,694],[97,143,644,664,673,703],[85,97,143,687,694],[97,143,670,672,674,677,678,679,680,684,685,686,689,690,691,692,693,694,695,696,697,698],[97,143,683],[85,97,143,643,670,672,673,674,677,678,679,680,683,684,685,686,689,690,691,692,693,695,699],[97,143,681,703],[85,97,143,642,643,645,724],[97,143,725],[97,143,644,655,719,726,752,757,759,763,765,769,780,782,809,813,816,818,822,841,849,853,855,857,859,866,881,891,896,912,925,932,936,938,946,966,976,980,987,1002,1004,1006,1014,1026,1028],[97,143,854],[85,97,143,645,849],[97,143,642],[85,97,143,725,727],[97,143,641],[85,97,143,644],[85,97,143,645,675],[85,97,143,645,790],[97,143,783,784,790,791,792,793,794,795,796,797,798,799,800,801,802,804,805,806,807,808],[97,143,747,783,784,789,790,791,792,793,794,795,796,797,798,799,800,801,802,804,805,806,807],[85,97,143,226,642,643,703,785,786,787,788,789],[85,97,143,785,790],[97,143,785],[85,97,143,645,647,664,665,672,673,699,703,790,809],[85,97,143,226,790,803],[85,97,143,785],[85,97,143,645,789],[97,143,856],[85,97,143,790],[97,143,858],[97,143,860,861,862,863,864,865],[97,143,860,861,862,863,864],[85,97,143,645,860],[97,143,867,868,869,870,871,872,873,874,875,876,877,878,879,880],[97,143,867,868,869,870,871,872,873,874,875,876,877,878,879],[85,97,143,645,647,728],[85,97,143,645,883],[97,143,883,884,885,886,887,888,889,890],[97,143,883,884,885,886,887,888,889],[85,97,143,642,643,645,727,882],[97,143,893,894,895],[97,143,747,893,894],[85,97,143,645,893],[85,97,143,642,643,645,727,892],[97,143,900,901,902,903,904,905,906,907,908,909,910,911],[97,143,899,900,901,902,903,904,905,906,907,908,909,910],[85,97,143,226,642,643,703,899],[97,143,898],[85,97,143,647,664,665,673,699,703,897,900,912,1029],[85,97,143,645,899],[97,143,915,917,918,919,920,921,922,923,924],[97,143,914,915,917,918,919,920,921,922,923],[85,97,143,916],[85,97,143,226,642,643,703,914],[97,143,913],[85,97,143,647,664,673,699,703,915,1029],[85,97,143,645,914],[97,143,926,927,928,929,930,931],[97,143,926,927,928,929,930],[85,97,143,645,926],[97,143,937],[97,143,933,934,935],[97,143,933,934],[85,97,143,645,647,933],[85,97,143,645,939],[97,143,939,940,941,942,943,944,945],[97,143,939,940,941,942,943,944],[97,143,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965],[97,143,747,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964],[97,143,747],[85,97,143,645,967],[97,143,967,968,969,970,971,973,974,975],[97,143,967,968,969,970,971,973,974],[85,97,143,645,967,972],[97,143,977,978,979],[97,143,977,978],[85,97,143,642,644,645,727],[85,97,143,645,977],[97,143,981,982,983,984,985,986],[97,143,981,982,983,984,985],[85,97,143,645,981,982],[85,97,143,645,982],[85,97,143,645,647,981,982],[85,97,143,642,643,645,981],[97,143,989],[97,143,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001],[97,143,988,989,990,991,992,993,994,995,996,997,998,999,1000],[85,97,143,645,728,989],[85,97,143,990],[85,97,143,645,647,989],[85,97,143,988],[97,143,1005],[97,143,1003],[85,97,143,645,1008],[97,143,1007,1008,1009,1010,1011,1012,1013],[97,143,645,1007,1008,1009,1010,1011,1012],[85,97,143,645,780],[97,143,1017,1018,1019,1020,1021,1022,1023,1024,1025],[97,143,1016,1017,1018,1019,1020,1021,1022,1023,1024],[85,97,143,226,642,643,703,1016],[97,143,1015],[85,97,143,647,664,673,699,703,1017,1026,1029],[85,97,143,645,1016],[85,97,143,643],[97,143,645,1027],[97,143,671,700,701,702],[85,97,143,670],[85,97,143,642,643,647,664,665,701],[97,143,645,647,673,699,700],[85,97,143,666,699],[97,143,656],[97,143,657],[97,143,657,658,660,661,662,663],[97,143,660],[85,97,143,226,660],[97,143,659,660],[97,143,2668],[97,143,666],[97,143,667,668],[85,97,143,669],[97,143,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931],[97,143,2082],[97,143,1082,1301,2081],[97,143,656,2141,2142,2143,2144],[97,143,226],[97,143,1438,1446],[97,143,625],[97,143,1447,1448,1449,1450,1451],[97,143,1446,1448],[97,143,1447,1448],[85,97,143,1445,1446,1447],[85,97,143,226,626],[97,143,627],[97,143,1438,1441],[97,143,1432,1438,1439,1440,1441,1442,1443,1444],[97,143,1438],[85,97,143,1173],[97,143,1434],[97,143,1434,1435,1436,1437],[97,143,1433],[97,143,1154],[97,143,1139,1162],[97,143,1162],[97,143,1162,1173],[97,143,1148,1162,1173],[97,143,1153,1162,1173],[97,143,1143,1162],[97,143,1151,1162,1173],[97,143,1149],[97,143,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172],[97,143,1152],[97,143,1139,1140,1141,1142,1143,1144,1145,1146,1147,1149,1150,1152,1154,1155,1156,1157,1158,1159,1160,1161],[97,143,1385],[97,143,1382,1383,1384,1385,1386,1389,1390,1391,1392,1393,1394,1395,1396],[97,143,1381],[97,143,1388],[97,143,1382,1383,1384],[97,143,1382,1383],[97,143,1385,1386,1388],[97,143,1383],[97,143,2680],[97,143,2679],[85,97,143,196,460,1397,1398],[97,143,1652],[97,143,1639,1640,1641],[97,143,1634,1635,1636],[97,143,1612,1613,1614,1615],[97,143,1578,1652],[97,143,1578],[97,143,1578,1579,1580,1581,1626],[97,143,1616],[97,143,1611,1617,1618,1619,1620,1621,1622,1623,1624,1625],[97,143,1626],[97,143,1577],[97,143,1630,1632,1633,1651,1652],[97,143,1630,1632],[97,143,1627,1630,1652],[97,143,1637,1638,1642,1643,1648],[97,143,1631,1633,1643,1651],[97,143,1650,1651],[97,143,1627,1631,1633,1649,1650],[97,143,1631,1652],[97,143,1629],[97,143,1629,1631,1652],[97,143,1627,1628],[97,143,1644,1645,1646,1647],[97,143,1633,1652],[97,143,1588],[97,143,1582,1589],[97,143,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610],[97,143,1608,1652],[97,143,600,601],[97,143,4158],[97,143,2131],[97,143,2154],[97,143,4162],[97,143,546,547,4164],[97,143,2723],[97,143,157,184,191,3426,3427],[97,140,143],[97,142,143],[143],[97,143,148,176],[97,143,144,149,154,162,173,184],[97,143,144,145,154,162],[92,93,94,97,143],[97,143,146,185],[97,143,147,148,155,163],[97,143,148,173,181],[97,143,149,151,154,162],[97,142,143,150],[97,143,151,152],[97,143,153,154],[97,142,143,154],[97,143,154,155,156,173,184],[97,143,154,155,156,169,173,176],[97,143,151,154,157,162,173,184],[97,143,154,155,157,158,162,173,181,184],[97,143,157,159,173,181,184],[95,96,97,98,99,100,101,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,154,160],[97,143,161,184,189],[97,143,151,154,162,173],[97,143,163],[97,143,164],[97,142,143,165],[97,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190],[97,143,167],[97,143,168],[97,143,154,169,170],[97,143,169,171,185,187],[97,143,154,173,174,176],[97,143,175,176],[97,143,173,174],[97,143,177],[97,140,143,173,178],[97,143,154,179,180],[97,143,179,180],[97,143,148,162,173,181],[97,143,182],[97,143,162,183],[97,143,157,168,184],[97,143,148,185],[97,143,173,186],[97,143,161,187],[97,143,188],[97,138,143],[97,138,143,154,156,165,173,176,184,187,189],[97,143,173,190],[97,143,173,191],[85,89,97,143,192,193,194,195,196,479,524],[85,89,97,143,192,193,194,195,460,479,524],[85,89,97,143,192,193,195,196,479,524],[85,97,143,196,460,461],[85,97,143,196,460],[85,97,143,1369],[85,89,97,143,193,194,195,196,479,524],[85,89,97,143,192,194,195,196,479,524],[83,84,97,143],[97,143,533,538,539,541],[97,143,587,588],[97,143,539,541,581,582,583],[97,143,539],[97,143,539,541,581],[97,143,539,581],[97,143,594],[97,143,534,594,595],[97,143,534,594],[97,143,534,540],[97,143,535],[97,143,534,535,536,538],[97,143,534],[97,143,1030,1032],[97,143,1030],[97,143,2383],[97,143,2381,2383],[97,143,2381],[97,143,2383,2447,2448],[97,143,2383,2450],[97,143,2383,2451],[97,143,2468],[97,143,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,2439,2440,2441,2442,2443,2444,2445,2446,2449,2450,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498,2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513,2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558,2559,2560,2561,2562,2563,2564,2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588,2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2601,2602,2603,2604,2605,2606,2607,2608,2609,2610,2611,2612,2613,2614,2615,2616,2617,2618,2619,2620,2621,2622,2623,2624,2625,2626,2627,2628,2629,2630,2631,2632,2633,2634,2635,2636],[97,143,2383,2544],[97,143,2383,2448,2568],[97,143,2381,2565,2566],[97,143,2567],[97,143,2383,2565],[97,143,2380,2381,2382],[97,143,2058],[97,143,2057],[97,143,2059],[97,143,546,547,2669,2670,4164],[97,143,2671],[97,143,619,620],[97,143,619,620,621,622],[97,143,619,621],[97,143,619],[97,143,157,173,191],[97,143,574,575],[97,143,2767,2770,2773,2775,2776,2777],[97,143,2734,2762,2767,2770,2773,2775,2777],[97,143,2734,2762,2767,2770,2773,2777],[97,143,2800,2801,2805],[97,143,2777,2800,2802,2805],[97,143,2777,2800,2802,2804],[97,143,2734,2762,2777,2800,2802,2803,2805],[97,143,2802,2805,2806],[97,143,2777,2800,2802,2805,2807],[97,143,2724,2734,2735,2736,2760,2761,2762],[97,143,2724,2735,2762],[97,143,2724,2734,2735,2762],[97,143,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759],[97,143,2724,2728,2734,2736,2762],[97,143,2778,2779,2799],[97,143,2734,2762,2800,2802,2805],[97,143,2734,2762],[97,143,2780,2781,2782,2783,2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798],[97,143,2723,2734,2762],[97,143,2767,2768,2769,2773,2777],[97,143,2767,2770,2773,2777],[97,143,2767,2770,2771,2772,2777],[97,143,482],[97,143,430,493,494],[97,143,201,202,204,216,240,355,366,475],[97,143,204,235,236,237,239,475],[97,143,204,372,374,376,377,379,475,477],[97,143,204,238,275,475],[97,143,202,204,215,216,222,228,233,354,355,356,365,475,477],[97,143,475],[97,143,211,217,236,256,351],[97,143,204],[97,143,197,211,217],[97,143,383],[97,143,380,381,383],[97,143,380,382,475],[97,143,157,256,454,472],[97,143,157,327,330,346,351,472],[97,143,157,299,472],[97,143,359],[97,143,358,359,360],[97,143,358],[91,97,143,157,197,204,216,222,228,234,236,240,241,254,255,322,352,353,366,475,479],[97,143,201,204,238,275,372,373,378,475,527],[97,143,238,527],[97,143,201,255,425,475,527],[97,143,527],[97,143,204,238,239,527],[97,143,375,527],[97,143,241,354,357,364],[85,97,143,430],[97,143,168,211,226],[97,143,211,226],[85,97,143,296],[85,97,143,217,226,430],[97,143,211,282,296,297,509,516],[97,143,281,510,511,512,513,515],[97,143,332],[97,143,332,333],[97,143,215,217,284,285],[97,143,217,291,292],[97,143,217,286,294],[97,143,291],[97,143,209,217,284,285,286,287,288,289,290,291,294],[97,143,217,284,291,292,293,295],[97,143,217,285,287,288],[97,143,285,287,290,292],[97,143,514],[97,143,217],[85,97,143,205,503],[85,97,143,184],[85,97,143,238,273],[85,97,143,238,366],[97,143,271,276],[85,97,143,272,481],[97,143,2696],[85,89,97,143,157,192,193,194,195,196,479,523],[97,143,157,217],[97,143,157,216,221,302,319,361,362,366,422,424,475,476],[97,143,254,363],[97,143,479],[97,143,203],[85,97,143,208,211,427,443,445],[97,143,168,211,427,442,443,444,526],[97,143,436,437,438,439,440,441],[97,143,438],[97,143,442],[97,143,226,390,391,393],[85,97,143,217,384,385,386,387,392],[97,143,390,392],[97,143,388],[97,143,389],[85,97,143,226,272,481],[85,97,143,226,480,481],[85,97,143,226,481],[97,143,319,320],[97,143,320],[97,143,157,476,481],[97,143,349],[97,142,143,348],[97,143,211,217,223,225,327,340,344,346,424,427,464,465,472,476],[97,143,217,266,288],[97,143,327,338,341,346],[85,97,143,208,211,327,330,346,349,383,431,432,433,434,435,446,447,448,449,450,451,452,453,527],[97,143,208,211,236,327,334,335,336,339,340],[97,143,173,217,236,338,345,427,428,472],[97,143,342],[97,143,157,168,205,217,221,231,263,264,267,319,322,387,422,423,464,475,476,477,479,527],[97,143,208,209,211],[97,143,327],[97,142,143,236,263,264,321,322,323,324,325,326,476],[97,143,346],[97,142,143,210,211,221,225,261,327,334,335,336,337,338,341,342,343,344,345,465],[97,143,157,261,262,334,476,477],[97,143,236,264,319,322,327,424,476],[97,143,157,475,477],[97,143,157,173,472,476,477],[97,143,157,168,197,211,216,223,225,228,231,238,258,263,264,265,266,267,302,303,305,308,310,313,314,315,316,318,366,422,424,472,475,476,477],[97,143,157,173],[97,143,204,205,206,234,472,473,474,479,481,527],[97,143,201,202,475],[97,143,395],[97,143,157,173,184,213,379,383,384,385,386,387,393,394,527],[97,143,168,184,197,211,213,225,228,264,303,308,318,319,372,399,400,401,408,411,412,422,424,472,475],[97,143,228,234,241,254,264,322,475],[97,143,157,184,205,216,225,264,406,472,475],[97,143,426],[97,143,157,395,409,410,419],[97,143,472,475],[97,143,324,465],[97,143,225,263,366,481],[97,143,157,168,203,308,368,372,401,408,411,414,472],[97,143,157,241,254,372,415],[97,143,204,265,366,417,475,477],[97,143,157,184,387,475],[97,143,157,238,265,366,367,368,377,395,416,418,475],[91,97,143,157,263,421,479,481],[97,143,317,422],[97,143,157,168,211,214,216,217,223,225,231,240,241,254,264,267,303,305,315,318,319,366,399,400,401,402,404,407,422,424,472,481],[97,143,157,173,241,408,413,419,472],[97,143,244,245,246,247,248,249,250,251,252,253],[97,143,258,309],[97,143,311],[97,143,309],[97,143,311,312],[97,143,157,215,216,217,221,222,476],[97,143,157,168,203,205,223,227,263,266,267,301,422,472,477,479,481],[97,143,157,168,184,207,214,215,225,227,264,420,465,471,476],[97,143,334],[97,143,335],[97,143,217,228,464],[97,143,336],[97,143,210],[97,143,212,224],[97,143,157,212,216,223],[97,143,219,224],[97,143,220],[97,143,212,213],[97,143,212,268],[97,143,212],[97,143,214,258,307],[97,143,306],[97,143,211,213,214],[97,143,214,304],[97,143,211,213],[97,143,263,366],[97,143,464],[97,143,157,184,223,225,229,263,366,421,424,427,428,429,455,456,459,463,465,472,476],[97,143,277,280,282,283,296,297],[85,97,143,194,195,196,226,457,458],[85,97,143,194,195,196,226,457,458,462],[97,143,350],[97,143,236,257,262,263,327,328,329,330,331,333,346,347,349,352,421,424,475,477],[97,143,296],[97,143,157,301,472],[97,143,301],[97,143,157,223,269,298,300,302,421,472,479,481],[97,143,277,278,279,280,282,283,296,297,480],[91,97,143,157,168,184,212,213,225,231,263,264,267,366,419,420,422,472,475,476,479],[97,143,208,211,218],[97,143,262,264,396,399],[97,143,262,397,466,467,468,469,470],[97,143,157,258,475],[97,143,157],[97,143,261,346],[97,143,260],[97,143,262,315],[97,143,259,261,475],[97,143,157,207,262,396,397,398,472,475,476],[85,97,143,211,217,295],[85,97,143,209],[97,143,199,200],[85,97,143,205],[85,97,143,211,281],[85,91,97,143,263,267,479,481],[97,143,205,503,504],[85,97,143,276],[85,97,143,168,184,203,270,272,274,275,481],[97,143,211,238,476],[97,143,211,403],[85,97,143,155,157,168,201,203,276,374,479,480],[85,97,143,192,193,194,195,196,479,524],[85,86,87,88,89,97,143],[97,143,148],[97,143,369,370,371],[97,143,369],[85,89,97,143,157,159,168,191,192,193,194,195,196,197,203,231,236,414,442,477,478,481,524],[97,143,489],[97,143,491],[97,143,495],[97,143,2697],[97,143,497],[97,143,499,500,501],[97,143,505],[90,97,143,483,488,490,492,496,498,502,506,508,518,519,521,525,526,527,528],[97,143,507],[97,143,517],[97,143,272],[97,143,520],[97,142,143,262,396,397,399,466,467,469,470,522,524],[97,143,191],[85,97,143,1658],[85,97,143,1657],[97,143,1657,1660],[97,143,2961,2962,2967],[97,143,2963,2964,2966,2968],[97,143,2967],[97,143,2964,2966,2967,2968,2969,2971,2973,2974,2975,2976,2977,2978,2979,2983,2998,3009,3012,3016,3024,3025,3027,3030,3033,3036],[97,143,2967,2974,2987,2991,3000,3002,3003,3004,3031],[97,143,2967,2968,2984,2985,2986,2987,2989,2990],[97,143,2991,2992,2999,3002,3031],[97,143,2967,2968,2973,2992,3004,3031],[97,143,2968,2991,2992,2993,2999,3002,3031],[97,143,2964],[97,143,2970,2991,2998,3004],[97,143,2998],[97,143,2967,2987,2994,2996,2998,3031],[97,143,2991,2998,2999],[97,143,3000,3001,3003],[97,143,3031],[97,143,2980,2981,2982,3032],[97,143,2967,2968,3032],[97,143,2963,2967,2981,2983,3032],[97,143,2967,2981,2983,3032],[97,143,2967,2969,2970,2971,3032],[97,143,2967,2969,2970,2984,2985,2986,2988,2989,3032],[97,143,2989,2990,3005,3008,3032],[97,143,3004,3032],[97,143,2967,2991,2992,2993,2999,3000,3002,3003,3032],[97,143,2970,3006,3007,3008,3032],[97,143,2967,3032],[97,143,2967,2969,2970,2990,3032],[97,143,2963,2967,2969,2970,2984,2985,2986,2988,2989,2990,3032],[97,143,2967,2969,2970,2985,3032],[97,143,2963,2967,2970,2984,2986,2988,2989,2990,3032],[97,143,2970,2973,3032],[97,143,2973],[97,143,2963,2967,2969,2970,2972,2973,2974,3032],[97,143,2972,2973],[97,143,2967,2969,2973,3032],[97,143,3033,3034],[97,143,2963,2967,2973,2974,3032],[97,143,2967,2969,3011,3032],[97,143,2967,2969,3010,3032],[97,143,2967,2969,2970,2998,3013,3015,3032],[97,143,2967,2969,3015,3032],[97,143,2967,2969,2970,2998,3014,3032],[97,143,2967,2968,2969,3032],[97,143,3018,3032],[97,143,2967,3013,3032],[97,143,3020,3032],[97,143,2967,2969,3032],[97,143,3017,3019,3021,3023,3032],[97,143,2967,2969,3017,3022,3032],[97,143,3013,3032],[97,143,2998,3032],[97,143,2970,2971,2974,2975,2976,2977,2978,2979,2983,2998,3009,3012,3016,3024,3025,3027,3030,3035],[97,143,2967,2969,2998,3032],[97,143,2963,2967,2969,2970,2994,2995,2997,2998,3032],[97,143,2967,2976,3026,3032],[97,143,2967,2969,3028,3030,3032],[97,143,2967,2969,3030,3032],[97,143,2967,2969,2970,3028,3029,3032],[97,143,2968],[97,143,2965,2967,2968],[97,143,1337],[97,143,628,1337,1338],[97,143,568],[97,143,566,568],[97,143,557,565,566,567,569,571],[97,143,555],[97,143,558,563,568,571],[97,143,554,571],[97,143,558,559,562,563,564,571],[97,143,558,559,560,562,563,571],[97,143,555,556,557,558,559,563,564,565,567,568,569,571],[97,143,571],[97,143,553,555,556,557,558,559,560,562,563,564,565,566,567,568,569,570],[97,143,553,571],[97,143,558,560,561,563,564,571],[97,143,562,571],[97,143,563,564,568,571],[97,143,556,566],[97,143,1387],[85,97,143,1064],[97,143,1064,1065,1066,1067,1068,1071,1072,1073,1074,1075,1076,1077,1080,1081],[97,143,1064],[97,143,1069,1070],[85,97,143,1061,1064],[97,143,1058,1059,1061],[97,143,1054,1057,1059,1061],[97,143,1058,1061],[85,97,143,1049,1050,1051,1054,1055,1056,1058,1059,1060,1061],[97,143,1051,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063],[97,143,1058],[97,143,1052,1058,1059],[97,143,1052,1053],[97,143,1057,1059,1060],[97,143,1057],[97,143,1049,1054,1057,1059,1060],[85,97,143,1054,1057,1058,1059],[97,143,1078,1079],[85,97,143,2318],[85,97,143,2317],[97,143,2765],[85,97,143,2724,2733,2762,2764],[85,97,143,2169,2170,2217],[97,143,2262,2263],[97,143,2169],[97,143,2217],[85,97,143,2264],[85,97,143,2136,2146,2149,2151,2157,2158,2165,2167,2168,2170,2171,2172,2174,2214,2217],[85,97,143,2157,2217],[85,97,143,2136,2146,2149,2151,2156,2158,2167,2169,2170,2171,2175,2177,2178,2214,2217],[85,97,143,2167,2175,2219],[85,97,143,2150,2217],[85,97,143,2135,2136,2138,2146,2217],[85,97,143,2136,2146,2167,2208,2217],[85,97,143,2136,2176,2197,2201,2217],[85,97,143,2149,2158,2170,2171,2184,2185,2217,2258],[97,143,2135,2217],[97,143,2146,2217],[85,97,143,2136,2146,2149,2151,2157,2158,2170,2171,2196,2214,2217],[85,97,143,2136,2138,2175,2188,2241],[85,97,143,2134,2136,2138,2188],[85,97,143,2136,2138,2166,2188,2189,2217],[85,97,143,2136,2146,2149,2153,2157,2158,2170,2171,2185,2198,2200,2214,2217],[85,97,143,2140,2146,2217],[85,97,143,2140,2146,2214,2217],[85,97,143,2217],[85,97,143,2217,2274],[85,97,143,2175,2185,2217],[85,97,143,2135,2185,2217],[85,97,143,2185,2217],[85,97,143,2147],[85,97,143,2136,2185,2217],[85,97,143,2134,2136,2217],[85,97,143,2135,2136,2137,2217],[85,97,143,2135,2136,2138,2217,2274],[85,97,143,2159,2160,2161],[85,97,143,2146,2148,2149,2160,2185,2217,2220],[97,143,2207,2217],[97,143,2146,2147,2166,2212,2214,2217],[97,143,2134,2135,2136,2138,2139,2140,2146,2147,2149,2157,2158,2159,2162,2166,2168,2169,2170,2171,2172,2173,2175,2176,2185,2188,2190,2196,2197,2198,2200,2201,2202,2209,2212,2213,2214,2217,2218,2219,2221,2222,2223,2224,2225,2226,2227,2228,2230,2232,2234,2235,2236,2237,2238,2239,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,2268,2269,2270,2271,2272,2273],[85,97,143,2136,2149,2151,2158,2170,2171,2180,2182,2184,2199,2217,2233,2274],[85,97,143,2136,2140,2146,2189,2217,2231],[85,97,143,2136,2146],[85,97,143,2136,2140,2146,2189,2217,2229],[85,97,143,2136,2158,2166,2170,2171,2181,2189,2217],[85,97,143,2136,2146,2149,2151,2156,2158,2167,2170,2171,2214,2217,2225,2233,2236],[85,97,143,2156,2217],[85,97,143,2169,2217],[97,143,2141,2145,2217],[97,143,2139,2140,2141,2145,2214,2217],[97,143,2141,2145,2150],[97,143,2141,2145,2184,2202,2217],[97,143,2141,2145,2146,2151,2152,2153,2174,2178,2179,2182,2183,2217],[97,143,2141,2145,2159,2162,2217],[97,143,2141,2145,2185,2217],[97,143,2141,2145,2146],[97,143,2141,2145],[97,143,2141,2142,2145,2146,2188,2190],[97,143,2141,2142,2145,2146,2217],[97,143,2141,2145,2147,2173,2217],[97,143,2165,2184,2207,2217],[97,143,2146,2151,2164,2165,2166,2184,2191,2194,2203,2207,2209,2210,2211,2213,2217],[97,143,2146,2151,2164,2165],[97,143,2207],[97,143,2145,2146,2151,2163,2184,2185,2186,2187,2191,2192,2193,2194,2195,2203,2204,2205,2206],[97,143,2141,2145,2146,2148,2149,2184,2217],[97,143,2151,2164,2173,2184,2217],[97,143,2164,2177,2184],[97,143,2151,2184,2217],[85,97,143,2149,2180,2181,2184,2217],[97,143,2184],[97,143,2164,2184],[97,143,2149,2151,2184,2217],[97,143,2167,2184,2217],[97,143,2185,2217],[85,97,143,2175,2176,2217],[97,143,2149,2156,2163,2165,2166,2185,2214,2217],[85,97,143,2149,2173,2176,2197,2201,2217,2221,2244,2245,2246,2259],[85,97,143,2149,2217,2221,2230,2232,2234,2235,2237],[85,97,143,2217,2237,2274],[97,143,2146,2217,2267],[97,143,2140,2217],[85,97,143,2184,2198,2199,2201,2217],[97,143,2156,2164,2167,2184],[85,97,143,2180,2240],[85,97,143,2133,2134,2135,2138,2139,2140,2146,2147,2148,2151,2169,2173,2180,2214,2215,2216,2274],[97,143,2141],[97,143,2774,2807,2808],[97,143,2809],[97,143,2762,2763],[97,143,2724,2728,2733,2734,2762],[97,143,547,579,580],[97,143,173,191,405],[97,143,537],[97,143,2730],[97,110,114,143,184],[97,110,143,173,184],[97,105,143],[97,107,110,143,181,184],[97,143,162,181],[97,105,143,191],[97,107,110,143,162,184],[97,102,103,106,109,143,154,173,184],[97,110,117,143],[97,102,108,143],[97,110,131,132,143],[97,106,110,143,176,184,191],[97,131,143,191],[97,104,105,143,191],[97,110,143],[97,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,143],[97,110,125,143],[97,110,117,118,143],[97,108,110,118,119,143],[97,109,143],[97,102,105,110,143],[97,110,114,118,119,143],[97,114,143],[97,108,110,113,143,184],[97,102,107,110,117,143],[97,143,173],[97,105,110,131,143,189,191],[97,143,2728,2732],[97,143,2723,2728,2729,2731,2733],[97,143,3405,3406,3407,3408,3409,3410,3411,3413,3414,3415,3416,3417,3418,3419,3420],[97,143,3407],[97,143,3407,3412],[97,143,2725],[97,143,2726,2727],[97,143,2723,2726,2728],[97,143,2132],[97,143,2155],[97,143,591,592],[97,143,591],[97,143,543],[97,143,154,155,157,158,159,162,173,181,184,190,191,543,544,545,547,548,550,551,552,572,573,577,578,579,580],[97,143,543,544,545,549],[97,143,545],[97,143,576],[97,143,547,580],[97,143,542,611,616],[97,143,584,603,604,616],[97,143,534,541,584,596,597,616],[97,143,606],[97,143,585],[97,143,534,542,584,586,596,605,616],[97,143,589],[97,143,146,155,173,534,539,541,580,584,586,589,590,593,596,598,599,602,605,607,608,610,616],[97,143,584,603,604,605,616],[97,143,580,609,610],[97,143,584,586,593,596,598,616],[97,143,189,599],[97,143,146,155,173,534,539,541,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,616],[97,143,585,586],[97,143,146,155,173,189,533,534,539,541,542,580,584,585,586,589,590,593,596,597,598,599,602,603,604,605,606,607,608,609,610,615,616,617,618,623],[97,143,2071,2072],[97,143,2069,2070,2071,2073,2074,2079],[97,143,2070,2071],[97,143,2079],[97,143,2080],[97,143,2071],[97,143,2069,2070,2071,2074,2075,2076,2077,2078],[97,143,2069,2070,2081],[97,143,1301],[97,143,1301,1304],[97,143,1294,1301,1302,1303,1304,1305,1306,1307,1308],[97,143,1309],[97,143,1301,1302],[97,143,1301,1303],[97,143,1247,1249,1250,1251,1252],[97,143,1247,1249,1251,1252],[97,143,1247,1249,1251],[97,143,1247,1249,1250,1252],[97,143,1247,1249,1252],[97,143,1247,1248,1249,1250,1251,1252,1253,1254,1294,1295,1296,1297,1298,1299,1300],[97,143,1249,1252],[97,143,1246,1247,1248,1250,1251,1252],[97,143,1249,1295,1299],[97,143,1249,1250,1251,1252],[97,143,1310],[97,143,1251],[97,143,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293],[97,143,164,226],[85,97,143,226,624,628,1399,1653,2858],[85,97,143,226,628,631,1035,1036,1037,1039,1044,1045,1110,1312,1313,1340,1348,1421,1426,1501,1954,2084,2854],[97,143,226,624,1313],[97,143,226,639,1312],[97,143,226,1311],[97,143,226,624,1399,1421,1422,1653,2857,2863],[85,97,143,226,639,1035,1039,1048,1092,1115,1207,1235,1348,1361,1422,2816,2818,2856],[97,143,226,1036,1037,1039,1044,1045,1082,1311,1348,1426,1501,1954,2854],[97,143,226,624,1421,1653,2856,2863],[85,97,143,226,631,1035,1110,1421,1425,2084,2855],[97,143,226,624,1399,1421,1653,2861,2863],[85,97,143,226,1035,1038,1039,1106,1109,1245,1421,1424,2814,2834,2857,2858,2860],[85,97,143,226,1039,1174,1176,1188,1245,2859],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1245,1362],[97,143,226,1109,2861],[97,143,226,624,1399,1653,2893],[85,97,143,226,631,1035,1036,1039,1044,1092,1109,1110,1178,1214,1311,1348,1954,1956,2084,2868,2869,2870,2872,2880,2882,2883,2886,2889,2890,2891,2892],[97,143,226,1109,1222,2893],[97,143,226,624,1399,2811],[85,97,143,226,624,1214,1399,1653,2863,2901],[85,97,143,226,624,1214,1399,1653,2901],[85,97,143,226,631,1035,1036,1037,1039,1041,1043,1044,1045,1048,1082,1089,1094,1102,1109,1110,1115,1203,1214,1215,1315,1316,1354,1361,1951,1963,1967,1968,2843,2871,2896,2898,2899,2900],[85,97,143,226,624,1214,1399,1653,2863,2899],[85,97,143,226,1035,1036,1037,1039,1048,1094,1101,1115,1183,1214,1316,1361,1452,1956],[85,97,143,226,624,1318,1399,2863,2903],[85,97,143,226,1318],[97,143,226,624,1316],[97,143,226,1214],[85,97,143,226,1035,1036,1037,1039,1044,1045,1082,1094,1315,2896,2897],[85,97,143,226,624,628,1214,1399,1653,2904],[85,97,143,226,624,1214,1318,1399,2904],[85,97,143,226,631,1034,1035,1036,1039,1043,1044,1048,1082,1092,1214,1215,1315,1316,1318,1319,1348,1361,1487,1501,1963,1967,2848,2896,2898,2899,2900,2902,2903],[97,143,226,624,1214,1318,1319],[97,143,226,1214,1318],[85,97,143,226,624,1215,1399,1653,2863,2902],[85,97,143,226,1035,1039,1048,1215],[85,97,143,226,1036,1039,1040,1044,1048,1082,1101],[85,97,143,226,624,1214,1399,1653,2907],[85,97,143,226,631,1035,1039,1106,1214,1215,1318,1347,1956,2901,2904,2906],[97,143,226,624,1318,1399,1653,2906],[85,97,143,226,1038,1039,1048,1094,1174,1176,1188,1318,2662,2905],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1204,1318,1362],[85,97,143,226,1036,1315,2896],[85,97,143,226,1036,1037,1044,1045,1214,1315,2871,2896,2897],[97,143,226,1109,1217,2907],[97,143,226,624,1399,2851],[85,97,143,226,518,1109,1215,1217,1972,2850],[85,97,143,226,1109,2718,2851],[97,143,226,624,1399,1653,2921],[85,97,143,226,1348,1372,2920],[85,97,143,226,1034,1039],[97,143,226,1109,1222,2921,2922],[85,97,143,226,624,1399,1653,2863,2925],[85,97,143,226,631,1035,1036,1039,1044,1045,1101,1110,1311,1321,1455,1954,2084],[85,97,143,226,624,628,630,1399,1653,2929],[85,97,143,226,631,1035,1039,1106,1109,1323,1348,1369,1371,1455,2814,2834,2925,2927,2928],[97,143,226,624,1321],[97,143,226,624,630,1193,1399,1454,1455,1653,2863,2927],[85,97,143,226,630,1036,1039,1042,1183,1188,1430,1454,1455,2926],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1455,1700],[85,97,143,226,624,639,1399,1653,2863,2928],[85,97,143,226,631,1035,1036,1039,1044,1045,1082,1101,1110,1321,1455,1954],[97,143,226,1109,2929],[85,97,143,226,624,1399,2863,2946],[85,97,143,226,631,1035,1039,1040,1092,1214,1348,1456,2006,2282,2821,2936,2940,2944,2945],[85,97,143,226,624,1399,1653,2863,2936],[85,97,143,226,1035,1039,1348,2935],[85,97,143,226,1324,1325,2938],[85,97,143,226,1036,1037,1040,1045,1082,1094,1324,1325,1954,2871],[97,143,226,624,1324,1325],[97,143,226,1324],[97,143,226,624,1399,1653,2940],[85,97,143,226,631,1035,1039,1082,1090,1101,1214,1324,1325,2937,2938,2939],[97,143,226,624,1399,2937],[85,97,143,226,1045],[85,97,143,226,1327,1328,2941],[85,97,143,226,1036,1037,1082,1094,1327,1328,1954,2871],[85,97,143,226,624,1327,1399,1653,2863,2943],[85,97,143,226,1045,1327],[97,143,226,624,1087,1328],[97,143,226,1087,1203,1327],[97,143,226,624,628,631,1214,1399,1653,2944],[97,143,226,624,628,631,1087,1214,1399,1653,2944],[85,97,143,226,631,1035,1082,1203,1327,1328,1361,1476,2942,2943],[85,97,143,226,624,1399,2945],[85,97,143,226,639,1035,1039,1092,2276,2282],[97,143,226,1109,2946],[85,97,143,226,1109,1214,1242],[97,143,226,624,1330],[97,143,226,639],[85,97,143,226,624,628,630,1189,1330,1343,1399,2831],[85,97,143,226,630,1043,1045,1048,1092,1115,1178,1189,1193,1330,1333,1342,1343,1348,2821,2829,2830],[97,143,226,624,1332,1342,1399,3047],[85,97,143,226,1039,1048,1092,1178,1193,1333,1342,1348,2821],[97,143,226,624,1214,1332,1333],[97,143,226,1193,1214,1332],[85,97,143,226,624,628,1399,3049],[85,97,143,226,1039,1342,1348,1414,2814,2831,2957,2958,2959,3048],[97,143,226,624,1335],[97,143,226,624,1399,3048],[85,97,143,226,631,1214,1342,3046,3047],[97,143,226,624,1399,1653,2959],[85,97,143,226,631,1035,1036,1039,1044,1048,1092,1094,1214,1311,1335,1361,1954,2084],[85,97,143,226,624,630,1345,1399,1487,1653,2829,2863],[85,97,143,226,630,1035,1039,1048,1092,1109,1115,1178,1333,1345,2828],[85,97,143,226,1035,1036,1041,1042,1045,1091,1092,1109,1189,1345,1487,1508,1560,2825,2826,2827],[85,97,143,226,624,1189,1330,1399,2830],[85,97,143,226,1092,1132,1134,1136,1189,1330,2282],[97,143,226,624,1214,1332,1399,1653,2958],[85,97,143,226,1092,1214,1333,1342,1348,1414,2282,2821,2823],[97,143,226,624,1340,1343],[97,143,226,1214,1340,1342],[97,143,226,624,1342,1399],[97,143,226,624,1214,1342,1399],[85,97,143,226,1106,1214,1332,1341],[97,143,226,624,1345],[97,143,226,628,631,639,1109,1340],[97,143,226,1109,3049],[85,97,143,226,624,1349,1358,1399,1653,2863],[85,97,143,226,1035,1036,1039,1040,1044,1048,1102,1349,1352,1354],[97,143,226,624,1349,1356,1399,1653,2863],[85,97,143,226,624,1349,1352,1356,1399,1653,2863],[85,97,143,226,1035,1036,1038,1039,1040,1044,1048,1349,1352,1354],[85,97,143,226,624,1378,1399,1653,2863],[85,97,143,226,1035,1039,1090,1094,1101,1110,1347,1348,1349,1355,1356,1357,1358,1367,1368,1373,1375,1376,1377],[85,97,143,226,624,1373,1399,1653,2863],[85,97,143,226,1036,1042,1372],[97,143,226,1349,1355,1356,1357,1358,1373,1374,1375,1376,1378],[85,97,143,226,624,1359,1367,1399,1653,2863],[85,97,143,226,1035,1036,1039,1041,1102,1178,1359,1365,1366],[85,97,143,226,624,1349,1359,1365,1399,1653,2863],[85,97,143,226,1035,1039,1043,1092,1115,1178,1193,1349,1359,1361,1364],[85,97,143,226,624,1359,1363,1364,1653,2863],[85,97,143,226,1035,1039,1359,1362,1363],[97,143,226,624,1349,1359,1363],[97,143,226,1193,1349,1359],[97,143,226,1349],[97,143,226,624,1349,1359,1366,1399],[85,97,143,226,1214,1349,1359],[85,97,143,226,624,1355,1399,1653,2863],[85,97,143,226,1035,1036,1039,1349,1350,1352,1354],[97,143,226,624,1374],[97,143,226,1352],[85,97,143,226,624,1352,1357,1399,1653,2863],[97,143,226,624,631,1214,1377,1399],[85,97,143,226,631,1214],[97,143,226,624,631,1375,1399],[85,97,143,226,631,1214,1349,1352,1374],[97,143,226,624,631,1376,1399],[97,143,226,1109,1379],[97,143,226,624,1399,1653,3136],[85,97,143,226,1035,1037,1039,1041,1090,1110],[97,143,226,624,1399,1653,3150],[85,97,143,226,1035,1036,1037,1039,1042,1045,1094],[97,143,226,624,628,1399,1482,1653,3142],[85,97,143,226,628,1035,1039,1115,1203,1214,1348,1361,1482,2016,3136,3138,3140,3141],[97,143,226,624,1214,1653,1659,2863,3145],[85,97,143,226,1214,1661,2006,2821,3142,3144],[97,143,226,624,1399,1482,1653,3144],[85,97,143,226,1035,1039,1174,1176,1188,1191,1199,1361,1482,2017,2814,3136,3137,3138,3139,3143],[97,143,226,624,1399,1482,1653,3140],[85,97,143,226,1039,1174,1176,1188,1194,1199,1482,2017,3137,3138,3139],[85,97,143,226,624,1399,2863,3143],[85,97,143,226,1092,2282],[97,143,226,624,1399,2863,3147],[97,143,226,1109,1414,3145,3146],[85,97,143,226,624,1214,1399,1653,2863,3090],[85,97,143,226,624,1399,2863,3090],[85,97,143,226,631,1035,1036,1037,1040,1044,1045,1048,1082,1091,1110,1214,1354,1361,1406,3081,3082,3083,3084,3085,3086,3088,3089],[85,97,143,226,1035,1039,1045,1115,1174,1176,1188,1409],[85,97,143,226,624,1214,1399,1653,3081],[85,97,143,226,1044,1045,1092,1094,1214,1697,3080],[85,97,143,226,1035,1039,1040,1045,1092,1101,1115,1174,1176,1188,1214,1409],[97,143,226,624,1653,2863,3082],[85,97,143,226,631,1035,1039,1092,1214,1361,3074,3075,3076,3077,3078,3079,3081],[97,143,226,624,2863,3094],[85,97,143,226,1092,1115,3077,3078,3093],[97,143,226,624,1399,1653,3095],[85,97,143,226,1039,1043,1956,3081,3082,3094],[97,143,226,624,1653,2863,3077,3078,3079,3093],[97,143,226,624,1399,1653,3075],[85,97,143,226,1035,1036,1045,1110,1409],[97,143,226,624,1399,1653,3076],[85,97,143,226,1035,1036,1037,1045,1110,1409],[85,97,143,226,1035,1039,1045,1174,1176,1188,1409],[97,143,226,624,1399,1653,3074],[85,97,143,226,1035,1040,1045,1110,1409],[85,97,143,226,624,1399,1653,1697],[85,97,143,226,1040],[85,97,143,226,624,1399,1653,3080],[85,97,143,226,1036],[97,143,226,624,1214,1399,1410,1653],[85,97,143,226,631,1035,1036,1037,1039,1040,1045,1094,1101,1110,1214,1361],[97,143,226,1410],[97,143,226,624,1399,1407,1653,3102],[85,97,143,226,1038,1039,1407,3100,3101],[97,143,226,624,1399,1407,1653,3100],[85,97,143,226,1039,1354,1407],[97,143,226,624,1407],[97,143,226,1406],[97,143,226,624,1399,1407,3101],[85,97,143,226,1034,1035,1039,1354,1405,1407,3090],[97,143,226,624,1214,1399,1653,2863,3096],[97,143,226,624,1214,1399,1653,3096],[85,97,143,226,631,1035,1036,1037,1039,1043,1044,1045,1048,1082,1092,1115,1193,1214,1348,1354,1406,1410,3083,3084,3085,3088,3089,3095],[97,143,226,624,1406],[97,143,226,530],[85,97,143,226,1035,1036,1045,1091,1937,2871,3083],[85,97,143,226,624,1082,1399,1406,2863,3083,3085],[85,97,143,226,631,1036,1037,1044,1045,1091,1103,1214,1361,1406,1937,2871,3083],[97,143,226,624,1399,1653,2093,3092],[85,97,143,226,1039,1174,1176,1188,2093,3091],[85,97,143,226,1039,1044,1045,1048,1082],[85,97,143,226,624,1214,1406,1659,2863,3104],[85,97,143,226,631,1034,1035,1039,1106,1214,1348,1362,1406,1411,1661,2093,2834,3090,3092,3096,3099,3102,3103],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1354,1362,1406,2093],[97,143,226,624,1399,1653,3098],[85,97,143,226,631,1035,1037,1039,1048,1361,3097],[97,143,226,624,1399,1653,3099],[85,97,143,226,631,1038,1039,1092,1214,1361,1406,2093,3098],[97,143,226,624,1399,1653,3097],[85,97,143,226,631,1035,1039,1092],[85,97,143,226,1035,1036,1038,1039,1040,1044,1045,1082,3083],[97,143,226,624,1399,2093,3087],[85,97,143,226,1035,1039,1040,1045,1048,1115,1183,2093],[97,143,226,624,1399,3088],[85,97,143,226,2093,3087],[97,143,226,624,1109,1214,1399,1653,2863,3103],[97,143,226,624,1109,1214,1399,2863,3103],[85,97,143,226,631,1035,1036,1037,1039,1044,1045,1048,1106,1109,1110,1214,1311,1452,1453,1484,1951,1954,2084,2363],[85,97,143,226,624,1399,1653,3089],[85,97,143,226,1035,1036,1037,1039,1043,1045,1048,1092],[97,143,226,1109,3104],[97,143,226,628,1106,1109,1214,1421],[85,97,143,226,624,628,1109,1214,1399,1421],[97,143,226,628,639,1106,1107,1109,1214],[97,143,226,628,1109,1214,1421],[85,97,143,226,624,628,1214,1318,1399,1426],[97,143,226,628,1106,1107,1109,1214,1318],[97,143,226,628,1107,1137,1214],[97,143,226,628,1107,1214],[97,143,226,628,1214],[97,143,226,624,1430],[97,143,226,1174,1176],[85,97,143,226,628,639,1107,1109,1174,1176,1214,1430,1454],[97,143,226,624,1399,1456],[97,143,226,639,1109,1340],[85,97,143,226,624,628,1399,1458],[85,97,143,226,624,628,1399,1460],[85,97,143,226,624,628,1399,1462],[85,97,143,226,624,628,1399,1464,1465],[97,143,226,628,1107,1214,1464],[97,143,226,624,1107],[85,97,143,226,624,628,1174,1176,1399,1454],[85,97,143,226,628,639,1174,1176,1452,1453],[97,143,226,630,1214],[97,143,226,628,1107,1109,1468],[97,143,226,628,1468,1470],[97,143,226,628,1469,1472],[97,143,226,628,1107,1109,1469],[97,143,226,628,1087,1107,1109,1214],[85,97,143,226,624,628,1214,1399,1477],[97,143,226,628,1107,1109,1214],[97,143,226,624,1399,1479],[97,143,226,639,1106,1109,1340],[85,97,143,226,624,628,1129,1214,1399],[97,143,226,624,1399,1482],[97,143,226,639,1340],[85,97,143,226,624,628,1214,1399,1485],[97,143,226,628,1109,1214,1215,1487],[85,97,143,226,624,628,1215,1399,1487],[97,143,226,628,1107,1109,1214,1215],[97,143,226,628,1109,1214,1487],[85,97,143,226,624,628,1214,1399,1491],[97,143,226,628,1109,1214],[85,97,143,226,624,628,1109,1214,1399,1497],[85,97,143,226,624,628,1109,1214,1399,1499],[85,97,143,226,628,1107,1109,1214],[85,97,143,226,624,628,1109,1214,1399,1501],[97,143,226,628,1086,1107,1109,1214],[97,143,226,628,639,1107,1109,1214],[97,143,226,628,639,1340,1505],[97,143,226,628,639,1106,1107,1109,1340],[85,97,143,226,624,628,1214,1399,1508],[85,97,143,226,624,628,1189,1214,1399],[85,97,143,226,624,628,1214,1399,1511],[97,143,226,628,1107,1108,1214],[85,97,143,226,624,628,1214,1218,1399],[85,97,143,226,624,628,1399,1514,1515],[97,143,226,628,1109,1214,1514],[85,97,143,226,624,628,1399,1514,1517],[85,97,143,226,624,628,1399,1514,1519],[97,143,226,628,1106,1109,1214,1514],[85,97,143,226,624,628,1399,1514],[97,143,226,628,1106,1107,1109,1214],[85,97,143,226,624,628,1399,1514,1522],[85,97,143,226,624,628,1214,1399,1524],[85,97,143,226,624,628,1399,1526],[97,143,226,628,1107,1221],[85,97,143,226,624,628,1399,1528],[97,143,226,628,1107,1109,1214,1531],[97,143,226,624,1399,1533],[97,143,226,624,1399,1535],[97,143,226,1109,1340,1533],[85,97,143,226,624,628,1214,1399,1537],[85,97,143,226,624,628,1214,1399,1539],[85,97,143,226,624,628,1109,1399,1541],[97,143,226,628,1109,1214,1526],[85,97,143,226,624,628,637,1214,1399,1544],[97,143,226,628,637,1107,1109,1214],[97,143,226,624,1546],[97,143,226,628,630,1107,1109,1214],[85,97,143,226,624,628,1214,1215,1216,1217,1399],[97,143,226,628,1106,1107,1109,1214,1215,1216],[85,97,143,226,624,628,1108,1214,1399],[85,97,143,226,624,628,1214,1399,1550,1551],[97,143,226,1550],[85,97,143,226,624,628,1214,1399,1550],[85,97,143,226,624,628,1214,1399,1554],[85,97,143,226,624,628,1109,1399],[85,97,143,226,624,628,634,636,1105,1109,1214,1399],[85,97,143,226,634,636,1105,1106,1108,1214],[97,143,226,1109,1219,1227],[85,97,143,226,1228],[97,143,226,624,1228,1229,1399],[97,143,226,624,1228,1233,1399],[97,143,226,624,1214,1219,1399],[97,143,226,1106,1109,1218],[97,143,226,634,1105,1222],[97,143,226,628,1214,1556],[85,97,143,226,624,628,1214,1399,1558],[85,97,143,226,624,628,1214,1399,1560],[97,143,226,624,1399,1419,1420],[85,97,143,226,518,1419],[85,97,143,226,1109,1215,1216],[97,143,226,624,1214,1399,2700,2811],[85,97,143,226,518,630,1206,1214,1224,2700,2711,2715,2717,2718,2719,2720,2721,2722,2810],[97,143,226,624,1242,1243],[97,143,226,1206],[97,143,226,1109,3171],[97,143,226,1109,3191],[85,97,143,226,1036,1039,1048,1084,1572,2066,2871],[97,143,226,624,633,1214,1399,1653,1654,3218],[97,143,226,624,1214,1399,1653,1654,3218],[85,97,143,226,530,631,633,1035,1036,1039,1045,1048,1082,1084,1086,1101,1106,1110,1214,1361,1562,1564,1565,1572,1574,2066,2871,3197,3198,3201,3202,3204,3205,3206,3207,3208,3209,3210,3211,3213,3214,3215,3216,3217],[97,143,226,624,632,1562],[97,143,226,632,1086],[97,143,226,624,1565],[97,143,226,1086,1564],[85,97,143,226,1039,1048,1084,1086,1094,1572],[97,143,226,1086,1567],[97,143,226,624,1086,1564,1565,1567,1568],[97,143,226,1086,1564,1565],[85,97,143,226,624,1082,1084,1399,1653,3215],[85,97,143,226,1035,1036,1038,1039,1045,1048,1082,1084,1572,1574,2066],[85,97,143,226,1036,1037,1039,1048,1084,1091,1572,2066,2871,3200],[97,143,226,624,1570],[85,97,143,226,631,1035,1037,1110,1214,1570,1956],[97,143,226,3232,3237],[85,97,143,226,624,1399,1653,3220],[85,97,143,226,1035,1039,1042,1092,1094,1193,1214,1348,1956],[85,97,143,226,624,1399,1653,3208],[85,97,143,226,1035,1039,1092,1101,1361,1956],[97,143,226,624,1086,1214,1399,1653,3229],[85,97,143,226,1034,1035,1038,1039,1086,1110,1177,1214,1351,3218],[97,143,226,624,1399,1653,3207],[85,97,143,226,1038,1039,1048,1086,1092,1101,1115],[97,143,226,624,1399,3223],[85,97,143,226,1086],[85,97,143,226,624,1086,1214,1399,3222],[85,97,143,226,624,631,632,1214,1399,1653,1654,3222],[85,97,143,226,631,632,633,1035,1036,1037,1039,1045,1048,1082,1084,1086,1091,1214,1348,1564,1567,1572,1574,1956,2066,2645,2871,3201,3202,3204,3205,3206,3207,3209,3210,3211,3214,3215,3216],[97,143,226,624,1086,1399,1653,3224],[85,97,143,226,632,1035,1039,1086,1092,1115,1193,1348,1564,3222,3223,3238],[85,97,143,226,624,628,1214,1399,1653,3232],[85,97,143,226,628,631,632,1035,1038,1039,1045,1048,1086,1106,1115,1214,1347,1348,1361,1499,1501,2351,3194,3196,3218,3219,3220,3221,3224,3226,3227,3228,3229,3230,3231],[85,97,143,226,624,1399,3209],[85,97,143,226,1034,1035,1036,1037,1038,1039,1092,1115,1183,1361,1564,1965],[97,143,226,624,628,633,1214,1399,3237],[85,97,143,226,628,632,633,1034,1035,1038,1039,1086,1092,1115,1214,1351,1361,2351,2645,3234,3235,3236],[97,143,226,624,1084,1572],[85,97,143,226,1082,1084],[85,97,143,226,624,1082,1084,1399,1574],[97,143,226,1082,1084],[85,97,143,226,1082,1084,1399],[85,97,143,226,624,1399,1653,3214],[85,97,143,226,530,1034,1038,1039,1048,1354],[97,143,226,624,1214,1399,1653,3228],[85,97,143,226,1035,1036,1039,1092,1115,1214,1361,2922],[85,97,143,226,624,1399,1653,3211,3245],[85,97,143,226,1035,1038,1039,1044,1048,1082,1084,1086,1091,1094,1101,1572,1574,1956,2066],[85,97,143,226,624,1086,1205,1399,3221],[85,97,143,226,1034,1035,1039,1048,1086,1115,1354,1362,1564],[97,143,226,624,1086,3193],[97,143,226,1086],[85,97,143,226,631,1039,1086,1214,3193],[85,97,143,226,624,628,1086,1214,1399,1501,1503,1653,3196],[85,97,143,226,628,631,1035,1036,1038,1039,1044,1086,1110,1174,1176,1188,1214,1311,1361,1501,1503,1954,2084,3195],[97,143,226,624,1086,1188,1399,1653,3195],[97,143,226,1034,1035,1039,1086,1174,1176,1188,1193,1204,1214,1362],[97,143,226,624,1575],[97,143,226,1086,1565],[85,97,143,226,624,1399,3201,3245],[85,97,143,226,1035,1036,1037,1039,1045,1048,1084,1086,1091,1572,2066,2871,3199,3200],[85,97,143,226,1036,1039,1048,1082,1084,1091,1094,1572],[85,97,143,226,1036,1039,1048,1084,1086,1572,1574,2066,3212],[97,143,226,624,1214,1399,1653,3212],[85,97,143,226,1034,1214,1361],[85,97,143,226,624,1399,3204,3245],[85,97,143,226,1035,1042,1084,1086,1183,1572,2871,3203],[85,97,143,226,1037,1039,1048,1084,1572,2066],[97,143,226,624,1399,1653],[85,97,143,226,1039,1045,1048,1084,1572],[85,97,143,226,1036,1039,1045,1048,1082,1084,1091,1572,2066,2871,3200],[85,97,143,226,1035,1036,1037,1039,1044,1045,1048,1082,1086,1361,1655,1954],[97,143,226,624,1086,1655],[97,143,226,1082,1086],[85,97,143,226,624,1086,1399,1653,2863,3234],[85,97,143,226,631,1035,1039,1048,1086,1351,1655,3233],[97,143,226,624,1086,1399,3202],[85,97,143,226,1039,1086,1956],[85,97,143,226,1036,1039,1048,1084,1572],[85,97,143,226,624,628,1086,1214,1399,1653,3231],[85,97,143,226,628,631,1035,1039,1044,1086,1110,1115,1203,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,1564],[97,143,226,1109,3238],[85,97,143,226,624,1214,1399,1653,3270],[85,97,143,226,1181,1214],[85,97,143,226,624,1214,1399,1653,3271],[85,97,143,226,1035,1036,1037,1039,1044,1048,1110,1214,1311,1954,2084],[85,97,143,226,624,1174,1176,1214,1399,1653,3273],[85,97,143,226,1039,1174,1176,1188,1214,3272],[97,143,226,1034,1035,1039,1174,1176,1204,1214,1362],[85,97,143,226,624,628,1174,1176,1214,1399,1653,3274],[85,97,143,226,628,631,1035,1039,1174,1176,1214,1452,1453,2834,3270,3271,3273],[97,143,226,624,1399,2863,3275],[97,143,226,1109,1414,2922,3146,3274],[97,143,226,1106,1109,3296,3297],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1505,1700],[85,97,143,226,1035,1039,1044,1048,1110,1311,1505,1507,1665,1700,1937,1954,2084],[97,143,226,624,1665],[97,143,226,1505,1507],[97,143,226,624,628,1109,1399,1653,3323,3325],[85,97,143,226,628,631,1039,1109,1174,1176,1189,1206,1214,1217,1452,1508,1673,2109,2834,3322,3323,3324],[97,143,226,624,1399,1653,2109,3324],[85,97,143,226,1034,1035,1039,1041,1045,1174,1176,1188,2109,2705,3323],[97,143,226,624,1667,1669],[97,143,226,1134,1189,1214,1667,1668],[97,143,226,624,1653,2863,3332],[85,97,143,226,631,1035,1039,1110,1189,1214,1662,1668,1669,2834,3329,3331],[85,97,143,226,1174,1176,1188,1204,1669,3330],[85,97,143,226,1034,1035,1039,1115,1174,1176,1188,1204,1362,1669,1671],[97,143,226,624,1671],[97,143,226,624,1399,1653,3363],[85,97,143,226,1035,1036,1039,1042,1045],[97,143,226,1035,1039,1094,1115,1174,1176,1188,1193,1204,2109,2817,3038,3306],[97,143,226,624,1399,3371],[85,97,143,226,1109,1508,3370],[97,143,226,624,1399,1659,1662],[85,97,143,226,1661],[97,143,226,624,628,1399,1653,3373],[85,97,143,226,628,1035,1039,1106,1109,1217,1230,1348,1550,1662,1664,1668,3299,3307,3321,3326,3333,3342,3347,3358,3362,3364,3366,3369,3372],[85,97,143,226,624,628,1399,1653,3369],[85,97,143,226,631,1039,1106,1109,1174,1176,1188,1505,1506,1507,2834,3367,3368],[97,143,226,624,1214,1653,2863,3342],[85,97,143,226,628,1082,1084,1109,1217,1352,1477,1508,3337,3341],[97,143,226,1662,1664,3324,3325],[97,143,226,624,1399,3333],[97,143,226,1106,1109,1217,1550,1668,3332],[97,143,226,624,1399,1659,3362],[85,97,143,226,1109,1174,1176,1189,1217,1508,1662,1673,3306,3361],[97,143,226,3346],[85,97,143,226,1109,1214,3365],[85,97,143,226,631,1109,1214,1530,1664,3363],[97,143,226,1109,3357],[97,143,226,3371],[85,97,143,226,1189],[97,143,226,624,1673],[85,97,143,226,624,1399,1653,2863,3386],[85,97,143,226,1035,1040,1045,1092,1178,1193,1201,1204,1214,1222,1227,1348,2282,2821,3384,3385],[97,143,226,1109,2922,3386],[85,97,143,226,624,628,1399,1659,3396,3398,3399],[85,97,143,226,628,631,1035,1189,1214,1218,1661,2834,3392,3395,3396,3398],[85,97,143,226,624,1214,1399,1653,3398],[85,97,143,226,1039,1174,1176,1188,1214,3397],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1214,1362],[97,143,226,624,1399,1653,3392],[97,143,226,1039,3389,3390,3391],[97,143,226,1109,3399],[97,143,226,624,1399,2852],[85,97,143,226,518,1105,1214,1243,2700,2718,2851],[85,97,143,226,1035,1039,1048,1101],[97,143,226,624,1399,1653,3500],[85,97,143,226,1034,1036,1039,1048,1183,1237],[85,97,143,226,624,1399,1653,1679,3522],[85,97,143,226,631,1035,1036,1037,1039,1045,1086,1090,1091,1214,1347,1348,1361,1372,1679,2352,3404,3521],[97,143,226,624,1399,1684,3509],[85,97,143,226,1684],[97,143,226,624,1399,3501],[85,97,143,226,1034,1035,1038,1039,1048],[97,143,226,1675],[85,97,143,226,506,1039,1684,3503],[85,97,143,226,631,1035,1039,1048,1677],[97,143,226,624,1684,3503],[97,143,226,1684],[97,143,226,624,1399,1675,1684,3517],[85,97,143,226,1039,1086,1369,1371,1675,1683,1684,1687,2766,3508,3509,3510,3511,3512,3513,3515,3516],[97,143,226,624,1090,1399,1653,2863,3403,3521],[85,97,143,226,631,632,1035,1036,1039,1041,1045,1048,1086,1090,1091,1110,1206,1214,1237,1369,1371,1414,1452,1675,1676,1677,1679,1684,1685,1688,1969,2843,2844,3230,3295,3403,3421,3422,3423,3424,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503,3504,3505,3506,3507,3512,3514,3517,3518,3519,3520],[97,143,226,624,1399,1653,3511],[85,97,143,226,1035,1039,1101,1214,1369,1371],[85,97,143,226,631,1039,1048,1094],[97,143,226,624,1399,1653,1676,3505],[85,97,143,226,1041,1676],[97,143,226,624,1090,1675,3506],[97,143,226,1090,1675],[97,143,226,624,1399,1653,3507],[97,143,226,1035,1039],[97,143,226,624,1399,1653,3520],[85,97,143,226,1035,1036,1039,1045,1214,1676],[85,97,143,226,1039,1684,3514],[85,97,143,226,1035,1039,1101,1684],[85,97,143,226,631,1035,1039,1048,1094,1675],[97,143,226,624,1677],[97,143,226,624,1399,1653,3403,3527],[85,97,143,226,631,1035,1036,1039,1045,1048,1090,1452,1453,1679,1680,1683,1684,3403,3421,3424,3502,3503,3525,3526],[97,143,226,624,1399,1653,1680,3525,3527],[85,97,143,226,1039,1043,1103,1183,1237,1680,1969,2843,3423,3523,3524,3527],[97,143,226,624,1399,1684,3523],[85,97,143,226,1039,1369,1371,1683,1684,2766,3510,3513,3516],[97,143,226,624,1399,1653,3526],[85,97,143,226,1035,1037,1039],[97,143,226,624,1399,1653,3546],[85,97,143,226,1036,1041],[97,143,226,624,1399,1653,1680,3524],[97,143,226,1040,1361,1680],[97,143,226,624,1679,1680],[97,143,226,1679],[85,97,143,226,1039,1214,1414,1689,1990,2349,2844,3403],[97,143,226,624,1399,1685],[85,97,143,226,1083,1086,1452,1683,1684],[85,97,143,226,1687],[97,143,226,1214,1684,3421],[97,143,226,624,1683,3492],[97,143,226,631,1086,1214,1682,1683,1684,2103,3491],[97,143,226,624,3037,3493],[97,143,226,631,1214,1676,3037],[97,143,226,624,3037,3494],[97,143,226,631,1214,3037],[97,143,226,624,3495],[97,143,226,631,1214],[97,143,226,624,1399,3528],[85,97,143,226,1109,1221,1348,2922,3404,3521,3522,3527],[85,97,143,226,624,1214,1399,1653,1689,2863,3563],[85,97,143,226,631,1035,1039,1043,1044,1048,1102,1109,1110,1214,1311,1361,1689,1690,1692,1954,2084,3561,3562],[97,143,226,624,1399,1653,1689,2863,3557],[85,97,143,226,631,1035,1036,1037,1039,1041,1043,1044,1048,1091,1102,1109,1110,1115,1203,1214,1311,1361,1689,1954,1956,2084,2093],[85,97,143,226,624,1399,1653,2863,3568],[85,97,143,226,1035,1036,1037,1039,1041,1048,1092,1110,1183,1214,1361],[85,97,143,226,624,1399,1653,1689,2863,3560],[85,97,143,226,1039,1174,1176,1188,1689,3559],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1362,1689,3558],[97,143,226,624,1690],[97,143,226,1689],[85,97,143,226,624,1399,1653,2863,3566],[85,97,143,226,1035,1039,1043,1110,1115,1183,1406],[85,97,143,226,624,1214,1653,1689,2863,3558],[85,97,143,226,1035,1039,1048,1115,1214,1237,1689],[97,143,226,624,1399,2863,3561],[85,97,143,226,1039,1115,1956],[85,97,143,226,624,1399,1653,2863,3569],[85,97,143,226,631,1035,1039,1106,1214,1348,1689,1956,2093,2354,2834,3554,3555,3556,3557,3560,3563,3564,3565,3566,3567,3568],[85,97,143,226,624,1399,1653,1689,2093,2863,3555],[85,97,143,226,631,1035,1036,1039,1041,1045,1214,1361,1689,2093,2349],[97,143,226,624,1214,1399,1653,1689,2863,3556],[85,97,143,226,1035,1039,1043,1092,1115,1177,1214,1689,1956,3555],[85,97,143,226,624,1214,1399,1653,2863,3565],[85,97,143,226,631,1035,1039,1092,1115,1177,1183,1214],[85,97,143,226,624,1214,1399,1653,2863,3564],[85,97,143,226,1035,1039,1040,1044,1082,1109,1115,1214,1361,1954,1956,3562],[85,97,143,226,624,1399,1653,1689,2863,3554],[85,97,143,226,1039,1174,1176,1188,1689,3553],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1689],[97,143,226,624,1692],[85,97,143,226,624,1399,1653,2863,3567],[85,97,143,226,1035,1036,1039,1041,1102,1110,1115,1214,1361],[97,143,226,1109,3569],[97,143,226,624,1514,1653,2863,3590],[85,97,143,226,1035,1039,1092,1115,1201,1203,1217,1235,1361,1519,2282,2818,3586,3589],[97,143,226,624,2863,3589],[85,97,143,226,1038,1039,1092,1174,1176,1487,3588],[97,143,226,624,1215,1653,2863,3588],[85,97,143,226,1039,1174,1176,1188,1215,3587],[97,143,226,1174,1176,1204,1207,1215,2818],[97,143,226,624,1214,1653,2863,3585],[97,143,226,624,1653,2863,3585],[85,97,143,226,631,1035,1039,1110,1361,1515,1694,1973,1974,2084],[97,143,226,624,1214,1514,1653,2863,3586],[97,143,226,624,1514,1653,2863,3586],[85,97,143,226,631,1035,1039,1110,1361,1514,1522,1694,1973,1974,2084],[85,97,143,226,624,1653,1694,1973,2084,2863],[85,97,143,226,1035,1036,1037,1038,1039,1041,1043,1044,1045,1082,1089,1094,1101,1109,1214,1215,1217,1694,1697,1954,1956,1972],[97,143,226,624,1973,1974],[97,143,226,1973],[97,143,226,624,1514,1653,1659,2863,3593],[85,97,143,226,1035,1038,1039,1217,1514,1661,2814,3585,3590,3592],[97,143,226,624,1514,1653,1659,2863,3592],[85,97,143,226,1039,1174,1176,1188,1514,1661,3591],[97,143,226,1039,1115,1174,1176,1177,1188,1204,1514],[97,143,226,1109,3593],[85,97,143,226,624,631,1214,1399,3610],[85,97,143,226,631,1035,1036,1039,1044,1045,1110,1214,1311,1361,1954,2084],[97,143,226,624,1214,1399,1653,2863,3630],[85,97,143,226,631,1035,1039,1045,1106,1214,1347,3607,3609,3610,3629],[97,143,226,1976,3628],[97,143,226,624,1399,3619],[85,97,143,226,1039],[97,143,226,624,1399,3624],[85,97,143,226,1035,1039,1979,1980,3618,3621,3622,3623],[97,143,226,624,1399,3620],[85,97,143,226,1039,1369,1371,1683,1979,2766],[97,143,226,624,1399,3623],[85,97,143,226,624,1399,3621],[85,97,143,226,1039,1979,3619,3620],[97,143,226,1683],[85,97,143,226,631,1214,1683,1977,1979],[97,143,226,624,1399,3618],[97,143,226,624,1399,3616],[85,97,143,226,1092,3615],[85,97,143,226,1976,1977],[85,97,143,226,631,1214,1976,1977,3611,3612,3613,3614,3616,3617,3624,3625,3626,3627],[97,143,226,624,1399,3613],[85,97,143,226,1035,1036,1039,1110,1933],[97,143,226,624,1399,1653,3608],[85,97,143,226,631,1035,1039,1045,1110,1348,1369,1371],[97,143,226,624,1399,3612],[85,97,143,226,1035,1036,1039,1045,1115,3608],[97,143,226,624,1399,3617],[85,97,143,226,1035,1039,1045,1092,1976,3615],[97,143,226,624,1399,3625],[85,97,143,226,1035,1036,1039,1110],[97,143,226,624,1399,1976,3614],[85,97,143,226,1035,1039,1092,1976],[97,143,226,624,1976,1977],[97,143,226,1976],[97,143,226,624,1110,1214,1653,2863,3627],[85,97,143,226,1035,1039,1115,1177,1214],[85,97,143,226,624,1214,1399,1653,3609],[85,97,143,226,631,1035,1039,1092,1110,1115,1178,1193,1214,1348,3605,3608],[97,143,226,1214,1977],[97,143,226,624,1214,1399,1653,3607],[85,97,143,226,1039,1174,1176,1188,1214,3605,3606],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1214,1352,1362,3605],[97,143,226,624,1399,3611],[85,97,143,226,1035,1110],[97,143,226,624,1399,3615],[85,97,143,226,1035,1036,1037,1039,1115,1237],[97,143,226,1109,2922,3630],[97,143,226,624,1214,1653,2863,3046],[85,97,143,226,1035,1036,1038,1039,1045,1092,1094,1178,1204,1214,1348,2960,3040,3045],[97,143,226,1109,3046],[97,143,226,624,628,1214,1399,1653,3655],[97,143,226,624,1399,3655],[85,97,143,226,530,628,631,1035,1036,1037,1039,1040,1044,1048,1082,1106,1110,1214,1311,1354,1361,1954,1981,1982,2084,2871,3654],[97,143,226,3660],[97,143,226,624,631,1214,1399,1653,3654],[85,97,143,226,631,1035,1039,1043,1214,1361],[97,143,226,624,1982],[97,143,226,1981],[97,143,226,624,628,1106,1214,1399,1653,1981,3660],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1106,1110,1214,1311,1361,1954,1981,1982,2084,2834,2871,3655,3657,3659],[97,143,226,624,1399,1653,1981,2863,3657],[85,97,143,226,1039,1174,1176,1188,1981,3656],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1981],[97,143,226,624,631,1214,1399,1653,3658],[85,97,143,226,631,1035,1036,1039,1092,1214,1361],[97,143,226,624,1193,1399,1653,1981,3659],[85,97,143,226,1035,1039,1092,1193,1981,3658],[97,143,226,1109,3661],[85,97,143,226,624,631,1214,1399,2863,3671],[85,97,143,226,631,640,1035,1036,1037,1039,1040,1044,1048,1110,1214,1311,1361,1954,2064,2084],[97,143,226,624,640,1214,1399,1653,3674],[85,97,143,226,631,640,1035,1106,1214,1347,3292,3671,3673],[97,143,226,624,640,1399,1653,3673],[85,97,143,226,640,1039,1174,1176,1188,3672],[97,143,226,640,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362,2064],[97,143,226,1109,3674],[97,143,226,624,1399,1653,3682],[85,97,143,226,1035,1036,1037,1039,1044,1048,1091,1101,1110,1311,1700,1937,1954,2084],[97,143,226,624,1214,1399,1653,3683],[85,97,143,226,631,637,1035,1039,1214,2834,3679,3681,3682],[97,143,226,624,637,1214,1399,1653,3679],[85,97,143,226,631,637,1035,1036,1037,1039,1044,1048,1089,1091,1092,1101,1115,1193,1214,1311,1700,1937,1954,1972,2084],[97,143,226,624,637,1204,1399,1653,3681],[85,97,143,226,637,1039,1174,1176,1188,3680],[97,143,226,637,1034,1035,1039,1115,1174,1176,1188,1204,1362],[97,143,226,1109,3683],[97,143,226,1109,3695],[97,143,226,1109,3702],[97,143,226,1109,3704],[97,143,226,624,631,1214,1399,1653,3704],[85,97,143,226,631,1035,1037,1039,1092,1214,1361],[97,143,226,1109,3707],[97,143,226,624,631,1399,1653,3707],[85,97,143,226,631,1035,1036,1042,1092,1214,1224,1361],[97,143,226,624,1332,1399,2863,3716],[85,97,143,226,1092,1332,2282],[97,143,226,624,1332,1399,2863,3717],[97,143,226,624,2863,3718],[85,97,143,226,1174,1176,1188,1201,1204,1332],[97,143,226,624,1399,3719],[85,97,143,226,1332,3716,3717,3718],[85,97,143,226,624,1214,1399,1560,1653,2815,3723],[85,97,143,226,1039,1048,1092,1174,1176,1188,1193,1204,1214,1227,1332,1341,1348,1354,1987,1988,2006,2007,2014,2038,2282,2815,2826,2827,2957,3385,3711,3719,3720,3721,3722],[97,143,226,1332,1986],[97,143,226,624,1988],[97,143,226,1193],[97,143,226,624,1399,3724],[85,97,143,226,1039,1048,1092,1094,1174,1176,1188,1193,1204,2282,3038,3713],[97,143,226,624,1214,1991],[97,143,226,1214,1990],[85,97,143,226,628,1035,1039,1092,1174,1176,1188,1204,1214,1991],[97,143,226,624,1399,1653,3721],[85,97,143,226,1188,1193,1204,1348,2282],[97,143,226,624,1984],[97,143,226,624,1399,2863,3725],[85,97,143,226,1035,1037,1040,1214,1361,2766],[85,97,143,226,624,1109,1214,1219,1399,1426,1479,1558,1560,1653,2863,3727],[85,97,143,226,637,1035,1039,1048,1092,1106,1109,1193,1214,1215,1219,1227,1332,1341,1348,1426,1479,1558,1956,1984,1986,2006,2014,2038,2282,2821,2827,2957,3384,3385,3711,3712,3713,3715,3719,3720,3721,3723,3724,3725,3726],[97,143,226,624,1399,1653,2863,3726],[85,97,143,226,1039,1045,1106,1115,1227],[97,143,226,624,1332,1341,1399],[85,97,143,226,1332],[97,143,226,1109,1217,1218,3727],[97,143,226,624,631,1214,1653,2863,3740],[85,97,143,226,631,1043,1091,1092,1109,1110,1178,1183,1204,1214,1937,3739],[85,97,143,226,624,628,631,1399,1653,1995,2854,3742],[85,97,143,226,628,631,1035,1036,1041,1044,1045,1082,1092,1177,1217,1340,1696,1954,1994,1995,2084,2854],[97,143,226,624,1994,1995],[97,143,226,639,1311,1994],[97,143,226,624,1994],[97,143,226,3746],[97,143,226,624,1214,1399,1653,2863,3739],[85,97,143,226,1035,1036,1037,1039,1044,1045,1046,1047,1048,1089,1091,1106,1183,1311,1700,1954,1963,1967,2084,2097,2101],[85,97,143,226,624,628,1399,1653,2863,3746],[85,97,143,226,628,631,1035,1106,1174,1176,1177,1214,1348,1452,1453,1661,1958,1959,2834,3740,3741,3742,3744,3745],[97,143,226,624,1399,1653,3745],[97,143,226,624,1399,1653,2106,3745],[85,97,143,226,631,1035,1039,1040,1044,1045,1048,1092,1106,1109,1110,1178,1193,1207,1214,1348,1501,1503,1700,1958,2106,2816,2834,2838,3739],[85,97,143,226,624,1174,1176,1214,1399,1653,3744],[85,97,143,226,1036,1039,1041,1174,1176,1188,1214,3743],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1193,1204,1214,1362],[97,143,226,1109,1217,3747],[97,143,226,624,1090,1214,1399,1653,3765],[97,143,226,624,1214,1399,1653,3765],[85,97,143,226,631,1035,1036,1037,1039,1044,1045,1048,1092,1138,1214,1354,1361,1956,3421,3756,3763,3764],[97,143,226,624,1138,1399,1653,3763],[85,97,143,226,1039,1138,1188,3762],[97,143,226,1034,1035,1039,1138,1174,1176,1193,1204,1362],[97,143,226,624,1214,1399,1653,3767],[85,97,143,226,631,1035,1039,1106,1138,1210,1214,1348,2352,2834,3758,3759,3761,3765,3766],[85,97,143,226,631,1138,1209,1214],[97,143,226,624,1209,1210,1399,1653],[85,97,143,226,1039,1174,1176,1188,1208,1210],[97,143,226,1174,1176,1188,1204,1207,1210],[97,143,226,624,1090,1399,3764],[85,97,143,226,1036,1039,1040,1044,1048,1090,1956],[97,143,226,624,1138,1399,1653,3766],[85,97,143,226,1040,1092,1138,3760],[97,143,226,624,631,1214,1399,1653,3761],[85,97,143,226,624,1214,1399,1653,3761],[85,97,143,226,631,1035,1036,1037,1039,1040,1044,1045,1048,1092,1115,1138,1214,1311,1348,1352,1354,1954,2084,3756,3760],[97,143,226,624,631,1214,1399,1653,3759],[97,143,226,624,1214,1352,1399,1653,3756,3759],[85,97,143,226,631,1035,1036,1037,1038,1039,1040,1044,1045,1048,1082,1090,1110,1214,1311,1354,1954,1956,2084,3756],[97,143,226,624,1138,1399,1653,3758],[85,97,143,226,1039,1138,1174,1176,1188,3757],[97,143,226,1034,1035,1039,1138,1174,1176,1188,1193,1204,1362,3756],[97,143,226,624,631,1214,1399,1653,3760],[85,97,143,226,631,1035,1037,1039,1043,1092,1214,1361],[97,143,226,1109,3767],[97,143,226,624,1399,2863,3784],[97,143,226,1109,1414,2922,3146,3782],[97,143,226,624,1399,1653,3782],[85,97,143,226,1034,1035,1036,1039,1045,1048,1101,1174,1176,1181,1188,1214,1361],[97,143,226,2056,3792],[97,143,226,2056,3794],[85,97,143,226,2056,3798],[97,143,226,624,1399,3786],[85,97,143,226,518,1109,1206,1224,1550,2056,2061,2717],[97,143,226,2056,3800],[85,97,143,226,624,1399,2055,3790],[85,97,143,226,518,631,1035,1036,1039,1090,1177,1225,1237,1352,1683,2054,2056,2061,3498,3788,3789],[97,143,226,2056,3802],[97,143,226,624,1399,3804],[97,143,226,1109,1224,2717],[97,143,226,624,1399,3806],[85,97,143,226,1109,3798],[97,143,226,526,529,1370,2698,2699,2700,2701,2702],[97,143,226,624,628,1108,1214,1399,1653,3808],[97,143,226,624,628,634,636,1108,1214,1399,3808],[85,97,143,226,518,634,636,1035,1036,1039,1044,1045,1048,1092,1105,1108,1214,1311,1361,1494,1954,1956,2084,2353,2718,2871],[97,143,226,3808],[85,97,143,226,518,632],[85,97,143,226,518,3296],[85,97,143,226,518,3297],[85,97,143,226,624,1399,3815],[85,97,143,226,1035,1039,1105,1956],[85,97,143,226,624,1399,3819],[85,97,143,226,518,634,635,1214,1511,3815,3817,3818],[97,143,226,624,1399,1653,3818],[85,97,143,226,624,1399,1653,3818],[85,97,143,226,1034,1035,1036,1039,1044,1092,1311,1361,1954,1956,2084,2871],[85,97,143,226,624,1399,3817],[85,97,143,226,1361],[85,97,143,226,518,3819],[85,97,143,226,624,1193,1215,1332,1399,3711],[85,97,143,226,1039,1092,1101,1193,1215,1332,1986,2005,2038,2282,3710],[85,97,143,226,1036,1042,1092,1094,1102,1103,1132],[97,143,226,624,631,1090,1136,1137,1189,1214,1653,2356,2695,2863,3327,3329],[85,97,143,226,628,631,1035,1036,1039,1044,1045,1048,1082,1090,1092,1106,1110,1127,1128,1130,1132,1133,1134,1135,1136,1137,1189,1214,1311,1361,1428,1668,1951,1954,2040,2045,2084,2085,3300,3309,3327,3328],[97,143,226,624,1082,1084,1109,1214,1215,1352,1653,2863,3341],[85,97,143,226,1035,1039,1041,1044,1045,1048,1082,1084,1092,1094,1106,1109,1110,1129,1214,1215,1352,1524,1544,1668,1951,1956,2066,2085,3038,3309,3334,3335,3336,3338,3339,3340],[97,143,226,624,1399,3334,3953],[85,97,143,226,637,1036,1037,1039,1045,1048,1083,1084,1091,1094,1101,1215,1551,1969,2059,2066,2656,3303,3304,3309],[85,97,143,226,1036,1094,1132],[97,143,226,624,1214,2045,2863,3300],[85,97,143,226,1039,1214,2045],[97,143,226,624,1137,1189,2040],[97,143,226,1090,1132,1137,1189],[97,143,226,624,1136,1214,1653,2695,2863,3328],[85,97,143,226,1035,1037,1039,1115,1136,1211,1214,2043],[97,143,226,1136,2043],[97,143,226,1136,1214],[97,143,226,2045],[97,143,226,1133,1136],[97,143,226,1090,1093,1132,1133,1134,1135],[97,143,226,1130],[97,143,226,624,1399,1653,3304],[85,97,143,226,1035,1039,1042,1045,1048,1937],[85,97,143,226,1036,1039,1041,1042,1045,1048,1091,1092,1094,1097,1102,1112,1113,1114,1117,1118,1119,1120,1132,1134],[85,97,143,226,1036,1042,1094,1132],[97,143,226,624,1112,1132,1653,2695,2863],[85,97,143,226,631,1035,1037,1039,1109,1110,1111,1132,1214],[97,143,226,1111],[97,143,226,1039,1045,1048,1134],[85,97,143,226,1036,1042,1094,1136],[97,143,226,624,1093],[97,143,226,1135],[97,143,226,624,1132,1133,1134],[97,143,226,1090,1133,1135],[85,97,143,226,624,1132,1653,2695,2863],[85,97,143,226,1035,1036,1037,1038,1039,1041,1043,1048,1090,1091,1092,1094,1095,1096,1097,1098,1099,1100,1101,1104,1116,1121,1122,1123,1124,1125,1126,1127,1128,1130,1131,1133,1134,1135],[85,97,143,226,1039,1041,1042,1048,1102,1129,1130],[85,97,143,226,624,1082,1084,1399,1653,3335,3953],[85,97,143,226,1036,1048,1082,1084,1174,1176,1188,1352,2066],[85,97,143,226,1039,1048,1091],[97,143,226,631,1130,1136,1214],[97,143,226,624,3337],[97,143,226,631,1214,1352,2654],[97,143,226,624,1116,1132],[97,143,226,624,1114,1116,1117,1121,1132,1653,2695,2863],[85,97,143,226,1035,1036,1039,1042,1101,1103,1114,1115,1116,1132],[85,97,143,226,1035,1039,1045,1048,1091,1092,1093,1134],[97,143,226,624,1352,1399,3336,3953],[85,97,143,226,1036,1082,1084,1091,1352,2066,3309],[85,97,143,226,1094,1132],[97,143,226,624,631,1214,1399,3337,3338],[85,97,143,226,631,1035,1039,1043,1214,3337],[97,143,226,624,1113,1653,2863],[85,97,143,226,1035,1037,1045,1109,1110,1132,1133,1214],[97,143,226,624,628,1082,1084,1352,1399,3339,3953],[85,97,143,226,1035,1036,1037,1039,1045,1082,1084,1214,1352,1524,2066,2871,3309],[97,143,226,624,1399,1653,2087],[85,97,143,226,1035,1036,1037,1039,1041,1042,1043,1048,1090,1092,1101,1115],[97,143,226,624,1128,1653,2863],[85,97,143,226,1036,1039,1041,1048,1090,1094],[97,143,226,624,1124,1132,2863],[97,143,226,624,1133],[97,143,226,1132,1134,1135],[97,143,226,624,1125,1132],[97,143,226,1132,1133,1134,1135],[85,97,143,226,1097,1132],[97,143,226,624,1126],[85,97,143,226,1039,1045,1048,1134],[85,97,143,226,1133],[97,143,226,624,1214,1653,2863,3353],[85,97,143,226,631,1035,1036,1039,1045,1048,1082,1092,1094,1110,1214,1311,1361,1937,1954,1956,2084,3348,3349,3350,3351,3352,3357],[97,143,226,624,1399,1653,1698],[85,97,143,226,1091,1214],[97,143,226,624,1188,1399,1653,3281],[97,143,226,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362],[97,143,226,624,1214,1399,3281,3282],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3281],[97,143,226,624,1214,1399,3283,3284],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3283],[97,143,226,624,1214,1399,3286],[85,97,143,226,631,1034,1035,1039,1110,1115,1183,1214,3285],[97,143,226,624,1188,1399,1653,3283],[97,143,226,624,1214,1653,2863,3297],[85,97,143,226,634,636,640,1035,1038,1039,1092,1105,1106,1110,1115,1174,1176,1188,1193,1214,1348,1369,1371,1550,2662,3281,3282,3283,3284,3285,3286,3287,3290,3293,3294,3296],[97,143,226,624,1188,1399,1653,3287],[85,97,143,226,640,1038,1039,1045,1174,1176,1188,3291,3292],[97,143,226,624,640,1188,1399,1653,3291],[97,143,226,640,1034,1035,1039,1115,1174,1176,1188,1193,1204,1362],[97,143,226,624,631,1214,1399,1653,3290],[85,97,143,226,508,631,1092,1106,1178,1214,1932,3289],[85,97,143,226,630,631,1214,3161],[85,97,143,226,624,1399,1653,3161],[85,97,143,226,1035,1036,1039,1082,1094,1115,1178],[97,143,226,624,1229,1230,1399],[97,143,226,1115,1229],[97,143,226,624,1399,1653,3741],[85,97,143,226,631,1035,1039,1110,1178,1214,1932,1957,1990],[85,97,143,226,1035,1039,1048,1101,1369,1371,1683,2054,2766,2809,3512,3513],[97,143,226,624,1205,2061],[97,143,226,624,1399,2061],[85,97,143,226,518,1035,1039,1043,1206,2056,2060],[97,143,226,624,1214,1399,3797],[85,97,143,226,1039,1214,3796],[97,143,226,624,628,1214,1399,3796,3798],[85,97,143,226,518,628,1214,3796,3797],[85,97,143,226,1035,1036,1039,1048,1110,1225,1347,2054,2059],[85,97,143,226,628,631,1035,1036,1039,1042,1110,1115,1177,1178,1214,1215,1957,2638],[97,143,226,624,1214,1399,2863,3800],[85,97,143,226,628,1035,1039,1110,1177,1178,1214,2303],[85,97,143,226,624,628,1086,1205,1214,1399,3796],[85,97,143,226,628,631,632,1035,1036,1039,1086,1177,1214,1348,1354,3236],[85,97,143,226,624,1086,1205,1214,1399,3789],[85,97,143,226,631,1039,1086,1094,1177,1214,1354],[85,97,143,226,628,631,1035,1039,1115,1177,1178,1214,1347],[97,143,226,1086,1683],[85,97,143,226,628,1035,1039,1177,1214],[97,143,226,624,1399,2055],[85,97,143,226,2054],[97,143,226,624,1675,3295],[97,143,226,1086,1675,1684],[85,97,143,226,1034,1039,1086,1101],[97,143,226,624,1399,3513],[85,97,143,226,1035,1039,1101,1369,1371,2766],[97,143,226,624,1399,1683],[85,97,143,226,1039,1048,1682],[97,143,226,624,640,2064],[97,143,226,640],[85,97,143,226,631,640,1034,1035,1039,1110,1115,1183,1214],[85,97,143,226,640,1034,1039,2064],[85,97,143,226,624,1399,1653,3712],[85,97,143,226,631,1035,1036,1039,1044,1045,1110,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,628,1399,3168],[85,97,143,226,628,1092,1107,1109,1465,3163,3165,3167],[97,143,226,624,628,1399,1653,3165],[97,143,226,624,628,1399,3165],[85,97,143,226,631,1035,1036,1044,1048,1109,1110,1311,1458,1954,2003,2084,3164],[97,143,226,624,1399,3163],[85,97,143,226,1038,1039,1048],[97,143,226,624,628,1399,1464,3167],[85,97,143,226,631,1035,1039,1043,1092,1109,1115,1347,1460,1462,1464,1465,1956,2834,3166],[97,143,226,624,2003],[97,143,226,624,628,1399,1464,1653,3166],[97,143,226,624,628,1399,1464,3166],[85,97,143,226,631,1035,1036,1044,1048,1109,1110,1311,1464,1465,1954,2003,2084,3164],[85,97,143,226,1039,1369,1371],[85,97,143,226,1039,1091,1177,1421],[85,97,143,226,1204,1932],[85,97,143,226,1036,1037,1039,1045,1048,1082,1083,1084,1214],[97,143,226,624,1399,2818],[97,143,226,1115],[97,143,226,624,1653,2834,2863],[85,97,143,226,1035,1038,1039,1092,1110,1956],[97,143,226,624,1399,3389],[85,97,143,226,1034,1038,1039,1452,1453],[97,143,226,624,1399,1653,3390],[85,97,143,226,1034,1035,1039],[97,143,226,624,1399,1653,3391],[85,97,143,226,1035,1039],[97,143,226,624,1399,1932,3288],[85,97,143,226,1034],[97,143,226,624,1399,1653,3289],[97,143,226,1048,1932,3288],[85,97,143,226,624,1082,1653,1701,2863],[85,97,143,226,1036,1039,1043,1045,1048,1094,1183],[97,143,226,624,1399,2819],[85,97,143,226,1034,1195,1235,2818],[97,143,226,624,1399,2718],[97,143,226,1034,1361],[85,97,143,226,1035,1039,1048,1178,1204,1214,3289],[85,97,143,226,624,1311,1399,1546,1653,2084,3311],[85,97,143,226,1035,1036,1039,1082,1177,1311,1546,1954],[97,143,155,164,226,624,1399,1653,1934],[85,97,143,226,631,1035,1036,1092,1178,1932,1933],[97,143,226,624,1399,1653,1933,2863],[85,97,143,226,1036,1039,1041,1090,1452],[85,97,143,226,624,1082,1084,1399,1653],[85,97,143,226,1044,1082],[97,143,226,624,1229,1399,3982],[97,143,226,624,1399,1653,1952],[85,97,143,226,1041,1214],[97,143,226,624,1214,1399,1653,3352],[85,97,143,226,1035,1039,1044,1048,1092,1697,1956,2843],[85,97,143,226,1092,1094],[85,97,143,155,164,226,624,1939,2863],[85,97,143,226,1115,1938],[85,97,143,226,1041,1514],[97,143,226,624,1653,1940,2863],[85,97,143,226,1039,1045,1048],[85,97,143,226,624,628,1090,1399,1945,1950],[85,97,143,226,628,1090,1214,1348,1452,1945,1947,1948,1949],[97,143,226,624,2067],[97,143,226,1950],[97,143,226,624,1399,2835],[97,143,226,1115,2067],[85,97,143,226,624,628,1399,1945,1947,1950,2067],[85,97,143,226,1178],[97,143,226,624,1215,1399,1653,1951,2863],[85,97,143,226,1215,1217,1696],[85,97,143,226,624,1217,1399,1653,2826],[85,97,143,226,1041,1217,2825],[97,143,226,624,1214,1399,1453,1653,3308],[85,97,143,226,1035,1039,1044,1045,1048,1082,1110,1214,1361,1696,1954,1956],[97,143,226,624,1214,1399,1560,1653,2827],[85,97,143,226,1041,1214,1560,1696],[97,143,226,624,628,631,1214,1218,1399,1653,1959,2673],[85,97,143,226,628,631,1035,1036,1037,1039,1044,1045,1048,1082,1089,1091,1101,1110,1183,1214,1218,1951,1954,1956,1958],[97,143,226,624,1399,2022,2715],[97,143,226,518,634,1105,1233,1242,2353,2704,2705,2706,2707,2709,2710,2712,2713,2714],[97,143,226,624,1220,2720,2863],[85,97,143,226,1039,1220,1956],[97,143,226,624,1399,1487,1653,2863,3175],[85,97,143,226,1039,1109,1174,1176,1487,1956,3174],[97,143,226,624,1399,1487,1653,2863,3174],[85,97,143,226,1039,1174,1176,1188,1487,3173],[97,143,226,1174,1176,1188,1204,1487],[97,143,226,624,1217,1399,1653,2863,3178],[85,97,143,226,1039,1109,1174,1176,1188,1217,1956,3177],[97,143,226,624,1217,1399,2863,3177],[85,97,143,226,1039,1174,1176,1188,1217,3176],[97,143,226,1174,1176,1188,1204,1217],[97,143,226,624,1399,1653,2922],[85,97,143,226,508,1039],[97,143,226,624,2088],[97,143,226,2088],[85,97,143,226,631,1035,1036,1039,1044,1048,1090,1093,1110,1116,1128,1130,1132,1133,1134,1135,1136,1214,1311,1361,1667,1954,2084,2085,2086,2087],[85,97,143,226,624,1399,1653,2091,2863],[85,97,143,226,614,631,638,1035,1043,1092,1177,1183,1214],[97,143,226,638,2091],[97,143,226,614],[85,97,143,226,624,1399,1653,2863,3159],[85,97,143,226,631,1035,1038,1039,1092,1214,2092],[97,143,226,624,1399,1653,2011,2012,2863],[85,97,143,226,631,1035,1039,1110,1177,1217,2005,2007,2008,2009,2010,2011],[97,143,226,624,2008,2863],[85,97,143,226,1045,2007],[97,143,226,2009,2863],[85,97,143,226,2006],[97,143,226,624,1653,2010,2863],[85,97,143,226,1102,2007],[97,143,226,2007,2012,2013],[97,143,226,1215,2006],[97,143,226,624,1653,2007,2013,2863],[85,97,143,226,1035,1039,1040,1215,2006,2007,2012],[97,143,226,624,1990,2006,2007,2011],[97,143,226,1193,1986,1990,2006,2007],[97,143,226,624,1214,1399,2843],[85,97,143,226,1091,1214,2093],[97,143,226,624,2863,3312],[85,97,143,226,1034,1039,1092,1115],[85,97,143,226,1039,1237,2017],[85,97,143,226,628,1035,1039,1214,1361,2016,2301,2303,2342],[85,97,143,226,2863,3138],[85,97,143,226,2017],[97,143,226,624,2017],[85,97,143,226,624,1368,1399,1653,2863],[97,143,226,624,2292],[97,143,226,624,1197],[97,143,226,624,1399,1653,1960],[85,97,143,226,1035,1039,1041,1091],[85,97,143,226,1035,1038,1045],[97,143,226,624,1089],[97,143,226,624,2095],[97,143,226,1214,1215],[85,97,143,226,614,1046,1047,1214],[97,143,226,624,1046,2863],[97,143,226,624,1046],[85,97,143,226,1035,1038,1039,1041,1044,1045],[97,143,226,624,2097],[97,143,226,1046],[85,97,143,226,624,1399,1653,1962],[85,97,143,226,1035,1036],[97,143,226,624,2099],[97,143,226,1215],[97,143,226,1046,2097,2101],[85,97,143,226,624,1399,1653,3348],[85,97,143,226,1035,1036,1039],[97,143,226,624,1106,1242,1399,2863],[85,97,143,226,508,518,1034,1035,1039,1106,1109,1115,1206,1214,1217,1219,1220,1223,1224,1225,1226,1227,1230,1238,1241],[85,97,143,226,624,1214,1399,2722],[85,97,143,226,1035,1039,1214,1239,1240,1956],[97,143,226,624,1683,3403],[97,143,226,1086,1214,1682,1683,1684,2991,3037],[97,143,226,624,1090,1214],[97,143,226,1089,1214],[97,143,226,624,1086,2103],[97,143,226,624,1683,1684,3498],[97,143,226,631,1086,1214,1682,1683,1684,1687,3037],[97,143,226,624,1399,2836],[85,97,143,226,1115,1354,1932,1936],[97,143,226,624,1086,1966],[97,143,226,1086,1311],[97,143,226,1086,1088],[97,143,226,624,1088,1399,1497,1501,1503,1653,1963,2863],[85,97,143,226,1088,1091,1497,1501,1503],[85,97,143,226,624,1086,1088,1214,1399,1653,1967,2863],[85,97,143,226,1086,1088,1102,1214,1361,1497,1501,1503,1964,1965,1966],[97,143,226,624,631,1086,1213,1399,1653,3230],[85,97,143,226,630,631,1039,1086,1094,1110,1340,2871],[85,97,143,226,1039,1183,1964],[85,97,143,226,624,1086,1399,1653,3422],[85,97,143,226,1036,1037,1039,1044,1045,1048,1082,1086,1954],[97,143,226,624,1086],[97,143,226,624,1352,2107],[97,143,226,624,628,1214,1352,1399,3343],[85,97,143,226,1035,1036,1041,1048,1082,1084,1110,1214,1352,1354,2066,2107,3309,3339],[97,143,226,624,628,631,1214,1399,1653,3346],[85,97,143,226,631,1035,1039,1106,1109,1214,1477,2644,2834,3343,3345],[97,143,226,624,1214,1399,1653,3345],[85,97,143,226,1039,1174,1176,1188,1214,3344],[97,143,226,1034,1035,1039,1174,1176,1188,1193,1204,1214,1352,1362],[97,143,226,624,1214,1653,2863,3301],[85,97,143,226,1035,1036,1044,1048,1110,1214,1311,1954,2084],[85,97,143,226,624,1174,1176,1399,1653,3361],[85,97,143,226,1035,1110,1174,1176,1214,1215,2376,3359,3360],[85,97,143,226,624,1174,1176,1399,1653,3359,3360],[85,97,143,226,1039,1174,1176,1188,1215,3359],[97,143,226,1034,1039,1174,1176,1188,1204,1215],[97,143,226,624,631,1399,1526,1541,1653,2842,2863,3322],[85,97,143,226,631,1035,1039,1044,1048,1082,1094,1110,1177,1526,1541,1954,2842],[85,97,143,226,1092,2662],[85,97,143,226,631,1092,1178,1214,1932],[85,97,143,226,624,628,631,1214,1399,1653,2695,3307],[85,97,143,226,628,631,637,1035,1039,1048,1083,1092,1110,1134,1189,1193,1214,1217,1348,1354,1508,1551,1667,1668,1673,1932,2045,2088,2644,2657,2834,3300,3301,3302,3305,3306],[85,97,143,226,637,1035,1036,1037,1039,1045,1048,1082,1094,1115,1214,1311,1361,1697,1937,1954,1969,2059,2083,2644,2654,2656,3303,3304],[97,143,226,624,1189,1214,1217,1218,1399,1558,1653,2854,2863],[97,143,226,1040,1048,1177,1189,1214,1217,1218,1558,2019],[97,143,226,624,2019],[97,143,226,624,1399,3299],[85,97,143,226,624,1352,1354,1399],[85,97,143,226,1034,1351,1352,1353],[85,97,143,226,624,1352,1399,3038],[85,97,143,226,1354],[85,97,143,226,631,1035,1038,1039,1092,1214],[85,97,143,226,624,634,1228,1653,2717,2863],[85,97,143,226,508,634,1034,1039,1105,1115,1206,1214,1220,1222,1224,1232,1233,2353,2706,2707,2709,2710,2712,2713,2714,2716],[97,143,226,624,1653,2706,2863],[85,97,143,226,1035,1039,1231,1362,1429,2022],[97,143,226,624,2709,2863],[85,97,143,226,1034,1035,1039,1048,1233,2708],[97,143,226,624,1399,2022,2707],[85,97,143,226,1039,2022],[97,143,226,624,1234],[85,97,143,226,1653,2710,2863],[85,97,143,226,1034,1035,1039,1115,1228,1237,1417],[97,143,226,624,1228,1653,2716,2863],[85,97,143,226,1034,1039,1043,1048,1094,1109,1115,1228,1231,1232,1233,1234,1235,1236,1237],[97,143,226,624,1399,2712],[85,97,143,226,518,1039,1206,1362,1550,2711],[97,143,226,624,1399,1653,2714],[85,97,143,226,1038,1039,1040,2353],[97,143,226,624,631,634,1206,1214],[97,143,226,630,631,634,636,637,638,639,640,1046,1047,1085,1086,1087,1088,1136,1137,1205,1210,1211,1212,1213,1215],[97,143,226,624,628,1220,2721,2863],[85,97,143,226,1039,1220],[85,97,143,226,1047,2120,2837,2838,2839],[97,143,226,624,1958],[85,97,143,226,631,1035,1110,1957],[97,143,226,624,631,1214,1215,1653,1972,2863],[97,143,226,624,1214,1972],[85,97,143,226,628,631,1035,1036,1037,1039,1041,1044,1045,1046,1048,1082,1084,1085,1089,1091,1094,1101,1102,1106,1109,1110,1115,1193,1214,1215,1218,1414,1487,1514,1544,1550,1696,1697,1698,1699,1700,1701,1934,1935,1937,1939,1940,1950,1951,1952,1953,1959,1960,1961,1962,1963,1967,1968,1969,1970,1971],[97,143,226,624,1970],[97,143,226,1046,1700,1936,1950,1961,1962],[97,143,226,624,1215,1653,2841,2863],[97,143,226,624,631,1215,1653,2841,2863],[85,97,143,226,631,1035,1036,1039,1044,1048,1082,1109,1110,1214,1215,1311,1954,1956,1957,2084,2111,2638],[97,143,226,624,2111],[97,143,226,624,1971],[97,143,226,624,2115],[97,143,226,639,1311,2114],[85,97,143,226,624,628,1399,1653,3395],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1110,1218,1340,1954,1963,1969,2084,2114,2115,2854,3394],[97,143,226,624,1214,2117],[97,143,226,639,1214,1311,2114],[85,97,143,226,624,628,1214,1399,1653,3394],[85,97,143,226,628,631,1035,1036,1037,1044,1045,1214,1218,1340,1954,1963,1969,2084,2114,2117,2361,2854],[85,97,143,226,624,1218,1399,1653,2863,3396],[85,97,143,226,628,631,1035,1039,1092,1193,1204,1207,1214,1217,1218,1235,1348,2005,2352,2816,2840,3308,3314,3318,3394],[97,143,226,624,1106,1242,2000,2001],[97,143,226,1106,1242,2000],[97,143,226,624,1214,1653,2863,3354],[85,97,143,226,631,1035,1036,1037,1038,1039,1045,1082,1092,1094,1115,1214,1311,1348,1954,2084,3350,3351,3352],[97,143,226,624,1399,1653,3356,3357],[85,97,143,226,1039,1188,3355,3357],[85,97,143,226,1034,1035,1039,1115,1174,1176,1204,1362,3357],[97,143,226,624,1214,1399,1653,3356,3357],[85,97,143,226,631,1035,1214,3353,3354,3356],[97,143,226,624,1214,1399,1653,3714],[85,97,143,226,1174,1176,1188,1214,1348,2282],[97,143,226,624,1214,1399,1653,2839],[85,97,143,226,1048,1115,1214,1932,2120],[97,143,226,624,2119,2120],[97,143,226,2119],[97,143,226,624,1088,1214,1399,1653,2838],[85,97,143,226,1048,1086,1088,1115,1214,1932,1966,2120],[85,97,143,226,1115,1214,1932],[97,143,226,624,1214,1399,1689,2844,2863],[85,97,143,226,1091,1214,1414,1689],[97,143,226,624,631,1214,1399,1653,3370],[85,97,143,226,631,1034,1035,1038,1039,1043,1048,1092,1110,1115,1214,1347],[97,143,226,624,1352],[97,143,226,530,1351],[97,143,226,624,628,1174,1176,1214,1399,1653,2124,3296],[85,97,143,226,631,640,1039,1040,1048,1091,1092,1110,1115,1174,1176,1188,1214,1224,1348,1352,1675,1684,1932,2122,2124,2125,2126,2662,2717,3293,3295],[97,143,226,624,1174,1176,2122],[97,143,226,628,639,1214,2125],[85,97,143,226,1174,1176,1214,1454,2122,2124],[97,143,226,1115,1174,1176,1188,1204,1352,2122],[85,97,143,226,624,1399,1653,3348,3349],[85,97,143,226,1035,1036,1039,3348],[97,143,226,624,1399,3350],[85,97,143,226,1039,1092,1214],[97,143,226,624,631,1214,1653,2863,2960],[85,97,143,226,631,1035,1214,1945],[97,143,226,624,1399,1941],[97,143,226,624,1399,1942],[97,143,226,624,1399,1653,1945],[85,97,143,226,1941,1942,1943,1944],[97,143,226,624,1399,1653,1943],[97,143,226,624,1399,1653,1944],[85,97,143,226,1094],[97,143,226,624,631,1399,1531,1532,1653,3045],[85,97,143,226,631,1035,1038,1039,1092,1109,1110,1189,1222,1528,1531,1532,3043,3044],[97,143,226,624,1531,1653,2863,3044],[85,97,143,226,1035,1036,1037,1040,1044,1045,1082,1110,1311,1531,1954,2084,2127],[97,143,226,624,1531,2127],[97,143,226,1531],[97,143,226,624,1399,1531,1653,3043],[85,97,143,226,1039,1174,1176,1188,1531,3041,3042],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362,1531,2129],[85,97,143,226,1039,1348,1372,1531,2129],[97,143,226,624,631,1214,1399,1653,2863,2869],[85,97,143,226,631,1035,1036,1039,1043,1044,1092,1214,1311,1361,1954,1956,1957,2084,2842],[97,143,226,624,1214,1653,2863,3313],[85,97,143,226,1034,1040,1214],[97,143,226,624,1082,1214,1399,1653,3171],[85,97,143,226,631,1035,1036,1040,1044,1082,1092,1094,1110,1178,1214,1348,1354,2033,2834,2842,3159,3160,3162,3168,3170],[97,143,226,624,1399,1653,2863,2886],[85,97,143,226,631,1035,1039,1092,1109,1177,1468,1470,1471,1474,1956,2023,2834,2884,2885],[97,143,226,624,1399,1470,1474,1653,2863,2885],[85,97,143,226,631,1035,1036,1043,1044,1109,1110,1311,1361,1470,1474,1954,2023,2084,2871],[97,143,226,624,1399,1472,1475,1653,2863,2887],[85,97,143,226,631,1035,1036,1043,1044,1109,1110,1311,1361,1472,1475,1954,2024,2084,2871],[97,143,226,624,1399,1653,2863,2889],[85,97,143,226,631,1035,1039,1092,1109,1177,1469,1472,1473,1475,1956,2024,2834,2887,2888],[97,143,226,624,1399,1653,2888],[97,143,226,624,631,1399,1526,1543,1653,2842,2863,2870],[85,97,143,226,631,1035,1036,1038,1039,1044,1048,1082,1092,1094,1177,1361,1526,1543,1954,2842],[85,97,143,226,624,1090,1399,1495,1496,1653,3226],[85,97,143,226,631,1035,1036,1039,1041,1044,1048,1082,1090,1092,1094,1103,1177,1361,1495,1496,1954,1956,2025,3225],[85,97,143,226,624,1399,1653,2025,3225],[97,143,226,1035,1037,1039,1092,1348,1933,1956,2025],[97,143,226,624,631,1214,2025],[85,97,143,226,624,1399,1504,3227],[85,97,143,226,631,1035,1036,1037,1039,1041,1044,1048,1082,1090,1092,1103,1177,1361,1504,1954,1956,2027],[97,143,226,624,2027],[97,143,226,1504],[97,143,226,624,1399,1653,2890],[85,97,143,226,1035,1036,1038,1039,1044,1092,1109,1110,1178,1214,1361,1954,2029,2084],[97,143,226,624,1399,2863,2873],[85,97,143,226,631,1035,1110,1361,1537,2031,2842,2872],[97,143,226,624,1399,1653,2673,2863,2872],[85,97,143,226,1036,1037,1044,1045,1082,1183,1311,1354,1954,2030,2084,2871],[97,143,226,624,628,1399,2874],[85,97,143,226,631,1537,1539,2031,2834,2842],[97,143,226,624,1399,1537,1539,1653,2863,2875],[97,143,226,624,631,1399,1537,1539,2031,2842,2872,2875],[85,97,143,226,631,1035,1110,1361,1537,1539,2031,2842,2872],[97,143,226,624,1399,1653,2863,2876],[97,143,226,624,1399,1539,2863,2877],[97,143,226,1039,1043,1092,1174,1176,1188,1203,1539,2030],[97,143,226,624,628,1399,2880],[85,97,143,226,1035,1039,1092,1115,1193,1354,1539,2030,2031,2873,2874,2875,2876,2877,2878,2879],[97,143,226,624,1399,2878],[97,143,226,624,1399,2863,2879],[97,143,226,1039,1092,1177],[97,143,226,624,1539,2031],[97,143,226,1539],[97,143,226,624,1653,2863,2881],[85,97,143,226,1035,1039,1101,1115,1183,2001],[97,143,226,624,631,1399,2882],[97,143,226,631,1043,1092,1094,1109,1177,1550,1554,1956,2881],[97,143,226,624,1214,1399,1556,1557,2863,2883],[85,97,143,226,631,1035,1037,1042,1045,1092,1094,1109,1177,1214,1556,1557,1956,2810],[97,143,226,624,1399,1653,3170],[85,97,143,226,1035,1039,1188,2033,3169],[97,143,226,1034,1035,1039,1174,1176,1204,1362,2033],[97,143,226,624,631,1090,1399,1653,1949],[85,97,143,226,631,1035,1090,1361,1946,1947,1948],[97,143,226,624,1399,1946],[85,97,143,226,1039,1110],[97,143,226,624,628,1090,1399,1653,3039],[85,97,143,226,628,631,1035,1039,1090,1946,1947],[85,97,143,226,1039,1041,1091],[97,143,226,624,628,1090,1214,1399,1653,3040],[85,97,143,226,631,1039,1048,1106,1178,1214,1508,1949,2834,3037,3038,3039],[97,143,226,624,631,1399,1653,1947,1948],[85,97,143,226,631,1035,1039,1348,1947],[97,143,226,624,1399,1653,2821],[85,97,143,226,1034,1035,1039,2006,2303],[97,143,226,624,1399,1956],[85,97,143,226,1034,1955],[97,143,226,624,1399,1653,2816],[85,97,143,226,1034,1115,1195],[97,143,226,624,1399,3713],[85,97,143,226,624,1399,2277],[85,97,143,226,1034,2130,2274,2275,2276],[85,97,143,226,624,1399,2278],[85,97,143,226,624,1399,2279],[85,97,143,226,2130,2276],[85,97,143,226,624,1399,2276],[85,97,143,226,2274],[85,97,143,226,624,1399,2280],[97,143,226,2130,2276,2277,2278,2279,2280,2281],[85,97,143,226,624,1399,2281],[97,143,226,624,1235,1653,2863],[97,143,226,624,631,1399,1653,1968],[85,97,143,226,631,1035,1957],[85,97,143,226,1174,1175,1176],[97,143,226,1174,1176,1180],[85,97,143,226,624,1174,1176,1180,1185,1187,1399,1653,2863],[85,97,143,226,1034,1039,1174,1175,1176,1177,1178,1179],[85,97,143,226,624,1174,1176,1180,1182,1186,1399,1653],[85,97,143,226,1035,1042,1174,1176,1181],[97,143,226,624,1179,1399,1653],[97,143,226,1034,1035,1039,1045],[85,97,143,226,624,1174,1176,1188,1399,1653],[97,143,226,1174,1176,1183],[85,97,143,226,624,1174,1176,1187,1399,1653,2863],[85,97,143,226,809,1034,1039,1174,1176],[85,97,143,226,624,1174,1176,1180,1186,1399,1653],[85,97,143,226,1034,1035,1036,1039,1115,1174,1176,1185],[97,143,226,809,1035,1039,1174,1176],[97,143,226,1175,1176,1179,1180,1182,1184,1185,1186,1187],[85,97,143,226,1174,1176],[97,143,226,624,1195,1399,1653],[85,97,143,226,518,1034,1039],[85,97,143,226,624,1036,1082,1311,1399,1653,1954,2083],[85,97,143,226,1039,1048],[85,97,143,226,1034,1036,2059,2653],[97,143,226,624,1200,1399,1653],[97,143,226,1048,1193,1214,1215],[97,143,226,624,1201,1399],[85,97,143,226,866,1033,1034],[97,143,226,624,1091,1399,1653],[97,143,226,624,2814,2863],[85,97,143,226,2705],[85,97,143,226,624,1041,1399,1653,2825],[85,97,143,226,1039,1040,1041,1695],[85,97,143,226,624,1041,1399,1653,1696,2863],[97,143,226,624,1399,2957],[97,143,226,1035,1039,1956],[85,97,143,226,1038,1039],[85,97,143,226,1193,1332,1333,2822],[97,143,226,624,1041,1399,1653,2863],[97,143,226,624,1226,1399],[85,97,143,226,759,1033,1034],[85,97,143,226,1039,1092,1237],[97,143,226,624,1189,1190,1399],[85,97,143,226,1034,1039,1115,1189],[85,97,143,226,1048],[97,143,226,624,1192,1399],[97,143,226,1191],[97,143,226,624,1193,1194,1399,1653],[85,97,143,226,1034,1039,1191,1193],[97,143,226,624,1196,1399,1653],[85,97,143,226,1034,1039,1195],[97,143,226,1190,1191,1192,1194,1196,1198,1199,1202,1203],[97,143,226,624,1198,1399,1653],[97,143,226,1089,1115,1191,1197],[97,143,226,624,1199,1399],[97,143,226,624,1202,1399],[97,143,226,1193,1200,1201],[97,143,226,624,1203,1399,1653],[85,97,143,226,1034,1115,1191,1195],[97,143,226,624,1399,2705],[97,143,226,1034,1043],[85,97,143,226,1452,1453],[97,143,226,624,1228,1238,1653,2863],[85,97,143,226,1034,1035,1039,1043,1094,1109,1115,1220,1228,1229,1231,1232,1233,1234,1235,1236,1237],[85,97,143,226,624,628,1214,1239,1241,1399,1653],[97,143,226,628,1035,1039,1101,1201,1214,1239,1240],[97,143,226,624,631,1214,1399,1653,2872,2891],[85,97,143,226,631,1035,1044,1082,1110,1214,2842,2872],[97,143,226,624,1399,3423],[85,97,143,226,637,1091,1214],[97,143,226,624,1214,1399,1653,2863,3689,3691],[85,97,143,226,631,1214,3689,3690],[85,97,143,226,1039,1174,1176,1188,3689],[97,143,226,1034,1035,1039,1174,1176,1188,1204,1362],[85,97,143,226,1938],[97,143,226,624,1399,2863,3314],[85,97,143,226,1035,1036,1044,1045,1091,1110,1311,1361,1700,1937,1954,2084,2283],[85,97,143,226,1039,1040],[85,97,143,155,164,226,624,1653,1938,2863],[85,97,143,226,1035,1036,1038,1039,1043,1045,1048,1092,1115,1354,1932,1936,1937],[97,143,226,624,1214,1399,2863,3316],[85,97,143,226,631,1035,1039,1092,1178,1183,1214,3315],[97,143,226,624,2283],[97,143,226,624,2288,2863,3317],[85,97,143,226,1039,1048,1092,1115,1193,2288,2371],[97,143,226,624,3315],[97,143,226,624,2285],[97,143,226,624,631,1086,1189,1214,1217,1218,1399,1421,1487,1501,1503,1546,1558,1653,1966,2863,3321],[85,97,143,226,628,631,1035,1036,1037,1039,1041,1044,1047,1048,1082,1086,1089,1091,1092,1094,1101,1106,1109,1115,1129,1193,1203,1207,1214,1218,1311,1348,1361,1414,1421,1501,1503,1546,1697,1698,1699,1700,1932,1934,1935,1937,1950,1954,1963,1966,1967,1969,2084,2119,2120,2285,2289,2352,2640,2816,2834,2836,2840,2846,2854,3308,3309,3310,3311,3312,3313,3314,3316,3317,3319,3320],[97,143,226,624,1106,1109,1399,1550,1653,2863,3319,3321],[85,97,143,226,1039,1048,1106,1109,1193,1204,1214,1550,3318,3321],[97,143,226,624,2119],[97,143,226,624,1214,1215,1487,1653,2863,3320],[85,97,143,226,1036,1039,1048,1089,1115,1174,1176,1188,1197,1204,1214,1215,1235,1452,1453,1487,2817,2818,2848],[85,97,143,226,624,628,631,1089,1214,1399,1546,1653,1659,2863,3695],[85,97,143,226,628,630,631,1035,1036,1037,1039,1041,1044,1048,1089,1094,1101,1106,1110,1214,1215,1217,1218,1311,1348,1414,1546,1661,1697,1698,1699,1700,1934,1935,1937,1939,1950,1954,1963,1967,1969,2084,2119,2814,2834,2854,3309,3311,3313,3321,3691,3692,3694],[97,143,226,624,1215,1217,2034],[97,143,226,1214,1215,1217,1990],[85,97,143,226,624,1215,1217,1399,1653,2863,3694],[85,97,143,226,1035,1036,1039,1041,1109,1174,1176,1188,1215,1217,1218,1452,1453,2034,3693],[97,143,226,1034,1035,1039,1174,1176,1177,1188,1193,1204,1214,1215,1362],[85,97,143,226,624,631,1214,1653,2863,3692],[85,97,143,226,631,1035,1036,1038,1039,1040,1089,1092,1115,1214,1218,1361,1700,1952,2854],[97,143,226,624,2289],[85,97,143,226,624,1046,1214,1215,1399,1653,2847,2863],[85,97,143,226,631,637,1035,1036,1037,1044,1045,1046,1048,1089,1091,1094,1106,1214,1215,1218,1227,1361,1514,1550,1697,1698,1699,1700,1701,1935,1936,1937,1940,1950,1952,1954,1960,1961,1962,1963,1967,1969,1972,2067,2084,2102,2289,2291,2293,2843,2844,2845,2846],[97,143,226,624,1109,1214,1215,1218,1399,1653,2815,2848,2863],[97,143,226,624,628,1109,1214,1215,1342,1399,1501,1503,1653,2815,2848,2863],[85,97,143,226,628,631,1035,1039,1092,1106,1109,1110,1115,1193,1195,1200,1207,1214,1215,1218,1342,1348,1487,1490,1491,1501,1503,1514,1550,1936,2067,2106,2292,2640,2815,2816,2820,2824,2832,2833,2834,2835,2836,2840,2841,2842,2847],[97,143,226,624,1399,2832,2863],[85,97,143,226,1342,2831],[97,143,226,624,2293],[97,143,226,1215,1311,1936,2289,2291,2292],[85,97,143,226,1039,1045,1048,1082,1937,1954,2293],[97,143,226,624,1399,1653,2820],[85,97,143,226,1035,1039,1043,1048,1115,1195,1207,1235,1362,2817,2818,2819],[85,97,143,226,624,628,631,1106,1399,2848],[97,143,226,624,1332,1342,1399,2824],[85,97,143,226,1092,1106,1333,1342,1348,2282,2821,2823],[97,143,226,624,1370,1399,1653,2713],[85,97,143,226,1035,1039,1370],[97,143,226,624,628,1214,1399,1653,3698],[85,97,143,226,628,1035,1039,1040,1115,1214,1361,1951,2016,3141,3697],[97,143,226,624,2863,3697],[85,97,143,226,1034,1045],[85,97,143,226,624,628,631,1214,1399,1653,2863,3701],[85,97,143,226,628,631,1214,1414,2036,3138,3700],[85,97,143,226,624,1214,1399,1653,2863,3700],[85,97,143,226,1039,1045,1174,1176,1188,1214,3697,3699],[97,143,226,624,1174,1176,1214,1399,1653,3699],[97,143,226,1048,1174,1176,1188,1204,1214,3697],[85,97,143,226,624,1399,1653,2863,3702],[85,97,143,226,1414,3698,3701],[97,143,226,624,1035,1347,1399,1653],[85,97,143,226,719,1034,1035],[85,97,143,226,1033,1034],[97,143,226,624,1236,1399],[85,97,143,226,757,1034],[97,143,226,624,1115,1399],[97,143,226,859,1028,1033,1034],[97,143,226,624,1399,2704],[97,143,226,859,1028,1033,1034,1043],[85,97,143,226,624,1035,1399],[97,143,226,759,1033,1034],[85,97,143,226,624,1399,2275],[85,97,143,226,1034,2274],[97,143,226,763,1034,1039],[97,143,226,769],[85,97,143,226,1029,1034,1035,1038,1039],[85,97,143,226,818,1034,1035,1039],[85,97,143,226,809,1034,1039],[85,97,143,226,624,1044,1399,2673],[85,97,143,226,1033,1034,1042,1043],[97,143,226,925,1034],[85,97,143,226,1033,1034,1035,1036,1037],[85,97,143,226,912,1034],[97,143,226,936,938,1034],[85,97,143,226,624,1035,1036,1037,1042,1043,1092,1115,1177,1178,1361,1399,2275],[85,97,143,226,946,1034],[97,143,226,624,1045,1399,1653],[85,97,143,226,966,1034,1039],[97,143,226,780,1034],[97,143,226,1034],[97,143,226,976,1034],[97,143,226,629,1039,1370],[97,143,226,980,1034],[97,143,226,987,1033,1034],[97,143,226,624,1048,1399,1653],[85,97,143,226,1026,1034,1039],[97,143,226,624,1361,1399],[85,97,143,226,1034,1360],[97,143,226,624,1214,1399,1653,2892],[85,97,143,226,631,1035,1036,1039,1044,1045,1048,1082,1214,1311,1361,1954,2084],[97,143,226,624,1214],[97,143,226,624,631,1214,1399,1653,3302],[85,97,143,226,631,1035,1039,1044,1110,1214,1311,1361,1954,1956,2084,2871],[97,143,226,624,1109,1214,1215,1399,1653,2099,3385],[85,97,143,226,1042,1048,1102,1109,1188,1193,1204,1214,1332,1932,2099,2282,2848],[97,143,226,624,1332,1399,1653,3710],[85,97,143,226,1092,1174,1176,1188,1193,1204,1332,2282],[97,143,226,1986],[97,143,226,1332],[97,143,226,624,2038],[97,143,226,624,1214,1399,3715],[85,97,143,226,1040,1048,1092,1214,1348,2006,2282,3713,3714],[97,143,226,624,1214,1399,1556,2810,2863],[85,97,143,226,1035,1039,1214,1556,1956,2766,2809],[97,143,226,624,1091,1138,1399,1969],[85,97,143,226,1091,1138,1214],[97,143,226,624,1352,3756],[97,143,226,530,1352],[97,143,226,624,1399,1653,2303,3179,3181],[85,97,143,226,1035,1039,1181,1203,1235,2303,2818,3179],[97,143,226,624,628,1214,1399,1653,2863,3182],[85,97,143,226,628,1174,1176,1214,1351,1452,1453,3179,3180,3181],[97,143,226,624,1174,1176,1399,1653,3179,3180],[85,97,143,226,1036,1039,1045,1174,1176,1188,3179],[97,143,226,1174,1176,1204,2818],[97,143,226,624,1399,2313],[85,97,143,226,624,1653,2312,2863],[85,97,143,226,1039,1101,1193],[85,97,143,226,1039,1048,1092,1115,1178],[97,143,226,2298],[85,97,143,226,624,2298,2299,2863],[85,97,143,226,1048,1214],[85,97,143,226,624,1653,2299,2310,2863],[85,97,143,226,1048,1193,2298,2307,2308,2309],[85,97,143,226,624,1653,2299,2307,2863],[97,143,226,624,1399,1653,2863,3191],[85,97,143,226,1348,1361,1414,3175,3178,3182,3190],[85,97,143,226,624,628,1174,1176,1214,1215,1399,2095,2303,3183],[97,143,226,628,1106,1174,1176,1214,1215,2095,2301,2303,2868],[97,143,226,624,1399,2302],[97,143,226,1034,1115],[85,97,143,226,624,1399,1653,2332],[85,97,143,226,1039,1101],[97,143,226,624,1399,2301,2305,2863],[85,97,143,226,1035,1039,1048,1115,1204,1352,2300,2301,2302,2303,2304],[85,97,143,226,624,1399,1653,2329,2335],[85,97,143,226,1039,1101,2329,2334],[97,143,226,2340,2341],[85,97,143,226,624,1399,1653,2329,2336],[85,97,143,226,631,2329,2331,2332,2334,2335],[97,143,226,624,1370,1399,2318,2319],[97,143,226,526,1370,2300,2318],[97,143,226,624,1399,1653,2301,2340],[85,97,143,226,1035,1039,1048,1092,1101,1115,1193,1211,1348,1361,1682,2300,2301,2303,2310,2311,2312,2313,2314,2315,2316,2319,2320,2328,2339],[97,143,226,624,628,1204,1214,1399,2301,2341],[85,97,143,226,628,1039,1181,1193,1204,1214,1348,1493,2295,2297,2300,2301,2302,2304,2305,2306,2320,2340],[85,97,143,226,624,1399,1653,2329,2337],[85,97,143,226,631,2300,2329,2331,2334],[97,143,226,2329],[85,97,143,226,624,1399,2339],[97,143,226,2330,2336,2337,2338],[85,97,143,226,624,1399,1653,2338],[85,97,143,226,1039,1048,1115,2331],[85,97,143,226,624,1211,1399],[97,143,226,1034,1039,1115],[97,143,226,624,1399,1653,2331],[97,143,226,1034,1035,1039,1048],[97,143,226,1034,1035,1039],[85,97,143,226,624,1399,2334],[97,143,226,1034,2329,2333],[85,97,143,226,624,1399,2333],[97,143,226,1034,2329],[97,143,226,624,1399,2316],[97,143,226,624,1399,2315],[97,143,226,1048,1235,2300],[85,97,143,226,2300,2301],[97,143,226,624,2320],[97,143,226,624,2303,3184],[97,143,226,2303],[85,97,143,226,1035,1036,1039,1094,1237,2295,2303,3184],[85,97,143,226,624,1189,1399,1485,1533,1535,1653,2295,2863,3183,3186],[85,97,143,226,1036,1040,1041,1045,1188,1189,1215,1485,1533,1535,1696,2295,3183],[97,143,226,624,628,1214,1399,1452,1453,1653,1659,2301,2303,2863,3190],[85,97,143,226,628,1174,1176,1188,1204,1214,1215,1452,1453,2296,2301,2303,2342,2848,3183,3185,3189],[85,97,143,226,1039,1174,1176,1188,1215,2301,3183,3186,3188],[97,143,226,624,1188,1399,1653,2301,3188],[97,143,226,1174,1176,1188,1193,1204,1352,2295,2301,3187],[97,143,226,1178,2321],[97,143,226,2321,2322,2327],[97,143,226,2321],[85,97,143,226,1348,2321,2323,2324],[85,97,143,226,1034,1039,1115,2321,2325],[97,143,226,624,1399,1653,2301,2322,2327],[85,97,143,226,1039,1101,2301,2322,2326],[97,143,226,624,2301,2322],[97,143,226,2301,2321],[97,143,226,624,1399,3187],[97,143,226,2295],[85,97,143,226,1039,1101,1352],[85,97,143,226,1109,1193,1214],[97,143,226,1039,1174,1176,1177,1188,1200,1204,1214,1215,2817,2818],[85,97,143,226,624,1214,1215,1399,1487,1488,1653,1659,2815,2849,2850,2863],[85,97,143,226,1036,1039,1041,1174,1176,1188,1215,1217,1218,1452,1453,1487,1488,1661,2814,2848,2849],[85,97,143,226,634,635,636,1106,1214],[85,97,143,226,518,2054,2055],[97,143,226,624,1399,2711],[85,97,143,226,630,1214],[97,143,226,628],[85,97,143,226,1214],[97,143,226,2349],[97,143,226,2345,2346,2347,2348,2350],[85,97,143,226,624,628,631,1214,1399,2354],[97,143,226,628,631,1214],[97,143,226,624,632,1214,1399,3216],[85,97,143,226,631,632,1214,2377,2650],[85,97,143,226,624,1369,1370,1371,1399],[85,97,143,226,1369,1370],[85,97,143,226,1086,1214],[97,143,226,624,1214,1399,3235],[85,97,143,226,631,632,633,1214,2351,2377,2650],[85,97,143,226,631,632,1214,2351,2377,2650],[85,97,143,226,1108,1214],[97,143,226,624,1351],[97,143,226,1205,1212],[97,143,226,624,1127,1128,1137,2356],[97,143,226,1093,1127,1128,1132,1134,1135,1136],[97,143,226,624,1034],[97,143,226,1030,1031,1033],[97,143,226,624,1082,1399,2361],[97,143,226,1082],[97,143,226,624,2363],[85,97,143,226,624,1311,1399,1653,2084],[97,143,226,1082,1301,2083],[97,143,226,624,1213,1340],[97,143,226,630,639,1212,1213,1338,1339],[97,143,226,624,630],[97,143,226,624,1212],[97,143,226,624,1213],[97,143,226,1212],[97,143,226,624,639,1981,1982],[97,143,226,624,1353],[97,143,226,1351],[97,143,226,624,630,631],[85,97,143,226,629,630],[97,143,226,2059],[97,143,226,624,1106,1227],[97,143,226,1106],[97,143,226,624,633,634],[97,143,226,633],[97,143,226,624,631,1193],[97,143,226,631],[97,143,226,624,1207],[97,143,226,624,2377],[97,143,226,624,635,636],[97,143,226,635],[97,143,226,624,2638],[97,143,226,2637],[97,143,226,624,2640],[97,143,226,624,1240],[97,143,226,624,1228],[97,143,226,624,2645],[97,143,226,624,633],[97,143,226,632],[97,143,226,624,1964],[97,143,226,624,1214,1668],[97,143,226,1106,1214],[97,143,226,624,1682],[97,143,226,624,1214,1221],[97,143,226,624,2059,2654],[97,143,226,2059,2653],[97,143,226,624,2059,2653,2657],[97,143,226,2059,2654,2656],[97,143,226,624,2656],[97,143,226,1105],[97,143,226,624,1106,1214],[97,143,226,624,2662],[97,143,226,624,1419],[97,143,226,624,1215,2005],[97,143,226,624,1083],[97,143,226,624,1205,1206],[97,143,226,1205],[85,97,143,226,624,628,1105,1399,2700,2852],[97,143,226,2671,2683],[97,143,226,2671,2685],[97,143,226,2671,2687],[97,143,226,2671,2689],[97,143,226,2671,2691],[97,143,226,2671,2693],[97,143,226,624,2671],[97,143,226,624],[97,143,226,2674],[97,143,226,624,2676],[97,143,155,164,226,624,1137],[97,143,226,624,1399],[85,97,143,226,624,628,1399,1653,1659],[97,143,226,624,1109,1332,2863,3385],[97,143,164,226,612]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"814d5c7384f3ca276e9dc4bcfde5545801a3ea0bfae09916b3336774e662fd1b","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"27fdb0da0daf3b337c5530c5f266efe046a6ceb606e395b346974e4360c36419","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"47ab634529c5955b6ad793474ae188fce3e6163e3a3fb5edd7e0e48f14435333","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"fad4e3c207fe23922d0b2d06b01acbfb9714c4f2685cf80fd384c8a100c82fd0","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"66be1299a7a3129ceb488b340c291cf575bebb0e337f92e169dec38231472e34","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"b838d4c72740eb0afd284bf7575b74c624b105eff2e8c7b4aeead57e7ac320ff","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"48dcc919f76c040a999c0d46d2bf25ab089645ca21b837f120b222f56a86cd76","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"cd9304972e6d616197fb44fce00540a904f38b54306a1951b5dbeaf3c01ab5bd","impliedFormat":1},{"version":"77438e2c397a3db78407621cfc57241a305b310ddea2c185f1d555248297f587","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"7715134a0cf07dd41a9da2895d708625a3a303a0385e355ecaaf0b8bfaef2550","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e5c939d896565dcac0f6fbdbada11284e7728ef26a069561c09aa5aa4a788393","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"2467b00d963828f540f4acd7910f4c04cfe4b489550e6bb682212f65583bca5b","impliedFormat":1},{"version":"854e50b93090b3f8fd6e355b074e1d24dce1ae0240f1ce46563e35fea210a6d5","impliedFormat":99},{"version":"5a16e93d5d53d987dddda1ec606c9821f6bd31d1bdf0635e05e3841312cefa8b","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"b55eb9f72166093b5460d34b34f5d8699c968de3bc3fc696e40f2c93f2ebf650","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"c30436b130b6218b7714314dc41d3f459590db4bdf099eecd51cb1bda32109a8","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"6ee598cdfdd0fa52039dca135b3dfff7b49035dc13292143e0a93843e3861967","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"eb5acb58487367e502d994b57e2c58255d8241f481ea8efa8e79af23af3f41c2","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"5757b78830c681b3124af568b94c269259ea5e8171a4316508ef67310c2ed1ed","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"db2d933d8101f90deeec6698e70f1e14729495c5daab3199f4cdf0ac78a87bdf","affectsGlobalScope":true},"7ad303e40d4fddf44f156129e397511953a71481c5cfd86b1862649aaaf240cc",{"version":"04471dc55f802c29791cc75edda8c4dd2a121f71c2401059da61eff83099e8ab","impliedFormat":99},{"version":"5c54a34e3d91727f7ae840bfe4d5d1c9a2f93c54cb7b6063d06ee4a6c3322656","impliedFormat":99},{"version":"db4da53b03596668cf6cc9484834e5de3833b9e7e64620cf08399fe069cd398d","impliedFormat":99},{"version":"ac7c28f153820c10850457994db1462d8c8e462f253b828ad942a979f726f2f9","impliedFormat":99},{"version":"f9b028d3c3891dd817e24d53102132b8f696269309605e6ed4f0db2c113bbd82","impliedFormat":99},{"version":"fb7c8d90e52e2884509166f96f3d591020c7b7977ab473b746954b0c8d100960","impliedFormat":99},{"version":"0bff51d6ed0c9093f6955b9d8258ce152ddb273359d50a897d8baabcb34de2c4","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"13918e2b81c4288695f9b1f3dcc2468caf0f848d5c1f3dc00071c619d34ff63a","impliedFormat":99},{"version":"120a80aa556732f684db3ed61aeff1d6671e1655bd6cba0aa88b22b88ac9a6b1","affectsGlobalScope":true,"impliedFormat":99},{"version":"a7ca8df4f2931bef2aa4118078584d84a0b16539598eaadf7dce9104dfaa381c","impliedFormat":1},{"version":"10073cdcf56982064c5337787cc59b79586131e1b28c106ede5bff362f912b70","impliedFormat":99},{"version":"72950913f4900b680f44d8cab6dd1ea0311698fc1eefb014eb9cdfc37ac4a734","impliedFormat":1},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"36977c14a7f7bfc8c0426ae4343875689949fb699f3f84ecbe5b300ebf9a2c55","impliedFormat":1},{"version":"ff0a83c9a0489a627e264ffcb63f2264b935b20a502afa3a018848139e3d8575","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fd8a5aefd8c3feb3936ca66f5aa89dff7bf6e6537b4158dbd0f6e0d65ed3b9e","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"41c41c6e90133bb2a14f7561f29944771886e5535945b2b372e2f6ed6987746e","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":1},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e4af494f7a14b226bbe732e9c130d8811f8c7025911d7c58dd97121a85519715","impliedFormat":1},{"version":"cbb1c5ba5dbabe42c19ca31b83e48fec95895484fe1d1a8fb649b69ea224c5b8","impliedFormat":99},{"version":"45cec9a1ba6549060552eead8959d47226048e0b71c7d0702ae58b7e16a28912","impliedFormat":99},{"version":"6907b09850f86610e7a528348c15484c1e1c09a18a9c1e98861399dfe4b18b46","impliedFormat":99},{"version":"12deea8eaa7a4fc1a2908e67da99831e5c5a6b46ad4f4f948fd4759314ea2b80","impliedFormat":99},{"version":"f0a8b376568a18f9a4976ecb0855187672b16b96c4df1c183a7e52dc1b5d98e8","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"092944a8c05f9b96579161e88c6f211d5304a76bd2c47f8d4c30053269146bc8","impliedFormat":99},{"version":"b34b5f6b506abb206b1ea73c6a332b9ee9c8c98be0f6d17cdbda9430ecc1efab","impliedFormat":99},{"version":"75d4c746c3d16af0df61e7b0afe9606475a23335d9f34fcc525d388c21e9058b","impliedFormat":99},{"version":"fa959bf357232201c32566f45d97e70538c75a093c940af594865d12f31d4912","impliedFormat":99},{"version":"d2c52abd76259fc39a30dfae70a2e5ce77fd23144457a7ff1b64b03de6e3aec7","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"f73e2335e568014e279927321770da6fe26facd4ac96cdc22a56687f1ecbb58e","impliedFormat":99},{"version":"317878f156f976d487e21fd1d58ad0461ee0a09185d5b0a43eedf2a56eb7e4ea","impliedFormat":99},{"version":"324ac98294dab54fbd580c7d0e707d94506d7b2c3d5efe981a8495f02cf9ad96","impliedFormat":99},{"version":"9ec72eb493ff209b470467e24264116b6a8616484bca438091433a545dfba17e","impliedFormat":99},{"version":"d6ee22aba183d5fc0c7b8617f77ee82ecadc2c14359cc51271c135e23f6ed51f","impliedFormat":99},{"version":"49747416f08b3ba50500a215e7a55d75268b84e31e896a40313c8053e8dec908","impliedFormat":99},{"version":"5e91172586d1be7d1508d1a40c8bf76161f6f4179d1c25158a20599f9fb26a66","impliedFormat":99},{"version":"09d215b379a93f8cbb6caf9e9f5d7b4d48042295ee697e7e4771288c7917a21e","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"891694d3694abd66f0b8872997b85fd8e52bc51632ce0f8128c96962b443189f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"971a2c327ff166c770c5fb35699575ba2d13bba1f6d2757309c9be4b30036c8e","impliedFormat":99},{"version":"4f45e8effab83434a78d17123b01124259fbd1e335732135c213955d85222234","impliedFormat":99},{"version":"7bd51996fb7717941cbe094b05adc0d80b9503b350a77b789bbb0fc786f28053","impliedFormat":99},{"version":"b62006bbc815fe8190c7aee262aad6bff993e3f9ade70d7057dfceab6de79d2f","impliedFormat":99},{"version":"ab80d2cb170a92c61ca8c822d0db0fc50eb15c7c6cf1a24cb01852a5a15a7db8","impliedFormat":99},{"version":"210955af8573671abebb6e1391d134d82fbcff130aa68922ea1e839f8f48a0ad","impliedFormat":99},{"version":"78a8f2e95e2904914c509e4bbc68e626f8b8ff8f30ca96cffc7a795fa17394f5","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"061446b67af18b541c723104f25aa94667dd438c050fc873f3c02a7b5a9a3ef0","signature":"b8ee70929b7bfa2ced6aded5f38945440e9ff6809c61d2972b59aaecf88c254c"},{"version":"8ef0b457802d1883c0c185f90610a8aaf33283250633a31853de01bb45565b7b","signature":"b730dbc27807d6a94494d69e0154827379b8ed4606f3dd3a4584a1e2242b1e53"},{"version":"d689a82dec12ec02e1c558870a50f71000e06f173151fcdff0458c587dbf016f","impliedFormat":99},{"version":"e58c0b5226aff07b63be6ac6e1bec9d55bc3d2bda3b11b9b68cccea8c24ae839","affectsGlobalScope":true,"impliedFormat":99},{"version":"5a88655bf852c8cc007d6bc874ab61d1d63fba97063020458177173c454e9b4a","impliedFormat":99},{"version":"7e4dfae2da12ec71ffd9f55f4641a6e05610ce0d6784838659490e259e4eb13c","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"c371ea3efec17691f019bca670c161bee2f4787b1f464bdbbb10c4117bf0a55d","impliedFormat":99},{"version":"5a2cdf6adeec348bbc876221be4367e8adff0bb78a5680ebd7d71e5c3bad6cc0","impliedFormat":99},{"version":"e004826eac62081f867c66dabd92d3ef7d126d93a70430a2c88429228c3ecc50","impliedFormat":99},{"version":"38d6857b58d2ac42442e396311c542062d4f0dad40f2adb496dd5fd0756ee400","impliedFormat":99},{"version":"34b7d1e2d15845cf08bcf5e3c01adbb92cea1ec27564ee249ba486cdfb28526c","impliedFormat":99},{"version":"64bc7684d633c835220935b80701168771e6ddc8c3d9145af8bb3a3ac7d0c59a","impliedFormat":99},{"version":"8cdbb0f1c8db096cfd876dcecbb38c54e495a4203af88417b762dd6f62daa1b6","signature":"2dc0c34bb13c5ca57ccd5a048d076fec5305f279d22010fc565a27b9a8d66ef5"},{"version":"598c32af38ceddfaf9699b9013ecf2e0b2df7b5d76795c9de010d5ff92c52ad5","signature":"e064b7ccad9850f3a78ba58a45e43e4b3eaf126cd2bd2979896b5885dea07f57"},{"version":"618bb47ac74b0ab39b90743cfe920a6e670b2d3bfa24f37d106b535c1c18ae37","signature":"4c8a45f845c330caf5dc2bc33da6e8e4f317035d08a20fd5f1da986bea511d60"},{"version":"9f50731b7a6739ad4d5d0e00b5d0be3650535cd74d92bf86ba3b81cf57000269","signature":"64be38d2ab0fa005245ad20baf0fc7899f1db575a219b4428e0fc3e550d02410"},{"version":"1d0628911b56f83b654ca0ba826d503b3605926e73b985e754513832eeab5592","signature":"45b43c38bc20ca8c0edcee887ed49ed8b771dbe78e1cb5b4e3ba794e853fa887"},{"version":"6d575d93896c413b308c3726eed99ddd17e821a00bdd2cc5929510b46fe64de4","impliedFormat":99},{"version":"1beebd50610b0c9701d2de263e0183ec22aad6c051d0e15ce9e6cce295c6a40b","signature":"3c49b34b1c62e5d74c637b63276c9acacb605689334f5450cc3f67f560ac0ecf"},{"version":"310c820b803950d18c0ed9376df2cd73def2f56cfcc993f9012008403cdd4843","signature":"6fb95390f4022e0327e4a170917a06de5caad8c8c563c8b00be3cd40a71c759e"},{"version":"4ed96213860296593b569b425eec8dfac37cb5bdaffbce2206c000dc673007c7","signature":"0fbe920fa2bb3439dfa680647a4ea264b7a8ea9bfa75e4cdd9ff2507d69df783"},"a7e69a45d5f2dc289b662813446d0cc7b2b972b05165e41a5501b2080aba0807",{"version":"4720053f6a578540743ee8f3c02278636ada05dfc7184b43433262c4aebbbff5","signature":"20f656d6480d8146a5128b53fee43e77e2851f98fd61b3da28f2d8a5560578b1"},{"version":"ddc62c8eb6b7fb8e8fd0f0f19809530b1e5ba5a131471a6eef65028d0d3b5a6e","impliedFormat":99},{"version":"802cbde8e06732ca0356927b4c9fbc39f5961df58a18b74fdc4e131269293a5d","impliedFormat":99},{"version":"480713ff75c24f445e3f159da28444406e1334730375c94d0aa24523c5e52e1c","impliedFormat":99},{"version":"3ac6eb2cafcb89a552a4923213c705f0fc3c2b50e466eae9dc1a540e3af18bc0","impliedFormat":99},{"version":"073f96a1cfddfedf8695401f8328a8e84a4d98fa5b08b4d894c3885069083cd6","impliedFormat":99},{"version":"66ba40fa928c2fada9a280a61c2b426dfbbfa69085f99913650ff72ddec75b1a","impliedFormat":99},{"version":"4d857105510df8011cfb5b3769dec55624a1df92e85d399cd03bc82bb89d090c","impliedFormat":99},{"version":"19a22f3446387435f13445a31e3d4eb65f132d8e6b7b060d249f0fb138cec698","impliedFormat":99},{"version":"ecc46b24349caeab20d889baf0a6f9d3beafa739a0f2c36afc107dceb15e7b2b","impliedFormat":99},{"version":"b887624859a2f03e78ae6018e96bd269b5318685f42adec4e93256ed7579c125","impliedFormat":99},{"version":"14191b461a91229ff4b388d15b9e15392a8a3af9bf11fa7d0d4cd31405178e75","impliedFormat":99},{"version":"23a564e852dc91b6e6f050584994b35156f6ee8a2d08c493dad04309046a8397","impliedFormat":99},{"version":"daf66c9de89f11011ef703af894970bb15985fd5a4156b8038e895ad4e4616a7","impliedFormat":99},{"version":"d59c3d0c3283c1878913fc2bc88d84160dbcdc69cf06f822ca7ffb39eefef13b","impliedFormat":99},{"version":"05128b72488ad970c2e30ae6b82c7ee232be49ce6def3b4dd56f62d8b7f7704c","impliedFormat":99},{"version":"9eb8e1320fc0ecbfba15c0f3452dfc1957543dfbd466aaf8b67ddb0f2ad0f217","impliedFormat":1},{"version":"4faca872dbd194a17b3ee267bd8ddc3daf3d16df96f4e43a02c7d9a862022c4f","impliedFormat":99},{"version":"87654de60b5cd8d91d59632ec576fa7e313b41c2540073d52814b6cf5bb739e6","impliedFormat":99},{"version":"144a4e5780b800c0553949169f50be285eccbdb0298afd83ef2ae03fef77e2d2","impliedFormat":99},{"version":"66aeb47bf8638d6767f7b4ff684c2d794391c981590073025e98f98e1afed499","impliedFormat":99},{"version":"cd5b0672c9699fe169d69efd65472a874de9d1e25fa8669a934f5f326bf0f025","impliedFormat":99},{"version":"4577621880c696b0aacec6ebd2dbf97ac178ee2e2bfaa0aa3a5260a798220ab4","impliedFormat":99},{"version":"26731910f98a56ed001d25d5167d85b1320def4ffbb76e1cc4b0c6484482a5e2","impliedFormat":99},{"version":"cbaadb95dcc68691900ffa857b3bd7eaa99eeb6c351afca15103560bc87f0d15","impliedFormat":99},{"version":"97b02501eb45f487174d5a0ff89b6a95690d50e9eae242e2162118edd5f2705c","impliedFormat":99},{"version":"bbea0619511648a92fe83d5c8eed6149106d7fbf3065310a1986d18598b83bbf","impliedFormat":99},{"version":"963ece6abb58542445eda863960cf053a98da8f4e8634b7a8826aa04f6f85a56","impliedFormat":99},{"version":"5ecea63968444d55f7c3cf677cbec9525db9229953b34f06be0386a24b0fffd2","impliedFormat":99},{"version":"1d226c1e6786584e97efede708d49f2dbd6f887905f16c785d5f09b300bc098d","impliedFormat":99},{"version":"07ff7d4360fbc945963d7a4a8105a5520d1681a00745c20a962fb36bf04452de","impliedFormat":99},{"version":"1b1c48c4d7cbe6f40616594c2a3f6f95bb1dcefd200a7e4167e47b67725b631a","impliedFormat":99},{"version":"62076be1e1e8b668a8ddcb803402f1aec725a31d592e8722ff39ad368d9cd472","impliedFormat":99},{"version":"2806e4d2a88e0461c3b0c8cd9e7bc8e927034690e33345aa0853439d67f801b4","impliedFormat":99},{"version":"b1d72bde8f54695b85883613af13295a615034b2829dde3a31bd3d2a40eb6bc4","impliedFormat":99},{"version":"26cfaec143443411bc7d5363f274f885ced430b8f4bee25a81f7827248848d7b","impliedFormat":99},{"version":"6870f32dc76ff6f8f6a419ce55add0a011909e8895252d7cca813835f431783f","impliedFormat":99},{"version":"29fcff21ff0ecbe700c7db7f719af2fb4822a08d6d703b6687822535e8bd3126","impliedFormat":99},{"version":"5597cbcd19e16f5c9148c76c914e158680de55849b625c2f6b69723f01f1007c","impliedFormat":99},{"version":"7864233f21a3bd04eb6dfa79103a6c1d0648cf17eb4c47cc7aef19d274dd639c","impliedFormat":99},{"version":"b27f7733758db8f462dadf0ee250056e370413028c99fc723c4a93baa54a7c1c","impliedFormat":99},{"version":"5bd7f6f573ac89ec20aaf326e79394da8a89fbff8a297aa864de9137ca045678","impliedFormat":99},{"version":"81b2bebeae6ec1e73b491fe22a82c7e2d3a8369271e622ed74b6e94ba108a475","impliedFormat":99},{"version":"d0e4a184f48eba140f30e8f770853b884e01694f6aff59b38d0be55b0410d397","impliedFormat":99},{"version":"1d34b3ef8e5926334d86d305477d3592d648adb41fa0110970a68059e13d45c0","impliedFormat":99},{"version":"791e26804cd328b19fc37f7903813e8e41892e70d5241dbe2c39fdb52fdd0c9a","impliedFormat":99},{"version":"02c2773eb8536a50f6e647483e78e8c2991fea8ac32ab69a37f9a24255401530","impliedFormat":99},{"version":"4ac6d584eada1621a7eaa4bfb3dd54e81c2c8a82c7ffdf421ae58d84c3a3490e","impliedFormat":99},{"version":"a522abad9b9b959a9c4bdb4e6bdad96e65d97da9385be13ffc8affc3669fd786","impliedFormat":99},{"version":"ae733b8a8fc9659e24821aa3797d25cfdc205bd31674227b49411ca4d54e510c","impliedFormat":99},{"version":"0aa9c5135c3a086d7c01d8d18409da6b01fd32f09a4a261048f8cb4653f22be1","impliedFormat":99},{"version":"1e185a3af4b4f3bd6fd52fde968f14dcf9a8cbbc4924237270e290e25d81fe40","impliedFormat":99},{"version":"ae42c6173cc8ad49d6ae21187d0bb7c7c65da10204f9e2614eeb83b29c58f4f7","impliedFormat":99},{"version":"050240464b97ffce2e353ccd5251660f5d3dcf9dc834f88504732ded7cfe926c","impliedFormat":99},{"version":"3f896952650454552b2584ef1e3dd072e97f8498908cd2ab25e6b0217e8bfeb2","impliedFormat":99},{"version":"3b3d0685f081f6a02cda029e4d1e1ba5f10690870c971e6697e0c2539501e835","impliedFormat":99},{"version":"5998b174ccb38a61393170f40448f80152ca5518f9d2048f5b5d3cbe0a9fbac2","impliedFormat":99},{"version":"6707d39d8afa069222d0674016d48c4772067eb671f9b62528a6cc8218fd5b40","impliedFormat":99},{"version":"481aab62f04afa6eab4e439fb4f39af392c5c51519f548897ff71e6bad0b6771","impliedFormat":99},{"version":"51de9d738596fcc085d13bdf86c0014f15d9b4e6986631c7be3df9d2f61590d8","impliedFormat":99},{"version":"8eca47167dadd486582ecd4e41f7fba6ae66cc4a4c5202f1f7acf34129a0dadf","impliedFormat":99},{"version":"29cc3322fd17fd1b55ea2150ad6f7cb37f0b587efca5696819cc5b6e95331bd4","impliedFormat":99},{"version":"d09b7414a64adc7cae660ecd6e8a222ad9fa58585dd2390eb0aaabfee812b354","impliedFormat":99},{"version":"769b6f9f1cd9471261d137513abc391a744a3c3a62f492491bcde520219fab53","impliedFormat":99},{"version":"49fdbd971a9b57df943498b37cf11c40fe09b2675493039a0b7841671f385108","impliedFormat":99},{"version":"faba6f3b673c89279d3b41a47e8ea2c850665eadfa1e2a56be4f50a6bf4356c6","impliedFormat":99},{"version":"5cbc3c3c6475704af132c35b095da392a03815baf2e9f2853178ff9b370b64d2","impliedFormat":99},{"version":"074209bc8fc6979cfc363d392a8babe62685adc61c62a8742ecdb86fb9b62ad0","impliedFormat":99},{"version":"6826e70645f65e77bcceb9230962687109301a4ad9d6dbb71a7785167d4a4b9e","impliedFormat":99},{"version":"3c8b637a833f97a085417e7d0024ac82f7fafc0834a4c61d5e48f8edd8da6c10","impliedFormat":99},{"version":"a3a4132d6c64f431b6d0cc890557c392f57eb43371bb73979ea38d27c86a1c4c","impliedFormat":99},{"version":"ec03be0777b98df75dcd97657ebfac0eb7a9153867aab050a591b6caadf1c2a2","impliedFormat":99},{"version":"c788aaea8be5712b40c3bd9cf589c9510930af7b2aa3d986125df0dedc569290","impliedFormat":99},{"version":"e7983072a038e512514c146b25e7e97a8a070ca3507950658ab1e96f6598957c","impliedFormat":99},{"version":"f2333bb4a221631fe506a0354fffb808507d4e5f6fe2c85b69890618226f7d9b","impliedFormat":99},{"version":"6c9114366ff07ee8f5c3cd4ba94ad189a098ce8368040909d605fb38e636d026","impliedFormat":99},{"version":"13e930c27d68ecfa906c24d599b10927b152030d07da0fa0889fd4fddc5b4115","impliedFormat":99},{"version":"b4e61f4f522304f7fce1038590ca1f6d091d58aff84833861848f8157732d8db","impliedFormat":99},{"version":"8c86f563e8bcefb0b5b1ac62e5a27ee6a2a9b775e72dea5793823edeb24d36e9","impliedFormat":99},{"version":"d9134c8daef2565f20b72171f634800efb204eba63b03142d5dae5f36088e95a","impliedFormat":99},{"version":"faf9a217d8d237b02ab6d95508d8736ae431bbeb38d98885eb5b8fb6dbe48cec","impliedFormat":99},{"version":"e702ed1fd1dcb24ec2634901441fd156449f75458359c771074cbe7675e86614","impliedFormat":99},{"version":"bbb044421875fc84b7d2f2aac4fb14499687cae5a5063da51bfa28c58239bcfb","impliedFormat":99},{"version":"74b564cd3da8f83d5e472a5b0cc53bf7e276b25576097cb89e6f67caf95b12dc","impliedFormat":99},{"version":"3705ba677801103461ff0a06d34b6b2149072952365e55d8266969978dd33154","impliedFormat":99},{"version":"cca68a7703ec3717b6d4c287884fc79ba811f894c472718126010418cd306aa7","impliedFormat":99},{"version":"590708a598f58b156518493c563df1d03040d3b2b7f75fe614e1ada06dbb44dc","impliedFormat":99},{"version":"dc2c32ad9c49a7c3e56a18f3f42933e91474bc26ecc2ea47cf533818a54e6471","impliedFormat":99},{"version":"e061e898ffe9970c067278f5a7462665e2706e7cc6ce2362276eca1c92c128f7","impliedFormat":99},{"version":"e5ee49966285e5afa0dd2db7f66acf1e8a9e1d0bc5724b03b67be92ea7819bfc","impliedFormat":99},{"version":"4db2be160aa80fecd367876f8cf1aa197cd1f296e5f82ed8d8b961d9ececb204","impliedFormat":99},{"version":"5a8e4a5e571755e265bd6a840d8ab48eeb1ca2e35487d96bbd601ed296e2d1b3","impliedFormat":99},{"version":"d93cac0bbb7e1fe241f4b0493cd47466df00d9f1c51a53b69e5442456cb4d102","impliedFormat":99},{"version":"9de94cfccea0da314e8554d6b2f1f01a1b63fa4c79dc24b54277e86b918e9d6f","impliedFormat":99},{"version":"b4f7f4e2e4d0e668ab7cfd94ae5b72b6c690eafeac0e7a6d2218b16afdf7432f","impliedFormat":99},{"version":"5e8d925c0b8f6f91ac0af131a83f72683f88d80a61f5eea37d8883afbe8f74fa","impliedFormat":99},{"version":"056e9235afb474b7b2ffb6df16ff331f5238027b185367ca745103eb228fe57b","impliedFormat":99},{"version":"2eb77a708b1d812a8b0a57a6a12cbdb659bf43acf839b21b8996dbbd511d6e53","impliedFormat":99},{"version":"9a2d7fe034d084982a18ed744a3e0748f4768fdc5b9f2cdbf5f190e5226b54a4","impliedFormat":99},{"version":"1cbf7a0290d370c2843e79344bd494a10d267b3e0323bb77cf1b34a36ecf4200","impliedFormat":99},{"version":"2dd580520217749fd86cd77b8e48075a6c2ff32339e2334aef676bd3800f345f","impliedFormat":99},{"version":"939fdf70427033c0a05d112c2b03e8e31f037b8f2ac4617df680107162ffb423","impliedFormat":99},{"version":"9bc7a3d724ff20d2429d94e087c276b9256946b2cd66c9f9bce79ab54ec9c115","impliedFormat":99},{"version":"aa813b5adf5ecf364ddcab7bc6652db73d5c4e43ee5f6ccfdc7737f6d3184667","impliedFormat":99},{"version":"e5fcc46e6fc608a77c7efea569e56e3cb02491a9fc0d74f49e784d0a4a6aee14","impliedFormat":99},{"version":"fd413d87e8bf7a8e523c70d194b2c3279016d1ec733a9db43640cc1e0cabde6f","impliedFormat":99},{"version":"126cab464ce86f9c155c0b79f9c38fa906c422ac02c856ff9874051ca35ceb14","impliedFormat":99},{"version":"4d84f055621f07107b6e882b0cb79848106d08899bde344eb6ad0c9bc3539eae","impliedFormat":99},{"version":"25ada8b073df8f9b669aa007ee66298095904b83236652ee940d827c2ed5fe9b","impliedFormat":99},{"version":"24bb5860d0b4310843a2ce164c113315db19861f3fae4f2a56727ca9b98dc4b4","impliedFormat":99},{"version":"b62d96002ec0c8710d0e99aa3175434e1df0f22f5a09291b19e5ec05e8a877e6","impliedFormat":99},{"version":"221c86478853bcca59d83ce0eb2832e575779f2244a9a0176971de55c45b9690","impliedFormat":99},{"version":"7b8940dddb145146d5e62f9d817d5cb9f54345cd17bb91a363c293dd5216a377","impliedFormat":99},{"version":"7d331ed732ddb23a5e04eb12716cff50491ba01b712f4810df496c174547403f","impliedFormat":99},{"version":"a444b1d18b18c90477babf60511e8348ab9d591698205ed1bf12f3a0bf5862e0","impliedFormat":99},{"version":"08ca4dca79ba1cc23d4610ddec493102d3fdef6bb57f025d99b1cba9759f71b3","impliedFormat":99},{"version":"39c2d0f3d8d82809c02668743fb19a50e66f05d4336d48765946e4a051d0579f","impliedFormat":99},{"version":"495e122ec7cd8b18150ec1191e48edda4e23b2587022e59805571ddf8a3b516a","impliedFormat":99},{"version":"9277cadea8fcd4c10616d7667f521274c5fc6cef385861f6962ef880db3e612d","impliedFormat":99},{"version":"20b20c535eb79b2a4a62229abf83f0fcdd3dc1f041fc3c588dbe01e3a7666ef9","impliedFormat":99},{"version":"e98970286b6514c67e3b0f916f23f8bb81ad6fbe3b5ef1f2bb013272e9ccb00a","impliedFormat":99},{"version":"09c1c46f10e01ae7399f2fb391178be7ddd42d70dc6a3abc41d80ccf73badad9","impliedFormat":99},{"version":"31c9882e1d08811f5821ea24554c0bd8a0d97fb7efc661ef76393a28d9a8eb26","impliedFormat":99},{"version":"7c9aecf4946da6395949f23bacaa6d7e9ce287f5aa65e50e69332ee5f4d1960f","impliedFormat":99},{"version":"6701adb65ce407ccabefbaf20862daf55d52dbdb2a663c899d163cc6cbb59192","impliedFormat":99},{"version":"f73df64d28c41e3bc777eca2fb49cb5cda69b52c3786b32d4dc473f855fca42b","impliedFormat":99},{"version":"b342ffdc48ee317927f88cb38871b984b7edf94634428fcad875c7a9fe5515ae","impliedFormat":99},{"version":"247fa787c809e9036079d3f4bf429f5c6e4d76a31647d5547e668fa25c46477e","impliedFormat":99},{"version":"f015d64096dbfde32ec9117706e6e1376e9ed0ea8534d17d1c4035262fb82ebe","impliedFormat":99},{"version":"0af38d2d00fc29764aead613ae52e263e235289ec9e2f365e909226e8b2df2a5","impliedFormat":99},{"version":"c610c569ccfdbcb03d9e531ac1be3ed944586e099bf4f756885fce2d5e1a680c","impliedFormat":99},{"version":"2062be175b1e4f6a0b6b21b4ad08c1e241833349fd82aae558000acb2a9c905b","impliedFormat":99},{"version":"32c98d5e98a05f108f4e405c853db481f83c5a1a9cd6c53870501d8248f9afad","impliedFormat":99},{"version":"c717d81d125641e3d95b30cb00d3c0179fdcb30c9e716c360aeb23c699e51321","impliedFormat":99},{"version":"9a96c65bc8d115c4cd1f6d61305013640593f0c0f869a2e6cebb7bbcdcd7313c","impliedFormat":99},{"version":"d5e101bf2eaafcf94b79c0a80a8b86e26ea0b24234f8f5b2c88b58cac0842a74","impliedFormat":99},{"version":"27caf95cace62037352d836d1c547a73363248289ba8b05205cb9eef146768ba","impliedFormat":99},{"version":"8ac1275f4eef836ace2b3779aa240cece0a7094cee65e3a56fc730a270695b0b","impliedFormat":99},{"version":"4dae97da440251bfb634edda1739b3cf39e66b56076e05d7b06bd3181a6fc500","impliedFormat":99},{"version":"66a183f89f492290d10baa4bc6840fac3a0212cd3e32f2230c1506eb6b1f84db","impliedFormat":99},{"version":"6576f83f333348274a02f3a9a048dfd9c0fbcc3515ec4e654def0ec5491a6261","impliedFormat":99},{"version":"f657e9bb81b35be0d298f305f4a6924c4b652692f9d48512038015e7eb79c7fa","impliedFormat":99},{"version":"5ea4c5fd9091e33b07825015ed1cce784854121cf42d337f0762f1d707ffacfa","impliedFormat":99},{"version":"0a6af3e7a2a63fec578ef9940ace9987eaa91450112efa665dd94cf26555463f","impliedFormat":99},{"version":"172423ba720956a2999c4e44a640d6b141c1c8646d96e8a88a333181eddb1eea","impliedFormat":99},{"version":"8d1ee20c4ca7a97ffb6c9b19049a1a9ebb34bfff32379261bc6295e82cb77abb","impliedFormat":99},{"version":"258436bc14be16b94eefec3da57b4eca7a3c1df633c79d4ccc35f18eaa9d8107","impliedFormat":99},{"version":"0ad0d843d93b5bf3fdaf79de4e159e28d6f9367a945970413205f345e9797cbc","impliedFormat":99},{"version":"f9a161a77ec523402d8d7dbaf9a04e9fc3d32d0b304dca4d7a86412bfdd1b1c9","impliedFormat":99},{"version":"ecaa337dd6eaa40a78934bc53a46455c969a9e2ec75e07da806552e5e1f5f575","impliedFormat":99},{"version":"747703dab2b5bfcb0f4372616373cbbe85a8a9e246bf4f2002252c54f79750a6","impliedFormat":99},{"version":"c09f4c7ec02ad3b5be269a3e220d69d3f16d43fe3843e2e75263344d3ce7981c","impliedFormat":99},{"version":"d351678cfdd7d86b5dbc0c75eaf66ada923f7ff1c76102508ac22f703cb9b927","impliedFormat":99},{"version":"b3d820765aa7672d9276e319e9a2b4d7a928b5dbfe34169e287bc2c0a03be70b","impliedFormat":99},{"version":"96ce9dcfef17a1945dbb4ec0ff2256f3847e813671bcba46381fa6673cf8b202","impliedFormat":99},{"version":"0d852b4958e9b9dee49676e33381e33280a0345bec8fde3f902b479bd0f69e37","impliedFormat":99},{"version":"1ee834bd1a5b21ee9f0f8e683ed8f46410f2548f5b81ae090c14fa41ebe3173e","impliedFormat":99},{"version":"fd875069349f1541cdbf2859ca8b0acdb81acaffeeb579f74dc08b332e8a2fc4","impliedFormat":99},{"version":"fb6994ae9a491ff440c5a78667f4d5783fb6c5827050db94f9ca7fb14f8ff260","impliedFormat":99},{"version":"3318f774e0fa8cd7decde2830e561c401e53057ea505c031f687966a16f4b32c","impliedFormat":99},{"version":"c130a5e599c565b49b02dfdaef22c5dc68bf648a9678339b44e8913d3d27ce71","impliedFormat":99},{"version":"06cb5fd4ff2e5cf532dfc6bbeae7b47ad7c2879909e6727ffdacc558115ebf0f","impliedFormat":99},{"version":"5cf2e81f262bee804fa9d50112c5288ed4224243b1837c653c9eec5a621a9b13","impliedFormat":99},{"version":"aeea67ef93786c8625e6c2840c5be41e6f6679f9890bc75628ac0a3cf8ea0c04","impliedFormat":99},{"version":"a37b86cc490287c9723338ed95965a938886313f0f912ba12f789462d8bad89c","impliedFormat":99},{"version":"41a073e65cbf693b4ca1f61f6847e16227d023cacfa75a84fb989efc3545cb19","impliedFormat":99},{"version":"e726badbad2c619272fe4fe528dd07cd5ef87bda456dc3656e4fd1bcc11976d0","impliedFormat":99},{"version":"60b0f3b27eed4652b4cf70ff359eecf92d1dadce962239812474436b4d608da6","impliedFormat":99},{"version":"4d7002dcc54793296ab4c4b1e28c00e99cdda63ef31b83ef616c58f8773c25bd","impliedFormat":99},{"version":"040dbae8a47533338afa394e6974e753b4bfc1895c322a3a715eb1be21eab5bf","impliedFormat":99},{"version":"a9d4d662f3494ab31e98c8193f20b0725a9488225df92bb4df2d9f96b5b7a166","impliedFormat":99},{"version":"6170c6827bcca40ead01d9a8e92e73049b82a0e595f1c11ef39bb98282781f7d","impliedFormat":99},{"version":"5dd074521b20eeb26c76fb3e1d0f85fb4bf26cd247c7dffbed08bd888a6d29d3","impliedFormat":99},{"version":"f3a8d4b406af14afba34488fec9b89859900a8df10510a23d9f1c2e8a116d3fd","impliedFormat":99},{"version":"b03aa91aef645f9856216a2223a47001a84954caf37b7ffb1d63d1327b4231fe","impliedFormat":99},{"version":"01b6435dae2508e231ded5ca79334075da7d6ca12d909765cb335211a90ba86e","impliedFormat":99},{"version":"75fc3992422a1d3b15788ee84656da98a10ce15ce5ba257a0df623a024a0d845","impliedFormat":99},{"version":"b6c5cede83853964b2f753d7e202613e1d461857cc3780a57a2a3d346c5afc0a","impliedFormat":99},{"version":"ffc4846043b7f71f310692e4bd38f349373981b832f907963e4bbdd4288f130e","impliedFormat":99},{"version":"54a730e06094b37f96436ccc8e736bb65b74d256439bf1663344e3fab16d2246","impliedFormat":99},{"version":"1389cb1ca8557f7380f983f00c337969542d6c932b1ba294b48f97f6fd1cb69e","impliedFormat":99},{"version":"85cfc4f1cd043b1df65ba7714d292ba7c6c79c9e288db0d4a9ea6a7b567a675b","impliedFormat":99},{"version":"74cc10ca21f4fc15188d7e7aafd66de5c34c82d011f0c9e02b05b9739e0fa31c","impliedFormat":99},{"version":"abe83f442f76121715241d0fc207d2c325510c6a4dfa6b07662f550c95c6a2a2","impliedFormat":99},{"version":"4b6de64797fd57745c2856f26b4c7de6be543f9335dfde7870a186c3541ff183","impliedFormat":99},{"version":"765122fafa15af14742c91619b7e30b36e5c38f01e6ad079d2c5ecd38a4fc45d","impliedFormat":99},{"version":"1194d3241ea56738d7d8e2b4908572a350cfe7a85b82ef89828ea32e20ab1803","impliedFormat":99},{"version":"6449789627c9555d2914c88498ab494cdc4f18e28a7426a1e74dcef3401f181d","impliedFormat":99},{"version":"17326f1b693cd3a0e89fdc1248097f0135adacbc072b0ed62cab9eecb1c21743","impliedFormat":99},{"version":"1b322be99b786ec951d3d14283aeddd32c3ab25033c4cb984b5224630317b232","impliedFormat":99},{"version":"c7523c0ac422da80b521031667dc06ca66d817ce5ac47f69db6fa98531febb26","impliedFormat":99},{"version":"19203771ab06e45e1524b7f608b332cad7143ba3ff473e302827a835ecd99dbc","impliedFormat":99},{"version":"3b7b9365174c24792ba2c762637b0bd5cbb8d88a72153e3f7f82d34e115d5647","impliedFormat":99},{"version":"7e3a2715195f927935488d7565bc30e7f540797776e1de208c64720d4ef87f77","impliedFormat":99},{"version":"5804ffbc65b78751fd510218b90827a7ca677ca34a45b4709a00783b658cbaba","impliedFormat":99},{"version":"0945a03ba41861ce8f75468e2bd1bfd424185418921fc2f55cac5eeeb5049c3d","impliedFormat":99},{"version":"246dc85745d220f0a1041d67bef89de1e02fabf49e6ce896bc1a345eab1fc507","impliedFormat":99},{"version":"616b74da95e0f9bca845458de4a8b25f12142b4a7b02e89882da05b4cc115802","impliedFormat":99},{"version":"b894722e4b4205a60154ee3d6fa8ecc3ffdfb92a7bd38936f666d3f00be6649c","impliedFormat":99},{"version":"1f7f05258c0992bd696cf00984e640011ae5477d7aac3b80fcf61bf27f42fe88","impliedFormat":99},{"version":"9129342b97e39ef2c9df4848dfe011329cef9b27e719c7913fd3859be5fc0cca","impliedFormat":99},{"version":"351edaf90b54a559e1759f7ceb54b7881079cba5f4d6dcf15bdb26f1877dd2c6","impliedFormat":99},{"version":"3f79205d951373afec1ca713cbda4be9816d97daa795a9e0a37fa3ae5429afbe","impliedFormat":99},{"version":"bcc4c8b5a39356915b8d366e3499a28adc89e2e0bffc02a108eaec1c4797a58e","impliedFormat":99},{"version":"5b2287eec9804a7fc7c6021ae0a7a92b0160750eb21604b77203589e2ad905f8","impliedFormat":99},{"version":"03870a19c7cbbad803b0ee2d69b777e12be7734e087ccfb0c862529a41cb493b","impliedFormat":99},{"version":"46a52d6ee42784826515dd6ab9f5afaab3a05dfb49ddd8298a2026b6c756b944","impliedFormat":99},{"version":"995f334b04df585cb2a77b74533441293ff1e1d4549c86dd5495494c1fc3969f","impliedFormat":99},{"version":"e83b7824f3d983e9b8c2785541579cd8d8c153e96959e71ab4f69bd83c71f953","impliedFormat":99},{"version":"7b0030262f3d2cc74ae1dd79f4990a7131c34935b2c177e6cfa17a88a6ea56ee","impliedFormat":99},{"version":"a923cde26c2e5431e455844ac5f31126d45976c85f347c7dfd2b9eba3e8ef63c","impliedFormat":99},{"version":"3b2738cfacb777ea1f53acdb26b4f4306fa3dbac7fc5d0f1c4750350d3f5741d","impliedFormat":99},{"version":"b95d11a17e57f0cd0ab04aa8148c8f0ca3a68f56c9a44ac9179cea8a6cccb546","impliedFormat":99},{"version":"7688f3196338007600eba7158240aaa15ad524ca42c204fdb3888446fd690086","impliedFormat":99},{"version":"514f33cfc8bf4a00d0603f6df438959657ce42f94e93e29df29fa9b58e7d54f9","impliedFormat":99},{"version":"7568cf2d6e505847c539e63406ddbde2ccc0f96f2e6c5f115a4b9774d0b55aad","impliedFormat":99},{"version":"d05bd9004c654c2583de473d77f047f03719e3e7bdbe62861371755208e36d59","impliedFormat":99},{"version":"74371225d6032ec7f73b46e736d9ff6ea3626be6fc7959e8b71fedff0bb75cf4","impliedFormat":99},{"version":"06dd247275efd44b3f91270763246700353f1add0945380bdbca8c90a517f9f1","impliedFormat":99},{"version":"0833e55be9920ff787cedb7ea623e97ac9bab28961e0e11aa4a56d36d6074dd2","impliedFormat":99},{"version":"92fbb2b6566fdefc6ba3f151299b2618bd1780cf26c2d0078dcd7f1bdc1c551e","impliedFormat":99},{"version":"3b5317db0574b276c1ecf6ebad9faa974f4e416786b682ed1f854cc85837c3df","impliedFormat":99},{"version":"5f27b1f1b03636451e90fc414bd8426a1db25ad438782354bea60f47d7efb9d6","impliedFormat":99},{"version":"53eb12cfe4c56afff32a3b8adec4fefefa12685c84202c8207351004d30c3b3a","impliedFormat":99},{"version":"05bc3698de467024d02654162f1eeb4edcb0ed9d855a96133572969a6f3675c4","impliedFormat":99},{"version":"a5ba4a306d8bc21ac2fef4e40e9076708dded0176aa21484f1f6da23a4d400e2","impliedFormat":99},{"version":"1c825d1f1bd9e70c306f6c16a0a6b76ccfe4be9350857831eba93e59b95fbb5b","impliedFormat":99},{"version":"b27224caf8db7ed9edf9b12368cedb963bbba3a9b5143c68dff53f5fb2351c96","impliedFormat":99},{"version":"eb3bfb8488f260946c5bbf5d9e730a6e23e0c4a568fbbbe782f3c365e0595dde","impliedFormat":99},{"version":"15de6ee96c8e0f6a78fed11e60c3a0f9b4535c1e6a802c55d65028d500e91e75","impliedFormat":99},{"version":"052f62cd94d56a5ca9d8ce7e68a2201fe8f399a12d7803be2619fd03dd36f1d9","impliedFormat":99},{"version":"06e98ec1e0428de740d985f3480b2e699826d5cd2fe2457f1265b32ff4797ae4","impliedFormat":99},{"version":"01b8daaa0be6124a730b7170c1bb1375f7ed6acf1b4b49c1389199b5ffb600e7","impliedFormat":99},{"version":"7b0d3cca9104d4d9f484ca0a64bf731ff1aea842c8a4bf93618814b1a8281992","impliedFormat":99},{"version":"e40aa12df628390fb3819a883c52c51ef94fd3998e74965fce6a38917a0530f4","impliedFormat":99},{"version":"0df397db19a2db183105dfe900d75798622677a5db73038608bd325f86a556ed","impliedFormat":99},{"version":"ab505b9c7ee7649920023b14384c71e3c542bc7535f51028dff27d70d2b1d6fd","impliedFormat":99},{"version":"29a9fb009bcc76c847dcf73d820d276d6353e5c6c4c016c847d51e42796f68f5","impliedFormat":99},{"version":"743751f2d8819fd7ac9d3ef6378614b6675d3101e42ddc18767901693621cb2f","impliedFormat":99},{"version":"e4a995fd487783122df0848df4c871dbb536e1636e8e7b6f6186d2993e9761e8","impliedFormat":99},{"version":"45da721f9a605485a439778c248dfbc6351341d87de448ec266b74185e090631","impliedFormat":99},{"version":"ec47a4e180f0cf61787ada2d4691a1cf4f7fd65482f6fa9e01444adff3cbd6eb","impliedFormat":99},{"version":"3209d42dcb86b35a13c127fc39981a644b61a1fb0e59524038d0f3bd7fe25768","impliedFormat":99},{"version":"c8c16f7fdc34f8bda36cd9827b11c065e94ae25473465b2b35aa71df336ecf63","impliedFormat":99},{"version":"db07a4e9f69cc9b58930c2d3a4ad1fd9f882794b92208d55ab057443081f649a","impliedFormat":99},{"version":"735a572ced293fa984b3675cce56091902a0529cef028fe016d9670e3a94dc8b","impliedFormat":99},{"version":"dcf0056dec8dc80fe76eac1e8c6fa778a2e4c094fe2d4b120e6f5bcabd820be8","impliedFormat":99},{"version":"6268a89f0ce2f857f6f7ada0045bf8dc990b449f648b51522c0a7d84d016fe85","impliedFormat":99},{"version":"efd3f26f59c3291a0998435ad54c67191b39b4cd0d451ac807afd8da86bc1996","impliedFormat":99},{"version":"19b70aecc85035f5faef7f3da8dcbf199af4ceccbce15a670950377b388c1d9c","impliedFormat":99},{"version":"d621382b4ad80cc27b2f670e44e0bb11a7e85cb0f6a0b043aa0c9b6b21b16a15","impliedFormat":99},{"version":"5c1f5f0c20f5171a182440cd0347dbb94e5c84f5976184f2f36dec92afbd9b9c","impliedFormat":99},{"version":"1a7e2345e3b20202800bc92adbf628d22a74902b8a5c87a6ce3c361d3ba314a9","impliedFormat":99},{"version":"eea9dc67b1bd75f72aad8483567241f5fdbe46436f018df7f0719e7ee5aa85da","impliedFormat":99},{"version":"5bf7ec4d84bfa8c29f32b7cde878e8ef4e11b1bbf0f4edbb9e851efbfdccbd2b","impliedFormat":99},{"version":"3a49927f72440d36c50e1b62f5cbc2f296253d151ea4e5484ecebc8bc461ad4f","impliedFormat":99},{"version":"ce293a2b914083388ff1de83875cc6e82792c5dc1e99c3be4b787f6b150516bf","impliedFormat":99},{"version":"f8d6e2784bb518d523898f614b8c0ae55341968c982d4617f08867b5d11cf354","impliedFormat":99},{"version":"1413f593b860e74f717f40bbf5c934fd77ee6cbbc630216954bc1a364d5d58a6","impliedFormat":99},{"version":"f7e358590496240e80dc08cd1b71ca492e4d27664bb403d3efbb9acef5075b40","impliedFormat":99},{"version":"656e30f229e3a05096b21a8d0b4a37cadda6201d74631fdfd6a6f52f0c158831","impliedFormat":99},{"version":"03206d1ab6b7f08b118786a903cf849768c8c927a21022df88fe63910ddc3433","impliedFormat":99},{"version":"702cb19c1b38ca1b2d158d765869b667b2c1e5ca0e62862b7792285055cb86d2","impliedFormat":99},{"version":"396f903b4d3bcc1d5a72580bb0a8d9f90c7dac5e481b81c2b58df80c968b64e5","impliedFormat":99},{"version":"0e8707f15586d91f92a120b4751048061e04fdee756246667158d4df0105dbe8","impliedFormat":99},{"version":"c37e7b3d6c0b5da08a46d028e980becdd8d48d7b32b7644209695d75d43f653c","impliedFormat":99},{"version":"fecc5365f9a1dd29cc8c582bc0427a7bf06a52c2a42cdb4b25012976628faa6e","impliedFormat":99},{"version":"0c073335c77c5ad0240a0303cae56c8be8da93e206591c5a5a8bd6a613d78d18","impliedFormat":99},{"version":"827c1178e5058f0aaa9047725b845d598f0f52871792441412059173f895597b","impliedFormat":99},{"version":"9e8ed20b5058a6f5f773f420c0efce5c8eb802c0af94cdb96b782cf2acf1b00b","impliedFormat":99},{"version":"8b0336c60458945b1fee185149fa4b5a512917aa171d4232b1f0805c3c12e31b","impliedFormat":99},{"version":"b15377bca02bd4d77f5d089fa0c7a13dc251b104de3b43f62dde6955cc8ef7e8","impliedFormat":99},{"version":"020409dbb29a4396e3c1c0732a0f8afa939e47c935182f6fd0603e21d5a6a8f2","impliedFormat":99},{"version":"bb259ffb75be8a11b1be05d135a561391f7123110d75074eeb5207be382ceb70","impliedFormat":99},{"version":"1f52c9be8dfb11cc31d9e2aa4f950ef56aa8eaef1b78949431882f70e10487de","impliedFormat":99},{"version":"1e685ffce849148fe9e9649189957078d9495608e9df42cfeab20367d2d70c75","impliedFormat":99},{"version":"97e30735672fbe25393231a53ab5e3b63d34e74d0697c59ffc034f9119c23d31","impliedFormat":99},{"version":"84ffde0a761e4b6cbf3cf90c97c4c01608962e8b55082f3705d29465a194f449","impliedFormat":99},{"version":"d874bdb89c1172b0eb109873d39175a5f210f5d853439e7eb250102622edb0d1","impliedFormat":99},{"version":"2b7e61a49cb27bbfc53fd5b888705290beb2d1fe78a8b433bac1ce7544113904","impliedFormat":99},{"version":"8b28a7039c2ccb5108bb3a3b771ca430db73c4ec9e47031303b8e87732a859a0","impliedFormat":99},{"version":"156eb4c6ef17eb61507364b320e2812cfc5afd862cb1baa251b2ab412384a2a9","impliedFormat":99},{"version":"9efc47a0e98346bfd4b386050634b4e150e6c41dd6d9b2bc1288e80a0f345390","impliedFormat":99},{"version":"2bd0a3ea02475382ae8e87d78e3be763dba251ddf9629664ce73c706b400dc94","impliedFormat":99},{"version":"20008d2327e19c4fd051a2c0ee88ea696d704bc6d7ad39988fc509d81c27a485","impliedFormat":99},{"version":"6cee28d40bc224e61f12e867140ab6d677a03a1defc9ade08b1bd60ab1c06524","impliedFormat":99},{"version":"3a0bb28315b2084f25a012275ef45e180ea80d9ca4bbc37665b9c67e912e998c","impliedFormat":99},{"version":"17662ae9763596c2ddaa833f9e326b3de9289098a71457ee18d2db9407cc681b","impliedFormat":99},{"version":"9442dcf95088615dd8ea58077ebed1f7d5dd662caca210b245a6b19f38984038","impliedFormat":99},{"version":"baf0ad4aa9df446c5b08370689dc08e23e112fdd1a022293676254fbb7897a47","impliedFormat":99},{"version":"e3a929f769e33c3001244a06d6a3e025083be64599c1e961aee31145d623e824","impliedFormat":99},{"version":"083493311f28114ab250a8f379798214e91f264dce121fa2140ae58376fc48c6","impliedFormat":99},{"version":"fd03b3ac929f2bcec6710176bbcdb34969d7f9810b01f65d19cbdac143a2c7d9","impliedFormat":99},{"version":"f3e2f84bdacbe962c856add41824ccfd66fba7b320753f6e9c6871cd6fd5133c","impliedFormat":99},{"version":"d70ae743099d2615ffab06760a3571a2beb01fcb27366cce4025544603a6081a","impliedFormat":99},{"version":"530fcba9474606ca2eca0b85f91b26d5e24c31431c27d20403928d51f9c1931f","impliedFormat":99},{"version":"c483babd94cb2effd09a918f5cacae5fbc8cdcb8b65b1a28cf07c2a9381f2a0d","impliedFormat":99},{"version":"c808470b50113d547da502f2380c6674fd41908d641663e5944a6070113469cd","impliedFormat":99},{"version":"f0568ac6f1c90cb01c4a2b3d14c0c6e734cfbfa34eebc57d789db55e7d0d34f1","impliedFormat":99},{"version":"0ae4ff7dd81505058a06f617152c94802f16fc7a8d2f768c8794f53f8be57178","impliedFormat":99},{"version":"3f61a28c42e990b337e084e92d7fa7df04f8a6b6699da3754dc59611d189b40e","impliedFormat":99},{"version":"73fbbf32113d791d019c474cf474344bb36d4c375f9622728163ad5640492a39","impliedFormat":99},{"version":"91b6fbc14c8a81bc1751cc033f55e0cb6f3b346653d51e30efb7995ecf969ed2","impliedFormat":99},{"version":"77e2fd9131fc81ffaffdc85a8ab553f869f2a67b236ceb95b85b9a1bd72b8823","impliedFormat":99},{"version":"330213ff23c7adbbb6f1b5ead11fb8dfb731c5c24f8c4a18586acaaa47e74077","impliedFormat":99},{"version":"a9f07992ccd51ff2a089628480d51364e19be7e5b22e04edd7e18a519c50e2fc","impliedFormat":99},{"version":"26f62f6b63fff6ad7abd3fc5d89d36f8c74f6ddb32d64795556d0ad3ac6b2d29","impliedFormat":99},{"version":"30c55932c3859c15cfb16c4cf3cda9c303588f3216f8b1ca205e2c41bf801402","impliedFormat":99},{"version":"b727fb19b28fdd8abf41b989f9ec0a6aae52cf07f3918386ad068b33d20c3468","impliedFormat":99},{"version":"d7fad08d42a437ea163bec1c3d08e5e4714a27636d89809602f04328a54a3fa4","impliedFormat":99},{"version":"22469dbd699381a169d6e02d5c080ba9d94b9d6567b7a5c41cb17f505e6a4ad7","impliedFormat":99},{"version":"44412e7238512522c472296f100c52c0accc20d3ee75db7aa503ad4d92b80754","impliedFormat":99},{"version":"27c4c4f9114b51cd89d2ba83e9fa60bacc6c29a1279f2f3b91d19c2f7b2c68ac","impliedFormat":99},{"version":"6acb809bb284648297faaefcb03e0e4500de5f78194a08b75512e13e5887829b","impliedFormat":99},{"version":"c31062874243eeb47ba70f53686f860d4c238bed5587af12ea4f73389ce2333c","impliedFormat":99},{"version":"b4f0992a1069bd5af311d02a49dae7aceb5e0400856449bd766b994267e2adba","impliedFormat":99},{"version":"adf2b0d2362e1b4c99336c56293ac3da8aa0d3ebbda67f963d4a0f3d3ef2a021","impliedFormat":99},{"version":"5fc3d9350eb34ad3cbcb1b69249161a33ffe19d7c0e72e6087c947046de6f756","impliedFormat":99},{"version":"b0418e08aab8aa9e4e406428964d2adf8187dd29f6cdaea32ede28fc36e86f56","impliedFormat":99},{"version":"be4147ddded6518b57942a23f89b50b772d841a97e22c93b70eddf901c7581d2","impliedFormat":99},{"version":"6b952ce628d71b1e1644cf8aea26a4de997596197158dc7b6e71ec356a8cf992","impliedFormat":99},{"version":"2f4dad0e02e51c0d630d46dd18b3a99a1d8c9f184af3e9d109027d8d11735f9f","impliedFormat":99},{"version":"41c5600e8662d67b2a149c2eebb422c80cc2337945f5b79dde92d41427499496","impliedFormat":99},{"version":"9c1e78acaead99ab9c612e54f5e16c0675cb6863627ec2dffa0c3d5651d53659","impliedFormat":99},{"version":"6df75e65602bbd54c977312ed62988e0c64423b046ed74ca126b529970233a2e","impliedFormat":99},{"version":"3ffc0815b3b1da65f6fc42a2a10aece2bda56d024cbcf7477b6380d4249ff8a1","impliedFormat":99},{"version":"7cb50c74ced03d93407f80f61840b52540cdc0ff7189ca603e6995306c2b25c2","impliedFormat":99},{"version":"a44dd85a5c1ba838eea01fd555504229ee74d97b1d237741598e7d97c0e857ca","impliedFormat":99},{"version":"aa7f2b3a9f4bb8a225b3a5e5c611b1a034ad76c3d870a1062e241485e3968e23","impliedFormat":99},{"version":"eb41d07bb7e2d527ac33c71146a3a4802a24d39defb6b8e4d707e5510074d076","impliedFormat":99},{"version":"405bf967f547561f6810f2903df5c5b3c7528d55917fcea0cf251951bedd879b","impliedFormat":99},{"version":"0060d5fcac50ed959be8765d1f5343eda5641109a62eba69696577e004b891d0","impliedFormat":99},{"version":"def4730fa85f358f1257bf2116242bec72080b1a0c70046d0c05ff7f90164707","impliedFormat":99},{"version":"5336f4657e6ffcc8bae26bd762b09b80ae6e3b67dce0a4b4aa99f5baab00c65a","impliedFormat":99},{"version":"8e728eefe8c7160465492dafb86f25085ede8c6b05e360dd2c7129955a155da8","impliedFormat":99},{"version":"c665cdd809976f388c82e21c47a040e5e19ba6cb953d0e0c1c38e1ce61f40922","impliedFormat":99},{"version":"c3bdb6cc2b1abe32815c4894c4d011d4ea80c79d0934d264b467cc6ec0051bcc","impliedFormat":99},{"version":"d40c02d227da200dd6be4e7d56ec2c560c08b9e24a4688a071f392b37953143a","impliedFormat":99},{"version":"01b9b0a56a739482aadb7da55886fa724bc2b557e9814ef4841d2262efb9846b","impliedFormat":99},{"version":"891117d566ab7e1a7798d83c58a20957c1703e92d5a351802081c643cf58faf0","impliedFormat":99},{"version":"6f925dbb5e83ba81d632287af1706945f435bfaec89258540eaae87817804c84","impliedFormat":99},{"version":"0fadf459265643344979f57c02e7ae5fdb5c70244fc9ccece5a1a977fe0b1fb8","impliedFormat":99},{"version":"0e77a1ed700a09eae143529750cc2eef65b8e28d76cf8a6eaf78b7f1afa24c63","impliedFormat":99},{"version":"5408800bf96b2cdd0d8d77e3d52f6848514efbf1590d96d9f8aa86c8ee95bbdf","impliedFormat":99},{"version":"b6e0ad0ba28715ae23a61b1192cdb24c06a909aa58b2048e64e56574aa4da7a8","impliedFormat":99},{"version":"c20b3e5d792dae26f5bbf8d1b73ddd16d9ddc336e32a301b9dd99c68a779f61e","impliedFormat":99},{"version":"d71713801d5419399f8edaaf0471dea5e578dd8b71eefde7abf387fb372feb1b","impliedFormat":99},{"version":"89b56bb82308d69d9ea109de95fff39ef64bcabd250688da972fcea05f50dad3","impliedFormat":99},{"version":"214f90578d41d0f5bf61b4d3de16b4671dc75fe893b803a483a4e7b96e80a1e7","impliedFormat":99},{"version":"32b6a2a6fc20f85513ffb0f35e455dfbaf058f65f063a10dda07ffe9592cd98f","impliedFormat":99},{"version":"8b2bdf89d903b856b52e4d416930701068a3522e9e8c2705602c6e7e2394e86d","impliedFormat":99},{"version":"c988c702e73a0ae03ff6d7868ebc2dd0497e921c3b7ea4fbde42aa781831b8a5","impliedFormat":99},{"version":"8409e2185704c03d12e1522dc4c7b137b6b7524e2fc1f9baee7581ee28fc3d86","impliedFormat":99},{"version":"95195cfacab74280a41490ca2c731fe499a37d7ffcaeac7dd2d9056cdc694623","impliedFormat":99},{"version":"9c8746b57866938dccd94775ccc3abe27e41d182b6f6d32ce82a1044084d3778","impliedFormat":99},{"version":"be71dce0024b565b17433b79dfb73c200bd087568e24e796d712cbd42eebf8cd","impliedFormat":99},{"version":"4d9081308548bde06c710ad7bc3af5e6d7e24538378a4c10eff2e769dec31bd5","impliedFormat":99},{"version":"09e693240afe609150a21882e64d8f34b664eea485d16ae78ac86cb3a47de3f9","impliedFormat":99},{"version":"89502a94ed72858e0018b65766f8deea38577994b7df9d406afc47224fc259c9","impliedFormat":99},{"version":"2c19973a0dad8e650d42349838ecc7bec9e181c28f7aacfc045eb3c0b8c7db19","impliedFormat":99},{"version":"851bba631a33a4413ce53ca3586b8a2d5799d0450207e8f7f9b594340e8d0af6","impliedFormat":99},{"version":"9772a1a4b6a4a8c16e2564c0d83848bc92c5410378710f9da8fb2d912ac32b57","impliedFormat":99},{"version":"ae66b8a49700f9b0e1e857eb7989a033392b92b5a19690c9ed7f8f403a1e219c","impliedFormat":99},{"version":"a095cd74b349b5c587c52343a00871d3a522d5d00614275a608e5c3ff690468f","impliedFormat":99},{"version":"ce2b17e7bb13676b9cfe8b9d71db509625851486b845475bf336e2c6f58a2cf7","impliedFormat":99},{"version":"68ff0025b0ff8a90165ae54d417191c8dddf93c794fd54fbfef6d4ea75f6ca82","impliedFormat":99},{"version":"6ff5a35137457c0c733501de9300f1801ae9abb33aea7bc9c6bf5e9d6d98cfc5","impliedFormat":99},{"version":"71128c986c2bd2554d203c724e897471277d96efae9d67721835e0174bb19a97","impliedFormat":99},{"version":"5118e5ed493b74299ca53eca1e5a422fb8f3207337285fe9206dd5d1f88785ad","impliedFormat":99},{"version":"aacbc0d9b6f47db9784a2193fcc7f4bfb1fc6cc711587a4bbac43e45432332ea","impliedFormat":99},{"version":"5be892a93003f44bc4420408ec0726322928020fe22f9a68264a176dc4eb8b96","impliedFormat":99},{"version":"9cf47cb5d151b9a09d0d2fed8b5858d726cbde497560ccb136557aa203364208","impliedFormat":99},{"version":"981feaf9d706617834eb318674966a8741ea35c93ce33e0ce155498e665d2593","impliedFormat":99},{"version":"8e661b24aed6caeef42e16eba111174c13ed178a660b41fd8f82401dfe129515","impliedFormat":99},{"version":"74606837f50a3a16d02993364c004db527b47cdb828edbd770595d5e4ee8dbde","impliedFormat":99},{"version":"d7e7588481cd78747b1d6a9439feede87c2e497df8448acc74d9803867cfdcc9","impliedFormat":99},{"version":"a46d60895edd2436d8927e02798c82975267d0b6fe3af28d7596177f23da3639","impliedFormat":99},{"version":"f93b561633fc4bf5005f34f0c2f96f48c3e548d1593136cfaa9331d7294ca417","impliedFormat":99},{"version":"d64b9ad5dc93f6dc86e1c13f5e483583597b35fa0ad3170c928e436253b1a252","impliedFormat":99},{"version":"23bbc076a14d01df086f77870c735b053cb1c9dc07c2c8b160f6a04db80c469e","impliedFormat":99},{"version":"7f1f69fdcac775d124fe626182219327b833a962de2c9751073d2643695ce2e0","impliedFormat":99},{"version":"ad774bd48cdebf2909e354cb24ed9ade7763306edda185b7692890a2aa96be4b","impliedFormat":99},{"version":"f53c345523d49bc3e6a11a5f6540ba145b441af3618efaf58d25b58db03d2922","impliedFormat":99},{"version":"164af37e5cde8d2d830b5a5f2aaa6be547b8004e4e98b33fd6977581f8be4d4a","impliedFormat":99},{"version":"2aa08243d9c596b3e993b558033dd391f39ba4d6525ccba992b11bb5be54c04e","impliedFormat":99},{"version":"208371c97acf811ef41ba4b217816aedb802a570129042463b198c7d72d1cca1","impliedFormat":99},{"version":"6311ecffa1680ff0f9587217df76d9556d4c8c623f12464b8beb44461d1d22af","impliedFormat":99},{"version":"16e2700613d061c8a3c21fd26bdff099948396954d5935ce913424d93c97815c","impliedFormat":99},{"version":"0890d6e6870d35b625590a98abc2bd3fa880fa46d0dc3de22dbf01628cfd34b7","impliedFormat":99},{"version":"193814fef68f60058efb9c02cffd20bcbf70eec1d32ea0fce4b5887aef746157","impliedFormat":99},{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"51954e948be6a5b728fcfaf561f12331b4f54f068934c77adfc8f70eea17d285","impliedFormat":1},{"version":"2fbe402f0ee5aa8ab55367f88030f79d46211c0a0f342becaa9f648bf8534e9d","impliedFormat":1},{"version":"b94258ef37e67474ac5522e9c519489a55dcb3d4a8f645e335fc68ea2215fe88","impliedFormat":1},{"version":"a9ff5614fec6e47cd306851cd39e2bb0bd1b939a9776cad032bc06753a24b105","signature":"2641cc270e66b5b412cf0f887ef90e12173ac7773390a8e0008f653358f66841"},{"version":"709504c4a347b021a9984ee3e65359992e9f0f172d22e63030207d0c604296d6","signature":"b0a30a6f3075e34a6a108ff4fb8c54e7714f964c0690db0b6e82bed93ef6568e"},{"version":"c91f4f63d6c2c84d2e0f7368a97dfb126ac74804e775e5aa5bcefd853917e69e","signature":"96d032d99c255b941936f513419610586f7e642f2abb57d1b8d2581f7d442eb8"},{"version":"a313760e9f66c6f819c3426e038acb9aa8f47a59be74062f51321caa88a688ea","signature":"439593d167651f2e1c0c439482dc3d5d5eb248ea221ecd8feb5c62cd0d60cd86"},{"version":"2c82ac3566fa4072c5cc6320a0a786afb9d27c061d41316411483f61353560eb","signature":"e0d9f1fd5544f50032be81792d9409f65c8ea46853ed0450b9934372d4255930"},{"version":"829b5cb87df9dfb327efb8a4e55644d809f3e03de209067122b99ffebf284f00","impliedFormat":1},{"version":"415510d38ea33f28cb571ef11ebd6ee777a377e0d1886b6771dcb15fdde7a02f","signature":"5a4e0d921d1c64c046a46838efd87367a659f8debed6c7f7801b8440576657de"},{"version":"6e9445b11a3d075d64853d8b32efd159b4a45f37b481bbbb7d3bd57f5a5d5f35","signature":"589cdbba6bdaf20ddef1fe78e3bdedfd4e7f6b6e08179a9d8197ded860ebaed0"},{"version":"80d23e36921e787529d3ccae753675b91180dc2326b4c1a3d8f270205b85af79","signature":"845a9728a8fd9284d40c63aaea7b11076866271659517e0ab1a1cbd041bf8588"},{"version":"1aadf3c39d08e4aeea1b9950040079b0fa8baa1d5f9644667cbbb6b9c8c0837a","signature":"730f18e9a86d7032845d6a326f8c5ec9469490304565e2a637f4dbdd8db08977"},{"version":"03a87f22d5567ad70a9761d76f0d16ca6ae32b6201d79c4946e751f2c4cb4e8a","signature":"daaa96af8feb9c538eac60042eb231ecb684bd361d5d7d5fccb0a614a41c365b"},{"version":"07e1a6c1c01468d20ce760a06f39c9e59c56103e63556c7240bbc9e8c2b8d24b","signature":"e6ec95dc819ab75e36c9e4492ba3e6bcf21507403a6afb5bbe8cdea76fd77fc7"},{"version":"e7205096e87497cb983cffe2ea271035dc0f7bae9db702859e5a2d0941d99597","signature":"1a85b0cd6837d60863844ad43f065863cd13b3cb956c369d493761bb603f4b63"},{"version":"47f5078d810ecb6e57eea5f0382dbfb9db641a35460fdb723e920c4898852e0b","signature":"df6ab0ed5a36c6500e0cd4e0928f73f80fa1bc047359a22f5023393f4023cdcd"},{"version":"21e365e7414b00e1dda3cb0e8c1ffe7eaf8f4cee8665857e7a4ab0051c694811","signature":"d36c6cc5adf1dd3c897e4bfe96cfc0506c9352c7413cd83da0d3032f820781b8"},{"version":"800de8bb8ea525980e16dd155bb6e6847e7fdeccaf816e5c2674e1a24c5bfc9a","impliedFormat":1},{"version":"88efe27bebddb62da9655a9f093e0c27719647e96747f16650489dc9671075d6","impliedFormat":1},{"version":"e348f128032c4807ad9359a1fff29fcbc5f551c81be807bfa86db5a45649b7ba","impliedFormat":1},{"version":"8ee6b07974528da39b7835556e12dd3198c0a13e4a9de321217cd2044f3de22e","impliedFormat":1},{"version":"deefd8c43b40f9797c3921d78d3f9243959621a17b817be7f5d95c149f23a9dd","impliedFormat":1},{"version":"5f12132800d430adbe59b49c2c0354d85a71ada7d756e34250a655baa8ad4ae5","impliedFormat":1},{"version":"ec27c0cee1436f58e785f621703d19d588ebbd489eca245e5198b4d6b715790d","impliedFormat":1},{"version":"b16e757e4c35434065120a2b3bf13a518fc9e621dc9c2ed668f91635a9dc4e75","impliedFormat":1},{"version":"efe2821496a760b9128309bb69ad43f1a99feb49d3fd004673c5e406de523da6","impliedFormat":1},{"version":"ea0e3c7d1347a549ac7ec32d3c61a30e473dbbbc901d458064db03f673128145","impliedFormat":1},{"version":"4374cefdde5c6e9bad52b0436e887b8325b8f407c12035194ad02c28f1553a3a","impliedFormat":1},{"version":"5f1ba0898eb0a54a644cb9c95c2240beaa961d87fd080cbb90807a6cc03daeb3","impliedFormat":1},{"version":"8e92ee8710ba85b158c5d91b0bbc9d0d033f5e062b6e70178063f01b20f63a14","impliedFormat":1},{"version":"ee933420aacba1f60aa70fb8ba47c5e69001b005073b71973114587089a13c7f","impliedFormat":1},{"version":"0a0714999d0a5bdfacd15c7b34cffbcc6f263f6cb0ccb42076cdc541c6987797","impliedFormat":1},{"version":"56584bfc655f9df64afc0f22f7d1122c29e5b74b342c203b891e19de9fa37de8","impliedFormat":1},{"version":"40ec58f0fadd0b3981b3d383e1c12fa0680115ae9f018387fc2cfc0bbcf23204","impliedFormat":1},{"version":"59709e26e08d4fd4c6a133552ad8f94c5b31463f295c4bf75fae1907738b8441","impliedFormat":1},{"version":"849b9e7283b7309a4556c9b90bb8e2dfc27751f157798065bbc513dcddb09a8c","impliedFormat":1},{"version":"76bba0c97594248c1be19af32d5799f7eff51cec2926d8e4dd59267d7636a0b4","impliedFormat":1},{"version":"10e109212c7be8a9f66e988e5d6c2a8900c9d14bf6beadf5fa70d32ada3425cf","impliedFormat":1},{"version":"f4558bcdc26690cc593cd59217cd17d8e00af0f5fbd0c4f1c0d71ba75029c42e","impliedFormat":1},{"version":"51d621c4e724720dd1b7ba6374d8a5b988beeda22d620ac84634a13691b631d9","impliedFormat":1},{"version":"f57a588d8f6b3ce5c8b494f2dc759a8885eaee18e80a4952df47de45403fedbe","impliedFormat":1},{"version":"34735727b3fe7a0ed0651a0f88d06449163d1989a2b2de7f047473adc7c1c383","impliedFormat":1},{"version":"a5b13abc88ab3186e713c445e59e2f6eee20c6167943517bc2f56985d89b8c55","impliedFormat":1},{"version":"8b29e3ed0c90b2ebc40b2bce5a518a0e86c0c417f7fe99a5e7658a61166bd9cd","impliedFormat":1},{"version":"7ae65fe95b18205e241e6695cb2c61c0828d660aca7d08f68781b439a800e6b8","impliedFormat":1},{"version":"c2c8c166199d3a7bd093152437d1f6399d05e458a9ca9364456feecba920cda4","impliedFormat":1},{"version":"369b7270eeeb37982203b2cb18c7302947b89bf5818c1d3d2e95a0418f02b74e","impliedFormat":1},{"version":"94f95d223e2783b0aef4d15d7f6990a6a550fe17d099c501395f690337f7105e","impliedFormat":1},{"version":"945be5a9505194381cfd4a8551a5f0ae48090847e454fecf834e054207c5a57b","impliedFormat":1},{"version":"d1e8b78a5ce49cee9ef4cd2565d4645d269c6fd0650e3592f85ba481f13da3a3","impliedFormat":1},{"version":"61be8f1d5345cf5750aed87af2869888ca1b675ffa481f1d4d80554e10084b4a","impliedFormat":1},{"version":"eeb6c806376b9c3464f29b6058aecf113328f9ce290af0375e520f1a844529cf","signature":"61f11ef9f7b473f14c872a139f0a329251738f177edfdcaefb3745adf8967036"},{"version":"216c830de5b7e1ff7336a1bf11dfe9c98ae2de2da56f616e7e4b4405aa14050d","signature":"f9653d5c0a8d7199894c3721eae87d898c8ce6668c3c28461dde2236367c94e6"},{"version":"ebfca49b6f505f572648960feb0bc5e131c9a6bea97f3f5883dfa9374ed4028d","signature":"5d2270355cb77cb6e68e65ae1d5c258abf97b4845ab9653f0ed1626154bbc114"},{"version":"57bd6ff2a23d6d3df5b81817735cb34c18e6c75e13425f9915d146273a27e776","signature":"a1f8f9135feda1e04f2f32d3f5fb9ecbdaf2dfd4fb30be0d1aacc901a55929e4"},{"version":"b16d890b0ea02f67586f064f87af862c601884fd40ec000b3ec8dacdf1c4c7cf","signature":"c4bf08d84391225b229f7d67fe8f7b3ff511782f27e0d6f5f4680aab2cf451af"},{"version":"ed0300836934be9970c950c0d485e8276fac87cf1fec92f07e5f13fb7203604d","signature":"e3d48af43b4af0455edee6944467120f4272a8306e90d504935da490b053cafd"},{"version":"a7e4a0f02427c3e07643a6d5bb9bf0cc09f2ebe28b37a42189f923133c43c186","signature":"01279e64b86fc37995c2df2f8acd601c7126eed6c6245b1e913a0eaa353f4362"},{"version":"5a6ea8dd0a1da3b272eb9ce6c258c6513b5473f7ebcb2395ff7ec7b352b61e93","signature":"0b02029cc6faaf310eaddc6a996db9c2bc36df3075ae2318bca2847f71444afb"},{"version":"172445546b246f00923ce61b907837020174c84335bfa24cddc78b6a5d28d0a3","signature":"b34528c74b3ff693ae3d27488992d045d0da79151d70e3240ea701f4a8910b5e"},{"version":"a207d5278346c5ef6ea5ce0b34dcb377bf4cccbd7153ab83953cee72c59ab34a","signature":"1dd308df0c17f9580459e35f573f15a40609c032465913c8d86a10883edcda1a"},{"version":"f299ec29ad652a02319d39bcb58adf0803a2bb2387a025aec1a0a16f50519176","signature":"9093242bf5a271587e65352246412d050ee6cca17b21bc0990a7fa7f0c5ae5d3"},{"version":"b53aedd97ce9ebdcaf94f60bb120b172149f79045edf37b22858ff10bb61e09e","signature":"ec13261703b24c5ffb56fe30e3d7b64fb29d7ea5fbf548dbb3440646b65e1316"},{"version":"e8731d253cc0a26c73597bb44242c9dce9e7b2c2e3ef9fee709784f301b90ee0","signature":"20e6bc4b20ee7a3a764dafd105809550487d4bec3cefe2eba50c030422d59de2"},{"version":"39a31ce781dfe7a75500c7e3b106b91257ba43c89970570bcf2f74b3b0852145","signature":"1b402b21cb6f47459c223905112ea4d3e37b3be572fd633ba9786b760bd4a49e"},{"version":"d32fa498a8aeb58c092a884aff775cf3bbc2c734cf62a6d3976dbbeef663b4df","signature":"49859dcca80078ff2d8839e53d9a1aa9a9f89ec3f5951123bf04b25bf5b241da"},{"version":"350fce1cf0e1ebb8beaa4205ef82d2d17c0a963bf17829198d7f2bab3508701b","signature":"c50b967b871d37bca57adc25e444664be2536774dcf45576625ddc4cb0711f38"},{"version":"e63c358caa122f2976f93a564b2d27772cd9a87cc8ad3334a4796f89aeb17e85","signature":"2d220697a8464f714de95c5dceacb017948d1ad9814160f51a0ce1a9a98b8e28"},{"version":"6e94f91e94cd57bc68b891cac44e2ca3044f0242761a183c07dc3c5eb523aebf","signature":"b8b8943e02f90219b68ec7d495ecd78c6f8532f3794df792cfa1743da376a71e"},{"version":"1251b05bdf0f9d8118139c3ed7bb0ea60466ee664c2d58e710be9b39032c6f6c","signature":"f2c6a00624f44434d49aef27eac8b74b150c4ad7ea531992cd5ec7b61cff698a"},{"version":"5bcca0c2e2f15929cfa8e0d91bad9f61ab85e7f256377ccc56d6b0f9f8552960","signature":"d77db17aa371d965761001b744dba64792f22b53c0a9ddd4d80d8c8b359c482b"},{"version":"641984c05f82a6e0b8dac973196b8ba146f1644b3706d318427096d844ac4f0d","signature":"eb5c97b219f68b8629c278d916c59c82b514b848ff10eb0db5d4196d69654147"},{"version":"064945c8a414c7a78b237a277403afd2b7ba4bb433d8cdc41fde3cddf09880f4","signature":"20bd6d8b518e6345256f0e7d38f412028f1c31d21376c07a4f41e3b65d0efdf1"},{"version":"5323f2f109370900f8d4f85c82ff47df76a7d63dbef322abf601217e4e677086","signature":"f59baba97905164ae2797a2a2869308ff3435aa1c66fd33034c0237abeababe1"},{"version":"1beb3dd4e06334a36673fbdf6df977bb28d28134285a21da8584cef98b0e7c46","signature":"1f03749fcec8cba452cffab3b318b2bde43ae572704f238a8d8f8ae059d3b86a"},{"version":"f13cc653015347688208b6a2817d6a024cae82cea1b2be422061ae1fb40e86a3","signature":"9d34eaf37fb26f7e3b5d52527e1cb097956ecec3deece2590b99f360ab4428a7"},{"version":"cc319ff8f06a331d4b359aa39f43dc18f8d4402f1f3c446b22a197389c4067a6","signature":"a6d8aa22b2e3abe3192321c687b18ff88b15d42a8c3165a2ceef83a58045e9dd"},{"version":"3c27fb3f66fa5c3798c663843ad30a16957d6cf39d4c4ee8c154dc03b777bd80","signature":"ad4ff92dbea4696533340e64c444a2c6d93c4cc8f12fe2c7017af7d0eb8d2dba"},{"version":"0c50f7da7e287df66e69485e4e5b56c4a0fb9f8730571541873377e7ed45a2c8","signature":"987de9b3dd9352f138928040bd0776e179cdf67c235a18bd54580bc4163a2999"},{"version":"ae5f21d33e9cece1850a7223c30edff9bd2b842b05492b4d1f5c74891683186a","signature":"1bc767c3ecaad8c2a205ad502ee7c4f20cffc11447fadbd4ab3f573481073582"},{"version":"bcbdfb8a08bb7ace1743deca0384743dc28179759d8d856455913401b3e3f2b1","signature":"cf836e95cd5f7ff40aece41f706a0b25b70f4d6ed2d4b82a65f01a41c6e19745"},{"version":"0bea811d597d5b10907dfb36a49c188fc9b1d7ba4785325447e1c78e0eed2d7a","signature":"b5ace5dea1bff2d9f60db27e5b08c348733034474a3abbc97d47cae81d167f9e"},{"version":"721652119aa07fa7df69fec15bb05e7818c69f4798e424b2a444889f482d5118","signature":"c4b089b69cbaadfddd82db7f25dbb73ba7125d4d401edf45e1aaa9262747a529"},{"version":"31f74c987ac1c8dd1bd2a84a270623b054c1fc4ce81a30eb788ce3d579d95e40","signature":"58a5ec371db12fd72d7b69a8c237fc87c5a131763b45d262a3d191c5d1356d6d"},{"version":"7efed9d38ce35662483150baaecb0eb98e400391ada29a436626063a3cd09be5","signature":"56e3f4727284e65c0f755411270bbf10da22e3fe5529baed216b93557b41276a"},{"version":"b3247c06acbd296275f69ae7aaa4572cfc9228e70de48b19ceb4584247fe05c8","signature":"a6a5dd455139bdffd774acbcf9371280adccee55dc5dd44c7eec8e5a5ac9325d"},{"version":"3ac434b15ae79961bd05e34522420d556094a115c238e97824bb72474b367fe9","signature":"02fca86b2afcbe184abefb4b157f0d795eb0062889ea788cf1bd7da67b76eacb"},{"version":"ca88391208aad15040edba757c64498bdd0ae0bcdd591e7c8407621501307108","signature":"6439ab6cac63fd2353c93b1795e267135c30379faaf24c595846710a20c9fa73"},{"version":"27f4208f541705c9e22a604a7ad66a4b03eb6132e1014489689122c33ccfebd7","signature":"bb26db505bc0e0cbfa1d5618a5ca643d928fbfc0b012df4c0e111c96748c6acb"},{"version":"2b3c21ba0eaf7f23e3d91a4d8871886eb43cfc5fe4af69508ea9109119417e2e","signature":"40ec6fbee6bcb9edbe5e5726919d09de143e600a85ed019f4f0647dd7f946032"},{"version":"8b571e8f3e760621f044a9dee0ea495b7cf5da82cb75828992ded169b5cefe9e","signature":"d0948979f76da0bcd6723ad5fe1ceac5c6e7ab4dac9025b278f4ca3119f835d4"},{"version":"10f72058876ff0b07c27916ebff1c9a5e350117fa4632635b2c18e4e287ecde8","signature":"751ba76f055fbbce30097cba1ce5f5d3e6453224fef819a7eaf7f0383ec9310b"},{"version":"3caef034a84be3e33a76222b9eb965906198d8aa25cd874ff933780f55869f18","signature":"3564adb90073a7f8046f6fb26f3a00d86a7681f0bd8433d7fdca8ee81f3a2feb"},{"version":"afa6b33bd7d4bb9694536653237820f279876c907e0fa0436e14f3b5b09e4310","signature":"14ad6a418f276ae00176848d46d92337e4bda91e388663afbe5cadc4e74db63d"},{"version":"2cb5bcfcddafa73663cc7a0b9d07913ff00864af96cdf56ce809d55f80a1753b","signature":"f543efc561c3e8efe2d9061153ffc4a5881bbd1727d13e2eb8f3afd8f61a023c"},{"version":"8605ab3907c8332a03b0fb2bb8ecb8259321c15adf6ec70b4032b85d771cf2f3","signature":"06ae795b9ca99a2466c46639c2ab809198e6c67d400165f05424a012b1bb817f"},{"version":"0b596ac641129a560bec8f495f52adda3c82d92b1a115434a46e9c48080c9157","signature":"19485a0daffc617967e78d145ebf48193c8b2e01162a202afb02bc4cde9547b3"},{"version":"4a96c42ed30bf77b8c127453fda697a212495a055f6dcafda400e42eb233d029","signature":"c4e5d6b5f65bfd77c192b36ab608481de02288d42739f978b0c01a812dc94321"},{"version":"d2f9a9603312036b4c99561d7ad5049bc3e471140cdda96dc8d18f053c81a06b","signature":"48e6c8e53a8e681596e770eb3a65c70c14302492c4cbb406c67581059091a194"},{"version":"63cbd997e6553c877bbbfd11bdf92e99ce911bda174e6106d6597dc931f126bf","signature":"f052f97a9028807638935bdfd4013cc7e5702556e87ebb14d0a5883d33520daa"},{"version":"b5688bd390446b7c8e669650489199cc24b497853a44c358b45e23ef6a4ab84f","signature":"4211d664ea3e6cdf079df95c3d542279138d328db6a19c6f0916b201ab920d89"},{"version":"000d2e7631f4814dca372a4cb7e3fda4a0197d3a22723e3820f2ae3f74821e08","signature":"6594f6c4fb747b7405701aa752898fded1a76f395aa9c77ed615c28d848c4b44"},{"version":"d37220c6451fc2a23f0f15014dcd63a9a75816d5c9b39d8aaf4a69e09429a5b5","signature":"fdcbd6c4e023a4fbc39b4f1161838845e2d7491dd46a26fd2706675a13fa4e94"},{"version":"1d01e6bb5f169a300b055a8746fa89b35c851d3d09a593bbe42dcf69ec526e17","signature":"b38b35552f23ce23723086036788c7121e840421b6dbd402bf865cfd5cbc790c"},{"version":"dcb53ab6d5213ce22d7073c947756ed346f559ec36284f6c33069957cee62c41","signature":"2313b8fb3f40f12cd9c0f285433be8a4e832ca14383d5eb72ceafb7ad4eca47c"},{"version":"093bafe4dec3ab6fcf9e9a5753856775bb1e0fc6d032a4913a8d847828884271","signature":"634bd3ee5a6e4ba1c1671ed182dc9969b7a429116ba5a3cc8bc899a8aae2e8f4"},{"version":"bb23c7b441db38d447145cda42a252dd88d0ac4113dc27e43a3a7db35524bda9","signature":"c4e6581c0c2bf8d017173140969f491108dcd5784f12ddf140da8b0daf20ac83"},{"version":"e7c2f40dc99121500ad108a4f86541d29cac105ed018f994c7c5a2836e77b257","impliedFormat":1},{"version":"90e930283286ab117ab89f00589cf89ab5e9992bc57e79f303b36ee14649bdd9","impliedFormat":1},{"version":"6d48a6c907c668a6d6eda66acec4242e367c983e073100e35c1e234c424ad1a4","impliedFormat":1},{"version":"68a0e898d6c39160f1326ef922508914498c7a2d0b5a0d9222b7928d343214eb","impliedFormat":1},{"version":"69d96a8522b301a9e923ac4e42dd37fc942763740b183dffa3d51aca87f978d5","impliedFormat":1},{"version":"ff2fadad64868f1542a69edeadf5c5519e9c89e33bec267605298f8d172417c7","impliedFormat":1},{"version":"2866ae69517d6605a28d0c8d5dff4f15a0b876eeb8e5a1cbc51631d9c6793d3f","impliedFormat":1},{"version":"f8c4434aa8cbd4ede2a75cbc5532b6a12c9cac67c3095ed907e54f3f89d2e628","impliedFormat":1},{"version":"0b8adc0ae60a47acf65575952eee568b3d497f9975e3162f408052a99e65f488","impliedFormat":1},{"version":"ede9879d22f7ce68a8c99e455acab32fc45091c6eed9625549742b03e1f1ac1a","impliedFormat":1},{"version":"0e8c007c6e404da951c3d98a489ac0a3e9b6567648b997c03445ac69d7938c1c","impliedFormat":1},{"version":"f2a4866bed198a7c804b58ee39efe74c66ecdcf2dfebef0b9895d534a50790c4","impliedFormat":1},{"version":"ad72538d0c5e417ee6621e1b54691c274bcacaa1807c9895c5fa6d40b45fb631","impliedFormat":1},{"version":"4f851c59f3112702f6178e76204f839e3156daa98b5b7d7e3fc407a6c5764118","impliedFormat":1},{"version":"57511f723968d2f41dd2d55b9fbc5d0f3107af4e4227db0fb357c904bd34e690","impliedFormat":1},{"version":"9585df69c074d82dda33eadd6e5dccd164659f59b09bd5a0d25874770cf6042d","impliedFormat":1},{"version":"f6f6ce3e3718c2e7592e09d91c43b44318d47bca8ee353426252c694127f2dcb","impliedFormat":1},{"version":"4f70076586b8e194ef3d1b9679d626a9a61d449ba7e91dfc73cbe3904b538aa0","impliedFormat":1},{"version":"6d5838c172ff503ef37765b86019b80e3abe370105b2e1c4510d6098b0e84414","impliedFormat":1},{"version":"1876dac2baa902e2b7ebed5e03b95f338192dc03a6e4b0731733d675ba4048f3","impliedFormat":1},{"version":"8086407dd2a53ce700125037abf419bddcce43c14b3cf5ea3ac1ebded5cad011","impliedFormat":1},{"version":"c2501eb4c4e05c2d4de551a4bace9c28d06a0d89b228443f69eb3d7f9049fbd6","impliedFormat":1},{"version":"1829f790849d54ea3d736c61fdefd3237bede9c5784f4c15dfdafb7e0a9b8f63","impliedFormat":1},{"version":"5392feeda1bf0a1cc755f7339ea486b7a4d0d019774da8057ddc85347359ed63","impliedFormat":1},{"version":"c998117afca3af8432598c7e8d530d8376d0ca4871a34137db8caa1e94d94818","impliedFormat":1},{"version":"4e465f7e9a161a5a5248a18af79dbfbf06e8e1255bfdc8f63ab15475a2ba48bd","impliedFormat":1},{"version":"e0353c5070349846fe9835d782a8ce338d6d4172c603d14a6b364d6354957a4e","impliedFormat":1},{"version":"323133630008263f857a6d8350e36fb7f6e8d221ec0a425b075c20290570c020","impliedFormat":1},{"version":"c04e691d64b97e264ca4d000c287a53f2a75527556962cdbe3e8e2b301dac906","impliedFormat":1},{"version":"3733dba5107de9152f98da9bcb21bf6c91ac385f3b22f30ed08d0dc5e74c966f","impliedFormat":1},{"version":"d3ec922ddd9677696ee0552f10e95c4e59f85bb8c93fd76cd41b2dd93988ff39","impliedFormat":1},{"version":"0492c0d35e05c0fdd638980e02f3a7cdec18b311959fc730d85ed7e1d4ff38a7","impliedFormat":1},{"version":"c7122ba860d3497fa04a112d424ee88b50c482360042972bcf0917c5b82f4484","impliedFormat":1},{"version":"838f52090a0d39dce3c42e0ccb0db8db250c712c1fa2cd36799910c8f8a7f7bf","impliedFormat":1},{"version":"116ec624095373939de9edb03619916226f5e5b6e93cd761c4bda4efecb104fc","impliedFormat":1},{"version":"8e6b8259bfd8c8c3d6ed79349b7f2f69476d255aede2cd6c0acb0869ad8c6fdd","impliedFormat":1},{"version":"02b6d443cd64d2a7e8dba0f1d59944e55e91a16b21a7d7d4fb5a81724c832dc4","signature":"e66fec1c73ea068e8541b003c79af072b1b18910017d07c47ad151a438c709c1"},{"version":"a1cc7006ac0ac2dd2748f5b4a07092a330d175b16adbcd49c0eed365e9b4575f","signature":"6521410466cd5930d8f9db814cebdf1094def90f68293a3b330296a35ff2c1f6"},{"version":"d3984cd8c4d6cdf73a81ea0891dae87ae6a01c1895fd68df0b6d740006acb9d6","signature":"0ac76f72a94a13f3081c41c43b58679492c219ccada653f40801a89fcd5e9d04"},{"version":"64a1df79fbba93c3a1642f66057608a3d7e2fd24ba015b260f7014ddde542908","signature":"feb053fdd4dce7ad7c1ba7791bb6f65fb66d38bb9c1f0543012dab8f663e88b4"},{"version":"9b47bc8dd5a4d6b7f03a22a9ff4f46883813ae93554718511b93888e39ee58d2","signature":"32937206cdaee2551a23ce603292dd67e9606a27fc71a984eab852fbea3b9ad2"},{"version":"98efe4bddac330a746d7f43a39faa36ca747935ea6a8322a8afaca68c5e70f35","signature":"4dd7e1bfc2c138b564a1ff5bddcae96f4cefd39724115166bdbe071cd00b3cb6"},{"version":"c4528c70ebf1acf226f198422561ad4348ac9e35a8990b1fd15e17ad9268d60b","signature":"71f762a4ed63ccdd8a60c9930b445ab8e81bdf4b9919c5b94761511cd866f447"},{"version":"d6d3f9395cfd6f2ed3c9eaf572f882a03a1fecfc1e13acc4519df67833342bd5","signature":"5c597452991cbc579454bf8e1c5f549816d79f80ddd3514b52fbb26cc1cdeced"},{"version":"7c6ce84284a608e8ca9b7636cb5da89481d8c945d03ba511da6a2fd56bcdf78c","signature":"c169279b909f77b0c7b26ce990b20c6719869fd76be6f95f4eadf4f3befda363"},{"version":"83e1bfa7986a958fd6e069fc5df9dec6aa1e63f3dd81ddae889c19edf3a6c450","signature":"6efc188b6e1596f593cdcb356be53ede31fa87f972e5d2adc9377fa511e2685e"},{"version":"c463facc7d18f4c36823714a285903d1123cc38a9dc91a5d099c64145432f75c","signature":"2150afbdeb24336371088cf931c6081d224326f5c57580ee0b36925d1569ad5c"},{"version":"dc916450a7fe9f02ea4f2b015b836fb7d3e6291e59c93b47f711623ec4c62fe4","signature":"1609615e284b1a86bbaebd997d03c23cbe145012ba3b3d4376aa8a43a701e4e3"},{"version":"02a313eaacd1d0d97e7e1605737ac03e732648ba6d92fbf2c24716c1349c30bb","signature":"e42b8c3731c42dd2bdacdcbd0b7639df957c3f9b5fdc1edabac4a5e63772a4b2"},{"version":"af2d7b90a50168850a399d83b4e9afdc302a1025148194e2e94e1a31060b93c6","signature":"9836be02a489f0fb61392d0e3fe4127c72f079fcde9e9fed4c282bb070832fb6"},{"version":"c029e55dd039a7085f8a0f08edbfd0d563a07c48a8d319c8fa56320dac00a465","signature":"dc37598a12fb0f6f547b4a8a14da785916df9f822edbdb9657ecc589bfa33f7d"},{"version":"086d9066a9edc176d4baeb61d0075de9353ee4695c94ecfae51f293be8fefab9","signature":"90c1986dad477ad10a8330aeb2b86a0695d484a12a6f3d6507147e07791b1476"},{"version":"94fe52c96742b25429d30bc54d7ab2a2324f37025cbea99f819a77ec87bb1772","signature":"d570651c0a2c5e78e74c52a792b94ccc2cc9b2b927bfb3a5419acc0150942695"},{"version":"1897adbce3874a07180bb47daf0e8ebedd6d1793819143c63cbce290ca2ec80e","signature":"aaf435d6dc58d0a18a54421b3a622efedf9a7a996d8f75a06354219d91707650"},{"version":"68fe3c692ad2824bc811643cd5e239d872cba48006000dfe185146ad106066b3","signature":"0342e61cdf2eadde061c53e1c6fc7907ad69390beddb2aa50e656dc2a45a632b"},{"version":"b15c4ee8a756cf303d0efc482e861876a2e90b194c7cc393a8acbe7080fb186f","signature":"4a1ee69e5477f0d725306d7c9281d127f43ec0b40a23689a1a27b9430f030177"},{"version":"f6a08a8d8fa7acf45c3ba85e864da549befc88abeca247da5b6732a82685bf45","signature":"afc9b47eb28f4775396aacf528a98d207e4714ed7600c47bd33d01d4d6d3852b"},{"version":"bb0365d741d36b7f82832dbcf1b2e0025b6638516fda9ba3061d7d41f7f073c2","signature":"1e5e485956159fcc1eee2c73dcba5186c0a66f780ad21ec3760cd35a723930ca"},{"version":"431206d65e5858f0534c8be80eb4081627924a9d1cdd853982a3a4d811999681","signature":"e4b7681fdfe65ce81bcf251c1bcdd71b93740fde81479a2e3531a23fd347d951"},{"version":"7b5bae142db908800ba59fb353d7274e1f1ef0eb46074f1675af3ee77c4d789d","signature":"a4b716d6fb7e2bc0cde7e9e02904a13247381b91faaf9092fd6c60e2b96c2d48"},{"version":"058aa6a9383796a202fdc9eb0c5eac4cce8a19ba60bd8b551091beee197fe25d","signature":"b067ed3257c3d808d867d834eb5c1688ce5984ce6377ce6213f7fbea90bd6b58"},{"version":"c837dc2de1fef03dadb1fbe3ae46565ab80cbeea60c057acd5bf1e1d8df1b509","signature":"b4ffb98ee6415d12844fda388c4ca6ef430f4e4217d9ea7a139c252576a464cc"},{"version":"e6c4a6a15416b28fac47309cf33fe8040115a2849ccd012bd098efa4ec4ce9ef","signature":"f3afc6db2c2172dfa631f271d2bc28e8dfddcb2807285ebd2ff547fb786e49bb"},{"version":"283ec3ae2b171cd28e6778d2bdba3f8b055e818dac238b8a2d27403c551974d2","signature":"b8eb376386840de0303ba01f15f27e04407fd37199eccff44e6f51ff9410bba3"},{"version":"63ff2eb7041e18879c303484288ffe060db245e20fb92be664b39fb9f56c4b35","signature":"f350f485c8889421846f7acbdfe9ade72bdb827ef1905f5cc94576b802ed1a57"},{"version":"e0340f2e710b3caf03d7435335ed6441df684f5f416b3008077280a53bc0d195","signature":"043d0bf84c084c637ced77530bd97faa0aa3a8e01e2915aa8cc2129f79d9cedb"},{"version":"d4e9258acb020b007d2a76f0d9870d5d10f0a2d6bc8d24bcaa0f65931892b736","signature":"345eb0a009f9b07377ff2e8bcbd390da1648e549914b3bd027bd6b4987f92481"},{"version":"ea17df0c800a3917b052e4711913019aa15c567d1112d0f12ae22655f1c8e75b","signature":"c333598ce529f5350d9ef0c9a1bcd8c777b86845fa327aa24eef575669cacc91"},{"version":"f5c622b1d6ee97b57e7ea203c6a7a373f7a17eb412b16246b956bbb548fbf1d4","signature":"f8e7efabfc4eda9c2900703c25975deb5ac7d331e2ede7cfc50dd2da9011d789"},{"version":"c32feab5e5456978529c9eb1c2d8b56a04d9074f2f43e757edf680e132d37d00","signature":"ea673b0a7771824aa72008f0f86c71b712e5355684f05f24f8c387accd03f14b"},{"version":"133f92295e085a33a1827544debdab026bfbddc984a1a6b0865c924c38df13a1","signature":"fdd9cbb46caa8f1ba8359945e433ad1f2b954b1496a933f3eb4d29c8ae3deac9"},{"version":"e111d7709868c64a5ec40c93a0831eff084f5f3747bb50300878504738e28c19","signature":"8ac220f8baeacc2d3ee8abf3398308e10ea42de2e146be47ccb866ceb017a397"},{"version":"3dfa41a4c00d6e79b3618e1dc111188c9288bcbe16547bea8305e6af449329f4","signature":"e367993516c9f05fa87238bc5b53220f06b7f84b72629958930e2a7a37436c24"},{"version":"4d6792c606bdd2a9b2cddc4d24923ccc18f7f438cafa31e0e21285e97c58421f","signature":"2d8f81759b547e64f1b0e290fd4b0ac7316dc9c3e96f5ca93db1a1c790ec6038"},{"version":"b8b666a3d41df3b7cf4066283f67e72bc5e8e04ff4414695eb972ba7561ce133","signature":"171b8eafff7d0d126a6df4cb220dfdf7ae67c7c6687fbdc02bf4b791bca40091"},{"version":"bf66b3a8b8a28e911776d8ead5e2b6553ee37f8fa1bf0ed209aeaac310786fd9","signature":"6302a282d76ef8475759f62e01049001af013cf8f9cbcd07dc9fda62eb87c9bc"},{"version":"18ca2e6d4cb671ed4530429e6b4886aa0792c79c4e5e74078dd290668b540599","signature":"c606b46f5784bed24dcbe2bec2d9ee535c29050ba160ea11fd41b6a173bbf25c"},{"version":"93c88804801702c2ebf4d7e282ff71d90f118253ee206e7f0ba03305cc581546","signature":"0a7f51c3fb4b7c9a30745a92c15a4cb4eb88aa3ea69dec8f6286491fdfb99dab"},{"version":"cc2cc8e0ceffbbb09594103d85e205eb53b848b7ecb27d91d7b5cde80e792236","signature":"7f20f857d4b5aa3fa620d0708434711ff7092dd304c9c38f8854ab9bd334edf5"},{"version":"81aa2ba3522bc10222837dca4556efb07a6628de274c94545a536c1955684ef4","signature":"daf649274b917c1d7d6b8e8488d04d7e47f3bbbb09842c2a9899b4ec507fb243"},{"version":"fa1702a90530bc09b078bbfc9e98010c20333706f9d225c18558a146ac9e2219","signature":"c75b37dd144d43a77944ee5a7b8195d397ae78b74065052bf3a1bc721b1f77b4"},{"version":"d5ebf3405d09e5eb9e3316e8b6a7329bba4fa306433222f109b9af077ec77525","signature":"71108da668d27a617e4f2ef6aad932227d526487149856bcb3705f0a2aa9fe9a"},{"version":"c7aa0820877b8341f78e794fde60d464746eec82b0e2624982000ebb19fb8f8c","signature":"488c8eb8a1054444f74a12eb49f8e21ae583aa2ba59ac9cfe4ffc71754c3b1f7"},{"version":"9c2b60f6bc9aa74d213f7dc5bc0b666e7c18c3572adef9d884521c36412bb814","signature":"ca4a72006161d37a422dde05feffb9667a0e38b99737b94e0b564b5a7409ec21"},{"version":"f38eb0a2421beba20ee66d42353732cf2ce6f343c1b1c322252842e0f22b8308","signature":"cd575032c427cf4eba79247a61418781b801f14952fc1bf8a48ed2747def2bcb"},{"version":"f7409ccc7875d9cebabc5e27d9df8f3aca19ba959f30fa1b486418ae9c3058e9","signature":"de963461fc2f6d1fd065c283aae92de10d72a4cca1f7fc0afef6301e741fd381"},{"version":"f51d0b8cfa092a592caf543d772238f191037278e2004d58e7354447d830863f","signature":"986bf1e9bc3d1b0b157927aafcfbf9e94478b28eda319c209ed8e9e613e14827"},{"version":"3b9d0ef4847a6525e297172e340c0dc383c8ab6c58a27aee0a27b2df991ecef1","signature":"57069ea736148610272f87e767f23439015d900f230c3060afa193d6b9029cf2"},{"version":"b6ce8cc18189aa155b9d4386c03e3547f48121a7e3f37b66ed9ad43190b20dd3","signature":"12dd7bcb0994252cc8b7a0155db5662ea1c3437584c67f010758f962b023797c"},{"version":"2c4beeb10c123c02ff09c390cb92953e811cb0e846e2d040c65a8a0e73746894","signature":"5431108d0a4a15cc5f6d78abd5a358d13bcabb849877e09f3efc265eba21e6e2"},{"version":"40f8a5ff101ec9d2a6a08af84db2d4865c35e2deb3da94075a482d94612ca24e","signature":"57e73f014bbd5a960cd0a3b39a240cddbf1842f7f06a09757be73de97a234a79"},{"version":"812fbe241e51f1fb745bfdb0cf447cff8a9802beeac16df1980f14499990900f","signature":"a4c0f47a1176dc8ca692834c31a2f1c95994955eb191e76cbf3e58dbd16ec08c"},{"version":"5559d4fcad759cc07a71aca5a792755409db3b683788828abad2a84da3dcd7fc","signature":"c367bae6e0535dda7431e73df32e233511c1e9b1181082d551efc822fcbaae83"},{"version":"46ea0bba2f2e36762dae6bd527f342a19fe9f46c653dc4e8061d9c8530ba8dca","signature":"19467dd75b0a6bae42afc2ac103445b6ddc4a3e259599ac20b2ec70e75fff499"},{"version":"5112478f7f7dcb622157981c8ac9a0fb3cad40c5eb87a19ff2e37674c75c0fd5","signature":"8ce6788984fbc5caf642946b8dc8a405629def762f166473ae6389aab4822034"},{"version":"3cdbef5d6bb0c6201d2edacc2f930322c36b9798b90131787398324b67bd2130","signature":"b84e31232011f6ac9ded7e727871f7c1ece606d8179950f791bcc03ac3c03b6e"},{"version":"d6c2a85159f32ddee646895592b5c53e04b18cfb5346de79c2d003b814b601e4","signature":"2bc381b2105d5a05c2724fa4ae393e83f0adefda1e390db743e55a0cb949c099"},{"version":"9fda6fefc6a936e326113a3d3dcb56da7dc7c2a7a064bf451429b51e7b645d8f","signature":"f668ac39f924b2946f0e323d23da14308c0d996f579dce2b5fe5c9f2085c9ad2"},{"version":"182221370b4c51b9fdd08f71c259596d747b565fcebeed2875832d1f2f556c8a","signature":"00ec18666782d50d3be062bceb46231a3e2c4abae3128f6638529e9fdabefab0"},{"version":"e1d7527f3d057bd92e487081450d9037a1dc9dd5e2f8e84e1fb2f6c09903db4c","signature":"0b482267029d52a5a2ed300385e2fa5accbe0f69d22bcc5c5f541536e169ad5e"},{"version":"827a3da73904af54ac6cad259cbde0bf0b811d253a3fc370664cf80cb65ad6ed","signature":"02778fe052be781d64d090064f311da1b30eda7863ab768850a522f3c83dabd7"},{"version":"41d344efc8e2dcfc00c0cd0d7bc8f5dabcc6bb0062766fd17aaf85deb4d60ecf","signature":"83df5dd9f98fa4184cd1227ae312c09558f5a00b35243e263069a3a545e7f6b9"},{"version":"d6452b09863385bd57e48e1fb836f95c3a6f36ebe690e342d834fd2868d6ba74","signature":"b9288778951e14a9a541d06ead6a2b1abf3b7541a1680af62e4769e632ff1263"},{"version":"fc002c1c913427c1e1f45dba404ef61759bb7ee2bae7bb396000e3cc521009fc","signature":"486e05d05d1c3a7899f66c9c98d8ae6b2c3a5e391bd74086d2d795f3c091a159"},{"version":"f697b44f7c3fb5f78ccafcf98d0b863e87e997ab71eda6ea9fb8290313fea562","signature":"95e6dbb8526dbc167bdbe072c5c78a5f281bddc36f430a34f36192566c2ff2fa"},{"version":"d0fe15b48303f79346e34ea1b4d207022219399afef4af6451928eec88606e3c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"447809300abe6967b66fe4226b18bcd8513258b0139a8fd1ac516767bc510372","signature":"6d31cb09b5e87e0588c937c73aff7673a15865e355903fe16ac3bdcb3d2894d6"},{"version":"309ebd217636d68cf8784cbc3272c16fb94fb8e969e18b6fe88c35200340aef1","impliedFormat":1},{"version":"91cf9887208be8641244827c18e620166edf7e1c53114930b54eaeaab588a5be","impliedFormat":1},{"version":"ef9b6279acc69002a779d0172916ef22e8be5de2d2469ff2f4bb019a21e89de2","impliedFormat":1},{"version":"71623b889c23a332292c85f9bf41469c3f2efa47f81f12c73e14edbcffa270d3","affectsGlobalScope":true,"impliedFormat":1},{"version":"88863d76039cc550f8b7688a213dd051ae80d94a883eb99389d6bc4ce21c8688","impliedFormat":1},{"version":"e9ce511dae7201b833936d13618dff01815a9db2e6c2cc28646e21520c452d6c","impliedFormat":1},{"version":"243649afb10d950e7e83ee4d53bd2fbd615bb579a74cf6c1ce10e64402cdf9bb","impliedFormat":1},{"version":"35575179030368798cbcd50da928a275234445c9a0df32d4a2c694b2b3d20439","impliedFormat":1},{"version":"c939cb12cb000b4ec9c3eca3fe7dee1fe373ccb801237631d9252bad10206d61","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"b42d3651103a532f7492e719a828647af97306b2356ae757ebb7f17f4a8c41e5","impliedFormat":1},{"version":"03268b4d02371bdf514f513797ed3c9eb0840b0724ff6778bda0ef74c35273be","impliedFormat":1},{"version":"3511847babb822e10715a18348d1cbb0dae73c4e4c0a1bcf7cbc12771b310d45","impliedFormat":1},{"version":"80e653fbbec818eecfe95d182dc65a1d107b343d970159a71922ac4491caa0af","impliedFormat":1},{"version":"53f00dc83ccceb8fad22eb3aade64e4bcdb082115f230c8ba3d40f79c835c30e","impliedFormat":1},{"version":"35475931e8b55c4d33bfe3abc79f5673924a0bd4224c7c6108a4e08f3521643c","impliedFormat":1},{"version":"9078205849121a5d37a642949d687565498da922508eacb0e5a0c3de427f0ae5","impliedFormat":1},{"version":"e8f8f095f137e96dc64b56e59556c02f3c31db4b354801d6ae3b90dceae60240","impliedFormat":1},{"version":"451abef2a26cebb6f54236e68de3c33691e3b47b548fd4c8fa05fd84ab2238ff","impliedFormat":1},{"version":"6042774c61ece4ba77b3bf375f15942eb054675b7957882a00c22c0e4fe5865c","impliedFormat":1},{"version":"41f185713d78f7af0253a339927dc04b485f46210d6bc0691cf908e3e8ded2a1","impliedFormat":1},{"version":"23ee410c645f68bd99717527de1586e3eb826f166d654b74250ad92b27311fde","impliedFormat":1},{"version":"ffc3e1064146c1cafda1b0686ae9679ba1fb706b2f415e057be01614bf918dba","impliedFormat":1},{"version":"995869b1ddf66bbcfdb417f7446f610198dcce3280a0ae5c8b332ed985c01855","impliedFormat":1},{"version":"58d65a2803c3b6629b0e18c8bf1bc883a686fcf0333230dd0151ab6e85b74307","impliedFormat":1},{"version":"e818471014c77c103330aee11f00a7a00b37b35500b53ea6f337aefacd6174c9","impliedFormat":1},{"version":"dca963a986285211cfa75b9bb57914538de29585d34217d03b538e6473ac4c44","impliedFormat":1},{"version":"d8bc0c5487582c6d887c32c92d8b4ffb23310146fcb1d82adf4b15c77f57c4ac","impliedFormat":1},{"version":"8cb31102790372bebfd78dd56d6752913b0f3e2cefbeb08375acd9f5ba737155","impliedFormat":1},{"version":"bb9b5a18147a0f927e0fffe91515a39610e2477b0d8a0d0b391c283013e0bfac","signature":"d373335450e0c74b3455541e03c0ff8fef26b51201c49ef145a0afb217a9f026"},{"version":"4bc5159b0bb1e303f1b662d485b7f9dcfaf785a29f8cd101ea85817fdb3a518e","signature":"70cdd1bdaa655ea305231ef8f3d9f830459ae85cad5a2395b70b4caa2d81abe0"},{"version":"25bb698c825c728521550bae3d4d8777520fea078d96529db79d3901278e084f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b71e71966963661cbc81a93e3017c50f97fe724452ffc49059ed4cf0540e464","signature":"eb4779cf04d947168653273aa00f2a6a72a61fe039f17400e6d76179a7d62fd1"},{"version":"6fbedb59be020e7d349de8a1ffe8aaa52d16c78f9aea437249f14782b290aee9","signature":"eba9ab6bd63d7d7bc2a05d255e9d56cb7321477c3ec92364db4cdfb12873e8b7"},{"version":"1318f5ed0496352fb48c4c03ed01567db651e1794260df1b2da1e146a1aefab7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fcbc73a398e35777c583049d7a6315455a1c340d06a7ba06fd65a08a998576a3","signature":"bb33db3843913e4d9bba12a3c10ed9c8bb77a67266905cfd9e0afeb093e715fd"},{"version":"8f9f32fa2ac067466268c5f25be56dec212c0bdb9b62a123fa588e72bd6b0019","signature":"a01968c471a290a4b9657a07cf814af299366e7c83a56d8ae6ea425e9b83c81e"},{"version":"24defda36322571a1cc5b324b17b3c1e721804a2add0166477d24efaa523ffc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70ac7fbe8555de02f7cb0fe42f479173ddb89a737908c560014d733348422046","signature":"d9b4f0fd652a60e8727bf295164c2d0a652cb6d79ac90e8b13c48d4230a47039"},{"version":"656ebe6a1e35fb1e45ace5b3d8975099fa82a7a42542c09ee1e1e975b4951722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c4f3d9c6228f744351b3f3d6ac2593edba9e7cf0a965cc6ccb1805595f44f275","signature":"96dacaf48c43f86fe63578c47b008b14f31ea3b26ba6604869be23386548a0af"},{"version":"37d5de471b9dc564c40740998405448999d504e4199bccb259fc7b0734b1ed88","signature":"cead6dd08c0e7a7b5fe6b1665ac0c10e08d5bb8ed6e044a5716acdf61bbc658c"},{"version":"256ac94c8da7010cbaacfb3e0f55cab2ce49beb7f21309659ab1e5c44b66cba3","signature":"4932a57ec8dc885c99967df2c08c4be4dcde303de1727465afc901bb526c9dce"},{"version":"16774047f3b38fcf2d977046b054d22bb00258771f607fa6e4a06e76cc114cfa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45d8c8b72f837a46ca63ef01ea3f4244112587c0abac142367982f443e31ea7d","signature":"eb8463d6df0ca2c38399823f1e38ff66180aaefb12e5b403155c2abe1eda8b5a"},{"version":"964e363030719b2e66f7eb64663b22f039bc64985dd1e75eae362e378608ad32","signature":"52b37759b4c21b0266e113f72e72db24ca11859fca9beaae88ac286fa508c5eb"},{"version":"2aca0bc14bc6a0e2ce70f410e002eb4aec77e7622afd0e40200a2d6c36542db3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d21bdd834776d159085df8f067883d3437dacf6eab3d356f7c3bb2bce9a9c98d","signature":"114811397c9ad0f10abb90a439425e93671eb3698dc832ceeb9147bfc4848dbb"},{"version":"912bade28ea48bda95091b26d3082abbaa1f48637b4b4ac3e289ded44a153a1a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c29a2bfef4c6aaed66d0e46bf5601f98e4ea8fba4e230773bcf8a57d76d7ddcb","signature":"724894208b6a6516915c69b386dc88e8ae38db466a047c122baa0acb05b2466e"},{"version":"338fbeac4bc8d461eed91e454d837c2cae2c403822f1c3f8a0a68f1d1e2488b8","signature":"ded5f2d0a193751dcb1172fb8ae1678a74ec41f30a52c8992268992fc21b9f6a"},{"version":"dd902a78d8188c4f89377024097f0ba90aed6822e4f13c742e498eb1fe9fbe8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2ade88925e99f1feff0c33e971814e734e05f6f9e32c2fb7c6260247635417ac","signature":"06ca53e7c778e43262f44194db43a44dae84e02e9d9ae674f74a4039f043a39a"},{"version":"a5110f54ba5e9c7c7fdc029cd20e65f35a9ddab6830394949279d03f4baaa112","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e08859a0433654484c23ea7d2447e7da43768e228643cde336290e80359be015","impliedFormat":99},{"version":"427d5d08714a73f909d965ace2642ea9819e49245620483d47acb73b4eb922cf","impliedFormat":99},{"version":"242e53307a5705e9235ca1be47168a4c3155ea674e80f5a13f94d7938c23bb5d","impliedFormat":99},{"version":"44a6c89655f418dd8250d22276530cfa20826b2fd8d51e90fa4d1ada108706f7","signature":"b8ceda97cfbcc009561ba63ca8e39df0dcab8ad77f6bb03d001f12d0f5174f03"},{"version":"8ffa57b994af8cee7411cd7bfec0409118909a2a897b417d5ba378025b9b8eb3","signature":"66d21fb03c05d9e19a9e6328311f0e929ca450163fe3ad5a1f19b3e0563710df"},{"version":"a2a59b41271e830cdf2ce76662b32e2f9d3c2a3c35e9f9fc359e9bf994ca8104","signature":"9b835bb9c177bb27a9d814290adebe192bcd22f633810aa7d4f0a4ec1d6fda4d"},{"version":"c4e23c61fd109ee83f2f5c355698161dd4edccf0882115c1c1450e55d02fba1c","signature":"cfb868d5da01515efc27424a0c579a94c56621768b4e7a4d2275203046da4685"},{"version":"338381ea6d7ef717f6f5c22294b6c1d820d06133f4f5fbc86cce64fc4fb81f5d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"93fae08c020f721198112b7bd06a7a405f3d5ed77c756e20bcaa264d585b906e","signature":"c9d3eacf4c5ea3ad593e74b88cb8b3dda3258df41d236c336936146d3847fdd7"},{"version":"849d186951b6fe08777eb595e7b5423a933404a59b255b15b3ef91eaa9e03e2b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"398322573c9e0ba2826eaea162b97f4987dd00caaa4dc29df93d7dccaff40c2f","signature":"dc1df284b2ecb2adb8124f0411490cbb6adc27d6b3f783cb98e4de022894c67c"},{"version":"0e614492dab5ee5f4418895293386b642203c8f1a3a9d14a8eca94a906c91c04","signature":"1ce004dab6fc4c13fe2a946bf541afc29f77e4b6d197bd7e078c216bf331c288"},{"version":"f22a38020548019be436f7d33fae243aecf68c9e068dd7fb5d88ec31fcfce2c5","signature":"990a86a4c51ffd7c3c146bc5b5e4f2a6eb31f8a08186da00d699ec62be38d14c"},{"version":"07a57ea6e42f784f7664053d917baf68d010a79f0df1fdb8fba87a6af92ddd7b","signature":"e57424bb8ca9fcb02c7b73c295bff56d7889e92610490ef4dbcf83dcf5006809"},{"version":"9462d342a282781c02c97f4c036158faa37e7fc0ab0a14e1df622ccd9c757f5a","signature":"d55fa4fbc6da7d276cc39c6f62402a7a65cbb0924015aeb475013a0d50eb9d5f"},{"version":"50418661dd171c08d9cae6e3b5ff41663e70267a80e0716eef30fdaa381b3607","signature":"438a206e90ab4a99894d015b8659ad993a2e15df506e3e52ef38b5892098917d"},{"version":"2c0d69ddfa8c836d8052a3a7589b0d6c6e8132d50949d6440d5672e0f0948ea7","signature":"8858e8d480c6f9ce98c8f48ea265e292dbe3fc51a9f9c3b0d125ffad18d89e29"},{"version":"ba0789108c9bf8c7ec38cc853e188f9d131c7850a44fba82868eebd7230cbe2e","signature":"e6cceaf655d91958114f0707a4d6c800cfd0d72ea8673f4f0face6b049c90ec3"},{"version":"9f2145479716604449381a636127459790f9e428a5c526cd0795223bb66dd9b3","signature":"d86c8b7a6c6edbeb6a14c73aad61eec9d13cec0040264ef13c78a8c2f7ecbf43"},{"version":"fa32219cc14042734452368d122b82bae2849be88d8941c5b363e3c47c9b651a","signature":"7c56fc0ecedef369430a6cc78f797e1f72ac6aa30cf3861bea222fc9efe93d49"},{"version":"f52646c7394ab792adfca993338d590f7d9030ae3269526d5dede9b131247717","signature":"d1d2efd128275df07279bf887018192c1b38c0cc2aea96243de78a8e92bc30ad"},{"version":"b1ffda5abe3874eaf7ee7e57cefd4c4ed1e85e00932b5da6847cbe0e22c7eedc","signature":"29afa7f4d2f64a222d590227109f01361ddb9c6588355096f6aaa036b9d67d05"},{"version":"0cdab5351929ad0e2d94ecea2b8d60d11fcdb153ff2355ab9c5109e84a697ddb","signature":"91537516c066b5bda3446b1dbd01a6b3ff342925cde014d5acb7b6f8b99ed12c"},{"version":"0172224d148517f7cf90527aa73f03cb436b3fe7f06ac70c039d6886b8f9dfdf","signature":"d01168279f1f9a417a578d3768c58eb5170a5b3445e7b2dedf880dbf0d1fe873"},{"version":"a6d5aa9d1ace7ec2a992729d02341737fc267a9d2d7f1467313bd170e4b26b15","signature":"a65ecfa05330aaeae23d23b899f0bd37c34e42fa5083d180b4a0bff3dc3ae25e"},{"version":"831b967c1911010eb3adbfe96d76340dce858803d80310236352a7b52de799c2","signature":"76d26c617c0a9f48d4e21938e684ae22166d2d3604d00cafab5212b0e15b57fc"},{"version":"d09b5dbb314f199a0d7ae3c2c2a9c7258b611f3f28d1cc83c2685d3c738aec3e","signature":"068d0597a17af822c2ec3af9b1c2a9b9a26c0a4387eb66f655a0f1d26e36ba84"},{"version":"35164c8ef06e7c366f6f45f993da6e0df0f7c2cc93e78198c199bec111da8fa4","signature":"942546eaf5ae2d0c5948c6d25a748fba25b6f4d760911ed595b40f043fbae102"},{"version":"ea484c456f3f9236d0b324d2c6563f6e77571c9414768590248a016b2e248a3a","signature":"65bb767048368601ad35597c54f6b112e3147dc84fc338199931af5d57b8fe95"},{"version":"1ac040d4f47be4c1b44b4a521c7bc1264c97371b3c51bc2170326827fc8a5406","signature":"dd24f7d41609a7eb1c990ec2f7d7cdd63a419e355e6294050a67c029bdad0d78"},{"version":"f3d7aece0ac20c6911c50aa54b50c1ae6768a8793a72412d346aab2b66b4a7f7","signature":"ab04dac1f806941027328926be16232e21366ff829c3be737572abc646b0ff3e"},{"version":"6a282a4b745d9ac9d04d759b34b9e51124a950ba33d83a1408f76742cab5d8a7","signature":"519c584866e4d804355422ce52d8088fda0124969a9668779f10b0d50e114153"},{"version":"3cef134032da5e1bfabba59a03a58d91ed59f302235034279bb25a5a5b65ca62","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c05d0fcee91437571513c404e62396ee798ff37a2d8bef2104accdc79deb9c0","impliedFormat":1},{"version":"373cf226ee7ddf9535231d4ea2c24d47e4262372e1c075aee7b48e0d2d38e759","signature":"3a700951382c62ca71c0a4fb951071e1a2692a3ddfc899ba0145c275ff12a006"},{"version":"29a8061c2db56515c67ce514bcc6f91c853407acf4539f5d08aa723526270a1e","signature":"7cf5ac50b3def9f8df750c1e7ea9a102216484b4bba94f9e0bf68458bc77eacd"},{"version":"2d4b53789aab997f99121021686c05f5f54aae58fbb0525243fdd322c80d612d","signature":"5c975df906b720e560dc80cf99f12cb2763329a0d5c42ffe3039256137dc3a70"},{"version":"e43472b89b27f89f28fcb57260e48230b95baff6fa6c489c5710115cfa6c9506","signature":"2f9e549adb20bf7d44ab18efcdb5e7dab6bdf423d310f3df05e5ac78e3828990"},{"version":"91d7a64938101c27f0f5493074dd0ebc4f82ed6d58c42c8d148235de3f8978ed","signature":"b4698bce6f7a4a17593cff994a72d565662855439386bcfabf5f0335ea8d4be1"},{"version":"d563b38c81c713a23b730e0e385c44442992d3b1dfad2424fd9c635e3eacf593","signature":"97ebafc9d89ce29d62958a732cda28a5cd408a1257cfcac0d253584fcc850e6d"},{"version":"6d4b59d8a599531b5bd5cef904c5f8832f062b79eac5298805b9aade268d66b8","signature":"97ea7a733867ec926cad347ae90178d3c3fb96a4fc076d2d2d41201e9a1bea75"},{"version":"fee8eb73b4397c9d3fc50904fb4d93947f32879251345c687761a5ac20a76314","signature":"4e69e7b65fb23298ec08b6e0cc86692fb6602df5473948f46baf219a07967697"},{"version":"0d922edfe50c6ffb2c16d49dcdd160dc468b0f93f69fcb021d6464db95664a21","signature":"aaa2dfcda87fdc4c24fc251d7d04070f379d25c631d2b130c846becc582e1b77"},{"version":"df3a3dc2616be7db489fe6a853faa1e52a83dfde06d2f3214994ee7ef81f18e4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"3cfb7c0c642b19fb75132154040bb7cd840f0002f9955b14154e69611b9b3f81","impliedFormat":1},{"version":"8387ec1601cf6b8948672537cf8d430431ba0d87b1f9537b4597c1ab8d3ade5b","impliedFormat":1},{"version":"d16f1c460b1ca9158e030fdf3641e1de11135e0c7169d3e8cf17cc4cc35d5e64","impliedFormat":1},{"version":"a934063af84f8117b8ce51851c1af2b76efe960aa4c7b48d0343a1b15c01aedf","impliedFormat":1},{"version":"e3c5ad476eb2fca8505aee5bdfdf9bf11760df5d0f9545db23f12a5c4d72a718","impliedFormat":1},{"version":"462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","impliedFormat":1},{"version":"5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","impliedFormat":1},{"version":"d0570ce419fb38287e7b39c910b468becb5b2278cf33b1000a3d3e82a46ecae2","impliedFormat":1},{"version":"3aca7f4260dad9dcc0a0333654cb3cde6664d34a553ec06c953bce11151764d7","impliedFormat":1},{"version":"a0a6f0095f25f08a7129bc4d7cb8438039ec422dc341218d274e1e5131115988","impliedFormat":1},{"version":"b58f396fe4cfe5a0e4d594996bc8c1bfe25496fbc66cf169d41ac3c139418c77","impliedFormat":1},{"version":"45785e608b3d380c79e21957a6d1467e1206ac0281644e43e8ed6498808ace72","impliedFormat":1},{"version":"bece27602416508ba946868ad34d09997911016dbd6893fb884633017f74e2c5","impliedFormat":1},{"version":"2a90177ebaef25de89351de964c2c601ab54d6e3a157cba60d9cd3eaf5a5ee1a","impliedFormat":1},{"version":"82200e963d3c767976a5a9f41ecf8c65eca14a6b33dcbe00214fcbe959698c46","impliedFormat":1},{"version":"b4966c503c08bbd9e834037a8ab60e5f53c5fd1092e8873c4a1c344806acdab2","impliedFormat":1},{"version":"3d3208d0f061e4836dd5f144425781c172987c430f7eaee483fadaa3c5780f9f","impliedFormat":1},{"version":"34a8a5b4c21e7a6d07d3b6bce72371da300ec1aed58961067e13f1f4dc849712","impliedFormat":1},{"version":"6a534c594838029f9096b88db91c054e612ff951a57ed9d9efd92f19643a2753","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6223f56cb79eac77e1211e76830da993ddcd9baea0dfe2d10a61a131d39f427a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20d8184cc9bf496dfd9415be762d5233809d005d149417d3c30a16084b0c3842","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f72a954c349bccf89e393e243763fb141257a54d6647e369c79beda371378f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6b47f0ea0108d741abf731f728b357a8370c238ffe73fcee007619484c24356","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0d1598df1729ec0e33ea6dfd7ca8e063941308fc28b90fe59660158167b9627","signature":"f592c7e333a33b4e5dba58516b31a9ba2c3f5639c989fce88351556ba49606fc"},{"version":"99338b1762fec1fed1659b7aad2bcdd744d239177d0c147c540b34ef93ecc986","signature":"408859ac0de230e5b42b2bb888ba6da273ead46b0a8123a5d21282ddf6335afb"},{"version":"20410f27b941544a2d84068655c0f03e95d6bfc15348fd081814eedb7dd1a023","signature":"571d3448b7e5dbb700ec919745d70b84c0859909793935026a8331b7666d91ed"},{"version":"ce2867edd9fc13f9654dfd5cc76cf42d05110585108187658b8cdd4a797bb144","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa4db26266d6f651c711350ddf671278179e6f59b28d3c390ae50a9b20a3aae4","signature":"921c81a312317ce376b3db64ec158a40d264b56c798653f7985b9361289d951a"},{"version":"541204b45188f36c133a8dbdb67627cacea1fcccc8f56e34ab3a6df5a8842e23","signature":"03da542307af2869e7b2c1de8d1237d05b584cf0acf36c9d40f8e994f21adb2e"},"5f1ab4340b3a3f3d2c88167d0b98d1d8ae6c6d4b1ed845f25c3069d7d1b902d1",{"version":"62fdb9ea1d1284dc72bae3338d2a20c737814b30d30c9d0ce40aec4fcbd51746","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cab9185002bd5ebd154b6106ff93ae480bb26be2bc14bbf19180ae690449af28","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b980df9c1d9398fb15cda202074eeb45eca1b733888708d0fb43c021b5411991","signature":"b0ba848f7538ba06336d964c03d2289007500242648df4d1a2e1f693d4823c38"},{"version":"3d64c2914a71ab3af9fd253eda13ea735a784923284005d07af763636578b46e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"178dc732f2d61a4fd094b6672b6c438d1b1d6cfd5489564206a84e3df486beff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6f815019b8b763503cecf9ac86f9de6bd8593180a0db3a624f98acf88dad162f","signature":"b5e89db47e4299930bf6020c3ac33fe228d590042b7dd4c5a3dc245027bd9a83"},{"version":"8f6aa64ab08524e8ee85ed63f8dffa377a7f4017680001a3669a963162f9ddef","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03c74f23371366e58d1160b9f84eb63aedecd51d3ca319bfb994b8f6b3ea344d","signature":"7b27496df462d7c5956667f688b1b318c2ab3081852bcd634ba80e1de4e9ffe0"},{"version":"9382ac249f4efbc0256803deafe838b51123955ca8b68c68a4be2b2c4a94027b","signature":"f4956881b9e58a4a626bbd99a98451461e46649ddbdc1560b635cb904b527c19"},{"version":"e9cf4f29b3c48849f12aa161f42fa890e606873410f535dd09def91b19b4924d","signature":"8f1e73205101242a9924a904f571655aa7b1c1c193ff6c4ba0e7e272aeecaa37"},{"version":"e1ae660c622a39b34c87f6dd244d59846a5d110d2c39bcf4316a499b166b6e7a","signature":"6940bec49524de4fcb325d41bbb724aada9cc73b4df1ae6498284a510bf97a59"},{"version":"9071146edc7aae0d6978d242a8f9d5988db42204717a627555a2342181c3ccd0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"695e9a27d875aae3f404cbd6ff901fedf0d77c193cfd9552edba781658ed0e85","signature":"a299ef368d46feb485cacc1257882c710c962c3835c15268544a95d9385c6641"},{"version":"d0608ad5086ad006c7c2a10e4fce58cbdac946d73cda4c270717bbc11751afcb","signature":"9d735a325ce1bbc7e6f8c5188f6eab0f769559979163f6083635913e83cbe5c7"},{"version":"42b18867a7543fec221e4f0321e077538e596cbacfec0595872df4876635dccb","signature":"baa8a117328606a5a80729fa29d3b99e604d1c58274ce6c705b1dd17550d4173"},{"version":"c07f3037b31e0bd7e1384c41a6fd6524ac141e8f56b93a4a12ec63d75a704edf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da142229522424470a86d0f568ba8eb149a4792e52f53cb7ce8c6b117ef65a3c","signature":"131aafceaec427354fbdb2f0c518ce34b5d29ed41ba96f5be2e0c6828e34c36d"},{"version":"6e925ea850d5c27ac93c1d0a26203779a347ded9a658643ac71febb557086d09","signature":"faf83f25a2e1c4c2cdd395f86877ce483031a5fde85d0bfa74cd27548f3139ff"},{"version":"25b749d6ada24514fd767c7212f8710ed80c3f54499a13642246913e678553a1","signature":"27bb3ddf3da26f0251f6fa1f7b1d888720e20fcb54f8513e691c7276c730e0c0"},{"version":"f77ebf90d0877e84d5f546d128be5e362554f93395f46ab6fe1fbf060b962765","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8efd52cc22298b4d6f0540b8c96ad97a563fd1a0effd9c245b9da300b5eef04","impliedFormat":99},{"version":"0d8cb9539485655600b329ddfcdb91d1b4b20f5d1b40a9e40c8017938fb68d5a","impliedFormat":99},{"version":"51aae950c97b61105064619e52f8b4e702ed9d88f6a38d8a6d461389be28dd0b","impliedFormat":99},{"version":"08a8feab6868367d5112474f9015e5b00c101012a639309e88fb105f94ced534","impliedFormat":99},{"version":"a15a870f6ab5a26a7eb91ddd8c47ff4e00bc23ece96ab48ff8aaf42450478a50","impliedFormat":99},{"version":"73390a82cbd5ea87d8bcdf183d66853207a111de00a8512c68ca17b47a11e65d","impliedFormat":99},{"version":"53755d0e8037d36720dc68e2e8c77512698befd0c0d48ac5d20c41986df91bc2","impliedFormat":99},{"version":"e0f44fe626dcd0026670f01dc0af34c40332729e8a1ce2dccc67e2ad5c96e3a5","impliedFormat":99},{"version":"2919501096a871a68fa6bda28480d7c237b232928215d6edec67e75dafc820c3","impliedFormat":99},{"version":"7f7f3dabb63cde6344d767f66379aa90b17c87362d999507724758247445d005","impliedFormat":99},{"version":"1d6f7095a9b7bfd7d035a4b07f23378ba7c44993d881b75593a252f186671e51","impliedFormat":99},{"version":"952f574aeb762b9927559c9d128dccf663352aa92736ebb856d2cec80fabceda","impliedFormat":99},{"version":"c06c1379c3f1bcf21007bd9a92e8c7a8e63611387392411bbb2399c0a4c5ae04","impliedFormat":99},{"version":"0fab16fa249312e20d9a96e0464c7ae63c841b17c02401a59ccd0bcdfa67bfcc","impliedFormat":99},{"version":"027464bfd5f5d3110b7b5303ee3a09d3bd74e630393c5caef2cbfe1bb6ca59d7","impliedFormat":99},{"version":"3e9aad7dd39dc61c41d0c249427d39b3548f7ac02f2fa2e4a813c38a8e1a2e01","impliedFormat":99},{"version":"3f28c2bdd8d3da9487f032bf85ea09bf9f24f6f02ae2336cb65e6988aa92de5b","impliedFormat":99},{"version":"6a437b4b58f8b3b220f3ae8af2230bf3bdd0fa4c17db62a9a2a03fd224a68a70","impliedFormat":99},{"version":"e16749a9377888735e5edcc765da4ac2f5a552de2ef46d930039b2d54f199fb6","impliedFormat":99},{"version":"bf973f547b27688728916b64a98fcfa836772e7382211a9692684947220ad550","impliedFormat":99},{"version":"507b0e93358d09b74a0caef2370175290a47c790dbf71fb63d1b4593b7e070ff","impliedFormat":99},{"version":"eb7e05259b0603e91365983fffb6e6dc1e574f1cbcf09c51bdad4f3717869a82","signature":"5679163e510a4314da81e928dfe7e72c6671b0377ebfa606c80e18db43ad402f"},{"version":"8269474f9aca3f56fe5ff007900aed4be90d6271a628d561d20cc29de0d5576e","signature":"298cce3b54e8d74b37facacfcc1297add32f454323d60ed4b4ee24ad651c76d4"},{"version":"88b5d609cf1c008e5d7926489df81bd606581dd083772e8ca735c1c0bc103093","signature":"3ba28f6b4d58c39bee9b307f9a7267970b31adae4c3163ce2fb889c48f25396f"},{"version":"dd0a4bfc93ee858cf6af173c428400652c01288761e7dc00b513652d005cd91f","signature":"a32a75b40daf9f63898f39c282c8975b28c1f9086ee5957707c075c8cadc8bf1"},{"version":"f35ccdbcb49becc34f1c71a68ad0d843bf02fea572cb852884d0a96ac7169830","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3d1ac90c29f450b8b90705d05264fd29f1034b5ebb6c2d2e9807489969e0a33f","signature":"140c9f01bd8744fc1fbb72ee2a7747039b9637b3976f2284bf1423d1bcbc045c"},{"version":"6cdf25b7999cac1327ad91005d4aba65743aee71a2972eba11f5b1164e39fa4d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0d96088dc89a49f328fc47dd75713536556aca01e187da7dce124fbd2f395f09","signature":"77e82444cebc04e9edeb4d759ea3c8be067ac6bbc3b652d668a3f483b0d5f7fc"},{"version":"ef05bfe2ed3c6fc7575cca3a8ee25f4a4aa28878b83c5d986ee47f4483f73b3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dcdb66da2fdcdf0e80f084c8347e2114f573d97619a5e37f680a9ad5656f614b","signature":"f090f8e7e1db58d734c2c7434bcd43e2ea1c30e049be3443fa3a83a063e59324"},{"version":"d322344da57346fad32f22627e0028bdfe12501d7de9eb4a103d47c9a075a85b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d5bb04855694db19d13174de99509d4d4799d6bf3468318bb74b54ffe994129","signature":"9a6473983696c0401765d2ba2558ef9b0670592e8d74239b4d5623c42d686600"},{"version":"dbbdee1f403eb2a952f5e8ea724cb1a20a2b4dd63d8232e375a750d4929baa88","signature":"7f1336dec949b3008a181a8873c5aebe07ea42b6730e6a5c6efaeff90abd09dc"},{"version":"28c7612edce38076988a57695e970654fd09467b806a8e37b38a26946afafabc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2a40fc7affa68ec88b30b2d2d19639d8fa8dadee567da499d460257372e04ab3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4e50f3ba9e8934a6d1fe00ae73264a3ab2202db1939a6fe875e3375022d62b4a","signature":"29022c2d2e6ec02be440b4da758f983ded6daf9a771dfb932ad7f3b93a04142d"},{"version":"7f13d1469822b3ce57bd440274f4a8d9c2d9925fa644d284fdeeb520f0ab43fa","signature":"248bf8fb49df3283090f3d2a137e3d74dbe93490ca12bcca8686a3e1004e5c40"},{"version":"38b62c6f9b44cd3b4e899e63d64a5dda62a2abfd9a14807995944543680ae4e9","signature":"6c7d3152f35e7feb3abd1e338a59f61ff28eb27f96fbc42a01b988084cb1999b"},{"version":"0ffafc0be8faa4fdb62e0a657016a6ddf07e40096b6f5aea41809208e4465534","signature":"7f9e8c2aef5ef9f51990ac594879a01c44bb8459d6c0d49fe5c311cf2e08af8b"},{"version":"0ed05108ce0f4538b5351bcf9d523912d200abe5b804951cf18b2e12feff6b70","signature":"f453c01ca04957da00f261867fea88fe674b34dde9b1da183dc55f2bed19f364"},{"version":"fe55ccd61eb15dd0480443215f5556cd56d9a68075a79a1294e2d9cae200d70d","signature":"9992a93411c1d80cef73f32ab5ac10acddd25700903cf8b5b47925eae8be2a60"},{"version":"bc37c33a1ff23e40250be0eb50b16aa3ebc6d24f58a55e5c41d968c4a23439ff","signature":"dbeea29fcca6bdfffbac8a6c482bb4199d8fb77cc64503880fb3dddee81212b6"},{"version":"7fd1557a212fb2a671abe96de2e1b6a3e5110a07c49076705cdaf9c3e0f81f7c","signature":"dc57421ea686f59b81eeb7885916d7a5cfcf6aac9113b8908c5edbe4d4a7a296"},{"version":"b9eb4fbe039e06b65ba30bb786e50fa9b48e25d7eb26c4cc1cceba3a6c81615a","signature":"70faab149c7f9a9cfde8ede12a99419d9ebbc61d822a7c16757902918cee94aa"},{"version":"7ad085c66c15e665b43f1055b09ddc1d9c11a3d8f21174c9d3d9205d7dbc03c8","signature":"5b361261b9d93e4a2c0d2e02bf9f0dfc60fd8c761ef6fccdabf563bd3aebb419"},{"version":"68dbb99a0ef2ffb046b1385fca8116795a7b4bf5700193b7bf2fe9cfb62e72e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ded33ba85bd4f5491a76b8972dfb3104e8b7b4e1b256c44313d7ea9d21647d2c","signature":"43d84b56d871c4b5bcfbaae3b58381ff0a77d0bca1733ded9b89350275269033"},{"version":"1f260100362d7309e0cbae29fc09c4c36be2e4512013a3f6cd4706ada09c6675","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f911a52096fa219660557bc3c9ccb5745889d8a357674e8ae67585678891e106","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb2d10f9aa6efc083456e706d085f717688c6c0a4030e0c0a1afe45068b41d1c","signature":"88525a755e322d9d9eac12bc44f4e51464db195ab9b4a5e0534d7a453fcf586a"},{"version":"b1a83e0013403df0cac8f2fbc11e735c29efdfc41757d6c49702a330510605d0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a2cadf5506cf6c268f65051dd63ddd5cdc82f5effcf658778af2b12e497726b","signature":"3d3c808c01d46ac6f212b5bf9a3780af30c9ac93fabf3f754e01eece8478207d"},{"version":"2e1f498ba2e37492111a97c6048e07af342ff8df126eeaac6cb99f94372f8ca7","signature":"4a997dea3de3d148c650f5ad6c57d75d5adb6655108e0af42e57f9661d5a9297"},{"version":"759a24229c02dafd2cd73ad6e3325c043d16ad85081d2e76296664bd96226ae2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"315789c8527a430a3cd968051ca6d731a0f13a47ff395dcf180e0d02dbee4dc4","signature":"0b8a37fabf134f4cfdaefe57ab21468b1c97d26b90c4ff558d695fb8e4c11be6"},{"version":"9b7287bd51e848b323551afe464c4a91ef2b74bf1ed703dc7c7c5e35cd9073f4","signature":"bf47aee07d830c691e0bb1caecf0a38aba368d98da54866d98258c4057feaaee"},{"version":"45eddefc55733af83490debe404d6f06892219189f1b3cf4470e304415600773","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89f0ba8c9a02ad55e23f296ff35f0d1d6361a190811401c3f0d767a1aeeefec1","signature":"11a904b87a71e58a2283da4755e0042d78cd7f56395ab9a1b6ecb09290f3672b"},{"version":"5aa6936f80aaf206b952e46cb830f50e49e37862c6cc4fdca99000c797995a54","signature":"2bb79d1f86f6d11a1a240d2a4a538d676a6ff8231126766ef84667cc2e945903"},{"version":"1f607599e3d2f94f8bc20f8f46a594132cd1b1b1004f0a4619dcfe84f792c774","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca3da68186289d0f9bd93dc9a7e5c3eb30dae2526ca55be4b106af239b63d0fd","signature":"d539ac9920f9a947cd986cb61772e65f48bc0442d1d94e2ce8d6e25f394cedac"},{"version":"fe17ac85f4e0b305bf44f3a4d81cb75397d9d7776aa2649ead1caf2291f0d416","signature":"8326d42a2c5e8fd8339f23ecbcb4a89f839e171090d9d2daf0e758e12a154c89"},{"version":"22c8ef3ac26c1ded1d40c6e1c48b6382fdf0d56a5a3e77f8bcb3bb198a1f769c","signature":"d394730410e0700f09bd96049137fcf096ceed2f5e7bce00a2937aebc9bf4240"},{"version":"c7f6c31638211891b8f6ea106d4d9ca0cfe249adba1528b2d1ebd0f267fa324e","signature":"b70605e01f0bebcb73e3de21b8c1dfa27372859c9a142d66d8d69f1f91e99adc"},{"version":"5d513a6a908bdec9f0c3a72c4fce232063a7365983847c1ade848a9970e97aa9","signature":"302704f3830a828ac25e5b3d330b257810004892d2acf61a6a656b05978d7a2c"},{"version":"bcf829509dc2bdb8f3851efb4451010e917abcdd8a52e2ae302886045c0e38f1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10ebed90f6cf7c3e637d5595d4dedc3377575aad097fd0b28e15312572c09622","signature":"7d26b77586708051f6f1735b57756edf0be83ca4670c4af58a8e28b965a33a08"},{"version":"0ba02535f72f0cc1712b1a073897c522f3460eb86620046f1f3b250ea79fa567","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2b4b00a99c93370ce060e954393ebac6d299d87a0400b9a69307c97e8021abcc","signature":"1af687725c0895163ae338b1c94acf5819a042e98cfac2dd6e83b993c57d5623"},{"version":"1f1d8292770ec1d17820eda3e3ca60550d3c356d4f4832e211e9ab2ae5d5c9f7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"07e108a85e9c41db3bfca18812565985fe9c4185c09e44cbfd365364daf67e80","signature":"7231ddbad84b7265695458d181b33e24e857a11dcf40f694a4dd42b3e265293d"},{"version":"a82585ad71d31401551de3ffa5389e8b078cc4211320f84ac2bf055e522c2765","signature":"60ff22f8744ceb8cfa14cb7ba540a56a25d7f4902114a3ff67a6e54586a3d3b7"},{"version":"d98bc291f61d6be8db9ae5fab941757fb72f211684af075289bd48cccb780aa5","signature":"4b0eec9971cc44cf2d3c76d33c5d4a7e7465908fb225e9b612064b90d5fb394c"},{"version":"92b944d4674ccbb19cf8373ffa9e6f50fd707709093aee9fc445163550002a40","signature":"8f7b9781bbbed468f49e1f74933b7c0e5d2c6ddf8afbd6a5d81183d053d43a28"},{"version":"5be39f3f33dcda0458cca755ee8c910507c3d2683e10541dc7e5a8393fc82601","signature":"94b0e10227654e51b43772a7332db7c9bebe31377ab1cd1cef4797d40a3a0c6b"},{"version":"7236d482e2ca1a6307e2182466f18dc8ee373209e5588f156b019f949cd9ece0","signature":"c92738c8f42ef530edebc3a1912a4ba2ec85ad86494839d23b6084782f9f2e91"},{"version":"da35015d12dac52832201a4900b07e4b0f4fc08f282eee6e7131d895db1930c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7d0aee097d3d04fc9227b58b76f92516a8b29f419ac2b93d10b58ea75f213258","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"24ac88c5cd809281eca45ae09b8b16dec4318221ea50d1b47888254798a046f9","signature":"c97c4207c753de5cccfb48d3488e193f8846f302690bd3ff73f4de951675b01a"},{"version":"63bb37a1d958427795e2e6ca7fb9451aff711e665b45bd4878ba693da9a140cc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8f785b87f0a4f596648fb2e5fb351d34d4bffa5288980a8eb52ee82ed27180b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b581577a6affe98a790ea707971843ad284ae4cbd4bd1dc17642196e3c0de158","signature":"840d3e2bdea7d5a418436aafedac9749b1d9de78bda0f825a48869cfbb3e7f83"},{"version":"3b8a02a9dda0124bf30a030727c96e3d34272b55369bc55cf55102cc90ff4a41","signature":"09d3eb6502b5bfea1281c54cfb4111b4a05d9716f4643a5029f964c230b5b551"},{"version":"0eee1242c13bce68990b788037aaecdb865d63943bc7b5681b8688cbc6d64e60","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3be9843036cf2c658821504c4931c7e1055db71db2a607b9bc5d8aa7be679a2","signature":"3a526554ad06e5c46700e8b1ac5e6f817fdca923787a3c9344acba81e8d17ff1"},{"version":"ae3566ec3d167f05176112f609460a77863016fec3216a87a15c36bef01d370d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c914982336f64f67076a86d5569fd1b878a42f415962a07d1d151a4d65d187d2","signature":"33831d2be2fefd1ecc0b722e8270094b857a42109f3eb3bdb5c5e666233c588c"},{"version":"4d5caf53ba49b41ff378943f6b1dad1ea8e042426fa713b57c9524219bbbf573","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd703e7c27cc5dd4780eba552a51a698b459c9f12dbddfeafa9e0d216a4e66b2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c09c42c93bcd3ed631ea6902d069b5798db25862e47fdf4ba5f47ff0d36b2a51","signature":"fa9886eda8d6ff931fdf8e61b9af2aa42491e152278324020c487e489e778f70"},{"version":"bf82447aeb19b4df2e40900f920c15695a8557392588397ce359c51b133c00df","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1e4dd6a5a91d2798fa4dd70d7e6f682475942bedb36b9f870cbb15e5c1f1a54b","signature":"a8a191cc7d792c8cc2d87c992ffee823187689960dc717e122e158f24b77a242"},{"version":"0a5fe133b4ef41f0f2443b3cc82c4b99be11738a93d613fd12014e9d632c2fcd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbf3758a6cc16ff397b8a2a27221d1f6d5f053265e353af0f37b356f0384b85b","signature":"3d07ef5fca347d934f76c6eb3558e0a81da33951129d307c04716a0812321893"},{"version":"e9dc912bf8a7678feb40d7d996c9263bad3b7fdab9ad9ac95d4e4ed023403d29","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b3f5e2463bca0844de6802fe4ee8e1e29a3c11d14fdd3fa7c96ae69fba8d74e","signature":"053cec7f0a8bd24eeddfee887cbc9883f56cab39ebed9e143de9f0a6cf34d202"},{"version":"d878f9fb504fbde395cd7c61e48f2fc7bbc7d0cf14828004f95e5f9fe64f238c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"12564f17d21c31e77240293e7434c0c933945a3a3a142130eaa166d0042f5529","signature":"ed4aed28c29ff0fefa86143fc6824969cb43f6bde467d4f9254c84372fa63cfc"},{"version":"3e8a4a82fa1baa0281a0ccf309c22c954488a949c13a7ab6fd7c171b1bafb361","signature":"13428a7125f291070d1c531f233c9e8719c80160dd3587d51adf86a3e41bb62f"},{"version":"b51fb5ae439f311990f5a5c2fc4914fda89e5ebd585b33d22d0251afe382d391","signature":"f49a7f528e4e42999b277a5ea73799e735e349e562a0ac6c97b99644a13a3ed0"},{"version":"deb873b1dff75e59633350db7fdd9e3c125d248ac7bf2c193a81e9665bbad9a1","signature":"0502f677499fe5b2d8cbb7f8e703465005e5c77788839d14377ee4b3da22fe5a"},{"version":"d9628bca2f50c1a70ef77c452fe293c91380dccd76c285dd3aa988c0f93fed8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"59c0ddf46c0d1d17e34ceb4c253a3f3bc7654c450002d8f8080476a3baaf5755","signature":"f7d9758437ce102b893d78f8a901109a32ffc713b3c2ab288e8e15860dd3a835"},{"version":"7adf0dcdc081964a00a2235aa42fd757563b15038955013b98097c5731705a2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d9df7df9ddd168648cb6e27223b325763738bccb1b57e965ba0d8443cd166fe4","signature":"d786daad1509af6e601e8de4259a2c6abae27fd33287b4936fd079fbfd1f0ce0"},{"version":"99f67ae9774e4bb88839948649b26a55dfbe8b99ec80ecab4c548102d2ddcaf3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aae8eb9b4f313c457b2f82fde10a63f117333645b818b48d2ed26fb2333ca42c","signature":"b76cb4bbf6287754fb7246ca57b8b0cfc52c84d5696a3363f193d2a3fa0b1e16"},{"version":"53ed7ccce63fb30e129e73dd0abff74d68929d92fbe3b20f8ade965780b2353c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3abcb2afd6c10eadcd9e5cfcffe3f93172b891ac80b079ad1421105d1574b983","signature":"2cdedb09674dadec42708ff08cf53e8ebfb3dc9402a0aa42464a061d228c7ef2"},{"version":"ade0a7d50f110afaed90ffd5f24f1617bf9b8a0d2f118530892d139ad67f29fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4bc4b9b2e5a66597bed3af39f456ab78cc11601400c5adb4ad46a173bd03da41","signature":"2dc1bb408cf19157f86ca0f3984f3837afca66209bd009a34b84daf16f8c7543"},{"version":"a3311c2d4225d8eacfa5a9e662811aed57fdb4de78b824f1a702311b3f99b09a","signature":"651d947dcd8fb9009c702ecf43eea7c50c9ebe342b6e0619cbcda9d8a8b64e10"},{"version":"d7e17a1b90344a6d9c26f1462f77d6350a6882706064a36e5d640f5726ce49a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ea2d922ef7dd2904b092b91fbaa35be0af427504b9fc7e14ab5fbb6cd7c40846","signature":"cc4b917492e221996d1271af2f86e5e864c2d8053a299038dcae940e332e312b"},{"version":"5456720ba13d5a5037b07c10816207ca9a81cd79a370af608115c578d61146fb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"695b2c91c87bd844d4b97002869db4dae9f2c96de4cc397b6841670d184a3a8c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a073d10bc03e721d0b1ce2620253ae9b69daa32fbebb2f7a8c67e7e1579c6f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a80fa136e8559ddb40afdfe957e7c625614d02ad4a72476a1dd758fc31ed2e54","signature":"a1acf9b04a9f848692e1a5cb1bafa0e53bcefdbcd40c5bf311062ba23188a339"},{"version":"e79d387b4470cc2ef4df34e09b4113eb85200a7c8c6508e4a2f418c63e29ae5a","signature":"ca37703109f463d6107118f4b3d1fa0eca1bab385f6e35583a2fd13ef66b3112"},{"version":"56396e7c37789adc6f28a7c461ae904c01688c2836ac6278a9f9ee864078c7b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4025a1efab8877af2ed8d8edda349736055a800404134c6b24ee95cdfb0c0ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0a46d4afc023db18a3c9d7816e3f90d960122319e2765c5d686f76da661864bb","signature":"b5a97abd79ec3360bbef597b4f34eab1b9f0d3545d0c3f46e3b3e2ec6e91771b"},{"version":"4f649bd1b169333de666f079f7989d184c27109ce26544354957f5be00abd232","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f84acbbf9c1536d22e69d354fae1dc2d43430ca0e524721df713722f9e26890f","signature":"bcea8c0d3b0636e8255a7b6f3c42b075dff08702bc473fa7d6ad74adaef773b1"},{"version":"b5da1cdeaf5fc3b53aab62bbdd5da7d9385fdb2839a18fad0e3b2c31c5d888da","signature":"a43861be0f45c9bb0763d1c8aaa880b6c5d0b2a37a07bc65bfde949ce648ad79"},{"version":"0e30e16f547666851c4fe51505a9feeff6508b2d720b7b8c60691e5158db10d6","signature":"cc094a8b2d9686d5ff268266e02eedf9d66e2389a04c46fc49cc819d23134a39"},{"version":"1c03c99ee1b5f5bcf0704dac9398e922805929cd23a7ad246ad0d67cfc3826db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8adc0df2bc9038f9423e9947d03f490375a9f615cc8119055af8a695bb830a5","signature":"bb01d18cd374f84cff1cc159163df3a7f602a298d8e925ac993012fbcd7e2bfc"},{"version":"6096174ef99bb11f2656cd3f15a2fb649e504782c6ee27090448b681e33c2b40","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"987a3bc405a132b704d415e99a6708c6ea54d0a70766ecf1ae59bd13034d848d","signature":"2d44dcdbe1d297af2ef6785176a9165f4feb886490712c82ab8578ca96ee0d10"},{"version":"187610881a6b1f7370788848d0a2af5a17e94b9b437727556ef3d2fe018a98f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0709632ce350e4970bc7fdb88e656ecea4b7579875a6f20ad20cdaaad26f369","signature":"f9a530c655221c9f5a24fc3421f341b21bd38da824f7612da7c87804306eca36"},{"version":"2b3793a5342b5d7ef5498271aa50c1fd31ce56b70f70d0dc5f9da4174eb1e5cc","signature":"9ae9233a7cd435509757e52ca1503b31fc923e1bf163d9bfba847b1a7dd89e51"},{"version":"1391eb93befc7b56fcc8fc9d4c37affcb37252ce6e91400da018023fac32c807","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17b39df68c9417376ab9d3f845ad45905499eec1ee9798fcdb980cbcacbf2f44","signature":"f8c081ad7f58588db5940385eaf280c202e6ce42c0117ad62f774ffc421712ad"},{"version":"5ed076225f05deb1d6965f90227ac87a5b06e66b9a30481c699bc0fec1ed6c9b","signature":"883833dec7bf0238bfbbb33db50c709cc7ca3a1f6714d17992c0d7e82a964d00"},{"version":"e0de0a265fa6fa0c0460995f60fe2255ab2ee907915b7a2fd8f509053af31ab1","signature":"3d655def48973efb420a82a2e05119da3a2c45672bdfc7a695f6e569edaa417c"},{"version":"ef25eb1291626f363fdd9b26186aadc8a5821fbdcba58c9464050b1269e27063","signature":"f793afe8c2bd867c272d5562b829000a5df12669a8922a4977e3ea420299480c"},{"version":"6e1b2b7611a6083e43d49888a2437c99c989141e611966568fe0527037358608","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3f072b168376dc71baf99d36fea4aba49a269f5852826888a3c6c95e5c9cb202","signature":"7fb20dbe5a83b73a18118cafefc659b66c75e285f8f6100023eed6218035191e"},{"version":"98ba07f2f211272213e4201fa31bbe0de1f95049cb411f0c7dec9e9de1fc8232","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d9a8784de549deea41f12c4241eb87f77fe7b7f8222ddd7a6ea05085980d5c9","signature":"88af6abc2bcc060a798687a7bc8f8bc23f47f5bb2ea736e89666093f4e682a0c"},{"version":"365e7300df17dc4f13c79443fecf0e1adf057cf17556c2c6fc20f1021f72ab22","signature":"e18de9a7b62fac87db7bdfab03946f00b49de5cfc11b37f39d95c1f6d05b7dc4"},{"version":"731bdeaef898c61ad06b71022c0669d246ac039c1368fbb38c2605d34d867da3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da0f84fcd93700b4a5fbf9c6f166a6cc19fc798231bff56dd1e3875bfc6966eb","impliedFormat":1},{"version":"634ff08e0143bec98401c737de7bfc6883bfec09200bd3806d2a4cfc79c62aaa","impliedFormat":1},{"version":"90a86863e3a57143c50fec5129d844ec12cef8fe44d120e56650ed51a6ce9867","impliedFormat":1},{"version":"472c0a98c5de98b8f5206132c941b052f5cc1ae78860cb8712ac4f1ebf4550ca","impliedFormat":1},{"version":"538c4903ef9f8df7d84c6cf2e065d589a2532d152fa44105c7093a606393b814","impliedFormat":1},{"version":"cfcb6acbb793a78b20899e6537c010bfbbf939c77471abcdc2a41faf9682ca1a","impliedFormat":1},{"version":"a7798e86de8e76844f774f8e0e338149893789cdc08970381f0ae78c86e8667f","impliedFormat":1},{"version":"eebc21bb922816f92302a1f9dcefc938e74d4af8c0a111b2a52519d7e25d4868","impliedFormat":1},{"version":"6b359d3c3138a9f4d3a9c9a8fda24be6fd15bd789e692252b53e68ce99db8edc","impliedFormat":1},{"version":"9488b648a6a4146b26c0fd4e85984f617056293092a89861f5259a69be16ca5c","impliedFormat":1},{"version":"e156513655462b5811a8f980e32ccd204c19042f8c9756430fe4e8d6f7c1326e","impliedFormat":1},{"version":"5679b694d138b8c4b3d56c9b1210f903c6b0ca2b5e7f1682a2dd41a6c955f094","impliedFormat":1},{"version":"ca8da035b76fb0136d2c1390dda650b7979202dbe0f5dc7eaefcde1c76dee4f4","impliedFormat":1},{"version":"4b1022a607444684abeee6537e4cace97263d1ef047c31b012c41fdc15838a79","impliedFormat":1},{"version":"dd0271250f1e4314e52d7e0da9f3b25a708827f8a43ceff847a2a5e3fd3283e8","affectsGlobalScope":true,"impliedFormat":1},{"version":"47971d8a8639a2a2dd684091c6e7660ec5909fed540c4479ca24e22ac237194e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e1075312b07671ef1cbf46409a0fa2eb2b90bb59c6215c94f0e530113013eeda","impliedFormat":1},{"version":"1bfd63c3f3749c5dc925bb0c05f229f9a376b8d3f8173d0e01901c08202caf6f","impliedFormat":1},{"version":"da850b4fdbabdd528f8b9c2784c5ba3b3bedc4e2e1e34dcd08b6407f9ec61a25","impliedFormat":1},{"version":"e61c918bb5f4a39b795a06e22bc4d44befcefd22f6a5c8a732c9ed0b565a6128","impliedFormat":1},{"version":"ee56351989b0e6f31fd35c9048e222146ced0aac68c64ce2e034f7c881327d6d","impliedFormat":1},{"version":"f58b2f1c8f4bcf519377d39f9555631b6507977ad2f4d8b73ac04622716dc925","impliedFormat":1},{"version":"4c805d3d1228c73877e7550afd8b881d89d9bc0c6b73c88940cffcdd2931b1f6","impliedFormat":1},{"version":"4aa74b4bc57c535815ae004550c59a953c8f8c3c61418ac47a7dcfefba76d1ba","impliedFormat":1},{"version":"78b17ceb133d95df989a1e073891259b54c968f71f416cd76185308af4f9a185","impliedFormat":1},{"version":"d76e5d04d111581b97e0aa35de3063022d20d572f22f388d3846a73f6ce0b788","impliedFormat":1},{"version":"0a53bb48eba6e9f5a56e3b85529fbbe786d96e84871579d10593d4f3ae0f9dba","impliedFormat":1},{"version":"d34fb8b0a66f0a406c7ce63a36f16dda7ff4500b11b0bd30a491aa0d59336d1f","impliedFormat":1},{"version":"282b31893b18a06114e5173f775dd085597ca220d183b8bd474d21846c048334","impliedFormat":1},{"version":"ed27d5ce258f069acf0036471d1fbb56b4cb3c16d7401b52a51297eca651db62","impliedFormat":1},{"version":"ec203a515afd88589bf1d384535024f5b90ebe6b5c416fb3dcca0abd428a8ba4","impliedFormat":1},{"version":"32a2a1374b57f0744d284ca93b477bd97825922513a24dfe262cbf3497377d96","impliedFormat":1},{"version":"a8b60d24dc1eb26c0e987f9461c893744339a7f48e4496f8077f258a644cffab","impliedFormat":1},{"version":"3f9df27a77a23d69088e369b42af5f95bcb3e605e6b5c2395f0bfcd82045e051","affectsGlobalScope":true,"impliedFormat":1},{"version":"9fd080a9458c6d6f3eb6d4e2b12a3ec498d7d219863e9dca0646bdee9acce875","impliedFormat":1},{"version":"e5d31928bee2ba0e72aeb858881891f8948326e4f91823028d0aea5c6f9e7564","affectsGlobalScope":true,"impliedFormat":1},{"version":"9a9ba9f6fd097bb2f57d68da8a39403bbe4dc818b8ccd155a780e4e23fa556f2","impliedFormat":1},{"version":"e50c4cd1f5cbce3e74c19a5bbf503c460e6ae86597e6d648a98c7f6c90b596dd","impliedFormat":1},{"version":"fa140f881e20591ce163039a7968b54c5e51c11228708b4f9147473d06471cf5","affectsGlobalScope":true,"impliedFormat":1},{"version":"295eca0c47be1191690fd2fe588195fff9d4dc43852aceb8b4cab2aa634579f0","impliedFormat":1},{"version":"59ee7346e19b0050508a592702871dc943083c6dcb69a47d52e888115d840781","impliedFormat":1},{"version":"067712491fb2094c212c733dd8e2d56e74c309a9ce9dac9e919286b7245a1eb4","impliedFormat":1},{"version":"a5eae58ac55bd30c42359e4b01fb2be5eddac336869d3f04ffb4daa54b58f009","impliedFormat":1},{"version":"d12d691ef8933e8db39f2ca81d6973940ff5e37bb421752f5b6e7bc15dea3abf","impliedFormat":1},{"version":"4c5f8bd9b3a1aae4e4fddfee41667e495a045f73ed603993038fa6a8ba92fa14","impliedFormat":1},{"version":"dfb274ab0f319cf18ce7152067c25f984c7fd1924fc72b3f66734588444c934a","impliedFormat":1},{"version":"108c8c05cbc3fbbbd4ff4fc0779c9bef55655c28528eb0f77829795dc9f0b484","impliedFormat":1},{"version":"a7e5444d24cdec45f113f4fb8a687e1c83a5d30c55d2da19a04be71108ad77bd","impliedFormat":1},{"version":"41ec17e218b7358fcff25c719bc419fec8ec98f13e561b9a33b07392d4fec24c","impliedFormat":1},{"version":"23c204326746e981e02d7f0a15ab6f8015f9035998cb3766c9ddbf8ea247aea2","impliedFormat":1},{"version":"25f994b5d76ce6a3186a3319555bbba79706dac2174019915c39ac6080e98c7e","impliedFormat":1},{"version":"dfa4e2c6a612d43851ccbc499598cb006a3a78bc8c7f972c52078f862fa84e47","impliedFormat":1},{"version":"02c1705fa902f172be6e9020d74bcd92ce5db8d2ef3e1b03aabc2ac8eb46c3db","impliedFormat":1},{"version":"99d2d8a0c7bb3dd77459552269a7b5865fa912cedab69db686d40d2586b551f7","impliedFormat":1},{"version":"b47abe58626d76d258472b1d5f76752dd29efe681545f32698db84e7f83517df","impliedFormat":1},{"version":"3a99bbbbbf42e45c3d203e7c74f1319b79f9821c5e5f3cdd03249184d3e003ce","impliedFormat":1},{"version":"aaacc0e12ab4de27bdf131f666e315d8e60abec26c7f87501e0a7806fc824ae6","impliedFormat":1},{"version":"3b4195afd41a9215afc7be0820f8083f6bd2e85e5e0b45bb0061fb041944711e","impliedFormat":1},{"version":"108df8095f5e25d7189dd0d1433ac2df75ec40c779d8faf7d2670f1485beb643","impliedFormat":1},{"version":"ddd3c1d3c9ff67140191a3cf49b09875e20f28f2fc5535ae5ea16e14293a989b","impliedFormat":1},{"version":"7b496e53d5f7e1737adcb5610516476ee055bf547918797348f245c68e7418fe","impliedFormat":1},{"version":"577f44389d7faedd7fc9c0330caf73140e5d0d5f6c968210bff78be569f398a7","impliedFormat":1},{"version":"3046c57724587a59bceefadd30040d418e9df81b9f3cfd680618a3511302ed7a","impliedFormat":1},{"version":"15ccc911ed15397e838471bfe6d476c28deffe976c05cb057e6b1ea7491242c2","impliedFormat":1},{"version":"64b5a5ebdaead77a9a564aa938f4fb7a45e27cda7441d3bee8c9de8a4df5a04f","impliedFormat":1},{"version":"a48037f7af5f80df8973db5e562e17566407541de284b8dadf1879ea3aed8a2f","impliedFormat":1},{"version":"dab97d96ce986857150db03f0d435b44c060d126b4a387c7807f4e9f6c92e531","impliedFormat":1},{"version":"85f39366ea7bc5e34b596fc97de18a7e377856755e789d8e931054f2191d9b8b","impliedFormat":1},{"version":"daf3ea3d49f6e8a2fa70b7ca1f21bd97f1b65021b31fbfccb73dd55f86abb792","impliedFormat":1},{"version":"b15bd260805f9dd06cd4b2b741057209994823942c5696fd835e8a04fb4aab6b","impliedFormat":1},{"version":"6635a824edf99ed52dbd3502d5bce35990c3ed5e2ec5cef88229df8ac0c52b06","impliedFormat":1},{"version":"d6577effa37aae713c34363b7cc4c84851cbabe399882c60e2b70bcbb02bfa01","impliedFormat":1},{"version":"8eaf80ad438890fe5880c39a7bbf2c998ce7d29d4c14dd56d82db63bd871eefb","impliedFormat":1},{"version":"9b3e7f776f312c76ac67e1060e5398d7ac2c69d6a3a928a9daaae2eb05b15f56","impliedFormat":1},{"version":"202042eccb4789b7dee51ba9ecab0b854834ea5c1d6a3946504bfc733d4468c3","impliedFormat":1},{"version":"2b2ef76a9f36094b07ee6f76a5ac6903f2f65c0a20283201814a8d1e752cb592","impliedFormat":1},{"version":"8882e4e087d0bc8cc713cb3d8090c45d33e373e6f5c83e0f8d00fe6a950ef875","impliedFormat":1},{"version":"baab78e9401b9a82e8fb3634de0d3750cbf4d3d6afb59ba28472ab15afd3a749","signature":"3d01773dca02fddc18139d243b8adbbe1c6c6447b8235bdc3cbbd4b493e7ffbd"},{"version":"1cce0ed0784dfa68a0572c20ceb1a173664dbb3ac59eead22d55be246fdf17d9","signature":"5de2fd3f978ef1724ed1d72271f8d9bd911d19d80a709137225e173127e3c615"},{"version":"57e3b4916970da260c692cda82bc670552fa93563710da8485ef3f1a40fc0cd8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"24772aec0e0f59dd17a2a1c4a924fa4fc228f24c64bee4fee8c5c08965f05925","impliedFormat":99},{"version":"e636cb7d61143bd3901daa91d1c2c3d8a53b677f6bfe51fafbd2d14a51efdfd0","affectsGlobalScope":true,"impliedFormat":99},{"version":"d33b19c5e9b2f8b26b1875c7aa12229cdd1e3ff0e809c89189805a05c626dc3f","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"077cd7acbb4a3b50b4a01690d6a7d2583ebb39335f612763442a4d33dde01c36","impliedFormat":99},{"version":"31b29d52cedf2cb6be5fe1a8ada9aac55fd4933351ad18d71d77466659c95bed","signature":"6444595fd97d8356ac957d200cf99d4653612f84715bfd4905ec02a1162856b4"},{"version":"2a01af100d878ce1be4ad967937beb31bddb09778227c52f425fd81cee137c8f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cf0f4d9e5edafe3f777e151b9719afb37ca6abaa904fb8289367fe99913f0ad1","signature":"307d71207bdccbbf886a1b1044f39eafddb7b2457a81eb1a1843a81db10e37eb"},{"version":"a30abd322bbe6fce3aae6ef89bfdad79fe165b3dc284d001d8dd05392cf22033","signature":"13573020c64550f2efba1d5a0380288280a85b4da7c0693f1f25c759a8114211"},{"version":"cfe8a898ca6cd1e4e6aba4518ff485036d62faaafbe219f8192067d32016306b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03493681f3175378f73cc1994441b55eb2f178585c613377853cdf5dfb39ecc2","signature":"4540e50e72fce7b0cbb91e773d9c3ace94268cad237d1a032f294b9786a348b1"},{"version":"0ff60462f4b1f78e599d6266ad526e552d85c49043b0d5f48f992585da46ae81","signature":"5c539fd09316939fa4dabeae933f1289e99585d6fc398630670d5d16bb41137a"},{"version":"a0cd91fdf58c51fd2e581171ba5d3bb4a8f374abd39509a597314c7d3ce2c0c3","signature":"bb97d8ce68b8061ea36f60acf43e82f3560c4e53522b05949721c6b7d014d89a"},{"version":"533063ff41e323fba7e5cbdcfee392348005ed117c5badb1151f6b4803a7a3d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d096c0b73874d64e56e5fe99fb94f14b5a6fc0824a4a1cc0251147a823c25a3","signature":"06980b548bb6ff2b15c92032296a46d7f80d3e8ad9af172f5c2ccdefa86b3fb9"},{"version":"d330961532fa59192f0330dd430076475d3a3f5cbbb60c2ba196351c069243ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34ef539c1384da9fe858fb9c16368ddf1624a4914dc3f0fabc7a39811bcd2668","signature":"ed6cfc1cf330cbbc602b7a0305aebcef220219fcd5cd3e4493e5afe79058fcee"},{"version":"f7409e1093e57b3f7be327a71c71087e1f7a767333fc10cff7c2504af7222f88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b203b12c30dae8bb84ff0f770a857445942d8c9ec8d6ee472c3ca6b0b5b261d0","signature":"9fc50c5741ded49943ed4b81fc428d0aaa18cefc596400fdb71fdd11a21d8d8c"},{"version":"24803211766a60a0fca7ca541d6a158d376c65f7ae4827a8661063c2e70c06e5","signature":"6e2681371e356de1c4fa982616bd1d26b7c525b5ca9f75e1f688b83332e8a81f"},{"version":"1453393d564bcb47dd35ada6b469c661ef5d6c98f9dcbd7bc0f9eee3470ac944","signature":"22462cc125563699336669ccb959793d6c462626957a1da4ec4a639d4341fb3c"},{"version":"7c56faad4a628f9671b73a1227c941f930b55649699ac62931e360389775edff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"579a472a70260bf6b40d68aeffa65bf00fa5c59a44bf214f2385dce399f5898c","signature":"fb4582d6a3a9b2152a49c918d6c98ace7ce35978ebe850af889e7aaa526551eb"},{"version":"3fa0c656d89d31fa67f25c8b3eb9974b1aab6fd8fe5ae0fafd521e7d6808f71c","signature":"cf64d4b205595fbd260e6fced4216298b35c82faba7dce73a9e205add66ef85d"},{"version":"ad5ab4bc064063efa662328e47c8eb39c80888a25467fefd231a32e874162d6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0e29314c7c3239de9961d460d5347081cc6e49bf065dd0a6cca6e7132a99ee9","signature":"ccb2b3ebb1fa7bd3fa3e02c7a23ecfb2ebad06df9c3c8a9e685113d81026d0bb"},{"version":"614e5cdc3c5f89f035510a0c61652fdeba39a62c9904acde7de79fce2d60bfe3","signature":"bfe40cd3dd4d0d35754643dbf07ecb96362953f4fdb490803e122593e679db64"},{"version":"36e5bb11081348bd0869d683fadc9a4115fb28720594bdf185a13ff19faac88d","signature":"d356e9c1bad769f9e8d358a35c420cc37a0ad01ea4f865d968f3b7fe10c9c3de"},{"version":"4d46dcb6027f62db92924103d77c199455ed38d1dc1c6c29bb65a707c25f847e","signature":"14084429a03e8974f38aaa1890d6a80ea62d5c8f0b627e0f824afb0ac621a65c"},{"version":"1d5a2531ab660f7a4f8b4572b7a19c23fc5a431299ecb8c1846cf8e279b97851","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d6a819db1ca73bc3e3b29597b18ca5c36e2dafa02b171e8bb92208b2d50b353","signature":"1571b1b7546d0267d42d0c0b3e1e4593b2ef990541b260a9652427dd82758bb7"},{"version":"a62a7e45347ca1dc80dd4df8d2094790870c03b1c890960cce9628934f695295","signature":"af0b9205baeb5b5d2e4470da33f26673b5c363db0ccfe761276f8af34cfe3e9b"},{"version":"4217680981bab6d62ee8fe0cbac591599bf18f30cf2be39170d344eca5f7885f","signature":"5d3ccf27d7ce9e5f390fa882da69e253103b64bcc4e1af716d4e385d1f7dea5f"},{"version":"0571fa29dd502778997d9453169040a83607ae311f6ab6a7ce90fdaa83f86a72","signature":"6abb8469a763dfe1299c79302eb5559ecc978df41c92c0444a30c1b55710860b"},{"version":"afaea598c95b1ad2bd0ed30bfb6ad867d78bf021b73a048b8fdaeb90cff22d88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1bddf61aee12dc67fa70b5e40a9124f1f71f960b6bfe006fe4078273a7f77e5","signature":"77307295274cc402aca163afe863f0ae8a1d2e94588f2acd35ec24d77af97b75"},{"version":"d579bdba0db63d615c541195af19b783527c13bbcc870e83ea61d198e3be56c6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10412b70545a4b21b51229be0a35ddb2bdff35e164c35e214f5f56baf863f12a","signature":"8aa83e11d68ccfd7360e5de9cb82c18ce6dd67f9c7dd89c81dc67e55dab60864"},{"version":"ae6af43f3746a31699410d070400739e19ef2281ccaba80b5e4b1c4dd4ce644b","signature":"1fd318722bb7bd17560dd18e11b824febc7904ec355636c7e042efcf18859c12"},{"version":"acce55a78691675d126c86174822bc67282435510ece51f11f0fe3e3e3439a2d","signature":"69db250a4e6aaf0f4ed86907855c93327ae8e663b3b1fdb429fa0405ee768215"},{"version":"7aa764a1146707f1b9e18292969e24f394ef3c347d4f396cc6f90d39c3f3b6da","signature":"7c52c6c55104753b3519528829004136bfbe6e76535ec0a19430668fabd41269"},{"version":"688c5e58ff9137a2c5d6eb1a79475ec4c9d61c34bb10080e21d09babaa30ae1c","signature":"7a49a822cb790c72be6db966c7f0d69c641479732f211b020cbb08bc4f30a3d1"},{"version":"c51f1961b6b22a86183d4e8e166a4b08df4cf3537533f9249a79d3b460efe6ab","signature":"60f3fa0096effac152c038612c40d100ab675f4d07eaade19ab26291654c5322"},{"version":"13cc0e63b3212f43a760f9618ac9a5a26a3123954baa408ada44dd9744d060f4","signature":"6d2de774f7f1930f5a1a0061d45b779777be9dc7e6125661388a11a43f386636"},{"version":"693212d0a67ee305c09bfdd670455ad335e448c9fd52fb8c69ecbcda23eb2b93","signature":"225d95d4c8f9caffa003ab70fa3ac2d8b66e4bca291dc775b2d1ad4b676b660d"},{"version":"41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","impliedFormat":1},{"version":"e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","impliedFormat":1},{"version":"e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","impliedFormat":1},{"version":"a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","impliedFormat":1},{"version":"2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","impliedFormat":1},{"version":"6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","impliedFormat":1},{"version":"a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","impliedFormat":1},{"version":"cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","impliedFormat":1},{"version":"33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","impliedFormat":1},{"version":"cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","impliedFormat":1},{"version":"f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","impliedFormat":1},{"version":"f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","impliedFormat":1},{"version":"261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","impliedFormat":1},{"version":"e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","impliedFormat":1},{"version":"9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","impliedFormat":1},{"version":"d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","impliedFormat":1},{"version":"69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","impliedFormat":1},{"version":"fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","impliedFormat":1},{"version":"1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","impliedFormat":1},{"version":"4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","impliedFormat":1},{"version":"dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","impliedFormat":1},{"version":"bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","impliedFormat":1},{"version":"7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","impliedFormat":1},{"version":"54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","impliedFormat":1},{"version":"7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","impliedFormat":1},{"version":"b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","impliedFormat":1},{"version":"a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","impliedFormat":1},{"version":"6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","impliedFormat":1},{"version":"f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","impliedFormat":1},{"version":"325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","impliedFormat":1},{"version":"79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","impliedFormat":1},{"version":"e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","impliedFormat":1},{"version":"edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","impliedFormat":1},{"version":"511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","impliedFormat":1},{"version":"496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","impliedFormat":1},{"version":"d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","impliedFormat":1},{"version":"67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","impliedFormat":1},{"version":"e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","impliedFormat":1},{"version":"2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","impliedFormat":1},{"version":"1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","impliedFormat":1},{"version":"3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","impliedFormat":1},{"version":"d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","impliedFormat":1},{"version":"8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","impliedFormat":1},{"version":"e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","impliedFormat":1},{"version":"ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","impliedFormat":1},{"version":"7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","impliedFormat":1},{"version":"fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","impliedFormat":1},{"version":"652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","impliedFormat":1},{"version":"790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","impliedFormat":1},{"version":"89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","impliedFormat":1},{"version":"9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","impliedFormat":1},{"version":"8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","impliedFormat":1},{"version":"d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","impliedFormat":1},{"version":"d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","impliedFormat":1},{"version":"fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","impliedFormat":1},{"version":"af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","impliedFormat":1},{"version":"784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","impliedFormat":1},{"version":"e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","impliedFormat":1},{"version":"a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","impliedFormat":1},{"version":"96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","impliedFormat":1},{"version":"0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","impliedFormat":1},{"version":"72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","impliedFormat":1},{"version":"cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","impliedFormat":1},{"version":"bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","impliedFormat":1},{"version":"b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","impliedFormat":1},{"version":"2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","impliedFormat":1},{"version":"01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","impliedFormat":1},{"version":"bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","impliedFormat":1},{"version":"a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","impliedFormat":1},{"version":"affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","impliedFormat":1},{"version":"3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","impliedFormat":1},{"version":"a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","impliedFormat":1},{"version":"348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","impliedFormat":1},{"version":"235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","impliedFormat":1},{"version":"ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","impliedFormat":1},{"version":"15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","impliedFormat":1},{"version":"d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","impliedFormat":1},{"version":"7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","impliedFormat":1},{"version":"96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","impliedFormat":1},{"version":"5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","impliedFormat":1},{"version":"d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","impliedFormat":1},{"version":"fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","impliedFormat":1},{"version":"cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","impliedFormat":1},{"version":"24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","impliedFormat":1},{"version":"7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","impliedFormat":1},{"version":"461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","impliedFormat":1},{"version":"829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","impliedFormat":1},{"version":"162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","impliedFormat":1},{"version":"4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","impliedFormat":1},{"version":"3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","impliedFormat":1},{"version":"5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","impliedFormat":1},{"version":"96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","impliedFormat":1},{"version":"e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","impliedFormat":1},{"version":"63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","impliedFormat":1},{"version":"adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","impliedFormat":1},{"version":"fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","impliedFormat":1},{"version":"e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","impliedFormat":1},{"version":"8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","impliedFormat":1},{"version":"811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","impliedFormat":1},{"version":"f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","impliedFormat":1},{"version":"c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","impliedFormat":1},{"version":"e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","impliedFormat":1},{"version":"dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","impliedFormat":1},{"version":"41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","impliedFormat":1},{"version":"f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","impliedFormat":1},{"version":"970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","impliedFormat":1},{"version":"8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","impliedFormat":1},{"version":"e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","impliedFormat":1},{"version":"cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","impliedFormat":1},{"version":"6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","impliedFormat":1},{"version":"cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","impliedFormat":1},{"version":"db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","impliedFormat":1},{"version":"aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","impliedFormat":1},{"version":"298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","impliedFormat":1},{"version":"a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","impliedFormat":1},{"version":"341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","impliedFormat":1},{"version":"238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","impliedFormat":1},{"version":"8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","impliedFormat":1},{"version":"bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","impliedFormat":1},{"version":"e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","impliedFormat":1},{"version":"3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","impliedFormat":1},{"version":"f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","impliedFormat":1},{"version":"e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","impliedFormat":1},{"version":"ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","impliedFormat":1},{"version":"69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","impliedFormat":1},{"version":"d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","impliedFormat":1},{"version":"eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","impliedFormat":1},{"version":"a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","impliedFormat":1},{"version":"76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","impliedFormat":1},{"version":"815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","impliedFormat":1},{"version":"afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","impliedFormat":1},{"version":"b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","impliedFormat":1},{"version":"54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","impliedFormat":1},{"version":"65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","impliedFormat":1},{"version":"969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","impliedFormat":1},{"version":"b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","impliedFormat":1},{"version":"3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","impliedFormat":1},{"version":"4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","impliedFormat":1},{"version":"5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","impliedFormat":1},{"version":"98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","impliedFormat":1},{"version":"355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","impliedFormat":1},{"version":"8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","impliedFormat":1},{"version":"dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","impliedFormat":1},{"version":"71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","impliedFormat":1},{"version":"49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","impliedFormat":1},{"version":"d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","impliedFormat":1},{"version":"4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","impliedFormat":1},{"version":"5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","impliedFormat":1},{"version":"149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","impliedFormat":1},{"version":"3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","impliedFormat":1},{"version":"ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","impliedFormat":1},{"version":"394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","impliedFormat":1},{"version":"19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","impliedFormat":1},{"version":"728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","impliedFormat":1},{"version":"8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","impliedFormat":1},{"version":"de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","impliedFormat":1},{"version":"8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","impliedFormat":1},{"version":"4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","impliedFormat":1},{"version":"ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","impliedFormat":1},{"version":"de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","impliedFormat":1},{"version":"c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","impliedFormat":1},{"version":"fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","impliedFormat":1},{"version":"7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","impliedFormat":1},{"version":"29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","impliedFormat":1},{"version":"7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","impliedFormat":1},{"version":"4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","impliedFormat":1},{"version":"d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","impliedFormat":1},{"version":"665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","impliedFormat":1},{"version":"3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","impliedFormat":1},{"version":"4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","impliedFormat":1},{"version":"7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","impliedFormat":1},{"version":"9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","impliedFormat":1},{"version":"726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","impliedFormat":1},{"version":"05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","impliedFormat":1},{"version":"134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","impliedFormat":1},{"version":"264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","impliedFormat":1},{"version":"98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","impliedFormat":1},{"version":"1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","impliedFormat":1},{"version":"54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","impliedFormat":1},{"version":"60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","impliedFormat":1},{"version":"236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","impliedFormat":1},{"version":"e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","impliedFormat":1},{"version":"4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","impliedFormat":1},{"version":"4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","impliedFormat":1},{"version":"7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","impliedFormat":1},{"version":"b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","impliedFormat":1},{"version":"a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","impliedFormat":1},{"version":"724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","impliedFormat":1},{"version":"b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","impliedFormat":1},{"version":"3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","impliedFormat":1},{"version":"62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","impliedFormat":1},{"version":"656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","impliedFormat":1},{"version":"b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","impliedFormat":1},{"version":"2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","impliedFormat":1},{"version":"2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","impliedFormat":1},{"version":"080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","impliedFormat":1},{"version":"7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","impliedFormat":1},{"version":"07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","impliedFormat":1},{"version":"79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","impliedFormat":1},{"version":"8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","impliedFormat":1},{"version":"4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","impliedFormat":1},{"version":"4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","impliedFormat":1},{"version":"87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","impliedFormat":1},{"version":"4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","impliedFormat":1},{"version":"e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","impliedFormat":1},{"version":"1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","impliedFormat":1},{"version":"c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","impliedFormat":1},{"version":"e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","impliedFormat":1},{"version":"973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","impliedFormat":1},{"version":"5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","impliedFormat":1},{"version":"85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","impliedFormat":1},{"version":"88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","impliedFormat":1},{"version":"8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","impliedFormat":1},{"version":"ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","impliedFormat":1},{"version":"997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","impliedFormat":1},{"version":"aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","impliedFormat":1},{"version":"1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","impliedFormat":1},{"version":"25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","impliedFormat":1},{"version":"87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","impliedFormat":1},{"version":"56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","impliedFormat":1},{"version":"ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","impliedFormat":1},{"version":"d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","impliedFormat":1},{"version":"fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","impliedFormat":1},{"version":"35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","impliedFormat":1},{"version":"b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","impliedFormat":1},{"version":"beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","impliedFormat":1},{"version":"2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","impliedFormat":1},{"version":"da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","impliedFormat":1},{"version":"17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","impliedFormat":1},{"version":"a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","impliedFormat":1},{"version":"bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","impliedFormat":1},{"version":"6bc9832d675edd15ca0c8e096cc4008e2791d822cddbe218e7fe65d33de8fa2e","signature":"78f739f5b91e1135aadb4752b0fbd6b6bad0fa86b3f0e889900982b12176fc2e"},{"version":"baad5518e27c0ff3bc6192606a3c70d64e52338ecf1a1492a3582c9e8827a7bf","signature":"9d01797abc1ce5d2b2ca095bee592fa4887661c3cb1603e9f126b767d68bb57a"},{"version":"a418d3e5729d2bc1f21789a3926a6e5db364e9f80410207f4eb28b55a5c70cff","signature":"afdce15dde5537aa0c81dab15a2367924eac28ab8f25ba3e403f7338da845b92"},{"version":"71dde8ef5faa2b2f5f4a8f56944429ff768600489ba021017b68473c93660eab","signature":"b3a61d1bb2c4eff882c25e5284189e1934aeb4af535fdb36694fc461cf4b7068"},{"version":"0b791c213954a91e7d80daceb4b7d7b53600a731e2227d3541d88a09fcea1621","signature":"b6e882b417c55fc40bb0b42ad061d8f97bd0b2fdbd2aec5aa2aa257420c7c2ec"},{"version":"c041ad3802a420609f6fbb3200a946b897838cb21b76e176e78b0cafa83698bf","signature":"63b2f936da9faea8c52723d6b78ca09ee0bb769833160bbcb000be8b7cb456ac"},{"version":"9d0c46e2b8776a71972db76904d933f54d190601cbd57b82438254c275808ccf","signature":"95bee50322d4d787b4a886030c691a2317aca49f557c115e52f95938343f65cc"},{"version":"df804257d254a2e00d640a55eefb2ae628da95dba0085ca824a04ea3ff69ac99","signature":"2302a6d37e153539b259b1f3bda1c10d344984b15efa30ea39ff5c83b5825977"},{"version":"74226e280a2991fdeba3808665dcce17f87736137ca79404c5d8d7c668eec8df","signature":"b4e98fe21b2b7cca7ccabb5169df346479a0e3bb6bf46c25946174977917d316"},{"version":"bb520dd5abb511ac234e88f420dfbfba03a6ef74a9c783850bddd833b8235b23","signature":"80923ea73f37baf4c06eb870a02060c68149fc8a8e47daddf6b55af1047d2b0c"},{"version":"372db055c8310930dcb90ffa00df06b44ac8e725c75e0c172786676ea6a11794","signature":"6179a86622a28cdafe5d99fb99e1ab06b1a06011bb9a8ae9d65d4697e61d5316"},{"version":"0d7213f8b71376061118e4f91a6faac51b38b372ad171b4df50bf3559ac2b3c0","signature":"37a718acb4d240ce0d45b7082a821b9bc8d9c523df47980aed0547887beafcb0"},{"version":"b858c7849e256828563264a2345354ac829be7d7afc77e2c04f7683b81ccc79d","signature":"e724140889de1a68b6fac45652942a37dbe94d0951da3249196817e9004181c8"},{"version":"a4c991c3fa2bc9437a6d84cd1b2557b904adf57f6e7098943d65a01b8c57acc7","signature":"4fa89a082213215027fc85892fd3a42bf898e652eded33469c2c31a75cc7db12"},{"version":"9da149c4fd78a4ccab4e68a54c0cec7c3bbe48163c7e4cb550569bbca603ddd4","signature":"f50138c9b21bb7d4b52c5bcb99ff08cab9112b3fa3eb67ba583c1c033f5658fc"},{"version":"baa1b838cd0e200f302fa49ee523d8f74fdf7a16c6d14a121621aec564cc92a1","signature":"fe0fcdbbfc40a17d638651589c6fdae7c4d56ed10a0bf9e04dc47fa42b94ead4"},{"version":"ea99af3c9a22cee8ea6b5754cb9d29c7076588361543519e0a95675747e2c17b","signature":"c77e23e46e99ad3082a3636b3fc4955831a6c50cc6be267983ed20d86c42279c"},{"version":"9bd02ddf990e7c7a97c5b70d4357ed3d8ca8f7bc5061615ea7b6d36f2e469ab4","signature":"59955746d15769af662a431f4bea5f2887de185eaadcf0f8e0df9766d138cff8"},{"version":"4dd94a38316ef3061df0358c3174ae3b95728649913ae705fff8343908b394f9","signature":"cf8dbf5f0b2752bab0df676b52d9848117b17fb7306ea6e45145ab60205b4e25"},{"version":"276463327ef1cf4bacf602f9a03566fa793923397e6bd8b85c4ffb2da5554a02","signature":"88658d83f1351b1364c8b286846e79d8ab064ec332f8c693f8d133347cd2fb52"},{"version":"eb3f998132c1ee368d9196be6771f374f6b809b6693f1f6a75be7118cca56145","signature":"f6a21896af14802ff331fa38713f7c2649cc5e19bbe7c90707dadcc592236ad1"},{"version":"5a11dbf49dfc0bacac057085c4c818507b8613630a6080846a330caa09f40a1e","signature":"887a929e952df6c08de135d3c73360dd80e833b99706ce3aef0c8b64b26ce68b"},{"version":"a128283ceced70086ed7a99436e55575c7d385f95ec1937b86e2d7c725c6e532","signature":"bb7d350c5b0c764dc29222248163f61f8540997db099b636c993ce1ec6981018"},{"version":"954eafdd8e119ae7fd13c652d092ac62f95a3c450127f9bf2c4235b9a5550f9a","signature":"7515a48dc017014e10b59b93449b24053cfe2f6cbec7424292ff04fb29f14569"},{"version":"6d9e1b7a1fa967fb8505a5fa33073efb38aec5e7b75f2dc6383c9f84f3b5c0ba","impliedFormat":1},{"version":"0462956c97fcc2f9a0f7a498600008751aae2b004f8ab4da34af41eb2fb5317d","signature":"8c23d09073975011bf5b8adde26ee58c4c5c27b5c4cc656a32313963f3388846"},{"version":"763e7dd605f87e9ab676c1dbd4ae54ae2bf6f75de50bfe5625b951ecea65dd58","signature":"d546106877ee81adbcf30a6867700a253c87d52a5cacf673d034666e2837a8d0"},{"version":"40c9ed5a63b54bd64ea351b5d853e67c373d730a8243e2fad4757eab3ec5ab8f","signature":"26ab3593d88f84d8250fde332b61ef8e6c9331bf4da6e89698ed83e95c57f7ee"},{"version":"42858b5e9f40b8a0b2f860a6304d779419ecf0c8773f6cf498c882bcd9aae1fb","signature":"2f55fd6804783792ef44c4afb78fd8a5d6a2810a4c02007e53ded6f01e24b521"},{"version":"819a9152da954b548e16204dfbcd75208938e5e1a21464998d2d155c14f08f64","signature":"b6ea2388d7e17effc8c7a702bd5e736213f77468d04bda4d8871a07ff6b191a0"},{"version":"7d57f62963f7f76d3e4604f86fa9e7fd005e3e11bc81490b32193dd9b3f019e4","signature":"f12acaa6f04cc3698628891d95a523a4bf0c03d03fb103edc7e4929709f1baf9"},{"version":"d6f4b9a418add129da3898008b6036a68dab54ec3997b04dd0bee2534d59a2fc","signature":"2bde30c5f8e10d5820a401c68e63e2b81a23077352c01977a10cc10a15f266f9"},{"version":"e918c0d2318411d705b553170e3a914970fddab501547d7e3db4fa9886565c9e","signature":"383dfa02a89c8b915ec43f711d0c60864b9a9f9921468dc25c5e67891c451c6c"},{"version":"e2af71fd224c850cd29f8cf8165e9eaa251df3dcb5fdb19cb10e1a20119a6728","signature":"e7a405682b3c45cda0ebe90315e22f754732efeacd7010bc0f6d8fac3582eadb"},{"version":"1632954ce7df258a88f3bcf040fd8a47839bc8b3fa71b64ad14ecc4819388202","signature":"b99c3b4cb099944054ae1cb1917bd5fcce00dc0837b241497749f379cdc0e9d2"},{"version":"a67823a8d4a16991b3653dda2eb722a15efb2762dec299662a23322bf2394e43","signature":"419996c73365008124de6ed63224ae81323de45079174bcac6b0dbdfc0108b44"},{"version":"2076d2cf1cdaeaeb896e27ec77082c91b5e485d297935597e76c8fec1c08e39b","signature":"dc1097eff258d192d1b76e71f09eb7a5a8c9b4776c6e8ce8af855e41ce73a274"},{"version":"a84795af5152dc3fc5782eedd4031079b9753301847f158ca3c979e551a4ad34","signature":"363524bcf11b6a009efd4becfed098d9fb297e3ce41c225410cc1ac2534b2025"},{"version":"92dc4e8b3d0e8dea1f5abbe30adfd3910a7be441c12ed6fadc738adb59f9bb2c","signature":"322baceb1c9f45aedb9e5100ebbbedd07a164fc03ab9e98c462e32b41cbdc90a"},{"version":"384f588ecd80fa9da15753c86111d3563d56538d719f8023902703847dc198e2","signature":"fe0cea76d1c6a718447bcd594059ac0a4c7f60b9452227e9bd6af7d564519f45"},{"version":"673b4a9dc7a23138c3dcb75f1a77cccf8d2a11167df9587286d6140b2c60499c","signature":"2a19b3b4d3185bf1f4436a1b8e98727005b207da50322e24f3cde25162e2ad2b"},{"version":"4ccc411eab7ab26ee65e6796dc137a43eb6d3145e6c616cc3fba32bd3901c240","signature":"6ae54612df3cf99e70e10a844f31d4aa1629ae828bfe1e915701d5dc1311278b"},{"version":"c37155416601c041802206333c2537b309d8031770da2717d9ebbc0fbc0f1527","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d870a6ebc4af392ccd2875f99d1dd4c90db167636e4b267de2e1d3ff14a6104","signature":"d02c70b928a2b77ba435d387595ed3b27e4466e3115d7b71a79c36c592238798"},{"version":"2f79a63626adba271a461cbaa34c976c26be8e474095b1bc8a80ce6e4fc68536","signature":"019c10e3a4d1413779d87a055ffea70d5dfd127b4b65a51cb2e20fca9e8f1f66"},{"version":"ef68d70baf9635137535142e9df63e515a9b8bdcf9906d6edaaf0e93313ac3aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1d17705658519c7d57ce6c5cf95f0c894c2dd754ad1fcaeb1e2c4207d54e6e5","signature":"0bdd8b0d06cc0910bf6a36f2bdf87794634ab7014e178c7a7eb928437c6e5b76"},{"version":"688842a137a6cf51df9153af0452d1099fa559ac47504178827954a0957f4d1b","signature":"b4617ba82469c6492e4a6309e2cfad3c4a4b8b9feeeabe26293e7b3559362ba3"},{"version":"a8419536a4ca141f1b73aaa6a223d024f4d380c94cf9a014f96592a17c6690e8","signature":"5ff75228fff32475fdc9bd5c7d166c2cdf98f66300937ea4fb7e4ff39f9d0c8e"},{"version":"70f9e74061da378a88537a5e6745381f00c470abd9d6e42ce93c6a7ae63bcfc5","signature":"ab7896a0010701f3ac6de01e3802ee76c98f7e3f658445a3d854394d1d9b8590"},{"version":"c8a733a097280e5ea476cacf56518883a0fd509d36b0f04e0bcc5e5a81b68c0e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"697256913297b09bb0b83b40de56ad875baa198a0d1c03b8bd9a8f8f77c2e500","signature":"bce98e573080ea97bd3c360011d50db0affee86bc74443866897c0061708072b"},{"version":"3b455aef3a8f1084aec20cf655ea99e1f68620df4c3f6071e8eed404a1c379f7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cf8a587e0092b7b11c83927802ac7efb7c40acf07276fe9f1175c5cc3ffacff1","signature":"2c542a6fdafe6885c0be90ce444b56c9f6871bf121ac656b5662e8c79fc96561"},{"version":"cfd8b3c5fe8e27f93868763708b0a25221595b18dae6f5a28c515bce83cd9715","signature":"76964f1fd067c7ecd79d2dc18affd81fb2f0148dce268546b64bb6cb0cad859b"},{"version":"2c17c6e842123c5c921ba98cee5bd3886f3eeffd42eb3011819cf99cb5b02ebb","signature":"fad4e252103942053bbd84c183603d06e19da7332de7d295294f368a69af0752"},{"version":"47762a84ce21afc46f46c000e87fbf6b3035b5944da16ca8ac62de576d877fdb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fa8dbed00530fb4114906cd93f7fb55512c8eb9551d2f2e9796c69a4da4b594f","impliedFormat":1},{"version":"5a3158e3ef43807bcc86f1b8d387e35156859e577cf3ef44f71aa2c3822e5b6d","signature":"1449aeeaf41373c6ebb083751de3477f2dc63177873dd65e1af39288e0f8108e"},{"version":"bd6e83a9e6d18e2fa74d9dcd45f986329d347eff178ad9923a2ebbf3fcbfd97b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"937a3521bff8fc032a57777777feb260c9ab218d266ac3d7723f7de32a48a430","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3858cf95d68efd835700eb41b1fdf881906eaebd35b07596bbd5b7c1c6fec6c","signature":"1671fc31a114078bc9cb71989c1919c504f1af4e0690995b055181a1932bc74a"},{"version":"339fbca5cf5752f3fa77eeef5ec37c42010f1549655b3796eff5f4747e419488","signature":"62fe02bacba35050e65ee17fa4bab71e61914182c3dc9339cb6d40ae242efb41"},{"version":"357afcfd45b1bbdf4029dc5107fbf70fbfb519eb1f7cce5c9d9e5dfceed98efb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9d2066069488cecf420d111d0201193958022a2905ac6c66689f50ccecda6b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5452448d44362f60de4ad50c0c5eff76066ef5b1b9f2b4921e83fb50a0c568d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1f8647ce956bc178fa042840bd76b650e34d22b37292621c146c8fbcc4fdd5dd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5a7b42900c17657e4fecb9034c6bbd87a02fc402ee49415ae9cafdbe6f9d1dc","signature":"12ee1ad5a651c4484cbbdc6ea7d594fe1f8adc9684006988275bd671f510f581"},{"version":"e1f90140453b0fd52c46cc6ebfec2ebe754483c05e50dc74caae321d41e7a9ac","signature":"da215cd8311e3d53ac952d9a12e0fcebedf7d76b9f5692525046d7bb0ecb1cc5"},{"version":"5563052849dd3f93ba63a20568ac06acf6b8e15c0b57aee1bd452e7e4b1d5d95","signature":"91db33413af7e79f8f5639385fa3fa68c2595993f3f5bd6d7efaa4fe19dc94bd"},{"version":"bb2cef14d223750bf32f070eea09b2b2e176e10811d5c34f7a824628bad9dbc6","signature":"cc4068562a009b8285b75a2c53ea7b7323cc91785c59635e98b38256e80a2514"},{"version":"c5e286949fb1b24d3395196df616ec5f9090c2569534e48d1aa86e14308f6f2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"908b7b3f2c71140accb3333fa49485ce1ad10ac4e14faab10ed51c0c28c68782","signature":"d19e4f9294c2efb124088d0e5dc71b2faceaed9cb614ef974ab0c3b925374891"},{"version":"637c70ab565be71168064142fdc7fde5a58ab95425066d3c8a6c3c592ca7167d","signature":"5baac7ee5e50c4c52bf4905d5cf4f735c939053555b99a56d1b743630788f665"},{"version":"2178789bd22566bcaa973006fa541e2c70d5698b5c099831828c9a1ec141802d","signature":"b905f364397e04bc6a90718495a5af33bf9720262dfdf619a090e7164d4f5408"},{"version":"6fa0ea6916329d3aa5c6056e13512e1757edecbce26dae1e8e5a3334e81fbf93","signature":"98d26fed15c1969891bc73c9dedc7278cdbd15f3afe3e34efe01c27dde514ba5"},{"version":"dae66bf6a17992ce4aaa4a16b8d8c590e84c396a341cb70ddf61ac2fe710e089","signature":"173c629dcaca1da42db9c0a508d079657fcc0cc56db24103f8a8171294902ff1"},{"version":"e7a672c4cf7f2314673b2fded201b122b6b4eda779709e2cb235531e8fac004f","signature":"db1016666977bab29ab1854fb90c9ed76f0632bdf412c73c6fa81412a02bc5b6"},{"version":"8bb437a4f36353bd4f61c7d432a87e38fc13f7b17e2d56b670d8d9b52bb25c29","signature":"8f04114d05b5db969453536c2b5f0b92cb28745a7a03fd47f425146e4b9ad8c9"},{"version":"ad5ea69c890012a5b61d4cad41a2d1c2bf581a023eb58290c5ea86554184bae3","signature":"d73e7d9f551a968dcbd471ca03440ca263efb053defa3a163b94c429ac47729c"},{"version":"7b94368c5875f22f86e56257f96884f67d2b68b2f967b06f52e5b735fd7e5727","signature":"2367a890be9d6752275d2ec6b9afd812c0b856d943b32e51ec662d6aaf6968be"},"6958e241f880588015372a690454d0f7c0727b78e0a9882f493e2ac0fada857e",{"version":"fceca4d896e6fd11de25ba760ff482c087c3a2150da1d841b8092bf8e1dd812c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e685858913eebc48e218f3546127d9e35f9e61eaf110891876c589096b31418a","signature":"423a9884352dad4696190f73f7dcc7316a8b1360ab70811a6ba9642929940718"},{"version":"7deb2948f4295f5751846f0e22361a43fa92480461be9c0b118cb388172308ae","signature":"6011e7e17c36babf1dd074634babde201615d25815d9140579d346b903bc9dda"},{"version":"7885c9bbd5a3cb3082f0a05df1f9c448d1c39e6c1302e935f9f986bc479ccb27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2d4dfa9bb5bbafa31b4423a78c2df02ccb51ad3f4abe7dcbbfaeb8dcf2cb82f","signature":"90b39c231c33d05240cbaabcfc21d94f68e05b5d9d2e972b644363be2133bebe"},{"version":"94adbb305113a8e6572989713200d1eba425e7a01443f1d02f4bd9a66f7f4fa3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3f8860ee39a7c1ca7824f29d948eb3f2160caa4cf4b1df9380a9b9b0c1ea184f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"01637488bd4591490ea3957f7e0d5a0744299e13944168b0b29015e57beab4f7","signature":"9d9ba57804b618dfd2e432502395172f7ccde0e2543ba0633ae9405c50451d7a"},{"version":"10aa66ae348d5ce62f6447d5af00fe40adc1f961e8fd6fe4f331ee6156c547fd","signature":"22039e2144d09a04262d19b5f0e4237ee40bec8322b5c9574403a9103af52044"},{"version":"cbcd7e3639eae69860983466b671c160570ff8bef3295eb9d9cf3a918f7c3924","signature":"22039e2144d09a04262d19b5f0e4237ee40bec8322b5c9574403a9103af52044"},{"version":"2b7bd4c530f8df99a7c513289d15cc3d919182a3e47a509f7dd66f7c0c618c64","signature":"72347dfb5a68565183de9758ca357bb879acff2d8dd025002d023281dbc9b755"},{"version":"700a699bc316498b27b98820c837965a737debebb4fee5d0a027e95d3c4a1925","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a569d1174a24e8305c08d91a99e9f7f03610b40a3639da982b923f0fa380cc4","signature":"a693156ef4795aa6c9cdf5e6e88fd5a8aa3db1e770643c679f5f0bc652b6e384"},{"version":"8292f04350336900ed9f70ce80be9ff51b36602e17de450e43185054c9c9bc32","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b61da04f747568084ac75ba893c009197a7a0bb511ce6e8ea11ec3727b1e0bff","signature":"659c6cddd4e661edcbf460b40c7b690f346714057fd0faf27d1400d95cb6a398"},{"version":"5e92985539c56d5b665b392fd3883c103e0a83b63a79955d940547f494a87f27","signature":"d273813f1a71341c5f482788561acb719f12c65fcafb4f36423a6d409856d472"},{"version":"1e36aa6fd246d7240a3598e917647e1d2ca0380a1b7bb3b8e3945cb26941b031","signature":"9e2bb88f173d3209e25d8856088cd88006b416949bf633f766578ae5b18f8488"},{"version":"205f7ac530c6e5712a640fe3b0dd9f29296ace25043f7179ec1adb56882a1c37","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5e9142cb5f88305d5b5db601df9fe78244144630b03316f24b123c2bdaec5cd8","signature":"72a4b4bcd25bb33acac0c8d83f0d4d198714a06e03c49046690f380b9736998f"},{"version":"735334eab2d97fbdb987695b8ce10bb987dc318c3dd3f76f65a2f955bc4dce45","signature":"eea48c3b3a4a380ccfec9ac95ea1f6535cc57b17efa2d13009895942af52555c"},{"version":"b6fa86562861ef430157dbc9d6913461f6bb416c58b7052efa28a7fd503a2e59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34817c134a9a64cb3564c424f056a13554d6c40d31af05be7ea6b28cd9d0ac53","signature":"cb7b15b1e17883bae1ff4a7a2edc4e33d311a2addd22d3799520aca9c35809f8"},{"version":"a23c66cb088ab278c1b732ff0c39c3f072940848f593eb78807712292c9d4082","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90453ef456c9284945d132c4d1c29753bcc06b0b7b1df7910768f748d4630160","signature":"d4d3b854dee0def611af8377422b5caef70f3b8c2c2d10ee3bb9ffb97d51cd45"},{"version":"404d9825e0fe3cc10db20060d068fd4f33c85c245ec9d2f99a20d05b02291b20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3450fcaa834dde1d97160d430e46beddb77253f999ca2a9730a518cf8ff9b41","signature":"6e557cc6a5e01a72e3f8159ca0c3c0b55d5d85613540b6fab72f9c70458b6c4b"},{"version":"e04940a8b56da3de47e0cc13fc8343b6d61620cc5d888100e2bdf51826f1a2ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d740794e45fbbeb9fcf59f3255e9e6bbee5abf88c3e7736089e534fcb2c55689","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"354afe485d131f817329e133ea768376707f9e4041d68975ba6f8b6eb2deca05","signature":"6fa430bbcceaa6953e336c4592420298d31fe66327f7ca06e6763ec70c20240e"},{"version":"0d0fb8169becb3c35ffb1069d105e59d36e1152bfac10d47d122129c8b6ac89a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7a97cc4473384c86ecdd6b6c2d092db47c5420ade0eac940468dbfee5c21cf9e","signature":"f84c23548103c81ed53b1364e06c7c00104b7dee0573f4f7e2e74e8788222aa3"},{"version":"a08516f71cc818d1a07dc95126ecc36862a01bcb5c8ba86bf347a159c22029f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b5d29855d82e8158a99ed976da509012e99a5fe1bc877009aff91627250cecee","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc4ffeecc189d198af9ce492abed824b47bff7e7e6f8ec739a0eccc849836e4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"598e1063a09bc7bbf1bc527cd19769aadf213b151d89921fafd9eb6c74121fc7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9704b377c8700004a19f328ee8a4f7ec61fa305e24545b5621e562410a63e2ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9b05dbef22d051726098dcbc6490886790bf7bdb93aa9f8a46403fabd59128cd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3db9bee2b4afd4032f6040039fed1022b127166da7f0143b4a8b69a8a85d264","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b290d4bb3bf8f9c797f36ed8543b50571df566dbfee6f4234c3d0e63d837011d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d0006e2c2a094ea0fabc4465b2cab0d7e8f5e785b3dda2961c2242257908b6e","signature":"a82c92852eb3872216a45757430fb88588440285e6f17c1bb864abe9f209fcd9"},{"version":"2f1a45e754761c4c17af1ecbd707a35ef9421ddb2daf244d1237aa929f919ba1","signature":"a76cde90a90b5582bffaa8aecdbdef0ee7d82667c57cad2c076404a3bcb741b8"},{"version":"5ebf1bcfa735477bf05c2a72f05efa171db37d28e39a690cc57d28447e09b070","signature":"ff96e4d1e720fdea29de66b9f495391d4c8c6b20fa4db88964df688d5a8538d4"},{"version":"73a0ee6395819b063df4b148211985f2e1442945c1a057204cf4cf6281760dc3","affectsGlobalScope":true,"impliedFormat":1},{"version":"d05d8c67116dceafc62e691c47ac89f8f10cf7313cd1b2fb4fe801c2bf1bb1a7","impliedFormat":1},{"version":"3c5bb5207df7095882400323d692957e90ec17323ccff5fd5f29a1ecf3b165d0","impliedFormat":1},{"version":"ed79d26639d1d98ab19d6f419180e5abe2f7fc6c194877d809282813888c98b5","signature":"46713144a8e07e24962b43c73b40a4f4b16e696eb52b8b519876fcd1f5e6eaf3"},{"version":"02c59c05069a1174a93fc2886fcd4ecfc2686711c1432511940f93d42f5a8dcb","signature":"3e2364dba15210b59a74593c721b4946e89b6cabf1c4852738003ee79509f4a7"},{"version":"26a7fc2c9efa90591c196a780ebb5940a3cfd7a74245698b2c0e648986755e76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef20d9124a12e824c20d04362452ecbbdc6c9f3de2c3c4b231222870b9586e27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"02ae185acd25001f4af91e9f275661d7d284ca994374867cc564ddf22f8a6082","signature":"ffdc10811704d4d944dd41a35e45ce568ee973c9ac0d5c0ec71fe098829aab6e"},{"version":"535fb697e71bce5739129ef269f852ba83a2eea358ce8ca090f4b1cc905af9bb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27cefa9a8df763b7c4e3abc76cb9867d1ddad908ac8c8d1e2bb32c3838616d4a","signature":"ea71b9399fcc1d3c46ec554ae15f397c40bf146ca2d9a58374cfb7116c343ab1"},{"version":"d1520fdce7489a3ad57359fab13c79ddc0a2a6d743940358a4dd3ad8d959fb38","signature":"45b074b67e77cbd4509dbcb1d78e40925cca1a0e67ff79fed1021bc48c262eda"},{"version":"c65bec5967ebb52be456a4fb70ac4cd92ffd671aaae4661cde2062fe3117fb7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3cfde44f8089768ebb08098c96d01ca260b88bccf238d55eee93f1c620ff5a5","impliedFormat":1},{"version":"293eadad9dead44c6fd1db6de552663c33f215c55a1bfa2802a1bceed88ff0ec","impliedFormat":1},{"version":"08b2fae7b0f553ad9f79faec864b179fc58bc172e295a70943e8585dd85f600c","impliedFormat":1},{"version":"f12edf1672a94c578eca32216839604f1e1c16b40a1896198deabf99c882b340","impliedFormat":1},{"version":"e3498cf5e428e6c6b9e97bd88736f26d6cf147dedbfa5a8ad3ed8e05e059af8a","impliedFormat":1},{"version":"dba3f34531fd9b1b6e072928b6f885aa4d28dd6789cbd0e93563d43f4b62da53","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"e4b03ddcf8563b1c0aee782a185286ed85a255ce8a30df8453aade2188bbc904","impliedFormat":1},{"version":"2329d90062487e1eaca87b5e06abcbbeeecf80a82f65f949fd332cfcf824b87b","impliedFormat":1},{"version":"25b3f581e12ede11e5739f57a86e8668fbc0124f6649506def306cad2c59d262","impliedFormat":1},{"version":"4fdb529707247a1a917a4626bfb6a293d52cd8ee57ccf03830ec91d39d606d6d","impliedFormat":1},{"version":"a9ebb67d6bbead6044b43714b50dcb77b8f7541ffe803046fdec1714c1eba206","impliedFormat":1},{"version":"833e92c058d033cde3f29a6c7603f517001d1ddd8020bc94d2067a3bc69b2a8e","impliedFormat":1},{"version":"8e6427dd1a4321b0857499739c641b98657ea6dc7cc9a02c9b2c25a845c3c8e6","impliedFormat":1},{"version":"58da08d1fe876c79c47dcf88be37c5c3fab55d97b34c8c09a666599a2191208d","impliedFormat":1},{"version":"e770447d49d5c7ee25f80ccfff0f95003e08bf1147d039f0e8320d95d882c76b","signature":"399eb8b682bd93241cc96cb483306f8634ba94bc17ddb123e9106088240e9c7c"},{"version":"15ba1669f8cb8433a7a7b40422f81fed4f7e037e3cd4ca65b7b4af0434a43560","signature":"4f83f97fe204009c8bbad58d06e956970062930bd694b7ecd88d13a6f85f7e3a"},{"version":"a18970969188e47a48af09738dde83579f9c85bfd731675b671c1f32c5bdc134","signature":"f6c3f2c52494a1c44f58bc28dc1f8f89c7e3b0d005a5c3bb8789f82131996dd5"},{"version":"68ec8a37a3f7ce830a6be8e0ed448f8907f638e02a22a12a0f76a900d9f7b258","signature":"37f7acbfb476967d35f33541ea813afd46bee4432026b3d709b41a0fa1b0168c"},{"version":"e3f54d1b036952cd9973fd6a6e68b5de9ae9620cbdfb9a421c5362175891b7cc","signature":"d5368ef391f2681ad969a5bb0d8d858701641ac38a7de983786492d2e5698ba9"},{"version":"d3f39e4c09a88118ba35eefddfa580674f5b89df79c1477298d4c4de0bc8ec35","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a2ffbcac73b1af75bb70a1fc540f059a227257d9746279d67af5698e9d9c0a77","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e70993be79de2ffc2132f91126903db8573e68b0f5318ec48eec97a5e09c5f8c","signature":"c4c000f5db2334ea4e2bc0b9bc437d27c292ba078ea53202378b878846840865"},"9eed204f26aed45ba513a001aaa78dffd4bf0194ed42fb59fa4a5b48dc382767",{"version":"bbed8132ccf8ed24e09b7a0c103afe746ec74c3f6d497676ce9a2b09a8e0e4ad","signature":"e9805c8a045ade45cf5dda8406be734ed77bce51fe25e6a431345e403964f502"},{"version":"1b046683cc56fca31919c8cfc9a7b47796d986b2df18c1e55615f7f67a464c0a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"954e64b65c8632c8e6c602f86ddb7a855b541f719153c52586da47df81740592","signature":"5747318e625d94d50968119db96e4e9b57f386c0fce3b015e26a5e06819ded72"},{"version":"8e67d08427faa2cd614ffde8279aca632928a75610fab7f0e80eea0481c3ffa0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4dd230e5ffb901e4d715a7507fb671f3510cbcb3781701177e09efce8cf30c6f","signature":"7a3b7f911a6906b2fd8d38f7347bc751ff290914c35f2998438f2985dcea418b"},{"version":"d79917970e2012fea644dd1c3d00e7499579d4adfdd3628bc4d4153c2fa38d2e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eaf4a58ef586c168ad73bf0bf2e4fc7b50b0207046cb03e70f064e23cc7410a2","signature":"dab2cd6f392f32d0e94793582857c06a2d1fc79ebafc631e2b80e4b0c40778c9"},{"version":"08e69d30c063db86ea2221adf2282b1c118723cec16131cbd40024f3f43e5a90","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80ccde47e35b2546400135ffc69d55feb65a4a473964a40cdeda39bcfa10aef9","signature":"2cc35ee4dd1c4f9d97475451cc25f443a692f68f9bc47fb0044f009e356da599"},{"version":"a2d8e5740b1d7e274651ad4e68fd99942d7b33d67adce2d3ff8b976d12327840","signature":"d14e729f535d0e6d801090b439ff6f73f8ae7d713de7468a36d5989f0f10f19a"},{"version":"1d57813ffd927563821c58c16a5a7c35d350415b2b8de5978b370c78b8a750ff","signature":"d5d64072f36683f1af5cdbc66e7ac58d839b6b2d99cee1b0e96df9f4413640a2"},{"version":"70fe6d07a4bad7a73b493a4bfbe2c5b501167449f0e95e3a896261e08d647b67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2a312ff9bf104a2617f538482d33f951d2de095a79bfe2f641f52ddf558a3c5f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7d2792a15bf4bcd948e330c3cb747a075d137db57ac53adc6900f69009dd8978","signature":"fb6fdfe7ee4e1c16d6bc8b3c8da0d22ebd365981b6c4dfe881b391328d68f220"},{"version":"75e64a7fcef4db0c9ff13acc31c53cce109194012351733ce9833347e0a8e518","signature":"a97e6b4712135857efbdd73004c551d3a71d65d6b8a9d8f661f608a47b607cf3"},{"version":"a24154a3954030448c58433c23ca4f6d78e763a3af035de3d9633cc9158d7038","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"72fc4be3f73ed954fa04a52ebc5975c56b5f13c4265392191c95028ec27daab5","signature":"13a8b4fdf45f95814460c5001fc04194f85ca7055d460a9f852eed3fbd5c2293"},{"version":"d3a3a8fa4cac4860d3fabd83dbbe072bd0db08b6dfc5447fbc3f65a480bbb896","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"773df341514640879d77b0b24b636e6a8ccae2e88bbb09cee7383274046eab2e","signature":"3f3e3ab94baceade05836e0805fd32550fc1cad12d3d31a2fcae6d56882ac2f8"},{"version":"2d96663076cc7fea06c11a0165be63c11b533672c6d02ef361bd86f8394ecdb2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"95f053ad6f9e8f22fb9a0309e14a768302ff5f8072b9bc24b8decdcbdaaad0ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc4cc5cacfa347d15035093ecc8a2c650968fd9208de260c8c141749d1797d23","signature":"8efda6ec7129eb4762df1d2b2a593fe59c69f1a2d5696d6d7bddeff50c24b17d"},{"version":"de7f6eb89010bc7d22b76bfd8d01ebdf803df6bdf7e7b7528d2705f74c401e58","signature":"524d6c27b0e7b81e021da931ddfc29e60f33e2573ff117ed95e8cbeb32f5c8ad"},{"version":"745615f591324c1ce4fd8a905b5af838474e781548807dff21154e64b51e945d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1a2e508046cbc9709255c938bf9935cbffa6cfe006cb5bb7f36b9f4c5a3a2db","signature":"ecaff6497b5a358a301ee7363dfd9c78325e9cb23d95bcc873322faedca7d3a7"},{"version":"d09eaa9c4d651a351d0ed84a88a22b35bd41f307ff7aa0fc356a2b7ac41ccf25","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26418c7b2f5a9860191966b63f2761e14f52fcf8b0cd040bb94b9623f348b9f1","signature":"01762a4e0d1830ca6bddf2667adc23596b265be66f3b25b96193103aabe080ab"},{"version":"c2882b00edded3f8e7bdc5f2169866bd6a1c0402b77684c4eba188ae9801e6a5","signature":"745546f965215f793614c1212548989fa75b322859fe83033f26979cd44e5722"},{"version":"8d3496abc1d78cdf10e4e6066233bdef27a1a00c40796f8cf8a5a34ead5e3fa5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d187fa89b22ca44e8ba47b4eedbb5fbfb1efeae4d14f6c8756b751067e4164ea","signature":"ce13f793f9bda7ea5885e373ee25d9140369d5eab45bdea4eb8526ea020257ff"},{"version":"268e798c4155b57314f6fb0ac3ff224e049c5fd8dad1619771328f77cf66e958","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"998e905a8579d49c9ff4cd1dfad86ed6608ac548b240d2ddbee896c013e6dd34","signature":"3e4b13cf490d9a92245cbc3e5477dc486352b38942d390fbafe48c4d9d226d1d"},{"version":"cb96c5f2b7a4c06fdfeafd2b2bb52e5026215b5f9c81264a502b58372d393c8e","signature":"36eb3a3f2bb64aba7182a39a6360214c084660224ab8c23e3a9e293d79065d37"},{"version":"f247ebbdf2915957f47462152c65fef96da9273631b739c263009db4efc13b4d","signature":"7c4575057d5b4dc088c1e7f338e1075351c0cff2877360a00f5102baae76dd6d"},{"version":"ca140e99f367b2a1a666b6f61ebb703d059f3b81d59a710058b6606dec01d72f","signature":"31c64d4bfa7de3f3b9ae69741ad29192023a49595897e764d62fce0eb931023f"},{"version":"265c9ae2b7a62781e57de439be00ccb1b8693156cfb98a0618ba6c5c54596e42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34330c5f52442c69dd7c50d7a95912d87b85cc01be135026a5d7ac060b184464","signature":"720e771373458011bd56c0c6bbeea34302eea42ccf08c8a6b5840a338e7e93b9"},{"version":"e7f0547a22cdcb3e5d9b0fd91191cc2dba8f75a2694eeb4d45a9ddf2a8352960","signature":"c1f5f74ae95ba44d64781ed79486fe7192478040d82d787876f44bc7e77418b2"},{"version":"56208c500dcb5f42be7e18e8cb578f257a1a89b94b3280c506818fed06391805","impliedFormat":1},{"version":"0c94c2e497e1b9bcfda66aea239d5d36cd980d12a6d9d59e66f4be1fa3da5d5a","impliedFormat":1},{"version":"eb9271b3c585ea9dc7b19b906a921bf93f30f22330408ffec6df6a22057f3296","impliedFormat":1},{"version":"aa4a927d0c7239dff845a64e676c71aeed2bbda89a7fb486baab22eb7688ba1d","impliedFormat":1},{"version":"340a990742a00862049b378aaa482b5bb8323d443c799dded51ce711f4f8eb51","impliedFormat":1},{"version":"89eeeebbc612a079c6e7ebe0bde08e06fbc46cfeaebf6157ea3051ed55967b10","impliedFormat":1},{"version":"4c72f66622e266b542fb097f4d1fe88eb858b88b98414a13ef3dd901109e03a1","impliedFormat":1},{"version":"15d8dcd70d6cc6c75476a75ea83c53df1115bdd551c73ef2168a9b4a4bd55a51","impliedFormat":1},{"version":"2acad3ae616a9fb5a8c3d4d7bb5edb11d1d0102372ee939e7fc64359fec4046e","impliedFormat":1},{"version":"c812eabb7d2e13c8e72e216208448f92341a4094dd107cbb0bdb2cb23d1a83e7","impliedFormat":1},{"version":"f734b58ea162765ff4d4a36f671ee06da898921e985a2064510f4925ec1ed062","affectsGlobalScope":true,"impliedFormat":1},{"version":"9619b4a3db123eee6912ce9cbeae535739a1b1736dbbc224a697a2a98fee560c","affectsGlobalScope":true,"impliedFormat":1},{"version":"9c5c84c449a3d74e417343410ba9f1bd8bfeb32abd16945a1b3d0592ded31bc8","impliedFormat":1},{"version":"a7f09d2aaf994dbfd872eda4f2411d619217b04dbe0916202304e7a3d4b0f5f8","impliedFormat":1},{"version":"86ac756569f83cf0571646c916b546634652e92a775e964304912ecafa81dc42","impliedFormat":99},{"version":"a7f23fecdccf1504dae27c359db676d0a1fbaaeb400b55959078924e4c3a4992","impliedFormat":1},{"version":"bee66a62aa1da254412bb2c3c8c1a0dd12efea0722d35cc6ea7b5fdaa6778fd1","impliedFormat":1},{"version":"05d80364872e31465f8a1eaf2697e4fc418f78aa336f4cea68620a23f1379f6f","impliedFormat":1},{"version":"7345ba3b9eb2182d8cdc4c961b62847c3c9918985179ddefd5ca58a80d8b9e6a","impliedFormat":1},{"version":"81c4a0e6de3d5674ec3a721e04b3eb3244180bda86a22c4185ecac0e3f051cd8","impliedFormat":1},{"version":"39975a01d837394bcac2559639e88ecdc4cfd22433327b46ea6f78eb2c584813","impliedFormat":1},{"version":"7261cabedede09ebfd50e135af40be34f76fb9dbc617e129eaec21b00161ae86","impliedFormat":1},{"version":"ea554794a0d4136c5c6ea8f59ae894c3c0848b17848468a63ed5d3a307e148ae","impliedFormat":1},{"version":"2c378d9368abcd2eba8c29b294d40909845f68557bc0b38117e4f04fc56e5f9c","impliedFormat":1},{"version":"9b048390bcffe88c023a4cd742a720b41d4cd7df83bc9270e6f2339bf38de278","affectsGlobalScope":true,"impliedFormat":1},{"version":"c60b14c297cc569c648ddaea70bc1540903b7f4da416edd46687e88a543515a1","impliedFormat":1},{"version":"efcdea26e9115d5c05b3f4c5827fe3b32b4fef1b59dbd67f529c6cb685c7d9c4","impliedFormat":1},{"version":"bb0c361fd2b4bdabbf1307f1a61fd14c953f2692fa642391f93276f2df41de50","impliedFormat":1},{"version":"90588fb5ef85f4a8a4234e8062eb97bd3c8114dfb86a0c67f62685969222da8b","impliedFormat":1},{"version":"6ce50ada4bc9d2ad69927dce35cead36da337a618de0a2daaaeeafe38c692597","impliedFormat":1},{"version":"5fbc333346d28f290d42ac81cf16e454fd3947c6e524384dfd3ce59d4ac3af04","impliedFormat":1},{"version":"072163fdea42ece03bd323b907f5d6acf575a34a9dac4620e517e4378d773d0d","impliedFormat":1},{"version":"df6251bd4b5fad52759bfe96e8ab8f2ce625d0b6739b825209b263729a9c321e","impliedFormat":1},{"version":"846068dbe466864be6e2cae9993a4e3ac492a5cb05a36d5ce36e98690fde41f4","impliedFormat":1},{"version":"94c8c60f751015c8f38923e0d1ae32dd4780b572660123fa087b0cf9884a68a8","impliedFormat":1},{"version":"db8747c785df161ef65237bac36a7716168e5ebf18976ab16fd2fff69cf9c6ce","impliedFormat":1},{"version":"3085abdf921a6d225ad037c89eb2ba26a4c3b2c262f842dd3061949d1969b784","impliedFormat":1},{"version":"8e8f7b36675be31c4e9538529c30a552538c42ff866ba59fe70f23ba18479c5a","impliedFormat":1},{"version":"1fe8b45c1564eca8b1bd27d427d193ea8c1a5d64f7144a5a64665d5d0f27a9e4","impliedFormat":1},{"version":"a03c6f93651e458531f223d52eac1a12f2aee8adc2cbc4b4154a3fe515984e5c","impliedFormat":1},{"version":"8d05dbd747569cb1b0cc2ec1018a3378c47d803de0e7d34f7e12909ff48bb437","impliedFormat":1},{"version":"1afb31819f4b7d04f4089d575acd30854a4cc614baea960066f7cc5755e9efcd","impliedFormat":1},{"version":"35cc30df63b9fa7c9d3637ef315eb5f21f5b0dc0f982c736cad20d39e29b579c","impliedFormat":1},{"version":"c5b47653a15ec7c0bde956e77e5ca103ddc180d40eb4b311e4a024ef7c668fb0","impliedFormat":1},{"version":"0528a80462b04f2f2ad8bee604fe9db235db6a359d1208f370a236e23fc0b1e0","impliedFormat":1},{"version":"94153ca0b430f575f45a5e07d66771dc5ab331af7791691855ba3499958c4e49","impliedFormat":1},{"version":"dd361fe00d3033451e4a43c9eaeafcd1b9b6777adfbc8b8f91d63ea56818c31c","impliedFormat":1},{"version":"b86720947f763bbb869c2b183f8e58bca9fa089ed8f9c5a1574b2bea18cfbc02","impliedFormat":1},{"version":"fb7e20b94d23d989fa7c7d20fccebef31c1ef2d3d9ca179cadba6516e4e918ad","impliedFormat":1},{"version":"8326f735a1f0d2b4ad20539cda4e0d2e7c5fc0b534e3c0d503d5ed20a5711009","impliedFormat":1},{"version":"8d720cd4ee809af1d81f4ce88f02168568d5fded574d89875afd8fe7afd9549e","impliedFormat":1},{"version":"df87c2628c5567fd71dc0b765c845b0cbfef61e7c2e56961ac527bfb615ea639","impliedFormat":1},{"version":"659a83f1dd901de4198c9c2aa70e4a46a9bd0c41ce8a42ee26f2dbff5e86b1f3","impliedFormat":1},{"version":"1db5c2491eebd894eb9be03408601cddfe1b08357d021aeb86c3fb6c329a7843","impliedFormat":1},{"version":"224f85b48786de61fb0b018fbea89620ebec6289179daa78ed33c0f83014fc75","impliedFormat":1},{"version":"05fbfcb5c5c247a8b8a1d97dd8557c78ead2fff524f0b6380b4ac9d3e35249fb","impliedFormat":1},{"version":"322f70408b4e1f550ecc411869707764d8b28da3608e4422587630b366daf9de","impliedFormat":1},{"version":"c4ef9e9e0fcb14b52c97ce847fb26a446b7d668d9db98a7de915a22c46f44c37","impliedFormat":1},{"version":"0e447b14e81b5b3e5d83cbea58b734850f78fb883f810e46d3dedba1a5124658","impliedFormat":1},{"version":"b16a6680ef4108fb2982b1d47e7ce36a8b2c382cf76b3e1b500de70f0a62fdff","impliedFormat":1},{"version":"929939785efdef0b6781b7d3a7098238ea3af41be010f18d6627fd061b6c9edf","impliedFormat":1},{"version":"fca68ac3b92725dbf3dac3f9fbc80775b66d2a9c642e75595a4a11a2095b3c9a","impliedFormat":1},{"version":"245d13141d7f9ec6edd36b14844b247e0680950c1c3289774d431cbbd47e714e","impliedFormat":1},{"version":"4326dc453ff5bf36ad778e93b7021cdd9abcfc4efe75a5c04032324f404af558","impliedFormat":1},{"version":"27b47fbd2f2d0d3cd44b8c7231c800f8528949cc56f421093e2b829d6976f173","impliedFormat":1},{"version":"d5426c0e36296daf07cf2f38227907469c33a53473d9c2721d21dc515c5724df","impliedFormat":1},{"version":"cc03a3e284393b02fdb646931e8576f6dbe839a249d172eb3397adec80559450","impliedFormat":1},{"version":"3d94a259051acf8acd2108cee57ad58fee7f7b278de76a7a5746f0656eecbff6","impliedFormat":1},{"version":"fc745bebefc96e2a518a2d559af6850626cada22a75f794fd40a17aae11e2d54","impliedFormat":1},{"version":"199d42a358b3b73312d499dd04d9f855bf8ad492452765de4ef80b8cb6871cd3","impliedFormat":1},{"version":"eafa048ffaf72cdb64fa1d0dae49aa91280a7bb94e0b034883ae48cec27a04d7","impliedFormat":1},{"version":"593bcf66433eff881c9abb75d2e55a7403c57905aa61d818a616bb3c7f076b49","impliedFormat":1},{"version":"3f3526aea8d29f0c53f8fb99201c770c87c357b5e87349aca8494bfd0c145c26","impliedFormat":1},{"version":"6ee92d844e5a1c0eb562d110676a3a17f00d2cd2ea2aaaff0a98d7881b9a4041","impliedFormat":1},{"version":"b9dc36d1f7c5c2350feafb55c090127104e59b7d2a20729b286dab00d70e283d","impliedFormat":1},{"version":"45d3f1d53fa99783a5e3c29debb065d6060d0db650a6a1055308a8619bd6b263","impliedFormat":1},{"version":"a14febaf38fd75a88620a0808732cf9841afc403da2dc3de7a6fc9a49d36bdbc","impliedFormat":1},{"version":"6052522a593f094cfee0e99c76312a229cf2d49ac2e75095af83813ec9f4b109","impliedFormat":1},{"version":"a0ceb6ce93981581494bae078b971b17e36b67502a36a056966940377517091d","impliedFormat":1},{"version":"a63ce903dd08c662702e33700a3d28ca66ed21ac0591e1dbf4a0b309ae80e690","impliedFormat":1},{"version":"2b63d2725550866e0f2b56b2394ce001ebf1145cb4b04dc9daa29d73867b878c","impliedFormat":1},{"version":"e885933b92f26fa3204403999eddc61651cd3109faf8bffa4f6b6e558b0ab2fa","impliedFormat":1},{"version":"22338cc18afb909a95a6c55417f1a67db99badecbac1710a963f0bf63c952124","impliedFormat":1},{"version":"e61b31fd5fd627c73da6041d201c0bbd721170288381f09055cad4fcb2ad327b","impliedFormat":1},{"version":"6e2d2b63c278fd1c8dd54da2328622c964f50afa62978ed1a73ccd85e99a4fc7","impliedFormat":1},{"version":"e151e41c82004cf09b7ea863f591348c9035e0f7a69d4189cbac89cc9611b89d","impliedFormat":1},{"version":"dedf4655c327e9c5294a63d75764946308700825e8d8c1d4318a10602581cd6c","impliedFormat":1},{"version":"b83ffe71adbac91c5596133251e5ec0c9e6664017ee5b776841effe93de8f466","impliedFormat":1},{"version":"61ecf051972c69e7c992bab9cf74c511ecba51b273c4e1590574d97a542bd4ea","impliedFormat":1},{"version":"068f5afbae92a20a5fcd9cfce76f7b90de2c59a952396b5da225b61f95a1d60a","impliedFormat":1},{"version":"18d97e6b17d1196d88d4deb0e37d8edb7fbdd102ae8a5681f03e15030b6b2fd4","impliedFormat":1},{"version":"d7ded5d2060ac6a4404e6001a46d5a704e3f325f95e2cb0dc055ea05404c9cf6","impliedFormat":1},{"version":"99c88ea4f93e883d10c04961dbf37c403c4f3c8444948b86effec0bf52176d0e","impliedFormat":1},{"version":"e88f3729fcc3d38d2a1b3cdcbd773d13d72ea3bdf4d0c0c784818e3bfbe7998d","impliedFormat":1},{"version":"f25b1264b694a647593b0a9a044a267098aaf249d646981a7f0503b8bb185352","impliedFormat":1},{"version":"964d0862660f8e46675c83793f42ab2af336f3d6106dee966a4053d5dc433063","impliedFormat":1},{"version":"292ad4203c181f33beb9eb8fe7c6aaae29f62163793278a7ffc2fcc0d0dbed19","impliedFormat":1},{"version":"aa8e5ac3f73eede931d5da74ef1797c174b00854ac701ead5c4a7d6ce4a49029","impliedFormat":1},{"version":"f1a4ca3688d951daa2d7740da5a0827fa34d4a7709eed7b8225215986ee87108","impliedFormat":1},{"version":"08e159b5ef9d14bdd329457c5cbe181e84f13c4ff2546a24b9eb9129b0c71c46","impliedFormat":1},{"version":"f8453a3fe0fe49ab718357120bec2b8205e15eb91ff62eada60a4780458fa91e","impliedFormat":1},{"version":"06f186bb9a6408ef8563dbf17d53cbe23e68422518b49b96afac732844ddbaa1","impliedFormat":1},{"version":"525f9c06245b5b43b1237cfd757396fd7fd8090e5d6a4ded758c7ce17a04bf42","impliedFormat":1},{"version":"e46b752c48b3aec77516d23b5cbc0b85df78c740c058a822b43a32c958e468f0","impliedFormat":1},{"version":"f693b1fce39951823f128590c6c837b70f844b6d3746ef778b7fae7f1340338a","impliedFormat":1},{"version":"bc264419318f0b174b5dabdd465e1eddb82f872e899b6c696c67217b346e958c","impliedFormat":1},{"version":"6046bffaa17bbb55ffd62926a966a7badce21b27d6239ba0b569b8266bedaf19","impliedFormat":1},{"version":"9376cce4d849f1d6ad2cb0048807c77cfeb78cee6e29b61dcfe74c7ab2980e18","impliedFormat":1},{"version":"2e0dc55ea1ade444d285576a4ed7915834d4a87f71b147c38afdb877ebb0ad2d","impliedFormat":1},{"version":"4edfc4848068bf58016856dfeb27341c15679884575e1a501e2389a1fea5c579","impliedFormat":1},{"version":"0c3d7a094ef401b3c36c8e3d88382a7e7a8b1e4f702769eba861d03db559876b","impliedFormat":1},{"version":"1a3b915d24b2a26df000ef55ed356028dec11ff54f7e93a5c095c313d7016e1c","impliedFormat":1},{"version":"7e3a4800683a39375bc99f0d53b21328b0a0377ab7cbb732c564ca7ca04d9b37","impliedFormat":1},{"version":"b1b5e35575486918e155ef02d995598be2b5d8e729f857f8309ba0b76e14d833","impliedFormat":1},{"version":"8d87de8b839a017541ec1baec68292ddbcdfad0f2f3b5f2ae8abd61f06105cbd","impliedFormat":1},{"version":"7cb0d946957daea11f78a31b85de435e00bcd8964eba66d3e8056ba9d14b9c55","impliedFormat":1},{"version":"b3e441cdb9d9e55e6e120052fe8bf2a8b5e5a46287f21d5bc39561594574e1a9","impliedFormat":1},{"version":"0870e8eb0527c044e844a1d83127f020aa7f79048218a62b2875e818355f8cb2","impliedFormat":1},{"version":"38400b70ac70600c632ad498df2d956ed8ca6c6774dfb0ef69a2d35a9450df7a","impliedFormat":1},{"version":"abab86c01d001d0cc410c7aee59168eb09bdb7d6d9d39d3c0081b36235e2824f","impliedFormat":1},{"version":"7ae39872b4f4d38b9df079cce4223e999754eb3b3f90e4e46b978b29e72c419e","impliedFormat":1},{"version":"dc0f3099379383bf14f2263c7987584e81b6d9b60259c9e31390455ca0619dba","impliedFormat":1},{"version":"6dd704b0ba0131eb9e707aeedc39be6a224b4669544e518217a75eb7f5dd65c2","impliedFormat":1},{"version":"6effa89f483e5c83c0e0063df5f1d8b006d9d0f1de7eed2233886642424dc8fb","impliedFormat":1},{"version":"5c6dc17513298b4daac99bf8e88ad4e4a504310cf69a0cf3cffefa5912b85234","impliedFormat":1},{"version":"d43130c35762a80da2299f8b59a4321b6e64acfb0b11a36183379b4c7b83314b","impliedFormat":1},{"version":"6bf44b890824799af8e20c0387ffa987e890fac5c5954a3a7352351eefe55d5d","impliedFormat":1},{"version":"e61999c06ae79ec587c2e7db514a024d85732b32ee2c997bf4a1ceb2b561c611","impliedFormat":1},{"version":"aecd29a5bc49b1de6b933344e9c96384cd098162c46873673ffa1408e6195c52","impliedFormat":1},{"version":"f83afa274e0f11860c6609198ecca220f5df60690923b990ca06cae21771016e","impliedFormat":1},{"version":"af31f37264ea5d5349eec50786ceca75c572ed3be91bdd7cb428fdd8cd14b17c","impliedFormat":1},{"version":"86d01647c3c215e53729aa2cb15d7bcc2b049088bc76bdb5e04a0bf25f97c386","impliedFormat":1},{"version":"9d3173cf740b742d1048d8ab20469060a2b5e2d426f8ff7df36042e6829c4aa8","impliedFormat":1},{"version":"f1063f0e6ca22a9fae0c0338768b03911c954b8e6ad4fff5381cc6a964b34324","impliedFormat":1},{"version":"4f85d12a28937e950b123e5385448a3bce0f04dccbca7ceb8aef351ffeccb228","impliedFormat":1},{"version":"85e4673ec8507aef18afd4a9acfae0294bdfaac29458ede0b8b56f5a63738486","impliedFormat":1},{"version":"40683566071340b03c74d0a4ffa84d49fedb181a691ce04c97e11b231a7deee4","impliedFormat":1},{"version":"81c8ab81daa2286241ad27468d6fc7ad3ecc62da04b18b77ce9b9b437f6b0863","impliedFormat":1},{"version":"268755fe3b7dd5ca84fc043de1502c56c5cf8ef70271c017964a9dff8af94a7f","impliedFormat":1},{"version":"8e56db8febfe127a9142435940c9a5a1ad17ddb2b2a6d8e9e8984785a76db1fd","impliedFormat":1},{"version":"f1efa458a3f630de51e30c823a8e1109eadf8562b8b90c764ef1fd989329bcaf","impliedFormat":1},{"version":"1ea64554b23db011171f7e0dd59d006b239fd4ec2e7e8b31ecf995528de79423","impliedFormat":1},{"version":"46788ee6b4670d904a54d56fe9e3bb308ab4c4ba01a435d39d8beaebf85f1a54","impliedFormat":1},{"version":"f4f6e61f620861b576f466e8af34e6064a997aa93ad62593c0c3f51489784e5c","impliedFormat":1},{"version":"f92fe945f94fee5c2811d6ee81b1751a1f1970b29063907d48067f1c2389bc3b","signature":"a33394f16beeee3312796eddb1186492dc73074f2cb480027e2de871ddb32a1d"},{"version":"3974befffa7647e5d975081c15016cda5f16062c8957af8d5b93b85bd6b57b21","signature":"8364a4867aade4b7b8e12b3116edc4c0cc374833476df15a0cbbe7b147bb1387"},{"version":"da094bbe2ba0c875d680fa8957a0b4056d806ed8093c4eb84f1d1319bc148924","signature":"2ecfec679572556d5739697241ee12faf6d1c088a64eb646f358d6b908201893"},{"version":"884b3c4b6de733bea0363994edfdbc08f23168c3819ee92eacf9ee2ff38b9e31","signature":"8b18201daa2caa4d6dad664291f923d8607cf8211ebd0dec3986e400f02376b4"},{"version":"d61b3b8b5d54ffbc1159015019c05472841f9b12287ad1eb0febb9d50b3fcf2b","signature":"7bd1aae3ca5e15b45dc603fad958b8d228f09e8c43ad9a4efdc70c7b3f96fc35"},{"version":"4b9b77c14bfa8102fcb57b14ffe92dbff3b513a8c4ba62893ab009fbd4c73647","signature":"9d2c9cbb279702e44a3ea7fe24bfe19cf27352d4cbe4882bbe5d521d27c9741e"},{"version":"aee88de82317641d6391f0686ca4acceedfaae5ade43d00dfdbb2e32e83870b1","signature":"979a61915ecd6734d45f9ab06a423a5b75cac28c23c512c838c10e333ff88a02"},{"version":"9a889402f27da6ba13bcaf7e0731fa06758e971c0d4ed730d6b46f08d9a05f34","signature":"f9d6f6e5c3e8a1dbf9499c426fb4d97386c7aa5b205662a4777f9289ef9152ab"},{"version":"f6534bed93400a60ab02368c4a698062e31ce5ad4eefd0f4994c2385ae83c54b","signature":"9c2f866be60bdff85a59bf2cd9b85041d63bfc369560cf59b88d7a95c6072f28"},{"version":"b6ced0b0b07feec87098d3eb446bdf772cc268ee3ac4230a4069e61dbf75cfe4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb040b86775b25fc0848d43e3cc03e1b5cf4f00c40cbd350dbce010c06404df1","signature":"6d46cfeb3c048590784e9c099f0dfb8de954141c9fbf25f6d9ec87981ebc6fe7"},{"version":"d20eaead87726a364899203678c3e81614bfc368630e7a38ed0008acf7f7c1f4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a2958fdf63b7d83f8d734d08ab6975b2a66defa7d7ee4988c0abfec0881b3a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7859b822b52969cf5421d8d07df464fabd68c67579733fd51cc994c6f3b9452e","signature":"91d869ffb8dc40ecfa1ed7675197ed893ea5501d5eab07a48f22dbe192cbd9b0"},{"version":"05a0701aab09b3b50154c4469670a6af40d716c1bd84258ab88c4486efccc2de","signature":"cd7eee6f9641bca037731468d9b1012d11858efb65ccb7a23e35377d824b2a4b"},{"version":"cef9a872724202d022975121422e03878a38b6c4a78977b7e277733a2ed5151f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2776443e3c5ce498f62ac5661d0e35884afea55b0d3f6f9306f8ffb97b35e9fc","signature":"c049b08ee071ee35f8623f69360d9b11a4e78f6f903a9601e9f76346ff07ffc4"},{"version":"18981392c502332d353be793e0eee6b4b71b92c4cc159879c76c0e412b50166f","signature":"85a5f8ec84196d475ea68d0239a7ee678d96c40274f16d0e940937166e2f9fa7"},{"version":"ef39108e7cfc70e36f0d3a795f5ee50fe39e5a9694626b9b88b3b24d505303e5","signature":"49497b292dab6540b4e9ffc2e066aba503cda20ff5407de0bd946016f7acfd01"},{"version":"ede33324139612cc144cb9ab0658d31f633fbbf6e5654b4867ad17964e494463","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90bdcbca0e0bf6c2b0bbb00673a340a12647ee9e9c0b7ffa08c1cbee51683f6a","signature":"081dc84b2cfc91d910771f621446c86f99ef16cba361a195bc5b6415671e3940"},{"version":"c3a6e46122a15e372681357101c151aefc21040e65611ab4edb7366b6694b2ea","signature":"84a5f8d870d0e3a83ea81b7fdd41940ea8af6ad244f7b5a41347a696ce8ee863"},{"version":"5c6fad27889b29555ec4270fa34f6d318520fc36e3c368efef6988c2240f943d","signature":"e569b4fa591b1a8f2e8bc65df628ea7b8314ea9e9fc4de877bae1db7073f82d3"},{"version":"75d850624f64a90b0709ea1dc2742d4b189c106098f94125af7cdfcbc9db0852","signature":"2bb5816f801dc67b86549dcb0c662be56b248c5dd1662c1042691c7329148f98"},{"version":"4e127bfcc9d5d93ab906153cc602cfcc13188847639ded607d4e83fd57838e12","signature":"f91e8b73b979d76dc70cf739261a03907357f4b464c2c2b57aa1da3ef60840c4"},{"version":"43fbe80cee30066d6ade0e64b13f0987cd6b23946ec6265728fa2adb27146000","signature":"017bbf6636858e6e607294afca49a452e39c06854a04aa20ad3850defd0025b2"},{"version":"77d37faa796f4c1b4eb99dd6645176245701ac08aa52886f8f7e3f340f3619d4","signature":"a615ebfd3685ac1590d379b501100df211539f37bdf6c544e83e35b14f69a057"},{"version":"753dc412c871f3fdc65bfea46ee79b435fabb41509238f866f6249d44f7c1dcd","signature":"c286b503f750f73cbf22d1031c189fb27e7d8a93ef017dc18d17bbe37fd5dd9b"},{"version":"4051f6311deb0ce6052329eeb1cd4b1b104378fe52f882f483130bea75f92197","impliedFormat":1},{"version":"23cfe48b32326ffa0bc8a8ebcad5414bcb7c3f6b7a525b3cd30c2652370f237b","signature":"eddd8bad575c2203156baa8df91727157606bda7ae0d100246ae7deae963bc2a"},{"version":"cb715246977191dfd17c33a8b96ae65a8c62199f06a09d36501951fee268a28d","signature":"e922958ca59e7c52c6e0dcfe9748dab953af2b18d58d716884620d408935efe2"},{"version":"d6ad5279f8d296ebb50264aabcbd50a5296edfef7f23ae4c159579028935d119","signature":"48cf5a32fd77ba27774c29824c8d1bf27cfe16081169b1a013e0874292c3709b"},{"version":"6f2d007923eb835494e65dcc1034da47cf8e60aef0554d323273a59b8b8c2f86","signature":"bcb9686b97930d312e851d879aa0ceb39656e4e49b07b8aef72ec0eae03cb376"},{"version":"406af28178f025030a57332cb2a36516048ecab7acf102b84f1c1a84f09d77fa","signature":"aaeb521b6f9317f1358efeed044f7b8c9da2de643c9c444c86efa4b5974707ac"},{"version":"00e2552694e9ca66c48d911ae3a46b5ec592ceaf1aa11fc892a11ea68e8f61b4","signature":"7edc93fe90f8fabb25092054cd2ba3454b5665dc1470229cd49300d2787f9256"},{"version":"72a2cdb9a957dd93ac6c7a5ec584a41a2eac59df7693ee69a8cfa93503b7d8d3","signature":"385f9da32da6c7c156c4c273f1e08ab2fbe264bb424469850b65c9e6ffa9c9ed"},{"version":"fa8896708f7c899af3f718f77f46489b8d3efd15204184f74b878992dd516270","signature":"e89614e458edec1676ac424f0a893a6e87bf5bf38d34a8758b3e4823f0d2b48f"},{"version":"85b1d0061b1268cbaa7efeba177d96bac002d38d7acdffd7a023decbaab2ef7f","signature":"0fafd7d6cde3e37c7c4045f298b00355745dcb2147c0e8d0f1240fba867274de"},{"version":"bb5660a80ad6edc1e4a7831bdc38cb4f70adbf718846aa3bb936a27b62d742d6","signature":"e017494fbef6d93da248b22d25416f95d8412cb4a4e8cb12c7e64495f16de3ec"},{"version":"91d4ebbf20c7ce05ce56b901d34ac84f18c5de49cdcc8b4e2e79416bf5863a52","signature":"00aed0049902c591b92c49af96b5a8d1b3e202604017f34241bf72cf89f80756"},{"version":"f7cde16e51986d5a1361c4d7e36cb8f8089acd60e7b43b7c0cb7ec9d3c58bbb8","signature":"fbfd3cb405fce3aab2cc8b6c68371f03f340b5bedfb22d1a0b46408ca184aa4b"},{"version":"0f55704e7fce1025a74958ce04d7d099a3605ab1ba105c63b7fde02139a17eef","signature":"69652f240dac09436bdaa4cedabd63700a279aaa035b43ade48742fbe5b37d08"},{"version":"4d8d7e049c7a369a07b41963903b7041bd8c88560b55af2b4b6c4fd7be645cd5","impliedFormat":1},{"version":"83f6b233e11c9f2855f7f318f608570e9a45db007ae924278e7a581d7ef99b35","impliedFormat":1},{"version":"c450dc59cb99ddbba3191c8964bd40dc732d13be5d9436d3557cfa38417939cc","signature":"1fc7a196b7cb9628c96283d1c55177082524e81f5e607404a5ca9a1ff53e45e4"},{"version":"231a843f95abff5b70bf76ded015c4d7c0ff006544d27c9747471a495743c2ed","signature":"1507e471793e1215912dd1ab92c0797ae9259ebf7fd0f3146e2bcee42b776bc8"},{"version":"deb4df42f640706245617d22c38500d0d24e34689f405224004477c47b30a287","signature":"84225d531b0d673c7dee0a7abb7592e937c207fb0393e85d8e9808505e415642"},{"version":"05196723f295c088f92bd93f9edf2f9d72a0e6fb7a468f55aa270214519857a5","signature":"1c7c3f06b140f7f31f69c3f2a6f87659c1a487c552bc733f9de6ed963779a17a"},{"version":"6326a3828a9427e88b8f11ae4fbe842af33faaf0fda37cf710d8f7a8f76fb912","signature":"a0b2ed7ed78ffb63bdb8c45c49596bf2792676cc3c527c599be027c2d772c840"},{"version":"6f4aaa2857e98b57b5f3239f2b05d4c0010dac665952f9b57c2b11f4b93ab550","signature":"9813abe08f8dc60f627701e1576bfbbe8498fa01840b3500ef120ffbe3ece69b"},{"version":"babf8f17c539cd8e5309393275eb17fa2a6790a848f9b6736e3e75b69ca12ae6","signature":"ee79b4e030d4b005413044e47295b78001ccb4849995c4dc59e42e65c509f21a"},{"version":"41ab97f834dc579a841339dd2d5ce54bd7458891414c624cadd12c2ed7985cfd","signature":"d703ffb3cf86f2e1cf7460554b6fc0a3a0eada0040fc48aafeacca14bffb7ebc"},{"version":"cb262ae73b7b864a9cc5e62142dc12600f5afddafa458e6c26218259d5ff67d7","signature":"433e57f0df48dbb4612309330aee7b075651c0ba5d29c483b17bd92e81cad910"},{"version":"f060e1946eb32ff62b101bbac21a6cd02835440c0892554566d0dde5d4838cec","signature":"036240f98ae8d5e07ad6f648996ff0630d6112eaee5b53fc3b309a1acd7c0721"},{"version":"a8f8ddbbd5a595a3a45b89108072fd7c11afcc5df839f3b2d234ce93bf5ba511","signature":"621d7479105eaf0b7002459dd4a7746134df8f621a6d9e62ac5c69f4b73902af"},{"version":"25a9445362108d35961825c730d1385aa52655c253523603fe3a514699a08308","signature":"0a82088daf1f69f93a6f03b7ba430d6605a8b48febb578e7ecd2c3564b8d235a"},{"version":"8ac5cd94bcb63ff4ace80712b0b2da1d8e3005c15c6fc2e96e3964e2cbad7221","signature":"6e0962e848047bf57be651109d9ee3d4e499277d114392e6efdbf60a3863fec6"},{"version":"b92e316d7caef01a7d96aae2fc81bac3411d81aa08c08369fdd79b79052d0804","signature":"a15f6b3477115f885bb24033267a6e06e889bbc393c5ae977513f0ef2c29efdc"},{"version":"883e6a16350e6a237822deb193859ba6f80f68b5bc63d37932eb5a222afabcfb","signature":"4e4f390cf28f71013350ead1ba25290872b936b31244feb495c7da040c655c54"},{"version":"20eeac8a87d7e85f13f2ce118073cec7275054be646bd47823f1e9cc8951ed4d","signature":"9f2d02e65e22f5bc32f727fb091f17315fe58a8792d8280ec59ab072272e3376"},{"version":"88f2985b43e7af3d4dbcba54e609861fcd28cef3ee74ca4d54e82917a9165b30","signature":"2e54daabbe58c730286e014d2bfe4a80b6d533a2bc9c5ab6fb1e3e654d3a4872"},{"version":"417c3d98d4efb99cd7f3c683c2caf02ae28758f18fed72ae0389aecfdab29878","signature":"2a39da52aed89ee43bf5dcadf72fc7ab5d16b8dee17ff890bf0ad3b72a0320c0"},{"version":"15e9ece6b9f5f2ce89f2ec8a96bc9303b35f07374b94005eb2443efaa0c6a49a","signature":"46676fa7ca6a5b6552a61d40d41f41eebc81cf838c14933cddd35203d298b874"},{"version":"0aaaaf9e39d6225f0fcce6949faf7254a473de642dd96f1b6cf5501b87347546","signature":"1f8e872ea16e6ef3029e47f25725a22c286734fcb4a88ea2e13c437e905f0c21"},{"version":"735af8f14d6e3866b5863742bec289903bdde848ba8754fb628afb54af74d5bf","signature":"6058d5942879388147f8aa5e9c2713af05d0f1680d7ba91d1999b97dc6b5ca01"},{"version":"69e1aa237956759cc510e91cf22b8b81ed371fd80d864593b86acb355e43795a","signature":"29df4852e710dfc4ccf300cbbe4f3ed1e109cc09bc55d540eb40bf2ef0906d0e"},{"version":"e49fff7bc7242932d50b6f1d894784704a65bec211535046a7b7983891e30972","signature":"aa5b770dd1b4e7ce9fea3c83330240ac673f9913a0535d02994bc0511eb85cf9"},"98ebfeb0805807ae08415404af1b664e76353e70e5e71e6f086c7ba264b76fba",{"version":"fd79331caf4d1c981f82d179a8f8ee1f5f9db5485b5960d2ac5252ff91ba195f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec213e4001fcd5e8446fea02e4a123873df592e8205987405c0e6664886104fd","signature":"f5ff573f4a6451fc2e8c7e1e6ceb8537cd0f25338065baae8a5735c24f22d431"},{"version":"c38e3b9be1619f15bec612db3c9867c9e3435bcc9798dd0d8f6342ab0d8ae946","signature":"e8f50d40694344b6b22ef6d4c3d5fe9347601c7a435fb7e60f820bf37d881d0a"},{"version":"1d25e7af5af3830d0cbc77493b047838dc152a775920710be72c3c8472b980a6","signature":"3560dc1bdf53f078348f0e499ab1e3339be7659310d92861fba7a9277024ffa7"},{"version":"cb83a4611b876ddcf649c18d80609654bb2c80fc160da1e7539cd3be15811a60","signature":"4220ee655e09296874f3ae3d3145efdd76398677bfa27fbef8e133c1b09cf50d"},{"version":"51a8c66060d8e08fa1607e33758afebaa47c542b40cf7091a791218255e7f769","signature":"475b03ccd54597a3115e13c07c1fe8ac417f6f6857d485114a00039d5d1ea179"},{"version":"817f663192fcc81412090c3e7167fa81c63eab9ea977e6a6ae8eeafb8742001e","signature":"77ce1243fed91f1a9f7be9c18192af18f4ea1603611a3dac2ba4f726c298de4c"},{"version":"4c31fd07fd4e530b6da7febec131d259907d5e179ef3bdac0a4a43cfa56b6935","signature":"b1eb5e11871638368fc4cab710e40fe4b23e0f7e9d4a6e22052edfa50d185fa9"},{"version":"9c5d15efba9e25be56ae6fe11057225c74316fb0c90e7d34b81ec8f633a9810e","signature":"a68280c12af5525ec8003356652c9ce50a24a6b3b6fc83bf793fafe60909fdbb"},{"version":"b02c8a9ebc617e95159a1d928fce2fbc345f3e9ccc9f7f6684195d8f8d9bab5e","signature":"45e169847975d5baedaaa5fbe3da4bc92db0b90a305f2536491b7a4a2d262341"},{"version":"963c32aa76aee7050bf4b5749bec7c775046f03bcd791d54992b3345f7b80e2e","signature":"66be142f9806a1a6a875064f7a0416e1ccedcdcf6a9a209b1c633e475b975dd2"},{"version":"9e473cec8b5dbb77baf8db593da0a943701f1edca3b3b1ac81af9ce178dac9cd","signature":"219dfcb98664c09e2a901a0bebd0a1990dece13622fa81b99a4fd16e6352c936"},{"version":"10ea972b401fc77b7e35429345f02bb02dde34fc9d7d1fc3232a187f5b52facf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b85f8b41e677c1e4e486b8856355198d5266556bef1fda2f1b0cdd23aea308f5","signature":"f25e6f98b3f2f28fe6ef1e8a2d53a260e20c60f99d311e57251ab5dad00a1e3c"},{"version":"eba4d3bd1b2ba75f33dec7019769e75d9cb5ab19609cb3181e08ee56e1c8fd45","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1ab7dfe2e40a14457c44447646438563ffbf187e60a175f258af4189bb414e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3616c07b4b641b8456821754d23d7db606498fbf52d04c97fb8cc285ccdd3347","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0d29ff571f05d9f1c9ecabd53c10cb9bfcaa313b3b64612593bec64745c4d224","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cd5944f91eaf3e04d8c66d1c7c44508f932ae86fc033403193a81a0e3a95e53b","signature":"2630a9fc3e9a2205f1df08e9d39ac89290da5a35ab782d1504364baa70c67104"},{"version":"3ab2b6455439badb3d984aef6d2519029dd8595f19f614654072798269598876","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8f3b78fbcee37a708acd2a86f1c22645cf34b444cfd7459be341415228f4b63","signature":"14fe776ea9f72086fe119d5df096c39513d6bdd3ba1615b8d9f5cbce35933f54"},{"version":"f14799e6e43275054eb876159fdcb6c55b4e76808911ffdc9f81a2e3e5baa564","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aeabfd5da8290656189b20d20600d0df6381dc3881c381b815807e9fb745f5d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3dc95ec98d2db484ccbaa31a47c2633bd619a4d86fd655739ed248f081f49f07","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"647f3c90fdbbd31bc9f337d7674f74f40f32c3adb60bde42ad85adb0de922b48","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9ac8c2249f0a97698a155031023e87eaa74c871229e36b51c3c83fd1a0bc92d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35d7cacd428e674f89a928ed99f34ddc7c36958b395627a9196a8ba22618a29a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c51ed8506a74e96ec467047bfb51a97c31209bbe090f330fb29306a19cdba765","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69a81ee2b65949687067f23e7d5ca0fec2110620ff93ca8b92bc56740340b760","signature":"bc160a1badf1d1e0e9b92666b4848dfdc428b553d00ac5d2304b7ad80ac4cbea"},{"version":"5f44765c75000e8fea925fba6c2ba696386103cab9d813e72070cdcf45e1f804","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b11dcc6b3a1e92851fa7626c01c543833b96a9f37a29d80de6f11b320b626c9a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63e34e9210807a4d1af057003031a6689dd3295f8f2524ae7597ab27f326335c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f825e992a9f7f8576702cb198f6eb82c0fa45188b57991f5d23ee5f0bd16e85a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad7e4e17c67808e01a84f46591f2d4b763173ecc84a4863a727b7058b5786945","signature":"b7902bfba5bc8b901152f1ed5f8d9c2cbf2ba2790d351e5e5d61e00bdebbc624"},{"version":"3e5b494cda4d0ac2bce024928f0eacc77f8d9e4ac3d51eadde967ce542efc27e","signature":"b94b0bc72e5a26387c9314dc4f72106bddc0e5056469dcf4a43a351c1523c49c"},{"version":"1c230948c8120cd778cb258a0b70eec899b458db58229de16a2951a7ebdc57b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0fef5a24cad5c4948eb776b00fa114e2560ce3dded3abc27db2e2b2b66832331","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2cef84bf00cbdb452fdc5d8ecfe7b8c0aa3fa788bdc4ad8961e2e636530dbb60","impliedFormat":99},{"version":"9e2f5dc3da9d83bf4a0a9e5d39d8c9918482d586e0c403a44021e4ae7662697e","impliedFormat":99},{"version":"799003c0ab928582fca04977f47b8d85b43a8de610f4eef0ad2d069fbb9f9399","impliedFormat":99},{"version":"a62e448d3f09fee63ec1230acb23fb54f8f6ccf8d6f0001c7b94fd51594b7c9b","impliedFormat":99},{"version":"5366549884acc57185eeeb64561c2060af008230a8ea645f048f747cfac6549c","impliedFormat":99},{"version":"cd3229a2e4ca10207178e22f215c8e196c837254dd34ee440612a2a14993ffc2","impliedFormat":99},{"version":"73b7e3d5300ad64f9231f5bb145fca4892574d85e2d1a015ce095628f16915ca","impliedFormat":99},{"version":"42944c2dd3115e25cb0aa77aa05fe9e3d0f8a3b4ac251896cc680b7be41ad60c","impliedFormat":99},{"version":"7ed8ed496092801dd5f25f39af223ebeddc97bd64a7d9a5621f790dbc836eebe","impliedFormat":99},{"version":"abc549dfea982be25e0379cdcb6ef2aa6716b0013c11d8d6b14c814cc955d7c8","impliedFormat":99},{"version":"0e6ba4003cfaa90748b69ed0dcc9f99299d1af70f4bd835a872e52705b0c850c","impliedFormat":99},{"version":"3decd4c8e355126e76c9a43cc7ae08017fbcf1d766b204d696ccdfa5128de1ac","impliedFormat":99},{"version":"40410f51558d0b3d635584333fbba6b58b4b7f74037f59a08c0577828539637e","impliedFormat":99},{"version":"096350f9446ef08832b935d4a97c66f74a9133faebd90a40a12abd5f8bc7eab2","impliedFormat":99},{"version":"26baad6aa356ef75b2e1ee150ef6325988be9700bba14249f9e8d0f66bb36087","impliedFormat":99},{"version":"237dd4f246265a3efb18c3d40f54f98336ba2c329a9f9e30b4bb0f1a27baf324","impliedFormat":99},{"version":"3fa62b954262157916a65b3dd57faf6cfec7544579e673204da30eba00852543","impliedFormat":99},{"version":"8ab0b13972c8018bd18d49236b8c08448a38c823e28f5620b3ef0b43ff521589","impliedFormat":99},{"version":"065dff95b2b9cd6f5f7404222ecdbd371f15c22b844b731eb32286540f499d2a","impliedFormat":99},{"version":"d1c03f0339b8514b7d5420e075684e6b1dfb9d6c27a7fc6fbb09bc3f25fc7764","impliedFormat":99},{"version":"f8900ddf4a4944cad4a81de965c4761094758ee39bfe24198668c397caf5db3a","impliedFormat":99},{"version":"b2b8376bb1ac24155cde89574c32edfdefdb926845d9426ab52815421b3d19a1","impliedFormat":99},{"version":"4e805f78a8acff48feea70836df232a6db887b2e376492666f6b70985fb706fd","impliedFormat":99},{"version":"cfc5a66408fb9a7dd136ec2afd50a3eced54baa3321c473ee4d29046e761a3a2","impliedFormat":99},{"version":"594201c616c318b7f3149a912abd8d6bdf338d765b7bcbde86bca2e66b144606","impliedFormat":99},{"version":"35c190fc184fc2fdca132bb8aad00ac84819f135428b0e906c3e599c74125d24","impliedFormat":99},{"version":"8f567d63ab28f074ab2be3ddc2da27107de8022488f4a3bd91609752045bb612","impliedFormat":99},{"version":"f6c7ae690e2d224a310c8f967cdb415d8c7c55791a30c00da30e0c19ef4def49","impliedFormat":99},{"version":"f0ee7287284a844f4d04b80ae7a955b12cb50f85fb0021a78cc7f20a90459823","impliedFormat":99},{"version":"956e7dae5b888d02ec65dfe4113b541042cd2c70f96f6b9de0a5465bdf9565fe","impliedFormat":99},{"version":"75722639ade81b4d9a9a7f67f9cee2abbb68c52367322fe4fcd51949dbf60706","impliedFormat":99},{"version":"f90d3104f554535c4bfcf9d429e41318563c40b3b7e0827c0975624722546514","impliedFormat":99},{"version":"c61b9b3f161eb34fe5ed7fd3bb84f0774d74445928a06e9089ddc0a152f2a016","impliedFormat":99},{"version":"17268b7c5aed233ecafd22ac3e751c3aafc101b7ff982de8617bf19fafb7058e","impliedFormat":99},{"version":"bf6060c0585e76d2670629cc4c592e1dd938ac356e974916ced7f46587ba8181","impliedFormat":99},{"version":"dfe68566e870382e203fbf082e3e094b3d3d6712a3b6bf56fe66f69271d27cce","impliedFormat":99},{"version":"f230e4b9b3a7c27975a8af6131b08f6b17505e829073a3faa6ebff4a163090aa","impliedFormat":99},{"version":"e89ae5ee53771a98d89105723fc4dc73205bd96bfd2a784597b5ec6c2ed35abb","impliedFormat":99},{"version":"7f79b823d4b2a1fdee3a799a6a46792a21e4400ed0c2f45f1e1a9bb8de21d18c","impliedFormat":99},{"version":"42b828f21d7b672495a1f538ac49e93ea12da980d07d28999c7eb8dc55f297e5","impliedFormat":99},{"version":"35e7486045f8a29b25ec8adad02823bb82e0876fcce76228bc683e0da0726e98","impliedFormat":99},{"version":"a43f4964d97d0feeb6b33944f750707dbdb539e1c9c3a0496c40789a90d7e0d9","impliedFormat":99},{"version":"8dc6b9b1f772053689d3b298f089ffedf29ee93be2eead0d9c07d77e68aad9e4","impliedFormat":99},{"version":"b44e0ca6cba9c3f98a1b277e93dedcc31990c57c08f0fb37c29eb929afae3a49","impliedFormat":99},{"version":"e236485fde7c092508a177ccfef03ba15ec72ac697b50e241802d68dd99c5f73","impliedFormat":99},{"version":"b7ea66bd111288844e2d0cfb12abc02242af0786a83ddde14abc156a7f80d500","impliedFormat":99},{"version":"83fd9ba9e82b881f410b69b30d4fa9e41b1b6e445e4d7c7eaec836d4cc5a5712","impliedFormat":99},{"version":"ba2733b454a9756b8207e110896e4889859d4a23581e54fdd659f09267a63ecd","impliedFormat":99},{"version":"d94b9c4da700bf7e011fbd442c54b5c88a52db58bc71bb69db67f46a1c525320","impliedFormat":99},{"version":"bb19a13fddc505d633b9d08340c851a16638a3a2c6ba4971d538908b0cce8671","impliedFormat":99},{"version":"da588a0328ea4fa648563415d1ee4cad0587e3d1e1d29cf54d761fbe83ed9670","impliedFormat":99},{"version":"673f71885a78cdf431dd29b801ef2f811a2c793b415c44e64a55489ad010f6e2","impliedFormat":99},{"version":"4c0c16f5d60671e0654e560a94cc549a858a5fb9397a072d3f9935b3068be740","impliedFormat":99},{"version":"2cb58371baa22dbaa02e2abfc40b5640f00e7ed203e70e97fa20226a776b2e16","impliedFormat":99},{"version":"29db777661a60ea3a85cd21ce29b0bd877bb44f52cd583f9f3f7580ee08d4fd1","impliedFormat":99},{"version":"cdf79d50d5ca102a6ccd1ead392b0f5ebcb9b6c8b230e4f4931f0fab8b6ff3c4","impliedFormat":99},{"version":"6e21729eb1f94c93f99d1c13492b6e835e5c2d2ba552693c1c699f0e34d1fa1d","impliedFormat":99},{"version":"8267fbe09febe68384466808d3feaf055ebb7b15903728d23e7fb4c01949148b","impliedFormat":99},{"version":"54e45f5f4f7684c5c49d3e6367ba73c55c69f82973ecf7aca793a86bea5a99af","impliedFormat":99},{"version":"55a9664e49c8e8db27d8eb413749957eb222485b91b1148840a73e065ef6c028","impliedFormat":99},{"version":"af7945629e88f161817436aeab27906b947cea60102066575eb31071b4f84168","impliedFormat":99},{"version":"847b7eec4ffc81b7eaa1bcb473fd5da4aa73ab7e56944df3caf7d284317e95f3","impliedFormat":99},{"version":"5dd262cbb746c2a4d0a26f09369b3ede4a1a36e15c272adfd0289c47cef81ad7","impliedFormat":99},{"version":"677e4d55a1353f1b83ad68faffbdd91ffa7dbc34d67b1e91e88d3ac71b88be0b","impliedFormat":99},{"version":"21ba9b6a4c6dfc6dc403884d34dec961eeb965a4e0c99521ba2b3f9929e26b75","impliedFormat":99},{"version":"452a373c93cae3a20fb8f8309ac48b40cb2a33f05c3d54b090582ce3b8ae96c1","impliedFormat":99},{"version":"112f147e1f4b44b4a4f186cefcae4e58c49d6a0a61faacf7a12f55694b9f2232","impliedFormat":99},{"version":"c293793b601177e19a4230a9ecdaa167e6a44c93147da549941eb8e154510f4f","impliedFormat":99},{"version":"82ece43251947dd304e6f5dbfaf8b97588e5676ddf0bc0fc1a6a861aaa3eaf7c","impliedFormat":99},{"version":"f67c58823afbf2590f2c239d09a46aba9d3456327eee05b593c48ee248758ce0","impliedFormat":99},{"version":"e2647503f56e5c6d41b256af0b17ad3b98455cd8b852ba7336221af5fe99d805","impliedFormat":99},{"version":"c2d12e71e905f9ae80895201ae4b52b0082716d3177d794799f0140c3bbdb65c","impliedFormat":99},{"version":"668eaa98e8d54dc5a22d7a66d659a47f0b152e7b109f798cb295a3c3dd817dbb","impliedFormat":99},{"version":"81d447a1f248a2345a89673774ca673e79da5df8e25c6fd6bffb495d3b704362","impliedFormat":99},{"version":"900f1f5341752c6c2824ea871ae941d60be1359793a0284e56abcf277955a511","impliedFormat":99},{"version":"00c8b548f04329a012af189dfd8e3f3ddd8d4fb187f4fd22fdeba5e1eb740d92","impliedFormat":99},{"version":"919ea552c5b52ac5c8303a96dd7357986a2597de5760416468550b659113bad6","impliedFormat":99},{"version":"8255114fec0d6189524bf52d90580a2fce40bdac621215e562aa5f5b058fea33","impliedFormat":99},{"version":"6020d3e324725ee474aa4637005d2449eb8bce66e8aaf85163d683df86384dd0","impliedFormat":99},{"version":"d95e4069a535a118c22ac66a8b018818f9f74ca7000c8eac977dceaf752d0f95","impliedFormat":99},{"version":"5d8097f4e2588d7912d82772ac6f05ee6def5b738f5e4605f2e9bb24d26b4e86","impliedFormat":99},{"version":"6703ee0cb2405fc9e98a8835e4266ed4131fd25c31bcc0c302e66e9b05271eee","impliedFormat":99},{"version":"4b83d4ffdcb29aa6562749ca797b76a3b914d80f54819c6a08f1014fb6841623","impliedFormat":99},{"version":"41ecfbc96066dc0d03f1a8139e28b4b3297bc231257d27a7c5796d017962a438","impliedFormat":99},{"version":"46e060979c9bb359578744342c37b843529c284e20ebc219bd71d5fbc04b3704","impliedFormat":99},{"version":"720b258293ffe0939688db7b4729d24f64809718157b14ae50fb9e2397c69fbc","impliedFormat":99},{"version":"1273795a90591a538b11c91a7840b1facbb5b6d500146cc055324a16f58c0346","impliedFormat":99},{"version":"a06814aa3f18bf501a7bbd1cf3ad9b1fb090cb89b19375debf6ac3b906ad9090","impliedFormat":99},{"version":"785afd3f604c75ef24a65c0f2ce4b3ce2137f941773c201842abaa7385b12e3b","impliedFormat":99},{"version":"ee3bfff84df83f9e3cf0ec85aff97df52fc57e740e41fd4780de1cb3f9e73780","impliedFormat":99},{"version":"2025d7779d9356a37ed4142da93898d39f811d9c5937f8c107f44ab2344e87b7","impliedFormat":99},{"version":"471b3d02d1af08c6b58a9a2ff5c85da205910f782a7783d7a1f59dcb681ee8ea","impliedFormat":99},{"version":"e1902decb3f07a58e9be70b5136e3d715997025e0f0f20cf7e2610363f38ee04","impliedFormat":99},{"version":"323156c80e3ac6175f4b75952ed871ead30b58b9ec463131e368e572d89777be","impliedFormat":99},{"version":"7c54717447fdfa134e43c6f1a71f8ae4e955538f9e59a8bbd60eb65f5bb965e6","impliedFormat":99},{"version":"0d153b01d0b1e33ad2b8c778765c3f3539a3ffaa595dc3e9d53d91cfe5615f11","impliedFormat":99},{"version":"0ef8dbf7f717c2d8912df768687073cda1d7ec73ce2861fa8ee30ea8c15455e7","impliedFormat":99},{"version":"355ae3751ad1378804c850b212bbfed1bb68af9e4cde0cde857b86c6cbbe2140","impliedFormat":99},{"version":"06b02b230ad18789680a5d286d55d566451973456fa33b63ddff6c9b2c2ab41c","impliedFormat":99},{"version":"971f0be2884711cdbd2dc522224ba68db24abea620e9089b5432a9ed73dd406c","impliedFormat":99},{"version":"1e45c92c3241e189027db53310d5b3b8d713fad08ca6ec5f8e0734b275b6dd76","impliedFormat":99},{"version":"a374180a9dc60b15b4fea69423ae9d8e3cdfdf604e8cb314325db23a2a8e3cf9","impliedFormat":99},{"version":"acc82e49137ccc0be7e523164613032cd0a35a08b38721138a228926539a33f8","impliedFormat":99},{"version":"990951a94433c2efe6e42266ebd096f63154115a37c1f4e5bd37bee57bbd3563","impliedFormat":99},{"version":"b65b675fe2b1ad0d621ce5ad94e9fbdbd16b17e8afebe2863361e0d028dc73fc","impliedFormat":99},{"version":"1bc87b80ef30a78d0cec6f6c56ad41b68a8f03d30a7052d1a0f1e946f5eb5150","impliedFormat":99},{"version":"79ace3491ac2d2585e2e3748827466f99d0fe06acfb8cfd7bb5ac6e272d9b742","impliedFormat":99},{"version":"f51bf6581de40babf85946efb37bf4bab0a5357b46b4a0cf904278f3b8234350","impliedFormat":99},{"version":"c728002a759d8ec6bccb10eed56184e86aeff0a762c1555b62b5d0fa9d1f7d64","impliedFormat":99},{"version":"586f94e07a295f3d02f847f9e0e47dbf14c16e04ccc172b011b3f4774a28aaea","impliedFormat":99},{"version":"cfe1a0f4ed2df36a2c65ea6bc235dbb8cf6e6c25feb6629989f1fa51210b32e7","impliedFormat":99},{"version":"d94d06e50f58be0a417ebc0336be0c51e5aeb06cbb59ae7d5d4cba95e4948418","impliedFormat":99},{"version":"02246d22f0fc51c76534d953f606aab7c012d1acdb182f822c8ac8a37926a72c","impliedFormat":99},{"version":"0166e0f095473027f6f8744378f5ac5cb6557e788540fdad76e0abca9eef2567","impliedFormat":99},{"version":"f950a4cec73ccf53ee3c56f117e5c585872bd13328c487cdf7a614246feb075e","impliedFormat":99},{"version":"f325583644b63525d1c4d22825633c220e478411d813f134d5930207cdf8aab3","impliedFormat":99},{"version":"e25a05c0fd866cf73c00a281ea11bb51fa8d2a9955f2edf8a7b8f3081b37c165","impliedFormat":99},{"version":"2c86f279d7db3c024de0f21cd9c8c2c972972f842357016bfbbd86955723b223","impliedFormat":99},{"version":"df6ccc0d7f7324035b05a6294404b310a23b2f07fbbebe1cd298f88647ab8b6d","impliedFormat":99},{"version":"8cfc293b33082003cacbf7856b8b5e2d6dd3bde46abbd575b0c935dc83af4844","impliedFormat":99},{"version":"62391e62217e8a22a4d5f3ff123912bb4d182598e051f31b287096d187cbaea9","impliedFormat":99},{"version":"81895ab68da9cb1656eca90934f01924181d57439e980aa3df8c788488363272","impliedFormat":99},{"version":"cb1ee5692cfe21d8865ab74cc64aeb2f3319f2c2ea2f63cf2662b6319160beee","impliedFormat":99},{"version":"ef6ed27ceed062efa353f3c108dd31d3e4e83e222ed9e18566fa85ed4600e366","impliedFormat":99},{"version":"faa076baad26c7c20856aa86a22c8afd9113f0bc47feeacc680a9e6d4493ea5e","impliedFormat":99},{"version":"782d76ae47ae31c1169c04d93f11e6e13b50c704833517ffeb933516abc4dc12","impliedFormat":99},{"version":"9036dd2d0b09989692fb0eb69b5142647a709aae1f2bfea464701df33758345f","impliedFormat":99},{"version":"14eeb7d5737bc074d1020b7648358ad0488dfb576aa82937e8447586c1b02bd8","impliedFormat":99},{"version":"73b252fae9083ac46b9f2fa376c3a4f5d2c98f5fc0d31922e6d74e7a416f1034","impliedFormat":99},{"version":"0c35ff99747044453c64a4fe3e0e813adc45e67c16d6c47a39cda7d5b2c45764","impliedFormat":99},{"version":"a127363b7f50b5ce89ee98b2faa52a3e7247af32785f937e827e9ee32578d803","impliedFormat":99},{"version":"ae9e8befa5a81361fda14b5c44953b69a6b32abed1e9c62c533230796ba2b39f","impliedFormat":99},{"version":"f3eeac608cb47badfaf2218914776864558c08a392fa626d3a0ad678b0fbfe38","impliedFormat":99},{"version":"2c05477216d0da559ce805e5b5cb8f3c72e1897110886a0fe22808ac37a2f5f6","impliedFormat":99},{"version":"6307d21b4a02a9de0ec25ad7c8a36bfb3a25d38adb1dbe877f7e73595a4a924c","impliedFormat":99},{"version":"cc78721e9ec12b7b62352b8bfa1e37abe055c17965d5fc956d4edccb1bf4f673","impliedFormat":99},{"version":"53565e07ff42ff137d862dd402cd1799785904a50cbd75fbf8402d7ae76fb6b8","impliedFormat":99},{"version":"c8c4e8de61ce90831b7342b6e4800a3e70f4c06eadb17dd743e652ece3562ebd","impliedFormat":99},{"version":"37ed869a9de36bb1ddf343286b5cd0e0afaddd892ba28e82fd652b7ee2c46dec","impliedFormat":99},{"version":"5dccac21bdd7a3a3f399f2a0110bb1bb22a7bb002e3c4a3403f781299faf3f53","impliedFormat":99},{"version":"976ff2cb836f3b64382f2090462966b6b82a059b8d90c4eba54ffa2021e5c150","impliedFormat":99},{"version":"9432e9ba2ed3ef0169d133a2fdb113002be901691ec78ec9d2329c12c16d5065","impliedFormat":99},{"version":"f7e369493bd11921421f51025608f6450675e5d5fba73a1f5617c96072449ab9","impliedFormat":99},{"version":"0919c74e404e0f876c1687425547263ceffe5cc184404492ed2f8deb8a13cbcd","impliedFormat":99},{"version":"7df13a374704470d39a931dd1fa3602a3bd1cadf064115784e4acc3b25e6c24f","impliedFormat":99},{"version":"36944fe70fea641703d40efab3585844c0ed20ce7e783fdcde90bad50bf77f5d","impliedFormat":99},{"version":"cde65d40e64bf0aedba644d8841fba8fecc6f4793d7e4a4364be954bf273ec0c","impliedFormat":99},{"version":"ab9a48af27d31f50da02f40b83b2e8695c4ac28bd446f37d34d5ded0443aed3e","impliedFormat":99},{"version":"0b1a50c36805a5f3be773ea73339750c3619a7ac53c0f441f5e9f1cdfbddc695","impliedFormat":99},{"version":"b85424e3eeb4843556cc1838289e1d3aafc8907b44fad864f228e2abf1af55d4","impliedFormat":99},{"version":"0bdeb9f8d6472b196355591ea4a4313cef5434d24bc79c6e5e733132380b87ea","impliedFormat":99},{"version":"91fe1b91f77a6080c156f0f6af3f6b12524f04604b6e0925432c48f7ef58cfd9","impliedFormat":99},{"version":"9866369eb72b6e77be2a92589c9df9be1232a1a66e96736170819e8a1297b61f","impliedFormat":99},{"version":"e84281e45703810be96251405f8051317362e453f39f26e078cde8967fd2945f","impliedFormat":99},{"version":"0bcb04a160a2a2a934480e3b899b1d2255970b25ffc7408a5d07aaa07baf2878","impliedFormat":99},{"version":"8e3a9c17439b657424fc7e311943dcf9444fbcac73f3b9b72aec2f449a11e203","impliedFormat":99},{"version":"a6c3df80c7c5e8a15e302df97c8a35b1deec48f6a8639110663d6c85ea562fff","impliedFormat":99},{"version":"4c69a93a4645185c445f0050939645592d49f2b8dbc999ff63176c607f3dc319","impliedFormat":99},{"version":"0e2d2919246a4491005fba1612d101a68dad27a5592a77baab1523b2de335cc2","impliedFormat":99},{"version":"c32be5821ff157b2845dacfb257531e932a1161b933e6cd1cd0a4de9e057bdea","impliedFormat":99},{"version":"eb14bc57e220517c752f74ab7c810b72a80632c26eccbd7af690ed9ea7b5ee03","impliedFormat":99},{"version":"ee0de1f85e4fcafe9019c89085cedbde41a22d4492bab87623eed5afb91065ec","impliedFormat":99},{"version":"588b99d933490c59f0ac74e43491ec1b71348b049b1a391f24318b84bdc17b97","impliedFormat":99},{"version":"d78f57a7b922e855a90900275fc93805e07f8cfc7689039840118eb6bf6f0057","impliedFormat":99},{"version":"3c20a3bb0c50c819419f44aa55acc58476dad4754a16884cef06012d02b0722f","impliedFormat":99},{"version":"82c69793fa09d8b58a3589f08c7d16163c566cca5657dcc45deaf5160f2c0e95","impliedFormat":99},{"version":"b89268c927a997e32030d8d8daeb0ee65a7c7db40b167a39296459e114ba7511","impliedFormat":99},{"version":"fb8bc4e79a3b9442dd3e8b1bea89b3e0ad93dd154f94fcb7ca81f511c7c06b65","impliedFormat":99},{"version":"b6c9a2deefb6a57ff68d2a38d33c34407b9939487fc9ee9f32ba3ecf2987a88a","impliedFormat":99},{"version":"f6b371377bab3018dac2bca63e27502ecbd5d06f708ad7e312658d3b5315d948","impliedFormat":99},{"version":"31947dd8f1c8eeb7841e1f139a493a73bd520f90e59a6415375d0d8e6a031f01","impliedFormat":99},{"version":"3a4b1b3e62543a3955e1ad5cddfcc59b25074f722d5dbf7aee1971a43de8acd2","impliedFormat":99},{"version":"19287d6b76288c2814f1633bdd68d2b76748757ffd355e73e41151644e4773d6","impliedFormat":99},{"version":"fc4e6ec7dade5f9d422b153c5d8f6ad074bd9cc4e280415b7dc58fb5c52b5df1","impliedFormat":99},{"version":"3aea973106e1184db82d8880f0ca134388b6cbc420f7309d1c8947b842886349","impliedFormat":99},{"version":"765e278c464923da94dda7c2b281ece92f58981642421ae097862effe2bd30fa","impliedFormat":99},{"version":"de260bed7f7d25593f59e859bd7c7f8c6e6bb87e8686a0fcafa3774cb5ca02d8","impliedFormat":99},{"version":"f9d8848e3c6d82c1e348a9e5cc531e433be58c4ba233a6683a4e9bf6d923a462","impliedFormat":99},{"version":"48a3ae8b6325c87135a210f6d6a7ce15d58417870a2ad78d70858313c47eee99","impliedFormat":99},{"version":"9822da8046d00ef9b8a230345cc163599e58629112081ba55cf4f8d88ba5bd93","impliedFormat":99},{"version":"260a0f4a8a6dc69a2dec8ea672d702629ff7624d5684b29be55cca02a3e42e7e","impliedFormat":99},{"version":"9789d7263d261044cf33f0bb5fd31a2f4ae3a4cc2a010aa45db6f7d01fb019fa","impliedFormat":99},{"version":"d81f0485800e8813d917c2edf184ca3a7fdeada1472cad6dc41e43c37e240800","impliedFormat":99},{"version":"f59c2a64fc652509e0cc56fffb59d7b81f4c7950c7dcfc2da44b681637604797","impliedFormat":99},{"version":"ac0d6f9d09ee9ec076ec3045d20f0d6f5b32300d5a2fa05b5c5a9b6492c0de1f","impliedFormat":99},{"version":"1d8a6497f663251332519c392c6053d5b5e93e5a2189e2669620851b93fbab65","impliedFormat":99},{"version":"52f2d4cea9e3b8e4821b6ca71077ec5f41316d1b3c7d599ef10fd7c8c839ee09","impliedFormat":99},{"version":"013d7f1c5798ac843bcf24e6f3d97efa42c79f038da9cae4fc95ec686b3087ce","impliedFormat":99},{"version":"83b28136beeebb45a635f0179b828e0d0ec9c59330db43060c5958d796e35ddd","impliedFormat":99},{"version":"81c1ea7f9b00460828ef1c92fbbcfa9ff0a7bfcfb2dbfe2510bf7916c914fa75","impliedFormat":99},{"version":"6cf0bf08cc2ffa6d25c7a9852e58f7de9b26122a42380a89105c201e8bde13c8","impliedFormat":99},{"version":"07350c1be768f0446138cf700b47a8aae8e2f6d828310e519bc500200d519a92","impliedFormat":99},{"version":"4253e0bc9530f4c0eec62d1c566350dffef04ab26d0f72befd2ddc08ccb61925","impliedFormat":99},{"version":"9237ce9c67ba997f8cdbc795be7628c1eafefc3317260c38c1e2df4ebd63a62b","impliedFormat":99},{"version":"1ae2b7f6a1352e73754401f16a7894c1335f3fd199acf4c473274243f89c3230","impliedFormat":99},{"version":"ecfe3af749f3c44ab0fa260d7027b067332f0841bcdca1c8db75eb9b1890bbb5","impliedFormat":99},{"version":"94899ca690be8b491a49004460b79426162b218ef26948625fc025cb40a092e9","impliedFormat":99},{"version":"7fd2e48e2ebd92a381e745c7cfe58003969296f7d0cb0109808e6e867bef6a4d","impliedFormat":99},{"version":"98d7fcdd7c0c682528a70f6781f7a00cc0f314b720d1b15996f223c74dc0cf69","impliedFormat":99},{"version":"155e18326afb2fb26a380b480e0c892cc85cc9449537b3346fcf5aaceeb953a8","impliedFormat":99},{"version":"523d1775135260f53f672264937ee0f3dc42a92a39de8bee6c48c7ea60b50b5a","impliedFormat":99},{"version":"e441b9eebbc1284e5d995d99b53ed520b76a87cab512286651c4612d86cd408e","impliedFormat":99},{"version":"f67db9e9b24275680e88888b618e0d6514a40cef9aec2b6ea8eb1de899f97933","impliedFormat":99},{"version":"0968374af7bf8bf67301b89a4fd4bc8594dcb90b16b4be06ee57d26a708bb776","impliedFormat":99},{"version":"f57e6707d035ab89a03797d34faef37deefd3dd90aa17d90de2f33dce46a2c56","impliedFormat":99},{"version":"cc8b559b2cf9380ca72922c64576a43f000275c72042b2af2415ce0fb88d7077","impliedFormat":99},{"version":"1a337ca294c428ba8f2eb01e887b28d080ee4a4307ae87e02e468b1d26af4a74","impliedFormat":99},{"version":"98a667d4585d5b040af90fb5062e31da7c215abcb47521ff57e33f62755fdc17","impliedFormat":99},{"version":"c29e02568f8b68e62b83db2243e4bbacb5ced2d6c8d120e322b56a018d1070b8","impliedFormat":99},{"version":"53c8e58bcea418aa22f5ee013774c08dfe15f0df9625868b7ce5a7201de29785","impliedFormat":99},{"version":"d56ca5a8aa5dd4937a82df98dd930ef154f340d259bcb2980d36c28a47ff2901","impliedFormat":99},{"version":"3d421ded6ae2260cfd45b230eabe38b6c8498a1a35db809384c85ff2bc3ba822","impliedFormat":99},{"version":"ead83f43dcb956f13b924b9e43e7a64380f830efc67439a9e9e479bc985df8f8","impliedFormat":99},{"version":"d04ba54e15442a067fd28679bf18d11ca2162d64f3b0695b9ddfba2b8e1c3b59","impliedFormat":99},{"version":"6b6cced9b26444d621bb62a1b8cb65911c22505fd559411b5a57e699c7aa519e","impliedFormat":99},{"version":"dafc53212e800bc9bbfed7f3a7732ba8f401516b2bc0c71b2f601ae2583a007f","impliedFormat":99},{"version":"940e51654c3c1967f34160a9674e3bf1dc436a5d36c5d7833718aea235e52fda","impliedFormat":99},{"version":"16f2023402fd0a4eeac99edb5d75d3dd8cb4b2f25f46e9bcdaf0c0bd9670e77b","impliedFormat":99},{"version":"9d7765384806b08a522ff85c20184667eec635fdb809736184da23e89533dabd","impliedFormat":99},{"version":"d53593a008e289638eac5b0a0dfbd4296233e395205831367992a87e81eda13b","impliedFormat":99},{"version":"aa0981acabb92a87323aa1579664c293a968138d9377310fde29429e92febbc6","impliedFormat":99},{"version":"796d8fd55590f854e79d3f4181b54f28108e90118314c858726163bb9961e7ae","impliedFormat":99},{"version":"b37e4e4f8f34745d839c334991d9cf227c34c2ed7fb3b297011ddfddf3ac7d68","impliedFormat":99},{"version":"ef55aaa329259ffcb1694dc5d0d688f05e5a37eec2ae34510ef751e9d608b90d","impliedFormat":99},{"version":"264b53b60d27b252258cca58f80b81e143b6299a866402819d5524fd20febd0c","impliedFormat":99},{"version":"714456dfe665ce8b398af312b56b68a927a8a182f8e78dd7c1ef5cfb596ade25","impliedFormat":99},{"version":"27c9ce7c539db9b37ec0d7476b4e9d9ba7439dc41549e466aeadde43746e8390","impliedFormat":99},{"version":"903e813fb2d906d278ab54626f4ade4f43f96f4e636dc66f5aced69d1afb871b","impliedFormat":99},{"version":"c80bc9ee4fa024302308d14084c0f6c3026301db9abbf6789e6b1caf686ce35c","impliedFormat":99},{"version":"8cd470e7936934cb17c70c18a2e03282980d8d047ec08467925a31bf99ec1bf1","impliedFormat":99},{"version":"c09f5d7d8cdee279972790105f90d6adbfb18efb905cf04815ac59d033f7bb7f","impliedFormat":99},{"version":"e92673d9d3c39fff66b14270f144fd32d2ec6fe92e8b2c51e65bd7b4a0e5f355","impliedFormat":99},{"version":"d465455e9f29288b7c879ecd390256571ba306f8b947698f03b1429d6300ff67","impliedFormat":99},{"version":"108b9e022f7dddd5e5ed8165170d65b752fa7b21ced5dd1005ffad3c36242c57","impliedFormat":99},{"version":"1890b77d7c36efdd18174e345b295ece38e66179dae192fad21e8c3642b993a1","impliedFormat":99},{"version":"636f9c9b34b3f33b2258704da1187e271fbf36081a8e22da97be5b53488a9863","impliedFormat":99},{"version":"fa6693c8ad74ce099f2a93ca8d1b0a643dd7f6026f41ba4b244d440d8dd07f03","impliedFormat":99},{"version":"fd76be177303d35dbd29c11de5f935f5d21ad605d34aa4aad9e309ec494b51a2","impliedFormat":99},{"version":"f31af014cf064d7cea0392f02595f09d8cd4b9d06c7794397cf3ddce13111d81","impliedFormat":99},{"version":"d15de8944d6dfb1c8fab88ed1d56947c4ae438b9fcbd9be18f7840b78c9c3bbd","impliedFormat":99},{"version":"040fd90833b34b59436ca6545a00a3b5988f5a95e6cce0a378ddd66bd2cf44f2","impliedFormat":99},{"version":"f332d07979b46f12410417a97153271e1bf5ea11677423718c59010df71a3f2d","impliedFormat":99},{"version":"06911ddbb7160760c75015d2d6fa0f1c0f94d9f0d61265b2d211238b571a3ff2","impliedFormat":99},{"version":"af0612a0e9b7efc543168628fe60a8d3f4d7ae8d97fe257788cb60bdac2459c3","impliedFormat":99},{"version":"9dd05d844e6b99e0a3c8ab8e37bac8f6297d531a844af0738f9b1eaf4aead087","impliedFormat":99},{"version":"5f7be41a9ceed0632c19b7cdb5ad9e07ac19093cbe23a738fe0f1c8c2f27b036","impliedFormat":99},{"version":"b33e84f2148cc81a9afa6d4177a27a1d246fabea3c0cf391aebd3e62eec04f4f","impliedFormat":99},{"version":"5dd273430ddfd576316532f118feafc41f18d5128d7d84e674d98f4a57107384","impliedFormat":99},{"version":"a9c40d74fab8e810c62cfea99a21d09f529fe6a0e60c39353510974c33df980d","impliedFormat":99},{"version":"2665ad2e88b3633b417e176af058b1c20bf5645327a8c4fd4f08e35636b72f9d","impliedFormat":99},{"version":"2321ad799e7ff9c6c6a886dea5ab208d08072a8d33da312f1b9a10ebc888765d","impliedFormat":99},{"version":"8e2f56264cfd71093034fadc1c788d6f46d58036a57e7189e8eda9a7f87eb9d9","impliedFormat":99},{"version":"06deb0a45f5a6dd23244cae8f1ebfa2400ec7de804980f044316d2d9d35a6ce5","impliedFormat":99},{"version":"b27242dd3af2a5548d0c7231db7da63d6373636d6c4e72d9b616adaa2acef7e1","impliedFormat":99},{"version":"e0ee7ba0571b83c53a3d6ec761cf391e7128d8f8f590f8832c28661b73c21b68","impliedFormat":99},{"version":"072bfd97fc61c894ef260723f43a416d49ebd8b703696f647c8322671c598873","impliedFormat":99},{"version":"e70875232f5d5528f1650dd6f5c94a5bed344ecf04bdbb998f7f78a3c1317d02","impliedFormat":99},{"version":"09b103d94e6bf3723cc3642b164dcae50bea1d1f0ab1f5cccc38dfed3fb2beda","impliedFormat":99},{"version":"e3c2cc25f470bea78afb3af5ca6f30759cfd44e4b1212e84657fb36e45a3a1d3","signature":"6e0fa1db91df6484a033340dfafa435f2e98844b66e876a01f963ff84a878646"},{"version":"16bed64f9c23abdf4e18425ab522343d5c8f180214832e5b6d40135d838f7841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5e6e6f01cb776da72cb3ca11adf3fa20c16d0f68841c2f550e485376491fe584","signature":"981a1e9bb280cbad4485d10bfb76e890079fcc75cdf11f502bd995e1065d2616"},{"version":"2e0902468e1a220489a3f33dc82d5eff8f70521cc4ad8eb35abee7a792a14a6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca55e9c482d5da0295fc69d21ed6822af32439b9fc3b1fc55ab593deb4a83880","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a0395b4c83044d52eb3954c29d53ccba5aab9acf9765dbe663f8f95783629609","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abb13a376d731db2984464da46235b3dd198602a97e200bf687c9a9a2bb43593","signature":"909a9f6b4a08c0af15d0c0e3cb1f290ccda985ee205dadc0c735d3bd1467d5bf"},{"version":"1652f9e9c84699da6c0050e0818ffbd6093fb9dc67e082bfb5d3df1fc92012b2","signature":"e7ed13b72a29fe7f0c628bb06ed80ba591e327268a8acf0cb66fa1725ce5e930"},{"version":"ccb92614f79729906fc8a9f5c9c18f163c1352d2e549cc49b42c4302fa591f30","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec90498fea3cfaefc1dc5badcfa5d2c8f05a73f96abb856d63707c0cd25351eb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eedbc489481913977cc00e055bd0f493c8ed3115928ca006929d1b353411e722","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1635afb57a6aa4a470f3b256348826c36e42594db09683647dcd00391b2521b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"73a98325658b7d7509ec766ae0f0b34b9a4d9819b388d4e5b3c74ef8af5af185","signature":"211206a1cc64d3623a54c919fe8e7e8cb70032936ce7dd2625ba2f185580334a"},{"version":"8072581b3b7e9ce43d9553465431ebc422579042d0a644394d018c6803c45918","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"938165e53c76725415bd8152fc8363d628ac4a95e04d4c2884f75349e3d337a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c868f50837eedd81fa9f61bd42de6665f74e7eb7a459135c6a14ac33ddc86798","impliedFormat":1},{"version":"42cf6b642a67b27545981d06932f7e5ef948a68dadf5779cdfa9e052e3a13d76","signature":"41302973852bac2a0d545eb886ea0b819803722d9d6344a011477d235854894a"},{"version":"cb61a5aafcdee23a7ccf20343670924ee6cf6ec6f631b65a3ab249e27d9db542","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d2e1e7f9ea69da6734503f8b7077edde2e9fc91596141725e2beffba76ea2ec3","signature":"0f87709207a3c70d4c4dd8ca7a866e5114b412c6629abcd9f4bac4a7b91495e1"},{"version":"1e3bd35220cea102b5a84d579f9bb1adf4dc20dea714829473bb3aa87499a64d","signature":"230d47db97c6f501ec507c267dbecfcf25a3a8c8c13854734008f4294a0da41e"},{"version":"ad3e839b384c5231de4906ea0d62e778d95f1e46937d9c005487b0897ffc48f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebe9b7b5b1909551f7fe8a5aedab9f4c713b928f5ffeb7b83c9ac876861a74fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dae983fc2e940a628dd197d10e67ca9cdaa071d87d7018ceb8fa5c8a690eccf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eeb5551958a1e9c5493e02cc7a0eaa112e946b7590a018f1bec0e29de91a64de","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d6fef48c89f7fc4804cd6056fa0e5495a24ff66773aecbce8fc3445564796888","signature":"a10492e4eb6f7d58638216495d74d70dfe714fc6977cfa5510ffb772d56a0c79"},{"version":"eaea9c64fced041f983824a099b8212dca0270f05c328053ab935b9a7546f0ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"29e02239f94241d9f26f19f570a5eb688c86873d1e77e43868fd69f6a38e771d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"681ab80103a45e835b91035d733228aa210d75cb0cd45355dbe6e72fcbe1806a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cfc4d1e3e0f3fa0a4a3de8483598fe4ca1f9677de760b092b4919748ca383fff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aeda61f5b4aa7621f64d34f54d63b5d05051ad117ff705e19069b5ede8e50a16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"ac450542cbfd50a4d7bf0f3ec8aeedb9e95791ecc6f2b2b19367696bd303e8c6","impliedFormat":1},{"version":"8a190298d0ff502ad1c7294ba6b0abb3a290fc905b3a00603016a97c363a4c7a","impliedFormat":1},{"version":"5ba4a4a1f9fae0550de86889fb06cd997c8406795d85647cbcd992245625680c","impliedFormat":1},{"version":"57c98d540df72bdc219a9d4e23e7c1f844459414e4e62eda3439067fadf743ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef4a6c603a685a8786368a221e0fbabdc221135d6b59eb1ab442a1e0d260da42","signature":"b5744d0126cbd200c1bef21bac2f14fa3223bafc24178f26d27d834c9b4ce7af"},{"version":"723d1d05be7e263d358580c9bba607944fdf6e5093e7bf62a2f578754b779390","impliedFormat":99},{"version":"7a59476a46fd4b3e1522e9c6ec6cf436b6d5ab8ac97a17ae867aeb9cdf0371ff","signature":"57f1ad6cd433ecc0e78e4616e780d4db68642604162b65747c70a6142d28e49b"},{"version":"ff3e228e751934dd42a9f05cfd75bccfedfb529eda504ee0c4f0d184da345050","signature":"4a1201a691800bf407a2703017b769c5ce1a53418279b7682e4cde1afc7dc6d9","impliedFormat":99},{"version":"ee70ae40394baf9312c35363c42fa429ba3e037ab10cf767a184ec38d24b5427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b5b7c87e72f384980ca1d92c4f54d6c30b2f099556e3843588073cfe0a0a893f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f329dfad7970297cbf07ddc8fce2ad4a24e2a3855917c661922ef86eb24dd1f1","impliedFormat":1},{"version":"841784cfa9046a2b3e453d638ea5c3e53680eb8225a45db1c13813f6ea4095e5","affectsGlobalScope":true,"impliedFormat":1},{"version":"646ef1cff0ec3cf8e96adb1848357788f244b217345944c2be2942a62764b771","impliedFormat":1},{"version":"c864801e02e8547ed49024b3a469d6fbf600ee240be6bf413bd6149f26241348","signature":"90ec9100c29e008c3d9194acd818e2cfa6dc6e177154bc8e10c5959aa35619ed"},{"version":"6c8c958cc35f90494284a36edeedf503f3a56a93960016a618a1e587d19d86c4","signature":"2b02e2635e94d92d8a4c1fb05177aa1f9bee04c362dc8600559080aafe963e14","impliedFormat":99},{"version":"9c947051913ac9feed2de4ec57656a9f38ef4bccd22518b765f5877c69894082","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0260838d2473bd7872f0fcef24bdfebc247cdf5c95217670ef50931bf93f2e91","signature":"2affb08b140b8e89210e4b39ed75b00cd5e5ccc3553a80bb3e83514fd2461e7b","impliedFormat":99},{"version":"e9e4ac4ee6a2c612f408e17bfd9bd5398bab08053196d8e8c6cf64d8a7335a51","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15128feed70d09b1e4f994cee399f093af7c7c42e224db77f3ace502a457a2f2","signature":"c7108b0b3c30b5aa5fe1fb0c2399dbe7da3e7730cfdd42e7403a0402394bf466","impliedFormat":99},{"version":"3bbf19210a7e08f50ce1518710ba0ffa8e13a0d55d78fdf3cb62cbad44d30e1b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca273ec7d7789662ab7ae9e00a4556a0e42416d6f8a13702a5746b5ea6862061","signature":"dc89f83d1e61d147d010a811cad4539c273b3ed227aabfa8a9a130b4180d2cd0","impliedFormat":99},{"version":"2e71945b350a81ff50fd4e21a3660e7e6055a5cd5691d6d8d867d0e6f10cf313","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5d0e499d96f070dc2607f23d55fe54ac074d1a840b6505e1978f70f57232cf7b","signature":"9e4d212471d83031de81b7c76834be81b4d32b5eb573cda6c61023d1cd5f326f","impliedFormat":99},{"version":"f20e59aa1f8ad6e7dbfb10f7c7147773dab8b5d8e4d59eeeca34944b51e4dd14","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"84daf1ace1a44a36500dd4dbedc8a92e50c4a1c1e935ef732dd18b7e2fb0aaf7","signature":"5cbfab9a555788720d027df70fa580bd727ad40aa2d325eb0b04ec4642f9faf8","impliedFormat":99},{"version":"1784d27f3095418bde9b61739c7ca7bd30b1bf05c95bde803514bfe48ce23f57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e62fecd6655ce82858142ac7225caded25ac9b7da81632bec4c7c054983bfc68","signature":"2bcc2d03633b291af104b7774e1f7da0ba4dd09809fceb39db956b6a7e127ae7"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"3255b97f3f24af29c79cc1aa88004efb13b6285ebdde0a567bf32e19bb65250d","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"8589487932fd916840218fdedbb143741d22216ddf630c70d401ee674c448e1e","impliedFormat":99},{"version":"c0fc68a185e7479c68bb3304bf208d87e9d8bbe9a684302d06c40245670cabf1","signature":"42288bb7189ed22d6ecbacd5477ddce0e5ae1fbc1dfe48b1038c58af794199dc"},{"version":"31caf28f1dc08edfe3cb8e7c4011ced09f4bf3f5f725c50e5dc0b5ae573b498a","signature":"04a996928d0f8d5efd87a2990c4f4ce70e00fd0c975971fcbc570df7961daee5"},{"version":"502c011687aee1a48fa221d356f8f2d8eeb035c0706e8f8e9ef0104660cfc51d","signature":"1a734856e43cee0599e8a537f131cbaa1e9290b47f2b496fb504f95e252b8495"},{"version":"1a7163e59864fbaa14672752a70c8b38086117e5a14afa00893611dfc2fa803c","signature":"01a977ade994fe0de990222140f158a0dc3b03529994c449aa39333d0facac02"},{"version":"348b8169a6c19556863ffe85bf1fe1ddb0006affc951bee6eeb7dcb3a2d6eb30","signature":"f12359b22cbaca86f938ddee38c0c33924e768a93042ad939fc2288f2471e5e9"},{"version":"43ee1831235987ca593e76b22b4116009f1ff6fb0e7a3fa6bf1e5df1420fd6dc","signature":"a05af3719b211bbf59b553f0760633dc3095778bb0171502d7bb7342a54d3b15"},{"version":"b2e86ee920713d5b287b9b175ebad35f82e979a456a9b276fa16bd4df708bf20","signature":"138cf0cf601045c4741faaf79f2d7024eca30e59e3b533a4d60e01a99209ad68"},{"version":"ddd3e5c54eaf7ed7710f0b95669f32c7b1693bb3289a4d2d559d03de0c62ea39","signature":"a53ef0141acd28c738153d5b7b9c637142cab2fe0db1dd0d6a55b06aac99925e"},{"version":"45311c218ffe1c8393be29ebab04527a9167c2e48a5fdb15adc0f22cd541614f","signature":"dfb3bb27e47ca92752033b3171dbe6a1f8e9404b34577d1b16eac221e1745a2a"},{"version":"093616375ac2af574eac9fdfcd18193c3f9394e1b1d4d8c79d2e6068790ac100","signature":"335f66607c072668fef3e0563ad5619a7632e75eef9f85740e84e35523d1ee82"},{"version":"383fc1e3823bc2d2cccdbf51be644b7f2297d6d04190008c1ef7ccf82eed9b76","signature":"77658513755ac8d8ad639e6f969539b6d98cdc9ea85a2eabeb33fc94a839f395"},{"version":"eb2ca2e825628da3e61a98cbf8338f9af1da351244346ee4955b09972d60e7c3","signature":"0a759888cb435532132e0066d5bac2f2786bc7160a24f07e7b60ee958d45b88d"},{"version":"2aff6fc2089561043f6978a98e05783fd6fea921b6068a4b3dd36e824221cf0b","signature":"0f2ab2d398a5484d267cfbae7f4512671debe1dfc0056d474a6d6add63a148b6"},{"version":"a3e0707d4c2d3cdc9afc01b85b6503f0a9d52d5910ac5766dca6236f401cd6e5","signature":"257d9dcd4c3e61e552ab3f34ead65de21367da6f92f9d62979a26fd748982849"},{"version":"6b43dfa5e9c9d89bcaca0ffe7da88f34e20d760ca158398a3276cef61f738c4c","signature":"410aae1dab008177682aafcfbeeb27bb71cf90e2644309d0473a9f4840d460b3"},{"version":"4bbaca1506dfa9d1c4075c7c10b707c2349f2017f751e32e6bbbca1f7e11e2c2","signature":"511d2ed9fee12f9ca9d9cb4bcbe6b0ed455f5da5473733413da9b4d10d916a79"},{"version":"63d9dc36da9bc05dfdb5ccf23b5738648c073c545320dbb619c6b0b27ce304b3","signature":"859b36849fa1a6871f9dc68605252132a625792e315c5e58d893b28aff84c7c5"},{"version":"7f35d5d9a42febaab1e7079785cfae8c9f977d777dfe871730f0a360c254a356","signature":"0cc24adad526e7c075f3223582ca642a555751bddc0088d47a1fe62ac19ebe31"},{"version":"bc7bc237e289f8d435d34601a22322d303d64d497e25d80d555f06f7acc34e4b","signature":"7da246bb1c2b2ce4879114715c5bd7714bef80824031c70e814efa143acfdd51"},{"version":"38a9567883eed6a8174f841b784734b949c25fa1f913d1b96eada6bc083c160b","signature":"f4ad04f8b4b9e4ef78fa12961f2c36f0032035ccbac259ca95cc05fecb70d3ad"},{"version":"9a7b469bc32fae75951dc069e760b7945d91829873247f00a5ede47eddfc5d2d","signature":"acaee283946e562a6a4f999558a47c3d5110e5e2ae0581f90b5d2d4e35dd74cf"},{"version":"5b8eb6e16859a5d0b869e2607f6510cbdb93ff3b24942edfb5098f2e6b07e773","signature":"03b23eb17ac097b361cad4f90128b223cfc584893f86a350ad9337aff15890bb"},{"version":"983793b81b9d3f63b32a2b4aed4cbecdd215d0c00487729c5ee788f9d8a77c13","signature":"afd02efecb9f6288c3098659c94182a2d6fcde4620ebda7c2aa229cc5d2c54b1"},{"version":"89121c1bf2990f5219bfd802a3e7fc557de447c62058d6af68d6b6348d64499a","impliedFormat":1},{"version":"79b4369233a12c6fa4a07301ecb7085802c98f3a77cf9ab97eee27e1656f82e6","impliedFormat":1},{"version":"2b37ba54ec067598bf912d56fcb81f6d8ad86a045c757e79440bdef97b52fe1b","impliedFormat":99},{"version":"1bc9dd465634109668661f998485a32da369755d9f32b5a55ed64a525566c94b","impliedFormat":99},{"version":"5702b3c2f5d248290ed99419d77ca1cc3e6c29db5847172377659c50e6303768","impliedFormat":99},{"version":"9764b2eb5b4fc0b8951468fb3dbd6cd922d7752343ef5fbf1a7cd3dfcd54a75e","impliedFormat":99},{"version":"1fc2d3fe8f31c52c802c4dee6c0157c5a1d1f6be44ece83c49174e316cf931ad","impliedFormat":99},{"version":"dc4aae103a0c812121d9db1f7a5ea98231801ed405bf577d1c9c46a893177e36","impliedFormat":99},{"version":"106d3f40907ba68d2ad8ce143a68358bad476e1cc4a5c710c11c7dbaac878308","impliedFormat":99},{"version":"42ad582d92b058b88570d5be95393cf0a6c09a29ba9aa44609465b41d39d2534","impliedFormat":99},{"version":"36e051a1e0d2f2a808dbb164d846be09b5d98e8b782b37922a3b75f57ee66698","impliedFormat":99},{"version":"d4a22007b481fe2a2e6bfd3a42c00cd62d41edb36d30fc4697df2692e9891fc8","impliedFormat":1},{"version":"9d62e577adb05f5aafed137e747b3a1b26f8dce7b20f350d22f6fb3255a3c0ed","impliedFormat":99},{"version":"7ed92bcef308af6e3925b3b61c83ad6157a03ff15c7412cf325f24042fe5d363","impliedFormat":99},{"version":"3da9062d0c762c002b7ab88187d72e1978c0224db61832221edc8f4eb0b54414","impliedFormat":99},{"version":"84dbf6af43b0b5ad42c01e332fddf4c690038248140d7c4ccb74a424e9226d4d","impliedFormat":99},{"version":"00884fc0ea3731a9ffecffcde8b32e181b20e1039977a8ae93ae5bce3ab3d245","impliedFormat":99},{"version":"0bd8b6493d9bf244afe133ccb52d32d293de8d08d15437cca2089beed5f5a6b5","impliedFormat":99},{"version":"7fc3099c95752c6e7b0ea215915464c7203e835fcd6878210f2ce4f0dcbbfe67","impliedFormat":99},{"version":"83b5499dbc74ee1add93aef162f7d44b769dcef3a74afb5f80c70f9a5ce77cc0","impliedFormat":99},{"version":"8bf8b772b38fc4da471248320f49a2219c363a9669938c720e0e0a5a2531eabf","impliedFormat":99},{"version":"7da6e8c98eacf084c961e039255f7ebb9d97a43377e7eee2695cb77fec640c66","impliedFormat":99},{"version":"0b5b064c5145a48cd3e2a5d9528c63f49bac55aa4bc5f5b4e68a160066401375","impliedFormat":99},{"version":"702ff40d28906c05d9d60b23e646c2577ad1cc7cd177d5c0791255a2eab13c07","impliedFormat":99},{"version":"49ff0f30d6e757d865ae0b422103f42737234e624815eee2b7f523240aa0c8f8","impliedFormat":99},{"version":"0389aacf0ffd49a877a46814a21a4770f33fc33e99951a1584de866c8e971993","impliedFormat":99},{"version":"5cb7a51cf151c1056b61f078cf80b811e19787d1f29a33a2a6e4bf00334bbc10","impliedFormat":99},{"version":"215aa8915d707f97ad511b7abbf7eda51d3a7048e9a656955cf0dda767ae7db0","impliedFormat":99},{"version":"0d689a717fbef83da07ab4de33f83db5cbcec9bc4e3b04edb106c538a50a0210","impliedFormat":99},{"version":"d00bc73e8d1f4137f2f6238bb3aa2bbdad8573658cc95920e2cdfa7ad491a8d8","impliedFormat":99},{"version":"e3667aa9f5245d1a99fb4a2a1ac48daf1429040c29cc0d262e3843f9ae3b9d65","impliedFormat":99},{"version":"08c0f3222b50ec2b534be1a59392660102549129246425d33ec43f35aa051dc6","impliedFormat":99},{"version":"612fb780f312e6bb3c40f3cb2b827ea7455b922198f651c799d844fdd44cf2e9","impliedFormat":99},{"version":"bcd98e8f44bc76e4fcb41e4b1a8bab648161a942653a3d1f261775a891d258de","impliedFormat":99},{"version":"5abaa19aa91bb4f63ea58154ada5d021e33b1f39aa026ca56eb95f13b12c497a","impliedFormat":99},{"version":"356a18b0c50f297fee148f4a2c64b0affd352cbd6f21c7b6bfa569d30622c693","impliedFormat":99},{"version":"5876027679fd5257b92eb55d62efee634358012b9f25c5711ad02b918e52c837","impliedFormat":99},{"version":"f5622423ee5642dcf2b92d71b37967b458e8df3cf90b468675ff9fddaa532a0f","impliedFormat":99},{"version":"70265bc75baf24ec0d61f12517b91ea711732b9c349fceef71a446c4ff4a247a","impliedFormat":99},{"version":"41a4b2454b2d3a13b4fc4ec57d6a0a639127369f87da8f28037943019705d619","impliedFormat":99},{"version":"e9b82ac7186490d18dffaafda695f5d975dfee549096c0bf883387a8b6c3ab5a","impliedFormat":99},{"version":"eed9b5f5a6998abe0b408db4b8847a46eb401c9924ddc5b24b1cede3ebf4ee8c","impliedFormat":99},{"version":"dc61004e63576b5e75a20c5511be2cdbddfdbcdff51412a4e7ffe03f04d17319","impliedFormat":99},{"version":"323b34e5a8d37116883230d26bc7bc09d42417038fc35244660d3b008292577b","impliedFormat":99},{"version":"a5dbd4c9941b614526619bad31047ddd5f504ec4cdad88d6117b549faef34dd3","impliedFormat":99},{"version":"e87873f06fa094e76ac439c7756b264f3c76a41deb8bc7d39c1d30e0f03ef547","impliedFormat":99},{"version":"488861dc4f870c77c2f2f72c1f27a63fa2e81106f308e3fc345581938928f925","impliedFormat":99},{"version":"eff73acfacda1d3e62bb3cb5bc7200bb0257ea0c8857ce45b3fee5bfec38ad12","impliedFormat":99},{"version":"aff4ac6e11917a051b91edbb9a18735fe56bcfd8b1802ea9dbfb394ad8f6ce8e","impliedFormat":99},{"version":"1f68aed2648740ac69c6634c112fcaae4252fbae11379d6eabee09c0fbf00286","impliedFormat":99},{"version":"5e7c2eff249b4a86fb31e6b15e4353c3ddd5c8aefc253f4c3e4d9caeb4a739d4","impliedFormat":99},{"version":"14c8d1819e24a0ccb0aa64f85c61a6436c403eaf44c0e733cdaf1780fed5ec9f","impliedFormat":99},{"version":"d36518bd617ff673c7d9f372706f241932a43f27673187f2a8472e93c40041c6","impliedFormat":99},{"version":"f8eb2909590ec619643841ead2fc4b4b183fbd859848ef051295d35fef9d8469","impliedFormat":99},{"version":"fe784567dd721417e2c4c7c1d7306f4b8611a4f232f5b7ce734382cf34b417d2","impliedFormat":99},{"version":"45d1e8fb4fd3e265b15f5a77866a8e21870eae4c69c473c33289a4b971e93704","impliedFormat":99},{"version":"cd40919f70c875ca07ecc5431cc740e366c008bcbe08ba14b8c78353fb4680df","impliedFormat":99},{"version":"ddfd9196f1f83997873bbe958ce99123f11b062f8309fc09d9c9667b2c284391","impliedFormat":99},{"version":"2999ba314a310f6a333199848166d008d088c6e36d090cbdcc69db67d8ae3154","impliedFormat":99},{"version":"62c1e573cd595d3204dfc02b96eba623020b181d2aa3ce6a33e030bc83bebb41","impliedFormat":99},{"version":"ca1616999d6ded0160fea978088a57df492b6c3f8c457a5879837a7e68d69033","impliedFormat":99},{"version":"835e3d95251bbc48918bb874768c13b8986b87ea60471ad8eceb6e38ddd8845e","impliedFormat":99},{"version":"de54e18f04dbcc892a4b4241b9e4c233cfce9be02ac5f43a631bbc25f479cd84","impliedFormat":99},{"version":"453fb9934e71eb8b52347e581b36c01d7751121a75a5cd1a96e3237e3fd9fc7e","impliedFormat":99},{"version":"bc1a1d0eba489e3eb5c2a4aa8cd986c700692b07a76a60b73a3c31e52c7ef983","impliedFormat":99},{"version":"4098e612efd242b5e203c5c0b9afbf7473209905ab2830598be5c7b3942643d0","impliedFormat":99},{"version":"28410cfb9a798bd7d0327fbf0afd4c4038799b1d6a3f86116dc972e31156b6d2","impliedFormat":99},{"version":"514ae9be6724e2164eb38f2a903ef56cf1d0e6ddb62d0d40f155f32d1317c116","impliedFormat":99},{"version":"970e5e94a9071fd5b5c41e2710c0ef7d73e7f7732911681592669e3f7bd06308","impliedFormat":99},{"version":"491fb8b0e0aef777cec1339cb8f5a1a599ed4973ee22a2f02812dd0f48bd78c1","impliedFormat":99},{"version":"6acf0b3018881977d2cfe4382ac3e3db7e103904c4b634be908f1ade06eb302d","impliedFormat":99},{"version":"2dbb2e03b4b7f6524ad5683e7b5aa2e6aef9c83cab1678afd8467fde6d5a3a92","impliedFormat":99},{"version":"135b12824cd5e495ea0a8f7e29aba52e1adb4581bb1e279fb179304ba60c0a44","impliedFormat":99},{"version":"e4c784392051f4bbb80304d3a909da18c98bc58b093456a09b3e3a1b7b10937f","impliedFormat":99},{"version":"2e87c3480512f057f2e7f44f6498b7e3677196e84e0884618fc9e8b6d6228bed","impliedFormat":99},{"version":"66984309d771b6b085e3369227077da237b40e798570f0a2ddbfea383db39812","impliedFormat":99},{"version":"e41be8943835ad083a4f8a558bd2a89b7fe39619ed99f1880187c75e231d033e","impliedFormat":99},{"version":"260558fff7344e4985cfc78472ae58cbc2487e406d23c1ddaf4d484618ce4cfd","impliedFormat":99},{"version":"413d50bc66826f899c842524e5f50f42d45c8cb3b26fd478a62f26ac8da3d90e","impliedFormat":99},{"version":"d9083e10a491b6f8291c7265555ba0e9d599d1f76282812c399ab7639019f365","impliedFormat":99},{"version":"09de774ebab62974edad71cb3c7c6fa786a3fda2644e6473392bd4b600a9c79c","impliedFormat":99},{"version":"e8bcc823792be321f581fcdd8d0f2639d417894e67604d884c38b699284a1a2a","impliedFormat":99},{"version":"7c99839c518dcf5ab8a741a97c190f0703c0a71e30c6d44f0b7921b0deec9f67","impliedFormat":99},{"version":"44c14e4da99cd71f9fe4e415756585cec74b9e7dc47478a837d5bedfb7db1e04","impliedFormat":99},{"version":"1f46ee2b76d9ae1159deb43d14279d04bcebcb9b75de4012b14b1f7486e36f82","impliedFormat":99},{"version":"2838028b54b421306639f4419606306b940a5c5fcc5bc485954cbb0ab84d90f4","impliedFormat":99},{"version":"7116e0399952e03afe9749a77ceaca29b0e1950989375066a9ddc9cb0b7dd252","impliedFormat":99},{"version":"3654ba818fbf4ac2c49aa3dbb050b912277acc71b6d5e4f434720c27a1a68f3d","signature":"02d33dd7ec31c9ac3c91582f2d0a3f665d587d5f98aa667ad74d4b543e626610"},{"version":"6f3c420a59fe4ff53cc42fb1aa2b2702fb048145fcb3c26515d3db35e3854e2d","signature":"1f71e9c9d089eec515e086adb2e10e09414ae876ef4744115edfcd57c6684f7f"},{"version":"2a7a18a2cc9b4656d9eb1d5f4fd0e3f3466f600c32ea8148643dd8c909bb3476","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d9cb0facf05859f0f35707063253d8b55d8fbb565afb642c0edbd72ce77817e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c8924b198de81de4222b2f0b171e9262f80bdf62beaabdf8ee7aa13b27245871","signature":"2f5adff38c8a75301b364bad4bd26f79cd3a86bbdd3cbba4541673d903d47b4f"},{"version":"0702499aa6384244a89e13df162163ed41949de76518a8580c8044e783876dea","signature":"401f1208590180b74cc9007c8a894d499d48b469bb110c769cb004aff4819b3c"},{"version":"534cced4db5dcc639cd555583be09c6891c0633dc395308c87f60b47dd54a6b2","signature":"33ecf206edccc488e96cfb5177f19809e8bbb549ed0e94ff66d1cd1ff1a1fcb3"},{"version":"9f4f376e778fd1560de1f3afa4b8ba1971bb8bc5f272324ea61e65e15b685f1c","signature":"6b211c08718dabbbcb8d48382a8416b20d9c90e3a7a3a9f8dbb192baa29018dc"},{"version":"b16a9573271f151e37a10543a8faffe67811ac8570d87054108ba01799b73ba9","signature":"c274af3f97f26f9143c42701bf431c06ff0af56cd5b14e86c661a294f335d8db"},{"version":"48f96604f28e1d321ea8c94e7e5cc889f4ab3720d92ed9f412ac7dbc2931a1d9","signature":"0d095606a67e17da85041e7a56c4d15c377ff643b56ca69eef8b42d670748bb2"},{"version":"1d899a3b3c762069c87a2363e38fc467d3fc0c17f6d22f98de3a98e1a691540d","signature":"57bf2ffbbff6d58bb1422d725989e22ba10b14159c98d2e3185361f5d609d9f7"},{"version":"3f56d8959b17508732d17ec607714398e73009d9eaec652c8fb9d5891d1c7c7e","signature":"b4f0b3be4ce1aab443b18ffd19432c63b332180881e573f620cdf4d257b5426c"},{"version":"0429300653bc8825a3a00ba906eaa03d5b322bb198eba44726cc305936446bbc","signature":"9da79310a8073ee98ed1de785aa8283fcbee7e7481b71408c5a99cf0e601dfc9"},{"version":"11ad149a0b977ca7a0518fbe321b005ed1aee824ebe4ae74800837a4700dff2e","signature":"bfc2c8f0a629b259b1e101742e40731bf7af0e33a25c1d4de46cf85d129c212e"},{"version":"6463c4d2ab1246111f0c7be3929d40188e121fa90b62bef6468e455ecad7cb1a","signature":"69b5aa3a3485b7780595d1c9474a3103be32c05414d7409b89972d21535c60e8"},{"version":"978ac9ab1977c957fe99662e28e319fd04ebeaf373bc16fa6adfbed404f61b75","signature":"be1719b0c3e5f1de72217c7107e2106c1e3762ce3ca52d3d26db16dc36c52150"},{"version":"33c2eaf9f2216da640acf1814500bdbc10e08241fd17d8e19eaa203c50152815","signature":"155a0ce1ae5dea70b7c62b88bad1d252f860edbe6973cfb381851ae84fbc57b0"},{"version":"7c3c3e194f59da1a744d6d5c1090d144769d025c068b2f84524dbae0fd481d97","signature":"5fc8fcce3719297a5a3c0d9b41aea6db99a3fc963c76fac983b32894e6694193"},{"version":"b38b60efd27019b6a729129142cfe2e6853164174eb3513547c9c13837407e30","signature":"b4f67dedcee400ef1fcf589dc8a3f74a60f77640595d82519762104c3162223b"},{"version":"eca7b0894466635ec8350b68f82f12ad8f5e538a14a2c6a20e26be53f2775a42","signature":"036a6610a766102c1728437ec9454c572a29f908e5cbb80bcd71040c1a48cfa7"},{"version":"75b6d54e60e07ccdc53fd68958e5fb4dffa6e3cef5e13d478ac8901bbdbd814e","signature":"990169bd34d817d6b9bf57e56e3173cdde174fdcce0cbb5b5648b0aa8fc83f76"},{"version":"e1c0d6996b82a7c4eec322c0b62c75711d6f902f75a5a755b5e0cb7ead8bd72f","signature":"db1738fd62be473a0e572c91da37c7966dcca15d00dc7f9694bf78b170275ea1"},{"version":"dfb5c15f9f6428e3f92028355a9b2fff5ba76aabe338fc058f2cda201b866d8c","signature":"a507a22f915815dffd2fdb731fc8fc2d3a0b2fdee404c673c8340fda0317022b"},{"version":"aa40d71dd57a81028c76d4080716d6dde78ff51e92ad1460e5f973adbfaa193b","signature":"b8df9c14d085533e16aaa58dfa061788e5dea6b8d7e3a17ace1562e6d904cd85"},{"version":"955ae27fdc755f32aabee0f82c2db6b3d8505f99551cc8376df389eb90e7c84b","signature":"4675797b0de56fe3c5a6e468df193709c7f066a244e2da0d02690f193eed5345"},{"version":"4eb900416055b66a7063f285dc36561ccd1d276de8a637165beef04a3b3aa162","signature":"ac2b3808b01524a4b3ecc52121b04eb3b74c6d267ad7db0c4082c2934c8da0cf"},{"version":"7abfcb37b73f3b4fcca65caa3cfe40a12b8a89fafcfa783f93605acdddb0cc25","signature":"1a574fe33afec63182b358ca9e29944cbdd13c69413c53fcbb8e924018e33b8c"},{"version":"4bbc8169c9196d2768927f96e35712502c0445e653a5d427f670aac13452f77b","signature":"0112553dbd79407a27c58713ea3d744d72a100f4d89bc8c817d0a4d027bd8d34"},{"version":"6e525359ee1b437f87a241ee6e5197cbecb25d0a2a575840886e4f0ce4d27017","signature":"ca5e8662d40a41d49018ea294491f822a60db9fccbaabe8281ecc138a9e955d7"},{"version":"2344ae91463701eaa9402c073e769bd7a70557449fc13c0bc4d52ea2d95f6d6f","signature":"a87a0cc69a8844e8d64858edeb2f5b9defe9db4b03f6081c3cd27e2979067a80"},{"version":"964e2cf748eb23916c541b47ca7aa8621f6f99a9da2ee2b53cf62fafa784243a","signature":"7e126f3b1bbbca7fc0092253c0cf8cda8ecaebf818d91bb35bedc35e19f75fc8"},{"version":"e99bce1c616138462e9ad01d669d9667759a66a549795aad43cbd8d3829eabd2","signature":"bccd3d911c3cb5fb8442848a10723e7ac2fba4a94c37c8fb2700ee31645b28e4"},{"version":"88cdbc3bcb4689a70130597de7c941b2450bb2760674a02ae816e0667a1958f6","signature":"6dbaf13dab6dc2db0cb7312fba7996ca7f548c7929bb627315cc89b43bf93ada"},{"version":"93fdaea06f53eda94b236d54909091dbd7046bc96315b59d224962d4a95299da","signature":"71e0ae0ab79469ca19dcf4d5566240e5019b5a59f0aa86a6d1b5afee5edac2b7"},{"version":"5437c086fa05daccd0b205f10e71c34f7a5c65a60b70c449a77d71c547777399","signature":"6a891a6cb7835fc4aec6da5acfe7e699a7657630100a6eb7670e371e5a4d2ea4"},{"version":"9636142d048623bb863eed744dba8c1aad681a8b29574d438931ce5c3d465c2a","signature":"ba520a1e00fa71ff0e6e101beb05e138cbb2dd908976441ed96beda042500f77"},{"version":"55a9358103d4d3fc812e7226cbf54ec885ab5d274c3f0fd7c7b89138761420fe","signature":"09ac449d6431aaeea979c72f664e0179ac698d66b9dd695199bcc985f21b10b7"},{"version":"a64240132dbb4eaab110bb3733845bbfe14bff96b09fb6d46a64232c8dda2b1e","signature":"476b4071f1aac8d5027274bfece00a4fb738c3caf9cb2a033600c06edd10a0f8"},{"version":"9078a75f8fe253aef64d238341117aa3019d80988b1a6c58b49495a42b392505","signature":"4c3c517995254a3515b6df45737e5ce8e1d8debfe98dc634ba28ec324e591163"},{"version":"cf6b889353036a253a6fe8ff0db6438a559bf559d9dea64dce99840eb83b191d","signature":"56e9849331ad4578371de0e5c8b3203f930d0d06a0d62f5209e9c17e41693bbb"},{"version":"f2ca6f145f319cbda91e6d4b0a05184127040e7471ae54e22c4c13c457940bd4","signature":"c44b0bd9da5f7907a8f132f289ad93a7e7b57a9943b661612900ff609dc8ebcb"},{"version":"99a509f3f5decfd71596dc886af5c93bed0ab51a1e69855374290ecf66636dcd","signature":"4edde3cd15f3e6efd0e5d77a9d8b78997e2faf00a87f5741161b140472c267b0"},{"version":"f72683c914bd21a52511b4dfc3f25f622bd7ca5dd0c1c0dfea0c747f7466a7b2","signature":"82795623788e3260d9c6ee7f093c27b61c7257c31135e0bc833258ebfbf21f25"},{"version":"e86e3055a12f91b39baec8443daee70ec414ba65a82282c62163ddd6e968e45e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2564e83977f854fbd3ce140f2d86f6992c1332945634a2f803306596ee0bf69c","signature":"2e3b8ee6e5682bd9e7cad45bc2a5ed071302f74f8bda226965fa0693fa761f16"},{"version":"b66cadd5b2da034134f0112a8d584d76eec9e8025f23eb6b556dea5aa74fe3a3","signature":"10284337c30baf75130ecfa1c52aa566eafbbcf0391f1bbf7e21cad62835a0c9"},{"version":"3402b3070b7f9a2c6ea7f3082c2ed7f2f0d8c589badb6f3ec62044c3b7f0184c","signature":"6f3a722497ec70b05e83ac5087cc5bee72d7b19fc760554762b40072bf77fde8"},{"version":"dec924e208b541616842e85b39e55ef79d7a97062654794b62d56622a67bb974","signature":"ac6b01a79d5ff4dcd12aba55bb4ae5b0886bf9246467659a3bda4620813147bf"},{"version":"913dd719a5b5a91dfc16f29bbb6af8d1f8aa0f2b141eb4320cb2ff4f973bec35","signature":"18e3bf7eab3bfb15ddcbd0e06c36856ace9c7bb9b7a179505fd0f7a9f15b5c38"},{"version":"a4150749c6aa9db1224cefcb07931a35d19f1f8f00f4b79674f6d25c5423f181","signature":"6364708272ae524befeb1cf48d39cc0539e266b6062b8d26e89d41f02afca5fc"},{"version":"5eb87fa9a117af0d5672f63271c070d9294dc2592a8a71c7f67dda52635bb8d6","signature":"71f5409f85ed8b4c3910cdc686ac98abe30d2807197945e3844dc7bf5b9d6479"},{"version":"132f976bdb7a85c0fc4a180cb4673d199394e4b38feaddeae1d0939c90df34b1","signature":"11794a33220970f2e2b523767a9724247e154a37985d933321a00b9b31d6223e"},{"version":"2fa9260ba8c9c073651025b09d81b2da143ed8a4d7334d20a0f7f7eaca3c3ec3","signature":"5012e56859c8f84faba4532e014d0fb50542726d165cb40e5f1f5d2207e1d465"},{"version":"63d2523c2cf8126e1a3c3a11bb79e28fa66c6f816359bd57b87688afd9b3d350","signature":"5307c1d3d1cd0c5977d376721cded10dc36d16d06d1e83cace7e70cd20e49b08"},{"version":"33f4a53a232f13211090183cb571ff2fd60748eba70de4252d62cc6d7a17f1f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f2bb4ac13713c6726c29cdb23be59f025c6d9b2d9a22729187be2502a069978","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"311ae6055f6a3f6c98a79cf2f957dec6858d5ca3c4167f403ef2249775f0531e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c742723bb689a361dc0e32cdacf7f4160145254716deb013292a2f45e6f5e1ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7b6e8d32728e05107c573c5dc2b6fe9cb14332dd7c82fb530093a840d6b59dc7","signature":"22146890ab30bea45bc289ccc48192249fe1cead53510eda7d9af2b09e065189"},{"version":"7a81b4127658262d3b44f32ca2fb5589bdd370f3c971b7023ec2bf0fa80208f3","signature":"0774366c811ec1c799b0c0922d3a58dd6e81ab902ff9847ef804b1cda0b16cd4"},{"version":"0471574e07ea402de091b23741e7759f0293fc476645407c75e8807fce4d508d","signature":"d621400249ba8e7421459928f59ca558d53c61417f4d07833f994947592fba99"},{"version":"54a60dfbef03a8f34a21a1b21e6f8c6b991390b6bdca741071f0e9aa378b4610","signature":"2e695038b1f0a6040ae88a1a869e11cc466e03a7b13b526efa90b3ebfcb0068c"},{"version":"62b5adce04fcafc2bed80017c0b0edce1ec7219e0d019a8e9c6c8476906dfd80","signature":"b86d5d8bd5104f1ab29d23cd5be61bc514b8146a091257366678f5d99000a957"},{"version":"503b83a8c33ffdf3a4fae4b560df55b7e98c0722c4ea69e32b7e71427888f440","signature":"4d981d6aa5d8dc5af9b343ecb2c5f4c5a9e1e9890c31158e049df1c31bbf7a72"},{"version":"ea6437c6eda871607d5a01adf7cc5afdcd66f674509289cf2c226cc8b9734773","signature":"6ff5a08113fb520f023cd78f8c7151bcccc3824aa41cc276419d8f031e790082"},{"version":"61a5d2a3dd261b3c2b751c713d088f6548be6199705c2c9c5775d12bba1b8fcc","signature":"df57aab767d70420721669a994c9995859df1ca3188599bd5d693061c9a20367"},{"version":"24863e2f4b2b1bb3a3450294a76b5e0eea7b3a2e295f2225745da9c8592ee216","signature":"861096a3a6ca8f6ad72022664dd68b02ce3c37ff2d8f05354e1cd3fb3342b366"},{"version":"ca5c0df4cf20a1e1a7b2961248f35767785a03058ed250be5afa76c2713b202e","signature":"5f733c3a82d525121c2b95a101038ad31ca21b64d9ea2d5841bf71a2b5a931a5"},{"version":"9859454fa6df442ae16cb0ac31d0c02a0a85bac28c82b9783e8f370adb33b245","signature":"f6b87832d9447b2e9d26a9676efe78dc75cff9279ee64a499f3e4360d22f2730"},{"version":"583137ad8d520191737844c217f6e5d839105c7ec976abbccd46060ed8cf928b","signature":"213e8f64d2aee549df8047a587e27018fee7674c72407c2a191a634d8e05ae4f"},{"version":"92f78731c5130df45847dfa1a46a00a27686891e38ba51f116c586e520498ee7","signature":"5782b5f14e5b6835f9effd28f0e567b0b5e5c6901453140d43d5f88f07c9928d"},{"version":"25cc87856525e88d4007f5f84251a00b6c47b90fb435ad8459037f18ba6b8a11","signature":"55dd73018b5b47f33dddfbd384f86789d5d3a081b0bdc1fd2fd0f81e1e4287b4"},{"version":"ce0d61b977618ef61cee89091bf0bc0ac139c64da5b41080486c84f0002e755a","signature":"ca04aadfa23178ab9d04e4e66d60d149595721be7e7b6bfc49ca32bacb93ec40"},{"version":"be2f617d92b80f8cc4e567b59cae553cecfa618a81b93ffd974ee7f2a94ecdfe","signature":"bcbd39c8414cf019ff5752da5e81763bdc747423be425b5f6c7b1a6233076f92"},{"version":"727daecc99427aba51aacdcaf9649bbb8104da32adfc2f0eb01ed3347a173078","signature":"78335135acb2abe0a697de3e5fec33bc89954af4ff036f25bb26752170b1a755"},{"version":"4cd146d14339dd6298c8a257f30e55f207f32c3709deecf4234f32309c2cd23c","signature":"9168d159f9f1ad6b869e06877eb05d2b3a544fb1fdc76b4003ff52f5ede6992c"},{"version":"f016ec19b667fa817360e6f772723b590c5a507b5cbc0de0a53a9635f1846a54","signature":"1b866cda605b122b9310e604b6a6408a903636b8cdc7a0613aa9f9ebb6c6e2db"},{"version":"f264d234b8645ae1bdd723fdeb71a0314d77e06d8e7f6aabeef77c6607acd56a","signature":"411c558ddfdef650e590b3eee15829ff8efc820b6456dde9bacdaf8a19b9385e"},{"version":"a47d50bd2f57719021eb5184bc1314ce3f5837f2f78c4d25858e15c721e07ad8","signature":"516fbe6606f98a2736d92faf0b928b6f1084ed15368ba3cc8f055ebec38fb818"},{"version":"b26e8bf9c6f7701c5fb76c46235e05380573408867c4d57f68000bb3f543937a","signature":"7f80d74fc54976e64175d4796a5077a43b3cd982619d82adc5dac2a996e6a3d0"},{"version":"8b726542035580da854bccfbea23223e0fdac7df070292db0856bc04cc3989bd","signature":"c86f51169bdd99d2a52f43ff7126410a099a75cac62538f1c1f78e4fdaea824c"},{"version":"96164479311e65dfb12975f7cb97fa997328e7f94a0174377f4d6b8884e9ff83","signature":"a107421e44626e27aee78ecbdcc5e93e37b9addb0f6761d5cf2041e5c249f5b9"},{"version":"7ac8e07828dcc1a5e01fee4cc13c788dcbdce430795ff0eab6d39e7b3c095254","signature":"533c37afc84f4a66e5d320a3d8bd4d8fa4d7756da0712e42abc776962f08ce84"},{"version":"0925aa935fdf941309523ecfdc70cfd5650fddf52edee72d25f53aa303fe0f43","signature":"14b994430a17c83325fae751d73b4b91dc638fb2a13b138935416812efc5b08f"},{"version":"f71d2d69ba2857a6ba490861f2ee808e7c362499c19e5f2fd350d2e48b990d93","signature":"fbf6a03985783bd32574b3166c2a0e9fefaef80c4616e8c1a709ff554cab7be0"},{"version":"85a2c2bee88d22f7366d94f7d7b2c78f36d1d67945e7b88ea8348175cabf3a65","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c09242a97ddc30a0ed86ad6e481998869972d43d60027ea2dea569dfc4ff79d6","signature":"0fe3236fcc755ecae3aea84e78a420d59c851fc19f1623254decd6408be9747e"},{"version":"15f6b22a1a9dcb5d6ae6b4cb465b0c628f5d065489e0250ce46921de4c343df6","signature":"80366674fad0d2eb8bac45ad76aacdf3112cabf2e032fee7755a61ee0fd9914c"},{"version":"ed078b6e6e7eea82b93d3e16aecf4e5264db34569ccecf89ea244e130a0fcaed","signature":"92c9c93878f36fe51e3431455c359340aeacd788cd1f7dc1ba24faeb4fa87d3d"},{"version":"1d6bf45b076d03144b3058c0df777f1efec117c18e32e691f41bd9787514eea5","signature":"ec384f17e55f9991111747d49fc1dec792ed0ef8f3780416b3bfd79f4f2178d2"},{"version":"0192d556d58c264cc8f34e445f124160bf192380717c784cd88c27349fcce061","signature":"dfaf8ce103eb00ebc169bd1cd3e26987962b4da62bd249268a3193f0a7b9f688"},{"version":"7c4257d9e5829e9da31d3a15b30507ad7abaa83062fb7b54bf4a406217ff8dc2","signature":"3ca35b3c39d9a46ce3eba317f661fbe4fdf88afe33cb8615f00ea04adc902055"},{"version":"7c7e71e5e39435b48e0271eec28ab242ed6f1a65e740a29932cb83b9e617c83e","signature":"321ff8aac5ff81a75d851738cd323ae2ba1c54955901b7ca936485d93377bf92"},{"version":"cc60fd980e5701b006200ca499fcfc09b7ac317785fe53307bc9a50fc4bec464","signature":"7caf7749ce99278db7ce5e5cb505f29d838da91038eab7447336688cb42001b4"},{"version":"f989d2403a82ca13a448a70d7fbbf3e31542848ce822368c0a470dc148d231bf","signature":"0fc0c1e35e42c295ddd822600742ad7b6d8469daa236585225e2cb4416abc996"},{"version":"d0b7e2a5548f56597acc899917e354c348407549ded42ee13332c83b5c045bfa","signature":"f0cb4703a6fe127422dea8d27cdf77e8bd0f58b380945bade496723a537d8832"},{"version":"26828d7ec809e32ad1bfac8f039b8d2bd214a648a679cfd522f550e1a77a8b6a","signature":"3743762554f6bcdb60b48a23d63898d7c2906b9b64917b05cdec068049b72343"},{"version":"29228a2fd8fa9e03243e2af185473f8abfeb407cdbe4f72ed329bdadbdc484b8","signature":"d2315a4871f3b1af40dc6e9ecaca5a7271273bbcd91f00496b0038c3be25b671"},{"version":"84ee6c19db9aebc0f267dad9f38b59769a344e20ae762030ba2d8db629f925ce","signature":"6a8734b879bc7d5a8fbd40ea622c74e40431165154b8d043d0d54e59081c26fb"},{"version":"1314a35a2551c127f4844fb29fd49321ffaf3701afc6ed7131c90833121593aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec29995066c87e2f5fc0d6779e5be2d40ea3cae986f780cd1d007dc76767d6fd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab2f5059de5eb22a286a8bcca17d8803b1e866c115f2a9170abcc882864eff10","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc058abfc4773e645f0baf1ef95422e63b6c1d9962bb9d4c6540c12a650be499","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ac37a6d8ed49983b7045356b04ad84f58799843ea2afdc53a08f2614c11b662e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1fa13f317d3637fabb663edd46b39ccdc420e0c5a3913b7fa4e906d99497cb2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0a3ea64a69a40bbd8806c9e1a477bdf18f0ee91624c8d9c9a658617fca971a89","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ddac272aa9a5f0f716b38e2fed52714c5cbc10a6a4b60a2b5586d6a75dbd4a1b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"62fe41879cae66d14c865c973556b0e24a904d9c6445557f5414007a236ea56b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0e226d432a81a3d1447e3eef012f251f196e7002e654d35fae4342128122e36a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc39c13ff8d9df5c8ef59f0e717e3b5b4e96f7dc05859a531f1f703718d4192f","signature":"e6ec51d846f163b420d420782dd42e40aee266aeff314d141b11a0307a86fe09"},{"version":"10acfb644142d4c7da056485bd721efacd6ee61c0543c5762862a88f4ec9be94","signature":"1857ecaad23982cebb7ec28e547ecdb341d40713e95988b7f7d9da4c20f9646b"},{"version":"d337b2b575efa0ae09ab5b8bb94ca907728beb48ad4f9a43c653c247ebdf871b","signature":"be1b859329d61b0f74fe1393fd56c1542d1807ddf5a035b27a7153c471f53e32"},{"version":"9af90dcfb3df248fa3f8abf701c073fa30d6ee7b5758ba4de460594c56e4af8f","signature":"5b09eaef203954c253a646fea5d827882c557488a4ec3fd8cc50493e9ac5ef4b"},{"version":"c44bd1c97aec9b3731f94e4ca33797b718f355040fd1a3531cd1cdf72a092f98","signature":"a646dd3345b4cc02b5dae88b89ddb10adbd4b4158ad8c2a6f72bb83d0b38ab05"},{"version":"bd82c97d17c1bf3ae541b1dd8f1de5230455e9dd0128afb3694867baecea1407","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9d116a47a60bd0dfe34a66d2a4857a9a73bf2406915bd5b19bab3d1f42b8115f","signature":"6d6e3b1d30af0c368c85a34df9c95d2f1318f7080ef2e5749aa4bdaf637f073f"},{"version":"a0460c3775eae1effe1641d510f8cbd74a3b430951edbddf3b8ca9cddf732bce","signature":"ff633c25e6b6144a8904e3f82d41783e674fe44816ac76c8cc92dfdd8a9c8367"},{"version":"6aba6fd003ec6b75e94e40335a2315213295714f33e38b4164aaf7bdb2a3aae0","signature":"d17ae8ee1e9f7c65ef6f4c78ce2b6a7dd5fd1524565c12e6044ba3db661b8ed9"},{"version":"b72a531a79d4cb645c43c6782dcccedaa609b2c7efd71547a56ee74fad0c3dd0","signature":"7350f43a093be766aba20830ce8da6d5e1196d3bc17184977283e038cd281fbd"},{"version":"53cc94938d41698f1994b5de600edb7e89aa936944ce1d2955720f69be6d460b","signature":"311e004a849383cdcdf5bc484d374e5c55b8494a7a0b86f08ae78a9aa7cd0871"},{"version":"259cc7fcae5804316e63f5d00416e69fe28d9a3bae59dd20c767d714626dcd5d","signature":"6f96022250225ecbb218212131161d0ddf026fd636d134eca2e2d4a16637e9ca"},{"version":"f5c2a1cb2d8619642ba9bd687227fe3ed43787235c8e980c34aa844645728465","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6632cc5aa98bf6e8dbf04703a67620807ad7ac96c006255feaecd6d98d37eb14","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f4d43d34056d61a57ff787c29fbe5b2ef301a333ba157449ba3df4f0a45649b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3bb5a48b30edfb6665a89bf7df4c42dda63a75cfa27c8018f859f2281ea05d4e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4edf0a9027ff9279ede897f9c304c9f7e42c93170d2b2f66570698048e887ec","signature":"abe72455f516e18ed06bbb7e01ea1450572ff48cd86d4153c5853474dae5e8c1"},{"version":"d45ebed0a7af7351812afbdfe2cbfc7f88163d72bd79807532bce53cea6e9cb4","signature":"4ae59f31cbb1d8f65520a2852714b43fa800e651ddf50dc1a3e68c56d6537f9c"},{"version":"13bf5a8573fc1891a43ebea36a1ef5517d59f06c22f6e3bcacd8c4fbfdc0be76","signature":"654865d2998e7e7aa50e64fba9f1dcd717a7f378ee65b7e40311035c011e91ae"},{"version":"71722573d8814adfc67a2fcbcd61d3c4250e6c370dcbe927a6bbcd8d08471560","signature":"0e6ee02a5692f58fae9680a1c9b1dc94d3af9a97456ec14bea39bf4a9e5931ad"},{"version":"2f33f28160bfb02bedb63ddf4f6a8241cb2ff6967041643a0d7ee0909f75c3e6","signature":"e7d315801dfb219e04a94c847f0ae759b7d2b451783d38974a72e7b695436803"},{"version":"04fd50ba4fdfc24324446f14648d1c95fd08fb7c3f91b6de6a17ef503f052e36","signature":"b79d4edff2b414e35a3bd893e38505d7b8fee3cc678f7c8c06d3ded65ec13913"},{"version":"fa160d0c5713d8259b2648497fd70ba7c7b7a6602a840c574eb1c0a6f46e0454","signature":"4951a5459b063778e07d022547e89168c941ebe6bf458f07ea66f68b5f2e8de2"},{"version":"e74268ffc9270115d1d343bcbba879e819fb149e693a0e0524e1f321bd55362e","signature":"72dcdb99ca1e3ca76a476fa8bc73a89768a7404721c1ff2266d2c649bfb9e11a"},{"version":"ee82aa0ef404999ad87bb7a2baa1d75b0fd94aa2a0ff93bd673b39f7901fc37d","signature":"080b3addbb0d6625d7af627d88f46c15af2dcb962ca35a4715510d924cd470db"},{"version":"9267691f6b1c001d1ad417d316eb19e3448db243cd5eccd9e7fe1933dc80303d","signature":"a9674a62883f5e91daf466b8c3688f5bd9b54750ea57cc07a0318e56edbb9ae6"},{"version":"c256e102702b489676e3738666b34d985b2bed2835c1c6a7da638a2442ac8d88","signature":"70ab92cd22bc23f6f464975b988f1abd8fa3c78cdb14f620730d735585761f93"},{"version":"1fbf86d5c06434863bf58d1e0b464481274e989244d9553ff867d4f742ab0832","signature":"868c432b61889f1028f7b0d5ac70541268dd34a158b7def3d464c0ebc5a0306b"},{"version":"5bb0181380d7d4f24d5b59efd31845ed2835be7a0e6ee2fa113735e2d14f7be7","signature":"98b45d50fc16be69aedeac7631b365ad44e5c1c85f8c535df06f90199d43e64a"},{"version":"42f84fb7fb1bdea79ffd6b67b36c9906b21f0457783277abd39c047f053b3e42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"65c5c1e3cfa7e96ddf00b29103d558810220aeec2c5e15bb281ff6bfb7e61148","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"279e1abd50429cfe84b8dd7cb57e9684d8ca7864af5c3fcf853efcacf680830c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"09687d78bd8457aede9a7577ef5f833caf9718f2cad6020261caf32b7f237845","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dde235d0a151d2c8ea509d77e32c185b6e3b996563e29d5817af11ae5c09b9bd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9ee3a3696c5ab964b6ba7d41121d5b4d91ed7d70d2ba7cf0dbdcfaa617d19735","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4473be7e8a5b994db4e58c3d4ad7f50b91cd00c3be1c8c3a1867c0fdc08a6e54","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c22f3ec19a761c9989950f01e38fc127ef63f2c0a3300cdd0b3b54cc28dc75c1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a31abfd6a1707f3d3fa8fcd6380a7cabf458285d7190030215d8a92b0c360827","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc5ea5422be0834017b7ea3550c58d61ed1f7f976feaa321634d7fe60a0f26e3","signature":"117ec0eed14f00ef3524ba8069fbda8cbb45fde70d22b16ed255473b2108f1ce"},{"version":"8de2610573a2f40a3405defab8d036042b069cbdaecd61a2b813fd75f3a2c3ae","signature":"939b6572bef8a2c9bf87136e11498758aa328ba7dbfa32b7387ec8905cb0744a"},{"version":"22d768ed04ecd7cea3fc40851466b04fad6078e979dc2ea835646413b2a05acf","signature":"91da61e42b3cb07db395436e29d0d6569f0ee7755098753b533c9f2b20023e98"},{"version":"0ef3b705c81fb51f3b20c828fc50e9d2902644ce8343281c7a5c057da23c5f86","signature":"3c4e06cfccaf61e890399a0f86638295927ab217e0faaac5e8e7c2a830604f9d"},{"version":"b1535397a73ca6046ca08957788a4c9a745730c7b2b887e9b9bc784214f3abac","impliedFormat":1},{"version":"1dab12d45a7ab2b167b489150cc7d10043d97eadc4255bfee8d9e07697073c61","impliedFormat":1},{"version":"611c4448eee5289fb486356d96a8049ce8e10e58885608b1d218ab6000c489b3","impliedFormat":1},{"version":"5de017dece7444a2041f5f729fe5035c3e8a94065910fbd235949a25c0c5b035","impliedFormat":1},{"version":"d47961927fe421b16a444286485165f10f18c2ef7b2b32a599c6f22106cd223b","impliedFormat":1},{"version":"341672ca9475e1625c105a6a99f46e8b4f14dff977e53a828deef7b5e932638f","impliedFormat":1},{"version":"d3b5d359e0523d0b9f85016266c9a50ce9cda399aeac1b9eeecb63ba577e4d27","impliedFormat":1},{"version":"5b9f65234e953177fcc9088e69d363706ccd0696a15d254ac5787b28bdfb7cb0","impliedFormat":1},{"version":"510a5373df4110d355b3fb5c72dfd3906782aeacbb44de71ceee0f0dece36352","impliedFormat":1},{"version":"eb76f85d8a8893360da026a53b39152237aaa7f033a267009b8e590139afd7de","impliedFormat":1},{"version":"1c19f268e0f1ed1a6485ca80e0cfd4e21bdc71cb974e2ac7b04b5fce0a91482b","impliedFormat":1},{"version":"84a28d684e49bae482c89c996e8aeaabf44c0355237a3a1303749da2161a90c1","impliedFormat":1},{"version":"89c36d61bae1591a26b3c08db2af6fdd43ffaab0f96646dead5af39ff0cf44d3","impliedFormat":1},{"version":"fcd615891bdf6421c708b42a6006ed8b0cf50ca0ac2b37d66a5777d8222893ce","impliedFormat":1},{"version":"1c87dfe5efcac5c2cd5fc454fe5df66116d7dc284b6e7b70bd30c07375176b36","impliedFormat":1},{"version":"6362fcd24c5b52eb88e9cf33876abd9b066d520fc9d4c24173e58dcddcfe12d5","impliedFormat":1},{"version":"aa064f60b7e64c04a759f5806a0d82a954452300ee27566232b0cf5dad5b6ba6","impliedFormat":1},{"version":"7ffb4e58ca1b9ed5f26bed3dc0287c4abd7a2ba301ca55e2546d01a7f7f73de7","impliedFormat":1},{"version":"65a6307cc74644b8813e553b468ea7cc7a1e5c4b241db255098b35f308bfc4b5","impliedFormat":1},{"version":"bd8e8f02d1b0ebfa518f7d8b5f0db06ae260c192e211a1ef86397f4b49ee198f","impliedFormat":1},{"version":"71b32ccf8c508c2f7445b1b2c144dd7eef9434f7bfa6a92a9ebd0253a75cb54a","impliedFormat":1},{"version":"4fd8e7e446c8379cfb1f165961b1d2f984b40d73f5ad343d93e33962292ec2e0","impliedFormat":1},{"version":"45079ac211d6cfda93dd7d0e7fc1cf2e510dad5610048ef71e47328b765515be","impliedFormat":1},{"version":"7ae8f8b4f56ba486dc9561d873aae5b3ad263ffb9683c8f9ffc18d25a7fd09a4","impliedFormat":1},{"version":"e0ab56e00ef473df66b345c9d64e42823c03e84d9a679020746d23710c2f9fce","impliedFormat":1},{"version":"d99deead63d250c60b647620d1ddaf497779aef1084f85d3d0a353cbc4ea8a60","impliedFormat":1},{"version":"ba64b14db9d08613474dc7c06d8ffbcb22a00a4f9d2641b2dcf97bc91da14275","impliedFormat":1},{"version":"530197974beb0a02c5a9eb7223f03e27651422345c8c35e1a13ddc67e6365af5","impliedFormat":1},{"version":"512c43b21074254148f89bd80ae00f7126db68b4d0bd1583b77b9c8af91cc0d3","impliedFormat":1},{"version":"0bfacd36c923f059779049c6c74c00823c56386397a541fefc8d8672d26e0c42","impliedFormat":1},{"version":"19d04b82ed0dc5ba742521b6da97f22362fe40d6efa5ca5650f08381e5c939b2","impliedFormat":1},{"version":"f02ac71075b54b5c0a384dddbd773c9852dba14b4bf61ca9f1c8ba6b09101d3e","impliedFormat":1},{"version":"bbf0ae18efd0b886897a23141532d9695435c279921c24bcb86090f2466d0727","impliedFormat":1},{"version":"067670de65606b4aa07964b0269b788a7fe48026864326cd3ab5db9fc5e93120","impliedFormat":1},{"version":"7a094146e95764e687120cdb840d7e92fe9960c2168d697639ad51af7230ef5e","impliedFormat":1},{"version":"21290aaea56895f836a0f1da5e1ef89285f8c0e85dc85fd59e2b887255484a6f","impliedFormat":1},{"version":"a07254fded28555a750750f3016aa44ec8b41fbf3664b380829ed8948124bafe","impliedFormat":1},{"version":"f14fbd9ec19692009e5f2727a662f841bbe65ac098e3371eb9a4d9e6ac05bca7","impliedFormat":1},{"version":"46f640a5efe8e5d464ced887797e7855c60581c27575971493998f253931b9a3","impliedFormat":1},{"version":"cdf62cebf884c6fde74f733d7993b7e255e513d6bc1d0e76c5c745ac8df98453","impliedFormat":1},{"version":"e6dd8526d318cce4cb3e83bef3cb4bf3aa08186ddc984c4663cf7dee221d430e","impliedFormat":1},{"version":"bc79e5e54981d32d02e32014b0279f1577055b2ebee12f4d2dc6451efd823a19","impliedFormat":1},{"version":"ce9f76eceb4f35c5ecd9bf7a1a22774c8b4962c2c52e5d56a8d3581a07b392f9","impliedFormat":1},{"version":"7d390f34038ca66aef27575cffb5a25a1034df470a8f7789a9079397a359bf8b","impliedFormat":1},{"version":"18084f07f6e85e59ce11b7118163dff2e452694fffb167d9973617699405fbd1","impliedFormat":1},{"version":"6af607dd78a033679e46c1c69c126313a1485069bdec46036f0fbfe64e393979","impliedFormat":1},{"version":"44c556b0d0ede234f633da4fb95df7d6e9780007003e108e88b4969541373db1","impliedFormat":1},{"version":"ef1491fb98f7a8837af94bfff14351b28485d8b8f490987820695cedac76dc99","impliedFormat":1},{"version":"0d4ba4ad7632e46bab669c1261452a1b35b58c3b1f6a64fb456440488f9008cf","impliedFormat":1},{"version":"74a0fa488591d372a544454d6cd93bbadd09c26474595ea8afed7125692e0859","impliedFormat":1},{"version":"0a9ae72be840cc5be5b0af985997029c74e3f5bcd4237b0055096bb01241d723","impliedFormat":1},{"version":"920004608418d82d0aad39134e275a427255aaf1dafe44dca10cc432ef5ca72a","impliedFormat":1},{"version":"3ac2bd86af2bab352d126ccdde1381cd4db82e3d09a887391c5c1254790727a1","impliedFormat":1},{"version":"2efc9ad74a84d3af0e00c12769a1032b2c349430d49aadebdf710f57857c9647","impliedFormat":1},{"version":"f18cc4e4728203a0282b94fc542523dfd78967a8f160fabc920faa120688151f","impliedFormat":1},{"version":"cc609a30a3dd07d6074290dadfb49b9f0f2c09d0ae7f2fa6b41e2dae2432417b","impliedFormat":1},{"version":"c473f6bd005279b9f3a08c38986f1f0eaf1b0f9d094fec6bc66309e7504b6460","impliedFormat":1},{"version":"0043ff78e9f07cbbbb934dd80d0f5fe190437715446ec9550d1f97b74ec951ac","impliedFormat":1},{"version":"bdc013746db3189a2525e87e2da9a6681f78352ef25ae513aa5f9a75f541e0ae","impliedFormat":1},{"version":"4f567b8360c2be77e609f98efc15de3ffcdbe2a806f34a3eba1ee607c04abab6","impliedFormat":1},{"version":"615bf0ac5606a0e79312d70d4b978ac4a39b3add886b555b1b1a35472327034e","impliedFormat":1},{"version":"818e96d8e24d98dfd8fd6d9d1bbabcac082bcf5fbbe64ca2a32d006209a8ee54","impliedFormat":1},{"version":"18b0b9a38fe92aa95a40431676b2102139c5257e5635fe6a48b197e9dcb660f1","impliedFormat":1},{"version":"86b382f98cb678ff23a74fe1d940cbbf67bcd3162259e8924590ecf8ee24701e","impliedFormat":1},{"version":"aeea2c497f27ce34df29448cbe66adb0f07d3a5d210c24943d38b8026ffa6d3c","impliedFormat":1},{"version":"0fbe1a754e3da007cc2726f61bc8f89b34b466fe205b20c1e316eb240bebe9e8","impliedFormat":1},{"version":"aa2f3c289c7a3403633e411985025b79af473c0bf0fdd980b9712bd6a1705d59","impliedFormat":1},{"version":"e140d9fa025dadc4b098c54278271a032d170d09f85f16f372e4879765277af8","impliedFormat":1},{"version":"70d9e5189fd4dabc81b82cf7691d80e0abf55df5030cc7f12d57df62c72b5076","impliedFormat":1},{"version":"a96be3ed573c2a6d4c7d4e7540f1738a6e90c92f05f684f5ee2533929dd8c6b2","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":1},{"version":"137272a656222e83280287c3b6b6d949d38e6c125b48aff9e987cf584ff8eb42","impliedFormat":1},{"version":"5277b2beeb856b348af1c23ffdaccde1ec447abede6f017a0ab0362613309587","impliedFormat":1},{"version":"d4b6804b4c4cb3d65efd5dc8a672825cea7b39db98363d2d9c2608078adce5f8","impliedFormat":1},{"version":"929f67e0e7f3b3a3bcd4e17074e2e60c94b1e27a8135472a7d002a36cd640629","impliedFormat":1},{"version":"0c73536b65135298d43d1ef51dd81a6eba3b69ef0ce005db3de11365fda30a55","impliedFormat":1},{"version":"2a545aa0bc738bd0080a931ccf8d1d9486c75cbc93e154597d93f46d2f3be3b4","impliedFormat":99},{"version":"4e49c2a5cd5b413d6f345797cf2db9b1de533863cc4ab32c4de16d4866480867","signature":"4ea82f415bb35563eae553ebdd9cfc541d18967c536d1bd821f38ddf50836ec5"},{"version":"b49285ffdee55942615f0dbefbad0034203e214cb288d2cee09d3e7b011c92ac","signature":"1ce3453cdf163e11309e394025bb62220b69cd2db35e2d0fa33e14cf38efe226"},{"version":"d46d48d5ccca19b55042e2d48a773fb97d0bb9769d9f457112c8273851b84d0c","signature":"7b3fe3dc7a57dab64ad89df76681f912b6782a94c9bfd6f8db407b657c6433dc"},{"version":"565e7c8592a98903a22c5caa7be9df48b5defeb0f9dd5c95cff6cc02db46add9","signature":"19f13e301afd7de9e6c815b06b16029cb6ba524d50bebd2b381b4b5009521f72"},{"version":"6e3cc8174feee7c91df7b15357a2a608ed4389ba83455b70278f0ca5630cdfe7","signature":"1b4f6432935df03e81a8939fb7c4a6db593c5c4bb564504599aadfab1addb27d"},{"version":"a4de4b6980e14aac3f0c46ad86db25711cbcc735d7adb7720af6995fbe045eea","signature":"76bfe2b4ee9eca5bb254288b19e87b463765fd1a10b33269c4d134ad898ad9b5"},{"version":"d15c20e1a122549fe870b5fce2c85714ec80a3d3b74f2536bb8a3fcc20dc4eea","signature":"8eb5d3569824aba69bac738c173d655d8fb61b8585dc4417a04c591d8e1b26bb"},{"version":"954ba07c66ad24d7d4bb222993578083a4423c0f92a9bac4fb9e736a3d4eb813","signature":"0b4872603cdab838437f754c0ab373796accb15efe9d82e3d45782ce193369a8"},{"version":"bae8f47ccab731836cb7117c0a8609e8c02d0addd4fd4c7009e8cecd476e818e","signature":"723cbc31e62b22b09eecfc383ee07ad39e535c9f332b022fd88ee66532c124cb"},{"version":"14a5129ed9a94b8e4a84095dfbc088e5a713ecdb391ee5bd7b0a733e64d69301","signature":"d9bac9f20a21ebebbd29475f51b36635cba15ef0ac64757307d85a4bc3eecf79"},{"version":"8166c477f254219baa01afacf9e1c7f90a4afc2efde83183553b666f582fd1cc","signature":"f4c94ca77daf02588f850cb2f4b5a1ed661d547356c7b49ddb688df1d19aa9a1"},{"version":"af0cf510af3d03a0b9fe72d343822474a7fb9d983a5055e6ff3230b7b5be14af","signature":"fcc4eb2a4b4b3c403097e96ee78482251afad86a6ff172e8104717c80c1475d7"},{"version":"014f38ff04103744e6afef3477513156f3764c2b716e29dc06654ab68ee9b20e","signature":"db3297cab37c1e9cacfe2a3592be82a2a209d1dc46b80256578f8f2caa76a385"},{"version":"b46c791b78356c3de6414d22095ab43fd2963edba051f4286b09859fbade6b8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"affe446b13aec93b99f351f9ee1e431308f5cdeb18f63302cf24cbca89182e1a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b57647b19d10fc0f2b076f761caebe20f272aee93647f1bd8c9978056211a07d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a8d2915aea530942db273b35206c1352a68a651976b374cebb38c01c27dd3160","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d8f8601853f7e61892c40633650d5ab6fe8a564f1f9d6172095c6ac1d544e5c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d0fa6fb03d1d5584fcb167aad7269de2625bba64cc45c92d023558309bfe6552","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8ca0ab1ccfa1229324946caa1b9f3cde6e5bc13c68fa5423d5424920c4e71fc0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"041ab714ffa87e79e6efd79998e2d537af753b1450011b048f673d2f550ecc50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5fe27a379f724d75a3db7c7b0745f6a14140ab74934c6b0331f08c73171f9f98","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"715026154864fc6f3fcc36520f09209416001d92d9f0a244ce52e8e3c9fbf0ca","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6dfc25ef25b3a57faca3f159634d5233d8fc0bf9d77e7e71b1eb3e0a3594699a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5b228259001804ec6228a9a0ebd02b8b549529319be68558801a8d93cd50a5ea","signature":"ad332b9a10d0249b8cbce5d8d9c10f0ff8f585d909d8c4b4987437c15fdb6569"},{"version":"662f4f9aaef37a862d00552a59d1aa314f681e424eebf9576b16df78419903bb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b24944dbb9cae7dcc4282a42546e31fb53bd8a2f2cc7f8ae6c272d5924a2ba55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b1cb47f4d87126cf3f2973da87105edb404a1c98c0aef3a2a289b98fc879029","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1856c2b5c7e6167bd7869d46273e730aedb23f80c1fc013f9f018cde1ac508c9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0387f0bfbd708bda5035a03775563836aa22508d2459e017f75b415c5f6b3452","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dde98beb8bef53cee95b020cbfddc90d0012e9d98fa19595035191cd7d2cc1ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d06d3fee2b986f19cca9483a4420497ff3909f6487e229467e75e62e283161d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f78fab13e0f5ee19bf3e2ef18b5ab38a47dc60899def7a82dc05860915155308","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3eba367c1921c8f9e7f231a941cac022824cb666e652cbe754ac1e50804cb11","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2e0c7c56fd6742b25af440e2a83916cff12be55ca6c91f899f1b4fea9827a69a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0257b9ac5edeb935209106b79f9b4565fc6bdef9f2b4c5be6bed787a60ffdf1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"982ce1b9e222732ba63b4312ad1702dc81cdf4e277bea0040898e35fde086058","signature":"1af3e359f2c3a3b25e6cf0532c9b12b27c8ade0e7eb582007452ce06db289d9b"},{"version":"86e3ae5677e1c3559ed9078231e1e54111f3bff63b1adea3d14ff48d76e2ef0d","signature":"bf3ca96bc59503b3214f0618af79741f5a28de7d7ac663c13578af8a12fdc385"},{"version":"169351f1ba2dc3837650208074687baa7b7abdb18e43d3547565ac0ca05918d1","signature":"dbd0c498b5edc07924a5f7ebf0ae90efb9436dc2229792eaf175e16c03248f98"},{"version":"87bebd1c58e39741b573ee8d32b69b74d318782eace1a9a6d9f80586324b2d3a","signature":"800a7b9dc46ee24c46db2afa893e0bcf0b8c8bb1bd9b8db5361e34fa3c2aad18"},{"version":"06350267a4b707023301d6af8e0011c9403dcfc5eff31ef7cedb13d8f97c22e2","signature":"b7b254f81d9a367bcf98769a957c2f8dfef2267573a862b52ee2748647af39e1"},{"version":"f1844ef5a7fe33fe520fb2ea00ee03f96e3ed528392ca36de5f42964490a70cb","signature":"d949e121e0b71673df6140eda41341316408ac47555ba72b02b4abd3f0b6bc3e"},{"version":"eab55adf55f35c0e404ef2ed03340e5bfbfcc9f8e631c1ccb99d28686b79c60a","signature":"dd569f5b0cf0ca74aa2b1b5f2559d99655fdb41881b534f6d27e226903a24880"},{"version":"013936b36694b71563cf2eeeb8a159cfe7832432802c33a2d991b0932c81a36a","signature":"7bb19ff78f5dad94999cc2e0debb65a5ef8812b4507a7652b0b3fc455a9e8ddb"},{"version":"80ea52c65ce80ac3d8d81821de8e8675a7497210ee37b23efa79f21bd57fc86a","signature":"d96bc2df413362d899c2a26a8be1fbf15d38eb758a9d65112d7eee95611f0bb4"},{"version":"a384e31103a21be8505d837fc43ff1a3653f70ae795b4f46d1484bd9e2623301","signature":"214644d2fea678926fe214494d5b88720514df481a09f665137efc5ae653499f"},{"version":"bc2c8875db5a1430437c82f060faae49d0eab2295f7ff81c5b82279fafa8394d","signature":"5cd36275e5e2e7c71e522a445740890253664d68f28df4d62a4a13c21e3bf45b"},{"version":"ef40ec2b5dd17a51783f18514fd97931d6b673207bc23dbab5914003fb90947b","signature":"ea58ac73dbc859ec9bd2c6e497b70d04e0b87d52aff26ec74c0b2fcf0e4d548a"},{"version":"414844c14d31371280f1024fdc10ff268455384385eea30dc5ba252f3e4fbeb3","signature":"da8aa5942188ad3147f0afacf4c3f11b24942ed40114ac1a2fb9444119d69e17"},{"version":"ac002c49c6dc6a9a524d074a6f4c324cbd4c320e222eda80415d53150d3b10a3","signature":"ac68bf7e24525499431c6bf39d62b264a7708d2393d1aca05a3b8d153657b2c3"},{"version":"7516f8012b8b4fceff405a25b09facf3eea5aa640fd6bbd91c169ef0ba7119cf","signature":"49dbff2eb0425c00c48128b4ff64bc5c8ec07f8aa6fda343bfb9302a2398392a"},{"version":"61df7340e77582676d6a10c309862970af30ffbb6cb10e86b49005764fea89db","signature":"9a1fa87e956dd9e945a728ecbf0bcdf59b5bc35bd4ab98618c8f51fe319ae756"},{"version":"547ea3ada84754869bc28f5822c247c0525383c3d8805f342a512ac2ed139f0f","signature":"d3fcd7e5c042241fda26edb5e44b7987838092f2c30b9fcfd5fde8cc5af1b958"},{"version":"8758d5e30c12540491d40282af28875a47bca5b8bd5e7f3136ebffb4d57a86c7","signature":"30c7af840c72864017bd24ec10cf1173f1c643359a9feffa51f6fa141d08850c"},{"version":"2645d9404e6f5fe66144a16e69d73d297de82df97280772374e42d514a6a075d","signature":"799433a95f4bbcb14479e6fa908d6ccf8c23fc369fbd7c6b5143026e698e1156"},{"version":"43ff1f43dfaad43d87026e3a953b74f70368ec1ba49f67eb8df40164a4ba3056","signature":"4a26aafff5702c778bf7349914063554cabacbf4b74ba530aea9fd2b5c060e1b"},{"version":"c954faf290c5251991902e64a51f18bf0a99836430e50c38126a7ec753629bec","signature":"e5d7539a72d07ef9c3d686776f885111a063fc63c60c975d027b6b643970a358"},{"version":"be035cd5d01eb15b85322a205f090f64d333dc047ca1082de84837dc31c31d97","signature":"af8541ad25caf543ae81e642a69d481f7bb2d0b642df88c46a1fe8910626a935"},{"version":"4e9d357be94551ce5e2d42ed4b0ed6546e32243ef0504cb771919b1f4bbb586d","signature":"562105feb1d69fc9516ca37ffc5e5af73d1339f62eaac2693c4856b3a06f1a21"},{"version":"5dbd0527243a6d622ede33b461f27551614d1d4071c9dc1b246a7cc9db850cab","signature":"5136f18880c11778e02967105e9fae9a0482deaad8f1583230676bdb59fb7ab7"},{"version":"3ad1bdb57b05fd29dc468a42e71c4ec8f12781a647edf5029bb60f5a8afee701","signature":"5ac419d5eeb2a884c1d260bf31248fb2a853d3628aa0d7c3a99757ef99fd6c2c"},{"version":"678dd9537cd28a491bd13f7f3177c851120fdf39f27e9a93b349979bb21641af","signature":"e425fc0486e0242cc540bab0d335759f9f3f7ddd1d8ed233eeccfecbfc5aee61"},{"version":"0d010c0b5a9166166771c8c48bf48e48d9d037de37903d2b2aba860d1108a2a8","signature":"137de1e22724e42c5f197f61b17ec1264467852dd37b27811c311c97d705c138"},{"version":"5d70f802801ac149a5830d8877cb4167e9f9913f7e788079e587b5d63a624d81","signature":"dba53de0cd1e77ba275a61f6203783f10a2b35ff248306fbd6d9689303d50f03"},{"version":"aba095f915652dc697979c0b9ca5a3111b7160144f9a1e18efc81fd485ec9c3f","signature":"f19a0c7e1142fc0502d9e0014961ee6a6fe8b9fc26c4602a72aea8c904f15349"},{"version":"fd1383591235471dc0499e38fa8f0be6bf354c4ca3aef2bd052f93e34c821f38","signature":"2ea178cccad298208dd3300fecfc1e882484d9fdfca4a8c473cc345f0a34eed6"},{"version":"2e496f56e091df0cbbd36aab6e55e16c37efc31091904e36e71ccc3c34cd4825","signature":"72d589144cb568b0b803e59532ac2df4c04998cc89d175d259815ce6a1acd5ec"},{"version":"464597875def0d40d6a0ea4bc5be50373eeb35af3023b4901fc539c19fb088c4","signature":"b8ddd1cdd822f53a7a29b4fa58240afd0688de547a5c640753bfaf99a37c93a7"},{"version":"560ec4980f9fdf84e4df149a31c676fbc624df75f33af2159b30aaad1d624506","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a31e889fd61c82203d9d046edf845eb54c12d63b13d2028e5c1f16c26c5a535","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e0875a51dcc08220ab37ee44e0d604789fb0c24c6435826eff6522f9af79faf6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b8f60248250f207cc5481619bdbee3e9ec7645ea74d2b257bcbfdc27037a68e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2f9d111343117be248f5860e96c68b5c55e402894408fbbaa4b031ab12572474","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e8252a1e88c45e4b76044e3ced48484fa04faf5873eeb2a15e88813fcae79808","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bae7d0911c58609a404bcd7255d5c80cdda6d568c3b95fb189620ed7bad20843","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23d90e3d7b8e5a17f760fff35617a57ecd7b7f042602b3f9dbe314e938c77330","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"00ae1a801699b73d425782db51a2eba53741776741421dc8446480d09091377a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f89a50a4a14e6ef1a1c81b263997c2d728ce5b56bd1d93dcb907d57114ccf955","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"830b21dc28f068d3d362d407c17d010f37a9a29cc412527c274b8254c448dbde","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4492c3ccf40d889bf6eb454af8a7fba4199af810c53d10ef8d0bcc16156e72ff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"60efb61013763a932a1ebeb1ebbdfba609e6f2483bd2def49702f82decb94fe5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6fb49bb8359a76bbd80e39616d5cc6de09d3a9ff938cf58be22c155e8ff42916","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3cc35b9a2be3c9593d41df15a4fadee9a4cc7c145fd9d8dc485ab95eb1015898","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"effed161b9f183f637fba8f96864ffa67bbad3a3339b18d9d368438fbfc00bd7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e2f2f7c6411243d900d1f4199d20c94122f03d61de95cd5435856a125b91d134","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1afaf4cea2ba6a2e1fc5c696e0420842fd52a5a024f77fd63106e46f430d42b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d872c4f980d7e6288d80742c84f1dc087a0ec7531e18cdadfb47448a669c2f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"179703b328f92994e719755b197ff2310945583fded682cb02b88aaaec0b3d33","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d2e752aeb02ae1be73703cc7834f9bf1de14b84d32121fef58982b29bb138019","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"954fa93b29dc7267fecdb55b80d28bc943cf370e0165963ca051c0cc6899e114","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc8201479e29d49966186df4e5c359d507dbbcd4f772499b365e6836e500bde1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ce4cf241091329ede4bf94c365874f20cb8309b02ec32980d9bb47f6527e86c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"522ac15e66211cad975f897a7eb70e77ba20b34ba8f9c4babb8f75f37e19c24d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9bbbbd6f4a35a22eefdd4d13b639ad27d2b1316a6e833e262a126d4310d904ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80c14a78262fb095d375cbeffe6a6b53a300098928410181ee1140a3a8869a47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"66cce2ff73442b6f95408d5847e2c8748bb4e47e44334546e94e52be58c0d163","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad628be53a44b47262b560ab15866282ad4d257f2f214369e5f8579c84d503d2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"98021cc3dddc6c8ce5498e301424f2314a9f17deb130c609269c900716a6c129","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d4011e0ecac2f22d7f639baa671cb23d19be79b0dc64c1cedcfd026469e5dd46","signature":"444074570bf4108baba10fcc87aa17bbd8f6661575c2c6784199b147faff4e80"},{"version":"ae9060e96d1b1a768ceabbc133424cdaaab0b34210ade597f476ab2106964506","signature":"ab27ea6c0a82fdffe3cce64f5731ebfa2b266fd04fcd0b85e27bfe26699ea15f"},{"version":"622e6f18814b4016fe1103587063f23f819c36bd42158ba3011eb2e12095941a","signature":"f2edf0a07c79c131987b90c4fd6e571506e2de2217b1f01edd5440c94143c463"},{"version":"560ce56fa31cec56226446c1422aa0861d70ef0b1f92fa90d501eebc22fc9f3f","signature":"01c8666120a2b10fd3ee7bab4731eaa395ddace6a8133ea44f410db0846b0f54"},{"version":"72845acc3528236682c69a7b735ec85ed2beea38d8fd4c0b1e7779521b5d4ef4","signature":"112f2f56109f2deccf3073df9c73bd87c6605a307265aef3e247e6e2bd9fd4ca"},{"version":"563fa16b249fb0bf5ed14f72e40b6ead283ccb254dc1ecf0304c0165ffd4dc6c","signature":"506df86169965c18acf5c22cb324fcd3460cfe230046f06de7ad63860e014c1b"},{"version":"42d5990e1bb01e3e1c48a522cba12b25bf4a3b87fa7490bf69d02855c5019390","signature":"900375f92b808a9c742d612bc93108ab61fa9adb4b4cabee9b45a7ba8d30dfd6"},{"version":"92d32911e086e087141b1aac3b7876089e26ada9ca8758a91280a05b4efd3a7c","signature":"6d53e68963aec64794baff110983e875c60a42a3e3d1bf17ea385752c914c1ec"},{"version":"e1c9ad51c0bb65697455db21f2d0a89adfb65c96288dff8254d8d8a88638f671","signature":"b45cf13a19ce92456461eb346ffb6bc8bb229d8f04521fa539a761470de6ab30"},{"version":"7d116f501462b8c241b1c1d042feeacd041b9c48de3cca53984e539d2eeeb3b6","signature":"b61620ca847f6b7d40ef82faaeb0dfff55ef897fd2ab60024001a674f4d91e08"},{"version":"fd3e19108e40b4bd6502bcd08768a75693473f1ac31649f1f4ef6ffd7c88d36f","signature":"0b2eefc3650c7cb2c277d27ea3a3290f5835e2ad871b17041ff92843b06bf99a"},{"version":"b14453b02122266e37e186d1935cd337dde89929a1417cb87c6b962b39af0d36","signature":"d390eaec15d04e3957d9c597121ff483b28f79e54db4c916fd164dfd70372e82"},{"version":"7247f127bd65a32b0c8d5f84fced373fae4e4c541630ae7b05f1eeffde33b9ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20a066d0baec26f8ee4902ff7cc7afdec57496053b60b5d3bc5c85732a14597b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c216d4cd926b1cd512c039ee12dfdca10a292a08b76ab11198dc2293eec74ed5","signature":"64718cf0d577ae9ed2926faff603162ccee149cabf0f7d6c3d2eff8bab3f54fd"},{"version":"c8fe61044fac5d42706c4c8854e03e5eb073792202ec4e7180f7397155e34f9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"970fc7d6d0e9d5716f75362209aa4747f46dbdf1c601727027c3a8b22a9ebde2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ff33e494e30ea1a42746069c83751d1a458d4d8b2336df2c24089872132a2be1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cedadc70983ad1d699a7efc9abd8008710dbd21449893d7273795f68649a4547","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89af2ec4c45ffa09d872b1aaf9a98fc0c6ab8cce06c5bd6347d80ad8faa69f3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"65db26f870db2f36af509737119f27bf6fbcfe7aa413b169dab0f6215758243f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"52977bca0c3c391efb84678029b1816a997a5069d91f8de7277079ad39b00c53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d15dbc0f96e7b8141a77749e01a4e920a5381ca32a2aa58132bc5f7223f291d2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"544ad2754ea5eb052e793f75425624b7522f638801fbfe50cc252e8bda11e0ba","signature":"47aeb932730902d4d8c41ec941269a416311f709495b5d20f2ddeb6f8b483073"},{"version":"376cc50be40b8b36c55a073dd73a382908b575f74972789cdc3ccf786bc91d30","signature":"ac35cd214f851f08cfe6b29eaa9fe5780b0f124b1b30c0fe772a1d7f8b518d1c"},{"version":"f7483014a4b848ad25b27073b061599f520dcc6d57e6068e7ac647aca74eee12","signature":"1416dec78fa5f6be6be2e406d0cc50d6f98ce0c77dad79080d5e5a80be18084e"},{"version":"dce5de27f8737a33d7ac4aa50f6874b6cd16bc7420b3a9b97dd3abfdc263ae68","signature":"e28422e9a6af42ba47f7aef0833e002816fa287c438cdfb33754716547da6bfb"},{"version":"b7277bf592b4832b905af6bdf6120ea14f1b9a9210efb7a4ae3803a87287150f","signature":"3922dbc2ce29d177e9d0c1abe636860f8a1559bc545abd790c0060803fe2e1ea"},{"version":"9a106f225ee7bf695ed69744df6cb6982083b3f9c2fec9610d5ea74e2e49f6d6","signature":"9dab80bdc4cbca67c3eddb3cd102b87f111b1e4d1ba1b3a0e27a38258e31e426"},{"version":"d1df8b7eb29b69426f6328b503a12b4408d4ba4a3a305ada40af859fd0d1542f","signature":"c7a00bbb89a2cb3e0d5521251755518d81666c561cfe0378d03268570d5bdc23"},{"version":"71590a10d662a3f420f700c10793764811c558938e36311c61eefb13033a21ba","signature":"4645d41794484aad552e40382c358fa91dab4452296914190ee23be7af3970ba"},{"version":"ef07c47a9f22bffbb585da6ab96f379d97e6b72fbb658e78c03b11702cd1dc6a","signature":"3896cd910e22538e767007c0f988c5967112fbdd06cb050421f37d80c3736229"},{"version":"e561876a844b5d66796e60c5374a55e3666d17b8026012a3de1e78dc03e045a5","signature":"14001c191ef845d0b28e603f563f4c8d73166db39417fbeb04d227ab4918296a"},{"version":"2bba20822fb6a665abb0944bd00e587f93272c5aa1b2a513eeb9f2fed00a7e7b","signature":"785da1f883cb1f23d0ea0ff209153ea69a2c92d6fe7cd29f8c60fb9776e679fe"},{"version":"fc9258c7768321dff71ae7ff240ad1b5a6b204acaaaf8c088d37f1c4d644f20e","signature":"cf15966bba8aa58508d7159937e65485e4a40ab41fa2accefb0598833cef3af5"},{"version":"78fcb0f3d8effb684b13289aadc4680cfd9ae3628b1f39775621ec50448f22ee","signature":"677d31b96b2ed39787da58e41524dac24a285d4847b9413d4ca54e165afbc66e"},{"version":"c78318850aa730e4e4d4900d62112d87546cf76bd8c0f8a5389de62a8fab97e9","signature":"99f2cb08736fb3f90c502f329487966ef577077791b334b1d4ed5f0ac57a4e86"},{"version":"e21face6fb69732353a584c8ca54d4c4f32840b9d976bd2ed16e6e04ddb1b689","signature":"ff61de4e1af35108cd592760b2ff1a5f58eef3a4b29f4172412ef408143d3ae5"},{"version":"cb29061301b3205ef763d9159d82d81dced1528068d82d9248aa828b293473b2","signature":"49e666988a2e38af956233e189f9cb4667ee80b4bf738bd8ab61b8cecfee2e45"},{"version":"47049411d18af8a4afd89d6507e87fa1e1f761cfadfc49db94ff0fda85e2db4b","signature":"696d0b315d34950d1c089eec0c54c6ccdb7f2c19eabfed730b115fc4f63ec0a5"},{"version":"ac87cb8a327e1a7e15b3597bf1ea9207128645094941d34c91a4f4294cb50c38","signature":"834bfde39ed7879cb9e282fa632acbe344fe8d7efa6d01d05c6c6ffccfe806ea"},{"version":"897ec00e887fac246f9604b78bc3434fb61b57f6933ff36c8e5167d8fb4be86e","signature":"b68511cd2934d124b9d935ae94f90d131221ff96f95f2eca61e963926cbd69cf"},{"version":"2aed22685bfd84e88aedbc3c1d0943655e851ecf53da220041d42fa63e52eb10","signature":"51b1d705ced6ea26b44f528941620e1b0fe53538c5a245505f55960ebaef5dfc"},{"version":"b552dd1e51bbd18d0d9b4904dee01cad165d7f0d3471b42f00ee36ca78cb81d1","signature":"c1961b1d48bc6a1c7f3d115979d6728a6a8ac59869688a5bde08933c18adefc5"},{"version":"0e233393348d2a4b45ed807756c2d967887d7f689c522c936403b137cfe09e47","signature":"def7b08005b8d1ead7ec1cebff3703d1edd146dce701e3547a25d6f830bf1b6c"},{"version":"b83570a2939d33a6ecfdd2766a3e416ba612d0e8d6f83ad11156a004fa033c77","signature":"2d58b00459deb63b953324cacce6a50bdd0b7d7487eec5e0cb52c21a94e13212"},{"version":"3df325ba47f75517dd4720f0dbf492a07168a52264fee2acec5d81b68551b323","signature":"2141658926fd33244c616646eb68fc34928c2c3e76cb2f0fcc49f66b7a6c2e71"},{"version":"bbc074919308daabfa9d8eeb4636d8d82db13ecf1d497ba88070e64bfed7c0ee","signature":"722928e1b1078348b1fae3539d0944ee9db279ff7731a5c90baeed1a7afdc11e"},{"version":"a6bc0506fd785d58fc01916eed093992884c77047de6ef24a1984e870958f3b9","signature":"6c67fa30e0db9490403ce70c9bd112dc16256f74e793f48501b0af56cf31c2f5"},{"version":"ccee7e5b2e10837265d6396acc046566cc79da4210d0a50e1f1795cf86b47ccd","signature":"37d3ec125450ce03a992226a8a4952bdacacfa88aee5d09f821236d9c63b20a0"},{"version":"419ca4ab657409f45b8db6cee2d5d6888a2b08a2fba70ddda952d968c69a16e2","signature":"072d63362c70c19e5647e1dd12ada4492213157c48c17ccd13a008f9c6b4a12d"},{"version":"8231663779bfba7f580018479f74d02df7c9160b3e8dade1940a569ed9d80ac8","signature":"b19e055eff7a9ba8d3416874c9a679d800a5df0a0c5219cdd5aa5335c6b8b072"},{"version":"7bcbaef9af060a63375362bc183bcfa7633039fef24e75cfcf087591154693c9","signature":"0537610aa50477efe02a1d81e01f834e498a0db8f1bed3289ebdfd4775774c4c"},{"version":"3e41afb4ef179dd27f926c46f913abc3bdb66001716ac56256147179f5f15252","signature":"bf27ec3588059046cf735262879e81aab81ef0d702250e085ce6c07b17bc58b8"},{"version":"a0cbcb3cf77ff16618753dcd282cbac664597f8d6d775370609f8e128c598407","signature":"0e094d3f18ed4a44baa44ef3264239439eadb03b3f8e2ae278d766c852fa0754"},{"version":"ca572f8634347350a00e96f47b44ede6cc62545d87e839f959bff12bee235037","signature":"bc973f44ba5c54e1074bebbecdab061751028be94340dcc5481d06befed1f855"},{"version":"889c6cf5d2f17ebbce07ec946521f4f1a8fc55d9530b4959a7ae954ce7f0300a","signature":"170e54c7b03aa71a92de1afdd4cf56b47c9df01195f98a8d933771b75ecff8f6"},{"version":"bbc45438a3de93d2d44f46fd0cbded993b3f8afc82779a42d0a6819a10898fcc","signature":"f376da706da2e3ce62334b6d086d2d91040531879603f039d3cb7682d8d889aa"},{"version":"c15fd275a051a6770515950834e07dc22b7ebce6a9e8a93bce69d67d92f39e40","signature":"6f166a044fc4fed85f167f611d19bef8a2070e23281ad7aec01e77836c80287f"},{"version":"f2733e4721a9ff2047d46ddf9f771aa053a8f482f93d4820401d0be58dde660b","signature":"e3afb59a25c83c15f7f195f4fe92300ff8710b2a60850e9f869a07ec5a228838"},{"version":"6026573111eb1beffbdd6e3e3609ea711d4c2582469a258b7cdb6e4d36aaf28e","signature":"10e113ec036dd44b69d961f2b6616239ccf7f025823f75eec292640c3b4a793b"},{"version":"7db8deb452f9faf63b51a33fc3a09dea5a305e4dc231b770aced708f902dc7ba","signature":"2fe7ae68eac160827cc1ef3f71109e12ae1ba4c407fcf41d653877c7a3008970"},{"version":"8dab908f81bf0eeb9611fbbccb2508c2b4a8e1d57622968cf98993e878a972fe","signature":"b01970e81b7e682cd2d51def6b76c7bffa451a1b58fc54b528629c35dd89c9f5"},{"version":"9367e99e6028dfce0d37891b19a17bf1a3b04fb2649a89ae7ea832ffc7507b99","signature":"ba000331a8a0915160cf82ffd04d583bed6ea5547a117b8d88ed7d3ff6eece7a"},{"version":"9c9c3a0c8c7de68218d26f06c8a16c2901984f7486f1b2a494454f70ae694cea","signature":"24203ac0990f3fe71e252d5ac9b73e0c6e4ddf584be3cf38d14b1bcdec5176a8"},{"version":"3ad4c8f86a215282206449b63fcc1ddc7ccf6cb8e5b80a04bf64d92d2aaf6fb8","signature":"525d97773ff298b2f2fbdf14a791f0587ea0172827b6ac8821f3eb70b20aaf1e"},{"version":"24892b8255b88ef0102847ef8b231c6bfc0ee618a69b17e40ff1438f9997f2a7","signature":"59203658389170eec22beaac1509a33cbbdb6dff49b69e34593aa96c90c7de1d"},{"version":"d644c33e3d80969acb5b187976c8cf99eb0a259f63bcef80a6ee38da18e83247","signature":"2769f26e263572cb6b16ff1b24f373ded17e74e710e33725c98a22a0b7ae79b6"},{"version":"314650281c03451fe80bb91889aec0b247946fd5b52a318d51c5faf64cdc57ef","signature":"5fbeb568fafddc09e602cdbfda7df5cd0e561ba1dd8443318f1bb3b586066c9a"},{"version":"a45b7bb6b8126f59efdf77d2f8cf8ef3483aadbe34abf5d03038b1876a5cd1c5","signature":"0ce70420a3f859e7ada24e14d433bf75f91954ea538e9f25cf32a9afe7e86539"},{"version":"fea326b4e1f45f43699900930e1a685557728c14a394fd847072bbb227f7bdf7","signature":"e3575536a31286b081d4db3ae027a171f9567fb73765c91c67550cd330650e49"},{"version":"656330b9d0697dbe04cb1d8b8402b3ba3953dcf48e4dea01887c992036bb173c","signature":"24733ebd4c83b4d7b05b39d79f1eaf60c6edfc8f0da5c2f848b01517947697f7"},{"version":"8c4c8c4467f9519b0878333232f88ea38920588b21fad94d09e7d191c1fac691","signature":"5bbc828ff668bd2cad6f88b3f8bc1e85e3ab4a84af3eae83b3931bddb79d5d5f"},{"version":"b7567dec5ce2d27ed70feca5c5a53b033bbda727b3d65c1eac4d5256adf09315","signature":"31f22cee584992be54d06fdaa9dec55d060c358cf67c4d162fb2f5fc0c98283f"},{"version":"7354576cc5cd9410252734f2a40c4fff01428a753a672f354975a958e7c63329","signature":"b4444c70cf4bbb122a1983ec33c297027a05fc1dfc23376d3b140745a58a2ed6"},{"version":"3e8f5153df2b58ffc421a7d8440d3f92fe8ed9bade9a7b18bb0ed161998b40f4","signature":"15c69a20c8c5420b76b7c62d82cb284a1608ad67c2e0d1a71e3e3caf90bc4201"},{"version":"4d87ee3b202e0f2f91804622d86dc5cacdf3596c0fd62e4debf04d02ae25bfed","signature":"fa281b36685faa9c4a9d379f8a1ebb2f13f7a09f19e184becbc8e58848dc2396"},{"version":"6788a1deef524d1bb463645a178f02627169ebb47346eafb1a61faa5cb144333","signature":"ae6b5544fce2c65f20d0e7702aeb8e5bc2faf2c4e813c4ae999a44ca2d6b9929"},{"version":"2ddeb4d8ce27590153aa6ee84b36bf9764700d7260124167167a2d2a32166bee","signature":"68597f354e4595d3f1f99cfed58a445a66dd9b5d4ed5d8133e445ee2a3de6cfb"},{"version":"f7f56d7774204ea550efee0d9e05494e8df297bdf32634dd601fef7fe45f54a6","signature":"1aaeb72b56fbfc7fe35e5c5195bc4b102fdcb25a30fa39a8dcf26e4e03afe4f0"},{"version":"2ac12549f1ae0aa1775782876baa9c06e9d845be26d99ce56a36276a8831a395","signature":"39df2da2a2737d9f0561b052a23093c44d84bab8f276b5bdf2b3e41094666a45"},{"version":"79e36ac740e122de9323550df934df06c908a26820f221c758dcc16beade6618","signature":"24bc52911181a6e9ce7ccd5c8fc3b03b998f5a3ea71cf80e3c93051b68523ac9"},{"version":"bbe55ae5cb40ed5f38ecbfe673ec070dacc7e0d55dd02472263f7903b3c5ffae","signature":"21566e332d1f7e6c8890b6bc364f4d7e12afb504b71651d6fb92fef4d17835d9"},{"version":"671715f351a1eb2abe9ed42846a3c47ef495fbcc7c45891305b75cef89cf7dab","signature":"aaec910c194451376eb035a9e90aa018ce9121d164eefb346aaa3e19fb2aa90f"},{"version":"c10b1247cc334d64f4740702063dc4dc4251b96427e0d846b5eb9a7d0379bb1f","signature":"b204b8dc8a299d4fce994ceac613f46bfffcf4486c9a5b9f457f42f5f518b0fd"},{"version":"672ec17aebc02c37f3bd6a75778652f5cfcc450b0b2f4dbd8d5821ccc7909af4","signature":"0799f99f4e37567f2fe31840ff206efb30c29b21bdb0af72d55aeae15c70760d"},{"version":"6c6bce5fd86564171cf1bfc4122e6b4906a820790b4097c87723c2fb92eca8a1","signature":"1ad6ef3b1c1c48d5cf24ed8ff9b0a5a5592dce6ade6d6827d3fceaa920f6c500"},{"version":"7bd42610639a14bb0c854bde2d3bab07cce272b4f9699027258eee83d5c11a73","signature":"cbe7252f19d4397211500df1c2861e7c4ed9218b8d1614ae2d11ca03679f9551"},{"version":"b7b303f6ccc15e4db96956737e538d893c25b7092a159a39c0aa8ad932d3c636","signature":"c2f55b90471ad64c25a4d547225d37cec7d8f869fc5bb4cffe6c71a8b836f4b0"},{"version":"8b2eedc0f7bacc05c6f0b56dc41f46d1b06ba1b9868fe0fe77e8cb22bef6f2a9","signature":"198353c3f827b800288c5a5cb74460fd080c22ccc9881f49e9f5ccc59b35ee84"},{"version":"0c7459b35e2665327b17a7693b824fa83a3cc5647510a2cdf09a6635b4561c60","signature":"da68b6e91d25229268f69fa9173920364f23c6b50469e9e01e663e0de32fa6ce"},{"version":"674282b47fa4cca2df2e68b4a3674045b7a51ebab24287c15286ac61afa52110","signature":"0d3a59858de0d93e48bf7541964e12ac7d59c6d970c824cfa307bb3ea932f66e"},{"version":"e0c10ae5e38df160cb240dc9e46ac464dd22ca7432f783f75d77b1b0e1aabf46","signature":"a53c9821c1526959393efaf002082f8644ab2054f490e9aa6a0d23862d0ecba5"},{"version":"ae6c80232b4c2c4a00fa2f7dc51552a73683afd6acd88dc6c8a745cd39a823ef","signature":"b5184ac9282a657b51d247adf925cbc239c16ad3cdd8b4dc54dd369673e9a321"},{"version":"2a4f83a64245f53cbd1eecade0cd429c73f5b4e992439b773fbf2e8680ca4572","signature":"26dfe7cb950c6dacabb59453b56e2a71df14e67dc91ba3a35402e37a109393e5"},{"version":"3e0238000be6f6ecf94fff98c1c71072caa73ccf6c318c7b8fb324ff2903103b","signature":"ea3bb88cee2f2752e48e75c00ba80500d1ec9404160859804ccddccef1002ddb"},{"version":"aa28402c225fece07db6a4decdabe92cd905b329b6cbc2673f4c464fa4fa300b","signature":"37756386a07460ca40caec0a192629c709af57ecee057bcfe7c311f5da0be5b6"},{"version":"622cd7a5b9b304ca18aa1723952f21ec5c939f7c70431c1063bc25e59a912dc7","signature":"90c7406dcc6fe0fd8b0fa3e23b8b1440b2506d841d8e629a6b1df0283c8fd1b6"},{"version":"83837a404834ce7ba3f2498e3faf5dc31ae0a5859cec3101d684f824f8cbe3f1","signature":"3eca308a8adead7d78f165d89c01c30c4dbf141cfc5900a563ef47bd2b652a27"},{"version":"64cd8e7ebad2b8827d66171a80c2b516c5a57a91eddfe3f9b317faf8879dad26","signature":"084cd2150bfe1929b5fdad5847010232f8d7ed1acb1a965409d1009ab02b945e"},{"version":"68ee63044e87286b7a2100c05437babf550d647e748e3ee66ea6ad4cb268d52f","signature":"0d27b4098a7f8d9daca5e7f0304750773f03dd567b4d7d49db9a983be5a2e57e"},{"version":"ff60cb0d4b987911a9db25c4e372a81da6211e9248bf9eb336d2070b77771bfb","signature":"ffae22976581bd977560fb6a27d3aef9508d68c714c28c9be036c1ebe38f26da"},{"version":"187b3e36e643d6484ba0286c361eadcf4d3f4174865c8bb2e92ede5cfc0206c7","signature":"742178df5f8a476681d0544713fe87cee7a790042b11b27da9b102e80c318bb7"},{"version":"b5cc0ba3df9e33e2fa3849d474b4c558b77c854ec3addb719919720786f2462b","signature":"7ffccda9f5233cf7f4dd76c403921a51a2db0fba00c6d1c5156f463d95781b86"},{"version":"996c05dee2488fcd52dea0baa6bb03cbcbbd451bf22ca0982ffc1bb412ee5dc3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e83b7266b4bc5653f60004a5a07e2dd1484a92b256fded2dc1fc65e828b4bb57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2853d5c65a6ad064deedce24ab8dbf06aaa5ce9542a47f078fe02f03ac7cdd03","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3383d105a4eb14ea4ed618769f30b75f90188e7935364332f7082793d1196b9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d96eb2f5d3802c4a877dde7cf5c19f3e938d792a6c623e806c9cb3d64f134d19","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"df1cb31463ecfb08b80ee1fb021dc44fe79934972679382b951fd13eded5d250","signature":"2af67711c0b92f1ec7bfe590266fb550a2a274b8e60fdf1a37d57af36b0bed07"},{"version":"40b19636fdea5f4ff717e2b8c783e06978d56ddf2e56cadc547203802f3ac0ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc0e638cb5f96071486da3fcc349b7f938455220ad96d4e80a1afb444b7fe0f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b65293780e6f9a13e12fd16c069c51294f40e1e12a28add6a26d8205c74b17fb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"82a1830c87e3d98fdd26f717ed49b8781d7fc773c0de5264e05e62640da8987a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e7b9731ae386cc1518aaa4172cab2116a0b1a791cd8ad34ffe09459f4574a415","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0dda19732a158aac112629965a55ef3a3dbb4696aae000963193b51fb860c59a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab754ad0ec19423ea27bc7313015d6cf738360f4631148d2d49b9b87c0a46929","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1395fd01fc1397b94c1c12676294a680c57d649db4b3b28e1c260f0ebb541e6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"80a9cb4a7a8161419dfe4d452a08286676b67d360f2bed4047942d1f76f2bdb5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4156d5f13cb167807cd3b50f1ab673c57ff0051d3c5ba40aa2dcf95f310465f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1cc522ad210d2c7dffe081392e099776aea1a12b7341387bcceae1546565fdc6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0eeae146f6113ee176a29b1625a3e63bf9e84e3d15c25b672653db26e45d8ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"85251bb0af0b000acc3eddaabb09b481db2d5b09be42f25d52b056b966ddc6c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a63ab84a834b223bc3cd8224f1e39c2ff0f906f3c29375f1dbca5a34ea1b4005","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"093fd001ca343779855f7f386b448d526e734c0d8c707eb3b979eddb84d40161","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9598e8f2c6880331c2f57e6fe39fe65d279d5fcee0879cdfcb10f676f2af9ae0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2deaf139a18640875564d069b8df011081214018c145526504bf2e378c716a3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e7a92c6c568e3073f67648be4f0ab0e8d77e36fcfad8aa97bbb268ffce6cae5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"be1f6a316e168ee956b44f0e9587e97a5989614d65651278328e6de12800fe42","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1c26429a84968da0f9f6818874208d5395d5d681789eb62d1e97874afbe55156","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cfaf579c6bd64023a9f079619c3c42aa47cab047556d1299c9789a6bb4a93be6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5032259dba8af052f8650efd4b8290d61a0213e30c43255cbae505fcf6d1581","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a61e00af63164e828f24a3b5abb6c76837562fb18ad83607d245762b92566c22","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec80f7ad05865aa145a03395e48710a388452c5573da23b8680f9fc4c40f7023","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c2cca1042ac885087aefc61dd07c98d4d763e1b93da979e9cffefba1535103e","signature":"956433971a3be8ead015f2ba25dfa9b9a9dad91a092766686625e24987c820d6"},{"version":"90f918fa4bfc8a1ca28e5ee6c726fb4314e3dd5e4e6e5c138d3722a4406dec1c","signature":"243160e9793898a75bb1706e22e14be7dc4f7503439d0bd4385c9002bb73a9f3"},{"version":"c1acbe64c0dffe2769da1455ffa7a7a54c630553e711ed9ce686509d5a7ca22e","signature":"ed6c54273b0447c505914973fedd613d6bba8426779fbcdf58d1a900bf95d3cc"},{"version":"3979212bf8fa950277a8d6177a6ad6c4839a771a7074d9610bae0e35e6ce9040","signature":"f6aa1162ff9538566b39c5592f558b1dc70974ab34c3d1a592bd7b65711e988f"},{"version":"d275631a49859ae715b00bbf0b5f027c44d809861c2436dfcea1797176f9f4ec","signature":"2f0415ffbf291a21f7b8e32657ea9757e9b49bcd4b1dad5524ee463a1927916d"},{"version":"68a712c8150b2351406c2564d71be4e6bcf2ca9d5d5a241e99421ecd917043d1","signature":"f1447d898e5612d1a748da9566c03045a70199ff3535c97280baf53da785bcbe"},{"version":"75b3fb36bd172a0191b3540170778693e0d098328f7f6b783d0155848717a104","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c360f159bf7cc50cdbf9fd68912ac63bf5889b7220045435cc681b4fbe0b8f99","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a93c39a33bbf74c81cd249032ab84d98f1bb5b86a5d557111792af7fa51fd3b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f62ca695015598c5971af372d459d280dc75e566c024bf83c3c3fed392218927","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f6df81427b32ed9bff8b40394ae1322afd73b4607ccad4b57225a153efd6d6d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aaa89e91914fae87683fc9b47537b7561fe705e3a19ebe436209a8553a9504df","signature":"78ed4e422bb1101f6a3186fe4b0b70d24d3503382ce07299b406a68f01141809"},{"version":"6d55514cfe052291428316f8b5ddd2620f161abcf9d180a39ad04f2572852a5d","signature":"8d866b691c98d47c7eecc6462e0581e3c9e62a4b13b87c6a7f830f0c2b918015"},{"version":"e34138d79ebd653b7302f054d2f5b086c40075dd387ea3c02b9cff1cafbf66c2","signature":"488ad3e9fa660fbbf03ee600d13285edae90e156fbb5b1c5f4ab396e5ba87226"},{"version":"6c6f6ae7a61d464a58070d9204181c34f88def3da2364ab213b2769fe1da314b","signature":"0183321e9456c163a3b9630a73441c67d709bfbcc7425c09a97ab1ebb83c1216"},{"version":"17af83b68e8692e64f6e475e6d405dd8dbf0756b44f05b22f61535e88f84acb6","signature":"aa43218ec3abf932ac6aa3fc859581b8f2fd8c9469e3c919408062e57d232bda"},{"version":"31c12db46e3320bb3d198856123b8875d4c18a00a9e8e8e6aa4c87153954a24f","signature":"4910911105559d5ec48fa179743ed357001b66fa8df1227bdb5820ec71c3c5bc"},{"version":"442f6f1df0e9859c783e9c1260324833376cf2ef977b0a8e7c5fe85ac45b2b4d","signature":"2e9fd6dc4a8c33cf0b4b359754e567e8c5c4a714fcda3716a4c1ea413102c04c"},{"version":"727b5a06aa2c2d16692b1ff55cac347033ee492fb0132f3843133567175c5926","signature":"edcc0d9e675c37f8b8345ef683965422335c183997a5abae692e03fae3b476d6"},{"version":"104321bbbae499a49b02b529e4e5176eeb094395ccabb51475b94ee7ec3fac31","signature":"cd789dd692d4dd223dfd8938a1dfe00325b137c3852e6a85bfa9ace8ed00a10b"},{"version":"8d1549cef4bfbd34d863903405a6f4146fca4310628edf97cca7f43eb1b5b70f","signature":"b5e0ea031f83d837aab204deb17bb3dc7bb49f3bc3d5f5ae00de7055b0957bdb"},{"version":"90ddbf56e752ea3aac5906c0bf372a35361bfdd05e37674295b930d1a6145676","signature":"f9dfcc6ba837fe9dfbdb57b71db828463f35d1938c29d7405b78935fd6551ab7"},{"version":"0bbccab1ed21d3bfef65a11d71e402b85c240e811a9a0f5d557927e6ab7a1ef0","signature":"60e6043a56300fa24f867fa24168c0ce827d6154625db4aa79ed2d49432f06af"},{"version":"f8c99e101fa518ab3c47320437e1cab29097021f2e4db90088d14cec9ab15db3","signature":"e605c17826925ef50254a6f1fe1b7615d239bf637b30cdb0df4637d362fe265a"},{"version":"1ea8cf150ecfa2e7100ccb91fb039af6b12d8f5f022266a716f6c6d3d0564280","signature":"307b1f6b818d93140e0f0a31acaf65d20eb606098df57cc97103fb0d83e79529"},{"version":"1d75e713898af44896feaec991b57d2e9a23e8790c7715eab5617b37c81b1304","signature":"c44cc8c4930b76d1fbf935484045f099a078a0e56218439da28da5d829065a89"},{"version":"84dc9514ce5ccc030dbec7dbe9731508854c3e7004783452997054dbda666a6e","signature":"0d2eed2f304bc1f3b1d854f9f66a454ab06477ac5e5c4541e10b6111c9b288de"},{"version":"487eed855632a69ade2c123a6190838937543c0ccb9adff04b2fa4acc6acc55f","signature":"1ac9fb8cd09e61aaf85cced63abfbebfe7620efd14a30559e8c4197a629212b7"},{"version":"73179d9bed7d28d279c73fdf5c7ec4dbc78493af676d383b3f3c5c35d839e7c7","signature":"65c789d98597042f1c69b58091dbb80443537c26f1dee66c37a32ee3c625087a"},{"version":"cfdec451e6198722f6f1a470ae1d702e91aba34c5a82ddc8ca2c46eb2841b25d","signature":"51e03b177ae1693a016731d78123a9375e88191258438907cfb8c28289ccb8bd"},{"version":"aa0e1cf4ba7438c59141ab42db4252344f3c2a78fe48e04a4a74dacef405bee6","signature":"f26c96975df3621c30ad0e860d7cb2679f76721cf94e7f8a733b9f3e73f87925"},{"version":"2e818b0de54379a805ff642430dd2ebf684b6cf4d3ae133f12fe826a47eaebd9","signature":"bac0a2c0df7457f1aa977c6199e5579baad02f84ad8594b715b67a312124bcb6"},{"version":"fe83b119da0f5ad1d6de35dd8a8ff11c6d3b4f430d2ded235430d4cb84bf32e0","signature":"53f10c22876bc751399d19641a5d1df99980c8d1b24ea5bd17e074a4034e56db"},{"version":"993f4c89fd25bd6aa86e3329183f0ebcf30123d243bc6505e945c7d23213fbd1","signature":"076becc81584aedfa7349ab56ec3058a2c48e51aecc5ecdabc2fd4aee654cdb3"},{"version":"5c0d0bc099cf3cde30d02b2d11f7fbbb934c2434cbfb69d8d67595bb2ccc1d95","signature":"43b7a0a2b2def00095492d167729428073dedf1d85c28159254d3b64e77eb0b6"},{"version":"70b90a13137fcb5ceaefaec6c636bdf5ee4fec1b03803f5bf1d93d3443231741","signature":"a7ea8dac8d777c73d4af8f9fab282c874cafe6b2e8398ddfc1e4e1a00f2f44fd"},{"version":"d9aba09758928ec2439f08c4736980c6e52ddf6a0cab476c161b3592a34d44e5","signature":"ffaea7fcaed416769800cd74682a38d1335953e1eb903bb59c22e45cced12b52"},{"version":"dc96972b8f5a164f425ad5dbc5a07ce9e4e802fd71ef03f44c19ed9b1a4a1fd1","signature":"c17952b18383043b4bb2c233acf6c88794227a30f545cbaf0d701e44a1562ccc"},{"version":"5b42e9d056ba435957700d0585861287052c08afde82e5349a21a43ae7113457","signature":"c892b55f40a8f35ede8ef7f1e0cdd1dfa70b22bee55d10674222bffbb703ef02"},{"version":"fe2e6c470b89b10fdc90714c6c734713dd0809189913130fd31fde1c152dd96d","signature":"d5dfca986d325fb72b02cb63065520cc0128b46d77c1c68441ca2241ce17113b"},{"version":"e221838e10f6d2c4b1fe86acc4066491685a9c6e878ce39480638cadd1fa2650","signature":"b98c55bf5fe063f227004fe751cd334ce9690aa25afe7be7570d66c48cf86e56"},{"version":"742bbb2ee54b65f16f77094b8444fff1e3f1c4aea3a2bd44cae5de3fcc369411","signature":"8d2883f78b4357f180fa333405e5d6c5d1d08305042060e331f9b4b21c262dae"},{"version":"dac20589f2919a63805df5e02ca738dc974c5363fae35c526eccf6b7f9dacca1","signature":"a6d58c8a4ac0a18d66afe6789e372e54e3663f5198753e6e94481cff20b7452a"},{"version":"be476947bb48a7e4e2a2bf100c43026c646e115085d076247f276f88111d254d","signature":"c34b363d2b6cac61ffe29e155fdac051d7caaa197bcd33c1dbebb9632c10dcf0"},{"version":"a1c585659ad6a50677fc7ac3252133c90ec1d60d2f44a716b6ed4f945c0c337e","signature":"092f1a685f107b5dcb94b5d54e07eaa58894ea17312c10bcbf11921448776f41"},{"version":"0c0f21b2b2173e3f325fb91099b32f3e19843b80a92f4ff2ff5a3d4235afa72a","signature":"e5861628b3c26867cdd721803b8fd63ac2568b1a32ece964adb39bcc895a1ce2"},{"version":"f8bd37d0e25c4048cb2f19e6039b8ebfa0bac6d24ac8ba58aa0fa4efeaa571cf","signature":"e21a61ef0088c9d954930ad728286dd23c0dc3790192a6ee38dfe04a0f074140"},{"version":"09256332eb93d63b5de0c4c87a64562486589143068b03be359bbaf2038601e7","signature":"b33f409b8ccb6bd57b604bf76bed489fcad328a2706408b6c444333ef7bbbb7d"},{"version":"2dbb440d516e8a8107ae311ca6371d7808832cab07de9c432b60d3e3a7e89d5b","signature":"eda6d5dc9807881492ab8f1b3d2e72637da870377f3a4742979f049f492a8e14"},{"version":"e46586b8eff1754102c56c3132d0e4622535a474a8a6b82f001baadaffe33779","signature":"7d73e178ecb304b871ac7db31ef6508abbacac21c1234cbb54dcb173b53a0a6a"},{"version":"f513672a732d70b885487041083cc99b50430dd50ee20a8e8175cb68dd6c8cc5","signature":"638eac046436ecd6f612425af86067e56d2a699a83a5eed192b905a4e9e97eeb"},{"version":"0712f215785e5dd0cad60b45d6812d835d1b65bd2d9634228daee48becd93c6e","signature":"f20151181ce2ce06fb16e3cce85a185ebd37032c37598144071c3ffa634b10d6"},{"version":"268d9444d7e21783addb24299011460c065a57101057d5ce904524742f7fd5a7","signature":"5ad606a8ca9d6d3baf284b4af21e08329b8bb2b9963eb9c8730a4f4a0251026b"},{"version":"b820c9c3de6cb1040413353cacbee04c9b8bc8dfa653a4aab2c25aa6c7c65120","signature":"35170f7ef283cd4dd0a6848be2cbdb95d0d3a1e3472a12bcf21fa59d6f81c778"},{"version":"5ec9b5391ad2f1fc9329b4d7f8684642d34f6c7fd339fbf5074ea3115ce9b5dd","signature":"9eaae9cb3456005143c5fc9a8938ff63f654a96cc6e1b0df490e68d0815a6390"},{"version":"40d627eda1855b36de7021d43907d566cf190cc41bb19c45098750d39f6fd454","signature":"97670088c72dc3f74a553fbd7594d6647c0425baddb95661037559a2d34c6030"},{"version":"e03417e94b5cc0dcf823345bb66d315e527526d7c114a3d8d5334279c7cfda97","signature":"ce511872d83ff623f8ef004de954c7688b48dc397e4d06da86d2b3ee8be28cb7"},{"version":"f33f31e669affb7cc29fda99eb3aaa0a0f3780667e31ed20375abeaaaa30138f","signature":"e4fe18f29c0d82feba07a9633acda1463e3bde4813bcc719fb4d0e5e26b468bb"},{"version":"f7956442417275691905a10a694cf23e778b1d4650fc39f23e4ea91435e92cfc","signature":"7f145dc473fcbbd9152b5f0eec88bcfefe5e415ca70d3edb84aa0038037f61e2"},{"version":"a090199ef0404e5cf68230c4180a409125dc70299541cf6894355022f641fe0f","signature":"e1b2e5515776399fcd972989e7208004f188f075498aa4f77bd888046110ae61"},{"version":"9cf58515da90a71a9353678a765a8b6c94b63b625fc05bf5f9d691992d0c5ef1","signature":"0646461331d1a1e9f1dd6b22fb002a043259f5210cd693c5959bb6d1737415b6"},{"version":"b667b78d8db3056c14aba18e2138fd530838de4e44437153b8c338d97eb7944f","signature":"67cd5d46643ba488aeb104da791a837e1c564361ce47f8bf02f18a49b1ff1eff"},{"version":"6b440e18084a343fc1590e831adced1b079f7a344aade681cc70cb688769c674","signature":"0e651dc17c75471cfc564c0c2fd648826e431307eec33d167fad6f8943b0b9cf"},{"version":"a1f703b3b7e3200dc2d735de4ae5942ced19ce3cfd5423da9467fc280fdc0329","signature":"bb851ecf30c98fe3b901290ae1fc05bdc55da8bf15ba10e1a3d10dd12da09cc2"},{"version":"070a5b980cf70e9e54d6291f31a634d1346707662aa0b906ebb47695316d94f5","signature":"0e92e6224e167b5d58f377e0513c39bb3b513cb2916a2166f87a20d3ac9cc629"},{"version":"cfca522a29f53430f1d0447baa732bf2fbfa5bebfe68d2e475432b228a496110","signature":"f8ac07e911fd9a3bff7d0a2cb3b8589e14a3b52a3d26a2fad493348f494edfac"},{"version":"431b70b424860910a8ba2560f83bd864a2939b87109f11ce22873964f1823b62","signature":"24cf0f162a2bcae8f5ae4b678da2ca97a91699cfc30898c606cda9ebec4d9f69"},{"version":"5d60d719297b5cca5a0e20daa44b23493c8d65f7e64593468e3c97293c236894","signature":"d316a8da36d661ba0c2110e7fa8961db330ee9c39732cdce1b693c8608a06100"},{"version":"703e7b32062955f1941d78af2bf1a972cee1905d9f64c8c945e0307b71c6c8f2","signature":"5c8e01f96eccbc91b7243158ddef84763ac292ad5e914a352f8422f4b374aa6a"},{"version":"5320b2c2ba15fc1d0250cfacd6deec6a1b242c1d03f5d5bcc2d7ea8186fb8787","signature":"7a977a3406b9510b629b97156a3917b9f347835d16ccc19e110f0bda76af6621"},{"version":"7b7a0b908bf6dbaee29816d012f9ef2ff0b0745ebe5977484e12ff0d2a1d4fd3","signature":"7b90b0d17d565ad2bcb84936503634ee9f77cf9f40b2353797af5c1dd26b6161"},{"version":"7ccdddbe6d980d43d92d5bbdacf45f1198b4142db652f6e90ab97375400ac468","signature":"ae5b73a3e026381b16450aa4673ea1b2ca1e7fe4ee196acc0a3f09c299081d51"},{"version":"043caefcacde199496905b469f7251c08948c4921f91e5a0f5c0df4f03cd2d55","signature":"08a95f68e870bfccbd65af83835d7f74d53e33f3a90926375171726c9394185f"},{"version":"d33e44d9c563ba82cceb0c3fd5a20de58d19a4ad63160482de55bd9c50c3ad2a","signature":"c53d5f0a2eac0af33a7fd617b3d035c3f95ed81fc152da2058542e01ced0fdb9"},{"version":"58cca2c47d5dd00d8585348eec7067b9e45f9fc89c9c430cbac18e2846c4bb80","signature":"7eca6e5608816544c2487977bcadb1118578578f54eb343e7ec2ab82302f82d2"},{"version":"002b0f75fc367ff79a5c95065b8d1a38cb78121d2588ba872110920bb9643024","signature":"512558fba7e0f5f8d0cfaad40f05937124ee8bf4c3a11dcab9f618afa626fc0f"},{"version":"920099117da73b53caf5e84b81cc4d2200bec4f82e818bc23b7d079a2a56907c","signature":"396d5e07f113ce101976ef3238521989b0162a3440f8f15e5115f00d67aba169"},{"version":"7444ab226ecde90756e4e31ca68280797132b5c3b38348dfafbb101346ff9c4a","signature":"41a5855424d478222c6ea0546f8f0e7563b8ea830f0b02418285fee2f6010104"},{"version":"4674b23baba8d8d1145d47b4d8db58a1161a0f0327cc5e05aaa3c70dea3aa4f2","signature":"597635cd2982b768c8075e33902d4bcad6b823ad6837b83bdd5df1108a8b5ef1"},{"version":"dbdf5c99dd4d0362a790a664fda2f7d80f0b90ec20d2dcf9f4e71e5d859ee247","signature":"7b9aa1a8a9728abd8faf699093ec32552e44ceb2e3e4eea7fd39fd4a105abc61"},{"version":"7007c577d3881953fee9f301de570abe4ba1f6a54fbe2873968dc002ab5e5629","signature":"e7dc47606500af2c3ea9d69e3d9ed293bdbbbf81bfcb0c48df6568c1ffee4b02"},{"version":"cfad3779365697cae7b23669d57fdb286de38dcd3c9b1fd53689f9ca3a91a2f0","signature":"51838b26378f28235d88da3177a2f581d6325b1c546464f2f5bcec82149eda0e"},{"version":"a6e432450d84b15cefce91791dc06498a08e357e05526537db4bc137807316d5","signature":"92cc6638c98debee2178e4ef0e1cd3859c86121ea2163b0b0685d41a96d14e73"},{"version":"2cdc33138be52678761d11065245a401d3499f110f502a2cb34cc2632e9c5e61","signature":"41f303a470c94ecbfca891884533f4544fb3ccf7cf97aef0ee9b65df992d97e8"},{"version":"236b3b7d3b7a86bfa27e5bbf1998dd7a09b9b6ae3ebcaf1040be305324dcb5bd","signature":"347ca2f28c70154d1081c55c3772b0e5073d3482e261847eb6ed655894401136"},{"version":"e22bf0ed5f47dd3f92ca02adde6e4459657a649c00c57071c4b401d6883009bc","signature":"c21b522e44f78bed8f3053a3824eeba6f32c27dd933d6b45cce037e8be0d0538"},{"version":"6f15ed3572bab8df8b145e2808ae12e8945eb7174add99ccd556f8bc1fa91b5a","signature":"dcc74f798751ba65a1cc7a24a795e46be9ec56f409261005e1d69b732b013560"},{"version":"d592e7b22c830bdf0c8da2ee4c4d5d3587675ef62db03bdc0c78df8e7f7b7c80","signature":"8f3dcdeaa6a4c6257d53aebff62fd88889876d55839a85929f5ae2d3a37d5a73"},{"version":"19edabca93b6826a91c26832d55037e487218d8f29f2172917ef87ff08f8f380","signature":"cb9da35a72a402b315b10d2569a304b12543538dcaf383f4d9f8dd5a8114927c"},{"version":"fae8d4ba3bdfd3f087c40507d8748236b0c08aae3d74c706651a15c0e27ba16f","signature":"2496283dc414126ef574138ede1396f27877de39dafe04d183e30d2c38e2cda8"},{"version":"9041eb411777fc80385d1b639173fbc6675ad3d7cbce257f52605d4b18616543","signature":"748edc1e544cacbc98bbdfd79c2a36b98f9e35ece62316b401da130aa0769631"},{"version":"3aaac2c7f4e18c47e5197948b4f1c4d1d569257499c1dcd2395bcb15849fdae4","signature":"bf80d1b3fd049b9db79c5bac94e6a4b2cc9df97720f65c91a62e095d793499b7"},{"version":"ced161d675dae30f24f7d001a14ad62504d69069b971753e9a8003b2200e7cc4","signature":"1c51e31907648173207e55a21000c17277311c053c658c91e5505c4f1cf4e9c6"},{"version":"9a5d95bb08f3be86b32723cdc4d4165a66d3008f375283cb31100fdabbf913d1","signature":"dd0fdb6f0c71a53e434d39a22ad54d9d196de67178d156fa5b13df073c527f19"},{"version":"22772822785aea051e4454632aa2bb73baab3d08d48cf7366cafd8f19e1e0c4b","signature":"7c89ddbb992896a2006feae6ff5ce82f22e2158dc35be692798b4744a624221b"},{"version":"08069afec7cba0f29e89b4fab6af533440854664607e1a6381781df96676115d","signature":"a78d3790cc5ae1b4930c299095023c07eccff89cca32e2939a4722a716c9cb55"},{"version":"cc5667037f805a2921883f7fa5e091aadf2ee8907c46d61a7d7992911965eb66","signature":"442965f5309d0a2d4d8def49f2aa35996d1b73d37c0b296eb305501cabe8829f"},{"version":"7ca021d1c2dce515eefcf872fd521c489e8ae3509706d5af45e4a53f7ff22549","signature":"094975115f35bbba2f336b32b78b856d111acc84119f62ae45f2261094e048a4"},{"version":"a2bc61e9998909cdcb970a57b46b9bb502242bc9d93a9a09be2c2f446baa729d","signature":"a7ea6ec679bdb15b1fbf1f1a938e8be370ec62bef5744c84361453a59c182a1f"},{"version":"736e4e795c9de961b77adbb54f1e457e47abfece7bb141eb1fcb6ca6fbe2c9ec","signature":"0f071aacf6b65da0adf7ede684da36b0ee0f310cf6b429e2fa37fbeb90ce79f8"},{"version":"82dbb5baa7af6aca1b1392a81acc3bbbc07f50ccd8cff2b3eff2ceb1c5db2182","signature":"e9708da92b0cb69d4b46485f491a6f053fa07145eadaa8101b16fa6738100f9e"},{"version":"fd3444a7b0304c83565c7d69296748987a09c2a55377bc9e6c4d32961f8c99cc","signature":"669ed183124d2bd3cc638ee9422002a758efdd672388d6d6121187f2d073d024"},{"version":"65f0fb9a264a44649bc963291e7d6e810c6c06350748007de4e1575eaba8d319","signature":"814699b1fd707185dac005f3047ce5badb2b34bbe355ec84b4c3cadc82008b8b"},{"version":"08fd220005ac42cce029a0f6ee96478b45715a8a39ebeb71e42aa726597ba44c","signature":"d81983bdc0492ab963061b9fa1fc64926ff2ccf744fc3ce3922061f6016ff571"},{"version":"904f2be841ddcf41a9358e1ffd053618fd6bb1ff1446d429fc2f253af774c5f2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2ca66d64ada0abbc61f1e626ec99774d85b81f4090bb27f7f3d29c599970bb3a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e27dfd3b35176a3a2e4307206a9ec3909995b23657330dc835e7b5fd50ae89a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"72ac7a0ae5374dad1652ef8e41ef145bb371e9b8af2394b91a3b6e0220b5f39e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"943d9fea1a3def860d7398ac4c9c72712f969552e4b6e913da7f37b20defaaa2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cd34778c118327f37b7d398e6f0eeb648ded19b5398bbf6aca8af751167a18fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f9527355cd60b21f51b107ab7097d05e058c0d4a38f82738d111c2c9c47c4a87","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e37af3d628ddbcd48c89790cb73dc028f308dba050d3ba3ae64d1cdd491dd7c2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5681b305d9e4a3f2a27695871b6685c547771c8df3321ba62aba2569d3537076","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90e42071689c6160951272f117347d7859a2ef54dc83f3879eacbffcbfee1868","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"faee2c92e93b04bcf0bc3cf951a6ab15c80022850773ba6721dba52e84a5ba41","signature":"1342888987d6078504543599fffb3dd6029c2c0f768b489cd232fb10bedb675a"},{"version":"e353b7b008a1c093f02f51d6c46b2c1ef2c28fddbbd38889a6a7e4c224916779","signature":"8c40edee0d9d2d04dee8654ba2f8f239f1662ba5d78ee296068d1e17dece3391"},{"version":"b2ab2a7ba089dbf1480307647bb4fe436039011171626946c12efa7ac7ef23ad","signature":"ecacb7a344532575d0bcd497fdd22d7e66c42117aadfa1fd211a47dbde3b364f"},{"version":"b858241a65512e697bd928eb1675541fa5fcf2b516aa837f90cca9ccf23162f3","signature":"6a9bb53e41455547f9529dccd266b05e7cfd3fd72264f41bbc97581094096369"},{"version":"045b680cd4cc18bf4d40193feb610f9692e31e055ea84a89bac2f417831c7ed2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e6107e7fa315d770a69b9edc7dd077036f115479e102f2380306a7a92629329e","signature":"0717bfe8ecec022eb7f964ce697b1b0d749e217864e7a11b438c462ca1e55412"},{"version":"b26bd1398c6f71549fa23175f3c8b8245fdd2d2092a6380794cf8ae8ee67666e","signature":"99173fef2fa963dad3c3a06cef6bff8e4a9e4fea656ec6414aea2f84126ffc23"},{"version":"002de79e07e5851f180fd44a17d1f855b5a17fb9a00f9a17cd53ca055d27ab8f","signature":"08e14aa9343103c4781efad6b4d287b2d577a8266f51f389b2ed4db8957cb5ed"},{"version":"ca319732ee32ab8064188cb5e284f7d8994c1e05e67bd2f7f55203eaf67b33de","signature":"0de5e5a2fd2db15c16147aff67475c913395e62c14bd7c5313880b001a88e009"},{"version":"04ff795f13235dcc2df104c2363bc370338976c37fd408129eee133fd481b1b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d043ae7012e9b61fc3a47946b043e6feabd01d8a1d43f5613bcfe1fb1d144fd0","signature":"ca0cc7ec2073444d6a6e3edc6a759fd98917651115e6c1b56e805d7226d122f9"},{"version":"e48cbc2646a758124caa3c87b05a722e8da250afbc6fd5f4eefda072772b4616","signature":"2a57bf7b0ebfba810bdcc71f9eed2403e5a2aba006c55788b48195c51aaab8f1"},{"version":"056fc04ab05389b453bdac4ec2e3c1eedd8bb661c20c9fe2e184125c8d69dfb0","signature":"abed2e07ab4d9ae16437a73aaa4382973966df349afdf5eaa654b3f766c30625"},{"version":"cf4d4045a6ef47b776863026fea118f50fefbf94bfaca15b330d5c939ebeae61","signature":"f670d82642bcedf7ae7e34c49a5dec771f607f89b47602cd0b7508aa981ec2ce"},{"version":"b0f38f471c06b85328632e90a5823eec4d4bbdaca72b634b477325cdd68d62bf","signature":"704594b25466b609c3bccd775f15b2118e1ac95cbfbca960e5819c93ebc1f8ee"},{"version":"2d3dd03df960f48735d9ea246405ce7f2f6501675599c7342965217e6873ac28","signature":"95e604b1fe25d3994cb3ff463ec3c46968a7ffcc3615814407b7a78359431ea1"},{"version":"dc2a05a4d3db8795c9c161d8e05a72d0548cf61e5b5a1e992b958d287d148f20","signature":"3844cd66a0ac7b19cd62be77527a4a53499fb22a7a122d735603ae6979064756"},{"version":"2c008ab0e0b102c7d0752086dd258c08086879c4ee036b40eb909f53cd444f76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"078f20355a4a20354fb1a4ac5e7b0398aa43eb0d374aa77846c74b1496e7fb79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e5cd51e11096be95c01de7fee203750f0b365f46dad987f3afe9fb535b99122","signature":"c84bce3c55622bfec01668fa58087cc8073885a20ba030d9e9b519debfecdd96"},{"version":"e50fe642408753de3208274d3a7c83c42bf821821740b79012eb581ccb425bd9","signature":"8674781878cf01b59ae950a13994a74b11e766bdc3d6a87ecfc77d1e4e0fb7a7"},{"version":"6ada175c0c585e89569e8feb8ff6fc9fc443d7f9ca6340b456e0f94cbef559bf","impliedFormat":99},{"version":"e56e4d95fad615c97eb0ae39c329a4cda9c0af178273a9173676cc9b14b58520","impliedFormat":99},{"version":"bd821b87e2c0fb5f509cedf47da465c447451835ce0fe2a752c4fc53a9f95a5b","impliedFormat":99},{"version":"f1d7352c0f7041abb43e1054abb14fb8c53a13dd54bcc1d67b97d2c02bb5028c","impliedFormat":99},{"version":"fc820b2f0c21501f51f79b58a21d3fa7ae5659fc1812784dbfbb72af147659ee","impliedFormat":99},{"version":"08f88f75fc2f516413477606a4122b6d3f6eb6680e8eb79f3fda5a5d2ed306df","impliedFormat":99},{"version":"6ab9821afd2a06879620eb4e041b9492a90f294e9b733ae5eb022edaa3964a45","impliedFormat":99},{"version":"003533cc3fa10cc457668d4256d21a65706a67a04251962cfe85d240502f8d67","impliedFormat":99},{"version":"6c00cb8a4b187505dfe21aff242b07f69f84f5c832e8ab4357af69daaee1b0df","impliedFormat":99},{"version":"de14ddf9d780367c6a117bd8a1718d491aff66094186523b3eea680ea7035a7c","impliedFormat":99},{"version":"ee06b94d0521cfaf91e4b003518eeefc45bbd594b0c22955fe35be282958252b","impliedFormat":99},{"version":"9e5f8fdaeb03f1699392b4724a58ca7b47c5cbb6762920d2bfc722c265495ede","impliedFormat":99},{"version":"d05bd4d28c12545827349b0ac3a79c50658d68147dad38d13e97e22353544496","impliedFormat":99},{"version":"a1597b0039f39e9f3eeaf120f02d0c94a826fad30b027a2abfdb8d580c89be70","impliedFormat":99},{"version":"04ace6bedd6f59c30ea6df1f0f8d432c728c8bc5c5fd0c5c1c80242d3ab51977","impliedFormat":99},{"version":"57a8a7772769c35ba7b4b1ba125f0812deec5c7102a0d04d9e15b1d22880c9e8","impliedFormat":99},{"version":"1de82ba3718b2b3bc5333c5bc35da5cfc46d1b654edc012de46bbce48126fcce","impliedFormat":99},{"version":"36eafdce35542335372c9104a44db2597b5ecbdb11af1177da13d457efc94fb3","signature":"d6ab9d3f4bfde85a62fea7182d4e68ba2104946541f47eab58799009a38ba2db"},{"version":"d153e1cec75d95055701de32dec8d0ba9c9a89ce85bd371b7d51fa15e495137c","signature":"2eefd9c7b8dddc8d713b7ec2f408480a4c1ff14f1975c85f91834b8886963f8d"},{"version":"fbe6ac1edabe62f9565a5dcf644865c3b839c759c6b81a0991e7314186c77c14","signature":"5e5a13138a956d69dc4e30dcc820b816b253b6907b02e168e1067a6f026bd4b3"},{"version":"86d4ff8ba66b5ea1df375fe6092d2b167682ccd5dd0d9b003a7d30d95a0cda32","impliedFormat":99},{"version":"736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","impliedFormat":1},{"version":"4340936f4e937c452ae783514e7c7bbb7fc06d0c97993ff4865370d0962bb9cf","impliedFormat":1},{"version":"b70c7ea83a7d0de17a791d9b5283f664033a96362c42cc4d2b2e0bdaa65ef7d1","impliedFormat":1},{"version":"eb541103f61ea69ee1795085a473b727d46fe49ebc7091c721623b5ecd87c0f7","impliedFormat":99},{"version":"014ba72e2add59d6d2d2e82166647982c824639e2902ccd7b3103cf720a0cb65","impliedFormat":99},{"version":"e22273698b7aad4352f0eb3c981d510b5cf6b17fde2eeaa5c018bb065d15558f","impliedFormat":99},{"version":"b78c801c3c21015ee487f6494448bcff55bb6b61f41172dfc2c26f2218d99138","impliedFormat":99},{"version":"de97e016d8dd4869febd5bccce02eb96957089d04b74ea5d1dc0e66112493b64","impliedFormat":99},{"version":"671ccab2e6a253d2516c0e4699b3077fc30cdb70b4436d8c79d76c91266a1a94","impliedFormat":99},{"version":"a11fbd8ffbee6e5a7fe4c7c23e6a391be615de2e710a6946d7d1f947a85a1374","impliedFormat":99},{"version":"2d383c515b9b606aefcde23da9c312a69bc7976b75abb85c02592f7a8589a343","impliedFormat":99},{"version":"e760f7860d08e9d42b6ecd7dd341602fbc0c13d60eb30beaf1153f1c7c44d66d","impliedFormat":99},{"version":"fb04e1ca667399e7302c033656cc285e6c1cff9c29f264cf229dd25e3962a762","impliedFormat":99},{"version":"ca6fb77e3480af8f2287ccb756ac88d047ba8a8bcc0512f6720ac1216e274ea2","impliedFormat":99},{"version":"c0cc44b0ad2fd65c933d187c4faad6157efbed33c3c21023802aa6a89d9b9d13","impliedFormat":99},{"version":"ddaf5d3ddc45282b19fb0fecec91c87fc9b4d1f45c2ee611677345c81383c5c5","impliedFormat":99},{"version":"5668033966c8247576fc316629df131d6175d24ccf22940324c19c159671e1c1","impliedFormat":99},{"version":"d76df1670eeb97afbab6c87b8cd31bbd09dbf9026ff0ca533b5d7d3fc0291f79","impliedFormat":99},{"version":"e9b947b944887cf2cdea2d6188f412a25125eade82f2fe2334658af86f14cded","impliedFormat":99},{"version":"bc05fb9d657d30e61d50d690615f379b0d0415b8f29e69196e1dc6bfc664dc57","impliedFormat":99},{"version":"e315bab2f28d53f9ab473d9de610c455b6c414757bb19589b31ec8f490cebd4d","impliedFormat":99},{"version":"d999dd5abf4befbdab5f1248193cbea69b323b71131a02bb120f9462807fcd5a","impliedFormat":99},{"version":"031f1805f87171e8a9125cd99105bea4a869018ab2356c2e29dca7c86925510c","impliedFormat":99},{"version":"bdcb070ed484b40b84dad668b58e4861f7c3d36f38632072dad5f905bd8cc0cb","impliedFormat":99},{"version":"54a97fd1e2f33041d7b4cbbe8fe3235f97fdf1a05e9fa41e78417851bb8f1c78","impliedFormat":99},{"version":"f63e0743e2ac9f7eb4d3b8bc110834465f164eb9e75e4f531046e4ff9822f3a4","impliedFormat":99},{"version":"0372d6d6a41ae89f9aa86eef998b44bc0ba035d9d09c9226e0a150ef4578fc3d","impliedFormat":99},{"version":"cd8a4297d0ab56dc571dadd2845e558c9d979fe1e120a0dec537935bc8a36dd2","impliedFormat":99},{"version":"079a12cb0e0c42655d77da5185e882b4cc94bd5c6c2131171a9289fc1f4287fc","impliedFormat":99},{"version":"50cf14b8f0fc2722c11794ca2a06565b1f29e266491da75c745894960ebbce06","impliedFormat":99},{"version":"d4ce52c42c23981d958206037138e05f7b48d41faa1cfaba7e9eecce8c2e5489","impliedFormat":99},{"version":"8a3be5afe0275ce84a6a6298010e66d54d2d2f8e927df6bcde0ac326b5e81792","impliedFormat":99},{"version":"167edfac7664bec77aa2efb2ce9d515c41b5cc4269091a946b3fa6ec4e7e8738","impliedFormat":99},{"version":"218997a627f0efc4b8be5e6bc0b58e0c9edf250baa3674b661d3f7e6a7ef21e8","impliedFormat":99},{"version":"c3f1acbd39f587a7539d435d6c78ce8647b3bfdc5435df153a70cb2656b52b80","impliedFormat":99},{"version":"a1f568855887e2b5121e8b5c4cacd66dc5428259981c47026d95be1d844adf71","impliedFormat":99},{"version":"1db6f2cf99c83598669df0c7166405b0e83c153a03b59115000df3cb1afba79c","impliedFormat":99},{"version":"49af73d71b88a99b1a211ec02bacd321c21397062d253c605bb8d140082eb7b0","impliedFormat":99},{"version":"a50de7f1a7eadab7732d80dcf9c8a0c0d7d00e33315425316757006b5bec6e46","impliedFormat":99},{"version":"4c6fe268c2a984b3f14031a4b09ae7b2d9e51673258f4b4352e48d0c6ebed679","impliedFormat":99},{"version":"3bc3d81dbfb842bcf15454aacbe37cfeac57f1d15e829812ad02a05cc42be873","impliedFormat":99},{"version":"016952415e1ad35f9070b4d454946e79cd3881f3c76c0978d759b168fe033018","impliedFormat":99},{"version":"6bf5dc0c9f6b6c79fce77b56c985dadca4d4d474c9abf9139ae0785cb5c01992","impliedFormat":99},{"version":"d507276467f554e383b4fa058f42b8fdf5c15f1d1db84a2372bb569ce9d57a66","impliedFormat":99},{"version":"b3af5d182c9ef267d58cf43f3e51ab73986862436790a4dbf076b5994758d53f","impliedFormat":99},{"version":"1c7f26a88f861afdccd8f9d9e793f1affef4635d16d2609c487480c41c42f253","impliedFormat":99},{"version":"4d4551dcb3fd19a4f22aaa63c6c391d42ce44a15602a6f6a19d582709edb24d9","impliedFormat":99},{"version":"a76075b5aba8187b1fc5c8f565745daed6e4341e64b44e6ec41412a16d575d62","impliedFormat":99},{"version":"f836bf3653e31c3bba120071196c95d416b83c5d860ce27549975f8785cd670a","impliedFormat":99},{"version":"058d970583137cface729371715449aac0c1388bf7a5ba15e0be952677485fe3","impliedFormat":99},{"version":"31c45c074a9acb94dbe340d9336d3c915635eac2df3308916fcf41f2ba6ab84c","impliedFormat":99},{"version":"774256d456ca1d8266f6e2170a51bad2659cb7116334d1e7977595999533a5d0","impliedFormat":99},{"version":"07916d3ee50b94b3217c0bac71fa9d70d48f51586481c7cf61af2a8ebc2a9db5","impliedFormat":99},{"version":"f687f35c2206a319dc7d8f0b751e182638c912838ff54034fb782beae50f7cac","impliedFormat":99},{"version":"1ea2d362005804d980325c2fe6ca0abbb145197b856a64d80016554129966c97","impliedFormat":99},{"version":"7a81f15892b1c8d0cbfb35605038ce5c6d0cf93542946aa0b8c415dbefdea1cd","impliedFormat":99},{"version":"22ef1a1604bed6e226888a2414676ef477a7ad5d6ed907a62d6e40c831797366","impliedFormat":99},{"version":"2ab500573da35083b48fa8f4fe719860099d1502df3384f977eb22ab6b14caf7","impliedFormat":99},{"version":"9fd7d60e314f01c950ba31932c150dcec5db2c82de3c7fe0d0d24ee8b54f1fca","impliedFormat":99},{"version":"841d1f32f66723468772802e1558eb36ae0226c95d0527a3ebfcfa2c75b6f6d0","impliedFormat":99},{"version":"d3327c9f7dce1e11f5ce85b8ca921668a13068980f7f4ea4daedbc2c81589e9b","impliedFormat":99},{"version":"a698ef4e27ad6053ad8c189b53c468f857501046aacb554eb52be0403ba7f262","impliedFormat":99},{"version":"94b576c860480aac3eafdf904cd81755f5c9b16c3e0ef3253953a8f4fd8cecce","impliedFormat":99},{"version":"8dc7c54b72cb2a49a7639dccd99a559c243667a74abfb09545cf8afaecc58056","impliedFormat":99},{"version":"d2166d3793936235216ee5d014bcf0d8695f3a954ad54c01b1976c05f544ceea","impliedFormat":99},{"version":"41bf8c3193b575946682ca243de53370f61917035c3ff3fb747067bc680f2509","impliedFormat":99},{"version":"916fafd410b9c3a04bb3720774b0cca93d1dee94bc88b4ecb6edf56cd5585abb","signature":"50e5d708858d82cbd8bd30ca7a76597632b0dff659403765266a4891b35a712d"},{"version":"ef82bdd9d674d855785bbfcbec2181e8d602c430bf73b4b65ef581d78ecdc64a","signature":"24644a17b266badb345ca337c9d9c80300473c40b2c87a28e5a3ddc011551909"},{"version":"846affbec83fefdf905e16b3fbdf845edaa248b5895279498aa6ac733ff2a4b8","signature":"71e597ff732221dcbf043d2de4000ccc5326c9ac63b12f2a27f89b5adf18e609"},{"version":"227f3a03b267191752ed1a2381855cd73b0915794ed51151e5ce82ffd786dbde","signature":"22c51e70701555882fd248a93bda5c759c024c0b88a58ce37a54ef186729e795"},{"version":"4a700720ced7ebe4c0c973bfc450c6a7ae31f82fd447e0f464c7171562e8aa53","signature":"212577e3f6db3f7bfb26e82ef9385a9c0c241b3906ccb6d80e4ae6bdd657e00d"},{"version":"b69c8778c50bf0caee3dd1d2da2fc7d5f6157498b51cdf51fac81476850c715f","signature":"0270d8376c084b2e07697ef2de94f943eef66b6de4b77fb20147d306f645e990"},{"version":"39bc5068f51c657236fa9a763dde5bdae05b46bdd49d0390c4a72fa9dcb45dbe","signature":"2f67546822e0445ed6a5fc1d2e96bea837385d7b11803f8214835933d03ede63"},{"version":"2a6012a4f4a4695bc0a97d29f47861bda054359a9a60d295ab26f416f95e8940","signature":"dfb01e57f4a98a678b16d78007abe78b8600ec7545e63331d72a0daa6ce961ad"},{"version":"094e1a72a14a0f38f950e388d9a4e8f6118b493a5918235de9781d5c47f327c6","signature":"d7cd6120b5ccddff937be1aa22a538829f8a93ffc9b4715519f67bd21da26689"},{"version":"60523c590e7ec5b89c49c0728f8b64ec2132482709ea5c5909752d6d68c93401","signature":"2315efae7ec760b18fa4c15f987003721972b75388eb00f80f3a419e91159751"},{"version":"96118b858afcbe20db893025dcd75e19fd530bd5540c65029500e8cc251c34e1","signature":"bad3fb3837da6b89c49e110430e58827c321031273ba09aeb1c83a1e0e9dec70"},{"version":"17ae4d2c0c3487e077a7f6db647df1ebc56194a135262b8af15b19a3a1072452","signature":"7d5919c7ef0b53f308a78754ac25e352473a2de6bf6e25e109cd03dd493aab54"},{"version":"a389c1e6da14dc436285d19455229c3ecb445f0d26b4de5e4df0c223e43545c4","signature":"7e2734061c31bb7fcc162aa37af53a181cb8db1bc2ea1168ecf1c816cf52e045"},{"version":"c40cca5deab8288e95cacc2e5f8d1d2717f9b49e3617cb3ac992847d5a143fc3","signature":"d23b1f070ca79bf4cececb66c23b78eb3f35e10b3ef7d0119549521c9fd2ccbf"},{"version":"be28318b8f96ff27ce32fc1882bf6f18e306dc8ae65ca3361d7769ff98c933b7","signature":"36b9e77029aced4614bb01342291bcc4fc65360f32e9d3d639c8b38edfb86169"},{"version":"f8fcc667b4a4cc586bfbf3d76cc17e91bad6749ee634f736ca957ea7377cab3f","signature":"4f3963b6ccad89bd71ea9c5e491a83c9b448df7d36a01ec887aea29400c52cdc"},{"version":"fee446e0178c52a271d63c9d12598620eeba7a0a0178def71ab7eb70837d7f26","signature":"5eccb4db63e70774c70de6e6e6f67f3f4b26f2801767073541a772077c2b8458"},{"version":"dc1d17e168090dd4df773ba839410a2e106ff0f43163a994f681a0044b4de578","signature":"6113e636ad4fffa72648f2a41eb42ef83262089973d54e080d130cb4219dab4c"},{"version":"01ba304e845f2081cf6ce244153824e727d70d9acbb973de8e2b6340e4355185","signature":"9adbd23317b76a09a9364daa02f7ad358ef30c277dca1b05e8df356f7751702e"},{"version":"e8ac8a0a426c433de3f592188e1fabc47b43cc63be440be87f36f5f90980fc56","signature":"098fb9262c019dd7c7d2bc1efe85f61d7fef30a8c6ea0267398aee95321cf1f5"},{"version":"5e8cfa753701ab1bdd8545e9436da3c53f24c179978efab36a5d919484516735","signature":"03f8247a05f82c8f96aac14f06944d2dab5061d08fe71d98e429251144b7d9d8"},{"version":"e9ff70874950ac2fa288fe64a6fd622a06d3579bfd781be96bea79fee7fd1381","signature":"1b758ade259220a7723152591ae4997ead9ed62664cd36c08289f94fbaaa5511"},{"version":"574fede341569986c736fd5468e28194913dfe16685e804a5d5fb0a24e71384c","signature":"cb0d718f6419c7cf0ae1316734875540e46cc33e10e9aeecdf79e2f29f6eab4f"},{"version":"2a02d329b308ce5a74632fe2062c72c049e672e5941c5a8204bd14408859c3b3","signature":"291d5759f6ff0d7180456b40f53ac4ec52d6fb91b6688aa2748e70feabcc8124"},{"version":"3970978fc2242f1ee735a0b0f0dfa8a42e17bf5bc8d9200e4e7059c63877f4d9","signature":"f8328683d5b3f602c387cf200cb1726c422dc90197a7fdb0d578fcb7c9bc6786"},{"version":"7f4483fd17f3160331ba5ace194e18e3687009b63ff4f66489586a25f497c60e","signature":"4550911de88ba268a6ebe2afc50d958d54100677ee698557cc2d5a6a36e100d9"},{"version":"9607e2d3418c1e50af1dac762f78b031f5f9c24f13ca4990b062f27c4f09a340","signature":"4cb3d1e907efe7537c8b4603e87bba3e9afd8e3294a436401b5b95fd2bdebdfd"},{"version":"3f9ae10a4a447dc5fd8d079cdac3d973bfbfb61149d6d34421ca8ccb9fc25a8c","signature":"6fb16d7f85050f01ba2e8248d33306db324277a87040aed2ac58e20343a0c2ac"},{"version":"d8a627c1f6473ead38c4a7fc6c22a1718e4f4b83855f85eea45cf645aee63cb8","signature":"c404855e249e727a187122c5a1809d1e93cf3bb3af6d64d68dabae61754a2da7"},{"version":"b9de8a489cf0a4692ea10cebeb35b2dfe35539ba92c47f23aae36f1aeedffc3f","signature":"37fa56790fd8a57b9e8e21bd7f2aa4cdd33b7a833ae9626d8bcb9eb41e0288e2"},{"version":"7ebcbdfd5763421e021e5472bdcde0bb7dadd2fc6bb2d81f70309a89362155a2","signature":"d1e471f636d7ec618d53420476543d28b575d5c30f18b86931f648214ced21b0"},{"version":"826e0ef6771e8bdb186b153dfac8f181926f29570c7443560bbb665099eee80b","signature":"39dcbe7a573f3d3df729c6028108cea477260aba94ca082a95de9d02a267ef27"},{"version":"f955a769066260ff6a27a22deed2c93ed071342093caf86e7a6309d35eaaa480","signature":"3b93cb46f96d399b4cd9ed35122df5e43b202839ee7c8282632c5c47c4e697e7"},{"version":"60ba574b03771c2da031380bda16f8ebe86e64be2a04f31c53d572edf987d8d4","signature":"c81efaefef37848e456f15aca0b42ecc599fb9fb73ed61c95fa7f7851c280506"},{"version":"316b866c3bfbe957ec585f572fab4b2f7a35e8d9cb266dffe597e57927a5d66a","signature":"19d7ddc11ff468813dcf97fb05f4e51d6f78e16a0030933a608aa0fb9f2ff9ad"},{"version":"a17a4fdad4f5f7be2b342254233644413c8ef984661db43c951953933083d8e9","signature":"209ff798fc5f35a3705982a320e8dbcb321571e046a96de4092b4465b74fcdb6"},{"version":"db6562108a47f4a746b4bea1694912ec1ac7ec51b48e3a31b274b4c8102ab772","signature":"e1a2f10bdb3e04997994496c5f189b4eaa3bcd92e06761164845c59133af8c4c"},{"version":"85592302683b0f3d636e53a571bee7fe59803339b8b3eeaa9a5f3e43717bf81b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77e9654c2e90c0915a4894800e66a9c269ffd3f0fe06bb17c14bdc23ef7f5d1e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"861c7b678c64d3cdfa0ad2a3f529dc1f57ad0252f6bf7db739be18e14c79c617","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8ab61002066e314d8b266725a4ed3db857e41c7aa11927aa4a38386370204af6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"62845c5b09ae355ab3bc4c4745dc5585b77b447706ebffb09ea3641e5c963da0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca973ee48e138941af5747d17cc31073d1d08f57241a81b8a6370fba035c57c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"734738e3b2da030816fedead484741535108b3bc904a7b90a6f8b2176d05970f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a2cb0d579cfd7ee8015c6adea94ddfeb2d7e79c040ae9ea9b57275096512bf0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1d95bd896b6216d08fbd7ec10a33b40d09d711e3fa102786292ceb82e4b8193f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6dbc20023316e17c6ae6382458fa64ee65049a6367dd648e89ec443cd59ca18a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5c6eff88e726a6c9ccb73bd9f6b02dd0e248fba87dc47e7b3e211f3e9680b24c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"31cf5e25aaf868c1ba0e195b7b62f5cb516b70a5bd6e2ffd8eebad1bb011c24b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1896da12486c6e51bb02c20eaf22d1826fe48349e584f2c59c8506e925172b44","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"daf5363c1b026284d12cf8d49242827d18004e624ffd100bcb74536ea92645c7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5903b40fa3676f924372e37bbdca65ba67e3191a92f52852d5b70a2153f664c2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f7e7141383221df9f2f32fa96d8ac65ab8cda4e4f19eab9f69c24e09ca31b172","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ac0bb5930db453fdd87419f223d44c23e8852223300428032ca09c4a497d9ade","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f3acc54979130b5a6786b3cb3a1f47f0330b6acecf6c509a09c071a1760e3f09","signature":"73351372b4295fa8b882bc93e30276d7a911cadee0f013b17f66d50ae3de6a29"},{"version":"c7b6e3a82a16fd54330388cc5023d8686071c102d3a4cb1899a74064910e7704","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7739faa1d7e4719d14729d52aef996a6d8c8b1b1447dd9441728c642f46d4f79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d1e9b0eaec1fc9821665f79d5cb10b16f5aedda997b77f67cfc634c219be45cf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eddd32b79454e4df90e6e3bd8d43a997c8813aa61e2be0c79875c87a5d9f7b2a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2c217dd4af49f75ea5671d76d7129d3d3154589fd0193c323ec2c687ed13ca62","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70879fcfe03c15515033be18baa3afa57f4f4a6d6bce8801e87050b02e04df55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"811af3b90fea77ad0ddc26b1a7f884d9366a44b20efff4c2a15de5bc9b35bb2e","signature":"16a8c433300e1e2ba1998062452df2b0ef51cfd21584e8bdb0553d9b0aa8bd5c"},{"version":"03a54222bd4814fd65957c052e3f6f68b85189a69e50bcca23e1fbee4238ac1a","signature":"ede3e24a18d5288414797441a3b532bcf9dc229cb41a5bcad089a4814f438a3d"},{"version":"e292ff26b159b2acb49baf29c18d233486c78afdd409e727b71ef6cffd21378f","signature":"2fd39dc262c1fc3f21d6e25374b30919d9315210346a585dc31a758918996577"},{"version":"4ed889a15e24a9e0f20d3642768a080cbf47254ba81997f4c88a37d1a7d0a7d0","signature":"f1088a946445f681d8bfd7cac8fc99d0549d70cff4e47179d361377e529118a9"},{"version":"a161552e025ab65f8b854f9a3338f8c69229c0493b13c323252d07407ecbb1c1","signature":"b5984247ba3e47fb79e844881c939f38398dc60958d0b29f9cb87d0e29fe73f5"},{"version":"154af56b732ad2cf00fb80508d1f3158f0497507c9309670b66758fdc0461bd1","signature":"66383839201674f99a40f904e89c5c9454d3d344ed91210206f28c5776fae9f3"},{"version":"70e051b3ac6969f054669d0eec72f57662efbeeebaec77174e96cb91dd3d7b9f","signature":"de7b7c00fc17f6accb9531e5271897cc70db0063fddf8a17d735db6fcf91b395"},{"version":"e8dae3ce6fefe470c1bcef3bd55eed9f3bd5b5211ab5ece54899783b80cd7b1e","signature":"31c27b104652e1136c1f2c56ef27f83380ae8587517ed95205649ad261a45812"},{"version":"1747682b50a243bbda982e8ef09306e5dc2bf9b0a0def44da795c851ef31d6e1","signature":"fb891c7c97af75b1638fcbf3b1cf51815a1f7aa9192f202bc4fbe295827537c6"},{"version":"8acf6816ce4505a5ef68bb1ccd8d4fc30815a83e00da1353b90102ae5160da81","signature":"997cba142aed5347c9d15f4e15f6daef2889c2fd037587841778b8ba476ea168"},{"version":"0c82b7ea29dce9d9c5ad81687769d14ff730a377fb9bc3c03cb16fb8ebdfdcb4","signature":"9ad6faef6958e6870ea4aba7cf6c40cf2399cf55b92f7bfcbad371186edd9636"},{"version":"80c8854964c4a39f42fdcf47a985104612a776c8de5b7e08e929c4389331a06a","signature":"8bccf01cb22376a08d48b284667f4b812ed8d38f2b723d6aeaa8c96d133d2ab8"},{"version":"ff619d9cbb2254ea51e7d71384abbbd5d72f2c93c071fea9c32b64ec3342888d","signature":"542542c33ba947f131b795d3630b41a32a460aa588a03aeaa5eecc38f3592041"},{"version":"5c0d00b05aedbf7b0bc483dcbb388e94b5948cfb1fbff930af60dfd9298dfc50","signature":"064b44fe73f0f7a084ed8e01bfb2ccbf0a6203b191fd521fc2f9c76d21e652c2"},{"version":"e89aeb89eb7cf6060c0af880e07093c91b08938d8b3a82a8a9b8fd5ae1d056f5","signature":"f3a1e1f72b8affbcc2c49613db77c9ff271fb02b9db2615fcfc355b194855b08"},{"version":"e8373b5d06c8923b34324f9df29eb35bea64a6a995b607a0b0fb2fb8c3a3a140","signature":"d86e8a694d25e37475962cecce34728a00aad3f3ae57454fb83e80c2e559cf84"},{"version":"6be1b5921e30052b789c02a63eda3517e0686c0d8e359d9ba5bbbee4e738d1b0","signature":"38d9938720a626eaf80d7c415abdc14d165e67cc0a3a5fcc4f40f0a16d2ce5ff"},{"version":"0b208ce8494b358652ba9030cf0e14619451807547d25b3b5b720aa57bb940cf","signature":"327348398994bb43ed73a2877af0f313518ed43453dfd4c68b77f47b77611738"},{"version":"d985bff3e70be34ddba319f5e9209e8eb799e392218201acb3afbd77b6ad4d5f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f45030e7b52dbd77b0e101bccf5bbc08537605f8fb10927b0281a51fb2abbd6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6897d0d0498030dd4d7b6190a78010c071e924f62811f51897f63268faca2248","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbc7b28d500a1738964097922cd6c6db2adb129dadcfdba9c1d56b77697afbfc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a219c1949667d439c27329b94cfdc416e2839e8214497fb621c491eb24cf3bc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20769f36dc2e033c8fcc237bc2d7a75682dfba17022efbe30ae06f22767869b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"05a2797ae1b679bba91ebd96c9fee9bcfeee3b3dd3e400ebb3ddbedbba606306","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae2e40a1fdc7fd8cdab6be243e4541f50b54445387834299471885785e3b2489","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"96fa384dc9c129874b902106257491e15eb6cc80bf921cbf2906a779ac96e60d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"db3b9f398ad210cb961c2b5d638e28f99792cefe9c50c81ed383d2942aa226e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b3277a8f5c5b9d50cdda98e93cc145820c3983f5e8aaffd31f4316eeb0ce465c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bb6288a9c750095a16037444e6026de8bbdee3e77af676ceb41d4ab7a8aa465d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3780496f95996c8b34890fae81d23cf74833dddcdff259a695f7952e18c9607a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab9cef431c5ba3ad0da377558211af661ac8ef1b0e3bc5c66bb36f4cfc3ad177","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"13d80d19b3c6cf01d42d51623f934ba1ce71c75aadba05d91d0d67860d86e629","signature":"0f8aeb5191b3424d8865e154335c144f70d1d1bd13be767471d43557fe97ed96"},{"version":"f2b455abf7da931e2c6af9e90e22ace14c7f357bd2cffbddd865d15b442030b5","signature":"5776f350899f8d645398751759ebb4fd2af323fb1171a47aacb022f5d707473f"},{"version":"70b399f171d822aca03548b1644217d752bbfde4d4ec2cfed1a214d7fc79840f","signature":"cd78f41f9d6f04e36cc052c74bfefb7c2db0779f89d2659aa2c3179b054b1c8f"},{"version":"d02be3bb5d64b49b5ba9e768fa8448b5c127d09e2b8ba14026773c1fabde1592","signature":"cabe51c63a81cba61c652a509354e7ab4fb262ecc04a5433dafe9edccc3ce1e2"},{"version":"a88dcc474c044ec5c3ac8536ae40771d408085bba71d322d73bf2204ea023dc1","signature":"39000aa5f4d43f9f6cc8762b6cd8029cea4b6f0511e7d1f47e4d6ed7da095a15"},{"version":"b1d512503e816355be4952330e0a427949fafd8cb3ee124017b7a535dbb26209","signature":"89f540ca38000b4ab06d97bef735703c375a50b0b4aacf9f4d28c14cd138e59e"},{"version":"842955471f601e4c1d21afb2cbb3d250ef424ced61416e8d7ffda5addbda9eb2","signature":"25e6d9fa0f3dcbfeef48b9738ad3a3efb1f07f8c32381d838fed05543afc20f3"},{"version":"493e9f05ac502360eeea2d5c72d28984c6bb2e03dd0c1bff35e2e5265cd8a6ce","signature":"a27763fffd538d56a65d2ee0de520e77a21958e31a87f4cd0c57efa7b9cc348f"},{"version":"5dc03bbe2c52976d8b054be1fdfafa1b7e43f328bf48a19d5f62f0563dfee905","signature":"b013ce777eb845733b2d4fb5608890fe38f7a0829738da416cfed813adf39080"},{"version":"3e06d8650c98a672c6d811bc035a7fa2561bc2c87ad01172e5df460a8629d489","signature":"4b4d1c5dcc9a153360f0bea18e847d0123bab4e18678d00780beeaa4e8ab01bd"},{"version":"feb5cba45f6c40b8b4601f40eb48697fa7e2f7e3db51337f15c308cf2800da36","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c0638b8d32100f3827e3535c4307c24b5ea5e7ae6b33476db318a8d706386626","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0fb10fe09a6f0c5fb2f5f7bfc0855cbabc27cc4fb9fa3c56e5956f0673746ddb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7b0daa318777bc57de0c9198d4ca71d7f1ee1e3f02c5bd860ec5bf390e08fce2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4c5ca16923d08d3df7ce8003095ee7cf136956b93a3a87d06e046c967a07d379","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6ddec7fe4e03cf6c98a431bd4b7998cc9a11ad1f5aace1c73f6a0784c7c9d503","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"048955e8ea4fff7090afeacc7a8d9a0d843199be15fb1ab402679f63d2a18a54","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca2ccf002342ee1f87be1682f2aab080fab7316eb8f37aaa6a76a58859b3de76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e85d9d5252dfb1dde90672424170e3b89cd14b07086f790d3c45aa3f023a93a1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"789e5210de191f9b2c090a2acc40b4d8a1e86e02626cf05cbc6b60079b132f3c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3fbbbf2353edb06efd1d6d25286c1cc267f3346dd7a73424e35606ef0fc04eb9","signature":"d1caf598b76a5d9cb02c68f802fccbe10bafe10d88cb6c0b78350e1b63f44ba9"},{"version":"d86965dd2a2cbcc99a7fe6d86c26dfc2509fe3b640d6c290e141cf41d0df613e","signature":"226dcb5115901da170f4421a0382fc00bc2ba83c97d9bdb91ecfb29ef4e8f2c4"},{"version":"a65cbbec2ad16ed203df68cc7474b8e45722ec2a6d2a6a6f6c0625754c9a63aa","signature":"1c1fc02bd4a8c8331473b19ae20b92b8473d8337bcab684d5bbc262b7a31f07f"},{"version":"4185e0d631c6b1e03a2ab79d937f8d47f25695cd2065f88744c5d237f0dcc4bf","signature":"dfbb24194637a7659be87169ccb8b885de89d979ef098ef29e16b314938b16ec"},{"version":"5efb20d08f4e3e7a5454d5403a500512464a4d59d30dc442a162e9f9873beece","signature":"19483d92b38fb23f95f7dcf3d6bd88df63a983d3d8c11fa3b4bea4c6d8141059"},{"version":"a50db966163020665ec8a68d0ecd79d8a9fb0d059c0f4d25ba53bdcd7e43cd75","signature":"d406c57bf0c60a88a4cddaf0944ed69a7554658e068fb62559e2d823f9255236"},{"version":"5880d909fcd7aa478c019c0916f68012f10427b2d90d203a9060517bb9ce4de5","signature":"109a47009ff1ea87255fbb9bd75f5f3a918ac7ab44ed123b521028f580aff53b"},{"version":"1bf7d9677be10753f2ede73e0578c4eff97e9b8fe1b7628d0d49cceeced9f051","signature":"8146af3e7bf09f628b043910cfa6b72b86f8109aa7b06167b9eb51a3f0a75da4"},{"version":"d97193507f74ca55d696adf4c7bf4dcaa581cc38da8993320385450a4837b988","signature":"5e25c87cc967b7bcd7949f75916a6757b59aada3685fddd966093696c85163b1"},{"version":"b7426a25a7942fd04027ef39d6e57d3652de5850a59c04b7a3b74ad2f335db99","signature":"ed09ce0bd7cf961caa2bbaa0265743b1a22acb59fe82fc227698550a7f0b1e14"},{"version":"d7e15183b1073666220cad96a18914084528dc05dc1e2af175c863afa3023e07","signature":"7afb481364c9e976ea5c55b9b02006f2496e68cc009eedc57f38264121a77836"},{"version":"b56e4b6d8d241dc9428b20e7be5d13487de4d263c5999f91d547983fffd8bed9","signature":"91ee1b220ead097d3cc5b596db9be622f0dccd9c05e4f4cf069f2e1db077511a"},{"version":"c5391ca708a239529f9f132919def5d73d4cd67786f87536da7e539d247bf149","signature":"44248c8a13f35779d07d3168c64fe9a1040ea2e66bfc4ff92567095c5b243e55"},{"version":"7fd84794a97f879f03f3067cc042ac622063d821e7b60b27100ce300bc65d833","signature":"78361a8f013fc8aea9c04034475febbc49998b63c3fe09f56dacba1e1c73f8fe"},{"version":"eaae5968ffd536215d143ee0c4a295cc4ab730c6306c0ff39da500a259fffe48","signature":"11fa086538a611fe1a99a34d1378e2579a4de6eac405ad7fb9eeaa51836977c0"},{"version":"169d8b256bea5a05efb2049b4bf5b8d916d986a97fe9000ad3af60c1804deb62","signature":"6840721f787baca46b15289facff041cf00967e6d746b6e2fcf657881b6e6c5d"},{"version":"0ad029b491ecca9c3bc7994015f376562f9fe7196e2c7815a7e7914545fcdb65","signature":"87d3a353f4a5033a14c02bebecb39e225f521c82a998c294c33481b9c5198271"},{"version":"505f10cf78d9caaf7df503e3c495055785de4c93e0286843574106d787d9f97a","signature":"4929cd61e267755bf505ff0a66adda55af5d318b84798ab1b46ead808203bc59"},{"version":"cb431697c9e94cf9faf8cb15dc79c36f21d951f2ae68a6cfa106b93edc373044","signature":"d21e563fc29f32dab8756bf5797d4c39b98ff0828fe02f8b766a8cd0f2130729"},{"version":"3c5a7c91728aa49db1d5eadc0e9f0d724dbb50b01ac203b8c577781846962d23","signature":"b524e9c8c9572a85e539e60885e7cd27a4a3734d72040582434a25e486702df4"},{"version":"c5e33bc47d97c9161f3cf286f89238e4097589e4dc86632a8a575135353883d7","signature":"bc3dc9e5cb7a7493571d35b9b2fa5a1f39cc7ad76f998ad62e7a98b56fb8df6c"},{"version":"a422e96804615648c7cfcaf2e23d5353ce5dbd305ef5f5467c7fff7ab39f5bdf","signature":"063c721b1237aa52f454a374210ba793cc38a5267af12e5f937c7f36bb33b6c6"},{"version":"0ef1d6c0063b12f4dea951dc976267bd8e11aca63fddb3aa10213ebd2abedf04","signature":"3e0b6c4d0b2d1c058853b3054d0ca2f00a36d93b462a4cbc97e0e20de4917691"},{"version":"a1d10e7fa181933ae7eeb34361f76d99ad2872cf6da8542528df84e4311da86d","signature":"de0b7ec69d1de3d88340668f43c9b8ef7086f96671d741ffb16d82ef844fc18e"},{"version":"db4c881c4d0036d8676e76f60ff17c6fcf240dbea3e48b5961e17d9a3b73831c","signature":"a2885e55e65c47dde0e39e7dbe3f9d931149d439b3b3c2a4480ae20b609bdc83"},{"version":"c50edce2d99b12b4447e3de82a3cbfb87332a0eed710e31f3a5306ebeedfee9a","signature":"3d577b57ecd8ee26a71f8dcaa01d354301d4155aa8fc228210f7980278d5a40e"},{"version":"c82897568bbe3658edfc608ed84b0615593c444b3dfe66fb884fe1f6f9ea0254","signature":"71ed8f0856c314b1c9270b9feb94da47f13e458a6b7041e75ea43a5a48e6d8e7"},{"version":"83b0bd3a78d0d2fd8794a9983dafa4f7fe6ba8ee95f7cd956dbc1b5ef1ae3f76","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc1c0d6d5a958523960410c45f1e15874e8d8091120d3d7ef90f6d510b00438f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bcf28bc54cdceca8071e58514b793a72685363a07a190e0a093f90a74a9236e2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3daa25bd4bf7c1253c5bc577e84a9c3e08b4898204163d94e8f06bf93fe4657d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20fdd22451018bcdf123b42bcf8f3607b54ec5bfc1a40ce6f3aa195114fee50d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77aa32ef7822978656a1cf7a8955056e16072d0b6b3c71c8fe81998678532695","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2bd3da8d18b5839c651f5dfffc391a3f583de5e4a3d7f856d908a60f47b04ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c840fa95d2134e19a21130e67e75c7d75715d95f35921d62b1d50262d7e34cf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a6bbc1073d12694e6b875ef3444737c39391622528871c6663c36bd214779309","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6449a0751d077973e8e32ee57044cc4d03a50623c5a291196dbd19178fc3e649","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c8bb1a6ad7f07b0c3af284d80c5b76724ec9b6c2dbc1720d1af4018b571cabe7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5585e289b764f82b17802e044380f2f72b584c02f0a9e5e5f9994fa14079179","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc40f72f1c03ad660c6ad52cc2ec092594bd05e49bc5c960a4b0d30620dc55c7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"968339f16a5177a5ee35cf9b77108d92938ec1da02bd41e361585030b4f00da4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bdcb2c9e692ee3ee605a7704fdb479fa10ef6d4271ff6b9ff995d355d40e2206","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"044ad589837559611012aad8bd6a946acdc485aef131a351c8a01c1bcfad9db4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"53a1989193c0f9f558c62b7eee59b3ecf57cc7c3bea2fdd469ed4fa2aafeb0fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5af67615072b85cf169a9b15a5bc2f54f874f32ff594fc80135b0229d46ed148","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b3d3b1134cd416e62ad730ba82293706888320b0ab860aa34a61c02aa48789","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eef952dea22ec228c085f41b939f8824d7a8a9d5d53edf570d0fd162be862e8b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41c362e66d4ade6b4727a2d3dcf1a3249ab336e6812cd51cad747284736a3610","signature":"d257d8bae8cfbd36ea0ea1c5333150f5b290f7fdc60c41083a81153a4ca4cbbb"},{"version":"c83c8f01896aed99315ae67c6fb0a5c948bada628c8f7b19665a228711c2d340","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"03c10706dc050b16e0ab8f3c5adde2d44fd9c4510394ded88c1254b29614bcf4","signature":"a257a955f81d30464899ba91ac6e7caa9c165d10f49b0e06bc9cae4cdad3bafd"},{"version":"5c45a3522e7ed545e4c383833e4e9590374dee2c6d2a4d8d50080bfd4a41188d","signature":"a8dd6879adaddc6d84af4fff927c3da912e5c65198c208823a713fb268cfb047"},{"version":"9d2e9036f50ec7b8066dc9536bd50a76f1a2e503c4fa7ee1c92725b694600d94","signature":"4f2f07fd2750e73d86f4763ee55f0ba88d59585ca882aac5cf6b5218af52a735"},{"version":"43aeb84aea08c738c5373e8ea3b4487907677f3e3e619f5f583d60925d221504","signature":"c1f55fce6df97a3f32d64e8e2b485c90ede6b9b6feadd640a3c16bb6329c192e"},{"version":"cf6a54f50ebe9b1fa179e3ae972e17bb5132bc1dddc612dfc2d868ca309999d5","signature":"619b2bf107c7c61e145876687ac47175b223d7cbbe8414b8d1ab5186064bd02f"},{"version":"6b3d4163477739b98bbda0e3722c1df15427f4fcdbcc044d4ae093622fc07691","signature":"0e81f3c44d6d754411d9b3fda7802a8c6c9567fcc7298542fafd23d879519d12"},{"version":"c4af4eaa49b5afdd70def3eb9ee71b509fa90dec11ea33591f7a1b1822400fd1","signature":"ba31eeb48994cf91f0acacad869ecb708d6840d7a8df864ecb86522256949502"},"920205f073d9e7a7cec8b42adeda1b66e3287253232703d75676ae0ee280ce5b",{"version":"8e209754fc98efbd0db41c1da78eb4f46d55d6da176ad9c6988b5947c02ea59c","signature":"34bad0db72824bba1a3419664c94ebe765c196975aca12cc24a7bf309f3fd68c"},{"version":"c4368b3ccf0073b6a7f2526ea0aa22e8184e98b099727db5884414724ee62151","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc0c38dbb4436cef6d4ad0462c0b9230363a23303589e36042685c1132f33696","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a999f9be568ad3a72ecf729bcd348b4bcee26719790f21290a16b5bc7dfe839","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fb37ccdae5fa6b7325f5aaf5d1b28caaea4c148957839827fc5b1f7ab2b2e2d1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"177cba3134e2dee9afd65d1d508127f10141c81769cef693f3493e5f691892b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b54e6b18ed48a74d2d6129ca2ddda0aff1c30d2c46e7640113d2fe6669a5974f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69a9d780f923c1940e4c1676d0d7d02b961490eb84cadbd80376c98b07d1ae23","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b34a6f3217920c7cab89d81ea67dc64b48ebca1b4fe06e38852af49ea78068b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8a354ab401139f416005ba61675a503152089ecad7ac237da3d508779c29957b","signature":"b30c66f8aeae088710859fc3c16836dedd29bdc025e7304fc50dce105b9c04e6"},{"version":"76b797eb5bc8fe7158378f9ae1a16f98a76f4963b2a6eb40e1afce5f7574dc6f","signature":"f665a621665bf4b9ac13011827bc5cd5cb272d0adc1cf91afe269a599e6be31d"},{"version":"ba4ad41a9d1e344185c41db9d8bcd77361758caf451a76ac4844e690f096192b","signature":"6f3369ea3292063709715ccdc83ccf6bed46b409fbde2ac5c8b23bd5ca192401"},{"version":"fdc0244b111f72144b4b5ffeb4be73d77985a2c9839d87630366739702a7d069","signature":"0d7b280414b0cb316adfab6c3609f4d3b0c34aa5f942a74f5d0b330aee061cbc"},{"version":"2c7d3b8e7fac27fcfcce5e3b5a0043f58baf786e20e951be19087e05956faf52","signature":"6bf95bd5997a54eacb05169d05e4a3ac009a2ee4b1202cf0e609c84e711d28cd"},{"version":"feec6c48848e9e9fd2cc1dce253451511a02574223035461557a4bb97f173c2d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"213c42f7d5367619fc1f7a022520c0cfaf8828c3dd910d8abb68b229c44f97ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fe4805a16fec6d9ceaa3834ce1ab4d8d3ec80c3c41ad093c2d09e7d7a00fe81b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebc3418849ce69c3e4935c9c8ae98abd05c1bda372d9ad08cd259635d6bcf475","signature":"4a68e8778087ddeb83520b7ed367b8e7a5413545211f1c12181b036b98b46972"},{"version":"29775ef79bb6d19d569a24c59922476271b093a1afffb1678254d66e938e6980","signature":"1a01f741b2cf1e9d7d9a1bef2e8547b013e1ad3bcc8fbcfe1389c9eece787977"},{"version":"426e0fc6a30225af3662c056bd33bc2af1b58e67ebc1dc7c4d47aa9075a3df94","signature":"edc9cbb7eb4f1ec26911e7cdfb0673eb04ab03be7a74654ca4b68935792dfde8"},{"version":"bbffefcf2d2194e3c9cae686f981935765cee13a5f390c97363fed32cad90d63","signature":"c10afa01e312d1ec1d2e455117340bd869610913a3ddee3e1903060237b2d330"},{"version":"3ad0a06341e90f9e25826449160fccecf9e83b3ae923de99bf6318f2a0adcac1","signature":"8f0537d31f337710a710ca9da779d9fbe37da09f82a2bccb3159ce054a303771"},{"version":"754e504a4f7e802cc110ec7cfab158438903677b6d1e5320e3d06b4215f42d7c","signature":"f50252519f170601d78c919fbbcc6aba2864e344ef66c8dbae519080a9ab6763"},{"version":"a3e35d26f2d2ba764a55bf9af3fb0c22806c238a222679a83b40c838c30c7499","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4d1729c06dd03ddd24e92985fec6aa5863373fdce658884e07eb4827df021f67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b80c68e22c6ef3a8c82b3e48dece693fd7b4e628542ac28b02dff88b31385882","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"22f5bdac2994c065f821a3c19074445873b02b4c89c5c4d26f95fb7319bd7298","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"da3a4a4651c78a13301492b76a30f6b89aff6919a14ce20dc48fab5473b99bd0","signature":"b9e301a99266862c3a04eac2c53d225b50d31203c93c570bb44b51e6df966f6f"},{"version":"084a417795f6930358662f806575b15cbe6975483daa9c40ae3764a1ae955b68","signature":"9e21029095d6b935b82ef9e8dabc88e552da4446f8551bb8e66bb608e221e7ee"},{"version":"9ffd818baa22a5a4a3494bda2daf646849c2635ad622ea25e34f4ee2c9a8f400","signature":"f1223da6f0fc1fca4ad9ae12e3c41227b7da0de5f55b38d9b871f3c651464039"},{"version":"3efe902a8539920b21bd44d2d0bed08ef8a95d3c4601ede6848a192af8563536","signature":"651a00540b7a7805a8de79cc6972dede798970c3b718c8374be90813037437cb"},{"version":"0bbcbcd6dd929d9dad0cf660bb39c2c578888071f5a7d80db51857ebc1c57923","signature":"9205ed03aeab041ae8db74ab3df06c747ba006d4bf2ec67df0fe59daa1a87d56"},{"version":"41f8f38651d123496907c1aa1b37c2361f1dd2cedc3aba6cb53c9ee253c7e4e8","signature":"7199bac5eac9213b52fe3a6d9481a0d20ab76d2bb99cbdafaf6ead4e5914e7a1"},{"version":"4631ce1c40a09fbe889057b38bf9d6daa45137ae0fa25fbf1b066ca41ce6f2b6","signature":"c685b52193d4c3022b8210703605d2b21a467ae9387aa15a8d9940785400fbde"},{"version":"1ff67eb52f40826c7d5512f924be11f6bec373c92896df0df557a13a8658f693","signature":"9d0fecb8068df90d9ab52aad97173c385ff2a17baf50297fc75cc31b3938c945"},{"version":"7025dad7d78fd9ad96f064ff669d353f930ddddbb39aa3c4984144fc6760118a","signature":"7d3b48b39ec46eacc882956538307aeec6db56edc31f31be7d6289ec2c92a385"},{"version":"8a9404494ea982c2bff41003f8de1daf258f83a54e917a6df73e6a6201862cbd","signature":"c93a0c999b510d141f69facbcc4d763280501bfbf78b8f1cdc4270af272d805d"},{"version":"b1f167490ed130cf9c920ee60fb21e9dd2ea9e601e9567e457f609a61f2f062d","signature":"96ac0d54822a7637a651aad1726587e96e5adeb6fd3e92f04e0c957313aaa83d"},{"version":"786aa97ed22b1c1aadb445ee997a12785c863377f4dd4a45365a1a90e1bdfe98","signature":"3d8b96ad1cab0524e81ed5283ba02e42100191203cd7f5e1280500f36a9abfc4"},{"version":"01f31174c59202f69635b2957a7a556a01c2ea194906befae45997b6d3c470b2","signature":"12f9e010df1bc3628cdb97e06e5b41a3bd149a6b61eb4ed5d9eab248bf5e2b67"},{"version":"6abbb171efa9fad3d88c9320ec5eccb199b726f832482379414fd55bdd485a66","signature":"f9fbed20734c2279dbee3f4186691fe847ff186337649bdccf7de42363d02022"},{"version":"08bcfa6546d768789b5134c6344f18ae851abd4513e3431e91b5e955f07d7eb9","signature":"90e7eec60d281be24fac0f9230a9c60c67de9d04a915ad11aadac11ea2715da3"},{"version":"d9bfe44b7126fd3ce4741db90af68d24cf8a56104826770276ee19f133496d37","signature":"3bc6339203e14955ed7d1f9bed916418c12a6a692d269a609b3a33dc4a863951"},{"version":"ae6be9a07e940a6f4b0743220077f33259542ae908744ab349a1a70d22f723f5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70442efc456b60235ae408e157a6f0caa8ceacfac13f5af9d27265a6a3f57dc8","signature":"926011fd4f1072faf06ef6de9b938f68b655974be67f2ea7cd63d5ee58d69338"},{"version":"31409dc7d6946f1566b501934ee84e4d61916cb6893791c2da2731b12ef24b89","signature":"cb9d2bede0762fcbf1f1f6e59445d5671d08ddfd920f8ea01612d81b3a4384e1"},{"version":"36c6b6a3bbee15a10e445a9aad4f1287d7d1039b6b58224a01524fc62446e533","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"11fe25d3baf912c908f4a63d2e05da668fd9a9838f258130cca57cc5be4744d2","signature":"3c7233fe8f8bb2292a9100dc596c64a103235700d7d34096d5db17ec0cb9cb9e"},{"version":"ebaecc11e0bd3f3451f11514cc0ca76bb2c763d10240b59cb187e587b9e01f66","signature":"a406bd45d11ccf4449bed75df91936ec197ad7de0facd342bb0ac55597dd7cb4"},{"version":"56dd3ebfbb1e02d419ccbf55a7a298872f8090d3803610e2faf132a5fb07389a","signature":"823c47cdde5eb643974b725bbfada0576890962d21434906d18ce26b06bd9544"},{"version":"635713a99868407271583323a9aaca2958b2abe2ddd43d7f2ea987160f6ff89f","signature":"866bc33412b93d1a44a41d1a8ee37e688082c1979b4dafc2ef88edc53a7a999d"},{"version":"fb888ca2d1491a87202204b095f2816e2e8041f8a0dc67718d21e3e963afeaf2","signature":"f6eb9961bbb1fc5507f52b8081d6e82eb3ef5eab264a5d926518296dc4244127"},{"version":"4dc46afaf28a8d1726a39f5e7e00b37df65de1ed452b673bd74c90797d4c6201","signature":"a0a545639911994ff57683e710206d749c7d92037f30c37ab2cc070178a7fc3d"},{"version":"63b2318993b6e0dcf67bc21cc8aa94e41c7de4936bc0d33feed3828d589d33ac","signature":"1184cd7ebecbdb9ed966cea0f626822a3247878fe0a83dee49e89b0f89a92973"},{"version":"454b346ab7c6e6fea1963daa8abc997bf20a61e172018cc3fb4f3da99adc3ec1","signature":"29c3c744e646ac31f51cb4ae4b0cf912d8d251972c6a958b100df797025a94ac"},{"version":"fc357aeb6dafb0b0088062750d702118459f2385d31434c8ce94ed1c1e7914be","signature":"995cd4a56687721b9ebcde8c6499921201e7bdae56f437f08f6a2ec2b1e1ca0a"},{"version":"6ba0e711d73e317b739a4b0b083a109fc3fd294985c81e7f3c284ce4bc6427d4","signature":"08470625f34c0ff0200976ad34ce7d65a1fc9286f8b8a884e0553e19a4662610"},{"version":"bdbdf92aecef77ec1ce77d842bad71821d8c11bb84335c99cbab6e6519885583","signature":"d507737f7aa3a9dc2f94c67379888cd7e1e6ee3c96ca265ed0dea283869e2642"},{"version":"f46250a3d3cd3bd34994208caa7d245088a529540bdf459e7225f4752509085b","signature":"4c117079aad8524348f5b782625f9663c24202732204ab321cc56f0913c99318"},{"version":"2fc8086bb1e429d2786b7d38419c2dd195328c42631f4c03800ba8b7d691fc6a","signature":"55853877ee77b90e1a143d58d14c4f5c2003b54d251cc9ef4c809f8ecbd4aa1d"},{"version":"e558a1a4ba6ab77b902576da0a477e0a728245e47785eb24bcb352f5204fd506","signature":"fcf5168806c6395e7acce0b4494632bd61d8dd3b7be90a4b3b86fd645f898b3f"},{"version":"93835e1c93a13d4c2e896a885c90698d28d07b65788cdc1a9b55636eb05e57bd","signature":"3b65f98cd92e0cddcfa1ed665b6b2d2ab06584d87079d3ca16d473c24009b2bb"},{"version":"c15e4b4deaf1fb4877793b7cf7d89f6254a54419ce5357ef98a2f800c97825c4","signature":"0a6956cb83f672f2aaf173e306a81c48ef904b9444d51c28c5d07a7a90321840"},{"version":"df70517f2532151afcebc39b9984bfa3c5ee4677c6e9938df86d17dcbe6a8222","signature":"c55b5dd40b4e4244911fb70bec24eab327488b4b6414513f29d5c0d4669d8399"},{"version":"b6bcd22d528966ac3b3226ce4368fa2548b9d27086496f200acb6778b4be9e37","signature":"67c1fa6b68da9af0b53802564e5571e32e74faebcad03bee79ddba76534c2c28"},{"version":"6b79a17847fdbf4ba0c82d9a7ee2d987197680ca5317c2a6a68ebb5bc9a4a829","signature":"a7d6ad6e9eb8f49ed5a46f2764a8fba42de8ef651c256c04a16abc78d4b787b5"},{"version":"d99e261147a8ca0295f772e82edaae16711db8d30e2511b98c59131f6583c216","signature":"46c0c755480b2e33e77428563ef84a0fa949e17e287c83baed1c53d6e9fa9014"},{"version":"caa900f1d326dfd6bc47d123e685680bcc21d4462bcda44c92ca7cb4318efcbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4e14e16a2da8fcfa4982c8cfc32231f7ec7651440030365345e64956a86d5c3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"963e29c03860a04f42f2ca7723bf2f6c8aabcce3c2aed54a011716e20c1d65df","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3571219af6edabae2c952146660e1734804bad8169857f4b8ebe6433463ec3a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5f610e2b5a184bf4fa504123d543d6c34a35afa82f6a58cde23d70942c8d77d9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b6a64100f55b037a2788401e6a59d3850ce656c85f3e4a0a8eaf66a750c6ed0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"85e753e4bd1bde91bdfd4f516cbde1f0eda265356b0898e7e7623e45a1fa446f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"16ed5d4e6d9bf022f732e27adf9081604d593b3ec37e9c7a2094c67d115d6e51","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba2e7bb3085f0acf77f6e173b0318d0db592580a32aed9c1d9a4bee49693996c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc529f36460fcb82d608cbf7dfca17bf60caa2efcfa2ffc62dae265cf1eedc81","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d36609a41f06c7968f01527e56c8a1cb6d5697867d2c64fe0d46e0d8efba55ed","signature":"4eadc8f12f74708d36f7a73dbd6a4dba984b83f96a8c0875a27b54e88331c516"},{"version":"7dafd83200a4776fbc6fd2bbda38b6bf4743cd754535adc2d0ac4a5cae258aca","signature":"bb8f5c8174b21b9b1a9d318205301ddeec2d0cf85ba3a7cd68ca9bfa0517f36a"},{"version":"b3f296dacd56947df11418f474b12eb09c180449cc833fbbb203c13e657b96bb","signature":"35f2abe6c86b8ee3741319a9d7a8c3eb0230e9b42f7bd7d543db7a94dc4e9051"},{"version":"8ca12e1da31b750904a7e9c542da66d01d735bbe9b798bcfbf9753bc9451aa66","signature":"74a54bff2e7775037930699111384bbb5d74f4de57c1359b5880f0f86182f56e"},{"version":"4e64be35164e01cacc75bdc277a3412e76636435de76c0b193b3f3c5d4290d48","signature":"feb2d5fdc50e327f8560baa4feed95edc4e786e9b164d7718d7857a96f27fd15"},{"version":"5be3881384cca3b8cb54b455055859bb3417730c43fb7ea39b57102833b60ab7","signature":"ca02a04122eca135259518c85da5210e6d924d9a19cac98e0f1cd55cd75efdaf"},{"version":"2d4000626b78819a6a26c46ab8fd01ea13296c078a8ac19ca144933e47826a28","signature":"45988a2c99eceb92797c0825e6351b563dc059cde42a94107c00c34530b64500"},{"version":"d50a41d0c949f23759d25178df952afebb72b777ff9aa3dcdebbcd298b6dcdae","signature":"cd09cb9b335e1a378ede556e1a96dfd9fd412e9caa02bf73cc09d256252beb47"},"1cf312f6363cab7b3a2e6c67480fa5a5924eb6488bb43b766244327a6ab75db3",{"version":"dcbd3305da0fa9f9bfa2b6775179a16b18185d820810dd243be17ca852b7bd99","signature":"7837dd9c018c571283ac6e26b11fd830ca92edacdfde40f0dbf8ad4e9643b736"},{"version":"da3e0ab10454bff69d784689a6017755f62f51f9270bc5ca33a780d8f1effed6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3514987210d9b0ff763ffd0a1aecd4fa7a2b4fc1f199c39b86fec4a717bd2ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c282008ac1468a683e39296fa4bfecf89cb06be55359f6b6f7cd97acf4ac453","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"402e78c9fc8f2d232f0ba377e70c2ebba520dfde76cdf4cf3d71e28515c8f33c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9279672d35d72514a5d65cb870ae38fc12b87f6e814f1c8f60769021d49629be","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77d640a224467919d1eaecefed3e3bddbcdd6ed34ae045f4c6c879b03ea8552c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6a5acc53f383e0d586667ea6e6a22dc8ee83f9cbeb851f73a287c833818465e5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"917d97183b85b400b895ef923475bda325f1a593dee6be6cda4163e0c649e1e6","signature":"925c75746c624e162e30df934c8b19a5b81be5a03978e80325c4e9f09bac61db"},{"version":"83d32bb6c68c36dc2c27d16caf470e429254d3de8c5c8f9ef91134d33299aae5","signature":"59ee2a3667021f2c7ed7061717eb0e9c7f8b0a4abccd93a8aade0900766e5e91"},{"version":"2af1638b9ec1f87086b8ff286b71fb279cad7a6256fbb6ec7192f31306f146ca","signature":"8edda68fc04a498391fe3e3d486b469c92b2e4afcbdd0b4a5a8bfe78cff9be0b"},{"version":"b2b32e7018f450b50765d979cfbedb39d6ef0443d6223b79e626db3d80c708f7","signature":"8490537159f5b3a3fd14f628b32e977a351e70a3bd09b890fae5616aaf894cca"},{"version":"b27e07ee2b259916d71aa0453912cae34b32a3492b7509feda6c537b796f957d","signature":"cf65707352be96547e90a932227cfc57bb9a3a71bccc6659328bd161ebffc36a"},{"version":"b26b234c627799e3b90925ea36bd4cec5e57e26e3eb4b95d598b0ee19d1fdedc","signature":"a476746c1a3430e74dc7d6764252eb4efaab3f931bb8ed285d82185b2efb30ba"},{"version":"ae3c82b6b88fe1315f8e92eb498df792923d5da97e77376a8e6434ac660bbc62","signature":"a5b40c328c53179858f4850879d0e77ea5f554c6076eb084c7a077ba81adfbf3"},{"version":"5d1a09f5ab37a76ea0dbcc20cd08dd4dc224e263389aeac1c61ff592cc825690","signature":"8e81241cc6e2de102991340c8878879924b204883de36540bb6d9c3931611147"},{"version":"d89ff4c66bb8ce9ecce1e47d62c2a11000e5cb57d27604af1ee22374cc7d6a32","signature":"eb07f404debd5b6bdaa86469be47c6b2a1e1ebe7c4d263730ba3fb4b32cf85df"},{"version":"7e488d2c1064204830ff271a055a09dc04d15aa208f1c2aa19be88ba19f57bde","signature":"2a346de3340c2b612e54eaf8f283d10e06620b02d5ad511a26faa5178f473b06"},{"version":"6a52477ffa08adc8d4bd84879ac20a5436f46333996cde7ca4a2e53e4e2f1776","signature":"b07b68f5938a55bf423545b75b3a448653410a1b1a09533ed9b00bfdd4c0ed64"},{"version":"b718744f7bdf7e86cd910c7e57698380454e0e94be0df2e1a4e658125ffe5516","signature":"5171645eaf377c98ada2fe1f7b90520dcf9b9356d4f4044b7cff35580836b049"},{"version":"ff3eda4c49da2ab76d049b988fa44b8a7658527d9abb1a22cd93c7917cd8dc27","signature":"a13db128a389010a8c44cc19ca53aee045a3f3309a1cf4468ab110962054254a"},{"version":"c11954f6c73d0bfdcafe0036d47648d71e8ca4f1a70b1ae88c815a703fa9ab80","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc9adafb376ae31c4ea9501ef266f0faaf29de7d76aefde50ec9c6ceb67655fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"af03f35fd7d1a7a1b59e8fefab3d87aa8fc45501cde4d5f42657a0af2dbd3b85","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"765ccc4e3f7042c4bf9a0288838c93f3841d85e2c3fd10e15a17ef5da7e348a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc705423f5e581231d5ab8472659fb37060aa3ade1a052f301ccfaa5eb836748","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23f751ecd2c2b9ce4449c843400093a3359bd77b541c50c815b3f3bb234ddbcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1f283a5f8e29c8def3e16de0233029b469cb0c493d586c737e4d9c373e7cffa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e93b1c62e445dda55a6dfb00d29b489d986c6b2fae90a1aec65f9020cdcd6a59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"566e62da419b55e2c0504baa8b1e36b7af570e68ff1efecd7db3fdbd67d75984","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd48793e5b03253b7fbdd55f3d0cc8aa7c66077cb7c43b9afafd4fc69aa65c13","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3a4e8bf48ecbb6823f47d4883928e917080125816b24714745069c8a7f05627","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ff310bdb1d2c5121653e826dd2e72cd137c909bb92fbbcaa12d612e6008eca9d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca5e466b1cd54a780167ecb1b23e6be6ebb99ccd3e500bdb6909343f4eb08e70","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"86bebba5823cd4c0c8c264ab1e5ca89532125029e01b3701365d7c8b57ff7b03","signature":"80e816ce6ab347f332104a3b4295fcd234484a8c0f78af931f6f679bc819854c"},{"version":"15c6a3bcc2ccaba6a79ea23cc968005bd86ae7c98e1851abbddacda91561027f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba697cec2494efd4491a9b92cf45a2b453b381938429db999e6b0ad8eb91b607","signature":"462de0ebec39f4608d311ce5efe2f7996417ee2ba050330f9589541e27badc9a"},{"version":"3b9f374fb01fb21e7d3dc1ac1bda5a6ca485e8a42d80c5857c0a907fb1d56d9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e3ff7ae3ddb410e74e0c46707084795c01be15481fcaa355cfeef2da8a92dac1","signature":"27c8473ae6d0631063de4e25ab27c0203e687c741721337d833ee7a8d114d9ec"},{"version":"726922c369c19248989afd3de749124e1acbcbf0b9916cee980e10b8cfc02557","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"178b68ad3da8447deb3fa36b903515c68a878693390dce2c3c51887138a4d358","signature":"d80742a6a41d9f569db06f2a6f1ce341e38a8023e1f172dfc596ebf59b320d89"},{"version":"2fe04834987c803287fedee95429f29ed93194477634301a80acea18732b0584","signature":"c8cea0f80c3f03c528c1ee5bcd4584ac807b414b573259bc90cd3787ca4645b5"},{"version":"64e96839e33ffc472581904d3d5f5101ea95a39fdac17d42bf3b8080ed452416","signature":"4ad6c2671041ef9cb7425418a493fca3c8b38243087e4533aeae67d0a9da5616"},{"version":"c632abd896e5fc858119334bc27fe15d828dd2ecb2efa72b19ade831564e4a56","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad56f09ec02b513928021933ba8ccb5322184a5f145211adbb54bec8ab7c939e","signature":"02d62b21f2b1b3ae90d6f4c2a2177c849c94a135893850b697a16146152533b6"},{"version":"4d9ac1ae59eaa55088de16aa37191db82e512889ea2e3475c923acb0fb5dd1ec","signature":"e6ec51d846f163b420d420782dd42e40aee266aeff314d141b11a0307a86fe09"},{"version":"2b207ad5750863999cb3b248b98e29d8cf15b832e77bee46c23dd5c712094bcc","signature":"8494e8d1afa0d76f70eea09873120b790df6fe7b084458941c2ce07b55155b33"},{"version":"8a5547b3ef575d99d9981c3f2cec446e2b348ed27d728e3758ce04518afe2236","signature":"418948af8b278bd71d186eb1cd3e77e0692f5d950fb4f486a805fcbfe934e4f6"},{"version":"0ec34d2579a19920da577b641066c31e0bf05a7b1f5b2700f0cc325daf817a31","signature":"d3d6c1bd0b87f5ba67cd605f6bec93838c20c6f6086cf1480f783117cdf7056f"},{"version":"a109dbde88de9c839d3a7dc6422ffa7c7d5edb1f2de052d90c4f5a4f227831a3","signature":"e6c6732d88e711c29a07fdcf4cf22361827cdb0527c668da5e91b2c63923674c"},{"version":"9f9cfe8a6db296e29b8d96c60a6b2571f64ccc19bbb365971270c013cae6cd81","signature":"9a87bb6fb82202486cd4a2c71006169c516a1eeaf1a3e2303b374c2ed9f3b7c2"},{"version":"d165ab27ea9971500334bb5c37f5c56741d65f4c70ad1d6788e4582a222d2794","signature":"79ebf04474cb0d7a058c41fb366280437bd079dd47f2138ec94f3918daa05ae3"},{"version":"3b40021cf5c4b492aa5cd8fa0871ab438f0da413ca344de421849513e4332ba7","signature":"ba994537d2ab9e6ef4ac8ffc86dc36ba2b9fdd034a5725d1986d97759876b755"},{"version":"9a6a75a9d4cbcfe725e96855f3af3803559790aa6b7e48a6314be4497e3aeb8c","signature":"b05b871bd13173d03b8a6ccfd9d1d187d6f612bf672f565eff21e0da7055aa3d"},{"version":"579925bdfaa8ffdf328f0aaf7a2b98a43acd6c7e56f4902c31f81cb93597fb98","signature":"4478ca9bdbf267e8ba293c55d26d03b720b9006964a13d4ee05afbed4509335e"},{"version":"f749d4843e5fa4533b0e8bae2784314231b4404e332e8d56abc2692272965212","signature":"46c0c755480b2e33e77428563ef84a0fa949e17e287c83baed1c53d6e9fa9014"},{"version":"71c8ad895db3c65dfbefa63d75e779b2ce821e8badb100fdcdc6bc241f2f4544","signature":"01fedc4512be58611b781ddf06d6575cce9825bb18f1492ddc0b7174273b8f31"},{"version":"25ff64eed6d319715fece8d041173a27719a7616837f57626e812d1ec3c6faa1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"02f5133b4da482864f51bd4c061e1af4d67011eced5db16da712c5ca6d0af7d3","signature":"9ba498bea3aed8b2794b31437cf2cc47c2e1e500cd72521b38dd4e8a772d2459"},{"version":"2160b2891069ac50f73bd737ea2d4a26ed3f8cd43f483066e9715018ad38fd53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2006492de4323a0166b032c02a1f8f5f6433b5e9756bfde1d98f7902aad7643a","signature":"45b373ad2e114de335dd3eaf62f9658266d71c2f34537489f88f3b4815fa72f8"},{"version":"38dfac0e60c6379a3276ffe33739a19e2c81f3359a73f80370b7dbd615239da2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7ecfb0fe515e7462466e1fffddf551ab3cbbb0120b9c60d8b0882da1184d719b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d6e05af866b9a2fba0ca03edd8461339787f298085067eac7179ff66826fbf6","signature":"845a8c55efa3e6c366c3d4fe6aba5af60a822523d537e8a3930466b565b738a4"},{"version":"c7a40c6af045ffba5250fd4b2805c5e57e5f7ce518690f180c83b65018840f3a","signature":"cf231aee194a0a458e33d6b2a8017c04c869079c965b00b9d294016e5f331617"},{"version":"fa4fd1a6c106daad4d2048e50518a1707039d574d96f2282addc0157b2143b29","signature":"b59523722261669df66b7a54b3d8686823768c90c5a8a04fd1a7c0bc07064fb0"},{"version":"22c9076e33fb7efff1af1d8c1a9c3e38eb017a1a07e62d78b8b64ab33664f2d3","signature":"53e646710346887942688dfceeb46259c4d04547c3f4909366bf5a9e3ac41392"},{"version":"78baac76996d1d214302749ad18c6424d1952fc441004bc8b1ff78e16ae94f2a","signature":"e0fa0f834bef15145ff38c4f94b555e406815bff1d72c3cc4b911bed38024c17"},{"version":"a41f813b81e3ee6f2fe6051c05f77671ef035853004832795377479c61cbcb81","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e374ce00606b66ae99a8dd321694504f11749fa9f407bcc445dd4eb6c6b3b5f4","signature":"6a2a0e9055a691ef8a292a143dd336005e96f4cfed93373adb6d1fb2f7d67cee"},{"version":"bc8339d6590cff26e515017178e6a430e53c0fe8f4f858355180bc22278a1bcb","signature":"0c25e09a2b6916bfd4fb6138feb16d394bfedda3d5fce6464478918e2f3a32ef"},{"version":"50a97612fd2557f947fff7bc53118552d9eeae0f349814c410151cc6dc305f81","signature":"b3deb4cfcdd96ff391f83c5cbe1f6880f7c11facf2ecf8e8c60983ba70664cbb"},{"version":"93b01da13427e2d00a8d73767869b61102d6ced5b8e6ef297006047e7a36b63a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ed34dcbd916c8746407bbe31966464ba2a40992a7d3eafc7b89fe9487322e0f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"980ff563c04a7ee054838de6d5581a1c74f879aa573e49083b767661eb497b06","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"073d7d72dada0f47cf563f302854c2f4a56a0fbdb4ca0bb02878abb996b14c71","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"81f5e70b2efef928bafed1f1ed517b5f3d7dbe1bbae734620b1680b8cdd0bc66","signature":"31055f7d0532f460a1f2ec3229a6c990bfec524fb95332e3108bb50913d60c09"},{"version":"2f9876fe775220881f9a1dc662c4d45a1fc6c69dcbdf3394d4dfa7d38e7abf08","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd7cc12f27a14a8b4160e7eaefb41b48701477ae419951c0063c8625d0ce9ba3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20e933198c82a21395745ca8398ef702bab9881d5224febcdffdded08766df03","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23a5597b552c4fa9218e4ff75c6fde15c735a495a7eba796b21a28102ab16307","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"23fa3382c09d278365b7a211300808076300a0d16e6b7a7aceb22bbd6a5e2850","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32890338fb3db8ba265d19c7192bfa9a11bc5ee4c15154a4db81a4ddf1c8b38a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebefa20d8e7844bf717e29dea823d72e0e3851abec67bd7442f18d5e1c929979","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4cfb9e24d12ee634464b2e685f0e830f3871b28e0173cc89558416f194d49f73","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ef7f54e0c441529398e2666a264256395d244f143f2f97ce5737b8ba12f9dfb3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a2f242d01ce2d89d7afdfd1fd83653b8d751731fe8484472e55caff6fca829c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dec2391eb73f6d626e7679f9c1a15a5a3939f799b408ee2ace519ebb16802d9a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6c8520dc79618d2ef97bd41bd2d9f9615e8d7c31289ad6ff40202de2520d8a0d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d7c9c2e7dc4c35e0a79a12add067b79cb96493da0593a7e063db435257c7ece0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5afdadab59d5ba59e11b585459cc467cc1f23eafce2e1357e7ae9173112cb540","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ed8ce303eb9c07bea6cfa724060c049d83421b0a03c671040208438adcc1ddd0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bc2358aa66dfb3288e71f8568e09cbf493eb412a7ec67ffa33cdc24b0eac922a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"71c4bc806bfef481e0a6ad07ad37d0be53ac5d8b0d19fb843e6a9549080dcefb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e133c38d7312361e47e684f52022933865ca28b6d5d1bac3fa6e306c64e54e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bf92a5c54601a670a6c8b9c02336b7a63a05b0cb9a05cf290d1cfaa95f28f284","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e8991aae569d37156933dbe3b3f24083dbb48ce7d8382f671da7c0ad563dbbb7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3557cba180e795e1819088a6d2897084fe66b04f6c0c03d72eb196d960790b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9da4f2245cc7c86108bcfa87bffa093cfd5b532a90783a7f52c3baf79cf42393","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b55ad93c7c4c1b77f78a46e1d78564d3dae464706a767f3d25ffa5e3dcec0cd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ee249a2e5c93e9110ec235c1e89cfde32b81e509c667abb08fe9c1f2e324a810","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8f33490c1ff132d5483cf4e1eb80e6e6495eeee76803ad9e0bf039c16f6214f0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9530414f935f2d4311ff2b25d6d8fe9b119e40eb052183336306fc8be3c84e88","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc772c4125fc9d21fa0bce97821fb5879dd123fdfdc00b666165d69a62c5006b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6ad131fba9f64b1c6efecc01403b93c63b294fca637e29d8d515eef286d78348","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"453246aeba13c55733a15b8aa23939b666b7dad48e20c2b940f6895f2d8c1a85","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a376d7cc82fea71186921ef0f2779295f1ae28d8685f2dcf5aecebd6ed897e7a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0c9b5c4082f748ded869361cbb3f97d405998ff5512bff4ec98ea95213085ae9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b3f09f17c91d57b6a841936dd215929d1ddb25b6cc36e2d5af8c2ad22efaea57","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e878fc887c74d6c43af5acbaa72e7f0d5e598447ee16c0c4eedc223bfee16a0c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ffba69cef9d354ab21efcc26daafa01e3426d6ce70629064bc121269544e2f0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f5fdf3fdfbda3df361d340493fb1df84a700ba2d928cf6d48fb3d47804cca24c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d098b2c0bf6c0333deacbc30836b8b0cd048f48b76cc4f7f32c5c448bbb9a3c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83780a3b4577d40f2094e631b3929043444b0bb16097fcb8c7eca08dcb3c1427","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c0acf3d7f2a5d62332da4fc79bcf475ec142934b00b1b0c8bfd3893f64bd1c24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b64de355d67c8f5b9c0bbba60fbfa188e0e16f107bdc4a72632a213c5ab9bd09","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d767afd9e2f82e7e899edc3775e1d86e5acb4c7e6268acfa95c551fc7c02d676","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34a2803e9127b665802f3808b668a5474c0e95e2efa58720312bed19f4461187","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"49c7bcb065270b2137eb524c7f0dc6c2de882ed30fbb903686d9fcfc71b1ae1c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6c4b346b7396de0a88562c85f142a3e6c71f0f0c3a51d8956d9d3d656bece75","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17428ad5e6272b4958e99bad33e44b2c65c554fc5a4511c5ca18f6ee88277296","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32a475b7d9fb82fdd7df4de62fab6df47c2906058f70f5b68b7dd4796e0407c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"87525aac3b68b128ede1c21fe4f43b896ffb651c5507ec5bf554021789f0ec68","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2b52485c59bb8f5f8ecebc36f9eebd5bb9e839006267e67f14f40bf57c21e545","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6230518eb3bb41f00853f984b9208154c9180a11639ac532d115aa34daf08a4c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63e54be11fb7b740bfdeadd63e8f451830470fb4add677af84ca53813253f593","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41222d3dac2c14b6a1f71e0b5105f2e3f860186aa3db1aff6ec4d95f833bf6ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"59d51e5e8361f7051ead0c29c8a03483e6929dbb6cefc3b77c2c497f2d895762","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"34bd75b6379933f0a0371170d95905d43f72c8a3a2ee431fba5129470947bc84","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4ac0ebe63335c8cf5fd698cefa7904ccccca2f9e5d27dc9e0e18ae1cbb5ba066","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"44da1db5f81f80f935eb95e20e3c925d71d68ab43379c478ef6aea748a3a0b92","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bd6074dbcef6177b94d63a539d60447a71cc249f93982528095f888e24d1fd9f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"405ab3515b5d2f07531943438c5ecf082bd61434adbf4860e3f83cea145175dd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7356415ae2693e3f94e126d3fb31d42990d0efd882d063661d8a588124fecb67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d0dd8957db84d11780ab6f4fa208bc3827c49b5986f0b5efd5bb98171bb5a944","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"25028aa767cb234fb49871cb5dd6784ad018d94609a519cdc5334f590085d21a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"28f03986bd300c037d0dfaa877d4e1ec84e84f56f87e6a28354988c4dd313325","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f9e276f89ff3b71df956676c5779ac82c1a96513a360e6fa66de0de81195adf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"13bd658bf78bc035a961497fdb6d06b0329edfbc22985c2ce9bac485ab8e1cf3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9cd1dcce9c4466749da349558f09c55628157e19f3755e2043833faca3f3d3a2","signature":"407c70ddc24d5c90bc55d198041d27c6ce2cb0f42fe30a091ef7533f5ac3686f"},{"version":"ad3602cf898d906862e748a847deed9392213387659382a47bda93090c24d2e7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47beeb485aff1ad4e1fbd8ac6e8d36a24e150de10a30b6899840faa301655414","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"487344aba34994c6bea6db3099ad923d847a27e7c900106a17d5273f8cccaa06","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90f9cb383eb3d9b17a982664056a8b1f86b8fb7b391e2bacca3b58e220a2a38b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0568baac3cdb203dd85282b137b649bbf8171ae2f1a61264cfdfdc1dd5f6c1ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2d05701eea88d40fa2962c3e988e6e8c751892445eeceaebb8f76bf10d8fb47e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"73167e4dcf4eda771cdd7eacdefb1f35871c2fe41104840ed700732f69feca46","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"10074d86632d44722c8168202803d73c95697b4d6117ecbd68e5a433031eb52c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a5f1a3661ab26f668a8195833f02b4085991113b44a0bc7f790e9c9af257f07a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"77a216493a9f360de8197a4996159822af842dbab96da3d7aa502563ec12e64f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8fbf898b003bf3d70416df534552735d946ee7c578766469039551b5b5989a16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d43aea7abe28c92b8494f0fdd74762bc1d3ec18b972711d2a883ede1ca8ae628","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"598384c7786700c7d6208cac6007b37f123131de52f69441e496d3086f01599d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8237ed9ac77a0e6c957c04ae1939d077b1eb214150e0f2ee2330dcc698ebdb6e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9db5db65827dce6c3005c0ab5feb8dbc60776a2767d1f3779e4e56b6ac0eee26","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"133e42066e3a748c1236a82709776425d0592eaa5a1bc0195b102df8160076a7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a57babc764911fd4f52710a78128abaec189a6b12a0adcc0e06267741434f4a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"750e7f25638270d4fba9ee9fa59e79d2d97cc88e655bc8bf27573dce9ecf52d1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"745001d456418763f9801cd2f8e00a519d597d29efac153f41db8ca2b4cb5cbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32552e3b687bed279decc2ff81ba12b6d194998a102b5592537ef0a7bc246ed0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"17000b2a7cbc8febc1c38e79ca4aff5a824bca523973aa7b5c4be0313c10278c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ae3187226d80dbd2906f54a87fe586f0b33961a92b99f74baddf23943ddf197b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6290d7ae201e2cb37a3462e8f0474823749c74478df2c024483ba0f66b9201b7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1ed513cffd7653b442cb3d8865b226957b6a84390ad18d7702cb73bf169402b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3ffb3ab371ef4a1c49f3d70e6cf58152abbcf97f79b87b81fcecf0e349c9e47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"43c903a3a3e6bd110c6e1e0edf3f119bc3863e25f534de171957fceb9373b791","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6060795a11dbdc1b053619d909275140681f310638413d7f75dae71c0698a0fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9b536f09a14585f7a60c6198eb73475cfda55bdb6eb7982562b14d9745ab3f58","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f21b2e5f827fd2bfa35959f943d5f7c38bd76195247f63a7e00c35c869583c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c93a6690c5ddf530c35ab275c70a4a15ac6ca4a74275d3a0205d1acdc8f99d2f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a1d4f7b55a0f4585ce971998ad5602b25f56fa82c105750c8f770fd89f61fdb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a25ecb2df0785bca73dcc65bb1971e06ab8dec22390defd7bc0fb6e56bae645","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"71cf715fe7ac9cbda3398c62715e9e41e205bb9f66c14db522c05d33d9bed871","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c170d6a07e32644b63485cb4fec95a7b4210c95b0106bf604f77f60be4590609","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7e8ad08f464b4d38665019d1a2e7abcf8431a2fafd4af65bcd93e71e9defe276","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"284afc03d292b1476a7abafc7a199b1374eece1304d742dfa2fffe29d1ef0c25","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"724ba566f050f9a5c9d59f094d43c5986a190bc913ea545fadd79e99201c1cb7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"63c289c6931d3546d36c0cb59ea38f2d22ce5df282547200bf86dadb4cf442aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e9a831e721e46b46f371bea50434b366775486045b009ed500a273a0c87cbc6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"220c41cb6d922f9df023fc9633b25d3f277be8ca0b6959d35510aa0ce0d7f435","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fcf4e15889b48532a4fab0550d27792e595ac7e53b656745561bbf0499175c37","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9f93119e73d9aae89eb4897d9fcacebfc8131e4fd6add6bd0af2f085efbc1b5d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bed5a24b28678ac3060e6247e7f1028d52c3cd0a5da6f8de620813357bef52ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8e8323f9bf61781a5c665b85254c338ac0bc879cf252c408a9155fcde6d3926d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"58da354bb341bfae058822830e25841c7d4e322f2c01b523533d976788288a79","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5905fe6fd8787042e9f2a85ac2c37182f7a47b7dac70e7929ccea5bfa8fc51ab","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a45c928a098549927b5e3e27d83b1abaafa8ffe97af5ff78d51dc9e983f1cb41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"de10d6cc7a07ce5c5d961316be25ee61e38b528aefc5b78bf4890f24c0749f6b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"569beb54f189db6412e1bd14225b3c003cb7ea7a8b8ac9d2bb4a98d443a1202a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2bcf3b15c705b78d2624ca829055672f638ce38a4ec0bb25d7f776265ac833c6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d7a0f90adedfb247320507bc1f490cbff7e5c0236bf52363e4dcfae1219bb9d4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2a4650779610aa8626f855bdead2a9ee445074ac77f0df56d4c3d74d471ac27","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1a138b16a062718039f7b4a0189c173d4612c918f1391c15a13ff9d74d76c0cb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f2066a8d178c8a82454b1b1ec4593e7dc6b52d802df5d6073180fc4affe3942","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0cb88121abd9fc2f1e7d2aa933930cff72eef65fa010ed23f923307e56193e66","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"570d900e54c02bb666819963695f97ab355d3a10137e4c90d48647fbef5a8bf1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"279259cf3f1278d44774d4e4ca7738ca2a7b23d5df3422c8f04a4ff8e435460d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dacd876d911d48bed49a48b86cf54c0ef8ddbd05ff05a7396ed34f3f1709400b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"535301d34adfb07b738b5511b1aeea6996f5cd1e1ab900f49e43419d8944fc2d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1b5f939daee2f045b7d23a12a0d3650dd49e4f9bc351d2f22e696469051d4eb1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6c3b470578a5bd66ef16829c185d96ccefc3d2a3377d9976410f500610ab9628","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f13b437a12555b0ec040c3d4f6d3aed3eda3ac447ef37cdfb0e458b697a97b8f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc8cb3e5ebcc82df41bf9b42982dcadda2f7715d8b1b2df23b707388de721fed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"972a8cd8b3335703b18119089e6d0ea65460a6b0502350734fdb77941bb0762d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1003da076e1cc06db0b7632ad85dad4c0af857ecd949856c40e87a733c9ad2dc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27a14d107fa2f36104dddfe0d0f3ad259d6a5a8cf3ff91cce99b5e493f9395c6","signature":"c754e6829c741e6b805b1868f57d8dccbecec8f04c2bea49c8fd3906a9b4bb9c"},{"version":"00796a0b209c7a10afa20dd2ea1561eccaaee5ab2798c4b2b7641d5eb16d9205","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26db1d864473277cb2b8327fe5a4ca010b2894eb1be5087e817095a95f74938b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b03d1c836d3624a6ab8fd8395bcd1df2106a4c7da12ad82bbc7fe448968e7f41","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20d1bd40bc36713f75dde61ff02bdda74cee057be3c13af6ee23fecdae565d53","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f70f7a7defcca6061e9a022f5847772015898ff7f582b3e9f9991181cc23b7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0f5206a0b32d6320d4207fec04712a52460492e4a27e4af8d7e864bf09acd24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"26085d6a7b985e91fad21164ed5cba66427dbeded7e0a672532ecff63d2e7c4c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"87dbf0346d5746894eca4b429e98201f34a03e11331cf456d13e71c81212e426","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dbee469d488b262f97f892153e62cd20ee4724dd8b7d253ba771770ac8114c67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"37c5b9f699c69780b09804adbd63f971d028523664a9a769f92d9b407915c2ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"061d0dbd29081c82e7739053859db508c8bd9038d05d659505671fff58e03a90","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a13b36699f297594179427601a6b8cec07112c1638ca4ac3ad451a7164bdb3e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"52d51a3b16158da8046ce3051acfd48586035a6ec20e3186b40f9fb0a622bc7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"95658c91b67a72ec6af1ede02ccb5802d685bf848391710bb006f1ff1de9cc67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6b1c0e19c69e765adb21c7b24a911f38d3ec08f8a4c5bbad7cd7c15e416f106e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"baa9f93cd885deed2211a1f17e2b64074d45217f6f95784d9d7db3b9adf39f7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdd21ddbcdf8e5073e31fe7f730fe3c4023c66309625b87b5b9785fe140190db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cac72a71dd33cd4dcf93a4c06a34590d661d2ce406b9734cca33f567ddcc7208","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"508859ba82d5f926349e4a9d51add2f33fec2eb154fed40a6a80f12df4d99bec","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9fa26168f88bfa67f9b9f82b7cdc70c643822adc48535a76c320bc7d262ad78c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9f11d83ac8f4908d460984e703c13f43b69aca1572d2949292bc9b95ecb7a2b0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47c08e2137ad5ac7254ddef770e68221d041e397acf64e2059b92018c4d38b4d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5458b79d513a3c28249bd399e109764da57de09097034437d65d13753035ec7b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"abd6d932a4f5ffeb10ea89ddc43d53467aabd67f1424f03634d2bdb9e91bb39f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e7484d772180a4fe32c9d12b3701087ec6479a1fb4027d02443b362d6748f265","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d22df3d0d4a171faea1356d2ed06746654b7b54a6f134ad5ea64f2bbffbe282c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e3ae440b75e800d2a80437da11c4e3a68270925cd4382fbadfc5e2a426b144ac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6256390bc79dff5190177864fca522b99f1ff8c690ab411abb268d2660660479","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c871c193395edb4f0bc64f8dedd55c8d15a51a9519046dec95c4904242d7b2c6","signature":"3b5031a79ad3b873f4979dd714732927534e3a6d3ae7a9ec689c5725ca791ea6"},{"version":"3cd49322854ce1d737e709347cfd3aea195ff6e1b262d5958bb256c8beecfa0b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f7d44afa8a022241eb790ad981f9ca901650e125283ee25ecef8dfba63b1b1f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2c144fb7b835575d4eb400187da6e88cb37e0e58c7f2d430bfaa511f7f471fda","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ec563dab247f022b8527fe82436349f3792b975c4e939886ce128d095583abf0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f815436168a53475078bbd0aa903c756c66bca0ec8c468ff534ca4312eca4bb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fa2aa9a0b964ca9bd71c8f1b2554010f338e89979fc0581e1f273a56897086f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"01655680390019da612e557fa6c87313dd411791e200ec4a960546fa1c73860b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"79bdfee706e5a2f5afc91eff7c3a186da1c451fc3827038d6bcead0160ead42e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f719002191cbec9176949717a9a57b621e3a1d307a74ede4cc94dcb78c249c8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cb67219ebb0d8339188f55657be0482d7eaa73cfa5fbbabfc76bf0757fe217a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9cd6b43d528855022541993f9db3ff28d73c7183ab1043df00fc36073d6555d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dc96489dddaa0e3e3c918fea22584a4823f0f5b0ea27df04311ca25224969acc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9e68ae5f6432e9c50c43e9d2835536cfa152255e680aceddeb3ba2c13b5a24b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fb4e3677e240e9cbc6268542e651d8d7142cafc9b716002ecb94db2923231f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4fab0835f3f0569611b4185f74019264c8ac4338acdae9786c36fc5e73165f72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"095cc9d0709e52c5869e31a60f719773a43b60940f726480b689e301eb661d9c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c6e4a5152f75e9d77ddeec6158887b08565816164545f301243fb653d7c57c47","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b850436d0a9d744cd3fb92eb3be65206791e3b4c21cd66fa1af395074ccf9520","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5ae00f9eb7202bad96cf17277b37f8eb7ea8dea3e1d29766ca904c2b81f54043","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"33195f2e0363a39a049cb3839f69891f3e92cdef82661f683823b7d4f2f3d3cc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1a91b7d838f633711b64538b4a4fdfa77eb8ffc9e1a5cad23d66a43cc9d1bbf5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"e30d7ffd5f108b4f12429dca91377297ac7b070fa87b5680201b4c3da07ff6db","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6eaea317fe5b6bbceeddd6440306eb6dfe56c86796b5e90b0c86f03d98fff955","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"539ea0ca2bc54254c4751432472c80d8a6336e592b9701695ac473aa6c9b4001","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8b10a43468849bb4be5c7ebf6e582671999c7378ba459e6307db755757aef954","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ce30fa93f285427c6251e073491cecdaf1e80751e13ebb7da419092fce4393ae","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1eed9281109c026b9f052241336e80589c39df225980919ec591a01ae388f11b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2a018598e7116c04eb2caaff9bc3de6378bf8646bd96429ac82c6ff2668b9e0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a0f194912ffe562a67d5570ee74538fc74e5b9ac3eda0c8188b314e72bc0b1a4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9c63668af53291cbd777acdc086a76266b1f9c51e354ea2787619ffc3c10cd24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eafd0838df9188d3b117c9fe53e0c77b707f5a985d3b8af99f664de7a4bbed33","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83e6f2be1beac83f30dd5f1e56d42e907c7ce21c05ac72970b6ebd370e5432d7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f59b8b9f9609eced7551d65f5a9d36c47c3e8e8f946304c4a9202d8748c87e4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ccc6e094800a0ec7e18a71109ed4efc28f2070b608838fb625afe4ccf0dc9b87","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"313a574dfe32f592b23877fc0677f33c8656ea9970e4af30ef78b96e17e0a032","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3fcdf7901f8d9f9e77895e5b0743e77242c2710c17d8ac73beba8a79e433b57c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a991ecc505e086074ced217226118fbee9bd97d37d94e9a73cbe73cefc82b23","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3c91bf1628b9af6723816e7f06ec22cbf5627ea3c793e802eee02aea37406231","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cea188c7223d92eaeaf903844518a77ba6daa9160538bcf25d4f7e2990a8be01","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0daabc5b3a23e77755acff32e410a9365d2266246af76bfc96de9738a6915bfa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ca5a2320c781052b195b38bd95a7424e01468dc5e78ef946d8eae4d218437eb8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0dbc717d091c928ea25aac5b7118713c489b0b07f74b6ae3a57803d4d704c841","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fa3cf6e959f107cb2e099dbdd80e4d78f6aab3a0c012a77a7b0d1288917c2b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"29a34d2e9a40f421e4983fb1849a26f666b67513417ae53112b81f859d0ed18f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2f0f520b1cab36fdc9f80c54b74f17e7189921be14e5e6384c9e76dd694c5df1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1f20c6cfef7afb6001fe5361b024b2f4863f2c37eb8bd6e3d3c2f7891ee91317","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9210170f2fa566053f02e2c5c3a77faed4e7e51d8366ec02adcce7953297fa56","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b01dbe7929b0a92420ded501af329eacee87e3465038b6b1a0950bc7c8f90421","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"92e3daa4f9c571d990375be84c4f55ac2d68e6eebb26ba066b18e6712b4ad2f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d3e64b1b1acb43e370665f3816223c516a760ff13f6d29f08043d1cb18afc95a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"803ce94cdd49ca8ed653e63004ed3fcb16ef302b983ede0d5291257babef6bcd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ebd54d09bcb969873eff5f50835348d41ed084785a1a53110b155cfe0875b7f1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1d57277c40e6512296d6635e280c6228aeb08d789aff2eb4ef865eecf86cc78","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d71d12f9748990e5a21ef6fae3483650f1da187533e520785ba561f8e8f177af","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a3c1c6e2d0c647263a8aee2d16655f525c930d6b9784eb6080c93ccac28a7c9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a93fe9b1bfbc124b8f4777276537084f37469f91fb5ea6ba8637f62222f9d378","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89e0c0b9430b0635a17c439eb81fe536ac9ad69c9229a832c1a661dab780a362","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3c21d7eca64d0c7d8abaf6e2a69f445aa6e514be6f0e4feb54c76781a507b3e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b57719fe1738f95cf28675ad0e55eb81a991bf372a7a5dda6c45b162bd094d96","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fee02d6d186cd9b1dc4824242b05768bb2edc61614f01ad6207145744366a731","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a83e4dd75c54300e6314ea2c0c5813b418d1a2244391acb001f263c9b1b37521","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"27c25a73ab8e8e6ea25f0679d1ef24c446a929a7b9da8fc842af72349beb9ef1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f0eef830593e6ca3e34d7c4af265fcbcd5d7ec2a6c980a442a8a395c98b7d872","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"752bc0bd543fc478323820a8595d137ea1fb8fd0d8ceb0ea05c3ded4bf1d3729","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4410ddaa3d6e3c1441fc5f669ea5c3e3390fd75f6127f06b1240625558160a9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"36919106e6e5c86f0628d2542222b4f6a09cf7955bd96c53a9f17a09b62f3903","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f6be390877d224b02106db41d582eca38b6d52215c0843d3e6d78d210c956f95","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"477bc6781d39427cdfb58b00ac6744fd72a76ddb9add5ee2b6fd7c0123e8c133","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d77a4e85ac4e7465ee559c7aa33e9b67794fb42eb006094e41de859e0f574567","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"efaba8801c46c71114040269fdbc963f3496d01a5b185ef05612d3d71f6c1fbe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b4f65e7dc37a2e488233381af3c9fdd7ca6e0171adc8a9cb2d49d2fa17cd7d67","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90866b821f4e9d5b76370bf9af71b3bf92fc1fa9e4498452ce90b1035a4e4737","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d534cfa39fd1fa8f70dbd3349d67920554475cefd0087e7c3e418d544eeb3adf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6cda8181158bb3331be538ad07a3a6627c80322ea32904bc0807eae370c510ba","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4733417da9fe7eeed82209b53ddbf53bb76c0a7706f747945278a2c037ba2bcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b78fa0476aae9c90f1ba345f48912c46ff37b4c95cd6242b75cb57efd8f2bc4b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9959681b8cffae14e821fbfdf3daac7759ccd92bd04413f45100301d8d08d20","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"57ebd17c1bf0e09d222252bb67deb0cef31476b2476c680ea5ceeca29cfa3efe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2813a4305e2d3d23e4997d9a2f482fde783962eec4c66dcd112a3348a1b1f6a8","signature":"b84cea73e43cd5d152e01d2870e7736075b6c5ffd9355dfe2660b98078c17e9d"},{"version":"c6a12b1489747a7b0203fd3e8ce5905c18b426aa79fdf9c47578dbf0f91058e9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"48392b4f5473115f4cbd2da11efb0fda7bb0610c15185a5838260c9c2b2e5745","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"931e404359129b88ae22b707a39298f2d8351f150a5ad6feabb975603272beeb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b522f77d275268e582dd53f3dc4f93082eb2f79a0022d066bcadb94a59b6c88b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"002c7bb5c3156eee0fe39dff54c4741c297b56aa6d4378a8acd34507505ea63e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c77cad2e3e80373964256a967f064b23ff95f5fc46788636eac8b765b2fea524","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"75424e9c1527a998d62a8b265f710ad1302623e4de736fdccb156f78d2909ba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35c528989066fabdde1d1aafeb745a6d41d5909a454992c1107c318f59f80f8d","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"69b3922251aa575049849afbf72d429c17965f5827fcfc0b6636263d0a261779","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a23b58c3087d419c5d21fba70096b8a9eb42977ad61f22f6f7fba5e09e0e6ae3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"055e5ac1ac33ae174595f55c76aa1e371ca8819456cc5b97a69872037139ac72","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45f9d9bf9998e2ec7147ad27c7edc2e5ed387302c4018c9f4f7ff088eb22af8a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"90e9e2b93e5bae19a5e66972efb5e6ec11dc1b50b9e8259f882055ccdb3d4aac","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f397adc5718aa5b8ea60ec16afed311eafe510111cdc0de0378994c629ff4eff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7a83e8d50eecbdb731f8da23ee08494be2d247cb9e8e5c3857da7cd9e07fdc50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"671265f64fc5c31cd317267ead0afc5c6c4634fb51204bfb54e3bac5d19d4db7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f1f4a3bf8d46ac603eaefa297ebfafb18a111a4854577d169bc3c0358bb373aa","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89a4895e643533b7b76f782b52aa9b695d0961f2695ca7720dc50b48e9e55215","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd12c9b4822755161ebb4ba65818948561a5982f5f493eca9f6f0db242a468b3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"114445bc0794c2c9a3f03a42134748f545ea788a004e4667d7b9eff39211a61f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3a47055c95d43ba5fec9e3d06bdf5e2458db8a93246b28c9efd0525fffb77f74","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"09b4d72988c4682aa5713bc6a6df7892a7b8e2f10e1af3dc02985c6d4aef84ad","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"baefb08b615c9fc53a31c27981bf8899af8e01a0c9e2ab60c23cf0d324d77274","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"465069c75ef1e4b084bce885c0a2ee70520c5ebb8f201fe6f85090a28fb34703","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1e1d5c76c59c49b2b6f32b7065d0a95bfd229908da2db72526ebdb9312ed2cdd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"99eedc96ba7fa339e3ac82727c73628382af56287cc1219589004ea36e1b0c64","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"40510866633ba6c635e0495e40994f4e3f30d9378f23cc26887b3ff5e56391a3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c9cc3eb942b0cb85daf312f463a21c77b019a156a10240188e26976bc5f274b9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"288e930c1a2661f6345d07635585f1fe13c2deda86e2ccfc349413716c420555","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7118997a67de7cc91539da74dd524459d5cf3f9e2a8b90efcf140600ec9a4d82","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"beabd1db71dc8e0911944d9400ced2cd02de425ffeb61c6ca0d2124cbe64d785","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"945b34137fa3efb0ae22928275c1c42a722dbb81a26c57fb02f64f71e5daa3ce","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ccbb103828aaddf117b3d694cb122ae8657298732bd550ae10bd1284366080b6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"294041d51d1910e6986cfe979cd3732a5f7eae7f329589ca4f2799248e5a7265","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4516f87a2270fb1f66171535148068380734007309d9568c4b81e42124e08cd1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ab4a93c3142d6f517f1d764f00e83b838fe91e8533dd3b006c2b3c2c901b4a6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"de385ab9ee98320c98662be263959c7d9c55bb2cba5e8d7f9107def8bb82e4f6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c86fd4b8708bcfe085c01b61dc75aff81070eb58ac0ee7bde066589d1876b7a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"2309f180e82b613a4f9276b83512a7a85241e7651e734437bc3e89f2cfb342e1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"78228dfdc1a7024c9e4eabee22c057409886b9014ebd22319ee8a0a9ed31e799","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"18beabc03110b8f4e1d1eb5a556e6de09834d365995b2f10b17d26a574dba141","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"70e8e8b44c2fca9791db4f3c06c3cb310556b19335d656bfe3cfe6aee8d65622","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"995394b9345e8eae0c2413b22ea07faa239769d45a58bb219b9222d86bf2d9b7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"04b5ec07def664c916b3a73a5b4b31f3930a626739ddb528569bdd33f0300456","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"aa82aeaf9be0588a652ec636bca8e6d7be86a81f85bb22e857b02a469e8ab2b8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"028ca5c0547be6d2d8d1d20306614c16c3dee3d6501f2c38031e03aa6c9ccbed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4b54f4c0273db6878a1823ac888998ad7a0dd816f1c45a2fa24e0417702fc7c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32b1d470365f006b5fb2ad91d9097eefce0fad34bba93764e4501f2611104482","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"41b4c577be164a41be457fa1eff74c8923c8f08e8ba7e5e57d894424f48de2a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"15c805dfe9b0eedb507e5a9d32ae6e321327d77673ba6181de4710f2c2634cc2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5cc1c46b57a52eba565ad27fa54cf2e09d763de1f3412354357e6085e0d89ec4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd7ca639d42b7f9566dc6f58188e1919ddc8f3349dc7163c0efa1cb4205cbfdf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5597c2bdc52f017f791a1962e3595e58f628d60de80407f11e0c3020565b52f8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"47c0e01bceee2d7e95b691b2417954d55251167544413855e8440495dd67a5a8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c2b1d972a2b83eb6f85a7d894c57331aa5be4e9b93d1b9b16d697112b52069bc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4599379d9c9ef5f827a03600471298c7f5ce06580b0ef6364a0f1e82423ad9c5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"931f024c731f1ec9fe2618f279ec2ad7c4a74e5378728dd2a60f427afc066227","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a1ff1783f054a0cc5e2a938d35db521e61071d522c9b9ba9036bf33844dedd99","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"35bd7fa89a59ca3b136c221ef604ba3a8e30cc88bb03cbc4d26fee0659d02ce7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d596725c15eee936539fe4bcb0ec9f08b2d8392f0e9bce03effb76ed734910ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b08b6177c9234876e6836895b0bbf4465e14c9b64bbb7467da5b89b9b5b11d89","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ed0d11407fc46bc08b4b3b36c4d168f207ebdcf78496adef7dc126513efb6877","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a85610ba8978234a59d37629fafe05c27fb2154cb62b6b775eb8119802e0a38e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"adfa78b8af8a8be5116202f634a2f113d7801ed20c47767339f1505f952ebcc1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f328b3203d26b5f709e5a082bc956c2e95fbd9fbdca6abee0802b59b633e0dc0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4435ab8057ea5a31a324cc45bf3845138b7589c4ba8399f4f554b17b8fb79489","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d54083855fcc2ed66dedb389bf2efc33b892dcf572829e43758309fe7bcaabb3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f44df634432426f2b1249398b735f171a84c3902b4e0452ea2f7cc3d02568bd1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7952ed742b48930403868cdd2e09a9b5aa543c9adbed9f012618d6b58c289dff","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"32466921bab35fe4558bf82916cc03ac02d5d6d87d592fe1c4a141ae801ba5ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ba0d14c18aebe4a5cba52b4a7b902247dd5a91106737e06d6e2112b1b4cbcacf","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b8983ef3b44f8779b91c7604f70379f8c40f88da3d6863e4bb7a5d7f95b2c98","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"046287f8ba5cdeb90b848a6eae7311c5a34b343e63b3fe23d3eea19eb59ab284","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6889f8eea64d18bcd198ca1ad22b306cf48f4e8e213e15d3c789e98d67e4c87b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ecaaff298281e8bd8bc234e03d4bc1ba565a804edb846005ea6566cbcc47fc73","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ceded34bed1c475b90671a320a8fd84a6a4a4d7c56c3f3f88d9a6804e933eba4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6679ae1bd78dea53dc058ae235a3708f27ac7f87da929ddd38f7d4c222c18f9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9a40eaa7da4b2085746448671fad7ca6da6a84c58cb1d0e2ebfba17888d040a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b2dd58af1e54e5d04b9130dbef33c83234955d0faa6c32018be9934e12ac2015","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"16e6cada57ba26e2ea6c272e08dc7017b9df78f114061b8144ef1c0537bc3274","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"9815b675507e394e469b6bc395afbe8c63d6736cc7290a73f56cfaaca549b027","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0248daeeaac242de0ea72ac0f093a31b55e70b43020d40380dbf609803a45e7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"5a31a79a9691f6153276381e906dd27e985f53c6920adab35199527cbfaeace8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3414d416e51f2846b21f4aa1492fd44bf9ffeaece26743ae1527154e3da6f7fe","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6309b32da582c7b3e5afdf30678bd7d456cd9a1118ea1c660dd73ee32770d683","signature":"4c372df16f354b44e6e653a4442eb9f26b95f2d43efcbaa75b59506276b92df7"},{"version":"26f213bee14ac8092e7a36473db58d1955fa4867bf5b091950ad8dfd31956809","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4b332cfe58c80b9e5abef88dfe157a88f9170f64035fd2a83dc395b334c440fc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"262dc2495f719b674acd7919e678de874580311f4a0cb71f04c69995bf61650e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1161b186fb7ef72c0dbd14af1652937e6cb3453231dd6f56d396f43d46d638f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"89ac6a7385062683575fc5ad85a18f77e6c9617a3786f49aba644d55ae277f4e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e33a62342fe8bc07fd5ffb6e870ed8f0d906f8021115bea5b4ef5cbd3632d04","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8bad29d0d9cfb3316a408a53c38ce6c56dc4d90b1155f5adc9a72c5e6ef6476e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b247732a1ae37a5e0307d4333ef15e3d5393951e3236be0287adeaa48d553b35","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"1e11adf04ff8cb1c16d42cd6efe0e1039cc1335c26671292bc2c96ecbf9a0b30","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b1538a92b9bae8d230267210c5db38c2eb6bdb352128a3ce3aa8c6acf9fc9622","impliedFormat":1},{"version":"6fc1a4f64372593767a9b7b774e9b3b92bf04e8785c3f9ea98973aa9f4bbe490","impliedFormat":1},{"version":"ff09b6fbdcf74d8af4e131b8866925c5e18d225540b9b19ce9485ca93e574d84","impliedFormat":1},{"version":"d5895252efa27a50f134a9b580aa61f7def5ab73d0a8071f9b5bf9a317c01c2d","impliedFormat":1},{"version":"1f366bde16e0513fa7b64f87f86689c4d36efd85afce7eb24753e9c99b91c319","impliedFormat":1},{"version":"fb893a0dfc3c9fb0f9ca93d0648694dd95f33cbad2c0f2c629f842981dfd4e2e","impliedFormat":1},{"version":"89e326922cadcc2331d7e851011cf9f0456a681aaf3c95b48b81f8d80e8cdfba","impliedFormat":1},{"version":"5d08a179b846f5ee674624b349ebebe2121c455e3a265dc93da4e8d9e89722b4","impliedFormat":1},{"version":"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","impliedFormat":1},{"version":"7fa8d75d229eeaee235a801758d9c694e94405013fe77d5d1dd8e3201fc414f1","impliedFormat":1}],"root":[531,532,613,614,[630,634],[636,640],[1034,1038],[1040,1048],[1083,1138],[1175,1245],[1312,1336],[1340,1368],[1371,1380],[1400,1431],[1453,1576],[1654,1656],[1662,1701],[1933,1956],[1958,1989],[1991,2056],[2060,2068],[2084,2130],[2275,2302],[2304,2316],[2319,2379],[2638,2652],[2654,2667],2672,2673,2675,2677,2678,2682,2684,2686,2688,2690,2692,2694,2695,[2700,2722],[2810,2960],[3038,3404],[3422,3424],[3492,4156]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[4155,1],[531,2],[4156,3],[532,4],[3490,5],[3438,6],[3436,7],[3439,8],[3443,9],[3432,10],[3442,11],[3455,12],[3491,13],[3425,2],[3454,14],[3453,2],[3430,2],[3437,15],[3433,16],[3431,17],[3441,18],[3429,19],[3440,20],[3434,21],[3463,22],[3464,23],[3460,24],[3459,25],[3480,26],[3483,27],[3482,28],[3484,26],[3481,29],[3479,30],[3449,31],[3465,32],[3448,33],[3486,34],[3444,35],[3445,36],[3478,37],[3466,38],[3450,35],[3452,39],[3451,40],[3462,41],[3467,42],[3485,43],[3446,35],[3468,44],[3471,45],[3470,46],[3469,47],[3474,48],[3473,49],[3472,36],[3447,35],[3475,35],[3477,50],[3476,51],[3487,52],[3489,53],[3458,54],[3456,55],[3457,56],[3461,57],[3488,35],[3435,2],[651,58],[655,59],[654,60],[650,61],[653,62],[646,63],[652,58],[707,64],[719,65],[718,66],[708,67],[716,68],[752,69],[751,70],[731,71],[743,72],[722,73],[729,71],[723,74],[755,75],[754,76],[757,77],[756,78],[753,72],[758,72],[759,79],[764,80],[765,81],[763,82],[762,83],[761,84],[760,80],[769,85],[768,86],[767,87],[648,88],[649,89],[766,90],[740,91],[737,92],[779,72],[778,72],[777,72],[733,92],[745,74],[746,72],[742,72],[741,72],[732,72],[782,93],[781,94],[773,71],[730,71],[776,92],[775,72],[771,95],[734,72],[739,96],[736,97],[738,91],[721,98],[770,73],[749,99],[750,2],[744,72],[735,72],[774,71],[772,74],[813,100],[812,101],[810,102],[788,103],[811,72],[814,104],[816,105],[815,106],[709,92],[710,72],[711,72],[818,107],[817,108],[712,109],[713,97],[706,110],[705,111],[704,112],[714,72],[715,113],[717,92],[820,114],[822,115],[821,116],[823,92],[824,72],[825,72],[826,72],[828,72],[827,72],[841,117],[840,118],[832,119],[833,97],[834,104],[830,120],[831,121],[835,122],[836,72],[837,113],[838,92],[839,104],[845,80],[844,95],[843,123],[849,124],[848,125],[847,95],[842,95],[727,126],[846,127],[853,128],[852,129],[851,72],[850,72],[695,130],[674,131],[677,132],[673,133],[693,134],[672,135],[688,136],[696,137],[678,135],[679,138],[697,135],[691,139],[680,135],[684,140],[685,135],[686,141],[683,142],[689,143],[698,144],[690,145],[699,146],[692,147],[694,148],[687,135],[682,149],[725,150],[726,151],[1029,152],[855,153],[854,154],[643,155],[819,74],[748,2],[724,156],[675,2],[972,72],[641,2],[642,157],[720,74],[681,2],[645,158],[676,159],[647,74],[783,91],[784,92],[792,92],[791,160],[794,72],[793,72],[809,161],[808,162],[795,72],[796,72],[797,96],[798,97],[799,91],[800,160],[802,92],[801,72],[790,163],[786,164],[789,165],[785,166],[804,167],[803,168],[807,72],[805,169],[806,72],[857,170],[856,160],[787,171],[859,172],[858,72],[866,173],[865,174],[862,175],[864,175],[860,72],[861,175],[863,175],[877,91],[875,92],[870,92],[879,72],[881,176],[880,177],[869,72],[878,72],[868,72],[876,178],[872,97],[873,91],[867,63],[871,72],[874,72],[886,179],[884,179],[885,179],[891,180],[890,181],[887,179],[883,182],[889,179],[888,179],[882,2],[896,183],[895,184],[894,185],[893,186],[892,2],[905,91],[906,92],[909,72],[908,72],[912,187],[911,188],[904,96],[902,97],[903,91],[900,189],[899,190],[898,191],[907,72],[901,192],[910,72],[921,91],[922,92],[925,193],[924,194],[920,178],[917,195],[919,91],[915,196],[914,197],[913,198],[918,199],[923,72],[932,200],[931,201],[928,202],[930,202],[926,72],[927,202],[929,202],[938,203],[937,80],[936,204],[935,205],[934,206],[933,95],[942,207],[944,72],[946,208],[945,209],[939,72],[941,207],[943,72],[940,207],[960,91],[953,92],[964,72],[963,72],[951,72],[966,210],[965,211],[958,92],[959,72],[957,72],[948,95],[956,72],[955,96],[952,97],[954,91],[947,98],[961,72],[962,72],[949,71],[950,72],[780,212],[747,72],[970,213],[976,214],[975,215],[974,213],[968,213],[967,80],[973,216],[971,213],[969,213],[980,217],[979,218],[977,219],[978,220],[987,221],[986,222],[983,223],[985,224],[984,225],[982,226],[981,224],[998,72],[1000,91],[997,72],[994,72],[990,227],[995,72],[1002,228],[1001,229],[999,195],[988,230],[991,231],[993,232],[996,72],[989,233],[992,72],[1006,234],[1005,63],[1004,235],[1003,63],[1010,236],[1009,236],[1014,237],[1013,238],[1012,236],[1011,236],[1008,72],[1007,239],[1022,91],[1026,240],[1025,241],[1021,178],[1019,195],[1020,91],[1023,74],[1017,242],[1016,243],[1015,244],[1018,245],[1024,72],[644,246],[1028,247],[1027,159],[916,97],[703,248],[671,249],[702,250],[700,2],[701,251],[728,252],[829,74],[659,74],[657,253],[658,254],[664,255],[662,256],[660,2],[663,257],[661,258],[665,74],[897,2],[2669,259],[667,260],[669,261],[670,262],[666,2],[668,2],[1702,74],[1703,74],[1704,74],[1705,74],[1706,74],[1707,74],[1708,74],[1709,74],[1710,74],[1711,74],[1712,74],[1713,74],[1714,74],[1715,74],[1716,74],[1722,74],[1717,74],[1718,74],[1719,74],[1720,74],[1721,74],[1723,74],[1724,74],[1725,74],[1726,74],[1727,74],[1728,74],[1730,74],[1731,74],[1729,74],[1732,74],[1733,74],[1734,74],[1735,74],[1736,74],[1737,74],[1738,74],[1739,74],[1740,74],[1741,74],[1742,74],[1743,74],[1744,74],[1745,74],[1746,74],[1747,74],[1748,74],[1749,74],[1750,74],[1751,74],[1752,74],[1753,74],[1754,74],[1755,74],[1756,74],[1758,74],[1757,74],[1759,74],[1760,74],[1762,74],[1761,74],[1763,74],[1764,74],[1765,74],[1766,74],[1767,74],[1769,74],[1768,74],[1770,74],[1771,74],[1772,74],[1773,74],[1774,74],[1775,74],[1776,74],[1777,74],[1778,74],[1779,74],[1780,74],[1781,74],[1782,74],[1783,74],[1788,74],[1784,74],[1785,74],[1786,74],[1787,74],[1789,74],[1790,74],[1791,74],[1792,74],[1793,74],[1794,74],[1795,74],[1796,74],[1797,74],[1798,74],[1800,74],[1799,74],[1801,74],[1802,74],[1803,74],[1804,74],[1805,74],[1806,74],[1807,74],[1808,74],[1811,74],[1809,74],[1810,74],[1812,74],[1813,74],[1814,74],[1815,74],[1816,74],[1817,74],[1818,74],[1819,74],[1821,74],[1820,74],[1932,263],[1822,74],[1823,74],[1824,74],[1825,74],[1826,74],[1827,74],[1828,74],[1829,74],[1830,74],[1831,74],[1832,74],[1834,74],[1833,74],[1835,74],[1836,74],[1837,74],[1838,74],[1839,74],[1840,74],[1841,74],[1842,74],[1844,74],[1843,74],[1845,74],[1846,74],[1847,74],[1848,74],[1849,74],[1850,74],[1851,74],[1852,74],[1853,74],[1857,74],[1854,74],[1855,74],[1856,74],[1858,74],[1859,74],[1860,74],[1862,74],[1861,74],[1863,74],[1864,74],[1865,74],[1866,74],[1867,74],[1868,74],[1869,74],[1870,74],[1871,74],[1872,74],[1873,74],[1874,74],[1875,74],[1876,74],[1877,74],[1878,74],[1879,74],[1880,74],[1881,74],[1882,74],[1883,74],[1884,74],[1885,74],[1886,74],[1887,74],[1888,74],[1889,74],[1890,74],[1891,74],[1892,74],[1893,74],[1894,74],[1895,74],[1896,74],[1897,74],[1898,74],[1899,74],[1900,74],[1901,74],[1902,74],[1903,74],[1904,74],[1905,74],[1906,74],[1907,74],[1908,74],[1909,74],[1910,74],[1911,74],[1912,74],[1913,74],[1914,74],[1915,74],[1917,74],[1916,74],[1918,74],[1919,74],[1920,74],[1921,74],[1922,74],[1923,74],[1924,74],[1925,74],[1926,74],[1927,74],[1928,74],[1929,74],[1930,74],[1931,74],[2083,264],[2082,265],[405,2],[374,2],[2145,266],[2144,267],[1660,2],[1447,268],[1446,2],[625,2],[626,269],[1452,270],[1449,271],[1450,272],[1451,272],[1448,273],[627,274],[628,275],[1443,276],[1432,74],[1445,277],[1442,276],[1439,278],[1440,278],[1441,2],[1444,2],[1174,279],[1433,2],[1435,280],[1438,281],[1437,2],[1436,280],[1434,282],[1153,283],[1163,284],[1160,284],[1161,285],[1145,285],[1159,285],[1140,284],[1146,286],[1149,287],[1154,288],[1142,286],[1143,285],[1156,289],[1141,286],[1147,286],[1150,286],[1155,286],[1157,285],[1144,285],[1158,285],[1152,290],[1148,291],[1173,292],[1151,293],[1162,294],[1139,285],[1164,285],[1165,285],[1166,285],[1167,285],[1168,285],[1169,285],[1170,285],[1171,285],[1172,285],[1395,2],[1392,2],[1391,2],[1386,295],[1397,296],[1382,297],[1393,298],[1385,299],[1384,300],[1394,2],[1389,301],[1396,2],[1390,302],[1383,2],[2681,303],[2680,304],[2679,297],[1399,305],[1639,306],[1640,306],[1642,307],[1641,306],[1634,306],[1635,306],[1637,308],[1636,306],[1614,2],[1613,2],[1616,309],[1615,2],[1612,2],[1579,310],[1577,311],[1580,2],[1627,312],[1581,306],[1617,313],[1626,314],[1618,2],[1621,315],[1619,2],[1622,2],[1624,2],[1620,315],[1623,2],[1625,2],[1578,316],[1653,317],[1638,306],[1633,318],[1643,319],[1649,320],[1650,321],[1652,322],[1651,323],[1631,318],[1632,324],[1628,325],[1630,326],[1629,327],[1644,306],[1648,328],[1645,306],[1646,329],[1647,306],[1582,2],[1583,2],[1586,2],[1584,2],[1585,2],[1588,2],[1589,330],[1590,2],[1591,2],[1587,2],[1592,2],[1593,2],[1594,2],[1595,2],[1596,331],[1597,2],[1611,332],[1598,2],[1599,2],[1600,2],[1601,2],[1602,2],[1603,2],[1604,2],[1607,2],[1605,2],[1606,2],[1608,306],[1609,306],[1610,333],[1381,2],[602,334],[4157,2],[4158,2],[4159,2],[4160,335],[2154,2],[2132,336],[2155,337],[2131,2],[4161,2],[4163,338],[600,2],[4164,339],[546,2],[2724,340],[2668,2],[4165,2],[2734,340],[4162,2],[3427,2],[3428,341],[140,342],[141,342],[142,343],[97,344],[143,345],[144,346],[145,347],[92,2],[95,348],[93,2],[94,2],[146,349],[147,350],[148,351],[149,352],[150,353],[151,354],[152,354],[153,355],[154,356],[155,357],[156,358],[98,2],[96,2],[157,359],[158,360],[159,361],[191,362],[160,363],[161,364],[162,365],[163,366],[164,367],[165,368],[166,369],[167,370],[168,371],[169,372],[170,372],[171,373],[172,2],[173,374],[175,375],[174,376],[176,17],[177,377],[178,378],[179,379],[180,380],[181,381],[182,382],[183,383],[184,384],[185,385],[186,386],[187,387],[188,388],[99,2],[100,2],[101,2],[139,389],[189,390],[190,391],[1990,392],[1957,74],[195,393],[460,74],[196,394],[194,395],[462,396],[461,397],[1398,74],[1369,398],[192,399],[458,2],[193,400],[83,2],[85,401],[457,74],[226,74],[2723,2],[4166,2],[542,402],[589,403],[587,2],[588,2],[534,2],[584,404],[581,405],[582,406],[603,407],[594,2],[597,408],[596,409],[608,409],[595,410],[533,2],[541,411],[583,411],[536,412],[539,413],[590,412],[540,414],[535,2],[601,2],[1033,415],[1032,416],[1030,2],[84,2],[2468,417],[2447,418],[2544,2],[2448,419],[2384,417],[2385,417],[2386,417],[2387,417],[2388,417],[2389,417],[2390,417],[2391,417],[2392,417],[2393,417],[2394,417],[2395,417],[2396,417],[2397,417],[2398,417],[2399,417],[2400,417],[2401,417],[2380,2],[2402,417],[2403,417],[2404,2],[2405,417],[2406,417],[2408,417],[2407,417],[2409,417],[2410,417],[2411,417],[2412,417],[2413,417],[2414,417],[2415,417],[2416,417],[2417,417],[2418,417],[2419,417],[2420,417],[2421,417],[2422,417],[2423,417],[2424,417],[2425,417],[2426,417],[2427,417],[2429,417],[2430,417],[2431,417],[2428,417],[2432,417],[2433,417],[2434,417],[2435,417],[2436,417],[2437,417],[2438,417],[2439,417],[2440,417],[2441,417],[2442,417],[2443,417],[2444,417],[2445,417],[2446,417],[2449,420],[2450,417],[2451,417],[2452,421],[2453,422],[2454,417],[2455,417],[2456,417],[2457,417],[2460,417],[2458,417],[2459,417],[2382,2],[2461,417],[2462,417],[2463,417],[2464,417],[2465,417],[2466,417],[2467,417],[2469,423],[2470,417],[2471,417],[2472,417],[2474,417],[2473,417],[2475,417],[2476,417],[2477,417],[2478,417],[2479,417],[2480,417],[2481,417],[2482,417],[2483,417],[2484,417],[2486,417],[2485,417],[2487,417],[2488,2],[2489,2],[2490,2],[2637,424],[2491,417],[2492,417],[2493,417],[2494,417],[2495,417],[2496,417],[2497,2],[2498,417],[2499,2],[2500,417],[2501,417],[2502,417],[2503,417],[2504,417],[2505,417],[2506,417],[2507,417],[2508,417],[2509,417],[2510,417],[2511,417],[2512,417],[2513,417],[2514,417],[2515,417],[2516,417],[2517,417],[2518,417],[2519,417],[2520,417],[2521,417],[2522,417],[2523,417],[2524,417],[2525,417],[2526,417],[2527,417],[2528,417],[2529,417],[2530,417],[2531,417],[2532,2],[2533,417],[2534,417],[2535,417],[2536,417],[2537,417],[2538,417],[2539,417],[2540,417],[2541,417],[2542,417],[2543,417],[2545,425],[2381,417],[2546,417],[2547,417],[2548,2],[2549,2],[2550,2],[2551,417],[2552,2],[2553,2],[2554,2],[2555,2],[2556,2],[2557,417],[2558,417],[2559,417],[2560,417],[2561,417],[2562,417],[2563,417],[2564,417],[2569,426],[2567,427],[2568,428],[2566,429],[2565,417],[2570,417],[2571,417],[2572,417],[2573,417],[2574,417],[2575,417],[2576,417],[2577,417],[2578,417],[2579,417],[2580,2],[2581,2],[2582,417],[2583,417],[2584,2],[2585,2],[2586,2],[2587,417],[2588,417],[2589,417],[2590,417],[2591,423],[2592,417],[2593,417],[2594,417],[2595,417],[2596,417],[2597,417],[2598,417],[2599,417],[2600,417],[2601,417],[2602,417],[2603,417],[2604,417],[2605,417],[2606,417],[2607,417],[2608,417],[2609,417],[2610,417],[2611,417],[2612,417],[2613,417],[2614,417],[2615,417],[2616,417],[2617,417],[2618,417],[2619,417],[2620,417],[2621,417],[2622,417],[2623,417],[2624,417],[2625,417],[2626,417],[2627,417],[2628,417],[2629,417],[2630,417],[2631,417],[2632,417],[2383,430],[2633,2],[2634,2],[2635,2],[2636,2],[2059,431],[2058,432],[2057,2],[2653,433],[2267,2],[551,2],[2671,434],[2670,435],[621,436],[623,437],[622,438],[620,439],[619,2],[3426,440],[2142,2],[635,2],[574,2],[576,441],[575,2],[1039,74],[2803,2],[2777,442],[2776,443],[2775,444],[2802,445],[2801,446],[2805,447],[2804,448],[2807,449],[2806,450],[2762,451],[2736,452],[2737,453],[2738,453],[2739,453],[2740,453],[2741,453],[2742,453],[2743,453],[2744,453],[2745,453],[2746,453],[2760,454],[2747,453],[2748,453],[2749,453],[2750,453],[2751,453],[2752,453],[2753,453],[2754,453],[2756,453],[2757,453],[2755,453],[2758,453],[2759,453],[2761,453],[2735,455],[2800,456],[2780,457],[2781,457],[2782,457],[2783,457],[2784,457],[2785,457],[2786,458],[2788,457],[2787,457],[2799,459],[2789,457],[2791,457],[2790,457],[2793,457],[2792,457],[2794,457],[2795,457],[2796,457],[2797,457],[2798,457],[2779,457],[2778,460],[2770,461],[2768,462],[2769,462],[2773,463],[2771,462],[2772,462],[2774,462],[2767,2],[2303,2],[1370,74],[483,464],[488,1],[495,465],[478,466],[230,2],[238,467],[378,468],[381,469],[353,2],[366,470],[373,471],[255,2],[355,2],[236,2],[352,472],[398,473],[237,2],[228,474],[380,475],[382,476],[383,477],[455,478],[347,479],[300,480],[360,481],[361,482],[359,483],[358,2],[354,484],[379,485],[239,486],[425,2],[426,487],[266,488],[240,489],[267,488],[303,488],[206,488],[376,490],[375,2],[365,491],[473,2],[215,2],[494,492],[433,493],[434,494],[430,495],[512,2],[330,2],[435,104],[431,496],[517,497],[516,498],[511,2],[281,2],[333,499],[332,2],[510,500],[432,74],[286,501],[293,502],[295,503],[285,2],[290,504],[292,505],[294,506],[289,507],[287,2],[291,508],[513,2],[509,2],[515,509],[514,2],[284,510],[504,511],[507,512],[274,513],[273,514],[272,515],[520,74],[271,516],[260,2],[522,2],[2697,517],[2696,2],[523,74],[524,518],[198,2],[362,519],[363,520],[364,521],[202,2],[367,2],[222,522],[197,2],[447,74],[204,523],[446,524],[445,525],[436,2],[437,2],[444,2],[439,2],[442,526],[438,2],[440,527],[443,528],[441,527],[235,2],[232,2],[233,488],[387,2],[392,529],[393,530],[391,531],[389,532],[390,533],[385,2],[453,104],[227,104],[482,534],[489,535],[493,536],[321,537],[320,2],[315,2],[469,538],[477,539],[348,540],[349,541],[428,542],[337,2],[451,543],[325,74],[342,544],[454,545],[338,2],[341,546],[339,2],[452,547],[449,548],[448,2],[450,2],[345,2],[424,549],[210,550],[323,551],[327,552],[343,553],[346,554],[335,555],[328,556],[476,557],[401,558],[319,559],[207,560],[475,561],[203,562],[394,563],[386,2],[395,564],[413,565],[384,2],[412,566],[91,2],[407,567],[231,2],[427,568],[402,2],[216,2],[218,2],[357,2],[411,569],[234,2],[258,570],[344,571],[264,572],[324,2],[410,2],[388,2],[415,573],[416,574],[356,2],[418,575],[420,576],[419,577],[368,2],[409,560],[422,578],[318,579],[408,580],[414,581],[243,2],[247,2],[246,2],[245,2],[250,2],[244,2],[253,2],[252,2],[249,2],[248,2],[251,2],[254,582],[242,2],[310,583],[309,2],[314,584],[311,585],[313,586],[316,584],[312,585],[223,587],[302,588],[472,589],[470,2],[499,590],[501,591],[465,592],[500,593],[211,594],[208,594],[241,2],[225,595],[224,596],[220,597],[221,598],[229,599],[257,599],[268,599],[304,600],[269,600],[213,601],[212,2],[308,602],[307,603],[306,604],[305,605],[214,606],[456,607],[256,608],[464,609],[429,610],[459,611],[463,612],[351,613],[350,614],[331,615],[317,616],[299,617],[301,618],[298,619],[421,620],[322,2],[487,2],[219,621],[423,622],[471,623],[329,2],[259,624],[336,625],[334,626],[261,627],[396,628],[466,2],[262,629],[397,629],[485,2],[484,2],[486,2],[468,2],[467,2],[399,630],[326,2],[296,631],[217,632],[275,2],[201,633],[263,2],[491,74],[200,2],[503,634],[283,74],[497,104],[282,635],[480,636],[280,634],[205,2],[505,637],[278,74],[279,74],[270,2],[199,2],[277,638],[276,639],[265,640],[340,371],[400,371],[417,2],[404,641],[403,2],[288,510],[209,2],[297,74],[474,522],[481,642],[86,74],[89,643],[90,644],[87,74],[88,2],[377,645],[372,646],[371,2],[370,647],[369,2],[479,648],[490,649],[492,650],[496,651],[2698,652],[498,653],[502,654],[530,655],[506,655],[529,656],[508,657],[518,658],[519,659],[521,660],[525,661],[528,522],[527,2],[526,662],[2699,663],[1659,663],[1658,664],[1657,74],[1661,665],[2962,2],[2968,666],[2961,2],[2965,2],[2967,667],[2964,668],[3037,669],[3031,669],[2992,670],[2988,671],[3003,672],[2993,673],[3000,674],[2987,675],[3001,2],[2999,676],[2996,677],[2997,678],[2994,679],[3002,680],[2969,668],[3032,681],[2983,682],[2980,683],[2981,684],[2982,685],[2971,686],[2990,687],[3009,688],[3005,689],[3004,690],[3008,691],[3006,692],[3007,692],[2984,693],[2986,694],[2985,695],[2989,696],[3033,697],[2991,698],[2973,699],[3034,700],[2972,701],[3035,702],[2974,703],[3012,704],[3010,683],[3011,705],[2975,692],[3016,706],[3014,707],[3015,708],[2976,709],[3019,710],[3018,711],[3021,712],[3020,713],[3024,714],[3022,713],[3023,715],[3017,716],[3013,717],[3025,716],[2977,692],[3036,718],[2978,713],[2979,692],[2995,719],[2998,720],[2970,2],[3026,692],[3027,721],[3029,722],[3028,723],[3030,724],[2963,725],[2966,726],[1338,727],[1339,728],[1337,2],[569,729],[567,730],[568,731],[556,732],[557,730],[564,733],[555,734],[560,735],[570,2],[561,736],[566,737],[572,738],[571,739],[554,740],[562,741],[563,742],[558,743],[565,729],[559,744],[1388,745],[1387,2],[1049,2],[1065,746],[1066,746],[1067,746],[1068,746],[1082,747],[1069,748],[1070,748],[1071,749],[1062,750],[1060,751],[1051,2],[1055,752],[1059,753],[1057,754],[1064,755],[1052,756],[1053,757],[1054,758],[1056,759],[1058,760],[1061,761],[1063,762],[1072,748],[1073,748],[1074,748],[1075,746],[1076,748],[1077,748],[1050,748],[1078,2],[1080,763],[1079,748],[1081,746],[2317,764],[2318,765],[2766,766],[2765,767],[2171,768],[2264,769],[2262,770],[2169,2],[2170,771],[2263,2],[2265,772],[2173,773],[2172,774],[2176,775],[2243,776],[2238,777],[2139,778],[2209,779],[2202,780],[2259,781],[2137,782],[2208,783],[2197,784],[2196,774],[2242,785],[2239,786],[2190,787],[2201,788],[2244,789],[2245,789],[2246,790],[2254,791],[2248,791],[2256,791],[2260,791],[2247,791],[2249,792],[2252,792],[2255,792],[2251,793],[2253,791],[2257,794],[2250,795],[2148,796],[2223,74],[2220,797],[2224,74],[2159,791],[2149,791],[2215,798],[2138,799],[2158,800],[2162,801],[2222,791],[2135,74],[2221,802],[2219,74],[2218,791],[2150,74],[2269,803],[2233,795],[2213,804],[2274,805],[2231,2],[2229,2],[2234,806],[2232,807],[2228,808],[2230,809],[2235,810],[2237,811],[2227,74],[2157,812],[2134,791],[2226,791],[2175,813],[2225,74],[2198,812],[2258,791],[2192,814],[2146,815],[2151,816],[2203,817],[2205,814],[2184,818],[2187,814],[2163,819],[2186,820],[2194,821],[2195,822],[2191,823],[2206,824],[2193,825],[2168,826],[2214,827],[2210,828],[2211,829],[2207,830],[2185,831],[2174,832],[2178,833],[2152,834],[2182,835],[2183,836],[2179,837],[2153,838],[2164,839],[2204,822],[2147,840],[2212,2],[2177,841],[2167,842],[2199,2],[2271,843],[2272,844],[2273,771],[2240,2],[2270,771],[2261,2],[2188,2],[2160,2],[2236,845],[2189,2],[2140,771],[2268,846],[2166,847],[2200,848],[2165,849],[2241,850],[2180,2],[2216,2],[2217,851],[2161,2],[2181,2],[2266,2],[2136,74],[2143,852],[2141,2],[2809,853],[2808,854],[2764,855],[2763,856],[656,2],[548,857],[547,339],[406,858],[629,74],[553,2],[1031,2],[604,2],[537,2],[538,859],[2731,860],[2730,2],[81,2],[82,2],[13,2],[14,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[57,2],[58,2],[60,2],[59,2],[61,2],[62,2],[10,2],[63,2],[64,2],[65,2],[11,2],[66,2],[67,2],[68,2],[69,2],[70,2],[1,2],[71,2],[72,2],[12,2],[76,2],[74,2],[79,2],[78,2],[73,2],[77,2],[75,2],[80,2],[117,861],[127,862],[116,861],[137,863],[108,864],[107,865],[136,662],[130,866],[135,867],[110,868],[124,869],[109,870],[133,871],[105,872],[104,662],[134,873],[106,874],[111,875],[112,2],[115,875],[102,2],[138,876],[128,877],[119,878],[120,879],[122,880],[118,881],[121,882],[131,662],[113,883],[114,884],[123,885],[103,886],[126,877],[125,875],[129,2],[132,887],[2733,888],[2729,2],[2732,889],[3421,890],[3405,2],[3406,2],[3408,891],[3409,2],[3407,2],[3410,891],[3411,891],[3413,892],[3412,891],[3414,891],[3415,892],[3416,891],[3417,2],[3418,891],[3419,2],[3420,2],[2726,893],[2725,340],[2728,894],[2727,895],[2133,896],[2156,897],[606,898],[592,899],[593,898],[591,2],[544,900],[580,901],[550,902],[545,900],[543,2],[549,903],[578,2],[573,2],[577,904],[552,2],[579,905],[612,906],[605,907],[598,908],[607,909],[586,910],[616,911],[617,912],[609,913],[618,914],[610,915],[599,916],[615,917],[611,918],[2674,919],[624,920],[585,2],[2073,921],[2080,922],[2075,2],[2076,2],[2074,923],[2077,924],[2069,2],[2070,2],[2081,925],[2072,926],[2078,2],[2079,927],[2071,928],[1305,929],[1308,930],[1306,930],[1302,929],[1309,931],[1310,932],[1307,930],[1303,933],[1304,934],[1298,935],[1250,936],[1252,937],[1296,2],[1251,938],[1297,939],[1301,940],[1299,2],[1253,936],[1254,2],[1295,941],[1249,942],[1246,2],[1300,943],[1247,944],[1248,2],[1311,945],[1255,946],[1256,946],[1257,946],[1258,946],[1259,946],[1260,946],[1261,946],[1262,946],[1263,946],[1264,946],[1265,946],[1267,946],[1266,946],[1268,946],[1269,946],[1270,946],[1294,947],[1271,946],[1272,946],[1273,946],[1274,946],[1275,946],[1276,946],[1277,946],[1278,946],[1279,946],[1281,946],[1280,946],[1282,946],[1283,946],[1284,946],[1285,946],[1286,946],[1287,946],[1288,946],[1289,946],[1290,946],[1291,946],[1292,946],[1293,946],[2683,948],[2685,267],[2687,267],[2689,267],[2691,267],[2693,267],[2676,267],[2867,949],[2858,950],[1314,951],[1313,952],[1312,953],[2864,954],[2857,955],[2855,956],[2866,957],[2856,958],[2865,959],[2861,960],[2860,961],[2859,962],[1245,267],[2862,963],[2895,964],[2893,965],[2894,966],[2812,967],[2911,968],[2912,969],[2901,970],[2913,971],[2899,972],[1315,267],[2914,973],[2903,974],[1317,975],[1316,976],[2898,977],[2915,978],[2916,979],[2904,980],[1320,981],[1319,982],[2917,983],[2902,984],[2896,985],[2909,986],[2907,987],[2910,988],[2906,989],[2905,990],[2897,991],[2900,992],[2908,993],[2918,994],[2851,995],[2919,996],[2924,997],[2921,998],[2920,999],[2923,1000],[2932,1001],[2925,1002],[2933,1003],[2929,1004],[1322,1005],[1321,267],[2931,1006],[2927,1007],[2926,1008],[1323,267],[2934,1009],[2928,1010],[2930,1011],[2949,1012],[2946,1013],[2950,1014],[2936,1015],[2939,1016],[2938,1017],[1324,267],[1326,1018],[1325,1019],[2952,1020],[2953,1020],[2940,1021],[2951,1022],[2937,1023],[1327,267],[2942,1024],[2941,1025],[2954,1026],[2943,1027],[1329,1028],[1328,1029],[2955,1030],[2956,1031],[2944,1032],[1087,267],[2948,1033],[2945,1034],[2935,104],[2947,1035],[2719,1036],[1331,1037],[1330,1038],[3051,1039],[2831,1040],[3052,1041],[3047,1042],[1334,1043],[1333,1044],[3053,1045],[3054,1045],[3049,1046],[1336,1047],[1335,267],[3055,1048],[3048,1049],[3056,1050],[2959,1051],[3057,1052],[2829,1053],[2828,1054],[3058,1055],[2830,1056],[3059,1057],[2958,1058],[1344,1059],[1343,1060],[3060,1061],[3061,1062],[1342,1063],[1346,1064],[1345,1065],[3050,1066],[3063,1067],[1358,1068],[3064,1069],[3065,1070],[1356,1071],[3066,1072],[3067,1072],[1378,1073],[3068,1074],[1373,1075],[1379,1076],[3071,1077],[1367,1078],[3072,1079],[1365,1080],[3073,1081],[1364,1082],[1403,1083],[1363,1084],[1359,1085],[1404,1086],[1366,1087],[3069,1088],[1355,1089],[1380,1090],[1374,1091],[3070,1092],[1357,1089],[1349,267],[1400,1093],[1377,1094],[1401,1095],[1375,1096],[1402,1097],[1376,1096],[3062,1098],[3149,1099],[3136,1100],[3151,1101],[3150,1102],[3152,1103],[3142,1104],[3154,1105],[3145,1106],[3155,1107],[3144,1108],[3153,1109],[3140,1110],[3156,1111],[3143,1112],[3148,1113],[3147,1114],[3112,1115],[3113,1116],[3090,1117],[1409,267],[3093,1118],[3124,1119],[3081,1120],[3079,1121],[3125,1122],[3082,1123],[3126,1124],[3094,1125],[3127,1126],[3095,1127],[3128,1128],[3129,1129],[3075,1130],[3130,1131],[3076,1132],[3078,1133],[3131,1134],[3074,1135],[3077,1118],[3132,1136],[1697,1137],[3133,1138],[3080,1139],[3134,1140],[1410,1141],[1411,1142],[3114,1143],[3102,1144],[3115,1145],[3100,1146],[1405,267],[1408,1147],[1407,1148],[3116,1149],[3101,1150],[3117,1151],[3118,1152],[3096,1153],[3119,1154],[1406,1155],[3084,1156],[3120,1157],[3085,1158],[3121,1159],[3092,1160],[3083,1161],[3109,1162],[3104,1163],[3091,1164],[3106,1165],[3098,1166],[3107,1167],[3099,1168],[3108,1169],[3097,1170],[3086,1171],[3122,1172],[3087,1173],[3123,1174],[3088,1175],[3110,1176],[3111,1177],[3103,1178],[3135,1179],[3089,1180],[3105,1181],[1422,1182],[1423,1183],[1421,1184],[1424,1185],[1425,1185],[1427,1186],[1426,1187],[1428,1188],[1114,1189],[1429,1190],[1431,1191],[1430,1192],[1455,1193],[1457,1194],[1456,1195],[1459,1196],[1458,1190],[1461,1197],[1460,1190],[1463,1198],[1462,1190],[1466,1199],[1465,1200],[1467,1201],[1107,267],[3158,1202],[1454,1203],[1468,1204],[1469,976],[1470,1205],[1471,1206],[1473,1207],[1472,1208],[1474,1206],[1475,1207],[1476,1209],[1478,1210],[1477,1211],[1480,1212],[1479,1213],[1481,1214],[1129,1211],[1483,1215],[1482,1216],[1484,1211],[1220,1189],[1486,1217],[1485,1211],[1488,1218],[1489,1219],[1487,1220],[1490,1221],[1492,1222],[1491,1221],[1239,1189],[1493,1223],[1494,1190],[1495,1211],[1496,1189],[1498,1224],[1497,1211],[1500,1225],[1499,1226],[1502,1227],[1501,1228],[1503,1228],[1504,1229],[1506,1230],[1505,1231],[1507,1230],[1509,1232],[1508,1189],[1510,1233],[1189,1211],[1512,1234],[1511,1235],[1513,1236],[1218,1211],[1516,1237],[1515,1238],[1518,1239],[1517,1238],[1520,1240],[1519,1241],[1521,1242],[1514,1243],[1523,1244],[1522,1238],[1525,1245],[1524,1189],[1527,1246],[1526,1211],[1222,1247],[1529,1248],[1528,1211],[1530,1190],[1532,1249],[1534,1250],[1533,1195],[1536,1251],[1535,1252],[1538,1253],[1537,1223],[1540,1254],[1539,1211],[1542,1255],[1541,1223],[1543,1256],[1545,1257],[1544,1258],[1547,1259],[1546,1260],[1548,1261],[1217,1262],[1549,1263],[1108,1189],[1552,1264],[1551,1265],[1553,1266],[1550,1189],[1555,1267],[1554,1189],[1412,1268],[1413,1269],[1109,1270],[1414,1271],[1231,1272],[1232,1272],[1415,1273],[1229,1272],[1416,1274],[1233,1272],[1417,1272],[1418,1275],[1219,1276],[1223,1277],[1557,1278],[1556,1189],[1559,1279],[1558,1211],[1561,1280],[1560,1243],[3157,1281],[1420,1282],[2815,1283],[2813,1284],[2811,1285],[1244,1286],[1243,1287],[3172,1288],[3192,1289],[3197,1290],[3240,1291],[3241,1292],[3218,1293],[1563,1294],[1562,1295],[1566,1296],[1565,1297],[3203,1298],[1568,1299],[1569,1300],[1567,1301],[3242,1302],[3215,1303],[3206,1304],[1571,1305],[1570,267],[3219,1306],[3238,1307],[3258,1308],[3220,1309],[3259,1310],[3208,1311],[3260,1312],[3229,1313],[3261,1314],[3207,1315],[3262,1316],[3223,1317],[3263,1318],[3264,1319],[3222,1320],[3265,1321],[3224,1322],[3266,1323],[3232,1324],[3267,1325],[3209,1326],[3268,1327],[3237,1328],[1573,1329],[1572,1330],[3257,1331],[1574,1332],[3245,1333],[3243,1334],[3214,1335],[3244,1336],[3228,1337],[3246,1338],[3211,1339],[3247,1340],[3221,1341],[3248,1342],[3193,1343],[3194,1344],[3250,1345],[3196,1346],[3249,1347],[3195,1348],[1576,1349],[1575,1350],[3251,1351],[3201,1352],[3198,1353],[3213,1354],[3252,1355],[3212,1356],[3253,1357],[3204,1358],[3210,1359],[1654,1360],[3199,1361],[3205,1362],[3233,1363],[1656,1364],[1655,1365],[3254,1366],[3234,1367],[3255,1368],[3202,1369],[3200,1370],[3256,1371],[3231,1372],[3269,1373],[1564,1343],[3239,1374],[3277,1375],[3270,1376],[3278,1377],[3271,1378],[3279,1379],[3273,1380],[3272,1381],[3280,1382],[3274,1383],[3276,1384],[3275,1385],[3298,1386],[3368,1387],[3367,1388],[1666,1389],[1665,1390],[3375,1391],[3325,1392],[3376,1393],[3324,1394],[1670,1395],[1669,1396],[3379,1397],[3332,1398],[3331,1399],[3330,1400],[1672,1401],[1671,267],[3377,1402],[3363,1403],[3323,1404],[3378,1405],[3371,1406],[1663,1407],[1662,1408],[3374,1409],[3373,1410],[3380,1411],[3369,1412],[3381,1413],[3342,1414],[3326,1415],[3382,1416],[3333,1417],[3383,1418],[3362,1419],[3347,1420],[3366,1421],[3364,1422],[3358,1423],[3372,1424],[1664,1425],[1674,1426],[1673,267],[1216,976],[3388,1427],[3386,1428],[3387,1429],[3401,1430],[3399,1431],[3402,1432],[3398,1433],[3397,1434],[3393,1435],[3392,1436],[3400,1437],[2853,1438],[2852,1439],[3508,1440],[3530,1441],[3500,1442],[3531,1443],[3522,1444],[3532,1445],[3509,1446],[3533,1447],[3501,1448],[1676,1449],[3510,1450],[3502,1451],[3534,1452],[3503,1453],[3535,1454],[3517,1455],[3536,1456],[3521,1457],[3537,1458],[3511,1459],[3504,1460],[3538,1461],[3505,1462],[3539,1463],[3506,1464],[3540,1465],[3507,1466],[3541,1467],[3520,1468],[3515,1469],[3518,1451],[3514,1453],[3516,1470],[3519,1471],[1678,1472],[1677,267],[3542,1473],[3527,1474],[3543,1475],[3525,1476],[3544,1477],[3523,1478],[3545,1479],[3526,1480],[3547,1481],[3546,1482],[3548,1483],[3524,1484],[1681,1485],[1680,1486],[3404,1487],[1686,1488],[1685,1489],[1688,1490],[3424,1491],[3549,1492],[3492,1493],[3550,1494],[3493,1495],[3551,1496],[3494,1497],[3552,1498],[3495,1499],[1679,976],[3496,1497],[3497,1497],[3499,1499],[3529,1500],[3528,1501],[3573,1502],[3563,1503],[3574,1504],[3557,1505],[3575,1506],[3568,1507],[3571,1508],[3560,1509],[3559,1510],[1691,1511],[1690,1512],[3576,1513],[3566,1514],[3577,1515],[3558,1516],[3578,1517],[3561,1518],[3579,1519],[3569,1520],[3580,1521],[3555,1522],[3581,1523],[3556,1524],[3582,1525],[3565,1526],[3583,1527],[3564,1528],[3572,1529],[3554,1530],[3553,1531],[1693,1532],[1692,267],[3584,1533],[3567,1534],[3562,1137],[3570,1535],[3595,1536],[3590,1537],[3596,1538],[3589,1539],[3597,1540],[3588,1541],[3587,1542],[3600,1543],[3601,1544],[3585,1545],[3602,1546],[3603,1547],[3586,1548],[3604,1549],[1973,1550],[1694,953],[1975,1551],[1974,1552],[3598,1553],[3593,1554],[3599,1555],[3592,1556],[3591,1557],[3594,1558],[3633,1559],[3610,1560],[3634,1561],[3630,1562],[3629,1563],[3646,1564],[3619,1565],[3651,1566],[3624,1567],[3647,1568],[3620,1569],[3648,1570],[3623,1480],[3649,1571],[3621,1572],[1979,1573],[1980,1574],[3650,1575],[3618,1139],[3622,104],[3638,1576],[3616,1577],[3626,1578],[3628,1579],[3639,1580],[3613,1581],[3640,1582],[3608,1583],[3641,1584],[3612,1585],[3642,1586],[3617,1587],[3643,1588],[3625,1589],[3644,1590],[3614,1591],[1976,267],[1978,1592],[1977,1593],[3645,1594],[3627,1595],[3635,1596],[3609,1597],[3605,1598],[3632,1599],[3607,1600],[3606,1601],[3636,1602],[3611,1603],[3637,1604],[3615,1605],[3631,1606],[3653,1607],[3046,1608],[3652,1609],[3663,1610],[3664,1611],[3655,1612],[3661,1613],[3665,1614],[3654,1615],[1983,1616],[1982,1617],[3669,1618],[3670,1618],[3660,1619],[3666,1620],[3657,1621],[3656,1622],[3667,1623],[3658,1624],[3668,1625],[3659,1626],[1981,1038],[3662,1627],[3678,1628],[3671,1629],[3676,1630],[3674,1631],[3677,1632],[3673,1633],[3672,1634],[3675,1635],[3688,1636],[3682,1637],[3686,1638],[3683,1639],[3687,1640],[3679,1641],[3685,1642],[3681,1643],[3680,1644],[3684,1645],[3696,1646],[3703,1647],[3706,1648],[3705,1649],[3704,1650],[3709,1651],[3708,1652],[3707,1653],[3732,1654],[3716,1655],[3733,1656],[3717,1655],[3734,1657],[3718,1658],[3731,1659],[3719,1660],[3735,1661],[3723,1662],[1987,1663],[1989,1664],[1988,1665],[3736,1666],[3724,1667],[1992,1668],[1991,1669],[3722,1670],[3737,1671],[3721,1672],[1985,1673],[1984,267],[3720,267],[3729,1674],[3725,1675],[3730,1676],[3727,1677],[3738,1678],[3726,1679],[1993,1680],[1341,1681],[3728,1682],[3749,1683],[3740,1684],[3752,1685],[3742,1686],[1996,1687],[1995,1688],[1997,1689],[1994,953],[3747,1690],[3750,1691],[3739,1692],[3751,1693],[3746,1694],[3754,1695],[3755,1696],[3745,1697],[3753,1698],[3744,1699],[3743,1700],[3748,1701],[3769,1702],[3770,1703],[3765,1704],[3771,1705],[3763,1706],[3762,1707],[3779,1708],[3767,1709],[1210,1710],[3772,1711],[1209,1712],[1208,1713],[3773,1714],[3764,1715],[3774,1716],[3766,1717],[3780,1718],[3781,1719],[3761,1720],[3775,1721],[3776,1722],[3759,1723],[3777,1724],[3758,1725],[3757,1726],[3778,1727],[3760,1728],[3768,1729],[3785,1730],[3784,1731],[3783,1732],[3782,1733],[3793,1734],[3795,1735],[3799,1736],[3787,1737],[3786,1738],[3801,1739],[3791,1740],[3790,1741],[3803,1742],[3805,1743],[3804,1744],[3807,1745],[3806,1746],[2703,1747],[3809,1748],[3810,1749],[3808,1750],[3811,1751],[3812,1752],[3813,1753],[3814,1754],[3816,1755],[3815,1756],[3820,1757],[3819,1758],[3821,1759],[3822,1760],[3818,1761],[3823,1762],[3817,1763],[3824,1764],[3844,1765],[3711,1766],[2085,1137],[1104,1767],[3952,1768],[3329,1769],[3340,267],[3942,1770],[3341,1771],[3954,1772],[3334,1773],[1095,1774],[3955,1775],[3300,1776],[1667,267],[2041,1777],[2040,1778],[3943,1779],[3328,1780],[2044,1781],[2043,1782],[2046,1783],[2045,267],[2047,1784],[1136,1785],[2042,1786],[1130,267],[3956,1787],[3304,1788],[1121,1789],[1119,1790],[3944,1791],[1112,1792],[2048,1793],[1111,267],[1118,1794],[1120,1795],[2049,1796],[1093,1797],[2050,1798],[1134,1799],[3945,1800],[1132,1801],[1131,1802],[3957,1803],[3335,1804],[1122,1774],[1127,1805],[3327,1806],[3958,1807],[3337,1808],[2051,1809],[1116,267],[3946,1810],[1117,1811],[1135,1812],[3959,1813],[3336,1814],[1100,1815],[3960,1816],[3338,1817],[2086,1137],[1096,1815],[3947,1818],[1113,1819],[3961,1820],[3339,1821],[1123,1815],[3948,1822],[2087,1823],[3949,1824],[1128,1825],[3950,1826],[1124,1774],[2052,1827],[1133,1828],[2053,1829],[1125,1830],[1098,1831],[3951,1832],[1126,1833],[1097,1834],[1099,1023],[3845,1835],[3353,1836],[3962,1837],[1698,1838],[1318,1038],[3867,1839],[3281,1840],[3873,1841],[3282,1842],[3874,1843],[3284,1844],[3875,1845],[3286,1846],[3868,1847],[3283,1840],[3869,1848],[3297,1849],[3870,1850],[3287,1840],[3293,1851],[3871,1852],[3291,1853],[3872,1854],[3290,1855],[3162,1856],[3963,1857],[3161,1858],[3825,1859],[1230,1860],[3846,1861],[3741,1862],[1936,1155],[3788,1863],[2062,1864],[3964,1865],[2061,1866],[3965,1867],[3797,1868],[3966,1869],[3798,1870],[2060,1871],[3792,1872],[3967,1873],[3800,1874],[3968,1875],[3796,1876],[3969,1877],[3789,1878],[3794,1879],[2054,1880],[3802,1881],[2063,1882],[2055,1883],[3970,1884],[3295,1885],[3512,1886],[1675,267],[3971,1887],[3513,1888],[3972,1889],[1683,1890],[1684,1573],[2065,1891],[2064,1892],[3294,1893],[3292,1894],[640,1038],[3847,1895],[3712,1896],[3876,1897],[3168,1898],[3877,1899],[3878,1900],[3165,1901],[3879,1902],[3163,1466],[3164,1903],[3880,1904],[3167,1905],[2004,1906],[2003,267],[3881,1907],[3882,1908],[3166,1909],[1464,267],[1372,1910],[1699,1911],[2833,1912],[1700,1023],[1085,1913],[3973,1914],[2818,1915],[3974,1916],[2834,1917],[3995,1918],[3389,1919],[3996,1920],[3390,1921],[3997,1922],[3391,1923],[2066,1332],[3998,1924],[3288,1925],[3999,1926],[3289,1927],[3975,1928],[1701,1929],[3976,1930],[2819,1931],[3977,1932],[2718,1933],[3318,1934],[3978,1935],[3311,1936],[3979,1937],[1934,1938],[3980,1939],[1933,1940],[3981,1941],[1084,1942],[3983,1943],[3982,1860],[3984,1944],[1952,1945],[3985,1946],[3352,1947],[1935,1838],[3351,1948],[3986,1949],[1939,1950],[1953,1951],[3987,1952],[1940,1953],[3988,1954],[1950,1955],[2068,1956],[2067,1957],[3989,1958],[2835,1959],[3991,1960],[1350,1961],[3992,1962],[1951,1963],[3993,1964],[2826,1965],[3994,1966],[3308,1967],[3990,1968],[2827,1969],[2868,104],[3826,1970],[1959,1971],[3827,1972],[2715,1973],[3828,1974],[2720,1975],[3883,1976],[3175,1977],[3884,1978],[3174,1979],[3173,1980],[3885,1981],[3178,1982],[3886,1983],[3177,1984],[3176,1985],[3829,1986],[2922,1987],[2089,1988],[2090,1989],[2088,1990],[4000,1991],[2091,1992],[2092,1993],[638,1994],[3848,1995],[3159,1996],[3887,1997],[2012,1998],[3888,1999],[2008,2000],[3889,2001],[2009,2002],[3890,2003],[2010,2004],[2014,2005],[2007,2006],[3891,2007],[2013,2008],[2015,2009],[2011,2010],[4001,2011],[2843,2012],[2093,267],[3830,2013],[3312,2014],[3137,2015],[3141,2016],[3892,2017],[3138,104],[2016,267],[3139,2018],[2018,2019],[2017,1665],[3831,2020],[1368,1565],[3849,2021],[2292,267],[1998,2022],[1197,267],[4002,2023],[1960,2024],[1961,2025],[4005,2026],[1089,976],[2096,2027],[2095,2028],[1215,2029],[4003,2030],[2094,2031],[1046,2032],[2098,2033],[2097,2034],[4004,2035],[1962,2036],[2100,2037],[2099,2038],[2102,2039],[2101,104],[3850,2040],[3348,2041],[3851,2042],[1242,2043],[3832,2044],[2722,2045],[4006,2046],[3403,2047],[1687,267],[4007,2048],[1090,2049],[2104,2050],[2103,1343],[4008,2051],[3498,2052],[3852,2053],[2836,2054],[2105,2055],[1966,2056],[2106,2057],[4009,2058],[1963,2059],[4010,2060],[1967,2061],[4011,2062],[3230,2063],[1088,267],[1965,2064],[4012,2065],[3422,2066],[4013,2067],[1086,267],[2108,2068],[2107,1091],[4014,2069],[3343,2070],[4015,2071],[3346,2072],[4016,2073],[3345,2074],[3344,2075],[4017,2076],[3301,2077],[4018,2078],[3361,2079],[4019,2080],[3360,2081],[3359,2082],[4020,2083],[3322,2084],[2109,267],[3285,2085],[3365,2086],[3853,2087],[3307,2088],[3305,2089],[3893,2090],[2854,2091],[2020,2092],[2019,267],[4021,2093],[3299,1923],[4022,2094],[1354,2095],[4023,2096],[3038,2097],[3160,2098],[3854,2099],[2717,2100],[3895,2101],[2706,2102],[3896,2103],[2709,2104],[3897,2105],[2707,2106],[2021,2107],[1234,267],[2022,267],[3898,2108],[2710,2109],[3899,2110],[2716,2111],[3894,2112],[2712,2113],[3900,2114],[2714,2115],[1999,2116],[1214,2117],[3833,2118],[2721,2119],[1047,1038],[2840,2120],[3855,2121],[1958,2122],[4026,2123],[4027,2124],[1972,2125],[2110,2126],[1970,2127],[4024,2128],[4025,2129],[2841,2130],[2112,2131],[2111,267],[2113,2132],[1971,267],[2116,2133],[2115,2134],[4029,2135],[3395,2136],[2118,2137],[2117,2138],[4030,2139],[3394,2140],[2114,953],[4028,2141],[3396,2142],[2000,267],[2002,2143],[2001,2144],[3856,2145],[3354,2146],[3901,2147],[3356,2148],[3355,2149],[3902,2150],[3357,2151],[3857,2152],[3714,2153],[4031,2154],[2839,2155],[2121,2156],[2120,2157],[4032,2158],[2838,2159],[2837,2160],[4033,2161],[2844,2162],[1689,267],[3858,2163],[3370,2164],[3859,2165],[1352,2166],[3860,2167],[3296,2168],[2123,2169],[2122,1192],[2126,2170],[2125,2171],[2124,2172],[3861,2173],[3349,2174],[3862,2175],[3350,2176],[4039,2177],[2960,2178],[4034,2179],[1941,1139],[4035,2180],[1942,1139],[4036,2181],[1945,2182],[4037,2183],[1943,1023],[4038,2184],[1944,2185],[4042,2186],[3045,2187],[4040,2188],[3044,2189],[2128,2190],[2127,2191],[4041,2192],[3043,2193],[3042,2194],[3041,2195],[2129,267],[1531,267],[3834,2196],[2869,2197],[4043,2198],[3313,2199],[3863,2200],[3171,2201],[2023,267],[3903,2202],[2886,2203],[2884,1466],[3904,2204],[2885,2205],[2024,267],[3905,2206],[2887,2207],[3906,2208],[2889,2209],[3907,2210],[2888,1466],[3908,2211],[2870,2212],[3909,2213],[3226,2214],[3910,2215],[3225,2216],[2026,2217],[2025,1499],[3911,2218],[3227,2219],[2028,2220],[2027,2221],[3912,2222],[2890,2223],[2029,953],[2030,1155],[3918,2224],[2873,2225],[3919,2226],[2872,2227],[3920,2228],[2874,2229],[3921,2230],[3922,2231],[2875,2232],[3913,2233],[2876,1923],[3914,2234],[2877,2235],[3915,2236],[2880,2237],[3916,2238],[2878,1466],[3917,2239],[2879,2240],[2032,2241],[2031,2242],[3923,2243],[2881,2244],[3924,2245],[2882,2246],[3925,2247],[2883,2248],[3926,2249],[3170,2250],[3169,2251],[2033,267],[3927,2252],[1949,2253],[3928,2254],[1946,2255],[3929,2256],[3039,2257],[1947,2258],[3931,2259],[3040,2260],[3930,2261],[1948,2262],[3146,104],[4059,2263],[2821,2264],[4044,2265],[1956,2266],[4045,2267],[2816,2268],[4060,2269],[3713,1763],[4068,2270],[2277,2271],[4069,2272],[2278,2271],[4070,2273],[2279,2274],[4071,2275],[2276,2276],[2130,267],[4072,2277],[2280,2271],[2282,2278],[4073,2279],[2281,2271],[4046,2280],[1235,1921],[4047,2281],[1968,2282],[1176,2283],[4061,2284],[4062,2285],[1180,2286],[4063,2287],[1182,2288],[4064,2289],[1179,2290],[4065,2291],[1184,2292],[4066,2293],[1187,2294],[4067,2295],[1186,2296],[1185,2297],[1188,2298],[1175,2299],[2006,267],[4048,2300],[1195,2301],[2842,267],[4074,2302],[1954,1942],[3309,2303],[3303,2304],[4049,2305],[1200,2306],[4050,2307],[1201,2308],[4051,2309],[1091,1137],[1937,1139],[4052,2310],[2814,2311],[4053,2312],[2825,2313],[4054,2314],[1696,2313],[4055,2315],[2957,2316],[2871,2317],[2823,2318],[4056,2319],[1041,1137],[4057,2320],[1226,2321],[2822,2322],[4075,2323],[1190,2324],[1191,2325],[4076,2326],[1192,2327],[4077,2328],[1194,2329],[4078,2330],[1196,2331],[1204,2332],[4079,2333],[1198,2334],[4080,2335],[1199,1665],[4081,2336],[1202,2337],[4082,2338],[1203,2339],[4058,2340],[2705,2341],[1695,2342],[3932,2343],[1238,2344],[3836,2345],[1241,2346],[3835,2347],[2891,2348],[4083,2349],[3423,2350],[637,267],[4084,2351],[3691,2352],[3690,2353],[3689,2354],[2846,2355],[4085,2356],[4086,2356],[3314,2357],[3310,2358],[4087,2359],[1938,2360],[4092,2361],[3316,2362],[2284,2363],[2283,267],[4088,2364],[3317,2365],[4093,2366],[3315,267],[2286,2367],[2285,267],[4089,2368],[3321,2369],[4090,2370],[3319,2371],[2287,2372],[2119,267],[4091,2373],[3320,2374],[2288,1223],[3838,2375],[3695,2376],[2035,2377],[2034,2378],[3933,2379],[3694,2380],[3693,2381],[3837,2382],[3692,2383],[2290,2384],[2289,267],[4098,2385],[2847,2386],[4099,2387],[4100,2388],[2848,2389],[4094,2390],[2832,2391],[2291,267],[2294,2392],[2293,2393],[2845,2394],[4095,2395],[2820,2396],[4096,2397],[4097,2398],[2824,2399],[3934,2400],[2713,2401],[3839,2402],[3698,2403],[3935,2404],[3697,2405],[3936,2406],[3701,2407],[2036,1190],[3937,2408],[3700,2409],[3938,2410],[3699,2411],[3840,2412],[3702,2413],[4101,2414],[1347,2415],[1955,2416],[4102,2417],[1236,2418],[4103,2419],[1115,2420],[4104,2421],[2704,999],[2708,2422],[4105,2423],[1035,2424],[1092,1925],[4106,2425],[2275,2426],[1183,2427],[1101,2428],[1040,2429],[1110,2430],[1362,2431],[4107,2432],[1044,2433],[2817,2434],[1038,2435],[1036,1925],[1042,1925],[1237,2436],[1102,2437],[4108,2438],[1225,2439],[4109,2440],[1045,2441],[1043,2442],[1181,2430],[1177,2443],[1103,2444],[2702,2445],[1094,2446],[1178,1925],[1348,2447],[1037,1925],[4110,2448],[1048,2449],[4111,2450],[1361,2451],[3841,2452],[2892,2453],[3842,2454],[3864,2455],[3302,2456],[3940,2457],[3385,2458],[3939,2459],[3710,2460],[2037,2461],[1986,2462],[1332,267],[2039,2463],[2038,267],[3865,2464],[3715,2465],[3843,2466],[2810,2467],[1138,267],[4112,2468],[1969,2469],[3866,2470],[3756,2471],[4125,2472],[3181,2473],[4113,2474],[3182,2475],[4114,2476],[3180,2477],[3179,2478],[2301,267],[4115,2479],[2313,104],[2295,267],[4116,2480],[2312,2481],[2311,2482],[2299,2483],[4126,2484],[2298,104],[2309,2485],[2308,104],[4127,2486],[2310,2487],[4128,2488],[2307,104],[4121,2489],[4122,2489],[3191,2490],[4123,2491],[3183,2492],[2296,1408],[4129,2493],[2302,2494],[4130,2495],[2332,2496],[2300,267],[4131,2497],[2305,2498],[4132,2499],[2335,2500],[2342,2501],[4133,2502],[2336,2503],[4134,2504],[2319,2505],[4135,2506],[2340,2507],[4136,2508],[2341,2509],[4137,2510],[2337,2511],[2329,267],[2330,2512],[4138,2513],[2339,2514],[4139,2515],[2338,2516],[4140,2517],[1211,2518],[4141,2519],[2331,2520],[2304,2521],[4142,2522],[2334,2523],[4143,2524],[2333,2525],[4144,2526],[2316,267],[4145,2527],[2315,2528],[2306,2529],[2343,2530],[2320,267],[4124,2531],[3184,2532],[3185,2533],[4117,2534],[3186,2535],[4118,2536],[3190,2537],[3189,2538],[4119,2539],[3188,2540],[2323,2541],[2328,2542],[2324,2543],[2325,2544],[2326,2545],[4146,2546],[2327,2547],[2321,267],[2344,2548],[2322,2549],[4120,2550],[3187,267],[2297,2551],[2314,2552],[3306,267],[3384,2553],[2849,2554],[3941,2555],[2850,2556],[2700,2557],[2056,2558],[4147,2559],[2711,2560],[2701,2561],[1224,2562],[2350,2563],[2348,2563],[2347,2563],[2349,2564],[2346,2563],[2345,2563],[2351,976],[4151,2565],[2354,2566],[1360,104],[4148,2567],[3216,2568],[4149,2569],[1371,2570],[3217,2571],[4150,2572],[3235,2573],[3236,2574],[2352,104],[2353,2575],[2355,2576],[1351,2577],[2357,2578],[1137,2579],[2358,2580],[1034,2581],[2362,2582],[2361,2583],[2364,2584],[2363,267],[4152,2585],[2084,2586],[2365,2587],[2366,2587],[1340,2588],[2367,2589],[630,267],[2368,2590],[1212,267],[2369,2591],[1213,2592],[639,2],[2370,2593],[2359,2594],[1353,2595],[1205,267],[2360,2596],[631,2597],[614,267],[2371,2598],[2372,2599],[1227,2600],[2373,2601],[634,2602],[2374,2603],[1193,2604],[1453,267],[2375,2605],[1207,1287],[2376,267],[2378,2606],[2377,267],[2379,2607],[636,2608],[2639,2609],[2638,2610],[2641,2611],[2640,267],[2642,2612],[1240,267],[2643,2613],[1228,267],[2644,267],[2646,2614],[2645,267],[2647,2615],[633,2616],[2648,2617],[1964,267],[2649,2618],[1668,2619],[2650,267],[2651,2620],[1682,267],[2652,2621],[1221,976],[2655,2622],[2654,2623],[2658,2624],[2657,2625],[2659,2626],[2656,267],[2660,2627],[1105,267],[2661,2628],[1106,976],[2663,2629],[2662,267],[632,267],[2664,2630],[1419,1287],[2665,2631],[2005,2038],[2666,2632],[1083,267],[2667,2633],[1206,2634],[4153,2635],[2684,2636],[2686,2637],[2688,2638],[2690,2639],[2692,2640],[2694,2641],[2672,2642],[2673,2643],[2675,2644],[2677,2645],[2356,2646],[2695,2454],[3953,1330],[2678,2643],[2682,2647],[2863,2648],[4154,2649],[613,2650]],"semanticDiagnosticsPerFile":[[1344,[{"start":2507,"length":35,"messageText":"Type instantiation is excessively deep and possibly infinite.","category":1,"code":2589},{"start":2507,"length":38,"messageText":"Type instantiation is excessively deep and possibly infinite.","category":1,"code":2589}]],[1486,[{"start":5247,"length":6,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'undefined'."}]],[1489,[{"start":1996,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3425,"length":10,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[1540,[{"start":643,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: string; google_client_secret: string; microsoft_client_id: string; microsoft_client_secret: string; microsoft_tenant: string; generic_client_id: string; generic_client_secret: string; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}},{"start":7416,"length":6,"code":2739,"category":1,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is missing the following properties from type 'SSOSettingsValues': saml_idp_metadata_url, saml_idp_metadata_xml, saml_sp_entity_id, saml_allow_unsolicited, generic_scope","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/sso/usessosettings.ts","start":1521,"length":6,"messageText":"The expected type comes from property 'values' which is declared here on type 'SSOSettingsResponse'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ google_client_id: null; google_client_secret: null; microsoft_client_id: null; microsoft_client_secret: null; microsoft_tenant: null; generic_client_id: null; generic_client_secret: null; ... 7 more ...; team_mappings: { ...; }; }' is not assignable to type 'SSOSettingsValues'."}}]],[1561,[{"start":1402,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }[]' is not assignable to type 'UserInfo[]'.","category":1,"code":2322,"next":[{"messageText":"Property 'models' is missing in type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' but required in type 'UserInfo'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type '{ user_id: string; user_email: string; user_alias: null; user_role: string; spend: number; max_budget: null; key_count: number; created_at: string; updated_at: string; sso_user_id: null; budget_duration: null; }' is not assignable to type 'UserInfo'."}}]},"relatedInformation":[{"file":"./src/components/networking.tsx","start":32568,"length":6,"messageText":"'models' is declared here.","category":3,"code":2728},{"file":"./src/components/networking.tsx","start":32875,"length":5,"messageText":"The expected type comes from property 'users' which is declared here on type 'UserListResponse'","category":3,"code":6500}]}]],[1978,[{"start":328,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":784,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1182,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":1684,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2044,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":2529,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3149,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: { temperature: number; max_tokens: number; top_p: number; }; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":3683,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4135,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":4538,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: { name: string; description: string; json: string; }[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}},{"start":5174,"length":6,"code":2741,"category":1,"messageText":"Property 'environment' is missing in type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' but required in type 'PromptType'.","relatedInformation":[{"file":"./src/app/(dashboard)/prompts/_components/prompt_editor_view/types.ts","start":368,"length":11,"messageText":"'environment' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ name: string; model: string; config: {}; tools: never[]; developerMessage: string; messages: { role: string; content: string; }[]; }' is not assignable to type 'PromptType'."}}]],[1993,[{"start":4983,"length":43,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type 'SpendMetrics' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Index signature for type 'string' is missing in type 'SpendMetrics'.","category":1,"code":2329}]}}]],[2037,[{"start":56,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":95,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":131,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":251,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":431,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2041,[{"start":1826,"length":24,"code":2739,"category":1,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic_v2\"; }' is missing the following properties from type 'ComplexityRouterConfigPayload': classification_mode, session_affinity, deployment_affinity, modality_routing, modality_pin_override","relatedInformation":[{"file":"./src/lib/autorouter_presets.ts","start":1068,"length":24,"messageText":"The expected type comes from property 'complexity_router_config' which is declared here on type 'AutoRouterPreset'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic_v2\"; }' is not assignable to type 'ComplexityRouterConfigPayload'."}}]],[2042,[{"start":170,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":225,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":293,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":389,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":607,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":753,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":973,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1119,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1359,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1507,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1890,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1941,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2101,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2336,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2434,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2669,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2771,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2964,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3060,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3613,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3677,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3756,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4194,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4290,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4598,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4669,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4945,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2044,[{"start":497,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":729,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":835,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":935,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1135,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1374,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1431,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1486,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2046,[{"start":425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":690,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1694,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1795,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1851,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2039,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2311,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2503,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2770,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2871,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3139,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4094,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4354,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4619,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4691,"length":12,"messageText":"Parameter 'defaultModel' implicitly has an 'any' type.","category":1,"code":7006},{"start":4905,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5006,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5320,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5590,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6026,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2047,[{"start":613,"length":10,"code":2739,"category":1,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 16 more ...; returnRawModelName: false; }' is missing the following properties from type 'BuildComplexityRouterConfigParams': defaultModel, planModeMinTier, classificationExamples, heuristicFirstMaxTier, classificationMode","canonicalHead":{"code":2322,"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: string[]; COMPLEX: string[]; REASONING: string[]; }; tierLabels: undefined; classifierType: \"heuristic\"; classifierLlmConfig: undefined; classifierContextWindowSize: undefined; ... 16 more ...; returnRawModelName: false; }' is not assignable to type 'BuildComplexityRouterConfigParams'."}},{"start":1362,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1412,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1879,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1922,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2250,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2318,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2428,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2537,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2989,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3576,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3669,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3929,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3977,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4076,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4554,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4850,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4913,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5378,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5436,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5564,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6070,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6138,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6549,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6689,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7035,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7102,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7175,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7566,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7630,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8085,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8342,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8405,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8472,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8892,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8949,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9075,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9583,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9635,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9692,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9797,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9859,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9919,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10839,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11244,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11297,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11355,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11414,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11568,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11631,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11752,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11863,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11945,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12063,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12189,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12309,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12396,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12536,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12737,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12790,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13055,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13210,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13268,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13614,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13654,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13728,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13781,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13836,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14217,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14257,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14384,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14427,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14491,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14575,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14648,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14816,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14904,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15092,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15228,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15401,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15468,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15596,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15718,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15801,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15951,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16016,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16186,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16274,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16445,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16686,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16733,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16798,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17065,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17158,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17408,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17482,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17667,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17856,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":17864,"length":8,"messageText":"Parameter 'keywords' implicitly has an 'any' type.","category":1,"code":7006},{"start":17883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18136,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18211,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18225,"length":24,"messageText":"Expected 2 arguments, but got 1.","category":1,"code":2554,"relatedInformation":[{"file":"./src/components/add_model/build_complexity_router_config.ts","start":11634,"length":24,"messageText":"An argument for 'rows' was not provided.","category":3,"code":6210}]},{"start":18570,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18659,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18814,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19058,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19240,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19319,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19545,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19884,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19968,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20094,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20180,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20415,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20824,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20922,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21335,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21416,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21488,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21796,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21882,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21981,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22299,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22448,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22534,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22777,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22934,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23301,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23392,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23787,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23873,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23986,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24650,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24747,"length":5,"messageText":"Parameter 'extra' implicitly has an 'any' type.","category":1,"code":7006},{"start":24874,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24973,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25007,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25086,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25172,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25468,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25521,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25753,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25833,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26139,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26194,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26235,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26340,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26641,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26735,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26834,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26928,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27007,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27098,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27170,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27262,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27353,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27465,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27536,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27599,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27641,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27935,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28014,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28136,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28206,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28262,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28327,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28808,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28951,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29074,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29111,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29200,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29411,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29790,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29960,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30120,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30182,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30258,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30431,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30668,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30734,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30932,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31123,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31191,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31381,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31492,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31658,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31759,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31882,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31950,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32032,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32131,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32382,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32423,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32584,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32630,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32716,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32803,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32879,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32969,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33065,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33548,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33728,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33781,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33934,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34151,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34340,"length":25,"messageText":"Parameter 'supportedReasoningEfforts' implicitly has an 'any' type.","category":1,"code":7006},{"start":34367,"length":13,"messageText":"Parameter 'expectedError' implicitly has an 'any' type.","category":1,"code":7006},{"start":34577,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34626,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34663,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34768,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34940,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35018,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35196,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35301,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35481,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35586,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35733,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36126,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36245,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36305,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36370,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36552,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36646,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36705,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36835,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37127,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37201,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37487,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37595,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37684,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37807,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37972,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38184,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38582,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38654,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38696,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38893,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38999,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39151,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39253,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39983,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40130,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40227,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40418,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40473,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40585,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40670,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40767,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40847,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41167,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41272,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41377,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41649,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41726,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41871,"length":14,"messageText":"Parameter 'classifierType' implicitly has an 'any' type.","category":1,"code":7006},{"start":42061,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42137,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42604,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42714,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43028,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43470,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43618,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43834,"length":5,"messageText":"Parameter '_name' implicitly has an 'any' type.","category":1,"code":7006},{"start":43841,"length":3,"messageText":"Parameter 'key' implicitly has an 'any' type.","category":1,"code":7006},{"start":44691,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44866,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45088,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45156,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45210,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45411,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45471,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45571,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45666,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45765,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45860,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45955,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46061,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46162,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46369,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46532,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46722,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46791,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46834,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46950,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47035,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47721,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48073,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48902,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48977,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49044,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49327,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49787,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49843,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49895,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49962,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50288,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50371,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50409,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50749,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50873,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51142,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51246,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51380,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51566,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51780,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51854,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52297,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52371,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2048,[{"start":196,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":501,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":595,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":679,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":786,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":976,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1134,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1208,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1349,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1425,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1587,"length":12,"messageText":"Parameter 'systemPrompt' implicitly has an 'any' type.","category":1,"code":7006},{"start":1601,"length":8,"messageText":"Parameter 'expected' implicitly has an 'any' type.","category":1,"code":7006},{"start":1620,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1707,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1746,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1920,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2040,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2089,[{"start":11595,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ session_affinity_ttl_seconds: number; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; ... 4 more ...; some_future_backend_key: { ...; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":13641,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ modality_routing: boolean; modality_pin_override: boolean; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; ... 4 more ...; some_future_backend_key: { ...; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":14094,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: string; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; escalation_keywords: string[]; semantic_keyword_matching: boolean; embedding_model: string; match_threshold: number...' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":14515,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: string; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: string; keyword_tier_rules: { keywords: string[]; tier: string; }[]; escalation_keywords: string[]; semantic_keyword_matching: boolean; embedding_model: string; match_threshold: number...' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'classifier_type' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'ClassifierType | undefined'.","category":1,"code":2322}]}]}},{"start":31386,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":31954,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":32495,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }' is not assignable to parameter of type 'StoredComplexityRouterConfig'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'tiers' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ CASUAL: string[]; AUDIT: string[]; }' has no properties in common with type 'Partial>'.","category":1,"code":2559}]}]}},{"start":32661,"length":21,"code":2339,"category":1,"messageText":"Property 'classification_prompt' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32748,"length":23,"code":2339,"category":1,"messageText":"Property 'classification_examples' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32832,"length":21,"code":2339,"category":1,"messageText":"Property 'classification_prompt' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."},{"start":32916,"length":23,"code":2339,"category":1,"messageText":"Property 'classification_examples' does not exist on type '{ tiers: { CASUAL: string[]; AUDIT: string[]; }; tier_definitions: { name: string; description: string; }[]; fallback_tier: string; classifier_type: string; classifier_llm_config: { model: string; timeout_ms: number; reasoning_effort: string; }; }'."}]],[2090,[{"start":2335,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2392,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2586,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2656,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2844,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2916,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3012,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3051,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3147,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3184,"length":12,"code":2559,"category":1,"messageText":"Type 'string' has no properties in common with type 'StoredComplexityRouterConfig'."},{"start":3244,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3460,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3570,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3739,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3840,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3886,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":3985,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4022,"length":12,"code":2559,"category":1,"messageText":"Type 'string' has no properties in common with type 'StoredComplexityRouterConfig'."},{"start":4087,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4428,"length":15,"messageText":"Spread types may only be created from object types.","category":1,"code":2698},{"start":4551,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4612,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4830,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4895,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5080,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5160,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5336,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5410,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5724,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2094,[{"start":1600,"length":17,"code":2322,"category":1,"messageText":{"messageText":"Type '{ budget_limit: number; time_period: string; } | { max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'.","category":1,"code":2322,"next":[{"messageText":"Type '{ max_budget: number; budget_duration: string; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; }' is not assignable to type 'ModelBudgetConfig'."}}]}},{"start":2144,"length":12,"code":2322,"category":1,"messageText":{"messageText":"Type 'string | number' is not assignable to type 'number'.","category":1,"code":2322,"next":[{"messageText":"Type 'string' is not assignable to type 'number'.","category":1,"code":2322}]},"relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":2388,"length":12,"code":2322,"category":1,"messageText":"Type 'string' is not assignable to type 'number'.","relatedInformation":[{"file":"./src/components/key_team_helpers/modelmaxbudgeteditor.tsx","start":506,"length":12,"messageText":"The expected type comes from property 'budget_limit' which is declared here on type 'ModelBudgetConfig'","category":3,"code":6500}]},{"start":3742,"length":8,"code":2739,"category":1,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is missing the following properties from type 'ModelBudgetConfig': budget_limit, time_period","canonicalHead":{"code":2322,"messageText":"Type '{ max_budget: number; budget_duration: string; tpm_limit: number; }' is not assignable to type 'ModelBudgetConfig'."}}]],[2357,[{"start":16399,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":18287,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":18967,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":20970,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":26215,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":32436,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; match_threshold: number; escalation_keywords: never[]; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; match_threshold: number; escalation_keywords: never[]; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":32734,"length":356,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; enable_context_window_escalation: false; context_window_escalation_buffer: number; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; enable_context_window_escalation: false; context_window_escalation_buffer: number; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":33824,"length":45,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ classification_mode: \"user_turn\"; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ classification_mode: \"user_turn\"; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":33999,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":34250,"length":265,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: false; deployment_affinity: true; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":35426,"length":64,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tier_labels: { SIMPLE: string; REASONING: string; }; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tier_labels: { SIMPLE: string; REASONING: string; }; tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":35675,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":36212,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":36912,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boo...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boo...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":37948,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: bool...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: { model_name: string; litellm_params: { reasoning_effort: string; }; }[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: bool...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":39150,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: ...' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: never[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: string[]; }; tier_model_configs: { REASONING: ({ model_name: string; litellm_params: { reasoning_effort: string; temperature: number; }; } | { ...; })[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: ...' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}},{"start":39954,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is not assignable to parameter of type 'ComplexityRouterConfigPayload'.","category":1,"code":2345,"next":[{"messageText":"Type '{ tiers: { SIMPLE: string[]; MEDIUM: never[]; COMPLEX: never[]; REASONING: never[]; }; classifier_type: \"heuristic\"; classification_mode: \"every_request\"; session_affinity: boolean; deployment_affinity: boolean; }' is missing the following properties from type 'ComplexityRouterConfigPayload': modality_routing, modality_pin_override","category":1,"code":2739}]}}]],[2366,[{"start":3271,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]},{"start":3928,"length":4,"code":2322,"category":1,"messageText":{"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; } & {}'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key_alias: string; }' is missing the following properties from type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }': aliases, allowed_cache_controls, allowed_routes, auto_rotate, and 7 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ key_alias: string; }' is not assignable to type '{ access_group_ids?: string[] | null | undefined; agent_id?: string | null | undefined; aliases: ({ [x: string]: unknown; } & {}) | null; allowed_cache_controls: unknown[] | null; allowed_passthrough_routes?: unknown[] | ... 1 more ... | undefined; ... 46 more ...; user_id?: string | ... 1 more ... | undefined; }'."}}]},"relatedInformation":[{"file":"./node_modules/openapi-fetch/dist/index.d.mts","start":3474,"length":4,"messageText":"The expected type comes from property 'body' which is declared here on type '{ params?: { query?: undefined; header?: { \"litellm-changed-by\"?: string | null | undefined; } | undefined; path?: undefined; cookie?: undefined; } | undefined; } & { body: { access_group_ids?: string[] | ... 1 more ... | undefined; ... 50 more ...; user_id?: string | ... 1 more ... | undefined; } & {}; } & { ...; }...'","category":3,"code":6500}]}]],[2367,[{"start":1322,"length":3,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":1327,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":1491,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1616,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":1945,"length":26,"messageText":"Object is possibly 'undefined'.","category":1,"code":2532},{"start":1969,"length":1,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":2301,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":2345,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":2383,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048},{"start":4907,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":4951,"length":4,"messageText":"'init' is possibly 'undefined'.","category":1,"code":18048}]],[2660,[{"start":272,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":354,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":907,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":1076,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1114,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1276,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1338,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1481,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1665,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1757,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1836,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1976,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2023,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2370,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2447,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2802,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2838,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2914,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2969,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3051,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3148,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3523,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3642,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3690,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4031,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4484,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4533,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4878,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4940,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4977,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6141,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6218,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6485,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6532,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6573,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6639,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6703,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6766,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6823,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6888,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7012,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7255,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7452,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7497,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7663,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7736,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7807,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8020,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8403,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8481,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8933,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9013,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9490,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9631,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9757,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9835,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9876,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10661,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10785,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11070,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11970,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12047,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12318,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[2661,[{"start":3595,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4010,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":4616,"length":405,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}},{"start":5031,"length":406,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: never[]; max_budget: null; budget_duration: null; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: { ...; }[]; }' is not assignable to type 'Team'."}}]],[2812,[{"start":3077,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '0'.","category":1,"code":2493},{"start":3083,"length":4,"messageText":"Tuple type '[]' of length '0' has no element at index '1'.","category":1,"code":2493},{"start":3175,"length":4,"messageText":"'opts' is possibly 'undefined'.","category":1,"code":18048}]],[3051,[{"start":7973,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}},{"start":8039,"length":4,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.","category":1,"code":2345,"next":[{"messageText":"Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, autocorrect, and 129 more.","category":1,"code":2740}]}}]],[3057,[{"start":3761,"length":7,"code":2741,"category":1,"messageText":"Property 'by_router' is missing in type '{ by_tier: { group: string; turn_count: number; real_win_rate_pct: number; shadow_win_rate_pct: number; tie_rate_pct: number; avg_judge_confidence: number; real_spend: number; shadow_spend: number; cache_hit_turns: number; }[]; ... 7 more ...; shed_count: number; }' but required in type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'.","relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":1378747,"length":9,"messageText":"'by_router' is declared here.","category":3,"code":2728},{"file":"./src/lib/http/schema.d.ts","start":1372928,"length":7,"messageText":"The expected type comes from property 'results' which is declared here on type '{ baseline_model?: string | null | undefined; created_at: string; direction: \"reverse\" | \"forward\"; ends_at: string; error_count?: number | null | undefined; job_id: string; judge_model: string; ... 10 more ...; targets: { ...; }[]; }'","category":3,"code":6500}],"canonicalHead":{"code":2322,"messageText":"Type '{ by_tier: { group: string; turn_count: number; real_win_rate_pct: number; shadow_win_rate_pct: number; tie_rate_pct: number; avg_judge_confidence: number; real_spend: number; shadow_spend: number; cache_hit_turns: number; }[]; ... 7 more ...; shed_count: number; }' is not assignable to type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'."}},{"start":28794,"length":7,"code":2741,"category":1,"messageText":"Property 'by_router' is missing in type '{ by_tier: never[]; by_current_model: never[]; overall_shadow_win_rate_pct: number; overall_tie_rate_pct: number; sampled_real_spend: number; sampled_shadow_spend: number; }' but required in type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'.","relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":1378747,"length":9,"messageText":"'by_router' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ by_tier: never[]; by_current_model: never[]; overall_shadow_win_rate_pct: number; overall_tie_rate_pct: number; sampled_real_spend: number; sampled_shadow_spend: number; }' is not assignable to type '{ by_current_model: { avg_judge_confidence: number; cache_hit_turns: number; group: string; real_spend: number; real_win_rate_pct: number; shadow_spend: number; shadow_win_rate_pct: number; tie_rate_pct: number; turn_count: number; }[]; ... 9 more ...; withheld_count?: number | ... 1 more ... | undefined; }'."}}]],[3058,[{"start":6233,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' is not assignable to type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 9 more ...; turns: number; } | { ...; }'.","category":1,"code":2322,"next":[{"messageText":"Property 'classifier_cost' is missing in type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' but required in type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 6 more ...; turns: number; }'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type '{ sessions: number; turns: number; avg_turns_per_session: number; avg_session_seconds: number; avg_tokens_per_session: number; spend: number; saved_spend: number; baseline_spend: number; saved_pct: number; saved_per_session: number; cache: { ...; }; }' is not assignable to type '{ avg_session_seconds: number; avg_tokens_per_session: number; avg_turns_per_session: number; baseline_spend: number; cache: { coverage_pct: number; first_visit: { hit_rate_pct: number; hits: number; turns: number; }; ... 8 more ...; unordered_turns: number; }; ... 6 more ...; turns: number; }'."}}]},"relatedInformation":[{"file":"./src/lib/http/schema.d.ts","start":883611,"length":15,"messageText":"'classifier_cost' is declared here.","category":3,"code":2728},{"file":"./src/app/(dashboard)/cost-optimization/_components/autorouterbenchmarks.ts","start":508,"length":5,"messageText":"The expected type comes from property 'stats' which is declared here on type 'BenchmarkView'","category":3,"code":6500}]}]],[3111,[{"start":2067,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2572,"length":41,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3058,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":3554,"length":8,"code":2322,"category":1,"messageText":"Type 'undefined' is not assignable to type 'string'.","relatedInformation":[{"file":"./src/app/(dashboard)/hooks/useauthorized.ts","start":1740,"length":50,"messageText":"The expected type comes from property 'userRole' which is declared here on type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'","category":3,"code":6500}]},{"start":4033,"length":42,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5026,"length":34,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; token: string; accessToken: string; userId: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3119,[{"start":3309,"length":15,"code":2339,"category":1,"messageText":{"messageText":"Property 'CustomGuardrail' does not exist on type 'Record | typeof GuardrailProviders'.","category":1,"code":2339,"next":[{"messageText":"Property 'CustomGuardrail' does not exist on type 'typeof GuardrailProviders'.","category":1,"code":2339}]}}]],[3120,[{"start":1123,"length":14,"code":2322,"category":1,"messageText":{"messageText":"Type '{ \"hide-secrets\": { ui_friendly_name: string; detect_secrets_config: { param: string; description: string; required: boolean; type: string; }; }; }' is not assignable to type 'ProviderParamsResponse'.","category":1,"code":2322,"next":[{"messageText":"Property '\"hide-secrets\"' is incompatible with index signature.","category":1,"code":2530,"next":[{"messageText":"Type '{ ui_friendly_name: string; detect_secrets_config: { param: string; description: string; required: boolean; type: string; }; }' is not assignable to type '{ [key: string]: ProviderParam; }'.","category":1,"code":2322,"next":[{"messageText":"Property 'ui_friendly_name' is incompatible with index signature.","category":1,"code":2530,"next":[{"messageText":"Type 'string' is not assignable to type 'ProviderParam'.","category":1,"code":2322}]}]}]}]},"relatedInformation":[{"file":"./src/app/(dashboard)/guardrails/_components/guardrail_provider_fields.tsx","start":1281,"length":14,"messageText":"The expected type comes from property 'providerParams' which is declared here on type 'IntrinsicAttributes & GuardrailProviderFieldsProps'","category":3,"code":6500}]}]],[3151,[{"start":198,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":366,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":417,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":500,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":702,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1191,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1353,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1621,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1713,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2069,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2354,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2908,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2988,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3401,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3592,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3750,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3254,[{"start":2766,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":2896,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."},{"start":3912,"length":5,"code":2339,"category":1,"messageText":"Property 'value' does not exist on type 'HTMLElement'."}]],[3263,[{"start":10763,"length":423,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '{ status: \"healthy\"; last_health_check: string; health_check_error: null; teams: { team_id: string; }[]; allowed_tools: string[]; has_user_credential: true; approval_status: \"approved\"; submitted_by: string; ... 47 more ...; env_vars?: MCPEnvVar[] | null; }' to type 'MCPServer' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Types of property 'approval_status' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '\"approved\"' is not comparable to type '\"active\" | \"pending_review\" | \"rejected\" | null | undefined'.","category":1,"code":2678}]}]}}]],[3375,[{"start":4578,"length":15,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: boolean; isAuthorized: boolean; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3379,[{"start":4867,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":11772,"length":24,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }[]' is not assignable to parameter of type '({ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; })[]'.","category":1,"code":2345,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; auto_router_config?: undefined; auto_router_default_model?: undefined; }; model_info: { ...; }; } | { ...; } | { ...; } | { ...; } | { ...; }'.","category":1,"code":2322,"next":[{"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'litellm_params' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }' is missing the following properties from type '{ model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }': auto_router_config, auto_router_default_model","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ model_name: string; litellm_params: { model: string; complexity_router_config: { tiers: {}; classifier_type: string; }; }; model_info: { id: string; db_model: boolean; created_at: string; }; }' is not assignable to type '{ model_name: string; litellm_params: { model: string; auto_router_config: string; auto_router_default_model: string; complexity_router_config?: undefined; complexity_router_default_model?: undefined; }; model_info: { ...; }; }'."}}]}]}]}]}}]],[3653,[{"start":2185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2365,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2415,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":2652,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3085,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3188,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3674,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3998,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4041,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":4130,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4412,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3686,[{"start":2516,"length":2,"code":2345,"category":1,"messageText":"Argument of type '{}' is not assignable to parameter of type 'void'."}]],[3730,[{"start":11320,"length":300,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":20262,"length":14,"code":2322,"category":1,"messageText":{"messageText":"Type '(value: TagListResponse | PromiseLike) => void' is not assignable to type '(tags: Record) => void'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'value' and 'tags' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'Record' is not assignable to type 'TagListResponse | PromiseLike'.","category":1,"code":2322,"next":[{"messageText":"Property 'then' is missing in type 'Record' but required in type 'PromiseLike'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type 'Record' is not assignable to type 'PromiseLike'."}}]}]}]},"relatedInformation":[{"file":"./node_modules/typescript/lib/lib.es5.d.ts","start":71612,"length":239,"messageText":"'then' is declared here.","category":3,"code":2728}]},{"start":21563,"length":15,"code":2322,"category":1,"messageText":{"messageText":"Type '(value: TagListResponse | PromiseLike) => void' is not assignable to type '(tags: Record) => void'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'value' and 'tags' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'Record' is not assignable to type 'TagListResponse | PromiseLike'.","category":1,"code":2322,"next":[{"messageText":"Property 'then' is missing in type 'Record' but required in type 'PromiseLike'.","category":1,"code":2741,"canonicalHead":{"code":2322,"messageText":"Type 'Record' is not assignable to type 'PromiseLike'."}}]}]}]},"relatedInformation":[{"file":"./node_modules/typescript/lib/lib.es5.d.ts","start":71612,"length":239,"messageText":"'then' is declared here.","category":3,"code":2728}]},{"start":22118,"length":308,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":33445,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":34328,"length":331,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3783,[{"start":3053,"length":46,"code":2352,"category":1,"messageText":{"messageText":"Conversion of type '[url: string][]' to type '[string, RequestInit][]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.","category":1,"code":2352,"next":[{"messageText":"Type '[url: string]' is not comparable to type '[string, RequestInit]'.","category":1,"code":2678,"next":[{"messageText":"Source has 1 element(s) but target requires 2.","category":1,"code":2618}]}]}}]],[3810,[{"start":9097,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304}]],[3828,[{"start":401,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":442,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":647,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":711,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":957,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1064,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1307,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1383,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1651,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1701,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1948,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2297,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2528,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3833,[{"start":792,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":835,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1122,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1226,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1306,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1527,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1712,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1913,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2009,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2261,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2311,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2510,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2731,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3843,[{"start":780,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":813,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":891,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1371,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1570,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1620,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1789,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2053,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2107,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2176,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2342,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":2352,"length":9,"messageText":"Parameter 'iconClass' implicitly has an 'any' type.","category":1,"code":7006},{"start":2505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2857,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3006,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3063,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3571,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3661,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4075,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3880,[{"start":2005,"length":27,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]},{"start":2084,"length":26,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]},{"messageText":"Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined): HTMLElement', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Argument of type 'string | null' is not assignable to parameter of type 'Matcher'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'Matcher'.","category":1,"code":2322}]}]}]},"relatedInformation":[]}]],[3888,[{"start":162,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":205,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":602,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":751,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3889,[{"start":119,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":155,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":397,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":451,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1165,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1233,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1560,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1820,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3890,[{"start":211,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":252,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":384,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":615,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":788,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":875,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1063,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1570,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3891,[{"start":877,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":917,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1106,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1769,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1848,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2010,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2521,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2584,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2872,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2940,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3082,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3483,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3633,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3667,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3766,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4439,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4498,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4989,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5022,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3892,[{"start":144,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":359,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":488,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":554,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":618,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":732,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":793,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":961,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1031,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1248,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1396,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1468,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3898,[{"start":236,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":276,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":330,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":583,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":859,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1267,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1590,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1854,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2168,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2981,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3925,[{"start":1201,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1242,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1434,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1517,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1627,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1963,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2273,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2391,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2426,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2681,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2876,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2924,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3198,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3285,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3314,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3448,"length":8,"messageText":"Parameter 'severity' implicitly has an 'any' type.","category":1,"code":7006},{"start":3458,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":3609,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3933,[{"start":5200,"length":36,"messageText":"Object is possibly 'null'.","category":1,"code":2531}]],[3935,[{"start":208,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":242,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":387,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":451,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":525,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":605,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":681,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":864,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":932,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1101,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1167,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1423,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1485,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1663,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1726,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1772,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1825,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1947,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1998,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3940,[{"start":1780,"length":8,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15138,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; token: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3941,[{"start":3402,"length":15,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."},{"start":3423,"length":7,"code":2322,"category":1,"messageText":"Type 'number' is not assignable to type 'ModelBudgetConfig'."}]],[3942,[{"start":3610,"length":8,"code":2741,"category":1,"messageText":"Property 'spend' is missing in type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' but required in type 'Team'.","relatedInformation":[{"file":"./src/components/key_team_helpers/key_list.tsx","start":785,"length":5,"messageText":"'spend' is declared here.","category":3,"code":2728}],"canonicalHead":{"code":2322,"messageText":"Type '{ team_id: string; team_alias: string; models: string[]; max_budget: number; budget_duration: string; tpm_limit: null; rpm_limit: null; organization_id: string; created_at: string; keys: never[]; members_with_roles: never[]; }' is not assignable to type 'Team'."}},{"start":5344,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":5861,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6795,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":7743,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8690,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9485,"length":43,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10249,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10894,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":11581,"length":56,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12876,"length":49,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[3943,[{"start":1457,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1501,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1553,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1638,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2186,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2268,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2372,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2539,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3054,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3159,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3944,[{"start":837,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1657,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1766,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1811,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2179,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2275,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2634,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2723,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3206,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3312,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3386,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3808,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4375,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4449,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4642,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4701,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5192,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5251,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3945,[{"start":1408,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1453,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1553,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1641,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1763,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1828,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1893,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1959,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2032,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2160,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2220,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2300,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2466,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2678,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2761,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2980,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3046,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3188,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3259,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3580,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3618,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3673,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3718,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4090,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4582,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5320,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5383,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5847,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6204,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6280,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6356,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6458,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6535,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7105,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7245,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7505,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7602,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8188,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8233,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8290,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8377,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8839,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8914,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9609,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10227,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10339,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10960,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11056,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11616,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11900,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11988,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12565,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12705,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12988,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13067,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13164,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13729,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":13736,"length":11,"messageText":"Parameter 'replacement' implicitly has an 'any' type.","category":1,"code":7006},{"start":13749,"length":8,"messageText":"Parameter 'expected' implicitly has an 'any' type.","category":1,"code":7006},{"start":14281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14428,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14510,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15129,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15170,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15380,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15464,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15810,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15867,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16652,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16732,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17485,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17587,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17813,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17889,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17984,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18393,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18475,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18599,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18687,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19160,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19287,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19325,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19404,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20092,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20216,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20803,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20950,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21018,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21303,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21382,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21457,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21541,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21821,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21890,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21968,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22546,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22613,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22642,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23087,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23172,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23254,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23388,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23474,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23943,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24032,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24496,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24591,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24910,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24994,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25068,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25439,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25587,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25739,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25835,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26078,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26360,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26456,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26829,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26867,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27541,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27666,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27969,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28057,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28745,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28825,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29313,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29407,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29886,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29924,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29997,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30245,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30516,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30596,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30781,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31325,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31738,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31888,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32296,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32477,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32838,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33002,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33040,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":33567,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33669,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34220,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34793,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35416,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35465,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35547,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35784,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35857,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36463,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36634,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36762,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37118,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37211,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":37701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37746,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37803,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37893,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38263,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38355,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38456,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38601,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38704,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38939,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39181,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39244,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39315,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39394,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39566,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39653,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":39748,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40033,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40117,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40436,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40474,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40547,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40711,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40810,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40951,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41043,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41284,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41343,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41406,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41768,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41885,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41945,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42340,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42408,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42501,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42754,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43050,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43418,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43512,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44018,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44090,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44181,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44503,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44617,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44677,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44930,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45045,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45162,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45543,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45640,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45907,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46031,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46502,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46573,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46677,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47108,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47224,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47327,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":47743,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47847,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48002,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48283,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48608,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48725,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49059,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49097,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49168,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49654,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":49719,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49991,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50062,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50622,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50743,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51226,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51350,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51552,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51844,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51931,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52339,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52457,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52518,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52996,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53083,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53459,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53576,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53648,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53911,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53965,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54356,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54407,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54850,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55011,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":55567,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55668,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56214,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56482,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56561,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56733,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":57069,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":57698,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57756,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58198,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58349,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58465,"length":6,"messageText":"Parameter 'action' implicitly has an 'any' type.","category":1,"code":7006},{"start":58919,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58975,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":59193,"length":5,"messageText":"Parameter 'model' implicitly has an 'any' type.","category":1,"code":7006},{"start":59200,"length":6,"messageText":"Parameter 'effort' implicitly has an 'any' type.","category":1,"code":7006},{"start":59208,"length":5,"messageText":"Parameter 'label' implicitly has an 'any' type.","category":1,"code":7006},{"start":59215,"length":7,"messageText":"Parameter 'warning' implicitly has an 'any' type.","category":1,"code":7006},{"start":59367,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59507,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59571,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":59678,"length":5,"messageText":"Parameter 'model' implicitly has an 'any' type.","category":1,"code":7006},{"start":59802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59950,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60019,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60173,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60457,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60763,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60917,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60989,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61374,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61448,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61881,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62183,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62591,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62906,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":63185,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63332,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":63738,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":64338,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":64346,"length":5,"messageText":"Parameter 'value' implicitly has an 'any' type.","category":1,"code":7006},{"start":64398,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":64482,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":64845,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":64937,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":65991,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66137,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":66232,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66455,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":66604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":66964,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67088,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":67264,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67348,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":67699,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67756,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":67813,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68094,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68251,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68308,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68447,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68519,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68569,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":68785,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":68977,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69074,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69153,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":69442,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69648,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":69919,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":69997,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":70113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":70549,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":70650,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71037,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71115,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71233,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71310,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71531,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":71824,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":71911,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":72476,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":72592,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":73177,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":73299,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":73895,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":73960,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":74178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74275,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74386,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":74463,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":74933,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75001,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75046,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75192,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":75500,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75601,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75691,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":75817,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":76249,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76345,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76435,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":76556,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":77078,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77170,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77271,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":77467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77558,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":77682,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":78289,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":78568,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78606,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78728,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":78815,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":79163,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":79454,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":79644,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":80140,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":80468,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":80723,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3946,[{"start":10172,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}},{"start":11331,"length":28,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to parameter of type 'UseQueryResult'.","category":1,"code":2345,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverRefetchErrorResult | QueryObserverSuccessResult | QueryObserverPlaceholderResult<...>'.","category":1,"code":2322,"next":[{"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is missing the following properties from type 'QueryObserverPlaceholderResult': error, isLoading, isLoadingError, isRefetchError, and 18 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type '{ data: ComplexityScorerDefaults; isPending: boolean; isError: boolean; refetch: Mock; }' is not assignable to type 'QueryObserverPlaceholderResult'."}}]}]}}]],[3947,[{"start":1213,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1412,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1478,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1878,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2020,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2305,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2574,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2996,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3076,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3207,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3717,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3910,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4162,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4286,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4544,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4630,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4727,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5116,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5238,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":5600,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":5817,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6317,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6708,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6776,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6866,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7042,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7129,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7231,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7640,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7722,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8356,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8618,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8663,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8736,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8982,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9239,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9341,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9448,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9710,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9883,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10017,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10109,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10204,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10624,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10809,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11251,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3949,[{"start":670,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":995,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1089,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1162,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1222,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1294,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1454,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1549,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1753,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1842,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2056,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3950,[{"start":450,"length":5,"code":2322,"category":1,"messageText":{"messageText":"Type '{ SIMPLE: string; MEDIUM: string; COMPLEX: string; REASONING: string; }' is not assignable to type 'ComplexityTiers'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'SIMPLE' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string' is not assignable to type 'string[]'.","category":1,"code":2322,"canonicalHead":{"code":2322,"messageText":"Type '{ SIMPLE: string; MEDIUM: string; COMPLEX: string; REASONING: string; }' is not assignable to type 'ComplexityTiers'."}}]}]},"relatedInformation":[{"file":"./src/components/add_model/complexityrouterconfig.tsx","start":14750,"length":5,"messageText":"The expected type comes from property 'tiers' which is declared here on type 'ComplexityRouterConfigValue'","category":3,"code":6500}]},{"start":833,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":884,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":974,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1098,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1205,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1331,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1389,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1460,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1504,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1629,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1724,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1894,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1939,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2113,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2415,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2460,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2655,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3021,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3066,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3177,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3558,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3625,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3894,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3932,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4009,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4133,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4196,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4276,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4450,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4545,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":4747,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4845,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":4883,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3952,[{"start":3331,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3861,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":6669,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":6708,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":7325,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":7445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":7698,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8039,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8117,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":8214,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":8985,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9032,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":9086,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":9795,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10087,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10266,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10549,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":10665,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":10755,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11066,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11155,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11246,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11379,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11459,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":11673,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":11742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12087,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":12683,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12742,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":12857,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":13419,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13477,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":13544,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":14178,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14378,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14463,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14526,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":14595,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15088,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":15444,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":15941,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16107,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16200,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16267,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":16761,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":16949,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17033,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":17130,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":17917,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18019,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18109,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":18796,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18888,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":18978,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":19715,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19774,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":19977,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":20478,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20571,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":20824,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":21467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21560,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":21627,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":22064,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22319,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":22476,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":23025,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23191,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23250,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":23423,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":24161,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24325,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24400,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":24480,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25331,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25390,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":25561,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":25604,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":26053,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26199,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":26362,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":27089,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27235,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27306,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":27382,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":28080,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28284,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28430,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28501,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":28776,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":29473,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29532,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":29697,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":30557,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30616,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":30813,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":31424,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31483,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":31649,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32097,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32286,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32345,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":32503,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":32929,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33166,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33225,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":33384,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34015,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34074,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34234,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":34696,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34755,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":34948,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":35735,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":35794,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36109,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36352,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":36530,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36567,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":36938,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":37026,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":38304,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38451,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38492,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38553,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38611,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38758,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38875,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":38946,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":39752,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40103,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40209,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40250,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":40712,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":40772,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41120,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41217,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":41438,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41629,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41689,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":41789,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42193,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":42531,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42657,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":42743,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43048,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43370,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43467,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":43778,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":43995,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44093,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":44412,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44587,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":44944,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":45477,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":45538,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":46240,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":46701,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47376,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47544,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47645,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":47716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":48260,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48321,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":48628,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49308,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":49459,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":49852,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50175,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50365,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":50436,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":50771,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51281,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51326,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51373,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":51448,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":51491,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":51592,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52050,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52111,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":52276,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":52956,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53017,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":53194,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":53991,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54371,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54461,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":54564,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":54974,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55113,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55395,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55456,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":55910,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56324,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56520,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":56716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":56920,"length":6,"messageText":"Parameter '_label' implicitly has an 'any' type.","category":1,"code":7006},{"start":56928,"length":9,"messageText":"Parameter 'modelName' implicitly has an 'any' type.","category":1,"code":7006},{"start":57278,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57365,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":57452,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58005,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58369,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58459,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":58562,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":58925,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59255,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59316,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":59780,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60250,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60309,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60432,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":60531,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":60696,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61146,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61301,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61453,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":61610,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":61660,"length":9,"messageText":"Cannot find name 'afterEach'.","category":1,"code":2304},{"start":61751,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62011,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62124,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62200,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62461,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":62540,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":62871,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63038,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63075,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":63210,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3955,[{"start":907,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":954,"length":10,"messageText":"Cannot find name 'beforeEach'.","category":1,"code":2304},{"start":1006,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1338,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1383,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1456,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1533,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1610,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1716,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2009,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2084,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2163,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2237,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":2746,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2827,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":2918,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3025,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":3104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":3519,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[3978,[{"start":3670,"length":6,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ metadata: { key: string; value?: string | undefined; }[]; }' is not assignable to parameter of type '{ metadata?: MetadataPair[] | undefined; }'.","category":1,"code":2345,"next":[{"messageText":"Types of property 'metadata' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }[]' is not assignable to type 'MetadataPair[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'value' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string | undefined' is not assignable to type 'string'.","category":1,"code":2322,"next":[{"messageText":"Type 'undefined' is not assignable to type 'string'.","category":1,"code":2322}],"canonicalHead":{"code":2322,"messageText":"Type '{ key: string; value?: string | undefined; }' is not assignable to type 'MetadataPair'."}}]}]}]}]}]}}]],[3984,[{"start":806,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1045,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1459,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":1920,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":2291,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]},{"start":2754,"length":13,"code":2322,"category":1,"messageText":{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }[]' is not assignable to type 'Organization[]'.","category":1,"code":2322,"next":[{"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is missing the following properties from type 'Organization': updated_by, litellm_budget_table, teams, users, members","category":1,"code":2739,"canonicalHead":{"code":2322,"messageText":"Type '{ organization_id: string; organization_alias: string; budget_id: string; metadata: {}; models: never[]; spend: number; model_spend: {}; created_at: string; created_by: string; updated_at: string; }' is not assignable to type 'Organization'."}}]},"relatedInformation":[{"file":"./src/components/common_components/organizationdropdown.tsx","start":179,"length":13,"messageText":"The expected type comes from property 'organizations' which is declared here on type 'IntrinsicAttributes & OrganizationDropdownProps'","category":3,"code":6500}]}]],[3987,[{"start":221,"length":8,"messageText":"Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":265,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":376,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":454,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":589,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":667,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":802,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":880,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1023,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304},{"start":1104,"length":2,"messageText":"Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.","category":1,"code":2582},{"start":1445,"length":6,"messageText":"Cannot find name 'expect'.","category":1,"code":2304}]],[4090,[{"start":2930,"length":304,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ isLoading: false; isAuthorized: true; userId: string; userRole: string; accessToken: string; token: string; userEmail: string; premiumUser: boolean; disabledPersonalKeyCreation: null; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4098,[{"start":5253,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': project_id, key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; max_parallel_requests: number; ... 44 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}}]],[4099,[{"start":5009,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10433,"length":14,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; isLoading: boolean; isAuthorized: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4100,[{"start":4046,"length":13,"code":2739,"category":1,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is missing the following properties from type 'KeyResponse': key_type, last_active","canonicalHead":{"code":2322,"messageText":"Type '{ token: string; token_id: string; key_name: string; key_alias: string; spend: number; max_budget: number; expires: string; models: never[]; aliases: {}; config: {}; user_id: string; team_id: null; project_id: null; ... 45 more ...; key_rotation_at: undefined; }' is not assignable to type 'KeyResponse'."}},{"start":6416,"length":47,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":6903,"length":47,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8038,"length":55,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userRole: string; accessToken: string; userId: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":8468,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":9841,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10515,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":10960,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":11621,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":12378,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":13010,"length":104,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":14297,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15073,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":15868,"length":94,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":16630,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":17972,"length":96,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":19102,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":22855,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24098,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":24544,"length":111,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":25000,"length":115,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":25484,"length":113,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":26592,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":27013,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":27644,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":28274,"length":21,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ accessToken: string; userId: string; userRole: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":28857,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":30053,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":30808,"length":102,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":31694,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":32555,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":33756,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":37651,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":45209,"length":112,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ userId: string; userRole: string; accessToken: string; premiumUser: boolean; token: string; userEmail: null; disabledPersonalKeyCreation: null; showSSOBanner: boolean; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}}]],[4154,[{"start":1980,"length":293,"code":2345,"category":1,"messageText":{"messageText":"Argument of type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is not assignable to parameter of type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }'.","category":1,"code":2345,"next":[{"messageText":"Type '{ token: string; accessToken: string; userId: string; userEmail: string; userRole: string; premiumUser: boolean; disabledPersonalKeyCreation: boolean; showSSOBanner: false; }' is missing the following properties from type '{ isLoading: boolean; isAuthorized: boolean; token: string | null; accessToken: any; userId: any; userEmail: any; userRole: string; userRoleLabel: string; isViewOnly: boolean; premiumUser: any; disabledPersonalKeyCreation: any; showSSOBanner: boolean; }': isLoading, isAuthorized, userRoleLabel, isViewOnly","category":1,"code":2739}]}},{"start":2424,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2638,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":2857,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":5736,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6118,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":6926,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7351,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: undefined; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":7757,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: null; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":8309,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9294,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: TagUsage[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":9836,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: true; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10256,"length":10,"code":2739,"category":1,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ showTags: false; topKeys: never[]; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}},{"start":10874,"length":10,"code":2739,"category":1,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is missing the following properties from type 'TopKeyViewProps': topKeysLimit, setTopKeysLimit","canonicalHead":{"code":2322,"messageText":"Type '{ topKeys: { api_key: string; key_alias: string; tags: never[]; spend: number; }[]; showTags: true; accessToken: string; userID: string; userRole: string; teams: null; premiumUser: boolean; }' is not assignable to type 'TopKeyViewProps'."}}]]],"affectedFilesPendingEmit":[4156,2683,2685,2687,2689,2691,2693,2676,2867,2858,1314,1313,1312,2864,2857,2855,2866,2856,2865,2861,2860,2859,1245,2862,2895,2893,2894,2812,2911,2912,2901,2913,2899,1315,2914,2903,1317,1316,2898,2915,2916,2904,1320,1319,2917,2902,2896,2909,2907,2910,2906,2905,2897,2900,2908,2918,2851,2919,2924,2921,2920,2923,2932,2925,2933,2929,1322,1321,2931,2927,2926,1323,2934,2928,2930,2949,2946,2950,2936,2939,2938,1324,1326,1325,2952,2953,2940,2951,2937,1327,2942,2941,2954,2943,1329,1328,2955,2956,2944,1087,2948,2945,2935,2947,2719,1331,1330,3051,2831,3052,3047,1334,1333,3053,3054,3049,1336,1335,3055,3048,3056,2959,3057,2829,2828,3058,2830,3059,2958,1344,1343,3060,3061,1342,1346,1345,3050,3063,1358,3064,3065,1356,3066,3067,1378,3068,1373,1379,3071,1367,3072,1365,3073,1364,1403,1363,1359,1404,1366,3069,1355,1380,1374,3070,1357,1349,1400,1377,1401,1375,1402,1376,3062,3149,3136,3151,3150,3152,3142,3154,3145,3155,3144,3153,3140,3156,3143,3148,3147,3112,3113,3090,1409,3093,3124,3081,3079,3125,3082,3126,3094,3127,3095,3128,3129,3075,3130,3076,3078,3131,3074,3077,3132,1697,3133,3080,3134,1410,1411,3114,3102,3115,3100,1405,1408,1407,3116,3101,3117,3118,3096,3119,1406,3084,3120,3085,3121,3092,3083,3109,3104,3091,3106,3098,3107,3099,3108,3097,3086,3122,3087,3123,3088,3110,3111,3103,3135,3089,3105,1422,1423,1421,1424,1425,1427,1426,1428,1114,1429,1431,1430,1455,1457,1456,1459,1458,1461,1460,1463,1462,1466,1465,1467,1107,3158,1454,1468,1469,1470,1471,1473,1472,1474,1475,1476,1478,1477,1480,1479,1481,1129,1483,1482,1484,1220,1486,1485,1488,1489,1487,1490,1492,1491,1239,1493,1494,1495,1496,1498,1497,1500,1499,1502,1501,1503,1504,1506,1505,1507,1509,1508,1510,1189,1512,1511,1513,1218,1516,1515,1518,1517,1520,1519,1521,1514,1523,1522,1525,1524,1527,1526,1222,1529,1528,1530,1532,1534,1533,1536,1535,1538,1537,1540,1539,1542,1541,1543,1545,1544,1547,1546,1548,1217,1549,1108,1552,1551,1553,1550,1555,1554,1412,1413,1109,1414,1231,1232,1415,1229,1416,1233,1417,1418,1219,1223,1557,1556,1559,1558,1561,1560,3157,1420,2815,2813,2811,1244,1243,3172,3192,3197,3240,3241,3218,1563,1562,1566,1565,3203,1568,1569,1567,3242,3215,3206,1571,1570,3219,3238,3258,3220,3259,3208,3260,3229,3261,3207,3262,3223,3263,3264,3222,3265,3224,3266,3232,3267,3209,3268,3237,1573,1572,3257,1574,3245,3243,3214,3244,3228,3246,3211,3247,3221,3248,3193,3194,3250,3196,3249,3195,1576,1575,3251,3201,3198,3213,3252,3212,3253,3204,3210,1654,3199,3205,3233,1656,1655,3254,3234,3255,3202,3200,3256,3231,3269,1564,3239,3277,3270,3278,3271,3279,3273,3272,3280,3274,3276,3275,3298,3368,3367,1666,1665,3375,3325,3376,3324,1670,1669,3379,3332,3331,3330,1672,1671,3377,3363,3323,3378,3371,1663,1662,3374,3373,3380,3369,3381,3342,3326,3382,3333,3383,3362,3347,3366,3364,3358,3372,1664,1674,1673,1216,3388,3386,3387,3401,3399,3402,3398,3397,3393,3392,3400,2853,2852,3508,3530,3500,3531,3522,3532,3509,3533,3501,1676,3510,3502,3534,3503,3535,3517,3536,3521,3537,3511,3504,3538,3505,3539,3506,3540,3507,3541,3520,3515,3518,3514,3516,3519,1678,1677,3542,3527,3543,3525,3544,3523,3545,3526,3547,3546,3548,3524,1681,1680,3404,1686,1685,1688,3424,3549,3492,3550,3493,3551,3494,3552,3495,1679,3496,3497,3499,3529,3528,3573,3563,3574,3557,3575,3568,3571,3560,3559,1691,1690,3576,3566,3577,3558,3578,3561,3579,3569,3580,3555,3581,3556,3582,3565,3583,3564,3572,3554,3553,1693,1692,3584,3567,3562,3570,3595,3590,3596,3589,3597,3588,3587,3600,3601,3585,3602,3603,3586,3604,1973,1694,1975,1974,3598,3593,3599,3592,3591,3594,3633,3610,3634,3630,3629,3646,3619,3651,3624,3647,3620,3648,3623,3649,3621,1979,1980,3650,3618,3622,3638,3616,3626,3628,3639,3613,3640,3608,3641,3612,3642,3617,3643,3625,3644,3614,1976,1978,1977,3645,3627,3635,3609,3605,3632,3607,3606,3636,3611,3637,3615,3631,3653,3046,3652,3663,3664,3655,3661,3665,3654,1983,1982,3669,3670,3660,3666,3657,3656,3667,3658,3668,3659,1981,3662,3678,3671,3676,3674,3677,3673,3672,3675,3688,3682,3686,3683,3687,3679,3685,3681,3680,3684,3696,3703,3706,3705,3704,3709,3708,3707,3732,3716,3733,3717,3734,3718,3731,3719,3735,3723,1987,1989,1988,3736,3724,1992,1991,3722,3737,3721,1985,1984,3720,3729,3725,3730,3727,3738,3726,1993,1341,3728,3749,3740,3752,3742,1996,1995,1997,1994,3747,3750,3739,3751,3746,3754,3755,3745,3753,3744,3743,3748,3769,3770,3765,3771,3763,3762,3779,3767,1210,3772,1209,1208,3773,3764,3774,3766,3780,3781,3761,3775,3776,3759,3777,3758,3757,3778,3760,3768,3785,3784,3783,3782,3793,3795,3799,3787,3786,3801,3791,3790,3803,3805,3804,3807,3806,2703,3809,3810,3808,3811,3812,3813,3814,3816,3815,3820,3819,3821,3822,3818,3823,3817,3824,3844,3711,2085,1104,3952,3329,3340,3942,3341,3954,3334,1095,3955,3300,1667,2041,2040,3943,3328,2044,2043,2046,2045,2047,1136,2042,1130,3956,3304,1121,1119,3944,1112,2048,1111,1118,1120,2049,1093,2050,1134,3945,1132,1131,3957,3335,1122,1127,3327,3958,3337,2051,1116,3946,1117,1135,3959,3336,1100,3960,3338,2086,1096,3947,1113,3961,3339,1123,3948,2087,3949,1128,3950,1124,2052,1133,2053,1125,1098,3951,1126,1097,1099,3845,3353,3962,1698,1318,3867,3281,3873,3282,3874,3284,3875,3286,3868,3283,3869,3297,3870,3287,3293,3871,3291,3872,3290,3162,3963,3161,3825,1230,3846,3741,1936,3788,2062,3964,2061,3965,3797,3966,3798,2060,3792,3967,3800,3968,3796,3969,3789,3794,2054,3802,2063,2055,3970,3295,3512,1675,3971,3513,3972,1683,1684,2065,2064,3294,3292,640,3847,3712,3876,3168,3877,3878,3165,3879,3163,3164,3880,3167,2004,2003,3881,3882,3166,1464,1372,1699,2833,1700,1085,3973,2818,3974,2834,3995,3389,3996,3390,3997,3391,2066,3998,3288,3999,3289,3975,1701,3976,2819,3977,2718,3318,3978,3311,3979,1934,3980,1933,3981,1084,3983,3982,3984,1952,3985,3352,1935,3351,3986,1939,1953,3987,1940,3988,1950,2068,2067,3989,2835,3991,1350,3992,1951,3993,2826,3994,3308,3990,2827,2868,3826,1959,3827,2715,3828,2720,3883,3175,3884,3174,3173,3885,3178,3886,3177,3176,3829,2922,2089,2090,2088,4000,2091,2092,638,3848,3159,3887,2012,3888,2008,3889,2009,3890,2010,2014,2007,3891,2013,2015,2011,4001,2843,2093,3830,3312,3137,3141,3892,3138,2016,3139,2018,2017,3831,1368,3849,2292,1998,1197,4002,1960,1961,4005,1089,2096,2095,1215,4003,2094,1046,2098,2097,4004,1962,2100,2099,2102,2101,3850,3348,3851,1242,3832,2722,4006,3403,1687,4007,1090,2104,2103,4008,3498,3852,2836,2105,1966,2106,4009,1963,4010,1967,4011,3230,1088,1965,4012,3422,4013,1086,2108,2107,4014,3343,4015,3346,4016,3345,3344,4017,3301,4018,3361,4019,3360,3359,4020,3322,2109,3285,3365,3853,3307,3305,3893,2854,2020,2019,4021,3299,4022,1354,4023,3038,3160,3854,2717,3895,2706,3896,2709,3897,2707,2021,1234,2022,3898,2710,3899,2716,3894,2712,3900,2714,1999,1214,3833,2721,1047,2840,3855,1958,4026,4027,1972,2110,1970,4024,4025,2841,2112,2111,2113,1971,2116,2115,4029,3395,2118,2117,4030,3394,2114,4028,3396,2000,2002,2001,3856,3354,3901,3356,3355,3902,3357,3857,3714,4031,2839,2121,2120,4032,2838,2837,4033,2844,1689,3858,3370,3859,1352,3860,3296,2123,2122,2126,2125,2124,3861,3349,3862,3350,4039,2960,4034,1941,4035,1942,4036,1945,4037,1943,4038,1944,4042,3045,4040,3044,2128,2127,4041,3043,3042,3041,2129,1531,3834,2869,4043,3313,3863,3171,2023,3903,2886,2884,3904,2885,2024,3905,2887,3906,2889,3907,2888,3908,2870,3909,3226,3910,3225,2026,2025,3911,3227,2028,2027,3912,2890,2029,2030,3918,2873,3919,2872,3920,2874,3921,3922,2875,3913,2876,3914,2877,3915,2880,3916,2878,3917,2879,2032,2031,3923,2881,3924,2882,3925,2883,3926,3170,3169,2033,3927,1949,3928,1946,3929,3039,1947,3931,3040,3930,1948,3146,4059,2821,4044,1956,4045,2816,4060,3713,4068,2277,4069,2278,4070,2279,4071,2276,2130,4072,2280,2282,4073,2281,4046,1235,4047,1968,1176,4061,4062,1180,4063,1182,4064,1179,4065,1184,4066,1187,4067,1186,1185,1188,1175,2006,4048,1195,2842,4074,1954,3309,3303,4049,1200,4050,1201,4051,1091,1937,4052,2814,4053,2825,4054,1696,4055,2957,2871,2823,4056,1041,4057,1226,2822,4075,1190,1191,4076,1192,4077,1194,4078,1196,1204,4079,1198,4080,1199,4081,1202,4082,1203,4058,2705,1695,3932,1238,3836,1241,3835,2891,4083,3423,637,4084,3691,3690,3689,2846,4085,4086,3314,3310,4087,1938,4092,3316,2284,2283,4088,3317,4093,3315,2286,2285,4089,3321,4090,3319,2287,2119,4091,3320,2288,3838,3695,2035,2034,3933,3694,3693,3837,3692,2290,2289,4098,2847,4099,4100,2848,4094,2832,2291,2294,2293,2845,4095,2820,4096,4097,2824,3934,2713,3839,3698,3935,3697,3936,3701,2036,3937,3700,3938,3699,3840,3702,4101,1347,1955,4102,1236,4103,1115,4104,2704,2708,4105,1035,1092,4106,2275,1183,1101,1040,1110,1362,4107,1044,2817,1038,1036,1042,1237,1102,4108,1225,4109,1045,1043,1181,1177,1103,2702,1094,1178,1348,1037,4110,1048,4111,1361,3841,2892,3842,3864,3302,3940,3385,3939,3710,2037,1986,1332,2039,2038,3865,3715,3843,2810,1138,4112,1969,3866,3756,4125,3181,4113,3182,4114,3180,3179,2301,4115,2313,2295,4116,2312,2311,2299,4126,2298,2309,2308,4127,2310,4128,2307,4121,4122,3191,4123,3183,2296,4129,2302,4130,2332,2300,4131,2305,4132,2335,2342,4133,2336,4134,2319,4135,2340,4136,2341,4137,2337,2329,2330,4138,2339,4139,2338,4140,1211,4141,2331,2304,4142,2334,4143,2333,4144,2316,4145,2315,2306,2343,2320,4124,3184,3185,4117,3186,4118,3190,3189,4119,3188,2323,2328,2324,2325,2326,4146,2327,2321,2344,2322,4120,3187,2297,2314,3306,3384,2849,3941,2850,2700,2056,4147,2711,2701,1224,2350,2348,2347,2349,2346,2345,2351,4151,2354,1360,4148,3216,4149,1371,3217,4150,3235,3236,2352,2353,2355,1351,2357,1137,2358,1034,2362,2361,2364,2363,4152,2084,2365,2366,1340,2367,630,2368,1212,2369,1213,2370,2359,1353,1205,2360,631,614,2371,2372,1227,2373,634,2374,1193,1453,2375,1207,2376,2378,2377,2379,636,2639,2638,2641,2640,2642,1240,2643,1228,2644,2646,2645,2647,633,2648,1964,2649,1668,2650,2651,1682,2652,1221,2655,2654,2658,2657,2659,2656,2660,1105,2661,1106,2663,2662,632,2664,1419,2665,2005,2666,1083,2667,1206,4153,2684,2686,2688,2690,2692,2694,2672,2673,2675,2677,2356,2695,3953,2678,2682,2863,4154,613],"version":"5.9.3"} \ No newline at end of file From 6a950df5498e1d40ec086e6f0ef2bb35b85e9128 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 8 Sep 2026 12:37:34 -0700 Subject: [PATCH 104/223] fix(ui): put the non-reasoning switch above the tier rows The switch adds a row at the top of the tier list, so sitting below Reasoning put the control and the thing it changes at opposite ends of the card. It now heads the list, with a separator between it and the first row. --- .../src/components/add_model/ComplexityRouterConfig.tsx | 8 ++++---- .../src/components/add_model/NonReasoningTierToggle.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index 540b68cf9f7..c89c130af86 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -659,6 +659,10 @@ const ComplexityRouterConfig: React.FC = ({ + {!customTierSet && ( + + )} + {tierRows.map((row, index) => { const tierInfo = builtInTierInfo(row.id); const label = tierRowLabel(row, value.tier_labels); @@ -739,10 +743,6 @@ const ComplexityRouterConfig: React.FC = ({ ); })} - {!customTierSet && ( - - )} - -
+
Add a non-reasoning tier
- + Adds NON_REASONING below Simple, for operational agent traffic that relays or reformats information rather than reasoning about it. Escalation still moves up out of it when a request needs more. {!available && " Requires the LLM classification method."} + ); }; From cf0488316e23b3f9859691d77043e49eb97f6eb6 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:40:28 -0700 Subject: [PATCH 105/223] fix(spend-tracking): take each unresolved key's newest spend row and drop docstrings --- .../common_daily_activity.py | 4 +- .../spend_tracking/key_metadata_recovery.py | 42 +++++++------------ .../test_common_daily_activity.py | 5 --- .../test_key_metadata_recovery.py | 5 --- 4 files changed, 16 insertions(+), 40 deletions(-) diff --git a/litellm/proxy/management_endpoints/common_daily_activity.py b/litellm/proxy/management_endpoints/common_daily_activity.py index c6c5aad5926..a8aef30107c 100644 --- a/litellm/proxy/management_endpoints/common_daily_activity.py +++ b/litellm/proxy/management_endpoints/common_daily_activity.py @@ -462,9 +462,7 @@ async def get_api_key_metadata( This ensures that key_alias and team_id are preserved in historical activity logs even after a key is deleted or regenerated. Also recovers aliases for api_key - values that were double-hashed by the v1.99 spend-log provenance gate, and, when - spend_logs_window is given, for keys never written to either token table (CLI - session tokens) from the spend-log rows those requests wrote in that window. + values that were double-hashed by the v1.99 spend-log provenance gate. """ key_records: Sequence[PrismaVerificationToken] = await VerificationTokenRepository(prisma_client).table.find_many( where={"token": {"in": list(api_keys)}} diff --git a/litellm/proxy/spend_tracking/key_metadata_recovery.py b/litellm/proxy/spend_tracking/key_metadata_recovery.py index 81c1f4cf119..e207f226255 100644 --- a/litellm/proxy/spend_tracking/key_metadata_recovery.py +++ b/litellm/proxy/spend_tracking/key_metadata_recovery.py @@ -29,16 +29,21 @@ ORDER BY token, deleted_at DESC """ _SPEND_LOG_ALIAS_SQL: Final = """ -SELECT DISTINCT ON (api_key) - api_key AS digest, - metadata->>'user_api_key_alias' AS key_alias, - COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id, - COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id -FROM "LiteLLM_SpendLogs" -WHERE api_key = ANY($1::text[]) - AND "startTime" >= $2::timestamp - AND "startTime" < $3::timestamp -ORDER BY api_key, (metadata->>'user_api_key_alias') IS NULL, "startTime" DESC +SELECT newest.digest, newest.key_alias, newest.team_id, newest.user_id +FROM unnest($1::text[]) AS missing(digest) +CROSS JOIN LATERAL ( + SELECT + api_key AS digest, + metadata->>'user_api_key_alias' AS key_alias, + COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id, + COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id + FROM "LiteLLM_SpendLogs" + WHERE api_key = missing.digest + AND "startTime" >= $2::timestamp + AND "startTime" < $3::timestamp + ORDER BY "startTime" DESC + LIMIT 1 +) AS newest """ @@ -152,14 +157,6 @@ async def recover_double_hashed_key_metadata( prisma_client: PrismaClient, missing_keys: AbstractSet[str], ) -> Mapping[str, KeyMetadataDict]: - """ - Recover key_alias/team_id/user_id for DailyUserSpend.api_key values that - were double-hashed by the v1.99 spend-log provenance gate. - - Those rows store hash(VerificationToken.token) instead of the token, so the - exact join misses. Postgres hashes the token column itself, one pass over - active keys and one over deleted keys, so no key row crosses the wire. - """ sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key)) if not sha_missing: return _EMPTY_KEY_METADATA @@ -187,15 +184,6 @@ async def recover_key_metadata_from_spend_logs( missing_keys: AbstractSet[str], window: tuple[datetime, datetime], ) -> Mapping[str, KeyMetadataDict]: - """ - Recover key_alias/team_id/user_id for hashed api_key values absent from both - verification-token tables, e.g. in-memory CLI session tokens that never get a - token row. Their owner is written to LiteLLM_SpendLogs metadata at request - time under the same hashed api_key, so it is the only surviving source. Only - sha256 digests are looked up, matching the reverse-hash recovery gate, since - every current api_key value in spend logs is a token hash. The [start, end) - bound keeps the lookup on the startTime index instead of scanning the table. - """ sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key)) if not sha_missing: return _EMPTY_KEY_METADATA diff --git a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py index 4d3dc320d8f..4c8be415204 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py +++ b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py @@ -2109,11 +2109,6 @@ async def test_get_daily_activity_aggregated_with_entity_breakdown(): @pytest.mark.asyncio async def test_get_api_key_metadata_resolves_session_key_via_spend_log_window(): - """ - A CLI session token has no verification-token row, so the active/deleted lookups - and reverse-hash all miss. Given a spend-log window, its alias and owner are - recovered from the spend-log metadata and its email is filled from the user table. - """ from litellm.proxy.utils import hash_token session_digest = hash_token("cli-session-user-42") diff --git a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py index 9e42f017106..05a90137e3b 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py +++ b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py @@ -233,11 +233,6 @@ async def test_fill_missing_api_key_aliases_skips_named_keys_that_have_no_email( @pytest.mark.asyncio async def test_recover_key_metadata_from_spend_logs_resolves_session_token_from_metadata(): - """ - CLI session tokens never get a verification-token row, so both the exact join and - the reverse-hash lookup miss them. Their owner survives only in the spend-log - metadata written at request time, keyed by the same hashed api_key. - """ session_digest = hash_token("cli-session-repro-user-6852") window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) mock_prisma = MagicMock() From 61ab4307ecfac49aad12feffcba265a21a270e6e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:47:31 -0700 Subject: [PATCH 106/223] test(cost_map): assert provenance without patching module state --- .../test_get_model_cost_map.py | 5 +-- .../test_routes_model_cost_map.py | 37 ++++++------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py index f72d175d579..0495440c51c 100644 --- a/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py +++ b/tests/test_litellm/litellm_core_utils/test_get_model_cost_map.py @@ -310,14 +310,13 @@ def test_openrouter_catalog_costs_match_live_headline_rates(cost_map: dict): assert entry["output_cost_per_token"] != stale_out, model -def test_get_model_cost_map_stamps_loaded_at(monkeypatch): +def test_get_model_cost_map_stamps_loaded_at(): """The load time feeds each pod's reload-due decision; a load that does not stamp it would make manual reload requests race the proxy's startup""" from datetime import datetime, timezone from litellm.litellm_core_utils import get_model_cost_map as module - monkeypatch.setattr(module._cost_map_source_info, "loaded_at", None) client, _calls = _mock_client( [httpx.Response(200, content=_real_map_bytes())], client_cls=httpx.Client ) @@ -560,7 +559,6 @@ async def test_refetch_stamps_loaded_at_on_remote_and_local_reloads(monkeypatch) from litellm.litellm_core_utils import get_model_cost_map as module client, _ = _mock_client([httpx.Response(200, content=_real_map_bytes())]) - monkeypatch.setattr(module._cost_map_source_info, "loaded_at", None) before_remote = datetime.now(timezone.utc) await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0), client=client) remote_loaded_at = module.get_model_cost_map_loaded_at() @@ -568,7 +566,6 @@ async def test_refetch_stamps_loaded_at_on_remote_and_local_reloads(monkeypatch) assert before_remote <= remote_loaded_at <= datetime.now(timezone.utc) monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") - monkeypatch.setattr(module._cost_map_source_info, "loaded_at", None) before_local = datetime.now(timezone.utc) await refetch_model_cost_map(url=_URL, sleep=_SleepRecorder(), rng=random.Random(0)) local_loaded_at = module.get_model_cost_map_loaded_at() diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py index 36c364fb82b..40bd66ea91c 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_model_cost_map.py @@ -15,16 +15,15 @@ from pathlib import Path from unittest.mock import AsyncMock, MagicMock +from litellm.litellm_core_utils.get_model_cost_map import get_model_cost_map_provenance + from .conftest import VOLATILE_KEYS, normalize # Some response bodies include a "timestamp" — extend the volatile set so # dict-equality assertions remain stable. _VOLATILE = VOLATILE_KEYS | frozenset({"timestamp"}) -_PROVENANCE = { - "source_revision": "0123456789abcdef0123456789abcdef01234567", - "etag": 'W/"cost-map-etag"', -} +_SERVED_ETAG = 'W/"cost-map-etag"' _ROOT_COST_MAP = Path(__file__).resolve().parents[4] / "model_prices_and_context_window.json" @@ -49,13 +48,6 @@ def _attach_litellm_config(mock_prisma): return table -def _pin_provenance(monkeypatch): - monkeypatch.setattr( - "litellm.litellm_core_utils.get_model_cost_map.get_model_cost_map_provenance", - lambda: dict(_PROVENANCE), - ) - - # --------------------------------------------------------------------------- # POST /reload/model_cost_map # --------------------------------------------------------------------------- @@ -69,7 +61,6 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): table = _attach_litellm_config(mock_prisma) monkeypatch.setattr(ps, "prisma_client", mock_prisma) - _pin_provenance(monkeypatch) fake_cost_map = {"gpt-4": {"input_cost": 0.03}, "gpt-3.5": {"input_cost": 0.002}} monkeypatch.setattr( @@ -98,7 +89,7 @@ def test_reload_model_cost_map_happy(client, auth_as, monkeypatch, mock_prisma): "status": "success", "models_count": 2, "timestamp": "", - **_PROVENANCE, + **get_model_cost_map_provenance(), } assert table.upsert.await_count == 1 update_payload = table.upsert.await_args.kwargs["data"]["update"] @@ -120,8 +111,8 @@ def test_reload_model_cost_map_surfaces_the_blob_id_of_the_bytes_served_on_every monkeypatch.setattr(ps, "prisma_client", mock_prisma) monkeypatch.delenv("LITELLM_LOCAL_MODEL_COST_MAP", raising=False) body = _ROOT_COST_MAP.read_bytes() - expected = {"source_revision": git_blob_id(body), "etag": _PROVENANCE["etag"]} - served = httpx.Response(200, headers={"ETag": _PROVENANCE["etag"]}, content=body) + expected = {"source_revision": git_blob_id(body), "etag": _SERVED_ETAG} + served = httpx.Response(200, headers={"ETag": _SERVED_ETAG}, content=body) monkeypatch.setattr( "litellm.litellm_core_utils.get_model_cost_map._default_reload_client", lambda: httpx.AsyncClient(transport=httpx.MockTransport(lambda request: served)), @@ -339,7 +330,6 @@ def test_get_model_cost_map_reload_status_no_db_not_scheduled( from litellm.proxy._types import LitellmUserRoles monkeypatch.setattr(ps, "prisma_client", None) - _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") assert response.status_code == 200 @@ -348,7 +338,7 @@ def test_get_model_cost_map_reload_status_no_db_not_scheduled( "interval_hours": None, "last_run": None, "next_run": None, - **_PROVENANCE, + **get_model_cost_map_provenance(), } @@ -366,7 +356,6 @@ def test_get_model_cost_map_reload_status_scheduled( config_row.last_run_at = None table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) - _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -376,7 +365,7 @@ def test_get_model_cost_map_reload_status_scheduled( "interval_hours": 12, "last_run": None, "next_run": None, - **_PROVENANCE, + **get_model_cost_map_provenance(), } @@ -396,7 +385,6 @@ def test_get_model_cost_map_reload_status_reports_persisted_last_run( config_row.last_run_at = datetime(2024, 1, 1, 6, 0, 0, tzinfo=timezone.utc) table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) - _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -406,7 +394,7 @@ def test_get_model_cost_map_reload_status_reports_persisted_last_run( "interval_hours": 6, "last_run": "2024-01-01T06:00:00+00:00", "next_run": "2024-01-01T12:00:00+00:00", - **_PROVENANCE, + **get_model_cost_map_provenance(), } @@ -426,7 +414,6 @@ def test_get_model_cost_map_reload_status_no_config_not_scheduled( config_row.last_run_at = None table.find_unique = AsyncMock(return_value=config_row) monkeypatch.setattr(ps, "prisma_client", mock_prisma) - _pin_provenance(monkeypatch) with auth_as(LitellmUserRoles.PROXY_ADMIN): response = client.get("/schedule/model_cost_map_reload/status") @@ -436,7 +423,7 @@ def test_get_model_cost_map_reload_status_no_config_not_scheduled( "interval_hours": None, "last_run": None, "next_run": None, - **_PROVENANCE, + **get_model_cost_map_provenance(), } @@ -464,7 +451,7 @@ def test_get_model_cost_map_source_happy(client, auth_as, monkeypatch): "is_env_forced": False, "fallback_reason": None, "loaded_at": "2026-09-07T01:02:03+00:00", - **_PROVENANCE, + **get_model_cost_map_provenance(), } monkeypatch.setattr( "litellm.litellm_core_utils.get_model_cost_map.get_model_cost_map_source_info", @@ -481,7 +468,7 @@ def test_get_model_cost_map_source_happy(client, auth_as, monkeypatch): "is_env_forced": False, "fallback_reason": None, "loaded_at": "2026-09-07T01:02:03+00:00", - **_PROVENANCE, + **get_model_cost_map_provenance(), "model_count": 3, } From 384eee26a5c144290d922879c619f18126deec01 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:53:01 -0700 Subject: [PATCH 107/223] fix(bedrock): reject s3 Marengo media without bucketOwner and skip items without an embedding --- .../twelvelabs_marengo_3_transformation.py | 14 ++++--- .../twelvelabs_marengo_transformation.py | 6 +-- .../test_bedrock_async_invoke_embedding.py | 3 +- .../bedrock/embed/test_bedrock_embedding.py | 10 +++++ ...est_twelvelabs_marengo_3_transformation.py | 40 +++++++++++++++---- 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py index f4f9cb03dab..4aac6f22bae 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_3_transformation.py @@ -91,19 +91,21 @@ class Marengo3Params(BaseModel): return self.model_dump(include=MARENGO_2_7_ONLY_FIELDS, exclude_none=True) -def _s3_location(uri: str, bucket_owner: str | None) -> TwelveLabsS3Location: +def _require_bucket_owner(bucket_owner: str | None) -> str: if bucket_owner is None: - unowned: Final[TwelveLabsS3Location] = {"uri": uri} - return unowned - owned: Final[TwelveLabsS3Location] = {"uri": uri, "bucketOwner": bucket_owner} - return owned + raise BedrockError( + status_code=400, + message="s3:// media requires the 'bucketOwner' parameter, the account id that owns the bucket", + ) + return bucket_owner def _media_source(media: str, bucket_owner: str | None) -> TwelveLabsMediaSource: if not media.startswith(S3_URI_PREFIX): inline: Final[TwelveLabsMediaSource] = {"base64String": get_base64_str(media)} return inline - remote: Final[TwelveLabsMediaSource] = {"s3Location": _s3_location(media, bucket_owner)} + s3_location: Final[TwelveLabsS3Location] = {"uri": media, "bucketOwner": _require_bucket_owner(bucket_owner)} + remote: Final[TwelveLabsMediaSource] = {"s3Location": s3_location} return remote diff --git a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py index 35163ecf848..65ca2be191f 100644 --- a/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py +++ b/litellm/llms/bedrock/embed/twelvelabs_marengo_transformation.py @@ -35,7 +35,7 @@ from litellm.types.utils import Embedding, EmbeddingResponse, PromptTokensDetail class MarengoEmbeddingItem(BaseModel): model_config = ConfigDict(extra="ignore", frozen=True) - embedding: tuple[float, ...] + embedding: tuple[float, ...] | None = None class MarengoInvokeResponse(BaseModel): @@ -47,10 +47,10 @@ class MarengoInvokeResponse(BaseModel): def vectors(self) -> tuple[tuple[float, ...], ...]: if self.data: - return tuple(item.embedding for item in self.data) + return tuple(item.embedding for item in self.data if item.embedding is not None) if self.embedding is not None: return (self.embedding,) - return tuple(item.embedding for item in self.embeddings) + return tuple(item.embedding for item in self.embeddings if item.embedding is not None) class MarengoBilledMultiInput(BaseModel): diff --git a/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py b/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py index 00f5145269a..ddbd3a2e9ba 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py +++ b/tests/test_litellm/llms/bedrock/embed/test_bedrock_async_invoke_embedding.py @@ -204,6 +204,7 @@ class TestBedrockAsyncInvokeEmbedding: input_type="video", embeddingOption=["visual", "audio"], segmentation={"method": "fixed", "fixed": {"durationSec": 6}}, + bucketOwner="123456789012", output_s3_uri="s3://test-bucket/async-invoke-output/", ) @@ -214,7 +215,7 @@ class TestBedrockAsyncInvokeEmbedding: "modelInput": { "inputType": "video", "video": { - "mediaSource": {"s3Location": {"uri": "s3://test-bucket/clip.mp4"}}, + "mediaSource": {"s3Location": {"uri": "s3://test-bucket/clip.mp4", "bucketOwner": "123456789012"}}, "segmentation": {"method": "fixed", "fixed": {"durationSec": 6}}, "embeddingOption": ["visual", "audio"], }, diff --git a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py index c29a87cd0cf..bcd1a29d0e8 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py +++ b/tests/test_litellm/llms/bedrock/embed/test_bedrock_embedding.py @@ -1220,6 +1220,16 @@ def test_marengo_usage_without_request_data_bills_nothing(): assert response.usage.prompt_tokens_details is None +def test_marengo_response_items_without_an_embedding_are_skipped(): + response = TwelveLabsMarengoEmbeddingConfig()._transform_response( + response_list=[{"data": [{"embeddingOption": "visual-text", "startSec": 0.0}, {"embedding": [0.1, 0.2, 0.3]}]}], + model="us.twelvelabs.marengo-embed-3-0-v1:0", + ) + + assert [item["embedding"] for item in response.data] == [[0.1, 0.2, 0.3]] + assert response.data[0]["index"] == 0 + + def test_marengo_3_text_image_without_media_source_is_a_bad_request(): with pytest.raises(litellm.BadRequestError, match=r"text_image.*media_source"): litellm.embedding( diff --git a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py index d8d29cac35e..f149953b6f1 100644 --- a/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py +++ b/tests/test_litellm/llms/bedrock/embed/test_twelvelabs_marengo_3_transformation.py @@ -75,9 +75,22 @@ def test_image_request_from_s3_carries_bucket_owner(): } -def test_s3_media_without_bucket_owner_omits_the_key(): - request = build_marengo_3_request("s3://media/duck.png", {"input_type": "image"}) - assert request["image"]["mediaSource"] == {"s3Location": {"uri": "s3://media/duck.png"}} +@pytest.mark.parametrize( + "input_media,params", + [ + ("s3://media/duck.png", {"input_type": "image"}), + ("s3://media/clip.mp4", {"input_type": "video"}), + ("a duck", {"input_type": "text_image", "media_source": "s3://media/duck.png"}), + ("a duck", {"input_type": "multi_input", "media_sources": {"img1": "s3://media/duck.png"}}), + ], +) +def test_s3_media_without_bucket_owner_is_rejected_naming_it(input_media, params): + with pytest.raises(BedrockError) as excinfo: + build_marengo_3_request(input_media, params) + assert excinfo.value.status_code == 400 + assert excinfo.value.message == ( + "s3:// media requires the 'bucketOwner' parameter, the account id that owns the bucket" + ) def test_text_image_request_pairs_text_with_media_source(): @@ -147,12 +160,13 @@ def test_timed_media_request_nests_every_option_under_the_media_key(input_type): "embeddingType": ["fused_embedding"], "embeddingScope": ["clip", "asset"], "inferenceId": "req-42", + "bucketOwner": "123456789012", }, ) assert wire(request) == { "inputType": input_type, input_type: { - "mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}, + "mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4", "bucketOwner": "123456789012"}}, "startSec": 2.0, "endSec": 12.5, "segmentation": {"method": "dynamic", "dynamic": {"minDurationSec": 4}}, @@ -165,8 +179,10 @@ def test_timed_media_request_nests_every_option_under_the_media_key(input_type): def test_timed_media_request_without_options_carries_only_the_media_source(): - request = build_marengo_3_request("s3://media/clip.mp4", {"input_type": "video"}) - assert request["video"] == {"mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}} + request = build_marengo_3_request("s3://media/clip.mp4", {"input_type": "video", "bucketOwner": "123456789012"}) + assert request["video"] == { + "mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4", "bucketOwner": "123456789012"}} + } @pytest.mark.parametrize( @@ -211,7 +227,12 @@ def test_marengo_3_video_and_audio_still_require_the_async_route(input_type): def test_marengo_3_async_invoke_wraps_the_nested_payload_with_the_base_model_id(): request = TwelveLabsMarengoEmbeddingConfig(model=MARENGO_3_BASE)._transform_request( input="s3://media/clip.mp4", - inference_params={"input_type": "video", "embeddingOption": ["visual"], "output_s3_uri": OUTPUT_S3_URI}, + inference_params={ + "input_type": "video", + "embeddingOption": ["visual"], + "bucketOwner": "123456789012", + "output_s3_uri": OUTPUT_S3_URI, + }, async_invoke_route=True, model_id="async_invoke%2Ftwelvelabs.marengo-embed-3-0-v1%3A0", output_s3_uri=OUTPUT_S3_URI, @@ -220,7 +241,10 @@ def test_marengo_3_async_invoke_wraps_the_nested_payload_with_the_base_model_id( "modelId": MARENGO_3_BASE, "modelInput": { "inputType": "video", - "video": {"mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4"}}, "embeddingOption": ["visual"]}, + "video": { + "mediaSource": {"s3Location": {"uri": "s3://media/clip.mp4", "bucketOwner": "123456789012"}}, + "embeddingOption": ["visual"], + }, }, "outputDataConfig": {"s3OutputDataConfig": {"s3Uri": OUTPUT_S3_URI}}, } From fbd8fa8d3e0d5ce5081ff248865ff5ff0a22e50a Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 8 Sep 2026 12:59:07 -0700 Subject: [PATCH 108/223] refactor: tighten the comments added for the non-reasoning tier Review flagged the added comments as over-explaining. Cut the call-site comment that restated the helper's own docstring, and shortened the rest to the fact the code cannot state itself: why the constant excludes the tier, why the flag is cleared on a classifier change, and why the edit modal reads both keys back. --- litellm/router_strategy/complexity_router/config.py | 8 +++----- .../components/add_model/ClassificationMethodConfig.tsx | 9 +++------ .../src/components/add_model/NonReasoningTierToggle.tsx | 3 +-- .../edit_auto_router/edit_auto_router_modal.tsx | 6 ++---- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index d917c1b8041..1d03c56050a 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -56,11 +56,9 @@ DEFAULT_CLASSIFICATION_RUBRIC: Final[ClassificationRubric] = ClassificationRubri LLM_CLASSIFIER_TYPES: Final[frozenset[str]] = frozenset({"llm", "heuristic_first", "hybrid"}) -# The ladder as it has always shipped. NON_REASONING is absent because it is opt-in: an existing -# router must not gain a rubric bullet, a wire label, or a rung it never configured, and the -# heuristic_v2 artifact is trained on exactly these four classes. Read the active ladder off the -# config (`tier_names`, `active_tier_severity_order`) rather than this constant wherever the -# operator's `enable_non_reasoning_tier` can reach. +# Excludes NON_REASONING so an existing router keeps the ladder, rubric and wire labels it already +# has, and so heuristic_v2 keeps mapping onto the four classes its artifact is trained on. Anywhere +# `enable_non_reasoning_tier` can reach, read the ladder off the config instead. TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( ComplexityTier.SIMPLE, ComplexityTier.MEDIUM, diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 468dcf5b411..5ec9f72c2a1 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -237,9 +237,9 @@ const ClassifierTypeRadios: React.FC<{ }; /** - * The NON_REASONING keys a classifier switch should carry forward, or clear. Leaving the flag set - * under a classifier that cannot emit the tier produces a config the backend refuses on save, and - * the switch is disabled there, so the operator would have no way to undo it. + * The NON_REASONING keys a classifier switch carries forward, or clears. Only the LLM classifier + * can emit the tier, and the switch is disabled elsewhere, so a flag left set under another + * classifier would be an unsaveable config the operator could not undo. */ export const nonReasoningTierFields = ( classifierType: ClassifierType, @@ -303,9 +303,6 @@ const ClassificationMethodConfig: React.FC = ({ : undefined, hybrid_boundary_margin: classifierType === "hybrid" ? value.hybrid_boundary_margin ?? DEFAULT_HYBRID_BOUNDARY_MARGIN : undefined, - // Only the LLM classifier can produce NON_REASONING, and the backend rejects the flag - // beside any other type. Clearing it here (with the tier's own pool) is what keeps a - // switch away from LLM from stranding a config that can never be saved. ...nonReasoningTierFields(classifierType, value), }; onChange(nextValue); diff --git a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx index c0d26874357..622a460fb79 100644 --- a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx +++ b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx @@ -15,8 +15,7 @@ const NonReasoningTierToggle: React.FC<{ onChange: (value: ComplexityRouterConfigValue) => void; available: boolean; }> = ({ value, onChange, available }) => { - // Turning it off drops the tier's key rather than leaving an empty pool, which the backend - // rejects; turning it back on restores whatever pool the form still held. + // Off drops the tier's key rather than leaving the empty pool the backend rejects. const handleToggle = (enabled: boolean): void => { const { NON_REASONING: existingPool, ...keptTiers } = value.tiers; const next: ComplexityRouterConfigValue = { diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index 283fe978790..1ef713cea02 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -136,10 +136,8 @@ export const hydrateComplexityRouterConfig = ( parsedConfig: StoredComplexityRouterConfig, complexityRouterDefaultModel: string | null | undefined, ): ComplexityRouterConfigValue => { - // `tiers` is rewritten wholesale on save, so a stored tier this misses is deleted from the - // router by any edit at all, including one made for an unrelated reason. NON_REASONING is - // therefore read back from the stored config rather than assumed absent, and the toggle follows - // what is actually stored so the round-trip cannot silently turn the tier off. + // `tiers` is rewritten wholesale on save, so a stored tier this misses is deleted by any edit, + // including one made for an unrelated reason. Hence reading both back rather than assuming four. const storedNonReasoning: string[] = normalizeTierModels(parsedConfig.tiers?.NON_REASONING); const enable_non_reasoning_tier: boolean = parsedConfig.enable_non_reasoning_tier === true || storedNonReasoning.length > 0; From 91091fd93e7b7be6576be26e1ef1216391af667e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:59:58 -0700 Subject: [PATCH 109/223] fix: withdraw policy header claims while a background response is pending and log ungoverned retrievals --- .../proxy/policy_engine/response_retrieval.py | 31 ++++-- litellm/proxy/utils.py | 62 +++++++++++- .../policy_engine/test_response_retrieval.py | 36 +++++-- .../proxy_logging/test_guardrail_pipeline.py | 97 +++++++++++++++++++ 4 files changed, 205 insertions(+), 21 deletions(-) diff --git a/litellm/proxy/policy_engine/response_retrieval.py b/litellm/proxy/policy_engine/response_retrieval.py index 0aa9579ca71..8fc75e40070 100644 --- a/litellm/proxy/policy_engine/response_retrieval.py +++ b/litellm/proxy/policy_engine/response_retrieval.py @@ -1,6 +1,7 @@ from collections.abc import Mapping +from dataclasses import dataclass from types import MappingProxyType -from typing import TYPE_CHECKING, Final, TypeAlias +from typing import TYPE_CHECKING, Final, Literal, TypeAlias from pydantic import TypeAdapter @@ -29,14 +30,23 @@ PolicyPipelines: TypeAlias = tuple[tuple[str, GuardrailPipeline], ...] _POLICY_PIPELINES_ADAPTER: Final = TypeAdapter(PolicyPipelines) -def _model_group_for_response_id(response_id: object, llm_router: "Router | None") -> str | None: - if llm_router is None or not isinstance(response_id, str): - return None - model_id: Final = ResponsesAPIRequestUtils.get_model_id_from_response_id(response_id) +@dataclass(frozen=True, slots=True) +class UngovernedRetrieval: + reason: Literal["no router", "response id names no deployment", "deployment no longer in the router"] + + +def _model_group_for_response_id(response_id: object, llm_router: "Router | None") -> str | UngovernedRetrieval: + if llm_router is None: + return UngovernedRetrieval("no router") + model_id: Final = ( + ResponsesAPIRequestUtils.get_model_id_from_response_id(response_id) if isinstance(response_id, str) else None + ) if model_id is None: - return None + return UngovernedRetrieval("response id names no deployment") deployment: Final = llm_router.get_deployment(model_id) - return deployment.model_name if deployment is not None else None + if deployment is None: + return UngovernedRetrieval("deployment no longer in the router") + return deployment.model_name def _retrieval_context( @@ -78,7 +88,12 @@ def attach_post_call_pipelines_to_retrieval( if not get_policy_registry().is_initialized(): return model_group: Final = _model_group_for_response_id(data.get("response_id"), llm_router) - if model_group is None: + if isinstance(model_group, UngovernedRetrieval): + verbose_proxy_logger.warning( + "Policy engine: background response %s is retrieved without its post_call policy pipelines (%s)", + data.get("response_id"), + model_group.reason, + ) return context: Final = _retrieval_context(data, user_api_key_dict, model_group) post_call_pipelines, policy_sources = _post_call_pipelines_for_context(context) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 3d03d3858ee..1972d92b610 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -93,6 +93,7 @@ from litellm.integrations.SlackAlerting.slack_alerting import SlackAlerting from litellm.integrations.SlackAlerting.utils import _add_langfuse_trace_id_to_alert from litellm.litellm_core_utils.core_helpers import ( coerce_token_limit, + get_or_create_metadata_bucket, independent_snapshot, is_expected_client_error, ) @@ -156,6 +157,8 @@ from litellm.proxy.hooks.sensitive_data_routing import ( from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup, add_guardrails_from_auth_metadata from litellm.proxy.management_helpers.key_settings_audit import with_settings_updated_at from litellm.proxy.policy_engine.pipeline_executor import PipelineExecutor +from litellm.proxy.policy_engine.policy_registry import get_policy_registry +from litellm.proxy.policy_engine.policy_resolver import PolicyResolver from litellm.repositories.budget_repository import BudgetRepository from litellm.repositories.config_repository import ConfigRepository from litellm.repositories.table_repositories import ( @@ -533,16 +536,65 @@ def _is_pending_background_response(response: LLMResponseTypes) -> bool: return isinstance(response, ResponsesAPIResponse) and response.status in _PENDING_BACKGROUND_RESPONSE_STATUSES -def _log_deferred_post_call_pipelines(data: Mapping[str, object], response: ResponsesAPIResponse) -> None: - policy_names: Final = tuple(policy_name for policy_name, _pipeline in _post_call_pipelines(data)) - if not policy_names: +def _guardrails_outside_pipeline(policy_name: str, pipeline: "GuardrailPipeline") -> frozenset[str]: + resolved: Final = PolicyResolver.resolve_policy_guardrails( + policy_name=policy_name, policies=get_policy_registry().get_all_policies() + ) + return frozenset(resolved.guardrails) - frozenset(step.guardrail for step in pipeline.steps) + + +def _without_names( + bucket: dict, # mutable-ok: the applied_* header slots live in the request-state dict every hook writes in place + slot: str, + names: frozenset[str], +) -> None: + claimed: Final = bucket.get(slot) + if not isinstance(claimed, list): + return + remaining: Final = [name for name in claimed if name not in names] + if remaining: + bucket[slot] = remaining + else: + bucket.pop(slot) + + +def _withdraw_deferred_claims( + data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + deferred: Sequence[tuple[str, "GuardrailPipeline"]], +) -> None: + outside_by_policy: Final = MappingProxyType( + {policy_name: _guardrails_outside_pipeline(policy_name, pipeline) for policy_name, pipeline in deferred} + ) + running_elsewhere: Final = _pipeline_managed_guardrail_names(data, "pre_call").union(*outside_by_policy.values()) + withdrawn_policies: Final = frozenset(name for name, outside in outside_by_policy.items() if not outside) + withdrawn_guardrails: Final = _pipeline_step_guardrail_names(deferred) - running_elsewhere + _, bucket = get_or_create_metadata_bucket(data) + _without_names(bucket, "applied_policies", withdrawn_policies) + _without_names(bucket, "applied_guardrails", withdrawn_guardrails) + sources: Final = bucket.get("policy_sources") + if not isinstance(sources, dict): + return + remaining_sources: Final = {name: reason for name, reason in sources.items() if name not in withdrawn_policies} + if remaining_sources: + bucket["policy_sources"] = remaining_sources + else: + bucket.pop("policy_sources") + + +def _defer_post_call_pipelines( + data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + response: ResponsesAPIResponse, +) -> None: + deferred: Final = _post_call_pipelines(data) + if not deferred: return verbose_proxy_logger.debug( "Post_call guardrail pipelines wait for background response %s (status=%s) to be retrieved complete: %s", response.id, response.status, - ", ".join(policy_names), + ", ".join(policy_name for policy_name, _pipeline in deferred), ) + _withdraw_deferred_claims(data, deferred) def _pipeline_is_streamable(policy_name: str, pipeline: "GuardrailPipeline") -> bool: @@ -2939,7 +2991,7 @@ class ProxyLogging: response: LLMResponseTypes, ) -> LLMResponseTypes | None: if _is_pending_background_response(response): - _log_deferred_post_call_pipelines(data, response) + _defer_post_call_pipelines(data, response) return None _, pipeline_response = await self._maybe_execute_pipelines( data=data, diff --git a/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py b/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py index f7f29d6b46e..8c00b18eb63 100644 --- a/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py +++ b/tests/test_litellm/proxy/policy_engine/test_response_retrieval.py @@ -1,3 +1,5 @@ +import logging + import pytest from litellm.proxy._types import UserAPIKeyAuth @@ -128,30 +130,48 @@ def test_already_attached_policy_is_not_attached_twice(policy_engine): assert data["litellm_metadata"]["applied_policies"] == ["response-governance"] +def _ungoverned_retrieval_warnings(caplog: pytest.LogCaptureFixture) -> list[str]: + return [ + record.getMessage() + for record in caplog.records + if record.levelno == logging.WARNING and "retrieved without its post_call policy pipelines" in record.getMessage() + ] + + @pytest.mark.parametrize( - "response_id", - ["resp_plain_upstream_id", _encoded_response_id("deployment-missing-from-router"), None], + ("response_id", "reason"), + [ + ("resp_plain_upstream_id", "response id names no deployment"), + (_encoded_response_id("deployment-missing-from-router"), "deployment no longer in the router"), + (None, "response id names no deployment"), + ], ) -def test_unresolvable_response_id_attaches_nothing(policy_engine, response_id): +def test_unresolvable_response_id_attaches_nothing_and_warns(policy_engine, caplog, response_id, reason): data = {"response_id": response_id, "litellm_metadata": {}} - attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) assert data == {"response_id": response_id, "litellm_metadata": {}} + assert [message.endswith(f"({reason})") for message in _ungoverned_retrieval_warnings(caplog)] == [True] -def test_without_a_router_attaches_nothing(policy_engine): +def test_without_a_router_attaches_nothing_and_warns(policy_engine, caplog): data = _retrieval_data(GOVERNED_MODEL_ID) - attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=None) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=None) assert data == _retrieval_data(GOVERNED_MODEL_ID) + assert [message.endswith("(no router)") for message in _ungoverned_retrieval_warnings(caplog)] == [True] -def test_without_policy_engine_attaches_nothing(): +def test_without_policy_engine_attaches_nothing_quietly(caplog): get_policy_registry().clear() data = _retrieval_data(GOVERNED_MODEL_ID) - attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=_router()) + with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"): + attach_post_call_pipelines_to_retrieval(data=data, user_api_key_dict=UserAPIKeyAuth(), llm_router=None) assert data == _retrieval_data(GOVERNED_MODEL_ID) + assert _ungoverned_retrieval_warnings(caplog) == [] diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py index 7d2c232ad97..8da07f39fd8 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_guardrail_pipeline.py @@ -1481,6 +1481,103 @@ async def test_post_call_success_hook_runs_pipeline_on_retrieved_background_resp assert seen["response"] is response +def _output_passing_callbacks() -> list[CustomGuardrail]: + class OutputPassingGuardrail(CustomGuardrail): + async def async_post_call_success_hook(self, data, user_api_key_dict, response): + return response + + return [OutputPassingGuardrail(guardrail_name="gr-post", event_hook=GuardrailEventHooks.post_call, default_on=False)] + + +def _claimed_post_call_pipeline_data(*policy_names: str, extra_guardrails: dict[str, list[str]] | None = None): + from litellm.proxy.policy_engine.policy_registry import get_policy_registry + + step = {"guardrail": "gr-post", "on_pass": "allow", "on_fail": "block"} + get_policy_registry().load_policies( + { + policy_name: { + "guardrails": {"add": ["gr-post", *(extra_guardrails or {}).get(policy_name, [])]}, + "pipeline": {"mode": "post_call", "steps": [step]}, + } + for policy_name in policy_names + } + ) + pipeline = GuardrailPipeline(mode="post_call", steps=[PipelineStep(**step)]) + return { + "model": "m", + "messages": [{"role": "user", "content": "hi"}], + "metadata": { + "_guardrail_pipelines": [(policy_name, pipeline) for policy_name in policy_names], + "_pipeline_managed_guardrails": {"gr-post"}, + "applied_policies": list(policy_names), + "applied_guardrails": ["gr-post", *(g for gs in (extra_guardrails or {}).values() for g in gs)], + "policy_sources": {policy_name: "model:m" for policy_name in policy_names}, + }, + } + + +@pytest.fixture +def clear_policy_registry(): + from litellm.proxy.policy_engine.policy_registry import get_policy_registry + + yield + get_policy_registry().clear() + + +@pytest.mark.asyncio +async def test_pending_background_response_withdraws_the_deferred_policy_claims( + proxy_logging, make_user_api_key_auth, monkeypatch, clear_policy_registry +): + monkeypatch.setattr(litellm, "callbacks", _output_blocking_callbacks({})) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _claimed_post_call_pipeline_data("response-governance") + + out = await proxy_logging.post_call_success_hook( + data=data, response=_background_response("queued"), user_api_key_dict=make_user_api_key_auth() + ) + + assert out.status == "queued" + assert "applied_policies" not in data["metadata"] + assert "policy_sources" not in data["metadata"] + assert "applied_guardrails" not in data["metadata"] + + +@pytest.mark.asyncio +async def test_pending_background_response_keeps_the_claim_of_a_policy_that_runs_outside_its_pipeline( + proxy_logging, make_user_api_key_auth, monkeypatch, clear_policy_registry +): + monkeypatch.setattr(litellm, "callbacks", _output_blocking_callbacks({})) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _claimed_post_call_pipeline_data( + "input-and-output-governance", "response-governance", extra_guardrails={"input-and-output-governance": ["gr-pre"]} + ) + + await proxy_logging.post_call_success_hook( + data=data, response=_background_response("in_progress"), user_api_key_dict=make_user_api_key_auth() + ) + + assert data["metadata"]["applied_policies"] == ["input-and-output-governance"] + assert data["metadata"]["applied_guardrails"] == ["gr-pre"] + assert data["metadata"]["policy_sources"] == {"input-and-output-governance": "model:m"} + + +@pytest.mark.asyncio +async def test_retrieved_background_response_keeps_the_policy_claim_once_its_pipeline_ran( + proxy_logging, make_user_api_key_auth, monkeypatch, clear_policy_registry +): + monkeypatch.setattr(litellm, "callbacks", _output_passing_callbacks()) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None, raising=False) + data = _claimed_post_call_pipeline_data("response-governance") + + await proxy_logging.post_call_success_hook( + data=data, response=_background_response("completed", text="fine"), user_api_key_dict=make_user_api_key_auth() + ) + + assert data["metadata"]["applied_policies"] == ["response-governance"] + assert data["metadata"]["policy_sources"] == {"response-governance": "model:m"} + assert data["metadata"]["applied_guardrails"] == ["gr-post"] + + @pytest.mark.asyncio async def test_pre_call_hook_stays_quiet_on_background_request_with_post_call_pipeline( proxy_logging, make_user_api_key_auth, monkeypatch, caplog From d36e032241490a5a75063425a99da4ccdc85548a Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:00:14 -0700 Subject: [PATCH 110/223] fix(proxy): initialize string success/failure callbacks at startup after config load (#38226) * fix(proxy): eagerly initialize string callbacks Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(proxy): materialize string callbacks after load_config so later litellm_settings keys are applied Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(proxy): drop casts when snapshotting string callbacks so LIT006 stays at base Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: yucheng --- litellm/proxy/utils.py | 11 +++++ tests/test_litellm/proxy/test_proxy_server.py | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index ddf31cb1d8a..75d7972c621 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -177,6 +177,9 @@ from litellm.types.mcp import ( ) from litellm.types.proxy.policy_engine.pipeline_types import PipelineExecutionResult from litellm.types.utils import LLMResponseTypes, LoggedLiteLLMParams +from litellm.utils import ( + _add_custom_logger_callback_to_specific_event, # pyright: ignore[reportPrivateUsage] # only string-to-logger helper +) if TYPE_CHECKING: from mcp.types import CallToolResult @@ -857,6 +860,14 @@ class ProxyLogging: litellm.logging_callback_manager.add_litellm_async_success_callback(callback) litellm.logging_callback_manager.add_litellm_async_failure_callback(callback) + # Runs after load_config applied every litellm_settings key: logger __init__s read e.g. s3_callback_params + success_callbacks: Final = tuple(cb for cb in litellm.success_callback if isinstance(cb, str)) + failure_callbacks: Final = tuple(cb for cb in litellm.failure_callback if isinstance(cb, str)) + for callback in success_callbacks: + _add_custom_logger_callback_to_specific_event(callback, "success") + for callback in failure_callbacks: + _add_custom_logger_callback_to_specific_event(callback, "failure") + async def update_request_status(self, litellm_call_id: str, status: Literal["success", "fail"]): # only use this if slack alerting is being used if self.alerting is None: diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index 937e4f15741..6a901c5e7de 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -3166,6 +3166,47 @@ async def test_custom_ui_sso_sign_in_handler_config_loading(): os.unlink(config_file_path) +@pytest.mark.asyncio +async def test_startup_initializes_string_callbacks_after_all_litellm_settings_load(tmp_path, monkeypatch): + from litellm.integrations.s3_v2 import S3Logger + from litellm.litellm_core_utils import litellm_logging + from litellm.proxy.proxy_server import ProxyConfig + from litellm.proxy.utils import ProxyLogging + + config_file = tmp_path / "config.yaml" + config_file.write_text( + "model_list: []\n" + "litellm_settings:\n" + " success_callback:\n" + " - s3_v2\n" + " failure_callback:\n" + " - s3_v2\n" + " s3_callback_params:\n" + " s3_bucket_name: ordering-regression-bucket\n" + " s3_region_name: us-west-2\n" + ) + + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "callbacks", []) + monkeypatch.setattr(litellm, "s3_callback_params", None) + monkeypatch.setattr(litellm_logging, "_in_memory_loggers", []) + + await ProxyConfig().load_config(router=MagicMock(), config_file_path=str(config_file)) + ProxyLogging(user_api_key_cache=MagicMock())._init_litellm_callbacks(llm_router=None) + + success_loggers = [cb for cb in litellm._async_success_callback if isinstance(cb, S3Logger)] + failure_loggers = [cb for cb in litellm._async_failure_callback if isinstance(cb, S3Logger)] + assert len(success_loggers) == 1 + assert len(failure_loggers) == 1 + assert success_loggers[0].s3_bucket_name == "ordering-regression-bucket" + assert success_loggers[0].s3_region_name == "us-west-2" + assert "s3_v2" not in litellm.success_callback + assert "s3_v2" not in litellm.failure_callback + + @pytest.mark.asyncio async def test_load_config_max_budget_env_var_coerced_to_float(tmp_path, monkeypatch): """ From 6a425a5cc59ce67f3860ac04f752cf61340e1028 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:08:26 -0700 Subject: [PATCH 111/223] fix(responses): record spend for native Responses API WebSocket sessions (#38856) * fix(responses): record spend for native Responses API WebSocket sessions Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(responses): bill usage from response.incomplete WebSocket turns A turn cut short by max_output_tokens ends in response.incomplete, which OpenAI bills but the processor only read response.completed, so those sessions still logged zero spend Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(responses): hoist websocket usage test imports to module scope Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(responses): price websocket sessions through the standard cost path The realtime completion_cost branch skips cost_discount_config and cost_margin_config, so a native Responses WebSocket session was priced differently from the same usage over HTTP /v1/responses. Drop the explicit widening so the LiteLLMRealtimeStreamLoggingObject built by normalize_logging_result flows through the generic usage path, and pin WS == HTTP cost under a 50% provider discount in the regression test Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci: rerun proxy-infra after flaky test_check_migration process tree test Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: yucheng --- litellm/cost_calculator.py | 40 ++++++++ litellm/litellm_core_utils/litellm_logging.py | 12 +++ litellm/types/utils.py | 1 + .../test_litellm_logging.py | 92 ++++++++++++++++++- 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 9a9d2ceda03..c9c7df4d75e 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -2414,6 +2414,46 @@ class RealtimeAPITokenUsageProcessor(BaseTokenUsageProcessor): ) +_RESPONSES_WS_BILLABLE_EVENT_TYPES: Final = frozenset({"response.completed", "response.incomplete"}) + + +class _ResponsesWsEventResponse(BaseModel): + usage: Mapping[str, object] | None = None + + +class _ResponsesWsEvent(BaseModel): + type: str = "" + response: _ResponsesWsEventResponse | None = None + + +class ResponsesWebSocketTokenUsageProcessor(BaseTokenUsageProcessor): + @staticmethod + def collect_usage_from_responses_ws_results( + results: Sequence[Mapping[str, object]], + ) -> tuple[Usage, ...]: + events: Final = tuple(_ResponsesWsEvent.model_validate(result) for result in results) + return tuple( + ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( # pyright: ignore[reportPrivateUsage] # same shared transform the realtime processor uses + event.response.usage + ) + for event in events + if event.type in _RESPONSES_WS_BILLABLE_EVENT_TYPES + and event.response is not None + and event.response.usage is not None + ) + + @staticmethod + def collect_and_combine_usage_from_responses_ws_results( + results: Sequence[Mapping[str, object]], + ) -> Usage: + collected_usage_objects: Final = ResponsesWebSocketTokenUsageProcessor.collect_usage_from_responses_ws_results( + results + ) + return ResponsesWebSocketTokenUsageProcessor.combine_usage_objects( + list(collected_usage_objects) # mutable-ok: combine_usage_objects requires a list parameter + ) + + _TRANSCRIPTION_COMPLETED_EVENT_TYPE: Final = "conversation.item.input_audio_transcription.completed" diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index ca2cca5360f..e3c94866985 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -48,6 +48,7 @@ from litellm.constants import ( ) from litellm.cost_calculator import ( RealtimeAPITokenUsageProcessor, + ResponsesWebSocketTokenUsageProcessor, _select_model_name_for_cost_calc, ) from litellm.exceptions import ( @@ -2028,6 +2029,17 @@ class Logging(LiteLLMLoggingBaseClass): results=result, ) + elif self.call_type == CallTypes.aresponses_websocket.value and isinstance(result, list): # pyright: ignore[reportUnknownMemberType] # Logging.call_type is untyped + combined_ws_usage: Final = ( + ResponsesWebSocketTokenUsageProcessor.collect_and_combine_usage_from_responses_ws_results( + results=result # pyright: ignore[reportUnknownArgumentType] # raw event dicts from the WS stream + ) + ) + logging_result = LiteLLMRealtimeStreamLoggingObject( + usage=combined_ws_usage, + results=result, # pyright: ignore[reportUnknownArgumentType] # raw event dicts from the WS stream + ) + elif ( self.call_type == CallTypes.llm_passthrough_route.value or self.call_type == CallTypes.allm_passthrough_route.value diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 9b5fb08a45f..40a6d77482f 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -580,6 +580,7 @@ CallTypesLiteral = Literal[ "search", "asearch", "_arealtime", + "_aresponses_websocket", "create_batch", "acreate_batch", "create_file", diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 0fdca755685..b6366bc803e 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -22,7 +22,13 @@ from litellm.litellm_core_utils.litellm_logging import ( _get_status_fields, set_callbacks, ) -from litellm.types.utils import ModelResponse, TextCompletionResponse +from litellm.types.llms.openai import ResponseAPIUsage, ResponsesAPIResponse +from litellm.types.utils import ( + CallTypes, + LiteLLMRealtimeStreamLoggingObject, + ModelResponse, + TextCompletionResponse, +) @pytest.fixture @@ -6393,6 +6399,90 @@ async def test_prompt_hook_injection_marker_recorded_for_every_surface(logging_o assert pre_choice["metadata"]["litellm_gateway_injected_cache"] == "" +def _responses_ws_logging_obj() -> LitellmLogging: + return LitellmLogging( + model="gpt-4o", + messages=[], + stream=False, + call_type=CallTypes.aresponses_websocket.value, + start_time=time.time(), + litellm_call_id="responses-ws-usage-test", + function_id="responses-ws-usage-test", + ) + + +def test_normalize_logging_result_extracts_usage_for_responses_websocket(monkeypatch): + """LIT-6512: native /v1/responses WebSocket sessions logged $0 spend because the usage + carried by stored response.completed events was never extracted. The session must cost + exactly what the same usage costs over HTTP /v1/responses, discounts included.""" + monkeypatch.setattr(litellm, "cost_discount_config", {"openai": 0.5}) + logging_obj = _responses_ws_logging_obj() + events = [ + {"type": "response.created", "response": {}}, + { + "type": "response.completed", + "response": {"usage": {"input_tokens": 100, "output_tokens": 40, "total_tokens": 140}}, + }, + { + "type": "response.completed", + "response": {"usage": {"input_tokens": 60, "output_tokens": 10, "total_tokens": 70}}, + }, + ] + + normalized = logging_obj.normalize_logging_result(result=events) + + assert isinstance(normalized, LiteLLMRealtimeStreamLoggingObject) + assert normalized.usage.prompt_tokens == 160 + assert normalized.usage.completion_tokens == 50 + + ws_cost = litellm.completion_cost( + completion_response=normalized, + model="gpt-4o", + call_type=CallTypes.aresponses_websocket.value, + custom_llm_provider="openai", + ) + http_cost = litellm.completion_cost( + completion_response=ResponsesAPIResponse( + id="resp-6512", + created_at=1700000000, + output=[], + usage=ResponseAPIUsage(input_tokens=160, output_tokens=50, total_tokens=210), + ), + model="gpt-4o", + call_type=CallTypes.aresponses.value, + custom_llm_provider="openai", + ) + assert ws_cost > 0 + assert ws_cost == http_cost + + +def test_normalize_logging_result_bills_incomplete_responses_websocket_turns(): + """LIT-6512: a turn cut short by max_output_tokens ends in response.incomplete, which + OpenAI bills, so its usage counts toward the session like a completed turn.""" + events = [ + { + "type": "response.created", + "response": {"usage": {"input_tokens": 999, "output_tokens": 999, "total_tokens": 1998}}, + }, + { + "type": "response.incomplete", + "response": {"usage": {"input_tokens": 15, "output_tokens": 16, "total_tokens": 31}}, + }, + { + "type": "response.completed", + "response": {"usage": {"input_tokens": 40, "output_tokens": 4, "total_tokens": 44}}, + }, + {"type": "response.failed", "response": {"usage": None}}, + ] + + normalized = _responses_ws_logging_obj().normalize_logging_result(result=events) + + assert isinstance(normalized, LiteLLMRealtimeStreamLoggingObject) + assert normalized.usage.prompt_tokens == 55 + assert normalized.usage.completion_tokens == 20 + assert normalized.usage.total_tokens == 75 + + def test_get_standard_logging_object_payload_reads_overhead_from_logging_obj_for_dict_results(logging_obj): """LIT-5466: /v1/messages returns a plain dict with no _hidden_params, so the overhead recorded on the logging object must reach hidden_params.litellm_overhead_time_ms (SpendLogs).""" From 550e733bf35ac862970cfebeed872e45f539bc7d Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:09:43 -0700 Subject: [PATCH 112/223] perf(spend-tracking): recover key identity in one bounded spend-log pass with a per-worker cache The spend-log lookup for permanently unresolvable digests is back to a single DISTINCT ON scan over the requested window, keeping only rows that carry an alias, user, or team so a newer nameless row cannot hide an older named one. Results and misses are cached per worker for ten minutes keyed by digest and window, failed queries are not cached, and JWT rows keyed hashed-jwt- now pass the digest gate. Tests cover the JWT gate, cache reuse and partial misses, window changes, error handling, the with-window guard, and the daily activity wiring --- litellm/constants.py | 2 + .../spend_tracking/key_metadata_recovery.py | 105 +++++++++++++----- .../test_common_daily_activity.py | 74 +++++++++++- .../test_key_metadata_recovery.py | 99 ++++++++++++++++- 4 files changed, 248 insertions(+), 32 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index 8ef9523a60a..c1896573eb5 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1763,6 +1763,8 @@ LITELLM_SETTINGS_SAFE_DB_OVERRIDES: Final = [ SPECIAL_LITELLM_AUTH_TOKEN: Final = ["ui-token"] DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)) DEFAULT_ACCESS_GROUP_CACHE_TTL: Final = int(os.getenv("DEFAULT_ACCESS_GROUP_CACHE_TTL", 600)) +SPEND_LOG_KEY_METADATA_CACHE_TTL: Final = int(os.getenv("SPEND_LOG_KEY_METADATA_CACHE_TTL", "600")) +SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS: Final = int(os.getenv("SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS", "10000")) # Short TTL for negative MCP access-group existence lookups. Keeps unauthenticated # callers from forcing a DB query per request for unknown names, while bounding # staleness so a transient DB error (which surfaces as an empty list) cannot diff --git a/litellm/proxy/spend_tracking/key_metadata_recovery.py b/litellm/proxy/spend_tracking/key_metadata_recovery.py index e207f226255..7212d8b6acb 100644 --- a/litellm/proxy/spend_tracking/key_metadata_recovery.py +++ b/litellm/proxy/spend_tracking/key_metadata_recovery.py @@ -8,6 +8,8 @@ from pydantic import BaseModel, TypeAdapter from typing_extensions import ReadOnly, TypedDict from litellm._logging import verbose_proxy_logger +from litellm.caching.in_memory_cache import InMemoryCache +from litellm.constants import SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS, SPEND_LOG_KEY_METADATA_CACHE_TTL from litellm.litellm_core_utils.litellm_logging import is_valid_sha256_hash from litellm.proxy.utils import PrismaClient from litellm.repositories.user_repository import UserRepository @@ -29,23 +31,27 @@ ORDER BY token, deleted_at DESC """ _SPEND_LOG_ALIAS_SQL: Final = """ -SELECT newest.digest, newest.key_alias, newest.team_id, newest.user_id -FROM unnest($1::text[]) AS missing(digest) -CROSS JOIN LATERAL ( - SELECT - api_key AS digest, - metadata->>'user_api_key_alias' AS key_alias, - COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id, - COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id - FROM "LiteLLM_SpendLogs" - WHERE api_key = missing.digest - AND "startTime" >= $2::timestamp - AND "startTime" < $3::timestamp - ORDER BY "startTime" DESC - LIMIT 1 -) AS newest +SELECT DISTINCT ON (api_key) + api_key AS digest, + metadata->>'user_api_key_alias' AS key_alias, + COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id, + COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id +FROM "LiteLLM_SpendLogs" +WHERE api_key = ANY($1::text[]) + AND "startTime" >= $2::timestamp + AND "startTime" < $3::timestamp + AND COALESCE( + metadata->>'user_api_key_alias', + NULLIF("user", ''), + metadata->>'user_api_key_user_id', + NULLIF(team_id, ''), + metadata->>'user_api_key_team_id' + ) IS NOT NULL +ORDER BY api_key, "startTime" DESC """ +_HASHED_JWT_PREFIX: Final = "hashed-jwt-" + class KeyMetadataDict(TypedDict, total=False): key_alias: ReadOnly[str | None] @@ -62,6 +68,11 @@ class _TokenDigestRow(BaseModel): _TOKEN_DIGEST_ROWS: Final = TypeAdapter(tuple[_TokenDigestRow, ...]) +_CACHED_KEY_METADATA: Final = TypeAdapter(KeyMetadataDict) +_SPEND_LOG_METADATA_CACHE: Final = InMemoryCache( + max_size_in_memory=SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS, + default_ttl=SPEND_LOG_KEY_METADATA_CACHE_TTL, +) _EMPTY_KEY_METADATA: Final[Mapping[str, KeyMetadataDict]] = MappingProxyType({}) _EMPTY_EMAILS: Final[Mapping[str, str]] = MappingProxyType({}) @@ -179,31 +190,73 @@ async def recover_double_hashed_key_metadata( return MappingProxyType({**from_active, **from_deleted}) -async def recover_key_metadata_from_spend_logs( - prisma_client: PrismaClient, - missing_keys: AbstractSet[str], +def _is_spend_log_digest(key: str) -> bool: + return is_valid_sha256_hash(key.removeprefix(_HASHED_JWT_PREFIX)) + + +def _spend_log_cache_key(digest: str, window: tuple[datetime, datetime]) -> str: + start, end = window + return f"spend_log_key_metadata:{digest}:{start.isoformat()}:{end.isoformat()}" + + +def _cached_spend_log_metadata( + cache: InMemoryCache, + digest: str, window: tuple[datetime, datetime], -) -> Mapping[str, KeyMetadataDict]: - sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key)) - if not sha_missing: - return _EMPTY_KEY_METADATA +) -> KeyMetadataDict | None: + cached: Final[object] = cache.get_cache(_spend_log_cache_key(digest, window)) + return None if cached is None else _CACHED_KEY_METADATA.validate_python(cached) + + +async def _query_spend_log_metadata( + prisma_client: PrismaClient, + digests: AbstractSet[str], + window: tuple[datetime, datetime], +) -> Mapping[str, KeyMetadataDict] | None: start, end = window rows: Final = await _db_or_empty( - lambda: prisma_client.db.query_raw(_SPEND_LOG_ALIAS_SQL, sorted(sha_missing), start, end), + lambda: prisma_client.db.query_raw(_SPEND_LOG_ALIAS_SQL, sorted(digests), start, end), "Failed spend-log alias recovery for %d missing keys: %s", - len(sha_missing), + len(digests), ) if rows is None: - return _EMPTY_KEY_METADATA + return None return MappingProxyType( { row.digest: KeyMetadataDict(key_alias=row.key_alias, team_id=row.team_id, user_id=row.user_id) for row in _TOKEN_DIGEST_ROWS.validate_python(rows) - if row.digest in sha_missing and (row.key_alias or row.user_id or row.team_id) + if row.digest in digests and (row.key_alias or row.user_id or row.team_id) } ) +async def recover_key_metadata_from_spend_logs( + prisma_client: PrismaClient, + missing_keys: AbstractSet[str], + window: tuple[datetime, datetime], + cache: InMemoryCache = _SPEND_LOG_METADATA_CACHE, +) -> Mapping[str, KeyMetadataDict]: + digests: Final = frozenset(key for key in missing_keys if _is_spend_log_digest(key)) + if not digests: + return _EMPTY_KEY_METADATA + cached: Final = MappingProxyType( + { + digest: meta + for digest in digests + for meta in (_cached_spend_log_metadata(cache, digest, window),) + if meta is not None + } + ) + uncached: Final = digests - frozenset(cached) + fresh: Final = await _query_spend_log_metadata(prisma_client, uncached, window) if uncached else _EMPTY_KEY_METADATA + if fresh is not None: + for digest in uncached: + cache.set_cache(_spend_log_cache_key(digest, window), fresh.get(digest, KeyMetadataDict())) + return MappingProxyType( + {digest: meta for digest, meta in (*cached.items(), *(fresh or _EMPTY_KEY_METADATA).items()) if meta} + ) + + def _row_with_recovered_fields( row: Mapping[str, object], recovered: Mapping[str, KeyMetadataDict], diff --git a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py index 4c8be415204..d214ab6b5ac 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py +++ b/tests/test_litellm/proxy/management_endpoints/test_common_daily_activity.py @@ -492,7 +492,7 @@ async def test_get_api_key_metadata_recovers_double_hashed_key_via_reverse_hash( @pytest.mark.asyncio async def test_get_api_key_metadata_permanent_miss_never_pages_tokens_or_reads_spend_logs(): - """A dirty key no table can explain costs two digest lookups, never a token page walk or a SpendLogs scan.""" + """Without a spend-log window a dirty key no table can explain costs two digest lookups and never a token page walk.""" from litellm.proxy.utils import hash_token double_hashed = hash_token("b" * 64) @@ -518,6 +518,78 @@ async def test_get_api_key_metadata_permanent_miss_never_pages_tokens_or_reads_s assert all("take" not in call.kwargs and "skip" not in call.kwargs for call in token_lookups) +@pytest.mark.asyncio +async def test_get_api_key_metadata_permanent_miss_with_a_window_reads_spend_logs_once_within_it(): + from litellm.proxy.utils import hash_token + + double_hashed = hash_token("permanent-miss-with-window-6852") + window = (datetime(2024, 1, 1), datetime(2024, 1, 4)) + mock_prisma = MagicMock() + mock_prisma.db.litellm_verificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_deletedverificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_usertable.find_many = AsyncMock(return_value=[]) + mock_prisma.db.query_raw = AsyncMock(return_value=[]) + + result = await get_api_key_metadata(prisma_client=mock_prisma, api_keys={double_hashed}, spend_logs_window=window) + + assert double_hashed not in result + assert mock_prisma.db.query_raw.await_count == 3 + ((_, digests, start, end),) = [ + call.args for call in mock_prisma.db.query_raw.call_args_list if "LiteLLM_SpendLogs" in call.args[0] + ] + assert digests == [double_hashed] + assert (start, end) == window + + +@pytest.mark.asyncio +async def test_get_daily_activity_recovers_a_session_key_alias_from_spend_logs_around_the_page_dates(): + from litellm.proxy.utils import hash_token + + session_digest = hash_token("cli-session-daily-activity-6852") + records = [_daily_user_spend_record(user_id="session-user", api_key=session_digest, spend=1.5)] + mock_prisma = MagicMock() + mock_prisma.db = MagicMock() + mock_table = MagicMock() + mock_table.count = AsyncMock(return_value=len(records)) + mock_table.find_many = AsyncMock(return_value=records) + mock_prisma.db.litellm_dailyuserspend = mock_table + mock_prisma.db.litellm_verificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_deletedverificationtoken.find_many = AsyncMock(return_value=[]) + mock_prisma.db.litellm_usertable.find_many = AsyncMock( + return_value=[SimpleNamespace(user_id="session-user", user_email="session@example.com")] + ) + + async def query_raw(sql, *params): + if "LiteLLM_SpendLogs" in sql: + return [{"digest": session_digest, "key_alias": "cli-session-alias", "team_id": None, "user_id": "session-user"}] + return [] + + mock_prisma.db.query_raw = AsyncMock(side_effect=query_raw) + + result = await get_daily_activity( + prisma_client=mock_prisma, + table_name="litellm_dailyuserspend", + entity_id_field="user_id", + entity_id=None, + entity_metadata_field=None, + start_date="2024-01-01", + end_date="2024-01-01", + model=None, + api_key=None, + page=1, + page_size=1000, + ) + + key_metadata = result.results[0].breakdown.api_keys[session_digest].metadata + assert key_metadata.key_alias == "cli-session-alias" + assert key_metadata.user_email == "session@example.com" + ((_, digests, start, end),) = [ + call.args for call in mock_prisma.db.query_raw.call_args_list if "LiteLLM_SpendLogs" in call.args[0] + ] + assert digests == [session_digest] + assert (start, end) == (datetime(2023, 12, 31), datetime(2024, 1, 3)) + + def test_key_metadata_includes_recovered_user_email(): from litellm.proxy.management_endpoints.common_daily_activity import _key_metadata diff --git a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py index 05a90137e3b..3d7362b9de0 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py +++ b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py @@ -6,6 +6,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest from prisma.errors import PrismaError +from litellm.caching.in_memory_cache import InMemoryCache from litellm.proxy.spend_tracking.key_metadata_recovery import ( fill_missing_api_key_aliases, recover_double_hashed_key_metadata, @@ -240,7 +241,7 @@ async def test_recover_key_metadata_from_spend_logs_resolves_session_token_from_ [_digest_row(session_digest, "cli-session-repro-user-6852", None, "repro-user-6852")] ) - result = await recover_key_metadata_from_spend_logs(mock_prisma, {session_digest}, window) + result = await recover_key_metadata_from_spend_logs(mock_prisma, {session_digest}, window, cache=InMemoryCache()) assert result[session_digest]["key_alias"] == "cli-session-repro-user-6852" assert result[session_digest]["user_id"] == "repro-user-6852" @@ -255,7 +256,7 @@ async def test_recover_key_metadata_from_spend_logs_skips_query_when_no_missing_ mock_prisma = MagicMock() mock_prisma.db.query_raw = AsyncMock(return_value=[]) - result = await recover_key_metadata_from_spend_logs(mock_prisma, set(), window) + result = await recover_key_metadata_from_spend_logs(mock_prisma, set(), window, cache=InMemoryCache()) assert result == {} mock_prisma.db.query_raw.assert_not_called() @@ -267,7 +268,9 @@ async def test_recover_key_metadata_from_spend_logs_returns_empty_on_prisma_erro mock_prisma = MagicMock() mock_prisma.db.query_raw = AsyncMock(side_effect=PrismaError("db down")) - result = await recover_key_metadata_from_spend_logs(mock_prisma, {hash_token("cli-session-x")}, window) + result = await recover_key_metadata_from_spend_logs( + mock_prisma, {hash_token("cli-session-x")}, window, cache=InMemoryCache() + ) assert result == {} @@ -287,7 +290,7 @@ async def test_recover_key_metadata_from_spend_logs_ignores_foreign_and_all_null ] ) - result = await recover_key_metadata_from_spend_logs(mock_prisma, {wanted, all_null}, window) + result = await recover_key_metadata_from_spend_logs(mock_prisma, {wanted, all_null}, window, cache=InMemoryCache()) assert set(result) == {wanted} assert result[wanted]["key_alias"] == "kept-alias" @@ -301,8 +304,94 @@ async def test_recover_key_metadata_from_spend_logs_skips_non_sha256_keys(): mock_prisma.db.query_raw = AsyncMock(return_value=[]) result = await recover_key_metadata_from_spend_logs( - mock_prisma, {"cli-session-raw-1798", "key-hash-short"}, window + mock_prisma, {"cli-session-raw-1798", "key-hash-short"}, window, cache=InMemoryCache() ) assert result == {} mock_prisma.db.query_raw.assert_not_called() + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_accepts_hashed_jwt_digests(): + jwt_digest = f"hashed-jwt-{hash_token('jwt-subject-1')}" + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(jwt_digest, None, "team-jwt", "jwt-user")]) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {jwt_digest}, window, cache=InMemoryCache()) + + assert result[jwt_digest]["team_id"] == "team-jwt" + assert result[jwt_digest]["user_id"] == "jwt-user" + ((_, digests, _, _),) = [call.args for call in mock_prisma.db.query_raw.call_args_list] + assert digests == [jwt_digest] + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_serves_repeat_lookups_from_the_cache(): + found = hash_token("cli-session-found") + unknown = hash_token("cli-session-unknown") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + cache = InMemoryCache() + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(found, "found-alias", None, "owner-1")]) + + first = await recover_key_metadata_from_spend_logs(mock_prisma, {found, unknown}, window, cache=cache) + second = await recover_key_metadata_from_spend_logs(mock_prisma, {found, unknown}, window, cache=cache) + + assert first == second + assert set(first) == {found} + assert first[found]["key_alias"] == "found-alias" + assert mock_prisma.db.query_raw.await_count == 1 + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_only_queries_digests_the_cache_has_not_seen(): + cached_digest = hash_token("cli-session-cached") + new_digest = hash_token("cli-session-new") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + cache = InMemoryCache() + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(cached_digest, "cached-alias", None, None)]) + await recover_key_metadata_from_spend_logs(mock_prisma, {cached_digest}, window, cache=cache) + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(new_digest, "new-alias", None, None)]) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {cached_digest, new_digest}, window, cache=cache) + + assert result[cached_digest]["key_alias"] == "cached-alias" + assert result[new_digest]["key_alias"] == "new-alias" + ((_, digests, _, _),) = [call.args for call in mock_prisma.db.query_raw.call_args_list] + assert digests == [new_digest] + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_rescans_when_the_window_changes(): + digest = hash_token("cli-session-windowed") + cache = InMemoryCache() + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs([]) + await recover_key_metadata_from_spend_logs( + mock_prisma, {digest}, (datetime(2026, 9, 1), datetime(2026, 9, 4)), cache=cache + ) + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(digest, "later-alias", None, None)]) + + result = await recover_key_metadata_from_spend_logs( + mock_prisma, {digest}, (datetime(2026, 9, 7), datetime(2026, 9, 10)), cache=cache + ) + + assert result[digest]["key_alias"] == "later-alias" + assert mock_prisma.db.query_raw.await_count == 1 + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_does_not_cache_a_failed_query(): + digest = hash_token("cli-session-retry") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + cache = InMemoryCache() + mock_prisma = MagicMock() + mock_prisma.db.query_raw = AsyncMock(side_effect=PrismaError("db down")) + assert await recover_key_metadata_from_spend_logs(mock_prisma, {digest}, window, cache=cache) == {} + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(digest, "back-online", None, None)]) + + result = await recover_key_metadata_from_spend_logs(mock_prisma, {digest}, window, cache=cache) + + assert result[digest]["key_alias"] == "back-online" From 720f2ca7751b36111fb3aed8c212869a3363de3e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:11:24 -0700 Subject: [PATCH 113/223] fix(proxy): label a 408 invalid_request_error again and pin the in-route status on the files and realtime tails --- .../common_utils/openai_error_payload.py | 1 - .../common_utils/test_openai_error_payload.py | 2 +- .../test_files_endpoint.py | 46 +++++++++++++++++++ .../test_realtime_webrtc_endpoints.py | 35 ++++++++++++++ .../proxy/test_common_request_processing.py | 13 ++++++ 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/common_utils/openai_error_payload.py b/litellm/proxy/common_utils/openai_error_payload.py index 2d589871fea..89f735ee8b6 100644 --- a/litellm/proxy/common_utils/openai_error_payload.py +++ b/litellm/proxy/common_utils/openai_error_payload.py @@ -12,7 +12,6 @@ _OPENAI_ERROR_TYPE_BY_STATUS: Final[Mapping[int, str]] = MappingProxyType( { status.HTTP_401_UNAUTHORIZED: "authentication_error", status.HTTP_403_FORBIDDEN: "permission_error", - status.HTTP_408_REQUEST_TIMEOUT: "timeout_error", status.HTTP_429_TOO_MANY_REQUESTS: "rate_limit_error", } ) diff --git a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py index 3145be2d522..8b653ddfb71 100644 --- a/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py +++ b/tests/test_litellm/proxy/common_utils/test_openai_error_payload.py @@ -18,7 +18,7 @@ from litellm.proxy.common_utils.openai_error_payload import ( (401, "authentication_error"), (403, "permission_error"), (404, "invalid_request_error"), - (408, "timeout_error"), + (408, "invalid_request_error"), (422, "invalid_request_error"), (429, "rate_limit_error"), (499, "invalid_request_error"), diff --git a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py index 132b53792b0..5f1e7e1fe0c 100644 --- a/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py +++ b/tests/test_litellm/proxy/openai_files_endpoint/test_files_endpoint.py @@ -4773,3 +4773,49 @@ def test_get_file_content_reports_a_missing_managed_file_as_a_404( assert response.status_code == 404, response.text assert response.json() == _missing_managed_file_error(file_id) + + +def _setup_managed_file_stored_in_an_unknown_storage_backend( + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router +) -> None: + """Wire the content route to a managed file whose row names a storage backend the + factory does not know, which is the one in-route ProxyException on these routes.""" + from types import SimpleNamespace + + import litellm.proxy.proxy_server as ps + from litellm.llms.base_llm.files.transformation import BaseFileEndpoints + from litellm.proxy._types import LitellmUserRoles + + proxy_logging_obj = setup_proxy_logging_object(monkeypatch, llm_router) + managed_files = mocker.MagicMock(spec=BaseFileEndpoints) + managed_files.prisma_client = mocker.MagicMock() + proxy_logging_obj.proxy_hook_mapping["managed_files"] = managed_files + repository = mocker.MagicMock() + repository.table.find_first = mocker.AsyncMock( + return_value=SimpleNamespace(storage_backend="ftp", storage_url="ftp://bucket/file") + ) + monkeypatch.setattr( + "litellm.proxy.openai_files_endpoints.files_endpoints.ManagedFileRepository", lambda _prisma: repository + ) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", llm_router) + + app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth( + api_key="test-key", + user_role=LitellmUserRoles.INTERNAL_USER, + user_id="test-user", + ) + + +def test_get_file_content_keeps_the_status_of_a_rejection_raised_inside_the_route( + mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch, llm_router: Router +): + """A ProxyException raised inside the route carries its status as the string ``code``, + and the tail used to rebuild it as a 500 because it only read ``status_code``.""" + _setup_managed_file_stored_in_an_unknown_storage_backend(mocker, monkeypatch, llm_router) + + response = _call_managed_file_route("GET", f"/v1/files/{_unified_managed_file_id()}/content") + + assert response.status_code == 400, response.text + error = response.json()["error"] + assert error["message"].startswith("Storage backend error") + assert (error["type"], error["param"], error["code"]) == ("invalid_request_error", "file_id", "400") diff --git a/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py b/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py index 8cc5994dc81..7436cf84fec 100644 --- a/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py +++ b/tests/test_litellm/proxy/realtime_endpoints/test_realtime_webrtc_endpoints.py @@ -1241,3 +1241,38 @@ def test_realtime_calls_upstream_rejection_answers_an_openai_typed_error( assert response.status_code == 404 assert (response.json()["error"]["type"], response.json()["error"]["param"]) == ("invalid_request_error", None) + + +def test_transcription_sessions_rejection_answers_an_openai_typed_error( + proxy_app: FastAPI, + mock_add_litellm_data: Callable[..., Awaitable[object]], + mock_pre_call_hook: Callable[..., Awaitable[object]], + monkeypatch: pytest.MonkeyPatch, +): + """A model the router cannot serve surfaces as a bare HTTPException, which this tail + used to relabel with the literal string "None" for both type and param.""" + + async def failing_route_request(*args: object, **kwargs: object) -> None: + raise HTTPException( + status_code=400, + detail={"error": "realtime: Invalid model name passed in model=no-such-transcribe"}, + ) + + proxy_logging = MagicMock() + proxy_logging.pre_call_hook = AsyncMock(side_effect=mock_pre_call_hook) + proxy_logging.post_call_failure_hook = AsyncMock() + monkeypatch.setattr("litellm.proxy.proxy_server.route_request", failing_route_request) + monkeypatch.setattr("litellm.proxy.proxy_server.add_litellm_data_to_request", mock_add_litellm_data) + monkeypatch.setattr("litellm.proxy.proxy_server.proxy_logging_obj", proxy_logging) + proxy_app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(user_id="test-user") + try: + response = TestClient(proxy_app, raise_server_exceptions=False).post( + "/v1/realtime/transcription_sessions", + headers={"Authorization": "Bearer sk-test-master-key"}, + json={"input_audio_transcription": {"model": "no-such-transcribe"}}, + ) + finally: + proxy_app.dependency_overrides.pop(user_api_key_auth, None) + + assert response.status_code == 400 + assert (response.json()["error"]["type"], response.json()["error"]["param"]) == ("invalid_request_error", None) diff --git a/tests/test_litellm/proxy/test_common_request_processing.py b/tests/test_litellm/proxy/test_common_request_processing.py index bfae42f64f1..be666607823 100644 --- a/tests/test_litellm/proxy/test_common_request_processing.py +++ b/tests/test_litellm/proxy/test_common_request_processing.py @@ -2202,6 +2202,19 @@ class TestGuardrailBlockErrorPayloadNeverStringifiesNone: assert frame["error"]["param"] is None assert frame["error"]["code"] == "400" + def test_a_streaming_frame_keeps_the_status_a_proxy_exception_was_raised_with(self): + """ProxyException stores its status as the string ``code``, so a 429 raised before the + first chunk used to reach the SSE frame as a 500.""" + from litellm.proxy._types import ProxyException + from litellm.proxy.common_request_processing import sse_error_payload + + error_status, error_obj = sse_error_payload( + ProxyException(message="Rate limit reached", type="rate_limit_error", param=None, code=429) + ) + + assert error_status == 429 + assert (error_obj["type"], error_obj["code"]) == ("rate_limit_error", "429") + @pytest.mark.parametrize( "status_code, expected_type", [ From 8ce3fe4fcf5ba43278c4eb7ec09c58df513d36c5 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:13:36 -0700 Subject: [PATCH 114/223] style: give the header slot rebuilds and the in-place slot write their lint reasons --- litellm/proxy/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 1972d92b610..e48bd3851fb 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -551,9 +551,11 @@ def _without_names( claimed: Final = bucket.get(slot) if not isinstance(claimed, list): return - remaining: Final = [name for name in claimed if name not in names] + remaining: Final = [ # mutable-ok: the slot stays a list, the shape every applied_* header writer appends to + name for name in claimed if name not in names + ] if remaining: - bucket[slot] = remaining + bucket[slot] = remaining # rebind-ok: the slot lives in the shared request-state dict, rewritten in place else: bucket.pop(slot) @@ -574,7 +576,9 @@ def _withdraw_deferred_claims( sources: Final = bucket.get("policy_sources") if not isinstance(sources, dict): return - remaining_sources: Final = {name: reason for name, reason in sources.items() if name not in withdrawn_policies} + remaining_sources: Final = { # mutable-ok: policy_sources stays a dict, the shape its writer updates in place + name: reason for name, reason in sources.items() if name not in withdrawn_policies + } if remaining_sources: bucket["policy_sources"] = remaining_sources else: From a5cfe625e395c4edb401cf0d2daec3ccb686ed82 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 8 Sep 2026 13:14:52 -0700 Subject: [PATCH 115/223] refactor: trim the comments this PR added Cuts the explanatory comments and docstrings added here down to one line each, or removes them where the code already says it. Restores the four pre-existing docstrings this PR had reworded to their original text; the one remaining edit to existing text is TierDefinition.description, whose hardcoded tier list would otherwise misstate that a tier named NON_REASONING may also omit its description. --- .../complexity_router/complexity_router.py | 7 +- .../complexity_router/config.py | 20 +--- .../router_strategy/test_complexity_router.py | 91 ++++++------------- .../add_model/ClassificationMethodConfig.tsx | 7 +- .../add_model/ComplexityRouterConfig.tsx | 15 +-- .../add_model/NonReasoningTierToggle.tsx | 6 -- .../build_complexity_router_config.ts | 3 +- .../src/components/add_model/tier_rows.ts | 6 +- 8 files changed, 41 insertions(+), 114 deletions(-) diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index c40405ecf0f..66c328c49ea 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -100,12 +100,7 @@ else: class TierClassification(BaseModel): - """Structured response schema for the LLM-based complexity classifier. - - The four-tier ladder, which is what a router that did not opt into NON_REASONING sends. The - enum actually put on the wire is rebuilt per router from `classifier_wire_labels`, so a - five-tier or renamed ladder widens it there rather than here. - """ + """Structured response schema for the LLM-based complexity classifier.""" tier: Literal["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 1d03c56050a..9cd4ce1d2fa 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -56,9 +56,6 @@ DEFAULT_CLASSIFICATION_RUBRIC: Final[ClassificationRubric] = ClassificationRubri LLM_CLASSIFIER_TYPES: Final[frozenset[str]] = frozenset({"llm", "heuristic_first", "hybrid"}) -# Excludes NON_REASONING so an existing router keeps the ladder, rubric and wire labels it already -# has, and so heuristic_v2 keeps mapping onto the four classes its artifact is trained on. Anywhere -# `enable_non_reasoning_tier` can reach, read the ladder off the config instead. TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( ComplexityTier.SIMPLE, ComplexityTier.MEDIUM, @@ -73,7 +70,6 @@ NON_REASONING_TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( def tier_severity_order(non_reasoning_enabled: bool) -> tuple[ComplexityTier, ...]: - """The built-in ladder for one router, tier 0 included only when it opted in.""" return NON_REASONING_TIER_SEVERITY_ORDER if non_reasoning_enabled else TIER_SEVERITY_ORDER @@ -1524,8 +1520,7 @@ class ComplexityRouterConfig(BaseModel): return self.classifier_type in LLM_CLASSIFIER_TYPES def active_tier_severity_order(self) -> tuple[ComplexityTier, ...]: - """This router's built-in ladder, ascending. Meaningless for a custom tier set, whose - severity order is tier_definitions list order over names that are not enum members.""" + """This router's built-in ladder, ascending; not meaningful for a custom tier set.""" return tier_severity_order(self.enable_non_reasoning_tier) def tier_names(self) -> tuple[str, ...]: @@ -1626,12 +1621,7 @@ class ComplexityRouterConfig(BaseModel): @model_validator(mode="after") def _validate_non_reasoning_tier(self) -> "ComplexityRouterConfig": - """Gate the opt-in fifth tier on the two things that make it reachable and routable. - - The heuristic scorers cannot emit it (the v1 score ladder has no rung below simple_medium - and the v2 artifact is trained on four classes), so a router whose classifier can never - return the tier would pay for a rubric bullet and a configured pool that no request reaches. - """ + """Require a classifier that can emit the opt-in tier and a model to route it to.""" non_reasoning_key: Final = ComplexityTier.NON_REASONING.value if not self.enable_non_reasoning_tier: if not self.has_custom_tiers and non_reasoning_key in self.tiers: @@ -1834,13 +1824,11 @@ class ComplexityRouterConfig(BaseModel): return self.tier_labels.get(tier, "").strip() or tier.value def labeled_tiers(self) -> tuple[tuple[ComplexityTier, str], ...]: - """Every active tier paired with its display name, in ascending severity order.""" + """Every tier paired with its display name, in ascending severity order.""" return tuple((tier, self.tier_label(tier)) for tier in self.active_tier_severity_order()) def tier_for_label(self, label: str) -> ComplexityTier | None: - """Resolve a display name back to its active tier, case-insensitively, then canonical - names. A tier this router did not opt into resolves to None, so a classifier naming - NON_REASONING on a four-tier router is an unparseable reply rather than a fifth rung.""" + """Resolve a display name back to its tier, case-insensitively, then canonical names.""" folded: Final = label.strip().casefold() labeled: Final = self.labeled_tiers() return next( diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index ce9cd5d3b7d..2732694ab7e 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -1512,6 +1512,7 @@ class TestRouterComplexityDeploymentMethods: def test_the_shipped_rubric_and_default_prompt_stay_free(self) -> None: """Only an operator-written prompt is gated: picking a shipped rubric preset, or writing no prompt at all, leaves a router unmetered, so several of them register under a ceiling of one.""" + def rubric(model_name: str, model_id: str, preset: str | None) -> dict[str, object]: llm_config: dict[str, object] = {"model": "gpt-4o-mini"} if preset is not None: @@ -1648,6 +1649,7 @@ class TestRouterComplexityDeploymentMethods: def test_renaming_built_in_tiers_is_not_a_custom_tier_set(self) -> None: """tier_labels renames the built-in ladder without defining one, so it stays ungated: two such routers register under a ceiling of one.""" + def labeled(model_name: str, model_id: str) -> dict[str, object]: row = self._router_row(model_name, model_id, "heuristic") row["litellm_params"]["complexity_router_config"]["tier_labels"] = {"SIMPLE": "Cheap", "MEDIUM": "Standard"} @@ -2521,9 +2523,7 @@ class TestLLMClassifier: assert outcome.classifier_cost == pytest.approx(1.35e-05) @pytest.mark.asyncio - async def test_aclassify_timeout_does_not_inherit_router_retries_or_fallbacks( - self, llm_classifier_config - ): + async def test_aclassify_timeout_does_not_inherit_router_retries_or_fallbacks(self, llm_classifier_config): real_router = Router( model_list=[ { @@ -2566,9 +2566,7 @@ class TestLLMClassifier: assert real_router.total_calls["openai/mock-backup-classifier"] == 0 @pytest.mark.asyncio - async def test_aclassify_enforces_total_classifier_deadline( - self, mock_router_instance, llm_classifier_config - ): + async def test_aclassify_enforces_total_classifier_deadline(self, mock_router_instance, llm_classifier_config): cancelled = asyncio.Event() async def slow_classifier(**_kwargs: object) -> None: @@ -12415,9 +12413,7 @@ class TestTierHealthFailover: llm_provider="", ) filtered = (*cooling, *blocked, *excluded) - healthy = [ - {"model_name": model, "model_info": {"id": i}} for i in ids_by_model[model] if i not in filtered - ] + healthy = [{"model_name": model, "model_info": {"id": i}} for i in ids_by_model[model] if i not in filtered] if not healthy: raise RouterRateLimitError( model=model, cooldown_time=60.0, enable_pre_call_checks=False, cooldown_list=[] @@ -12846,9 +12842,7 @@ class TestTierHealthFailover: assert all(probed is not request_kwargs for probed in router.litellm_router_instance.probed_kwargs) @pytest.mark.asyncio - async def test_a_peer_whose_every_deployment_is_over_its_rpm_is_not_a_failover_target( - self, mock_router_instance - ): + async def test_a_peer_whose_every_deployment_is_over_its_rpm_is_not_a_failover_target(self, mock_router_instance): """RPM exhaustion is its own verdict from the owner (RouterRateLimitErrorBasic). A peer in that state would be rejected downstream, so it cannot be the substitute.""" from litellm.types.router import RouterRateLimitErrorBasic @@ -12881,9 +12875,7 @@ class TestTierHealthFailover: assert {r.model for r in results} == {"live-c"} @pytest.mark.asyncio - async def test_the_probe_forwards_input_so_window_checks_run_on_input_only_surfaces( - self, mock_router_instance - ): + async def test_the_probe_forwards_input_so_window_checks_run_on_input_only_surfaces(self, mock_router_instance): """The Responses API carries its prompt as `input`, never as messages. The owner only runs its context-window pre-call check when one of them is present, so dropping `input` would silently skip window filtering on that whole surface.""" @@ -12909,9 +12901,7 @@ class TestTierHealthFailover: ), "the eligibility probe must forward `input` to the owner" @pytest.mark.asyncio - async def test_a_group_the_router_has_no_deployment_for_is_not_a_failover_target( - self, mock_router_instance - ): + async def test_a_group_the_router_has_no_deployment_for_is_not_a_failover_target(self, mock_router_instance): """The owner answers an unconfigured group with BadRequestError. Reading that as live would both skip failover off it and let it be chosen as a substitute.""" router = self._router( @@ -13100,9 +13090,7 @@ class TestClassifierVision: routed as default_fallback on text the request never contained. """ router = self._router(mock_router_instance, vision={"enabled": True}) - response = await router.async_pre_routing_hook( - model="m", request_kwargs={}, messages=self._turn(IMG_PART) - ) + response = await router.async_pre_routing_hook(model="m", request_kwargs={}, messages=self._turn(IMG_PART)) assert response.routing_decision["cause"] == "llm_classifier" assert response.model == "t-complex" assert [block["type"] for block in self._classifier_user_content(mock_router_instance)] == [ @@ -13113,9 +13101,7 @@ class TestClassifierVision: @pytest.mark.asyncio async def test_image_only_turn_still_falls_back_when_vision_is_off(self, mock_router_instance): router = self._router(mock_router_instance, vision={"enabled": False}) - response = await router.async_pre_routing_hook( - model="m", request_kwargs={}, messages=self._turn(IMG_PART) - ) + response = await router.async_pre_routing_hook(model="m", request_kwargs={}, messages=self._turn(IMG_PART)) assert response.routing_decision["cause"] == "default_fallback" mock_router_instance.acompletion.assert_not_awaited() @@ -13185,9 +13171,7 @@ class TestClassifierVision: makes the image the only variable; a margin loose enough to leave the score undecided would pass whether or not the guard exists. """ - router = self._router( - mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra - ) + router = self._router(mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra) response = await router.async_pre_routing_hook( model="m", request_kwargs={}, messages=self._turn({"type": "text", "text": "what is this"}, IMG_PART) ) @@ -13201,9 +13185,7 @@ class TestClassifierVision: self, mock_router_instance, classifier_type, extra, short_circuit_cause ): """The negative class: same router, same text, no image, and the scorer still decides.""" - router = self._router( - mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra - ) + router = self._router(mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra) response = await router.async_pre_routing_hook( model="m", request_kwargs={}, messages=[{"role": "user", "content": "what is this"}] ) @@ -13225,13 +13207,7 @@ NON_REASONING_TIERS: Final = { class TestNonReasoningTier: - """The opt-in fifth built-in tier below SIMPLE. - - Two properties carry the feature. A router that did not opt in must be byte-identical to one - built before the tier existed, because the tier set feeds the classifier rubric, the wire enum, - and the savings baseline, all of which move live routing decisions and spend. A router that did - opt in must be able to actually reach the tier and escalate off it. - """ + """The opt-in fifth built-in tier below SIMPLE: inert unless enabled, reachable when it is.""" @staticmethod def _router(mock_router_instance, **overrides) -> ComplexityRouter: @@ -13249,8 +13225,7 @@ class TestNonReasoningTier: ) def test_ladder_gains_a_rung_below_simple_only_when_enabled(self): - """Tier 0 sits at the bottom. Anywhere else and escalation, the savings baseline, and - heuristic_first's 'highest tier' check would all read a different ladder.""" + """Tier 0 sits at the bottom; anywhere else and escalation and the baseline shift.""" enabled: Final = ComplexityRouterConfig( tiers=dict(NON_REASONING_TIERS), enable_non_reasoning_tier=True, @@ -13261,8 +13236,7 @@ class TestNonReasoningTier: assert ComplexityRouterConfig().tier_names() == ("SIMPLE", "MEDIUM", "COMPLEX", "REASONING") def test_default_router_is_unchanged_by_the_tier_existing(self): - """The regression that matters for every already-deployed router: the enum grew a member, - and nothing a four-tier router sends or resolves may change because of it.""" + """The enum grew a member, and nothing a four-tier router sends or resolves may change.""" default: Final = ComplexityRouterConfig() assert default.enable_non_reasoning_tier is False assert "NON_REASONING" not in DEFAULT_COMPLEXITY_CONFIG.tiers @@ -13272,8 +13246,7 @@ class TestNonReasoningTier: @pytest.mark.parametrize("preset", tuple(ClassificationRubric)) def test_rubric_gains_the_bullet_only_when_enabled(self, preset): - """Every preset renders one bullet per active tier, so an unset toggle must leave all four - shipped rubrics byte-identical while an enabled one must actually describe the new tier.""" + """An unset toggle leaves every shipped rubric byte-identical; an enabled one adds a bullet.""" enabled: Final = ComplexityRouterConfig( tiers=dict(NON_REASONING_TIERS), enable_non_reasoning_tier=True, @@ -13286,18 +13259,18 @@ class TestNonReasoningTier: assert "- NON_REASONING" not in off def test_enabled_router_puts_the_tier_on_the_classifier_wire(self, mock_router_instance): - """The response schema's enum is what the classifier may return; without the new label the - tier would be unreachable no matter what the rubric says.""" + """The schema enum bounds what the classifier may return, whatever the rubric says.""" router: Final = self._router(mock_router_instance) enum: Final = router._classifier_response_format["json_schema"]["schema"]["properties"]["tier"]["enum"] assert enum == ["NON_REASONING", "SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] @pytest.mark.asyncio async def test_classifier_verdict_routes_to_the_tier_model(self, mock_router_instance): - """End to end on the LLM path: the classifier names the tier and the request lands on that - tier's model with the decision recording it.""" + """The classifier names the tier and the request lands on that tier's model.""" mock_router_instance.acompletion = AsyncMock(return_value=_llm_response('{"tier": "NON_REASONING"}')) - router: Final = self._router(mock_router_instance, tiers={**NON_REASONING_TIERS, "NON_REASONING": "cheap-relay"}) + router: Final = self._router( + mock_router_instance, tiers={**NON_REASONING_TIERS, "NON_REASONING": "cheap-relay"} + ) response = await router.async_pre_routing_hook( model="test-non-reasoning-router", request_kwargs={}, @@ -13308,9 +13281,10 @@ class TestNonReasoningTier: assert response.routing_decision["cause"] == "llm_classifier" @pytest.mark.asyncio - async def test_a_four_tier_router_ignores_a_non_reasoning_verdict(self, llm_complexity_router, mock_router_instance): - """A classifier that names the tier at a router which never opted in must be an unparseable - reply that falls back, not a silent route to a tier the operator did not configure.""" + async def test_a_four_tier_router_ignores_a_non_reasoning_verdict( + self, llm_complexity_router, mock_router_instance + ): + """Naming the tier at a router that never opted in falls back instead of routing there.""" mock_router_instance.acompletion = AsyncMock(return_value=_llm_response('{"tier": "NON_REASONING"}')) outcome = await llm_complexity_router.aclassify("relay this") assert outcome.tier != ComplexityTier.NON_REASONING @@ -13323,8 +13297,7 @@ class TestNonReasoningTier: assert router._escalate_tier(ComplexityTier.REASONING) == ComplexityTier.REASONING def test_escalation_skips_the_tier_when_unconfigured(self, mock_router_instance): - """SIMPLE must still escalate to MEDIUM rather than to the cheaper new rung, or escalation - would route below the model the caller would otherwise have received.""" + """SIMPLE still escalates to MEDIUM, so escalation never routes below the caller's model.""" router: Final = self._router( mock_router_instance, tiers={"NON_REASONING": "cheap-relay", "SIMPLE": "gpt-4o-mini", "MEDIUM": "gpt-4o"}, @@ -13332,8 +13305,7 @@ class TestNonReasoningTier: assert router._escalate_tier(ComplexityTier.SIMPLE) == ComplexityTier.MEDIUM def test_tier_zero_is_never_the_savings_baseline(self, mock_router_instance): - """Savings are measured against the hardest configured tier. If tier 0 could win that pick, - every enabled router's reported savings would invert.""" + """Savings use the hardest configured tier; tier 0 winning would invert every figure.""" assert self._router(mock_router_instance)._hardest_tier_models() == ("o1-preview",) cheap_only: Final = self._router( mock_router_instance, tiers={"NON_REASONING": "cheap-relay", "SIMPLE": "gpt-4o-mini"} @@ -13356,8 +13328,7 @@ class TestNonReasoningTier: ids=["heuristic", "heuristic_v2", "no_model"], ) def test_unreachable_or_unroutable_configs_are_rejected(self, overrides, expected): - """The toggle is refused wherever it could not do anything: the heuristic scorers cannot - emit the tier, and an unconfigured tier would fall through to the default model.""" + """Refused where it could do nothing: no scorer emits the tier, no pool routes it.""" config: Final = { "tiers": dict(NON_REASONING_TIERS), "enable_non_reasoning_tier": True, @@ -13386,9 +13357,7 @@ class TestNonReasoningTier: ) def test_heuristic_v2_predictions_never_reach_the_new_tier(self, mock_router_instance): - """The bundled artifact is trained on four classes, so its 1-based tier index must keep - mapping onto SIMPLE..REASONING. Reading the enabled ladder here would shift every - prediction down a rung and make REASONING unreachable.""" + """The four-class artifact's 1-based index must keep mapping onto SIMPLE..REASONING.""" router: Final = ComplexityRouter( model_name="v2-router", litellm_router_instance=mock_router_instance, @@ -13399,8 +13368,6 @@ class TestNonReasoningTier: ) outcome = router._classify_with_heuristic_v2("implement a distributed rate limiter under concurrency") assert outcome.tier in TIER_SEVERITY_ORDER - # One probability signal per trained class, named for the tier that class means. A ladder - # shifted by the new rung would relabel all four and lose REASONING off the end. assert tuple(signal.split(":")[1].split("=")[0] for signal in outcome.signals[1:]) == ( "simple", "medium", diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 5ec9f72c2a1..33bb7283a7e 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -236,11 +236,8 @@ const ClassifierTypeRadios: React.FC<{ ); }; -/** - * The NON_REASONING keys a classifier switch carries forward, or clears. Only the LLM classifier - * can emit the tier, and the switch is disabled elsewhere, so a flag left set under another - * classifier would be an unsaveable config the operator could not undo. - */ +/** The NON_REASONING keys a classifier switch carries forward, or clears for a classifier that + * cannot emit the tier. Leaving them set there is a config the backend refuses on save. */ export const nonReasoningTierFields = ( classifierType: ClassifierType, value: ComplexityRouterConfigValue, diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index c89c130af86..38c2f13b3f9 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -78,10 +78,7 @@ export const DEFAULT_CLASSIFICATION_MODE: ClassificationMode = "every_request"; */ export type ClassificationFrequency = ClassificationMode | "session"; -/** - * NON_REASONING is optional because it is the opt-in fifth tier: a router that never enabled it - * stores no such key, and hydrating one in would send an empty pool the backend rejects. - */ +/** NON_REASONING is optional: a router that never enabled it stores no such key. */ export type ComplexityTiers = { SIMPLE: string[]; MEDIUM: string[]; @@ -362,10 +359,7 @@ export type ComplexityTierLabels = Partial export interface ComplexityRouterConfigValue { tiers: ComplexityTiers; - /** - * Opt into the NON_REASONING tier below SIMPLE. Off means the router keeps the four-tier ladder - * it has always had, so an existing router's rubric and tier decisions cannot move under it. - */ + /** Opt into the NON_REASONING tier below SIMPLE; off keeps the four-tier ladder. */ enable_non_reasoning_tier?: boolean; custom_tier_set?: CustomTierSet; tier_labels?: ComplexityTierLabels; @@ -510,7 +504,6 @@ export const TIER_DESCRIPTIONS: Record< }, }; -/** Every built-in tier name, including the opt-in one, for label and membership checks. */ export const TIER_KEYS = Object.keys(TIER_DESCRIPTIONS) as Array; export const effectiveTierLabel = (tier: keyof ComplexityTiers, tierLabels: ComplexityTierLabels | undefined): string => @@ -523,9 +516,7 @@ export const DEFAULT_HYBRID_BOUNDARY_MARGIN = 0.03; /** * Tiers the heuristic_first threshold may name. The top tier is excluded because it would short - * circuit every request and leave the classifier unreachable, which the backend rejects. So is - * NON_REASONING, which the backend refuses alongside heuristic_first because the local scorer - * cannot produce it. + * circuit every request and leave the classifier unreachable, which the backend rejects. */ export const HEURISTIC_FIRST_MAX_TIER_KEYS = TIER_ORDER.slice(0, -1); diff --git a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx index 622a460fb79..caadd79039d 100644 --- a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx +++ b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx @@ -5,17 +5,11 @@ import { Switch } from "@/components/ui/switch"; import type { ComplexityRouterConfigValue } from "./ComplexityRouterConfig"; -/** - * The opt-in fifth tier. Only offered on the LLM classification method, matching the backend: the - * heuristic scorers cannot produce the tier, so enabling it there would buy a rubric bullet and a - * model pool that no request ever reaches. - */ const NonReasoningTierToggle: React.FC<{ value: ComplexityRouterConfigValue; onChange: (value: ComplexityRouterConfigValue) => void; available: boolean; }> = ({ value, onChange, available }) => { - // Off drops the tier's key rather than leaving the empty pool the backend rejects. const handleToggle = (enabled: boolean): void => { const { NON_REASONING: existingPool, ...keptTiers } = value.tiers; const next: ComplexityRouterConfigValue = { diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts index 2f36ec2f43d..b1edae6b78e 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.ts @@ -538,8 +538,7 @@ export const buildComplexityRouterConfig = ({ const payload: ComplexityRouterConfigPayload = { tiers, - // Only written when on, and never beside a custom tier set: the backend rejects the two - // together, and an explicit false on a four-tier router would be a key it never carried. + // The backend rejects the flag beside a custom tier set. ...(!customTierSet && enableNonReasoningTier && { enable_non_reasoning_tier: true }), ...(serializedTierModelConfigs && { tier_model_configs: serializedTierModelConfigs }), ...(defaultModel?.trim() && { default_model: defaultModel }), diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts index 289a5645b51..cd3082b9456 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts @@ -4,13 +4,9 @@ import type { TierModelParams, TierModelParamsByTier } from "./complexity_router export const TIER_ORDER: ComplexityTier[] = ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]; -/** Every built-in tier name, so a stored NON_REASONING row is recognized as built-in either way. */ export const ALL_BUILT_IN_TIERS: ComplexityTier[] = ["NON_REASONING", ...TIER_ORDER]; -/** - * The ladder one router renders, ascending. NON_REASONING is tier 0 and appears only when enabled, - * which is what keeps an existing four-tier router's form, payload, and rubric unchanged. - */ +/** The ladder one router renders, ascending; NON_REASONING appears only when enabled. */ export const tierOrderFor = (enableNonReasoningTier: boolean | undefined): ComplexityTier[] => enableNonReasoningTier ? ALL_BUILT_IN_TIERS : TIER_ORDER; From f4aef5a1db67d35f606d09f5e8bfe008181af015 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:19:35 -0700 Subject: [PATCH 116/223] fix(spend-tracking): make the spend-log metadata cache limits plain constants The ten minute TTL and the 10000 item ceiling need no env override, and the documentation env-key check flags any os.getenv read that the docs do not list --- litellm/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index c1896573eb5..9654c84c412 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1763,8 +1763,8 @@ LITELLM_SETTINGS_SAFE_DB_OVERRIDES: Final = [ SPECIAL_LITELLM_AUTH_TOKEN: Final = ["ui-token"] DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)) DEFAULT_ACCESS_GROUP_CACHE_TTL: Final = int(os.getenv("DEFAULT_ACCESS_GROUP_CACHE_TTL", 600)) -SPEND_LOG_KEY_METADATA_CACHE_TTL: Final = int(os.getenv("SPEND_LOG_KEY_METADATA_CACHE_TTL", "600")) -SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS: Final = int(os.getenv("SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS", "10000")) +SPEND_LOG_KEY_METADATA_CACHE_TTL: Final = 600 +SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS: Final = 10000 # Short TTL for negative MCP access-group existence lookups. Keeps unauthenticated # callers from forcing a DB query per request for unknown names, while bounding # staleness so a transient DB error (which surfaces as an empty list) cannot From f59354d09b642bf50d888a82b5fac5b96d34169b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:22:29 -0700 Subject: [PATCH 117/223] refactor(guardrails): patch Anthropic SSE rewrites by field The ended-stream rewriters now describe the one field they change as a frozen _SSEFieldRewrite and a single applier builds the patched event, so the handler adds no mutable-collection constructions over staging's total --- .../chat/guardrail_translation/handler.py | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index 2049866b444..cbbccac17f3 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -13,7 +13,7 @@ Pattern Overview: """ import json -from collections.abc import Callable, Mapping, Sequence +from collections.abc import Mapping, Sequence from copy import deepcopy from dataclasses import dataclass from itertools import chain, repeat @@ -161,7 +161,25 @@ class _ToolCallShape: arguments: str -_SSEEventRewriter = Callable[[Mapping[str, object]], Mapping[str, object] | None] +@dataclass(frozen=True, slots=True) +class _SSEFieldRewrite: + """One field of one nested section of a buffered SSE event, rewritten.""" + + section: str + field: str + value: object + + +class _SSEEventRewriter(Protocol): + def __call__(self, event: Mapping[str, object]) -> _SSEFieldRewrite | None: ... + + +def _rewritten_event(event: Mapping[str, object], rewrite_event: _SSEEventRewriter) -> Mapping[str, object]: + rewrite: Final = rewrite_event(event) + section: Final = None if rewrite is None else event.get(rewrite.section) + if rewrite is None or not isinstance(section, Mapping): + return event + return {**event, rewrite.section: {**section, rewrite.field: rewrite.value}} # mutable-ok: json.dumps needs a dict def _tool_call_shapes(tool_calls: Sequence[object]) -> tuple[_ToolCallShape, ...]: @@ -1253,13 +1271,13 @@ class AnthropicMessagesHandler(BaseTranslation): message and content-block framing untouched.""" replacements: Final = chain((rewritten_text,), repeat("")) - def rewrite_text_delta(event: Mapping[str, object]) -> Mapping[str, object] | None: + def rewrite_text_delta(event: Mapping[str, object]) -> _SSEFieldRewrite | None: delta: Final = event.get("delta") if event.get("type") != "content_block_delta" or not isinstance(delta, Mapping): return None if delta.get("type") != "text_delta": return None - return {**event, "delta": {**delta, "text": next(replacements)}} + return _SSEFieldRewrite("delta", "text", next(replacements)) AnthropicMessagesHandler._rewrite_ended_stream_events(responses_so_far, rewrite_text_delta) @@ -1306,22 +1324,21 @@ class AnthropicMessagesHandler(BaseTranslation): {index: chain((rewrite.arguments,), repeat("")) for index, rewrite in rewrites_by_block.items()} ) - def rewrite_tool_use(event: Mapping[str, object]) -> Mapping[str, object] | None: + def rewrite_tool_use(event: Mapping[str, object]) -> _SSEFieldRewrite | None: index: Final = event.get("index") if not isinstance(index, int) or index not in rewrites_by_block: return None match event.get("type"): case "content_block_start": - block: Final = event.get("content_block") name: Final = rewrites_by_block[index].name - if not isinstance(block, Mapping) or name is None: + if name is None: return None - return {**event, "content_block": {**block, "name": name}} + return _SSEFieldRewrite("content_block", "name", name) case "content_block_delta": delta: Final = event.get("delta") if not isinstance(delta, Mapping) or delta.get("type") != "input_json_delta": return None - return {**event, "delta": {**delta, "partial_json": next(argument_replacements[index])}} + return _SSEFieldRewrite("delta", "partial_json", next(argument_replacements[index])) case _: return None @@ -1343,8 +1360,7 @@ class AnthropicMessagesHandler(BaseTranslation): @staticmethod def _rewrite_buffered_item(item: object, rewrite_event: _SSEEventRewriter) -> object: if isinstance(item, dict): - rewritten: Final = rewrite_event(_as_str_mapping(item)) - return item if rewritten is None else dict(rewritten) + return _rewritten_event(_as_str_mapping(item), rewrite_event) if isinstance(item, (bytes, bytearray)): return AnthropicMessagesHandler._rewrite_sse_events(bytes(item), rewrite_event) return item @@ -1374,8 +1390,8 @@ class AnthropicMessagesHandler(BaseTranslation): return line if not isinstance(data, dict): return line - rewritten: Final = rewrite_event(_as_str_mapping(data)) - return line if rewritten is None else "data: " + json.dumps(rewritten) + rewritten: Final = _rewritten_event(_as_str_mapping(data), rewrite_event) + return line if rewritten is data else "data: " + json.dumps(rewritten) def get_streaming_scan_key(self, responses_so_far: Sequence[object]) -> StreamingScanKey | None: stream_ended: Final = self._check_streaming_has_ended(responses_so_far) From 89e11949c834d0b466375b0442b27304946568a9 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:22:30 -0700 Subject: [PATCH 118/223] fix(guardrails): key Responses stream tool-call rewrites by call_id Bridged Responses streams give reasoning and message items output_index 0 and start function calls at 1, so keying rewrites by output_index rewrote the wrong items. Rewrites now follow each function call's call_id through the buffered item and argument events, refuse when an event cannot be resolved to a rewritten call, and the refusal branches on all three handlers get regression tests --- .../guardrail_translation/handler.py | 132 ++++++++++++------ .../test_anthropic_guardrail_handler.py | 25 ++++ .../test_openai_guardrail_handler.py | 56 ++++++++ ...test_openai_responses_guardrail_handler.py | 90 ++++++++++++ 4 files changed, 259 insertions(+), 44 deletions(-) diff --git a/litellm/llms/openai/responses/guardrail_translation/handler.py b/litellm/llms/openai/responses/guardrail_translation/handler.py index 208ceb959e6..280a8670c36 100644 --- a/litellm/llms/openai/responses/guardrail_translation/handler.py +++ b/litellm/llms/openai/responses/guardrail_translation/handler.py @@ -140,6 +140,10 @@ _TERMINAL_ENVELOPE_EVENT_TYPES: Final = frozenset( ) +_FUNCTION_CALL_ARGUMENT_EVENT_TYPES: Final = frozenset( + {"response.function_call_arguments.delta", "response.function_call_arguments.done"} +) +_OUTPUT_ITEM_EVENT_TYPES: Final = frozenset({"response.output_item.added", "response.output_item.done"}) _PATCHABLE_ITEM_FIELDS: Final[Mapping[str, str]] = MappingProxyType( {"function_call_output": "output", "message": "content"} ) @@ -930,70 +934,110 @@ class OpenAIResponsesHandler(BaseTranslation): ) -> None: """Write ended-stream guardrail tool-call rewrites into the completed envelope's ``function_call`` items and sync the earlier stream events, - keyed by ``output_index``. The guardrail sees the envelope's function - calls in output order, which is how a rewritten call finds its item; a - rewrite whose calls do not line up with the envelope is reported as - undeliverable, so the pipeline executor discards it and releases the - original events.""" + keyed by ``call_id``. The guardrail sees the envelope's function calls + in output order, which is how a rewritten call finds its ``call_id``; + the stream events find their call through the ``call_id`` on + ``output_item`` events and the ``item_id`` on argument events, since an + event's ``output_index`` need not match the envelope's (the chat bridge + numbers tool calls from 1 while the envelope lists them after the + message). A rewrite whose calls do not line up with the envelope, or + whose events cannot be found, is reported as undeliverable, so the + pipeline executor discards it and releases the original events.""" if post_guardrail_tool_calls == pre_guardrail_tool_calls: return - function_call_indices: Final = tuple( - output_idx - for output_idx, output_item in enumerate(outputs) - if stream_item_field(output_item, "type") == "function_call" + function_call_items: Final = tuple( + output_item for output_item in outputs if stream_item_field(output_item, "type") == "function_call" ) - if len(function_call_indices) != len(post_guardrail_tool_calls): - from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite - - raise UndeliverableStreamRewrite(guardrail_name) - rewrites_by_output_index: Final = MappingProxyType( + call_ids: Final = tuple( + call_id + for output_item in function_call_items + if isinstance(call_id := stream_item_field(output_item, "call_id"), str) and call_id + ) + stream_events: Final = responses_so_far[:-1] + call_id_by_item_id: Final = self._function_call_ids_by_item_id(stream_events) + event_call_ids: Final = tuple( + self._function_call_event_call_id(event, call_id_by_item_id) for event in stream_events + ) + rewrites_by_call_id: Final = MappingProxyType( { - output_idx: after - for output_idx, before, after in zip( - function_call_indices, pre_guardrail_tool_calls, post_guardrail_tool_calls - ) + call_id: after + for call_id, before, after in zip(call_ids, pre_guardrail_tool_calls, post_guardrail_tool_calls) if after != before } ) - for output_idx, rewrite in rewrites_by_output_index.items(): - self._write_function_call_item(outputs[output_idx], rewrite.name, rewrite.arguments) - self._sync_stream_events_with_tool_call_rewrites( - stream_events=responses_so_far[:-1], - rewrites_by_output_index=rewrites_by_output_index, + unresolved_argument_event: Final = any( + call_id is None and stream_item_field(event, "type") in _FUNCTION_CALL_ARGUMENT_EVENT_TYPES + for event, call_id in zip(stream_events, event_call_ids) ) + if ( + len(call_ids) != len(function_call_items) + or len(frozenset(call_ids)) != len(call_ids) + or len(call_ids) != len(post_guardrail_tool_calls) + or unresolved_argument_event + or not rewrites_by_call_id.keys() <= frozenset(event_call_ids) + ): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite - def _sync_stream_events_with_tool_call_rewrites( - self, - stream_events: Sequence[object], - rewrites_by_output_index: Mapping[int, _ToolCallShape], - ) -> None: - """Sync pre-completion function-call events with the rewritten completed - response: the first ``function_call_arguments.delta`` for a rewritten call - carries the full rewritten arguments and the rest are blanked, while - ``function_call_arguments.done`` and ``output_item.done`` carry the full - rewritten arguments and ``output_item.added`` / ``output_item.done`` the - rewritten name, so every event a client may read agrees with the - rewritten ``response.completed`` payload.""" + raise UndeliverableStreamRewrite(guardrail_name) + for output_item, rewrite in ( + (output_item, rewrites_by_call_id[call_id]) + for output_item, call_id in zip(function_call_items, call_ids) + if call_id in rewrites_by_call_id + ): + self._write_function_call_item(output_item, rewrite.name, rewrite.arguments) delta_replacements: Final = MappingProxyType( - {index: chain((rewrite.arguments,), repeat("")) for index, rewrite in rewrites_by_output_index.items()} + {call_id: chain((rewrite.arguments,), repeat("")) for call_id, rewrite in rewrites_by_call_id.items()} ) - for event in stream_events: - output_index = stream_item_field(event, "output_index") - if not isinstance(output_index, int) or output_index not in rewrites_by_output_index: + for event, call_id in zip(stream_events, event_call_ids): + if call_id not in rewrites_by_call_id: continue - rewrite = rewrites_by_output_index[output_index] match stream_item_field(event, "type"): case "response.function_call_arguments.delta": - self._write_event_field(event, "delta", next(delta_replacements[output_index])) + self._write_event_field(event, "delta", next(delta_replacements[call_id])) case "response.function_call_arguments.done": - self._write_event_field(event, "arguments", rewrite.arguments) + self._write_event_field(event, "arguments", rewrites_by_call_id[call_id].arguments) case "response.output_item.added": - self._write_function_call_item(stream_item_field(event, "item"), rewrite.name, None) + self._write_function_call_item( + stream_item_field(event, "item"), rewrites_by_call_id[call_id].name, None + ) case "response.output_item.done": - self._write_function_call_item(stream_item_field(event, "item"), rewrite.name, rewrite.arguments) + self._write_function_call_item( + stream_item_field(event, "item"), + rewrites_by_call_id[call_id].name, + rewrites_by_call_id[call_id].arguments, + ) case _: pass + @staticmethod + def _function_call_ids_by_item_id(stream_events: Sequence[object]) -> Mapping[str, str]: + items: Final = tuple( + stream_item_field(event, "item") + for event in stream_events + if stream_item_field(event, "type") in _OUTPUT_ITEM_EVENT_TYPES + ) + return MappingProxyType( + { + item_id: call_id + for item in items + if stream_item_field(item, "type") == "function_call" + and isinstance(item_id := stream_item_field(item, "id"), str) + and isinstance(call_id := stream_item_field(item, "call_id"), str) + } + ) + + @staticmethod + def _function_call_event_call_id(event: object, call_id_by_item_id: Mapping[str, str]) -> str | None: + event_type: Final = stream_item_field(event, "type") + if event_type in _FUNCTION_CALL_ARGUMENT_EVENT_TYPES: + item_id: Final = stream_item_field(event, "item_id") + return call_id_by_item_id.get(item_id) if isinstance(item_id, str) else None + if event_type not in _OUTPUT_ITEM_EVENT_TYPES: + return None + item: Final = stream_item_field(event, "item") + call_id: Final = stream_item_field(item, "call_id") + return call_id if stream_item_field(item, "type") == "function_call" and isinstance(call_id, str) else None + @staticmethod def _write_function_call_item(item: object, name: str | None, arguments: str | None) -> None: if item is None: diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index bd2dff9b33c..64c243343d5 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -381,6 +381,31 @@ class TestAnthropicMessagesHandlerStreamingOutputProcessing: assert chunks == original + @pytest.mark.asyncio + async def test_deliver_ended_stream_tool_use_rewrite_with_server_tool_use_block_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = AnthropicMessagesHandler() + server_tool_use = [ + ("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "server_tool_use", "id": "srvtoolu_1", "name": "web_search", "input": {}}}), + ("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": '{"query": "fruit"}'}}), + ("content_block_stop", {"type": "content_block_stop", "index": 0}), + ] + tool_use = self._ended_tool_use_sse_chunks() + chunks = ( + tool_use[:1] + + [f"event: {name}\ndata: {json.dumps(payload)}\n\n".encode() for name, payload in server_tool_use] + + [chunk.replace(b'"index": 0', b'"index": 1') for chunk in tool_use[1:]] + ) + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=MagicMock(), + deliver_ended_stream_rewrites=True, + ) + @pytest.mark.asyncio async def test_ended_stream_rewrite_leaves_chunks_untouched_by_default(self): handler = AnthropicMessagesHandler() diff --git a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py index be168ec83e6..c7011a6f00e 100644 --- a/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/chat/guardrail_translation/test_openai_guardrail_handler.py @@ -1252,6 +1252,62 @@ class TestOpenAIChatCompletionsHandlerStreamingOutput: deliver_ended_stream_rewrites=True, ) + @staticmethod + def _two_choice_tool_call_stream_chunks() -> list: + from litellm.types.utils import ( + ChatCompletionDeltaToolCall, + Delta, + Function, + ModelResponseStream, + StreamingChoices, + ) + + def chunk( + choice_index: int, tool_call: ChatCompletionDeltaToolCall | None, finish_reason: Optional[str] = None + ) -> ModelResponseStream: + return ModelResponseStream( + id="chatcmpl-123", + created=1234567890, + model="gpt-4", + object="chat.completion.chunk", + choices=[ + StreamingChoices( + index=choice_index, + delta=Delta(tool_calls=[tool_call] if tool_call else None), + finish_reason=finish_reason, + ) + ], + ) + + def fragment(arguments: str, name: Optional[str] = None, call_id: Optional[str] = None): + return ChatCompletionDeltaToolCall( + id=call_id, index=0, type="function", function=Function(name=name, arguments=arguments) + ) + + return [ + chunk(0, fragment("", name="lookup_fruit", call_id="call_1")), + chunk(1, fragment("", name="lookup_fruit", call_id="call_2")), + chunk(0, fragment('{"fruit": "persimmon"}')), + chunk(1, fragment('{"fruit": "durian"}')), + chunk(0, None, finish_reason="tool_calls"), + chunk(1, None, finish_reason="tool_calls"), + ] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_tool_call_rewrite_on_multi_choice_stream_fails_closed(self): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIChatCompletionsHandler() + chunks = self._two_choice_tool_call_stream_chunks() + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=chunks, + guardrail_to_apply=MockGuardrail(guardrail_name="test"), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + @pytest.mark.asyncio async def test_deliver_ended_stream_clean_multi_choice_stream_released_untouched(self): handler = OpenAIChatCompletionsHandler() diff --git a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py index bdf7f83757c..fa1e969b277 100644 --- a/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py +++ b/tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py @@ -1314,6 +1314,96 @@ class TestOpenAIResponsesHandlerStreamingOutputProcessing: assert completed_event.response.output[0].arguments == '{"fruit": "[MASKED]"}' assert completed_event.response.output[0].name == "lookup_fruit" + @staticmethod + def _bridged_function_call_stream_events() -> List[dict]: + reasoning = {"type": "reasoning", "id": "rs_1", "summary": []} + text = {"type": "output_text", "text": "Looking that up", "annotations": []} + message = {"type": "message", "id": "msg_1", "role": "assistant", "status": "completed", "content": [text]} + + def function_call(arguments: str, status: str) -> dict: + return { + "type": "function_call", + "id": "fc_1", + "call_id": "call_1", + "name": "lookup_fruit", + "arguments": arguments, + "status": status, + } + + return [ + {"type": "response.output_item.added", "output_index": 0, "item": dict(reasoning)}, + {"type": "response.output_item.done", "output_index": 0, "item": dict(reasoning)}, + {"type": "response.output_item.added", "output_index": 0, "item": {**message, "status": "in_progress", "content": []}}, + {"type": "response.output_text.delta", "item_id": "msg_1", "output_index": 0, "content_index": 0, "delta": "Looking that up"}, + {"type": "response.output_item.done", "output_index": 0, "item": {**message, "content": [dict(text)]}}, + {"type": "response.output_item.added", "output_index": 1, "item": function_call("", "in_progress")}, + {"type": "response.function_call_arguments.delta", "item_id": "fc_1", "output_index": 1, "delta": '{"fruit":'}, + {"type": "response.function_call_arguments.delta", "item_id": "fc_1", "output_index": 1, "delta": ' "persimmon"}'}, + { + "type": "response.function_call_arguments.done", + "item_id": "fc_1", + "output_index": 1, + "arguments": '{"fruit": "persimmon"}', + }, + {"type": "response.output_item.done", "output_index": 1, "item": function_call('{"fruit": "persimmon"}', "completed")}, + { + "type": "response.completed", + "response": { + "id": "resp_1", + "model": "claude-haiku-4-5", + "output": [ + dict(reasoning), + {**message, "content": [dict(text)]}, + function_call('{"fruit": "persimmon"}', "completed"), + ], + }, + }, + ] + + @pytest.mark.asyncio + async def test_deliver_ended_stream_rewrites_keys_bridged_function_call_events_by_call_id(self): + handler = OpenAIResponsesHandler() + events = self._bridged_function_call_stream_events() + + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + + assert events[6]["delta"] == '{"fruit": "[MASKED]"}' + assert events[7]["delta"] == "" + assert events[8]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[5]["item"]["name"] == "lookup_fruit" + assert events[9]["item"]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[10]["response"]["output"][2]["arguments"] == '{"fruit": "[MASKED]"}' + assert events[3]["delta"] == "Looking that up" + assert events[4]["item"]["content"][0]["text"] == "Looking that up" + assert events[10]["response"]["output"][1]["content"][0]["text"] == "Looking that up" + assert events[1]["item"] == {"type": "reasoning", "id": "rs_1", "summary": []} + + @pytest.mark.asyncio + @pytest.mark.parametrize("mismatch", ["orphan_call_id", "duplicate_call_id"]) + async def test_deliver_ended_stream_function_call_rewrite_without_matching_events_fails_closed(self, mismatch): + from litellm.proxy.policy_engine.pipeline_executor import UndeliverableStreamRewrite + + handler = OpenAIResponsesHandler() + events = self._ended_function_call_stream_events() + envelope_item = events[5]["response"]["output"][0] + if mismatch == "orphan_call_id": + events[5]["response"]["output"] = [{**envelope_item, "call_id": "call_999"}] + else: + events[5]["response"]["output"] = [dict(envelope_item), dict(envelope_item)] + + with pytest.raises(UndeliverableStreamRewrite): + await handler.process_output_streaming_response( + responses_so_far=events, + guardrail_to_apply=self._argument_masking_guardrail(), + litellm_logging_obj=None, + deliver_ended_stream_rewrites=True, + ) + @pytest.mark.asyncio async def test_ended_stream_function_call_rewrite_leaves_events_untouched_by_default(self): handler = OpenAIResponsesHandler() From 9d164fa34150ac7dc0551d9efec16cdb27848c5c Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 8 Sep 2026 13:24:15 -0700 Subject: [PATCH 119/223] bump: litellm-proxy-extras 0.4.94 -> 0.4.95, litellm 1.101.0 -> 1.102.0 --- litellm-proxy-extras/pyproject.toml | 4 ++-- pyproject.toml | 6 +++--- uv.lock | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/litellm-proxy-extras/pyproject.toml b/litellm-proxy-extras/pyproject.toml index 82d31fec373..91b4e4a7ba1 100644 --- a/litellm-proxy-extras/pyproject.toml +++ b/litellm-proxy-extras/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm-proxy-extras" -version = "0.4.94" +version = "0.4.95" description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." readme = "README.md" requires-python = ">=3.9" @@ -26,7 +26,7 @@ required-version = ">=0.10.9" module-root = "" [tool.commitizen] -version = "0.4.94" +version = "0.4.95" version_files = [ "pyproject.toml:^version", "../pyproject.toml:litellm-proxy-extras==", diff --git a/pyproject.toml b/pyproject.toml index af35c77d259..706b14b8a04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "litellm" -version = "1.101.0" +version = "1.102.0" description = "Library to easily interface with LLM API providers" readme = "README.md" requires-python = ">=3.10, <3.15" @@ -67,7 +67,7 @@ proxy = [ "azure-identity>=1.25.2,<2.0", "azure-storage-blob>=12.28.0,<13.0", "mcp>=1.28.1,<2.0", - "litellm-proxy-extras==0.4.94", + "litellm-proxy-extras==0.4.95", "litellm-enterprise==0.1.65", "RestrictedPython>=8.5,<9.0", "rich>=13.9.4,<14.0", @@ -328,7 +328,7 @@ members = ["enterprise", "litellm-proxy-extras"] profile = "black" [tool.commitizen] -version = "1.101.0" +version = "1.102.0" version_files = [ "pyproject.toml:^version", ] diff --git a/uv.lock b/uv.lock index 89205cd9527..12dc2c69d23 100644 --- a/uv.lock +++ b/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-09-02T16:58:34.594994Z" +exclude-newer = "2026-09-05T20:24:07.535116Z" exclude-newer-span = "P3D" [manifest] @@ -4358,7 +4358,7 @@ wheels = [ [[package]] name = "litellm" -version = "1.101.0" +version = "1.102.0" source = { editable = "." } dependencies = [ { name = "aiohttp" }, @@ -4776,7 +4776,7 @@ source = { editable = "enterprise" } [[package]] name = "litellm-proxy-extras" -version = "0.4.94" +version = "0.4.95" source = { editable = "litellm-proxy-extras" } [[package]] From 071f83980c319b9d9a21f77179781052c050b517 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:26:17 -0700 Subject: [PATCH 120/223] refactor(proxy): type the post_call deferral helpers' request-state parameters --- litellm/proxy/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index e48bd3851fb..e653ca6b62c 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -544,7 +544,7 @@ def _guardrails_outside_pipeline(policy_name: str, pipeline: "GuardrailPipeline" def _without_names( - bucket: dict, # mutable-ok: the applied_* header slots live in the request-state dict every hook writes in place + bucket: dict[str, object], # mutable-ok: the applied_* header slots live in the request-state dict hooks write slot: str, names: frozenset[str], ) -> None: @@ -561,7 +561,7 @@ def _without_names( def _withdraw_deferred_claims( - data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + data: dict[str, object], # mutable-ok: same request-payload shape as post_call_success_hook's data deferred: Sequence[tuple[str, "GuardrailPipeline"]], ) -> None: outside_by_policy: Final = MappingProxyType( @@ -586,7 +586,7 @@ def _withdraw_deferred_claims( def _defer_post_call_pipelines( - data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + data: dict[str, object], # mutable-ok: same request-payload shape as post_call_success_hook's data response: ResponsesAPIResponse, ) -> None: deferred: Final = _post_call_pipelines(data) @@ -2990,7 +2990,7 @@ class ProxyLogging: async def _run_post_call_pipelines( self, - data: dict, # mutable-ok: same request-payload shape as post_call_success_hook's data + data: dict[str, object], # mutable-ok: same request-payload shape as post_call_success_hook's data user_api_key_dict: UserAPIKeyAuth, response: LLMResponseTypes, ) -> LLMResponseTypes | None: From 0c2d0f4777f4113b42510e2daef2330ca0bb87a8 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 8 Sep 2026 13:28:13 -0700 Subject: [PATCH 121/223] fix(spend-tracking): forget an empty spend-log lookup after thirty seconds The daily spend rows and the spend logs of one batch are written a moment apart, so a usage read landing between them used to remember the session as nameless for ten minutes on that worker. Found identities keep the ten minute entry --- litellm/constants.py | 1 + .../spend_tracking/key_metadata_recovery.py | 19 ++++++++++++++++-- .../test_key_metadata_recovery.py | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index 9654c84c412..cfc5b6b86a7 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1764,6 +1764,7 @@ SPECIAL_LITELLM_AUTH_TOKEN: Final = ["ui-token"] DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)) DEFAULT_ACCESS_GROUP_CACHE_TTL: Final = int(os.getenv("DEFAULT_ACCESS_GROUP_CACHE_TTL", 600)) SPEND_LOG_KEY_METADATA_CACHE_TTL: Final = 600 +SPEND_LOG_KEY_METADATA_MISS_CACHE_TTL: Final = 30 SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS: Final = 10000 # Short TTL for negative MCP access-group existence lookups. Keeps unauthenticated # callers from forcing a DB query per request for unknown names, while bounding diff --git a/litellm/proxy/spend_tracking/key_metadata_recovery.py b/litellm/proxy/spend_tracking/key_metadata_recovery.py index 7212d8b6acb..354c6479d34 100644 --- a/litellm/proxy/spend_tracking/key_metadata_recovery.py +++ b/litellm/proxy/spend_tracking/key_metadata_recovery.py @@ -9,7 +9,11 @@ from typing_extensions import ReadOnly, TypedDict from litellm._logging import verbose_proxy_logger from litellm.caching.in_memory_cache import InMemoryCache -from litellm.constants import SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS, SPEND_LOG_KEY_METADATA_CACHE_TTL +from litellm.constants import ( + SPEND_LOG_KEY_METADATA_CACHE_MAX_ITEMS, + SPEND_LOG_KEY_METADATA_CACHE_TTL, + SPEND_LOG_KEY_METADATA_MISS_CACHE_TTL, +) from litellm.litellm_core_utils.litellm_logging import is_valid_sha256_hash from litellm.proxy.utils import PrismaClient from litellm.repositories.user_repository import UserRepository @@ -230,6 +234,17 @@ async def _query_spend_log_metadata( ) +def _remember_spend_log_metadata( + cache: InMemoryCache, digest: str, window: tuple[datetime, datetime], meta: KeyMetadataDict | None +) -> None: + if meta is None: + cache.set_cache( + _spend_log_cache_key(digest, window), KeyMetadataDict(), ttl=SPEND_LOG_KEY_METADATA_MISS_CACHE_TTL + ) + return + cache.set_cache(_spend_log_cache_key(digest, window), meta) + + async def recover_key_metadata_from_spend_logs( prisma_client: PrismaClient, missing_keys: AbstractSet[str], @@ -251,7 +266,7 @@ async def recover_key_metadata_from_spend_logs( fresh: Final = await _query_spend_log_metadata(prisma_client, uncached, window) if uncached else _EMPTY_KEY_METADATA if fresh is not None: for digest in uncached: - cache.set_cache(_spend_log_cache_key(digest, window), fresh.get(digest, KeyMetadataDict())) + _remember_spend_log_metadata(cache, digest, window, fresh.get(digest)) return MappingProxyType( {digest: meta for digest, meta in (*cached.items(), *(fresh or _EMPTY_KEY_METADATA).items()) if meta} ) diff --git a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py index 3d7362b9de0..4f065070330 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py +++ b/tests/test_litellm/proxy/spend_tracking/test_key_metadata_recovery.py @@ -1,3 +1,4 @@ +import time from collections.abc import Sequence from datetime import datetime from types import SimpleNamespace @@ -7,6 +8,7 @@ import pytest from prisma.errors import PrismaError from litellm.caching.in_memory_cache import InMemoryCache +from litellm.constants import SPEND_LOG_KEY_METADATA_CACHE_TTL, SPEND_LOG_KEY_METADATA_MISS_CACHE_TTL from litellm.proxy.spend_tracking.key_metadata_recovery import ( fill_missing_api_key_aliases, recover_double_hashed_key_metadata, @@ -395,3 +397,21 @@ async def test_recover_key_metadata_from_spend_logs_does_not_cache_a_failed_quer result = await recover_key_metadata_from_spend_logs(mock_prisma, {digest}, window, cache=cache) assert result[digest]["key_alias"] == "back-online" + + +@pytest.mark.asyncio +async def test_recover_key_metadata_from_spend_logs_forgets_a_miss_long_before_a_hit(): + found = hash_token("cli-session-found") + unknown = hash_token("cli-session-unknown") + window = (datetime(2026, 9, 7), datetime(2026, 9, 10)) + cache = InMemoryCache(default_ttl=SPEND_LOG_KEY_METADATA_CACHE_TTL) + mock_prisma = MagicMock() + mock_prisma.db.query_raw = _query_raw_spend_logs([_digest_row(found, "found-alias", None, None)]) + started = time.time() + + await recover_key_metadata_from_spend_logs(mock_prisma, {found, unknown}, window, cache=cache) + + hit_expires = next(deadline for key, deadline in cache.ttl_dict.items() if found in key) + miss_expires = next(deadline for key, deadline in cache.ttl_dict.items() if unknown in key) + assert miss_expires - started <= SPEND_LOG_KEY_METADATA_MISS_CACHE_TTL + 1 + assert hit_expires - started >= SPEND_LOG_KEY_METADATA_CACHE_TTL - 1 From 4a3a78c256bab366597a76d44930b323bce6335b Mon Sep 17 00:00:00 2001 From: tin-berri Date: Tue, 8 Sep 2026 13:28:57 -0700 Subject: [PATCH 122/223] feat(complexity_router): rebalance heuristic weights in the dashboard and grade custom dimensions by match count (#40205) The Advanced scoring editor now lists built-in and custom dimensions together. Editing any weight holds it and rescales the others proportionally so the vector totals 1.00, and Save stores those explicit values. The backend scores exactly what is stored, with no runtime normalization, so routers nobody edits keep their weights. CustomDimension gains an opt-in scoring_mode. match_count scores 0, 0.5 or 1 by distinct matcher hits; the default stays binary. The tuning fingerprint omits a binary scoring_mode, so routers written before this change keep their recorded baseline and the upgrade does not consume the free heuristic-v1 tuning slot. --- .../complexity_router/README.md | 12 +- .../complexity_router/complexity_router.py | 38 ++++- .../complexity_router/config.py | 13 +- .../auto_router_tuning_baseline.py | 12 +- .../router_strategy/test_complexity_router.py | 148 +++++++++++++----- .../test_auto_router_tuning_baseline.py | 73 ++++++++- .../add_model/ClassificationMethodConfig.tsx | 5 +- .../add_model/ComplexityRouterConfig.test.tsx | 6 +- .../add_model/ComplexityRouterConfig.tsx | 6 + .../add_model/CustomDimensionRows.tsx | 136 ++++++++++++++++ ...euristicScoringConfig.integration.test.tsx | 90 +++++++++++ .../add_model/HeuristicScoringConfig.test.tsx | 14 +- .../add_model/HeuristicScoringConfig.tsx | 85 ++++++++-- .../add_model/add_auto_router_tab.tsx | 4 + .../build_complexity_router_config.test.ts | 47 +++++- .../build_complexity_router_config.ts | 17 +- .../add_model/custom_dimensions.test.ts | 41 +++++ .../components/add_model/custom_dimensions.ts | 54 +++++++ .../add_model/heuristic_scoring_knobs.test.ts | 119 ++++++++++++++ .../add_model/heuristic_scoring_knobs.ts | 97 +++++++++++- .../src/components/add_model/tier_rows.ts | 1 + ...d_updated_complexity_router_config.test.ts | 49 ++++++ .../edit_auto_router_modal.tsx | 17 +- .../src/lib/autorouter_presets.test.ts | 28 ++++ .../src/lib/autorouter_presets.ts | 12 ++ ui/litellm-dashboard/src/lib/http/schema.d.ts | 9 +- 26 files changed, 1047 insertions(+), 86 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/add_model/CustomDimensionRows.tsx create mode 100644 ui/litellm-dashboard/src/components/add_model/HeuristicScoringConfig.integration.test.tsx create mode 100644 ui/litellm-dashboard/src/components/add_model/custom_dimensions.test.ts create mode 100644 ui/litellm-dashboard/src/components/add_model/custom_dimensions.ts diff --git a/litellm/router_strategy/complexity_router/README.md b/litellm/router_strategy/complexity_router/README.md index 88ed374dd3f..d605b43e42a 100644 --- a/litellm/router_strategy/complexity_router/README.md +++ b/litellm/router_strategy/complexity_router/README.md @@ -207,17 +207,27 @@ custom_dimensions: - name: sqlMigration weight: 0.7 patterns: ['\b(create|alter|drop)\s{1,4}table\b'] + - name: dataPipeline + weight: 0.4 + scoring_mode: match_count + keywords: [airflow, dbt, snowflake] ``` Each dimension contributes its weight once when any matcher hits the current ask. Repeated matches do not increase it. The built-in score and tier boundaries are unchanged, and the total score is not renormalized. Keywords use the existing case-insensitive word-boundary and CJK rules. Regexes search the first 2048 characters case-insensitively and compile during configuration validation and router initialization, never per request +`scoring_mode` is optional and defaults to `binary`, the behavior above. `match_count` grades the dimension by how many distinct matchers hit: none scores 0 and emits no signal, one scores half the weight, two or more score the full weight. Repeated occurrences of one matcher never raise the count, keywords are distinct case-insensitively, patterns are distinct by source, and a keyword and a pattern are always distinct from each other. Matching stops as soon as the selected mode's maximum is reached, so a binary dimension still stops at its first hit. Existing configurations without the field keep binary scoring and the same tuning fingerprint, so the field only counts as a tuning change when set to `match_count` + +### Weights through the API versus the dashboard + +The API and YAML store exactly the weights written. A `dimension_weights` map and inline custom weights are read literally, missing recognized built-in names score zero, and nothing renormalizes the vector, so a total other than 1 is legal and scores accordingly. The dashboard's heuristic scoring editor is the one place that rebalances: editing one weight there holds it and redistributes the remainder across the other active dimensions in the draft, then Save sends the resulting explicit values, which the backend stores and scores as written. Opening a router, applying a preset, editing matchers, changing `scoring_mode`, or saving unrelated fields never normalizes existing weights + Only `heuristic`, `heuristic_first` and `hybrid` accept custom dimensions. Each name must be a unique ASCII identifier starting with a letter, at most 64 characters, and cannot reuse a built-in dimension name or a key in `dimension_weights`. Set its weight inline, greater than zero and at most one Patterns are checked at configuration time against a grammar whose worst case stays a few milliseconds on 2048 characters. Every quantifier needs an explicit upper bound of at most 64 and must repeat a single character or character class, so `\s{1,4}` is accepted while `\s+`, `(a|aa){0,12}` and `(?:ab){0,64}` are refused. Backreferences, lookarounds, atomic groups and possessive quantifiers are refused as well. Each pattern is then costed: alternation branches and repeat lengths multiply the ways the engine can retry, and every later piece of the pattern is charged once per path that can reach it, so `a?a?a?a?a?a?a?a?` followed by a long fixed tail is refused even though each quantifier is small. The budget is 2048 work units per pattern and 8192 across the router. An invalid or over-budget pattern fails the write with a message naming the pattern and the rule it broke Limits are 16 dimensions, 32 combined keywords/patterns per dimension, 256 characters per matcher and 4096 matcher characters per dimension. Matching runs inline on the request path with no timeout and no worker thread, because the grammar is what bounds the cost. These are routing hints, not security enforcement rules -The existing heuristic-v1 tuning quota covers custom dimensions and their weights: one changed router without an auto-router license, unlimited with the entitlement. Omitting `custom_dimensions` preserves existing scoring. Routing decisions and spend logs include signals such as `custom (sqlMigration)` without recording the configured pattern or matched text. The field is configured through YAML or the model API; this change adds no dashboard editor +The existing heuristic-v1 tuning quota covers custom dimensions, their weights and their scoring mode: one changed router without an auto-router license, unlimited with the entitlement. Omitting `custom_dimensions` preserves existing scoring. Routing decisions and spend logs include signals such as `custom (sqlMigration)` without recording the configured pattern or matched text. The field is configured through YAML, the model API, or the dashboard's heuristic scoring editor ## Usage diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index c8644f52c57..ae1f82fc40b 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -20,7 +20,7 @@ import random import re import time from collections.abc import Callable, Iterator, Mapping, Sequence -from itertools import accumulate, islice, takewhile +from itertools import accumulate, chain, islice, takewhile from threading import Lock from types import MappingProxyType from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple, cast @@ -82,6 +82,7 @@ from .config import ( ClassificationRubric, ComplexityRouterConfig, ComplexityTier, + CustomDimension, TierDefinition, ) from .stall_detector import detect_stalled_task @@ -879,6 +880,15 @@ class DimensionScore: self.signal = signal +class _CustomDimensionMatchers(NamedTuple): + """One custom dimension's distinct matchers and the number of hits that saturates its score.""" + + dimension: CustomDimension + keywords: tuple[str, ...] + patterns: tuple[re.Pattern[str], ...] + saturation: int + + class KeywordOverride(NamedTuple): """A keyword_tier_rules match: the winning tier and, on the lexical path, the keyword that fired.""" @@ -1121,7 +1131,12 @@ class ComplexityRouter(CustomLogger): ) self.simple_keywords = self.config.simple_keywords or DEFAULT_SIMPLE_KEYWORDS self._custom_dimensions = tuple( - (dimension, tuple(re.compile(pattern, re.IGNORECASE) for pattern in dimension.patterns)) + _CustomDimensionMatchers( + dimension, + tuple(dict.fromkeys(keyword.lower() for keyword in dimension.keywords)), + tuple(re.compile(pattern, re.IGNORECASE) for pattern in dict.fromkeys(dimension.patterns)), + 2 if dimension.scoring_mode == "match_count" else 1, + ) for dimension in self.config.custom_dimensions ) if self.config.has_custom_tiers: @@ -1325,15 +1340,26 @@ class ComplexityRouter(CustomLogger): score: Final = score_high if match_count >= high_threshold else score_low return DimensionScore(name, score, f"{signal_label} ({detail})"), match_count + def _count_custom_hits(self, matchers: _CustomDimensionMatchers, user_text: str, scanned: str) -> int: + hits: Final = chain( + (self._keyword_matches(user_text, keyword) for keyword in matchers.keywords), + (pattern.search(scanned) is not None for pattern in matchers.patterns), + ) + return sum(islice((1 for hit in hits if hit), matchers.saturation)) + def _score_custom_dimensions(self, prompt: str, user_text: str) -> tuple[tuple[DimensionScore, float], ...]: if not self._custom_dimensions: return () scanned: Final = prompt[:CUSTOM_PATTERN_SCAN_CHARS] return tuple( - (DimensionScore(dimension.name, 1.0, f"custom ({dimension.name})"), dimension.weight) - for dimension, patterns in self._custom_dimensions - if any(self._keyword_matches(user_text, keyword) for keyword in dimension.keywords) - or any(pattern.search(scanned) is not None for pattern in patterns) + ( + DimensionScore( + matchers.dimension.name, hits / matchers.saturation, f"custom ({matchers.dimension.name})" + ), + matchers.dimension.weight, + ) + for matchers in self._custom_dimensions + if (hits := self._count_custom_hits(matchers, user_text, scanned)) ) def _score_multi_step(self, text: str) -> DimensionScore: diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index 3ec9f9b5394..37375d9727d 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -667,6 +667,14 @@ class CustomDimension(BaseModel): weight: float = Field(gt=0, le=1, allow_inf_nan=False) keywords: tuple[Annotated[str, Field(min_length=1, max_length=256)], ...] = Field(default=(), max_length=32) patterns: tuple[Annotated[str, Field(min_length=1, max_length=256)], ...] = Field(default=(), max_length=32) + scoring_mode: Literal["binary", "match_count"] = Field( + default="binary", + description=( + "'binary' scores 1 when any matcher hits. 'match_count' scores 0.5 when one distinct matcher hits and 1 " + "when two or more do; repeated occurrences of one matcher never raise it. Keywords are distinct " + "case-insensitively, patterns by source, and a keyword and a pattern are always distinct from each other." + ), + ) @model_validator(mode="after") def _validate_matchers(self) -> "CustomDimension": @@ -794,8 +802,9 @@ class ComplexityRouterConfig(BaseModel): default=(), max_length=16, description=( - "Named binary dimensions added to the heuristic-v1 score. Each contributes its inline weight once " - "when any keyword matches the current ask or a case-insensitive regex matches its first 2048 characters. " + "Named dimensions added to the heuristic-v1 score. Each contributes its inline weight once " + "when any keyword matches the current ask or a case-insensitive regex matches its first 2048 characters; " + "scoring_mode 'match_count' instead grades half weight for one distinct matcher and full for two or more. " "Regex quantifiers repeat one character or class at most 64 times. Unbounded quantifiers, repeated groups, " "backreferences and lookarounds are rejected. Conservative work limits include alternation paths, " "repeat lengths and subsequent matching: 2048 units per pattern, 8192 across the router. " diff --git a/litellm/router_utils/auto_router_tuning_baseline.py b/litellm/router_utils/auto_router_tuning_baseline.py index 74f7b82389a..9699ab886b9 100644 --- a/litellm/router_utils/auto_router_tuning_baseline.py +++ b/litellm/router_utils/auto_router_tuning_baseline.py @@ -52,7 +52,17 @@ def tuning_fingerprint(complexity_router_config: object) -> str | None: supplied: Final = ((_TUNING_FIELD_SET - frozenset(("tier_model_configs",))) & frozenset(raw)) | ( frozenset(("tier_model_configs",)) if validated.tier_model_configs else frozenset() ) - payload: Final = validated.model_dump(mode="json", include=supplied) + payload: Final = validated.model_dump( + mode="json", + include=supplied, + exclude={ + "custom_dimensions": { + index: {"scoring_mode"} + for index, dimension in enumerate(validated.custom_dimensions) + if dimension.scoring_mode == "binary" + } + }, + ) return hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest() diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index 5b1d8562abd..b0f2a65bb9b 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -826,6 +826,8 @@ class TestCustomDimensions: pytest.param({"keywords": ["x"] * 32, "patterns": ["y"]}, {}, id="combined-matcher-count"), pytest.param({"keywords": ["x" * 256] * 17}, {}, id="matcher-character-budget"), pytest.param({"unknown": True}, {}, id="extra-field"), + pytest.param({"scoring_mode": "graded"}, {}, id="unknown-scoring-mode"), + pytest.param({"scoring_mode": None}, {}, id="null-scoring-mode"), ], ) def test_custom_dimension_invalid_configuration_rejected( @@ -878,43 +880,125 @@ class TestCustomDimensions: ) @pytest.mark.asyncio - @pytest.mark.parametrize("current_ask", ("Hello!", "orbitmesh")) + @pytest.mark.parametrize("scoring_mode", ("binary", "match_count")) + @pytest.mark.parametrize("current_ask", ("Hello!", "orbitmesh", "orbitmesh fluxgate")) async def test_custom_dimensions_public_hook_scores_only_current_ask( - self, mock_router_instance: MagicMock, current_ask: str + self, mock_router_instance: MagicMock, current_ask: str, scoring_mode: str ) -> None: router: Final = ComplexityRouter( "test-router", mock_router_instance, { "tiers": {"SIMPLE": "cheap", "MEDIUM": "mid", "COMPLEX": "strong", "REASONING": "top"}, - "custom_dimensions": [{"name": "internalFrameworks", "weight": 0.7, "keywords": ["orbitmesh"]}], + "dimension_weights": {}, + "custom_dimensions": [ + { + "name": "internalFrameworks", + "weight": 0.8, + "keywords": ["orbitmesh", "fluxgate"], + "scoring_mode": scoring_mode, + } + ], }, ) result: Final = await router.async_pre_routing_hook( model="test-router", request_kwargs={}, messages=[ - {"role": "system", "content": "orbitmesh"}, - {"role": "user", "content": "orbitmesh"}, - {"role": "assistant", "content": "orbitmesh is ready"}, + {"role": "system", "content": "orbitmesh fluxgate"}, + {"role": "user", "content": "orbitmesh fluxgate"}, + {"role": "assistant", "content": "orbitmesh fluxgate is ready"}, {"role": "user", "content": current_ask}, - {"role": "tool", "tool_call_id": "previous", "content": "orbitmesh"}, + {"role": "tool", "tool_call_id": "previous", "content": "orbitmesh fluxgate"}, ], ) assert result is not None assert result.routing_decision is not None - assert ("custom (internalFrameworks)" in result.routing_decision["signals"]) is (current_ask == "orbitmesh") - assert result.model == ("top" if current_ask == "orbitmesh" else "cheap") + expected_score: Final = ( + 0.0 + if current_ask == "Hello!" + else 0.4 + if scoring_mode == "match_count" and current_ask == "orbitmesh" + else 0.8 + ) + assert result.routing_decision["score"] == expected_score + assert ("custom (internalFrameworks)" in result.routing_decision["signals"]) is (expected_score > 0) + assert result.model == ("cheap" if expected_score == 0 else "strong" if expected_score == 0.4 else "top") assert "orbitmesh" not in " ".join(result.routing_decision["signals"]) - def test_custom_patterns_scan_only_the_first_2048_characters(self, mock_router_instance: MagicMock) -> None: + @pytest.mark.parametrize("scoring_mode", ("binary", "match_count")) + def test_custom_patterns_scan_only_the_first_2048_characters( + self, mock_router_instance: MagicMock, scoring_mode: str + ) -> None: router: Final = ComplexityRouter( "test-router", mock_router_instance, - {"custom_dimensions": [{"name": "late", "weight": 0.7, "patterns": [r"zzz{1,3}"]}]}, + { + "custom_dimensions": [ + { + "name": "late", + "weight": 0.7, + "patterns": [r"zzz{1,3}", r"yyy{1,3}"], + "scoring_mode": scoring_mode, + } + ] + }, ) + baseline: Final = ComplexityRouter("test-router", mock_router_instance) assert "custom (late)" in router.classify("a" * 2040 + " zzz")[2] assert "custom (late)" not in router.classify("a" * 2048 + " zzz")[2] + second_hit_past_the_bound: Final = "yyy " + "a" * 2044 + " zzz" + contribution: Final = ( + router.classify(second_hit_past_the_bound)[1] - baseline.classify(second_hit_past_the_bound)[1] + ) + assert contribution == pytest.approx(0.7 if scoring_mode == "binary" else 0.35) + + @pytest.mark.parametrize( + "prompt,expected_score", + [ + pytest.param("Hello!", 0.0, id="no-hit"), + pytest.param("orbitmesh orbitmesh ORBITMESH again", 0.5, id="one-keyword-repeated"), + pytest.param("create table a; CREATE TABLE b; create table c", 0.5, id="one-pattern-repeated"), + pytest.param("orbitmesh and fluxgate", 1.0, id="two-keywords"), + pytest.param("orbitmesh then create table t", 1.0, id="keyword-plus-pattern"), + pytest.param("create table a; alter table b", 1.0, id="two-patterns"), + pytest.param("orbitmesh fluxgate create table a alter table b", 1.0, id="all-matchers"), + ], + ) + def test_match_count_grades_distinct_matchers( + self, mock_router_instance: MagicMock, prompt: str, expected_score: float + ) -> None: + dimension: Final = { + "name": "graded", + "weight": 0.6, + "keywords": ["orbitmesh", "ORBITMESH", "fluxgate"], + "patterns": [r"\bcreate\s{1,4}table\b", r"\bcreate\s{1,4}table\b", r"\balter\s{1,4}table\b"], + } + baseline: Final = ComplexityRouter("test-router", mock_router_instance) + binary: Final = ComplexityRouter("test-router", mock_router_instance, {"custom_dimensions": [dimension]}) + graded: Final = ComplexityRouter( + "test-router", + mock_router_instance, + {"custom_dimensions": [{**dimension, "scoring_mode": "match_count"}]}, + ) + _, baseline_score, baseline_signals = baseline.classify(prompt) + _, binary_score, binary_signals = binary.classify(prompt) + _, graded_score, graded_signals = graded.classify(prompt) + assert graded_score == pytest.approx(baseline_score + 0.6 * expected_score) + assert binary_score == pytest.approx(baseline_score + (0.6 if expected_score else 0.0)) + expected_signals: Final = [*baseline_signals, *(["custom (graded)"] if expected_score else [])] + assert graded_signals == expected_signals + assert binary_signals == expected_signals + + def test_scoring_mode_round_trips_and_defaults_to_binary(self) -> None: + dimension: Final = {"name": "graded", "weight": 0.6, "keywords": ["orbitmesh"]} + legacy: Final = ComplexityRouterConfig.model_validate({"custom_dimensions": [dimension]}) + graded: Final = ComplexityRouterConfig.model_validate( + {"custom_dimensions": [{**dimension, "scoring_mode": "match_count"}]} + ) + assert legacy.custom_dimensions[0].scoring_mode == "binary" + assert graded.model_dump(mode="json")["custom_dimensions"][0]["scoring_mode"] == "match_count" + assert ComplexityRouterConfig.model_validate(graded.model_dump(mode="json")) == graded def test_custom_dimensions_router_wide_regex_work_is_capped(self) -> None: heavy: Final = {"weight": 0.5, "patterns": ["a?" * 8 + "z"]} @@ -1511,6 +1595,7 @@ class TestRouterComplexityDeploymentMethods: def test_the_shipped_rubric_and_default_prompt_stay_free(self) -> None: """Only an operator-written prompt is gated: picking a shipped rubric preset, or writing no prompt at all, leaves a router unmetered, so several of them register under a ceiling of one.""" + def rubric(model_name: str, model_id: str, preset: str | None) -> dict[str, object]: llm_config: dict[str, object] = {"model": "gpt-4o-mini"} if preset is not None: @@ -1647,6 +1732,7 @@ class TestRouterComplexityDeploymentMethods: def test_renaming_built_in_tiers_is_not_a_custom_tier_set(self) -> None: """tier_labels renames the built-in ladder without defining one, so it stays ungated: two such routers register under a ceiling of one.""" + def labeled(model_name: str, model_id: str) -> dict[str, object]: row = self._router_row(model_name, model_id, "heuristic") row["litellm_params"]["complexity_router_config"]["tier_labels"] = {"SIMPLE": "Cheap", "MEDIUM": "Standard"} @@ -2520,9 +2606,7 @@ class TestLLMClassifier: assert outcome.classifier_cost == pytest.approx(1.35e-05) @pytest.mark.asyncio - async def test_aclassify_timeout_does_not_inherit_router_retries_or_fallbacks( - self, llm_classifier_config - ): + async def test_aclassify_timeout_does_not_inherit_router_retries_or_fallbacks(self, llm_classifier_config): real_router = Router( model_list=[ { @@ -2565,9 +2649,7 @@ class TestLLMClassifier: assert real_router.total_calls["openai/mock-backup-classifier"] == 0 @pytest.mark.asyncio - async def test_aclassify_enforces_total_classifier_deadline( - self, mock_router_instance, llm_classifier_config - ): + async def test_aclassify_enforces_total_classifier_deadline(self, mock_router_instance, llm_classifier_config): cancelled = asyncio.Event() async def slow_classifier(**_kwargs: object) -> None: @@ -12414,9 +12496,7 @@ class TestTierHealthFailover: llm_provider="", ) filtered = (*cooling, *blocked, *excluded) - healthy = [ - {"model_name": model, "model_info": {"id": i}} for i in ids_by_model[model] if i not in filtered - ] + healthy = [{"model_name": model, "model_info": {"id": i}} for i in ids_by_model[model] if i not in filtered] if not healthy: raise RouterRateLimitError( model=model, cooldown_time=60.0, enable_pre_call_checks=False, cooldown_list=[] @@ -12845,9 +12925,7 @@ class TestTierHealthFailover: assert all(probed is not request_kwargs for probed in router.litellm_router_instance.probed_kwargs) @pytest.mark.asyncio - async def test_a_peer_whose_every_deployment_is_over_its_rpm_is_not_a_failover_target( - self, mock_router_instance - ): + async def test_a_peer_whose_every_deployment_is_over_its_rpm_is_not_a_failover_target(self, mock_router_instance): """RPM exhaustion is its own verdict from the owner (RouterRateLimitErrorBasic). A peer in that state would be rejected downstream, so it cannot be the substitute.""" from litellm.types.router import RouterRateLimitErrorBasic @@ -12880,9 +12958,7 @@ class TestTierHealthFailover: assert {r.model for r in results} == {"live-c"} @pytest.mark.asyncio - async def test_the_probe_forwards_input_so_window_checks_run_on_input_only_surfaces( - self, mock_router_instance - ): + async def test_the_probe_forwards_input_so_window_checks_run_on_input_only_surfaces(self, mock_router_instance): """The Responses API carries its prompt as `input`, never as messages. The owner only runs its context-window pre-call check when one of them is present, so dropping `input` would silently skip window filtering on that whole surface.""" @@ -12908,9 +12984,7 @@ class TestTierHealthFailover: ), "the eligibility probe must forward `input` to the owner" @pytest.mark.asyncio - async def test_a_group_the_router_has_no_deployment_for_is_not_a_failover_target( - self, mock_router_instance - ): + async def test_a_group_the_router_has_no_deployment_for_is_not_a_failover_target(self, mock_router_instance): """The owner answers an unconfigured group with BadRequestError. Reading that as live would both skip failover off it and let it be chosen as a substitute.""" router = self._router( @@ -13099,9 +13173,7 @@ class TestClassifierVision: routed as default_fallback on text the request never contained. """ router = self._router(mock_router_instance, vision={"enabled": True}) - response = await router.async_pre_routing_hook( - model="m", request_kwargs={}, messages=self._turn(IMG_PART) - ) + response = await router.async_pre_routing_hook(model="m", request_kwargs={}, messages=self._turn(IMG_PART)) assert response.routing_decision["cause"] == "llm_classifier" assert response.model == "t-complex" assert [block["type"] for block in self._classifier_user_content(mock_router_instance)] == [ @@ -13112,9 +13184,7 @@ class TestClassifierVision: @pytest.mark.asyncio async def test_image_only_turn_still_falls_back_when_vision_is_off(self, mock_router_instance): router = self._router(mock_router_instance, vision={"enabled": False}) - response = await router.async_pre_routing_hook( - model="m", request_kwargs={}, messages=self._turn(IMG_PART) - ) + response = await router.async_pre_routing_hook(model="m", request_kwargs={}, messages=self._turn(IMG_PART)) assert response.routing_decision["cause"] == "default_fallback" mock_router_instance.acompletion.assert_not_awaited() @@ -13184,9 +13254,7 @@ class TestClassifierVision: makes the image the only variable; a margin loose enough to leave the score undecided would pass whether or not the guard exists. """ - router = self._router( - mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra - ) + router = self._router(mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra) response = await router.async_pre_routing_hook( model="m", request_kwargs={}, messages=self._turn({"type": "text", "text": "what is this"}, IMG_PART) ) @@ -13200,9 +13268,7 @@ class TestClassifierVision: self, mock_router_instance, classifier_type, extra, short_circuit_cause ): """The negative class: same router, same text, no image, and the scorer still decides.""" - router = self._router( - mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra - ) + router = self._router(mock_router_instance, vision={"enabled": True}, classifier_type=classifier_type, **extra) response = await router.async_pre_routing_hook( model="m", request_kwargs={}, messages=[{"role": "user", "content": "what is this"}] ) diff --git a/tests/test_litellm/router_utils/test_auto_router_tuning_baseline.py b/tests/test_litellm/router_utils/test_auto_router_tuning_baseline.py index f686a62db76..75c115cd3ab 100644 --- a/tests/test_litellm/router_utils/test_auto_router_tuning_baseline.py +++ b/tests/test_litellm/router_utils/test_auto_router_tuning_baseline.py @@ -7,6 +7,7 @@ from typing import Final import pytest +from litellm.router_strategy.complexity_router.config import ComplexityRouterConfig from litellm.router_utils.auto_router_tuning_baseline import ( DEFAULT_TUNING_FINGERPRINT, HEURISTIC_V1_TUNING_FIELDS, @@ -21,6 +22,33 @@ from litellm.router_utils.auto_router_tuning_baseline import ( _TIERS = {"SIMPLE": "cheap", "MEDIUM": "mid", "COMPLEX": "strong", "REASONING": "top"} _ALT_TIERS = {**_TIERS, "COMPLEX": "other-strong"} +_KEYWORD_DIMENSION: Final = {"name": "internalFrameworks", "weight": 0.7, "keywords": ["orbitmesh"]} +_HISTORICAL_FINGERPRINTS: Final = ( + ({}, "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"), + ( + {"custom_dimensions": [_KEYWORD_DIMENSION]}, + "b5c3c3f3be6341a8a16148d68d9067e03f94ed01a0bbfcde7955763042744372", + ), + ( + {"custom_dimensions": [{"name": "sqlDdl", "weight": 0.4, "patterns": [r"\bCREATE\s{1,4}TABLE\b"]}]}, + "814ce0017fc7f60a160b262f658d910e9bdf784e6139a4ba4f1e2657aa203950", + ), + ( + { + "tiers": _TIERS, + "dimension_weights": {"codePresence": 0.3}, + "custom_dimensions": [ + { + "name": "internalFrameworks", + "weight": 0.2, + "keywords": ["orbitmesh", "fluxgate"], + "patterns": [r"\bALTER\s{1,4}TABLE\b"], + } + ], + }, + "38970dc9224e265ab38c89674563d8d0537822591f9239b45251db6f5ca6cc39", + ), +) def _router( @@ -77,6 +105,27 @@ class TestTuningFingerprint: def test_explicit_empty_tier_model_configs_follow_omission(self) -> None: assert tuning_fingerprint({"tier_model_configs": {}}) == DEFAULT_TUNING_FINGERPRINT + @pytest.mark.parametrize(("config", "fingerprint"), _HISTORICAL_FINGERPRINTS) + def test_fingerprints_recorded_before_scoring_mode_existed_are_preserved( + self, config: Mapping[str, object], fingerprint: str + ) -> None: + """Literal hashes captured from the merged implementation at 9bc9104102, before CustomDimension.scoring_mode.""" + assert tuning_fingerprint(config) == fingerprint + + def test_binary_scoring_mode_hashes_like_its_absence(self) -> None: + historical: Final = tuning_fingerprint({"custom_dimensions": [_KEYWORD_DIMENSION]}) + explicit: Final = tuning_fingerprint({"custom_dimensions": [{**_KEYWORD_DIMENSION, "scoring_mode": "binary"}]}) + reserialized: Final = ComplexityRouterConfig.model_validate( + {"custom_dimensions": [_KEYWORD_DIMENSION]} + ).model_dump(mode="json", include={"custom_dimensions"}) + assert reserialized["custom_dimensions"][0]["scoring_mode"] == "binary" + assert reserialized["custom_dimensions"][0]["patterns"] == [] + assert historical == explicit == tuning_fingerprint(reserialized) + assert ( + tuning_fingerprint({"custom_dimensions": [{**_KEYWORD_DIMENSION, "scoring_mode": "match_count"}]}) + != historical + ) + def test_tier_model_overrides_change_the_fingerprint(self) -> None: plain = tuning_fingerprint({"tiers": {"SIMPLE": "x"}}) with_override = tuning_fingerprint( @@ -218,15 +267,18 @@ class TestQuota: is None ) - def test_custom_dimension_add_edit_and_revert_share_one_quota_slot(self) -> None: + @pytest.mark.parametrize( + "edit", + [ + pytest.param({"weight": 0.9}, id="weight"), + pytest.param({"scoring_mode": "match_count"}, id="scoring-mode"), + ], + ) + def test_custom_dimension_add_edit_and_revert_share_one_quota_slot(self, edit: Mapping[str, object]) -> None: baselines: Final = snapshot_tuning_baselines(()) original: Final = _router("a", {}) - config: Final = { - "custom_dimensions": [{"name": "internalFrameworks", "weight": 0.7, "keywords": ["orbitmesh"]}] - } - edited_config: Final = { - "custom_dimensions": [{"name": "internalFrameworks", "weight": 0.9, "keywords": ["orbitmesh"]}] - } + config: Final = {"custom_dimensions": [_KEYWORD_DIMENSION]} + edited_config: Final = {"custom_dimensions": [{**_KEYWORD_DIMENSION, **edit}]} added: Final = _router("a", config) edited: Final = _router("a", edited_config) second: Final = _router("b", config) @@ -240,6 +292,13 @@ class TestQuota: assert mutable_tuned_identities((original,), baselines) == frozenset() assert tuning_quota_violation(candidate=second, others=(original,), baselines=baselines, limit=1) is None + def test_graded_dimension_recorded_at_snapshot_is_its_own_baseline(self) -> None: + graded: Final = _router("a", {"custom_dimensions": [{**_KEYWORD_DIMENSION, "scoring_mode": "match_count"}]}) + baselines: Final = snapshot_tuning_baselines((graded,)) + assert mutable_tuned_identities((graded,), baselines) == frozenset() + reverted_to_binary: Final = _router("a", {"custom_dimensions": [_KEYWORD_DIMENSION]}) + assert mutable_tuned_identities((reverted_to_binary,), baselines) == {router_identity(graded)} + def test_violation_message_names_the_limit_and_remedy(self) -> None: message = tuning_limit_violation(held=2, limit=1) assert message is not None diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 15cfff01766..111eeebe5a3 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -44,8 +44,9 @@ import { } from "./ComplexityRouterConfig"; const DEFAULT_SCORING_EXPLANATION = - "The router scores each request across 7 dimensions: token count, code presence, reasoning markers, technical " + - "terms, simple indicators, multi-step patterns, and question complexity. The weighted score determines the tier:"; + "The router scores each request across 7 built-in dimensions: token count, code presence, reasoning markers, technical " + + "terms, simple indicators, multi-step patterns, and question complexity, plus any custom dimensions you add. " + + "The weighted score determines the tier:"; const HEURISTIC_V2_EXPLANATION = "The router estimates success probability for all four tiers with the bundled calibrated model, then selects " + diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx index 2970e14b335..0c12cc0ba1a 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx @@ -1521,14 +1521,16 @@ describe("ComplexityRouterConfig tier editing", () => { renderWithProviders(); fireEvent.click(screen.getByText("Advanced: Classification Method")); expect(screen.queryByText("How Classification Works")).not.toBeInTheDocument(); - expect(screen.queryByText("scores each request across 7 dimensions", { exact: false })).not.toBeInTheDocument(); + expect( + screen.queryByText("scores each request across 7 built-in dimensions", { exact: false }), + ).not.toBeInTheDocument(); }); it("keeps the scorer card on a built-in router, whose tiers the score still decides", () => { renderWithProviders(); fireEvent.click(screen.getByText("Advanced: Classification Method")); expect(screen.getByText("How Classification Works")).toBeInTheDocument(); - expect(screen.getByText("scores each request across 7 dimensions", { exact: false })).toBeInTheDocument(); + expect(screen.getByText("scores each request across 7 built-in dimensions", { exact: false })).toBeInTheDocument(); }); it("says why a custom row is blocked instead of only reddening its border", () => { diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index d7df6ce33bb..de80e714e66 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -50,6 +50,7 @@ import EscalationKeywords from "./EscalationKeywords"; import KeywordTierRules, { KeywordTierRule } from "./KeywordTierRules"; import SemanticKeywordMatching from "./SemanticKeywordMatching"; import { type DimensionWeights, type TierBoundaries, type TokenThresholds } from "./heuristic_scoring_knobs"; +import { type CustomDimensionRow } from "./custom_dimensions"; import CompressionControls from "./CompressionControls"; import { type AutoRouterCompressionState, DEFAULT_AUTO_ROUTER_COMPRESSION } from "./buildAutoRouterCompression"; @@ -432,6 +433,11 @@ export interface ComplexityRouterConfigValue { tier_boundaries?: TierBoundaries; token_thresholds?: TokenThresholds; dimension_weights?: DimensionWeights; + /** + * Operator-added scoring dimensions, each carrying its own inline weight. Undefined means the router has + * none and keeps the key out of the payload; an empty array is a real "the last row was removed" state. + */ + custom_dimensions?: CustomDimensionRow[]; /** * Score floor the reasoning-marker override must clear. Undefined keeps the key out of the payload, so the * floor tracks tier_boundaries.simple_medium; an explicit 0 is a real floor that promotes on the markers alone. diff --git a/ui/litellm-dashboard/src/components/add_model/CustomDimensionRows.tsx b/ui/litellm-dashboard/src/components/add_model/CustomDimensionRows.tsx new file mode 100644 index 00000000000..34d8370aafb --- /dev/null +++ b/ui/litellm-dashboard/src/components/add_model/CustomDimensionRows.tsx @@ -0,0 +1,136 @@ +import { Trash2 } from "lucide-react"; +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Slider } from "@/components/ui/slider"; +import type { CustomDimensionRow } from "./custom_dimensions"; + +const SCORING_MODES = [ + { value: "binary", label: "Binary" }, + { value: "match_count", label: "Match count" }, +] as const; + +interface Props { + rows: CustomDimensionRow[]; + disabled: boolean; + onChange: (rows: CustomDimensionRow[]) => void; + onWeight: (id: string, weight: number) => void; + onAdd: () => void; + onRemove: (id: string) => void; +} + +export default function CustomDimensionRows({ rows, disabled, onChange, onWeight, onAdd, onRemove }: Props) { + const [draft, setDraft] = useState<{ id: string; raw: string } | null>(null); + const update = (id: string, patch: Partial) => + onChange(rows.map((row) => (row.id === id ? { ...row, ...patch } : row))); + const editWeight = (id: string, raw: string) => { + setDraft({ id, raw }); + if (raw.trim() && Number.isFinite(Number(raw))) onWeight(id, Number(raw)); + }; + return ( +
+ {rows.map((row, index) => ( +
+ Custom dimension {index + 1} +
+ +
+
+ + update(row.id, { name: event.target.value })} + /> +
+
+ + onWeight(row.id, Array.isArray(value) ? value[0] : value)} + /> + setDraft(null)} + onChange={(event) => editWeight(row.id, event.target.value)} + /> +
+
+ {(["keywords", "patterns"] as const).map((field) => ( +
+ +