From 2496f9c14965c39589f53eea31bdb6d762b1d360 Mon Sep 17 00:00:00 2001 From: tc-mb <157115220+tc-mb@users.noreply.github.com> Date: Thu, 7 May 2026 03:54:09 +0800 Subject: [PATCH 001/158] mtmd : support MiniCPM-V 4.6 (#22529) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support MiniCPM-V 4.6 in new branch Signed-off-by: tc-mb * fix code bug Signed-off-by: tc-mb * fix pre-commit Signed-off-by: tc-mb * fix convert Signed-off-by: tc-mb * rename clip_graph_minicpmv4_6 Signed-off-by: tc-mb * use new TYPE_MINICPMV4_6 Signed-off-by: tc-mb * use build_attn to allow flash attention support Signed-off-by: tc-mb * no use legacy code, restored here. Signed-off-by: tc-mb * use the existing tensors name Signed-off-by: tc-mb * unused ctx->model.hparams.minicpmv_version Signed-off-by: tc-mb * use n_merge for slice alignment Signed-off-by: tc-mb * borrow wa_layer_indexes for vit_merger insertion point Signed-off-by: tc-mb * fix code style Signed-off-by: tc-mb * Update convert_hf_to_gguf.py Co-authored-by: Sigbjørn Skjæret * use filter_tensors and add model.vision_tower Signed-off-by: tc-mb * fix chkhsh Signed-off-by: tc-mb * fix type check Signed-off-by: tc-mb --------- Signed-off-by: tc-mb Co-authored-by: Sigbjørn Skjæret --- convert_hf_to_gguf.py | 92 ++++++++++- convert_hf_to_gguf_update.py | 1 + docs/multimodal/minicpmv4.6.md | 49 ++++++ gguf-py/gguf/constants.py | 25 +++ gguf-py/gguf/tensor_mapping.py | 46 ++++++ tools/mtmd/README.md | 4 + tools/mtmd/clip-impl.h | 13 ++ tools/mtmd/clip-model.h | 19 +++ tools/mtmd/clip.cpp | 143 ++++++++++++++++ tools/mtmd/models/minicpmv.cpp | 291 +++++++++++++++++++++++++++++++++ tools/mtmd/models/models.h | 5 + tools/mtmd/mtmd-image.cpp | 4 +- tools/mtmd/mtmd.cpp | 12 ++ 13 files changed, 701 insertions(+), 3 deletions(-) create mode 100644 docs/multimodal/minicpmv4.6.md diff --git a/convert_hf_to_gguf.py b/convert_hf_to_gguf.py index c4fbde3f56b..9a86aed8461 100755 --- a/convert_hf_to_gguf.py +++ b/convert_hf_to_gguf.py @@ -1360,6 +1360,9 @@ def get_vocab_base_pre(self, tokenizer) -> str: if chkhsh == "d4540891389ea895b53b399da6ac824becc30f2fba0e9ddbb98f92e55ca0e97c": # ref: https://huggingface.co/Qwen/Qwen3-Embedding-0.6B res = "qwen2" + if chkhsh == "1444df51289cfa8063b96f0e62b1125440111bc79a52003ea14b6eac7016fd5f": + # ref: https://huggingface.co/openbmb/MiniCPM-V-4_6 + res = "qwen35" if chkhsh == "66b8d4e19ab16c3bfd89bce5d785fb7e0155e8648708a1f42077cb9fe002c273": # ref: https://huggingface.co/alvarobartt/grok-2-tokenizer res = "grok-2" @@ -5499,16 +5502,101 @@ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iter yield from super().modify_tensors(data_torch, name, bid) +class _Qwen35MRopeMixin: + # Qwen3.5 always applies interleaved MRoPE (see Qwen3_5RotaryEmbedding in transformers); + # the upstream default mrope_section is [11, 11, 10] and llama.cpp's QWEN35 / QWEN35MOE + # loaders treat qwen35.rope.dimension_sections as required, so make sure it is always + # written even when a particular checkpoint omits the field in `rope_parameters`. + _QWEN35_DEFAULT_MROPE_SECTION = [11, 11, 10, 0] + + gguf_writer: gguf.GGUFWriter + rope_parameters: dict + + def set_gguf_parameters(self): + super().set_gguf_parameters() # ty: ignore[unresolved-attribute] + if "mrope_section" not in self.rope_parameters: + self.gguf_writer.add_rope_dimension_sections(self._QWEN35_DEFAULT_MROPE_SECTION) + + @ModelBase.register("Qwen3_5ForConditionalGeneration", "Qwen3_5ForCausalLM") -class Qwen3_5TextModel(_LinearAttentionVReorderBase): +class Qwen3_5TextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): model_arch = gguf.MODEL_ARCH.QWEN35 @ModelBase.register("Qwen3_5MoeForConditionalGeneration", "Qwen3_5MoeForCausalLM") -class Qwen3_5MoeTextModel(_LinearAttentionVReorderBase): +class Qwen3_5MoeTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): model_arch = gguf.MODEL_ARCH.QWEN35MOE +# MiniCPM-V 4.6: text tower is Qwen3.5 (linear+full hybrid attention) wrapped under +# `model.language_model.*`; vision tower is SigLIP + a window-attention ViT merger +# + a final DownsampleMLP merger. The same HF arch is registered twice below: once as +# the LM (text mode) and once as the mmproj (vision mode), mirroring the Qwen3-VL setup. + +@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") +class MiniCPMV4_6TextModel(Qwen3_5TextModel): + model_arch = gguf.MODEL_ARCH.QWEN35 + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("model.merger."): + return None + # MTP tensors are not used at inference yet; align with Qwen3Next behaviour + if name.startswith("mtp"): + return None + + return super().filter_tensors(item) + + +@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") +class MiniCPMV4_6VisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams_vision is not None: + # In MiniCPM-V 4.6 `vision_config.image_size` (980) describes the SigLIP + # positional embedding bucket grid (70 x 70), while the per-slice processing + # resolution is the preprocessor's `scale_resolution` (typically 448). + # The CLIP loader in tools/mtmd/clip.cpp consumes `clip.vision.image_size` + # as the slice size and warmup resolution, so report `scale_resolution` there + # to match the upstream MiniCPMV4_6ImageProcessorPil slicing rules. + scale_resolution = self.preprocessor_config.get("scale_resolution") + if scale_resolution is not None: + self.hparams_vision["image_size"] = int(scale_resolution) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + + # projector type string is consumed by clip_projector_type_from_string() in clip.cpp + # (mapped to PROJECTOR_TYPE_MINICPMV4_6). + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MINICPMV4_6) + + # ViT merger 2x2 + final merger 2x2 = 4x spatial merge per dimension; used for slice alignment + self.gguf_writer.add_vision_projector_scale_factor(4) + + # borrow wa_layer_indexes for vit_merger insertion point + insert_layer_id = int(self.global_config.get( + "insert_layer_id", self.hparams_vision.get("insert_layer_id", 6))) + self.gguf_writer.add_vision_wa_layer_indexes([insert_layer_id]) + + # SigLIP vision body uses gelu_pytorch_tanh, which matches ggml_gelu (tanh approx). + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_attention_layernorm_eps( + self.hparams_vision.get("layer_norm_eps", 1e-6)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # lm_head / MTP -> belong to the LM file + if name.startswith(("lm_head.", "mtp")): + return None + + return super().filter_tensors(item) + + @ModelBase.register("GPT2LMHeadModel") class GPT2Model(TextModel): model_arch = gguf.MODEL_ARCH.GPT2 diff --git a/convert_hf_to_gguf_update.py b/convert_hf_to_gguf_update.py index d8d10a10128..6e6cd057909 100755 --- a/convert_hf_to_gguf_update.py +++ b/convert_hf_to_gguf_update.py @@ -175,6 +175,7 @@ class TOKENIZER_TYPE(IntEnum): {"name": "falcon-h1", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/tiiuae/Falcon-H1-34B-Base", "chkhsh": "48f8e02c0359c0bbdd82f26909171fac1c18a457bb47573ed1fe3bbb2c1cfd4b"}, {"name": "kimi-k2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/moonshotai/Kimi-K2-Base", "chkhsh": "81212dc7cdb7e0c1074ca62c5aeab0d43c9f52b8a737be7b12a777c953027890"}, {"name": "qwen2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Qwen/Qwen3-Embedding-0.6B", "chkhsh": "d4540891389ea895b53b399da6ac824becc30f2fba0e9ddbb98f92e55ca0e97c"}, + {"name": "qwen35", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/openbmb/MiniCPM-V-4_6", "chkhsh": "1444df51289cfa8063b96f0e62b1125440111bc79a52003ea14b6eac7016fd5f"}, {"name": "grok-2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/alvarobartt/grok-2-tokenizer", "chkhsh": "66b8d4e19ab16c3bfd89bce5d785fb7e0155e8648708a1f42077cb9fe002c273"}, # jina-v2-de variants {"name": "jina-v2-de", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/aari1995/German_Semantic_V3", "chkhsh": "b3d1dd861f1d4c5c0d2569ce36baf3f90fe8a102db3de50dd71ff860d91be3df"}, diff --git a/docs/multimodal/minicpmv4.6.md b/docs/multimodal/minicpmv4.6.md new file mode 100644 index 00000000000..4aa13f8eb0d --- /dev/null +++ b/docs/multimodal/minicpmv4.6.md @@ -0,0 +1,49 @@ +## MiniCPM-V 4.6 + +### Prepare models and code + +Download [MiniCPM-V-4_6](https://huggingface.co/openbmb/MiniCPM-V-4_6) PyTorch model from huggingface to "MiniCPM-V-4_6" folder. + +The model must be the standard `transformers` v5.7.0+ checkpoint (no `trust_remote_code`); the architecture in `config.json` is `MiniCPMV4_6ForConditionalGeneration` with a `qwen3_5_text` text model and a SigLIP-based vision tower plus a window-attention `vit_merger`. + +### Build llama.cpp + +If there are differences in usage, please refer to the official build [documentation](https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md) + +Clone llama.cpp: +```bash +git clone https://github.com/ggml-org/llama.cpp +cd llama.cpp +``` + +Build llama.cpp using `CMake`: +```bash +cmake -B build +cmake --build build --config Release +``` + + +### Usage of MiniCPM-V 4.6 + +Unlike older MiniCPM-V variants, MiniCPM-V 4.6 is converted directly through `convert_hf_to_gguf.py`. The same script is invoked twice on the original Hugging Face directory: once to produce the language-model GGUF and once with `--mmproj` to produce the multimodal projector GGUF. + +```bash +# language model +python ./convert_hf_to_gguf.py ../MiniCPM-V-4_6 --outfile ../MiniCPM-V-4_6/ggml-model-f16.gguf + +# multimodal projector (vision tower + window-attention vit_merger + DownsampleMLP merger) +python ./convert_hf_to_gguf.py ../MiniCPM-V-4_6 --mmproj --outfile ../MiniCPM-V-4_6/mmproj-model-f16.gguf + +# optional: quantize to Q4_K_M +./build/bin/llama-quantize ../MiniCPM-V-4_6/ggml-model-f16.gguf ../MiniCPM-V-4_6/ggml-model-Q4_K_M.gguf Q4_K_M +``` + + +Inference on Linux or Mac +```bash +# run in single-turn mode +./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_6/ggml-model-f16.gguf --mmproj ../MiniCPM-V-4_6/mmproj-model-f16.gguf -c 4096 --temp 0.7 --top-p 0.8 --top-k 100 --repeat-penalty 1.05 --image xx.jpg -p "What is in the image?" + +# run in conversation mode +./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_6/ggml-model-Q4_K_M.gguf --mmproj ../MiniCPM-V-4_6/mmproj-model-f16.gguf +``` diff --git a/gguf-py/gguf/constants.py b/gguf-py/gguf/constants.py index b3fc4a88744..c68ec548fea 100644 --- a/gguf-py/gguf/constants.py +++ b/gguf-py/gguf/constants.py @@ -773,6 +773,14 @@ class MODEL_TENSOR(IntEnum): V_DS_NORM = auto() # qwen3vl V_DS_FC1 = auto() # qwen3vl V_DS_FC2 = auto() # qwen3vl + V_MERGER_LN1 = auto() # minicpmv4_6 + V_MERGER_ATTN_Q = auto() # minicpmv4_6 + V_MERGER_ATTN_K = auto() # minicpmv4_6 + V_MERGER_ATTN_V = auto() # minicpmv4_6 + V_MERGER_ATTN_O = auto() # minicpmv4_6 + V_MERGER_DS_LN = auto() # minicpmv4_6 + V_MERGER_DS_UP = auto() # minicpmv4_6 + V_MERGER_DS_DOWN = auto() # minicpmv4_6 V_MM_POST_FC_NORM = auto() # cogvlm V_MM_UP = auto() # cogvlm V_MM_DOWN = auto() # cogvlm @@ -1277,6 +1285,14 @@ class MODEL_TENSOR(IntEnum): MODEL_TENSOR.V_DS_NORM: "v.deepstack.{bid}.norm", MODEL_TENSOR.V_DS_FC1: "v.deepstack.{bid}.fc1", MODEL_TENSOR.V_DS_FC2: "v.deepstack.{bid}.fc2", + MODEL_TENSOR.V_MERGER_LN1: "v.vit_merger.ln1", + MODEL_TENSOR.V_MERGER_ATTN_Q: "v.vit_merger.attn_q", + MODEL_TENSOR.V_MERGER_ATTN_K: "v.vit_merger.attn_k", + MODEL_TENSOR.V_MERGER_ATTN_V: "v.vit_merger.attn_v", + MODEL_TENSOR.V_MERGER_ATTN_O: "v.vit_merger.attn_out", + MODEL_TENSOR.V_MERGER_DS_LN: "v.vit_merger.ds_ln", + MODEL_TENSOR.V_MERGER_DS_UP: "v.vit_merger.ds_ffn_up", + MODEL_TENSOR.V_MERGER_DS_DOWN: "v.vit_merger.ds_ffn_down", MODEL_TENSOR.V_MM_POST_FC_NORM: "mm.post_fc_norm", # cogvlm MODEL_TENSOR.V_MM_UP: "mm.up", MODEL_TENSOR.V_MM_DOWN: "mm.down", @@ -1449,6 +1465,14 @@ class MODEL_TENSOR(IntEnum): MODEL_TENSOR.V_DS_NORM, MODEL_TENSOR.V_DS_FC1, MODEL_TENSOR.V_DS_FC2, + MODEL_TENSOR.V_MERGER_LN1, + MODEL_TENSOR.V_MERGER_ATTN_Q, + MODEL_TENSOR.V_MERGER_ATTN_K, + MODEL_TENSOR.V_MERGER_ATTN_V, + MODEL_TENSOR.V_MERGER_ATTN_O, + MODEL_TENSOR.V_MERGER_DS_LN, + MODEL_TENSOR.V_MERGER_DS_UP, + MODEL_TENSOR.V_MERGER_DS_DOWN, MODEL_TENSOR.V_MM_POST_FC_NORM, MODEL_TENSOR.V_MM_UP, MODEL_TENSOR.V_MM_DOWN, @@ -4224,6 +4248,7 @@ class VisionProjectorType: NEMOTRON_V2_VL = "nemotron_v2_vl" HUNYUANOCR = "hunyuanocr" HUNYUANVL = "hunyuanvl" + MINICPMV4_6 = "minicpmv4_6" GRANITE_SPEECH = "granite_speech" # audio diff --git a/gguf-py/gguf/tensor_mapping.py b/gguf-py/gguf/tensor_mapping.py index cddbdf355b0..f27f0e4c997 100644 --- a/gguf-py/gguf/tensor_mapping.py +++ b/gguf-py/gguf/tensor_mapping.py @@ -1399,6 +1399,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_EMBD_PATCH: ( "vision_tower.vision_model.embeddings.patch_embedding", + "model.vision_tower.embeddings.patch_embedding", # minicpmv4_6 "model.vision_tower.embeddings.patch_embeddings.projection", # Intern-S1 "vpm.embeddings.patch_embedding", "model.vision_model.embeddings.patch_embedding", # SmolVLM @@ -1424,6 +1425,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_EMBD_POS: ( "vision_tower.vision_model.embeddings.position_embedding", + "model.vision_tower.embeddings.position_embedding", # minicpmv4_6 "model.vision_tower.embeddings.position_embeddings", # Intern-S1 "vpm.embeddings.position_embedding", "model.vision_model.embeddings.position_embedding", # SmolVLM @@ -1460,6 +1462,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_ATTN_Q: ( "vision_tower.vision_model.encoder.layers.{bid}.self_attn.q_proj", + "model.vision_tower.encoder.layers.{bid}.self_attn.q_proj", # minicpmv4_6 "model.vision_tower.encoder.layer.{bid}.attention.q_proj", # Intern-S1 "vpm.encoder.layers.{bid}.self_attn.q_proj", "model.vision_model.encoder.layers.{bid}.self_attn.q_proj", # SmolVLM @@ -1483,6 +1486,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_ATTN_K: ( "vision_tower.vision_model.encoder.layers.{bid}.self_attn.k_proj", + "model.vision_tower.encoder.layers.{bid}.self_attn.k_proj", # minicpmv4_6 "model.vision_tower.encoder.layer.{bid}.attention.k_proj", # Intern-S1 "vpm.encoder.layers.{bid}.self_attn.k_proj", "model.vision_model.encoder.layers.{bid}.self_attn.k_proj", # SmolVLM @@ -1506,6 +1510,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_ATTN_V: ( "vision_tower.vision_model.encoder.layers.{bid}.self_attn.v_proj", + "model.vision_tower.encoder.layers.{bid}.self_attn.v_proj", # minicpmv4_6 "model.vision_tower.encoder.layer.{bid}.attention.v_proj", # Intern-S1 "vpm.encoder.layers.{bid}.self_attn.v_proj", "model.vision_model.encoder.layers.{bid}.self_attn.v_proj", # SmolVLM @@ -1522,6 +1527,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_INPUT_NORM: ( "vision_tower.vision_model.encoder.layers.{bid}.layer_norm1", + "model.vision_tower.encoder.layers.{bid}.layer_norm1", # minicpmv4_6 "vision_tower.vision_model.encoder.layers.{bid}.norm1", # InternVL "model.vision_tower.encoder.layer.{bid}.layernorm_before", # Intern-S1 "vpm.encoder.layers.{bid}.layer_norm1", @@ -1542,6 +1548,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_ATTN_O: ( "vision_tower.vision_model.encoder.layers.{bid}.self_attn.out_proj", + "model.vision_tower.encoder.layers.{bid}.self_attn.out_proj", # minicpmv4_6 "vision_tower.vision_model.encoder.layers.{bid}.attn.proj", # InternVL "model.vision_tower.encoder.layer.{bid}.attention.projection_layer", # Intern-S1 "vpm.encoder.layers.{bid}.self_attn.out_proj", @@ -1564,6 +1571,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_POST_ATTN_NORM: ( "vision_tower.vision_model.encoder.layers.{bid}.layer_norm2", + "model.vision_tower.encoder.layers.{bid}.layer_norm2", # minicpmv4_6 "vision_tower.vision_model.encoder.layers.{bid}.norm2", # InternVL "model.vision_tower.encoder.layer.{bid}.layernorm_after", # Intern-S1 "vpm.encoder.layers.{bid}.layer_norm2", @@ -1585,6 +1593,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_FFN_UP: ( "vision_tower.vision_model.encoder.layers.{bid}.mlp.fc1", + "model.vision_tower.encoder.layers.{bid}.mlp.fc1", # minicpmv4_6 "model.vision_tower.encoder.layer.{bid}.mlp.fc1", # Intern-S1 "vpm.encoder.layers.{bid}.mlp.fc1", "model.vision_model.encoder.layers.{bid}.mlp.fc1", # SmolVLM, gemma3 @@ -1613,6 +1622,7 @@ class TensorNameMap: MODEL_TENSOR.V_ENC_FFN_DOWN: ( "vision_tower.vision_model.encoder.layers.{bid}.mlp.fc2", + "model.vision_tower.encoder.layers.{bid}.mlp.fc2", # minicpmv4_6 "model.vision_tower.encoder.layer.{bid}.mlp.fc2", # Intern-S1 "vpm.encoder.layers.{bid}.mlp.fc2", "model.vision_model.encoder.layers.{bid}.mlp.fc2", # SmolVLM, gemma3 @@ -1668,6 +1678,7 @@ class TensorNameMap: MODEL_TENSOR.V_POST_NORM: ( "vision_tower.vision_model.post_layernorm", + "model.vision_tower.post_layernorm", # minicpmv4_6 "model.vision_model.post_layernorm", # SmolVLM "vision_model.layernorm_post", # llama4 "visual.merger.ln_q", # qwen2vl @@ -1696,6 +1707,7 @@ class TensorNameMap: "mlp_AR.pre_norm", # PaddleOCR-VL "merger.ln_q", "vision_tower.merger.ln_q", # dots.ocr + "model.merger.mlp.0.pre_norm", # minicpmv4_6 ), MODEL_TENSOR.V_MM_SOFT_EMB_NORM: ( @@ -1769,6 +1781,38 @@ class TensorNameMap: "model.visual.deepstack_merger_list.{bid}.linear_fc2", # deepstack in qwen3vl ), + MODEL_TENSOR.V_MERGER_LN1: ( + "model.vision_tower.vit_merger.layer_norm1", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_ATTN_Q: ( + "model.vision_tower.vit_merger.self_attn.q_proj", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_ATTN_K: ( + "model.vision_tower.vit_merger.self_attn.k_proj", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_ATTN_V: ( + "model.vision_tower.vit_merger.self_attn.v_proj", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_ATTN_O: ( + "model.vision_tower.vit_merger.self_attn.out_proj", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_DS_LN: ( + "model.vision_tower.vit_merger.pre_norm", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_DS_UP: ( + "model.vision_tower.vit_merger.linear_1", # minicpmv4_6 + ), + + MODEL_TENSOR.V_MERGER_DS_DOWN: ( + "model.vision_tower.vit_merger.linear_2", # minicpmv4_6 + ), + MODEL_TENSOR.V_SAM_POS_EMBD: ( "model.sam_model.pos_embed", ), @@ -1828,11 +1872,13 @@ class TensorNameMap: MODEL_TENSOR.V_MM_UP: ( "model.vision.linear_proj.dense_h_to_4h", # cogvlm "visual.merger.up_proj", # glm4v + "model.merger.mlp.0.linear_1", # minicpmv4_6 ), MODEL_TENSOR.V_MM_DOWN: ( "model.vision.linear_proj.dense_4h_to_h", # cogvlm "visual.merger.down_proj", # glm4v + "model.merger.mlp.0.linear_2", # minicpmv4_6 ), MODEL_TENSOR.V_MM_GATE: ( diff --git a/tools/mtmd/README.md b/tools/mtmd/README.md index ef31d1957cd..70194194718 100644 --- a/tools/mtmd/README.md +++ b/tools/mtmd/README.md @@ -49,6 +49,7 @@ For the following models, you can use `convert_hf_to_gguf.py` with `--mmproj` fl - Qwen 2 VL and Qwen 2.5 VL (from [Qwen](https://huggingface.co/Qwen)) - [Mistral Small 3.1 24B](https://huggingface.co/mistralai/Mistral-Small-3.1-24B-Instruct-2503) - InternVL 2.5 and InternVL 3 from [OpenGVLab](https://huggingface.co/OpenGVLab) (note: we don't support conversion of `InternVL3-*-hf` model, only non-HF version is supported ; `InternLM2Model` **text** model is not supported) +- [MiniCPM-V 4.6](https://huggingface.co/openbmb/MiniCPM-V-4_6) ; See the guide [here](../../docs/multimodal/minicpmv4.6.md) - requires the standard `transformers` v5.7.0+ checkpoint For older models, please refer to the relevant guide for instructions on how to obtain or create them: @@ -60,4 +61,7 @@ NOTE: conversion scripts are located under `tools/mtmd/legacy-models` - [MiniCPM-V 2.5](../../docs/multimodal/minicpmv2.5.md) - [MiniCPM-V 2.6](../../docs/multimodal/minicpmv2.6.md) - [MiniCPM-o 2.6](../../docs/multimodal/minicpmo2.6.md) +- [MiniCPM-V 4.0](../../docs/multimodal/minicpmv4.0.md) +- [MiniCPM-o 4.0](../../docs/multimodal/minicpmo4.0.md) +- [MiniCPM-V 4.5](../../docs/multimodal/minicpmv4.5.md) - [IBM Granite Vision](../../docs/multimodal/granitevision.md) diff --git a/tools/mtmd/clip-impl.h b/tools/mtmd/clip-impl.h index 701dde5c656..817bf26b217 100644 --- a/tools/mtmd/clip-impl.h +++ b/tools/mtmd/clip-impl.h @@ -132,6 +132,17 @@ #define TN_MINICPMV_ATTN "resampler.attn.%s.%s" #define TN_MINICPMV_LN "resampler.ln_%s.%s" +// MiniCPM-V 4.6 ViT merger (window attention + MLP downsample), +// matching the upstream `vit_merger` module name in transformers. +#define TN_VIT_MERGER_LN1 "v.vit_merger.ln1.%s" +#define TN_VIT_MERGER_ATTN_Q "v.vit_merger.attn_q.%s" +#define TN_VIT_MERGER_ATTN_K "v.vit_merger.attn_k.%s" +#define TN_VIT_MERGER_ATTN_V "v.vit_merger.attn_v.%s" +#define TN_VIT_MERGER_ATTN_O "v.vit_merger.attn_out.%s" +#define TN_VIT_MERGER_DS_LN "v.vit_merger.ds_ln.%s" +#define TN_VIT_MERGER_DS_UP "v.vit_merger.ds_ffn_up.%s" +#define TN_VIT_MERGER_DS_DOWN "v.vit_merger.ds_ffn_down.%s" + #define TN_GLM_ADAPER_CONV "adapter.conv.%s" #define TN_GLM_ADAPTER_LINEAR "adapter.linear.linear.%s" #define TN_GLM_ADAPTER_NORM_1 "adapter.linear.norm1.%s" @@ -331,6 +342,7 @@ enum projector_type { PROJECTOR_TYPE_NEMOTRON_V2_VL, PROJECTOR_TYPE_HUNYUANOCR, PROJECTOR_TYPE_HUNYUANVL, + PROJECTOR_TYPE_MINICPMV4_6, PROJECTOR_TYPE_GRANITE_SPEECH, PROJECTOR_TYPE_UNKNOWN, }; @@ -379,6 +391,7 @@ static std::map PROJECTOR_TYPE_NAMES = { { PROJECTOR_TYPE_NEMOTRON_V2_VL, "nemotron_v2_vl"}, { PROJECTOR_TYPE_HUNYUANOCR, "hunyuanocr"}, { PROJECTOR_TYPE_HUNYUANVL, "hunyuanvl"}, + { PROJECTOR_TYPE_MINICPMV4_6, "minicpmv4_6"}, { PROJECTOR_TYPE_GRANITE_SPEECH, "granite_speech"}, }; diff --git a/tools/mtmd/clip-model.h b/tools/mtmd/clip-model.h index 391e9fc8ed9..48f8b1a193f 100644 --- a/tools/mtmd/clip-model.h +++ b/tools/mtmd/clip-model.h @@ -110,6 +110,7 @@ struct clip_hparams { bool has_llava_projector = false; int minicpmv_version = 0; int32_t minicpmv_query_num = 0; // MiniCPM-V query number + int32_t insert_layer_id = 0; // MiniCPM-V 4.6 ViT merger insertion layer // custom value provided by user, can be undefined if not set int32_t custom_image_min_tokens = -1; @@ -424,6 +425,24 @@ struct clip_model { ggml_tensor * mm_model_ln_post_w = nullptr; ggml_tensor * mm_model_ln_post_b = nullptr; + // MiniCPM-V 4.6 ViT merger (window self-attention + ViT MLP downsample) + ggml_tensor * vit_merger_ln1_w = nullptr; + ggml_tensor * vit_merger_ln1_b = nullptr; + ggml_tensor * vit_merger_attn_q_w = nullptr; + ggml_tensor * vit_merger_attn_q_b = nullptr; + ggml_tensor * vit_merger_attn_k_w = nullptr; + ggml_tensor * vit_merger_attn_k_b = nullptr; + ggml_tensor * vit_merger_attn_v_w = nullptr; + ggml_tensor * vit_merger_attn_v_b = nullptr; + ggml_tensor * vit_merger_attn_o_w = nullptr; + ggml_tensor * vit_merger_attn_o_b = nullptr; + ggml_tensor * vit_merger_ds_ln_w = nullptr; + ggml_tensor * vit_merger_ds_ln_b = nullptr; + ggml_tensor * vit_merger_ds_up_w = nullptr; + ggml_tensor * vit_merger_ds_up_b = nullptr; + ggml_tensor * vit_merger_ds_down_w = nullptr; + ggml_tensor * vit_merger_ds_down_b = nullptr; + // gemma3 ggml_tensor * mm_input_proj_w = nullptr; ggml_tensor * mm_soft_emb_norm_w = nullptr; diff --git a/tools/mtmd/clip.cpp b/tools/mtmd/clip.cpp index f35ae9fe348..513b94f2a65 100644 --- a/tools/mtmd/clip.cpp +++ b/tools/mtmd/clip.cpp @@ -874,6 +874,10 @@ static ggml_cgraph * clip_image_build_graph(clip_ctx * ctx, const clip_image_f32 { builder = std::make_unique(ctx, img); } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + builder = std::make_unique(ctx, img); + } break; case PROJECTOR_TYPE_INTERNVL: { builder = std::make_unique(ctx, img); @@ -1231,6 +1235,20 @@ struct clip_model_loader { hparams.minicpmv_version = 2; // default to 2 if not set } } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + // MiniCPM-V 4.6 unified merger projector + // ViT merger 2x2 + final merger 2x2 = 4x spatial merge per dimension + hparams.n_merge = 4; + get_u32(KEY_PROJ_SCALE_FACTOR, hparams.n_merge, false); + + // borrow wa_layer_indexes for vit_merger insertion point + std::vector wa_layer_indexes_vec; + get_arr_int(KEY_WIN_ATTN_LAYER_INDEXES, wa_layer_indexes_vec, false); + if (!wa_layer_indexes_vec.empty()) { + hparams.insert_layer_id = wa_layer_indexes_vec[0]; + } + } break; case PROJECTOR_TYPE_INTERNVL: { // use default llava-uhd preprocessing params @@ -1737,6 +1755,7 @@ struct clip_model_loader { || model.proj_type == PROJECTOR_TYPE_GEMMA3 || model.proj_type == PROJECTOR_TYPE_IDEFICS3 || model.proj_type == PROJECTOR_TYPE_MINICPMV + || model.proj_type == PROJECTOR_TYPE_MINICPMV4_6 ) && layer.ff_up_w && layer.ff_down_w && layer.ff_down_w->ne[0] == hparams.n_embd; if (is_ffn_swapped) { // swap up and down weights @@ -1838,6 +1857,34 @@ struct clip_model_loader { model.mm_model_ln_post_w = get_tensor(string_format(TN_MINICPMV_LN, "post", "weight")); model.mm_model_ln_post_b = get_tensor(string_format(TN_MINICPMV_LN, "post", "bias")); } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + // ViT merger: window self-attention + model.vit_merger_ln1_w = get_tensor(string_format(TN_VIT_MERGER_LN1, "weight")); + model.vit_merger_ln1_b = get_tensor(string_format(TN_VIT_MERGER_LN1, "bias")); + model.vit_merger_attn_q_w = get_tensor(string_format(TN_VIT_MERGER_ATTN_Q, "weight")); + model.vit_merger_attn_q_b = get_tensor(string_format(TN_VIT_MERGER_ATTN_Q, "bias"), false); + model.vit_merger_attn_k_w = get_tensor(string_format(TN_VIT_MERGER_ATTN_K, "weight")); + model.vit_merger_attn_k_b = get_tensor(string_format(TN_VIT_MERGER_ATTN_K, "bias"), false); + model.vit_merger_attn_v_w = get_tensor(string_format(TN_VIT_MERGER_ATTN_V, "weight")); + model.vit_merger_attn_v_b = get_tensor(string_format(TN_VIT_MERGER_ATTN_V, "bias"), false); + model.vit_merger_attn_o_w = get_tensor(string_format(TN_VIT_MERGER_ATTN_O, "weight")); + model.vit_merger_attn_o_b = get_tensor(string_format(TN_VIT_MERGER_ATTN_O, "bias"), false); + // ViT merger: MLP downsample + model.vit_merger_ds_ln_w = get_tensor(string_format(TN_VIT_MERGER_DS_LN, "weight")); + model.vit_merger_ds_ln_b = get_tensor(string_format(TN_VIT_MERGER_DS_LN, "bias")); + model.vit_merger_ds_up_w = get_tensor(string_format(TN_VIT_MERGER_DS_UP, "weight")); + model.vit_merger_ds_up_b = get_tensor(string_format(TN_VIT_MERGER_DS_UP, "bias"), false); + model.vit_merger_ds_down_w = get_tensor(string_format(TN_VIT_MERGER_DS_DOWN, "weight")); + model.vit_merger_ds_down_b = get_tensor(string_format(TN_VIT_MERGER_DS_DOWN, "bias"), false); + // Final Merger (DownsampleMLP) + model.mm_input_norm_w = get_tensor(TN_MM_INP_NORM); + model.mm_input_norm_b = get_tensor(TN_MM_INP_NORM_B, false); + model.mm_ffn_up_w = get_tensor(string_format(TN_MM_UP, "weight")); + model.mm_ffn_up_b = get_tensor(string_format(TN_MM_UP, "bias"), false); + model.mm_ffn_down_w = get_tensor(string_format(TN_MM_DOWN, "weight")); + model.mm_ffn_down_b = get_tensor(string_format(TN_MM_DOWN, "bias"), false); + } break; case PROJECTOR_TYPE_GLM_EDGE: { model.mm_model_adapter_conv_w = get_tensor(string_format(TN_GLM_ADAPER_CONV, "weight")); @@ -3055,6 +3102,11 @@ int clip_n_output_tokens(const struct clip_ctx * ctx, struct clip_image_f32 * im } } } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + // ViT merger 4x + final merger 4x = 16x total spatial downsample + n_patches = n_patches / 16; + } break; case PROJECTOR_TYPE_QWEN2VL: case PROJECTOR_TYPE_QWEN25VL: case PROJECTOR_TYPE_QWEN3VL: @@ -3377,6 +3429,92 @@ bool clip_image_batch_encode(clip_ctx * ctx, const int n_threads, const clip_ima } set_input_f32("omega", omega); } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + // SigLIP position buckets (same as resampler path) + std::vector positions(pos_h * pos_w); + int bucket_coords_h[1024]; + int bucket_coords_w[1024]; + for (int i = 0; i < pos_h; i++){ + bucket_coords_h[i] = std::floor(70.0*i/pos_h); + } + for (int i = 0; i < pos_w; i++){ + bucket_coords_w[i] = std::floor(70.0*i/pos_w); + } + for (int i = 0, id = 0; i < pos_h; i++){ + for (int j = 0; j < pos_w; j++){ + positions[id++] = bucket_coords_h[i]*70 + bucket_coords_w[j]; + } + } + set_input_i32("positions", positions); + + const int half_h = pos_h / 2; + const int half_w = pos_w / 2; + + // window reorder indices for 2x2 windows + std::vector window_idx(n_pos); + std::vector inv_window_idx(n_pos); + { + int k = 0; + for (int wi = 0; wi < half_h; wi++) { + for (int wj = 0; wj < half_w; wj++) { + window_idx[k++] = (2*wi ) * pos_w + (2*wj ); + window_idx[k++] = (2*wi ) * pos_w + (2*wj + 1); + window_idx[k++] = (2*wi + 1) * pos_w + (2*wj ); + window_idx[k++] = (2*wi + 1) * pos_w + (2*wj + 1); + } + } + for (int i = 0; i < n_pos; i++) { + inv_window_idx[window_idx[i]] = i; + } + } + set_input_i32("vit_merger_window_idx", window_idx); + set_input_i32("vit_merger_inv_window_idx", inv_window_idx); + + // block-diagonal attention mask: tokens in the same 4-token + // window attend to each other (mask = 0), all other positions + // are masked out (-inf). matches the window-major reorder above. + std::vector window_mask_data(n_pos * n_pos, std::numeric_limits::lowest()); + for (int wi = 0; wi < n_pos / 4; wi++) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + window_mask_data[(wi*4 + i) * n_pos + (wi*4 + j)] = 0.0f; + } + } + } + set_input_f32("vit_merger_window_mask", window_mask_data); + + // ViT merger 2x2 downsample indices + auto make_ds_idx = [](int off_r, int off_c, int ds_h, int ds_w, int stride_w) { + std::vector idx(ds_h * ds_w); + for (int i = 0; i < ds_h; i++) { + for (int j = 0; j < ds_w; j++) { + idx[i * ds_w + j] = (2*i + off_r) * stride_w + (2*j + off_c); + } + } + return idx; + }; + auto vit_merger_ds_0 = make_ds_idx(0, 0, half_h, half_w, pos_w); + auto vit_merger_ds_1 = make_ds_idx(0, 1, half_h, half_w, pos_w); + auto vit_merger_ds_2 = make_ds_idx(1, 0, half_h, half_w, pos_w); + auto vit_merger_ds_3 = make_ds_idx(1, 1, half_h, half_w, pos_w); + set_input_i32("vit_merger_ds_idx_0", vit_merger_ds_0); + set_input_i32("vit_merger_ds_idx_1", vit_merger_ds_1); + set_input_i32("vit_merger_ds_idx_2", vit_merger_ds_2); + set_input_i32("vit_merger_ds_idx_3", vit_merger_ds_3); + + // final merger 2x2 downsample indices (operates on half_h x half_w grid) + const int qh = half_h / 2; + const int qw = half_w / 2; + auto m_ds_0 = make_ds_idx(0, 0, qh, qw, half_w); + auto m_ds_1 = make_ds_idx(0, 1, qh, qw, half_w); + auto m_ds_2 = make_ds_idx(1, 0, qh, qw, half_w); + auto m_ds_3 = make_ds_idx(1, 1, qh, qw, half_w); + set_input_i32("merger_ds_idx_0", m_ds_0); + set_input_i32("merger_ds_idx_1", m_ds_1); + set_input_i32("merger_ds_idx_2", m_ds_2); + set_input_i32("merger_ds_idx_3", m_ds_3); + } break; case PROJECTOR_TYPE_QWEN2VL: case PROJECTOR_TYPE_QWEN3VL: case PROJECTOR_TYPE_GLM4V: @@ -3931,6 +4069,8 @@ int clip_n_mmproj_embd(const struct clip_ctx * ctx) { return ctx->model.mm_3_b->ne[0]; case PROJECTOR_TYPE_MINICPMV: return ctx->model.mm_model_proj->ne[0]; + case PROJECTOR_TYPE_MINICPMV4_6: + return ctx->model.mm_ffn_down_w->ne[1]; case PROJECTOR_TYPE_GLM_EDGE: return ctx->model.mm_model_mlp_3_w->ne[1]; case PROJECTOR_TYPE_QWEN2VL: @@ -3997,6 +4137,9 @@ int clip_is_minicpmv(const struct clip_ctx * ctx) { if (ctx->proj_type() == PROJECTOR_TYPE_MINICPMV) { return ctx->model.hparams.minicpmv_version; } + if (ctx->proj_type() == PROJECTOR_TYPE_MINICPMV4_6) { + return 46; + } return 0; } diff --git a/tools/mtmd/models/minicpmv.cpp b/tools/mtmd/models/minicpmv.cpp index 924117ab2a1..bac087ffdfc 100644 --- a/tools/mtmd/models/minicpmv.cpp +++ b/tools/mtmd/models/minicpmv.cpp @@ -112,3 +112,294 @@ ggml_cgraph * clip_graph_minicpmv::build() { return gf; } + +ggml_cgraph * clip_graph_minicpmv4_6::build() { + const int insert_lid = hparams.insert_layer_id; + const int n_pos = n_patches; + const int half_h = n_patches_y / 2; + const int half_w = n_patches_x / 2; + const int n_ds = half_h * half_w; // after ViT merger 2x2 downsample + const int qh = half_h / 2; + const int qw = half_w / 2; + const int n_ds2 = qh * qw; // after final merger 2x2 downsample + + auto add_i32_input = [&](const char * name, int n) { + ggml_tensor * t = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n); + ggml_set_name(t, name); + ggml_set_input(t); + return t; + }; + + // position indices for ViT learned positional embeddings + ggml_tensor * positions = add_i32_input("positions", n_pos); + ggml_tensor * learned_pos_embd = ggml_get_rows(ctx0, model.position_embeddings, positions); + + // ViT merger window reorder indices + block-diagonal mask + // (mask layout follows qwen2vl: -inf except for 4x4 blocks on the diagonal, + // so each window-major group of 4 tokens only attends to itself) + ggml_tensor * vit_merger_window_idx = add_i32_input("vit_merger_window_idx", n_pos); + ggml_tensor * vit_merger_inv_window_idx = add_i32_input("vit_merger_inv_window_idx", n_pos); + ggml_tensor * vit_merger_window_mask = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_pos, n_pos); + ggml_set_name(vit_merger_window_mask, "vit_merger_window_mask"); + ggml_set_input(vit_merger_window_mask); + if (flash_attn_type == CLIP_FLASH_ATTN_TYPE_ENABLED) { + vit_merger_window_mask = ggml_cast(ctx0, vit_merger_window_mask, GGML_TYPE_F16); + } + + // ViT merger 2x2 downsample gather indices + ggml_tensor * vit_merger_ds_idx_0 = add_i32_input("vit_merger_ds_idx_0", n_ds); + ggml_tensor * vit_merger_ds_idx_1 = add_i32_input("vit_merger_ds_idx_1", n_ds); + ggml_tensor * vit_merger_ds_idx_2 = add_i32_input("vit_merger_ds_idx_2", n_ds); + ggml_tensor * vit_merger_ds_idx_3 = add_i32_input("vit_merger_ds_idx_3", n_ds); + + // final merger 2x2 downsample gather indices + ggml_tensor * merger_ds_idx_0 = add_i32_input("merger_ds_idx_0", n_ds2); + ggml_tensor * merger_ds_idx_1 = add_i32_input("merger_ds_idx_1", n_ds2); + ggml_tensor * merger_ds_idx_2 = add_i32_input("merger_ds_idx_2", n_ds2); + ggml_tensor * merger_ds_idx_3 = add_i32_input("merger_ds_idx_3", n_ds2); + + // patch embedding + positional embedding + ggml_tensor * inp = build_inp(); + inp = ggml_add(ctx0, inp, learned_pos_embd); + cb(inp, "pos_embed", -1); + + ggml_tensor * inpL = inp; + if (model.pre_ln_w) { + inpL = build_norm(inpL, model.pre_ln_w, model.pre_ln_b, NORM_TYPE_NORMAL, eps, -1); + cb(inpL, "pre_ln", -1); + } + + // ViT layers 0..insert_layer_id (inclusive) + // Mirrors the separate-qkv path of clip_graph::build_vit so the two manually + // unrolled segments around the ViT merger read like build_vit() expansions. + for (int il = 0; il <= insert_lid; il++) { + auto & layer = model.layers[il]; + ggml_tensor * cur = inpL; + + cur = build_norm(cur, layer.ln_1_w, layer.ln_1_b, NORM_TYPE_NORMAL, eps, il); + cb(cur, "layer_inp_normed", il); + + { + ggml_tensor * Qcur = build_mm(layer.q_w, cur); + if (layer.q_b) { + Qcur = ggml_add(ctx0, Qcur, layer.q_b); + } + ggml_tensor * Kcur = build_mm(layer.k_w, cur); + if (layer.k_b) { + Kcur = ggml_add(ctx0, Kcur, layer.k_b); + } + ggml_tensor * Vcur = build_mm(layer.v_w, cur); + if (layer.v_b) { + Vcur = ggml_add(ctx0, Vcur, layer.v_b); + } + + Qcur = ggml_reshape_3d(ctx0, Qcur, d_head, n_head, n_pos); + Kcur = ggml_reshape_3d(ctx0, Kcur, d_head, n_head, n_pos); + Vcur = ggml_reshape_3d(ctx0, Vcur, d_head, n_head, n_pos); + cb(Qcur, "Qcur", il); + cb(Kcur, "Kcur", il); + cb(Vcur, "Vcur", il); + + cur = build_attn(layer.o_w, layer.o_b, Qcur, Kcur, Vcur, nullptr, kq_scale, il); + cb(cur, "attn_out", il); + } + + if (layer.ls_1_w) { + cur = ggml_mul(ctx0, cur, layer.ls_1_w); + cb(cur, "attn_out_scaled", il); + } + cur = ggml_add(ctx0, cur, inpL); + inpL = cur; + cb(cur, "ffn_inp", il); + + cur = build_norm(cur, layer.ln_2_w, layer.ln_2_b, NORM_TYPE_NORMAL, eps, il); + cb(cur, "ffn_inp_normed", il); + + cur = build_ffn(cur, layer.ff_up_w, layer.ff_up_b, layer.ff_gate_w, layer.ff_gate_b, + layer.ff_down_w, layer.ff_down_b, hparams.ffn_op, il); + cb(cur, "ffn_out", il); + + if (layer.ls_2_w) { + cur = ggml_mul(ctx0, cur, layer.ls_2_w); + cb(cur, "ffn_out_scaled", il); + } + cur = ggml_add(ctx0, inpL, cur); + cb(cur, "layer_out", il); + + inpL = cur; + } + + // ViT merger: window self-attention + // Tokens are reordered to window-major (4 tokens per window are contiguous), + // and a block-diagonal mask restricts attention to within each window. This + // mirrors the qwen2vl windowed-attention pattern so build_attn() can pick the + // flash-attention path when available. + { + ggml_tensor * residual = inpL; + ggml_tensor * cur = build_norm(inpL, + model.vit_merger_ln1_w, model.vit_merger_ln1_b, + NORM_TYPE_NORMAL, eps, -1); + cb(cur, "vit_merger_attn_inp_normed", -1); + + cur = ggml_get_rows(ctx0, cur, vit_merger_window_idx); + cb(cur, "vit_merger_window_reorder", -1); + + ggml_tensor * Qcur = build_mm(model.vit_merger_attn_q_w, cur); + if (model.vit_merger_attn_q_b) { + Qcur = ggml_add(ctx0, Qcur, model.vit_merger_attn_q_b); + } + ggml_tensor * Kcur = build_mm(model.vit_merger_attn_k_w, cur); + if (model.vit_merger_attn_k_b) { + Kcur = ggml_add(ctx0, Kcur, model.vit_merger_attn_k_b); + } + ggml_tensor * Vcur = build_mm(model.vit_merger_attn_v_w, cur); + if (model.vit_merger_attn_v_b) { + Vcur = ggml_add(ctx0, Vcur, model.vit_merger_attn_v_b); + } + + Qcur = ggml_reshape_3d(ctx0, Qcur, d_head, n_head, n_pos); + Kcur = ggml_reshape_3d(ctx0, Kcur, d_head, n_head, n_pos); + Vcur = ggml_reshape_3d(ctx0, Vcur, d_head, n_head, n_pos); + cb(Qcur, "vit_merger_Qcur", -1); + cb(Kcur, "vit_merger_Kcur", -1); + cb(Vcur, "vit_merger_Vcur", -1); + + cur = build_attn(model.vit_merger_attn_o_w, model.vit_merger_attn_o_b, + Qcur, Kcur, Vcur, vit_merger_window_mask, kq_scale, -1); + cb(cur, "vit_merger_attn_out", -1); + + cur = ggml_get_rows(ctx0, cur, vit_merger_inv_window_idx); + inpL = ggml_add(ctx0, cur, residual); + cb(inpL, "vit_merger_attn_residual", -1); + } + + // ViT merger: 2x2 spatial downsample + MLP (4 tokens -> 1) + { + ggml_tensor * p0 = ggml_get_rows(ctx0, inpL, vit_merger_ds_idx_0); + ggml_tensor * p1 = ggml_get_rows(ctx0, inpL, vit_merger_ds_idx_1); + ggml_tensor * p2 = ggml_get_rows(ctx0, inpL, vit_merger_ds_idx_2); + ggml_tensor * p3 = ggml_get_rows(ctx0, inpL, vit_merger_ds_idx_3); + + ggml_tensor * mean_res = ggml_add(ctx0, p0, p1); + mean_res = ggml_add(ctx0, mean_res, p2); + mean_res = ggml_add(ctx0, mean_res, p3); + mean_res = ggml_scale(ctx0, mean_res, 0.25f); + cb(mean_res, "vit_merger_ds_mean_res", -1); + + ggml_tensor * cat = ggml_concat(ctx0, p0, p1, 0); + cat = ggml_concat(ctx0, cat, p2, 0); + cat = ggml_concat(ctx0, cat, p3, 0); + + ggml_tensor * cur = build_norm(cat, + model.vit_merger_ds_ln_w, model.vit_merger_ds_ln_b, + NORM_TYPE_NORMAL, eps, -1); + cb(cur, "vit_merger_ds_normed", -1); + + // ViTWindowAttentionMerger downsample MLP uses gelu_pytorch_tanh (FFN_GELU) + cur = build_ffn(cur, + model.vit_merger_ds_up_w, model.vit_merger_ds_up_b, + nullptr, nullptr, + model.vit_merger_ds_down_w, model.vit_merger_ds_down_b, + FFN_GELU, -1); + cb(cur, "vit_merger_ds_mlp_out", -1); + + inpL = ggml_add(ctx0, cur, mean_res); + cb(inpL, "vit_merger_ds_out", -1); + } + + // ViT layers (insert_layer_id+1)..n_layer-1, operating on the downsampled tokens + { + const int64_t n_pos_ds = n_ds; + for (int il = insert_lid + 1; il < n_layer; il++) { + auto & layer = model.layers[il]; + ggml_tensor * cur = inpL; + + cur = build_norm(cur, layer.ln_1_w, layer.ln_1_b, NORM_TYPE_NORMAL, eps, il); + cb(cur, "layer_inp_normed", il); + + { + ggml_tensor * Qcur = build_mm(layer.q_w, cur); + if (layer.q_b) { + Qcur = ggml_add(ctx0, Qcur, layer.q_b); + } + ggml_tensor * Kcur = build_mm(layer.k_w, cur); + if (layer.k_b) { + Kcur = ggml_add(ctx0, Kcur, layer.k_b); + } + ggml_tensor * Vcur = build_mm(layer.v_w, cur); + if (layer.v_b) { + Vcur = ggml_add(ctx0, Vcur, layer.v_b); + } + + Qcur = ggml_reshape_3d(ctx0, Qcur, d_head, n_head, n_pos_ds); + Kcur = ggml_reshape_3d(ctx0, Kcur, d_head, n_head, n_pos_ds); + Vcur = ggml_reshape_3d(ctx0, Vcur, d_head, n_head, n_pos_ds); + cb(Qcur, "Qcur", il); + cb(Kcur, "Kcur", il); + cb(Vcur, "Vcur", il); + + cur = build_attn(layer.o_w, layer.o_b, Qcur, Kcur, Vcur, nullptr, kq_scale, il); + cb(cur, "attn_out", il); + } + + if (layer.ls_1_w) { + cur = ggml_mul(ctx0, cur, layer.ls_1_w); + cb(cur, "attn_out_scaled", il); + } + cur = ggml_add(ctx0, cur, inpL); + inpL = cur; + cb(cur, "ffn_inp", il); + + cur = build_norm(cur, layer.ln_2_w, layer.ln_2_b, NORM_TYPE_NORMAL, eps, il); + cb(cur, "ffn_inp_normed", il); + + cur = build_ffn(cur, layer.ff_up_w, layer.ff_up_b, layer.ff_gate_w, layer.ff_gate_b, + layer.ff_down_w, layer.ff_down_b, hparams.ffn_op, il); + cb(cur, "ffn_out", il); + + if (layer.ls_2_w) { + cur = ggml_mul(ctx0, cur, layer.ls_2_w); + cb(cur, "ffn_out_scaled", il); + } + cur = ggml_add(ctx0, inpL, cur); + cb(cur, "layer_out", il); + + inpL = cur; + } + } + + if (model.post_ln_w) { + inpL = build_norm(inpL, model.post_ln_w, model.post_ln_b, NORM_TYPE_NORMAL, eps, -1); + cb(inpL, "post_ln", -1); + } + + // Final Merger (DownsampleMLP): another 2x2 spatial merge -> projector embedding + { + ggml_tensor * p0 = ggml_get_rows(ctx0, inpL, merger_ds_idx_0); + ggml_tensor * p1 = ggml_get_rows(ctx0, inpL, merger_ds_idx_1); + ggml_tensor * p2 = ggml_get_rows(ctx0, inpL, merger_ds_idx_2); + ggml_tensor * p3 = ggml_get_rows(ctx0, inpL, merger_ds_idx_3); + + ggml_tensor * cat = ggml_concat(ctx0, p0, p1, 0); + cat = ggml_concat(ctx0, cat, p2, 0); + cat = ggml_concat(ctx0, cat, p3, 0); + + ggml_tensor * cur = build_norm(cat, + model.mm_input_norm_w, model.mm_input_norm_b, + NORM_TYPE_NORMAL, eps, -1); + cb(cur, "merger_normed", -1); + + // MiniCPMV4_6DownsampleMLP uses nn.GELU() (erf-based, FFN_GELU_ERF) + cur = build_ffn(cur, + model.mm_ffn_up_w, model.mm_ffn_up_b, + nullptr, nullptr, + model.mm_ffn_down_w, model.mm_ffn_down_b, + FFN_GELU_ERF, -1); + cb(cur, "merger_out", -1); + + inpL = cur; + } + + ggml_build_forward_expand(gf, inpL); + return gf; +} diff --git a/tools/mtmd/models/models.h b/tools/mtmd/models/models.h index 42d1fb22426..dbba233b16f 100644 --- a/tools/mtmd/models/models.h +++ b/tools/mtmd/models/models.h @@ -56,6 +56,11 @@ struct clip_graph_minicpmv : clip_graph { ggml_cgraph * build() override; }; +struct clip_graph_minicpmv4_6 : clip_graph { + clip_graph_minicpmv4_6(clip_ctx * ctx, const clip_image_f32 & img) : clip_graph(ctx, img) {} + ggml_cgraph * build() override; +}; + struct clip_graph_internvl : clip_graph { clip_graph_internvl(clip_ctx * ctx, const clip_image_f32 & img) : clip_graph(ctx, img) {} ggml_cgraph * build() override; diff --git a/tools/mtmd/mtmd-image.cpp b/tools/mtmd/mtmd-image.cpp index c1a36011788..1b058e02601 100644 --- a/tools/mtmd/mtmd-image.cpp +++ b/tools/mtmd/mtmd-image.cpp @@ -584,7 +584,9 @@ bool mtmd_image_preprocessor_llava_uhd::preprocess(const clip_image_u8 & img, cl mtmd_image_preprocessor_llava_uhd::slice_instructions mtmd_image_preprocessor_llava_uhd::get_slice_instructions(const clip_image_size & original_size) { mtmd_image_preprocessor_llava_uhd::slice_instructions res; - const int patch_size = hparams.patch_size; + // align slices by patch_size * n_merge so an integer number of merger output tokens fits per slice + const int n_merge = hparams.n_merge > 0 ? hparams.n_merge : 1; + const int patch_size = hparams.patch_size * n_merge; const int slice_size = hparams.image_size; const int original_width = original_size.width; const int original_height = original_size.height; diff --git a/tools/mtmd/mtmd.cpp b/tools/mtmd/mtmd.cpp index ed39b398b97..87da6876f7c 100644 --- a/tools/mtmd/mtmd.cpp +++ b/tools/mtmd/mtmd.cpp @@ -310,6 +310,18 @@ struct mtmd_context { } image_preproc = std::make_unique(ctx_v); } break; + case PROJECTOR_TYPE_MINICPMV4_6: + { + slice_tmpl = MTMD_SLICE_TMPL_MINICPMV_2_6; + tok_ov_img_start = {lookup_token("")}; + tok_ov_img_end = {lookup_token("")}; + tok_sli_img_start = {lookup_token("")}; + tok_sli_img_end = {lookup_token("")}; + tok_row_end = {lookup_token("\n")}; + tok_row_end_trail = false; // no trailing end-of-row token + ov_img_first = true; + image_preproc = std::make_unique(ctx_v); + } break; case PROJECTOR_TYPE_QWEN2VL: case PROJECTOR_TYPE_QWEN25VL: case PROJECTOR_TYPE_QWEN3VL: From 3980e04d5a374da025e1942eb1043ac2f33e6a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Gallou=C3=ABt?= Date: Thu, 7 May 2026 07:24:47 +0200 Subject: [PATCH 002/158] llama : add missing call to ggml_backend_load_all() (#22752) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Adrien Gallouët --- src/llama.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/llama.cpp b/src/llama.cpp index b5dd8433c4b..dfe30ce8f61 100644 --- a/src/llama.cpp +++ b/src/llama.cpp @@ -71,12 +71,18 @@ bool llama_supports_mlock(void) { } bool llama_supports_gpu_offload(void) { + if (!ggml_backend_reg_count()) { + ggml_backend_load_all(); + } return ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_GPU) != nullptr || ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_IGPU) != nullptr || llama_supports_rpc(); } bool llama_supports_rpc(void) { + if (!ggml_backend_reg_count()) { + ggml_backend_load_all(); + } return ggml_backend_reg_by_name("RPC") != nullptr; } From cfff1fc3003239cfe00aca031345107687d30d7b Mon Sep 17 00:00:00 2001 From: Shane Tran Whitmire <64436119+dogunbound@users.noreply.github.com> Date: Thu, 7 May 2026 00:25:57 -0500 Subject: [PATCH 003/158] sycl : fix test script (#22737) The error: ./examples/sycl/test.sh: line 122: level_zero:${$GGML_SYCL_DEVICE}: bad substitution was thrown whenever the user used this command: ./examples/sycl/test.sh -mg 0 Fix is to get rid of a dollar sign. --- examples/sycl/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sycl/test.sh b/examples/sycl/test.sh index 14dcac56ad8..38d2e926896 100755 --- a/examples/sycl/test.sh +++ b/examples/sycl/test.sh @@ -119,7 +119,7 @@ if [ $GGML_SYCL_DEVICE -ne -1 ]; then echo "Use $GGML_SYCL_DEVICE as main GPU" #use signle GPU only GPUS_SETTING="-mg $GGML_SYCL_DEVICE -sm ${SPLIT_MODE}" - export ONEAPI_DEVICE_SELECTOR="level_zero:${$GGML_SYCL_DEVICE}" + export ONEAPI_DEVICE_SELECTOR="level_zero:${GGML_SYCL_DEVICE}" echo "ONEAPI_DEVICE_SELECTOR=${ONEAPI_DEVICE_SELECTOR}" else echo "Use all Intel GPUs, including iGPU & dGPU" From e358d75adbfa28672143e8c0fa37db3c8e788843 Mon Sep 17 00:00:00 2001 From: viggy <70774793+vignesh191@users.noreply.github.com> Date: Wed, 6 May 2026 23:11:31 -0700 Subject: [PATCH 004/158] webui: fix flicker issue on dismiss animation on overlay primitives (#22773) * add fill-mode-forwards * generated diffs --- tools/server/public/bundle.css | 2 +- tools/server/public/bundle.js | 772 +++++++++--------- .../alert-dialog/alert-dialog-content.svelte | 2 +- .../alert-dialog/alert-dialog-overlay.svelte | 2 +- .../ui/dialog/dialog-content.svelte | 2 +- .../ui/dialog/dialog-overlay.svelte | 2 +- .../dropdown-menu-content.svelte | 2 +- .../dropdown-menu-sub-content.svelte | 2 +- .../ui/popover/popover-content.svelte | 2 +- .../ui/select/select-content.svelte | 2 +- .../components/ui/sheet/sheet-content.svelte | 2 +- .../components/ui/sheet/sheet-overlay.svelte | 2 +- .../ui/tooltip/tooltip-content.svelte | 2 +- 13 files changed, 399 insertions(+), 397 deletions(-) diff --git a/tools/server/public/bundle.css b/tools/server/public/bundle.css index ac69196e64c..0ae8085e7c5 100644 --- a/tools/server/public/bundle.css +++ b/tools/server/public/bundle.css @@ -1 +1 @@ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-green-50:oklch(98.2% .018 155.826);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-950:oklch(26.6% .065 152.934);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-600:oklch(60.9% .126 221.723);--color-cyan-950:oklch(30.2% .056 229.695);--color-blue-50:oklch(97% .014 254.604);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-950:oklch(28.2% .091 267.935);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-600:oklch(59.2% .249 .584);--color-pink-950:oklch(28.4% .109 3.907);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-xs:.125rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-xs:4px;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-foreground:var(--foreground)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground);scrollbar-width:thin;scrollbar-gutter:stable}*{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .2s}:hover{scrollbar-color:hsl(var(--muted-foreground)/.3)transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:0 0;border-radius:3px;transition:background .2s}:hover::-webkit-scrollbar-thumb{background:hsl(var(--muted-foreground)/.3)}::-webkit-scrollbar-thumb:hover{background:hsl(var(--muted-foreground)/.5)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.top-3\.5{top:calc(var(--spacing)*3.5)}.top-4{top:calc(var(--spacing)*4)}.top-4\.5{top:calc(var(--spacing)*4.5)}.top-10{top:calc(var(--spacing)*10)}.top-\[50\%\]{top:50%}.top-full{top:100%}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.right-8{right:calc(var(--spacing)*8)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-3{bottom:calc(var(--spacing)*3)}.bottom-4{bottom:calc(var(--spacing)*4)}.bottom-32{bottom:calc(var(--spacing)*32)}.bottom-44{bottom:calc(var(--spacing)*44)}.bottom-\[calc\(50dvh-7rem\)\]{bottom:calc(50dvh - 7rem)}.left-0{left:calc(var(--spacing)*0)}.left-0\!{left:calc(var(--spacing)*0)!important}.left-2{left:calc(var(--spacing)*2)}.left-3{left:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.left-\[50\%\]{left:50%}.left-\[calc\(var\(--sidebar-width\)\+0\.75rem\)\]{left:calc(var(--sidebar-width) + .75rem)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-9999{z-index:9999}.z-\[20\]{z-index:20}.z-\[900\]{z-index:900}.z-\[1000\]{z-index:1000}.z-\[9999\]{z-index:9999}.z-\[999999\]{z-index:999999}.z-\[1000000\]{z-index:1000000}.z-\[1000001\]{z-index:1000001}.z-\[var\(--layer-popover\,1000000\)\]{z-index:var(--layer-popover,1000000)}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-3\.5{margin-inline:calc(var(--spacing)*3.5)}.mx-auto{margin-inline:auto}.-my-1{margin-block:calc(var(--spacing)*-1)}.-my-2{margin-block:calc(var(--spacing)*-2)}.-my-4{margin-block:calc(var(--spacing)*-4)}.my-1{margin-block:calc(var(--spacing)*1)}.my-1\.5{margin-block:calc(var(--spacing)*1.5)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-6{margin-block:calc(var(--spacing)*6)}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-12{margin-top:calc(var(--spacing)*12)}.mt-auto{margin-top:auto}.-mr-1{margin-right:calc(var(--spacing)*-1)}.-mr-1\.5{margin-right:calc(var(--spacing)*-1.5)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-auto{margin-right:auto}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-11{margin-left:calc(var(--spacing)*11)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-2\.5{width:calc(var(--spacing)*2.5);height:calc(var(--spacing)*2.5)}.size-3{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.size-10{width:calc(var(--spacing)*10);height:calc(var(--spacing)*10)}.size-full{width:100%;height:100%}.h-\(--bits-select-anchor-height\){height:var(--bits-select-anchor-height)}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-16{height:calc(var(--spacing)*16)}.h-24{height:calc(var(--spacing)*24)}.h-40{height:calc(var(--spacing)*40)}.h-48{height:calc(var(--spacing)*48)}.h-\[1\.15rem\]{height:1.15rem}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[calc\(100vh-21rem\)\]{height:calc(100vh - 21rem)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-svh{height:100svh}.\!max-h-\[50vh\]{max-height:50vh!important}.\!max-h-\[80dvh\]{max-height:80dvh!important}.max-h-\(--bits-dropdown-menu-content-available-height\){max-height:var(--bits-dropdown-menu-content-available-height)}.max-h-\(--bits-select-content-available-height\){max-height:var(--bits-select-content-available-height)}.max-h-24{max-height:calc(var(--spacing)*24)}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-80{max-height:calc(var(--spacing)*80)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[100dvh\]{max-height:100dvh}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-4{min-height:calc(var(--spacing)*4)}.min-h-9{min-height:calc(var(--spacing)*9)}.min-h-12{min-height:calc(var(--spacing)*12)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-\[10rem\]{min-height:10rem}.min-h-\[48px\]{min-height:48px}.min-h-\[60px\]{min-height:60px}.min-h-\[200px\]{min-height:200px}.min-h-full{min-height:100%}.min-h-svh{min-height:100svh}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-0{width:calc(var(--spacing)*0)}.w-1{width:calc(var(--spacing)*1)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-28{width:calc(var(--spacing)*28)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-40{width:calc(var(--spacing)*40)}.w-48{width:calc(var(--spacing)*48)}.w-52{width:calc(var(--spacing)*52)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-\[10rem\]{width:10rem}.w-\[56rem\]{width:56rem}.w-\[calc\(100dvw-1\.5rem\)\]{width:calc(100dvw - 1.5rem)}.w-\[calc\(100vw-2rem\)\]{width:calc(100vw - 2rem)}.w-\[calc\(var\(--sidebar-width-icon\)\+1\.5rem\)\]{width:calc(var(--sidebar-width-icon) + 1.5rem)}.w-\[var\(--bits-popover-anchor-width\)\]{width:var(--bits-popover-anchor-width)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.\!max-w-4xl{max-width:var(--container-4xl)!important}.\!max-w-\[60rem\]{max-width:60rem!important}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-24{max-width:calc(var(--spacing)*24)}.max-w-36{max-width:calc(var(--spacing)*36)}.max-w-72{max-width:calc(var(--spacing)*72)}.max-w-\[17rem\]{max-width:17rem}.max-w-\[48rem\]{max-width:48rem}.max-w-\[56rem\]{max-width:56rem}.max-w-\[80\%\]{max-width:80%}.max-w-\[80vw\]{max-width:80vw}.max-w-\[85vw\]{max-width:85vw}.max-w-\[100vw\]{max-width:100vw}.max-w-\[150px\]{max-width:150px}.max-w-\[300px\]{max-width:300px}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-xs{max-width:var(--container-xs)}.min-w-\(--bits-select-anchor-width\){min-width:var(--bits-select-anchor-width)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-5{min-width:calc(var(--spacing)*5)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[200px\]{min-width:200px}.min-w-max{min-width:max-content}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-\(--bits-dropdown-menu-content-transform-origin\){transform-origin:var(--bits-dropdown-menu-content-transform-origin)}.origin-\(--bits-popover-content-transform-origin\){transform-origin:var(--bits-popover-content-transform-origin)}.origin-\(--bits-select-content-transform-origin\){transform-origin:var(--bits-select-content-transform-origin)}.origin-\(--bits-tooltip-content-transform-origin\){transform-origin:var(--bits-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-0{--tw-translate-y:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-45{rotate:45deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.\!cursor-not-allowed{cursor:not-allowed!important}.cursor-auto{cursor:auto}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-\[0_1fr\]{grid-template-columns:0 1fr}.grid-cols-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.justify-items-start{justify-items:start}.\!gap-3{gap:calc(var(--spacing)*3)!important}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-0\.75{gap:calc(var(--spacing)*.75)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-1\.25{gap:calc(var(--spacing)*1.25)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-5{gap:calc(var(--spacing)*5)}.gap-6{gap:calc(var(--spacing)*6)}.gap-10{gap:calc(var(--spacing)*10)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.-space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*-1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*-1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-0\.5{row-gap:calc(var(--spacing)*.5)}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.justify-self-end{justify-self:flex-end}.justify-self-start{justify-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-\[1\.125rem\]{border-radius:1.125rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[4px\]{border-radius:4px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none\!{border-radius:0!important}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.rounded-t-lg{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-t-lg\!{border-top-left-radius:var(--radius)!important;border-top-right-radius:var(--radius)!important}.\!border-2{border-style:var(--tw-border-style)!important;border-width:2px!important}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.\!border-dashed{--tw-border-style:dashed!important;border-style:dashed!important}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.\!border-border\/50{border-color:var(--border)!important}@supports (color:color-mix(in lab,red,red)){.\!border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)!important}}.border-amber-500\/40{border-color:#f99c0066}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/40{border-color:color-mix(in oklab,var(--color-amber-500)40%,transparent)}}.border-border,.border-border\/30{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/30{border-color:color-mix(in oklab,var(--border)30%,transparent)}}.border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)}}.border-current{border-color:currentColor}.border-destructive,.border-destructive\/40{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/40{border-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.border-destructive\/50{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/50{border-color:color-mix(in oklab,var(--destructive)50%,transparent)}}.border-green-500{border-color:var(--color-green-500)}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-purple-200{border-color:var(--color-purple-200)}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500)50%,transparent)}}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-accent,.bg-accent\/50{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-accent\/50{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-background,.bg-background\/5{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/5{background-color:color-mix(in oklab,var(--background)5%,transparent)}}.bg-background\/25{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/25{background-color:color-mix(in oklab,var(--background)25%,transparent)}}.bg-background\/70{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/70{background-color:color-mix(in oklab,var(--background)70%,transparent)}}.bg-black\/5{background-color:#0000000d}@supports (color:color-mix(in lab,red,red)){.bg-black\/5{background-color:color-mix(in oklab,var(--color-black)5%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.bg-black\/85{background-color:#000000d9}@supports (color:color-mix(in lab,red,red)){.bg-black\/85{background-color:color-mix(in oklab,var(--color-black)85%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-border,.bg-border\/20{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.bg-border\/20{background-color:color-mix(in oklab,var(--border)20%,transparent)}}.bg-card{background-color:var(--card)}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-destructive,.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.bg-destructive\/15{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/15{background-color:color-mix(in oklab,var(--destructive)15%,transparent)}}.bg-destructive\/20{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/20{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.bg-foreground,.bg-foreground\/5{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/5{background-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.bg-foreground\/15{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/15{background-color:color-mix(in oklab,var(--foreground)15%,transparent)}}.bg-foreground\/20{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/20{background-color:color-mix(in oklab,var(--foreground)20%,transparent)}}.bg-gray-500{background-color:var(--color-gray-500)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-500{background-color:var(--color-green-500)}.bg-muted{background-color:var(--muted)}.bg-muted-foreground\/10{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/10{background-color:color-mix(in oklab,var(--muted-foreground)10%,transparent)}}.bg-muted-foreground\/15{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/15{background-color:color-mix(in oklab,var(--muted-foreground)15%,transparent)}}.bg-muted-foreground\/50{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/50{background-color:color-mix(in oklab,var(--muted-foreground)50%,transparent)}}.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-muted\/60{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/60{background-color:color-mix(in oklab,var(--muted)60%,transparent)}}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-400{background-color:var(--color-orange-400)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/10{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-200\/60{background-color:#e9d5ff99}@supports (color:color-mix(in lab,red,red)){.bg-purple-200\/60{background-color:color-mix(in oklab,var(--color-purple-200)60%,transparent)}}.bg-purple-300\/50{background-color:#d9b3ff80}@supports (color:color-mix(in lab,red,red)){.bg-purple-300\/50{background-color:color-mix(in oklab,var(--color-purple-300)50%,transparent)}}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-red-400\/10{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/10{background-color:color-mix(in oklab,var(--color-red-400)10%,transparent)}}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-sidebar\/50{background-color:var(--sidebar)}@supports (color:color-mix(in lab,red,red)){.bg-sidebar\/50{background-color:color-mix(in oklab,var(--sidebar)50%,transparent)}}.bg-transparent{background-color:#0000}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-muted{--tw-gradient-from:var(--muted);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-clip-padding{background-clip:padding-box}.fill-current{fill:currentColor}.fill-muted-foreground{fill:var(--muted-foreground)}.fill-white{fill:var(--color-white)}.stroke-muted-foreground{stroke:var(--muted-foreground)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing)*0)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-12{padding:calc(var(--spacing)*12)}.p-px{padding:1px}.px-0{padding-inline:calc(var(--spacing)*0)}.px-0\.5{padding-inline:calc(var(--spacing)*.5)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-3\.75{padding-inline:calc(var(--spacing)*3.75)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-0\.75{padding-block:calc(var(--spacing)*.75)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-5{padding-block:calc(var(--spacing)*5)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-10{padding-block:calc(var(--spacing)*10)}.\!ps-2{padding-inline-start:calc(var(--spacing)*2)!important}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-1\.5{padding-top:calc(var(--spacing)*1.5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pt-10{padding-top:calc(var(--spacing)*10)}.pt-13{padding-top:calc(var(--spacing)*13)}.pt-14{padding-top:calc(var(--spacing)*14)}.pt-16{padding-top:calc(var(--spacing)*16)}.pr-1{padding-right:calc(var(--spacing)*1)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-9{padding-right:calc(var(--spacing)*9)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-0{padding-bottom:calc(var(--spacing)*0)}.pb-0\.5{padding-bottom:calc(var(--spacing)*.5)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-2\.25{padding-bottom:calc(var(--spacing)*2.25)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pb-10{padding-bottom:calc(var(--spacing)*10)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing)*5);line-height:calc(var(--spacing)*5)}.leading-6{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}.leading-7\.5{--tw-leading:calc(var(--spacing)*7.5);line-height:calc(var(--spacing)*7.5)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.\!text-red-400{color:var(--color-red-400)!important}.text-accent-foreground{color:var(--accent-foreground)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-blue-600{color:var(--color-blue-600)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-cyan-600{color:var(--color-cyan-600)}.text-destructive{color:var(--destructive)}.text-foreground,.text-foreground\/80{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\/80{color:color-mix(in oklab,var(--foreground)80%,transparent)}}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-muted-foreground,.text-muted-foreground\/50{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/50{color:color-mix(in oklab,var(--muted-foreground)50%,transparent)}}.text-muted-foreground\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/60{color:color-mix(in oklab,var(--muted-foreground)60%,transparent)}}.text-muted-foreground\/70{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/70{color:color-mix(in oklab,var(--muted-foreground)70%,transparent)}}.text-orange-600{color:var(--color-orange-600)}.text-orange-800{color:var(--color-orange-800)}.text-pink-600{color:var(--color-pink-600)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-900{color:var(--color-purple-900)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-sidebar-foreground\/70{color:color-mix(in oklab,var(--sidebar-foreground)70%,transparent)}}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-white\!{color:var(--color-white)!important}.text-white\/50{color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.text-white\/50{color:color-mix(in oklab,var(--color-white)50%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white)60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white)70%,transparent)}}.text-yellow-600{color:var(--color-yellow-600)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-100{opacity:1}.mix-blend-difference{mix-blend-mode:difference}.shadow-\[0_0_0_1px_var\(--sidebar-border\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-border));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none\!{--tw-shadow:0 0 #0000!important;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)!important}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-muted{--tw-ring-color:var(--muted)}.ring-ring\/10{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.ring-ring\/10{--tw-ring-color:color-mix(in oklab,var(--ring)10%,transparent)}}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-ring\/50{outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.outline-ring\/50{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-lg\!{--tw-backdrop-blur:blur(var(--blur-lg))!important;-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important;backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-none\!{--tw-backdrop-blur: !important;-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important;backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\,opacity\]{transition-property:left,right,width,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.\!duration-0{--tw-duration:0s!important;transition-duration:0s!important}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}.zoom-in-95{--tw-enter-scale:.95}.running{animation-play-state:running}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media(hover:hover){.group-hover\:pointer-events-auto:is(:where(.group):hover *){pointer-events:auto}.group-hover\:flex:is(:where(.group):hover *){display:flex}.group-hover\:hidden:is(:where(.group):hover *){display:none}.group-hover\:fill-destructive:is(:where(.group):hover *){fill:var(--destructive)}.group-hover\:stroke-destructive:is(:where(.group):hover *){stroke:var(--destructive)}.group-hover\:pr-6:is(:where(.group):hover *){padding-right:calc(var(--spacing)*6)}.group-hover\:opacity-100:is(:where(.group):hover *),.group-hover\/expand\:opacity-100:is(:where(.group\/expand):hover *),.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *){opacity:1}}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing)*8)}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing)*-8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing)*8)!important;height:calc(var(--spacing)*8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offcanvas\]\:pointer-events-none:is(:where(.group)[data-collapsible=offcanvas] *){pointer-events:none}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-0\.775\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-.775)}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-0\.775\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-.775)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.group-data-\[collapsible\=offcanvas\]\:opacity-0:is(:where(.group)[data-collapsible=offcanvas] *){opacity:0}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing)*-4)}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing)*0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[state\=open\]\:-rotate-180:is(:where(.group)[data-state=open] *){rotate:-180deg}@media(hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing)*1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing)*2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing)*1)}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing)*-2)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing)*0)}.after\:left-\[calc\(1\/2\*100\%-1px\)\]:after{content:var(--tw-content);left:calc(50% - 1px)}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.first\:ml-4:first-child{margin-left:calc(var(--spacing)*4)}.last\:mr-4:last-child{margin-right:calc(var(--spacing)*4)}.focus-within\:border-border:focus-within{border-color:var(--border)}.focus-within\:shadow-md:focus-within{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-accent\!:hover{background-color:var(--accent)!important}.hover\:bg-accent\/50:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/50:hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.hover\:bg-background\/45:hover{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-background\/45:hover{background-color:color-mix(in oklab,var(--background)45%,transparent)}}.hover\:bg-destructive\/10:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/10:hover{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.hover\:bg-destructive\/10\!:hover{background-color:var(--destructive)!important}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/10\!:hover{background-color:color-mix(in oklab,var(--destructive)10%,transparent)!important}}.hover\:bg-destructive\/30:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/30:hover{background-color:color-mix(in oklab,var(--destructive)30%,transparent)}}.hover\:bg-destructive\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/80:hover{background-color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\:bg-foreground\/10:hover{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-foreground\/10:hover{background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-muted-foreground\/10:hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted-foreground\/10:hover{background-color:color-mix(in oklab,var(--muted-foreground)10%,transparent)}}.hover\:bg-muted-foreground\/20:hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted-foreground\/20:hover{background-color:color-mix(in oklab,var(--muted-foreground)20%,transparent)}}.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\:bg-muted\/80:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/80:hover{background-color:color-mix(in oklab,var(--muted)80%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\:bg-red-400\/20:hover{background-color:#ff656833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-400\/20:hover{background-color:color-mix(in oklab,var(--color-red-400)20%,transparent)}}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-sidebar-border\/50:hover{background-color:var(--sidebar-border)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-sidebar-border\/50:hover{background-color:color-mix(in oklab,var(--sidebar-border)50%,transparent)}}.hover\:bg-transparent:hover{background-color:#0000}.hover\:fill-destructive:hover{fill:var(--destructive)}.hover\:stroke-destructive:hover{stroke:var(--destructive)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-gray-400:hover{color:var(--color-gray-400)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_var\(--sidebar-accent\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-accent));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:bg-muted:focus{background-color:var(--muted)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-primary:focus{--tw-ring-color:var(--primary)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-4:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(4px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.focus-visible\:ring-ring:focus-visible,.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.focus-visible\:ring-offset-0:focus-visible{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:outline-hidden:focus-visible{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.focus-visible\:outline-hidden:focus-visible{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-accent:active{background-color:var(--accent)}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:bg-sidebar-border:active{background-color:var(--sidebar-border)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-100:disabled{opacity:1}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing)*4)1fr}.has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[highlighted\]\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-\[highlighted\]\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[multiline\]\:py-2\.5[data-multiline]{padding-block:calc(var(--spacing)*2.5)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[side\=bottom\]\:-translate-x-1\/2[data-side=bottom]{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:-translate-y-\[calc\(-50\%_\+_1px\)\][data-side=bottom]{--tw-translate-y: calc((-50% + 1px)*-1) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:-translate-y-\[calc\(50\%_-_3px\)\][data-side=left]{--tw-translate-y: calc((50% - 3px)*-1) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:translate-x-\[calc\(50\%_\+_2px\)\][data-side=right]{--tw-translate-x: calc(50% + 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:translate-y-1\/2[data-side=right]{--tw-translate-y: 50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:translate-x-1\/2[data-side=top]{--tw-translate-x: 50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:translate-y-\[calc\(-50\%_\+_2px\)\][data-side=top]{--tw-translate-y: calc(-50% + 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing)*9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing)*8)}:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:color-mix(in oklab,var(--destructive)90%,transparent)}}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing)*2)}.data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state=checked]{--tw-translate-x: calc(100% - 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-bottom-full[data-state=closed]{--tw-exit-translate-y: 100% }.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:bg-transparent\![data-state=open]{background-color:#0000!important}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y: 100% }.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}@media(hover:hover){.data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.data-\[variant\=destructive\]\:data-highlighted\:text-destructive[data-variant=destructive][data-highlighted]{color:var(--destructive)}@media not all and (min-width:48rem){.max-md\:hidden{display:none}}@media(min-width:40rem){.sm\:top-\[50\%\]{top:50%}.sm\:right-auto{right:auto}.sm\:bottom-auto{bottom:auto}.sm\:left-\[50\%\]{left:50%}.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:flex{display:flex}.sm\:max-h-\[100vh\]{max-height:100vh}.sm\:w-auto{width:auto}.sm\:w-max{width:max-content}.sm\:max-w-6xl{max-width:var(--container-6xl)}.sm\:max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:max-w-md{max-width:var(--container-md)}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.sm\:translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.sm\:data-\[state\=closed\]\:slide-out-to-bottom-0[data-state=closed]{--tw-exit-translate-y: 0% }.sm\:data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.sm\:data-\[state\=open\]\:slide-in-from-bottom-0[data-state=open]{--tw-enter-translate-y: 0% }.sm\:data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}}@media(min-width:48rem){.md\:absolute{position:absolute}.md\:top-8{top:calc(var(--spacing)*8)}.md\:left-8{left:calc(var(--spacing)*8)}.md\:left-\[calc\(var\(--sidebar-width\)-3\.25rem\)\]{left:calc(var(--sidebar-width) - 3.25rem)}.md\:z-0{z-index:0}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-6{height:calc(var(--spacing)*6)}.md\:max-h-\[100vh\]{max-height:100vh}.md\:w-\(--sidebar-width\){width:var(--sidebar-width)}.md\:w-6{width:calc(var(--spacing)*6)}.md\:w-\[calc\(var\(--sidebar-width\)\+0\.75rem\)\]{width:calc(var(--sidebar-width) + .75rem)}.md\:w-auto{width:auto}.md\:max-w-3xl{max-width:var(--container-3xl)}.md\:max-w-32{max-width:calc(var(--spacing)*32)}.md\:max-w-\[min\(calc\(100cqw-9rem\)\,25rem\)\]{max-width:min(100cqw - 9rem,25rem)}.md\:flex-row{flex-direction:row}.md\:justify-end{justify-content:flex-end}.md\:gap-2{gap:calc(var(--spacing)*2)}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}.md\:p-6{padding:calc(var(--spacing)*6)}.md\:p-8{padding:calc(var(--spacing)*8)}.md\:px-0{padding-inline:calc(var(--spacing)*0)}.md\:px-6{padding-inline:calc(var(--spacing)*6)}.md\:px-8{padding-inline:calc(var(--spacing)*8)}.md\:\!py-3{padding-block:calc(var(--spacing)*3)!important}.md\:py-2{padding-block:calc(var(--spacing)*2)}.md\:py-8{padding-block:calc(var(--spacing)*8)}.md\:pt-0{padding-top:calc(var(--spacing)*0)}.md\:pt-4{padding-top:calc(var(--spacing)*4)}.md\:pt-6{padding-top:calc(var(--spacing)*6)}.md\:pt-28{padding-top:calc(var(--spacing)*28)}.md\:pb-2{padding-bottom:calc(var(--spacing)*2)}.md\:pl-8{padding-left:calc(var(--spacing)*8)}.md\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:opacity-0{opacity:0}.md\:group-data-\[collapsible\=offcanvas\]\:pointer-events-auto:is(:where(.group)[data-collapsible=offcanvas] *){pointer-events:auto}.md\:group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing)*2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing)*2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}.dark\:border:is(.dark *){border-style:var(--tw-border-style);border-width:1px}.dark\:border-border\/20:is(.dark *){border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.dark\:border-border\/20:is(.dark *){border-color:color-mix(in oklab,var(--border)20%,transparent)}}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-muted:is(.dark *){border-color:var(--muted)}.dark\:border-purple-800:is(.dark *){border-color:var(--color-purple-800)}.dark\:bg-blue-950:is(.dark *){background-color:var(--color-blue-950)}.dark\:bg-cyan-950:is(.dark *){background-color:var(--color-cyan-950)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive)60%,transparent)}}.dark\:bg-destructive\/70:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/70:is(.dark *){background-color:color-mix(in oklab,var(--destructive)70%,transparent)}}.dark\:bg-foreground\/10:is(.dark *){background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-foreground\/10:is(.dark *){background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.dark\:bg-green-950:is(.dark *){background-color:var(--color-green-950)}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:bg-muted-foreground\/15:is(.dark *){background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-muted-foreground\/15:is(.dark *){background-color:color-mix(in oklab,var(--muted-foreground)15%,transparent)}}.dark\:bg-muted\/75:is(.dark *){background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-muted\/75:is(.dark *){background-color:color-mix(in oklab,var(--muted)75%,transparent)}}.dark\:bg-orange-900:is(.dark *){background-color:var(--color-orange-900)}.dark\:bg-orange-950:is(.dark *){background-color:var(--color-orange-950)}.dark\:bg-pink-950:is(.dark *){background-color:var(--color-pink-950)}.dark\:bg-primary\/8:is(.dark *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-primary\/8:is(.dark *){background-color:color-mix(in oklab,var(--primary)8%,transparent)}}.dark\:bg-primary\/15:is(.dark *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-primary\/15:is(.dark *){background-color:color-mix(in oklab,var(--primary)15%,transparent)}}.dark\:bg-purple-500\/20:is(.dark *){background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-500\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.dark\:bg-purple-700\/50:is(.dark *){background-color:#8200da80}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-700\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-700)50%,transparent)}}.dark\:bg-purple-800\/40:is(.dark *){background-color:#6e11b066}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-800\/40:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-800)40%,transparent)}}.dark\:bg-purple-950:is(.dark *){background-color:var(--color-purple-950)}.dark\:bg-secondary:is(.dark *){background-color:var(--secondary)}.dark\:text-amber-400:is(.dark *){color:var(--color-amber-400)}.dark\:text-blue-400:is(.dark *){color:var(--color-blue-400)}.dark\:text-cyan-400:is(.dark *){color:var(--color-cyan-400)}.dark\:text-foreground:is(.dark *){color:var(--foreground)}.dark\:text-green-400:is(.dark *){color:var(--color-green-400)}.dark\:text-orange-200:is(.dark *){color:var(--color-orange-200)}.dark\:text-orange-400:is(.dark *){color:var(--color-orange-400)}.dark\:text-pink-400:is(.dark *){color:var(--color-pink-400)}.dark\:text-purple-100:is(.dark *){color:var(--color-purple-100)}.dark\:text-purple-300:is(.dark *){color:var(--color-purple-300)}.dark\:text-purple-400:is(.dark *){color:var(--color-purple-400)}.dark\:text-secondary-foreground:is(.dark *){color:var(--secondary-foreground)}.dark\:text-yellow-400:is(.dark *){color:var(--color-yellow-400)}.dark\:text-yellow-500:is(.dark *){color:var(--color-yellow-500)}.dark\:ring-ring\/20:is(.dark *){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.dark\:ring-ring\/20:is(.dark *){--tw-ring-color:color-mix(in oklab,var(--ring)20%,transparent)}}.dark\:outline-ring\/40:is(.dark *){outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.dark\:outline-ring\/40:is(.dark *){outline-color:color-mix(in oklab,var(--ring)40%,transparent)}}.dark\:focus-within\:border-border:is(.dark *):focus-within{border-color:var(--border)}@media(hover:hover){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input)50%,transparent)}}.dark\:hover\:bg-muted-foreground\/25:is(.dark *):hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-muted-foreground\/25:is(.dark *):hover{background-color:color-mix(in oklab,var(--muted-foreground)25%,transparent)}}.dark\:hover\:bg-muted\/30:is(.dark *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-muted\/30:is(.dark *):hover{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state=checked]{background-color:var(--primary)}.dark\:data-\[state\=checked\]\:bg-primary-foreground:is(.dark *)[data-state=checked]{background-color:var(--primary-foreground)}.dark\:data-\[state\=unchecked\]\:bg-foreground:is(.dark *)[data-state=unchecked]{background-color:var(--foreground)}.dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state=unchecked]{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state=unchecked]{background-color:color-mix(in oklab,var(--input)80%,transparent)}}.dark\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/20:is(.dark *)[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/20:is(.dark *)[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-8 svg:not([class*=size-]){width:calc(var(--spacing)*8);height:calc(var(--spacing)*8)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pe-0:has([role=checkbox]){padding-inline-end:calc(var(--spacing)*0)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing)*6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing)*6)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing)*2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\:not\(\:first-child\)\]\:mt-1:not(:first-child){margin-top:calc(var(--spacing)*1)}.\[\&\:not\(\:first-child\)\]\:mt-2:not(:first-child){margin-top:calc(var(--spacing)*2)}.\[\&\:not\(\:first-child\)\]\:last\:mr-4:not(:first-child):last-child{margin-right:calc(var(--spacing)*4)}.\[\&\:not\(\:last-child\)\]\:first\:ml-4:not(:last-child):first-child{margin-left:calc(var(--spacing)*4)}.\[\&\>\*\]\:flex-1>*{flex:1}@media(min-width:40rem){.sm\:\[\&\>\*\]\:flex-none>*{flex:none}}.\[\&\>\*\:first-child\]\:rounded-r-none>:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.\[\&\>\*\:last-child\]\:rounded-l-none>:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.\[\&\>\*\:not\(\:first-child\)\:not\(\:last-child\)\]\:rounded-none>:not(:first-child):not(:last-child){border-radius:0}@media(hover:hover){.hover\:\[\&\>kbd\]\:opacity-100:hover>kbd{opacity:1}}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\]\:text-current>svg{color:currentColor}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}@media(hover:hover){:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--spacing)*-2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--spacing)*-2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}@media(hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}a.\[a\&\]\:hover\:bg-foreground\/25:hover{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-foreground\/25:hover{background-color:color-mix(in oklab,var(--foreground)25%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary)90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(95% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(95% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(87.5% 0 0);--input:oklch(92% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0);--code-background:oklch(98.5% 0 0);--code-foreground:oklch(14.5% 0 0);--layer-popover:1000000;--chat-form-area-height:8rem;--chat-form-area-offset:2rem;--max-message-height:max(24rem,min(80dvh,calc(100dvh - var(--chat-form-area-height) - 12rem)))}@media(min-width:640px){:root{--chat-form-area-height:24rem;--chat-form-area-offset:12rem}}.dark{--background:oklch(16% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(29% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.3);--input:oklch(100% 0 0/.3);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0);--code-background:oklch(22.5% 0 0);--code-foreground:oklch(87.5% 0 0)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0))}}a.svelte-1q39rn8,button.svelte-1q39rn8{cursor:pointer}[data-select-viewport],[data-combobox-viewport]{scrollbar-width:none!important;-ms-overflow-style:none!important;-webkit-overflow-scrolling:touch!important}[data-combobox-viewport]::-webkit-scrollbar{display:none!important}[data-select-viewport]::-webkit-scrollbar{display:none!important}[data-scroll-area-viewport]{scrollbar-width:none!important;-ms-overflow-style:none!important;-webkit-overflow-scrolling:touch!important}[data-scroll-area-viewport]::-webkit-scrollbar{display:none!important}:where([data-scroll-area-viewport]){display:flex;flex-direction:column;align-items:stretch}:where([data-scroll-area-content]){flex-grow:1}html[dir=ltr],[data-sonner-toaster][dir=ltr]{--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}html[dir=rtl],[data-sonner-toaster][dir=rtl]{--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}@media(hover:none)and (pointer:coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-y-position=top]{top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px #0006}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:#00000014}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:#ffffff4d}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]:before{content:"";position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]:before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]:before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]:before{content:"";position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]:after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y: translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y: translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y: translateY( calc(var(--lift) * var(--offset) + var(--lift) * -100%) );opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]:before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{-webkit-user-select:none;user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media(max-width:600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 87%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 93%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 84%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 43%, 17%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 9%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media(prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}.agentic-content.svelte-1uhcmx5{display:flex;flex-direction:column;gap:.5rem;width:100%;max-width:48rem}.agentic-text.svelte-1uhcmx5{width:100%}.agentic-turn.svelte-1uhcmx5{position:relative;border:1.5px dashed var(--muted-foreground);border-radius:.75rem;padding:1rem;transition:background .1s}.agentic-turn-label.svelte-1uhcmx5{position:absolute;top:-1rem;left:.75rem;padding:0 .375rem;background:var(--background);font-size:.7rem;font-weight:500;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.05em}.turn-stats.svelte-1uhcmx5{margin-top:.75rem;padding-top:.5rem;border-top:1px solid hsl(var(--muted) / .5)}.processing-container.svelte-3902tz{display:flex;flex-direction:column;align-items:flex-start;gap:.5rem}.processing-text.svelte-3902tz{background:linear-gradient(90deg,var(--muted-foreground),var(--foreground),var(--muted-foreground));background-size:200% 100%;background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:svelte-3902tz-shine 1s linear infinite;font-weight:500;font-size:.875rem}@keyframes svelte-3902tz-shine{to{background-position:-200% 0}}.raw-output.svelte-3902tz{width:100%;max-width:48rem;margin-top:1.5rem;padding:1rem 1.25rem;border-radius:1rem;background:hsl(var(--muted) / .3);color:var(--foreground);font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace;font-size:.875rem;line-height:1.6;white-space:pre-wrap;word-break:break-word}.chat-processing-info-container.svelte-1ktvj8d{position:sticky;top:0;z-index:10;padding:0 1rem .75rem;opacity:0;transform:translateY(50%);transition:opacity .3s ease-out,transform .3s ease-out}.chat-processing-info-container.visible.svelte-1ktvj8d{opacity:1;transform:translateY(0)}.chat-processing-info-content.svelte-1ktvj8d{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;justify-content:center;max-width:48rem;margin:0 auto}.chat-processing-info-detail.svelte-1ktvj8d{color:var(--muted-foreground);font-size:.75rem;padding:.25rem .75rem;border-radius:.375rem;font-family:ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace;white-space:nowrap}@media(max-width:768px){.chat-processing-info-content.svelte-1ktvj8d{gap:.5rem}.chat-processing-info-detail.svelte-1ktvj8d{font-size:.7rem;padding:.2rem .5rem}}@font-face{font-family:KaTeX_AMS;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,d09GMgABAAAAAGLsAA4AAAAAybQAAGKQAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAhjwIWgmcDBEICoLGeIH6TQE2AiQDiFwLhDIABCAFiGYHjhkMgTIbXaAnxF175HI78Kb8Z553wo1x9jiQOhKMomZRVlmy//8/JekYww39AE2j6j8ky0WRjpFwiFyY86CAwykDjjUChwYd8q3z9AHJlLpPmSGzVKKJ2XoZM+GVtChwW0pH7YMyyekx8ooFtWZfsbo8QxDfEGQ4icXiQsDt6HHDedmvlS1hvd1v+yAZai/1/rqsL/8Q8CYJsv9QS6jxmu/WSU9GimNPFEdGXEbulZTI9mlKtZ2DhqHPqYY3Uc7PWsM5Pu4IjX2SOzw/t97/2/6i/18SSzaithH9WSQ5xqgc9ERoA6VEsUHCSE4sTu+MM+70Qk7PuDLqPL02L70qBt38PgnBQkIgQKCG1mn9mVVOXMcvNow6zXe+Hg89R+3PzB64dyVWwPvSE2/LAw4DTuS4OIJeFoUb7gzltJyRkwPJTgqCwpMQLSvwcAFBs/ubuf4GZCu0iSVj4oCztgNAO9L0Vf5d+4Rn5/2mltTdL2l9KDAkPPY4zMWUSBqlnb11Oe+el2F8x/YdGwILGCxSmjIMNJsDBN9vLX+SmQUS5vS9syVhq8QCkHUFdqr/p1f5V1XTHl8fL4gJgPXBfq+uDBYVkAJfmVS3zw7QgUPjhYR9G9IEpVvfFvUWZZ666svHyQ/eD9tgcX/CwzFvnjKiESah9ZspUzkp8ESkO1UO6JftCwR24PZ2GbtNmN/g9fFP/LsthGJIzVBMie3z5HA4ZhSFDW0A5ewSVatVIKcB9SuFMr9C2Yyzp/keZc44Vh303+918yLvpbjuERojS/MgJEJt4s4B9wu16VZsG58Ei2fQXfnenAD/ac2082YKyRyCKh2rkgd0snJ3NvRnLjnAzRwmpdyVkhLmNkdgi7qywkYVXGWFqTMVwlS6/p+qZTvkalOnvWrdrV2FXHR37tzZTefS5eAPQA7+gAEDUjcYUDII8s4SuLtPBKRnCtQGUSk4xaSlNlIXtU65u+emibFrXXRuW1/Tt/b7ls7u5ObTF7qKlxiz18pc6l5oRZg0ZaAbxUPIWhTKYZRk/tdqdSG5JfFMSKYt//U58zkX76jOmY9gMreIiPdg1sgQWuKxrFU1gcTojUXbeIAQAeu112//30/9dzGj3Xbycow1iIQScsHL6Nqf2nHst77SfVH3F7XrGYWBID0wGGUBHE/zJBIAP7hGANblQz+0BANTSXF6P/Dj1Hd/xJ9xZhWJ0EP2j5J1KJbTkZUBG28UAKCv3A0AGe5TQgTEGw1kdcQK+tX39EOcRgWWY4+9DjrsmDPS//uezz32J9RGbsblpbyWH+cPK11BhTVVMzVXcSlxSIbIaDKeTCzzl8llaplOdlfOlnPlmFwk95PHyLfK98tn5W/Kz8s/kN9WAAVBQVJQFHQFpvBRSBQyhVKhUoQqkhV6hUlhUdgVmQqnokpRq2hWLFUMKVYpNii2KvYpDireU1xW/Kh4oaQrlaoTqtdVb6reVf2o9qgXBj4L/Dvw35DUkKNfyObnATJQ5YM4Ku7iD7iu/h58Bb4HPyCM/619hIMyWEaVYTKRzE8m+4fRw4fk5+Tv4BpgkDtQBV/hf+7A42sUTYolikHFyroxxUu4gB/MfnGKUvGslWqIBfrTzvn2/wua+a9/dmb67JmTr760ad3KbjveJn3kw+95t+c+22t5TFrZinpbXE9dtVVn7vO11lht2KAmHBgDeeG3LZfTTjnphCMOm3XIAfvtM2mTURutt9oqPbq08ShTIk+udA4RwlGQx4uRHLHFnNTw+kzfXsoSlbDdbnEo+AOQ0hFNu/ggAK9Yf/Y/d8FGRaLhlncWfo8Nb16Z+uiNTcRh/923O6P+JEMDJbgVJga8Pwb25Xtq1z8DN9bYfYxL5FsPRJ5ODDHkxUJ2nPcE9whWMh4AlFXUihBMy7IignjxFRWg31PmL53Slh4RoxY032ppNVdE/LoCqRGxTT3xxWpAjQajMhve8ILuwV44vbyBs0t08JIn08p+cDXs2iAT2hjBXUowYEBaT6TVOtuhB390eLv8c+gmxsliQm03+MoZt/mvgMrlu1n36sXdPtGSO4vzO4+GAs3Us6utGs3lJzhRIj9mYYn8dz0JsWOEqRjJIr9FtlQ0KxtB5KRcEFwPBBfmtg+50/k4GR+nY0cW0vBYlee1s/pkhOEs9MQeAyVFraIKTEDsqR9DnV3A0JQG+s77cl4ehdbXFEnERe8pWsBVx207XCii0jWpdv3DD07k9OtDijIZbRHn7RA7l+YWxLyBJ9cB0VFibcf1KuqgydEAUIiJMds0FRCML5CIekvCvqLe7bQJIO22uTjG8vzZmPRJMfDiuLgVwWDOyWqiaz5piLU0iRxqwIchUkjsMbiQi4q+pyWoBYNe9Ldy0luKimed8fXvhK3rsvlFDiw7ITY4c7s9NiPKbhe8E29rnno4Cmrs8mVNs6KDjgM057u1jV2ZmgC13HXhxIq49GA9clscY3mRi0qXEfmpOmUq+tsXeP81FDwY212yoawqeni/Hd5/WeE63x3znt5HAYLfuNgyhFXphrkTI9fC93JOD+GigIqPWPquSnzX+W/dZNPpenlTqBwnI8iw7GDayDlrqTKEAYFaSfgOLQBcsDEhCY5+YUr7v7QApwq0+zVQmGJC25TMiIoiHkXLp4ErF0RVvSf+/oS1+BMl/4m1O6Z7JQoIomLo8pBEeUQqeYVU85jU8iqp5zXSyOskzhskyZtTJYT6odTK+69BUBO+3lJou3HQclKOfdx7eLI99vl5S3GKk/CxTVgPdgEUFPmGDClyKAoomlC0oGhD0YGiC0UPij40OwKaZGXj/MiooBVfO2zDCTdAHOVrdYIMj453j8/LFMAMkYwkrcud+sql6DwnM3MnKvbYbrYdxGm3qGlhEu6bh868b1Yub20AT1BPbuIUbLNrLyAMHMuKIiD/QZkZv5D1spECPz8vW0CDGXZFsOI/oIrbqK80FXe6UUdFo7R3FwWJdNSNJihuKdNv89/7tsYJYcfd4/aaDjsJLlxc0917IJDf4exi/7AuqlDx97U42s0jA5q8Ay9kv4baVJVaVFCkHj69P+k9uQx7WM+nbovz+arpvWyHELlQVjKNvCVvqhjv6cxHpH0wYy9UmXo2PDc5xHgvN1DdT4LKWVmmad11mnD6aOURWnidERMUsEXEGAhf9WQHdtDEeQvybiubdS96lR1uc/mABhtCXc2gpFlPnzz0SGR8CrBBiGD5SMwcMUmm0fPdDDUa8a64i8BUwq2u5sGmt89bzAAum+lWpNDSvXx8DMQXDjA+uRnobiG86gRSAwRSe/uQoXNzC2isLpPkjzL3Xbzk3HXbL/VWrPxxr8C92Fh2De5HTrXJr9cz73TrosaaE01OeUDmppNJV4m0N92Gw/mCBrWsmw5Po5A61O5rnc7/FbMMBn/gPgz1l42sO58rY94wsjMzYC8IobOBM4x5mTovlnAf5/qDm/zwAG7diQtSaKt7IedvXUAB0BviNiYu5mn3V18i8PVdQY1pLipRw5eRzM6De+ZdwQDAtMFVjgc2kBCGnB7Jsc/HCthEQphy+TPsESckzAVsUQFhKeiVCk987YHegAfYCnpnD7t8r/YYlhAETotyHHBJAZMlBIUzKibscM3b1zp4AEMwJiiAJdA2eABHuOeiEB8849WGyLf7fBR00OMeQzFOEiYF5CoCODFAJT1iBinlUcbLcgiO6BAeqSNAnQDpeZ6ULixdWbqx9BqCegOod0D6AZPRJ0dfHH1z9BOC+gWoP8Dx/5jwaz2r7aO2NuDG8XMQ77yZjmgMqWx3qwfqe6oEAQjsxmYg1UF7huADxR4fjnkC9DcApwQAEL7BDDz1FZg3hrAWcSCtKMZqcT0PAg+VJDYrTRq7GMCLeFG6XgrM4+iEYhQOFOjJABLvVyKsuFwa23oFT/A80NKSGkFEAMWNB0mUsgKPB1ETa1iVcraxYhZKTBSoF78oDZePhaD9q4vCvlWvP3euEDJxzP/2y7bdqOfLIsPClVH1+GyqXhz7kzbnw8AWdpYw2tig19hYjyk5CpzN8sYkKNe1eVmVDWczsU74rXbZvrYcbhiuwfGG3XDKgeiQ4QXV8Xat0GoehDe2pdeyE16e6R4Obyx2Nq5b1/KXBrUC1+nWL+rToWtfWZiCMprZNDwl+o7Z5BwLp+q4Ts555hMZ0F5Io/hkp8+saw8wjht1c/bamaV4TDJ7vL6ypNf4f+Sv9mQyPHv+YI+eYX9zkefZE784Xly9bJ8XDWNLNve5L8zdfVa67llhiM5m0JNBy2Iirg77B73BsddjdDBu54wKh3l5UAli+LmXndFw5A9pP4m9OpVepBNdi4gRWYSTiPGI0qqT+vXA5NtGSoQhnIiFobB5UfOZV/DEekZNHJTNfuVc1Wlb72lUHc0vXThzNVme7rIO8y3Vws5cCkZGL6Jb2FMBBtwnlFAkxj/+XPx4HqeAk8lTP06cw3aAPClo+u8Uwq+VGEaQCpI3/U4knoCYCK3FEN1whOfxpTM5J9xHYI+Di/Bx8n3Ur21FgJbXTd+2ybLMS1ymObOG8f+mAjP9pGCSKWCnhgnOZGi9KcKG5cWpsQfxlndxQgPg1XAiS8BJNBfvhuzRHTHBGs06cBzJANFrBA3qHA1dyLQpATq/yIJoJnG3GRTRqxW/OEAybMvPgCcgKkjdd5D129htiYywVehBhNB/4HxcEF5cRooGQKCOXdGGbC97tXm7622lZK6ZvvjPMOctJqP4KrzqANEdYVqJtJxH6jU6NBg2NwB65c7J252mX3aJfS0kYDGARSZsJOPFpkRySzikJlTXCu1HIp08CtQ5qs93sJfbqcGzzGyFFMJNgaggx+E0NJWn6FqUEUp2aiX4kYdCkb47ZAoSM0guhTNx5OSut9HqZW+zQj3QMxqMr+l+fByi4GFgFdmtdYWXHPc0lpIwhUzOkCfAgnfvAjSJVhF3kvBwBuiAsXiIHWAgw4VfDCwSJIMcFu55eicHkULTdtWXk7VyBfFZwbcbxHAO6WnmrII5UODqgmG5ba2X5Y1WDx8n9P79ygBpgov/xzGh40BBu3AZ4hm9WEiW/KA1WwD5pRvYNaFCHqRg1nf9DU2HBkToBzlNlDOhX/qcjgFM/yRSNYeE2h720tOqXWBNTdTEJzHhohAND8MEaZuAAUScqkL7DZHEJbQLHGndRF9Ip5gDM1bStQn45VDsrQVWdLLV9MMRdvHW/vK4V85G5XSSy+FnD7IbqfX0IaVTn7xGGlw2Cz3FHYekEsqnQGxkE0ZGClc1Fy6htI+HVYh33oGwqSHWQJpLqejC8tW4CCenldCI5NUwgkGEIMfXeQZ47FR9eb/peIxkR4OiCZBWTb3Krpwn2boxcJI2PUVysVMa2OOFiAbSh/4gLwBdYAm7MITDt2USeO/xhsrRJaE2aFDMJnACi7JF5NBbYhhSlkgRiBkyE6PuwTTpmi/2/TMrTKvvzJU1mBGhFeXYKWTzNcTgS9gDlDE5mvJCv527RDZu66gIu6PK2wpiUtMqgt1e7qlrTsg9IBcg/wLP/MJxYF8T4ceKf+1g+XgEa5QEs4l7q2prscoG5KEtapJGVHM7KcxA4CSiru2asFCgLv2SP4biy39XoBfEz3+OWy2S2fTFj376U8rMt5rwCUPlu39QNP6PhYdOh9qaGjXnTOL6r5Q9i1mqJuY87LX1Ec2rbOXRvEDBj87Ck5BSTcjQEONIWmcNBkCf2jdZ5T1B+DO2xQTAC85LFAJqdEZjkKaOMDXnPvZCitTNs0x/PN7zFihqolHniAAtbdxDqm0B8+rYC3IciUhQEvJjLrgoMP+HAE6AVnCWwP3g6/XXLpIb/cozXNQHB9ibU8yV5xk97LrVYLYoPsrgl9DZZo4CYMWVREYBaT99U3l6FgszvC5ePsViAYAmC47AZt32NSQhvyATbK5Um/ZsoEJMkJQfiEEj4Yn7g2gnRksGqroqJ9NHr0/7j6dLoaHpcGp3JG7WNYN4PW01J/Dkvj3q70qHwyDjUNAgZ6lrxCiAb4pLsmNUMHu3OSnjtvzZU72BBk/cCBOQwtXCFX62GeyVEbWmcjw4b8O2nK7ENHCdkxH2pdSRUbrSMjLw7qhxWc+Qh3r2q/ngHxsGFkFlshpYim/zYG6k/KillSaE18f9R3CK75qi8fkN+L5upW6AvtqaUF7KR1fMVfgN8pLgzC5Huk088NtrPka30XeCAs+MgFH3jGFzlWPkeJK0fOOcxfbJUU+pA9zJTUF8IwnG6G84X/8ZSWFfVVwZJMP6lzRjX1nS0O4RTdhfzjeOPWiAmOK5MWlLsZQyYmJzKh5hIJGrKFuUjlgy0hpxXI64UoqKWEgvxrnS17uhqhHvRxqvkAz42NeRjLJlfjcbkU7jS+QrKaQw62EmLir3yNawsLVXKwFnpXVTPLw28bUkc9xM/cZpX3kCyU9pCXsYvAmdTbGhFCfFLP+BDm5MhsSKDx+3Gyn583mQZHBWjLKaAWJrKHTA6QiVn95K7qlV6TIXKSpc8ZSiaq20XtGsLgZ5C8QKaJK9DUyFmUNPSbccjcjgaGGd4pCYyZz+dBedY3pwGDggDabVC71xL8OKQ158oB1UtgRov1gYGDoYl3ffK+GQW1vAoYnFslUi94sc5yNVL4OYus1dY2y264ZCr8HT+d8wvmMPng0UI89STzDd+CcREGpEciSvidzKnxmDvaf2F/wTk1kYkM45oj7iqeJNhd4mVQKBN8bjnwYsSk6Ve+FvKcW861sB5ntjWpVbhbxsMdOx6AApQAFTqdwfVf20/xswIaYfJ/E6qqF9aDw79wkYPGVP1NOYaq5xloKsHzcAemwF/gomsQPzMzOUjSEF0f9i0HhMh8bjRS/jWKeRVn16xXWks6A3DHz48OGJoHljEjP3OpQyIDo7RMuq0y+ZgekLsLdAjk3E9ugt/pTusYdpWi7pxbxsIzUFHLB1nf9wtegL9cx9VkYBv7aMtMIEk13sW4UhpB/ztF4UYUY6OcuhyKKNT7cqhaxXzC8ZAQnpPE6Uh7V7tDTV66BL+zwSLOaFI0cKUXGmB1EX3uidS0dRv/feMQkKFjjj5h4dUrtQAMuRgrwV6s5oJmeWXNiwm2xOkY76gyJQBgu0xC3hC5w5T5FR/FlWJ6aFb+dRoEU0W0V+jWNvP0nPI+w2axub9oExpIlgmQuCSYWwb0OAzhukdgoItxgQFLfKbxhrUv2DTOvfQAyD1WyyiIII17FLBWTDizGiu61YbafYPU1IJI4RDyeQzr4SziRKMqzhGv8BoBDe3c6jIsOa/w1Ffw8FNZdWea18JBexZn/zwC8hkvYbzP8JYOFFrcUWqzm+qNujT9IVydsY/1hOoRB9KSGwoFsP3MLxaWW4wq4rAd9XOLrVZxSa25t8ShWqlL/hM0zZorMiuYL8An8X/fcC8xh/AHnfpIAHUoRKa83q3cZuKf5SA6mT9zEFPRZv7V1GWncHUfrvMc9q19AhWyAK2lM3kd6CJvZreNcSUESwB7ebMjwD5CsQulgRSDPvTSPBYWcfu+0l7lw9Y8HBg+D+TSRgKXPoW4zUXtFuNoYSw87ZXDp5IwYNoGnRtyzlUu9T3240rRfTxgtJRdMzPYXBc5ShDEvUPEeUIghJXtXLZgLryU5AsagSu5DL8fI3/NwOtSvlTljzaXYbCy57tf6M3ASDKLx5wJ9wV2vRrqXfeJJEUo0/w27XWLT3Gt6bL7v1lZPungaPD7GrggZvHR18D0csCn6a17R73pMaueG7NrGjq+q3PitJAlPGuEIua06990WXS38cfy8QNa+pmeMvBYP1SoNH8lou6ZlDJkCXHguANo48WLRdhoo8Pta759QhYt/RA2EQfC1QHjogByLo2/EdMlo6kdZkLX1TbZ0SvUPAhLT1p/LZF2jJWRQ1tgBp1VqBXUBm9FACFs6m3OY4m8jpXa8w8HcTw+IQuK5kkAxm7vMQHIvKs9nPd33zeActkCOddJqijJXWOnYeJYA1oCOwMic0uhmU/vJVjXgHwqenO9ax4QRuT0v4F1nZ7Y2z8MAUNCrrY/c4L2MZaknSQ3JYdaf5csdnuOJNiWGZ9hYTasAY1l7mHS5wOsjzN/rIlbLNYr+1ggUNndYdMcRmkWoDgQMxGuCbqtzw069ki1axpz9OUcbCk+e7ONL8YvOinl/40MLtj4PSznKBr2SgbdwOvj5lCt9oRNkXSICiDqezXx4WW8GuY8D1RkouVVmJRpCySGNhajvFhcQmKpkYxVZeFMPRfhzFhQY7hBNQKwFEZ6FuQgfqIoQ1eq3Le/uImlXhAmKX8UxXr7KwoIl642b6MqTluGZbhdzTqEEdRnhm5odcxfwC3hPM49PrgjLpKuqciMOkdGYspVHGjUKMDRwVrfK74N1RYRPFOMPQa96GTXiyFATB29CY4cpXfmcNhRQtVfTvqSLKy45sQG71P4kf7mOraU/ZygCFpl1oSIZUtlpNFk6kbwfKAeSEtxtEjW5MpNreYLbGp3UFOXU1BZ3TieJscsREpDzIr7Et9qs7DkBgJLXtC9ERKFwNUuQUi4EV3kY+gQUoiKoXtvlT2B3r2EcGkiWylvaH6/wOfsRtpLubgu2NX7x5w37S9IEPHANq32slur+/2X7ve49DGj5gA5REFxFrQ4FkCa+rvghD5c9D0Eb23HGjir9hWNDwQm8Tp+Y7p97sECfb692NgLDBu+fuwcu9p1fX0Asxb/TjqZvJ1hMbkLcOFG42y1jDOG7lVzxXSm5MEbSIrFoy3lOlSM3P7k0bvxQo7h97g6Dux5arXJ3l23/iGMyBeI0RUSry5Q0u1u9PchFrRoXdBEdFX1pIWSNOvEJoAMsP76qRI4I/ZjIZ8HSqUI8eHWPvUHU5Ln67i2mWhBm3I4/HIj+LUyD6WffPZcLxAKIs9XO9IwYMEBc4GWW/pYzA6n24+D0XGxZcVqALLvaKnpdYmhQQ3BYgfALl60gbyI8RCx0xwE021IY8BXXgM1REFMTgjTQwUIVCmn7UJgOYXuLboMv3UUDEZln8Da0C0rCEBvC1E2eBSJldPeABZWH2TV7lwjq/uiSTes1ngtBKZgeCavATNIbFDLWVl3IVRZsL3+gcOFj/6IcoVIUkdCzT+Bf/9lTGDsJZxJiyjrRgGFKqCqS6fKJzCJYPQEkhcpphqdvLkUBu0dfhNuGVrG5ygjmKQ+WltUIKExT4WSJCde3TwJmmCG+iu73Yml+je662ni3nUS0Wv0muSpbvd9VR042wvetqDuHrnprmqkUYTE5WVSuSs9vY4WEpoLjLoAEy8wf4ow+pjkAPyvWWEZaXYgDZHdEC8DcX9C1xdNe0f0/lSqnC1DeA/ElZGCQvC+EwgPDZ4F9aZCRub6LJI6xff5oGY59XsZH5t+z3r1c7Z2k1O8+m7et7ZnP2umzioGkZcY8A9rdFuA3yIbasBez8obeWDpCAEn3dxynZnNMeo2qfEznj/oEHS1Ze+/5wImcyw3yFYa1n1uQZqH5E6d0ZpLqpTgvgIrEBQCoZV0nQbraAQxraEIItUnqUUzOJ6VRbRFkCNsHl2lMB8VMDWPJKFjgGlsBDI+Qo3UZbA7vdeoUxI6BJcB8U4FU6pWf5dIt4pLGpEQUm26LVJHudHt4pSI07jkCrCA3HelGrUQNdaTmq1ITri1lLwJphOL3mjJjwUwDfVDQLOzPVlS1b2cnIWThLFYQK6V4ltUlwdDtMejGM/SEyeCD7K/vUehHRqzyak7AGSE3g8CSKy7dYKCOLqGIIHMn4sYjSRkpseKS1ZF3plomAQFmX9FBHS/tea7SitrCjmzPGVSSO8CwMB/jqQ/LKeFs/3MeGNFmKhhhvPzPm1bggiNASP+Xgpc+ZUebeddzwUOYZjGiJUHMxun3NuEEV4xOMc1TR5K+wfryBs+sSIVW2RH5fQG4l5kPSSXfCV3NQPEj0OltgXQf8M3SJ2JmBYmUtZwES8LMpSNLZwhNOvHTSyS/tEmTvQvrIAQjTb4mqaAJQjPOpP/jTS8Yp06z5RVGPH8S6bKgOnkLaCWTK6oE6+MY6zFgZXmNtmRDOlqv3IGa23WxRCogLaWxDm7zQp7ukk2WvaKaOAenWT+qYHbkl3IUlhqXW1I7yA92NoOnUj4YFwa0CwpqstJYkKLWIiSPwLib2IJRHSNrYTzGlCsLWCiAKhZNypCibQhleRe/G/cHYdHaI2eC4nk2spNgC/HQsGqfa+c9Y+ieCrrllmwFmkQnlAmOBPaoYUt1gGyi5ObB4nRIbolfATyKbyFJaK1dJ4/7A+suvMn4pNsiXxqGa8FiLOdUDNlBE6lUhNX+h7RENSKuxjFmd6Sik574Sd8c3TTo6gYIU2Tf6TCkO0ug6uz+Rrsh+OVYoh1wKyyehRYssbdRRqovjTKgVrSFF1V/OeHxIgZvzOqDDat63HZraVSMCJ37wVN7OvOb2X4bE4uBYMQ5sjT3U3boT04/b9a56xpCU8V8EMgBOO0QC4gTfh9DEG6UwrfMKCuFnt1IZnab71a8rvEsvi292ADqelWDczqXOzGMWoWOETiKIoWE+jP+DGYKCgeGSuOP0We8ATrz8kMG+ooV5ZKPQcF0jdbvfzZWf9wvF96ButE3+Ipp/EVka3aA6o1DJhqgh8zdfjb8GzDCaHs+eAcbGJkYB1oX8zeFGHpnYXXDbeK5765vtZvNDiOnfaOrpvI7dgrInwh+h0di5vrrFU+wyOZd1w8LFMJgr10zW+u9Yt3I3sBHu42xwYd9MgMTWctK+M/aCnGtsA1zJjLDm6vxDG7EporRe2iIHEqB4iocL1HrHt9yVWX13Lw1A/BMMTjTvhFrOSkOzt+SFiWfKZOhpl8XqN3bnlS6EnT/rjRnn8YVfSgYH7a35lPS+JskzvZ3lD/SQ7q3ocRSIglflytWhMwwZi9qvuvGcLgnIf9pWfZmLoriAZLTFzVw63SC0v+ACF+1sL5/tGefFZ+12XI+LbxAyN+d0cZsT624Mq15r+eM6MNqAlauFDBP/a7WdriRagRmVA0Hf2pwn+EqP8x3h9SeFmbMb5QuzSps6fa4IC8IXmqaTE+c1kHCyVr1sfqCmBBdmtbAeDm8/ddVtD5kbwurL4Ucd3xEQBkJZSyCCa7dcIVxsdpzrpHWKmDxxYar26+Irp8waNGutJxq+AjoqudVddONWpzlg1ecJvl3hDErt3EHp+jwVOrHg/AahO7NxbDPtD3dVf+pGlfiAT8NzF6mTLjuAv40T27Jsw4iyQ/FEXkha9flIsSxcCQ/VMhx5Na9GAiHNCZZyBywuFzgjOhBoWTyD9lEcJx8ZklSamD699HzJiYzg7d6Q0IG0Yd0mQs+3efnWxj827NJjE/zRQjSi4nxGZeQD/Ku8h8ejgUcMVZhgU0GUTcAJKYpZ/kxG8vIIkqtzDA1v0DExI7PcO0RQT106CWS0oiLPE8DVzTAQWHKWRuFIXmHkrIqMCUBlQW+TBiYu0zIUr2qTOuQKsJdMcNldFkdi436T8v5F8UcRvymeSOGIKoV7kfkx+HjJ4D2Wi/0iNX3r4EsKX9ZemNCXc8FHuj8dXsaSExUPKnzy+umZFLmmvyRlL+eSfdT1qXyr+0XYYS8u7O9Arc8k3KRtOalM8YoKD/z/EHVKcRLN95xTVNIxqyqRT6u2F/QTacx0GJyC1aqoEZEQqewiIOMKat1GucQ294NyIrS30lm9QnGwWQOF2HmC0Fgwu7omn2saSytpD0hDe637eSZibjqdwf5LyBPkjYkWpbe0o45XGD7OcfcWIPuhclawO5Y+4ZMhebH5KsW/qdfKgDEQMWJ/+hHpYyBH3FK4yJ3pLLxGwNApjGnCHX21/fsONIb8PLvRZHyHNPEAoTFkWdMp+i016/EqAWn9BoXucpJBiGACoUtpUCh9yqwGyQkliCWhaxfxAmTvS/5nAwBfYkqbANmmNoAFkdZgwGalf10q7pj4JxJc4yoP9YFSao7PeS1d7DP5DLWC8EbC/SgNvivO8d7ojcC7n5A1qfAQ4I2W9tfexj7SHjzAHgTHdLqWra27K19rInCJobCc+Rjji/6g5YQZ1oMRkjElrlIVAkDY9cCRBmF5Iajo+pSsjY2v9kcIzKlMty1a0evZ7fx4diI7ooFlyh9IDzN2QjeIso90Eo/KyxtlaJzEHYmzLvI3pBiEH0Ha/JA/nIElY67304cP5aXTxJI965DhDcQIrrak71UISr9Cxi1CWzmTNIs1YMW0O3syYzaZ5rN4IsFFxwhkG4Z2uk+ZE8fLqzEG7K+fqJ3T2frbYWShGGt/6ZfhbfMv2pV/c0IIikz8nb+i/S2jvfFLf6PsyTisHFTH9bDwmbnY+pW/MX44aFtRUvwbknqFtIuT3NZq3//NP6V/ywFxYr3wLTkRz12Z9sT/0u3cl4MvtMECRf1CtjTCDL10FaTz0y1HS8EJT3gL2qXTP64X5T+o2gUW4UdAIzb+KiYsT1WpNf0SvixdlRomA8Z19UdOIxMADkvMThzL5tLSIGrHXu9dsc74MDeUWYa9M5t9zQ0xG9ntteuq9BJXQLJG2qT1VaQbmbmrespkFS8fXfFgZnzp358vZgeGeY979/cyPJj98kgYQHjzoVgOTng+Ht6Hf8KXBx5fnP1FmAoUa3A2pprlQG5xrj5TYx4NMcDfmtG9pPkNznQthwrqhzPZEp+nb/CsZvyjhnhC5RqTGv7nN7OnnKEVlcJnkiEV8LfWVZuMMX/EnMKwr2TRx5q8lKkG5XvFfQXbCqc7dlqgHlTseGpE/rOk7ynVv35BPrwcMsqhmNixncBOU1xsMXBwcJ/IUgQeRe6FfCJIq9m4ga0kis5XwueaXpBblsB9wSXBp2eV/baHlJvL0cZxezipVX05EnxSIamh7whtorngxKPrBv+ZHncLJ9fkHamUlzinrfDsziRvcY7vgGXXC3nnYhscnapXBcQxK9Tsky4LycJOnVPwgftfQeiP+D9NrVW86mdGb4LZYONNU+I4S9XPzWvmlxhI/RwitgP15G4fuBo00J1v/dz6laq73f4I8ygfFzTH0cvxLH+JIUXY3P5LKvBYTlOPg48r97HgYNJdy4xr40CtVviArnfV76MPkYLoulSjYytM1+MVgcS3c6RcR+VDHOQ1lCqvzc76p/jb14fTxvbc6ldzQvtGpM/71LzW2C81K/lFxbxvjGPFKcVgAn/3eNjwSyKJRqz5Gelas+p7THJ+pXLFrxemNLKAmZj6prx0ZAlH81ZIMGjCwLZqvpSKA8VSIgf8sXQAdRBRRK0fEZ/O7YP6hL8lMNvqIjzcV9cVljlm9FVrZTIvOUrqxR457t1Wu+Iae2kMzmgiuiE2TldSenrhbHfaPTMXEpU8HYVOOs6drssqy2mvVj0K4jYDkWxRkAxOub083nRqSSaynqGjFrK2ifqy4eWVE0G/l5MH8FjYhqbZP2tB0UDjIO1PgyqK5km8Rf4u3PJmC3GnPp65O6KCxtoCb9oLN7UuxVsssLvGz496g6oCMF7/Mt+OC/cowml4pZn3sUNKiZg4khV/Abee5+XCoUShpWECVfaBDNoeIrYj9LIkL8Cv8mRyJM4w8iwyxczfDSj09nl0cIJ7CHSNV4+Gs5ssVH11U/tdBzqqwrIER0k+g1cAnvqoidBl60e1hphfGPT5Mwb72kFuFKC34h3goUoN4Fi06Q+UpAtW0h/Y6Ddw9pXozhSxsCNkBWJ7NY7xrLLxPXpmS4NQlQJJWw9FVlgmJCvDVwrjGIR7lIZurQl2snQwa1NjhSRt+TViBozLRPQ+eWURfMOAySGcjuvNL6IcfsQ2GYYQDOQG+cqYXsQOO4+EIGUnF0VBTZwsOAggEmbg1Gz1HuJaannf9KIbvQQq8lUc/1CTbmtfwMn4NXymHjYtLm+5sV9e4B+ADf2J0j170fGn+B6t7pgOFHbK6HLFtSUwhR3TjsEGLSaOxWLbAKZiq+U/BKh+TPufokqyX1fpYrLipUOi8vSkA7TZD8jIwX/5eaIoCBoSCQPua1pnO+XBeQaXQcshwGWloJHH9X6UDHgOvpUL9/eBJpTTwgtZG7JG1oSu5TA5LF3mH3BlVUhtQE/gYJSXy0SZjRzLynUCHlZcD+qTIX4a3/Id4oBOyEOL14A1ycGrVsuggs/gMig5pIF5RbiWXjG60g7oG1zUKMMkaZ1E/FotmQvZ1pjNoZcoUEc7oQTT2LnJZlvNihQRIRqPIg0L6mVYpQaTF8m4tRfEovH8AaA1//oYuZrNqPOS04mssfZYp6cMgqcNLoteWeOUW7d2lWMO3oKW72Tl5bv+dS4euQuTC7bxaAOV/jpXunQGgkhwW+BYUchhqXk+r8aTN28JbnYnV7K0wiX2vK84TMAk8nn65+ZuhkRC4a2ZdIYI4u/W1t0li6V9B7L00QnzVsu6mPxN2kF9B7W/9JjvZ6oKC339Hhbk3IM5yCiFHWyKUGXz8m26tFBnY8vslq1pbf9bcerPTfnHEbzi/3UMJjyPvymIklAGSqgEnEgUtxSl4tUVkc2T+TEep9fyny8zZOyoITr+xmPl777MofJXhD6Z2/8rcn7mSAqxhZmzDxRYkhtimlDrTZojUYgISuyJ3yKkLQ9Zm/UR/FxSOJE0n85Is5szuPhOGc7myE8WY2e/Pp658X78/v0lJ+UcdlnK4rucQWt0FGL/DoY1CKHxzCwq2ir+oHf45Ua2D4v02eeh9RMQt2vz0pfPYT6ITMOnoXxeA9SSq22nCG7wJY1LQqGloc3+/C/51GptSy7Ea+Cjf/H4T7EuaXM/RaY9nTx+B29mb1q+WZkB+/bO8IaCGDgMggYeX2B0wN9fiMOt/lEd0v+zMSnHx7DhDAkZechapX04lSjIj4QIOiFf/SXC2NeqbUUcbumCuxpq9KJ3xX6MMwNsWfUTZO6TIYyppFMObrv0tf8AypQz/USEfQh1PUvdetSH93Fa3AaFJb5r6nj4XnGDA10YXuKKOanvaFyVGvhBNMUEenVbllTQAmJ3/gV5KcAhXsgNpHzpStMx8aGff4Zb7Vycuyo97gBLkIjJ5cM4cuc/1axSU7/cR/V1vidCb3We/uYlo2lkVh9+PC2xWb/hwuA2OFYJ4UTM9zOIeETsA6NV1W0dP//O2yFJsqclY87HRIXxVyT7d3N7bra7M+lPMdo6pP06m5dN0wjyUdtwZl2bXl8XF/B5xucqTXcn8fT9IENQd0K8+hqZE5jjxtCpL8kv9sBJAY6YsN9DA08rYwLOyBWzCAYPT6JMvLVMjB++d/iezKUhMhGx1qjCbt0qwovKivBP7vHHT9tLFgDK4DfyrfJYmczUkhD98NwhXpCKJlh2NlfunGoYYgnT0NkSkKvjGFVWH3d2pu9vudV8ztqMsm6Owt+3GkpI4AAX5zle29da4Zlo7TDV6rlrhLJZZFP6A0GoOYT9eWmwSKFMOLxO2ZtQ9n9mrK+ZrXX6py5OKo5zb50Ye3WYDv1E8kpye4Rym8x/UhYKbpgDxEeu/9a/LAWkd3TG6f+u7QF2E2WeyBqUbAptl3fsfGaR3DMK8mo5BhQnwnICLJUSqCumCBQuCZqAVohEKG/PyXXhNkpvODAT+T8hlSHeshh96J4c6sOvlWL9xddW7P8kpSQWs7Mcy9wVBT3JsZuVstUtDnXnlC3GHU5rXxOfkY+1ylO3AoHvWJdVzVyPO0IjVA4wXQIjeZ8mJ2ak0I4lzsrDhBG7hoiLMMjq2LE20DBbmzDcU5XsyQxt3SBU7/rWeX+D+7dsW4kpVVokk3TLQgCrLTJl7wvUuZ5dLQ/NymlYeD6w/K+y/RSH2QwrdZkcdVD4udR2un12fm7/QsUafKY0R6XqOl6/StvykAwRIMhcr8hjvG6E4VpGAEV7LZ4BmBTzNst2xQiDORJl3m7YArm+1AMxPGKqtQFrxco12KB5BMnPaMT4cYb6+hun4Y8/b+DzCmXDeiJUF1OWu5kcJieJu+lCmWt6UcK4Vb9VWc9gclj9K2OiRpiMEYVlu2kbsvfn5DoZl/9uY6xjDf/HcL0hsPXxnmGYRPcxajGe1ugrZ1q4zhTToggRNnfhG7nCLEAA54tgqezCA+PvQJX6B8o7r+IzG1/eN9m1s9G/7Me49cd3T60aDF8VEKDdX8w7+7Lplf1XZ9jj/j5iKXvNqekxqjDmndWsI7tZK8kBhYndsxp22jGgA1BeyWu68s4appvsl/t6snOZYCCPHWbzY3FtaP8QXLZK2Y/rDmWYqt+ZkiaMUoLmempvUPHoaBj6o9kUkGMQ+i8y/dEMwdHROB1dvx1l4uFh0NY3C+bz8wEcFo7nflcLopZrl8E1/0IU9UL8y8f9bmJH+udCAZfEqxiWguvPf7pr8n8ppHGKo/kf/IV7dbtbIkeiq8pLf3a5wEAF5nOrM5/fABqZ1dVgoJhHMwyim269NJttUF7qM8PpuBbVAgNbvsaQA7QR2hpqXlJFT338OoszRq+I0Vuc6/1aS9mRts+0dI88RO5hFZhMRUVrD92ZQccwoQhRL8SffAs0P/rH3j2uHbQV3bjkSGhl7XkGnuBeXSRoTbfqsovAxTRORjkxD1MMXAoromT3nT8pxvAgHBM5zhArZUSbpxzlf83JmIJxAhQi5BCW494wL76AiPr+DBFxIuRPzU52mign8qzPkUO6YzuMjNY6ZUTY7/ZJXbot4vkfVfo//KenAnl4EM4LnJr2/2MVjqwK7+nbXedoyxFsxYQNUbkpnh61wyeTcI2Y+9uuEa3r2bmUI3jJqerwQYsN9aub+UDoXuzDQd8XwzG6UZw9d5gB/Pr1BDrurHULjXLStMHvKTKq27ZDwcC9Oi/OUOzYpmPJ75TfMVvEZjBFSYGCkPUKvY6Jt+vacaZuz2640iJH2lQPp1/jmSNTgP7ir+tx9kuHGD8xWf16Ag3P+e0CHQ/pfmvoX4eaIvcx+sgtznWJdu+G6XUWnI6upP6Ejl6DyXhTh6epZCtk26czRxqGjl2LxGno0slNTDxACUHnEUjjGp/z4sign0RETLkmNMYdR40sfJVOE+PMpwQZHtA8pEftxBs/3KBOEGnGzkOFHy4Euo7kdU7lUKeLso4CHAIz9CEQAh4gFpt7+IKeP3PoMJPj1bO4YrhDgS54Haw6eakrctRqMPU4xtPKCMpnvg/ppFKyX6zPCa7UHl3J2ruHM0wNUFJZvRlZdZ7ZwD0r2TPb2YOYOMfHGNb+dJzurqpz51qITkiG2FTOy91qqq8PlcBhMzXWQBnVbduuo+YRceeUFbq9xlfZvNs35juNrQW7VQMqvvTiEQtaM0G6OM0Nau8/uJwcMxGZQ9FqUPoCLhrg3h5kUZivoFZCGAEGX/Aj1JvukbJ4R3MbVYBw2XD52yObvLqZkuInKocP5dATx6/p2oP/jssMQ56gXkUNhDAXHkhR4JIj4MHWRNuUNone1/7d1b/9gk+VlMwEhdPWKLZuCeUH7917hAGmKS0kBiNhhcvDgItrVPyYnL3ciWl463kMa0xYjIUpsNVp+DM/7qrW6/Gcnz6/ZU4EXrypQJirTp43ZwKzi1kg0DhXUQp1cSej02xi7W6FzMAtbnLI9yZWWyxESiup+v+azQc5WlRrFB8XGeV8ntbDpKZ58ly6oxFS73PDdrnsRV45zeDPC/ohpHk5r1/RYQ2rgZ3KFkRFC1BeG4dPc4VLSO/JRIIO8n5B9iK8nyEER6i1Bo0fsTUscCvOaX/n7XYOvjUwrJXopzFQaxEcSTgS95364BqZ4hlvb3/b2Vhdinz8fhKT9yl4QKXo0+PTj4o2UMCN614cyT5iXOg5TsE9K6Kb//Q3dBqicjr9DSfwXxs8OF2xagfKxAvLH1R88sZZbYe+/H4hjiToXlF0Uzjo0S9gMl7SEeljTdopyYIdnQaBcKgXdYYtWqhYqAhb5ERt2ASVJdCucMCSrKSdPtb2yFKcjg6ODzLx8HBo1+6U6ZTpvPeaYwPt3A3V4oiterDzKxpu+Kl6uHpVuCEuu2Z0QU2ldXtQC46E3MScRTNQLUac/tXqzp+p+ICrNacV+uZmSuG+jv1eKLkWj0pjqc5cmNGQ0QiFR+BImWLL1iwwcNs7veSN5rmWYt20zkr6q3vCLSkFQyc4IJUap6EdNXMUvLl5b+/rtxZQswaQGkmTNJp2QsgBUF8CJIPZDCbHmU4TLP+jqQaSSikwRwBRUFQ7I+JivYlEyJQJJpeKGGMkCoz0/fV0X0pDS0MKkvohU8bEWv0vfX1wG4WBMnlDkAg7gIiOpuxr/8FAliXsWcrZtRjmQIRTjx6dIkAcJCUKj0JeyBSL4xSHckvT6s2JStp0lqyfhPISfo1V6EiPBVKrXGryS6iOKB1407okE67seajhjQhEM/Kli6SH8TW3sHZoAaEMYS2jSI8UE4oJEOJvCSqoXdTbYIyr+nZTToEpMb3gC2l4OE+vZeK78uuykmjdv+SvMIR9WF9sd1LU640RLneGyiVR+1GT/PTpHTy+vJoKJygrE+3FlyuS+urvG83dx2vkJr1bJj5PeoGUH4GsSCFarbnxl0qhn0rhLymQQDD1q30irrsSF1SzLdV56bR+37zXKio2+iOLZwNb6KcehkEfqvlJvN+JWW83ULonvy8OaXXRCxuLuZParVuoWSlpZpKTrtr3dtoEV8ohQb/ixxzelQ2sYinWVW6gLSv1fbr3LyXzY0r03NIFN6i4RW/CTUk08V8L8EwS3Ips6itUSLsSO6W+1wjBWisxIbYd9+rzcaSGFRoZ0d7YXZeQOFXjdfxx8kYcdlsXzD8nwMY1hi6ODNav5KLro73qDF8JJWWis8N4p5nBKwvIKagErbnUEmtmWIUpLsIUVx7GItZE1a0eBjICNJUwuWYYUdNjpcYgy6S/TApXfCETRsr423GEll4xqeQLm9YxA49Ar5EhZBrsWdSWyzkmstoiD/iAkRELTp1rbXEhhxUjSOQC3yhbyi4RWLBAqnKjZaEIi0VcBMRW9d6EgnigiLBaRlbgOOV4/YK5ik0cnySEDosZOKV+OqPeJCP/X2GnmH5LSutKzI2KkDRI/Yel2D+EMXoCK537we0GU7RAgCfNzXC6PjdRilO/aiKPsvR7IATPf/XM7jjrwo12nHK8pAml4uwv4qzxtkp9iwWnzDU33aCg4wtJCO5FDkVc4J3bcsKBrx8lQ0geF3ctrH+6B4G3xVqbrI7Ize7T3XQGXNkvgSYmDHezUGp5NUYqa925Q3mXFVS5kxu24ooqr6Qlgxevqx72JPYV4QnmZpqz2z1tDL7gfJ5mOnLtWPexa0dM+i0U6etUHYRguNOodNd0Lbs636le88Dw/usu+9tvF9Fyx6zSerX7Rf7QcqUp3eShbAseORVR/d/fjXVtnZVUtvSFlf7FiLMn3or2ol56LEfjfCXtq41qPKEqwxJWulZDGQ/Arf7chQqYMq7HqMvIpQUFOG2ud4EXU5dwmEwKICitE89+8fVDHoXX4OrPLxYa6eesijeJ9JCcZUujrokCqTS5UVosfcjCzorQhtfG+Cj1NVxDjVBcJOZNnMlIf6WUVrBkclsho47H1+oqHpxBpr0s/O21+NqNYCIIwu77BXYTA2S4QiVwBxTPiZcK3Ybx1WlRRPnvj1UFSODrZYRyAnLsJAKax28HsAOKd0NJbALJh7NgK01V7kcop0/fbWhgm0kY5Bav2ZecGa6gd33AUL9wQSQTR0bPzuXY2hHcKoxLXJGsaw/oNERoVwTo2pMTV8QJrTjluKd1joprort07QSo7hsIEolwZOHZuTykP3E5Q8L1y6p/Xw+iuAqOlmAgkYm8C8VcetSAr7lBt3wpKL4QNLNYqmXAyccrUxXWhHrXmU6WKQv5Z/mpOHmwsBql4GIxTCh7Brl9DJGLEnDk67ky9yAZrzz2TnCCwWkpyMUpx6saUCruFyKG4KQgVSh8o7TIqiI52aZIdbpr+4On+KHsahCzdFqG4khuLf+QoDpqaTIxvq/jP09K1yzMUCSEG77p+CiqQ4OTXV8U6x81fwsrjec4ARlifRlfovAXZwdRzEgkCaYfJFDeeUe7l8eAsMGXBw+HtualaRamo5BUigDOFRi5dEk+w2eiAQOH9XkGd4j+17foZqsIflr1Dn0HX6P4aj0YWgeqia1p3Pp+0mG9nMOAIMDgcBNcJMArMwXkgDoU82hFg3cLzojBSrbURwyxiEQgPGYOvg4KFAXgujn4mBAQiSxI7CNjg5WCOuTNL7aEjHz6KdbJ429BMr+ViweerNn1svVlJaR5ic/Yngz4xyhZaD2nitSHqLorgVtJLz9cil96ag2cehRGPsk/dl35YpVQoTgr021RcFMsdRKZT36xf35FriCnyDk6JN+pGZT9TAwJ3sqC4rUeMmESigmQEUjqiUCBdWlZDiFUnq8Mz16cKRQ/F/pkTTqBBXsRh3972a8VcwE4JAwMUElDWHRUOJbBcwqD8175IVhyZ0nIuNA/Oib064qMJ06m37YD655PfhOZI6bEI/R3KP+PmL+XKR4oE0+0sV54MxXfGx8qAr4N+PIDSm6rISiQoeYReR4B38NrbeXzQCOPj+xfubKgdr+XIw2xTPsl8GLVnLfZfr0ByE+wKEfoc3mfa99lSr4iDiegjMj1bB+YmcR4Q7gdr/LnHEMeJkWJK7+gBRHhl3IF+qytrHFZ4hdRRI50WyR7FjUiwXOL6o9T8GiZunRLt6pUj/cSub4/wz7jIk/Lj093UCAHZCQHXCPVXrwUwcCDcEYeg/9OwPcpRZSMaYTbgGEEnACFCgFBh+d8fzCyK7ILCgvDX20gK6MHYwYhG+WCr7+Ewotpa+to7+jo7IZUZEGd6xttR0eYbntnp+zkRQGfIOR1fFPo5AkJDF88KRNpZde4ttYVHAIyK9dmQRD8+x6JQWrJb4zMDc8+lN9TafXlaCBo7vHWh0Rd+umebP62eNmAo+zXv+uydrlsAiKXsPWQ7cQeI6eDHuk2M0pc+dyK1+Dtv+78hfNxt6QIbZxLeAQRDu8zb9UuukG20bbAta4EZLGfJNzunG+Iy4jaswb1KRWh4e2uOxOd4noLcUNpW01p9MfNcjFbLJoXCwnkr7Hu2Czf/IxvFLRiFmrWcvORxsjeCDdiQWR+RKj/J5oeSa3yvNOi13Dd35dVN3hruZPaLVuYxKy0jCyH0Jw+weVbjFROeXR6e4MtJiqkjHZTslZeHsrSyRy6wyS+gpyZlqH+GQSSKsvHsprJvfSPiIkHW+9IZDPKGFWdjBp3W0zg9E9Sl/NpBT+EnA0N2xAY9qxPYyd41sjjxxrq69ZC9h/eCVofHnw5POhIaOAzmvyCtiyD4M0Q/Dsy7EntM3tGtPYTISEHblw9eY3LinBrzkUDEsQq2HE9J3u3m1Aw5l4/bHwzMHW+peSHeEjnzqCFvw5Dph5igyTcYCGH8Nl3NS6SeOrKetGy1SGl6kz3gc84wohMovubO0TI92d0reVUoppd9KTK+H1Uq7NbrqG19xg0jbzb2RpFzsoFlelUSwY7cz8JD0A3Qq37Pjeurjrqdi3OS9C1tLz1JkVJ6tqwaEF8ecqylxdaE7u7TYmN8yy01GxGE0SzqhbmiRw7TcXYs/RatNxGSPp1GW3jgpb5uvPkrFtvcbAkJwbohX/8YQ2ZHRC8LR/flSLZvqZdJmTleb/1Pv5jU/aA9MDAeGD2v5d3awxpW+y33ZcfGW+KM0MgPjA90B4wNYpIDPH25ExqFMn/SEiFNYlPeDO/1kKKosIkIbzPokMcVfLmOzKqGdXzWap5VUxLP5KgQ0L9yWuK83B6/f8EEqTs5ZEDiddXhvgA7caQfzxS4kkCKYjTEiWgE2iF6A/WIWNM2CocOX9a6+Oju6Rs26S2n965S96lgjs2K+2jR/NyNm0MSe++23imFXzZCigLOWEZB4radHv6mPvXiNq3aAYdxcIsBZWel3uUKe7Y9lHxL+X8nMl+8zq/seXSKaViagNHWuvMXQN3P8AVFxX4/bjS/NG2iuNkvAT+vFyVeQuIBa4V6YGfl8IlOB1dsTNSiP9a+vvpV7mOq+lXroXjSMw1yjWoH0L2iyGk2iLFfjz6ajSWRvCsNX78Ni43yvFHzDPz5Bs0nECjBB9UHhs9vmdaeTCYQiPgAj8NVaKE8cQ88DP+4VLy1ED+Y0loRHumEgJQLhXRtnrGBiBnhT5902EI5Ypm5A67JHBKKoVICCMv25WLmLqK21cuGKyLt4GNvasWE6HhjRChnFx7lpHvb+AXm9TQrKJMQ0ZXm7haDTcf1PRW+B+SmwpcCZwDEfupQidPP2xqJ2aSt06swlmu6moWvmpiK5mYaWof1vOcQur+iANIle6VI5hVTk3XVtLSkzPaM9KTV9KuNRW76OYkie7/CTN63uRGR0E779GQpXjuFehPiHZvJ0Qb+DpZo9/6UW+QoLrR6cUsdEa+pOp9ioWENcxzkWIidOlSy++zDRzCxM+hBZADQyiW96uWaCdQ1uLp0ToIIY72JI93T5GgmJhVOFIAA/mHXAJOANJNxTzXRRAIkQftxtI3TTXZrQ3mxW8YeO78Tp+W0iRaDZEliKAZYjLbQg2DZCgQXHTxijdJAQEncOUfAoLXK9/LY1GI+oA+39L6ZaqzLWdVy+p9SwP6iHoKi7dX7vUif0XhUT+UZSP463n7TwH8oA/lxpBul6vn5IoPHpApU1t75UhEV2//GQ/aqZIq+aI2jhG5ueanQz++wWBeCLWtCqGDHQL1VzRFnrtvSK9efcskXD9+K7+K7TP+OQl44bXCVLkU9g9/JuDKU4VrYS8gfT7u64/Gov6+4x8jEA0zIFKFl9LGOj9ho7LZVNsE6zylzcuAmP7kLEEtP5uHfDyOpD7w1flSrn/THmRYmqOFEbpOJoVaRObIdHQE1uYsvSD007YAZBH03VxevEs+6Sv8LopAfuBioix24pyGA8G+HxXi1YuWFn7kC0MczbkZH19ZotzP54FPQja/5C25CR8/yqZjqJ8E/fuHN+1HXaBWTguterePP0ohHtApYMbQ0dgvt2mQiZFElMFl8Kd+qUxEWWd/v2bWMZgQg/VGFPfDwy+DBh7qBdm+pgacCLW+Q2O0QsdZpF402qWH7QQWGoFwDKdNl+WrIMKqJRkqZTWvCJUeCu/6W04k05lO3cMfsms5CF/uoXC5anOC5RhV9ZlDh32dtCPOJ04cOxOhDUj5nT/cGPY3j2H1070P/joHvUMifkGwkzPD35gXdY7LUvPNIWKjNkLr4Z1xb3cSSBrakr8Wvjx/wJXrLPYxXzbTd9JjNRG+yOdiuQpG6Zoqpl3omwRF2MvYmrm4vKbIUQ/t+gUBgvHGeMIVvqyzs3bUwEkUzUuwG89Wh+PqS/4N1QVmerne9P63c1/Uo3C1PCMNHHsv+E/fzHI1HeLGPtTxLvxz+WiXYiogFFSe09BS/vURyRo4lqjsUIM7f6tsRQCJQYEdaYLEHDOok7uc8qi15wV9t37RR4a8zU3/DTJUcRIwQQA89ex7FyWcZWSngdiZPLOPOSuAJwpO86lQrJn1tPrULgdrd6mUvGoOv11lD7C9YOvv++nt3d7yitRppLwpPABruZVkXf0SF2u7Rs8hkBC9f1ZFd2St57iFfozAYZFCki0DMTHDUSTfpdkNLbHukCJ9UfT/DTJlgbB4XVdD5GSE6QAufOpHV1udqAw0tDnFNpC4FPmRQsqHhyAiIFjg0vwAdSDs6dwsCQ4SuRcdHH7sL37mz9ql+Og+S0ucKvBPlrDc2hCtm4PJtet9okNZZ+duhMempSfmx5l6WuySlSTSNUSgNcZUF+wikmty2/GJeQjxktuI7vn9o6xoGunTJkgiHaAZTKMExmUysUEbT0aWfdb9TUDaUE2ihrLlIZk+Z4BIEOy/NWUhCb3jZjxLSwLpoVX14DjQvFQBHweOVZmVNEoiC9bjUeHPIPglrJgK6ysnfSPXOjOopPxiRYzMJF/c0teooK5zllDJLdFKFNa6hUOilVnJd0IkurXZ5TRSPsqG9bhb/Q0EZ9VHikIH/mlfyvfBPjOT9Zlp8mqYVyNnx+xAqfzNEYmDApb1pCZjN5fbpUhPuBjqrwPGNnE6KrTuBLuaivitrp1OVOsyYLndPQWCmyzmzQJBdw+Wq8+VY86dLtSVYIn9aycAsrX0pWy+JRrL5vZ28LIgIazUynKHcrNF2YvbnCgh2sJnL6WzvFWKFPt2UFZ+EhV0gbLy7an2SoUX6P9GS3buBGGEWAPfrYXoM5wAucuA5nQudvP4ZAgholyjzMBx8zoXozmGGn6v1g+v2LUDFABmRrOVKhWpFstL+e0C+UJlBStsT7FXIS9rSAed4EQXt4myVRxaYxIvq7cjm4tFW0ASYQJpHQTuQyCMkGxnZ4/Y9Z37+AB95IDsFkW2MLqkTZjdTaMuTp4NwVcrP84NlVW60ZK2LBRjTJHvHar9M3H4FvUNCvWTEnMXLFpQPjncceGQNTndML1ufLCewQJMxKX5cq5HChMIBJ0qX2E/4xpyaXJRjLaAlVGps4+RqEH7rDKTJzP+b5Bc9U1EPvRO5bZhTTWZ+ROTvrguglCSFUW5vKRRbP777Ik4PkwqzLn4XWZCvcUgcyEoF9mlSyvPYC2gYWiuxjXkOqOwA/0U8wWDXC37eq5bCsNEgk6dr7D1uYUMNdkJZ8iU7++jgz+IQ5vqwSmOoOKiZ2zZlJbChBjElG/oH3LU+99TyAdzEmpSiq3fPzTM59we61ld52NewIEh0ug+TNAFjOf/+jo/HRflXM5OjEG2Scf+f9dggUpdPID6SUBvo7K5bQcho7YFkCKyNL+ckQlzruYkxMF+vuhAsbROh+fge867GBHhQUa5S77B4RhvNWtRvqU+sFEQav968ywolf0seHnzbyE2QWNgvYVPYnW1ESil1ls3QjyEyJ2Rfy+NBmdnweavQ+0VdcJPyVASbGObMuRDXB2RROK9y/nuOSgAuQLDt8+1ezESiajjDslzYFRp62KROtz2Q2y/bX75EKjnC4B+rq04wCsnhob+tKSDK7DFQcjo4w3b7CBNJ+CjxU2g6X5Nqus5HK1LtvbYs0bQfNhidXUAx6I99FFHoYDE7NqYvmqNfd0rtwPVgUGmJuGJkehS8NE72P6l25upyoCb8dufgO1RnEYOv13oJ4MUuoprHWPvlkEBmPIH+6X9VYbXyhjMzLT33y75QYkFQGXvjnUcfQTJ/ITtILWR0+Kwg20bHu8RijxabkeWoktPjDQJTUGE4vZaRUMDF9/YxSQJCh2j9D2LCunnDCe0qQl8MreG49JyOOsdG+43NYImcHQXUHED5DqDk0BEczrTcZ/JYyjRgB82WXPWW7bBGSqri+ayqjLgbZYbSEA/yLTB1Dk2WUq22yE8vyiKyF35ZFKcBc0yuUL7XvKLKOMrn79SwtIvSn5Pqxju+7Ro+hU8loeRaO/JQIV8ckVQUctwobSyd0WmLf58cVrkruxUoyWocHL48ZEGXDIdSiQQeZhLNsh5BS+aLm1XTJ88mGOaNKpPmfUxOWNnQMG/WgLRwy0C5Ufftmp1uQIur4BuqNJtugxWccCqy7e0b8npBTyuIFenPW+rPNWINlaeOm+7j3RPdq/Ex/ttS/mxUvdxkyQq5rRsduXsG/vzCkO7Z1cCsJn5E5PU3nEvIDe8rjCoXJm5ZQZjKOkixUZzD5PVE7XJMeb46MXYKCRzqAtt0/aXwIIPY043ghPU9SHh1lR96shZEuXs90K5RCr8V0BN4po9+RkIO7i1jtWxbIGOYwp8yVnsKumjOXkLJ402+X5RQ4EhO8lUYSnR/9D9pLfG5F/4OzRgygN9/2Mungs4i0lCLI87AbbO35YgomPt6RNR6goD6ToG+PGcBR9+qBWCnf8xcCayS5BFxGBPedDmxSOJ+9MUSGLQM0IOBw5JxjyqJeNkuoaB7NrF9ShelYy0eHhdX++ojNYHPkjOyMhYGLHMExo0bJ1cU0GosMLq9vA1ufN9llOqgLTd2wh50FO2byxMJECQui18HYjf4TOZNOlTcqD0wDm31TweO+62nnOXHNgU9TyyoPaMsQeGvtyqBpV/j7xxdgUNCuj3bYlCwW2gK5F2x0vpaEsdZu1lwL/+9lTgr+BXmNHrX1L5x33jUCdol77Zt8RTz9o+5Ft5EHTYDd5uTyvLnZ3nZnZIeQJ/Zqc7t7RokNK/sBE08HkCY3snqOw4noa21GOWVnl/efD4671UB2ijnjTofkBDEZutY5rOCVxfj5+yz0DW4mFTgJaK8nnIK72yC84y+gBsAl/TgJxXeIPQ+ZcvD5L5czz/Yujx8+c9wa5iP95xPtLve341zyvg3FXf5VB5i4uQwr7Mx34H1HvyqpquxTXl1QvzWS/41Vf/bhxrj3E9Vm3Jy9uS+6G4XfT5NQFJN2NYMJMEQ/Lx1xQ9gGsGjVcaLkP2IhI5j0zNrdtS+xfNBJSmfs+5GMnIr6bYrYAzN1u2Mq26VEwNUpIS2oU+A2VscxlCQmLMKW47c47HdUGBgWNKNrurIP278IKEzBzpdU1GSxXXXmT0cgylhSaahly+CNHThEZnUmpigWRAIBxrYPrMgAenvCEH6hf9XJ9f4xWqJfb/7LGccrJQImK5+7wldTnh93dnl057OPsqEKHzweY48A6eWVL4ghbS4vTXu+qhzA+tGRbmV5yojOg/hr/cO4DFuwy7BPyqzHBVBFdXz3E3PVxyjmywF2M+zvqdSYB4PF5gY9waF4dEvUqB4H00JBpYskd26h3/yn4A9Mt0eDPY0Fj8wQdfTPGwTcRP7xM7BJx9M5AVM+vaiNhe8e074k0ofwqUs1mAiUBeFpM7wj5fNyKJUiflCmmbzch/sY2e89G1Qc5/uT1AHw2RIBbwGaFB0bXruJaVUZtpwtwkdZ353uPXMwLaOEFbrkg0YDAFgsTl3WEmXxva+6pb6IIgHDST2VlXtgQN8c5fIJVn6Zt99EtsEZ11pAvneWBtSZoFzM86XPjdywWC7PJahBmU3/Fki2Iet9YsckQmV4j3gxML/UnQACva39beTinB3or6/CT/ljVnniYO6eqL4MvqpZJF+WkxjRvne+xw+ERbeMIZkOelXsVKq1ZYKC+okenYN3PXn4cb3R5HD4N6kEkva1ibH+WbUep+xH/qahx1IFVDPcy1pldlcYjGyKJvd++UBkvTSlwU/dkooiCsJKjkTlgA3/hPWEEWkRFj7V9JZW8DeX9Rr6KiBUx9TYGNdA0atoUbj+huTU5fXEesy2HEZCdescE9DMppJi01ufqxKdSaE129Uxc49dXtc+a9zJjshCsEtUS/yPeXkHoEEUqDr8XFG2T1FSy05BrI8ww9ZLRdxzLHHuiqGcLw7CaP22e4uTg6IySjRB7lDL+O+RDqmcSoq5L0FSpbMllSGj1n9AmoZFofM31Zh2l0vsZ/G7HcaPIbTiyJTgteVqqoUS24InYMwrNVrD46ehlov+KzYNHI7LI2d5foUnjEpQ0cKJfNjjAZEJOo15i0q+QDzQPYLT//WwBApuq16KI5rK1bx3TStNNhI52C9jzWEsEsWlWxuh1KRYdPweHToM8xIymgtN65Rp4gxjCYUuQLSWhHU7WsusE9vkNzSOtJMKCTsKZQ8cn2UM3ldsY1yhp0SxN7k+oi51mMfnyzpX4fpYFPfWcTCeH5q0Z10Un+thIUKwbLRSw2kzES7dgJlth4dSM+R2CDZ39eJZeHDhHEpLqmXJtzdbO553CJDlflaGREJpfBI59kTvK3ireLt5nryEWgzezEfYcMuUVQ3sHkMBk95guEIAZSqicpGANiIuPGfMdBMqCDLrbG6oJ8DoJ/HUw5g1bCr883ZyQtlQRcm/skTXm4hEZHGb56iu1idYdE6G6AKnuKo+ipXANd8TmW8eqDRj2okIogJsCND169gcRBXQtpwjiSygtwm5iSIHxd78tA6U1qvBhz+314gVALVPw9ctZUG+Mrj8b7vvI0PsfACdBqwizcpiq+4WK4tITrGukuZPSfAMcNdGo++Y9yOEPVcrvUfGfxxDHwZRNj5yqfBY6QkPoW89yuzHZOx5lDpo6dAK1GXkPtRUh4jprUcKto6DvP6CcFYfkkBPMNaNVoE3zj68Yj6DhFkDTU0bNy+/n4+HSMb1OtxXGZy2kZtVl5HqEhh2ezjqZhfLsN2EcvcgVdwGa320DDON6/eBJAfOtNEiLwD9wTHZsicZWi/AI90Af2l3EEtosiI8iXyFoxsbgk/98iXwWGWLS5L1CfCgrC8hFSpVxFqQsNpAMF/CcFAr5Nk58vXxwCg6LLKDYF+AIs612DQi5KDTmjngIE4p9giXFLyEiIgWT5reJ/9J17IDxrT/3r4LnLuxn8qD2j6MGDnI00tYrG2XD48Bj7hX+dO+iAS3SdX5Egg3tpk5toS0mdfl9ySDIZiTFWn/HRbLtcjlCXbBrtZVaLEZmcBMnaOqefS6UPHZha+3JeW5SZ4iH1XangHdpCG0D6EFp/ZbKs0qzExc2ZkRvhH7c21F+/XjfR2IDcV1iKlxgKrZa1ayOvWqH/VzcYqJ4NkFsVxrJygvcdTPe29WvXiLuzIhoWZSdOdSIUHVqpUzeWF+Bo2PrTJ0+eZumTPvqoCcXaiXxFsq8fV9j23bS2NUXe8h07cmR8cvbw5OjhV8anZl+2wNH/RGkfVKfoI4IXrATl2NTqnf7sNo09B/fQNDJPqP3tt02bIimwTLSIFjaNP1sctObp+K4zqT4DHix0Khrtvr2gnk//ok3blumPYINcGMyerOKtdHU39Vohn9JrHHZogdbZkcl8By8rNTczp+tJmwNCqusb45+/ByYlBv7xW+6jfYY//1R5m3l//altO+ip/qC22jM0ZDSsYDMHHH/Lf1GYBeTAL8GTCILqBKlYUEwaXu8Jtx6VU4nkNQh9ApouheDA/JBgiEC+TCFyNgx2GZM/yeMQKZfJBAhKP6B6qWyCjqwhE0D8nn8+hKrfB1D1B/DVVXyqUZN3ISP8/s3FeRq3hoMIugUnUZIMOlh5bZFfZkTBxSoZSbAodjQJ+vAjuOoDiHG0rjAMOGINltfvXrX8daenEoonivekAQXjthDqt+AvAuC/z8+op95YfptIYHKEIglruaYz5gVekPz5sqpcvupt9gVL9S03Dt7/Xn1m7JMChuaJ+jcALVQ+aSDNh4cz+95VcAWUNzPZbMbbsbbX9y29m/zHYb+B5rveDTYClc1msr/q2PK1X63RVNvKfLU0Nu0rYCwJRJgyny/G9mg1dhm4xmYoGKTA4m2nTytdesZl9fKhraAYFPIpKuhy8kfWmF6U2RO9cy2BT8SKf3J5yyddLhCzdh/pmPA4qDkGwGOmKGkuEM2Wqufmt6EhjuiHz1yLf4avgnt7dUIekU/YuTY61Ean2DpsV5Hp17QgCSx7UotteV1pXbhaIxwTFq4D/kzmi7wNm3elvLegQUZLJJNXB3pwJ2d15xtrO5dnjNKzkwqT6M70jRvWd376UDB0JzGzgrusAT9gR5kYxTkVtPjTNXsPPBgJWco3r+Obgxc/8OvNPFwdsuTbAExxlhMoaRQdGzaUe8ioUBsUrBWiZE85bGDr5D/95IbF/UVMSfeRCC5CSUuwXMhNRXY9llgqzrH4h/wM5oK1dc6RKFuu29p7uR/0TujNvtoKts7UsOm39+wJqc7EAbmY7kxOD5wfWa0fvRS6AuN3hlwayE7dOh+YnE53iuXe09rk1CIrG3EEOu0Zr/3W+/76nd81Z+J1Xm9dQoizXC1JLTD0Z0RnrLGmBLWCEx+4EM57f8bY69cPN1YloJgqx4G5Voz9z9e4XpPeDjWsW+nlXlbmC2nCfOVl7vltvbdPLFK2BezEFHAAgBafm1m7Ndq9f13PWSt05MWCHRNLze49K4GhMyehku1LMy+syHEtLKdZiIJKZwK0BpIrVtUT5CRztdelr5kycmoUiwhkan3yKXa9clrp/jyv3mpJq1vwCy9+Ciy7+HpYckbqSZGwKCEbLxjJXQF8E7CvsMBNHFEatq6DFJ8ppyfDKjC6AYbeyWuie/wBfpMrgRkJGBECXbG9iYZjJVhfPyqIe0JiJKJvQIqvG4XHSrB9MxhEOz8FPVpSpwGTU1Cdpl4DctaQqmMFTDqBHI/yn1wM60AYCWh8kpl76uJknOhPhG7nGQwdQ5w7emlaEiGK+2fwUI4KMxo8FHo8VsZj2KOSycdOrlANU6vgCJ0ZNeLbXSo0smm6MDIFfaSZD7s9c0D59/RUOvXtj+C3r9CYHAZvKKjyUcVTTsWTOu2iHdAQmP87l6efDCjwT9tKYEYysNa3Cq8zxHkGvwbfGkAZcib1VbcDxtaHO+PFOhAEXUuIdrDRD//4Zo/EbXv2+6ROnI7KynJ+yvdAnMFmpcFPBf6I7z6Oz4rme4MXr69HSnmveTk8R3HXrjItcHKIfjle11WDjZrpuU2mU6Nn7Un0JyQAQUelLJXrh2RbE9NHvYG/mj/QIWmERFyvAJvURkbqgnWRkfK9fIGXy6uFsmohXia1QdB6gtmcII923fqsMZRMRvfKX6lelODfMyGMvSdO8ZsEitzfP+Fe8x6BIWm9RFbf7EfogqjY4M8cLpUSTpjmLuzrwEsfNFaK+B0CLntRZVoSGcKSG7B47joDZlB4qEyxa19GvSgDMKkBXulNt2IElGme9BOwYFgXejJQQucbsK3ZEwmeOl1Wmp9tlhK4nJ5WlOMwJGhDBMRUj/ryImf7PzDnP1sMN+kpwh1JQYNDSVlAyVSYUMHXPCSAlTmo6FoaCRuylA+v+rX2/UkehQPwzPeDzbItkTK2HUxTOfW1JrZ1pOrwkc+O/oxgqmGlVr8V6e33rxeRnY25WwXjYSuvQPxmS6O9Ih04XDQOBQGLVdLEnoQ1xgwWRS5U39XXbdiQJcTBzGIr0dBmTi1GxLBcC+khaUFQ4a3ljVRGW5xgqdCESqLs18Ot3C/bim3fZSwzu0NHEbSPuwn0zH7rhLx/du/apQsLB/e0dZnRdw0+NXh95LMeQhlC5ipXZGhlvw7bgWp7K5FCZo6cclHd00cuQMYq2acyxv09C5zOLBvevYprPdtU/cnJNiRxACKmVH/pIkt/7oQp0eP0Ymc0CNOfU7uef+fiepFC5eiV1VxqoIKdSVlILDea121Xzgij8Pvh3rzUgGit2bN7qNXva43el0RNWApTHE1DwLVhApapRQQa7jUo85Ajw5L3UKuIQj2ePCIayVFiZ/fKvo9CtoE8rF8BUclYGX+nF7PbavrzbiDI/H5rw7PZa9pNJMPcdKyMjkD5ffczVjFJ3lhswlPmRu915w/+wl9tf7VYhNPj6xGIQU7oCHMotrIyod6uGkVlAbTjb6CDMHSEv390o4hLqOUtek6ld+UNspyGUqXR2+ot4RI5YqFP92EWLlCqpnAFJxKgWSurknrwzKizNlmuUutZlqUM1XO1gkQnUOwm2tUQ/2rpVogRtqIMvUbMLpQalthr7coscEgqc0mUs/Iw9+VkgqUL8muJ1T4iILm1GL+NW/fjZQQxlucAvE/2jfWned1n1cwGKVUIUiQJSQWJICjoPhf5Vxl5i0rE2NNxYVJN912Y3rNv44szFXzEvAPEt1ZI83SErIQBWHUl5RnCrbpqhOfB7KHWUYf7FCpYH6jf4jz+ooyMuU7Zn1djckMjwxTxITlEP75Du6mj2a+NKEClHqYsK2+JpZqpszxPQx9SVfEW/1oU/QoQfnNjDBw4Y7mKrgSxJgQgkxj/dImWvAnRvDr7qWaD81RICfDfz/JN4Safofv9f37DfumQGPQrP4jl9hsO6KNnKQOENOzCZguxgoVUfpsEohyRKmY5KDslgzL5bIS8JY7nVdlUZe55eRthcMRyHlnGgGn6hthY6bHYFJ+xhMjtQPHSGK+M4VRVIFOmogxGbWVBTPlGBhWrW4N8hCtJvw0uiOQpeTWk2bZnqIH4v7kUl8com5f/WWpJrrhZibi8BbUyetu1SEAq+ur0JjMXrNLKgrYMqaZ7UWqsDTmk8Lur3nNP0wfaM43UEwaACOuvSQIGKJ+l+gkEcEC9ml4COiAfL3Ughm8diiO81YHF+LdDtSSftZqog2VNlP6ubUM+6TvI5ckFdCpNBAnice98Hc1qE4QvSMsPypjJQjVyGuQ6CjKJGz1FxsE+7QXNaItWNOIpPW+wQRh8TGLflks+Nr4Oz+10SM/nilxWQ1XX4IgWAyrZrIlASYk2UC/1PF+OhKaiCfwUrq8VYSnpoM6RlFqKlXVpOhHPTLcCxdvPGSWY1p0KmysTNlnPSe7FAlj5GsNNZ15o+EdYwRFYylqyWWEdnhVbpTC2v9MxWcTlk6KSkm1WgqLWE8mC2cGR8rLIEjcJXh/qUO4BhdIRQRJJyauNSFgytENdYCTLBmExsqbf0FY2m6dWZHR9hjaeSsiD/ghUwTYXkSoJfrI2CKr5FhmmypotGFSOtiRaZBwmT1PkajuIESOvcbR5sZyy6HVztdx8tqq2eke1O+V63aNTxd545aKBy/05FFQ0dAxMLGwcAIRgBMVwBpPF5nB5fIFQJJZIZXKFUqXWaHWZ9h85mswWq83ucLrcHq/Pr6CorKKqpq6hqaWto6unb2BoZGxiamZuYWllbWNrZw8HG7bBjCHjPjPqJ2NW2+OMaXcddSzEPV+aimKYpbtRNemW7bjcHq/PD4AQjKAY3vFWeli3Tqvda6+TmMlic7g8vkAoEkukMrlCqVJrtDq9wWgyW6w2u6MtEl1uj7cffX4FRSXl9rG0iVVNXUNTS7sdMnX19Dsgx9CoiRi3iamZuYWllbWNrZ09TCCSyBQqjc5gstgcLo8vEIrEEqlM7uDo5Ozi6mbAoCErDFtplRGrrbHWOuttsNHoCzLuainvoICvErseiRptXatq27B2F6dc3Q3pEhrt+P4oCXneyP1GPWrMH++atM5l03fSbyitOYPOeV2XNxK9kIMhUBiyW7eR0AXLnenwJMJ+NOqmsF7W6fBb5HdPbZlO8zKF82wNW+7U2JsOhY3bp34/VrLF3F4Hp/DH+BYwb5xvAfOGCSd2l27SGbZpHI7g1GVILt2Dpx4A7A7PcjtqkJXOkllK/8A/jhW843jJOw5M7E99jsuu4f7REs2lvHJrgHbHc7pYP+AmLRNijo0uswZZ5w3Jzl/bkDCHbtqnavGGm+eNbS9cIcFO5GcAeWMat+RNutoSg9XXAAzNZBP72BSX3rczD4dwl2oym5gD0gt0rACv+Pnd0bN1e4NGMdk1hfe3xZHYorUCbu2V91b20umnyZfBkndu/Ib3W0TKIQ6KCALdbiyuE0FgK69fEsAbeV54dc5+0Q/ekXOVBOPUReAz5TuY7XAv1LhlyL1m4RIEO6zg0Gvxnt24xhBIFLcmcnheG8lThmh8bR22hgnhu2bI2nQZrF+4JBOwnL3mKzj1VhgcgUQ5Y7V4o2b67hfPdwSH9E9weR5eIfT/OeHglh66ewXk9YkuYP3F+MmaxcaIzOstg68NUtdndYAVpBYl9KAO9PgP+4AAzKcmjZyFl5P/d+fqZa2782OA6RCY55e/Pu/7M/pBg5rqxdeVXvn2Z8PiDsB2vwfnh6nWP3E//KLWIMT6EAAAAA==) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,d09GMgABAAAAAGagAA4AAAAA0lgAAGZHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAhlQIWgmcDBEICoLXJIKILwE2AiQDiHQLhD4ABCAFiHgHjkUMgTIbracHZF4Lnrsd2PH+Ms9+JELYOIBYG7aPRNiETSqb6P//vKQiY6bZTNMNFRBQ/blSCatQNSrOgpeJiUJlHLKRhjMn7i7ko8jo8gxzFR801TSw0i0Tat37bi+vKqaOBz+5uGKeanHTsl+X8UYhIVJBUVEKJwyTI2zsIvkh6xfj98GIibTjsk28o20/ozGZ+q9xI7q5M/7Ff/5jhriYoIYFajzorm4vUYsO8bviPnAGto38SU7e4WlO/91dcrn4SRwIEQLBQgQSpEDiEAIkkODFrIZVqFIqUKpUxNdV1nWV/dZ/dZW/Ma1uaydtZ+1+p4XguZ93v3L1vGhiI98Eh2UyhsYKnv/n/ts+5743Y1N/+vzFAg00DDhrpQkmmNFYJdT/vtM+O2ke2DNseG8R0IkfAHwukHT2z95l6cqxU44dwJYDbaYdLqAlLcI30KCb/SchWCgh4IWaUVGuR3s9E3+6v+3vusx/FFvfvH2oaLpEJvR2O98orrkqHEYgDRbjgnAYhyUA/vqmVr/sX/cuNwgMiYXmtWMZQugA90zbq9tTwlXnZRjdsXTHI8mJAmw5wLChAcMC8S3a1czbwX6Y8W9+/lr2DDEJ2ZNM0w1i8vX10JR0jVgN0X8xN/JK+oiptYl4qYhXjh7Jb3D+wXkTU3+sEw6nd/d2e2drW7fQljpUVEAKjLKpHi5AhgvtPSRsszYo+c9f1F/U6qrRmfsiRG77S4aSFxQHIqJac4m17YPaHJCyO89oyy3p6bkWwB8g8cfO0LJqBRART934F3hBDEETBBjoCtxN10wShPTvT369Lqtbs/hbWhRUbU6UbZSYtXWIQepnZ68dSv8v0YyPI8bKx9IRVH6V3/+Zara70AXyInUhh9en6lx0zkXpcvbPAIuZWYDALCBhF4xL6RlckM8ElroD9xIJQMzSpZggiEqg5KcjlSLlHEKVm9qlm9JFGVPvunVXhaJxa3v+fp/OzDn3p8oWyJq9yK2mCquyCl2BxAVKYN5797Ys/GQos0RRi2OV4mEsxoRJ/rZsdnm/UWqXSHQ3kq5buZPXjT2hzwh195fVd1FlySpt0ogIxZs5XP96jLlCs/Fj97yZKeEJJxwjsl/2YxnT+mV7+9fudFZR2AkkoP8a6FU9eQW4bEkxDwDOLj9BB+DPDgGwiN86tGcIzMPh+4CfOuD55Vnjfx2/EFkp9oYo/qkVM5AzwwwsfeEAgL7bAIDCuFKGoIKFQ9lo2sz+crlJH5RPjZm22mGvg4543weu8BkPecobexbXEvy3N/pF78/KCSaaiZma2UmmdoMqCsXAJrYwWbS9/cRXdtXTWHOd9NZu7deHOq+b+gSgGlWsylSlqs+UJYYjsOPMHQcucsjHTx1NjGEacxhgCevYxV6ucJWf+EXNVk/cJxwLJ8OH4WL4KTbECen79FP6M+bvuOxt9R++fQtQqDNG3rP24VGfyv+Xi57ylV7rZ/1qVry/4qnewIpc0bexzc1m8mvnfTqjc/IduXK6e48BiyNPB2+knan0M3/0MNvlS/LLJ8fUY1cdujaOS9+ll4nv6nn7Zfr26z9tjM6cfP/okZ0rlwxOacIv8932zDTMz//+Z3/7Nz/58Y8a7NmxafU2ue5aXXq42N/Py10ficHi/OYXzx588+CUkz5wwnHvOuSgA/bZa481Vlpu2FILLdBrsm5NaoxWKiRfHr1aGPPflv8yKJC/vHJyJHnhW4opUEnF6x8Za5PiP3nQvCKl8hsATWjz/wceBk9CipCUW6Ff3/T/XbP8i/Jd/l7j18zvNJcJTsPYgC9ngJ58oXD2MzCaY+0mzpFPr4ksnUvSLflM9p33BJcEyzLqABRGHRKCuWQ+JIJ48UNyQHvIjV9f0oYmEaMWNN5oKVeXibi9ABNKxCa1xMeLDlUqjMp8edfXpnZ04sLSXcQpop0ZzBpiHx8chAPrnIB6lHAK57QZEI1IjrdFS4cW/K7hzOLPrhZUjmdjChvBsxvNNn8FTHX5YpkHx3d0QQjGpab4xlBYfWpZk94Kqnu6rCsofW+BOfKPaSwRGgss18RIyTOUhrFo1lqCyJjgjOAoEGwvqc/9Tnoy6Zm9dMZkJlDwjJfMrE1G6MFrgGRzQYeiNiQPjEFsqV3qRK4HrFOlhIbzvqchGVKwN0LJ7SJmir0wXM3pD5sYUWLryXf84+ITOff6e/DSrZeJ40aI6y5tWhDzBi6cBWSOFq8uAw0pgjL7HUBulqSWsqqAYDlYIsrmhHmMYseaMoA2lXJ2hiVrUVZzITGmx1E+I4JOHhSOtecDR1I6BpKWADGgS7mO2GLWnIspWmsLEDgEpehTuWCxGDljaKM++qMeHmZpSAY55xKFAc3sUO2X6sZ68E689TxlaCiossEXXv2QDNYPMDV+7kCJbFUjq8hHnPlL0lGCrVIIZ1jyXEypc8ifLQqoQZKZGviyHQouRvaIaimLgmSO2whfttZh5+0oc68WCjC44zdOF2Gq1N2GZCPTol3JLINwUkDOFlbsFyK+M/OPy/3Ws46XPl9Zc80SUjy+SRg1Tg0SSjGLs/Jx4aAFAFNHhCSY+4QhXI+YjaUC2tgONFQxJlWlqkSJIp560xfAiFwVVSVAsgoTpsXqbvAnn3tosoWDAmwPgXAF4QkhKQ6+MALxSElAWiIykpCVvNiA0P8lheTLdhBUhA/JEpn7zb1Nx597tpoVf/m0HJbYupnrhOnxOmEXzGOeORYSjqIQSuJQFkZFPKoSUJOIUBLqktfaQB2w6OyN9BI0ch1qXJochUPtmBUMdVH1u/V3kjulB2AKb1qSluXEnr8Ac8ZLfbKyCm1sRNzU2OsUPkyMwxMzUhvYJ3+Rhgh4ibx6Ckusjw2VyDLHkrveAs43eHCtvJXlEgXb1DtlCNSY4o8RLGtkEtOobtafSYrbooAqSnvmCAXJpKFSJkExWRKf8o9betgJYd+NupmqXRzgxLWeTB2DwHl3tzC2N24XKwm5FlTYP8htBeocw5HMRFDBhSgUfMLowh7GlMyFk3CIl6/k3GnHK77pSJskRC5KxGQSda88Xlkc7ci7UgHM2Ap5plYrl3ByyOJIl1XGSAJ2iyiTtOysSTh5x9IJlZxhxJwJo1Jh0RkGb8lnbWUSRy08RrnTO5K2U+W3LTihgqIY5SOLSb38tO6sTtLsnwD0Cl4MzjXhwRCiUeEgJ6FCLd6pawZcAdwiLFjW49NnF+LEnNHu9RbTkA/O3wP5vhsWP+BkHbLPxfYCBBWJyM/cZVh3mxZQW1W6wepXM/ZeHLzp1p9ekr22xIeXwbH3eH4FnPGcW65j7Jk5bruHAit2JVangROfI5H4hcTElA09uT0qqFHJMvzBNRRSMz3klkP6X0yjODEpGcde1rLsfLzMesOJ9UUwdlLqjSyaZ70UuTGlDbg5Yh/3Ux1I8OpquTXsDGRrexsFwMskp7Ha4dSjD3b/As6GoMIkFyV1ZE0pCOjoMY73qPgQGEVNhN4B0BCHTBGyshIGuwOgI+518SMgkugZ4XcBDIxDoZhAkSgWJTKUqiStLMmRdwAsxKFShJyqhKHuANiIwy1cpKzN1iFlXbYeW59twDbknRFeZBw4PK5MPpjxCfGhqdXTjEQ1exgFEcwD2CIAlvPIgnLllmu3QxuW3XGb32EXAPsA9WFrOh7d8eSOZ3e85AzXALgFqO/Q9Hy459M9X+75zhk+ARDrjwF6yw0/4VP5ZDtTCnMdrlQ3gnjnzXQ5tlSSW1tfnK1LDYsBEOi/BQCArjZh+CmRvlqnfmTA8Y4eDuMEkD8wDhcGgqZ7h1glzEkWQMRxRZXFLOctNKqLfATUTSVSUvO8ZVXsWVCOQjQELJdJOFeb1kYI7/iQHWNPBFfuZU5lWaVyESLDqoJcjQGESMxzUKTWxOmQ594RaBnpUJ9Lvv2mf7AhuOlX71TCmFHfPyG7GokN3do2mJ9MzTs+psxOF+LjEl5r24OqafccSmnSU1R/QK+tSC57xkIQbC1frdVXnrpbQ9+tOsM8PbSO5ia944fFjqRJNl5gdXsQsrIy2mLJU5ql5YfmDye2OU9Dc22rPNvZv1/qL5OZzJt8oCc0aujZanChdPuqdnVIDSrRjsqyN25k0LLNsG4HLb+yMprW0kQ/SIy9hN8Zb24pytMjxik/CHx/aW2GUqqX70e7sXFg/qp/CwfV4fLTaDSZnxpGSc+1XS9eGMSb+roRCLHtRqZJpU5PyjFtizKaGVg1zfSJbuYz9XAUVZokUtR+N5dTVEokkiYeCYLT00q90+5IH6QwZxCdmKYruxVxVdVNECK7esL1GdOZVrAE2hErhM5CukEv80GuMdGFJDEEU8wEk1Ytx8481SV3omNWhmF1YxK7i1OXodSQdIVXsIuhJX0SHsXHiR9FeijB5ND3YNyNBAlmDQAOx71NSSEVxg8xj0HbB6+6iNzfjBLSMKc4FvqyFIy4F/WgsaxnUKF8ilCCw4KDiDOGFEsV344soB+aTEQAau1iHOI8LKNcX9dRAQdZcSNmZmsvm+VCbwPcoZUmXU0YH90ahGhQwNwE2EwaddAthS2V92zEai3jIFjDMKZdhkW0Ob01jjGDiyeQGzGaYfoiYKLsd5NOxzQy6gLmEAFwJzLi5VyjaSEVe4DhROn/kS6zYHHp4h8xdcjyQh0zWl1t2OU1Yl45ArTLjxm90aEWbzMB4p2O0eVLYVwA5oyIsyUbqepKg2umfVG9VsUhHNSmxIUueF5FzE6ajiKMHmVrxG+VqiarLTzolpFiOoBapu4EzBOI/IQpAzgH8M20IZv4DAZvODXPec6rqjt8OrN5CIdYIW6Dkop1CQQHzST9o0YTtk+sL+cD1l+WJOvp0p2E/JMOHMdtPSKiogQHQIQxarMC20y8mzmVa5sFYzFCHd5CwwpxkB2ZfJ4UMDdyt3ylQImbFCd1mRFnmaI36ImsS/FsPurFTipnM0lO5yCOlUrCbaUWQnA/3c0RxuKtyJEORvoMRf4Ywv8TkvisWjVHZLd1FVP34fN+QT0VrVoSWUgxX1NeI17ZWKcmAQ4Mzh0zzKCm5hEmcqSr4zS1cUZFsDTLrFJiJnEvytBu2FqRhtCUqzO/lPcF87OAaiKV28OgpJK1EuBgjiKUGMSxETn/RLJaeB0NQZswyq4H44TNO5hOTZh8pbYEjV3igzugmAyN77ClP5dXRFbcAYYkmM3EpAYd7uB3REyqqX0gmGCguabXUKzKmGAxMoyO10XPruRd2KYDOFTFpRM1bUxJkhyG7QJJ5DgnTn0hFAszbCnXiN8BOqTMEYnPykPvqDd6o1I02ReJt/TsQRTF8EzNHI6DYYJYqroUxQphsNhaGzN6gBQXjxKvjdaBKtbGmXIxMTE22GdxGZwo0FSWJOpEOVKlbQVLpWqoj2qkxBMcRaiC2y0H4O9YnA+KQSYtENllukHCMxGhCYVvNkKeMayKtNBpnydT7C8M9iG7lQ3ACNDhELeMnfsDhQpBA2VYGjlWxl4ELUEAfWjk8WqHfMa1cGEXkshKEu+e4xR9L4vHrxI/vHsdoCQ8Fg+7nm2VLE2sx61LgnjSmSS1kYO4GGD5HH5IvYeRmXpXL5w2tH8dXy2J9bZ8msZzdnph3XciohVykJmn4lthUP+OlqeN1tz7tFmcpS9whf4n/nu6XTMGTX4XMY/K1lNTGSDmafyjDA/W1XYqG3lQHMNsfhVlS0j7qb30k8iyjmDWQ5Zb1QuYYcrFWBkOQjNPzrAclBWJiwFAF/oWZmt6/FTYWuxyp9h6T4kkEu1gEDlUqYri/BCli9zw6I3+E1wda6v1JpmFrT3Wsv7+gG2aMvqGtgjgkC7uAL2gXeoijB5Muriof8uoX/8obGysD7zDLrfs1hURSxo8MugUMZkMi9MlHsVOo5fal9qTwh0lKGTZZsJecdvLoqBBIs3JUTXAk6ZiWbKMGUYeElJLFX/GHo/xWeYbO8Ifj0qWWuTCmZxgyhTR5iKrTEVBoY/YGzL2XIYn1aLbkysxYEOhMsKGnhYi0/u3bQ+KllewRt9Pw7CJru/hKCrbS1XLgt7eFob1oOKqWzsZiIRqYvZQ5c5eOgKd/PrKlcs27QsneL39B/sPr9pq5aF9jVtrNBPSBY9zxFqumPh+4l253SApUALnbplUWNXWRcvAfBqlDDRywEGt87abpiO64HfXU0e5Q3dZY/2GGBrf/PKyuWjZ+JPx73REbsNu2+TAE2xF/4/3QcTxkdsQE5Nxbi+FIGH0YkJUTpdiZ6xLwulcmUyCxrAkDBfpYNeq7vd0yhKocuvfOyUAOI4Zmi0PQcuo2ixw2MtKB7uslTYfRiyjt3fQOMSs5vtIhegNcdA2MQv0fcse483xKeCkmIC0bNLb4uP+RrZdhfP5JHExQLqY+Vv0KbkqYTpQt3lMUT/cewTHfkNJit+jdBoGWse1hLrU6LdPvtVzwZh7rait5bPv6G0cWdhFt2U6ajnin/JMDVVPIksJpLBKdSKmHa5VU9kDfb35mrUJa7vrceUcCfCJHvs5h2hm2bhydjFM+GT9HA5cr6NwuYLW2nmeI20dWwKzKiovTCXoYnPEr5Uq3lLcgqwGgllRL5uBclHHt4iHenUmq5Q2SgJzajkqkwtUIzWGqLwC0k08UgH42kabCFG21KKxPyZhVbkkbTa+NaRxVY1SDFfndCJI8KCD4wuPha2CJqNEsMSEmmcczD0jAa9TvwqgOjUb1FlOo+AuNeYOHWDrabT8tSwnNEhSYsz6QFpWVNPJENU4KBCSwMgGSgoFYwRnPxBpmQt6UDI2shtmRb3R1BVMDsj60JJes7+/C3kTgK6ILHa+ZrQqA2l2Y0wiLx++5laQ0kGlINC/m6ODUPcQ1C3MyUIH/dpIT6rbQwnSS37FpDar0OsUVCg6an3rj7F3owSDPQmFybsWqm+acXiT24iovXcx1HmvWt6VMHBoa8ZE668BPJygaRH9huUC+uxA+/P0PdTfhyBxtfCfNWGgpkzR3jdjeHqK7wc4Yz5iELNziyftuLk7pMtdzbMfsJRYEv3rcMvkYfMEYE31S1VBx0I7JI5dKDz51D4202QU0g1EylkPhoKXLABN/f1AmhpMMbx4owneY5LAPjb+CyKikfg1odqeoLb30a+aHBVqwPQvfI4UqEG4ZY/QUEi/vxf+CBGlFdMVyArHAUOekslyGhzBPOZTOwBULkVJCBjZaJJKV/CJ1zDRi2gYk/aBH9UmM+G7kKyBJLFvsoqCZbdmW7cQk0h7mTbZ5OH8DxeLEsxRhsJaMUp8LPNPAkrmPwL9jNnlUowOkeoN4pFBgOVIgieq0khH1yF8gMKDtrGODq1sgxVnxU5ZVNPBiulHYQYQe1RFi2Xtm0MaVrWQ4S3i12rtBxE9IUY1fxR+hlZNtLsff8Wpn4AdKbYUOvC+8QMVUiANIh4lmpYu27PuRL+LvHcUp06zUou0fMQTSIVIqG0T02XJiHg3IxPTBwILtdOtggRVtjaiaUwOkHK6YTwPOTj42yy2ygBQySJ2H1C77ofecIm3OOgk8eN+lvTNYB7xkPbkHLb6bAuiEldjQcCxcs3PAPMQyUYBgTqptv1jxhwSDDzchDlsOBAZqxFjqYFyji7UYAcVdYBMYM8EzuOyjdo+E9IFLBiY7v7gk8fNl9NSRMk2TwiP4uYxiikEUR1T++pxI16zq+wKjsq6m82Ke4lM2bZntB3c0Vftsg9//VXJjZNriaJ4Tz3TM1wF4mr8UDaS7GhmQ+xPZflyYz1jaadPN12lztCB9qHbrm5SUzItZvFEK2KNg6lsCXRj18w3VD0nGKT1pOseXg2mrc1nw3RLyO55nlRs4iOPA5UJCw7iUf/8Lk9xMj7a9EtfuARZairBoBW6l8Xfyi5iO4QqIdUbsfm4N/Zq2EEEgxgkdBhYftPwnG0xeX4XgWM7ewm+iWkeyYOxy6Hr/rkCc4RisS/keRbJYuPlG0jLVRGGk/CVsNA/SOCaQqbNhG9tiAnVzDpkr2AjxZWV3MrwlK7TmztT7x2pHPXaslnxIAJTEMRDJBwpSXQ3LFXsy4YypTlloItlHqJCsYRUVd9WET+H0A9ODUFe3xGSStFNfPGsZxJhV2btTwnRtHrRbe7f0gtp0Q+93NGEjI7L8rv97nPC8CiJIAmJ1kg29qMhKx72wl4VV2xPV/obOwZ+buBWd7EoKbyTW+gteR64VM8odek6CYgK00SxTT7ZSQmjePqyg99YKATFJNGC1cjgSRWDZVmeC6yAcRMgNFEVf1XVqnLe7kdWKhvKRrhlhgXPICOz+Uho82CZVqWCH0dUwAIDj2RfdzCzMKyY62d5JUVUOVKRLmEPk982lz2EFSAmV6xox3xG7RTPtugqfMOjg5g5SO9l4zbxTIgtZnlIp0xpED3ALJG3aUm1UMvhvTJr4bxLEa/QHG7ilbZpoujQLQ5zSB8zalgWruzEsV+/7Eoh1QrnMThmCd4esVVgWkXEQgY4hE5qaAvBcrPTpMQkPeuF3Oe5K8dLQ1f6uY0VrxhKEmgkGPakSzxEM8SScrUovQMI+WBkkmCOmmxvESE0zYi9GUTAwdot169Wj9NIwuARMfhDkZyiPGH2zKCxsYO40xoiatYhqG2L+lCLyfVp8QGmIzEf9zuNI/hEXeHtET/3q8lD/SwEZyi0SEHNH42uEa+jvUfIXhtyW83fDAgD5psVLI7SET+pMNGqFShUHnwNn4oPmZO8xa7y012bbxYdYU+X4je+TVeBgdkYbTBNd0tOWQ+JDqVWqpbIWAm/xiTYr9tey97yJ7/MQMSoa+wxJ/KN++HArub+IZxAX8rda+5d5e2oIG0h2EGNgGRwJADXxJo99qqiGIf0Avq4XaFcDcPqIhgFg/xwajBMUzp5nnkWi8Zgk24gJemkr6Pl7KDmoRZceorP4No3WjCIJDYIxk2iRXsp88eWXe26uPcPuuW9j3SzHnG31kUtkVEoBuLCDAeaE4Rarx7mOU4QxXL6I8Ds9Ua1ZoIzLJsMb8mRItKWCSFmUxvBoxXioxt1mmgmoErXYjAIRKdNPaXrDcMOWU0tOIZcYvupP+AEdmymE9QzqYGYFkpfIDCHNQd2P5gp5uKVHHO1vGQVEXwp3CWSpddRWTJq4G0CRei5+Nf0JS4m68yMl6vfRLupPWuIYwEhtQ/sQuR9toCnhkcz5BzexwFTNpHhrvsLijJ8HvAxF6EG4hiHvbj0aXhOScMltkpdEL+iVrElRjVdlYSinMCGJKgqL1NNPtiuUpfa1ujpe1E5ygXYoPZM3ZKD+mBazKxsKIAcF48i8Uo0oN5hyGoI3Zgqp5MrCNOn1RglSczK8OevY14vKiF2eY/4yBFgnpgM3KNh/yITa8FZ7gRiaIk3+jb55FqO9EuZ49SH3KsW0nE1ekZXtwUErisaCblyMXT05ElmoCgAFN9MjN+QET2CXAM8WeFlp8yj9pJBjpPYYSP6n7CuWmI4CQt/DFCnE5FfO1JeeJk05+4MBqySWw49Aj4TOtvmE4QW8AOpyZPSiCyGJdRlid70TGKDraQ577RZJMU3Yg3S0QEQxKK9vZkP/jR18KMngtiHaUmHvAxiwnOpS9lb8cgF/AtdNpICrOXwd0pWIylNNbmj8bRc2kIHwgPzSnbjPyIsJ3QzsasWGntu6uyx5fJsdf6fJRZVbt/ilIN4buTWukK3sGB3FErOBuJhqGGxZXtvpfQ2P3L1cuVI4ZxQ7VFk9xDyWUfh0Ewm5BxUp9lp6oxxFvAaZtWBDFCmnZAPTlHJCRU+EuY0V+76I5CFPoVvo/YVE7pGh9/U2vQffJitMG7XWVCpGzTqL74Y2Zo+CQxesjTp0dImANeydpCMzfA7wlJxcRCfVQuZPjQKmcb0iFSz/smm6BR8WtpDTzBwqqiVPotvAilzxhfafadqFWLjjKMuqtd3LasRZRMAv1Qchdihs3cVTckLniZVCASk00oZNLrQAwzz/i0AgUklfRpA72NC5rriaY0D5D2Pr1oI6GEl7gxZODlsbLGM5DsEIr44z0Jdfz66Strup32o04u5pRN+JsNuQXLKo3RFVoh3YxHRuFyCnT/vNCJLZipbIgCBqFQNbFgsvJb2WMyssEyA3ngrjQyc8lVmrks0wJBzUfYEbMziTkUHTUcNIjCLpGLmeio3CLNidEOGJEDZDGxUFSWP0rNpEGOXFl9+OpP+AdyPJWJ6vWQG8oX9veM3loA/jEL6EuEE0C+i37YBuTSLOyDo19PVKuL8KcLwQB+pKXXpe7HQbwA2hrWY9UfEXT3WLDCIFGAL9vO5B0GnZBfVjLgVeNNo0JIVndhrKoVQA4aGGSgVGWjfVC6ODkQc8wPGrlYcHH0ZoGws0zRrWlBTPwo7Pr7ofcPTdp+wtrodPXzYy8L1Y3oAxIVzeM9LUfNwjE6eXDr0Gt9DDFxw28X3kunF7pa1g6GVa/4obLpHrPtCUKgB4+RGJv43vdQIpvhAeNcH5UrmRr5XRQ5KxYZs8iFQXKiTCsVLiHd8K/GRbOi9uAdCpqgnM7dZSBUtQEED4U1Xs8Wpog2pJZtoot1sgyEdiCKaAeq57dw7hoviYRUjZJFRbdsmG5HDVlkNlWCkc78NIhXMMmRyw+gOJAqZwULetbQ3bvBNcFESt6trW7200IlnlWcUsmpV3n+zpQAwo5Fu6VWzXE5IJZI4o70+SASp+5s6Um2+cUR8A6MUAcS0imQXIF+yGkwNYhWlM4eYN0uAkkI4V/0plKbT43OaQpaZ8xpi/VuMS82rCR89w+kiM3L9al3Priyb+zMkRtMPJoszVtBNOqmX5aEv5JfTLU+AsMNEgEWF474/AhynwjK6yY+obsmu1lAmxxCcfcB3Dww1bYB/VkUcxrTjjOT/DeNUBVLxuYxneUZzHOHsAylZlpLG6dYDJHO5ZC6dE0dTBWMok0H/24egp8TMqAOZDN0a61I8V0r3NTcpTg96uyREgJcURe/Z3OA9mYcgxkpe9NSTPmKYpmGmzENKHEVlILFsKtstssm9ny3voZndFIz0bKyRarBkaBC+8+0grxvMdkuYTcjvGbrsiwwUJL8v3zMWAHGuBqAezBy77nciMxhQJ9lGy7O1v9pjXI+0WIz4QDHdJYNn8eY028WKfEqvYF5RhnnImAClp71vaMm/JCQNS6SLOaG4nnwRFeIcXSjk55V/i1Riiw5tpx8FdWAKSiFMpkgdHQHbcjNiqpRP4eWKVkfRcdeNn/N3wXLSyYWpRbYhAI9jzzq+pyfKbiUm7vpgLwD0O89FYpKz5LYYnWZKya+G/xsplkDXD5UR4LXGpFm9P2lRH+LgpWiAXI11aFMBx3rMcA4Y3kOj9Gyj6TLxzTxDu4exHiWwnPzJ+pAxY0YPt4WkOLvn1IMbvHYNL9qd4F9wZ5z52rn9eaxu9qLRbXaoUWSsfiNkewdvdDLd4hIwsv/HK3ckZdenmSsAhEY+ACtkZcuD1h+C6mU8KSo89kgGzao127otwycUydWbOqHdYcZtWwqq6iNdUHi49aHYCALZ2ULOsU/NZ+bpZl8B35puZaxjxYtuH8thXDHMadVSEi9A/ziF67PMbMsEy7DGaZgbGhMLY+1cMk+JMoSDdb7Z6ag+vReiBi03V7pJK/BrYVSHYUr0mK0+A2FIispQ+bB16tOphnm9xTzdDKbY1lYHu1XGVLNIeEt3MovHUyps5Bn02tRq1cH9gXLOz65X7E2GktnxQ3yamSt3ZDn/4hTbM6S2Xj8p0xur1uqYWBhp53bH9I6Ty9HuPCwE+7iIuhN8ugj/nf96WxNkDhB6CVbwYsCSxuslIGdIEy0BrQcNaXkluoQc0VIJTKuqGfDKic+mM3jhraQ42bOxjjoaH0K+Jl9k+VxO78YZRc8ztyyZzegBNmpAhZZUKD1Z+kKrXiM8bWjkvrHLn4eHQPI9RAtZg/51dy64pXfViA9tuAqvA/tjcwO13ZtyqyOSH7ZrL/lVsTCBrp1wUmkIBGSNEEPEeqwd8qZJAGy6+xYHDa/JM8ZI5YC1EFzHIRQyEkp0G3ATfe3X79ETWd14JDAb6rizrNnKVgyj8QnIKKPfgCbrvFb5WjcK0aowelLDCmcldY6sB0qNLnRXor8Wwf1a7LAbUSYSfE26AGW1tKMCkzjUc9c0NVKPDlVZjjpf9DNMabvN4Jgz+7rreglxu79ihSsUdoJitS1d4rFnwC3jaBsteU8gT/x9rThq7K0atxyF7OWH7LQ2YUuMiC/UU9UbfiuJyEOkDm+9NDTiOSYqF1fiRB72t5K5W3fk61uML/YIBjnWdsa+ekObiwlAWH/Hsp/cNsLLyIfuP8mEYhXnfxY+Isu5bVP9AVGTPWCDsiVhf0lq4c5CDTN9qnP69NTpOzyup7AP9/cnIuJeavdqWOXu9r9jqLgL70SfbZdcMAoGd2pHLEb16FbxS0ncB7dr2g6mHBYRlGddCkM8s3lXDTWpfEwbD8hSNJOvRWOjdSSRpDEbZMuWkwrmn1Tyo8biEl4A+EUiNs5yoOWiklk6h1UbvFNP+jkTqRloCkvfC/eJ2arAE7g7jCbTWXyxu3w3pAC0QSwvexAEwyXJRMExvBaKEXoF2nWVRoAR9XDScNRq3fGjDLxuDXwADucge0PxtMNYTJJuAOcf0z1YnMn97HD689hGMDwAadxB2Zjofd5vBNF8rYKept1qqhDPEYxC62VlnEMHAZy30lnKzrlC3MlWQmCFxegYgg+nNo/pflqE7YqLGHAEUBLRAE5IR4yZ89rC1suSlXKqcAKGtei2IC6QZLImrp5rf2TKDmpP/IlvNa1l3z11Gk+sijuGd1jtM/DgGdoXloS+O0qTuI0QlNxI8tg/2DuHiBNCAf9e9bZ0/GSK77ozbP2A+4neq/tfcLb7T55dUKt4GGFDv35wbxSMaIc+CsCJAfuoVHIWt3Ct+KRTkDDSMO+hcSzreIInjUKfgEHFvNPDZ6Rr/C7FzquBAYf+6RGETduTlkLx3qd1+VGxMibMbTdAvKbOOJOXw8m+bTLGaw/0ykU+rXTRh9AYsbn6F3iju86xT3zhMBQPhwADU9Jfo4kR4iKPshoDmTTW8jtuk0z21k+AAwjyqp0cuHIorgh12/gE+Hl8Lrdoye2xYndK/1W/704t5AjxskXDJGx247xseLf5TiiP7pw4ZMVT2Ty2SZJn1TzbZpXPpfzhKwSD7xvuEjhwVd4XiAXizj0zLxVk5xaG4TQxmuOeRFwx08kn3Lm+spkXPpX8erJJK21ULmR/E+qUjgvn0JZ7D1BJdt2crcWMHpM0uz1btOHjUEF7r2yl1UwjyyWOBHyYLQDkUBQRqp59VPBwiAg58F+JCl8zdqzwnoGMNTsuk1jSSXKvZ+0Wd0FpzhQYBz+ImKsV1PlpUjm6OldgcrE4+gg65q0WdRhOHpl8fUTBGQ4jWUiqP7m8VITY/zfvbeMd9n4lguQer62dCgcceTYJX1TR8gYmeWvv23Ic7v2epPe0Lvv7wG+TSdFgHV8f3UV6giVXJKMH08nu/6LduMWcC8I1lIccpjZ4w4nGo5TpasfbeAqqCuX9CFYsqu8EGHBvztu3ZBDHOSfg/8dM/pMgRq5HYUqXMCcIvX6TQ5x0irC5Nz0G/8XUBlD4DdtZrYrgmFpTgNd+WGbGQO2khmqD3i5rZO7ZyCWX5bJJoKrhYzlxGFANyotgS/pshl2Mtt/+Cfw772O0LQxx7+qcVT64PCTcjUkbXkI+/eFT65+kP1uwzJKHLTh/b1a1XygAe7JQCG4PRoWNQA7jQSWVdB/H+qo1FCvooEXmIDozehTZLUVZGsS4HNrEsQr+ix4dwhCjYlMHuIBEVobGR9u2Cd+uY6LjNpVKtXCkE3VZlcMTVQFdCnf71AhRIXS8rJQsGtXaO/iGtlbeqmYiqmyMlGMlYJMLC1Lc0IyDNg6l1zF7mNMiUbomgQPcsjISU9GiCyAM61Wv5tq8idc+mNnSR+xtCAN8QocPOk0mnrvON2Mx/I84pK7EVOPWW20IivyEXtUce87drTzqs0wz+FcCZgD8xPcFa8dQ/ICHLEdx1YLm8RsZfItllqXFxfF2SVpKCh+A561ve9BnylYUvMYeqQmXgc4bFPqzAL4zX4BzdEO23+Ab9hPCbQom4UpPOc6V1Ih/Kd6afdjwqcyo8hw1rCYi2X8x86G2Q7oFATdAOvkvrUv5sLpfcbOruS0VhAyBnw7apbjBWG2glWt8lLYQkzvdSMn+XRr/FjXoN2maqyiT4ltkzFn95p0nTXR70QfP2+BJOsemrHV+LDmj8lQXi2/RHHbt4cdOjH5SztMj53MOFlyl1jdRV05UH3302URPjfqaPesWoehJtmVp7nLG3L7lajVfK6+0ECKQv5eLGJtBl0Wz4jhCpvsN3NYPZLT9H4Eo5Vjgc6zkQ5byX2eemFXwa3gFbpOjmB9oKfrxavT/egbswTe/Za5emUaYL3xKqcQqYsScgcUEKYZjzmQJZv8bFnXoKhXLEPLCk7CcqTQ1etpqQ8o4EIuSqmRSWBbV9DYx83jqzZfXmoaHWSq4I7/FbtSuT0nlkATq19KgD7dXtIyFXmWwC6MR6OhTLmdlUXUNlSfwQT1dRavY3Ke/iTiuMbiFUbBMh6rj6AiX/+rMTavurldf0KHD8evfNNi2U6KHYhS5cveiXqiHbLMVpVRscVp8Vmbl7xWlsE6nOxfgXD+lJWjGy5T4pVj0QKyHwx2lLP2fT4aGFybVpHhy2+buYyFJOh06mih/kUTS7Ffi7lrzJsG06IQvtDG6DYoADqf39iPQ3LmnRuXUGp2srL7z8WWtHnOkptA8vTB3QusResxLd1aI40+vreKl5hIeQq2wIswPmQoTvcfEwA5gKAl/6Wnb1LEHas102CIURXheskkopjRnJ3FsJkJAo1Qv6+pv/1aYO771CGpqbi8t70Gc4Yi5zWutDs/uwgC7+E9BfR33QkV0Y1WxaTCF34/Fek1yeJcr52Z0bfOs271W58QkjKiX0nVMsr3oAqrj59NWgOnflXumfd4KCLzpk7joHjWmZXoLvNYi2Dfqg70F2WW+tkpbwqp2f+HmTgVDXaXLE7mzphSVrIYxhKdYdOf/2RZfgvV+D/yysP0/L4bBzR7WxECRjDK59Yuk7B2ZqO9nJsltsh27LN3U8MbEja01TXIVlxNCaPvX7+hYv2M/mvFcT6dzBHadFl6fuCmTw2nAwIb1W3veL7ANAEMb1vLdfN5ebbd5bpZqDS6q+GSXoaMIcFexDFyens1dVfNiRLHrkwoRvmbAVqYd2cujaq5DqHdqZil4/KTb/69bhFqey0biV4NVG5nc8XFxzyTP4uLGycbFxaP5KiMii04yUJHcsnfv05C3atqBB/QKDmS/zJOhrKO5rFx0oUwtEb2bfPF/pm0CmRFd/XUut+M0ShceO51bzsJOYSzhxQ/RmhyLvy63bl9sVNHwhrjAyfKV8d3JrXGBDaMlU0DW7eDDEr4N8qPAJqoVIq6/Q+/Exb4T+htxCWtFwIZCfr4N2wovWQRzdLJ0uONPOlx0t4/SUcVwl/QmKWiKB/EvjUqZDh13MPw3Wc7YZl9JRxXHS/DTVSeDOVl/78kDm1bqi9PXIlok7H2kmxKQUFHV2ClP7RU+u7MsK91EmaNguvRriRRGZ34U9jgsrdy3m5/92qbgxBIZOVUNNiveea64KiM5OTTQ5bI3WeQrU+2ycFOYeDhYV+W2vAecgpq33x4sU5JcTggO//h4YkmmTZMvM1YY1qrVCr1hm0gC0I/udU+5x9RFbl3RaYGSjYhu9dO1z3CCIkM3q3RVpliucxKI0B25V/CFbyzA2j+RAKk4UsiI+unzOM+PUZUtO5ZFF8Q4zPVsQkgdkIMAOxUlYKFV6vRYw7rpGMn1106PnDd8cPEoKmjHgxMLK2fOGZ19NP7j8Znhb9GeslCX+0eddQEyfaFbpwpllHaFRZJixynqoPc3CfWkkgi+N29s3jZHQig73Z1plt7S35Amws+OJWiawbjyI5v+UJp2fdNrwfG0Xsx8DeK389thgYDGuu9F5vTGOKq+KcogTX7mQCpw8zhMpv4ZKTkXwU/jzc8fyM4uezD60DxDQ3E6DiFa9EABP9mVFv93VdBscTa4T9BJkZhny0vCGyB8IuHNKA9M6iqfllY+xyQW8/L8KcUz/ns0ftrkWlmKab4iPtW5cxWYGapgaDJCvrRYu5LFjIhXTPbqP6PN6zn+XXov62CNMA/WQ5/p21RU3V9QyxDDo3V4/RbnRYkjOV2eGzJ5UAMh7PQTicG4koWLMmpj6rPM2b70jhTxjcbXw2T0ftdNr9Pj4kVHvDvpoKqg0zcOG0oJ9l7E21n3P4+DVDIss3WGm7j+mkE0kkIO/nczXR0JqfTh2HESXcLTLFjiWOpsZLF7RhE8xhIUawKBaMXgjnzxv03oOWPRZjzlsj/NBFmvjvFfTQeNqmga9+mMEawY8BahjOGKpY4lhQWNhHIwcgAlSLomOezl/xMnJA9Yv5cko3Mg3XNxztG2SzBOp82LKXguz24bbacqPdHhYQYFG4+CEtg/pZbfJjdHcRN36FPzVAr9UKw4c/dZ743sbpUecwsdv6ldwwAB4PTYWOWN3blj8wsciTkluJ8qMxdpJFJcj3F7lvq6SP7q8vm1qtyk6vL4M5mjzvg01atbTLdERdOnhPgmZebSSLQ+Y12wNVc2JX5mvkOZp2kaZtcylcuPXJnJ7iB8NX+z1giTt2znsxRfpLQ5ipVUEvtoJqP8ZjkDe0vWlR9l6Pge2Ay3JDcbQQzO1KeAs2lit8a0Ty/XMZ90zH7C0nHdJCz2iN3fp6ejdHVVrYiuW1MRXlKTblhlXA01NWP949ap+5S6a1fB+TJqWf2pcFMgaOMvdzEa82/QSbH4el2xk3n+EzCiU/ap1027mJTsWANJa6NqZfC5aPWMGcXFYFhRFxhncEoFaRmFPzSCxgULIYoMVhvihO8Yc1krQIhPKx+zICi8EqVZNUG4dg8xDVMpmXjvur0TRK8p+DAiGc7hBG+M7DRBfoQRkoRUIRYWyJ/cFR3Z6nIVFOzp1bh+6HJ4cse0st05FCOEztZ6uPtBR7BOMHcXdxylo7jjF28dQ5y/Cq7p6sKyYDGQA5Gew5TIYMlwZ6nNS43Ktx5tip8+8Qw2qx3i5WYUjdvN7aZpaTDBHm13BmjL0hs7+2Khqb1mK/jmRKpUoJVoBdLUE99YgXn+AISogjpTxZ3V1gTuxwtTIUSLwNE0EslVGUA7XSfxUWcFPvyunR9cWaOPD6oQaPas5GXFyE5CiPMqJHYixcuS+8Csya8Ob9WE6TjLF81SsWG+dD+Hdw2yrCz2zz/DmI7HihpDtTSHENrn1eBG0l9v05orNM7joY3efI9vQpI7djjWmPxnx68ZMh06a22x4X9h1bfP0QZ6p0a8qqs2tDmqBN/18SMGwED2kUZzfsV+ZetEMduPekixiPQc3R4j0rGLDUXiHxrQssNhRrnktkNBvFMs4fVfbcuBIo1sn0qaomOrZBijXEVmhus4RctYHSKAw5gOZ8fOgH5gbJ4Q8KCzkTJP5jQaK9Kk/Q8gkcSMQxvB+uHFw0soUix6H66FaDoW3IUdZmD+wxCq43mRY84jB+Avcso2fypdJtRh8KNHAj1zqp2tw3QyVPNu/uSSfsf9tTe+qqlWT906PFXVM4zsW0lydGGarYdfTQZiCYyogm7Pqq2WfFYOXauGBQI6KQYknZBaa85Hob3N6LyjgUKPC+5FFN51Gtvaad3s1ze/frXI8721FPxdmzlFkfJ9KwlVuHMA91gOQuiYF5pbL2A6woG80KBbhaNoy7PCdLdV+P4/g9OPup1qO8zeC0Y2NF9gosFBj4IYrdLE46tCS/M8NAB39sqB3g2uufi/IVlDG752K9mREzcQaPu6xptZg/UR48o9/nu2AK+b+Fc6O1pry3W2M715af9rDmDZIr+Dm20r3ZOWJ1iLpeDH2ulztT76pHKOXSwSij+oKXoebATVUyaT93WiVL6qfZSM00dCd6LVixd78v12utmWm3QBmyMcOv+/NUqMO8EewvBKrSe4E13Fy5Zdhq55zidGi5QcJr0R8iTX7gtQsYMnv/iRP7xhdrnGpiFa7XsrT/S6gtuXJhenBMs5tVxbFVdoK910qT3USYNhODqYLTxECTcIlfv2Iqzt2awBvLK8exKoFMNXVdVJ3CIaOzVYvBy34riqyxOD2xBqGtJgoxu4dO5+Uwy7mcvpYUXFDb2WraSL2mRgWnBo/pphG7DPAf2ejM6cVTyfW+5vKNGU8/3QuWj13LnpeR7OfrBPq8DKnoQY5V2NNTHXntrjE9zxq6LAirxGx6jM0SPd2kLoe1WB+91k9RH8fH8RItkpCnxGsbYwOtluT1FJflpZTLlJ27ycVk9ReUcOb1jWbktz4snVEgjHGKGJkyEBiapPQcRDGG1AX78/aVGEQmsX0SqcqWHi7VpIKJgKG81yHdfutnN1crMRnioQQtrt4rCkZbvn/slopxBUOyoMfuNRKmaNum4lfj2V73/XfPNMoNUwL9yZPy+uyJPAbAME4YXTOkK2/0xIE07HwD20c8x+gTVCx7w3fkoRWye8s8+IwMd+hPbfZlArv39sBdeXbP/dyPCWQY9qgKYThxFCA6GG/gNka/5RVMf9NTRZE11nqIknnAQZTWz4DtTk/5Z6jDILQAq4NlldvvV/0YSOORya9Jit4+r+vxyl5f3gfZ51wXoTFuaE1t8J12H32nvtLB3XQ67+bPERAUz91c/WobwLPfVHMR1RBv+esvk3CD4iVMnNPIGO+WTC9CK2jk6sFyCTTsFQy1ao4uajJ5iOeUBGACZVrlZbs97nzWzVlOO1kWXT6iV0SRiIpzMnR0Vselr01NYAAwbvdQDToeivfTPjYiZbp0a1tjcfbHlHdV8vFIyJXzv6+mVA/qyDg0tDS/Jm5fdDJDPif0wdc1u9y8DQ8YuQg6B6S+W61jYq5t6TJJEOdZ5ism6EfRLi8gHZHSDC08pAnxVzWK5WoDlMLQbxaQj2y89AGcXlUWcSTsZAy87l0bwulscigAXoxD++W6CtW1unRS1fUBS73Cy8+CGDeRJLZHUSFV7WLmjxVx364fS9k2dPg0tabhlq8sdf39M8kbh9a50JzdL8pkGH9LqHIl7n9fyIVHttxk/5cF09yBnN446ujI/SxjBEC18ppfWtNJXAhNmSA/H/66KSmpvlDnkbnSdS1SwTahxYdYRoxbZ0CMEHMY1nh1T1j1q7tF+ECffTVrBgZM1aSKkaV0Q+cXOrp5RRkRx/1/hQfnvzMDk2B8rRC+ISEdVYiBSLzZIuhntd6S/uh88vklbX1IlHrd73CrAuQ8J9kfilE+pbITbdfjm+6OUilzR25RtxMZcXQqgBg6fSeKXeSE2iJ6PM/NYJPib0Mzpb6NDyxGi8DsutN8LqsRCpg1cy9QzLpKW57uoRbhkjrG7a7BLvsubWxcuiN/iR01C8AHdlTVj7KEn9WpGCbX7FaJbxvSnq3LL426Z340JcAYumVRfZR1Fzh8oDscc0VWNmFjD1rMrq9oANG2sV1d9J5meXMT7CCHJc/b13OynuH6Pt2zw6jx3Ypb8HHFcgQFf+oRKf1p0Wk0La7PPdrZuKgdc+QTd263bI0rlia6QOzVl7Ok36QIabcEV+hBuCIL21O7sHJkhMH7tb56zVx+8g1cvmgUfFoKcDp6rFFCm8NyO9oOij+3eE/NjnLxm7slJ0V05b4nYX5GmSTCffnWYW6xQd3iSn4ow9Ylle0nejq1tMEY7LqU1CtnbSJDelVFCe+c0Crool209fwYIQT6a3aBq7A53bB82IY+M67MKVC5gOx1JwaBDMZ0x4rkgkVEFITZfwlUwhA21FBj07jBkeDUwZYZpiYLMoIBufF+3f+El8PjwvaEtZMDI99+PDYh22fXQlSViLpavTuk3sjmPAthQqeEFfx4JoMPiSF69gYFRqokMXK76veymWdn+Mcvo3M3LCNtJN63GBWByAL9Rn4Nvjx1PKMNJO1zEvtDNqOYplEF3HddKe5mbRoJkftN5LjGTr0KN59RcwHaOTYJijRi+ATn61OjXd8Nrtl7YPan26YawdnZ84bD99OQtGMjKFBVnVUX7XW8sO0ZZ9cGqjIZDia6iFhTmCGaok5wxbl8Pm9WS0zvfHA4bjBZHALRZr0tCFsMdq0PXQz5ewTyq7+/3MX5ilb129BoH3P2VVPzVFyJu85OgpFHpAKj8wjeUndDUxf+dO3aGwuW9iwkPe+lyUMSfdyBcNu7ije0upSKbVN/rK5wpnxqRQKdTrwAwplJCYIlF500EEtP4aGx7uMVl/0no8cYLSBldm2x491qM/nZ9anrHw/0OuMbkrouW1ds78Uuj33zVyBeASQZVwMKLMwUYOw0e09nNuu6X9QIDg+518RS4+ecmyu5L+Fx2e3qARfRV3Q0RMpSDC9f9JE+mlMWg4N4JOovQEDjCTH1unnK2u9ojR+UtNdIau21Bl41ID7322IO8F4aSTnqDHN+rYpMFQcB/GCBVaaQacx3XET5SrsuoKIohf1yBPp6bZ5O8g7O5FXINB++DZ/OvzmCM7NxQeQheV8rUfDOgGloHpoHpEncB0M6LVWrXOsccWb/7Tji5eUKhh2jc+dLi8eqGOgxN0LxEVrt3A9EN8Q4IB56tOj9Gj742V8vCvD9fytmiV1aVbgYLZYCzTs8XIFovRwCFbfFRaJ13H7hJQ/QFBjoDKWdhKS3t2pEOiQ5eX1jzBdLzaRJpxjnGOvCBjC4avwGk69GKfHsfK3wq0k2J7UeKCAdMxLhQ5i+g6FNqKPP1zot9CUmaQNJA4L/hstZukLLO72l9tFenQ7YEKwJfOAB+KnxU/AzlELFQ1+ReoB3GBDl3urbrH0KHXngS921Edz00DphdfpuWn+4lBEr0brmMYHAkpAUyXzEaySifP3oaYmfj7uEFP1zEulPj60YZZ/vw0NLPtMvkqM2gtJV5mEPufcSVVJFOHsCtQyD8ejAqozRj7JxYsFqNJtrgzQt4fOwyGBi7/ky8tKpbnfe+p8YlMvo6xPVQNYzpmEtH4FkrsjO9k4l0kokNn+yzDdB27R0UUfzb6h2/HVFrxWGuMGI1FYeZ6GAuGWBzAQO2Vf/Yf/Tmra09aHloYCQkEKMQ/DdB3DwFlFJMLCFoTmW4HtHkPJqGFTC165Ck3RPrGGi+LwAKhApSlA+g/joRYdkgWYsc6Ev6TwQQTwaBoVs/pMhSuRQUhDldF8rp8WUW+gX46M3+carwG89L7B3wbHOrZPFKFrnkjG/cUfDtdOqzrqPULbgqL6W9LuacFPMYKlykBeAoxzvh3fCzQAxa8FqW4k425uewUzOCO3UIDg2CAKGoKY2G156Qx0itI3+LWGrAUdHHSaLHw/DaJemPzl19AFlQ7Qoi3HVn+N/8u+nceeUyNCwJkQX7i5OYolig1I29UmzWalDwO+BlNuXGZjofRie/BUEJCCEaWANVoqsi6Jis4odLihXpWnfpxhODr559dlu7gSzBYLBldnCnlHzo/9r6VM5tKep6HIdI5XqORDOJ6GMJlbYKgNWR+JZISuPCJbi/mVGXkl08Wa62x/1yXUaUUMZmUnzAozez+dUprnC7J0xV9yHlHnqTzNBx3J1Q6SGtJlU3z04teTetd7fJHP2S/huUkZjhWroXi7cbAD6yOJ7E2Q1luHK7AIay6QQRLJbBIDEvn/XxeKdiqhjpgsVIsUYpFSZjFLhvh1ff+cpPBWgTax7xGV8yfbo7yra4iqarVnpjQDITHb87WLJfHPrNK6OcgaUlYxL5TfVJ26fhVq6hyDnMdbdKKuKeyp3HEtHd4xsiDn4AlMX9UNW92BQMiDqsTRQufcdi7DSqVbZxsnM29GflbN2GIEf5XITmunOfSox+5DUEhLeXKJwnhUMoAoj+noSvUJWBgLRF1jcZHAVl/wWoJsNx5lvd5gVydgQaFfQvRtAwbYb9t52TmXnRxDA5ovW+TpkeGkvTWG+ydBsX2deZl5/D4YueWtIst5aRGezEh8Fyb2rsmqm0wuiBf+b2UrlszZrHYStMvTBqAYIcTmxiOnWbSI32+lpbW1rYWpQHBbmCwUNPc3OLQGdJQN9IZI6BsqyNo6/NF0pmnMZplQYsiXigWNGkR5E78taeHCaVo3pTfAsUFAUFXtTYuh+xYu7UNd4SXFxktpdAr6MyZwcZKfBlmDyaxz/Zyz1jrqe4/1I76bJ/Nvy8kHfN809+b/0GCh1MNee1QxhsI+f3KZymmKv17rH648ijie1Ut0BXkgvx0SPBRZXp6WokxdV9EstV3d+7SnCmFvrpWN2IPVvMECyhygArzKiFGGEPRmDe+PHbmPtDFDLG5rsO/mh2pxU3f8DTX61GgViOIYisKAPu/ox/kyJMjDV7Ozum+mITCijEhSGkK4yyo0v4Yrsh8X2pSyVRB//sLMtZIKrPdLStq4jJC56dHpe+91o51yyGhmlE480qdOMxP/9XvB0AF/SsWHUSEroxo4tcxYRDc0oLKxfuwuXRWBG/r13W6T9N5kWdVyZ5RrMy7kaXyZBDDU4QUhsgY+ohsZPozybPp/7/495/ssCr3Zo+DYfj376il7HHfqBpNma6qmll9oy1zHd165dx5SiVk64QdzvHxVabctp9rUsy6fkURlIJM2O6XFqjcllCjtHzjxP7DKQQ077LVHN4maZhgqRyysCIPgcbPIJo5zCOjaTBy4ybo2ybyylpC2VOryB2YnZo4MR73vWhb32VwWXWXggaJ2Airjjf5y2yJV78sFprWO8oOQY2f4azho8av9kB2aNTAIBS7rNMd8HJ/tFOzYEz4Guk/JznMtHNwkNV1bFpP7elc6RqpoaGfkas4/p6SkYM9f401h3O2TZN2krmS4jT1wvyUjhUutGaENRWS8fYya3Fjlr3IjLcxJHIp19fcWsDgFCxqOOuGTCnZBZVFZRN2xNiTtEcubXXaydxr3Hi96fa48VEmdOzH+sfkx6/xghAPbm1hpRyCxvnFhM/5zno/ND4Lbux7AyV/q7HrnfpPKlBnpK5Nh1adwk8S+H3gzXSnvbgPQUZ7ijtFgLYtIU4R/P1QzWorBCCT8794Ri1fC8B9dPb2Gf/oVCt+vi+ma9ipGaUPzWHh5ocZpalsDR1iootw/VlHcTt3FhYvDiYfix+dpj4gmBaqYiaEQ7BuIG3Ph3/wkcXrEO8/SMxGOM0FmR2BzmEJLAZCDregyUaHi06ozRKxW63L8MEP022rK5DXry0djqGpf70G2CW4NZsUf1REOwy7WY0T+UWbXbZJHF9++/y/FKbK1Lv6onl1sjxvefDN4Bat6i+V9mbJWNkXPSQC8+o0RH1Wgy0l2dhpYPtxWpgOe9I+BTNX3BwKGf4EErmeiDtFnQhVVSeh13aC6wPb0UHttGfgbDOq+xtGF6NQAvQ6PC1MTan/+OjH9VMoWFv5/5NaU7JJmw+9D0HQtQyBAE4cEx9adnTo6LJQ/JhEWCDYHWBZjZ+eQOpNN118fZkRciCvW2F03e+k+DlJi1SpIUoT9esv2Z5zUxfO2D/1HS7lUCoik24uuTVZ/iDWMKetZUlHSt6Fu2b1K/E/1pA2uZRViTB+VXuRNKqxFVzeXBIdm5ufJkjujNC8pwMRks7ml7bIjSApX8duwKkoiAbJXerkxxR7JrRjmwurVHGqy7sL8PGelYeyieNNhJ9Uu1AeGU3Wz6wcBo89jLuhJDyXTpH0EyX+u/nxHwO4PGg/9uhRN2r7mbyaXH7Xx5zq6vtRofqhL6A0erdc1ffrCSG3I9fJpJsQVsqMZNerz5wsu8fjyHDfOI/TTSzn30yXCFafV8fXaU+uYcIzQ/u+64c+/bSXZwUY+RSiV1Qk0LYz15zU1iUkmJTSiWj9nqtw3H8TZq8Qn0/kGNjekNfxik/D0Cfw1T2oD+KF+EEC1tLomWNnJisv+ROQodkjN7ObX69UyKcKvYvHVjP8TP7ict9swjZVrlj5Ort57L+0QiTBf8kQNX56Jp2mhQlekB+Cq7y1AooVMXuyvDqydnVb2OJm04q45pTgowJV8erIWnn15D45t/kU+mYyCX1Jz+bv8gkpJY12/5tIRbDzrm4KFO9fR0P+OHAT/hTICPSfBPX0O0Z1/2N/niRDbzyLkr1U7e+q/7Ossgfn2H+0ViBRLM8WDh0e2CAi50dNoiTTJXSEYc+7ZTlN3IoNhYdiP21OW27l2RkIvYY4L1ySmEyJNuz9hcbZwvJECT5E3ztXlBS++wwBQfByZ+dNkppIsWfNX0VIvZ3O5TCAOf/ZHSEqiy4TRew+XTR6JcSHMhshb95I3JOErytKijjVktaPqktVWsupw0mjqkX7616w/y/0d0/ln3dZl5NkxWK6FD34LmGatJbGGH8+9yFaP3WiELvBczoKVn69m80M7dr1BrhwNaykwdhJJvXswH5wlcW4CcqNzD8CM5gswWFhCFHYMWgJC+KwmOIKuKrqGYf1x3mEKiTzoJs+iy0lWazrT5yBi7i97HDOvbhiroCXekrv/lkrjZFr8A26KRyHTpxhIDvtXGNgrHJ+5Oy865GcCRx2Nj1yumI6rIrCvwHmP4umdAcLvKZguvfXz9mRkADr4sAQE7VqUM7YpbUJ3SsszoyDuZJw88Y0+zRUmLNEamW62Rw3yzeljO/UN+zkNrAljxRcw79WaXSrn7WvRsi6HuWu4vdljhoOxmGL5vf+ygqxCRvGqGXx/NDFmXu4ezdea13yrp/kW3oCR/d08DCXW4HHmHJN6exGFihrwX5l8YMFmWWCeTroOo2+oRRbN5+PQiwkXVSm2eTrSsr9Hq6Fk86IpbKI8N0HTk2JIuQZFE/otAqjR+bLJWEqMaqfIrfJXfGHCBcN+im1tb1fNugCbWOFlcLnorjWElHZvwpNhFKEWnlTGhPzW+DSkrwNu3P3ytJzxjD0WF59QVSL/1JqxehDpztpHIxbSTHr1/oiGmOiGTV7B83JpojSMlVHe6j8umIwQvtclBEtAW9TLDHL5mUruHmQvYQgRfFDUjCzne7saqBf4RUw/O7/Ho4sjxd5EuRGpfjnd86MZsiQYV/4SwWzR9LNiiPZL/0MVC1uzxUtEhMeVacvB2tRPbhhykmbhiEKGpfAF7FghHAO5Zhh1jUhGknX/jbpZxWCzlQJKCjkUwKe+XX1lUg6jiK0Bno9Y1w6CM7DqxBCG//0O8jQuW8KDaPTaCYalnkBVr68fIkD5cYH/OBVqYaBjOoa+fTIi5i1oPg4i6vPmMqMNMXI3wes+cqXv4myF9p76AnTPOWDqq8cSeseCMiUhKc2houFbN0Go1hY2F6cgrC3iz+7Gp6ETkExrj5DNWHq/OUbnMi18ThS8vUTMR5xlGa1xD2iIC5bReLElxRZN0E1TS0Y/WFS8Tbo7f9/H0/jRWStBBkQKnn2t6q5IHJqCGELOY1SkZTIaex0h30bxaXcYY2dRA4chsQYO1EhGwlFTGotUv0NZJtYFI2OkLANrsN91IQpHD+r6IoOootgPH9Ml5fE62AbTCJ0moCF9ndHWmvbHoyTjXvQlujK14jA/5tG5h93crR36UwYgdgCTqNE3SO0N7U6RP14v0PU1Cq0NxqljZw+71U+fUzhP+Cz0PzjwkuO6cLzYREd0l3xczj+CVN81MwxYQJVwT28PnmB5nfA+jm2mGOCvbm9MHK6wROnehI2W+eydCD7UbcBxndz85UmFY0ptcfjGNudS2j/oupbbNTbUcqkqUwqnm9cN5GBf0lc46Y8xIQuTiqodu1Ng+mT9G9zAXawuq7SEBPCYDnjilgGPKoRXrT4n38WDtEg2rfM54xhaMjn+/rJVze9crzFUrYVuD5ATOhn3U7mE2Xm56tQTIKk73VVJxUsRk0IY6u5pE0mMbPRABr6TA0NGQIwdgajQYKhoX//XbgIatTghiKWM44Far5Ee521phFauPjff4YWUhCCvYOhytdCEMPLCgqKix0QkDPlbeaSrQztZ5eOSDB01eeZyidOZjcI7znTUtYioOQAcoygMTQ6nhhYUN/7NJCFC//5Z9FiuDFqFmIganDa1S4HC1bYFhhWQPpJdDjNhHdlIHVvHI6tQzLzvom2T0G/yctE1pHr6EMHgP3x4sZie9oRMr/vbXksUu1ZPkwKZGnArG0M2tOkAnJ4dyeG/mpKA5nfhhhYyuoBf8ZyCM7Q99Fxodqf1N9SqOI+kzwD5reMUEO05rnOH5rz8tW1frUQ5xTnZ62YW7g6BRz9jS3gBH0ZiybmZYCeKLy5YUQ2wtMEOsf50rBoQzODt2MomDHsC3JgE+MDHRre0NFbcaanLetvhDPAOu7ThnUalTOPhgnY9am5w0tcK79cHmQFlzdJN/YnVlbYuDgTyVM5Nesa/k5V3ANy1qCOCGOgxbJm2X+qXv7A10dBispoQ7CDwGA4FXj5XxRu5nJSdpXG9W+V1UNChML1MGaV1fdvLY3blcLhNsPof9/KKaA3LuLwrIsW+iNslGBt4ULgAZYK7og+s86lsYWMetJduHmn85/3aTgNDzsnsSK1iCPAaPDn3crFN9Nf/AosO85ZZAT6U/q3OmSWxfdtDGuy4tYvFKGLr/eXu5eBUwZgUDc3vIpW5wD3ImuElRKs9S9cPB7E/e0r5hIchiXMmmyDXr+C1KZPw+x5NJaoNpCS6lg11wWn7MB3pMBDeZlLU1MCtSIWLU/0Mb56XbECX1FwQV5C/IBFdXHVqkJ5MAbkhfSCAspfyRjSjmC1EYG58wKK9wkuWWdFP7emgFUJYNXXf6HWOpJLvH913NzH4WPr8Lqx4RuvLTnRLkPkjEhoLGtMWDTR1dM7saQqNMnq/kNNfQtP2T0l/2UHtmFnMJZbFd8T362P61rffhvK4REkDdmncgvcanj3RgjCoYH6qVZw7S9qLska9W1OTsJcBkfpL+LVD7VkCAOm98oDfm99iitqb029JExRKmr2OhyZNo5aLDfbRLgjjioydxeo9odH/xY/XxnmvH6tzc+3svU5SSkGYQnhUdY0cvw+js6nSJAAy0rZyLY10WsFe2+DuyPWmB85ZVtzUyLnA++JUTOzphfhNOwavm336QVoTMmuzZpQ3H2/K0p+DBpVuKvYiDbFJp/KKD/vNy+LKJK6l0ms8b9MQ5YfJY2Fndy+DiGXgczvL4pIil+7fWb2unit/IywAq5ncIX6waj4dhguDgeJO2j2L+y079jCrZu/Yg3lQ28gwZt/oT/eBoo/Jm/d1MWGfk6WNDzlsq62gPFgpZdsqyWcpVz4zDPJszMwt5RwtDfk8HeNB61X2dyndeO6K3lDM4gSNyhibHu4S+Rxu8mqdbMbGLXNdU+57Kut48HK///jneBMEQY623RENrKNs60+d6Yt4Coofi4bcT2TPHMBy71EnqHHEROsA66YRHOGME4FtWpV/DRr3+Cvwn2CH+hXL/gmgjMPp6ZAHfC5JA7BmYk6Tm3fUYRyPbJ1xKxRgU/K4wd5fAEfMyi7i5khri6L6Lg3G+zYflJgdSpv2eSfTMtmBU6/CXR3FAQXlvoDHRMCoUC7vi0lsdyWVOj3oV49InsG/kKh11OQrFZg61H0uyc/fGvmM1Xnd1l1dBylWU1OIyAQRu7BXujWvo/ejRoHLJ+WNwwWNRU3PJk4Ji5njCcDi4qhdeLkcBE3s34Nk55e+vD4ojAhgZhWkR6bKTbeDgDkGRNaNlPFY2R1RrlzHWMzhJ0hfkat8Nf0jOCNuzy2/vP0WlDonFNqzSwpGFWdda/NWR9QvBuj/GJ13kRfQWHPN3sjS3x71kja5QfW/Eabt7DNbuHvMpc9e9ObX3M9x5bXYB+VVn+rVJSsX7X6anEVnzwC1lHcp87fubCwzTJUhNOZ1zE0GbgKBzdZc7nJTMC+Cn4FWQow92E+zqqgrlMVHCwEDGDahMeXGOUcprPc8DSFMDl/FV4HmQXNTKwE9Kv6tShBoj1RlvTSC11QhCNrcf1XMeUHOy1RPShJoM8Pbr8KmB02Pmf2Mu4WuLv6TgGlAtm425EGMC8TguZ/XT7FkFM0JwPem0q/dW08l7XIBVGuzY8et0Wnz3LnCkbTblwFBSH2EokV8U7XuwvmjSJW5aM3wdVFLPai2+CWlGuf7wvNvnrHA3OH6JT2tIAshwPwAsW1Z5+seYwf4szDO8TbBNVhc3N4VmbC2JsRrZ73byT7Z/NXi//W/0KUi/YbY06Di+N5StLUmVxgexCOhZc+zLNXz1gbk2g3vR5MknN4SjbKyM2v2zvMpo6uGv3HF3sXO0GyXeWM4vQBs4Ufbs+pz5XoN8f5tL7BNG5gncm/bagu293sLu5rU1y+1rtg8axHwMXbPIXVVmU1UyzV82pn8s8RP8pp5oIcTURHIqe43oGt2y26XWn5dCbfk8LunreG/2rq71zOsxkj4KJP09s3v5Y7MuMZ58/LBHTun/Ai0zL9SXvDlPk/uBKjPr86pZYytvLU4QbBrFMWLj1oIeDDtHgxPt33waD/6YPwqxCri/F9uALPPlGSa5Xa4OTAzIDt8fF/eusH8CNS8d35eDIXJ/EDGEJTE814uWe03CPKyvZoQWmxvYK+WxanMD3HJVx/FJ4zsQP4yd76+fhdsfTIEDGp7sB+gj9mnzZ/3eWCOLCcJqGJ+/vUV+dOhueawX2H3mOv7Q0gzIP4n769/9tOaOgkgYZSykGikcM63h2PZr9gf+c4RHK9PYpa0UUMLGowYhD88fta4n+bwSei34zMjMFPYfQ5cwWrocWUmqeOUsfZ9ArkYx4WUgEJLANmegtEzzCVgERXjuCKZA0dTabw1QM583MaMIrCQpAtarb7LoAABACEv64N8tmp3t4f/PI4IIKb9Axw/yr1szGQy57D4DguEeAe/tzVGVP1j5EYw2rRakOMHfjQn8TB5vHT5Qs5pJBR8Mxb+Z/oKnGg2T85I3JsC9jGxU6x2LuZb9FKuIZ6YxLu9+ACyWol1sonpyvGdfJFJK1oJTl9fLD5Z7EXdQCW/9ybRj2KMUo2LJdoM9gDYFDrp8aN5RQywmSMzXm1Y3iXk/ULwJabw67bVNvIXDuLrCLb0kyVbdKxcR3f0g1aC4zx1m2KJKnITV5YT3ecbT7nnkLlat+hqZOyEcMzyTMDKGrRoB6CINira7VQl3ltV7QDZ166Bx4p7+yT8VzuT33DZv65PVBEeXlYKJlmC2x/I3qTS4E6Li+dCkXB6DEZx/KfSZ6lvEpOiCIFtiFAMm/xX5VUSpSPgOwK2UhBfW9cb33BJ/ETmV6lcK+xvyttLNXIG7eVsyUChLU20NZEp26lyO+A/GMZd3DOeyWLvBM5XjY+VbAXALD6m+a6/sWNdTXNc0UR7JYZK0eH/2sMeNSeqNr5rDI0HeV1a+fg9NlH8iLq5zL8WA6D1bZ0ST3nLoTguMWCE1i1Dk5nYDqGQHdTJCa620jtNMkv7r/Iwxsryf4hViOWq8dyMVb78JImTq0srLaJMww0Aj8WeVPHhjcGcXDuXE1tbY1yrWGgpobQtKZM8PhPOatrHFwk1zzA7gKal+MLHcbZs7Ktx9U1a5bbreJR11GD6yeU63MTaaOrOjs/uqVc+x572v690wy6kTuuMkh3xsGJ+gVmz7lFYC/KLmjNHjg7odzFfS9rZ7YUUXPI3DJHzleUvHIFdJiGLFCqDqiMvnGg2rUlVm/hbB2X+uFT2dMPgdsdTOKMZRdbu9dvAU83jNkivukq0QjuRUpW+43oTVxju9M0xX8ACnacEeohCNro1fvCHeCXb+c0GO7fp2JiqLWAoWGO7csvNRaL4DSuf3fG1VXV1FTR+YChD88dq+WuFI44D9FouzMiLkJnI+kQk0N/abDP6TaUJVQeQVfXAbdL/GB3g7rVKFLxTg+jPIECEIcJgWTcuAwuvQKXXMYHZnLgAL6j+mr3OXB63J3JGAwIKOB94A1ABICxO5M3VT55fKlzVw0egDkzb/7EfrepZdQbaBmEl1wGQxGITke8bvuISwF/p0yjnxecP10907vj81LgnfsZx8aONeyS7DLU1h5xhQk4P7z6D4uSyuNc9nHl+HFrosGHfxzLuOoY/5F73LpuLPjzRVbgumDvWFC2dR3G5aGH38mHt4C8KdhNlLkxzuYxz63fSddPpXVnt47KrtnIRG9izCPzznC4884cAeYmbAOTs3lU4d1y1p3yUYVntxIkHUpf1NAUWhwea7VJl4Q3hRoWpUPg5Yy2cIg9oSdHY0vM82mYbILNUQJFeSbeGQGWxnDkELNnDo3mK+7+rV6gYnK7SzPI7gjw8m/AhUG+VhIvo7MxGszvq8rq5jJVgtKEhLJZSnpTDOiEGdMlcftfjLOqsusxBIZgFg2CaTAzX5Pg1bidPcB0TsAd+foNm6aJbl1PCqlGwwehE0Ef5G2AchsgIP3O2zO4MamZYGDGNVe6Q+G0s1/6fCQyHjO/+3qEi54InQwpjTyj4R3b+8Hpluvc1VBuA5zbAD5jhlRx6PrEa93GuaK59qVP7aEzJuZEyP1vIBjznk2YGTw+LXbXHXyJMCxPnKvp2r11QtiPqfHvlfDW1hu9tlredFZ0gcitalu5rm/tntsv1pJ9sUlDPRG7Tgim2WPTDovrZq2r0LgjCkS/7CTa5/pqeP0UuHejxdrrTHZp68Q0mhHLE+XZ6MzdBrthN4tuFeXlYUaafL7NzsKF3LByeIw6l+MIjfPaAnmG2aN7Wew9htmBvFJfWYINddDB/Rtpwcw+MiqmwPKtnpOa57UsQWGY2TFuaq/HZSrLrG8oorH3ZZaZPK7ecVOZHTCMFujHXHkkbSZ3+mKdhR3jVjOGEbXg5TjG6kLfKldV5YezzHsnD1r47KHEPmve534pU08DnddoV1F6Q1vKTLjr4cGtX5Ffvbe397dUWerE5zs3nro5XzD/GjV3Ue2p/4ZvWrW8+1O1YNyt1StwvPyjNsjKMrsTk8yfaPnFkYQ7o+mBSGwgCBMOvJWrwDTSJpN8WFBy1Y/HPQrEkE0yvYyTO965ajfATQSRLJ8xR19jTa7M81lMent8DoO2Xwog7wV7onne1FST07aNLpFhwnU3l/zJ5YZA7zTIfPhAjVn6us/DnLPD69VWbAC5NUgoniB3vJ4S1hLbrEWgeV/+zaO7b8C3isLGHnEDaM9uaHQ0nAkc1XBjUEm++tbpTNlxud4ATNbiyM1DSmjHJasNT86GbIFvJzNUYfb6/ksjHSp6c8vdaKygfk6toPkmH4dt4sJ6CImmQSJQLnVPC66He8SZKVjzR/mLiucMA8ksj37/PmvKjI5qUCAQSTCKpw9+3GuVswjD6jSNP6dbP7oStdWSudoVAVp6nj+wfJGALTSMcCSyo1PPQtP/fYzpKYzTXTYK74oCg3MSuC3YyQ3i4Jnk2dl5F3nx1MrFFlSAyVyl/eYDO7k1yw5C0JmaVRMHfznfTY5LE7HBS++Wp8JjL8beC7Cs5b/yHwYXRN8VAAgaBbJe8bkjj6dUb8/jEUAcDNzpopuzj4sh5eTljibD5YG0m/C/2IJ+sFTa290uzp0VKKdHIIwfgqUyeCheKBGSPLIYLy7CiyN0VSqbG4VikfBFIoTbaqSlQMC18OQ2COUKtULe4g8EPRwUYwY5eBDYgwDnBJngeKFYwO6N/KpySz+JfagdfBOKtdRENd9fyAu3jf9K9G9L6VO9NWm8lshetkBcRHm/QHxv+QUh1TCfRY9A5hmMDIrAzNfCjlWDB2EFnShARlGzkXr7feYL+MDbR+FV8C1gecuBmuDveQn4+fHjIpwFw8b447EyPuC1FSEjY8+JutEVBdkyRD5kZbVs+puzvaVRmeL7dh/vj3cm/PhfGk6kMg0jG3UBlQAhTCyVXfdPyVEhyoxX52X4zAuEhrpyyQLepJDXxZ1isjSF9/23NZlqBCK31E76YWbhH5uKgKPHlDqqHVAlVGHqxGiQlEUmXm+O13nteJqCTf9tLEaNjAnGUlzXyFi8WUZjpjdYT5laGS3gxyXm8CxkGY8BiMFKoNtxIgpTvIoNN+eH680E4sGb4z44buYxj7NRlpRXLLnBgLOQNGKMY8xAdfVbi1XRb1gGYzNbN6hraLod5m2SyWdtNyc7XThoIEhZoZDk/l9VX5nEDUsZW6wu52h4cHk+B1o3bKVMU7/FGd93IR0O8ikATFVlnLiYwVhCyFOYds1Z+OfT2zoBnzg7aHA8cHUfMnEhMAhCahwxZgpKbvDhmqL3AmRi3ObSzCCNvbJdXf/19eoopFwSl/UsJqO25cl44Ph/EejaD7ce2f7f4db8VNevT9Js1A1CmIQMoZb3CqBLGhI4Wixinh/UNSHa4oZT9UBHng2RLVZLSY8gNbr/0cHeQG6Kuz2tpDOmRQE+Mua26ZAr2SYGpXx0E9E8dpVaB5Q/14KeC08RYqWFIj0CD5e/JZ9pO+qKaeGemZn+uOnLv/qb/vvzVgm3AMDAgITMZXFMxdZRLXn7H6o58LABuuBPjKwUejZc/nNK5xTklWpsUoPH0jAs4uqNeao9tQC1lkalVudjy4A4jQFa7GWjHNc9vCxbPPGQQ2YNi2IY4FY+YylPoS8pekl476VT/zus+0i7mHSIplRp2ggL55RVljs3WSUCy8b3CePSI1cTsPSeqCbFN1ILDVJQMbjZnbNExCTMipBFtANkQFVjcUHOZLKqMTxGZIJG5FQKKN2n8X+tOAJrbKT1YCbIKSKmzGAoz5AxD1mSpQbVe+kmOGyAVPi2LSImc6ALaK9HQllNdsVruoFKvsIUwJp6xvQZoMhxnSKaBb1HU+bhWIzf3TmLwOc1R+4COHpD0HagVbzmwr4M7iM+lb64jU6N832KbhBHl3GPGSAPpHKaIM4FkJ84iYI9z1IxbpJ/q8+xxoLcB/QxSx5eVpamTCEH/p3C4eh7T6Zuw+iSI2VaE/X5Lj4oEdZOXdvCInLyTFhkaWbt63kqIzlQ1j2Se1QHaQIAPiGOxCmYXd8Ngh5iUAtSqWayNWCJfzkHQ2PcxG907lgPy7PItvhVgkfUMZP1a6d6ilWsRG2vxRzgvGXZyjKfQT4rA9qAqap4A7YAywc2MiuyMtDfZoWZl6FDPijiPvrKshEJJTV6ZYxRWfCMApzZTGHh6a/4gI3L40He8054kyuSzYJbR1lOjadu7dy29KVwkyzFFo1C+4Cu2I+Ox6oKicVNQL7j8JBg6oKs5Usubyfl3SEC23eH8by/O8Lp0u40UcF2p7MnoTtavzu3i4xfXJ7kucfhKqmab/AsZ0GQDAUjnTzipdgtTWkndGlTlCpK3iAh05Q8abTAR9lFiyPAW6aycpFOOBwHlVsGTumE7E/S8u3OR3RGeUnX+c6sLTUafbXELU6NMqcV8DheWvY5AXqJLSdehO6BG8aPUo1U3NNKQq2FMhxqC1TjnDQxlztCtOgvzkyI7MkVXQJMvOo+NsZlXBicEKq9OC0/J/WQX7IpDIhCPfBFR8VGFGFyHhcHYrlJJ3XOWgq8uE+Y43Jur0BsQToByT1NObpJZIa73gJ5IkClwEnSAqYqagOnlHPAw2lpYzQV5kIT7QjccwogR/OIBS2OLLdaG3DHBBxNowmpOxDCcYXx1qpsW2XEO4qkmqCJI26SXqOaQpLHlWvLSxTf/UQ4KuTmk/Fh255dvb0ZRdAV12MC8PGvg2FiYePg4uHDEUgUASERMQkpmTDhIshFUlBSUYuiES2GVqw48RIk0kmabP87jUySpTCzSJUmXYZRMmXJZmVj5+Ti5pEjl1cen3wFCvkFFCkWFFKiVJlyFSpVGa1ajVp1gZlnviV2mmOFQQ9Y5meGLbDVB7b5lMOOJMtnPJZmbMKXfMV9PuehDDNzC0sraxtbOxKSUgoUKlKshKOOed8J7zmu3wUO5FZlhhluhHIjVahUpdooNUYbo9ZY44yPHqJi4hKSUtIysnLyCopKeRAVVTX1/HlNOAKJyleQkxCLwxOIpHxEFCotPxmDmUt+JZvD5fEFQpFYIpXJFUqVWqOlraOrp29gaGRsYmpmbmFpZW1ja2fv4OjkrNZodXqD0WS2WG12h9Pl9nh9o7ptEYpUnAhRCdw8KFvr6hy/gezLdqfHMalWHx3yGmlNzS4N99nvbnPfsLabl7GOYZge0Vu6AoAOBg4ZcKWH1HZ/hvqsyr3x63Ljea/bPYrDnS1d91Ocvm9h+D5K85OOKvN/7T8Ao+GWwfdQe8XFqDViY9QaoTVaGS6IW+ZhukDAxVPQE07OcJTKpygBDcoU7vlZuZyyyT1nr+qRjESPpI0eOpvRaTvLZF5vTkjP01IjoCYX7/SI1nKhum8t54nMCDBJwphUKUiyELB+b/hOiJbEg+m66kZ306EGXblE81vm8Ehnv5tJwhQWtKh8DBuZD9mR46xogIs9maaTPC0D4lInC5P1EpmY6y2QmVhm7ZF1audS9OwZVtmGQkOzTeV2YbEZe0l7m7q28yDHs9FXrnnO+n3aKUFaT6g6eZDqEpFmACOYwmTW6jsQraXrVmbmJRaJZXvdbarG04TNCKYjk7kA+O7ZTXSBI5nROKGnJ0KHWXiqcLm4O+Y2VJr0qZAGNm4XRiM3CbueGSpT7CZI407r8N7qctO7O+oaB2HYbDZonpEGvOnarFMFF3NXWlypkAYu7hbuFDCDBVIhDWzMUWEzucGm6/QNLr1RD/I+AFeLyo5A0JLywxAY2mMdRwDDfkwLYBBn2Xo8DOPTYNahXoMK4fVM+2kGoIGmNJtS7KweWIH105MAAcB5JAoaDJQdtg6WHQoaHe3ABxjYUYiJ8rAjMYc1p7A+ANF4UUPQh57vH4/ACBrMi/sAejtvB23xEXjaeIzeiAKnEw==) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(data:font/woff2;base64,d09GMgABAAAAADUAAA4AAAAAbBwAADSmAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAgVwIWgmcDBEICoGjdP04ATYCJAOCDAuCBgAEIAWJKAeDJAyBMhvlVgXs2AtuB6Saw35rFDVrkeqJItg4AJHwLyj+vyRwIkOKN1DnvUAUhJKMODqjGMxi1tBigiseflYsmAft5jnKWZ2ajGAY6FVmGFBFbedR5zFHax71T77ZDiY+hLu89hMO4eA2R2jsk9zhaTr/3eVyufhdzuKNVyxJxdNo0za1pGappVgVWIsXLzZ8jhc2p9hgjuiKyYf5HxNj4th4/vsbnfvma00eFJRh0HhhhMGaLOqCLPD3q7Xe9/qAaFJTs2Knjfp9GwA8tCGhIgGVDKtIEWGi/8LawiSTfxFTa/xORGr83++1pXuhogFSYJRJdX7aABk2pCMjaILSUZ3SRa2uyZ/dgKFqQ1XneXPmJ810SZreBEmGCoEBKiAb2ua/TWEJDlfr7c3eoy0l8dBv4wyVAAyfIfnB+61c6tyYSdQ9rQCWIJbOyC/dpfpTWnPlTmCQ/YBEz/Pp+UVKUvKZPx5fk16yKa37P51VaZWWACLOIYgwyDbFUPolWa4qyWvL7R653KCWu2fH7gVbDTstD9ntmWcvMbphyL3IHFEKmOGFG14QXhYTBUF+UXpBdn7fWmbrV/e8ZCpAKuODOsIAKR2hlnqpZwO9AWIXGR4ZaUkesIvKuTujckKzsSeModdhF9Hs+RWl7X7M8Q9naxEp0sk8Diz+HItspQymyhWreir7SwHgavArLgB/6i8AVnPHDv3YIjiteAT4efLj3+1T8fg6gZNuM3LcaaaGetNXK7D2DgMAfWsPAJ3iMiUHpVYOcTMhzsaT6OiWllhgu5eMU3AEssEZ8Bl4Ar4Df8LZxHmG8zxnK2c7ZxdnD+dlnUJnNxn/+w86T2n9T8Pn4P/BH3M2Lrqb85JO/t8Wvf/Gi09d+NjmYvDxjy33fOVzn/rYR2KRMOZ/2e+WAsLJGLe9yr6AZFAU8vzsLy6C24BKyODS961vBXRqdORw9ufmaR2d3X9b+EfmGxIcQHXoCdHKEZDcWAF6631A6gwxD+KUcvV9wCEdYyhjP+KhsRagHsDD3RpAsOgdIIVuM3WAlS1bB47SuOH0rxa+A9IoRq9wv+PVnDPAtntIEHnA1kljINcPa1jEoof+eNsOZdeSQevkNjFZ7GuSPUNfDFARJlIzlHkRksWJoC2g7ESMNu5CGxoTjl6BJO/rGDasN+oBzYL1pIJsfwiYbXKdQ8X1BnUGC5n8RpkPhJQ1pTFioiaSTZKVWeXR0QyaUl65ZZCARpnpJJiAl9JRuh1FBqoADhDZCCAUAAI9tWLQREnr1V5KRzhGcs4RFztXxjgJoFhpfiC5EGCMvThwlXoEJKTx6vBsM6DjPTCMG2sHkxsHNOgyophW3s7TGHDaME7AtQ14U5rATa3mqhtl3ftxRrUWApBkK8RNk7YlsFghOG8FZAYdzaD+HHgKvs3UAI42wjL53jMBrMtKgHdTQHsbvLQIvpLN2RIbHSHYKZVNODfCorFbj0+gNb8J6/m2TR8kzE8nQwQVhyJ3HSAhSiIbt7ElIbeAKi6bQjeDM2w2OMFmdGfaMuKnRGDyYtBtgyTg1oVvxHXkss1gDVtpWwepKiwZOs1NDrgWDxGktvwc5Z32XoCLcIjjpCEiCjw0E6QjBPts3IKwSDnsKlmHeIIyXdkNihy68gDiIx/KiGzeCis764ixZRJyyZQqkNleUPb0Adp6EFEcS8PBq3SD8aEADZnACSvTIIaBNERe6WZvwlPLwwGBYaQQrDzILlMgtQFwUHoFYlZMJAdUAHq+C8gAHD8gcnuz0Voo4Gw3QOY99SDxHmIPeOMp/pKPW+jgMrz3OCDqQgPaKlWOWBVn98WBUQV0dGArCUJyCUYKiYOUEoJUEhepJRRpJB7SShiKkvgN8cok9Ap0s7IbmEpAT14ZeSpNGDMFM+mrlSZOYZn09Ymm0wKt6G0zoO2PNbA8L7Ee67ABO2zEDpuww2bssAU7HI0djsEOx2KH47BLJilVaYuSsyPkG+owyzqRhaXIkjL3XWNVJEckpw42JRoQpS6nWX6/Fy/HDAbIrq5clRSzvOsLtlS5HPTCI/HVSAlS8ZLJVKXn9tenacG2INMsAEWLuh90AfMLnHzr1zyrqRx4fKKkK1U08MIG8NBryCFDy+uVQ+aEMKC+MlQlSxXGMFBJAwDjK9PrUl55zg84URia0nsRvK8zycGVtkO2AjDf2CmChBsZbasIbEWhzUxykieqciasQk4NlaCDgXqvIgjhwlLVeSM0iLvoTIMkF6fiy22rwCgb1WzuR9pcW7Qsljz1Y1MFxOKYwbU0JoWLJIMslruCivHE5Okt4X6aNQyB9T9uyS+iawcjNYwZ5T6LMTl8bkjgQgsgiSlSoJynVk5H0ThYHHIRodSLcRNmczWrmCIvCoFUsA8wN1AZckgQl1S8/QBVoaSK7UfdwCNG1pnIuAgapLKlz2DUWHab8xplzCd+KP9IKNB2LH6aU9DxQKRtp7IZsLwwfbAWNs22BKqkVCv7M5aVu1a7bTa32A1QtNqR9iiy47yZXkVOzLoFMm1H5swJWUCthNWHfFHAZKmR4G8niGY1ALDvyFRRybNanAJVgYP5iWM6sX8vBrkMnkExO3TFs8ZmTcyKtcjfALIjNrRQkpeY5VbzSQ9EOYpCevG7Grzm2cvd0vS0K8XHA6oA6l9GhrBYosh6UekCYDKO1dTPacxcmaZ1TBNnDpVrDcDZjAqGKCgQEGLmKyVxjqsSjKoFAmqYC7XagAWWq1sURPVlENCgmG88MrmJyc1MbmHmWzUj4zAybkPG7cjyHZI4z504j66kmCIKst3WYc2TkjxNLoOAKYp2KkswTFMQ9ZRBQK/ifp8qyI2j7RcNwznpAbjyg6FPVnBnp7cLkCTN8CA104PoiXGQB40hnYdhnWdnseQBs82B5ngQzfWg3Ly9xIH52oEF2oER7cBCw2iRB9FiD8otgZJ8LNX5AF6m87Fc52PUMFrhQbTSg5ZXJewXfn774eOoaj8Cc3x1Rr1KX1/vjh/HusY5porhD9m3JIYBIFBP6uQvUg013HMAUM0AaC+nNhTAOcEs3Pafg4sDisIc4AJHKSnKACTZmXtz7ipkII0Ohk4BOT9ACNj57c9zOlO14/xJtLxNpCKS7JWUigSkhJJJA7hEpY/q0ykxEUXLYnJDdrQ0lUpIEEgTGZFIJFfyRYp4YbrMIU0SE2kaUag40BybEkypKSXlyWJjdFBam+331DUEnDqK1WPabEm8xBotixXkeoSUT6QRJlUZGvJZSaIknkgN5WSmt7fX+JyCQpOYEBMSYYImSa+QUqQGkwhNhTaFlFSYvPE6ldfhqJBKWpq0KiZZyBUiO0UYuBF9Ka7jyYSlaW7Nlsy4EW4GxhEnIa6XxJRDX1YLy8RFZLJqxOaDnDofxDFUCQQ6r0MUFI5+e8J9J02tEKhmsCbaO0QDIDsfgTSm4NXxjnNR+0QhKSNOMmGKuwbm2qx+tCLKMK/5V4MvTjM7tZEBfUT6SWD3DY0Ay0o7k4G+dP5ZLLDpbursBipMXYshzP1qjxWg4D6eUA8EF70SCvR9I+UCWwJ8Ie4ttPdmKTwviWSRo/Ck8yYQZl3ErlumRE7KAPW4D/GBGQmufnUTtAd/MlBzVMaVKSwY2Uy5wa5g0dUsigQrF0TA6vHlqSFdzUt4s2A2kxCK40RCcve1hAOE3sTyIbAq4Gx20SUjbxBftQ1D9PurullsijyzEVtcYCa/3a7235trjxvpy3mFsf4vxRSkOAs8w/FJiJaTtENbEDxs0Wh1xUHx3l81xRWWE/FsqH3h1SPdK0mpPU29zigivqpWQ5wUiiemD2HEX5LqSVmWa4GAWdefwxAcckKypQ48XOeTw+SzaxAG0SNPNCjaEOtNCCWgCcQrTyJGkMxN+FavS+TQ9Ezv6VUsaHokmx5hPjpuZwzboqyeZzY0fdWgToDYkqbbtyBus7cOA+yUrNqVX+5WzZ+KwnSj1WSOQjfDN9DAFunUdSldx6IPV0TYu+ba7NHKGCLRH4SYKHCdHicQ8UaJ7mzJoFAFhRvT5yqgXnpaZptNBnoESuRKcSYn2XYZo4jmVOiuebM9fT1iyqGepztJmqT03+l+3z81pWLEHjLAxgH8X8kmhTMuegZ7w+3kKn05c2q7MESMZ4UKRoibWHER8UKJEEAuqBY9LjwPd51K6Q6XuGBtmPMxbqSDDMmkmbbUp5tVgKNHG4LJAP6MLsnhjc6dcLVuOokGsdollEKiMt7KZ3CM8hfx2LQAxK9jLXgaWRszql1/xLU8erWUYEW5kXkJQkHUY272QjgwSAB/HQP7rV6h1JmOG+trRh7vh7LDFqv37AaPZrzefbvKqa7JS7oN0Z6Y9zmCD9gGhAo6PXChL5eMd4SaZblgL7ipaNRPON8lxKNXo9ZgyogNh48k2/5QisOu3TsafHYIRE6y33k3MxwjoT8d/fUN4rOud0re890J1p6IGK4Z27ByErqq7L5liXnwFmJ28pus3MwFWEmzahbsWDEHSxsEeYPiwCPYs7vvMvBtWKH49X4F/ehyot/IjVx9+7K1HDrsFTM8Vtffv5CMcZOTofbokeUU/LoonsRXxEVkoB03geVuNhxwU8B1UU47zYmpJkuwvIHFm7AnD3x0IHdkr2JxsXKRcZCBaBjeYskqm0RBa6d7WKvUi5AkbzQ3gv5vRZyXiKQmN0zOc/jYUSco9GqqnluBQ5HJoFQZFBaepJ6ayBd7DAvm5FOuCsqhAfU+jqh1UDC7FJ0KsRzoRrwoq0ONaQCiKoUKevFl+iQQLiYPo+UZDOeTP3p+99Xwy/chbh0YvrdQSEXDKD52+7AdQ0/DO2igfsXTryi4oyzpvnOIy0sBussTE1sDcMJFvhAboGOFxorWvC+TdzRSLBf+0IA3PU5KorBkpMs8oENNXBlbDIEYqVEHtQY81PdWU32v6dz667Bkocp8OYJjR0md0jhmzU5KjqMV1DEUQGGmzHCGkXtHwyVHWwKseacFOpUOCjZX52aRKxXSi2aVhDRhBtgbRV9520YD1YTFvFRVAh5K5uYXMOjQgSQxQDe+lJA0pu9Jg2qUnEidU7RNSS56+9UINLRRn4UwgDOT0xtb3LyqU6Bf2lbT7up6Lkj7hG6vKqy9nBv2LIDISx6hMVdInx4t2JckU6HuQBDYZglXFQXU0yOAOhBmch4uBFUrwAMmgFXA/DHEULBBLSRlOyONuM0FQduiRs1AUJ04aU//uJU26kSFd8dez1TnfjKLacgq/eUzqGwWXD4e2rEP4nD10kL7RtUgWisTEc/7vH0Eom1YrfmkGTaL8od8+It+FyCjofZOK53tini3UG4M0qrvD1sESSbnzkVx/lFfNROEVpHvGmtgcPGcs4ZoEfV8vr2T6bTDVXIcInPsXL5ud7jhKJD+8GwuwHZ58ZS7GLVWgJ7xDgI8j06kKzM/BGYx3zeWlbaK18YSHrLhVKGrIeIL2Yv7y8duNyuId2aE1X1dLJVMxuwRlGLP/QiNwEKkStJDOhMjvgCXcsuf8/SIPo3AUYKXOyWosutDiYyEagOCLNGL1YjwBsrbnjvyedBv0ODfg+Q/3ZRIOrG5hspcmXmOjtYG/JQO3U3OG99vVyPm3pRSvFSFCt6HUTpqpVBBRSc/Oc6R7S7qqkBs/0eczsnkwYPKS8cV+6ftcMRT4vvDUfskyGqLa6RmQBu0OqXRfMw4iJgkp2htzSl6pANz3Owkgh4358w0TBpMuMm+IEC2bRj8qeswtnU2nfab58U67VbwW2wFAhMOjpfLuK+v50QIFp7EmUD8EafiuTTr98PiAYtGQGi6zQJNFQKrplLzTAIooxfuuR95sy5DnjR38x1JpQnYWBrRPdP4v4INkjEturBSJBNp7+dDAIwXlb8wqZf+5kXQaeV01Frr3tXg/mCZspncutUNUWVkqk8zqiLEX60rFwTPpYg5RWpBzqiFLcBRyG/CWdZdWrwPo5keyEVXVCMOvQQuIy9QGU3fT5JFl6tcdqtqHICgMinrEIXba8DLaqjlsL2zrJSk0UF5djXlERBorXj5ti0Z7TkRmiJ0BAFDuZfkaWauCtU+7GxUBxuz6xlKyaZyI5EKTZCy6y0UEhqcbsAByGBGt7vwWWwe9prK5/Dc1k+5F8Iid3CJdGMmtUCHLp+WK5JJmFEDJbdd14d5TS8fbyk+tcCdRPNIglfs+RhED1pcEKYKFKnypnxGQJm1hAJn86pO0yihlIUw1REXMheYwJk6ToBLrqYhnditYNyC2MfZJEQ/O3VUvEK8A7x0XmsSsUbGgU6pnw6IzGS7KGCJQ8fGkqWdXtEtENWMGgKOYNXJtPHycopoKHX1kqATXam5tj9KGYbdkRZaFzBa/yc6WSb2gTMBQrHDL89Wx6GNlcIMTZxoyakwRJtnmf0RU3+1/XmE+Wg87Iio/k/w3aCfOq/1U4wtX6xckOQoMtXEAF6PkwL921ciajaEM4FTGifczJsmei9XJICIzfT+HKuX38bqnJjp6fk1ICvIvpFhFtkdIxe3zYu3HW0BGPi1vE1oMla0DmKNRk+58MaGFDw57Buqzde/bj5NCDwo5kRv0yFiXjbdBe8tPxxUYVPcuFP1ElBU79Fpag0sAgo7UGaJDVmJP57nQ2uLUXiA3Ps+rOWBJFi9ntt3yLAL2SvhO6Bgsia28Y8JHBXE0roR52OI7tvgjuF1Dm6Ourj1TDXfE/ZZA2iwIMpFczxQw8+IuJSnaqcewtdzMjrqeIHSsPLWrDJHgnHQbJVWCvWnM1Y0grad/zcVuDJDnhT72DBMFbr7sA78kL+p32lN/sqdlUWV18ubJbaN+vWMNAyUL7Cb6vp50yyposjqLt1t/aDTEYjiPkUaCUV2YRMFV4xAXBDIoVll1LXCMGnATAFE6JbPo4ZO68xoY+hF3RTfCuWNHC0E98le/QyX7u4jObDNsJ4ez4ZVvzgOM4HLPqOh0kvr4Zgijtv4JZRVOgbUq9CoFfDjSi1zFqCklcT9iMQu8x787i2o63q4maeSTeTVEeJuppLufr5sL2Nqa9A9k+RGSL6U4MU83FmO9ycfG8wwVjXko0lFr+oxT5o5NBOTOycJmWoprSKDiwm92uqBq0ujJDbNxSqUMl7lHr5sHbJuQneCKp+I6qvV0H/LoNXCDZ8PdQgbVjMfIPS/jEt2rU1CY1VEBFqkaG+lLQbC2aA6wvl2nyqx3EU7E7n7fSa36MX3cgqanMWd8kYnGaPR2oNeQ75BL1q7JnVnbJYypGYFujwDMyB0uf5aXv5dlZubpvRpyBKtbOmrVL716RF89IAGNEXuli3bntPCK0NCy44ldmi/KGXGg0SRp07VmI7A7crE1cKSCljBMaETiEbM6v90wLP4CgoQfJQExBhdaRo4BcVvANQUJ6utWjLRb8IdUKmbql2PB1tU7wVj2Ak9GdjnhNn/rwt+maj+g0ysOwOhOHfbkI3aAeVcxbp8eqTdb43qNDWmOcNhucGjYLph2J4no1Zdw+hF+pRQA1/qPwYrqmPEeAIJ4xjVxHAiw8p4NUBo/DM3tFgW0P5sy5o8G67QP7HrSy0mVzkKEOxiqrIAW5MhLeiVT5+VahdtsersRnOiUNb4cEeWnmYow8w5G1GPaFEpeUMYcOdlQMSyKJJFrdFM5fORJjGoKzPV6sCrRRvUgEubCGxkwRi2S6XBnlF2zeaXHbGeYOXQHsSe4+H1aNI3GK63XYLQXfoKz8MgJCXaxG1jS3eTo/BWr3ndkP2EuACdGJO6pkQEeVWeT0GwyaJztdWqgLclLlG2C0XysN9fGDvfLnz4dl0iRIPR1Cjw2PzAD8z2+jNOzaoyVpfwSf0nA0eyr1k7kxqcb2Os64SYrl1yuj9pHxmgM8IECK0NTw5Frai8bX4LaegsgGirzo1nyaak2Fc2tA5Je50q4dpytQ6X3QgZkHVkbAkQ7HEqFsj0IWgQn9KhV7LVApNKygdZh82+zw4uqOxSZfieHdnI3dvMS/qr5fWFIY0a+Emh/E+puKdQSbFZDuqjuO9pMdrGbYh5Yogih56I4Tag4XHx8ZbDjKdQRcRwGBRwYHwUghPGVzHKSDA52ZHK91GLEhblSCXTuHBqirB1Ay22q9phVergkILaojNMrhyfADqIKn0axUUUQ38MSYIB6iRuVpLT/nZDKaninMxgRHseTkmtNe5Jg105qXpPybzLUpfTgSDYDBo31FNxhdUJ/tARp0inMqwYorVdQvUOIro9r3owhfmUwe9GLLorO5a8+1daaWJlVZruNP7orkZlXqjWDa3QqzizZLrGWb9Oewma/sXANDyxvlGw65RevDLhMMTLfl1gSE62lGRC2zk7fAX+ZJP/9qBIu/TSPD0DImRXDYnfrapppdslXgkX4tDHqQ2UdP/3GUIfMy98eYnNUpSpw9TemcVJRv+DNpE6ZWakKOYTAKGT5THyyTwIHJW2XvDlNGTI6i/oi+xpeLnhlwPWwmldmnLLFghAaRRBzrmZmR5iMpmQQnnftnz71OFMg/AdPMpEfUEhpQ1/05NSJl4x5UZ/8NE6jbVgof2deKZgQ3n2ah0HVp+XK5MOjH42naMorxuBJxlPxeZt3Wj8d7/gPG7/Tym6+8qj+PzcLtG3gnUTQlnJfzlXnAQKwyx6MFVjkdpOLg7lNb0/7fn64Yrfd43ZpBYNQmcJVjYz75KKSrU/bqAj/zt02uwzKwXaaKl+10EPavyx+mSg3+r3W+NxmmBWnlnYoNrZGt8RVxdbhf7B07VrNCKMEAk5HAiCj4htG1H8CRn9vS2cCviH1nwL/UDJIjh/+iGRmQNBHI5QRGAijbZdildOXxuZ73StqfoLoUFjpigI+hwIhioqgpHUSPDVJQSC52hCMcbPNGQfetCgfH2nPqpAG5ud6PTVuxahwreV+G1wUpDh/RjNZFPPhm/bb4fRoz5trUq3WRfVFaXzos0hPFWCsFTRvp5NjMQr4bAIc5Hnj6GvVKxKVFasfL+xeees3n1BaUVcxZRP6V+nNt/Ty7Ep7qUVncbC2GA237nK+Ux9qWNx4hUxNvqi7HaThP7MdgX51Fw2dsJG2Db189PTQoxa2ZA4KZMJpaUfOwrp7avRSU/JLyjSvcX63GhOyWCg7AV9XlqiZ97sVi7UFRHKCOtoRKMzUU03hk6ukeFU2sXX2g41SIxZ9V5PxifUdpn8VXTue+wlZUpVxjcVGC6fiSQX5+tEhNC96sWnq3iHuTI5K/IMN1TVTXcvV6HsBYZ+yAoWp1Q3GAsWyZM9OXedsUaXRUON+q5CnqwtuQwYd2QcAzgOBfn0T+j3QPv/+9QtCVlimhYvAn+WUAr+lE/DqR+K4ApBzNe9XQlxITadDcUlNMVYQkvqLBJ13VO/pXMWXYDbAPaHM7PanZVsdRbux0v6Jx+UyVx7sjhbt3Ca73IRnB7XmojnQSfhkZkRHqJsmAfmvQZtGsuO1np8nW4Jl188p6tpWVLzu9ZPGPoHRjabhP8zE3rI7YwrzAlkpcvfkYusuRZuC4qc4ktT7wRK6cUI8uI/pbJfJw8ZhC6CSXrv3vmezVFEuF3csmdRS9HqzVdSyjIT4+YdEi0kDcGIKS0KrfaWYxP8OHtCk+KD6c8PDH0tlI0OKSjbWIBKSCQ7jcq9pcJoRigtevxf9PNvc39BRTyoD82acM4Ml5n6/YLW+pwEh/t9XGfTB79wOJdzZSwr8sysf2lmvcjDsjLus26XAQdWfODNRZ96dCZfSrvfZokJ2OoCtqHWrBJXksg55ktOzir1JDGLDAWC5fC69vjhtwmhG1XY5p53+jtDs0pK36RpfF5Z4McFR/QVk96JZ8+z8vcYQcsrZruqMJXosQEQY9lSbC6PWrJ4NLpQFee2/DQPkco/wI5OSvdt+5DULzD8IE0vnJIdVaCJnppctiyiEn7yqfLZmwD0cKzYtXpppRgdDFXdTHlF+4o9dNM9HZNUSiXvENHvxL73Dmqepzy77Lb9mTXPqHecVSqGradPbaUUt7GGdLHHRcZ6X5HiBgnW/iTnHfU7Ka97LNSO5v2Y25MfHGwasVnDT8eVxcXLJdr5f7PMzTVao4dpmuX4ZjWFU1URX6RHTF9MurDgWYVU+dHvIZn6GRZvm7N8b0RQIWZ9ywz/PfHOkC2lqPT6upjy6ln5asW1zoXqyslPNlfVL/FLBuh011sqxmahxBMphcL/sxJnRLDy9Z5QjcH3sksgmf6C1hMbLzwIbHLb29ipgELKqFhbeVmNVko27NF9DsF9vet6+2CIAwt1expIqcZfXm5jVYxUQcIg/UEmJ7qMCWa60h3RKB+XG8irb/MAxFMrV7Czs+B+OIudrRzFwsvpPyKanWDp0KYiEpetOQlWrQb7QZt87k/AEGvzrR+LLazO0Dz7vTv77A452eZ1H3BcbpPv12ubs1vZfxFnke+8ny+tOAl055IFcsG0JcdnzZl7oFH4K+Ywby4oXH9bwgxSDY1Uzl+UYniZUGX+MSPtfGHB3IFc3z/5idmuJFkuI9cynMD0KWhFjVZ+v52kJijZ4H25tgYsmAcWuiX3fI0rVxRUP3CK10+Lrw8sXVGRYp1Idb5XOAuT5WprUJRWV8RY2UsiCzVMBcZslGIooo4yfHZwJs4SclloLECtHuwyyuIzJ/5sxwcrZNpeqlGq6cbhWg3etDvEyifkbIiP0sBIs3o2+bU1/PKDWf3NOlqwubJFLFtz8gLgQnLlhFL+SU5VRVwFMwaZpv3ZWfvpqs6+f37Fsu5WQvO3nUPnRh56gtB98pINc75dsrq6PHsEy2LiNzTrIc3Rk6stq06/0qSg/tOL9vbcotkP0XDn4pAEWbhkkKQnaHKQp4hKd1VVGhGVmvddDhw/QFPtSwqt8g+lLz/mE10Hs1S25uTLzzqWdoo+f/z89pNFW8CpLYSKDeXlT+TnhViVtsD64h5iqd7HVaDVPS5bdRo9p82tQTgi3MmoxNOXdrUI5YJBRn7pJW6JuDnLUVCX9DKNK95l6Y9Zfp44xnGkULrnoh1HRyJauSDW15psF8g13aAkEFDK7w/KqMFSE6jrr+lDPqFUbKjAD3wLtFO0t85XlxRbUroK17kEcnXdyMnVJH3RoMrpGU/z7ZiRMzkaPxofdk3u/gjgGM0TH8Xl0oGDLpPrYIC+KJdAX/b2oCcSrYuOLNMpJhS6ZcJNS01FbUzwMa1Jd6Xn5TSmm+fHrKIQACGqjCO0P1C0Z7cjLcSq6MdB6YTU3E7F2twe2/3K/7+KTtb1mkbqDucernvJzP5ORq/+f2zjBLo94CQOhLSBhl2VT8uVxbPb34g7QZODhM065+YLZELVx/2bQkqtdcZoR+us8HjcSVktUt/GroCzS+IvEGz++Kd7H88oEC4qEM74+N5Pds3+5xeWvMsNkh2zX+t59dR0eapq+WuzpzsD2EiaBxX56mKY6pdYmcYF4qEAand1OAnN3ppqF1bhcz8nZ9uRvqNe3xU3dUXF0dyvco9WrJiKnZopPOtsixBvnbM7wYy3i86JmqAC708fF1N0I8u3Ft0J37nzzjhkKeVJv8R1Wjvwb9gFmz4QFmtyS1R5silvz4h8Ig5EOcoUDm4PP0rE3PkbMnV48w++vDsd6qSQeOcTEPBCw1tCIr48G+daMap4Y8gU+otMsjqjm/FuNg2NvvWdMzHuqB0/91NkA+PxH8SPDxwcX9JLmvTY8hfRrjVxg2s6590/YRvRWNcEqvOqAiktyZW/7PniVBBVdFB7KNozbB9eJhC3s+VN0V1bWaH8zzVx35URBfhgu6fBBRc0NruwMNGxOQWGBtJWHMCYQSoUY0mqHlXIh5ejIzLLqtO+iV5DlDpSrF++PHH/NRRlNBWxNvn1WFLbO5EyNv3sJPpvuwed96t70ZaCtsofdEUDPWgI3fuqyepBY71HXFJWPq6MAwMOeODymaAueObyQD4AT43v/qMjLu3ahP4vl73HSqnTeJE78484PgJzYjN/txHFZykpc48l59x8rsMzScWXko2nSZkDOGCq57rL4breQzngPECSpxtJqbYGlJZsrmxB5zJKCatiQ/NDrMp4FTPAquszar7Fadp1cK5lhXmvGYMAZjG/vsIy96CLpvFva2aorhtg7KpRW2B1RourzEqUNCLyyg6LpiDFIihJ1iQpLjlYcLBEUixrSoJELTpF0+xFUNxSz4Vhs2an9k2DU7vTulnaMqKlpYxfDONxDB67ffvFj5fyfzMSq2+VluCpolWxtm5rN+KYlT8nzMSAT8ZWVlajNV4dAeYyi4VhDGWQFSU+o4g1p9B9ICBzXm6tOxpm6PZiY5WVyvniGX7nQ1kN/m00B+YwvmffgvjEJUq6FV54cwshF7TTTNi57daXPfLlKwA3VpM4hcS+59v3qQucF5dLKnBjPH2E2COrFWB7G39OSChAEV9BGj/6Y/D3jqnTyLITrr4++Hml1UyoIjgQrAix6fVo1gaJCafHeor2USwiTpOwm+YznREWzQ9Oytx9/zBqTbqrr0+ef8vFRR+G9VjYb/y7MxZsxgVrRzbd3A3mzwe9edDum5tG1grwzfoVBWhmdfvx4eZvUSbJpWOvPI5v9njeec6Gjd+7IOjYHrE6dJ6N+ZTReO5VZ/W/XOObjvT1LEEvd68gVazNlDD02NANNp8o99TX5BrHCrUlxIENUi5XccYyYjBa0TTLnXcXmbAReEkpoa21JLoMq2jMxPXkmKVW5VAp+E/U5xGtphs4cpTzO6O1dWl7YcoXc+iSKRY1Z9t3b4YSfR/neGuyDJvupM95cDcutSyEGbgNy+aFK1KdGa4agZWHyzIf2L0a5lOG/onmroO289krcfml2fltQWcK2fItTdoslHBjYiOqvVXft7jWEbV0FPfDZlP/4/wmhbrOkW9jWf6yele0tM41tdKf7a1J8Rr95kF4GDq2nxA6/3aWY3+/uU6AVkHhbrQGFRFb3nwWPBt7rz43NyejWL/3OEsKxooarJmBWRUK95VYh2vaHN2rL60sLayQ2yLP7sDiVoKVZ3q10cHvmzMcx2thlp6gZaeiEIH3Nker8bia8Y1cHjYnu9j+D+i6sVqmYkPZOWxGeohFcwhvR3NRYuj00tvxxp811GuU6jsLDJBwRnuSu601EFu+JAsjlNKwC5J+TvnBxgdlnlxHd6BAsFi/wOv2JbsWeTJEZnlsQxcSG5McY/7VEi0tk4Kf+/sNT92LaNhrFYH0VtMqmsv+6hekqaY4JnR0CdpaYYnLfGWhupOpc4OghpytvtD7NeWAFNM/A4k/HJVRcqxbAZpb9txaVyLouSRWI2eSaEbLKiQLfyfWnF7Fup2gJrcGtLWKLNTu3Xm5fDc31GDIkXsxLopRaLfPYC8yaL9e2XbvRBl37HxxIajVaGs4HKFER8lF3SIL9fXvXcLVWlRKbL7fK1Z8gMna41e0e3L6OA+NH+hxnxvklL+QX5WzMfBrNVrqc93449cX1tbqovL1ui8i0m0DEUKnn/j7h6qSUnN3WbPSQ7uyjeNg76u3UXsueVee/3hT8kir6+bUapKFtXGCxlsUXRJoe8Ro/KsjWvrxAMESuEsgV02JjQmdcibpM9dUjeaZ6yJtHLBoAYEO/fS7zbifN71L3vQkoZ+UwW2u334nUM6oaoojN8TSCPRW/jRDrCmyaBPV+u8zNurzCDp92O0os83otMtmRsmy11jDk2W/SnJs5Sl4V5QpShhwdWb5gxlp/sqMPF9zpqbi5gSGvR+G4i5tH22uJ5we+mJzqjpUt12f8qHGJlz4z+3dtDFsL5v+ZuO04TIMvoz4CmcKZYSc/q4lSfnmneXh2doCz3zOWz5tmqiUF1I4ykNp0oF3Px7RsFJtuIZfU1SKXXwD78K9MjMPQ2UeT1v+7GYR/R5NbrbDY8XtNn8J1+Vw5odYlcafGdEqp7azNvDHzYJyfHNzWDDVL/AJ/U1RRfH3umt48Dh/5yuLoMizODNI2W2A/GHJqmAkOs6Wdu95Snx2wdet0v8FNRKWwASen1rshf7vZ0Am88GEQSovjYiMKuiZlX2MG33qkOIh7Wqv82R7Dc3cgWm25BCrASvPtNnhaf9QuPfs2mYbHy5cglpQN6UHQZ3ZOYIyaNkQyjMnoPGeci7hu6cx/jJmyojvgQ329jx4dp4jMFKsrp/6EDQCV3t3785vQj95Q/4TXqdMGUvbTS5MXSeQCNalLiQ5mR9eORLRaNNdlZWVhkPSCSsvu5QtKbU0VTn0l9bO/EJrC6xVwZAhbiUoq1z7Fs0FgmS6fJYso4eUtR+Yk17FVpowCIa5nHjVy90cSH9WaA9E/jb9wnwOqcbUL5INhwZJJlhQMUq3yXkojIjYrrk9flW/vEFSJalWjUFh9HKb3ZPsgXhXjSo2FKwse+eh0yc7rU4YurU+3Y/rBjh33Cu1GuqSXgjFu65N/vlnJ0//Ka3xv3JyjUzcUrnZnVaYWgj17LK5DLo2tqYyBBr63s9DfwCQfal7733VOpkOqqqE2ROV4RBNL8vyHgly415yn4PyYe7L1fUvMXW/yJVQ+YqXqehOv9zPGH+zsOcZ+g7NkVEWgpe6KlMeQPuvz6N5PIRDmIjIwx+rL9Pqac2tYJX2X0O5pBKYEuh3iSUGHy3gCfhwjQw8+umVL1X6FaOtpJWaBgQeZq+jKi2rsbh0/SemSEmvAnRGTPeKMWsI31BEyQiqaAMeshZj98xdoItseBbdLsJ0w6Mxo8Pq3nugs4N+fWjGzKHX2HbQcQn98vkR5DPqRtqihCOo94BNHz7xzMfLoeXQwXKMc2w9TmtwS9EGfP0xTil2DVoOKUjvFOmBfvWxyChNLR9WT/ve1NnOvjY002Yfep3u6DJ+P009vJyiR6vRy/B6rvHIcUR6/z9xXvDJBYkg4nTujHvm37OI8sIvYx+i3anMYZZyFhuocwwv48NEYtJvsBMCov5bTFImj/kQQ+t6MFGN66KHgOw1dMwkUCHAF0hEtx/q+DblNmzftU2dLWJqeeSK9fLXfgJ3r1+384lGvRFO+sNFSgf/5Qp2DRQvUBAs7e0VHshC/eaMs7eiHQ1ZZkxUWZkD6c/yebfePH8n88Td9gsC5u7iRQmyZYnuuwz/PbT194NbgD7NtWf3sqo+oboe/fKu1IiTXy8djYFGOEQtLjy0yFBE+xl0eqaUvgb1czbHeb8mcaNU4GeKaEMR+nS7xUDpXY2kJP+7JTKFLJHa2WI8Rj5HHjMGNoRqvKJlupMvh836G7ImaNa378rlxij50YTZmFQOIFKzo7NT/+LzTQ+X+zlPgz9V2cUx7rFLBX/jO/G/BdI9duOTqYOcJAgBsNS21/tL5j9Q9Kr8m1invB051on6b6RGTyyeE68o61E/vIN7SmIo6/JlPBiGecuG5PTpeZ09t2j50L+bvykVWJuVq0XIYJABMhHTfIrL/YWnLU0ns5AMCIJk+GALfbW3T1KTK7pHSBg6QAStqfejUQCh5tRHViJ4AILJEfh8+evRy+36mXovYhYKBWp1ZMg+NIpjSgyPJEP0OnPP8fSxo1VOSzJbUEpOsfByqCYqh2eZQnJXcv/ZdvrOsJgWGub0oFYFXsEt1xvNBhOn6W9Bv73TPvt0QLTqieMTvU8X7VD6uSil1kUnMaeN+Ly/f//dPZVsCsy79bNBq4nEuNxs/Fxr62iMIjY/4CLP/EWj5p9sOb4z/LQkzwscD5fpKErxBVLlmItU/MhEPab4ocdYgx28sSsdXOkPHpa2C3oD4Q1ZQzJXQIc+tD9EV2pza7go1a+XdoK6V7qsbYFx2AkPteSekUtx9mxz7nJ2AofhZLTlXnGiRkusOZ3TAsO4FV7eknOOxaXys1VMXW2F8dfSHs3+KhsmR1esFAkm82dtQOCOgf1wdg4se4YUN95EUHLHJA7CSoGaWMBHoCefVK0rFzMg3GLy0HZV+NUgjmIBrhEo4aDYFB4apWSX7jo4iWlRnjJyERcOO/foYZi7qIyM8qQlymEU+YCi7qHRG+vnjzQqxgolnYNBefr0uLbXt6xDYYoUmkzED2AS2H+soqLrjSQRJS0Qq1TepxZrM/KLsX3iNL9jWwKhXfyUyqu+L/XgxISv65+tiIVarDCahBQJo+u2vG7tL0G/8rvIT/F+M6/mMOeZqIcZYhG3ldsQXRAZ0A1ECqK5DdxK9LOZ/SdWfN0MXq6VKWihgEtCejAqFo6mQAad6Wvn1zI5JcBMEEGMykcJj5DQg9OdhyipAIbt8Ye0tJCrf6KvSvm6h1uaykrdao493ikKFruz4vRqRsLltlt0NdeUZcKLB0SZPuEqOCQEIhlAZJCIC4KKwnw+Dh1S0SLKIkqJmHii4Qcpdga4HLUoyE2Nh3wWHDmcl56SpNA9dCir8sUSUJKylLH41AGpqE5LKsVahJhOSZRJLnn7VqRe8k9TURePuCf2ZAZ4YnnS0yY9rt96bLI51yKmkEARouQJDkQNSRzqMsiqqGe82zqUnkoBLsg1UvBS9SOL3BUdCoWIEkgSVYq8xoBrpSUsg3RRChYhrizeOuQe6FFNQVoiCUKB77Wxesl0YqwWR0nkH6m7BHwp2E7MwhELu6uzmniu6K8WmidKrmMF0kGUQszljGSDgJtKlHOVxCLsm4Nbvt0M8cckli+utNUPqmjhUUyQI6F9oKD4kuZ8UpKAZyBK4TWzAouQQIQR8vHiArgGqQxQlNSpVEtGDc7JhlbOchQlopOBulXTIS1RjBT5Yue5aKFEq87/pqAVFV8moiPDUOVLZAlAwNUHnVneuT9p7v+yNPcPAGDuvuUo98x/dFWdkT8AwMCAWkyM9RY1lNhCPybEJ09biPvsrBQ6fHj4P7hoNpNsRmp77VHLy+I1WY7JFqdDqgQaqRIlSJTkmL2yWGRxSKBGiIEjMSxwhbLIKIhQMpEy4VIp+yMkETWK1U3ZYoVlGWeFcItiZaSBKGQlltL/gEiFMJArpICQw2koGdjdZVNrEgAmOeR51U8d1q7IcTOUy3JILRsFo0xaergYFrHMdMbtdU0CCACLzMUFAEDyHrk1ANCNddQbfIM4m98qtNMIGiA6OxtgEscbOCpcbkBY4xq43KmIooMN4nqZDY8pkdoEj1hQaXwGHotjAVmkHFBXVdVT2qHTWmgLlmimAlUyeigB05R80qLMd9VeK0vBz1HTxF16MHKExrM4oigl8x0E/Ex9/TxFqgF3oam1ypneczbLJkEptaZS+lBTY+W0Tl72YK/1AeTmlPoxq8TfQ4DTJGq8pvywByAqRBkjtau4iltNBRnuyNLuY+9syNGydRV7AEsMuedUmQxDnCNU9Z4yan8gYzkCC+k1Rdky7PL10yJxF+EEBzpuokqdZyMFKF4yOEPy6NIKGZlIeeJebgLRR40Z5tIHgJoclSpO4mia2qkNnILkYCHUqYedY8UvessV+PIjDCoWkx1G2b7utacAcoDz+TUTQSoFjDjqTczcBGlLX4ouJVIt0MqVMJFoUI2oCcwE1FE/+VdcrB0Vo4TDTmv532J2oUX/tjjw/4sfMzB8AkIiYhJSOIIMiUJjsOQUlFTUNLSi6OgZGJmYWUSLEStO/McFnpx/frsUqdKkf931Z0yWbDly5XHI5+Ti5uHlU8CvUJFiASVKlSlXISikUpVqNWrVqdegUZNmLVqFtQVmDk9a7FO3ksf/fJECiyzhIx/7xG2fpcwKq6yxzgabbLHNDrt40+H02C8hsRW+Wu2suWlm1Vnrjc50i95ks7bz6AZlGUWVGr1m9MnRFsaOnZ4JCkNfPnTlow+e1VV+aSYvqUr4yvHhK6NOq5v1Yv+0F+5einqz3gi0buUInA0FhfhZvkH/zqu3rwp4J00MziHl2/9QlVt5qfbzmIuDcx7+ySkP+Y9RtLYh4TzAWTirHgMOsFYT327dh/NtXY8OUow4wtDCOFeMRvcZJ9ibAsqbaztuP4bThH9oyp0L0kyPoNOlyH9S6Xob7uFSse4CAAA=) format("woff2");font-weight:400;font-style:normal}.katex{font: 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:""}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;display:inline-block;white-space:nowrap;width:min-content}.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathsfit,.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed;border-collapse:collapse}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .vbox{display:inline-flex;flex-direction:column;align-items:baseline}.katex .hbox{display:inline-flex;flex-direction:row;width:100%}.katex .thinbox{display:inline-flex;flex-direction:row;width:0;max-width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline,.katex .hdashline,.katex .rule{min-height:1px}.katex .mspace{display:inline-block}.katex .llap,.katex .rlap,.katex .clap{width:0;position:relative}.katex .llap>.inner,.katex .rlap>.inner,.katex .clap>.inner{position:absolute}.katex .llap>.fix,.katex .rlap>.fix,.katex .clap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .rlap>.inner,.katex .clap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:solid 0;position:relative}.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .sizing.reset-size1.size1,.katex .fontsize-ensurer.reset-size1.size1{font-size:1em}.katex .sizing.reset-size1.size2,.katex .fontsize-ensurer.reset-size1.size2{font-size:1.2em}.katex .sizing.reset-size1.size3,.katex .fontsize-ensurer.reset-size1.size3{font-size:1.4em}.katex .sizing.reset-size1.size4,.katex .fontsize-ensurer.reset-size1.size4{font-size:1.6em}.katex .sizing.reset-size1.size5,.katex .fontsize-ensurer.reset-size1.size5{font-size:1.8em}.katex .sizing.reset-size1.size6,.katex .fontsize-ensurer.reset-size1.size6{font-size:2em}.katex .sizing.reset-size1.size7,.katex .fontsize-ensurer.reset-size1.size7{font-size:2.4em}.katex .sizing.reset-size1.size8,.katex .fontsize-ensurer.reset-size1.size8{font-size:2.88em}.katex .sizing.reset-size1.size9,.katex .fontsize-ensurer.reset-size1.size9{font-size:3.456em}.katex .sizing.reset-size1.size10,.katex .fontsize-ensurer.reset-size1.size10{font-size:4.148em}.katex .sizing.reset-size1.size11,.katex .fontsize-ensurer.reset-size1.size11{font-size:4.976em}.katex .sizing.reset-size2.size1,.katex .fontsize-ensurer.reset-size2.size1{font-size:.8333333333em}.katex .sizing.reset-size2.size2,.katex .fontsize-ensurer.reset-size2.size2{font-size:1em}.katex .sizing.reset-size2.size3,.katex .fontsize-ensurer.reset-size2.size3{font-size:1.1666666667em}.katex .sizing.reset-size2.size4,.katex .fontsize-ensurer.reset-size2.size4{font-size:1.3333333333em}.katex .sizing.reset-size2.size5,.katex .fontsize-ensurer.reset-size2.size5{font-size:1.5em}.katex .sizing.reset-size2.size6,.katex .fontsize-ensurer.reset-size2.size6{font-size:1.6666666667em}.katex .sizing.reset-size2.size7,.katex .fontsize-ensurer.reset-size2.size7{font-size:2em}.katex .sizing.reset-size2.size8,.katex .fontsize-ensurer.reset-size2.size8{font-size:2.4em}.katex .sizing.reset-size2.size9,.katex .fontsize-ensurer.reset-size2.size9{font-size:2.88em}.katex .sizing.reset-size2.size10,.katex .fontsize-ensurer.reset-size2.size10{font-size:3.4566666667em}.katex .sizing.reset-size2.size11,.katex .fontsize-ensurer.reset-size2.size11{font-size:4.1466666667em}.katex .sizing.reset-size3.size1,.katex .fontsize-ensurer.reset-size3.size1{font-size:.7142857143em}.katex .sizing.reset-size3.size2,.katex .fontsize-ensurer.reset-size3.size2{font-size:.8571428571em}.katex .sizing.reset-size3.size3,.katex .fontsize-ensurer.reset-size3.size3{font-size:1em}.katex .sizing.reset-size3.size4,.katex .fontsize-ensurer.reset-size3.size4{font-size:1.1428571429em}.katex .sizing.reset-size3.size5,.katex .fontsize-ensurer.reset-size3.size5{font-size:1.2857142857em}.katex .sizing.reset-size3.size6,.katex .fontsize-ensurer.reset-size3.size6{font-size:1.4285714286em}.katex .sizing.reset-size3.size7,.katex .fontsize-ensurer.reset-size3.size7{font-size:1.7142857143em}.katex .sizing.reset-size3.size8,.katex .fontsize-ensurer.reset-size3.size8{font-size:2.0571428571em}.katex .sizing.reset-size3.size9,.katex .fontsize-ensurer.reset-size3.size9{font-size:2.4685714286em}.katex .sizing.reset-size3.size10,.katex .fontsize-ensurer.reset-size3.size10{font-size:2.9628571429em}.katex .sizing.reset-size3.size11,.katex .fontsize-ensurer.reset-size3.size11{font-size:3.5542857143em}.katex .sizing.reset-size4.size1,.katex .fontsize-ensurer.reset-size4.size1{font-size:.625em}.katex .sizing.reset-size4.size2,.katex .fontsize-ensurer.reset-size4.size2{font-size:.75em}.katex .sizing.reset-size4.size3,.katex .fontsize-ensurer.reset-size4.size3{font-size:.875em}.katex .sizing.reset-size4.size4,.katex .fontsize-ensurer.reset-size4.size4{font-size:1em}.katex .sizing.reset-size4.size5,.katex .fontsize-ensurer.reset-size4.size5{font-size:1.125em}.katex .sizing.reset-size4.size6,.katex .fontsize-ensurer.reset-size4.size6{font-size:1.25em}.katex .sizing.reset-size4.size7,.katex .fontsize-ensurer.reset-size4.size7{font-size:1.5em}.katex .sizing.reset-size4.size8,.katex .fontsize-ensurer.reset-size4.size8{font-size:1.8em}.katex .sizing.reset-size4.size9,.katex .fontsize-ensurer.reset-size4.size9{font-size:2.16em}.katex .sizing.reset-size4.size10,.katex .fontsize-ensurer.reset-size4.size10{font-size:2.5925em}.katex .sizing.reset-size4.size11,.katex .fontsize-ensurer.reset-size4.size11{font-size:3.11em}.katex .sizing.reset-size5.size1,.katex .fontsize-ensurer.reset-size5.size1{font-size:.5555555556em}.katex .sizing.reset-size5.size2,.katex .fontsize-ensurer.reset-size5.size2{font-size:.6666666667em}.katex .sizing.reset-size5.size3,.katex .fontsize-ensurer.reset-size5.size3{font-size:.7777777778em}.katex .sizing.reset-size5.size4,.katex .fontsize-ensurer.reset-size5.size4{font-size:.8888888889em}.katex .sizing.reset-size5.size5,.katex .fontsize-ensurer.reset-size5.size5{font-size:1em}.katex .sizing.reset-size5.size6,.katex .fontsize-ensurer.reset-size5.size6{font-size:1.1111111111em}.katex .sizing.reset-size5.size7,.katex .fontsize-ensurer.reset-size5.size7{font-size:1.3333333333em}.katex .sizing.reset-size5.size8,.katex .fontsize-ensurer.reset-size5.size8{font-size:1.6em}.katex .sizing.reset-size5.size9,.katex .fontsize-ensurer.reset-size5.size9{font-size:1.92em}.katex .sizing.reset-size5.size10,.katex .fontsize-ensurer.reset-size5.size10{font-size:2.3044444444em}.katex .sizing.reset-size5.size11,.katex .fontsize-ensurer.reset-size5.size11{font-size:2.7644444444em}.katex .sizing.reset-size6.size1,.katex .fontsize-ensurer.reset-size6.size1{font-size:.5em}.katex .sizing.reset-size6.size2,.katex .fontsize-ensurer.reset-size6.size2{font-size:.6em}.katex .sizing.reset-size6.size3,.katex .fontsize-ensurer.reset-size6.size3{font-size:.7em}.katex .sizing.reset-size6.size4,.katex .fontsize-ensurer.reset-size6.size4{font-size:.8em}.katex .sizing.reset-size6.size5,.katex .fontsize-ensurer.reset-size6.size5{font-size:.9em}.katex .sizing.reset-size6.size6,.katex .fontsize-ensurer.reset-size6.size6{font-size:1em}.katex .sizing.reset-size6.size7,.katex .fontsize-ensurer.reset-size6.size7{font-size:1.2em}.katex .sizing.reset-size6.size8,.katex .fontsize-ensurer.reset-size6.size8{font-size:1.44em}.katex .sizing.reset-size6.size9,.katex .fontsize-ensurer.reset-size6.size9{font-size:1.728em}.katex .sizing.reset-size6.size10,.katex .fontsize-ensurer.reset-size6.size10{font-size:2.074em}.katex .sizing.reset-size6.size11,.katex .fontsize-ensurer.reset-size6.size11{font-size:2.488em}.katex .sizing.reset-size7.size1,.katex .fontsize-ensurer.reset-size7.size1{font-size:.4166666667em}.katex .sizing.reset-size7.size2,.katex .fontsize-ensurer.reset-size7.size2{font-size:.5em}.katex .sizing.reset-size7.size3,.katex .fontsize-ensurer.reset-size7.size3{font-size:.5833333333em}.katex .sizing.reset-size7.size4,.katex .fontsize-ensurer.reset-size7.size4{font-size:.6666666667em}.katex .sizing.reset-size7.size5,.katex .fontsize-ensurer.reset-size7.size5{font-size:.75em}.katex .sizing.reset-size7.size6,.katex .fontsize-ensurer.reset-size7.size6{font-size:.8333333333em}.katex .sizing.reset-size7.size7,.katex .fontsize-ensurer.reset-size7.size7{font-size:1em}.katex .sizing.reset-size7.size8,.katex .fontsize-ensurer.reset-size7.size8{font-size:1.2em}.katex .sizing.reset-size7.size9,.katex .fontsize-ensurer.reset-size7.size9{font-size:1.44em}.katex .sizing.reset-size7.size10,.katex .fontsize-ensurer.reset-size7.size10{font-size:1.7283333333em}.katex .sizing.reset-size7.size11,.katex .fontsize-ensurer.reset-size7.size11{font-size:2.0733333333em}.katex .sizing.reset-size8.size1,.katex .fontsize-ensurer.reset-size8.size1{font-size:.3472222222em}.katex .sizing.reset-size8.size2,.katex .fontsize-ensurer.reset-size8.size2{font-size:.4166666667em}.katex .sizing.reset-size8.size3,.katex .fontsize-ensurer.reset-size8.size3{font-size:.4861111111em}.katex .sizing.reset-size8.size4,.katex .fontsize-ensurer.reset-size8.size4{font-size:.5555555556em}.katex .sizing.reset-size8.size5,.katex .fontsize-ensurer.reset-size8.size5{font-size:.625em}.katex .sizing.reset-size8.size6,.katex .fontsize-ensurer.reset-size8.size6{font-size:.6944444444em}.katex .sizing.reset-size8.size7,.katex .fontsize-ensurer.reset-size8.size7{font-size:.8333333333em}.katex .sizing.reset-size8.size8,.katex .fontsize-ensurer.reset-size8.size8{font-size:1em}.katex .sizing.reset-size8.size9,.katex .fontsize-ensurer.reset-size8.size9{font-size:1.2em}.katex .sizing.reset-size8.size10,.katex .fontsize-ensurer.reset-size8.size10{font-size:1.4402777778em}.katex .sizing.reset-size8.size11,.katex .fontsize-ensurer.reset-size8.size11{font-size:1.7277777778em}.katex .sizing.reset-size9.size1,.katex .fontsize-ensurer.reset-size9.size1{font-size:.2893518519em}.katex .sizing.reset-size9.size2,.katex .fontsize-ensurer.reset-size9.size2{font-size:.3472222222em}.katex .sizing.reset-size9.size3,.katex .fontsize-ensurer.reset-size9.size3{font-size:.4050925926em}.katex .sizing.reset-size9.size4,.katex .fontsize-ensurer.reset-size9.size4{font-size:.462962963em}.katex .sizing.reset-size9.size5,.katex .fontsize-ensurer.reset-size9.size5{font-size:.5208333333em}.katex .sizing.reset-size9.size6,.katex .fontsize-ensurer.reset-size9.size6{font-size:.5787037037em}.katex .sizing.reset-size9.size7,.katex .fontsize-ensurer.reset-size9.size7{font-size:.6944444444em}.katex .sizing.reset-size9.size8,.katex .fontsize-ensurer.reset-size9.size8{font-size:.8333333333em}.katex .sizing.reset-size9.size9,.katex .fontsize-ensurer.reset-size9.size9{font-size:1em}.katex .sizing.reset-size9.size10,.katex .fontsize-ensurer.reset-size9.size10{font-size:1.2002314815em}.katex .sizing.reset-size9.size11,.katex .fontsize-ensurer.reset-size9.size11{font-size:1.4398148148em}.katex .sizing.reset-size10.size1,.katex .fontsize-ensurer.reset-size10.size1{font-size:.2410800386em}.katex .sizing.reset-size10.size2,.katex .fontsize-ensurer.reset-size10.size2{font-size:.2892960463em}.katex .sizing.reset-size10.size3,.katex .fontsize-ensurer.reset-size10.size3{font-size:.337512054em}.katex .sizing.reset-size10.size4,.katex .fontsize-ensurer.reset-size10.size4{font-size:.3857280617em}.katex .sizing.reset-size10.size5,.katex .fontsize-ensurer.reset-size10.size5{font-size:.4339440694em}.katex .sizing.reset-size10.size6,.katex .fontsize-ensurer.reset-size10.size6{font-size:.4821600771em}.katex .sizing.reset-size10.size7,.katex .fontsize-ensurer.reset-size10.size7{font-size:.5785920926em}.katex .sizing.reset-size10.size8,.katex .fontsize-ensurer.reset-size10.size8{font-size:.6943105111em}.katex .sizing.reset-size10.size9,.katex .fontsize-ensurer.reset-size10.size9{font-size:.8331726133em}.katex .sizing.reset-size10.size10,.katex .fontsize-ensurer.reset-size10.size10{font-size:1em}.katex .sizing.reset-size10.size11,.katex .fontsize-ensurer.reset-size10.size11{font-size:1.1996142719em}.katex .sizing.reset-size11.size1,.katex .fontsize-ensurer.reset-size11.size1{font-size:.2009646302em}.katex .sizing.reset-size11.size2,.katex .fontsize-ensurer.reset-size11.size2{font-size:.2411575563em}.katex .sizing.reset-size11.size3,.katex .fontsize-ensurer.reset-size11.size3{font-size:.2813504823em}.katex .sizing.reset-size11.size4,.katex .fontsize-ensurer.reset-size11.size4{font-size:.3215434084em}.katex .sizing.reset-size11.size5,.katex .fontsize-ensurer.reset-size11.size5{font-size:.3617363344em}.katex .sizing.reset-size11.size6,.katex .fontsize-ensurer.reset-size11.size6{font-size:.4019292605em}.katex .sizing.reset-size11.size7,.katex .fontsize-ensurer.reset-size11.size7{font-size:.4823151125em}.katex .sizing.reset-size11.size8,.katex .fontsize-ensurer.reset-size11.size8{font-size:.578778135em}.katex .sizing.reset-size11.size9,.katex .fontsize-ensurer.reset-size11.size9{font-size:.6945337621em}.katex .sizing.reset-size11.size10,.katex .fontsize-ensurer.reset-size11.size10{font-size:.8336012862em}.katex .sizing.reset-size11.size11,.katex .fontsize-ensurer.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:before,.katex .stretchy:after{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .x-arrow,.katex .mover,.katex .munder{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{box-sizing:border-box;border-top:.049em solid;border-right:.049em solid;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{counter-increment:katexEqnNo;content:"(" counter(katexEqnNo) ")"}.katex .mml-eqn-num:before{counter-increment:mmlEqnNo;content:"(" counter(mmlEqnNo) ")"}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;position:absolute;left:calc(50% + .3em);text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left;padding-left:2em}body{counter-reset:katexEqnNo mmlEqnNo}.markdown-block--unstable.svelte-wjdxdt{display:contents}.streaming-code-block.svelte-wjdxdt .streaming-code-pre:where(.svelte-wjdxdt){background:transparent;padding:.5rem;margin:0;overflow-x:visible;border-radius:0;border:none;font-size:.875rem}div.svelte-wjdxdt p{margin-block:1rem;line-height:1.75}div.svelte-wjdxdt :is(h1,h2,h3,h4,h5,h6):first-child{margin-top:0}div.svelte-wjdxdt h1{font-size:1.875rem;font-weight:700;line-height:1.2;margin:1.5rem 0 .75rem}div.svelte-wjdxdt h2{font-size:1.5rem;font-weight:600;line-height:1.3;margin:1.25rem 0 .5rem}div.svelte-wjdxdt h3{font-size:1.25rem;font-weight:600;margin:1.5rem 0 .5rem;line-height:1.4}div.svelte-wjdxdt h4{font-size:1.125rem;font-weight:600;margin:.75rem 0 .25rem}div.svelte-wjdxdt h5{font-size:1rem;font-weight:600;margin:.5rem 0 .25rem}div.svelte-wjdxdt h6{font-size:.875rem;font-weight:600;margin:.5rem 0 .25rem}div.svelte-wjdxdt strong{font-weight:600}div.svelte-wjdxdt em{font-style:italic}div.svelte-wjdxdt del{text-decoration:line-through;opacity:.7}div.svelte-wjdxdt code:not(pre code){background:var(--muted);color:var(--muted-foreground);padding:.125rem .375rem;border-radius:.375rem;font-size:.875rem;font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace}div.svelte-wjdxdt pre{display:inline;margin:0!important;overflow:hidden!important;background:var(--muted);overflow-x:auto;border-radius:1rem;border:none;line-height:1!important}div.svelte-wjdxdt pre code{padding:0!important;display:inline!important}div.svelte-wjdxdt code{background:transparent;color:var(--code-foreground)}div.svelte-wjdxdt a{color:var(--primary);text-decoration:underline;text-underline-offset:2px;transition:color .2s ease;overflow-wrap:anywhere;word-break:break-all}div.svelte-wjdxdt a:hover{color:var(--primary)}div.svelte-wjdxdt ul{list-style-type:disc;margin-inline-start:1.5rem;margin-bottom:1rem}div.svelte-wjdxdt ol{list-style-type:decimal;margin-inline-start:1.5rem;margin-bottom:1rem}div.svelte-wjdxdt li{margin-bottom:.25rem;padding-inline-start:.5rem}div.svelte-wjdxdt li::marker{color:var(--muted-foreground)}div.svelte-wjdxdt ul ul{list-style-type:circle;margin-top:.25rem;margin-bottom:.25rem}div.svelte-wjdxdt ol ol{list-style-type:lower-alpha;margin-top:.25rem;margin-bottom:.25rem}div.svelte-wjdxdt .task-list-item{list-style:none;margin-inline-start:0;padding-inline-start:0}div.svelte-wjdxdt .task-list-item-checkbox{margin-right:.5rem;margin-top:.125rem}div.svelte-wjdxdt blockquote{border-left:4px solid var(--border);padding:.5rem 1rem;margin:1.5rem 0;font-style:italic;color:var(--muted-foreground);background:var(--muted);border-radius:0 .375rem .375rem 0}div.svelte-wjdxdt table{width:100%;margin:1.5rem 0;border-collapse:collapse;border:1px solid var(--border);border-radius:.375rem;overflow:hidden}div.svelte-wjdxdt th{background:hsl(var(--muted) / .3);border:1px solid var(--border);padding:.5rem .75rem;text-align:left;font-weight:600}div.svelte-wjdxdt td{border:1px solid var(--border);padding:.5rem .75rem}div.svelte-wjdxdt tr:nth-child(2n){background:hsl(var(--muted) / .1)}div.markdown-user-content.svelte-wjdxdt table,div.markdown-user-content.svelte-wjdxdt th,div.markdown-user-content.svelte-wjdxdt td,div.markdown-user-content.svelte-wjdxdt .table-wrapper{border-color:currentColor}div.svelte-wjdxdt hr{border:none;border-top:1px solid var(--border);margin:1.5rem 0}div.svelte-wjdxdt img{border-radius:.5rem;box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a;margin:1.5rem 0;max-width:100%;height:auto}div.svelte-wjdxdt .code-block-wrapper{margin:1.5rem 0;border-radius:.75rem;overflow:hidden;border:1px solid color-mix(in oklch,var(--border) 30%,transparent);background:var(--code-background);box-shadow:0 1px 2px #0000000d;min-height:var(--min-message-height);max-height:var(--max-message-height)}.dark div.svelte-wjdxdt .code-block-wrapper{border-color:color-mix(in oklch,var(--border) 20%,transparent)}div.svelte-wjdxdt .code-block-scroll-container,.streaming-code-scroll-container.svelte-wjdxdt{min-height:var(--min-message-height);max-height:var(--max-message-height);overflow-y:auto;overflow-x:auto;padding:3rem 1rem 1rem;line-height:1.3}.full-height-code-blocks.svelte-wjdxdt .code-block-wrapper{max-height:none}.full-height-code-blocks.svelte-wjdxdt .code-block-scroll-container,.full-height-code-blocks.svelte-wjdxdt .streaming-code-scroll-container:where(.svelte-wjdxdt){max-height:none;overflow-y:visible}div.svelte-wjdxdt .code-block-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem 0;font-size:.875rem;position:absolute;top:0;left:0;right:0}div.svelte-wjdxdt .code-language{color:var(--color-foreground);font-weight:500;font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}div.svelte-wjdxdt .code-block-actions{display:flex;align-items:center;gap:.5rem}div.svelte-wjdxdt .copy-code-btn,div.svelte-wjdxdt .preview-code-btn{display:flex;align-items:center;justify-content:center;padding:0;background:transparent;color:var(--code-foreground);cursor:pointer;transition:all .2s ease}div.svelte-wjdxdt .copy-code-btn:hover,div.svelte-wjdxdt .preview-code-btn:hover{transform:scale(1.05)}div.svelte-wjdxdt .copy-code-btn:active,div.svelte-wjdxdt .preview-code-btn:active{transform:scale(.95)}div.svelte-wjdxdt .code-block-wrapper pre{background:transparent;margin:0;border-radius:0;border:none;font-size:.875rem}div.svelte-wjdxdt .mention{color:hsl(var(--primary));font-weight:500;text-decoration:none}div.svelte-wjdxdt .mention:hover{text-decoration:underline}div.svelte-wjdxdt .hashtag{color:hsl(var(--primary));font-weight:500;text-decoration:none}div.svelte-wjdxdt .hashtag:hover{text-decoration:underline}div.svelte-wjdxdt table{transition:all .2s ease}div.svelte-wjdxdt table:hover{box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a}div.svelte-wjdxdt th:hover,div.svelte-wjdxdt td:hover{background:var(--muted)}.markdown-user-content.svelte-wjdxdt a,.markdown-user-content.svelte-wjdxdt a:hover{color:inherit}.markdown-user-content.svelte-wjdxdt table:hover{box-shadow:none}.markdown-user-content.svelte-wjdxdt th:hover,.markdown-user-content.svelte-wjdxdt td:hover{background:inherit}div.svelte-wjdxdt blockquote{transition:all .2s ease;position:relative}div.svelte-wjdxdt blockquote:hover{border-left-width:6px;background:var(--muted);transform:translate(2px)}div.svelte-wjdxdt blockquote:before{content:'"';position:absolute;top:-.5rem;left:.5rem;font-size:3rem;color:var(--muted-foreground);font-family:serif;line-height:1}div.svelte-wjdxdt img{transition:all .3s ease;cursor:pointer}div.svelte-wjdxdt img:hover{transform:scale(1.02);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}div.svelte-wjdxdt .image-zoom-overlay{position:fixed;inset:0;background:#000c;display:flex;align-items:center;justify-content:center;z-index:1000;cursor:pointer}div.svelte-wjdxdt .image-zoom-overlay img{max-width:90vw;max-height:90vh;border-radius:.5rem;box-shadow:0 25px 50px -12px #00000040}div.svelte-wjdxdt hr{border:none;height:2px;background:linear-gradient(to right,transparent,var(--border),transparent);margin:2rem 0;position:relative}div.svelte-wjdxdt hr:after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:1rem;height:1rem;background:var(--border);border-radius:50%}div.svelte-wjdxdt .table-wrapper{overflow-x:auto;margin:1.5rem 0;border-radius:.5rem;border:1px solid var(--border)}div.svelte-wjdxdt .table-wrapper table{margin:0;border:none}@media(max-width:640px){div.svelte-wjdxdt h1{font-size:1.5rem}div.svelte-wjdxdt h2{font-size:1.25rem}div.svelte-wjdxdt h3{font-size:1.125rem}div.svelte-wjdxdt table{font-size:.875rem}div.svelte-wjdxdt th,div.svelte-wjdxdt td{padding:.375rem .5rem}div.svelte-wjdxdt .table-wrapper{margin:.5rem -1rem;border-radius:0;border-left:none;border-right:none}}@media(prefers-color-scheme:dark){div.svelte-wjdxdt blockquote:hover{background:var(--muted)}}div.svelte-wjdxdt .image-load-error{display:flex;align-items:center;justify-content:center;margin:1.5rem 0;padding:1.5rem;border-radius:.5rem;background:var(--muted);border:1px dashed var(--border)}div.svelte-wjdxdt .image-error-content{display:flex;flex-direction:column;align-items:center;gap:.75rem;color:var(--muted-foreground);text-align:center}div.svelte-wjdxdt .image-error-content svg{opacity:.5}div.svelte-wjdxdt .image-error-text{font-size:.875rem}div.svelte-wjdxdt .image-error-link{display:inline-flex;align-items:center;gap:.375rem;padding:.5rem 1rem;font-size:.875rem;font-weight:500;color:var(--primary);background:var(--background);border:1px solid var(--border);border-radius:.375rem;text-decoration:none;transition:all .2s ease}div.svelte-wjdxdt .image-error-link:hover{background:var(--muted);border-color:var(--primary)}.code-preview-wrapper.svelte-hp0zxr{font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace}.code-preview-wrapper.svelte-hp0zxr pre:where(.svelte-hp0zxr){background:transparent}.code-preview-wrapper.svelte-hp0zxr code:where(.svelte-hp0zxr){background:transparent}.code-preview-overlay{position:fixed;inset:0;background-color:transparent;z-index:100000}.code-preview-content{position:fixed;inset:0;top:0!important;left:0!important;width:100dvw;height:100dvh;margin:0;padding:0;border:none;border-radius:0;background-color:transparent;box-shadow:none;display:block;overflow:hidden;transform:none!important;z-index:100001}.code-preview-iframe{display:block;width:100dvw;height:100dvh;border:0}.code-preview-close{position:absolute;z-index:100002}button.svelte-19ekn76 [data-slot=dropdown-menu-trigger]:not([data-state=open]){opacity:0}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) [data-slot=dropdown-menu-trigger]{opacity:1}@media(max-width:768px){button.svelte-19ekn76 [data-slot=dropdown-menu-trigger]{opacity:1!important}}button.svelte-19ekn76 .stop-button:where(.svelte-19ekn76) .stop-icon{display:none}button.svelte-19ekn76 .stop-button:where(.svelte-19ekn76) .loading-icon{display:block}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) .stop-button:where(.svelte-19ekn76) .stop-icon{display:block}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) .stop-button:where(.svelte-19ekn76) .loading-icon{display:none} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-green-50:oklch(98.2% .018 155.826);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-950:oklch(26.6% .065 152.934);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-600:oklch(60.9% .126 221.723);--color-cyan-950:oklch(30.2% .056 229.695);--color-blue-50:oklch(97% .014 254.604);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-950:oklch(28.2% .091 267.935);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-600:oklch(59.2% .249 .584);--color-pink-950:oklch(28.4% .109 3.907);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-xs:.125rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-xs:4px;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-foreground:var(--foreground)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground);scrollbar-width:thin;scrollbar-gutter:stable}*{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .2s}:hover{scrollbar-color:hsl(var(--muted-foreground)/.3)transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:0 0;border-radius:3px;transition:background .2s}:hover::-webkit-scrollbar-thumb{background:hsl(var(--muted-foreground)/.3)}::-webkit-scrollbar-thumb:hover{background:hsl(var(--muted-foreground)/.5)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.top-3\.5{top:calc(var(--spacing)*3.5)}.top-4{top:calc(var(--spacing)*4)}.top-4\.5{top:calc(var(--spacing)*4.5)}.top-10{top:calc(var(--spacing)*10)}.top-\[50\%\]{top:50%}.top-full{top:100%}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.right-8{right:calc(var(--spacing)*8)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-3{bottom:calc(var(--spacing)*3)}.bottom-4{bottom:calc(var(--spacing)*4)}.bottom-32{bottom:calc(var(--spacing)*32)}.bottom-44{bottom:calc(var(--spacing)*44)}.bottom-\[calc\(50dvh-7rem\)\]{bottom:calc(50dvh - 7rem)}.left-0{left:calc(var(--spacing)*0)}.left-0\!{left:calc(var(--spacing)*0)!important}.left-2{left:calc(var(--spacing)*2)}.left-3{left:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.left-\[50\%\]{left:50%}.left-\[calc\(var\(--sidebar-width\)\+0\.75rem\)\]{left:calc(var(--sidebar-width) + .75rem)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-9999{z-index:9999}.z-\[20\]{z-index:20}.z-\[900\]{z-index:900}.z-\[1000\]{z-index:1000}.z-\[9999\]{z-index:9999}.z-\[999999\]{z-index:999999}.z-\[1000000\]{z-index:1000000}.z-\[1000001\]{z-index:1000001}.z-\[var\(--layer-popover\,1000000\)\]{z-index:var(--layer-popover,1000000)}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-3\.5{margin-inline:calc(var(--spacing)*3.5)}.mx-auto{margin-inline:auto}.-my-1{margin-block:calc(var(--spacing)*-1)}.-my-2{margin-block:calc(var(--spacing)*-2)}.-my-4{margin-block:calc(var(--spacing)*-4)}.my-1{margin-block:calc(var(--spacing)*1)}.my-1\.5{margin-block:calc(var(--spacing)*1.5)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-6{margin-block:calc(var(--spacing)*6)}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-12{margin-top:calc(var(--spacing)*12)}.mt-auto{margin-top:auto}.-mr-1{margin-right:calc(var(--spacing)*-1)}.-mr-1\.5{margin-right:calc(var(--spacing)*-1.5)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-auto{margin-right:auto}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-11{margin-left:calc(var(--spacing)*11)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-2\.5{width:calc(var(--spacing)*2.5);height:calc(var(--spacing)*2.5)}.size-3{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.size-10{width:calc(var(--spacing)*10);height:calc(var(--spacing)*10)}.size-full{width:100%;height:100%}.h-\(--bits-select-anchor-height\){height:var(--bits-select-anchor-height)}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-16{height:calc(var(--spacing)*16)}.h-24{height:calc(var(--spacing)*24)}.h-40{height:calc(var(--spacing)*40)}.h-48{height:calc(var(--spacing)*48)}.h-\[1\.15rem\]{height:1.15rem}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[calc\(100vh-21rem\)\]{height:calc(100vh - 21rem)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-svh{height:100svh}.\!max-h-\[50vh\]{max-height:50vh!important}.\!max-h-\[80dvh\]{max-height:80dvh!important}.max-h-\(--bits-dropdown-menu-content-available-height\){max-height:var(--bits-dropdown-menu-content-available-height)}.max-h-\(--bits-select-content-available-height\){max-height:var(--bits-select-content-available-height)}.max-h-24{max-height:calc(var(--spacing)*24)}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-80{max-height:calc(var(--spacing)*80)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[100dvh\]{max-height:100dvh}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-4{min-height:calc(var(--spacing)*4)}.min-h-9{min-height:calc(var(--spacing)*9)}.min-h-12{min-height:calc(var(--spacing)*12)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-\[10rem\]{min-height:10rem}.min-h-\[48px\]{min-height:48px}.min-h-\[60px\]{min-height:60px}.min-h-\[200px\]{min-height:200px}.min-h-full{min-height:100%}.min-h-svh{min-height:100svh}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-0{width:calc(var(--spacing)*0)}.w-1{width:calc(var(--spacing)*1)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-28{width:calc(var(--spacing)*28)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-40{width:calc(var(--spacing)*40)}.w-48{width:calc(var(--spacing)*48)}.w-52{width:calc(var(--spacing)*52)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-\[10rem\]{width:10rem}.w-\[56rem\]{width:56rem}.w-\[calc\(100dvw-1\.5rem\)\]{width:calc(100dvw - 1.5rem)}.w-\[calc\(100vw-2rem\)\]{width:calc(100vw - 2rem)}.w-\[calc\(var\(--sidebar-width-icon\)\+1\.5rem\)\]{width:calc(var(--sidebar-width-icon) + 1.5rem)}.w-\[var\(--bits-popover-anchor-width\)\]{width:var(--bits-popover-anchor-width)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.\!max-w-4xl{max-width:var(--container-4xl)!important}.\!max-w-\[60rem\]{max-width:60rem!important}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-24{max-width:calc(var(--spacing)*24)}.max-w-36{max-width:calc(var(--spacing)*36)}.max-w-72{max-width:calc(var(--spacing)*72)}.max-w-\[17rem\]{max-width:17rem}.max-w-\[48rem\]{max-width:48rem}.max-w-\[56rem\]{max-width:56rem}.max-w-\[80\%\]{max-width:80%}.max-w-\[80vw\]{max-width:80vw}.max-w-\[85vw\]{max-width:85vw}.max-w-\[100vw\]{max-width:100vw}.max-w-\[150px\]{max-width:150px}.max-w-\[300px\]{max-width:300px}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-xs{max-width:var(--container-xs)}.min-w-\(--bits-select-anchor-width\){min-width:var(--bits-select-anchor-width)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-5{min-width:calc(var(--spacing)*5)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[200px\]{min-width:200px}.min-w-max{min-width:max-content}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-\(--bits-dropdown-menu-content-transform-origin\){transform-origin:var(--bits-dropdown-menu-content-transform-origin)}.origin-\(--bits-popover-content-transform-origin\){transform-origin:var(--bits-popover-content-transform-origin)}.origin-\(--bits-select-content-transform-origin\){transform-origin:var(--bits-select-content-transform-origin)}.origin-\(--bits-tooltip-content-transform-origin\){transform-origin:var(--bits-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-0{--tw-translate-y:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-45{rotate:45deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.\!cursor-not-allowed{cursor:not-allowed!important}.cursor-auto{cursor:auto}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-\[0_1fr\]{grid-template-columns:0 1fr}.grid-cols-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.justify-items-start{justify-items:start}.\!gap-3{gap:calc(var(--spacing)*3)!important}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-0\.75{gap:calc(var(--spacing)*.75)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-1\.25{gap:calc(var(--spacing)*1.25)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-5{gap:calc(var(--spacing)*5)}.gap-6{gap:calc(var(--spacing)*6)}.gap-10{gap:calc(var(--spacing)*10)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.-space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*-1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*-1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-0\.5{row-gap:calc(var(--spacing)*.5)}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.justify-self-end{justify-self:flex-end}.justify-self-start{justify-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-\[1\.125rem\]{border-radius:1.125rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[4px\]{border-radius:4px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none\!{border-radius:0!important}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.rounded-t-lg{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-t-lg\!{border-top-left-radius:var(--radius)!important;border-top-right-radius:var(--radius)!important}.\!border-2{border-style:var(--tw-border-style)!important;border-width:2px!important}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.\!border-dashed{--tw-border-style:dashed!important;border-style:dashed!important}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.\!border-border\/50{border-color:var(--border)!important}@supports (color:color-mix(in lab,red,red)){.\!border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)!important}}.border-amber-500\/40{border-color:#f99c0066}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/40{border-color:color-mix(in oklab,var(--color-amber-500)40%,transparent)}}.border-border,.border-border\/30{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/30{border-color:color-mix(in oklab,var(--border)30%,transparent)}}.border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)}}.border-current{border-color:currentColor}.border-destructive,.border-destructive\/40{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/40{border-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.border-destructive\/50{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/50{border-color:color-mix(in oklab,var(--destructive)50%,transparent)}}.border-green-500{border-color:var(--color-green-500)}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-purple-200{border-color:var(--color-purple-200)}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500)50%,transparent)}}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-accent,.bg-accent\/50{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-accent\/50{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-background,.bg-background\/5{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/5{background-color:color-mix(in oklab,var(--background)5%,transparent)}}.bg-background\/25{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/25{background-color:color-mix(in oklab,var(--background)25%,transparent)}}.bg-background\/70{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/70{background-color:color-mix(in oklab,var(--background)70%,transparent)}}.bg-black\/5{background-color:#0000000d}@supports (color:color-mix(in lab,red,red)){.bg-black\/5{background-color:color-mix(in oklab,var(--color-black)5%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.bg-black\/85{background-color:#000000d9}@supports (color:color-mix(in lab,red,red)){.bg-black\/85{background-color:color-mix(in oklab,var(--color-black)85%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-border,.bg-border\/20{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.bg-border\/20{background-color:color-mix(in oklab,var(--border)20%,transparent)}}.bg-card{background-color:var(--card)}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-destructive,.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.bg-destructive\/15{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/15{background-color:color-mix(in oklab,var(--destructive)15%,transparent)}}.bg-destructive\/20{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/20{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.bg-foreground,.bg-foreground\/5{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/5{background-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.bg-foreground\/15{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/15{background-color:color-mix(in oklab,var(--foreground)15%,transparent)}}.bg-foreground\/20{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/20{background-color:color-mix(in oklab,var(--foreground)20%,transparent)}}.bg-gray-500{background-color:var(--color-gray-500)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-500{background-color:var(--color-green-500)}.bg-muted{background-color:var(--muted)}.bg-muted-foreground\/10{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/10{background-color:color-mix(in oklab,var(--muted-foreground)10%,transparent)}}.bg-muted-foreground\/15{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/15{background-color:color-mix(in oklab,var(--muted-foreground)15%,transparent)}}.bg-muted-foreground\/50{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/50{background-color:color-mix(in oklab,var(--muted-foreground)50%,transparent)}}.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-muted\/60{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/60{background-color:color-mix(in oklab,var(--muted)60%,transparent)}}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-400{background-color:var(--color-orange-400)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/10{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-200\/60{background-color:#e9d5ff99}@supports (color:color-mix(in lab,red,red)){.bg-purple-200\/60{background-color:color-mix(in oklab,var(--color-purple-200)60%,transparent)}}.bg-purple-300\/50{background-color:#d9b3ff80}@supports (color:color-mix(in lab,red,red)){.bg-purple-300\/50{background-color:color-mix(in oklab,var(--color-purple-300)50%,transparent)}}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-red-400\/10{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/10{background-color:color-mix(in oklab,var(--color-red-400)10%,transparent)}}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-sidebar\/50{background-color:var(--sidebar)}@supports (color:color-mix(in lab,red,red)){.bg-sidebar\/50{background-color:color-mix(in oklab,var(--sidebar)50%,transparent)}}.bg-transparent{background-color:#0000}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-muted{--tw-gradient-from:var(--muted);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-clip-padding{background-clip:padding-box}.fill-current{fill:currentColor}.fill-muted-foreground{fill:var(--muted-foreground)}.fill-white{fill:var(--color-white)}.stroke-muted-foreground{stroke:var(--muted-foreground)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing)*0)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-12{padding:calc(var(--spacing)*12)}.p-px{padding:1px}.px-0{padding-inline:calc(var(--spacing)*0)}.px-0\.5{padding-inline:calc(var(--spacing)*.5)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-3\.75{padding-inline:calc(var(--spacing)*3.75)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-0\.75{padding-block:calc(var(--spacing)*.75)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-5{padding-block:calc(var(--spacing)*5)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-10{padding-block:calc(var(--spacing)*10)}.\!ps-2{padding-inline-start:calc(var(--spacing)*2)!important}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-1\.5{padding-top:calc(var(--spacing)*1.5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pt-10{padding-top:calc(var(--spacing)*10)}.pt-13{padding-top:calc(var(--spacing)*13)}.pt-14{padding-top:calc(var(--spacing)*14)}.pt-16{padding-top:calc(var(--spacing)*16)}.pr-1{padding-right:calc(var(--spacing)*1)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-9{padding-right:calc(var(--spacing)*9)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-0{padding-bottom:calc(var(--spacing)*0)}.pb-0\.5{padding-bottom:calc(var(--spacing)*.5)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-2\.25{padding-bottom:calc(var(--spacing)*2.25)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pb-10{padding-bottom:calc(var(--spacing)*10)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing)*5);line-height:calc(var(--spacing)*5)}.leading-6{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}.leading-7\.5{--tw-leading:calc(var(--spacing)*7.5);line-height:calc(var(--spacing)*7.5)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-balance{text-wrap:balance}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.\!text-red-400{color:var(--color-red-400)!important}.text-accent-foreground{color:var(--accent-foreground)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-blue-600{color:var(--color-blue-600)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-cyan-600{color:var(--color-cyan-600)}.text-destructive{color:var(--destructive)}.text-foreground,.text-foreground\/80{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\/80{color:color-mix(in oklab,var(--foreground)80%,transparent)}}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-muted-foreground,.text-muted-foreground\/50{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/50{color:color-mix(in oklab,var(--muted-foreground)50%,transparent)}}.text-muted-foreground\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/60{color:color-mix(in oklab,var(--muted-foreground)60%,transparent)}}.text-muted-foreground\/70{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/70{color:color-mix(in oklab,var(--muted-foreground)70%,transparent)}}.text-orange-600{color:var(--color-orange-600)}.text-orange-800{color:var(--color-orange-800)}.text-pink-600{color:var(--color-pink-600)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-900{color:var(--color-purple-900)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-sidebar-foreground\/70{color:color-mix(in oklab,var(--sidebar-foreground)70%,transparent)}}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-white\!{color:var(--color-white)!important}.text-white\/50{color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.text-white\/50{color:color-mix(in oklab,var(--color-white)50%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white)60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white)70%,transparent)}}.text-yellow-600{color:var(--color-yellow-600)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-100{opacity:1}.mix-blend-difference{mix-blend-mode:difference}.shadow-\[0_0_0_1px_var\(--sidebar-border\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-border));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none\!{--tw-shadow:0 0 #0000!important;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)!important}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-muted{--tw-ring-color:var(--muted)}.ring-ring\/10{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.ring-ring\/10{--tw-ring-color:color-mix(in oklab,var(--ring)10%,transparent)}}.ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-ring\/50{outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.outline-ring\/50{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-lg\!{--tw-backdrop-blur:blur(var(--blur-lg))!important;-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important;backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-none\!{--tw-backdrop-blur: !important;-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important;backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)!important}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\,opacity\]{transition-property:left,right,width,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.\!duration-0{--tw-duration:0s!important;transition-duration:0s!important}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}.zoom-in-95{--tw-enter-scale:.95}.running{animation-play-state:running}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media(hover:hover){.group-hover\:pointer-events-auto:is(:where(.group):hover *){pointer-events:auto}.group-hover\:flex:is(:where(.group):hover *){display:flex}.group-hover\:hidden:is(:where(.group):hover *){display:none}.group-hover\:fill-destructive:is(:where(.group):hover *){fill:var(--destructive)}.group-hover\:stroke-destructive:is(:where(.group):hover *){stroke:var(--destructive)}.group-hover\:pr-6:is(:where(.group):hover *){padding-right:calc(var(--spacing)*6)}.group-hover\:opacity-100:is(:where(.group):hover *),.group-hover\/expand\:opacity-100:is(:where(.group\/expand):hover *),.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *){opacity:1}}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing)*8)}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing)*-8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing)*8)!important;height:calc(var(--spacing)*8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offcanvas\]\:pointer-events-none:is(:where(.group)[data-collapsible=offcanvas] *){pointer-events:none}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-0\.775\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-.775)}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-0\.775\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-.775)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.group-data-\[collapsible\=offcanvas\]\:opacity-0:is(:where(.group)[data-collapsible=offcanvas] *){opacity:0}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing)*-4)}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing)*0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[state\=open\]\:-rotate-180:is(:where(.group)[data-state=open] *){rotate:-180deg}@media(hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing)*1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing)*2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing)*1)}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing)*-2)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing)*0)}.after\:left-\[calc\(1\/2\*100\%-1px\)\]:after{content:var(--tw-content);left:calc(50% - 1px)}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.first\:ml-4:first-child{margin-left:calc(var(--spacing)*4)}.last\:mr-4:last-child{margin-right:calc(var(--spacing)*4)}.focus-within\:border-border:focus-within{border-color:var(--border)}.focus-within\:shadow-md:focus-within{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--accent)}.hover\:bg-accent\!:hover{background-color:var(--accent)!important}.hover\:bg-accent\/50:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/50:hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.hover\:bg-background\/45:hover{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-background\/45:hover{background-color:color-mix(in oklab,var(--background)45%,transparent)}}.hover\:bg-destructive\/10:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/10:hover{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.hover\:bg-destructive\/10\!:hover{background-color:var(--destructive)!important}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/10\!:hover{background-color:color-mix(in oklab,var(--destructive)10%,transparent)!important}}.hover\:bg-destructive\/30:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/30:hover{background-color:color-mix(in oklab,var(--destructive)30%,transparent)}}.hover\:bg-destructive\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/80:hover{background-color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\:bg-foreground\/10:hover{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-foreground\/10:hover{background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-muted-foreground\/10:hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted-foreground\/10:hover{background-color:color-mix(in oklab,var(--muted-foreground)10%,transparent)}}.hover\:bg-muted-foreground\/20:hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted-foreground\/20:hover{background-color:color-mix(in oklab,var(--muted-foreground)20%,transparent)}}.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\:bg-muted\/80:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/80:hover{background-color:color-mix(in oklab,var(--muted)80%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\:bg-red-400\/20:hover{background-color:#ff656833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-400\/20:hover{background-color:color-mix(in oklab,var(--color-red-400)20%,transparent)}}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-sidebar-border\/50:hover{background-color:var(--sidebar-border)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-sidebar-border\/50:hover{background-color:color-mix(in oklab,var(--sidebar-border)50%,transparent)}}.hover\:bg-transparent:hover{background-color:#0000}.hover\:fill-destructive:hover{fill:var(--destructive)}.hover\:stroke-destructive:hover{stroke:var(--destructive)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-gray-400:hover{color:var(--color-gray-400)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_var\(--sidebar-accent\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-accent));box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:bg-muted:focus{background-color:var(--muted)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-primary:focus{--tw-ring-color:var(--primary)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-4:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(4px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.focus-visible\:ring-ring:focus-visible,.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.focus-visible\:ring-offset-0:focus-visible{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:outline-hidden:focus-visible{--tw-outline-style:none;outline-style:none}@media(forced-colors:active){.focus-visible\:outline-hidden:focus-visible{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-accent:active{background-color:var(--accent)}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:bg-sidebar-border:active{background-color:var(--sidebar-border)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-100:disabled{opacity:1}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing)*4)1fr}.has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[highlighted\]\:bg-accent[data-highlighted]{background-color:var(--accent)}.data-\[highlighted\]\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[multiline\]\:py-2\.5[data-multiline]{padding-block:calc(var(--spacing)*2.5)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[side\=bottom\]\:-translate-x-1\/2[data-side=bottom]{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:-translate-y-\[calc\(-50\%_\+_1px\)\][data-side=bottom]{--tw-translate-y: calc((-50% + 1px)*-1) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:-translate-y-\[calc\(50\%_-_3px\)\][data-side=left]{--tw-translate-y: calc((50% - 3px)*-1) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:translate-x-\[calc\(50\%_\+_2px\)\][data-side=right]{--tw-translate-x: calc(50% + 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:translate-y-1\/2[data-side=right]{--tw-translate-y: 50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:translate-x-1\/2[data-side=top]{--tw-translate-x: 50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:translate-y-\[calc\(-50\%_\+_2px\)\][data-side=top]{--tw-translate-y: calc(-50% + 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing)*9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing)*8)}:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:color-mix(in oklab,var(--destructive)90%,transparent)}}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing)*2)}.data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state=checked]{--tw-translate-x: calc(100% - 2px) ;translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:fill-mode-forwards[data-state=closed]{--tw-animation-fill-mode:forwards;animation-fill-mode:forwards}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-bottom-full[data-state=closed]{--tw-exit-translate-y: 100% }.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:bg-transparent\![data-state=open]{background-color:#0000!important}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y: 100% }.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}@media(hover:hover){.data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.data-\[variant\=destructive\]\:data-highlighted\:text-destructive[data-variant=destructive][data-highlighted]{color:var(--destructive)}@media not all and (min-width:48rem){.max-md\:hidden{display:none}}@media(min-width:40rem){.sm\:top-\[50\%\]{top:50%}.sm\:right-auto{right:auto}.sm\:bottom-auto{bottom:auto}.sm\:left-\[50\%\]{left:50%}.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:flex{display:flex}.sm\:max-h-\[100vh\]{max-height:100vh}.sm\:w-auto{width:auto}.sm\:w-max{width:max-content}.sm\:max-w-6xl{max-width:var(--container-6xl)}.sm\:max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:max-w-md{max-width:var(--container-md)}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.sm\:translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.sm\:data-\[state\=closed\]\:slide-out-to-bottom-0[data-state=closed]{--tw-exit-translate-y: 0% }.sm\:data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.sm\:data-\[state\=open\]\:slide-in-from-bottom-0[data-state=open]{--tw-enter-translate-y: 0% }.sm\:data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}}@media(min-width:48rem){.md\:absolute{position:absolute}.md\:top-8{top:calc(var(--spacing)*8)}.md\:left-8{left:calc(var(--spacing)*8)}.md\:left-\[calc\(var\(--sidebar-width\)-3\.25rem\)\]{left:calc(var(--sidebar-width) - 3.25rem)}.md\:z-0{z-index:0}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-6{height:calc(var(--spacing)*6)}.md\:max-h-\[100vh\]{max-height:100vh}.md\:w-\(--sidebar-width\){width:var(--sidebar-width)}.md\:w-6{width:calc(var(--spacing)*6)}.md\:w-\[calc\(var\(--sidebar-width\)\+0\.75rem\)\]{width:calc(var(--sidebar-width) + .75rem)}.md\:w-auto{width:auto}.md\:max-w-3xl{max-width:var(--container-3xl)}.md\:max-w-32{max-width:calc(var(--spacing)*32)}.md\:max-w-\[min\(calc\(100cqw-9rem\)\,25rem\)\]{max-width:min(100cqw - 9rem,25rem)}.md\:flex-row{flex-direction:row}.md\:justify-end{justify-content:flex-end}.md\:gap-2{gap:calc(var(--spacing)*2)}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}.md\:p-6{padding:calc(var(--spacing)*6)}.md\:p-8{padding:calc(var(--spacing)*8)}.md\:px-0{padding-inline:calc(var(--spacing)*0)}.md\:px-6{padding-inline:calc(var(--spacing)*6)}.md\:px-8{padding-inline:calc(var(--spacing)*8)}.md\:\!py-3{padding-block:calc(var(--spacing)*3)!important}.md\:py-2{padding-block:calc(var(--spacing)*2)}.md\:py-8{padding-block:calc(var(--spacing)*8)}.md\:pt-0{padding-top:calc(var(--spacing)*0)}.md\:pt-4{padding-top:calc(var(--spacing)*4)}.md\:pt-6{padding-top:calc(var(--spacing)*6)}.md\:pt-28{padding-top:calc(var(--spacing)*28)}.md\:pb-2{padding-bottom:calc(var(--spacing)*2)}.md\:pl-8{padding-left:calc(var(--spacing)*8)}.md\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:opacity-0{opacity:0}.md\:group-data-\[collapsible\=offcanvas\]\:pointer-events-auto:is(:where(.group)[data-collapsible=offcanvas] *){pointer-events:auto}.md\:group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing)*2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing)*2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}.dark\:border:is(.dark *){border-style:var(--tw-border-style);border-width:1px}.dark\:border-border\/20:is(.dark *){border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.dark\:border-border\/20:is(.dark *){border-color:color-mix(in oklab,var(--border)20%,transparent)}}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-muted:is(.dark *){border-color:var(--muted)}.dark\:border-purple-800:is(.dark *){border-color:var(--color-purple-800)}.dark\:bg-blue-950:is(.dark *){background-color:var(--color-blue-950)}.dark\:bg-cyan-950:is(.dark *){background-color:var(--color-cyan-950)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive)60%,transparent)}}.dark\:bg-destructive\/70:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/70:is(.dark *){background-color:color-mix(in oklab,var(--destructive)70%,transparent)}}.dark\:bg-foreground\/10:is(.dark *){background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-foreground\/10:is(.dark *){background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.dark\:bg-green-950:is(.dark *){background-color:var(--color-green-950)}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:bg-muted-foreground\/15:is(.dark *){background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-muted-foreground\/15:is(.dark *){background-color:color-mix(in oklab,var(--muted-foreground)15%,transparent)}}.dark\:bg-muted\/75:is(.dark *){background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-muted\/75:is(.dark *){background-color:color-mix(in oklab,var(--muted)75%,transparent)}}.dark\:bg-orange-900:is(.dark *){background-color:var(--color-orange-900)}.dark\:bg-orange-950:is(.dark *){background-color:var(--color-orange-950)}.dark\:bg-pink-950:is(.dark *){background-color:var(--color-pink-950)}.dark\:bg-primary\/8:is(.dark *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-primary\/8:is(.dark *){background-color:color-mix(in oklab,var(--primary)8%,transparent)}}.dark\:bg-primary\/15:is(.dark *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-primary\/15:is(.dark *){background-color:color-mix(in oklab,var(--primary)15%,transparent)}}.dark\:bg-purple-500\/20:is(.dark *){background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-500\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.dark\:bg-purple-700\/50:is(.dark *){background-color:#8200da80}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-700\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-700)50%,transparent)}}.dark\:bg-purple-800\/40:is(.dark *){background-color:#6e11b066}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-800\/40:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-800)40%,transparent)}}.dark\:bg-purple-950:is(.dark *){background-color:var(--color-purple-950)}.dark\:bg-secondary:is(.dark *){background-color:var(--secondary)}.dark\:text-amber-400:is(.dark *){color:var(--color-amber-400)}.dark\:text-blue-400:is(.dark *){color:var(--color-blue-400)}.dark\:text-cyan-400:is(.dark *){color:var(--color-cyan-400)}.dark\:text-foreground:is(.dark *){color:var(--foreground)}.dark\:text-green-400:is(.dark *){color:var(--color-green-400)}.dark\:text-orange-200:is(.dark *){color:var(--color-orange-200)}.dark\:text-orange-400:is(.dark *){color:var(--color-orange-400)}.dark\:text-pink-400:is(.dark *){color:var(--color-pink-400)}.dark\:text-purple-100:is(.dark *){color:var(--color-purple-100)}.dark\:text-purple-300:is(.dark *){color:var(--color-purple-300)}.dark\:text-purple-400:is(.dark *){color:var(--color-purple-400)}.dark\:text-secondary-foreground:is(.dark *){color:var(--secondary-foreground)}.dark\:text-yellow-400:is(.dark *){color:var(--color-yellow-400)}.dark\:text-yellow-500:is(.dark *){color:var(--color-yellow-500)}.dark\:ring-ring\/20:is(.dark *){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.dark\:ring-ring\/20:is(.dark *){--tw-ring-color:color-mix(in oklab,var(--ring)20%,transparent)}}.dark\:outline-ring\/40:is(.dark *){outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.dark\:outline-ring\/40:is(.dark *){outline-color:color-mix(in oklab,var(--ring)40%,transparent)}}.dark\:focus-within\:border-border:is(.dark *):focus-within{border-color:var(--border)}@media(hover:hover){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input)50%,transparent)}}.dark\:hover\:bg-muted-foreground\/25:is(.dark *):hover{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-muted-foreground\/25:is(.dark *):hover{background-color:color-mix(in oklab,var(--muted-foreground)25%,transparent)}}.dark\:hover\:bg-muted\/30:is(.dark *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-muted\/30:is(.dark *):hover{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state=checked]{background-color:var(--primary)}.dark\:data-\[state\=checked\]\:bg-primary-foreground:is(.dark *)[data-state=checked]{background-color:var(--primary-foreground)}.dark\:data-\[state\=unchecked\]\:bg-foreground:is(.dark *)[data-state=unchecked]{background-color:var(--foreground)}.dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state=unchecked]{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state=unchecked]{background-color:color-mix(in oklab,var(--input)80%,transparent)}}.dark\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/20:is(.dark *)[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/20:is(.dark *)[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-8 svg:not([class*=size-]){width:calc(var(--spacing)*8);height:calc(var(--spacing)*8)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pe-0:has([role=checkbox]){padding-inline-end:calc(var(--spacing)*0)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing)*6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing)*6)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing)*2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\:not\(\:first-child\)\]\:mt-1:not(:first-child){margin-top:calc(var(--spacing)*1)}.\[\&\:not\(\:first-child\)\]\:mt-2:not(:first-child){margin-top:calc(var(--spacing)*2)}.\[\&\:not\(\:first-child\)\]\:last\:mr-4:not(:first-child):last-child{margin-right:calc(var(--spacing)*4)}.\[\&\:not\(\:last-child\)\]\:first\:ml-4:not(:last-child):first-child{margin-left:calc(var(--spacing)*4)}.\[\&\>\*\]\:flex-1>*{flex:1}@media(min-width:40rem){.sm\:\[\&\>\*\]\:flex-none>*{flex:none}}.\[\&\>\*\:first-child\]\:rounded-r-none>:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.\[\&\>\*\:last-child\]\:rounded-l-none>:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.\[\&\>\*\:not\(\:first-child\)\:not\(\:last-child\)\]\:rounded-none>:not(:first-child):not(:last-child){border-radius:0}@media(hover:hover){.hover\:\[\&\>kbd\]\:opacity-100:hover>kbd{opacity:1}}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\]\:text-current>svg{color:currentColor}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}@media(hover:hover){:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--spacing)*-2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--spacing)*-2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}@media(hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}a.\[a\&\]\:hover\:bg-foreground\/25:hover{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-foreground\/25:hover{background-color:color-mix(in oklab,var(--foreground)25%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary)90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(95% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(95% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(87.5% 0 0);--input:oklch(92% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0);--code-background:oklch(98.5% 0 0);--code-foreground:oklch(14.5% 0 0);--layer-popover:1000000;--chat-form-area-height:8rem;--chat-form-area-offset:2rem;--max-message-height:max(24rem,min(80dvh,calc(100dvh - var(--chat-form-area-height) - 12rem)))}@media(min-width:640px){:root{--chat-form-area-height:24rem;--chat-form-area-offset:12rem}}.dark{--background:oklch(16% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(29% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.3);--input:oklch(100% 0 0/.3);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0);--code-background:oklch(22.5% 0 0);--code-foreground:oklch(87.5% 0 0)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0))}}a.svelte-1q39rn8,button.svelte-1q39rn8{cursor:pointer}[data-select-viewport],[data-combobox-viewport]{scrollbar-width:none!important;-ms-overflow-style:none!important;-webkit-overflow-scrolling:touch!important}[data-combobox-viewport]::-webkit-scrollbar{display:none!important}[data-select-viewport]::-webkit-scrollbar{display:none!important}[data-scroll-area-viewport]{scrollbar-width:none!important;-ms-overflow-style:none!important;-webkit-overflow-scrolling:touch!important}[data-scroll-area-viewport]::-webkit-scrollbar{display:none!important}:where([data-scroll-area-viewport]){display:flex;flex-direction:column;align-items:stretch}:where([data-scroll-area-content]){flex-grow:1}html[dir=ltr],[data-sonner-toaster][dir=ltr]{--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}html[dir=rtl],[data-sonner-toaster][dir=rtl]{--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}@media(hover:none)and (pointer:coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-y-position=top]{top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px #0006}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:#00000014}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:#ffffff4d}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]:before{content:"";position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]:before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]:before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]:before{content:"";position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]:after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y: translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y: translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y: translateY( calc(var(--lift) * var(--offset) + var(--lift) * -100%) );opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]:before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{-webkit-user-select:none;user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media(max-width:600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 87%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 93%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 84%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 43%, 17%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 9%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media(prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}.agentic-content.svelte-1uhcmx5{display:flex;flex-direction:column;gap:.5rem;width:100%;max-width:48rem}.agentic-text.svelte-1uhcmx5{width:100%}.agentic-turn.svelte-1uhcmx5{position:relative;border:1.5px dashed var(--muted-foreground);border-radius:.75rem;padding:1rem;transition:background .1s}.agentic-turn-label.svelte-1uhcmx5{position:absolute;top:-1rem;left:.75rem;padding:0 .375rem;background:var(--background);font-size:.7rem;font-weight:500;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.05em}.turn-stats.svelte-1uhcmx5{margin-top:.75rem;padding-top:.5rem;border-top:1px solid hsl(var(--muted) / .5)}.processing-container.svelte-3902tz{display:flex;flex-direction:column;align-items:flex-start;gap:.5rem}.processing-text.svelte-3902tz{background:linear-gradient(90deg,var(--muted-foreground),var(--foreground),var(--muted-foreground));background-size:200% 100%;background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:svelte-3902tz-shine 1s linear infinite;font-weight:500;font-size:.875rem}@keyframes svelte-3902tz-shine{to{background-position:-200% 0}}.raw-output.svelte-3902tz{width:100%;max-width:48rem;margin-top:1.5rem;padding:1rem 1.25rem;border-radius:1rem;background:hsl(var(--muted) / .3);color:var(--foreground);font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace;font-size:.875rem;line-height:1.6;white-space:pre-wrap;word-break:break-word}.chat-processing-info-container.svelte-1ktvj8d{position:sticky;top:0;z-index:10;padding:0 1rem .75rem;opacity:0;transform:translateY(50%);transition:opacity .3s ease-out,transform .3s ease-out}.chat-processing-info-container.visible.svelte-1ktvj8d{opacity:1;transform:translateY(0)}.chat-processing-info-content.svelte-1ktvj8d{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;justify-content:center;max-width:48rem;margin:0 auto}.chat-processing-info-detail.svelte-1ktvj8d{color:var(--muted-foreground);font-size:.75rem;padding:.25rem .75rem;border-radius:.375rem;font-family:ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace;white-space:nowrap}@media(max-width:768px){.chat-processing-info-content.svelte-1ktvj8d{gap:.5rem}.chat-processing-info-detail.svelte-1ktvj8d{font-size:.7rem;padding:.2rem .5rem}}@font-face{font-family:KaTeX_AMS;src:url(data:font/woff2;base64,d09GMgABAAAAAG2sAA4AAAAA+ZAAAG1TAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAhlQIMAmcDBEICoOjbILCdAE2AiQDh3oLhAoABCAFiHAHkiEMgScbF8Yn2LYMqH+3gyd/6PAsswO12yEpWsM7RgaCjQOA0H9txf//n5dUxtAmsKQoiOrc/H9QyJEtsi2GVCpzFfRhZqLYbDKTtn0lSwsTw4QD7NnnQk643jskZDh6Xt7UYM3oxmzbFmaT31X7vZ1Ofhd9hkIf+BQk6AtGG/a+RmtE9xoXbdSFR9FOxB/VXmLkD83DqE4FExWNqd74/RMZBmGaKMQcZltI/65kuqt4ilq1coTJWyVukOiXfAqeKn6l+6QPtVT6rXYGto38SU7e4Uk3/727jLss7jIhrCQkYayEBAhDSEIYIWEkIewlIIiKCAiyxLFBwYljonXt6i7Ouoq1ra1dalvbWmuH/b91/tecWqj/pqac+1YCofNIkRQIBX76ptq8ukczdzwgMCUWWoodMkGQZ3ft6nyKqwI7KeFue1/SHUtaOwqw7TgF5tndJCoYCgA/+62qM3gYoIgYOam9285l9XfxkH/iu38HrbRFKJSoMJjBJjCgES++/OTHN6DBBueVEIYT2GWyRdAHtyHtUsaeIRvdS2u75fbihomUAGb5+yWIaWaO3JdsU7GIyb0Pb3poSrpKiYBzf7AK9SlVxD/8A+daldCmPrcJza8x8r/LpGgixmTJrFgX5G/8hAdL7CvF8O5+/iWvIDC3577J0maohbY0WFRACoy8qQwAew8Jnz+kDUr+8xf1F7W6anTmtgm0NQg6e6tf/qrhuxkLWVNIFCiMTKl8UgjTfNcN7gVSWtZyl4UhlL8cYBua79sSxvP/f68dTriql0Yh2+tr9L60ggEc4ek/vtP37WQoJx1Z1ph7B8h2XBh32wMgafuz3v4knSQuXEi4hGlue4EKF2tbQ/h7aMVcJjZv2b0jkEgFvr0tEdh6F9Id3/nfT1/78gFJ/RH5/llAOTlhNnfzEn7FlJJ28JoSvbym8F5GheQjYKiQfgjuZCkAfDdk1Juq3ISb0T1TwELasbb7P1WtdgDbm1O1FzalorsYu27wByCAGYCABqINDCmZhIJFUPKjYNpLg7aXoCgqbsqJ3KCTLmr3QghNEWMdq/46b9FdWx6EtZzNJndz2JcOq/87oSq6oisQtlqcQhiEgYeeMVcn97chl3h0QokzTZhIacRK0sfKpBUp06NxFAVNXtef5/fLZj+4LfFZimSKiBMyIeh+OG6P4XxkooIDrPkPY8tKb5EfFxapYBItbkYApP10JSqA3NoKgKXGiuGQeYGojtgD/Lr5/7Ig80pXqASMUvLebfJPPzYXK86kRESeAJC4usAODr9E4Lj1TR7/Xb7NRGMFbLC+7PSB13yR611fdKPZu1/bg96lvlAESkFlK9EUOpMjVxksDq+Xt25A6ZyZS7meWzK+TCjzlCll4bJpMiMGR6AyuSItXRMLJwBJYYkVOqPVp6ptZOZ0ZvLJJhOi4CtcFTP7b9O+W882Lndm+0r8f1q+/b7jN+9f60ZTcnr8ATGZUr9W/Yi68p7tJCnTZ86eO5UMf6zuOaBEppXFygy9FTqHUtelb27riSDThFL1p+586nVdWJ9p75b+Wh/ZqsVut3Hr9q15y1PWVPin/xWab5/m0NEa9sudNv6sYfKfeEwe/I+/ec22retH161dsXzx0GB/X/vJ0JfzQafdqpSi/BhfLgrCh4M3L56wwUEBivr929cvOumgveaaaaqJpIGKBTzE/dzDnQwApMR4uBhTDaqDEqP67wC2NRUXGv2x24RUnAmCBD77wM2zZsdO/z9mLUNBRuAMXQPeXALO+RvSLr8Fapfpdx9HyM47Ip6uMMGkYihHznuCPIIE6bQASkLUGUJQUkYzRCBe/AxRoDlBZ+5d04o8IkYtyEylRdFNIvw0BlmJCKvUkHI2bpGuLkaltH7iXaItZ/b65hOcIqItT6cdYEUSZIZja4XadViIIoIGBQwIFiEhox7WoQEv1phY/tb66Si7wy5p28Gv+LsNvgcUdTnXmHnW4eiBR50ZpLs3FHikhn6RYTMVu2QVVdHRxSqMkBdXDcQwo04lBMow5QgU4UeziWWIOFkcEtgDgWVsetVwUfaKex2mS0KGtOIlVcqXdmqSEYZZGsg+CwopajOkAl2Q4qkpi3TWAYtJiWHgvJ80io3RWh0jiqjQO4o60GjLNQK2FTf+KpHa9pYviciSr0MaRdXrpOTDEGuXBhbEvEmgvwwbdeJoR/RSM6SDOKdagHQ2wqrxpAKC6yyJSGdE+OaT3t4FDnCezOHwkiLlRuUW+mLwYke/GgMtPiYJXZ30/Qcx0/3JYoUKYMiwSIpHbSL7VGjanAP3bsEKfjn6dvOJus/qHGgx7L30Ub4qgSkHiAPNWuqEPSLodh28E2+TnupcUJCubVa6SzMksBsIwoWv96O8o6RGwibZGZE1ROKatM1SuKRIRfapSDIil4pB2pAsycWbT6FQ3jv2guxaxo/B04cPw5uP0z7n9zW8E/NRAJefDW6ZIKyUZFjDIsS1uMwkoo5wTkDUL1pa0SWlI/JiO3iJaHuZzlgsR0KIUpDFmNGF/Q2DMmrRZe105IoFgDupQ0iCuF+oOv+OCXCtQLY/BXKToktOUrITYVHEC9eF60LKHVFVGRD/syOsCn8guCSWJ2yGQhQgCDGIuJW8jIS8gjx5FfnyHhTIEgplGUWygmJZRYmMBrWYQEgWupJW3nwKglnC53MGb7OD6iCTMHz0Bydl+PyaBNe4RrJ7wupsmuMuSaRIkGH4YMgxFBhKDF8MPwx/jACs5qEQYLvfotBYpGtBdSSs6lhcYRMUrqvCYcRutOtHRA2gj5yGktbl8t4+jToJUJg6CQunb7vselHdLlSd7YZ5S5VpWmkaxCEtsMJ/IBzXsMB2ZEEYjKZ2hkD4D6pEZ1fWi1ZnE35EIoBt9JPwCRIEb2ORmH2w/TpXun/gE4+VqfooFESEjlkWBD7nzNirvHg35SghHLlrb33SVqc6e3cyTo4GgfBb9PRR/BupvXRhiZFMTh3nkARsZ93nHcT0YzaoS5qe8RFg6ZWlXn8eTih221wZ5dtLptfbCoPIPn6+9KLMy5OWxmueem96EQpjI6QyNQdu9SWHNF7vWnoGSbBSlaWX1t0uGOzdt/CLxLrYiAEVmDKmsUsCqqeiZV1BSj4W2U201K6nTRENe7KxgpgY5agZvmyvG/ac5pFBMnoBDg25zMYRSJNUubF+lqwwi23xLjOlYGdT6vXRXJvz6glG7copS17LGU09Pxu/JjnQFjQ+5rRseKajXT1qOislLpYWMdRuYAHbNltUOjPleXvDxw9cvbAxQNt+9zgBjI7DVpvAmMiSEwrtEmbdP7CrxFmq1lhiw6FIrSy/n8g61BaApSGTI5iV9SjxJBRGjys63bN3i34pQ2JwNbvjtqw7XzQ5b2xR8iCIDmnMFA2fOS9DLSW9JSSzJTj5eQvOc+POcK+I9ruSur0FBcCZO4xUSlYw6oXSikC4LfEg9HJGMt5RCvo1tiiNSSpaNAxLmhyk7wORDBk1iRIrWwBqAyA5sskuTtAgkiRvTZC/L0QK1qAhWQY5IqAxCKRkDZpGlmg5gxnNAZAKGS2JEidXAFoDQIS68gY7KG0Wc28hB23jHeSga/EectA31wEKum70oW1GbAsj8MG47QsF0U76IyDKNILNIsh8jhqaRSjLUF+hWLGuVrKJINsI3e5JsA9wCHAMcKog5whyidBdQ5JbkHuQR5BnBXlFkHeE3Ucp/DKfb29IW24pXfX/IN55M50iVhPdqMe37B8zxoFL8M+UMlhmyLTL0kt6bLI+0Mk92zvEdqGgQcuMirJGIQB1xD6huvNRiTyCI7TPwY0g7xMcQYKD2oEB2dYo2kJbOsi4SUsoSQK46lg8skEwZdE8LeqWHno2ynI2ysZBvVuG0zyaeayDulNLVZcktUybRDVzcBCdCpsy9JDpjb78MVftMQBHcNjXmYmPMOU9F9pnISP5ma/ANaLYfzi/lm555m9OtXNCeWkx5azqOJTsT0y7ij8C597MNMlFlKOjkiHfiY0jFL20PfW9TZQ7odxrGn7oqPp/T0bnnTvuQ7uDH2N1hb15zTZ3q0XfHzy6s91UpdmS23dvz/YfuHzZdYVI4mw0bA9b3PXcc/S5To7TvYf29SrOUjz9zn4EW9TdUoGzzvYzVGiosOhp0DCAtl5fVbsfVbPeQ5qnOmAdVKyrVsZYBWhvyxsaIRCYydEghut0QAO+rdyRo050ccD9gtdu0VXd1QtnyHXazV9NKY0sgQP7VhBQYw9T798IdUnGyNiDBRAAsiYNinzojGIhgi0EBENu+TGC0CQLMlmdSZOihlnb5e24jIvooNB8CIIg8oMQAgGhU7D6ufIkOilOFierk4WFBkAXMH5gQJ6G7LTHOWfMMPZQCsQwkBXizepGCJBETFCR5zzPo1KU4h1/56mqEFj37Yhm7VAMa33f9P3a5+Zzp6qtqnaLdjE9Xl2JGtF8kG7KN5Sv6J319g37fP8RlvCeuZzKWWn0C0pRwFUQiGybtAmT6Wcjo3z9yEhYMpmnIstVUYCoRqHm8wgwefy4vxCWRAWdUosDuLrpttvchp4IqYoR6x9hyggh00UATsPDw/Q1IG8VnMUYQVSrjVfcWRKhm5UsyYArgOA5m7wSEGSW5VmW5VoWHB6OBJjZIi6AfoNp5s08tRRXFV0BAsmCWTBNtGVus8L0uUZfnsF0hcm2I522KAgg7xPCfuYuV7h/ly69ZL+/lQP0CnZjVki9S7Tp1gNEI1R0Rhb1xNUHAYY2hLq/zrJqgWgUYOeYHEGGqcgWi3zQXd3CDM0r2W8AZiwyaLLALMUTE8ZURuB+LOe8BqSCWwwAuKFYQkay9ATmXUIt2gLSjo7gGjvUQKAANSZP2qHgRMnYktOZqyvsQUxQkR82UfoLRD3LntTgJkZwbBiiCpnEfrvLA7DuYMTiHbAqZD8YufAQ8G92MORwAFCj5RUeFTkAGBACiGoBxGFat/GW1CguMEmao3NeYqwmJCqcwbDTAuLLp3kEblAC3So/HDQRLse7TLsWkm9C9zntkG31BVGI3RDKaxlnPMJ4vIsrh8d1NuZ8EKcIBstDBqPJ77cLEAA3o0NbDC/0By6ISZg80UOMcaVx1GmSKAhwybcuVz4TfDS3SR4iIRHM2i/ODQkN4+Y722ZOY1wqOhpm/GUdCNxfjuOuzT4uqh3EvISEQQCv+2Ua5roySQW+PugTKCT8NLcxpm7pTk1TmSgmk4fC/NJ8dxBXC2DIsPe+qdFNs03vztHoEihC8109szPXmkC7zGcywAq2Yl3tX12uQD6PdyykfyoBFV2uFMgYAcFvMOb7zE1+r4niAgFLQLdAKjpph/YnaTeK20EivH8VD5oxgRA1ggeLqljklQgYagyTjqKDOvp8hXxUrBFSvcyGZdYcjCHxMhlgUG/OMNIiP+5yMUYR7JgsmwHi+yXRzG++PiGagObKHegQsCW+dl4+78UOh+ERehDmIv5GvesEiYT+f0IFanDRjL7SOCN4hUmH1VGGeIFRRWl4p/FjC6H7yDyINA/XhWGbhLN984juFp4Oi52Z6mee4YOw5xfKY95DxV60GiCZh6SB8Ykmhio6XR8EknhVmTdbDZ5zD88IF1hzmXBPV6WhM88hfL4rznEtDP6EYU99wBc+SqIRUBWfRTBxsaOooPgaRvSlKzijEZLj7xYsmC0eQdaKntecpn2pUxnVnziBi4lmhXGLbhIf+ujDtf3dr2kilpijWmv0qyf8WDOjMDuLQF28qpyLam4j3IewzhQHWh9N2qGSJ7QhudSucGbxBrxQwaizrfBkjNPlNM2ITwfCglrbu7LA3hPxf1jpwftyYv2DaM4DGIqLNLIk4UITAA2jgzFRtLpmmlgfWYwk2gg4JXFqToet1/26vGpl/FBxhHe6fOnBVzuNgINKmHUAkiT/h501dce7eRsvEGDOXgcxXqkoKHou5XcuNU2NDCtUCTAejqkoQmtfOur9rZpwe30nkgSx32582eownm9gp/iaou5HLGdJ35VinkE4UdMMUQIIbjGuAsn0UtVR/wrCBhxtJf6gQtI3rjCbZ7MxXnMTWMQXxWXhZ/86gCeadB/bKVGEZdxkf118HFCEd9mN1YlbvwQIElvkaRvx78TCs6/eam5V9QYlLYnX4Hd7pUzx/Ym44sl0azlKvcsKh5ooQq96Q0UH7XmUFL48LQVC+++nNRMEvZ1GKYq+qG1bjtqfMhGux9Ol8bzA/NokZbG7TBK1aILB+OBtkaA4IC9zRpPUko/UCoRGDqarF3frDOhu6rkqBqtekSjsatR9VvTtl+hbw8c8F+JPl8zl5qWUyREGmfZC6WDdi5ZCAt20mGBBm6K4IxLwbBUz9k/JJ3DK4+dJ8QEVHKmGoj5Z/VF4UmMCBWahwOSbrLOTNXy0Q4fR6PYgKlzFbsK0QXvJSekTx46hCnsCGWEIYW9yL4GiHMoBW4x/Ryar4iVMPjbh8smI4sqG6seMLfhaGS3tORDUhAsQZYXjx4kaO2/8SN9HB4Fhdv2yW43cHjynWC1ysUumUGWcs0eQn9AWySszOWdCw/D4zSIEWKwNGvCbLCHv9z5sbY8jeVRGCwCpYnsU+dnPH6E1ZPwmi95g2LTTlqbhX/9RRTkG7q9qgFLr7EST+UUwhHcinhdvlD06wO4P9RvEHrXPKgYErdGfBD5XnoebrEnX+GYFz7QQT+D9gQwzl3DFs8naQ8tQyrq1AMBNkaC4FYUIdUv0RTFHbAHmuDrDB0gRdB2fyFur+RevCPhYoEgeObV5TO5rxtB/vrz4AbUtjrRvhGdo/avko4KL6gAvlwW6VvR1PcIzcABoPkBFyCraJy66uok7orCFFQizxT9PUHcBS1dw4VIE4DrPeaXZ3NFTEYHB9qFp+TR1HFaP+yPuKWmIoZOfmk6bSxx9ND/S3gj05fpBdCs9gRK7Mo4V/MYpBZMi09ovAjAUJLnIQFrbhll0AygQGodCaV8FT8VnSHBhGTr9hOYcOX4je+ARy9c24HDEY5UH0ZsgoUwGJ/J5iYal0T8jKM1vUJZU0EiGJIy177ecjPjP0ifVItSoTcwqoJi+qG16kF4EFKzb8DSFPcoahTKPEh0kDQnebMwjmEBQ/Cxll9KNqrZIq+YE2Evw8IwTryO0/5WFkn34rJh4UQM2+d7RUFFdLlHl8sFmtRwZM1kIwws27CFVBFkcgEkU8uBbTTTTko5pl92lI1zKWKgRBFucb94+j5NhPupkI6TbfSzw8kv0CsfqgU02f7S7gc2qzm2ztc/JXDKmQZr6qjSFKfOVecSJ10nwl4NjgOpkgwkrJLioisGQqBfL8eWRCLIxoRT6ROr8uoZyHLUI31cHsdGk/SpWwnwJwxMBAJMatvSieczDgLLhs0punP9M9GMiFT9l/05P9Co3/b1aXAyRvcycsXUVEvILzOU7FmNflZ+U0+H9MGoUjK+vfM978EpTm/TLZaEYPLl354CxyotKGysmeSuQp+Juv9qJ6kwKwB680nj//V5UR6pEgx5PR1Ig7Ir9CdZSRAIAKi6YWkBMmPvdUux1Db9d0SZ40BgiOOTlnS5+eRwJlbg6EUmuYQsMolcCPoOr+mg1etsFQ1bx8DEX+8dAYHtBbcj0iIqd1KbCT68lFRQ58wQjlYRkZ9LKfmnPuEPUoQu1N3swBoLfh5qDKuqKQDEg8EYi/gEtnjUQMn1SiHQsjppthq4JbQCn7mFW5X15KsrsWukQy+w4QV3vbCibRmdJGb5hY8uDG5GIoFzlSHURqjjDAZWGmfJ4lexPWS5bYuMRKn67TpfaScsjvv5QKaB278Yce4AKLGu9Ug/AhjQKeCVQnC17CbBl3gr2PtCjIRyj4Izso9nc7MR8NcUKQ9x9bwqEJU2KjPeyMjMC3wDBJFqYU0lID6M6IKsQFP+nkNP4/vpzAbUDlsAmTnRlvFdQW/QT6Qg2Ot9Zuk24CKvet4ReglPIYsiFpSu0LcTUEhDE1lb5r8zt2Jg/CriK0oye/vRFGPDDm0sig7fPKyC4AI4ItuDm11innfV320gkpy6vfB5n0jiaKlZw80eHadZZml8EkEwKTqDjgB5MDxQAglM9BCnXBRJ5iiy1bpXjnbZFNC2axMbfZ0PFRH9L0+QR1HuX7aC6agDB7uwxEPol1qDDSjBrLoqucNaIhf+T9xUT9whF+CpH7MRWfYNBAEG55ymOgehd79izwzGhrzsFAg3aWyVrsgV6lfw8Sk5LlBJZns7cJy/Ya5iv1PbXhtK8RBPT7NKTl0mJVIH2TXkLMDNGBlB+h4xumcT+o8tmIGYmXpPLFfK4Hc3a1n3LMcPoVYdtLJxH3jXN1x+/vpqueyznmDrWBNuJSCKiFwjno+57724rS7vfzf4Hl2HmP/fxUWB0uZPcjOv0F9GsNMPOYy9q5wlwDIEYGIWKDhpBMNpjEUgzEjwdn+8drrTHK4dSzeNdQWDU8JnpXUWFTph4eiWshCm0r9iYkLIwuMK8SoacwCRP2uF4DhNNTXfcaYtdbcAYOLl6UDjGBCYbrLIFOgejbjuRCJ1YmbtM4AEqaeWk/to8FR/3Xz6MyVoTyES71cbxasUKeDZWwjSFVAOoP3TALYwReYDZ8HBvWTxSVUDDYpFf7iTTjvNGjaHqre5qj54LgGsVjA0n8tmFOK3u2yTb3oYVzKpM3Fujw7X2pSJPbRYcaiQKomu0PzaWlKm0hWOUw/pvpHm14XBxNE2sFOd72e2V05hg1Y7DPnZcntRDltfMsXGXg63rRRul36uEzcQrEaYUm1bqGNLrCrYrFOvhd0ucbm894LC6maz3mUEEQXgexWsrWK/WitSqpf+LQNgW2FQac3HEsksCVRbK7F/g0p3LeTNqvqiFrevrmfo8eStDk267s3BXHUjUIYveAkvcQsdjbwic+Il2e2WJAVznbAjirRukAo5JEf8EwbHYk7aPWFfHHcVX551eJk5rzFe3cWvCacMLZcgfAxPpwu08mMi8eeqxS4uC2bbQXbJpWrkVTyAbE/qZCIRX5nC6V6p8eY2NIKIkf2H0DLsCLkvhBXrZVDKJlkANtZ/ifRXgIkYC6Ig1N9eYjIveZjIZZnf4BvOEjCCWEWxvv9WsdsMmKCVyMI1mPS0u5RS16WoF9nHpWcJD1TcYV2tcMORZ2O22lGxlClt80GdZ1MaGSA+CxIx88WrHE5SwVbamJPyhGvDV6NQVCPkuVQKKlPGFsDRpqfUe7kH+DDLsb1+p+VPBTHutjVfK2PL7HBTQ/krXs8jiGuKsrmgzpm1ooRSSnACdqYiaymYoKhgurAWx18ArQkcYdjct6U8ZKKcGz+23ZZchh6n46rSDgqsE7fAACyNzJpZqD0eTWNycO5yM1MaMUzKjVLukljy8gnqlp7RrmsWw9YPRhsl/PHgm41q2Fow1QpoNS/2hEk2SeVMpyVjAc67gDhOIK9LhJXueA3aPfJU9c9i4T2Fom7GjlkfpzxJZVy7z9dl8+up5+QvLJGEUHKLngySgjJHF97BE1p0ty+mQD0LKLhJlGDOgwLgTYT7j+3w/YB6YicRCzAdoOoHqpCk4Ap4HF8p+6AXPIZp1PpS1+vRxaeTmle9MoEvGb0LDhNkTYhk0DN50IZJttVTI2ZF5xxazDKzx71YCKGUO6YE8IoXJ5K5byX8IjelO5KhXxsbyeVpoWwlo49AzjYE8LbVypIuAjkUittedtQhP1LkupaWIHsVPYVQpmpOjUcsM2ftiP2ETuXFzDPPIOzo3fS6zVLVqc3i9jO/0y5EkaFb9FS8OUUy3oVHtjMeGFebmBNA4Za3UzqlX4anEmKEfhqLZI+qAl0/VL15gNO3XSyGbti+TQ5R29Df7PUuSQin51htZ+bsIwkWZmTrGOzssVzB/X+bNRB9WSc9il7k4oXqG4rXLP6Uy8qRGLvWCzImVxddguspOmlNENdrNcms/THkCy9kbPC3G8ry3fC5sMrznNnwV2nuvz9ZoP+AAoW10H7J3CWY01fqNnBhOaRfKlv/z66CyqTajFZ0jWRAndoM9f4SE5MQWP80OnMkeTnoUH8g+1PeNwaVR5Gjm/43Z+1L1Fs60eH0G81YAUbj87Lrt8QWiJU1AaRBksVXzynPrl+pb7PbWgA6fwou4o8VYXscOQMMui6HSxiOt85iRlpscFPvYgM+1TXPDRsfiRf16mmMPxFxZOMTwFPapIy2BI08y8XDCV8XDHK8H7yldju0F9nXZEqdIk3Z0bSxYvlVt5U0HwwsxIea8ulCA/0SjyEFVe2vzoUirmkSnVW2+PHWQ2OadqKms1cP1BzTg5lLJnlMc2UsG/1Mjj0bCCCD+QVpWMpHKszbiOHLzR+meIzXErw3rOZ5RUEXWD0PwSmv5NrbO1/6GI3J+oDxZPqcjn6D9mIGeZ/SLRGQftheEUmlbFXBrKkDsMkpRaby5orc4TnEgnmfkeHDo9ZZqansFqS00SaKOxTpWUjl51plu4peKszuOivYyYbFvvTNLtUYqsHV1JXQ4qTJPkUKuMenfsqocJxqbNaFYAxxFLqavN6p904Vjn6Kqu3eo962HyVvgAcytN4mJ1KLZnlPG2zVZ1ovRmkvn92n8vwUsffb9M1xYzHmtTO2XYYXUTkSBlcdTb8Q9GambMXtwrGPcv3KnYSUIUlNWO5o326yf0Fcw6yu3AV7POSo3AWDzLaoUSF9YKmlllnfItyDwH6F7e4Jj5j/b0cuWKxTRpIy1Lx+iEHrzKz73BHx9cXPSk5ziUEh4zZiyQ8f81tcR0rvJ+D9XAy3aR4Auj6yml0Aqdzz70G5B1s2Gu+82ryiytOA5d//z0rHvvvum2iLjfPolWIwxtrAOk+XVD/WiWqxGhYYv0xFzGElNnsl4Pa5+YvWtbsduCyhQY9FitCnAcojYDqsE9l2Cq/pKe+UKnRwSRW4HQxtpI3M8VoZ32sCY2UGpo6ZKErhf6KjForbKK3qtF2u5oemsUsmbUkobUaOGOpfRYyjWxib19N4HuWFA4R4a8cI0Eu2MqYN6XbW34IQv4+UgkKZv1b2LBzJvekafAEgSEoBatyctEWvU4lhxf8rDcF1NvmmGwBNpWx1VvjPBM4Uj+bjr0v1moPnV9RwzfDfCa3yK+e3cvEoNZLT87LP3otZTYopMk4iKhjcMMgwRDr9uPxr29lygmJ5ZBYIpH8S88bgMR9FczZAAVp59G+ul0KL651MngdEhLlif9SH7ubbtckApGU85TF6Ain1aZD9R8Q06k0y7XKVtfbWBNzlJRWUu86/tcHDKPc/7EUp6uVcwrWKgQwbiYLKd8As/r9v42hirC0mDslcptVSymaYYI1WuT+POH9u1xI+hddnOXsf8W7rirb2eACw1fBlCdl79ixpNS79utjnRwYEKaFiG+ChppgvbwQj08kPg3a3dSJ6AEqgtlutgVrtfvcdzMGblphiFbYy0LuLdAP6R5ZfE3ydoI+EVglQTAKg3kK9DPnox/J9fC4qC3e41ah8XTDqmlJ6GvUtdc1er2BERS+0EaPkACq/UsmIRTgOJVZEhGbN96RKGmDNsdrdSJI2fBgmQHu93wXRVBzF4GfkYd0SPIcsGRZ3kge8FkxlWjMQMVw3/JgoZNRRAdhUi1F58lAiT43qjc9xVFPpPArrz0mj6tziryoKX/YfR8EwYeqz8Gkg2NRQNNvnFuy444kc0O4OYenm3A/hss8L+hhQhU0/D/Ryqkt2UZyxp8EQUEsSUBMJoZCZcvrMHPOADPVs/E9nnDk9ArvV2uTzw9DotRTNxVwl90MM/OSkomqHvr0/7WlY1uubXAYBvdVfPRip36Tl1MkT2vt1UTeRRJa8s++9u3/Oea04WaDgrpecO5j0fE2eM6O7olHTHTxaJtlAyMVTs5okV3BhwPrDi1Sev2Cji8cqe09DMq1Vyxysmsnz2tWrXU4C9FhK9LV8leh1usMwmaBnv/MHq88Mot0keZ0Lketc0eS6Pd73nntCltyw8yyQy9tH9pfqrzxuoOk8czB7m4DiSuSCOAFI3Y9Erbm095+woMWqym5nHdqDYihSe7gWeHft6TzqTwoXdddaiSfkH6Y7UryBd9Y/yagd+W8uk/jjy/d7xbu2BsTFqC+3aJO1E4mV9OHfoO77juK99EWoczaHH+1qekTW5lddeqJoqnVfOweFMV0+j4Ubz7mGfrX/LS01mW7IlKy2OZE3FLvGR4SIDltxCdU3anQYoZEB+F3xoD6WtDPuo1kXGQDTTvmG/n0b7Qfj7QtAUhuGcGWWiGmV4ql0ALbm2ZMYijcZzjsc22+hfxRBr2zHiArh/Yi/8TFA1LIE4ntEnP9lJlIkmPMWBgdtO9Oyv5W++0lvA60n1jF90dX7qJizSh+K8VZf+xg3w2N50l6sW3hBYuQA340fCBGOBxh5tKhO9vONWfq1ZDYrUBTPQk0a5ihVN7EFm5k4hF/2BF0yV4YGFukJcQcPZDtLGGD0LMsyEwmsgFpWnNCGf1zzDrRw4JZLjSuzweOGmD4LwsVpQ9wdsBd/3ah78VLEaZn1j0hLZXHIEAGijr+8fUbLYdINw8316zo2cdNfw+63gzR2qeyeeBgFgYYY8pLhwqp/7BUSwG8lzmpAG1pVud7qvqYrR079lNOpyVe9xB8Dsy+YgIZk0xmeNkG31AHqptqGe+f1FVPECg9GXCp2WUcj7JN595N/iNElXu2DoaNDI3uZDsA7zHNRWws8BdZpzip4YLogSEcOqdyT4uSzvT8vLBYHFyuF+PK7dCsC9YjiZIQBR3XUZbjPUFj3/PB6ZdQEbmstFrRHQPfG54NGwbLejsAy9spBQOxTdv2iOjHEnXkDUwhLBDS721w8ei6iHOmuSQg6MOGtc9nJji0aqJAqLV2In4LRh1MWU7UqB0ry0Rwy9bCUnuLrMbj6aTYqdKJdxZtDMmRBdk+1jV6OLR6tVeMnHsUs9jOUaAINsjqXjsU8/rY7uYiO5RtgD5gXc9Mm2Hk0eSNXuE1bIXK5A7uJtTgF9ftDVdwhJNlld3me7Rp1PVW9aD2pk/293RZPyZ1IX1l6iGUBib9vjH0Dzyon+FfdM4EIXrIc/nWNgExPR0S+kM3Lbb/svm6pBNT8j+JpJUtNNxCXQTPLcOrkklci8Z7+x3DEPZoA1zn+BSa/dVyN71ao4ZuuXWpl4B0YRFXEuXtp5yWzb30KOgRnAY9ZoY5ZdVSPlMrC+T2cAhHM+ooNjx3GODoiYmUktvXzOhmGSoydVwz9PtrsO0m8qeqLvAmfBjeee68qSF5TUoeGKnxuOqe1cUW4nh9VRCrYgLxje/xIrNycjsc88k4Yf6apv2I6lm/h+iQ39N0vHODXGcK6wvWGmgj9eGJ092Je9BvzDMyTgUWGMZDAZK57tyTuZGl373uaGAQUapfmXHKYBVG/BTc5Sc8X3mIVdlZ32zmE/vL0EHkbN3E14e1PZb2nLC90NLkHSGZdtN9CwdsqV2w36P9j5oRIruSAxzvYDFwrhwE2592z8HWOL0yUVcn9PpO5T4SvqiaTnxTf8dNlJLmhOatwa6aPPOqsUW8bHGzKmbscbKqgwlpAN+RjRoJrmKWW4ktZyASqFdjNDwTS+VYgOi3L4YuewQHl2y4A9grCXnQQjoVejw6TbhmNqorCu6kUpUZPECnIaKN1wCg//hdb4MfSxKmayMM/0dQKvH2QKF7hgOIwxAs19JVD7Evc57qRg9Pmo7+u2QFWeuzah4V0On/MJPfPrJrEq1jYFHDrwJ7sTlBZ6+VRIQ/hHunSLOGzAXNPcTZK8p+eGIshxIElqP2aRErzgr53OlBDzIIamRPg1Vjh0AfNMnWF14WsUPDfs0VbcyReQVXLZXjaTkzKO2e3Ujk4XWEloaea87XBTRC3fx2fdxAhh0IBh566HccNF4bZRoP5d19+y0nLSTwELdqolvJMu5pmsFU5enjoh9Z0fbKP1P6dtKudHq2ienzyVKfwWz1OH/aA1yfydn1727lXGm0FDS9Pa+lxBWMd+EdHiGsnWvZl/zdemOv8JGLcqKDB7afaZ1CuF5T46flFetk7gDzWsLBhZ4P3Yu+OG/DCQid+6q48Wp40K5mmzWYgqEaASimKRI8cVBrvHNGRJVhhqdh1ZFJMBsMXHO820Ue0ha5NGB1C3XKGNkOFUzjrzfms3+qqKkW4HBjNbl4QmCpZaXMTmdf2xcfsyCXNrdaIqtT1A5yr73UHnfCBgOuhqJSgCo0c6Mt2ob18hhNuOSBbk8J253ZZ0p9s1U3OF+PqyupHpeXo/He7z3swt79jqVf1QVmXa0ICUI8kU4yDfO68GgrRZGyHG8/tb+NNIG0BUZd3yKBWt154y24SRabxknYhX580AnLaYuPbHTXxWvzqdHXpQizuAqZ49NTbThnWErT9UtVmrk/Ex+2ULharAFvpvMwbdcycK0nXM/q+hg/3la+CncsoNy5aAtP1NWsaOztLWJ6HX+4X6TFUy+iZg6F8P7aTAMiNkn8d+Fe0An5lxCsmkqsYv/1pb+G3NmcxM0KtstKWwzMrPDSUdNXr/896A8XOFZ7wyknVpvrKBLfsAga3dyfY+SxetQMszk2jKXVROtg8v/UK2U5ojNryvsHcdsI0vj5mL8TT355zi4EEamOTO/JJNDDcHyuvSCN/cbT0vaSfbt+r7YNSwycL3qf2diOtHXU0rggtgtGV3/pSkzvJojx+3iczqDfxmL32900Kn2ZRPsu6msJFcnQzIgDDSWHhGu+ocg7oTUOM3hiVe2OUmJ2KwPqfX28O+TVfFfaa9ob6kUQ3NfyRyd893vbzoYFxjvjdhdJIE1Dc7e0yFrKD0c1Pgqa/noduBlddBYs+fX2JjKSPUuUg15Yc7n4/IbMiZ9wOlnpeO6ISzRa8DErmUS/R40IbW2y3QEti80tTHkR1gl/7sweyYfuOWfmcxPfUOdhSIaBfl1kLq8F9W/0RG8aaLzGj4zoEa4IO9U1a7aVxVrriH/B4sqTRyq2uF/C0+V97R7s9d2Ct8vWCPuf+1ejL6Qp7nkmp8XqsI/e5hV1zqGX4dcjGznfWkNY7tJrAfq+QOA4/vrg/bkTG7NpI9NVCBigFWtgxbq2/3ffELg25q43ioA6oQZ+hQzlnR47WkijK6Mc3KAPxY6sVk4uHNgih8s7KtwSPlNUDinCE73wFS/7AttI/0/qPt/U8qYGZkz92OhUYoebHE52J+qrOyD/MJ7C9S0/rHo+kJnWESD+2mhVP3pK/9NA3r798hBPI+UgJACjJIiIYGSpQCSxM7E1OYL5jq34ik7KgUuixLoQGR3VbHL2Cy7HaRpT/w3YYsu6tkXuEk9BYs8XIws2kYq9P5jM/R0h7hD0knINc5NSPcZL9cFXmwyM3pJnjZsjj0toyrOgEEWXbTW3cfQGAktB2X9Ke3JVhnJ8OOQDoG6MWHoGSnZiEfNcjlctzrwStlw//L5mPF+m64cWK+sfRHlKy1eadKfGespUKVHhk/RXXzysn8AgXaNm/pzzMvhifFl6sn1eVxEUkXy73vXn6WJnt6juh0H9Cs+Y85yMLXPwrg3U5OgkhtPbpvUVDNtHaBvBCBb+t/l9XwTc7lqUBC0W13d9Jg+fKrN/wEUHGw4rqTzdsnPfYhcKCrqlykRm5oYHRq/64rqqTU1a5iAXWiMT2X/fAIOERcZjFPQPo4tWXOIAElEcDgsDqAIVIC5akraSiVWQqPsJm96Z8IxWQgJRVprMtwcyHcMuoakVRKICkWCoIjfVPMh118z4OODnpGYnxPxvS5vCNUxDQvx+YHZKCXgCau9i+lX6zFcmcbVdX2qiLvmuSOPZle2j3alsQfSnBdCAY3k59kRV5ya/5oRhS2D8Mv+s2Yqs0eSteLbd51/Zw8e/D67DJHwRD7PhW+pulefqdge7OwvRyNCbM7MJOGMySIvpmTG9Esdc29r69nZXSqX5og/dmmjPsvr5klNgLRJJRkPRlU5hq72VOii79WH2KI90knYNwfgdqhPpz6nNbtuPSaC2YhkgzPJpNTs7NXbiouS0qoE36yanFPpsaBcY5gpbT7OA9KUSVIIQ+/T6M3b4k+DA9aGhWF6MTuXNJdrEMUGrLFLKG3p23OJFZxaL5cAAiKR3j4GkAcDNVP9QWMhN28YP2qsmAgw7tFuMied+Qhe/4FhsduVNBKeEp9IICflgfpK6m/iblQQjN+7BOoGMgV/0Zl+LGK7pD6EeVK6ExETRrOPpzq1mU3Th7V+qtPNIK2NnYN1SvpnETIZep4G9bzdExuUOa/JWZmH1jgZjqhDtYe3eUMPHuvjySp61ZfRsLD0SLU24XwfgHlVSXiVGBsFqZI8VVFrQ1Auv2yzoIPpAYdeYBq+b5zOMVl71UuP8Yao8cW9FMI52K9G8EmONuInQtKNeD78ToCUXzSGhV5VB2VaaAkxMeTWZUrq5LCW7+BlzJpILkuzwfngO9AuifvsKiA0AhoCILzA2xZ2fJco50O2Cmr5B5cesEn0NgZ/Iz82I904kiHxHuhS5b/Wvdm95IvIixs4e87Lu5icB4w8GcKVUCo8hmOX+ZwhSFfGozQtX5m5GC6wU2uyeSVjjBIVe59rxb9TWclH4s/825jwbpM+RrElJxz5tWU6GJoV535I7oUueps2aF3ccu6FA5WaOals933STd2qrS3P09w/U3MRTvnvpnbG+2v3IrMAttch9UbboF5Zm90XNxZd8XvmvD5ba2qs0OvceBsauWgPV1vukRsXJF2W/Px526cR+taR0p1JGPEcoKv3BvphE90oruK6KMfRi7iGV77pFt79PBS4YY+o65Ul8m0CpQqEFJRhVZWpl5JfYYKQLTf2p05wjj1gZ7uhIs7M/qgT3WsGUk+C0ppCVnWrASaFLJViC2IBEaKDxgjpdjAPun2Xj0tH64UhEK17g9P6Z/nndzM54iq6kXes+PIRXSmbwASBUxvQKh/5OCCbXyheflbNxgZgVB8YoDldSjKuQqHyjdwEumABZhIBvq21ItPOlzEs1hUiCBYD+MrknRDaJQPk67+ZNJKEupao5GVUtAs72b1VqV/zErQV1+9cPALgIqDZkkJ9jZifsU9rYlO8uTtXTWVPyVlJTtHj+9/en887LP69+r6iZ0vej3w3M4MSKBsJtMfFkSZXBFkX0WardAkyIDrAHnzrdyPS2U3fkVbR0HdLwH6cNRwW9cuMZgkvI/zqRyAR4MbGJaZmcrUaztOmWbvRrSTJhER5pFcmrggn2GE5IJmP4bXBPGN2oCAaw9g+UtVa9ZTY59VdEhromF7MZ6mMYVxD4D/NPeE20oyr91cJ53Cl5VLViG2v9UCCtrp3xUIknBm0V9pYO4yQJnYhFUurONEubVncBES8IkTLWSFk8489v8d3Jy8T5S+ZT/l1rQVFoS2zpNFLdp9bj4PasO9gCc1/lsYbxCF0WgApaLiidJ2EA64pewerqv3UX8aBdZ8fbnMhbmTaMhZaeLGbiYj54ADdatkXHM3TVqUWkpJSokWaxgNaDS8JBtmN30hnuJD4FwLfsxf5ePGZe4AmTkOzfEf1K2j7ROJzxVfeWObZpWa56nG61hpMR1l5xaZiorwEjPnG7VVZRabCosUcfeFujZr6sMNfukSw8zw6PAiiXhTT2YRRy9Znau6m5zN9YHY+JrcK9fWOJ9RuT7JWRP37lkLqc9WO6+vdTqdj47BXhqy2eJ90h17e6qpHfn5CXfHWqUF47PnotyA33jaaH27VPkJ89kCKEQEypVgsgUi8gJJzajLVtUpIvKEvPfDANWHYNFiX/BHkJs5TkPkrAII/KqgIlRCvVoqIdKoPG3zR+yneET9bNed/KosIgv0O2Q54k8qeYb0+jPzqfXyuRP99g8aR+cbcN7kkryFkjdYNPxrAuXTZiVaPBGpzb6AMpxKM3rxXMT7pKcuAhnRnMmuSBujiyynFupd50CaoaR+0z+IxADpYxyTNjM5QPmbHEBQPlq6Vj63A80RN3UG+6ACImiDgME9w3NAeOFH2/knINEihJERd91Ob430Pw8GF7pnwH931wdp0NLyorz/P3g4I1BbVKtUh0OPgjgURdwuSehHhUC1rz3MfNfF46+8htpiSjNG82voEnuBvXRmKrwICy9dlrvoP9x2+j4edj2E3/DMqTK5nXqYE8Wz57hJP+gespQGzQ/Shg1heNfXS3HSTXtKY0jgZIqMX8dwRC720WkVAbfB+CeTmdg57QUvL2lm+8YQqgvCtDl1q+aYxCm+c+UB8p91atlJ8odMn5dus9WXN7/+OV0vOdstlcI6ksYOnCAk3mq7H5Kb7RP5TaWTQzG+vPsI95JSBWaVsPhTemllqngOUVVmAVXqhe8oiGan8UAlYwEvN4+X5OHw/2ZtbKRCWaQMSgTndIhyhjIGfvYqfNraw75yd1/fISk32Vw2J9GXCm4/YlPSg61YpqvcXCIlFzLApi1Y/N+roU2lJ9VcFKU7Nc0Wa3OKzQ2uR6SRPrqejs3s7pxTvzDPxZnIAidd1QFUTyGNBgLJOpUmSvpjHWtGPUTTwMy4QIkWLFNDKJze4N4rozYhiaA2xFOPBIgXe6iACobzTvBIJGBzOIO7CNtHZwyr1801MqUXV7FP0b1ybqcfRdBN6RfJkjRX989kGEGtNX5HVFX+F1zsQDNU+yCwHqRcgnr+08TRwWeDfo3juz1dPkxORjoO8uG/QY0ewTBm7+wWf6ormjr9t4jTDO0bvVwh5pJ0k7Y0pYD4zljH4L7SzYhuUMEc2/3Uicuw9MuSLxR1OFYHauWN4VZcQN+LsbYT///Z+NY5dP90JSnis8ZcSwsZCl63Nx36lOj0Dw4lRcSVq5c2A+3tz8MukscZidbHgR0aeOCn1xXK+VQDtT+/DZpP1fkVsRAYn17UYmJkUHGmr88Q4BoNSPi8uwG1RAUdIvINi/dfKqPy84tIF76CRL9ABQcu6RrYeetJoc8TkmJJvKhKravd/Sn3qKv/czotyOtBkRFME5pknzBkt4YkXvCOWcugj9ERCkwyEOvH1MNM0i2eFBYtO5z7vKkpG/XgF5H4ejpjqq7eUd3oe+nuN9cXN8Qltx5ien/OwzbQGWvUwyPEtpEOiqD/21jb4nt9127cZmI9S/7Z/b/CJZd9jUkJ0FsKAUShLpx2Wxb3/4GKtVFZ2UM/sf/w6QOEOTTN1rRmrYlGX08n/xZWbk2dOxPM8YO8oMEeXrsG5rVRWDMN/Obqmg7KijNXtk1dqHuN9uTU1r21z2r3CsIgozdu8R587BvNFh3Lgs0uIXcYVDjnQRu3AlTQYYw/ikTpENQ/BtJBQwO3/qtcMswHbmZMf0NdR6G73wP0YcJPTev2mVuljEoEx/XMnJRSHxdWMWbH7DyFXfqGuOaBdDTKYLYTXDIzGioYicnnV464e0BBAtoGSZcAOzwsPavdXG1IOeG/m5BolkDQhUAEVO09mMRWkKQbSXNLcB64UpMjmx3HFnaR9L105rD6ptBqP9xNRvftaOoAaVDqRt9AZ20jNqrtvsijh0hztclPwBzHsTHCoWk2FxM7meys8vJcD5hZlds0l7+3+Vs23akZdzYSO7tKfPx8kXVUmAE6m0BHBqSuQ/IRXfaf1UIhEsG/OTltvrOkPbMSAqOhqvPFQ4Cx0TddHW+YIdLxefJU62UWycFLJQSAUB5rkM7v8r4Wnnu9X3aYf7IqpVkg0nBU1vZgmw8/BL+fE21awAjhlrbLKGHJwXPr/Z7pg9NCLDEo54IUD8G4FdlH6CEu6ZQdPoWjyKjUEJv32gyyJ8LzvLvm43cOOYSAkJTiHJJ1OdXC833wTagwxDICQ4LhkW1bjwSkYEs/HAhQ98zmHOtTlX6+KdVDEFLkNvr7w53758+cUek6XBQicLfwZibneC6xfyToCSYdNL13jv/sjS7Fye48H09i0bXLi4nDMunhmaxC80eHzPmcmZ4+PPdkolKfWbWAunDbh9swPw4vE4zkrUjSHD2UyeP49S13XEvziw2QEILmb5cnVHw3/xjbePAwX2LFq1xn4W6Ldc/dKRJJMZM0+oIi8d47Nn14AciL2gHf8T24Z45aeUolYbnSm4/4w8J83WvtAJCx7Sc5iayakhB/TV6IBDZTODaqqeYxW5gLpAMjEAwagjOHaBa6yGWNuU8VkSyRnmNkeIuyf9Gafqnycl2QzAlISKIZLuDyfbQTHxWqbGo2d23NCZKfA6QSuNIKh/XeDgoFRyW2qX/v81MkCb2pvAgTkbrvFx3mU/NzXlX4YY9sLC8Mf2frwhn8QwInKjFicDkDshi4KB8pLHzBYry7hPIyuBZ42xppCNeKQnqDuwghu53pwXoQ4GDzObozqqTXfm6+XgpiQ8hcVkKIKEbNbTGyw2wN0kHvBZab3qwZLGY81btT0onI5MR3NHoTkvL6GQUxq74ijHQ5h5LSfGEv0zlyOi3s277XkuJk7q8lmgJ1CvGLnfG/DfsRTJAr8Tf/PaS82P3KcjbDpSG6MCzFxSK+8kDR9Wm34XjL9icLJhfSVttnfOQoi38/+jiV1mV0/5RRbwvDqPZ0WwqQl0O+tDncjWzRopQ3C86Bc1TlBsUIUl8HFnyDfbOgATQqt/QKstdBN+C2H1VO47mxLEHW/P5Z85ISg5tOzP1ksVAuZo3KjIHvwoyerTE4LUvFfbVDqCT4DDjtj/yISGWslBJ5iD8CTrYVxRTGLhUpxcwhp97fGPjM4Gn2YOmlKaz5vlyh/kyJDsQFr6IovjI3XaJTRudoyP0HaW5UH+8R2ia8ge5gzszrEL7FSS7Ba3N29n3AWksyKaggHqlxusdMBNZLa71R+lmMtUM6Wz5T5mKI6xW5ItU7k9nx3zkQ/y/LoKRI1nIpFDIvTyOFfvvGPHP9WugJdM/iulk5fqUt6pUCb3qCX4tPTU+1BwPK9Sl5Tggko6jSwGJLZY3Frdw/Dsd4QdrID9rM+Oo/hiWe8/jpy6uLGL+J+grSeknDPE/J8B/x1drMH0Zo0Au7R1cWtBY3yqTgTpp46nXkFtZ44yh/z8fg/pR4atD7NeC9Y1DlyRxupuHHH6aeoMH7H9wD1+5mkiGEcNdfS80V5pY798D185kYgNDdzT4Vj2orCbUbGFukWcGI4G3njRcb1MvqsQWKWgNEbpOz1HPm/M2kvZmjIWy19XcLa76/dTCTGogUs4n4OTm2hkbQkgbaForf9LGghRzi4RlByS6ekTO+FnEs5fXT0Lcf3zUiKsz+7Cn4ECgVynUn/hb+veEb2berAsyHMqRVi1mFeBzOKniD1sXlYkRmuq8vSj+HIYIIs8M/r3ys1i/D31Esw11aF++pcM0zA9P2XrNLNbg93jhAckS2nUw8ZXpPrZwsyWjJquXWZrVklJDy2p7pKThzp8TDU9pqDahwhDx2fewIAbeOAg9Xe8X1Vi+FLwHwrRVq65BIYL1RfdAHcQLEgH9YL7aHZ0ZkSsOo2DmcGmgcn7mVDHv/6+1yCsP1YkW0f0Vx6AvWixK1X1l0xUVXnFp2/v37tK3Mgw8zCZSxUvxnbMHzq+Bq2AiIfMee2n6bCS8b3p8vpeGu9xJS/cpK1PawMAShJIwUq/zLUxPuuTIo5Xd+Acoi4x3aaQVfXrti7AdX1iuEIoi82XGwpmvQEi3ODKHLdtQPKQM1wMl1Ak2gcqh+h4weIs60RA921Rzc8QaBIqFCXl49jSSq4kEYuVVWXR9PEnUG7zLRw2xlqeDrp2h0WH0woF+HwTzAfhRRUDooEUHku3qjTwXSB/Cxz/Id8tSKwuzMPIsxI5mptAINbBQ3wsdl8v+fSrqbqG9vUrPVipIcgSW1562q6vURV9xIjXc1i2BecNjIdsl4r/lnYyX7SBIj941nZSQw0hoyfMqiNM3WbzFEjYlP5ynUoPpH0atmoHtXPc23NiRKbOyq6aypVs8alpzLtI9VL5qrmtm7fn37kPO1ZHiWutrC7nKqigo5kbfZqCyPCaxvmXJTotrAlZm01rSw4QHV8CgyMioZGA6zpmlNMq5BnWh62YZFpZbAYlB0dNHIft6GBXpVUZSymjvBH+WuXwIi2LFGullP0V9KLg/4ACADifcIboFrAZdC+Xeio5cKQ6wepi0MLAz8dw+KRMqa6rgw48iaI39YQyC1t2PiXwql1XdaQqABmuY2sASsz/3oDYjp8fLg0yqdOLps+4NW4TW58pM6waYdrBaxi+zT41RcRGOajl1OSyrsxmgNIcXlAdeS2OQ6YZIXZR+DURefrTKutmXReVEpLsPZQXM/4DTpgREKPBk85sabz4eZhMzWRuSlNxZxNzH2UZK8hOtIdsS18oDQ4gYa1l2YlnT5mIlJE+HU45/KXMSMvPDU/LnDPLXAOGqRzFgizLsDcGclaPby82D+fn8NUr0P2Xi36qO+DM+GlcxizyBPTmu6ffbjZB9b2H9FHl3DzBpaNI2RUzKY5HO97DsOVc4LSHeLz6yeY8uy5/Z07NvTEfybYkZd+Ad0xC/lwAd1qESyTZ8dwM2K1dwDff71Lu7yvifWcnE0z4fG+a7sutG7uJtlDU8J57ae3Dzfo2IGObaZ3UqLpjGLGlZlePZ9tHvp/iznuvtr7v/O9PDzuqe58OOJgGz9NokErfvgQIofQv+gLlwx4/+a1rXbGpil4Cw8xp/un5qqsDhFIojgI6eG5nfzLGILD0zunc4/duyKVt3zh06N4AgUiV7k7gLn98Zw2Kk9q93cfzowqwd3HLInCONu2IzRBQF2YEB63PW49MXYeJYb1wdNL4sOMxbo/KpFIuRN36b1/QPEQxfWiHpgcNGyyXtyqOEwcKDqY+JjOOh+uVPEmT8hIpHUcTF6p0x9MyULikRI0Uze9fpFg4PkDrbLQ2Kgf/2mPhAtPf6EyVirHhxc9Npdz/OTQ/6Ih/6Z98NHvZbBnhoAA+/v5bUiIdJEx96dI/mRfpW8Xt+8LM3Izr2JDmkItyLv3nugH9nEGF/KGh088J4CRaJKiaGRrw00ZwR8zPk4IyDIbI6prvcViSD1q/3rRllLx1mNoG9gVPXEbLCXG56oRkHEFtZLBrqTKYjyLQ8d6AfP2SQfdoQP5X48d/1rvcH3e/YzmvczRlVPDOV2g/mJanQA9DewqOu8bv9X1NWo942pNgcVUSnvDwyOgst/+SsSCDqevGSou5u3Co4d558o1BT+KD3+6RYmK6/XFW7P7tCCzQJv3jeRKAD2y+XWtMATfDNtQqP0dA8tSR4/W6Eix4CBGf+hjuztkP+Y5e+SkLYbPGChUUu498cUMpOFgvGZ5TrzquWJw5+vzmJkra5y29gbXJDiYPJxikVmUoxpvVK9rWQBm8dDopaRsLf3OZs1bF+0ZIsydx/YDyplSgr7eY0kXZKmMRFnrZf/eFtjQXbvXvcoyTvMVhO5buFCsBQPXAbPQB/NY3ejhcIQltrCdQkj/YlI+BpiTTiy2DJthS7cVipkUCzueq0B9vYJLZPXo9nYLTpEIIST3k5sx4isQqvGl7LgDIZkvseHvGVXRkYyvBa2zQG2lQvb2uC2SVHqCrBioVfG0CQQmc+eqpGke1vHiDMY6pHklQz5A+GNHCmiKxJn/UQhKHwafcH5OjuLj4l2f0v1jl4GcLdTbOanixcDY2DVxD7waDmNGx1oCZ6FGQMiGFPBECbzqkRhiEwWnf30ytxddzuyv46WyZAwURVUcLkABk8xWO9S2qPTrVGDLS3qnWzWDnW1k8H0WJ2lPeUdiHzHOP3dtQTkculxNvO4VCgE3dInoGWAjxcEQmELMEkHPwczW8AJkyQ/ZzRLs8wfbOydaXNYnVboMNsQ7BaGCOQ/BvX39+59udd9eoa1t38W8fiktSB7A1GdPUM8pXrh+kK1mvb/JIHj1Y1xzrhjRF4ihurn6N38lY9XKwzxyvXugiBTIm1HTfzGmrgRYUS4cF5idDufx/Ft7ufzimmJCf+6uq/3jTfAPPJQmu+f60DksHhqoB8hUolUEENwuYjnkACJ1K1TjvL3DIABxGdMx+ZX8SMipxbkzKFI13rMR2FWVkvtEa9lDWS6So309PhXHjAj3bvae5d3JreCEgOjccdo62yHtU0Kb84aPZFJULENGCoocUbn5dYbMvD66AG9m7gvi/2Pj3Arw0TYEGw/88MLMuiDKY9OOXJ8MBNtSEk6y3HQY+mh+6oYHVFcatrpZL+EJcboloqkaQs+NSx0mu7PSU8S/mZjzZrNtnuDOu+IuDDOgz/qdiFYXLosr9mmlDT/k5m1gkoaArJ3NiRwqlQBfxAkn/BRqkoYkpKY3BzwiM1LPo4sG6ELAey3+bf9fvZ7yhN84XZDPBWwAWzYiLObwgMev4DwRnFjXXKgYD02QadJywM3oVoa9hmGqiWh4wgX3FcLXdV5QYc3H/Wv9N1aEqTKeJBhrA0r2VGdZNLkB92vZB+2ma1mPMF5l1IoUGFOq6hIoVw6C9Or6y3yD93NsS8yPVOVXE81K2o/PwzGeOznpj/ZiKAucrGdoOI3MZoWJGYMbdKb2VocMCfBsQQXIp6S+EXZ3Bj7rKqaErpNYGNaCdHJfvLC9QXdLqqMTf62ffnDIbCYAcpFv1C5fOascqM0yo5AX1SWc06Wg/pCBPTqBxjYBI70BpHS5wI5Jhccy55oumDzyipGGo9+UwQppwUG0MEXN+5yHI3YDTb/2MRmLAXu7nFnTr0CYbQ1pY8x1hhzGBxcymAu3Qw2xa2h4xM3Gxli0ghi9zgxVj7v0UNePgtzmsDuXeDXPBY+BnbtBqYa7mDRi3NxJtOnpub8+eZGYoO7z95SE1TsLIYIlClJ5lTP711MJwrL6oedb0ptCIYePmZO8WIiINaLpWJXWVh+IM4+dJe5u6ncXCVu4t83RLlz3d1IsdsbbTwQvo7B766d8g5E7Et3NPylYmAPnq/wPXzoB//UpelezEV0VDYmTjXX/NsiELZ8vyXycnVjxry3y7uBoik9rwW1uWUrGD2s4NHlKdJf/nvxt6RMLvv1hK4iXsJBjInZ/PNJcWEBQ4cZL1USILtvQ7EJjKoAykI02Sn7J1CK8cbUW2MGzbmWPImNwuXTeV1YVKx4jw+SlFL+9K2wckHkB+KprheuL7pAH0cSE56/Eyp9Z/13admXM2Wcy5NxyT4w93Q5SohciSqrAsr9W8GhTXcdndgPPp1mmSew93pIiPiT2Wa9NO1mctCD2IcMJLyoS7P9Sjv/s+smjsJUbUFwJoLKMyi673APFsdLn5p1dpXQLaucAoMsgWlw7VqFgE2IqnpwF3y89sbPmnoCPgtK25adX+8kbmNUvySlMT0NfM/GbxbkgScxlU8Y71iMKZ/QLFUWdJj9P7jRVsoLq/3CCS5+S/qV2pSOUPIbnVNVpKGUsoNS5F8oWFI2fSEnIT3DSOd4NZtrLBnPWjlrfxmugorKdnvAPYhfdmihlq8XuJLA8Y6alhm6x12a9mNisPzJ5FxieByfnhrACl+yYn8kiRyiBIqITuupoUw6fRgz6T9wTcquzU4v5M0u5hJ3Yd9p6lzJwZp007TI9BTHQVPFoPKKa2TJdJE48iM/GXH96tujLm+vXm/jHv74PklFuX2EyX5+kJGWKLkjTQvS44aD4Gw67R+tuqaA/t+4LImeNs1b4y0Jms+e6lpcBPPxNBBXewTsYREIOGiY7M8YUQc6yTMfcyfcBT/YUJab3R4suP25Yjcf19aQNXyg6cfEYVZJnptws/zb3+Wbe4R0DYM4t722M72ztn3uHxtuzmYD8vo64fXbvQtKb2fcLh6xwG8VIV+G+myNPewR+m++Pn5NS/qXfhH7MsXaUarQl/4Md6LgwtcUDlWRfy6Z1FCOtpFVYvkKKuvP2s1cuIlE4n76YL7O/Hpx9bug+eaM/mJD8f1EFbApJUPb02ZoF+q9F2oVVC5JCwZKh8hKFuN4ayAt/hrzZcKf4ueJU+zJdWHmOwb7ObA/pS2lY/IhzyFQya8kpUPeC2kkl6rQhtX7a7bov2pwoKtMEBso5w0x/z4/VFrdvncPmOS/m3PvGWGnCPBgJWkB1oFEOb96dDfY+4RRA5szaZe+S8dNs4DbRA7PZiyKa57weFjF/4Jv7TPUodWWMt+9veGfWh/u/lmL2ScoTIJBYZ+ctXg/16f2n76374jED/mWOnz2TKsOuC6+10kKg2DWP/GxJV6H47zgmaMXDpevTtwA6/PncsZJ6aKolugpsPo0bVM4fFRNVPIrZS0HADn/f2QEm+SidQ+H+8r/TJHSCJLlJEuDiwMDsz8LLdY1bLVss5JVGG0zHU8YQ9LH0jeQ4W8qZh9sCM2P70qV9UxLkvbBPRlg9gH4/lrEMZtJjfrXQMk0LqKzIy2yIG7om77ceDJ7+mrLbVa90y8lCo4oFrQxSPSaa7Yvh0QKT/6MLDUkScGCD5uil2u7Aby965nJiTHX2j75VKxXFpDVdOypa9RSJDxCvZOFOTXSsGlx67bIcyHsil4Qq7n7Cqz8EMLyn2AUGzuNUaEV83HuP6eeHQGx71wwZ1h5yK1pa2LXwGWG5QmwipjAqcuMW+ci2k5N1xbL5lQIqjrp9s27Y4dTPpA5cbrkf5TtdzdGL1MWQ11U+7xyWMl1VuxM742NWvqVl7msBSHzOQNtT3g38rSWik8QVZDSAWHuJzBz08AnbPp+vmx1IkyeAE+qwOiT2Z53357nuGMZjoYbq5i8IyNF7z4r7qoJcKUujbR4cZkukrTMprOZ3LB9bzwq105EqowA9sntN64f7oSdo0+P0c9h6KJfKtZwGLM+6fuZgp2jM3eCSsWfRbLPM+cGbYzWzVwQCnYejqDvb4zuFO6sePFRbP9BGfH+wYmVPX8XGAF5A9U4T7A66hdZJb8SeLXL26mAy9kR88N9zhexbY82hocmyFye9kX2RaKN6C4ml6T5tHu1g2qMtUOi/hkcJ5o5LpGC48LgarKPZ9zOZuwK46ebaUxXW/uuLF8el0fL0xUUTKtRfF7WXNOwTqWp9Tc3bhbyme3ejJRE06mYWYibcS2D7xWXzHwgc3RAYFdjNzAyYHl32Qw3l6MhPFu1gsq6Di4jTR4PIwQbuMNGCv0mXTDpVVIV8fsMIfBsO6Dz75nsOcHj0fMSEma1vmZSmqnNyVoqfrPnH7yuLpGR4rUEHD8owq1NZ1NW9a8iK80IfNVrhWvVvkbQAm3Qewzd1Om1hMUV0NNfpBZ8qCwU2WpK2mK8G2oaJybqDtp6FzvDYO5S+7pR56WWRHFqvNuZfBEGDaNebdGSxhYXIZdiH+ZSdh39WpRKSwCMzLyjSE3bdQe/6wp7tLUF/plRMyGB5rFakEHuPXNgv6BqsvIjTQSD/hmGQn7R6cLjkBY8Gk30SJL7CAo2gckkaXmeJfI18d+k6ApdsnQZ0cKCTCqfxzFDigh1fKWpO+p/GZR3NdjK6WHgEqSYRG6VZTYdu0Wca4bIdwsCgnK/coj9ZxNncGqmfPzzqG+TdT6r5EZ7niRNhk1gFKIvWTJ9foypVzow/1o/QVpJINcHUHg9iW4FjExQs0A8VXc2uPlsKr+5zcWSlJRMOAa7cjjDucABDioLyGQ6/qtoyz81PBc/76z5q/ovKN0LxieZ7OfjW0HzfYkkzdMiQ+5+/JzNmHwMBEW2BQYNPV2kz6d3V3PT6kx11zPy5qwn4Y3vgKNAPDW2ve/vp9B8CEllaxJLfcsUuUiCI0fnWbE1xCeJcPR1OImB0kBvi7cVXr2GZFJ3wF05VI2mxFNdXqleFoAUFz9nMydrq0H5TzXqto0QWRN0rHTjuhMBvKJvCr8EiCwHGpXZOmFBnalOkJMR4QCOvLbxTTOZkPfMN8x1w5tFm8FugIDSTF5jAyuD7i6mb7aUTudc06oWgS39tnRsU2klPtrNLUzi7mc/p4fEqWwINoHRuHKvkDYwt6bQPSRY7cnAjsC5JaFNjWAS2Gu6Q/Ptk5OEVDi2oILevHGM7MFsia4PBnmO/WKremDS+Ne/56aWbfLm8rw3pZZODzlOMboZTD4iolj5vcGGYmexZZzg4lfNYCaVQmq971PRH0ATXujo+EQZMUdNC8LnQW8DjRONhsWAC+Lfiacay9sD2ssbLbO8sr/NAm7ai5F+zVWcNIMzlT8rPCWcKE9MsaTXcx3yYF2RcOEqouTWbutOm9onJtqr9ba3JzL11fOu6GrVB48/FfPuD00sche7Lz67J1OZuefsYuV2np34tDhwnX29X+BJ38AOhIWQ1kVoODT7bKdCES7n5sJm9JCEgYdQDVTBsav7JEY6O5HDHiN3fm/OC5X8Qo6xXaZwuFRy5tixJ29GqEfocwJN9nI15iIjOEPF8B+i86U5JEK6bq4kwcl1JkjmrlUf5bBA8tGYVGCurehep1zD3xMH+A0eXA+LB+nha/Lkelo8Sc+akA9gh1SThXV6Oegqb1/XXq4d4pko7LHxE+dfv7tMIaNyDKSid6F236+Lqmtg3xC9cabzTN/i/sF+wOgDwM/hW2Ypi3+r5brRZHdQzGkcZ21ZG0LgPT5vvrqdW3/OzxnofLXrpXUXVqKrfy5+Xv9V/zj9ntWYUlO/Bp+W/3CokoavK09QYmE1klg9uH5gNPcJxUMl+Xav6lndATrnM8btg4bOC5ziHqcpPg4EnGP/ddWcHW3YAXY9YTGmmuZP7wRbEo+fF01PDlgKhnDsS3+ls8xZngnsk7g3LCtbP81gqWp1c2GBahCxs5vNWPof+P/o6ipqAimUdKPBUVaA7U0aXrpqyTAYedVkfz8naxznNIHOrqoyVQ1unLc3nDOHW2iAWUYwdo7916uFjgxZHdfGuP5xm0F4P4AjeHbzzBl299xkiHqr9xbnl4PyybnFMq9biEQGjrs1jltQnRjsq8ZEWm0ou8kXfIG8fQU7orMJ/8whQKXfeKNiBZOiwfs/YTMMpChSwcBxRy9E/GzCCcJUguNJ8Bz9/3Bc4MFMJaiMBD3Cmj2dUveduNdujhbcn262T3Ob85k+6mSpYJojedgNIGnpseCLXHoaAtYVq/RGTcCZAIEgYGKbTRCSNpQJph2PjFh/Bfc4AHCmNRV8GrvbcOHDkYFxpvvnFmAFolu0SU7Di1m/6WawKHZbql/rr43P6dgZIhsYFjW9lFtmv7VsiQLVH936m4n/88kL1xcg4jRjaopE4x69e304jMpIIme9bf2dZuCw8sHDMw8KkhjYOcz/2ScC8ybBZoIMn2r1EoK2WGoCIP6+bjocpxx2enqreV7ePDX9mniKXVRkTUukqzRzbNK6mtramhqr1ZuKn8UoPGVhflEx+4XrC/YLNPhKHsXLx6neJk7NCSYYwS924o8sqqiI/e4yhkmC2R5dVg0/eDnVNlyByyGyxkbN/jWRZ4ImcEEwWzSf/eioRl06D0V1dQ2AchEKHoqdIzmT2PN9l+VW/5flBJEb/e9gzDEQ5dBaBRCB2Od0zARlOm7Wmi3ZXerEHAg/4dqt67YHUC4C7N23D79BFkIUv0nHnNJ2SkdXbEAttiRowuifK6Y80lqjHOC+WOrCCPGOM9xo4Qta3iVoLLq7fYHClv3WskRvDsuTqN7ny0StWfdLq9+EtXTU9/48U/ufbNaLzilgD+aJ6KqT7vpXp0mI4Pcjbq8KZQrEPO6ROUR8T2wP+lLBrRPW8XhaOeeNsyEqKZt1BNTI6XQ+mUDaUFY+V4H+EDs/bkEbcYTLEwuYq0Ijbt/HERgU1IToX510V9FFPO6RbFZUkrOB85sMON7ZDs+UyzJZL/6zKLq0iaHm/mMPSse3vGAxX45vLev6ByUVPX+VbR1/Cex0o4GGMC6q3+CVzMlhuUXU7GcMdHnqHRqR2KBI/e+5eApNFxFbsdKy5yCHL2782FsNBqZBOPht3lx1ir0vBtmjo965ERp+oSRCXuLmZ1/XKaO7k8z8IsqtydVsceOBxWzrtn8DVQm6HNGBA43ixoklzNTOhs5U5pIJkO5kLocLugpVSekDseRIGu02mAwpn/tjcMeNbRy0Oh/0aFovbjpwQFRriiw06WqFB8H+mH20N3uLQC2ak+Ck24ltiVGNdLRZD9EyULYRZ82/hEBAoeiRq3UBF9fvSjh3pFHceHiFS9oXM6aZKcxfOF9+Ya6pCug5VO0GTpxcCo5zacs5ikXLE1aYKhnMWbEkh76chrmi2or+q9p63nPUy2dmj9BrbuwocADOUhp9qdWKhOUxu5MMz/X51k1nvdbF1Xh7DcXOBfN9iTMYta+fvxYu4/lwfOQ+AQaVBH3EwZwy4IqIgYla72IJUo3YGcKZ9MHdhuaYm9PrI20BF4AT4zzCKG7DXqMSE9eklo4JVlNC2yUobZFsbOqk8KSf1xgcxOhnMbpT4D5q462q0sqcdCIR1V+DotVuIzwtS6uICsS4fDwL4EvxpbvzhKsdgaSDSQC/illyyWLGAPnnxWnzRqPG1EdduFzc/xGZEBrOKz+GfeTdynwZuhYhdbYex29hKuV94wiTUSkLfoTwSE6/5PHZ5P+VetAr72SAfkDVlGNncb3/wzDOqvB/hGMTGPPa24uNzhoARSDdNFsJV5uGP529Ye/LPgUY+jkklxL8CVnJYI4AVjW/DtjP4jfdnM7b7A6WjD+fokl7XrhaFC0v3+UrP04Ax8/wvOITmQ9ljo4uHX4WIwMYKYl1df/gbpPl9afdAscC8VSX2NiEuxuby3krPUWFO5HpYp+Mi5mXSQvbW3iSpB7BlfnBUHXMUBcizd5CBHLx/Rhj5vd/RCY2y1fPnAmzz8ZI17zfQnzTqv15TOhb4luceQk6YCrCI0K/QAohiATkXKH0C0GBdQbl5aP+mjrfMYcnYzJLT0ltmYek1uI/vp/JwPbj3MCsX7VVO3z/x0DkO5bwo1j4F27ojkW0IwNpHICd0mteAkilacsI503/Kv9KMmQS9nEJue9xG+vakv/Xq/DyIPujHoGAEll7+PCe5zFJKv87IpPjjqVlNNQma8tOHP4BOnFM/Z1Rd+TSF+zBSxcSqkkzTuPtP3GPmsS5SE//CorULNnkB9mXNm0tomlL3FivGu/DaKaNUos0FZke1qyV3X2FVO6GMHpBdB90EmeI0k+3W39v4sl5FuTpndbwO1E9UadbBppmROkgovdKl2cHpikv5+f65/nDgo+5JKgXRxm9lhiKAiHEA0O6mGP4kBoKWyOWOjHcqb5atCU1akFEt0nJwvYRofWDs+FWK41t4ZGbRDJ3kkWlQAK0RX9YwZ5//KlEY5c7fC0T/7oczkJQRf4tCF26wVYdTdgHuz3SNcuugY9AGCCdOHPAduuuq6Hk6hyYVoNjZzmuCemf+OlgT1AUm6COToTl5Wupvxw+AuD86N5pmruv4MX8D/P8ZqoXkiYGj2BY7i4zCEqGq11ZMF27/II4tdy2xOS93Zr/FsJ1yfmxX37e05/rgLzVhzZJx1unIddyK0Phq7uysZQVCZllHmkHyNrU6PyA2ceufFtPYUE1Wp2XKB1JrAiBHmke6VGbYOpgY0HQZnMuaXhq/HniXKNG2eDboILeOT/9RLtdHwbXJQNrO34Tp7P9kjc5k2qwHm6EZIUqbW3j2q69jbbmd0PFxe1yVxMxq0yesgpE5/lSeYLxBfvJsrKBKnv0K6XeHHkKI7m0xwGJA8VWziMf+SOby6pGsPsqYdfzKiUjq70rRCZztne6LKunO0ua5pnltHg6pT3dsizv9OwvD/fPM4uOPj3V3HT6VE0DTUVZyvjkJnulu8u+X17uXPXoiNf8C6Yt0en2hchFStL1LP6SMUCnOfc7ORwnXTTQ5DdNqaEf21TfemeaPdyQW5ro/GO2SFYzO50mqprgsJ20+GxTsq/GX7y/4Pr++tbzsekg4aj06BH1Tted6t27dvfTNEtMzISLcZO0hmMrec3PSXcpTrPBPo0oiK0ZelShA1pr9hEvTy09iYE1xctZdBAOZGuX5v+E2b0LaN4cy2zRlebD07Q6VGoKdgZDBXxNhQPAMS5NIyI2iabPwZNTvUDhGqpj8t7j4s7fY4Fy3ksmtcdVvyh+cQEbN4EFwl4PKvNlJUbHnfHbjDd5tZt+I5Gvv3EiUeCQCH+BKmBudRYIgQlnF1x3+N0DAuBYWvIYwLg4rFqAaVf3PZd2XO65mZNCoaZk99xShFI1R32jatzKjqDTIq6+6Xy8idJyZPYJAF9OR142wpdO0LQYFssAhZLllBV5U9bP7UGhj1LTVdCWDQVJilmC64EU2uUww9zc9gzKj/yGsMs0ZpEnnJvTma9R+vCeEd3g1oUWjGDSePmts3OO/l8zXOKSREzcWePCSpYldSS2Y6iPpfioB+0TtwALRKAfOjOKePgDuHyJ4jzopKBIGNlKnGrA7pKoRLdFmrN++1DYoT0mUK+y9ZeXsR4KUTK8V75sRRbRL5WjHYlSVyfZVhRg3WSY8NDZ7W+UvjoO6fnjC9BWd3/W7sR54twDtEk649vPBGZbGCPMZhbQKjDokzS82i2eejDarqX99E9JXmb7vNxeg5FwIHxKrakA9lptGznxuA0l1KxqY1nX1CocrP/77b/mT8pcngL6Z4adbC7Y7WhThbGfw5BN9/BKLi5MCvL92BB8tNYRo0Ky984UsRPLCpICfIlO63ZtQLLf6xXIncaTA3lTuy9N2TtrUk4KTpmwVjz+Z+ZL8Ze/xJFxFEh1ef3aDPZ0+/m24/3aRuOSZHbbPtPrNy6zxw34BKI0YyBANNn0gJQVJvJ//1vTbYWJO5E8Jn0Sw1P2QyAdvh6gHT5UckVdflvoPB7RK0Cv/vl1RnmBCch53RgyJ9nGoJ16QrJYhXiy9IzkiG4QUk/X11bRpyEwLwN8fsL305k7sUvltNJ/CskBBnYWV0U/MAyzmLPKox4mD4mnEp+Lnyeyz/zvwDng26Mk+r59DRuvksU9wkguawcQIaBqHjtGSuMeyqpw9hp71H0V7nYpwi7SLpA7QAF7KjFPoY0UyOqbNpA4lbyVoGITzZNlcUks7pGsksEahmSVZ908DMLOY/Sq+aKieYLOOGFT4fDihpRmcUabcy7VWB2X6lOCIHTYJzw0YVPfwwyr7a1/5vf6ZiiKg/rjrHLRf5Vg9Ge/aGCBO8XmP1gma8TADz6qAX297F7O9bnSn4WgZt034DqLuUvND4F8RQK4fCFEEdEdvwhaetDP0HnjE3Yhdhz9s8+6kSf9u/YF20wagopQRYmdamnNSVKScDEzDBOZ6Q9JIzUbROwY9hpjhBNoQAejVZpgGGSK2mNGx+xG2zC+09IL1PyG0QpNtJWbkE4dKlktzBSOf9j56LdBO6DaPiLcpY7IBgbqIzq/+lHTau76Lz/dkQpHCeUY6Lp7nk37CIntcJqI0zhVYpDoh7+RkcHKa7sSsTNP/sczH7aoAwikw9Cv3q797tWN8M+nlxEUkkvLD8wZviwnleuJ53Z5pckYvUoQs9rM++/kYYs6ms9fXBv5zo67cc1j17t0kGdE33QB9i+CmKMGz0COJ8cvBNqDV5rmxrRitQ3F9mF0yFuxxQYcNXHX+0Z3gl3noQ3fT0uSfu5jpd0OJhm43JzqvF07U6TD5f8DGr58Cnj5m1NCycXg2RF2KQ07g9N+W5z4T+5ucG1tTbaxgrchKT9XOc7ME1SASrTXigYEHKlNPtvHw3qBxufhThmCbOyimV4lVq1B+6xIbu7DDBphbGpDdlPnywn8DFhmB/P04AQFcw8/scsVAUHsP8XPOevZmXZgpCDQJ5/FzOMGB7lYyuAEL2lz9mQH1Py617D1gxlJTUzsczpEhMrYw+9dzf8pfHVXMz9mri54IHvBnYOlB6pU7D/oZnGzwuvj966m/M49ZcviqMySNDb21NX8OYSd6h1srEpz8ZAJeOell5Z49JpMK+1Vfu3+7fC74WS7gpJ1dZ8naoSMOQLO5JzO0Zgwl+8I6bxo1SJkX2a2V1RloaIQHsvKD8gaKdg5jO6Ufl5z3BlYHzlA5vWS0YAq3xI4R0dH6YfY5BmKqalT5lFyvq6n6ibgKaeRq54LDq9xl4LBwxz/HL+DjgoiN28HNGw2bgFfN/ypCBnQUwoDS4NkJoz4lpGS6TZ2q5cy0hQErkgYTpJV07Hq3TegV55fW9r0WYbqzyKgOhQSKLYfY+60vTfEEVsixpH2qM5oGXlrn/57K7R8zZ3QnPvvV6SEh0DYIVnYGQ0cbPHULmlaqX+UGaInvksUVVbyEraG+CRzC+SLQS2NA2+6DCRdStD27IECFieRBHFXsrd3098z3sHlKyRhbo6CUdPoGXB2ksmYvAKuSi1SK/z+++fi50/feiWQiTj9Bkb3N48oBEhsKHKPfpJUmRUxp372BQgTEgSZl7MtkUmzajKYtMa/VAF+Z10pT642rheNUOtQKNW7mOznVeWjlaCKKn4LmKm02QZ2yD0EDvH5BiOVfg6jLOTDiSnx1H1MX/P3+vVgksGc3LMLhKWGpjnzwHAOZ7CLyEpSIY8fMuhTI0jNYIOdpEtdGFHzkcqfyKzkpCTSubArl9Paw2cCd2oM4C682bPFuNru+aDiy4qnCE3v8r3w1vxbQUHhx9lXAGS00M2ZfzBTxDsoZ02pQ4B600hmD3Xmc4blrl4WLyu8eIlzpBvgZtzoS/CjmMd9rQrV6lhxfk8xDnX0KFPIo5H+P4vEhkQTH9A0K/D+CcUWOL1hSjz1MsSUaML6sPrYOxIKvqlVLzI6uhRQdVF97NoxIAUTGIWHCmkOkFWkYDLeEm+FO3a8cH1Rf/WoQ03jkogAutFSzWoIOVbr6hX7bC/gLO4is5OAvab3fV0NDwyzhEl++eUL1xePtXLjrxkC95yGfMH66eaQzAdPqMInpsRT13k5V+rnOSU2BNuH00iozYAA0mm0jNrAje0cYzOlrszSM4oLHPbUOBVCCQvUc1YuWdg7e3plid3sDmltwsnC7LQEbxfUQGZs2zCysGNGKiQrZG0cW7li8aL5M0No1PmhBge657dURfOJjLmN9ZWlOUbKGeXkjrHlfTEQrUWOV2f7QUQXy840RvBgWPz4hpX97fFuOkxddV6aGt95iBXLeufNaVJA3HF7moZARhJJ0yJCJEI2jIO2NlYUT1mLj0hBTUWhw2qKCPJk/ShjTWxbu3rxQE+DN+R3mPmd9bnOjFDKoXKybeakBCns09PeUBgf9mRedpopgnYEEmvXDHY01rjhPodavXLJgrkzwsVh2tvqiuV04v5tliR9pFpCWtDZPmtGaYiIcdflC8NB35uG27JVEgJufOsCOdPjqI2AZRMcGaUIWrgMCAEEYCS/763yxxKX2D8wF/QlSD6dik0XDcKfU/+pcX/0p2YQBhAA/MAJRijIxz09jd5/BNujVwHvwD8gDI1hGvQPAXkACGQDwCsbpYXPQSqKzigDKIkkHAB8+BbQA9eV4IEeAqSbIwwcWaAQiMxbEAQ8XYjEss8HAKAvAUbhcnjAEQJCRBX9BTDRo0CiRg1APIAK93z/e7EPAAD4c8r/W5QBopYC1JAHWCQP5AEIWsDqkA3v3zgXqQ5iJNINfOFboA+9o9GXILK+rnckJQfobDKsAT7URhDlpC601cG3FDIgBGUAne4Mt4Jztv4GrFhqIgNwqPNHtJGhvtVfR40A+qiSZkUD1iRCcAA0qjQrMSXQQsco8gBEbnUBX4ICKHz/Cg6wxFWIbAAipbjtSD1fpJLUb6uTXObjOOY4cEP4FqRCtjR7vE1Q2rkuK21oStLPjtpKM+VIRYKKIrd772/otQ9aMvQoOYLRKE/QG6AweXsNsuGClJC/gSPojKAYNKUnXqKGA10Ar1l2lU5iPp2ezyLOmsiJQYDBt4WjGaElZ7sLrSd78AORJHjaAqzKh6jSoB5SFoIEmxRY2tfpBKYqhDlZJO63iHCROXvAoW3MbmQzk4zginAKWA6jnRQBgKvPYdnCUpVxM11bzVk5fBtwHMT1fRyC49hxKKOf4yhkQTmOyhiO2TTncex2CerL5dDmTVlCJDlT0EgUDeCCcldBRCjn0/xLMyxbAjU41cMUWJpikHATLU2fztrq2gU8hT8yLCalzK6TwaGlcgDDpMw76yrSLFg5jt/UNzV5E0nHlRUN4irTDFMZMjwKC3C+ugwQZB7/eLo0cwFMSX6NGIQFCjNbm1o2CaepQyCiLltIlxlUM1Vcl1Fusc8r3xiC5XPMTTmAB0NAYaqBk4Aw/yMF7ry0imuz1DnwKB0/8HnyM5odA/IyVdTAgC85II8nBgZg/NS5g8LKM2QJPVf1Nae1RDAzYxBCpZlcxVDhbDWeqQeGMWCAE8NDBG09EEOSRzr83F4Y5GunHKwAv6mjlwGYERhdUALBOZpFYXDcfNnYgmM+4ylDWEwpmsGcV6GZMyXGkQU4hOw4mdf9oKGGnqfjleqU3l+EcYH13gEGk7NzcHJx8/Dy8QsIFszvrhFRMXEJSSlpGVk5eYO4stZiPwbqU8evVymUKrVGq9MbjCYzymJjHJxL8PgCoUgskcrkRsYmpmbmCqVKrdHq9Nas27Bpy7Ydu4YS51frOQV9OAKJQmOwODwhrYVEplBpdAaTxeZweXyBUCSWSGVyhVKlpq6hqaWto6unb2BoZGxiamZuYWllbWNrZ+/gaLXZHU6X2+P1+T0thGAEEoXGYHF4ApFEplBpdAaTxeZweXyBUCSWSGVyhVKl1mTS6vQGo8lssdrsDidBUjTDcrwgSrKiarphszucLrfHa8Ly+ZHhzwQ6BAk/FAIrf6lmoE+jOQxHRR6xO80YTQ7YEUIe2iFcM7JvTibKwj8IfrCiiOaXb2mNW6/B366zHKlUbCYgrLSJzQJImVQ2LpswoYwLqbSxcTmpYnKhTPMEuCh/b0awreLa3+NBonzS/w8weHKbCySH16hTRyFMvFX93pOgvShw59MOOtbOoZcFNaLSxuVDhCmLTlXGhbZxaQARpkwbG5cOkAup4jJAKmPjMgFyIbWxnfVyETDxuFQXfV+J2gdHtLHcBiGVNjYuByDChDIupLJxuQAp40IqXZ4Jnyf4vbT/TRshhNT8XeEKlaTlMXmFV3jj74MuFhDxdMcdgvHvZ1dvkbq0Wi42xupMTIgZ937nFN6WIasxqUk0YSvfqrTpWJf9gTIupIqeRBAxKVUpSSnpKE8autN3Io/rIVCDcaGNjUsESBkX8r/aamA5BSbtuYchXJ53CL68+BfNhwHdeARAEDC/OsqiQsZwhoeeY8YzqN40YFIfVwL8ehqgAJgOy+GQLfeQfijXWqgs+W2cwsCwWe8gz/2w7/jOGawHQIp7pSJrd7P67iiHCjkBp+D7gUOUZYeR5+FRaiUNmEw=) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(data:font/woff2;base64,d09GMgABAAAAABsAAA4AAAAAMGwAABqtAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAARAgsCZwMEQgKszSpDQE2AiQDfAtAAAQgBYkeB2gMgScbEypFRoWNAyACZ5bg/0uCNkYI5h+2VRUoeEajERW2lYBwWNviN1V2EP6v4zgONhql3j2nIyu3GtF8X/kCJazh0Hqc+zSfiSiwoxQjJJkdnrb575KjjyOOMI4oCQETY0SqWFg9rNrMxd+cm5vL/LUsf6U/K/e/OlP/dZNWybIDaMc+ApatAsJ6tyav2wFPBZwAUbHbqTz//ZGe/+5PMDGOZqJLaw1IsVPhglVkW4028b775t5+7O6rCxyDv1Omr8ZUnjCZd27GbjKBj1CCFIA32QNVeD51JRdVwgp85Yd37AAdOKSlQ5CC0q1vi/qK2l315GML3fybt5AdMa2I0LppovMnoZRuyfKn+JaJ00/09h+IdlhGoMXZrbuvQRAsDYjJ2fn/n+vT3vuSoRQAl1zRozAVusa8uZlMH2TmTDL/dwNLyRJl8v9+SHEZVMGj3LOqNLNI6lduezwJXe0rZJ1fYctC2so+hqongMmnhqft9rtXTaRruUqQIZj5FrPjq3d9ARy67oMB8M11JwDbeqFvFQMGjxsJz58B4AfSt9/5FhWXoU7gxTiKMJ2rMVEnGWBnK2KVwHcANtcL7UlJp6GH21AOSTANW69sdPTaNQ3z5fqvH+k1Lpt6g7ug6x31YP9QDDi/7govrAKo8f0gsnoFBDer4He/PJkHmF7iWJqaZ2BP14QQkhjpkbEW0AAIaK8FkASiK4AAU8cFMNSqLUAg9SmyeWvCdRFTYnQZ3uy6rO8SmHsnAs+A2U016PVJC3c6jA715IEFZrcVbE2ePhBgqmvFaaMbnQIq/HFoZakTGNAeJJDSL3wWZI7hVI3UwqICmn+1prTE/qgPXrwF8bDz2w/1mqZeqbjWpHf3ydWsyn0JklWSakzV0fIYcyiHAyxmZCzNlvQGHBkNxdgojoZlQ3YQQxg5A7DD4wgQ94CSB2llYSpLyaecpStSRpbnucJkaQh1CkARAhAI3yEyqgsFGKQvoJ5qgcUtD8schDljbQCqBTjufIFYkVljJ0ZUtmvKoLN6nN8J2Mi6pk5pmP+rk5EdUw6at33cMmkneA02CN47vyhDj5PSsQpYgM1OC2C+gciyzakAjqcl4PEY6DuANVojNiTrgoCjK1SWiqcUbw2m3dg7nYyDlr0sgb57bSsIuOtXlMvzdn4oFBAA9Sj9RtukRt0HKmbWwhzgRS4A8aDbC7Ur/GM8dNMINnoLAiosc8pfFKS4Tm2N2vDaFgiR4U6hM54UkFhzIgi5clD9ZYW5AKnFuCZlAxEXbHROKVeoJBptQ8QoTbse3Al6Sgqrez6jP73wFP5ETwwQL9r2q7tPnrGrIZ5Zggyo7r6gujxQc4ii93LA7rLwGcmJQNsqIL7lJxzzoO6aLRI+IUdWx5s619gBXOf2azYg5BAy180NsLYpXJ41A3BDe0AELGmhlF6umRQTGVz2PJQ56SMwB9+AqpN4Kd+dFNHEjQnnHBEguWSgliWzwdbOwZNQIEMG0k4B3TMI5AwGBUNAyVBQMQySGA7JjAUpjIBUxp5ngcowELyu7nmVEuhjaHjb1mpUkZm/L7RVDObfYWMwwTrgLgJq50+k1oOJamRQg4paVNShoh4VDahoREUTKppRMQ11oR1SVS6k5yLyKt1UdJfbFKrYW7fjQEjE+sWMCpwQGYqoqaY5nfCbbqkMCeBqPKZZMF0a72g4RxlLW33/NoB1M3DadNuFmZBPAOZmYULtpMSOgCKnje+nQHYGNoXeN53LmWkL8mHJhlQyhBEAAvQLKshBeacqyL0mAeJobKIbGRbZMykxAmiHZtlys2VobEUSf2SW70RzbW5KWzdf1948AGTQl4f60zY/deaJHy47x43dRKomN71E/pCUfjvgRtMpEtpXy0TvjT9FdVZNtyLP7ge3cFdyKI3WMdBBpPcjMAhaeo1vpz4oY61gTPWWoExGUvF9g65hUpkalHWQ5ozC4eCbq8thtINGuVWqpAZUZOXTMU9g3iPQnAxziXTOQn1PTBGudgElUsxhLKtAqzlPpbALx04MMgUnMdtLbOyiGoRz4ynVvpRKrXn9SUl+LdTQw13cbvk3TIPNjTa9I5qy2m97PmwRnFd+vC9Tx3dNrgMvNE5kcn5qmn7L7AQvVSizI212Qi/2vXrHbO3c72OTHT93AORF4GwQ5EfD7NRqh9jkXtMDzhLHJIS6QNambhZgVYJnDgOq1HVVKil1Lk4+jMzpubt2S9f2r2LYzASN1tnHK50ztm2GbcgXIvNAXoccGRX5Pmz1jkCthwUWudL+91sw6OKGXc0evZLiZSXIAHr1yFmSGHB/QumJgKyvUsqg0TIk0nypsj3Etx65JV1EhQGqBaHGULotPmaQAtOC0GL02qckbUDHANwbIPQMlG/PYAGDhQwWMVQcGw9L6AZSC8KMoXJb/KwgBaUFYcXQdX5Yo8EaLdbosMYAawyxxghrjLFrJ5kWk3qq9WDNnKGg5ujbJ+SLFxm2sCzBWJUA69k03nFjDm7NcXeXkMMegcw8oGAekcNpNtkvnk3jxTReTePNMYR7CdCXIB+5EnyawZcZfJvBj6PgWwL8Slj4b6O5v3AzV3HVEwoLF77QqlXqhga/5SrR9YDozqa/tAfYAATqa4Q2VqrqEsdgAOIe4LzYVFEPngQwBTs7ZyVoJ7BmffUDBOgMrDo3Dqq6sn44Wqz1RhJ1xU566XCLryarbbRykItx0VPuyQ3Yxd8Ad74zJyN+JImvSiM9Ys3w9IdNC5JYgPxzcyN4S+wNW67Xkfq+WKrGWOjHxISiejg70COSgO/Bums90UcIEbzAnpFMlq0zZHyz32ZneWS872ihMA52tRCgaKdPfiME4GO8KxDgxWeuM00M8By/XCqhRd/MqhEgbKSRz7NmhhGgiQPeO0GIszl8aMs37M8WsTVEjxTtqzPh8Gy4eRjbsSLE3SI09UBCgJ73fHBmQHVNV5T8L+C1YMiaTAGhPHlEhilK4RfsxivCLR3Fm5BV11LQt7cykwlsoSjUrgGmdgrnNICs5ahPyz+r1fHLVizQulvG6SMFgxuoP42+msrU7ZsRhRhP+VK0cwY18SScUt2zA7Tj1pCnQR3NbXLOoIb4rDQBVh9dZ5i3IDxqupFMciu4fGikzDaqAj/y1NZibI7tTbgAyytdgcNNl2OJoknyPApRulb4uZ4U5xl9sck66iG+I72HilS6I0BewWBPp5r7H5UsqkNb0KzezvQt6ke0eDJNJDdlaQCwo2vF0wjuX1jwRp2N5wC19dnqgpV9nqXq0riAoDyirLiJUYO4kaaE4jzAnzq2CapHA3srPhZHags/SRo+kDA6t0ok5RyOZxgX1/Q5oYXtSr7TR+3osupu3x3H0q6mrkdkIE2Xh1FETz+0pb9IRs0+URzTEfi2+rQ8ahenieav9nGYxxRt0yyZc7QInrC2qEwAVrwdQjsqkcbDnWuWVI+UmTB5Sy0zO5VWOKdwG5EZdu77qcaTZSGvj8YnWp3pS1N0gfPV2kuqOaMlFbk7YB1CNodrQzxQvCiSRs7KVtrIhwrX3wR32qp6Q/hU5fiWYlseuXmNw8MQrTPcW9QKO2uCcxAb1AR8JI1MuWkw5+RT/LMQtBn5wJkLN6L+F4nGPU7tnen3Z2Yb00zaSqwJMBG0UD9pNmsbhbBw3yu8Z/p4cO87up9DodwiFAV/1B/0kS+ZNgIOwATn/iqpvsBUGEJFo2+kLzSgkIimIFR4bMilAxdj43AdzSGTPCxB/2m7Lf2j415BapsAJgYhMLpfHNHNbsSXA0ni5fnFZi3JFL4HMu3wNtz8GfH/W1I87rWfueGBq9ZNsdDnlsfVHjnHAvmzytbCu1lnxjbSDKBVex/6sORpBeiqMXl7boECSVaenxoqoNjn3MN2RXFDZ309uvCK2pVaXD9VtumBSkr7T1ViFggXKGMIg/Vps0I76qlDD6AOacOaEYst2mGizeKKaZZbQes27eAWKeeS2ltXSocfK0y0UAvcqRqhGgSoFIsrnEhtoWkxNVPNlrC44YpQ02o4BSic8YrG9VgI1kz4/2khxt+MYLG2qhdaEGaOyXtLv3AMI7Y6NXnLNDIq8XHr+kAN9baMRPFGesFF6d20Rb2ymm8FzqKwBV5CFEJoqkUfQjVy4T8wF4qq+077v1WFMbsZuDsVOlGeoXxRTetnzp3nz6uet/HlWtQTPmtgO9ko3JIxBqrsp3OAqkVp4ulSUWYHX+WPOib5RO423Le2kQdxhuR7LVYf4cw3N9LiAxBqALF/3nDHKMmGwbpHl77ZaG6JZfSDuq5a4M/Fjovzfs+NTMMMyeNPeKy0PbmcrwNOs2iqtDCWwaj/EbuixigV4bc3xDg/ifNPrN69xOkUDJLBtAi+kzDA+0pg1TN4on73vqBI7rcl8Q1UwdGK8yBZn3gKdysIXa8Qq/PdKKqRAzy/rWhUNHjBBa8IVQtDLGhGBVepdqXLOojQeeFFB6QA3zEuW3CHs7m/ogEd9neS58cc4g36RkWIIu8N8c2eZ0Frn8WzH14osMheehJ9rW4vQn9xqj9o4tosHsPR4gujnFxm65V6P6wVtrluTydfI2fD88vQwl8jE+lxVW5Kv+Mf3Uv/Kn7ymYksepj6XumEzM+TcLoWXGC7w/S1TbkDfJkRhlwDcow83zmz+67JVyLJPE7uvjcfg48ivHkqaUbiFYTJsjsG2eiqO2a4f7BVzz4cTEkG7pd30omq3btA7lLz1F11tI1WlTRinGZkA4Ggwq8qdxL5D9BUKidMZnRp+htXC34Sj75/Y2GWOrjm1Pp4IOaOJrtv762a44/KipTPymBEGLzXz/0kd3Y02BcqJ/azZJQwdP/rnLVp8qdU6k/KTma2L6hGVAOuOvvIgC+JIm61xRQ9xnOy80akaYOSppL+u2M+MCvDTfeoxFzD9n1tBR1EO9U3sW4wRSuYjHZve+AbiXN3yudOuzju1xZdkvkYpUyCz9zUKxXqjInCcKRWuEIsHvDmfuEtRCF84HMubtg38Ydzff2HvHc4bEOcElUVZH3uN6TSFKL4oLoit966kgUFgFIRBrBL9Fa5tSK7ZSR6buhN7q4G88YriAgD8CiL/rL9g/Uwds9EcYlLXncfoblHJSKfzdgZK+Uc1dgeX57SIPIo+ieqXMc0vr353vufn/cG8AoCyD3RnSY+PfvHZCVXLsAuo5LfDhjdG6aMSUFtqSxNRuE56+BDn74UQxaw1QjbVpPuNhe98z1+iEuV333ANZzzfX8oy0vKXiqWHCZyyrLUWIXDL+oG53WY+FlTY/xW3YLn0HsozXmK4C6we3aXwszf/7CH2ni4eMJn+5TasBdjtVvqEQtVpu+Xvsamdv4VNuICp+AnaYc0DiLpyqFZJladKNIsvqpquRi1QSoRpurbmjpQPnd90BXjHjVGfBz/0v1sIaUZWMbLmH9ZXQ209aXnBhl7y9B4q0ot6Jg+0ZHZlbsM4+4iap8cY0Tj+feHLsppSkAtdsG4+QEZxX4ts+xC1wCLpM2ISBHGI3TTADQ0nBZ87eCjEZNKTqEX0nqiXwnKBfE0k5nzYWUY96uVMolmT7l7GlF/cdoOcxG8VdHdCy9/1REH7beltlx5ofjqPy8apen4n0yFskIWgSG3+0u2+GjeuqNKSFXA9+IlKAe2WLObzv4dTcNzfpaLULrrE28kuYRZBUNShzUv6da3CNbqRyofD4EQ9/qQcsBy1Ve+uRt0z9+lUVII/VhbcEvV0YfBn/NWHtl5Pk/my3WXpj2g3/nsVkt9FXvDG2/K8CfWYFmoqy6vUI6lpHr3Gg+ink+b2g9nFGwU9JdV9OE+tZIWYT5VeTinOtSb8l+CXD8b/VotkJteOlrRbTc2G5rNFwQphf0r8mvN5bn8WFI0oVRd//+3GTTekTwc/5M/N+efNUk5/gRNLZV2qjb5b02uPHE6ZP1JRRIt4fOWS8putFVww+lzK1VSlsmys7JZWflq66c1l4pOXqSlYumKq5HyHeV1zrthEtNbH8ydfpmrECo+U9+avzy0p2yYk0KlbytpW/0VT6y9/rXEMdEs8aFMxMre/drbJzJkja99mL6npHHJzvIw5vSlCc2K5vnqLL2MRDSo8oqSxb/33TRvu/GUIjHYlDK6SlzGbV9sqHcbZCRC/7mlKcyd0bqreFPUR+QT9+BVBkuFDJvsyljcfyJ/v+cmyQk3Mhm0aQTznsIfoqc0IRjprqncvOaxxYOkeplUJ4r/oNIUZ/cV8ODr52ZUFF+XserxmloxW1xp69iVv0p6FkG/ej9UePaXd3Y+OUP44vR/qVH7oGW7t0Y7F7ohdLNfbRRfjY3m4PYtIrPwehbTk3eL6G7Wtk+Pp7KW1UgKxO5LjU8aa2+48UUwICj3w/A7hpWwNVCestMk12u1IXmcH0SJ85J71QOe5zNfvBcABEG9oXQt1xV/OctvLl8yWf2OO5055j2ftz8sPi7QoI8kq1aL7uXiN99XyZGLcMOzN313Pq+USKB8dLbJf6Q6aV+3eMulCaw2PlImeeovfHtsz71PaRRiDN7+jaNkT2eMR8lTfikWVq28y1ylK960rtYykT+VIqrjTw+T1S1M9m/K1oNnezMAfs5PU9jv0zKZTgQZKlfcf41GTSlT42T56z75SkXTYzvGFAFBJm8adq1ehQX0dw1eW8ZHIZqL8paZj93+k3Mtq3nJ45hIKHuLyHlPSZFd75TTAfyXZOlPIV59e0nWFtfKTbXTpfNcGPLiH6KmiSpx99q2Sl2Rtb451hhdnaGJSLqS/MqIhl4Rdah5X3AwWFLal/3XuVGNdlcRa5WhXvXl3TNqEZ4zW/vEshf/50xPllUQfTi/bWyqtbChuKTn+lRBsKIsgLKy8HvIJBF+dopDSTgY9CNWxdLMA/29AvHmKMJlLWy189/RZKnyqV05/nbTY30L3wxlGYv/XkZYh1+zyilE2nb65u05S6SzsZPFar+pnPXblxt/kopY+vW1T1SOrsY/T9Gl+9ZNylBYLHkw9pSmiftZwIA/rVamCq7/+OaEgS+Q9kTmqvIWle+dkaSY/u7XhWSxgtO0mC3serOkZFWdtTXTRywfQTnypftDNihJhDox+tlQJs+u4NZd0yg/+/jmlh+mzGsfsxQ0jZQbuzNnfdyRZYMZynd10SplD17wHC3CTeJY15Ljfv5H9SBRD+Ze/qySI6eUs0eDLNiBQCSOMQpGmHA87Hqapss1of09Mr+OkovpGXVEHBi+HYo9+9mqcsy0p+etLNxodFFJ62LWUhZJFeYYk8KbUiPZ1726LjX7sFNO1pZm3PupeyR3+/nzn0cMKlpEM5FhiW1Gt/fbMrJ/1XjX/WPhOR/D+HMl+qCiIx6v3rNuWvJx5sD3zfYCg33Q1PR9JyUhhW7cGVOIJQ/Sy6QVqD1UI1m8DjRDyftG4n2zr+pZaS5Krk1eJbqHS7gD5QUp6x2P//9ad02pTcmisvMa4vliVldoFJe3ymPZJufWlkLy3Sy7Mlmg6bm/dmJb22FzAIE6ILoo08WDTgMY3u9ufpP5zC39aGJjVvc7nUYOK303rVNroqalvI+cxXlkKifmaC+7/sztgegdjyX25/GfynvUsBaH3rwBf/WTjw8kMIlegJHFx1M7/cd0xN04kS4Tyf+61JxPcK+OOZ+6CPPXo1DUXJ8rrEVJKx+Hp2IOffJRpaKEpQrkHKx9EYNE56GGuzTshFQtF0ummGLOUb2uY0B/Yg1RQeWwOXhp+ngguRaVfOIjhRngng4xW+WX06Wmv2KeF8dfr4ZQ3ItFq9eT55XsuSo8mianyNrSokZ5ZrMsa8zaTN1ExDUEBIee7x2yjV9mJ09oOGcEqreKGE7GfzvktOF965FNN42s29ze4hu6RZgVKbyUwdIMSQTh04sPqQlmf2FgYbgaEwuJa2ydq7Oae6ABHypcixbTCiLjSB8HJ+UkbsQfaouNchTJD6IKXeAnRCbiXa5q6WytYVAuBuYe58F0QpPCIhOL8kB1bMfI47vaX4bVpvjg9Y3ZqTSJpUlLd66uFDzkMX+LJmffyltQiLgPPmfePTI7PJf+Ic7Hi9Y2ZnT4fZveqYNxAAC4vSyQDGCX5VaGC3U1CXvh7fnZ6j0rlfBdHGUFGe16tRx8v8Dgcr/HTBMWBawWkRXTdfMhnze4VFYebaUCq8Jg2UjLzfLT8JMVgK183HJgtbgyBRAAn/v+cPw3aZuw4DdCiHyZ14DV+hsXz49x7bNuxopaSaLAv8o0HLnMzQUaF0tD1f9ftLP+ZkWqv7lUDdrD31NEbhnrW051kWQ1SbXRx46s81x5B39es/1ZCMhKD3MkzIulDDnXXybLkzSSXDCd99G6i6I2MNQz/Xs9MuZuijjl1h90cbH7GwBQJrePgu2z2+S2L1KueGAzmW05BDTZFY47umkQjePTYIRHpyFinp2Gsg75NIx/BLfF96fxa/nYOFWBzPwpYMSoFZbr06PXOIZJgrkPXRwsdrO9SSeNZAR1GXORw4hVvGCXTHTSauii00ez40S4xykTc2VJVHd4R1/YoZOWD1mhRMLqx+q1CehBv7ze1mFU9p3L/UYMzslwf8ewcbk8qrsianv+HzUP47Fte9hyLrI2rpeTY4yETnYu8wU5fsjuWTlR9Ih7a5gOPWl9ZOayy2AWY09ZH8hfXGDq03K7IR0l7NXfy2m5QddvZAmzbBuK3Bqw3q7jfv0MpJXiKX35xYw4PKJVTnbzOzHizswo02Fo8wWlRuSkq7Xbj3mTVQJ2y6kDl7uMpa10gkocw06c0J05aSZL3eUlLBIrUblRl/UjbB/zhNZNaBDXnuHG4y9ndJjM3JKDHS4l9R6adEfk2KdSx2uchyMZJzlaZTqLxySklKPIq7Rz8tkk/shPVy4s5tqFbV7zWs+lnfrf0ldNR8/AyMQsjYWVjV16C33fLhkyZcmWI5dbnnwFCgfMmrjqczh8DlDyAZ6M+wUI2nG6Cv2dn14vANZfsr60C6ueeSjEubuBquXE9gvx+iQXBFsywRk/AQIEH3Vj/rz+qne+Xg+dzBTdRYA7MkxPqgXjQ+YeYgY0428kqg1oc6pbBSgkSPuArs09zO2PdeWfsc4lCO4A) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(data:font/woff2;base64,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) format("woff2");font-weight:400;font-style:normal}.katex{font: 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:""}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;display:inline-block;white-space:nowrap;width:min-content}.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathsfit,.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed;border-collapse:collapse}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .vbox{display:inline-flex;flex-direction:column;align-items:baseline}.katex .hbox{display:inline-flex;flex-direction:row;width:100%}.katex .thinbox{display:inline-flex;flex-direction:row;width:0;max-width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline,.katex .hdashline,.katex .rule{min-height:1px}.katex .mspace{display:inline-block}.katex .llap,.katex .rlap,.katex .clap{width:0;position:relative}.katex .llap>.inner,.katex .rlap>.inner,.katex .clap>.inner{position:absolute}.katex .llap>.fix,.katex .rlap>.fix,.katex .clap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .rlap>.inner,.katex .clap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:solid 0;position:relative}.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .sizing.reset-size1.size1,.katex .fontsize-ensurer.reset-size1.size1{font-size:1em}.katex .sizing.reset-size1.size2,.katex .fontsize-ensurer.reset-size1.size2{font-size:1.2em}.katex .sizing.reset-size1.size3,.katex .fontsize-ensurer.reset-size1.size3{font-size:1.4em}.katex .sizing.reset-size1.size4,.katex .fontsize-ensurer.reset-size1.size4{font-size:1.6em}.katex .sizing.reset-size1.size5,.katex .fontsize-ensurer.reset-size1.size5{font-size:1.8em}.katex .sizing.reset-size1.size6,.katex .fontsize-ensurer.reset-size1.size6{font-size:2em}.katex .sizing.reset-size1.size7,.katex .fontsize-ensurer.reset-size1.size7{font-size:2.4em}.katex .sizing.reset-size1.size8,.katex .fontsize-ensurer.reset-size1.size8{font-size:2.88em}.katex .sizing.reset-size1.size9,.katex .fontsize-ensurer.reset-size1.size9{font-size:3.456em}.katex .sizing.reset-size1.size10,.katex .fontsize-ensurer.reset-size1.size10{font-size:4.148em}.katex .sizing.reset-size1.size11,.katex .fontsize-ensurer.reset-size1.size11{font-size:4.976em}.katex .sizing.reset-size2.size1,.katex .fontsize-ensurer.reset-size2.size1{font-size:.8333333333em}.katex .sizing.reset-size2.size2,.katex .fontsize-ensurer.reset-size2.size2{font-size:1em}.katex .sizing.reset-size2.size3,.katex .fontsize-ensurer.reset-size2.size3{font-size:1.1666666667em}.katex .sizing.reset-size2.size4,.katex .fontsize-ensurer.reset-size2.size4{font-size:1.3333333333em}.katex .sizing.reset-size2.size5,.katex .fontsize-ensurer.reset-size2.size5{font-size:1.5em}.katex .sizing.reset-size2.size6,.katex .fontsize-ensurer.reset-size2.size6{font-size:1.6666666667em}.katex .sizing.reset-size2.size7,.katex .fontsize-ensurer.reset-size2.size7{font-size:2em}.katex .sizing.reset-size2.size8,.katex .fontsize-ensurer.reset-size2.size8{font-size:2.4em}.katex .sizing.reset-size2.size9,.katex .fontsize-ensurer.reset-size2.size9{font-size:2.88em}.katex .sizing.reset-size2.size10,.katex .fontsize-ensurer.reset-size2.size10{font-size:3.4566666667em}.katex .sizing.reset-size2.size11,.katex .fontsize-ensurer.reset-size2.size11{font-size:4.1466666667em}.katex .sizing.reset-size3.size1,.katex .fontsize-ensurer.reset-size3.size1{font-size:.7142857143em}.katex .sizing.reset-size3.size2,.katex .fontsize-ensurer.reset-size3.size2{font-size:.8571428571em}.katex .sizing.reset-size3.size3,.katex .fontsize-ensurer.reset-size3.size3{font-size:1em}.katex .sizing.reset-size3.size4,.katex .fontsize-ensurer.reset-size3.size4{font-size:1.1428571429em}.katex .sizing.reset-size3.size5,.katex .fontsize-ensurer.reset-size3.size5{font-size:1.2857142857em}.katex .sizing.reset-size3.size6,.katex .fontsize-ensurer.reset-size3.size6{font-size:1.4285714286em}.katex .sizing.reset-size3.size7,.katex .fontsize-ensurer.reset-size3.size7{font-size:1.7142857143em}.katex .sizing.reset-size3.size8,.katex .fontsize-ensurer.reset-size3.size8{font-size:2.0571428571em}.katex .sizing.reset-size3.size9,.katex .fontsize-ensurer.reset-size3.size9{font-size:2.4685714286em}.katex .sizing.reset-size3.size10,.katex .fontsize-ensurer.reset-size3.size10{font-size:2.9628571429em}.katex .sizing.reset-size3.size11,.katex .fontsize-ensurer.reset-size3.size11{font-size:3.5542857143em}.katex .sizing.reset-size4.size1,.katex .fontsize-ensurer.reset-size4.size1{font-size:.625em}.katex .sizing.reset-size4.size2,.katex .fontsize-ensurer.reset-size4.size2{font-size:.75em}.katex .sizing.reset-size4.size3,.katex .fontsize-ensurer.reset-size4.size3{font-size:.875em}.katex .sizing.reset-size4.size4,.katex .fontsize-ensurer.reset-size4.size4{font-size:1em}.katex .sizing.reset-size4.size5,.katex .fontsize-ensurer.reset-size4.size5{font-size:1.125em}.katex .sizing.reset-size4.size6,.katex .fontsize-ensurer.reset-size4.size6{font-size:1.25em}.katex .sizing.reset-size4.size7,.katex .fontsize-ensurer.reset-size4.size7{font-size:1.5em}.katex .sizing.reset-size4.size8,.katex .fontsize-ensurer.reset-size4.size8{font-size:1.8em}.katex .sizing.reset-size4.size9,.katex .fontsize-ensurer.reset-size4.size9{font-size:2.16em}.katex .sizing.reset-size4.size10,.katex .fontsize-ensurer.reset-size4.size10{font-size:2.5925em}.katex .sizing.reset-size4.size11,.katex .fontsize-ensurer.reset-size4.size11{font-size:3.11em}.katex .sizing.reset-size5.size1,.katex .fontsize-ensurer.reset-size5.size1{font-size:.5555555556em}.katex .sizing.reset-size5.size2,.katex .fontsize-ensurer.reset-size5.size2{font-size:.6666666667em}.katex .sizing.reset-size5.size3,.katex .fontsize-ensurer.reset-size5.size3{font-size:.7777777778em}.katex .sizing.reset-size5.size4,.katex .fontsize-ensurer.reset-size5.size4{font-size:.8888888889em}.katex .sizing.reset-size5.size5,.katex .fontsize-ensurer.reset-size5.size5{font-size:1em}.katex .sizing.reset-size5.size6,.katex .fontsize-ensurer.reset-size5.size6{font-size:1.1111111111em}.katex .sizing.reset-size5.size7,.katex .fontsize-ensurer.reset-size5.size7{font-size:1.3333333333em}.katex .sizing.reset-size5.size8,.katex .fontsize-ensurer.reset-size5.size8{font-size:1.6em}.katex .sizing.reset-size5.size9,.katex .fontsize-ensurer.reset-size5.size9{font-size:1.92em}.katex .sizing.reset-size5.size10,.katex .fontsize-ensurer.reset-size5.size10{font-size:2.3044444444em}.katex .sizing.reset-size5.size11,.katex .fontsize-ensurer.reset-size5.size11{font-size:2.7644444444em}.katex .sizing.reset-size6.size1,.katex .fontsize-ensurer.reset-size6.size1{font-size:.5em}.katex .sizing.reset-size6.size2,.katex .fontsize-ensurer.reset-size6.size2{font-size:.6em}.katex .sizing.reset-size6.size3,.katex .fontsize-ensurer.reset-size6.size3{font-size:.7em}.katex .sizing.reset-size6.size4,.katex .fontsize-ensurer.reset-size6.size4{font-size:.8em}.katex .sizing.reset-size6.size5,.katex .fontsize-ensurer.reset-size6.size5{font-size:.9em}.katex .sizing.reset-size6.size6,.katex .fontsize-ensurer.reset-size6.size6{font-size:1em}.katex .sizing.reset-size6.size7,.katex .fontsize-ensurer.reset-size6.size7{font-size:1.2em}.katex .sizing.reset-size6.size8,.katex .fontsize-ensurer.reset-size6.size8{font-size:1.44em}.katex .sizing.reset-size6.size9,.katex .fontsize-ensurer.reset-size6.size9{font-size:1.728em}.katex .sizing.reset-size6.size10,.katex .fontsize-ensurer.reset-size6.size10{font-size:2.074em}.katex .sizing.reset-size6.size11,.katex .fontsize-ensurer.reset-size6.size11{font-size:2.488em}.katex .sizing.reset-size7.size1,.katex .fontsize-ensurer.reset-size7.size1{font-size:.4166666667em}.katex .sizing.reset-size7.size2,.katex .fontsize-ensurer.reset-size7.size2{font-size:.5em}.katex .sizing.reset-size7.size3,.katex .fontsize-ensurer.reset-size7.size3{font-size:.5833333333em}.katex .sizing.reset-size7.size4,.katex .fontsize-ensurer.reset-size7.size4{font-size:.6666666667em}.katex .sizing.reset-size7.size5,.katex .fontsize-ensurer.reset-size7.size5{font-size:.75em}.katex .sizing.reset-size7.size6,.katex .fontsize-ensurer.reset-size7.size6{font-size:.8333333333em}.katex .sizing.reset-size7.size7,.katex .fontsize-ensurer.reset-size7.size7{font-size:1em}.katex .sizing.reset-size7.size8,.katex .fontsize-ensurer.reset-size7.size8{font-size:1.2em}.katex .sizing.reset-size7.size9,.katex .fontsize-ensurer.reset-size7.size9{font-size:1.44em}.katex .sizing.reset-size7.size10,.katex .fontsize-ensurer.reset-size7.size10{font-size:1.7283333333em}.katex .sizing.reset-size7.size11,.katex .fontsize-ensurer.reset-size7.size11{font-size:2.0733333333em}.katex .sizing.reset-size8.size1,.katex .fontsize-ensurer.reset-size8.size1{font-size:.3472222222em}.katex .sizing.reset-size8.size2,.katex .fontsize-ensurer.reset-size8.size2{font-size:.4166666667em}.katex .sizing.reset-size8.size3,.katex .fontsize-ensurer.reset-size8.size3{font-size:.4861111111em}.katex .sizing.reset-size8.size4,.katex .fontsize-ensurer.reset-size8.size4{font-size:.5555555556em}.katex .sizing.reset-size8.size5,.katex .fontsize-ensurer.reset-size8.size5{font-size:.625em}.katex .sizing.reset-size8.size6,.katex .fontsize-ensurer.reset-size8.size6{font-size:.6944444444em}.katex .sizing.reset-size8.size7,.katex .fontsize-ensurer.reset-size8.size7{font-size:.8333333333em}.katex .sizing.reset-size8.size8,.katex .fontsize-ensurer.reset-size8.size8{font-size:1em}.katex .sizing.reset-size8.size9,.katex .fontsize-ensurer.reset-size8.size9{font-size:1.2em}.katex .sizing.reset-size8.size10,.katex .fontsize-ensurer.reset-size8.size10{font-size:1.4402777778em}.katex .sizing.reset-size8.size11,.katex .fontsize-ensurer.reset-size8.size11{font-size:1.7277777778em}.katex .sizing.reset-size9.size1,.katex .fontsize-ensurer.reset-size9.size1{font-size:.2893518519em}.katex .sizing.reset-size9.size2,.katex .fontsize-ensurer.reset-size9.size2{font-size:.3472222222em}.katex .sizing.reset-size9.size3,.katex .fontsize-ensurer.reset-size9.size3{font-size:.4050925926em}.katex .sizing.reset-size9.size4,.katex .fontsize-ensurer.reset-size9.size4{font-size:.462962963em}.katex .sizing.reset-size9.size5,.katex .fontsize-ensurer.reset-size9.size5{font-size:.5208333333em}.katex .sizing.reset-size9.size6,.katex .fontsize-ensurer.reset-size9.size6{font-size:.5787037037em}.katex .sizing.reset-size9.size7,.katex .fontsize-ensurer.reset-size9.size7{font-size:.6944444444em}.katex .sizing.reset-size9.size8,.katex .fontsize-ensurer.reset-size9.size8{font-size:.8333333333em}.katex .sizing.reset-size9.size9,.katex .fontsize-ensurer.reset-size9.size9{font-size:1em}.katex .sizing.reset-size9.size10,.katex .fontsize-ensurer.reset-size9.size10{font-size:1.2002314815em}.katex .sizing.reset-size9.size11,.katex .fontsize-ensurer.reset-size9.size11{font-size:1.4398148148em}.katex .sizing.reset-size10.size1,.katex .fontsize-ensurer.reset-size10.size1{font-size:.2410800386em}.katex .sizing.reset-size10.size2,.katex .fontsize-ensurer.reset-size10.size2{font-size:.2892960463em}.katex .sizing.reset-size10.size3,.katex .fontsize-ensurer.reset-size10.size3{font-size:.337512054em}.katex .sizing.reset-size10.size4,.katex .fontsize-ensurer.reset-size10.size4{font-size:.3857280617em}.katex .sizing.reset-size10.size5,.katex .fontsize-ensurer.reset-size10.size5{font-size:.4339440694em}.katex .sizing.reset-size10.size6,.katex .fontsize-ensurer.reset-size10.size6{font-size:.4821600771em}.katex .sizing.reset-size10.size7,.katex .fontsize-ensurer.reset-size10.size7{font-size:.5785920926em}.katex .sizing.reset-size10.size8,.katex .fontsize-ensurer.reset-size10.size8{font-size:.6943105111em}.katex .sizing.reset-size10.size9,.katex .fontsize-ensurer.reset-size10.size9{font-size:.8331726133em}.katex .sizing.reset-size10.size10,.katex .fontsize-ensurer.reset-size10.size10{font-size:1em}.katex .sizing.reset-size10.size11,.katex .fontsize-ensurer.reset-size10.size11{font-size:1.1996142719em}.katex .sizing.reset-size11.size1,.katex .fontsize-ensurer.reset-size11.size1{font-size:.2009646302em}.katex .sizing.reset-size11.size2,.katex .fontsize-ensurer.reset-size11.size2{font-size:.2411575563em}.katex .sizing.reset-size11.size3,.katex .fontsize-ensurer.reset-size11.size3{font-size:.2813504823em}.katex .sizing.reset-size11.size4,.katex .fontsize-ensurer.reset-size11.size4{font-size:.3215434084em}.katex .sizing.reset-size11.size5,.katex .fontsize-ensurer.reset-size11.size5{font-size:.3617363344em}.katex .sizing.reset-size11.size6,.katex .fontsize-ensurer.reset-size11.size6{font-size:.4019292605em}.katex .sizing.reset-size11.size7,.katex .fontsize-ensurer.reset-size11.size7{font-size:.4823151125em}.katex .sizing.reset-size11.size8,.katex .fontsize-ensurer.reset-size11.size8{font-size:.578778135em}.katex .sizing.reset-size11.size9,.katex .fontsize-ensurer.reset-size11.size9{font-size:.6945337621em}.katex .sizing.reset-size11.size10,.katex .fontsize-ensurer.reset-size11.size10{font-size:.8336012862em}.katex .sizing.reset-size11.size11,.katex .fontsize-ensurer.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:before,.katex .stretchy:after{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .x-arrow,.katex .mover,.katex .munder{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{box-sizing:border-box;border-top:.049em solid;border-right:.049em solid;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{counter-increment:katexEqnNo;content:"(" counter(katexEqnNo) ")"}.katex .mml-eqn-num:before{counter-increment:mmlEqnNo;content:"(" counter(mmlEqnNo) ")"}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;position:absolute;left:calc(50% + .3em);text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left;padding-left:2em}body{counter-reset:katexEqnNo mmlEqnNo}.markdown-block--unstable.svelte-wjdxdt{display:contents}.streaming-code-block.svelte-wjdxdt .streaming-code-pre:where(.svelte-wjdxdt){background:transparent;padding:.5rem;margin:0;overflow-x:visible;border-radius:0;border:none;font-size:.875rem}div.svelte-wjdxdt p{margin-block:1rem;line-height:1.75}div.svelte-wjdxdt :is(h1,h2,h3,h4,h5,h6):first-child{margin-top:0}div.svelte-wjdxdt h1{font-size:1.875rem;font-weight:700;line-height:1.2;margin:1.5rem 0 .75rem}div.svelte-wjdxdt h2{font-size:1.5rem;font-weight:600;line-height:1.3;margin:1.25rem 0 .5rem}div.svelte-wjdxdt h3{font-size:1.25rem;font-weight:600;margin:1.5rem 0 .5rem;line-height:1.4}div.svelte-wjdxdt h4{font-size:1.125rem;font-weight:600;margin:.75rem 0 .25rem}div.svelte-wjdxdt h5{font-size:1rem;font-weight:600;margin:.5rem 0 .25rem}div.svelte-wjdxdt h6{font-size:.875rem;font-weight:600;margin:.5rem 0 .25rem}div.svelte-wjdxdt strong{font-weight:600}div.svelte-wjdxdt em{font-style:italic}div.svelte-wjdxdt del{text-decoration:line-through;opacity:.7}div.svelte-wjdxdt code:not(pre code){background:var(--muted);color:var(--muted-foreground);padding:.125rem .375rem;border-radius:.375rem;font-size:.875rem;font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace}div.svelte-wjdxdt pre{display:inline;margin:0!important;overflow:hidden!important;background:var(--muted);overflow-x:auto;border-radius:1rem;border:none;line-height:1!important}div.svelte-wjdxdt pre code{padding:0!important;display:inline!important}div.svelte-wjdxdt code{background:transparent;color:var(--code-foreground)}div.svelte-wjdxdt a{color:var(--primary);text-decoration:underline;text-underline-offset:2px;transition:color .2s ease;overflow-wrap:anywhere;word-break:break-all}div.svelte-wjdxdt a:hover{color:var(--primary)}div.svelte-wjdxdt ul{list-style-type:disc;margin-inline-start:1.5rem;margin-bottom:1rem}div.svelte-wjdxdt ol{list-style-type:decimal;margin-inline-start:1.5rem;margin-bottom:1rem}div.svelte-wjdxdt li{margin-bottom:.25rem;padding-inline-start:.5rem}div.svelte-wjdxdt li::marker{color:var(--muted-foreground)}div.svelte-wjdxdt ul ul{list-style-type:circle;margin-top:.25rem;margin-bottom:.25rem}div.svelte-wjdxdt ol ol{list-style-type:lower-alpha;margin-top:.25rem;margin-bottom:.25rem}div.svelte-wjdxdt .task-list-item{list-style:none;margin-inline-start:0;padding-inline-start:0}div.svelte-wjdxdt .task-list-item-checkbox{margin-right:.5rem;margin-top:.125rem}div.svelte-wjdxdt blockquote{border-left:4px solid var(--border);padding:.5rem 1rem;margin:1.5rem 0;font-style:italic;color:var(--muted-foreground);background:var(--muted);border-radius:0 .375rem .375rem 0}div.svelte-wjdxdt table{width:100%;margin:1.5rem 0;border-collapse:collapse;border:1px solid var(--border);border-radius:.375rem;overflow:hidden}div.svelte-wjdxdt th{background:hsl(var(--muted) / .3);border:1px solid var(--border);padding:.5rem .75rem;text-align:left;font-weight:600}div.svelte-wjdxdt td{border:1px solid var(--border);padding:.5rem .75rem}div.svelte-wjdxdt tr:nth-child(2n){background:hsl(var(--muted) / .1)}div.markdown-user-content.svelte-wjdxdt table,div.markdown-user-content.svelte-wjdxdt th,div.markdown-user-content.svelte-wjdxdt td,div.markdown-user-content.svelte-wjdxdt .table-wrapper{border-color:currentColor}div.svelte-wjdxdt hr{border:none;border-top:1px solid var(--border);margin:1.5rem 0}div.svelte-wjdxdt img{border-radius:.5rem;box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a;margin:1.5rem 0;max-width:100%;height:auto}div.svelte-wjdxdt .code-block-wrapper{margin:1.5rem 0;border-radius:.75rem;overflow:hidden;border:1px solid color-mix(in oklch,var(--border) 30%,transparent);background:var(--code-background);box-shadow:0 1px 2px #0000000d;min-height:var(--min-message-height);max-height:var(--max-message-height)}.dark div.svelte-wjdxdt .code-block-wrapper{border-color:color-mix(in oklch,var(--border) 20%,transparent)}div.svelte-wjdxdt .code-block-scroll-container,.streaming-code-scroll-container.svelte-wjdxdt{min-height:var(--min-message-height);max-height:var(--max-message-height);overflow-y:auto;overflow-x:auto;padding:3rem 1rem 1rem;line-height:1.3}.full-height-code-blocks.svelte-wjdxdt .code-block-wrapper{max-height:none}.full-height-code-blocks.svelte-wjdxdt .code-block-scroll-container,.full-height-code-blocks.svelte-wjdxdt .streaming-code-scroll-container:where(.svelte-wjdxdt){max-height:none;overflow-y:visible}div.svelte-wjdxdt .code-block-header{display:flex;justify-content:space-between;align-items:center;padding:.5rem 1rem 0;font-size:.875rem;position:absolute;top:0;left:0;right:0}div.svelte-wjdxdt .code-language{color:var(--color-foreground);font-weight:500;font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace;text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}div.svelte-wjdxdt .code-block-actions{display:flex;align-items:center;gap:.5rem}div.svelte-wjdxdt .copy-code-btn,div.svelte-wjdxdt .preview-code-btn{display:flex;align-items:center;justify-content:center;padding:0;background:transparent;color:var(--code-foreground);cursor:pointer;transition:all .2s ease}div.svelte-wjdxdt .copy-code-btn:hover,div.svelte-wjdxdt .preview-code-btn:hover{transform:scale(1.05)}div.svelte-wjdxdt .copy-code-btn:active,div.svelte-wjdxdt .preview-code-btn:active{transform:scale(.95)}div.svelte-wjdxdt .code-block-wrapper pre{background:transparent;margin:0;border-radius:0;border:none;font-size:.875rem}div.svelte-wjdxdt .mention{color:hsl(var(--primary));font-weight:500;text-decoration:none}div.svelte-wjdxdt .mention:hover{text-decoration:underline}div.svelte-wjdxdt .hashtag{color:hsl(var(--primary));font-weight:500;text-decoration:none}div.svelte-wjdxdt .hashtag:hover{text-decoration:underline}div.svelte-wjdxdt table{transition:all .2s ease}div.svelte-wjdxdt table:hover{box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a}div.svelte-wjdxdt th:hover,div.svelte-wjdxdt td:hover{background:var(--muted)}.markdown-user-content.svelte-wjdxdt a,.markdown-user-content.svelte-wjdxdt a:hover{color:inherit}.markdown-user-content.svelte-wjdxdt table:hover{box-shadow:none}.markdown-user-content.svelte-wjdxdt th:hover,.markdown-user-content.svelte-wjdxdt td:hover{background:inherit}div.svelte-wjdxdt blockquote{transition:all .2s ease;position:relative}div.svelte-wjdxdt blockquote:hover{border-left-width:6px;background:var(--muted);transform:translate(2px)}div.svelte-wjdxdt blockquote:before{content:'"';position:absolute;top:-.5rem;left:.5rem;font-size:3rem;color:var(--muted-foreground);font-family:serif;line-height:1}div.svelte-wjdxdt img{transition:all .3s ease;cursor:pointer}div.svelte-wjdxdt img:hover{transform:scale(1.02);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}div.svelte-wjdxdt .image-zoom-overlay{position:fixed;inset:0;background:#000c;display:flex;align-items:center;justify-content:center;z-index:1000;cursor:pointer}div.svelte-wjdxdt .image-zoom-overlay img{max-width:90vw;max-height:90vh;border-radius:.5rem;box-shadow:0 25px 50px -12px #00000040}div.svelte-wjdxdt hr{border:none;height:2px;background:linear-gradient(to right,transparent,var(--border),transparent);margin:2rem 0;position:relative}div.svelte-wjdxdt hr:after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:1rem;height:1rem;background:var(--border);border-radius:50%}div.svelte-wjdxdt .table-wrapper{overflow-x:auto;margin:1.5rem 0;border-radius:.5rem;border:1px solid var(--border)}div.svelte-wjdxdt .table-wrapper table{margin:0;border:none}@media(max-width:640px){div.svelte-wjdxdt h1{font-size:1.5rem}div.svelte-wjdxdt h2{font-size:1.25rem}div.svelte-wjdxdt h3{font-size:1.125rem}div.svelte-wjdxdt table{font-size:.875rem}div.svelte-wjdxdt th,div.svelte-wjdxdt td{padding:.375rem .5rem}div.svelte-wjdxdt .table-wrapper{margin:.5rem -1rem;border-radius:0;border-left:none;border-right:none}}@media(prefers-color-scheme:dark){div.svelte-wjdxdt blockquote:hover{background:var(--muted)}}div.svelte-wjdxdt .image-load-error{display:flex;align-items:center;justify-content:center;margin:1.5rem 0;padding:1.5rem;border-radius:.5rem;background:var(--muted);border:1px dashed var(--border)}div.svelte-wjdxdt .image-error-content{display:flex;flex-direction:column;align-items:center;gap:.75rem;color:var(--muted-foreground);text-align:center}div.svelte-wjdxdt .image-error-content svg{opacity:.5}div.svelte-wjdxdt .image-error-text{font-size:.875rem}div.svelte-wjdxdt .image-error-link{display:inline-flex;align-items:center;gap:.375rem;padding:.5rem 1rem;font-size:.875rem;font-weight:500;color:var(--primary);background:var(--background);border:1px solid var(--border);border-radius:.375rem;text-decoration:none;transition:all .2s ease}div.svelte-wjdxdt .image-error-link:hover{background:var(--muted);border-color:var(--primary)}.code-preview-wrapper.svelte-hp0zxr{font-family:ui-monospace,SFMono-Regular,SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Liberation Mono,Menlo,monospace}.code-preview-wrapper.svelte-hp0zxr pre:where(.svelte-hp0zxr){background:transparent}.code-preview-wrapper.svelte-hp0zxr code:where(.svelte-hp0zxr){background:transparent}.code-preview-overlay{position:fixed;inset:0;background-color:transparent;z-index:100000}.code-preview-content{position:fixed;inset:0;top:0!important;left:0!important;width:100dvw;height:100dvh;margin:0;padding:0;border:none;border-radius:0;background-color:transparent;box-shadow:none;display:block;overflow:hidden;transform:none!important;z-index:100001}.code-preview-iframe{display:block;width:100dvw;height:100dvh;border:0}.code-preview-close{position:absolute;z-index:100002}button.svelte-19ekn76 [data-slot=dropdown-menu-trigger]:not([data-state=open]){opacity:0}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) [data-slot=dropdown-menu-trigger]{opacity:1}@media(max-width:768px){button.svelte-19ekn76 [data-slot=dropdown-menu-trigger]{opacity:1!important}}button.svelte-19ekn76 .stop-button:where(.svelte-19ekn76) .stop-icon{display:none}button.svelte-19ekn76 .stop-button:where(.svelte-19ekn76) .loading-icon{display:block}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) .stop-button:where(.svelte-19ekn76) .stop-icon{display:block}button.svelte-19ekn76:is(:where(.svelte-19ekn76):hover) .stop-button:where(.svelte-19ekn76) .loading-icon{display:none} diff --git a/tools/server/public/bundle.js b/tools/server/public/bundle.js index 50af17362fc..dd1329b9461 100644 --- a/tools/server/public/bundle.js +++ b/tools/server/public/bundle.js @@ -1150,130 +1150,130 @@ $$anchor2,spread_props({"data-slot":"tooltip-trigger",class:"cursor-pointer"},() $$anchor3,spread_props({"data-slot":"tooltip-content",get sideOffset(){return sideOffset()},get side(){return side()},get class(){return get$3(contentClass)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor4,$$slotProps)=>{var fragment_1=root_2$1e(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);{const child2=($$anchor5,$$arg0)=>{let props=()=>$$arg0?.().props;var div=root_3$Z();attribute_effect( div,$0=>({class:$0,...props()}),[()=>cn$1("z-50 size-2.5 rotate-45 rounded-[2px] bg-primary","data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]","data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]","data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2","data-[side=left]:-translate-y-[calc(50%_-_3px)]",$$props.arrowClasses)]),append($$anchor5,div)};component(node_2,()=>Tooltip_arrow,($$anchor5,TooltipPrimitive_Arrow)=>{ TooltipPrimitive_Arrow($$anchor5,{child:child2,$$slots:{child:!0}})})}append($$anchor4,fragment_1)},$$slots:{default:!0}}))}),append($$anchor2,fragment)};let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,0),side=prop($$props,"side",3,"top"),noPortal=prop($$props,"noPortal",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","sideOffset","side","children","arrowClasses","noPortal"]);const contentClass=user_derived(()=>cn$1("z-50 w-fit origin\ --(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",$$props.class));var fragment_2=comment$2(),node_3=first_child(fragment_2);{var consequent=$$anchor2=>{ -tooltipContent($$anchor2)},alternate=$$anchor2=>{var fragment_4=comment$2(),node_4=first_child(fragment_4);component(node_4,()=>Portal$2,($$anchor3,TooltipPrimitive_Portal)=>{TooltipPrimitive_Portal($$anchor3,{children:($$anchor4,$$slotProps)=>{tooltipContent($$anchor4)},$$slots:{default:!0}})}),append($$anchor2,fragment_4)};if_block(node_3,$$render=>{noPortal()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2),pop()}const Root$5=Tooltip,Provider=Tooltip_provider;var AttachmentType=(AttachmentType2=>(AttachmentType2. -AUDIO="AUDIO",AttachmentType2.IMAGE="IMAGE",AttachmentType2.MCP_PROMPT="MCP_PROMPT",AttachmentType2.MCP_RESOURCE="MCP_RESOURCE",AttachmentType2.PDF="PDF",AttachmentType2.TEXT="TEXT",AttachmentType2.LEGACY_CONTEXT="context",AttachmentType2))(AttachmentType||{}),AttachmentMenuItemId=(AttachmentMenuItemId2=>(AttachmentMenuItemId2.IMAGES="images",AttachmentMenuItemId2.AUDIO="audio",AttachmentMenuItemId2.TEXT="text",AttachmentMenuItemId2.PDF="pdf",AttachmentMenuItemId2.SYSTEM_MESSAGE="system-message", -AttachmentMenuItemId2.MCP_PROMPT="mcp-prompt",AttachmentMenuItemId2.MCP_RESOURCES="mcp-resources",AttachmentMenuItemId2))(AttachmentMenuItemId||{}),AttachmentItemEnabledWhen=(AttachmentItemEnabledWhen2=>(AttachmentItemEnabledWhen2.ALWAYS="always",AttachmentItemEnabledWhen2.HAS_VISION_MODALITY="hasVisionModality",AttachmentItemEnabledWhen2.HAS_AUDIO_MODALITY="hasAudioModality",AttachmentItemEnabledWhen2))(AttachmentItemEnabledWhen||{}),AttachmentAction=(AttachmentAction2=>(AttachmentAction2.FILE_UPLOAD= -"onFileUpload",AttachmentAction2.SYSTEM_PROMPT_CLICK="onSystemPromptClick",AttachmentAction2.MCP_PROMPT_CLICK="onMcpPromptClick",AttachmentAction2.MCP_RESOURCES_CLICK="onMcpResourcesClick",AttachmentAction2))(AttachmentAction||{}),AttachmentItemVisibleWhen=(AttachmentItemVisibleWhen2=>(AttachmentItemVisibleWhen2.HAS_MCP_PROMPTS_SUPPORT="hasMcpPromptsSupport",AttachmentItemVisibleWhen2.HAS_MCP_RESOURCES_SUPPORT="hasMcpResourcesSupport",AttachmentItemVisibleWhen2))(AttachmentItemVisibleWhen||{}),ToolCallType=(ToolCallType2=>(ToolCallType2. -FUNCTION="function",ToolCallType2))(ToolCallType||{}),AgenticSectionType=(AgenticSectionType2=>(AgenticSectionType2.TEXT="text",AgenticSectionType2.TOOL_CALL="tool_call",AgenticSectionType2.TOOL_CALL_PENDING="tool_call_pending",AgenticSectionType2.TOOL_CALL_STREAMING="tool_call_streaming",AgenticSectionType2.REASONING="reasoning",AgenticSectionType2.REASONING_PENDING="reasoning_pending",AgenticSectionType2))(AgenticSectionType||{}),ChatMessageStatsView=(ChatMessageStatsView2=>(ChatMessageStatsView2. -GENERATION="generation",ChatMessageStatsView2.READING="reading",ChatMessageStatsView2.TOOLS="tools",ChatMessageStatsView2.SUMMARY="summary",ChatMessageStatsView2))(ChatMessageStatsView||{}),ReasoningFormat=(ReasoningFormat2=>(ReasoningFormat2.NONE="none",ReasoningFormat2.AUTO="auto",ReasoningFormat2))(ReasoningFormat||{}),MessageRole=(MessageRole2=>(MessageRole2.USER="user",MessageRole2.ASSISTANT="assistant",MessageRole2.SYSTEM="system",MessageRole2.TOOL="tool",MessageRole2))(MessageRole||{}),MessageType=(MessageType2=>(MessageType2. -ROOT="root",MessageType2.TEXT="text",MessageType2.THINK="think",MessageType2.SYSTEM="system",MessageType2))(MessageType||{}),ContentPartType=(ContentPartType2=>(ContentPartType2.TEXT="text",ContentPartType2.IMAGE_URL="image_url",ContentPartType2.INPUT_AUDIO="input_audio",ContentPartType2))(ContentPartType||{}),ErrorDialogType=(ErrorDialogType2=>(ErrorDialogType2.TIMEOUT="timeout",ErrorDialogType2.SERVER="server",ErrorDialogType2))(ErrorDialogType||{}),ConversationSelectionMode=(ConversationSelectionMode2=>(ConversationSelectionMode2. -EXPORT="export",ConversationSelectionMode2.IMPORT="import",ConversationSelectionMode2))(ConversationSelectionMode||{}),PdfViewMode=(PdfViewMode2=>(PdfViewMode2.TEXT="text",PdfViewMode2.PAGES="pages",PdfViewMode2))(PdfViewMode||{}),FileTypeCategory=(FileTypeCategory2=>(FileTypeCategory2.IMAGE="image",FileTypeCategory2.AUDIO="audio",FileTypeCategory2.PDF="pdf",FileTypeCategory2.TEXT="text",FileTypeCategory2))(FileTypeCategory||{}),SpecialFileType=(SpecialFileType2=>(SpecialFileType2.MCP_PROMPT="mc\ -p-prompt",SpecialFileType2))(SpecialFileType||{}),FileTypeImage=(FileTypeImage2=>(FileTypeImage2.JPEG="jpeg",FileTypeImage2.PNG="png",FileTypeImage2.GIF="gif",FileTypeImage2.WEBP="webp",FileTypeImage2.SVG="svg",FileTypeImage2))(FileTypeImage||{}),FileTypeAudio=(FileTypeAudio2=>(FileTypeAudio2.MP3="mp3",FileTypeAudio2.WAV="wav",FileTypeAudio2.WEBM="webm",FileTypeAudio2))(FileTypeAudio||{}),FileTypePdf=(FileTypePdf2=>(FileTypePdf2.PDF="pdf",FileTypePdf2))(FileTypePdf||{}),FileTypeText=(FileTypeText2=>(FileTypeText2. -PLAIN_TEXT="plainText",FileTypeText2.MARKDOWN="md",FileTypeText2.ASCIIDOC="asciidoc",FileTypeText2.JAVASCRIPT="js",FileTypeText2.TYPESCRIPT="ts",FileTypeText2.JSX="jsx",FileTypeText2.TSX="tsx",FileTypeText2.CSS="css",FileTypeText2.HTML="html",FileTypeText2.JSON="json",FileTypeText2.XML="xml",FileTypeText2.YAML="yaml",FileTypeText2.CSV="csv",FileTypeText2.LOG="log",FileTypeText2.PYTHON="python",FileTypeText2.JAVA="java",FileTypeText2.CPP="cpp",FileTypeText2.PHP="php",FileTypeText2.RUBY="ruby",FileTypeText2. -GO="go",FileTypeText2.RUST="rust",FileTypeText2.SHELL="shell",FileTypeText2.SQL="sql",FileTypeText2.R="r",FileTypeText2.SCALA="scala",FileTypeText2.KOTLIN="kotlin",FileTypeText2.SWIFT="swift",FileTypeText2.DART="dart",FileTypeText2.VUE="vue",FileTypeText2.SVELTE="svelte",FileTypeText2.LATEX="latex",FileTypeText2.BIBTEX="bibtex",FileTypeText2.CUDA="cuda",FileTypeText2.VULKAN="vulkan",FileTypeText2.HASKELL="haskell",FileTypeText2.CSHARP="csharp",FileTypeText2.PROPERTIES="properties",FileTypeText2))( -FileTypeText||{}),FileExtensionImage=(FileExtensionImage2=>(FileExtensionImage2.JPG=".jpg",FileExtensionImage2.JPEG=".jpeg",FileExtensionImage2.PNG=".png",FileExtensionImage2.GIF=".gif",FileExtensionImage2.WEBP=".webp",FileExtensionImage2.SVG=".svg",FileExtensionImage2))(FileExtensionImage||{}),FileExtensionAudio=(FileExtensionAudio2=>(FileExtensionAudio2.MP3=".mp3",FileExtensionAudio2.WAV=".wav",FileExtensionAudio2))(FileExtensionAudio||{}),FileExtensionPdf=(FileExtensionPdf2=>(FileExtensionPdf2. -PDF=".pdf",FileExtensionPdf2))(FileExtensionPdf||{}),FileExtensionText=(FileExtensionText2=>(FileExtensionText2.TXT=".txt",FileExtensionText2.MD=".md",FileExtensionText2.ADOC=".adoc",FileExtensionText2.JS=".js",FileExtensionText2.TS=".ts",FileExtensionText2.JSX=".jsx",FileExtensionText2.TSX=".tsx",FileExtensionText2.CSS=".css",FileExtensionText2.HTML=".html",FileExtensionText2.HTM=".htm",FileExtensionText2.JSON=".json",FileExtensionText2.XML=".xml",FileExtensionText2.YAML=".yaml",FileExtensionText2. -YML=".yml",FileExtensionText2.CSV=".csv",FileExtensionText2.LOG=".log",FileExtensionText2.PY=".py",FileExtensionText2.JAVA=".java",FileExtensionText2.CPP=".cpp",FileExtensionText2.C=".c",FileExtensionText2.H=".h",FileExtensionText2.PHP=".php",FileExtensionText2.RB=".rb",FileExtensionText2.GO=".go",FileExtensionText2.RS=".rs",FileExtensionText2.SH=".sh",FileExtensionText2.BAT=".bat",FileExtensionText2.SQL=".sql",FileExtensionText2.R=".r",FileExtensionText2.SCALA=".scala",FileExtensionText2.KT=".k\ -t",FileExtensionText2.SWIFT=".swift",FileExtensionText2.DART=".dart",FileExtensionText2.VUE=".vue",FileExtensionText2.SVELTE=".svelte",FileExtensionText2.TEX=".tex",FileExtensionText2.BIB=".bib",FileExtensionText2.CU=".cu",FileExtensionText2.CUH=".cuh",FileExtensionText2.COMP=".comp",FileExtensionText2.HPP=".hpp",FileExtensionText2.HS=".hs",FileExtensionText2.PROPERTIES=".properties",FileExtensionText2.CS=".cs",FileExtensionText2))(FileExtensionText||{}),MimeTypePrefix=(MimeTypePrefix2=>(MimeTypePrefix2. -IMAGE="image/",MimeTypePrefix2.TEXT="text",MimeTypePrefix2))(MimeTypePrefix||{}),MimeTypeIncludes=(MimeTypeIncludes2=>(MimeTypeIncludes2.JSON="json",MimeTypeIncludes2.JAVASCRIPT="javascript",MimeTypeIncludes2.TYPESCRIPT="typescript",MimeTypeIncludes2))(MimeTypeIncludes||{}),UriPattern=(UriPattern2=>(UriPattern2.DATABASE_KEYWORD="database",UriPattern2.DATABASE_SCHEME="db://",UriPattern2))(UriPattern||{}),MimeTypeApplication=(MimeTypeApplication2=>(MimeTypeApplication2.PDF="application/pdf",MimeTypeApplication2. -OCTET_STREAM="application/octet-stream",MimeTypeApplication2))(MimeTypeApplication||{}),MimeTypeAudio=(MimeTypeAudio2=>(MimeTypeAudio2.MP3_MPEG="audio/mpeg",MimeTypeAudio2.MP3="audio/mp3",MimeTypeAudio2.MP4="audio/mp4",MimeTypeAudio2.WAV="audio/wav",MimeTypeAudio2.WEBM="audio/webm",MimeTypeAudio2.WEBM_OPUS="audio/webm;codecs=opus",MimeTypeAudio2))(MimeTypeAudio||{}),MimeTypeImage=(MimeTypeImage2=>(MimeTypeImage2.JPEG="image/jpeg",MimeTypeImage2.JPG="image/jpg",MimeTypeImage2.PNG="image/png",MimeTypeImage2. -GIF="image/gif",MimeTypeImage2.WEBP="image/webp",MimeTypeImage2.SVG="image/svg+xml",MimeTypeImage2.ICO="image/x-icon",MimeTypeImage2.ICO_MICROSOFT="image/vnd.microsoft.icon",MimeTypeImage2))(MimeTypeImage||{}),MimeTypeText=(MimeTypeText2=>(MimeTypeText2.PLAIN="text/plain",MimeTypeText2.MARKDOWN="text/markdown",MimeTypeText2.ASCIIDOC="text/asciidoc",MimeTypeText2.JAVASCRIPT="text/javascript",MimeTypeText2.JAVASCRIPT_APP="application/javascript",MimeTypeText2.TYPESCRIPT="text/typescript",MimeTypeText2. -JSX="text/jsx",MimeTypeText2.TSX="text/tsx",MimeTypeText2.CSS="text/css",MimeTypeText2.HTML="text/html",MimeTypeText2.JSON="application/json",MimeTypeText2.XML_TEXT="text/xml",MimeTypeText2.XML_APP="application/xml",MimeTypeText2.YAML_TEXT="text/yaml",MimeTypeText2.YAML_APP="application/yaml",MimeTypeText2.CSV="text/csv",MimeTypeText2.PYTHON="text/x-python",MimeTypeText2.JAVA="text/x-java-source",MimeTypeText2.CPP_HDR="text/x-c++hdr",MimeTypeText2.CPP_SRC="text/x-c++src",MimeTypeText2.CSHARP="te\ -xt/x-csharp",MimeTypeText2.HASKELL="text/x-haskell",MimeTypeText2.C_SRC="text/x-csrc",MimeTypeText2.C_HDR="text/x-chdr",MimeTypeText2.PHP="text/x-php",MimeTypeText2.RUBY="text/x-ruby",MimeTypeText2.GO="text/x-go",MimeTypeText2.RUST="text/x-rust",MimeTypeText2.SHELL="text/x-shellscript",MimeTypeText2.BAT="application/x-bat",MimeTypeText2.SQL="text/x-sql",MimeTypeText2.R="text/x-r",MimeTypeText2.SCALA="text/x-scala",MimeTypeText2.KOTLIN="text/x-kotlin",MimeTypeText2.SWIFT="text/x-swift",MimeTypeText2. -DART="text/x-dart",MimeTypeText2.VUE="text/x-vue",MimeTypeText2.SVELTE="text/x-svelte",MimeTypeText2.TEX="text/x-tex",MimeTypeText2.TEX_APP="application/x-tex",MimeTypeText2.LATEX="application/x-latex",MimeTypeText2.BIBTEX="text/x-bibtex",MimeTypeText2.CUDA="text/x-cuda",MimeTypeText2.PROPERTIES="text/properties",MimeTypeText2))(MimeTypeText||{}),MCPConnectionPhase=(MCPConnectionPhase2=>(MCPConnectionPhase2.IDLE="idle",MCPConnectionPhase2.TRANSPORT_CREATING="transport_creating",MCPConnectionPhase2. -TRANSPORT_READY="transport_ready",MCPConnectionPhase2.INITIALIZING="initializing",MCPConnectionPhase2.CAPABILITIES_EXCHANGED="capabilities_exchanged",MCPConnectionPhase2.LISTING_TOOLS="listing_tools",MCPConnectionPhase2.CONNECTED="connected",MCPConnectionPhase2.ERROR="error",MCPConnectionPhase2.DISCONNECTED="disconnected",MCPConnectionPhase2))(MCPConnectionPhase||{}),MCPLogLevel=(MCPLogLevel2=>(MCPLogLevel2.INFO="info",MCPLogLevel2.WARN="warn",MCPLogLevel2.ERROR="error",MCPLogLevel2))(MCPLogLevel|| -{}),MCPTransportType=(MCPTransportType2=>(MCPTransportType2.WEBSOCKET="websocket",MCPTransportType2.STREAMABLE_HTTP="streamable_http",MCPTransportType2.SSE="sse",MCPTransportType2))(MCPTransportType||{}),HealthCheckStatus=(HealthCheckStatus2=>(HealthCheckStatus2.IDLE="idle",HealthCheckStatus2.CONNECTING="connecting",HealthCheckStatus2.SUCCESS="success",HealthCheckStatus2.ERROR="error",HealthCheckStatus2))(HealthCheckStatus||{}),MCPContentType=(MCPContentType2=>(MCPContentType2.TEXT="text",MCPContentType2. -IMAGE="image",MCPContentType2.RESOURCE="resource",MCPContentType2))(MCPContentType||{}),JsonSchemaType=(JsonSchemaType2=>(JsonSchemaType2.OBJECT="object",JsonSchemaType2))(JsonSchemaType||{}),MCPRefType=(MCPRefType2=>(MCPRefType2.PROMPT="ref/prompt",MCPRefType2.RESOURCE="ref/resource",MCPRefType2))(MCPRefType||{}),ModelModality=(ModelModality2=>(ModelModality2.TEXT="TEXT",ModelModality2.AUDIO="AUDIO",ModelModality2.VISION="VISION",ModelModality2))(ModelModality||{}),ServerRole=(ServerRole2=>(ServerRole2. -MODEL="model",ServerRole2.ROUTER="router",ServerRole2))(ServerRole||{}),ServerModelStatus=(ServerModelStatus2=>(ServerModelStatus2.UNLOADED="unloaded",ServerModelStatus2.LOADING="loading",ServerModelStatus2.LOADED="loaded",ServerModelStatus2.SLEEPING="sleeping",ServerModelStatus2.FAILED="failed",ServerModelStatus2))(ServerModelStatus||{}),ParameterSource=(ParameterSource2=>(ParameterSource2.DEFAULT="default",ParameterSource2.CUSTOM="custom",ParameterSource2))(ParameterSource||{}),SyncableParameterType=(SyncableParameterType2=>(SyncableParameterType2. -NUMBER="number",SyncableParameterType2.STRING="string",SyncableParameterType2.BOOLEAN="boolean",SyncableParameterType2))(SyncableParameterType||{}),SettingsFieldType=(SettingsFieldType2=>(SettingsFieldType2.INPUT="input",SettingsFieldType2.TEXTAREA="textarea",SettingsFieldType2.CHECKBOX="checkbox",SettingsFieldType2.SELECT="select",SettingsFieldType2))(SettingsFieldType||{}),ColorMode=(ColorMode2=>(ColorMode2.LIGHT="light",ColorMode2.DARK="dark",ColorMode2.SYSTEM="system",ColorMode2))(ColorMode|| -{}),TooltipSide=(TooltipSide2=>(TooltipSide2.TOP="top",TooltipSide2.RIGHT="right",TooltipSide2.BOTTOM="bottom",TooltipSide2.LEFT="left",TooltipSide2))(TooltipSide||{}),McpPromptVariant=(McpPromptVariant2=>(McpPromptVariant2.MESSAGE="message",McpPromptVariant2.ATTACHMENT="attachment",McpPromptVariant2))(McpPromptVariant||{}),UrlProtocol=(UrlProtocol2=>(UrlProtocol2.DATA="data:",UrlProtocol2.HTTP="http:",UrlProtocol2.HTTPS="https:",UrlProtocol2.WEBSOCKET="ws:",UrlProtocol2.WEBSOCKET_SECURE="wss:", -UrlProtocol2))(UrlProtocol||{}),HtmlInputType=(HtmlInputType2=>(HtmlInputType2.FILE="file",HtmlInputType2))(HtmlInputType||{}),KeyboardKey=(KeyboardKey2=>(KeyboardKey2.ENTER="Enter",KeyboardKey2.ESCAPE="Escape",KeyboardKey2.ARROW_UP="ArrowUp",KeyboardKey2.ARROW_DOWN="ArrowDown",KeyboardKey2.ARROW_LEFT="ArrowLeft",KeyboardKey2.ARROW_RIGHT="ArrowRight",KeyboardKey2.TAB="Tab",KeyboardKey2.D_LOWER="d",KeyboardKey2.D_UPPER="D",KeyboardKey2.E_UPPER="E",KeyboardKey2.K_LOWER="k",KeyboardKey2.O_LOWER="o", -KeyboardKey2.O_UPPER="O",KeyboardKey2.SPACE=" ",KeyboardKey2))(KeyboardKey||{}),ToolSource=(ToolSource2=>(ToolSource2.BUILTIN="builtin",ToolSource2.MCP="mcp",ToolSource2.CUSTOM="custom",ToolSource2))(ToolSource||{}),ToolPermissionDecision=(ToolPermissionDecision2=>(ToolPermissionDecision2.ALWAYS="always",ToolPermissionDecision2.ALWAYS_SERVER="always_server",ToolPermissionDecision2.ONCE="once",ToolPermissionDecision2.DENY="deny",ToolPermissionDecision2))(ToolPermissionDecision||{}),ToolResponseField=(ToolResponseField2=>(ToolResponseField2. -PLAIN_TEXT="plain_text_response",ToolResponseField2.ERROR="error",ToolResponseField2))(ToolResponseField||{}),root_5$w=from_html("

"),root_1$17=from_html(" ",1);function ActionIcon($$anchor,$$props){push$1($$props,!0);let variant=prop($$props,"variant",3,"ghost"),size2=prop($$props,"size",3,"sm"),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),iconSize=prop($$props,"iconSize",3,"h-3 w-3"),tooltipSide=prop($$props,"tooltipSide",19,()=>TooltipSide.TOP),stopPropagationOnClick=prop( -$$props,"stopPropagationOnClick",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$17(),node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>$$props.ariaLabel||$$props.tooltip);Button($$anchor5,{get variant(){return variant()}, -get size(){return size2()},get disabled(){return disabled()},onclick:e=>{stopPropagationOnClick()&&e.stopPropagation(),$$props.onclick?.(e)},get class(){return`h-6 w-6 p-0 ${className()??""} flex hover:bg-transparent data-[state=open]:bg-transparent!`},get"aria-label"(){return get$3($0)},children:($$anchor6,$$slotProps3)=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{var consequent=$$anchor7=>{const IconComponent=user_derived(()=>$$props.icon);var fragment_4=comment$2(),node_3=first_child( -fragment_4);component(node_3,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{IconComponent_1($$anchor8,{get class(){return iconSize()}})}),append($$anchor7,fragment_4)};if_block(node_2,$$render=>{$$props.icon&&$$render(consequent)})}append($$anchor6,fragment_3)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_4=sibling(node_1,2);component(node_4,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{get side(){return tooltipSide()},children:($$anchor5,$$slotProps2)=>{ -var p2=root_5$w(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltip)),append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}const defaultAttributes={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};var root$1T=from_svg("");function Icon($$anchor,$$props){ -push$1($$props,!0);const color=prop($$props,"color",3,"currentColor"),size2=prop($$props,"size",3,24),strokeWidth=prop($$props,"strokeWidth",3,2),absoluteStrokeWidth=prop($$props,"absoluteStrokeWidth",3,!1),iconNode=prop($$props,"iconNode",19,()=>[]),props=rest_props($$props,["$$slots","$$events","$$legacy","name","color","size","strokeWidth","absoluteStrokeWidth","iconNode","children"]);var svg2=root$1T();attribute_effect(svg2,$0=>({...defaultAttributes,...props,width:size2(),height:size2(),stroke:color(), -"stroke-width":$0,class:["lucide-icon lucide",$$props.name&&`lucide-${$$props.name}`,$$props.class]}),[()=>absoluteStrokeWidth()?Number(strokeWidth())*24/Number(size2()):strokeWidth()]);var node2=child(svg2);each(node2,17,iconNode,index$2,($$anchor2,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let tag=()=>get$3($$array)[0],attrs=()=>get$3($$array)[1];var fragment=comment$2(),node_1=first_child(fragment);element$4(node_1,tag,!0,($$element,$$anchor3)=>{attribute_effect($$element, -()=>({...attrs()}))}),append($$anchor2,fragment)});var node_2=sibling(node2);snippet(node_2,()=>$$props.children??noop$3),reset(svg2),append($$anchor,svg2),pop()}function Arrow_big_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18v-6H5l7-7 7 7h-4v6H9z"}]];Icon($$anchor,spread_props({name:"arrow-big-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( -fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"m12 5 7 7-7 7"}]];Icon($$anchor,spread_props({name:"arrow-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2, -()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m5 12 7-7 7 7"}],["path",{d:"M12 19V5"}]];Icon($$anchor,spread_props({name:"arrow-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), -append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Book_open_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 7v14"}],["path",{d:"M16 12h2"}],["path",{d:"M16 8h2"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],["path",{d:"M6 12h2"}],["path",{d:"M6 8h2"}]];Icon($$anchor,spread_props({name:"bo\ -ok-open-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Braces($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"}],["path",{d:"M16 21h1a2 2 0 0 \ -0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"}]];Icon($$anchor,spread_props({name:"braces"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Brain($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 5a3 3 0 1 \ -0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516"}],["path",{d:"M19.967 17.484A4 4 0 0\ - 1 18 18"}]];Icon($$anchor,spread_props({name:"brain"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Check($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 6 9 17l-5-5"}]];Icon($$anchor,spread_props({name:"check"}, -()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 9 6 6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_left($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15 18-6-6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( -fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m18 15-6-6-6 6"}]];Icon($$anchor,spread_props({name:"chevron-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? -noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m9 18 6-6-6-6"}]];Icon($$anchor,spread_props({name:"chevron-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ -default:!0}})),pop()}function Chevrons_up_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m7 15 5 5 5-5"}],["path",{d:"m7 9 5-5 5 5"}]];Icon($$anchor,spread_props({name:"chevrons-up-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), -pop()}function Circle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"circle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( -$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_check_big($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335"}],["path",{d:"m9 11 3 3L22 4"}]];Icon($$anchor,spread_props({name:"circle-check-big"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), -append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]];Icon($$anchor,spread_props({name:"circle-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), -append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Clock($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["polyline",{points:"12 6 12 12 16 14"}]];Icon($$anchor,spread_props({name:"clock"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, -fragment_1)},$$slots:{default:!0}})),pop()}function Code($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m16 18 6-6-6-6"}],["path",{d:"m8 6-6 6 6 6"}]];Icon($$anchor,spread_props({name:"code"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), -pop()}function Copy($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]];Icon($$anchor,spread_props({name:"copy"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, -fragment_1)},$$slots:{default:!0}})),pop()}function Database($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}]];Icon($$anchor,spread_props({name:"database"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Download($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]];Icon($$anchor,spread_props({name:"download"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1); -snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Ellipsis($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]];Icon($$anchor,spread_props({name:"ellipsis"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( -fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function External_link($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 3h6v6"}],["path",{d:"M10 14 21 3"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}]];Icon($$anchor,spread_props({name:"external-link"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Eye($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"eye"},()=>props,{get iconNode(){ -return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"M10 9H8"}],["path",{d:"M16\ - 13H8"}],["path",{d:"M16 17H8"}]];Icon($$anchor,spread_props({name:"file-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2\ - 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"m14.5 12.5-5 5"}],["path",{d:"m9.5 12.5 5 5"}]];Icon($$anchor,spread_props({name:"file-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots", -"$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}]];Icon($$anchor,spread_props({name:"file"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Flask_conical($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"}],["path",{d:"M6.453 15h11.094"}],["path",{d:"M8.5 2h7"}]];Icon($$anchor,spread_props({name:"flask-conical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ -default:!0}})),pop()}function Folder_open($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"}]];Icon($$anchor,spread_props({name:"folder-open"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Funnel($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"}]];Icon($$anchor,spread_props({name:"funnel"},()=>props,{get iconNode(){return iconNode}, -children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Gauge($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m12 14 4-4"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0"}]];Icon($$anchor,spread_props({name:"gauge"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Git_branch($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"6",x2:"6",y1:"3",y2:"15"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}],["path",{d:"M18 9a9 9 0 0 1-9 9"}]];Icon($$anchor,spread_props({name:"git-branch"},()=>props,{ -get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Globe($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["path",{d:"M2 12h20"}]];Icon($$anchor,spread_props( -{name:"globe"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"2",y1:"2",x2:"22",y2:"22"}],["path",{d:"M16.5 16.5 12 21l-7-7c-1.5-1.45-3-3.2-3-5.5a5.5 5.5 0 0 1\ - 2.14-4.35"}],["path",{d:"M8.76 3.1c1.15.22 2.13.78 3.24 1.9 1.5-1.5 2.74-2 4.5-2A5.5 5.5 0 0 1 22 8.5c0 2.12-1.3 3.78-2.67 5.17"}]];Icon($$anchor,spread_props({name:"heart-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,[ -"$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"}]];Icon($$anchor,spread_props({name:"heart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Image$1($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]];Icon($$anchor,spread_props({name:"image"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, -$$slots:{default:!0}})),pop()}function Info$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 16v-4"}],["path",{d:"M12 8h.01"}]];Icon($$anchor,spread_props({name:"info"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, -$$slots:{default:!0}})),pop()}function Key($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"}],["path",{d:"m21 2-9.6 9.6"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5"}]];Icon($$anchor,spread_props({name:"key"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Layers($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 \ -0 0 22 17"}]];Icon($$anchor,spread_props({name:"layers"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_checks($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m3 17 2 2 4-4"}],["path",{d:"m3 7 2 2 4-4"}],["path",{ -d:"M13 6h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 18h8"}]];Icon($$anchor,spread_props({name:"list-checks"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_restart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ -M21 6H3"}],["path",{d:"M7 12H3"}],["path",{d:"M7 18H3"}],["path",{d:"M12 18a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L11 14"}],["path",{d:"M11 10v4h4"}]];Icon($$anchor,spread_props({name:"list-restart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Loader_circle($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]];Icon($$anchor,spread_props({name:"loader-circle"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Message_square($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"}]];Icon($$anchor,spread_props({name:"message-square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Mic($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, -["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"}],["line",{x1:"12",x2:"12",y1:"19",y2:"22"}]];Icon($$anchor,spread_props({name:"mic"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Minus($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}]];Icon($$anchor,spread_props({name:"minus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Monitor($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","\ -$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21"}]];Icon($$anchor,spread_props({name:"monitor"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Moon($$anchor,$$props){push$1( -$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"}]];Icon($$anchor,spread_props({name:"moon"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Music($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, -["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18V5l12-2v13"}],["circle",{cx:"6",cy:"18",r:"3"}],["circle",{cx:"18",cy:"16",r:"3"}]];Icon($$anchor,spread_props({name:"music"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Package($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"}],["path",{d:"M12 22V12"}],["polyline",{points:"3.29 7 12 12 20.71 7"}],["path",{d:"m7.5 4.27 9 5.15"}]];Icon($$anchor,spread_props({name:"package"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left_close($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m16 15-3-3 3-3"}]];Icon($$anchor,spread_props({name:"panel-left-close"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( -fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}]];Icon($$anchor,spread_props({name:"panel-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( -fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil_ruler($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"}],["path",{d:"m8 6 2-2"}],["path",{d:"m18 16 2-2"}],["path",{d:"m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"}],["path",{d:"M21.174 6\ -.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil-ruler"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), -pop()}function Plus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"M12 5v14"}]];Icon($$anchor,spread_props({name:"plus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power_off($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15"}],["path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68"}],["path",{d:"M12 2v4"}],["path",{d:"m2 2 20 20"}]];Icon($$anchor,spread_props({name:"power-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ -default:!0}})),pop()}function Power($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2v10"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04"}]];Icon($$anchor,spread_props({name:"power"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()} -function Radio($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4.9 19.1C1 15.2 1 8.8 4.9 4.9"}],["path",{d:"M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"}],["path",{d:"M19.1 4.9C23 8.8 23 15.1 19.1 19"}]];Icon($$anchor,spread_props({name:"radio"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Refresh_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]];Icon($$anchor,spread_props({name:"refresh-cw"}, -()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_ccw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}]];Icon($$anchor,spread_props({name:"\ -rotate-ccw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}]];Icon($$anchor,spread_props( -{name:"rotate-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Search($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]];Icon($$anchor,spread_props({name:"search"}, -()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Server($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2"}],["line",{ -x1:"6",x2:"6.01",y1:"6",y2:"6"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18"}]];Icon($$anchor,spread_props({name:"server"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Settings$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["p\ -ath",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0\ --2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"settings"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_question($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots", -"$$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"shield-question"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sliders_vertical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"4",x2:"4",y1:"21",y2:"14"}],["line",{x1:"4",x2:"4",y1:"10",y2:"3"}],["line",{x1:"12",x2:"12",y1:"21",y2:"12"}],["line",{x1:"12",x2:"12",y1:"8",y2:"3"}],["line",{x1:"20",x2:"20",y1:"21",y2:"16"}],["line",{x1:"20",x2:"20",y1:"12",y2:"3"}],["line",{x1:"2",x2:"6",y1:"14", -y2:"14"}],["line",{x1:"10",x2:"14",y1:"8",y2:"8"}],["line",{x1:"18",x2:"22",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"sliders-vertical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sparkles($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$lega\ -cy"]);const iconNode=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"}],["path",{d:"M20 3v4"}],["path",{d:"M22 5h-4"}],["path",{d:"M4 17v2"}],["path",{d:"M5 18H3"}]];Icon($$anchor,spread_props({name:"sparkles"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square_pen($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.8\ -52z"}]];Icon($$anchor,spread_props({name:"square-pen"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]];Icon($$anchor,spread_props( -{name:"square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sun($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 2v2"}],["path",{d:"M12 20v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}], -["path",{d:"m17.66 17.66 1.41 1.41"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"m6.34 17.66-1.41 1.41"}],["path",{d:"m19.07 4.93-1.41 1.41"}]];Icon($$anchor,spread_props({name:"sun"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Timer_off($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 2h4"}],["path",{d:"M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7"}],["path",{d:"M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0 0 1 .9 10.2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M12 12v-2"}]];Icon($$anchor,spread_props({name:"timer-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, -$$slots:{default:!0}})),pop()}function Trash_2($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 6h18"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17"}]];Icon($$anchor,spread_props({name:"trash-2"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Triangle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"}],["path",{d:"M12 9v4"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"triangle-alert"},()=>props,{get iconNode(){return iconNode}, -children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Upload($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3v12"}],["path",{d:"m17 8-5-5-5 5"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}]];Icon($$anchor,spread_props({name:"upload"},()=>props,{get iconNode(){ -return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Whole_word($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"7",cy:"12",r:"3"}],["path",{d:"M10 9v6"}],["circle",{cx:"17",cy:"12",r:"3"}],["path",{d:"M14 7v8"}],["path",{d:"M22 17v1c0 .5-.5 1-1 1H\ -3c-.5 0-1-.5-1-1v-1"}]];Icon($$anchor,spread_props({name:"whole-word"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Wrench($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1\ -.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"}]];Icon($$anchor,spread_props({name:"wrench"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function X($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$lega\ -cy"]);const iconNode=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]];Icon($$anchor,spread_props({name:"x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Zap($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4 14a\ -1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}]];Icon($$anchor,spread_props({name:"zap"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}const ATTACHMENT_SAVED_REGEX=/\[Attachment saved: ([^\]]+)\]/,NEWLINE_SEPARATOR=`\ - +-(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:fill-mode-forwards",$$props.class));var fragment_2=comment$2(),node_3=first_child( +fragment_2);{var consequent=$$anchor2=>{tooltipContent($$anchor2)},alternate=$$anchor2=>{var fragment_4=comment$2(),node_4=first_child(fragment_4);component(node_4,()=>Portal$2,($$anchor3,TooltipPrimitive_Portal)=>{TooltipPrimitive_Portal($$anchor3,{children:($$anchor4,$$slotProps)=>{tooltipContent($$anchor4)},$$slots:{default:!0}})}),append($$anchor2,fragment_4)};if_block(node_3,$$render=>{noPortal()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2),pop()}const Root$5=Tooltip, +Provider=Tooltip_provider;var AttachmentType=(AttachmentType2=>(AttachmentType2.AUDIO="AUDIO",AttachmentType2.IMAGE="IMAGE",AttachmentType2.MCP_PROMPT="MCP_PROMPT",AttachmentType2.MCP_RESOURCE="MCP_RESOURCE",AttachmentType2.PDF="PDF",AttachmentType2.TEXT="TEXT",AttachmentType2.LEGACY_CONTEXT="context",AttachmentType2))(AttachmentType||{}),AttachmentMenuItemId=(AttachmentMenuItemId2=>(AttachmentMenuItemId2.IMAGES="images",AttachmentMenuItemId2.AUDIO="audio",AttachmentMenuItemId2.TEXT="text",AttachmentMenuItemId2. +PDF="pdf",AttachmentMenuItemId2.SYSTEM_MESSAGE="system-message",AttachmentMenuItemId2.MCP_PROMPT="mcp-prompt",AttachmentMenuItemId2.MCP_RESOURCES="mcp-resources",AttachmentMenuItemId2))(AttachmentMenuItemId||{}),AttachmentItemEnabledWhen=(AttachmentItemEnabledWhen2=>(AttachmentItemEnabledWhen2.ALWAYS="always",AttachmentItemEnabledWhen2.HAS_VISION_MODALITY="hasVisionModality",AttachmentItemEnabledWhen2.HAS_AUDIO_MODALITY="hasAudioModality",AttachmentItemEnabledWhen2))(AttachmentItemEnabledWhen||{}), +AttachmentAction=(AttachmentAction2=>(AttachmentAction2.FILE_UPLOAD="onFileUpload",AttachmentAction2.SYSTEM_PROMPT_CLICK="onSystemPromptClick",AttachmentAction2.MCP_PROMPT_CLICK="onMcpPromptClick",AttachmentAction2.MCP_RESOURCES_CLICK="onMcpResourcesClick",AttachmentAction2))(AttachmentAction||{}),AttachmentItemVisibleWhen=(AttachmentItemVisibleWhen2=>(AttachmentItemVisibleWhen2.HAS_MCP_PROMPTS_SUPPORT="hasMcpPromptsSupport",AttachmentItemVisibleWhen2.HAS_MCP_RESOURCES_SUPPORT="hasMcpResourcesSu\ +pport",AttachmentItemVisibleWhen2))(AttachmentItemVisibleWhen||{}),ToolCallType=(ToolCallType2=>(ToolCallType2.FUNCTION="function",ToolCallType2))(ToolCallType||{}),AgenticSectionType=(AgenticSectionType2=>(AgenticSectionType2.TEXT="text",AgenticSectionType2.TOOL_CALL="tool_call",AgenticSectionType2.TOOL_CALL_PENDING="tool_call_pending",AgenticSectionType2.TOOL_CALL_STREAMING="tool_call_streaming",AgenticSectionType2.REASONING="reasoning",AgenticSectionType2.REASONING_PENDING="reasoning_pending", +AgenticSectionType2))(AgenticSectionType||{}),ChatMessageStatsView=(ChatMessageStatsView2=>(ChatMessageStatsView2.GENERATION="generation",ChatMessageStatsView2.READING="reading",ChatMessageStatsView2.TOOLS="tools",ChatMessageStatsView2.SUMMARY="summary",ChatMessageStatsView2))(ChatMessageStatsView||{}),ReasoningFormat=(ReasoningFormat2=>(ReasoningFormat2.NONE="none",ReasoningFormat2.AUTO="auto",ReasoningFormat2))(ReasoningFormat||{}),MessageRole=(MessageRole2=>(MessageRole2.USER="user",MessageRole2. +ASSISTANT="assistant",MessageRole2.SYSTEM="system",MessageRole2.TOOL="tool",MessageRole2))(MessageRole||{}),MessageType=(MessageType2=>(MessageType2.ROOT="root",MessageType2.TEXT="text",MessageType2.THINK="think",MessageType2.SYSTEM="system",MessageType2))(MessageType||{}),ContentPartType=(ContentPartType2=>(ContentPartType2.TEXT="text",ContentPartType2.IMAGE_URL="image_url",ContentPartType2.INPUT_AUDIO="input_audio",ContentPartType2))(ContentPartType||{}),ErrorDialogType=(ErrorDialogType2=>(ErrorDialogType2. +TIMEOUT="timeout",ErrorDialogType2.SERVER="server",ErrorDialogType2))(ErrorDialogType||{}),ConversationSelectionMode=(ConversationSelectionMode2=>(ConversationSelectionMode2.EXPORT="export",ConversationSelectionMode2.IMPORT="import",ConversationSelectionMode2))(ConversationSelectionMode||{}),PdfViewMode=(PdfViewMode2=>(PdfViewMode2.TEXT="text",PdfViewMode2.PAGES="pages",PdfViewMode2))(PdfViewMode||{}),FileTypeCategory=(FileTypeCategory2=>(FileTypeCategory2.IMAGE="image",FileTypeCategory2.AUDIO="\ +audio",FileTypeCategory2.PDF="pdf",FileTypeCategory2.TEXT="text",FileTypeCategory2))(FileTypeCategory||{}),SpecialFileType=(SpecialFileType2=>(SpecialFileType2.MCP_PROMPT="mcp-prompt",SpecialFileType2))(SpecialFileType||{}),FileTypeImage=(FileTypeImage2=>(FileTypeImage2.JPEG="jpeg",FileTypeImage2.PNG="png",FileTypeImage2.GIF="gif",FileTypeImage2.WEBP="webp",FileTypeImage2.SVG="svg",FileTypeImage2))(FileTypeImage||{}),FileTypeAudio=(FileTypeAudio2=>(FileTypeAudio2.MP3="mp3",FileTypeAudio2.WAV="wa\ +v",FileTypeAudio2.WEBM="webm",FileTypeAudio2))(FileTypeAudio||{}),FileTypePdf=(FileTypePdf2=>(FileTypePdf2.PDF="pdf",FileTypePdf2))(FileTypePdf||{}),FileTypeText=(FileTypeText2=>(FileTypeText2.PLAIN_TEXT="plainText",FileTypeText2.MARKDOWN="md",FileTypeText2.ASCIIDOC="asciidoc",FileTypeText2.JAVASCRIPT="js",FileTypeText2.TYPESCRIPT="ts",FileTypeText2.JSX="jsx",FileTypeText2.TSX="tsx",FileTypeText2.CSS="css",FileTypeText2.HTML="html",FileTypeText2.JSON="json",FileTypeText2.XML="xml",FileTypeText2. +YAML="yaml",FileTypeText2.CSV="csv",FileTypeText2.LOG="log",FileTypeText2.PYTHON="python",FileTypeText2.JAVA="java",FileTypeText2.CPP="cpp",FileTypeText2.PHP="php",FileTypeText2.RUBY="ruby",FileTypeText2.GO="go",FileTypeText2.RUST="rust",FileTypeText2.SHELL="shell",FileTypeText2.SQL="sql",FileTypeText2.R="r",FileTypeText2.SCALA="scala",FileTypeText2.KOTLIN="kotlin",FileTypeText2.SWIFT="swift",FileTypeText2.DART="dart",FileTypeText2.VUE="vue",FileTypeText2.SVELTE="svelte",FileTypeText2.LATEX="lat\ +ex",FileTypeText2.BIBTEX="bibtex",FileTypeText2.CUDA="cuda",FileTypeText2.VULKAN="vulkan",FileTypeText2.HASKELL="haskell",FileTypeText2.CSHARP="csharp",FileTypeText2.PROPERTIES="properties",FileTypeText2))(FileTypeText||{}),FileExtensionImage=(FileExtensionImage2=>(FileExtensionImage2.JPG=".jpg",FileExtensionImage2.JPEG=".jpeg",FileExtensionImage2.PNG=".png",FileExtensionImage2.GIF=".gif",FileExtensionImage2.WEBP=".webp",FileExtensionImage2.SVG=".svg",FileExtensionImage2))(FileExtensionImage||{}), +FileExtensionAudio=(FileExtensionAudio2=>(FileExtensionAudio2.MP3=".mp3",FileExtensionAudio2.WAV=".wav",FileExtensionAudio2))(FileExtensionAudio||{}),FileExtensionPdf=(FileExtensionPdf2=>(FileExtensionPdf2.PDF=".pdf",FileExtensionPdf2))(FileExtensionPdf||{}),FileExtensionText=(FileExtensionText2=>(FileExtensionText2.TXT=".txt",FileExtensionText2.MD=".md",FileExtensionText2.ADOC=".adoc",FileExtensionText2.JS=".js",FileExtensionText2.TS=".ts",FileExtensionText2.JSX=".jsx",FileExtensionText2.TSX=".\ +tsx",FileExtensionText2.CSS=".css",FileExtensionText2.HTML=".html",FileExtensionText2.HTM=".htm",FileExtensionText2.JSON=".json",FileExtensionText2.XML=".xml",FileExtensionText2.YAML=".yaml",FileExtensionText2.YML=".yml",FileExtensionText2.CSV=".csv",FileExtensionText2.LOG=".log",FileExtensionText2.PY=".py",FileExtensionText2.JAVA=".java",FileExtensionText2.CPP=".cpp",FileExtensionText2.C=".c",FileExtensionText2.H=".h",FileExtensionText2.PHP=".php",FileExtensionText2.RB=".rb",FileExtensionText2. +GO=".go",FileExtensionText2.RS=".rs",FileExtensionText2.SH=".sh",FileExtensionText2.BAT=".bat",FileExtensionText2.SQL=".sql",FileExtensionText2.R=".r",FileExtensionText2.SCALA=".scala",FileExtensionText2.KT=".kt",FileExtensionText2.SWIFT=".swift",FileExtensionText2.DART=".dart",FileExtensionText2.VUE=".vue",FileExtensionText2.SVELTE=".svelte",FileExtensionText2.TEX=".tex",FileExtensionText2.BIB=".bib",FileExtensionText2.CU=".cu",FileExtensionText2.CUH=".cuh",FileExtensionText2.COMP=".comp",FileExtensionText2. +HPP=".hpp",FileExtensionText2.HS=".hs",FileExtensionText2.PROPERTIES=".properties",FileExtensionText2.CS=".cs",FileExtensionText2))(FileExtensionText||{}),MimeTypePrefix=(MimeTypePrefix2=>(MimeTypePrefix2.IMAGE="image/",MimeTypePrefix2.TEXT="text",MimeTypePrefix2))(MimeTypePrefix||{}),MimeTypeIncludes=(MimeTypeIncludes2=>(MimeTypeIncludes2.JSON="json",MimeTypeIncludes2.JAVASCRIPT="javascript",MimeTypeIncludes2.TYPESCRIPT="typescript",MimeTypeIncludes2))(MimeTypeIncludes||{}),UriPattern=(UriPattern2=>(UriPattern2. +DATABASE_KEYWORD="database",UriPattern2.DATABASE_SCHEME="db://",UriPattern2))(UriPattern||{}),MimeTypeApplication=(MimeTypeApplication2=>(MimeTypeApplication2.PDF="application/pdf",MimeTypeApplication2.OCTET_STREAM="application/octet-stream",MimeTypeApplication2))(MimeTypeApplication||{}),MimeTypeAudio=(MimeTypeAudio2=>(MimeTypeAudio2.MP3_MPEG="audio/mpeg",MimeTypeAudio2.MP3="audio/mp3",MimeTypeAudio2.MP4="audio/mp4",MimeTypeAudio2.WAV="audio/wav",MimeTypeAudio2.WEBM="audio/webm",MimeTypeAudio2. +WEBM_OPUS="audio/webm;codecs=opus",MimeTypeAudio2))(MimeTypeAudio||{}),MimeTypeImage=(MimeTypeImage2=>(MimeTypeImage2.JPEG="image/jpeg",MimeTypeImage2.JPG="image/jpg",MimeTypeImage2.PNG="image/png",MimeTypeImage2.GIF="image/gif",MimeTypeImage2.WEBP="image/webp",MimeTypeImage2.SVG="image/svg+xml",MimeTypeImage2.ICO="image/x-icon",MimeTypeImage2.ICO_MICROSOFT="image/vnd.microsoft.icon",MimeTypeImage2))(MimeTypeImage||{}),MimeTypeText=(MimeTypeText2=>(MimeTypeText2.PLAIN="text/plain",MimeTypeText2. +MARKDOWN="text/markdown",MimeTypeText2.ASCIIDOC="text/asciidoc",MimeTypeText2.JAVASCRIPT="text/javascript",MimeTypeText2.JAVASCRIPT_APP="application/javascript",MimeTypeText2.TYPESCRIPT="text/typescript",MimeTypeText2.JSX="text/jsx",MimeTypeText2.TSX="text/tsx",MimeTypeText2.CSS="text/css",MimeTypeText2.HTML="text/html",MimeTypeText2.JSON="application/json",MimeTypeText2.XML_TEXT="text/xml",MimeTypeText2.XML_APP="application/xml",MimeTypeText2.YAML_TEXT="text/yaml",MimeTypeText2.YAML_APP="applic\ +ation/yaml",MimeTypeText2.CSV="text/csv",MimeTypeText2.PYTHON="text/x-python",MimeTypeText2.JAVA="text/x-java-source",MimeTypeText2.CPP_HDR="text/x-c++hdr",MimeTypeText2.CPP_SRC="text/x-c++src",MimeTypeText2.CSHARP="text/x-csharp",MimeTypeText2.HASKELL="text/x-haskell",MimeTypeText2.C_SRC="text/x-csrc",MimeTypeText2.C_HDR="text/x-chdr",MimeTypeText2.PHP="text/x-php",MimeTypeText2.RUBY="text/x-ruby",MimeTypeText2.GO="text/x-go",MimeTypeText2.RUST="text/x-rust",MimeTypeText2.SHELL="text/x-shellscr\ +ipt",MimeTypeText2.BAT="application/x-bat",MimeTypeText2.SQL="text/x-sql",MimeTypeText2.R="text/x-r",MimeTypeText2.SCALA="text/x-scala",MimeTypeText2.KOTLIN="text/x-kotlin",MimeTypeText2.SWIFT="text/x-swift",MimeTypeText2.DART="text/x-dart",MimeTypeText2.VUE="text/x-vue",MimeTypeText2.SVELTE="text/x-svelte",MimeTypeText2.TEX="text/x-tex",MimeTypeText2.TEX_APP="application/x-tex",MimeTypeText2.LATEX="application/x-latex",MimeTypeText2.BIBTEX="text/x-bibtex",MimeTypeText2.CUDA="text/x-cuda",MimeTypeText2. +PROPERTIES="text/properties",MimeTypeText2))(MimeTypeText||{}),MCPConnectionPhase=(MCPConnectionPhase2=>(MCPConnectionPhase2.IDLE="idle",MCPConnectionPhase2.TRANSPORT_CREATING="transport_creating",MCPConnectionPhase2.TRANSPORT_READY="transport_ready",MCPConnectionPhase2.INITIALIZING="initializing",MCPConnectionPhase2.CAPABILITIES_EXCHANGED="capabilities_exchanged",MCPConnectionPhase2.LISTING_TOOLS="listing_tools",MCPConnectionPhase2.CONNECTED="connected",MCPConnectionPhase2.ERROR="error",MCPConnectionPhase2. +DISCONNECTED="disconnected",MCPConnectionPhase2))(MCPConnectionPhase||{}),MCPLogLevel=(MCPLogLevel2=>(MCPLogLevel2.INFO="info",MCPLogLevel2.WARN="warn",MCPLogLevel2.ERROR="error",MCPLogLevel2))(MCPLogLevel||{}),MCPTransportType=(MCPTransportType2=>(MCPTransportType2.WEBSOCKET="websocket",MCPTransportType2.STREAMABLE_HTTP="streamable_http",MCPTransportType2.SSE="sse",MCPTransportType2))(MCPTransportType||{}),HealthCheckStatus=(HealthCheckStatus2=>(HealthCheckStatus2.IDLE="idle",HealthCheckStatus2. +CONNECTING="connecting",HealthCheckStatus2.SUCCESS="success",HealthCheckStatus2.ERROR="error",HealthCheckStatus2))(HealthCheckStatus||{}),MCPContentType=(MCPContentType2=>(MCPContentType2.TEXT="text",MCPContentType2.IMAGE="image",MCPContentType2.RESOURCE="resource",MCPContentType2))(MCPContentType||{}),JsonSchemaType=(JsonSchemaType2=>(JsonSchemaType2.OBJECT="object",JsonSchemaType2))(JsonSchemaType||{}),MCPRefType=(MCPRefType2=>(MCPRefType2.PROMPT="ref/prompt",MCPRefType2.RESOURCE="ref/resource", +MCPRefType2))(MCPRefType||{}),ModelModality=(ModelModality2=>(ModelModality2.TEXT="TEXT",ModelModality2.AUDIO="AUDIO",ModelModality2.VISION="VISION",ModelModality2))(ModelModality||{}),ServerRole=(ServerRole2=>(ServerRole2.MODEL="model",ServerRole2.ROUTER="router",ServerRole2))(ServerRole||{}),ServerModelStatus=(ServerModelStatus2=>(ServerModelStatus2.UNLOADED="unloaded",ServerModelStatus2.LOADING="loading",ServerModelStatus2.LOADED="loaded",ServerModelStatus2.SLEEPING="sleeping",ServerModelStatus2. +FAILED="failed",ServerModelStatus2))(ServerModelStatus||{}),ParameterSource=(ParameterSource2=>(ParameterSource2.DEFAULT="default",ParameterSource2.CUSTOM="custom",ParameterSource2))(ParameterSource||{}),SyncableParameterType=(SyncableParameterType2=>(SyncableParameterType2.NUMBER="number",SyncableParameterType2.STRING="string",SyncableParameterType2.BOOLEAN="boolean",SyncableParameterType2))(SyncableParameterType||{}),SettingsFieldType=(SettingsFieldType2=>(SettingsFieldType2.INPUT="input",SettingsFieldType2. +TEXTAREA="textarea",SettingsFieldType2.CHECKBOX="checkbox",SettingsFieldType2.SELECT="select",SettingsFieldType2))(SettingsFieldType||{}),ColorMode=(ColorMode2=>(ColorMode2.LIGHT="light",ColorMode2.DARK="dark",ColorMode2.SYSTEM="system",ColorMode2))(ColorMode||{}),TooltipSide=(TooltipSide2=>(TooltipSide2.TOP="top",TooltipSide2.RIGHT="right",TooltipSide2.BOTTOM="bottom",TooltipSide2.LEFT="left",TooltipSide2))(TooltipSide||{}),McpPromptVariant=(McpPromptVariant2=>(McpPromptVariant2.MESSAGE="messag\ +e",McpPromptVariant2.ATTACHMENT="attachment",McpPromptVariant2))(McpPromptVariant||{}),UrlProtocol=(UrlProtocol2=>(UrlProtocol2.DATA="data:",UrlProtocol2.HTTP="http:",UrlProtocol2.HTTPS="https:",UrlProtocol2.WEBSOCKET="ws:",UrlProtocol2.WEBSOCKET_SECURE="wss:",UrlProtocol2))(UrlProtocol||{}),HtmlInputType=(HtmlInputType2=>(HtmlInputType2.FILE="file",HtmlInputType2))(HtmlInputType||{}),KeyboardKey=(KeyboardKey2=>(KeyboardKey2.ENTER="Enter",KeyboardKey2.ESCAPE="Escape",KeyboardKey2.ARROW_UP="Arrow\ +Up",KeyboardKey2.ARROW_DOWN="ArrowDown",KeyboardKey2.ARROW_LEFT="ArrowLeft",KeyboardKey2.ARROW_RIGHT="ArrowRight",KeyboardKey2.TAB="Tab",KeyboardKey2.D_LOWER="d",KeyboardKey2.D_UPPER="D",KeyboardKey2.E_UPPER="E",KeyboardKey2.K_LOWER="k",KeyboardKey2.O_LOWER="o",KeyboardKey2.O_UPPER="O",KeyboardKey2.SPACE=" ",KeyboardKey2))(KeyboardKey||{}),ToolSource=(ToolSource2=>(ToolSource2.BUILTIN="builtin",ToolSource2.MCP="mcp",ToolSource2.CUSTOM="custom",ToolSource2))(ToolSource||{}),ToolPermissionDecision=(ToolPermissionDecision2=>(ToolPermissionDecision2. +ALWAYS="always",ToolPermissionDecision2.ALWAYS_SERVER="always_server",ToolPermissionDecision2.ONCE="once",ToolPermissionDecision2.DENY="deny",ToolPermissionDecision2))(ToolPermissionDecision||{}),ToolResponseField=(ToolResponseField2=>(ToolResponseField2.PLAIN_TEXT="plain_text_response",ToolResponseField2.ERROR="error",ToolResponseField2))(ToolResponseField||{}),root_5$w=from_html("

"),root_1$17=from_html(" ",1);function ActionIcon($$anchor,$$props){push$1($$props,!0);let variant=prop( +$$props,"variant",3,"ghost"),size2=prop($$props,"size",3,"sm"),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),iconSize=prop($$props,"iconSize",3,"h-3 w-3"),tooltipSide=prop($$props,"tooltipSide",19,()=>TooltipSide.TOP),stopPropagationOnClick=prop($$props,"stopPropagationOnClick",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$17(), +node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>$$props.ariaLabel||$$props.tooltip);Button($$anchor5,{get variant(){return variant()},get size(){return size2()},get disabled(){return disabled()},onclick:e=>{stopPropagationOnClick()&&e.stopPropagation(),$$props.onclick?.(e)},get class(){return`h-6 w-6 p-0 ${className()??""} flex hover:bg-transparent data-[state\ +=open]:bg-transparent!`},get"aria-label"(){return get$3($0)},children:($$anchor6,$$slotProps3)=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{var consequent=$$anchor7=>{const IconComponent=user_derived(()=>$$props.icon);var fragment_4=comment$2(),node_3=first_child(fragment_4);component(node_3,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{IconComponent_1($$anchor8,{get class(){return iconSize()}})}),append($$anchor7,fragment_4)};if_block(node_2,$$render=>{$$props.icon&&$$render( +consequent)})}append($$anchor6,fragment_3)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_4=sibling(node_1,2);component(node_4,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{get side(){return tooltipSide()},children:($$anchor5,$$slotProps2)=>{var p2=root_5$w(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltip)),append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor, +fragment),pop()}const defaultAttributes={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};var root$1T=from_svg("");function Icon($$anchor,$$props){push$1($$props,!0);const color=prop($$props,"color",3,"currentColor"),size2=prop($$props,"size",3,24),strokeWidth=prop($$props,"strokeWidth",3,2),absoluteStrokeWidth=prop($$props,"absoluteStrokeWidth",3,!1),iconNode=prop( +$$props,"iconNode",19,()=>[]),props=rest_props($$props,["$$slots","$$events","$$legacy","name","color","size","strokeWidth","absoluteStrokeWidth","iconNode","children"]);var svg2=root$1T();attribute_effect(svg2,$0=>({...defaultAttributes,...props,width:size2(),height:size2(),stroke:color(),"stroke-width":$0,class:["lucide-icon lucide",$$props.name&&`lucide-${$$props.name}`,$$props.class]}),[()=>absoluteStrokeWidth()?Number(strokeWidth())*24/Number(size2()):strokeWidth()]);var node2=child(svg2);each( +node2,17,iconNode,index$2,($$anchor2,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let tag=()=>get$3($$array)[0],attrs=()=>get$3($$array)[1];var fragment=comment$2(),node_1=first_child(fragment);element$4(node_1,tag,!0,($$element,$$anchor3)=>{attribute_effect($$element,()=>({...attrs()}))}),append($$anchor2,fragment)});var node_2=sibling(node2);snippet(node_2,()=>$$props.children??noop$3),reset(svg2),append($$anchor,svg2),pop()}function Arrow_big_up($$anchor,$$props){push$1($$props, +!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18v-6H5l7-7 7 7h-4v6H9z"}]];Icon($$anchor,spread_props({name:"arrow-big-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, +["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"m12 5 7 7-7 7"}]];Icon($$anchor,spread_props({name:"arrow-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$\ +$legacy"]);const iconNode=[["path",{d:"m5 12 7-7 7 7"}],["path",{d:"M12 19V5"}]];Icon($$anchor,spread_props({name:"arrow-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Book_open_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["path",{d:"M12 7v14"}],["path",{d:"M16 12h2"}],["path",{d:"M16 8h2"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],["path",{d:"M6 12h2"}],["path",{d:"M6 8h2"}]];Icon($$anchor,spread_props({name:"book-open-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( +$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Braces($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"}],["path",{d:"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"}]];Icon($$anchor,spread_props({name:"braces"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Brain($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3\ +-4 4.5 4.5 0 0 1-3 4"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516"}],["path",{d:"M19.967 17.484A4 4 0 0 1 18 18"}]];Icon($$anchor,spread_props({name:"brain"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. +children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Check($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 6 9 17l-5-5"}]];Icon($$anchor,spread_props({name:"check"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Chevron_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 9 6 6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_left($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15 18-6-6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, +["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m18 15-6-6-6 6"}]];Icon($$anchor,spread_props({name:"chevron-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["path",{d:"m9 18 6-6-6-6"}]];Icon($$anchor,spread_props({name:"chevron-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevrons_up_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m7 15 5 5 5-5"}],["path",{ +d:"m7 9 5-5 5 5"}]];Icon($$anchor,spread_props({name:"chevrons-up-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"\ +12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"circle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_check_big($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["path",{d:"M21.801 10A10 10 0 1 1 17 3.335"}],["path",{d:"m9 11 3 3L22 4"}]];Icon($$anchor,spread_props({name:"circle-check-big"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]];Icon($$anchor,spread_props({name:"circle-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Clock($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["circle",{cx:"12",cy:"12",r:"10"}],["polyline",{points:"12 6 12 12 16 14"}]];Icon($$anchor,spread_props({name:"clock"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Code($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ +m16 18 6-6-6-6"}],["path",{d:"m8 6-6 6 6 6"}]];Icon($$anchor,spread_props({name:"code"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Copy($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"14",height:"14",x:"8",y:"8", +rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]];Icon($$anchor,spread_props({name:"copy"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Database($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}]];Icon($$anchor,spread_props({name:"database"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Download($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots", +"$$events","$$legacy"]);const iconNode=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]];Icon($$anchor,spread_props({name:"download"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Ellipsis($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]];Icon($$anchor,spread_props({name:"ellipsis"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function External_link($$anchor,$$props){push$1($$props, +!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 3h6v6"}],["path",{d:"M10 14 21 3"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}]];Icon($$anchor,spread_props({name:"external-link"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Eye($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"eye"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, +$$slots:{default:!0}})),pop()}function File_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"M10 9H8"}],["path",{d:"M16 13H8"}],["path",{d:"M16 17H8"}]];Icon($$anchor,spread_props({name:"file-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"m14.5 12.5-5 5"}],["path",{d:"m9.5 12.5 5 5"}]];Icon($$anchor,spread_props({name:"file-x"},()=>props,{get iconNode(){ +return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}]];Icon($$anchor,spread_props({name:"file"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Flask_conical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"}],["\ +path",{d:"M6.453 15h11.094"}],["path",{d:"M8.5 2h7"}]];Icon($$anchor,spread_props({name:"flask-conical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Folder_open($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 14 \ +1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"}]];Icon($$anchor,spread_props({name:"folder-open"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Funnel($$anchor,$$props){push$1( +$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"}]];Icon($$anchor,spread_props({name:"funnel"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, +$$slots:{default:!0}})),pop()}function Gauge($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m12 14 4-4"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0"}]];Icon($$anchor,spread_props({name:"gauge"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), +pop()}function Git_branch($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"6",x2:"6",y1:"3",y2:"15"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}],["path",{d:"M18 9a9 9 0 0 1-9 9"}]];Icon($$anchor,spread_props({name:"git-branch"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? +noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Globe($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["path",{d:"M2 12h20"}]];Icon($$anchor,spread_props({name:"globe"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1); +snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"2",y1:"2",x2:"22",y2:"22"}],["path",{d:"M16.5 16.5 12 21l-7-7c-1.5-1.45-3-3.2-3-5.5a5.5 5.5 0 0 1 2.14-4.35"}],["path",{d:"M8.76 3.1c1.15.22 2.13.78 3.24 1.9 1.5-1.5 2.74-2 4.5-2A5.5 5.5 0 0 1 22 8.5c0 2.12-1.3 3.78-2.67 5.17"}]];Icon($$anchor,spread_props( +{name:"heart-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5\ + 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"}]];Icon($$anchor,spread_props({name:"heart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Image$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3", +rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]];Icon($$anchor,spread_props({name:"image"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Info$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legac\ +y"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 16v-4"}],["path",{d:"M12 8h.01"}]];Icon($$anchor,spread_props({name:"info"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Key($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]); +const iconNode=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"}],["path",{d:"m21 2-9.6 9.6"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5"}]];Icon($$anchor,spread_props({name:"key"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Layers($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"}]];Icon($$anchor,spread_props({name:"layers"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_checks($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m3 17 2 2 4-4"}],["path",{d:"m3 7 2 2 4-4"}],["path",{d:"M13 6h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 18h8"}]];Icon($$anchor,spread_props({name:"list-checks"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_restart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 6H3"}],["path",{d:"M7 12H3"}],["path",{d:"M7 18H3"}],["path",{d:"M12 18a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L11 14"}],["path",{d:"M11 10v4h4"}]];Icon($$anchor, +spread_props({name:"list-restart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Loader_circle($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]];Icon($$anchor,spread_props({name:"loader-c\ +ircle"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Message_square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"}]];Icon($$anchor,spread_props({name:"\ +message-square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Mic($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"}],["li\ +ne",{x1:"12",x2:"12",y1:"19",y2:"22"}]];Icon($$anchor,spread_props({name:"mic"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Minus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}]];Icon($$anchor,spread_props( +{name:"minus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Monitor($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21"}],["line",{x1:"12", +x2:"12",y1:"17",y2:"21"}]];Icon($$anchor,spread_props({name:"monitor"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Moon($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"}]];Icon($$anchor, +spread_props({name:"moon"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Music($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18V5l12-2v13"}],["circle",{cx:"6",cy:"18",r:"3"}],["circle",{cx:"18",cy:"16",r:"3"}]];Icon( +$$anchor,spread_props({name:"music"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Package($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-\ +2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"}],["path",{d:"M12 22V12"}],["polyline",{points:"3.29 7 12 12 20.71 7"}],["path",{d:"m7.5 4.27 9 5.15"}]];Icon($$anchor,spread_props({name:"package"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left_close($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m16 15-3-3 3-3"}]];Icon($$anchor,spread_props({name:"panel-left-close"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left($$anchor,$$props){push$1( +$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}]];Icon($$anchor,spread_props({name:"panel-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil_ruler($$anchor,$$props){push$1( +$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"}],["path",{d:"m8 6 2-2"}],["path",{d:"m18 16 2-2"}],["path",{d:"m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor, +spread_props({name:"pencil-ruler"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.\ +5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Plus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ +M5 12h14"}],["path",{d:"M12 5v14"}]];Icon($$anchor,spread_props({name:"plus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15"}],[ +"path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68"}],["path",{d:"M12 2v4"}],["path",{d:"m2 2 20 20"}]];Icon($$anchor,spread_props({name:"power-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["path",{d:"M12 2v10"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04"}]];Icon($$anchor,spread_props({name:"power"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Radio($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4.9 1\ +9.1C1 15.2 1 8.8 4.9 4.9"}],["path",{d:"M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"}],["path",{d:"M19.1 4.9C23 8.8 23 15.1 19.1 19"}]];Icon($$anchor,spread_props({name:"radio"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Refresh_cw($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]];Icon($$anchor,spread_props({name:"refresh-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? +noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_ccw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}]];Icon($$anchor,spread_props({name:"rotate-ccw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. +children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}]];Icon($$anchor,spread_props({name:"rotate-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2, +()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Search($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]];Icon($$anchor,spread_props({name:"search"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? +noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Server($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18"}]];Icon($$anchor,spread_props({name:"server"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Settings$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73\ + 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"settings"},()=>props,{get iconNode(){return iconNode}, +children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_question($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 \ +1 0 0 1 1 1z"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"shield-question"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sliders_vertical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","\ +$$legacy"]);const iconNode=[["line",{x1:"4",x2:"4",y1:"21",y2:"14"}],["line",{x1:"4",x2:"4",y1:"10",y2:"3"}],["line",{x1:"12",x2:"12",y1:"21",y2:"12"}],["line",{x1:"12",x2:"12",y1:"8",y2:"3"}],["line",{x1:"20",x2:"20",y1:"21",y2:"16"}],["line",{x1:"20",x2:"20",y1:"12",y2:"3"}],["line",{x1:"2",x2:"6",y1:"14",y2:"14"}],["line",{x1:"10",x2:"14",y1:"8",y2:"8"}],["line",{x1:"18",x2:"22",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"sliders-vertical"},()=>props,{get iconNode(){return iconNode}, +children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sparkles($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6\ +.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"}],["path",{d:"M20 3v4"}],["path",{d:"M22 5h-4"}],["path",{d:"M4 17v2"}],["path",{d:"M5 18H3"}]];Icon($$anchor,spread_props({name:"sparkles"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square_pen($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"}]];Icon($$anchor,spread_props({name:"square-pen"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2, +()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]];Icon($$anchor,spread_props({name:"square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( +$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sun($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 2v2"}],["path",{d:"M12 20v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"m17.66 17.66 1.41 1.41"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"m6.34 17.66-1.41 1.41"}],["path",{d:"m19.07 4.93-1.41 1.41"}]];Icon($$anchor,spread_props({name:"sun"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Timer_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 2h4"}],["path",{d:"M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7"}],["path",{d:"M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0\ + 0 1 .9 10.2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M12 12v-2"}]];Icon($$anchor,spread_props({name:"timer-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Trash_2($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M\ +3 6h18"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17"}]];Icon($$anchor,spread_props({name:"trash-2"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Triangle_alert($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"}],["path",{d:"M12 9v4"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"triangle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Upload($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3v12"}],["path",{d:"m17 8-5-5-5 5"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}]];Icon($$anchor,spread_props({name:"upload"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, +fragment_1)},$$slots:{default:!0}})),pop()}function Whole_word($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"7",cy:"12",r:"3"}],["path",{d:"M10 9v6"}],["circle",{cx:"17",cy:"12",r:"3"}],["path",{d:"M14 7v8"}],["path",{d:"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1"}]];Icon($$anchor,spread_props({name:"whole-word"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Wrench($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"}]];Icon($$anchor,spread_props({name:"wrench"},()=>props,{get iconNode(){ +return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function X($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]];Icon($$anchor,spread_props({name:"x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Zap($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}]];Icon($$anchor,spread_props({name:"zap"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}const ATTACHMENT_SAVED_REGEX=/\[Attachment saved: ([^\]]+)\]/,NEWLINE_SEPARATOR=` `,LLM_ERROR_BLOCK_START=` \`\`\` @@ -6533,126 +6533,127 @@ return showModelSelector()},get uploadedFiles(){return uploadedFiles()},onFileUp node_6,{get preSelectedUri(){return get$3(preSelectedResourceUri)},onAttach:resource=>{mcpStore.attachResource(resource.uri)},onOpenChange:newOpen=>{newOpen||set$1(preSelectedResourceUri,void 0)},get open(){return get$3(isResourceDialogOpen)},set open($$value){set$1(isResourceDialogOpen,$$value,!0)}}),template_effect(()=>{set_class(form,1,`relative ${className()??""}`),set_class(div,1,`${INPUT_CLASSES??""} overflow-hidden rounded-3xl backdrop-blur-md ${disabled()?"cursor-not-allowed opacity-60": ""}`)}),event("submit",form,event2=>{event2.preventDefault(),!(!get$3(canSubmit)||disabled()||get$3(hasLoadingAttachments))&&$$props.onSubmit?.()}),event("paste",div_1,handlePaste),append($$anchor,fragment),pop($$exports)}function Dropdown_menu_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,4),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","sideOffset","portalProps","class"]);var fragment=comment$2(),node2=first_child( fragment);component(node2,()=>Portal$2,($$anchor2,DropdownMenuPrimitive_Portal)=>{DropdownMenuPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-po\ -pover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node_1,()=>Dropdown_menu_content$1,($$anchor4,DropdownMenuPrimitive_Content)=>{ -DropdownMenuPrimitive_Content($$anchor4,spread_props({"data-slot":"dropdown-menu-content",get sideOffset(){return sideOffset()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_item($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),variant=prop($$props,"variant",3,"default"),restProps=rest_props($$props,[ -"$$slots","$$events","$$legacy","ref","class","inset","variant"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destr\ -uctive/10 data-[variant=destructive]:data-highlighted:text-destructive dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:!text-destructive",$$props.class));component(node2,()=>Menu_item,($$anchor2,DropdownMenuPrimitive_Item)=>{DropdownMenuPrimitive_Item($$anchor2,spread_props({"data-slot":"dropdown-menu-item",get"\ -data-inset"(){return $$props.inset},get"data-variant"(){return variant()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_separator($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("-mx-1 my-1 h-px b\ -g-border/20",$$props.class));component(node2,()=>Menu_separator,($$anchor2,DropdownMenuPrimitive_Separator)=>{DropdownMenuPrimitive_Separator($$anchor2,spread_props({"data-slot":"dropdown-menu-separator",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events", -"$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Menu_trigger,($$anchor2,DropdownMenuPrimitive_Trigger)=>{DropdownMenuPrimitive_Trigger($$anchor2,spread_props({"data-slot":"dropdown-menu-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_sub_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$\ -$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[\ -side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node2,()=>Menu_sub_content,($$anchor2,DropdownMenuPrimitive_SubContent)=>{DropdownMenuPrimitive_SubContent($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()}, -set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$N=from_html(" ",1);function Dropdown_menu_sub_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","children"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-hig\ -hlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",$$props.class));component(node2,()=>Menu_sub_trigger,($$anchor2,DropdownMenuPrimitive_SubTrigger)=>{DropdownMenuPrimitive_SubTrigger($$anchor2,spread_props( -{"data-slot":"dropdown-menu-sub-trigger",get"data-inset"(){return $$props.inset},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$N(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_right(node_2,{class:"ml-auto size-4"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}const Sub=Menu_sub, -Root$4=Menu;function useAttachmentMenu(getFlags,getCallbacks,close2){const modalityFlags=user_derived(getFlags),callbacks=user_derived(()=>{const cbs=getCallbacks(),wrap2=fn=>()=>{close2(),fn?.()};return{[AttachmentAction.FILE_UPLOAD]:wrap2(cbs.onFileUpload),[AttachmentAction.SYSTEM_PROMPT_CLICK]:wrap2(cbs.onSystemPromptClick),[AttachmentAction.MCP_PROMPT_CLICK]:wrap2(cbs.onMcpPromptClick),[AttachmentAction.MCP_RESOURCES_CLICK]:wrap2(cbs.onMcpResourcesClick)}});function isItemEnabled(enabledWhen){ -return!enabledWhen||enabledWhen==="always"?!0:!!get$3(modalityFlags)[enabledWhen]}function isItemVisible(visibleWhen){return visibleWhen?!!get$3(modalityFlags)[visibleWhen]:!0}function getSystemMessageTooltip(){return page$1.params.id?"Inject custom system message at the beginning of the conversation":"Add custom system message for a new conversation"}return{get callbacks(){return get$3(callbacks)},isItemEnabled,isItemVisible,getSystemMessageTooltip}}var root_6$t=from_html(" ",1), -root_10$e=from_html(" ",1),root_11$e=from_html("

"),root_8$q=from_html(" ",1),root_16$5=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$b=from_html(" ",1),root_22$1=from_html(" ",1),root_23$5=from_html("

"),root_20$4=from_html(" ",1),root_26$1=from_html(" ",1),root_3$R=from_html(" ",1),root_1$M=from_html( -" ",1),root$1r=from_html("
");function ChatFormActionAddDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),dropdownOpen=state$1(!1);function handleMcpSettingsClick(){ -set$1(dropdownOpen,!1),$$props.onMcpSettingsClick?.()}const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(dropdownOpen,!1)});var div=root$1r(),node2=child( -div);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$M(),node_1=first_child(fragment);component(node_1,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor4,{name:"Attach files",get disabled(){return disabled()},children:($$anchor5,$$slotProps2)=>{var fragment_1=comment$2(),node_2=first_child( -fragment_1);snippet(node_2,()=>$$props.trigger,()=>({disabled:disabled()})),append($$anchor5,fragment_1)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_content,($$anchor4,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor4,{align:"start",class:"w-48",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_3$R(),node_4=first_child(fragment_2);each(node_4,17,()=>ATTACHMENT_FILE_ITEMS,item=>item.id,($$anchor6,item)=>{const enabled=user_derived(()=>attachmentMenu. -isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_5=first_child(fragment_3);{var consequent=$$anchor7=>{var fragment_4=comment$2(),node_6=first_child(fragment_4);{let $0=user_derived(()=>get$3(item).class??"");component(node_6,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor8,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{ -var fragment_5=root_6$t(),node_7=first_child(fragment_5);component(node_7,()=>get$3(item).icon,($$anchor10,item_icon)=>{item_icon($$anchor10,{class:"h-4 w-4"})});var span=sibling(node_7,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,get$3(item).label)),append($$anchor9,fragment_5)},$$slots:{default:!0}})})}append($$anchor7,fragment_4)},consequent_1=$$anchor7=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);component(node_8,()=>Root$5,($$anchor8,Tooltip_Root)=>{ -Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_7=root_8$q(),node_9=first_child(fragment_7);component(node_9,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);{let $0=user_derived(()=>get$3(item).class??"");component(node_10,()=>Dropdown_menu_item,($$anchor12,DropdownMenu_Item_1)=>{DropdownMenu_Item_1( -$$anchor12,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},disabled:!0,children:($$anchor13,$$slotProps5)=>{var fragment_9=root_10$e(),node_11=first_child(fragment_9);component(node_11,()=>get$3(item).icon,($$anchor14,item_icon_1)=>{item_icon_1($$anchor14,{class:"h-4 w-4"})});var span_1=sibling(node_11,2),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,get$3(item).label)),append($$anchor13,fragment_9)},$$slots:{default:!0}})})}append($$anchor11, -fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7,fragment_6)};if_block(node_5,$$render=>{get$3(enabled)? -$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_4,2);{var consequent_3=$$anchor6=>{var fragment_10=comment$2(),node_14=first_child(fragment_10);component(node_14,()=>Root$5,($$anchor7,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor7,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor8,$$slotProps3)=>{var fragment_11=root_13$b(),node_15=first_child(fragment_11);component(node_15,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger_1)=>{ -Tooltip_Trigger_1($$anchor9,{class:"w-full",children:($$anchor10,$$slotProps4)=>{var fragment_12=comment$2(),node_16=first_child(fragment_12);component(node_16,()=>Dropdown_menu_item,($$anchor11,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor11,{class:"flex cursor-pointer items-center gap-2",get onclick(){return attachmentMenu.callbacks.onFileUpload},children:($$anchor12,$$slotProps5)=>{const pdfItem=user_derived(()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_13=comment$2(), -node_17=first_child(fragment_13);{var consequent_2=$$anchor13=>{var fragment_14=root_16$5(),node_18=first_child(fragment_14);component(node_18,()=>get$3(pdfItem).icon,($$anchor14,pdfItem_icon)=>{pdfItem_icon($$anchor14,{class:"h-4 w-4"})});var span_2=sibling(node_18,2),text_3=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_3,get$3(pdfItem).label)),append($$anchor13,fragment_14)};if_block(node_17,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor12,fragment_13)}, -$$slots:{default:!0}})}),append($$anchor10,fragment_12)},$$slots:{default:!0}})});var node_19=sibling(node_15,2);component(node_19,()=>Tooltip_content,($$anchor9,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor9,{side:"right",children:($$anchor10,$$slotProps4)=>{var p_1=root_17$8();append($$anchor10,p_1)},$$slots:{default:!0}})}),append($$anchor8,fragment_11)},$$slots:{default:!0}})}),append($$anchor6,fragment_10)},d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13, -$$render=>{get$3(d2)&&$$render(consequent_3)})}var node_20=sibling(node_13,2);component(node_20,()=>Dropdown_menu_separator,($$anchor6,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor6,{})});var node_21=sibling(node_20,2);each(node_21,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_15=comment$2(),node_22=first_child(fragment_15);{var consequent_4=$$anchor7=>{var fragment_16=comment$2(),node_23=first_child(fragment_16);component(node_23,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{ -Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_17=root_20$4(),node_24=first_child(fragment_17);component(node_24,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_18=comment$2(),node_25=first_child(fragment_18);component(node_25,()=>Dropdown_menu_item,($$anchor12,DropdownMenu_Item_3)=>{DropdownMenu_Item_3($$anchor12,{class:"\ -flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor13,$$slotProps5)=>{var fragment_19=root_22$1(),node_26=first_child(fragment_19);component(node_26,()=>get$3(item).icon,($$anchor14,item_icon_2)=>{item_icon_2($$anchor14,{class:"h-4 w-4"})});var span_3=sibling(node_26,2),text_4=child(span_3,!0);reset(span_3),template_effect(()=>set_text(text_4,get$3(item).label)),append($$anchor13,fragment_19)},$$slots:{default:!0}})}),append($$anchor11, -fragment_18)},$$slots:{default:!0}})});var node_27=sibling(node_24,2);component(node_27,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_2=root_23$5(),text_5=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_5,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)},$$slots:{default:!0}})}),append($$anchor9,fragment_17)},$$slots:{default:!0}})}),append($$anchor7,fragment_16)};if_block( -node_22,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_15)});var node_28=sibling(node_21,2);ChatFormActionAddToolsSubmenu(node_28,{});var node_29=sibling(node_28,2);ChatFormActionAddMcpServersSubmenu(node_29,{onMcpSettingsClick:handleMcpSettingsClick});var node_30=sibling(node_29,2);each(node_30,17,()=>ATTACHMENT_MCP_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_20=comment$2(),node_31=first_child(fragment_20);{var consequent_5=$$anchor7=>{ -var fragment_21=comment$2(),node_32=first_child(fragment_21);component(node_32,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_4)=>{DropdownMenu_Item_4($$anchor8,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{var fragment_22=root_26$1(),node_33=first_child(fragment_22);component(node_33,()=>get$3(item).icon,($$anchor10,item_icon_3)=>{item_icon_3($$anchor10,{class:"h-4 w-4"})});var span_4=sibling(node_33, -2),text_6=child(span_4,!0);reset(span_4),template_effect(()=>set_text(text_6,get$3(item).label)),append($$anchor9,fragment_22)},$$slots:{default:!0}})}),append($$anchor7,fragment_21)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_31,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_20)}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class( -div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}function Sheet_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class)); -component(node2,()=>Dialog_overlay$1,($$anchor2,SheetPrimitive_Overlay)=>{SheetPrimitive_Overlay($$anchor2,spread_props({"data-slot":"sheet-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const sheetVariants=tv({base:`border-border/30 dark:border-border/20 data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-sm transition ease-in-out data-[state=close\ -d]:duration-300 data-[state=open]:duration-500 ${PANEL_CLASSES}`,variants:{side:{top:"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",bottom:"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",left:"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",right:"data-[state=closed]:slide-out-to-right data-[\ -state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"}},defaultVariants:{side:"right"}});var root_3$Q=from_html(' Close',1),root_2$10=from_html(" ",1),root_1$L=from_html(" ",1);function Sheet_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),side=prop($$props,"side",3,"right"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","side","portalProps","children"]);var fragment=comment$2(), -node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SheetPrimitive_Portal)=>{SheetPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$L(),node_1=first_child(fragment_1);Sheet_overlay(node_1,{});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1(sheetVariants({side:side()}),$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,SheetPrimitive_Content)=>{SheetPrimitive_Content($$anchor4,spread_props( -{"data-slot":"sheet-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$10(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3);var node_4=sibling(node_3,2);component(node_4,()=>Dialog_close,($$anchor6,SheetPrimitive_Close)=>{SheetPrimitive_Close($$anchor6,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover\ -:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none",children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$Q(),node_5=first_child(fragment_3);X(node_5,{class:"size-4"}),next$1(2),append($$anchor7,fragment_3)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root$1q=from_html("");function Sheet_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$1q();attribute_effect(div,$0=>({"data-slot":"sheet-header",class:$0,...restProps}),[()=>cn$1("flex flex-col gap-1.5 p-4",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()} -function Sheet_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("font-semibold text-foreground",$$props.class));component(node2,()=>Dialog_title$1,($$anchor2,SheetPrimitive_Title)=>{SheetPrimitive_Title($$anchor2,spread_props({"data-slot":"sheet-title",get class(){return get$3($0)}},()=>restProps,{get ref(){ -return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Sheet_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,SheetPrimitive_Description)=>{SheetPrimitive_Description( -$$anchor2,spread_props({"data-slot":"sheet-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$3=Dialog;var root_3$P=from_html(" ",1),root_7$r=from_html(''),root_10$d=from_html(''),root_11$d=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(' '),root_16$4=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$6=from_html(" ",1),root_20$3=from_html(''),root_21$4=from_html("

"),root_19$9=from_html(" ",1),root_23$4=from_html(''),root_2$$=from_html(' ',1),root_1$K=from_html(" ",1),root$1p=from_html("
");function ChatFormActionAddSheet($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props, -"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),sheetOpen=state$1(!1);const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props. -onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(sheetOpen,!1)}),sheetItemClass="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allowed disabled:opacity-50";var div=root$1p(),node2=child(div);component(node2,()=>Root$3,($$anchor2,Sheet_Root)=>{Sheet_Root($$anchor2,{get open(){return get$3(sheetOpen)},set open($$value){set$1(sheetOpen, -$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$K(),node_1=first_child(fragment);snippet(node_1,()=>$$props.trigger,()=>({disabled:disabled(),onclick:()=>set$1(sheetOpen,!0)}));var node_2=sibling(node_1,2);component(node_2,()=>Sheet_content,($$anchor4,Sheet_Content)=>{Sheet_Content($$anchor4,{side:"bottom",class:"max-h-[85vh] gap-0 overflow-y-auto",children:($$anchor5,$$slotProps2)=>{var fragment_1=root_2$$(),node_3=first_child(fragment_1);component(node_3,()=>Sheet_header,($$anchor6,Sheet_Header)=>{ -Sheet_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_2=root_3$P(),node_4=first_child(fragment_2);component(node_4,()=>Sheet_title,($$anchor8,Sheet_Title)=>{Sheet_Title($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text2=text$8("Add to chat");append($$anchor9,text2)},$$slots:{default:!0}})});var node_5=sibling(node_4,2);component(node_5,()=>Sheet_description,($$anchor8,Sheet_Description)=>{Sheet_Description($$anchor8,{class:"sr-only",children:($$anchor9,$$slotProps4)=>{ -next$1();var text_1=text$8("Add files, system prompt or configure MCP servers");append($$anchor9,text_1)},$$slots:{default:!0}})}),append($$anchor7,fragment_2)},$$slots:{default:!0}})});var div_1=sibling(node_3,2),node_6=child(div_1);each(node_6,17,()=>ATTACHMENT_FILE_ITEMS,item=>item.id,($$anchor6,item)=>{const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_7=first_child(fragment_3);{var consequent=$$anchor7=>{var button=root_7$r(); -set_class(button,1,clsx(sheetItemClass));var node_8=child(button);component(node_8,()=>get$3(item).icon,($$anchor8,item_icon)=>{item_icon($$anchor8,{class:"h-4 w-4 shrink-0"})});var span=sibling(node_8,2),text_2=child(span,!0);reset(span),reset(button),template_effect(()=>set_text(text_2,get$3(item).label)),delegated("click",button,()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor7,button)},consequent_1=$$anchor7=>{var fragment_4=comment$2(),node_9=first_child(fragment_4);component( -node_9,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_5=root_9$k(),node_10=first_child(fragment_5);component(node_10,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_1=root_10$d();set_class(button_1,1,clsx(sheetItemClass));var node_11=child(button_1);component(node_11,()=>get$3(item).icon,($$anchor12,item_icon_1)=>{ -item_icon_1($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_1=sibling(node_11,2),text_3=child(span_1,!0);reset(span_1),reset(button_1),template_effect(()=>set_text(text_3,get$3(item).label)),append($$anchor11,button_1)},$$slots:{default:!0}})});var node_12=sibling(node_10,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$d(),text_4=child(p2,!0);reset(p2),template_effect(()=>set_text( -text_4,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_4)};if_block(node_7,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_6,2);{var consequent_3=$$anchor6=>{const pdfItem=user_derived(()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_6=comment$2(), -node_14=first_child(fragment_6);{var consequent_2=$$anchor7=>{var fragment_7=comment$2(),node_15=first_child(fragment_7);component(node_15,()=>Root$5,($$anchor8,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_8=root_14$6(),node_16=first_child(fragment_8);component(node_16,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_2=root_15$9(); -set_class(button_2,1,clsx(sheetItemClass));var node_17=child(button_2);component(node_17,()=>get$3(pdfItem).icon,($$anchor12,pdfItem_icon)=>{pdfItem_icon($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_2=sibling(node_17,2),text_5=child(span_2,!0);reset(span_2),reset(button_2),template_effect(()=>set_text(text_5,get$3(pdfItem).label)),delegated("click",button_2,()=>attachmentMenu.callbacks[get$3(pdfItem).action]()),append($$anchor11,button_2)},$$slots:{default:!0}})});var node_18=sibling(node_16, -2);component(node_18,()=>Tooltip_content,($$anchor10,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_1=root_16$4();append($$anchor11,p_1)},$$slots:{default:!0}})}),append($$anchor9,fragment_8)},$$slots:{default:!0}})}),append($$anchor7,fragment_7)};if_block(node_14,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor6,fragment_6)},d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13,$$render=>{ -get$3(d2)&&$$render(consequent_3)})}var node_19=sibling(node_13,2);each(node_19,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_9=comment$2(),node_20=first_child(fragment_9);{var consequent_4=$$anchor7=>{var fragment_10=comment$2(),node_21=first_child(fragment_10);component(node_21,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_11=root_19$9(),node_22=first_child( -fragment_11);component(node_22,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_3=root_20$3();set_class(button_3,1,clsx(sheetItemClass));var node_23=child(button_3);component(node_23,()=>get$3(item).icon,($$anchor12,item_icon_2)=>{item_icon_2($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_3=sibling(node_23,2),text_6=child(span_3,!0);reset(span_3),reset(button_3),template_effect(()=>set_text(text_6,get$3(item).label)), -delegated("click",button_3,()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor11,button_3)},$$slots:{default:!0}})});var node_24=sibling(node_22,2);component(node_24,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_2=root_21$4(),text_7=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_7,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)},$$slots:{default:!0}})}), -append($$anchor9,fragment_11)},$$slots:{default:!0}})}),append($$anchor7,fragment_10)};if_block(node_20,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_9)});var a=sibling(node_19,4),node_25=child(a);McpLogo(node_25,{class:"inline h-4 w-4"}),next$1(2),reset(a);var a_1=sibling(a,2),node_26=child(a_1);Pencil_ruler(node_26,{class:"inline h-4 w-4"}),next$1(2),reset(a_1);var node_27=sibling(a_1,2);each(node_27,17,()=>ATTACHMENT_MCP_ITEMS, -item=>item.id,($$anchor6,item)=>{var fragment_12=comment$2(),node_28=first_child(fragment_12);{var consequent_5=$$anchor7=>{var button_4=root_23$4();set_class(button_4,1,clsx(sheetItemClass));var node_29=child(button_4);component(node_29,()=>get$3(item).icon,($$anchor8,item_icon_3)=>{item_icon_3($$anchor8,{class:"h-4 w-4 shrink-0"})});var span_4=sibling(node_29,2),text_8=child(span_4,!0);reset(span_4),reset(button_4),template_effect(()=>set_text(text_8,get$3(item).label)),delegated("click",button_4, -()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor7,button_4)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_28,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_12)}),reset(div_1),append($$anchor5,fragment_1)},$$slots:{default:!0}})}),append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}delegate( -["click"]);var root_3$O=from_html(' ',1),root_4$z=from_html("

"),root_1$J=from_html(" ",1);function ChatFormActionAddButton($$anchor,$$props){let disabled=prop($$props,"disabled",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$J(),node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{ -Tooltip_Trigger($$anchor4,{class:"w-full",children:($$anchor5,$$slotProps2)=>{Button($$anchor5,{class:"file-upload-button h-8 w-8 rounded-full p-0",get disabled(){return disabled()},get onclick(){return $$props.onclick},variant:"secondary",type:"button",children:($$anchor6,$$slotProps3)=>{var fragment_3=root_3$O(),span=first_child(fragment_3),text2=child(span,!0);reset(span);var node_2=sibling(span,2);Plus(node_2,{class:"h-4 w-4"}),template_effect(()=>set_text(text2,ATTACHMENT_TOOLTIP_TEXT)),append( -$$anchor6,fragment_3)},$$slots:{default:!0}})},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{children:($$anchor5,$$slotProps2)=>{var p2=root_4$z(),text_1=child(p2,!0);reset(p2),template_effect(()=>set_text(text_1,ATTACHMENT_TOOLTIP_TEXT)),append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment)}function ChatFormActionsAdd($$anchor,$$props){ -push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1);const isMobile=new IsMobile;var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{ChatFormActionAddSheet($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){ -return hasAudioModality()},get hasVisionModality(){return hasVisionModality()},get hasMcpPromptsSupport(){return hasMcpPromptsSupport()},get hasMcpResourcesSupport(){return hasMcpResourcesSupport()},get onFileUpload(){return $$props.onFileUpload},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){return $$props.onMcpResourcesClick},trigger:($$anchor3,$$arg0)=>{let disabled2=()=>$$arg0?.().disabled,onclick=()=>$$arg0?.().onclick;ChatFormActionAddButton($$anchor3,{get disabled(){ -return disabled2()},get onclick(){return onclick()}})},$$slots:{trigger:!0}})},alternate=$$anchor2=>{ChatFormActionAddDropdown($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){return hasAudioModality()},get hasVisionModality(){return hasVisionModality()},get hasMcpPromptsSupport(){return hasMcpPromptsSupport()},get hasMcpResourcesSupport(){return hasMcpResourcesSupport()},get onFileUpload(){return $$props.onFileUpload},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){ -return $$props.onMcpResourcesClick},get onMcpSettingsClick(){return $$props.onMcpSettingsClick},get onSystemPromptClick(){return $$props.onSystemPromptClick},trigger:$$anchor3=>{ChatFormActionAddButton($$anchor3,{get disabled(){return disabled()}})},$$slots:{trigger:!0}})};if_block(node2,$$render=>{isMobile.current?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_3$N=from_html(' ',1),root_7$q=from_html("

Current model does \ -not support audio

"),root_1$I=from_html(" ",1),root$1o=from_html("
");function ChatFormActionRecord($$anchor,$$props){let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),isLoading2=prop($$props,"isLoading",3,!1),isRecording=prop($$props,"isRecording",3,!1);var div=root$1o(),node2=child(div);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{ -var fragment=root_1$I(),node_1=first_child(fragment);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>isRecording()?"animate-pulse bg-red-500 text-white hover:bg-red-600":""),$1=user_derived(()=>disabled()||isLoading2()||!hasAudioModality());Button($$anchor5,{get class(){return`h-8 w-8 rounded-full p-0 ${get$3($0)??""}`},get disabled(){return get$3($1)},get onclick(){return $$props.onMicClick}, -type:"button",children:($$anchor6,$$slotProps3)=>{var fragment_2=root_3$N(),span=first_child(fragment_2),text2=child(span,!0);reset(span);var node_2=sibling(span,2);{var consequent=$$anchor7=>{Square($$anchor7,{class:"h-4 w-4 animate-pulse fill-white"})},alternate=$$anchor7=>{Mic($$anchor7,{class:"h-4 w-4"})};if_block(node_2,$$render=>{isRecording()?$$render(consequent):$$render(alternate,-1)})}template_effect(()=>set_text(text2,isRecording()?"Stop recording":"Start recording")),append($$anchor6, -fragment_2)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_3=sibling(node_1,2);{var consequent_1=$$anchor4=>{var fragment_5=comment$2(),node_4=first_child(fragment_5);component(node_4,()=>Tooltip_content,($$anchor5,Tooltip_Content)=>{Tooltip_Content($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p2=root_7$q();append($$anchor6,p2)},$$slots:{default:!0}})}),append($$anchor4,fragment_5)};if_block(node_3,$$render=>{hasAudioModality()||$$render(consequent_1)})}append($$anchor3,fragment)}, -$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div)}var root_1$H=from_html('
'),root_4$y=from_html('Stop ',1),root$1n=from_html('
');function ChatFormActions($$anchor,$$props){push$1($$props,!0);let canSend=prop($$props,"canSend",3,!1),canSubmit=prop($$props,"canSubmit", -3,!1),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),isLoading2=prop($$props,"isLoading",3,!1),isRecording=prop($$props,"isRecording",3,!1),showAddButton=prop($$props,"showAddButton",3,!0),showModelSelector=prop($$props,"showModelSelector",3,!0),uploadedFiles=prop($$props,"uploadedFiles",19,()=>[]),currentConfig=user_derived(config$1),hasMcpPromptsSupport=user_derived(()=>{const perChatOverrides=conversationsStore.getAllMcpServerOverrides();return mcpStore.hasPromptsCapability( -perChatOverrides)}),hasMcpResourcesSupport=user_derived(()=>{const perChatOverrides=conversationsStore.getAllMcpServerOverrides();return mcpStore.hasResourcesCapability(perChatOverrides)}),hasAudioModality=state$1(!1),hasVisionModality=state$1(!1),hasModelSelected=state$1(!1),isSelectedModelInCache=state$1(!0),submitTooltip=state$1(""),hasAudioAttachments=user_derived(()=>uploadedFiles().some(file=>getFileTypeCategory(file.type)===FileTypeCategory.AUDIO)),shouldShowRecordButton=user_derived(()=>get$3( -hasAudioModality)&&!canSubmit()&&!get$3(hasAudioAttachments)&&get$3(currentConfig).autoMicOnEmpty),selectorModelRef=state$1(void 0);function openModelSelector(){get$3(selectorModelRef)?.open()}var $$exports={openModelSelector},div=root$1n(),node2=child(div);{var consequent=$$anchor2=>{var div_1=root_1$H(),node_1=child(div_1);ChatFormActionsAdd(node_1,{get disabled(){return disabled()},get hasAudioModality(){return get$3(hasAudioModality)},get hasVisionModality(){return get$3(hasVisionModality)}, -get hasMcpPromptsSupport(){return get$3(hasMcpPromptsSupport)},get hasMcpResourcesSupport(){return get$3(hasMcpResourcesSupport)},get onFileUpload(){return $$props.onFileUpload},get onSystemPromptClick(){return $$props.onSystemPromptClick},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){return $$props.onMcpResourcesClick},onMcpSettingsClick:()=>goto("#/settings/mcp")}),reset(div_1),append($$anchor2,div_1)};if_block(node2,$$render=>{showAddButton()&&$$render(consequent)})} -var node_2=sibling(node2,2);{var consequent_1=$$anchor2=>{bind_this(ChatFormActionModels($$anchor2,{get disabled(){return disabled()},forceForegroundText:!0,useGlobalSelection:!0,get hasAudioModality(){return get$3(hasAudioModality)},set hasAudioModality($$value){set$1(hasAudioModality,$$value,!0)},get hasVisionModality(){return get$3(hasVisionModality)},set hasVisionModality($$value){set$1(hasVisionModality,$$value,!0)},get hasModelSelected(){return get$3(hasModelSelected)},set hasModelSelected($$value){ -set$1(hasModelSelected,$$value,!0)},get isSelectedModelInCache(){return get$3(isSelectedModelInCache)},set isSelectedModelInCache($$value){set$1(isSelectedModelInCache,$$value,!0)},get submitTooltip(){return get$3(submitTooltip)},set submitTooltip($$value){set$1(submitTooltip,$$value,!0)}}),$$value=>set$1(selectorModelRef,$$value,!0),()=>get$3(selectorModelRef))};if_block(node_2,$$render=>{showModelSelector()&&$$render(consequent_1)})}var node_3=sibling(node_2,2);{var consequent_2=$$anchor2=>{Button( -$$anchor2,{type:"button",variant:"secondary",get onclick(){return $$props.onStop},class:"group h-8 w-8 rounded-full p-0 hover:bg-destructive/10!",children:($$anchor3,$$slotProps)=>{var fragment_2=root_4$y(),node_4=sibling(first_child(fragment_2),2);Square(node_4,{class:"h-8 w-8 fill-muted-foreground stroke-muted-foreground group-hover:fill-destructive group-hover:stroke-destructive hover:fill-destructive hover:stroke-destructive"}),append($$anchor3,fragment_2)},$$slots:{default:!0}})},consequent_3=$$anchor2=>{ -ChatFormActionRecord($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){return get$3(hasAudioModality)},get isLoading(){return isLoading2()},get isRecording(){return isRecording()},get onMicClick(){return $$props.onMicClick}})},alternate=$$anchor2=>{{let $0=user_derived(()=>canSend()&&(showModelSelector()?get$3(hasModelSelected)&&get$3(isSelectedModelInCache):!0)),$1=user_derived(()=>showModelSelector()&&get$3(hasModelSelected)&&!get$3(isSelectedModelInCache));ChatFormActionSubmit( -$$anchor2,{get canSend(){return get$3($0)},get disabled(){return disabled()},get tooltipLabel(){return get$3(submitTooltip)},get showErrorState(){return get$3($1)}})}};if_block(node_3,$$render=>{isLoading2()&&!canSubmit()?$$render(consequent_2):get$3(shouldShowRecordButton)?$$render(consequent_3,1):$$render(alternate,-1)})}return reset(div),template_effect(()=>set_class(div,1,`flex w-full items-center gap-3 ${className()??""} ${showAddButton()?"":"justify-end"}`)),append($$anchor,div),pop($$exports)} -var root_2$_=from_html('Send ',1),root_6$s=from_html("

"),root_4$x=from_html(" ",1);function ChatFormActionSubmit($$anchor,$$props){const submitButton=($$anchor2,$$arg0)=>{let props=derived_safe_equal(()=>fallback($$arg0?.(),()=>({}),!0));{let $0=user_derived(()=>["h-8 w-8 rounded-full p-0",showErrorState()&&"bg-red-400/10 text-red-400 hover:bg-red-400/20 hover:text-red-400 disabled:opacity-100"]);Button($$anchor2,spread_props({type:"submit",get disabled(){ -return get$3(isDisabled)},get class(){return get$3($0)}},()=>get$3(props),{children:($$anchor3,$$slotProps)=>{var fragment_1=root_2$_(),node2=sibling(first_child(fragment_1),2);Arrow_up(node2,{class:"h-12 w-12"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))}};let canSend=prop($$props,"canSend",3,!1),disabled=prop($$props,"disabled",3,!1),showErrorState=prop($$props,"showErrorState",3,!1),isDisabled=user_derived(()=>!canSend()||disabled());var fragment_2=comment$2(),node_1=first_child(fragment_2); -{var consequent=$$anchor2=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);component(node_2,()=>Root$5,($$anchor3,Tooltip_Root)=>{Tooltip_Root($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_4=root_4$x(),node_3=first_child(fragment_4);component(node_3,()=>Tooltip_trigger,($$anchor5,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor5,{children:($$anchor6,$$slotProps2)=>{submitButton($$anchor6)},$$slots:{default:!0}})});var node_4=sibling(node_3,2);component(node_4,()=>Tooltip_content, -($$anchor5,Tooltip_Content)=>{Tooltip_Content($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p2=root_6$s(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltipLabel)),append($$anchor6,p2)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})}),append($$anchor2,fragment_3)},alternate=$$anchor2=>{submitButton($$anchor2)};if_block(node_1,$$render=>{$$props.tooltipLabel?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2)}function ChatFormActionModels($$anchor,$$props){ -push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),forceForegroundText=prop($$props,"forceForegroundText",3,!1),hasAudioModality=prop($$props,"hasAudioModality",15,!1),hasVisionModality=prop($$props,"hasVisionModality",15,!1),hasModelSelected=prop($$props,"hasModelSelected",15,!1),isSelectedModelInCache=prop($$props,"isSelectedModelInCache",15,!0),submitTooltip=prop($$props,"submitTooltip",15,""),useGlobalSelection=prop($$props,"useGlobalSelection",3,!1),isRouter=user_derived(isRouterMode), -isOffline=user_derived(()=>!!serverError()),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),lastSyncedConversationModel=null;user_effect(()=>{if(get$3(conversationModel)&&get$3(conversationModel)!==lastSyncedConversationModel)lastSyncedConversationModel=get$3(conversationModel),modelsStore.selectModelByName(get$3(conversationModel));else if(get$3(isRouter)&&!modelsStore.selectedModelId&&modelsStore.loadedModelIds.length>0){lastSyncedConversationModel=null;const first=modelOptions(). -find(m=>modelsStore.loadedModelIds.includes(m.model));first&&modelsStore.selectModelById(first.id)}});let activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId();if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3(conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model)return model.model}return null}),modelPropsVersion=state$1( -0);user_effect(()=>{get$3(activeModelId)&&(modelsStore.getModelProps(get$3(activeModelId))||modelsStore.fetchModelProps(get$3(activeModelId)).then(()=>{update$1(modelPropsVersion)}))}),user_effect(()=>{hasAudioModality(get$3(activeModelId)?modelsStore.modelSupportsAudio(get$3(activeModelId)):!1)}),user_effect(()=>{get$3(modelPropsVersion),hasVisionModality(get$3(activeModelId)?modelsStore.modelSupportsVision(get$3(activeModelId)):!1)}),user_effect(()=>{hasModelSelected(!get$3(isRouter)||!!get$3( -conversationModel)||!!selectedModelId())}),user_effect(()=>{if(!get$3(isRouter))isSelectedModelInCache(!0);else if(get$3(conversationModel))isSelectedModelInCache(modelOptions().some(option2=>option2.model===get$3(conversationModel)));else{const currentModelId=selectedModelId();isSelectedModelInCache(currentModelId?modelOptions().some(option2=>option2.id===currentModelId):!1)}}),user_effect(()=>{hasModelSelected()?isSelectedModelInCache()?submitTooltip(""):submitTooltip("Selected model is not av\ -ailable, please select another"):submitTooltip("Please select a model first")});let selectorModelRef=state$1(void 0),isMobile=new IsMobile;function open2(){get$3(selectorModelRef)?.open()}var $$exports={open:open2},fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{{let $0=user_derived(()=>disabled()||get$3(isOffline));bind_this(ModelsSelectorSheet($$anchor2,{get disabled(){return get$3($0)},get currentModel(){return $$props.currentModel},get forceForegroundText(){return forceForegroundText()}, -get useGlobalSelection(){return useGlobalSelection()}}),$$value=>set$1(selectorModelRef,$$value,!0),()=>get$3(selectorModelRef))}},alternate=$$anchor2=>{{let $0=user_derived(()=>disabled()||get$3(isOffline));bind_this(ModelsSelectorDropdown($$anchor2,{get disabled(){return get$3($0)},get currentModel(){return $$props.currentModel},get forceForegroundText(){return forceForegroundText()},get useGlobalSelection(){return useGlobalSelection()}}),$$value=>set$1(selectorModelRef,$$value,!0),()=>get$3(selectorModelRef))}}; -if_block(node2,$$render=>{isMobile.current?$$render(consequent):$$render(alternate,-1)})}return append($$anchor,fragment),pop($$exports)}function Collapsible($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),open2=prop($$props,"open",15,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","open"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible$1,($$anchor2,CollapsiblePrimitive_Root)=>{CollapsiblePrimitive_Root($$anchor2, -spread_props({"data-slot":"collapsible"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},get open(){return open2()},set open($$value){open2($$value)}}))}),append($$anchor,fragment),pop()}function Collapsible_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible_trigger$1,($$anchor2,CollapsiblePrimitive_Trigger)=>{ -CollapsiblePrimitive_Trigger($$anchor2,spread_props({"data-slot":"collapsible-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Collapsible_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible_content$1,($$anchor2,CollapsiblePrimitive_Content)=>{ -CollapsiblePrimitive_Content($$anchor2,spread_props({"data-slot":"collapsible-content"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function useToolsPanel(){const expandedGroups=new SvelteSet,groups=user_derived(()=>toolsStore.toolGroups),activeGroups=user_derived(()=>get$3(groups).filter(g=>g.source!==ToolSource.MCP||!g.serverId||conversationsStore.isMcpServerEnabledForChat(g.serverId))),totalToolCount=user_derived(()=>get$3(activeGroups). -reduce((n,g)=>n+g.tools.length,0)),noToolsInfoMessage=user_derived(()=>toolsStore.loading||toolsStore.toolGroups.length>0?null:toolsStore.isToolsEndpointUnreachable?"To enable Built-In Tools you need to run llama-server with --tools all or --tools flag. To see MCP Tools you need to add / enable MCP Server(s).":toolsStore.error?null:"To enable Built-In Tools you need to run llama-server with --tools all or --tools flag. To see MCP Tools you need to add / enable MCP Server(s).");function getGroupCheckedState(group){ -return{checked:toolsStore.isGroupFullyEnabled(group),indeterminate:toolsStore.isGroupPartiallyEnabled(group)}}function getEnabledToolCount(group){return group.tools.filter(tool=>toolsStore.isToolEnabled(tool.function.name)).length}function getFavicon(group){if(group.source!==ToolSource.MCP)return null;for(const server of mcpStore.getServersSorted())if(mcpStore.getServerLabel(server)===group.label)return mcpStore.getServerFavicon(server.id);return null}function isGroupDisabled(group){return group. -source===ToolSource.MCP&&!!group.serverId&&!conversationsStore.isMcpServerEnabledForChat(group.serverId)}function toggleGroupExpanded(label){expandedGroups.has(label)?expandedGroups.delete(label):expandedGroups.add(label)}function handleOpen(){toolsStore.builtinTools.length===0&&!toolsStore.loading&&toolsStore.fetchBuiltinTools(),mcpStore.runHealthChecksForServers(mcpStore.getServersSorted().filter(s2=>s2.enabled))}return{expandedGroups,get groups(){return get$3(groups)},get activeGroups(){return get$3( -activeGroups)},get totalToolCount(){return get$3(totalToolCount)},get noToolsInfoMessage(){return get$3(noToolsInfoMessage)},getGroupCheckedState,getEnabledToolCount,getFavicon,isGroupDisabled,toggleGroupExpanded,handleOpen}}var root_2$Z=from_html(" Tools",1),root_5$s=from_html('
Loading tools...
'),root_6$r=from_html('
Run llama-server with --tools flag to enable Built-in Tools. MCP Tools.
'),root_7$p=from_html('
Failed to load tools
'),root_8$p=from_html('
'),root_9$j=from_html('
No tools available
'),root_16$3=from_html(''),root_13$a=from_html(' ',1),root_19$8=from_html("

"),root_17$7=from_html(" ",1),root_21$3=from_html(''),root_20$2=from_html('
'),root_12$8=from_html('
',1),root_10$c=from_html('
'),root_1$G=from_html(" ",1);function ChatFormActionAddToolsSubmenu($$anchor,$$props){push$1($$props,!0);const toolsPanel=useToolsPanel(), -hasMcpServersAvailable=user_derived(()=>mcpStore.getServersSorted().length>0);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Sub,($$anchor2,DropdownMenu_Sub)=>{DropdownMenu_Sub($$anchor2,{onOpenChange:open2=>open2&&toolsPanel.handleOpen(),children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$G(),node_1=first_child(fragment_1);component(node_1,()=>Dropdown_menu_sub_trigger,($$anchor4,DropdownMenu_SubTrigger)=>{DropdownMenu_SubTrigger($$anchor4,{class:"flex cursor-poin\ -ter items-center gap-2",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$Z(),node_2=first_child(fragment_2);Pencil_ruler(node_2,{class:"h-4 w-4"}),next$1(2),append($$anchor5,fragment_2)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_sub_content,($$anchor4,DropdownMenu_SubContent)=>{DropdownMenu_SubContent($$anchor4,{class:"w-72 p-0",children:($$anchor5,$$slotProps2)=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);{var consequent_4=$$anchor6=>{ -var fragment_4=comment$2(),node_5=first_child(fragment_4);{var consequent=$$anchor7=>{var div=root_5$s(),node_6=child(div);Loader_circle(node_6,{class:"mx-auto mb-1 h-4 w-4 animate-spin"}),next$1(),reset(div),append($$anchor7,div)},consequent_1=$$anchor7=>{var div_1=root_6$r(),span=child(div_1),node_7=child(span);Info$1(node_7,{class:"mt-0.5 h-4 w-4 shrink-0"}),next$1(2),reset(span);var span_1=sibling(span,2),node_8=child(span_1);Info$1(node_8,{class:"mt-0.5 h-4 w-4 shrink-0"});var span_2=sibling( -node_8,2),text2=child(span_2);next$1(2),reset(span_2),reset(span_1),reset(div_1),template_effect(()=>set_text(text2,`${get$3(hasMcpServersAvailable)?"Enable":"Add"} MCP Server(s) to access `)),append($$anchor7,div_1)},consequent_2=$$anchor7=>{var div_2=root_7$p();append($$anchor7,div_2)},consequent_3=$$anchor7=>{var div_3=root_8$p(),node_9=child(div_3);Info$1(node_9,{class:"mt-0.5 h-4 w-4 shrink-0"});var span_3=sibling(node_9,2),text_1=child(span_3,!0);reset(span_3),reset(div_3),template_effect( -()=>set_text(text_1,toolsPanel.noToolsInfoMessage)),append($$anchor7,div_3)},alternate=$$anchor7=>{var div_4=root_9$j();append($$anchor7,div_4)};if_block(node_5,$$render=>{toolsStore.loading?$$render(consequent):toolsStore.isToolsEndpointUnreachable?$$render(consequent_1,1):toolsStore.error?$$render(consequent_2,2):toolsPanel.noToolsInfoMessage?$$render(consequent_3,3):$$render(alternate,-1)})}append($$anchor6,fragment_4)},alternate_2=$$anchor6=>{var div_5=root_10$c();each(div_5,21,()=>toolsPanel. -activeGroups,group=>group.label,($$anchor7,group)=>{const isExpanded=user_derived(()=>toolsPanel.expandedGroups.has(get$3(group).label)),computed_const=user_derived(()=>{const{checked,indeterminate}=toolsPanel.getGroupCheckedState(get$3(group));return{checked,indeterminate}}),favicon=user_derived(()=>toolsPanel.getFavicon(get$3(group)));var fragment_5=comment$2(),node_10=first_child(fragment_5);component(node_10,()=>Collapsible,($$anchor8,Collapsible_Root)=>{Collapsible_Root($$anchor8,{get open(){ -return get$3(isExpanded)},onOpenChange:()=>toolsPanel.toggleGroupExpanded(get$3(group).label),children:($$anchor9,$$slotProps3)=>{var fragment_6=root_12$8(),div_6=first_child(fragment_6),node_11=child(div_6);component(node_11,()=>Collapsible_trigger,($$anchor10,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor10,{class:"flex min-w-0 flex-1 items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50",children:($$anchor11,$$slotProps4)=>{var fragment_7=root_13$a(),node_12=first_child(fragment_7); -{var consequent_5=$$anchor12=>{Chevron_down($$anchor12,{class:"h-3.5 w-3.5 shrink-0"})},alternate_1=$$anchor12=>{Chevron_right($$anchor12,{class:"h-3.5 w-3.5 shrink-0"})};if_block(node_12,$$render=>{get$3(isExpanded)?$$render(consequent_5):$$render(alternate_1,-1)})}var span_4=sibling(node_12,2),node_13=child(span_4);{var consequent_6=$$anchor12=>{var img=root_16$3();template_effect(()=>set_attribute(img,"src",get$3(favicon))),event("error",img,e=>{e.currentTarget.style.display="none"}),replay_events( -img),append($$anchor12,img)};if_block(node_13,$$render=>{get$3(favicon)&&$$render(consequent_6)})}var span_5=sibling(node_13,2),text_2=child(span_5,!0);reset(span_5),reset(span_4);var span_6=sibling(span_4,2),text_3=child(span_6);reset(span_6),template_effect($0=>{set_text(text_2,get$3(group).label),set_text(text_3,`${$0??""}/${get$3(group).tools.length??""}`)},[()=>toolsPanel.getEnabledToolCount(get$3(group))]),append($$anchor11,fragment_7)},$$slots:{default:!0}})});var node_14=sibling(node_11, -2);component(node_14,()=>Root$5,($$anchor10,Tooltip_Root)=>{Tooltip_Root($$anchor10,{children:($$anchor11,$$slotProps4)=>{var fragment_10=root_17$7(),node_15=first_child(fragment_10);component(node_15,()=>Tooltip_trigger,($$anchor12,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor12,{children:($$anchor13,$$slotProps5)=>{Checkbox($$anchor13,{get checked(){return get$3(computed_const).checked},get indeterminate(){return get$3(computed_const).indeterminate},onCheckedChange:()=>toolsStore.toggleGroup(get$3( -group)),class:"mr-2 h-4 w-4 shrink-0"})},$$slots:{default:!0}})});var node_16=sibling(node_15,2);component(node_16,()=>Tooltip_content,($$anchor12,Tooltip_Content)=>{Tooltip_Content($$anchor12,{side:"right",children:($$anchor13,$$slotProps5)=>{var p2=root_19$8(),text_4=child(p2);reset(p2),template_effect(()=>set_text(text_4,`${get$3(computed_const).checked?"Disable":"Enable"} +pover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node_1,()=>Dropdown_menu_content$1, +($$anchor4,DropdownMenuPrimitive_Content)=>{DropdownMenuPrimitive_Content($$anchor4,spread_props({"data-slot":"dropdown-menu-content",get sideOffset(){return sideOffset()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_item($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),variant=prop($$props,"variant", +3,"default"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","variant"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[var\ +iant=destructive]:data-highlighted:bg-destructive/10 data-[variant=destructive]:data-highlighted:text-destructive dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:!text-destructive",$$props.class));component(node2,()=>Menu_item,($$anchor2,DropdownMenuPrimitive_Item)=>{DropdownMenuPrimitive_Item($$anchor2,spread_props( +{"data-slot":"dropdown-menu-item",get"data-inset"(){return $$props.inset},get"data-variant"(){return variant()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_separator($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived( +()=>cn$1("-mx-1 my-1 h-px bg-border/20",$$props.class));component(node2,()=>Menu_separator,($$anchor2,DropdownMenuPrimitive_Separator)=>{DropdownMenuPrimitive_Separator($$anchor2,spread_props({"data-slot":"dropdown-menu-separator",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props, +["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Menu_trigger,($$anchor2,DropdownMenuPrimitive_Trigger)=>{DropdownMenuPrimitive_Trigger($$anchor2,spread_props({"data-slot":"dropdown-menu-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_sub_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props( +$$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-\ +in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node2,()=>Menu_sub_content,($$anchor2,DropdownMenuPrimitive_SubContent)=>{DropdownMenuPrimitive_SubContent($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-content",get class(){ +return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$N=from_html(" ",1);function Dropdown_menu_sub_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","children"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("flex cursor-default items-center gap-2 rounded\ +-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",$$props.class));component(node2,()=>Menu_sub_trigger,($$anchor2,DropdownMenuPrimitive_SubTrigger)=>{ +DropdownMenuPrimitive_SubTrigger($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-trigger",get"data-inset"(){return $$props.inset},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$N(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_right(node_2,{class:"ml-auto size-4"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})} +append($$anchor,fragment),pop()}const Sub=Menu_sub,Root$4=Menu;function useAttachmentMenu(getFlags,getCallbacks,close2){const modalityFlags=user_derived(getFlags),callbacks=user_derived(()=>{const cbs=getCallbacks(),wrap2=fn=>()=>{close2(),fn?.()};return{[AttachmentAction.FILE_UPLOAD]:wrap2(cbs.onFileUpload),[AttachmentAction.SYSTEM_PROMPT_CLICK]:wrap2(cbs.onSystemPromptClick),[AttachmentAction.MCP_PROMPT_CLICK]:wrap2(cbs.onMcpPromptClick),[AttachmentAction.MCP_RESOURCES_CLICK]:wrap2(cbs.onMcpResourcesClick)}}); +function isItemEnabled(enabledWhen){return!enabledWhen||enabledWhen==="always"?!0:!!get$3(modalityFlags)[enabledWhen]}function isItemVisible(visibleWhen){return visibleWhen?!!get$3(modalityFlags)[visibleWhen]:!0}function getSystemMessageTooltip(){return page$1.params.id?"Inject custom system message at the beginning of the conversation":"Add custom system message for a new conversation"}return{get callbacks(){return get$3(callbacks)},isItemEnabled,isItemVisible,getSystemMessageTooltip}}var root_6$t=from_html( +" ",1),root_10$e=from_html(" ",1),root_11$e=from_html("

"),root_8$q=from_html(" ",1),root_16$5=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$b=from_html(" ",1),root_22$1=from_html(" ",1),root_23$5=from_html("

"),root_20$4=from_html(" ",1),root_26$1=from_html(" ",1),root_3$R=from_html(" \ + ",1),root_1$M=from_html(" ",1),root$1r=from_html("
");function ChatFormActionAddDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),dropdownOpen=state$1(!1); +function handleMcpSettingsClick(){set$1(dropdownOpen,!1),$$props.onMcpSettingsClick?.()}const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(dropdownOpen, +!1)});var div=root$1r(),node2=child(div);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$M(),node_1=first_child(fragment);component(node_1,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor4,{name:"Attach files",get disabled(){return disabled()},children:($$anchor5,$$slotProps2)=>{ +var fragment_1=comment$2(),node_2=first_child(fragment_1);snippet(node_2,()=>$$props.trigger,()=>({disabled:disabled()})),append($$anchor5,fragment_1)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_content,($$anchor4,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor4,{align:"start",class:"w-48",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_3$R(),node_4=first_child(fragment_2);each(node_4,17,()=>ATTACHMENT_FILE_ITEMS,item=>item.id,($$anchor6,item)=>{ +const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_5=first_child(fragment_3);{var consequent=$$anchor7=>{var fragment_4=comment$2(),node_6=first_child(fragment_4);{let $0=user_derived(()=>get$3(item).class??"");component(node_6,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor8,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},onclick:()=>attachmentMenu.callbacks[get$3(item). +action](),children:($$anchor9,$$slotProps3)=>{var fragment_5=root_6$t(),node_7=first_child(fragment_5);component(node_7,()=>get$3(item).icon,($$anchor10,item_icon)=>{item_icon($$anchor10,{class:"h-4 w-4"})});var span=sibling(node_7,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,get$3(item).label)),append($$anchor9,fragment_5)},$$slots:{default:!0}})})}append($$anchor7,fragment_4)},consequent_1=$$anchor7=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);component( +node_8,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_7=root_8$q(),node_9=first_child(fragment_7);component(node_9,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);{let $0=user_derived(()=>get$3(item).class??"");component(node_10,()=>Dropdown_menu_item, +($$anchor12,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor12,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},disabled:!0,children:($$anchor13,$$slotProps5)=>{var fragment_9=root_10$e(),node_11=first_child(fragment_9);component(node_11,()=>get$3(item).icon,($$anchor14,item_icon_1)=>{item_icon_1($$anchor14,{class:"h-4 w-4"})});var span_1=sibling(node_11,2),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,get$3(item).label)),append($$anchor13, +fragment_9)},$$slots:{default:!0}})})}append($$anchor11,fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7, +fragment_6)};if_block(node_5,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_4,2);{var consequent_3=$$anchor6=>{var fragment_10=comment$2(),node_14=first_child(fragment_10);component(node_14,()=>Root$5,($$anchor7,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor7,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor8,$$slotProps3)=>{var fragment_11=root_13$b(),node_15=first_child( +fragment_11);component(node_15,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor9,{class:"w-full",children:($$anchor10,$$slotProps4)=>{var fragment_12=comment$2(),node_16=first_child(fragment_12);component(node_16,()=>Dropdown_menu_item,($$anchor11,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor11,{class:"flex cursor-pointer items-center gap-2",get onclick(){return attachmentMenu.callbacks.onFileUpload},children:($$anchor12,$$slotProps5)=>{const pdfItem=user_derived( +()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_13=comment$2(),node_17=first_child(fragment_13);{var consequent_2=$$anchor13=>{var fragment_14=root_16$5(),node_18=first_child(fragment_14);component(node_18,()=>get$3(pdfItem).icon,($$anchor14,pdfItem_icon)=>{pdfItem_icon($$anchor14,{class:"h-4 w-4"})});var span_2=sibling(node_18,2),text_3=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_3,get$3(pdfItem).label)),append($$anchor13,fragment_14)};if_block( +node_17,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor12,fragment_13)},$$slots:{default:!0}})}),append($$anchor10,fragment_12)},$$slots:{default:!0}})});var node_19=sibling(node_15,2);component(node_19,()=>Tooltip_content,($$anchor9,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor9,{side:"right",children:($$anchor10,$$slotProps4)=>{var p_1=root_17$8();append($$anchor10,p_1)},$$slots:{default:!0}})}),append($$anchor8,fragment_11)},$$slots:{default:!0}})}),append($$anchor6,fragment_10)}, +d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13,$$render=>{get$3(d2)&&$$render(consequent_3)})}var node_20=sibling(node_13,2);component(node_20,()=>Dropdown_menu_separator,($$anchor6,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor6,{})});var node_21=sibling(node_20,2);each(node_21,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_15=comment$2(),node_22=first_child(fragment_15);{var consequent_4=$$anchor7=>{var fragment_16=comment$2(), +node_23=first_child(fragment_16);component(node_23,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_17=root_20$4(),node_24=first_child(fragment_17);component(node_24,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_18=comment$2(),node_25=first_child(fragment_18);component(node_25,()=>Dropdown_menu_item, +($$anchor12,DropdownMenu_Item_3)=>{DropdownMenu_Item_3($$anchor12,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor13,$$slotProps5)=>{var fragment_19=root_22$1(),node_26=first_child(fragment_19);component(node_26,()=>get$3(item).icon,($$anchor14,item_icon_2)=>{item_icon_2($$anchor14,{class:"h-4 w-4"})});var span_3=sibling(node_26,2),text_4=child(span_3,!0);reset(span_3),template_effect(()=>set_text(text_4,get$3(item).label)), +append($$anchor13,fragment_19)},$$slots:{default:!0}})}),append($$anchor11,fragment_18)},$$slots:{default:!0}})});var node_27=sibling(node_24,2);component(node_27,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_2=root_23$5(),text_5=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_5,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)},$$slots:{default:!0}})}),append($$anchor9, +fragment_17)},$$slots:{default:!0}})}),append($$anchor7,fragment_16)};if_block(node_22,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_15)});var node_28=sibling(node_21,2);ChatFormActionAddToolsSubmenu(node_28,{});var node_29=sibling(node_28,2);ChatFormActionAddMcpServersSubmenu(node_29,{onMcpSettingsClick:handleMcpSettingsClick});var node_30=sibling(node_29,2);each(node_30,17,()=>ATTACHMENT_MCP_ITEMS,item=>item.id,($$anchor6,item)=>{ +var fragment_20=comment$2(),node_31=first_child(fragment_20);{var consequent_5=$$anchor7=>{var fragment_21=comment$2(),node_32=first_child(fragment_21);component(node_32,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_4)=>{DropdownMenu_Item_4($$anchor8,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{var fragment_22=root_26$1(),node_33=first_child(fragment_22);component(node_33,()=>get$3(item).icon, +($$anchor10,item_icon_3)=>{item_icon_3($$anchor10,{class:"h-4 w-4"})});var span_4=sibling(node_33,2),text_6=child(span_4,!0);reset(span_4),template_effect(()=>set_text(text_6,get$3(item).label)),append($$anchor9,fragment_22)},$$slots:{default:!0}})}),append($$anchor7,fragment_21)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_31,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_20)}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}), +append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}function Sheet_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data\ +-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,SheetPrimitive_Overlay)=>{SheetPrimitive_Overlay($$anchor2,spread_props({"data-slot":"sheet-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const sheetVariants=tv({base:`border-border/30 dark:border-border/20 da\ +ta-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fixed z-50 flex flex-col gap-4 shadow-sm transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 ${PANEL_CLASSES}`,variants:{side:{top:"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",bottom:"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",left:"\ +data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",right:"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"}},defaultVariants:{side:"right"}});var root_3$Q=from_html(' Close',1),root_2$10=from_html(" ",1),root_1$L=from_html(" ",1);function Sheet_content($$anchor,$$props){push$1($$props,!0);let ref2=prop( +$$props,"ref",15,null),side=prop($$props,"side",3,"right"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","side","portalProps","children"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SheetPrimitive_Portal)=>{SheetPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$L(),node_1=first_child(fragment_1);Sheet_overlay(node_1,{});var node_2=sibling(node_1, +2);{let $0=user_derived(()=>cn$1(sheetVariants({side:side()}),$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,SheetPrimitive_Content)=>{SheetPrimitive_Content($$anchor4,spread_props({"data-slot":"sheet-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$10(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3);var node_4=sibling(node_3,2);component( +node_4,()=>Dialog_close,($$anchor6,SheetPrimitive_Close)=>{SheetPrimitive_Close($$anchor6,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none",children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$Q(),node_5=first_child(fragment_3);X(node_5,{class:"size-4"}),next$1(2),append($$anchor7,fragment_3)},$$slots:{ +default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root$1q=from_html("
");function Sheet_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$1q();attribute_effect(div,$0=>({"data-slot":"sheet-header",class:$0,...restProps}),[()=>cn$1("flex fle\ +x-col gap-1.5 p-4",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Sheet_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("font-semibold text-foreground",$$props.class));component( +node2,()=>Dialog_title$1,($$anchor2,SheetPrimitive_Title)=>{SheetPrimitive_Title($$anchor2,spread_props({"data-slot":"sheet-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Sheet_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment); +{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,SheetPrimitive_Description)=>{SheetPrimitive_Description($$anchor2,spread_props({"data-slot":"sheet-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$3=Dialog;var root_3$P=from_html(" ",1),root_7$r=from_html(''),root_11$d=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(''),root_16$4=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$6=from_html(" ",1),root_20$3=from_html(''),root_21$4=from_html("

"),root_19$9=from_html(" ",1),root_23$4=from_html(''),root_2$$=from_html(' ',1),root_1$K=from_html(" ",1),root$1p=from_html("
\ +
");function ChatFormActionAddSheet($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),sheetOpen=state$1(!1);const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(), +hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(sheetOpen,!1)}),sheetItemClass="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allo\ +wed disabled:opacity-50";var div=root$1p(),node2=child(div);component(node2,()=>Root$3,($$anchor2,Sheet_Root)=>{Sheet_Root($$anchor2,{get open(){return get$3(sheetOpen)},set open($$value){set$1(sheetOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$K(),node_1=first_child(fragment);snippet(node_1,()=>$$props.trigger,()=>({disabled:disabled(),onclick:()=>set$1(sheetOpen,!0)}));var node_2=sibling(node_1,2);component(node_2,()=>Sheet_content,($$anchor4,Sheet_Content)=>{Sheet_Content( +$$anchor4,{side:"bottom",class:"max-h-[85vh] gap-0 overflow-y-auto",children:($$anchor5,$$slotProps2)=>{var fragment_1=root_2$$(),node_3=first_child(fragment_1);component(node_3,()=>Sheet_header,($$anchor6,Sheet_Header)=>{Sheet_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_2=root_3$P(),node_4=first_child(fragment_2);component(node_4,()=>Sheet_title,($$anchor8,Sheet_Title)=>{Sheet_Title($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text2=text$8("Add to chat");append( +$$anchor9,text2)},$$slots:{default:!0}})});var node_5=sibling(node_4,2);component(node_5,()=>Sheet_description,($$anchor8,Sheet_Description)=>{Sheet_Description($$anchor8,{class:"sr-only",children:($$anchor9,$$slotProps4)=>{next$1();var text_1=text$8("Add files, system prompt or configure MCP servers");append($$anchor9,text_1)},$$slots:{default:!0}})}),append($$anchor7,fragment_2)},$$slots:{default:!0}})});var div_1=sibling(node_3,2),node_6=child(div_1);each(node_6,17,()=>ATTACHMENT_FILE_ITEMS,item=>item. +id,($$anchor6,item)=>{const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_7=first_child(fragment_3);{var consequent=$$anchor7=>{var button=root_7$r();set_class(button,1,clsx(sheetItemClass));var node_8=child(button);component(node_8,()=>get$3(item).icon,($$anchor8,item_icon)=>{item_icon($$anchor8,{class:"h-4 w-4 shrink-0"})});var span=sibling(node_8,2),text_2=child(span,!0);reset(span),reset(button),template_effect(()=>set_text(text_2, +get$3(item).label)),delegated("click",button,()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor7,button)},consequent_1=$$anchor7=>{var fragment_4=comment$2(),node_9=first_child(fragment_4);component(node_9,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_5=root_9$k(),node_10=first_child(fragment_5);component(node_10,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger( +$$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_1=root_10$d();set_class(button_1,1,clsx(sheetItemClass));var node_11=child(button_1);component(node_11,()=>get$3(item).icon,($$anchor12,item_icon_1)=>{item_icon_1($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_1=sibling(node_11,2),text_3=child(span_1,!0);reset(span_1),reset(button_1),template_effect(()=>set_text(text_3,get$3(item).label)),append($$anchor11,button_1)},$$slots:{default:!0}})});var node_12=sibling(node_10,2);component(node_12, +()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$d(),text_4=child(p2,!0);reset(p2),template_effect(()=>set_text(text_4,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_4)};if_block(node_7,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append( +$$anchor6,fragment_3)});var node_13=sibling(node_6,2);{var consequent_3=$$anchor6=>{const pdfItem=user_derived(()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_6=comment$2(),node_14=first_child(fragment_6);{var consequent_2=$$anchor7=>{var fragment_7=comment$2(),node_15=first_child(fragment_7);component(node_15,()=>Root$5,($$anchor8,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_8=root_14$6(), +node_16=first_child(fragment_8);component(node_16,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_2=root_15$9();set_class(button_2,1,clsx(sheetItemClass));var node_17=child(button_2);component(node_17,()=>get$3(pdfItem).icon,($$anchor12,pdfItem_icon)=>{pdfItem_icon($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_2=sibling(node_17,2),text_5=child(span_2,!0);reset(span_2),reset(button_2),template_effect(()=>set_text( +text_5,get$3(pdfItem).label)),delegated("click",button_2,()=>attachmentMenu.callbacks[get$3(pdfItem).action]()),append($$anchor11,button_2)},$$slots:{default:!0}})});var node_18=sibling(node_16,2);component(node_18,()=>Tooltip_content,($$anchor10,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_1=root_16$4();append($$anchor11,p_1)},$$slots:{default:!0}})}),append($$anchor9,fragment_8)},$$slots:{default:!0}})}),append($$anchor7,fragment_7)}; +if_block(node_14,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor6,fragment_6)},d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13,$$render=>{get$3(d2)&&$$render(consequent_3)})}var node_19=sibling(node_13,2);each(node_19,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_9=comment$2(),node_20=first_child(fragment_9);{var consequent_4=$$anchor7=>{var fragment_10=comment$2(),node_21=first_child(fragment_10);component( +node_21,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_11=root_19$9(),node_22=first_child(fragment_11);component(node_22,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_3=root_20$3();set_class(button_3,1,clsx(sheetItemClass));var node_23=child(button_3);component(node_23,()=>get$3(item).icon,($$anchor12,item_icon_2)=>{ +item_icon_2($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_3=sibling(node_23,2),text_6=child(span_3,!0);reset(span_3),reset(button_3),template_effect(()=>set_text(text_6,get$3(item).label)),delegated("click",button_3,()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor11,button_3)},$$slots:{default:!0}})});var node_24=sibling(node_22,2);component(node_24,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{ +var p_2=root_21$4(),text_7=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_7,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)},$$slots:{default:!0}})}),append($$anchor9,fragment_11)},$$slots:{default:!0}})}),append($$anchor7,fragment_10)};if_block(node_20,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_9)});var a=sibling(node_19,4),node_25=child(a);McpLogo(node_25,{class:"inline h-4 w-4"}),next$1( +2),reset(a);var a_1=sibling(a,2),node_26=child(a_1);Pencil_ruler(node_26,{class:"inline h-4 w-4"}),next$1(2),reset(a_1);var node_27=sibling(a_1,2);each(node_27,17,()=>ATTACHMENT_MCP_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_12=comment$2(),node_28=first_child(fragment_12);{var consequent_5=$$anchor7=>{var button_4=root_23$4();set_class(button_4,1,clsx(sheetItemClass));var node_29=child(button_4);component(node_29,()=>get$3(item).icon,($$anchor8,item_icon_3)=>{item_icon_3($$anchor8,{class:"\ +h-4 w-4 shrink-0"})});var span_4=sibling(node_29,2),text_8=child(span_4,!0);reset(span_4),reset(button_4),template_effect(()=>set_text(text_8,get$3(item).label)),delegated("click",button_4,()=>attachmentMenu.callbacks[get$3(item).action]()),append($$anchor7,button_4)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_28,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_12)}),reset(div_1),append($$anchor5,fragment_1)},$$slots:{default:!0}})}), +append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}delegate(["click"]);var root_3$O=from_html(' ',1),root_4$z=from_html("

"),root_1$J=from_html(" ",1);function ChatFormActionAddButton($$anchor,$$props){let disabled=prop($$props,"disabled",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{ +Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$J(),node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{class:"w-full",children:($$anchor5,$$slotProps2)=>{Button($$anchor5,{class:"file-upload-button h-8 w-8 rounded-full p-0",get disabled(){return disabled()},get onclick(){return $$props.onclick},variant:"secondary",type:"button",children:($$anchor6,$$slotProps3)=>{var fragment_3=root_3$O(),span=first_child( +fragment_3),text2=child(span,!0);reset(span);var node_2=sibling(span,2);Plus(node_2,{class:"h-4 w-4"}),template_effect(()=>set_text(text2,ATTACHMENT_TOOLTIP_TEXT)),append($$anchor6,fragment_3)},$$slots:{default:!0}})},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{children:($$anchor5,$$slotProps2)=>{var p2=root_4$z(),text_1=child(p2,!0);reset(p2),template_effect(()=>set_text(text_1,ATTACHMENT_TOOLTIP_TEXT)), +append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment)}function ChatFormActionsAdd($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1);const isMobile=new IsMobile; +var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{ChatFormActionAddSheet($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){return hasAudioModality()},get hasVisionModality(){return hasVisionModality()},get hasMcpPromptsSupport(){return hasMcpPromptsSupport()},get hasMcpResourcesSupport(){return hasMcpResourcesSupport()},get onFileUpload(){return $$props.onFileUpload},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){ +return $$props.onMcpResourcesClick},trigger:($$anchor3,$$arg0)=>{let disabled2=()=>$$arg0?.().disabled,onclick=()=>$$arg0?.().onclick;ChatFormActionAddButton($$anchor3,{get disabled(){return disabled2()},get onclick(){return onclick()}})},$$slots:{trigger:!0}})},alternate=$$anchor2=>{ChatFormActionAddDropdown($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){return hasAudioModality()},get hasVisionModality(){return hasVisionModality()},get hasMcpPromptsSupport(){return hasMcpPromptsSupport()}, +get hasMcpResourcesSupport(){return hasMcpResourcesSupport()},get onFileUpload(){return $$props.onFileUpload},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){return $$props.onMcpResourcesClick},get onMcpSettingsClick(){return $$props.onMcpSettingsClick},get onSystemPromptClick(){return $$props.onSystemPromptClick},trigger:$$anchor3=>{ChatFormActionAddButton($$anchor3,{get disabled(){return disabled()}})},$$slots:{trigger:!0}})};if_block(node2,$$render=>{isMobile. +current?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_3$N=from_html(' ',1),root_7$q=from_html("

Current model does not support audio

"),root_1$I=from_html(" ",1),root$1o=from_html("
");function ChatFormActionRecord($$anchor,$$props){let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),isLoading2=prop($$props,"isLoading", +3,!1),isRecording=prop($$props,"isRecording",3,!1);var div=root$1o(),node2=child(div);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment=root_1$I(),node_1=first_child(fragment);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>isRecording()?"animate-pulse bg-red-500 text-white hover:bg-red-600":""),$1=user_derived(()=>disabled()|| +isLoading2()||!hasAudioModality());Button($$anchor5,{get class(){return`h-8 w-8 rounded-full p-0 ${get$3($0)??""}`},get disabled(){return get$3($1)},get onclick(){return $$props.onMicClick},type:"button",children:($$anchor6,$$slotProps3)=>{var fragment_2=root_3$N(),span=first_child(fragment_2),text2=child(span,!0);reset(span);var node_2=sibling(span,2);{var consequent=$$anchor7=>{Square($$anchor7,{class:"h-4 w-4 animate-pulse fill-white"})},alternate=$$anchor7=>{Mic($$anchor7,{class:"h-4 w-4"})}; +if_block(node_2,$$render=>{isRecording()?$$render(consequent):$$render(alternate,-1)})}template_effect(()=>set_text(text2,isRecording()?"Stop recording":"Start recording")),append($$anchor6,fragment_2)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_3=sibling(node_1,2);{var consequent_1=$$anchor4=>{var fragment_5=comment$2(),node_4=first_child(fragment_5);component(node_4,()=>Tooltip_content,($$anchor5,Tooltip_Content)=>{Tooltip_Content($$anchor5,{children:($$anchor6,$$slotProps2)=>{ +var p2=root_7$q();append($$anchor6,p2)},$$slots:{default:!0}})}),append($$anchor4,fragment_5)};if_block(node_3,$$render=>{hasAudioModality()||$$render(consequent_1)})}append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div)}var root_1$H=from_html('
'),root_4$y=from_html('Stop ',1),root$1n=from_html(' ');function ChatFormActions($$anchor,$$props){push$1($$props,!0);let canSend=prop($$props,"canSend",3,!1),canSubmit=prop($$props,"canSubmit",3,!1),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),isLoading2=prop($$props,"isLoading",3,!1),isRecording=prop($$props,"isRecording",3,!1),showAddButton=prop($$props,"showAddButton",3,!0),showModelSelector=prop($$props,"showModelSelector",3,!0),uploadedFiles=prop($$props,"u\ +ploadedFiles",19,()=>[]),currentConfig=user_derived(config$1),hasMcpPromptsSupport=user_derived(()=>{const perChatOverrides=conversationsStore.getAllMcpServerOverrides();return mcpStore.hasPromptsCapability(perChatOverrides)}),hasMcpResourcesSupport=user_derived(()=>{const perChatOverrides=conversationsStore.getAllMcpServerOverrides();return mcpStore.hasResourcesCapability(perChatOverrides)}),hasAudioModality=state$1(!1),hasVisionModality=state$1(!1),hasModelSelected=state$1(!1),isSelectedModelInCache=state$1( +!0),submitTooltip=state$1(""),hasAudioAttachments=user_derived(()=>uploadedFiles().some(file=>getFileTypeCategory(file.type)===FileTypeCategory.AUDIO)),shouldShowRecordButton=user_derived(()=>get$3(hasAudioModality)&&!canSubmit()&&!get$3(hasAudioAttachments)&&get$3(currentConfig).autoMicOnEmpty),selectorModelRef=state$1(void 0);function openModelSelector(){get$3(selectorModelRef)?.open()}var $$exports={openModelSelector},div=root$1n(),node2=child(div);{var consequent=$$anchor2=>{var div_1=root_1$H(), +node_1=child(div_1);ChatFormActionsAdd(node_1,{get disabled(){return disabled()},get hasAudioModality(){return get$3(hasAudioModality)},get hasVisionModality(){return get$3(hasVisionModality)},get hasMcpPromptsSupport(){return get$3(hasMcpPromptsSupport)},get hasMcpResourcesSupport(){return get$3(hasMcpResourcesSupport)},get onFileUpload(){return $$props.onFileUpload},get onSystemPromptClick(){return $$props.onSystemPromptClick},get onMcpPromptClick(){return $$props.onMcpPromptClick},get onMcpResourcesClick(){ +return $$props.onMcpResourcesClick},onMcpSettingsClick:()=>goto("#/settings/mcp")}),reset(div_1),append($$anchor2,div_1)};if_block(node2,$$render=>{showAddButton()&&$$render(consequent)})}var node_2=sibling(node2,2);{var consequent_1=$$anchor2=>{bind_this(ChatFormActionModels($$anchor2,{get disabled(){return disabled()},forceForegroundText:!0,useGlobalSelection:!0,get hasAudioModality(){return get$3(hasAudioModality)},set hasAudioModality($$value){set$1(hasAudioModality,$$value,!0)},get hasVisionModality(){ +return get$3(hasVisionModality)},set hasVisionModality($$value){set$1(hasVisionModality,$$value,!0)},get hasModelSelected(){return get$3(hasModelSelected)},set hasModelSelected($$value){set$1(hasModelSelected,$$value,!0)},get isSelectedModelInCache(){return get$3(isSelectedModelInCache)},set isSelectedModelInCache($$value){set$1(isSelectedModelInCache,$$value,!0)},get submitTooltip(){return get$3(submitTooltip)},set submitTooltip($$value){set$1(submitTooltip,$$value,!0)}}),$$value=>set$1(selectorModelRef, +$$value,!0),()=>get$3(selectorModelRef))};if_block(node_2,$$render=>{showModelSelector()&&$$render(consequent_1)})}var node_3=sibling(node_2,2);{var consequent_2=$$anchor2=>{Button($$anchor2,{type:"button",variant:"secondary",get onclick(){return $$props.onStop},class:"group h-8 w-8 rounded-full p-0 hover:bg-destructive/10!",children:($$anchor3,$$slotProps)=>{var fragment_2=root_4$y(),node_4=sibling(first_child(fragment_2),2);Square(node_4,{class:"h-8 w-8 fill-muted-foreground stroke-muted-foreg\ +round group-hover:fill-destructive group-hover:stroke-destructive hover:fill-destructive hover:stroke-destructive"}),append($$anchor3,fragment_2)},$$slots:{default:!0}})},consequent_3=$$anchor2=>{ChatFormActionRecord($$anchor2,{get disabled(){return disabled()},get hasAudioModality(){return get$3(hasAudioModality)},get isLoading(){return isLoading2()},get isRecording(){return isRecording()},get onMicClick(){return $$props.onMicClick}})},alternate=$$anchor2=>{{let $0=user_derived(()=>canSend()&&(showModelSelector()? +get$3(hasModelSelected)&&get$3(isSelectedModelInCache):!0)),$1=user_derived(()=>showModelSelector()&&get$3(hasModelSelected)&&!get$3(isSelectedModelInCache));ChatFormActionSubmit($$anchor2,{get canSend(){return get$3($0)},get disabled(){return disabled()},get tooltipLabel(){return get$3(submitTooltip)},get showErrorState(){return get$3($1)}})}};if_block(node_3,$$render=>{isLoading2()&&!canSubmit()?$$render(consequent_2):get$3(shouldShowRecordButton)?$$render(consequent_3,1):$$render(alternate,-1)})} +return reset(div),template_effect(()=>set_class(div,1,`flex w-full items-center gap-3 ${className()??""} ${showAddButton()?"":"justify-end"}`)),append($$anchor,div),pop($$exports)}var root_2$_=from_html('Send ',1),root_6$s=from_html("

"),root_4$x=from_html(" ",1);function ChatFormActionSubmit($$anchor,$$props){const submitButton=($$anchor2,$$arg0)=>{let props=derived_safe_equal(()=>fallback($$arg0?.(),()=>({}),!0));{let $0=user_derived(()=>["h-8 w-8 r\ +ounded-full p-0",showErrorState()&&"bg-red-400/10 text-red-400 hover:bg-red-400/20 hover:text-red-400 disabled:opacity-100"]);Button($$anchor2,spread_props({type:"submit",get disabled(){return get$3(isDisabled)},get class(){return get$3($0)}},()=>get$3(props),{children:($$anchor3,$$slotProps)=>{var fragment_1=root_2$_(),node2=sibling(first_child(fragment_1),2);Arrow_up(node2,{class:"h-12 w-12"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))}};let canSend=prop($$props,"canSend",3,!1),disabled=prop( +$$props,"disabled",3,!1),showErrorState=prop($$props,"showErrorState",3,!1),isDisabled=user_derived(()=>!canSend()||disabled());var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor2=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);component(node_2,()=>Root$5,($$anchor3,Tooltip_Root)=>{Tooltip_Root($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_4=root_4$x(),node_3=first_child(fragment_4);component(node_3,()=>Tooltip_trigger,($$anchor5,Tooltip_Trigger)=>{ +Tooltip_Trigger($$anchor5,{children:($$anchor6,$$slotProps2)=>{submitButton($$anchor6)},$$slots:{default:!0}})});var node_4=sibling(node_3,2);component(node_4,()=>Tooltip_content,($$anchor5,Tooltip_Content)=>{Tooltip_Content($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p2=root_6$s(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltipLabel)),append($$anchor6,p2)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})}),append($$anchor2,fragment_3)}, +alternate=$$anchor2=>{submitButton($$anchor2)};if_block(node_1,$$render=>{$$props.tooltipLabel?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2)}function ChatFormActionModels($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),forceForegroundText=prop($$props,"forceForegroundText",3,!1),hasAudioModality=prop($$props,"hasAudioModality",15,!1),hasVisionModality=prop($$props,"hasVisionModality",15,!1),hasModelSelected=prop($$props,"hasModelSelecte\ +d",15,!1),isSelectedModelInCache=prop($$props,"isSelectedModelInCache",15,!0),submitTooltip=prop($$props,"submitTooltip",15,""),useGlobalSelection=prop($$props,"useGlobalSelection",3,!1),isRouter=user_derived(isRouterMode),isOffline=user_derived(()=>!!serverError()),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),lastSyncedConversationModel=null;user_effect(()=>{if(get$3(conversationModel)&&get$3(conversationModel)!==lastSyncedConversationModel)lastSyncedConversationModel= +get$3(conversationModel),modelsStore.selectModelByName(get$3(conversationModel));else if(get$3(isRouter)&&!modelsStore.selectedModelId&&modelsStore.loadedModelIds.length>0){lastSyncedConversationModel=null;const first=modelOptions().find(m=>modelsStore.loadedModelIds.includes(m.model));first&&modelsStore.selectModelById(first.id)}});let activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId(); +if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3(conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model)return model.model}return null}),modelPropsVersion=state$1(0);user_effect(()=>{get$3(activeModelId)&&(modelsStore.getModelProps(get$3(activeModelId))||modelsStore.fetchModelProps(get$3(activeModelId)).then(()=>{update$1(modelPropsVersion)}))}),user_effect(()=>{hasAudioModality(get$3(activeModelId)?modelsStore. +modelSupportsAudio(get$3(activeModelId)):!1)}),user_effect(()=>{get$3(modelPropsVersion),hasVisionModality(get$3(activeModelId)?modelsStore.modelSupportsVision(get$3(activeModelId)):!1)}),user_effect(()=>{hasModelSelected(!get$3(isRouter)||!!get$3(conversationModel)||!!selectedModelId())}),user_effect(()=>{if(!get$3(isRouter))isSelectedModelInCache(!0);else if(get$3(conversationModel))isSelectedModelInCache(modelOptions().some(option2=>option2.model===get$3(conversationModel)));else{const currentModelId=selectedModelId(); +isSelectedModelInCache(currentModelId?modelOptions().some(option2=>option2.id===currentModelId):!1)}}),user_effect(()=>{hasModelSelected()?isSelectedModelInCache()?submitTooltip(""):submitTooltip("Selected model is not available, please select another"):submitTooltip("Please select a model first")});let selectorModelRef=state$1(void 0),isMobile=new IsMobile;function open2(){get$3(selectorModelRef)?.open()}var $$exports={open:open2},fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{ +{let $0=user_derived(()=>disabled()||get$3(isOffline));bind_this(ModelsSelectorSheet($$anchor2,{get disabled(){return get$3($0)},get currentModel(){return $$props.currentModel},get forceForegroundText(){return forceForegroundText()},get useGlobalSelection(){return useGlobalSelection()}}),$$value=>set$1(selectorModelRef,$$value,!0),()=>get$3(selectorModelRef))}},alternate=$$anchor2=>{{let $0=user_derived(()=>disabled()||get$3(isOffline));bind_this(ModelsSelectorDropdown($$anchor2,{get disabled(){ +return get$3($0)},get currentModel(){return $$props.currentModel},get forceForegroundText(){return forceForegroundText()},get useGlobalSelection(){return useGlobalSelection()}}),$$value=>set$1(selectorModelRef,$$value,!0),()=>get$3(selectorModelRef))}};if_block(node2,$$render=>{isMobile.current?$$render(consequent):$$render(alternate,-1)})}return append($$anchor,fragment),pop($$exports)}function Collapsible($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),open2=prop($$props, +"open",15,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","open"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible$1,($$anchor2,CollapsiblePrimitive_Root)=>{CollapsiblePrimitive_Root($$anchor2,spread_props({"data-slot":"collapsible"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},get open(){return open2()},set open($$value){open2($$value)}}))}),append($$anchor,fragment),pop()}function Collapsible_trigger($$anchor,$$props){ +push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible_trigger$1,($$anchor2,CollapsiblePrimitive_Trigger)=>{CollapsiblePrimitive_Trigger($$anchor2,spread_props({"data-slot":"collapsible-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Collapsible_content($$anchor,$$props){ +push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible_content$1,($$anchor2,CollapsiblePrimitive_Content)=>{CollapsiblePrimitive_Content($$anchor2,spread_props({"data-slot":"collapsible-content"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function useToolsPanel(){const expandedGroups=new SvelteSet, +groups=user_derived(()=>toolsStore.toolGroups),activeGroups=user_derived(()=>get$3(groups).filter(g=>g.source!==ToolSource.MCP||!g.serverId||conversationsStore.isMcpServerEnabledForChat(g.serverId))),totalToolCount=user_derived(()=>get$3(activeGroups).reduce((n,g)=>n+g.tools.length,0)),noToolsInfoMessage=user_derived(()=>toolsStore.loading||toolsStore.toolGroups.length>0?null:toolsStore.isToolsEndpointUnreachable?"To enable Built-In Tools you need to run llama-server with --tools all or --tools \ + flag. To see MCP Tools you need to add / enable MCP Server(s).":toolsStore.error?null:"To enable Built-In Tools you need to run llama-server with --tools all or --tools flag. To see MCP Tools you need to add / enable MCP Server(s).");function getGroupCheckedState(group){return{checked:toolsStore.isGroupFullyEnabled(group),indeterminate:toolsStore.isGroupPartiallyEnabled(group)}}function getEnabledToolCount(group){return group.tools.filter(tool=>toolsStore.isToolEnabled(tool.function. +name)).length}function getFavicon(group){if(group.source!==ToolSource.MCP)return null;for(const server of mcpStore.getServersSorted())if(mcpStore.getServerLabel(server)===group.label)return mcpStore.getServerFavicon(server.id);return null}function isGroupDisabled(group){return group.source===ToolSource.MCP&&!!group.serverId&&!conversationsStore.isMcpServerEnabledForChat(group.serverId)}function toggleGroupExpanded(label){expandedGroups.has(label)?expandedGroups.delete(label):expandedGroups.add(label)} +function handleOpen(){toolsStore.builtinTools.length===0&&!toolsStore.loading&&toolsStore.fetchBuiltinTools(),mcpStore.runHealthChecksForServers(mcpStore.getServersSorted().filter(s2=>s2.enabled))}return{expandedGroups,get groups(){return get$3(groups)},get activeGroups(){return get$3(activeGroups)},get totalToolCount(){return get$3(totalToolCount)},get noToolsInfoMessage(){return get$3(noToolsInfoMessage)},getGroupCheckedState,getEnabledToolCount,getFavicon,isGroupDisabled,toggleGroupExpanded,handleOpen}} +var root_2$Z=from_html(" Tools",1),root_5$s=from_html('
Loading tools...
'),root_6$r=from_html('
Run llama-server with --tools flag to enable Built-in Tools. MCP Tools.
'),root_7$p=from_html('Failed to load tools'),root_8$p=from_html('
'),root_9$j=from_html('
No tools available
'),root_16$3=from_html(''),root_13$a=from_html(' ',1),root_19$8=from_html("

"),root_17$7=from_html(" ",1),root_21$3=from_html(''),root_20$2=from_html('
'), +root_12$8=from_html('
',1),root_10$c=from_html('
'),root_1$G=from_html(" ",1);function ChatFormActionAddToolsSubmenu($$anchor,$$props){push$1($$props,!0);const toolsPanel=useToolsPanel(),hasMcpServersAvailable=user_derived(()=>mcpStore.getServersSorted().length>0);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Sub,($$anchor2,DropdownMenu_Sub)=>{DropdownMenu_Sub( +$$anchor2,{onOpenChange:open2=>open2&&toolsPanel.handleOpen(),children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$G(),node_1=first_child(fragment_1);component(node_1,()=>Dropdown_menu_sub_trigger,($$anchor4,DropdownMenu_SubTrigger)=>{DropdownMenu_SubTrigger($$anchor4,{class:"flex cursor-pointer items-center gap-2",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$Z(),node_2=first_child(fragment_2);Pencil_ruler(node_2,{class:"h-4 w-4"}),next$1(2),append($$anchor5,fragment_2)},$$slots:{ +default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_sub_content,($$anchor4,DropdownMenu_SubContent)=>{DropdownMenu_SubContent($$anchor4,{class:"w-72 p-0",children:($$anchor5,$$slotProps2)=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);{var consequent_4=$$anchor6=>{var fragment_4=comment$2(),node_5=first_child(fragment_4);{var consequent=$$anchor7=>{var div=root_5$s(),node_6=child(div);Loader_circle(node_6,{class:"mx-auto mb-1 h-4 w-4 animate-spin"}),next$1(), +reset(div),append($$anchor7,div)},consequent_1=$$anchor7=>{var div_1=root_6$r(),span=child(div_1),node_7=child(span);Info$1(node_7,{class:"mt-0.5 h-4 w-4 shrink-0"}),next$1(2),reset(span);var span_1=sibling(span,2),node_8=child(span_1);Info$1(node_8,{class:"mt-0.5 h-4 w-4 shrink-0"});var span_2=sibling(node_8,2),text2=child(span_2);next$1(2),reset(span_2),reset(span_1),reset(div_1),template_effect(()=>set_text(text2,`${get$3(hasMcpServersAvailable)?"Enable":"Add"} MCP Server(s) to access `)),append( +$$anchor7,div_1)},consequent_2=$$anchor7=>{var div_2=root_7$p();append($$anchor7,div_2)},consequent_3=$$anchor7=>{var div_3=root_8$p(),node_9=child(div_3);Info$1(node_9,{class:"mt-0.5 h-4 w-4 shrink-0"});var span_3=sibling(node_9,2),text_1=child(span_3,!0);reset(span_3),reset(div_3),template_effect(()=>set_text(text_1,toolsPanel.noToolsInfoMessage)),append($$anchor7,div_3)},alternate=$$anchor7=>{var div_4=root_9$j();append($$anchor7,div_4)};if_block(node_5,$$render=>{toolsStore.loading?$$render( +consequent):toolsStore.isToolsEndpointUnreachable?$$render(consequent_1,1):toolsStore.error?$$render(consequent_2,2):toolsPanel.noToolsInfoMessage?$$render(consequent_3,3):$$render(alternate,-1)})}append($$anchor6,fragment_4)},alternate_2=$$anchor6=>{var div_5=root_10$c();each(div_5,21,()=>toolsPanel.activeGroups,group=>group.label,($$anchor7,group)=>{const isExpanded=user_derived(()=>toolsPanel.expandedGroups.has(get$3(group).label)),computed_const=user_derived(()=>{const{checked,indeterminate}=toolsPanel. +getGroupCheckedState(get$3(group));return{checked,indeterminate}}),favicon=user_derived(()=>toolsPanel.getFavicon(get$3(group)));var fragment_5=comment$2(),node_10=first_child(fragment_5);component(node_10,()=>Collapsible,($$anchor8,Collapsible_Root)=>{Collapsible_Root($$anchor8,{get open(){return get$3(isExpanded)},onOpenChange:()=>toolsPanel.toggleGroupExpanded(get$3(group).label),children:($$anchor9,$$slotProps3)=>{var fragment_6=root_12$8(),div_6=first_child(fragment_6),node_11=child(div_6); +component(node_11,()=>Collapsible_trigger,($$anchor10,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor10,{class:"flex min-w-0 flex-1 items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50",children:($$anchor11,$$slotProps4)=>{var fragment_7=root_13$a(),node_12=first_child(fragment_7);{var consequent_5=$$anchor12=>{Chevron_down($$anchor12,{class:"h-3.5 w-3.5 shrink-0"})},alternate_1=$$anchor12=>{Chevron_right($$anchor12,{class:"h-3.5 w-3.5 shrink-0"})};if_block(node_12,$$render=>{get$3( +isExpanded)?$$render(consequent_5):$$render(alternate_1,-1)})}var span_4=sibling(node_12,2),node_13=child(span_4);{var consequent_6=$$anchor12=>{var img=root_16$3();template_effect(()=>set_attribute(img,"src",get$3(favicon))),event("error",img,e=>{e.currentTarget.style.display="none"}),replay_events(img),append($$anchor12,img)};if_block(node_13,$$render=>{get$3(favicon)&&$$render(consequent_6)})}var span_5=sibling(node_13,2),text_2=child(span_5,!0);reset(span_5),reset(span_4);var span_6=sibling( +span_4,2),text_3=child(span_6);reset(span_6),template_effect($0=>{set_text(text_2,get$3(group).label),set_text(text_3,`${$0??""}/${get$3(group).tools.length??""}`)},[()=>toolsPanel.getEnabledToolCount(get$3(group))]),append($$anchor11,fragment_7)},$$slots:{default:!0}})});var node_14=sibling(node_11,2);component(node_14,()=>Root$5,($$anchor10,Tooltip_Root)=>{Tooltip_Root($$anchor10,{children:($$anchor11,$$slotProps4)=>{var fragment_10=root_17$7(),node_15=first_child(fragment_10);component(node_15, +()=>Tooltip_trigger,($$anchor12,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor12,{children:($$anchor13,$$slotProps5)=>{Checkbox($$anchor13,{get checked(){return get$3(computed_const).checked},get indeterminate(){return get$3(computed_const).indeterminate},onCheckedChange:()=>toolsStore.toggleGroup(get$3(group)),class:"mr-2 h-4 w-4 shrink-0"})},$$slots:{default:!0}})});var node_16=sibling(node_15,2);component(node_16,()=>Tooltip_content,($$anchor12,Tooltip_Content)=>{Tooltip_Content($$anchor12,{side:"\ +right",children:($$anchor13,$$slotProps5)=>{var p2=root_19$8(),text_4=child(p2);reset(p2),template_effect(()=>set_text(text_4,`${get$3(computed_const).checked?"Disable":"Enable"} ${get$3(group).tools.length??""} tool${get$3(group).tools.length!==1?"s":""}`)),append($$anchor13,p2)},$$slots:{default:!0}})}),append($$anchor11,fragment_10)},$$slots:{default:!0}})}),reset(div_6);var node_17=sibling(div_6,2);component(node_17,()=>Collapsible_content,($$anchor10,Collapsible_Content)=>{Collapsible_Content($$anchor10,{children:($$anchor11,$$slotProps4)=>{var div_7=root_20$2();each(div_7,21,()=>get$3(group).tools,tool=>tool.function.name,($$anchor12,tool)=>{var button=root_21$3(), node_18=child(button);{let $0=user_derived(()=>toolsStore.isToolEnabled(get$3(tool).function.name));Checkbox(node_18,{get checked(){return get$3($0)},onCheckedChange:()=>toolsStore.toggleTool(get$3(tool).function.name),class:"h-4 w-4 shrink-0"})}var span_7=sibling(node_18,2),text_5=child(span_7,!0);reset(span_7),reset(button),template_effect(()=>set_text(text_5,get$3(tool).function.name)),delegated("click",button,()=>toolsStore.toggleTool(get$3(tool).function.name)),append($$anchor12,button)}),reset( div_7),append($$anchor11,div_7)},$$slots:{default:!0}})}),append($$anchor9,fragment_6)},$$slots:{default:!0}})}),append($$anchor7,fragment_5)}),reset(div_5),append($$anchor6,div_5)};if_block(node_4,$$render=>{toolsPanel.totalToolCount===0?$$render(consequent_4):$$render(alternate_2,-1)})}append($$anchor5,fragment_3)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}delegate(["click"]);function Switch($$anchor,$$props){push$1($$props, @@ -6717,20 +6718,20 @@ Button(node_2,{type:"button",size:"sm",get onclick(){return $$props.onCancel},va this,$$args)}),append($$anchor,form),pop()}function Popover($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","open"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Popover$1,($$anchor2,PopoverPrimitive_Root)=>{PopoverPrimitive_Root($$anchor2,spread_props(()=>restProps,{get open(){return open2()},set open($$value){open2($$value)}}))}),append($$anchor,fragment),pop()}function Popover_portal($$anchor,$$props){ let restProps=rest_props($$props,["$$slots","$$events","$$legacy"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,PopoverPrimitive_Portal)=>{PopoverPrimitive_Portal($$anchor2,spread_props(()=>restProps))}),append($$anchor,fragment)}function Popover_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,4),align=prop($$props,"align",3,"center"),collisionPadding=prop($$props,"collisionP\ adding",3,8),avoidCollisions=prop($$props,"avoidCollisions",3,!0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","sideOffset","side","align","collisionPadding","avoidCollisions","portalProps"]);Popover_portal($$anchor,spread_props(()=>$$props.portalProps,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{let $0=user_derived(()=>cn$1("z-50 w-72 origin-(--bits-popover-content-transform-origin) rounded-md border bg-popover p-4 \ -text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",$$props.class));component(node2,()=>Popover_content$1,($$anchor3,PopoverPrimitive_Content)=>{PopoverPrimitive_Content( -$$anchor3,spread_props({"data-slot":"popover-content",get sideOffset(){return sideOffset()},get side(){return $$props.side},get align(){return align()},get collisionPadding(){return collisionPadding()},get avoidCollisions(){return avoidCollisions()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Popover_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props, -"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("",$$props.class));component(node2,()=>Popover_trigger$1,($$anchor2,PopoverPrimitive_Trigger)=>{PopoverPrimitive_Trigger($$anchor2,spread_props({"data-slot":"popover-trigger",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_2$U=from_html( -' '),root_1$D=from_html(" ",1);function ChatFormPickerPopover($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),isOpen=prop($$props,"isOpen",15,!1),srLabel=prop($$props,"srLabel",3,"Open picker");var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Popover,($$anchor2,Popover_Root)=>{Popover_Root($$anchor2,{onOpenChange:open2=>{open2||$$props.onClose?.()},get open(){return isOpen()},set open($$value){isOpen($$value)}, -children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$D(),node_1=first_child(fragment_1);component(node_1,()=>Popover_trigger,($$anchor4,Popover_Trigger)=>{Popover_Trigger($$anchor4,{class:"pointer-events-none absolute inset-0 opacity-0",tabindex:-1,"aria-hidden":"true",children:($$anchor5,$$slotProps2)=>{var span=root_2$U(),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,srLabel())),append($$anchor5,span)},$$slots:{default:!0}})});var node_2=sibling(node_1,2);component(node_2, -()=>Popover_content,($$anchor4,Popover_Content)=>{Popover_Content($$anchor4,{side:"top",align:"start",sideOffset:12,get class(){return`w-[var(--bits-popover-anchor-width)] max-w-none rounded-xl border-border/50 p-0 shadow-xl ${className()??""}`},get onkeydown(){return $$props.onKeydown},onOpenAutoFocus:event2=>event2.preventDefault(),children:($$anchor5,$$slotProps2)=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children),append($$anchor5,fragment_2)},$$slots:{ -default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_2$T=from_html('
'),root_5$q=from_html('
'),root_1$C=from_html("
",1);function ChatFormPickerList($$anchor,$$props){push$1($$props,!0);let searchQuery=prop($$props,"searchQuery",15),searchPlaceholder=prop($$props,"searchPlaceholder",3,"S\ -earch..."),emptyMessage=prop($$props,"emptyMessage",3,"No items available"),listContainer=state$1(null);user_effect(()=>{if(get$3(listContainer)&&$$props.selectedIndex>=0&&$$props.selectedIndex<$$props.items.length){const selectedElement=get$3(listContainer).querySelector(`[data-picker-index="${$$props.selectedIndex}"]`);selectedElement&&selectedElement.scrollIntoView({behavior:"smooth",block:"center",inline:"nearest"})}}),Scroll_area($$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$C(), -node2=first_child(fragment_1);{var consequent=$$anchor3=>{var div=root_2$T(),node_1=child(div);SearchInput(node_1,{get placeholder(){return searchPlaceholder()},get value(){return searchQuery()},set value($$value){searchQuery($$value)}}),reset(div),append($$anchor3,div)};if_block(node2,$$render=>{$$props.showSearchInput&&$$render(consequent)})}var div_1=sibling(node2,2),node_2=child(div_1);{var consequent_2=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);{var consequent_1=$$anchor4=>{ -var fragment_3=comment$2(),node_4=first_child(fragment_3);snippet(node_4,()=>$$props.skeleton),append($$anchor4,fragment_3)};if_block(node_3,$$render=>{$$props.skeleton&&$$render(consequent_1)})}append($$anchor3,fragment_2)},consequent_3=$$anchor3=>{var div_2=root_5$q(),text2=child(div_2,!0);reset(div_2),template_effect(()=>set_text(text2,emptyMessage())),append($$anchor3,div_2)},alternate=$$anchor3=>{var fragment_4=comment$2(),node_5=first_child(fragment_4);each(node_5,19,()=>$$props.items,(itemData,index2)=>$$props. -itemKey(itemData,index2),($$anchor4,itemData,index2)=>{var fragment_5=comment$2(),node_6=first_child(fragment_5);snippet(node_6,()=>$$props.item,()=>get$3(itemData),()=>get$3(index2),()=>get$3(index2)===$$props.selectedIndex),append($$anchor4,fragment_5)}),append($$anchor3,fragment_4)};if_block(node_2,$$render=>{$$props.isLoading?$$render(consequent_2):$$props.items.length===0?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_1),bind_this(div_1,$$value=>set$1(listContainer,$$value),()=>get$3( -listContainer));var node_7=sibling(div_1,2);{var consequent_4=$$anchor3=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);snippet(node_8,()=>$$props.footer),append($$anchor3,fragment_6)};if_block(node_7,$$render=>{$$props.footer&&$$render(consequent_4)})}template_effect(()=>set_class(div_1,1,clsx([`${CHAT_FORM_POPOVER_MAX_HEIGHT} p-2`,$$props.showSearchInput&&"pt-13"]))),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root$1i=from_html(''); -function ChatFormPickerListItem($$anchor,$$props){let isSelected=prop($$props,"isSelected",3,!1);var button=root$1i(),node2=child(button);snippet(node2,()=>$$props.children),reset(button),template_effect(()=>{set_attribute(button,"data-picker-index",$$props.dataIndex),set_class(button,1,`flex w-full cursor-pointer items-start gap-3 rounded-lg px-3 py-2 text-left hover:bg-accent/50 ${isSelected()?"bg-accent/50":""}`)}),delegated("click",button,function(...$$args){$$props.onclick?.apply(this,$$args)}), -append($$anchor,button)}delegate(["click"]);var root_1$B=from_html(''),root_3$K=from_html('

'),root$1h=from_html('
');function ChatFormPickerItemHeader($$anchor,$$props){ +text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",$$props.class));component(node2,()=>Popover_content$1,($$anchor3,PopoverPrimitive_Content)=>{ +PopoverPrimitive_Content($$anchor3,spread_props({"data-slot":"popover-content",get sideOffset(){return sideOffset()},get side(){return $$props.side},get align(){return align()},get collisionPadding(){return collisionPadding()},get avoidCollisions(){return avoidCollisions()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Popover_trigger($$anchor,$$props){push$1($$props, +!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("",$$props.class));component(node2,()=>Popover_trigger$1,($$anchor2,PopoverPrimitive_Trigger)=>{PopoverPrimitive_Trigger($$anchor2,spread_props({"data-slot":"popover-trigger",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor, +fragment),pop()}var root_2$U=from_html(' '),root_1$D=from_html(" ",1);function ChatFormPickerPopover($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),isOpen=prop($$props,"isOpen",15,!1),srLabel=prop($$props,"srLabel",3,"Open picker");var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Popover,($$anchor2,Popover_Root)=>{Popover_Root($$anchor2,{onOpenChange:open2=>{open2||$$props.onClose?.()},get open(){return isOpen()}, +set open($$value){isOpen($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$D(),node_1=first_child(fragment_1);component(node_1,()=>Popover_trigger,($$anchor4,Popover_Trigger)=>{Popover_Trigger($$anchor4,{class:"pointer-events-none absolute inset-0 opacity-0",tabindex:-1,"aria-hidden":"true",children:($$anchor5,$$slotProps2)=>{var span=root_2$U(),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,srLabel())),append($$anchor5,span)},$$slots:{default:!0}})});var node_2=sibling( +node_1,2);component(node_2,()=>Popover_content,($$anchor4,Popover_Content)=>{Popover_Content($$anchor4,{side:"top",align:"start",sideOffset:12,get class(){return`w-[var(--bits-popover-anchor-width)] max-w-none rounded-xl border-border/50 p-0 shadow-xl ${className()??""}`},get onkeydown(){return $$props.onKeydown},onOpenAutoFocus:event2=>event2.preventDefault(),children:($$anchor5,$$slotProps2)=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children),append( +$$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_2$T=from_html('
'),root_5$q=from_html('
'),root_1$C=from_html("
",1);function ChatFormPickerList($$anchor,$$props){push$1($$props,!0);let searchQuery=prop($$props,"searchQuery",15),searchPlaceholder=prop( +$$props,"searchPlaceholder",3,"Search..."),emptyMessage=prop($$props,"emptyMessage",3,"No items available"),listContainer=state$1(null);user_effect(()=>{if(get$3(listContainer)&&$$props.selectedIndex>=0&&$$props.selectedIndex<$$props.items.length){const selectedElement=get$3(listContainer).querySelector(`[data-picker-index="${$$props.selectedIndex}"]`);selectedElement&&selectedElement.scrollIntoView({behavior:"smooth",block:"center",inline:"nearest"})}}),Scroll_area($$anchor,{children:($$anchor2,$$slotProps)=>{ +var fragment_1=root_1$C(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var div=root_2$T(),node_1=child(div);SearchInput(node_1,{get placeholder(){return searchPlaceholder()},get value(){return searchQuery()},set value($$value){searchQuery($$value)}}),reset(div),append($$anchor3,div)};if_block(node2,$$render=>{$$props.showSearchInput&&$$render(consequent)})}var div_1=sibling(node2,2),node_2=child(div_1);{var consequent_2=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2); +{var consequent_1=$$anchor4=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);snippet(node_4,()=>$$props.skeleton),append($$anchor4,fragment_3)};if_block(node_3,$$render=>{$$props.skeleton&&$$render(consequent_1)})}append($$anchor3,fragment_2)},consequent_3=$$anchor3=>{var div_2=root_5$q(),text2=child(div_2,!0);reset(div_2),template_effect(()=>set_text(text2,emptyMessage())),append($$anchor3,div_2)},alternate=$$anchor3=>{var fragment_4=comment$2(),node_5=first_child(fragment_4);each(node_5, +19,()=>$$props.items,(itemData,index2)=>$$props.itemKey(itemData,index2),($$anchor4,itemData,index2)=>{var fragment_5=comment$2(),node_6=first_child(fragment_5);snippet(node_6,()=>$$props.item,()=>get$3(itemData),()=>get$3(index2),()=>get$3(index2)===$$props.selectedIndex),append($$anchor4,fragment_5)}),append($$anchor3,fragment_4)};if_block(node_2,$$render=>{$$props.isLoading?$$render(consequent_2):$$props.items.length===0?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_1),bind_this( +div_1,$$value=>set$1(listContainer,$$value),()=>get$3(listContainer));var node_7=sibling(div_1,2);{var consequent_4=$$anchor3=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);snippet(node_8,()=>$$props.footer),append($$anchor3,fragment_6)};if_block(node_7,$$render=>{$$props.footer&&$$render(consequent_4)})}template_effect(()=>set_class(div_1,1,clsx([`${CHAT_FORM_POPOVER_MAX_HEIGHT} p-2`,$$props.showSearchInput&&"pt-13"]))),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root$1i=from_html( +'');function ChatFormPickerListItem($$anchor,$$props){let isSelected=prop($$props,"isSelected",3,!1);var button=root$1i(),node2=child(button);snippet(node2,()=>$$props.children),reset(button),template_effect(()=>{set_attribute(button,"data-picker-index",$$props.dataIndex),set_class(button,1,`flex w-full cursor-pointer items-start gap-3 rounded-lg px-3 py-2 text-left hover:bg-accent/50 ${isSelected()?"bg-accent/50":""}`)}),delegated("click",button,function(...$$args){ +$$props.onclick?.apply(this,$$args)}),append($$anchor,button)}delegate(["click"]);var root_1$B=from_html(''),root_3$K=from_html('

'),root$1h=from_html('
');function ChatFormPickerItemHeader($$anchor,$$props){ push$1($$props,!0);let faviconUrl=user_derived(()=>$$props.server?mcpStore.getServerFavicon($$props.server.id):null);var div=root$1h(),div_1=child(div),node2=child(div_1);{var consequent=$$anchor2=>{var img=root_1$B();template_effect(()=>set_attribute(img,"src",get$3(faviconUrl))),event("error",img,e=>{e.currentTarget.style.display="none"}),replay_events(img),append($$anchor2,img)};if_block(node2,$$render=>{get$3(faviconUrl)&&$$render(consequent)})}var span=sibling(node2,2),text2=child(span,!0); reset(span),reset(div_1);var div_2=sibling(div_1,2),span_1=child(div_2),text_1=child(span_1,!0);reset(span_1);var node_1=sibling(span_1,2);{var consequent_1=$$anchor2=>{var fragment=comment$2(),node_2=first_child(fragment);snippet(node_2,()=>$$props.titleExtra),append($$anchor2,fragment)};if_block(node_1,$$render=>{$$props.titleExtra&&$$render(consequent_1)})}reset(div_2);var node_3=sibling(div_2,2);{var consequent_2=$$anchor2=>{var p2=root_3$K(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text( text_2,$$props.description)),append($$anchor2,p2)};if_block(node_3,$$render=>{$$props.description&&$$render(consequent_2)})}var node_4=sibling(node_3,2);{var consequent_3=$$anchor2=>{var fragment_1=comment$2(),node_5=first_child(fragment_1);snippet(node_5,()=>$$props.subtitle),append($$anchor2,fragment_1)};if_block(node_4,$$render=>{$$props.subtitle&&$$render(consequent_3)})}reset(div),template_effect(()=>{set_text(text2,$$props.serverLabel),set_text(text_1,$$props.title)}),append($$anchor,div), @@ -10492,86 +10493,86 @@ Collapsible_Content($$anchor5,{children:($$anchor6,$$slotProps3)=>{var div_2=roo $$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-lg leading-none font-semibold",$$props.class));component(node2,()=>Dialog_title$1,($$anchor2,DialogPrimitive_Title)=>{DialogPrimitive_Title($$anchor2,spread_props({"data-slot":"dialog-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor, fragment),pop()}var root$P=from_html("
");function Dialog_footer($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$P();attribute_effect(div,$0=>({"data-slot":"dialog-footer",class:$0,...restProps}),[()=>cn$1("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this( div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root$O=from_html("
");function Dialog_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$O();attribute_effect(div,$0=>({"data-slot":"dialog-header",class:$0,...restProps}),[()=>cn$1("flex flex-col gap-2 text-center sm:text-left",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props. -children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Dialog_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=\ -open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,DialogPrimitive_Overlay)=>{DialogPrimitive_Overlay($$anchor2,spread_props({"data-slot":"dialog-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_4$n=from_html(' Close',1),root_2$G=from_html(" ",1),root_1$q=from_html(" ",1);function Dialog_content($$anchor,$$props){push$1( -$$props,!0);let ref2=prop($$props,"ref",15,null),showCloseButton=prop($$props,"showCloseButton",3,!0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","portalProps","children","showCloseButton"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$1,($$anchor2,Dialog_Portal)=>{Dialog_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$q(),node_1=first_child(fragment_1);component(node_1, -()=>Dialog_overlay,($$anchor4,Dialog_Overlay)=>{Dialog_Overlay($$anchor4,{})});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1("fixed top-[50%] left-[50%] z-50 grid max-h-[100dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border border-border/30 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:\ -fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg md:max-h-[100vh]",$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,DialogPrimitive_Content)=>{DialogPrimitive_Content($$anchor4,spread_props({"data-slot":"dialog-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$G(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3);var node_4=sibling(node_3, -2);{var consequent=$$anchor6=>{var fragment_3=comment$2(),node_5=first_child(fragment_3);component(node_5,()=>Dialog_close,($$anchor7,DialogPrimitive_Close)=>{DialogPrimitive_Close($$anchor7,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",children:($$anchor8,$$slotProps3)=>{ -var fragment_4=root_4$n(),node_6=first_child(fragment_4);X(node_6,{}),next$1(2),append($$anchor8,fragment_4)},$$slots:{default:!0}})}),append($$anchor6,fragment_3)};if_block(node_4,$$render=>{showCloseButton()&&$$render(consequent)})}append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dialog_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props( -$$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,DialogPrimitive_Description)=>{DialogPrimitive_Description($$anchor2,spread_props({"data-slot":"dialog-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$2=Dialog, -Portal$1=Portal$2;var root_1$p=from_html(" Add New Server",1),root_2$F=from_html('
No MCP Servers configured yet. Add one to enable agentic features.
'),root_3$z=from_html('
'),root$N=from_html('

MCP Servers

');function SettingsMcpServers($$anchor,$$props){push$1($$props,!0);let servers=user_derived(()=>mcpStore.getServersSorted()),initialLoadComplete=state$1(!1),isAddingServer=state$1(!1);onMount$1(()=>{if(page$1.url.searchParams.has("add")){set$1(isAddingServer, -!0);const newUrl=new URL(page$1.url);newUrl.searchParams.delete("add"),replaceState(newUrl,{})}}),user_effect(()=>{if(get$3(initialLoadComplete))return;get$3(servers).length>0&&get$3(servers).every(server=>{const state2=mcpStore.getHealthCheckState(server.id);return state2.status===HealthCheckStatus.SUCCESS||state2.status===HealthCheckStatus.ERROR})&&set$1(initialLoadComplete,!0)});var div=root$N(),div_1=child(div),node2=child(div_1);McpLogo(node2,{class:"h-5 w-5 md:h-6 md:w-6"}),next$1(2),reset( -div_1);var div_2=sibling(div_1,2),node_1=child(div_2);Button(node_1,{variant:"outline",size:"sm",class:"shrink-0",onclick:()=>set$1(isAddingServer,!0),children:($$anchor2,$$slotProps)=>{var fragment=root_1$p(),node_2=first_child(fragment);Plus(node_2,{class:"h-4 w-4"}),next$1(),append($$anchor2,fragment)},$$slots:{default:!0}}),reset(div_2);var node_3=sibling(div_2,2);DialogMcpServerAddNew(node_3,{get open(){return get$3(isAddingServer)},set open($$value){set$1(isAddingServer,$$value,!0)}});var div_3=sibling( -node_3,2),node_4=child(div_3);{var consequent=$$anchor2=>{var div_4=root_2$F();append($$anchor2,div_4)};if_block(node_4,$$render=>{get$3(servers).length===0&&!get$3(isAddingServer)&&$$render(consequent)})}var node_5=sibling(node_4,2);{var consequent_2=$$anchor2=>{var div_5=root_3$z();each(div_5,21,()=>get$3(servers),server=>server.id,($$anchor3,server)=>{var fragment_1=comment$2(),node_6=first_child(fragment_1);{var consequent_1=$$anchor4=>{McpServerCardSkeleton($$anchor4)},alternate=$$anchor4=>{ -{let $0=user_derived(()=>conversationsStore.isMcpServerEnabledForChat(get$3(server).id));McpServerCard($$anchor4,{get server(){return get$3(server)},get enabled(){return get$3($0)},onToggle:async()=>{const wasEnabled=conversationsStore.isMcpServerEnabledForChat(get$3(server).id);await conversationsStore.toggleMcpServerForChat(get$3(server).id),wasEnabled||toolsStore.enableAllToolsForServer(get$3(server).id)},onUpdate:updates=>mcpStore.updateServer(get$3(server).id,updates),onDelete:()=>mcpStore. -removeServer(get$3(server).id)})}};if_block(node_6,$$render=>{get$3(initialLoadComplete)?$$render(alternate,-1):$$render(consequent_1)})}append($$anchor3,fragment_1)}),reset(div_5),append($$anchor2,div_5)};if_block(node_5,$$render=>{get$3(servers).length>0&&$$render(consequent_2)})}reset(div_3),reset(div),template_effect(()=>set_class(div_3,1,`grid gap-5 md:space-y-4 ${$$props.class??""}`)),transition(1,div,()=>fade,()=>({duration:150})),append($$anchor,div),pop()}delegate(["click"]);var root_3$y=from_html( -" Tools",1),root_5$h=from_html(" Resources",1),root_7$g=from_html(" Prompts",1),root_9$c=from_html(" Logging",1),root_11$7=from_html(" Completions",1),root_13$5=from_html(" Tasks",1),root_1$o=from_html(" ",1);function McpCapabilitiesBadges($$anchor,$$props){push$1($$props,!0);var fragment=comment$2(),node2=first_child(fragment);{var consequent_6=$$anchor2=>{var fragment_1=root_1$o(),node_1=first_child(fragment_1);{var consequent=$$anchor3=>{Badge($$anchor3, -{variant:"outline",class:"h-5 gap-1 bg-green-50 px-1.5 text-[10px] dark:bg-green-950",children:($$anchor4,$$slotProps)=>{var fragment_3=root_3$y(),node_2=first_child(fragment_3);Wrench(node_2,{class:"h-3 w-3 text-green-600 dark:text-green-400"}),next$1(),append($$anchor4,fragment_3)},$$slots:{default:!0}})};if_block(node_1,$$render=>{$$props.capabilities.server.tools&&$$render(consequent)})}var node_3=sibling(node_1,2);{var consequent_1=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 g\ -ap-1 bg-blue-50 px-1.5 text-[10px] dark:bg-blue-950",children:($$anchor4,$$slotProps)=>{var fragment_5=root_5$h(),node_4=first_child(fragment_5);Database(node_4,{class:"h-3 w-3 text-blue-600 dark:text-blue-400"}),next$1(),append($$anchor4,fragment_5)},$$slots:{default:!0}})};if_block(node_3,$$render=>{$$props.capabilities.server.resources&&$$render(consequent_1)})}var node_5=sibling(node_3,2);{var consequent_2=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-purple-50 px-1.5 te\ -xt-[10px] dark:bg-purple-950",children:($$anchor4,$$slotProps)=>{var fragment_7=root_7$g(),node_6=first_child(fragment_7);Message_square(node_6,{class:"h-3 w-3 text-purple-600 dark:text-purple-400"}),next$1(),append($$anchor4,fragment_7)},$$slots:{default:!0}})};if_block(node_5,$$render=>{$$props.capabilities.server.prompts&&$$render(consequent_2)})}var node_7=sibling(node_5,2);{var consequent_3=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-orange-50 px-1.5 text-[10px] dark:\ -bg-orange-950",children:($$anchor4,$$slotProps)=>{var fragment_9=root_9$c(),node_8=first_child(fragment_9);File_text(node_8,{class:"h-3 w-3 text-orange-600 dark:text-orange-400"}),next$1(),append($$anchor4,fragment_9)},$$slots:{default:!0}})};if_block(node_7,$$render=>{$$props.capabilities.server.logging&&$$render(consequent_3)})}var node_9=sibling(node_7,2);{var consequent_4=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-cyan-50 px-1.5 text-[10px] dark:bg-cyan-950",children:($$anchor4,$$slotProps)=>{ -var fragment_11=root_11$7(),node_10=first_child(fragment_11);Sparkles(node_10,{class:"h-3 w-3 text-cyan-600 dark:text-cyan-400"}),next$1(),append($$anchor4,fragment_11)},$$slots:{default:!0}})};if_block(node_9,$$render=>{$$props.capabilities.server.completions&&$$render(consequent_4)})}var node_11=sibling(node_9,2);{var consequent_5=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-pink-50 px-1.5 text-[10px] dark:bg-pink-950",children:($$anchor4,$$slotProps)=>{var fragment_13=root_13$5(), -node_12=first_child(fragment_13);List_checks(node_12,{class:"h-3 w-3 text-pink-600 dark:text-pink-400"}),next$1(),append($$anchor4,fragment_13)},$$slots:{default:!0}})};if_block(node_11,$$render=>{$$props.capabilities.server.tasks&&$$render(consequent_5)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{$$props.capabilities&&$$render(consequent_6)})}append($$anchor,fragment),pop()}var root_6$j=from_html(' '),root_3$x=from_html(" ",1),root_9$b=from_html( -'
details
 
'),root_8$h=from_html('
',1),root_7$f=from_html('
'),root_2$E=from_html('
',1);function McpConnectionLogs($$anchor,$$props){push$1($$props,!0);let defaultExpanded=prop($$props,"defaultExpanded",3,!1),isExpanded=user_derived(defaultExpanded);function formatLogDetails(details){if(details==null)return"";try{return JSON.stringify(details,null,2)}catch{return String(details)}}var fragment=comment$2(),node2=first_child(fragment);{var consequent_3=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1); -component(node_1,()=>Collapsible,($$anchor3,Collapsible_Root)=>{Collapsible_Root($$anchor3,{get class(){return $$props.class},get open(){return get$3(isExpanded)},set open($$value){set$1(isExpanded,$$value)},children:($$anchor4,$$slotProps)=>{var fragment_2=root_2$E(),div=first_child(fragment_2),node_2=child(div);component(node_2,()=>Collapsible_trigger,($$anchor5,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor5,{class:"flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-\ -foreground",children:($$anchor6,$$slotProps2)=>{var fragment_3=root_3$x(),node_3=first_child(fragment_3);{var consequent=$$anchor7=>{Chevron_down($$anchor7,{class:"h-3.5 w-3.5"})},alternate=$$anchor7=>{Chevron_right($$anchor7,{class:"h-3.5 w-3.5"})};if_block(node_3,$$render=>{get$3(isExpanded)?$$render(consequent):$$render(alternate,-1)})}var span=sibling(node_3,2),text2=child(span);reset(span);var node_4=sibling(span,2);{var consequent_1=$$anchor7=>{var span_1=root_6$j(),text_1=child(span_1);reset( -span_1),template_effect(()=>set_text(text_1,`· Connected in ${$$props.connectionTimeMs??""}ms`)),append($$anchor7,span_1)};if_block(node_4,$$render=>{$$props.connectionTimeMs!==void 0&&$$render(consequent_1)})}template_effect(()=>set_text(text2,`Connection Log (${$$props.logs.length??""})`)),append($$anchor6,fragment_3)},$$slots:{default:!0}})}),reset(div);var node_5=sibling(div,2);component(node_5,()=>Collapsible_content,($$anchor5,Collapsible_Content)=>{Collapsible_Content($$anchor5,{class:"m\ -t-2",children:($$anchor6,$$slotProps2)=>{var div_1=root_7$f();each(div_1,21,()=>$$props.logs,log=>log.timestamp.getTime()+log.message,($$anchor7,log)=>{const IconComponent=user_derived(()=>getMcpLogLevelIcon(get$3(log).level));var fragment_6=root_8$h(),div_2=first_child(fragment_6),span_2=child(div_2),text_2=child(span_2,!0);reset(span_2);var node_6=sibling(span_2,2);component(node_6,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{IconComponent_1($$anchor8,{class:"mt-0.5 h-3 w-3 shrink-0"})}); -var span_3=sibling(node_6,2),text_3=child(span_3,!0);reset(span_3),reset(div_2);var node_7=sibling(div_2,2);{var consequent_2=$$anchor8=>{var details_1=root_9$b(),pre=sibling(child(details_1),2),text_4=child(pre,!0);reset(pre),reset(details_1),template_effect($0=>set_text(text_4,$0),[()=>formatLogDetails(get$3(log).details)]),append($$anchor8,details_1)};if_block(node_7,$$render=>{get$3(log).details!==void 0&&$$render(consequent_2)})}template_effect(($0,$1)=>{set_class(div_2,1,$0),set_text(text_2, -$1),set_text(text_3,get$3(log).message)},[()=>clsx(["flex items-start gap-1.5",getMcpLogLevelClass(get$3(log).level)]),()=>formatTime(get$3(log).timestamp)]),append($$anchor7,fragment_6)}),reset(div_1),append($$anchor6,div_1)},$$slots:{default:!0}})}),append($$anchor4,fragment_2)},$$slots:{default:!0}})}),append($$anchor2,fragment_1)};if_block(node2,$$render=>{$$props.logs.length>0&&$$render(consequent_3)})}append($$anchor,fragment),pop()}var root_1$n=from_html('

'),root_3$w=from_html('(Run
llama-server
with
--webui-mcp-proxy
flag)
'),root_2$D=from_html(''),root$M=from_html('
');function McpServerForm($$anchor,$$props){push$1($$props,!0);let useProxy=prop($$props,"useProxy",3,!1),urlError=prop($$props,"urlError",3,null),id2=prop($$props,"id",3,"server"),isWebSocket=user_derived(()=>$$props.url.toLowerCase().startsWith(UrlProtocol.WEBSOCKET)||$$props.url.toLowerCase().startsWith(UrlProtocol.WEBSOCKET_SECURE)),headerPairs=user_derived(()=>parseHeadersToArray($$props.headers));function updateHeaderPairs(newPairs){set$1(headerPairs,newPairs),$$props.onHeadersChange(serializeHeaders( -newPairs))}var div=root$M(),div_1=child(div),label=child(div_1),node2=sibling(label,2);{let $0=user_derived(()=>urlError()?"border-destructive":"");Input(node2,{get id(){return`server-url-${id2()??""}`},type:"url",get placeholder(){return MCP_SERVER_URL_PLACEHOLDER},get value(){return $$props.url},oninput:e=>$$props.onUrlChange(e.currentTarget.value),get class(){return get$3($0)}})}var node_1=sibling(node2,2);{var consequent=$$anchor2=>{var p2=root_1$n(),text2=child(p2,!0);reset(p2),template_effect( -()=>set_text(text2,urlError())),append($$anchor2,p2)};if_block(node_1,$$render=>{urlError()&&$$render(consequent)})}var node_2=sibling(node_1,2);{var consequent_2=$$anchor2=>{var label_1=root_2$D(),node_3=child(label_1);{let $0=user_derived(()=>!mcpStore.isProxyAvailable);Switch(node_3,{class:"mt-1",get id(){return`use-proxy-${id2()??""}`},get checked(){return useProxy()},get disabled(){return get$3($0)},onCheckedChange:checked=>$$props.onUseProxyChange?.(checked)})}var span=sibling(node_3,2),node_4=sibling( -child(span),4);{var consequent_1=$$anchor3=>{var span_1=root_3$w();append($$anchor3,span_1)};if_block(node_4,$$render=>{mcpStore.isProxyAvailable||$$render(consequent_1)})}reset(span),reset(label_1),template_effect(()=>set_class(label_1,1,clsx(["mt-3 flex items-start gap-2",mcpStore.isProxyAvailable&&"cursor-pointer",!mcpStore.isProxyAvailable&&"opacity-80"]))),append($$anchor2,label_1)};if_block(node_2,$$render=>{!get$3(isWebSocket)&&$$props.onUseProxyChange&&$$render(consequent_2)})}reset(div_1); -var node_5=sibling(div_1,2);KeyValuePairs(node_5,{class:"mt-2",get pairs(){return get$3(headerPairs)},onPairsChange:updateHeaderPairs,keyPlaceholder:"Header name",valuePlaceholder:"Value",addButtonLabel:"Add",emptyMessage:"No custom headers configured.",sectionLabel:"Custom Headers",sectionLabelOptional:!0}),reset(div),template_effect(()=>set_attribute(label,"for",`server-url-${id2()??""}`)),append($$anchor,div),pop()}var root$L=from_html("
");function Skeleton($$anchor,$$props){push$1( -$$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var div=root$L();attribute_effect(div,$0=>({"data-slot":"skeleton",class:$0,...restProps}),[()=>cn$1("animate-pulse rounded-md bg-accent",$$props.class)]),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root_4$m=from_html('

'),root_5$g=from_html('

\ -

'),root_6$i=from_html('
',1),root_7$e=from_html(" ",1),root_12$6=from_html('
'),root_3$v=from_html('
',1),root$K=from_html(" ",1);function McpServerCard($$anchor,$$props){push$1($$props,!0);let healthState=user_derived(()=>mcpStore.getHealthCheckState($$props.server.id)),displayName=user_derived(()=>mcpStore.getServerLabel($$props.server)),faviconUrl=user_derived(()=>mcpStore.getServerFavicon($$props.server.id)),isIdle=user_derived(()=>get$3(healthState).status===HealthCheckStatus.IDLE),isHealthChecking=user_derived(()=>get$3(healthState).status===HealthCheckStatus. -CONNECTING),isConnected=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS),isError=user_derived(()=>get$3(healthState).status===HealthCheckStatus.ERROR),showSkeleton=user_derived(()=>get$3(isIdle)||get$3(isHealthChecking)),errorMessage=user_derived(()=>get$3(healthState).status===HealthCheckStatus.ERROR?get$3(healthState).message:void 0),tools=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS?get$3(healthState).tools:[]),connectionLogs=user_derived(()=>get$3( -healthState).status===HealthCheckStatus.CONNECTING||get$3(healthState).status===HealthCheckStatus.SUCCESS||get$3(healthState).status===HealthCheckStatus.ERROR?get$3(healthState).logs:[]),successState=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS?get$3(healthState):null),serverInfo=user_derived(()=>get$3(successState)?.serverInfo),capabilities=user_derived(()=>get$3(successState)?.capabilities),transportType=user_derived(()=>get$3(successState)?.transportType),protocolVersion=user_derived( -()=>get$3(successState)?.protocolVersion),connectionTimeMs=user_derived(()=>get$3(successState)?.connectionTimeMs),instructions=user_derived(()=>get$3(successState)?.instructions),isEditing2=user_derived(()=>!$$props.server.url.trim()),showDeleteDialog=state$1(!1),editFormRef=state$1(null);function handleHealthCheck(){mcpStore.runHealthCheck($$props.server)}async function startEditing(){set$1(isEditing2,!0),await tick(),get$3(editFormRef)?.setInitialValues($$props.server.url,$$props.server.headers|| -"",$$props.server.useProxy||!1)}function cancelEditing(){$$props.server.url.trim()?set$1(isEditing2,!1):$$props.onDelete()}function saveEditing(url2,headers,useProxy){$$props.onUpdate({url:url2,headers:headers||void 0,useProxy}),set$1(isEditing2,!1),$$props.server.enabled&&url2&&setTimeout(()=>mcpStore.runHealthCheck({...$$props.server,url:url2,useProxy}),100)}function handleDeleteClick(){set$1(showDeleteDialog,!0)}var fragment=root$K(),node2=first_child(fragment);component(node2,()=>Card,($$anchor2,Card_Root)=>{ -Card_Root($$anchor2,{class:"!gap-3 bg-muted/30 p-4",children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{var consequent=$$anchor4=>{bind_this(McpServerCardEditForm($$anchor4,{get serverId(){return $$props.server.id},get serverUrl(){return $$props.server.url},get serverUseProxy(){return $$props.server.useProxy},onSave:saveEditing,onCancel:cancelEditing}),$$value=>set$1(editFormRef,$$value,!0),()=>get$3(editFormRef))},alternate_1=$$anchor4=>{var fragment_3=root_3$v(), -node_2=first_child(fragment_3);{let $0=user_derived(()=>$$props.enabled??$$props.server.enabled);McpServerCardHeader(node_2,{get displayName(){return get$3(displayName)},get faviconUrl(){return get$3(faviconUrl)},get enabled(){return get$3($0)},get disabled(){return get$3(isError)},get onToggle(){return $$props.onToggle},get serverInfo(){return get$3(serverInfo)},get capabilities(){return get$3(capabilities)},get transportType(){return get$3(transportType)}})}var node_3=sibling(node_2,2);{var consequent_1=$$anchor5=>{ -var p2=root_4$m(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,get$3(errorMessage))),append($$anchor5,p2)};if_block(node_3,$$render=>{get$3(isError)&&get$3(errorMessage)&&$$render(consequent_1)})}var node_4=sibling(node_3,2);{var consequent_2=$$anchor5=>{var p_1=root_5$g(),text_1=child(p_1,!0);reset(p_1),template_effect(()=>set_text(text_1,get$3(serverInfo).description)),append($$anchor5,p_1)};if_block(node_4,$$render=>{get$3(isConnected)&&get$3(serverInfo)?.description&&$$render( -consequent_2)})}var div=sibling(node_4,2),node_5=child(div);{var consequent_3=$$anchor5=>{var fragment_4=root_6$i(),div_1=first_child(fragment_4),div_2=child(div_1),node_6=child(div_2);Skeleton(node_6,{class:"h-4 w-4 rounded"});var node_7=sibling(node_6,2);Skeleton(node_7,{class:"h-3 w-24"}),reset(div_2);var div_3=sibling(div_2,2),node_8=child(div_3);Skeleton(node_8,{class:"h-5 w-16 rounded-full"});var node_9=sibling(node_8,2);Skeleton(node_9,{class:"h-5 w-20 rounded-full"});var node_10=sibling( -node_9,2);Skeleton(node_10,{class:"h-5 w-14 rounded-full"}),reset(div_3),reset(div_1);var div_4=sibling(div_1,2),div_5=child(div_4),node_11=child(div_5);Skeleton(node_11,{class:"h-4 w-4 rounded"});var node_12=sibling(node_11,2);Skeleton(node_12,{class:"h-3 w-32"}),reset(div_5),reset(div_4),append($$anchor5,fragment_4)},alternate=$$anchor5=>{var fragment_5=root_7$e(),node_13=first_child(fragment_5);{var consequent_4=$$anchor6=>{McpServerInfo($$anchor6,{get instructions(){return get$3(instructions)}})}; -if_block(node_13,$$render=>{get$3(isConnected)&&get$3(instructions)&&$$render(consequent_4)})}var node_14=sibling(node_13,2);{var consequent_5=$$anchor6=>{McpServerCardToolsList($$anchor6,{get tools(){return get$3(tools)}})};if_block(node_14,$$render=>{get$3(tools).length>0&&$$render(consequent_5)})}var node_15=sibling(node_14,2);{var consequent_6=$$anchor6=>{McpConnectionLogs($$anchor6,{get logs(){return get$3(connectionLogs)},get connectionTimeMs(){return get$3(connectionTimeMs)}})};if_block(node_15, -$$render=>{get$3(connectionLogs).length>0&&$$render(consequent_6)})}append($$anchor5,fragment_5)};if_block(node_5,$$render=>{get$3(showSkeleton)?$$render(consequent_3):$$render(alternate,-1)})}reset(div);var div_6=sibling(div,2),node_16=child(div_6);{var consequent_7=$$anchor5=>{Skeleton($$anchor5,{class:"h-3 w-28"})},consequent_8=$$anchor5=>{var div_7=root_12$6(),span=child(div_7),text_2=child(span);reset(span),reset(div_7),template_effect(()=>set_text(text_2,`Protocol version: ${get$3(protocolVersion)?? -""}`)),append($$anchor5,div_7)};if_block(node_16,$$render=>{get$3(showSkeleton)?$$render(consequent_7):get$3(protocolVersion)&&$$render(consequent_8,1)})}var node_17=sibling(node_16,2);McpServerCardActions(node_17,{get isHealthChecking(){return get$3(isHealthChecking)},onEdit:startEditing,onRefresh:handleHealthCheck,onDelete:handleDeleteClick}),reset(div_6),append($$anchor4,fragment_3)};if_block(node_1,$$render=>{get$3(isEditing2)?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor3, -fragment_1)},$$slots:{default:!0}})});var node_18=sibling(node2,2);McpServerCardDeleteDialog(node_18,{get displayName(){return get$3(displayName)},onOpenChange:open2=>set$1(showDeleteDialog,open2,!0),get onConfirm(){return $$props.onDelete},get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)}}),append($$anchor,fragment),pop()}var root_3$u=from_html(" ",1),root_1$m=from_html('
'),root$J=from_html( -'
');function McpServerCardHeader($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1);var div=root$J(),div_1=child(div),div_2=child(div_1),div_3=child(div_2),node2=child(div_3);McpServerIdentity(node2,{get displayName(){return $$props. -displayName},get faviconUrl(){return $$props.faviconUrl},get serverInfo(){return $$props.serverInfo},iconClass:"h-5 w-5",iconRounded:"rounded",nameClass:"leading-6 font-medium"}),reset(div_3);var node_1=sibling(div_3,2);{var consequent_3=$$anchor2=>{var div_4=root_1$m(),node_2=child(div_4);{var consequent_1=$$anchor3=>{const TransportIcon=user_derived(()=>MCP_TRANSPORT_ICONS[$$props.transportType]);Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 px-1.5 text-[10px]",children:($$anchor4,$$slotProps)=>{ -var fragment_1=root_3$u(),node_3=first_child(fragment_1);{var consequent=$$anchor5=>{var fragment_2=comment$2(),node_4=first_child(fragment_2);component(node_4,()=>get$3(TransportIcon),($$anchor6,TransportIcon_1)=>{TransportIcon_1($$anchor6,{class:"h-3 w-3"})}),append($$anchor5,fragment_2)};if_block(node_3,$$render=>{get$3(TransportIcon)&&$$render(consequent)})}var text2=sibling(node_3);template_effect(()=>set_text(text2,` ${(MCP_TRANSPORT_LABELS[$$props.transportType]||$$props.transportType)??""}`)), -append($$anchor4,fragment_1)},$$slots:{default:!0}})};if_block(node_2,$$render=>{$$props.transportType&&$$render(consequent_1)})}var node_5=sibling(node_2,2);{var consequent_2=$$anchor3=>{McpCapabilitiesBadges($$anchor3,{get capabilities(){return $$props.capabilities}})};if_block(node_5,$$render=>{$$props.capabilities&&$$render(consequent_2)})}reset(div_4),append($$anchor2,div_4)};if_block(node_1,$$render=>{($$props.capabilities||$$props.transportType)&&$$render(consequent_3)})}reset(div_2);var div_5=sibling( -div_2,2),node_6=child(div_5);Switch(node_6,{get checked(){return $$props.enabled},get disabled(){return disabled()},get onCheckedChange(){return $$props.onToggle}}),reset(div_5),reset(div_1),reset(div),append($$anchor,div),pop()}var root$I=from_html('
');function McpServerCardActions($$anchor,$$props){var div=root$I(),node2=child(div);Button(node2,{variant:"ghost",size:"icon",class:"h-7 w-7",get onclick(){return $$props.onEdit},"aria-\ -label":"Edit",children:($$anchor2,$$slotProps)=>{Pencil($$anchor2,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}});var node_1=sibling(node2,2);Button(node_1,{variant:"ghost",size:"icon",class:"h-7 w-7",get onclick(){return $$props.onRefresh},get disabled(){return $$props.isHealthChecking},"aria-label":"Refresh",children:($$anchor2,$$slotProps)=>{Refresh_cw($$anchor2,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}});var node_2=sibling(node_1,2);Button(node_2,{variant:"ghost",size:"icon",class:"hover:t\ -ext-destructive-foreground h-7 w-7 text-destructive hover:bg-destructive/10",get onclick(){return $$props.onDelete},"aria-label":"Delete",children:($$anchor2,$$slotProps)=>{Trash_2($$anchor2,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}}),reset(div),append($$anchor,div)}var root_2$C=from_html(" ",1),root_8$g=from_html('

'),root_6$h=from_html("
"),root_5$f=from_html('
\ -
'),root_1$l=from_html(" ",1);function McpServerCardToolsList($$anchor,$$props){push$1($$props,!0);let isExpanded=state$1(!1),toolsCount=user_derived(()=>$$props.tools.length);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible,($$anchor2,Collapsible_Root)=>{Collapsible_Root($$anchor2,{get open(){return get$3(isExpanded)},set open($$value){set$1(isExpanded,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$l(),node_1=first_child(fragment_1); -component(node_1,()=>Collapsible_trigger,($$anchor4,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor4,{class:"flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$C(),node_2=first_child(fragment_2);{var consequent=$$anchor6=>{Chevron_down($$anchor6,{class:"h-3.5 w-3.5"})},alternate=$$anchor6=>{Chevron_right($$anchor6,{class:"h-3.5 w-3.5"})};if_block(node_2,$$render=>{get$3(isExpanded)?$$render(consequent): -$$render(alternate,-1)})}var span=sibling(node_2,2),text2=child(span);reset(span),template_effect(()=>set_text(text2,`${get$3(toolsCount)??""} tools available · Show details`)),append($$anchor5,fragment_2)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Collapsible_content,($$anchor4,Collapsible_Content)=>{Collapsible_Content($$anchor4,{class:"mt-2",children:($$anchor5,$$slotProps2)=>{var div=root_5$f();each(div,21,()=>$$props.tools,tool=>tool.name,($$anchor6,tool)=>{ -var div_1=root_6$h(),node_4=child(div_1);Badge(node_4,{variant:"secondary",children:($$anchor7,$$slotProps3)=>{next$1();var text_1=text$8();template_effect(()=>set_text(text_1,get$3(tool).name)),append($$anchor7,text_1)},$$slots:{default:!0}});var node_5=sibling(node_4,2);{var consequent_1=$$anchor7=>{var p2=root_8$g(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(tool).description)),append($$anchor7,p2)};if_block(node_5,$$render=>{get$3(tool).description&&$$render(consequent_1)})} -reset(div_1),append($$anchor6,div_1)}),reset(div),append($$anchor5,div)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root$H=from_html('

Configure Server

');function McpServerCardEditForm($$anchor,$$props){push$1($$props,!0);let serverUseProxy=prop($$props,"serverUseProxy",3,!1),editUrl=user_derived(()=>$$props. -serverUrl),editHeaders=state$1(""),editUseProxy=user_derived(serverUseProxy),urlError=user_derived(()=>{if(!get$3(editUrl).trim())return"URL is required";try{return new URL(get$3(editUrl)),null}catch{return"Invalid URL format"}}),canSave=user_derived(()=>!get$3(urlError));function handleSave(){get$3(canSave)&&$$props.onSave(get$3(editUrl).trim(),get$3(editHeaders).trim(),get$3(editUseProxy))}function setInitialValues(url2,headers,useProxy){set$1(editUrl,url2),set$1(editHeaders,headers,!0),set$1( -editUseProxy,useProxy)}var $$exports={setInitialValues},div=root$H(),node2=sibling(child(div),2);{let $0=user_derived(()=>get$3(editUrl)?get$3(urlError):null);McpServerForm(node2,{get url(){return get$3(editUrl)},get headers(){return get$3(editHeaders)},get useProxy(){return get$3(editUseProxy)},onUrlChange:v=>set$1(editUrl,v),onHeadersChange:v=>set$1(editHeaders,v,!0),onUseProxyChange:v=>set$1(editUseProxy,v),get urlError(){return get$3($0)},get id(){return $$props.serverId}})}var div_1=sibling( -node2,2),node_1=child(div_1);Button(node_1,{variant:"secondary",size:"sm",get onclick(){return $$props.onCancel},children:($$anchor2,$$slotProps)=>{next$1();var text2=text$8("Cancel");append($$anchor2,text2)},$$slots:{default:!0}});var node_2=sibling(node_1,2);{let $0=user_derived(()=>!get$3(canSave));Button(node_2,{size:"sm",onclick:handleSave,get disabled(){return get$3($0)},children:($$anchor2,$$slotProps)=>{next$1();var text_1=text$8();template_effect($02=>set_text(text_1,$02),[()=>$$props.serverUrl. -trim()?"Update":"Add"]),append($$anchor2,text_1)},$$slots:{default:!0}})}return reset(div_1),reset(div),append($$anchor,div),pop($$exports)}function Alert_dialog_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-lg font-semibold",$$props.class));component(node2,()=>Dialog_title$1,($$anchor2,AlertDialogPrimitive_Title)=>{ -AlertDialogPrimitive_Title($$anchor2,spread_props({"data-slot":"alert-dialog-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Alert_dialog_action($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(buttonVariants(), -$$props.class));component(node2,()=>Alert_dialog_action$1,($$anchor2,AlertDialogPrimitive_Action)=>{AlertDialogPrimitive_Action($$anchor2,spread_props({"data-slot":"alert-dialog-action",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Alert_dialog_cancel($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","cl\ -ass"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(buttonVariants({variant:"outline"}),$$props.class));component(node2,()=>Alert_dialog_cancel$1,($$anchor2,AlertDialogPrimitive_Cancel)=>{AlertDialogPrimitive_Cancel($$anchor2,spread_props({"data-slot":"alert-dialog-cancel",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root$G=from_html("
");function Alert_dialog_footer($$anchor,$$props){ -push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$G();attribute_effect(div,$0=>({"data-slot":"alert-dialog-footer",class:$0,...restProps}),[()=>cn$1("mt-6 flex flex-row gap-2 sm:mt-0 sm:justify-end [&>*]:flex-1 sm:[&>*]:flex-none",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div), -pop()}var root$F=from_html("
");function Alert_dialog_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$F();attribute_effect(div,$0=>({"data-slot":"alert-dialog-header",class:$0,...restProps}),[()=>cn$1("flex flex-col gap-2 text-center sm:text-left",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div, -$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Alert_dialog_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component( -node2,()=>Dialog_overlay$1,($$anchor2,AlertDialogPrimitive_Overlay)=>{AlertDialogPrimitive_Overlay($$anchor2,spread_props({"data-slot":"alert-dialog-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$k=from_html(" ",1);function Alert_dialog_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy", -"ref","class","portalProps"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,AlertDialogPrimitive_Portal)=>{AlertDialogPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$k(),node_1=first_child(fragment_1);Alert_dialog_overlay(node_1,{});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1("fixed z-[999999] grid w-full gap-4 border bg-background p-6 shadow-lg duration-200","r\ -ight-0 bottom-0 left-0 max-h-[100dvh] translate-x-0 translate-y-0 overflow-y-auto rounded-t-lg","data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-full","data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-full","sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-h-[100vh] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-lg","sm:data-[state=closed]:slide-out-to-bottom-0 \ -sm:data-[state=closed]:zoom-out-95","sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95",$$props.class));component(node_2,()=>Alert_dialog_content$1,($$anchor4,AlertDialogPrimitive_Content)=>{AlertDialogPrimitive_Content($$anchor4,spread_props({"data-slot":"alert-dialog-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()} -function Alert_dialog_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,AlertDialogPrimitive_Description)=>{AlertDialogPrimitive_Description($$anchor2,spread_props({"data-slot":"alert-dialog-description", -get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$1=Alert_dialog,Portal=Portal$2;var root_5$e=from_html(`Are you sure you want to delete ? This action cannot be +children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Dialog_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards da\ +ta-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,DialogPrimitive_Overlay)=>{DialogPrimitive_Overlay($$anchor2,spread_props({"data-slot":"dialog-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_4$n=from_html(' Close',1),root_2$G=from_html(" ",1),root_1$q=from_html(" ",1);function Dialog_content($$anchor,$$props){ +push$1($$props,!0);let ref2=prop($$props,"ref",15,null),showCloseButton=prop($$props,"showCloseButton",3,!0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","portalProps","children","showCloseButton"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$1,($$anchor2,Dialog_Portal)=>{Dialog_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$q(),node_1=first_child(fragment_1);component( +node_1,()=>Dialog_overlay,($$anchor4,Dialog_Overlay)=>{Dialog_Overlay($$anchor4,{})});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1("fixed top-[50%] left-[50%] z-50 grid max-h-[100dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border border-border/30 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 d\ +ata-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg md:max-h-[100vh]",$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,DialogPrimitive_Content)=>{DialogPrimitive_Content($$anchor4,spread_props({"data-slot":"dialog-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$G(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props. +children??noop$3);var node_4=sibling(node_3,2);{var consequent=$$anchor6=>{var fragment_3=comment$2(),node_5=first_child(fragment_3);component(node_5,()=>Dialog_close,($$anchor7,DialogPrimitive_Close)=>{DialogPrimitive_Close($$anchor7,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:n\ +ot([class*='size-'])]:size-4",children:($$anchor8,$$slotProps3)=>{var fragment_4=root_4$n(),node_6=first_child(fragment_4);X(node_6,{}),next$1(2),append($$anchor8,fragment_4)},$$slots:{default:!0}})}),append($$anchor6,fragment_3)};if_block(node_4,$$render=>{showCloseButton()&&$$render(consequent)})}append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dialog_description($$anchor,$$props){push$1($$props, +!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,DialogPrimitive_Description)=>{DialogPrimitive_Description($$anchor2,spread_props({"data-slot":"dialog-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ +ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$2=Dialog,Portal$1=Portal$2;var root_1$p=from_html(" Add New Server",1),root_2$F=from_html('
No MCP Servers configured yet. Add one to enable agentic features.
'),root_3$z=from_html('
'),root$N=from_html('

MCP Servers

');function SettingsMcpServers($$anchor,$$props){push$1($$props,!0);let servers=user_derived(()=>mcpStore.getServersSorted()),initialLoadComplete=state$1(!1),isAddingServer=state$1(!1);onMount$1( +()=>{if(page$1.url.searchParams.has("add")){set$1(isAddingServer,!0);const newUrl=new URL(page$1.url);newUrl.searchParams.delete("add"),replaceState(newUrl,{})}}),user_effect(()=>{if(get$3(initialLoadComplete))return;get$3(servers).length>0&&get$3(servers).every(server=>{const state2=mcpStore.getHealthCheckState(server.id);return state2.status===HealthCheckStatus.SUCCESS||state2.status===HealthCheckStatus.ERROR})&&set$1(initialLoadComplete,!0)});var div=root$N(),div_1=child(div),node2=child(div_1); +McpLogo(node2,{class:"h-5 w-5 md:h-6 md:w-6"}),next$1(2),reset(div_1);var div_2=sibling(div_1,2),node_1=child(div_2);Button(node_1,{variant:"outline",size:"sm",class:"shrink-0",onclick:()=>set$1(isAddingServer,!0),children:($$anchor2,$$slotProps)=>{var fragment=root_1$p(),node_2=first_child(fragment);Plus(node_2,{class:"h-4 w-4"}),next$1(),append($$anchor2,fragment)},$$slots:{default:!0}}),reset(div_2);var node_3=sibling(div_2,2);DialogMcpServerAddNew(node_3,{get open(){return get$3(isAddingServer)}, +set open($$value){set$1(isAddingServer,$$value,!0)}});var div_3=sibling(node_3,2),node_4=child(div_3);{var consequent=$$anchor2=>{var div_4=root_2$F();append($$anchor2,div_4)};if_block(node_4,$$render=>{get$3(servers).length===0&&!get$3(isAddingServer)&&$$render(consequent)})}var node_5=sibling(node_4,2);{var consequent_2=$$anchor2=>{var div_5=root_3$z();each(div_5,21,()=>get$3(servers),server=>server.id,($$anchor3,server)=>{var fragment_1=comment$2(),node_6=first_child(fragment_1);{var consequent_1=$$anchor4=>{ +McpServerCardSkeleton($$anchor4)},alternate=$$anchor4=>{{let $0=user_derived(()=>conversationsStore.isMcpServerEnabledForChat(get$3(server).id));McpServerCard($$anchor4,{get server(){return get$3(server)},get enabled(){return get$3($0)},onToggle:async()=>{const wasEnabled=conversationsStore.isMcpServerEnabledForChat(get$3(server).id);await conversationsStore.toggleMcpServerForChat(get$3(server).id),wasEnabled||toolsStore.enableAllToolsForServer(get$3(server).id)},onUpdate:updates=>mcpStore.updateServer( +get$3(server).id,updates),onDelete:()=>mcpStore.removeServer(get$3(server).id)})}};if_block(node_6,$$render=>{get$3(initialLoadComplete)?$$render(alternate,-1):$$render(consequent_1)})}append($$anchor3,fragment_1)}),reset(div_5),append($$anchor2,div_5)};if_block(node_5,$$render=>{get$3(servers).length>0&&$$render(consequent_2)})}reset(div_3),reset(div),template_effect(()=>set_class(div_3,1,`grid gap-5 md:space-y-4 ${$$props.class??""}`)),transition(1,div,()=>fade,()=>({duration:150})),append($$anchor, +div),pop()}delegate(["click"]);var root_3$y=from_html(" Tools",1),root_5$h=from_html(" Resources",1),root_7$g=from_html(" Prompts",1),root_9$c=from_html(" Logging",1),root_11$7=from_html(" Completions",1),root_13$5=from_html(" Tasks",1),root_1$o=from_html(" ",1);function McpCapabilitiesBadges($$anchor,$$props){push$1($$props,!0);var fragment=comment$2(),node2=first_child(fragment);{var consequent_6=$$anchor2=>{var fragment_1=root_1$o(),node_1=first_child(fragment_1); +{var consequent=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-green-50 px-1.5 text-[10px] dark:bg-green-950",children:($$anchor4,$$slotProps)=>{var fragment_3=root_3$y(),node_2=first_child(fragment_3);Wrench(node_2,{class:"h-3 w-3 text-green-600 dark:text-green-400"}),next$1(),append($$anchor4,fragment_3)},$$slots:{default:!0}})};if_block(node_1,$$render=>{$$props.capabilities.server.tools&&$$render(consequent)})}var node_3=sibling(node_1,2);{var consequent_1=$$anchor3=>{Badge( +$$anchor3,{variant:"outline",class:"h-5 gap-1 bg-blue-50 px-1.5 text-[10px] dark:bg-blue-950",children:($$anchor4,$$slotProps)=>{var fragment_5=root_5$h(),node_4=first_child(fragment_5);Database(node_4,{class:"h-3 w-3 text-blue-600 dark:text-blue-400"}),next$1(),append($$anchor4,fragment_5)},$$slots:{default:!0}})};if_block(node_3,$$render=>{$$props.capabilities.server.resources&&$$render(consequent_1)})}var node_5=sibling(node_3,2);{var consequent_2=$$anchor3=>{Badge($$anchor3,{variant:"outline", +class:"h-5 gap-1 bg-purple-50 px-1.5 text-[10px] dark:bg-purple-950",children:($$anchor4,$$slotProps)=>{var fragment_7=root_7$g(),node_6=first_child(fragment_7);Message_square(node_6,{class:"h-3 w-3 text-purple-600 dark:text-purple-400"}),next$1(),append($$anchor4,fragment_7)},$$slots:{default:!0}})};if_block(node_5,$$render=>{$$props.capabilities.server.prompts&&$$render(consequent_2)})}var node_7=sibling(node_5,2);{var consequent_3=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-\ +1 bg-orange-50 px-1.5 text-[10px] dark:bg-orange-950",children:($$anchor4,$$slotProps)=>{var fragment_9=root_9$c(),node_8=first_child(fragment_9);File_text(node_8,{class:"h-3 w-3 text-orange-600 dark:text-orange-400"}),next$1(),append($$anchor4,fragment_9)},$$slots:{default:!0}})};if_block(node_7,$$render=>{$$props.capabilities.server.logging&&$$render(consequent_3)})}var node_9=sibling(node_7,2);{var consequent_4=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-cyan-50 px-1.5 \ +text-[10px] dark:bg-cyan-950",children:($$anchor4,$$slotProps)=>{var fragment_11=root_11$7(),node_10=first_child(fragment_11);Sparkles(node_10,{class:"h-3 w-3 text-cyan-600 dark:text-cyan-400"}),next$1(),append($$anchor4,fragment_11)},$$slots:{default:!0}})};if_block(node_9,$$render=>{$$props.capabilities.server.completions&&$$render(consequent_4)})}var node_11=sibling(node_9,2);{var consequent_5=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-pink-50 px-1.5 text-[10px] dark:b\ +g-pink-950",children:($$anchor4,$$slotProps)=>{var fragment_13=root_13$5(),node_12=first_child(fragment_13);List_checks(node_12,{class:"h-3 w-3 text-pink-600 dark:text-pink-400"}),next$1(),append($$anchor4,fragment_13)},$$slots:{default:!0}})};if_block(node_11,$$render=>{$$props.capabilities.server.tasks&&$$render(consequent_5)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{$$props.capabilities&&$$render(consequent_6)})}append($$anchor,fragment),pop()}var root_6$j=from_html(' '),root_3$x=from_html(" ",1),root_9$b=from_html('
details
 
'),root_8$h=from_html('
',1),root_7$f=from_html('
'),root_2$E=from_html('
',1);function McpConnectionLogs($$anchor,$$props){push$1($$props,!0);let defaultExpanded=prop($$props,"defaultExpanded",3,!1),isExpanded=user_derived(defaultExpanded);function formatLogDetails(details){if(details==null)return"";try{return JSON.stringify(details,null,2)}catch{return String(details)}}var fragment=comment$2(),node2=first_child(fragment); +{var consequent_3=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Collapsible,($$anchor3,Collapsible_Root)=>{Collapsible_Root($$anchor3,{get class(){return $$props.class},get open(){return get$3(isExpanded)},set open($$value){set$1(isExpanded,$$value)},children:($$anchor4,$$slotProps)=>{var fragment_2=root_2$E(),div=first_child(fragment_2),node_2=child(div);component(node_2,()=>Collapsible_trigger,($$anchor5,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor5, +{class:"flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground",children:($$anchor6,$$slotProps2)=>{var fragment_3=root_3$x(),node_3=first_child(fragment_3);{var consequent=$$anchor7=>{Chevron_down($$anchor7,{class:"h-3.5 w-3.5"})},alternate=$$anchor7=>{Chevron_right($$anchor7,{class:"h-3.5 w-3.5"})};if_block(node_3,$$render=>{get$3(isExpanded)?$$render(consequent):$$render(alternate,-1)})}var span=sibling(node_3,2),text2=child(span);reset(span);var node_4=sibling(span, +2);{var consequent_1=$$anchor7=>{var span_1=root_6$j(),text_1=child(span_1);reset(span_1),template_effect(()=>set_text(text_1,`· Connected in ${$$props.connectionTimeMs??""}ms`)),append($$anchor7,span_1)};if_block(node_4,$$render=>{$$props.connectionTimeMs!==void 0&&$$render(consequent_1)})}template_effect(()=>set_text(text2,`Connection Log (${$$props.logs.length??""})`)),append($$anchor6,fragment_3)},$$slots:{default:!0}})}),reset(div);var node_5=sibling(div,2);component(node_5,()=>Collapsible_content, +($$anchor5,Collapsible_Content)=>{Collapsible_Content($$anchor5,{class:"mt-2",children:($$anchor6,$$slotProps2)=>{var div_1=root_7$f();each(div_1,21,()=>$$props.logs,log=>log.timestamp.getTime()+log.message,($$anchor7,log)=>{const IconComponent=user_derived(()=>getMcpLogLevelIcon(get$3(log).level));var fragment_6=root_8$h(),div_2=first_child(fragment_6),span_2=child(div_2),text_2=child(span_2,!0);reset(span_2);var node_6=sibling(span_2,2);component(node_6,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{ +IconComponent_1($$anchor8,{class:"mt-0.5 h-3 w-3 shrink-0"})});var span_3=sibling(node_6,2),text_3=child(span_3,!0);reset(span_3),reset(div_2);var node_7=sibling(div_2,2);{var consequent_2=$$anchor8=>{var details_1=root_9$b(),pre=sibling(child(details_1),2),text_4=child(pre,!0);reset(pre),reset(details_1),template_effect($0=>set_text(text_4,$0),[()=>formatLogDetails(get$3(log).details)]),append($$anchor8,details_1)};if_block(node_7,$$render=>{get$3(log).details!==void 0&&$$render(consequent_2)})} +template_effect(($0,$1)=>{set_class(div_2,1,$0),set_text(text_2,$1),set_text(text_3,get$3(log).message)},[()=>clsx(["flex items-start gap-1.5",getMcpLogLevelClass(get$3(log).level)]),()=>formatTime(get$3(log).timestamp)]),append($$anchor7,fragment_6)}),reset(div_1),append($$anchor6,div_1)},$$slots:{default:!0}})}),append($$anchor4,fragment_2)},$$slots:{default:!0}})}),append($$anchor2,fragment_1)};if_block(node2,$$render=>{$$props.logs.length>0&&$$render(consequent_3)})}append($$anchor,fragment), +pop()}var root_1$n=from_html('

'),root_3$w=from_html('(Run
llama-server
with
--webui-mcp-proxy
flag)
'),root_2$D=from_html(''),root$M=from_html('
');function McpServerForm($$anchor,$$props){push$1($$props,!0);let useProxy=prop($$props,"useProxy",3,!1),urlError=prop($$props,"urlError",3,null),id2=prop($$props,"id",3,"server"),isWebSocket=user_derived(()=>$$props.url.toLowerCase().startsWith(UrlProtocol.WEBSOCKET)||$$props.url.toLowerCase().startsWith(UrlProtocol.WEBSOCKET_SECURE)),headerPairs=user_derived(()=>parseHeadersToArray($$props.headers));function updateHeaderPairs(newPairs){ +set$1(headerPairs,newPairs),$$props.onHeadersChange(serializeHeaders(newPairs))}var div=root$M(),div_1=child(div),label=child(div_1),node2=sibling(label,2);{let $0=user_derived(()=>urlError()?"border-destructive":"");Input(node2,{get id(){return`server-url-${id2()??""}`},type:"url",get placeholder(){return MCP_SERVER_URL_PLACEHOLDER},get value(){return $$props.url},oninput:e=>$$props.onUrlChange(e.currentTarget.value),get class(){return get$3($0)}})}var node_1=sibling(node2,2);{var consequent=$$anchor2=>{ +var p2=root_1$n(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,urlError())),append($$anchor2,p2)};if_block(node_1,$$render=>{urlError()&&$$render(consequent)})}var node_2=sibling(node_1,2);{var consequent_2=$$anchor2=>{var label_1=root_2$D(),node_3=child(label_1);{let $0=user_derived(()=>!mcpStore.isProxyAvailable);Switch(node_3,{class:"mt-1",get id(){return`use-proxy-${id2()??""}`},get checked(){return useProxy()},get disabled(){return get$3($0)},onCheckedChange:checked=>$$props. +onUseProxyChange?.(checked)})}var span=sibling(node_3,2),node_4=sibling(child(span),4);{var consequent_1=$$anchor3=>{var span_1=root_3$w();append($$anchor3,span_1)};if_block(node_4,$$render=>{mcpStore.isProxyAvailable||$$render(consequent_1)})}reset(span),reset(label_1),template_effect(()=>set_class(label_1,1,clsx(["mt-3 flex items-start gap-2",mcpStore.isProxyAvailable&&"cursor-pointer",!mcpStore.isProxyAvailable&&"opacity-80"]))),append($$anchor2,label_1)};if_block(node_2,$$render=>{!get$3(isWebSocket)&& +$$props.onUseProxyChange&&$$render(consequent_2)})}reset(div_1);var node_5=sibling(div_1,2);KeyValuePairs(node_5,{class:"mt-2",get pairs(){return get$3(headerPairs)},onPairsChange:updateHeaderPairs,keyPlaceholder:"Header name",valuePlaceholder:"Value",addButtonLabel:"Add",emptyMessage:"No custom headers configured.",sectionLabel:"Custom Headers",sectionLabelOptional:!0}),reset(div),template_effect(()=>set_attribute(label,"for",`server-url-${id2()??""}`)),append($$anchor,div),pop()}var root$L=from_html( +"
");function Skeleton($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var div=root$L();attribute_effect(div,$0=>({"data-slot":"skeleton",class:$0,...restProps}),[()=>cn$1("animate-pulse rounded-md bg-accent",$$props.class)]),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root_4$m=from_html('

'),root_5$g=from_html( +'

'),root_6$i=from_html('
',1),root_7$e=from_html(" ",1),root_12$6=from_html('
'),root_3$v=from_html(' \ +
',1),root$K=from_html(" ",1);function McpServerCard($$anchor,$$props){push$1($$props,!0);let healthState=user_derived(()=>mcpStore.getHealthCheckState($$props.server.id)),displayName=user_derived(()=>mcpStore.getServerLabel($$props.server)),faviconUrl=user_derived(()=>mcpStore.getServerFavicon($$props.server.id)),isIdle=user_derived(()=>get$3(healthState).status===HealthCheckStatus.IDLE),isHealthChecking=user_derived( +()=>get$3(healthState).status===HealthCheckStatus.CONNECTING),isConnected=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS),isError=user_derived(()=>get$3(healthState).status===HealthCheckStatus.ERROR),showSkeleton=user_derived(()=>get$3(isIdle)||get$3(isHealthChecking)),errorMessage=user_derived(()=>get$3(healthState).status===HealthCheckStatus.ERROR?get$3(healthState).message:void 0),tools=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS?get$3(healthState). +tools:[]),connectionLogs=user_derived(()=>get$3(healthState).status===HealthCheckStatus.CONNECTING||get$3(healthState).status===HealthCheckStatus.SUCCESS||get$3(healthState).status===HealthCheckStatus.ERROR?get$3(healthState).logs:[]),successState=user_derived(()=>get$3(healthState).status===HealthCheckStatus.SUCCESS?get$3(healthState):null),serverInfo=user_derived(()=>get$3(successState)?.serverInfo),capabilities=user_derived(()=>get$3(successState)?.capabilities),transportType=user_derived(()=>get$3( +successState)?.transportType),protocolVersion=user_derived(()=>get$3(successState)?.protocolVersion),connectionTimeMs=user_derived(()=>get$3(successState)?.connectionTimeMs),instructions=user_derived(()=>get$3(successState)?.instructions),isEditing2=user_derived(()=>!$$props.server.url.trim()),showDeleteDialog=state$1(!1),editFormRef=state$1(null);function handleHealthCheck(){mcpStore.runHealthCheck($$props.server)}async function startEditing(){set$1(isEditing2,!0),await tick(),get$3(editFormRef)?. +setInitialValues($$props.server.url,$$props.server.headers||"",$$props.server.useProxy||!1)}function cancelEditing(){$$props.server.url.trim()?set$1(isEditing2,!1):$$props.onDelete()}function saveEditing(url2,headers,useProxy){$$props.onUpdate({url:url2,headers:headers||void 0,useProxy}),set$1(isEditing2,!1),$$props.server.enabled&&url2&&setTimeout(()=>mcpStore.runHealthCheck({...$$props.server,url:url2,useProxy}),100)}function handleDeleteClick(){set$1(showDeleteDialog,!0)}var fragment=root$K(), +node2=first_child(fragment);component(node2,()=>Card,($$anchor2,Card_Root)=>{Card_Root($$anchor2,{class:"!gap-3 bg-muted/30 p-4",children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{var consequent=$$anchor4=>{bind_this(McpServerCardEditForm($$anchor4,{get serverId(){return $$props.server.id},get serverUrl(){return $$props.server.url},get serverUseProxy(){return $$props.server.useProxy},onSave:saveEditing,onCancel:cancelEditing}),$$value=>set$1(editFormRef, +$$value,!0),()=>get$3(editFormRef))},alternate_1=$$anchor4=>{var fragment_3=root_3$v(),node_2=first_child(fragment_3);{let $0=user_derived(()=>$$props.enabled??$$props.server.enabled);McpServerCardHeader(node_2,{get displayName(){return get$3(displayName)},get faviconUrl(){return get$3(faviconUrl)},get enabled(){return get$3($0)},get disabled(){return get$3(isError)},get onToggle(){return $$props.onToggle},get serverInfo(){return get$3(serverInfo)},get capabilities(){return get$3(capabilities)}, +get transportType(){return get$3(transportType)}})}var node_3=sibling(node_2,2);{var consequent_1=$$anchor5=>{var p2=root_4$m(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,get$3(errorMessage))),append($$anchor5,p2)};if_block(node_3,$$render=>{get$3(isError)&&get$3(errorMessage)&&$$render(consequent_1)})}var node_4=sibling(node_3,2);{var consequent_2=$$anchor5=>{var p_1=root_5$g(),text_1=child(p_1,!0);reset(p_1),template_effect(()=>set_text(text_1,get$3(serverInfo).description)), +append($$anchor5,p_1)};if_block(node_4,$$render=>{get$3(isConnected)&&get$3(serverInfo)?.description&&$$render(consequent_2)})}var div=sibling(node_4,2),node_5=child(div);{var consequent_3=$$anchor5=>{var fragment_4=root_6$i(),div_1=first_child(fragment_4),div_2=child(div_1),node_6=child(div_2);Skeleton(node_6,{class:"h-4 w-4 rounded"});var node_7=sibling(node_6,2);Skeleton(node_7,{class:"h-3 w-24"}),reset(div_2);var div_3=sibling(div_2,2),node_8=child(div_3);Skeleton(node_8,{class:"h-5 w-16 rou\ +nded-full"});var node_9=sibling(node_8,2);Skeleton(node_9,{class:"h-5 w-20 rounded-full"});var node_10=sibling(node_9,2);Skeleton(node_10,{class:"h-5 w-14 rounded-full"}),reset(div_3),reset(div_1);var div_4=sibling(div_1,2),div_5=child(div_4),node_11=child(div_5);Skeleton(node_11,{class:"h-4 w-4 rounded"});var node_12=sibling(node_11,2);Skeleton(node_12,{class:"h-3 w-32"}),reset(div_5),reset(div_4),append($$anchor5,fragment_4)},alternate=$$anchor5=>{var fragment_5=root_7$e(),node_13=first_child( +fragment_5);{var consequent_4=$$anchor6=>{McpServerInfo($$anchor6,{get instructions(){return get$3(instructions)}})};if_block(node_13,$$render=>{get$3(isConnected)&&get$3(instructions)&&$$render(consequent_4)})}var node_14=sibling(node_13,2);{var consequent_5=$$anchor6=>{McpServerCardToolsList($$anchor6,{get tools(){return get$3(tools)}})};if_block(node_14,$$render=>{get$3(tools).length>0&&$$render(consequent_5)})}var node_15=sibling(node_14,2);{var consequent_6=$$anchor6=>{McpConnectionLogs($$anchor6, +{get logs(){return get$3(connectionLogs)},get connectionTimeMs(){return get$3(connectionTimeMs)}})};if_block(node_15,$$render=>{get$3(connectionLogs).length>0&&$$render(consequent_6)})}append($$anchor5,fragment_5)};if_block(node_5,$$render=>{get$3(showSkeleton)?$$render(consequent_3):$$render(alternate,-1)})}reset(div);var div_6=sibling(div,2),node_16=child(div_6);{var consequent_7=$$anchor5=>{Skeleton($$anchor5,{class:"h-3 w-28"})},consequent_8=$$anchor5=>{var div_7=root_12$6(),span=child(div_7), +text_2=child(span);reset(span),reset(div_7),template_effect(()=>set_text(text_2,`Protocol version: ${get$3(protocolVersion)??""}`)),append($$anchor5,div_7)};if_block(node_16,$$render=>{get$3(showSkeleton)?$$render(consequent_7):get$3(protocolVersion)&&$$render(consequent_8,1)})}var node_17=sibling(node_16,2);McpServerCardActions(node_17,{get isHealthChecking(){return get$3(isHealthChecking)},onEdit:startEditing,onRefresh:handleHealthCheck,onDelete:handleDeleteClick}),reset(div_6),append($$anchor4, +fragment_3)};if_block(node_1,$$render=>{get$3(isEditing2)?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor3,fragment_1)},$$slots:{default:!0}})});var node_18=sibling(node2,2);McpServerCardDeleteDialog(node_18,{get displayName(){return get$3(displayName)},onOpenChange:open2=>set$1(showDeleteDialog,open2,!0),get onConfirm(){return $$props.onDelete},get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)}}),append($$anchor,fragment),pop()}var root_3$u=from_html( +" ",1),root_1$m=from_html('
'),root$J=from_html('
');function McpServerCardHeader($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1);var div=root$J(),div_1=child( +div),div_2=child(div_1),div_3=child(div_2),node2=child(div_3);McpServerIdentity(node2,{get displayName(){return $$props.displayName},get faviconUrl(){return $$props.faviconUrl},get serverInfo(){return $$props.serverInfo},iconClass:"h-5 w-5",iconRounded:"rounded",nameClass:"leading-6 font-medium"}),reset(div_3);var node_1=sibling(div_3,2);{var consequent_3=$$anchor2=>{var div_4=root_1$m(),node_2=child(div_4);{var consequent_1=$$anchor3=>{const TransportIcon=user_derived(()=>MCP_TRANSPORT_ICONS[$$props. +transportType]);Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 px-1.5 text-[10px]",children:($$anchor4,$$slotProps)=>{var fragment_1=root_3$u(),node_3=first_child(fragment_1);{var consequent=$$anchor5=>{var fragment_2=comment$2(),node_4=first_child(fragment_2);component(node_4,()=>get$3(TransportIcon),($$anchor6,TransportIcon_1)=>{TransportIcon_1($$anchor6,{class:"h-3 w-3"})}),append($$anchor5,fragment_2)};if_block(node_3,$$render=>{get$3(TransportIcon)&&$$render(consequent)})}var text2=sibling( +node_3);template_effect(()=>set_text(text2,` ${(MCP_TRANSPORT_LABELS[$$props.transportType]||$$props.transportType)??""}`)),append($$anchor4,fragment_1)},$$slots:{default:!0}})};if_block(node_2,$$render=>{$$props.transportType&&$$render(consequent_1)})}var node_5=sibling(node_2,2);{var consequent_2=$$anchor3=>{McpCapabilitiesBadges($$anchor3,{get capabilities(){return $$props.capabilities}})};if_block(node_5,$$render=>{$$props.capabilities&&$$render(consequent_2)})}reset(div_4),append($$anchor2, +div_4)};if_block(node_1,$$render=>{($$props.capabilities||$$props.transportType)&&$$render(consequent_3)})}reset(div_2);var div_5=sibling(div_2,2),node_6=child(div_5);Switch(node_6,{get checked(){return $$props.enabled},get disabled(){return disabled()},get onCheckedChange(){return $$props.onToggle}}),reset(div_5),reset(div_1),reset(div),append($$anchor,div),pop()}var root$I=from_html('
');function McpServerCardActions($$anchor,$$props){ +var div=root$I(),node2=child(div);Button(node2,{variant:"ghost",size:"icon",class:"h-7 w-7",get onclick(){return $$props.onEdit},"aria-label":"Edit",children:($$anchor2,$$slotProps)=>{Pencil($$anchor2,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}});var node_1=sibling(node2,2);Button(node_1,{variant:"ghost",size:"icon",class:"h-7 w-7",get onclick(){return $$props.onRefresh},get disabled(){return $$props.isHealthChecking},"aria-label":"Refresh",children:($$anchor2,$$slotProps)=>{Refresh_cw($$anchor2, +{class:"h-3.5 w-3.5"})},$$slots:{default:!0}});var node_2=sibling(node_1,2);Button(node_2,{variant:"ghost",size:"icon",class:"hover:text-destructive-foreground h-7 w-7 text-destructive hover:bg-destructive/10",get onclick(){return $$props.onDelete},"aria-label":"Delete",children:($$anchor2,$$slotProps)=>{Trash_2($$anchor2,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}}),reset(div),append($$anchor,div)}var root_2$C=from_html(" ",1),root_8$g=from_html('

'),root_6$h=from_html("
"),root_5$f=from_html('
'),root_1$l=from_html(" ",1);function McpServerCardToolsList($$anchor,$$props){push$1($$props,!0);let isExpanded=state$1(!1),toolsCount=user_derived(()=>$$props.tools.length);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Collapsible,($$anchor2,Collapsible_Root)=>{Collapsible_Root($$anchor2,{get open(){return get$3(isExpanded)}, +set open($$value){set$1(isExpanded,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$l(),node_1=first_child(fragment_1);component(node_1,()=>Collapsible_trigger,($$anchor4,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor4,{class:"flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$C(),node_2=first_child(fragment_2);{var consequent=$$anchor6=>{Chevron_down($$anchor6,{class:"h-3.5 w-3.5"})}, +alternate=$$anchor6=>{Chevron_right($$anchor6,{class:"h-3.5 w-3.5"})};if_block(node_2,$$render=>{get$3(isExpanded)?$$render(consequent):$$render(alternate,-1)})}var span=sibling(node_2,2),text2=child(span);reset(span),template_effect(()=>set_text(text2,`${get$3(toolsCount)??""} tools available · Show details`)),append($$anchor5,fragment_2)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Collapsible_content,($$anchor4,Collapsible_Content)=>{Collapsible_Content($$anchor4, +{class:"mt-2",children:($$anchor5,$$slotProps2)=>{var div=root_5$f();each(div,21,()=>$$props.tools,tool=>tool.name,($$anchor6,tool)=>{var div_1=root_6$h(),node_4=child(div_1);Badge(node_4,{variant:"secondary",children:($$anchor7,$$slotProps3)=>{next$1();var text_1=text$8();template_effect(()=>set_text(text_1,get$3(tool).name)),append($$anchor7,text_1)},$$slots:{default:!0}});var node_5=sibling(node_4,2);{var consequent_1=$$anchor7=>{var p2=root_8$g(),text_2=child(p2,!0);reset(p2),template_effect( +()=>set_text(text_2,get$3(tool).description)),append($$anchor7,p2)};if_block(node_5,$$render=>{get$3(tool).description&&$$render(consequent_1)})}reset(div_1),append($$anchor6,div_1)}),reset(div),append($$anchor5,div)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root$H=from_html('

Configure Server

');function McpServerCardEditForm($$anchor,$$props){ +push$1($$props,!0);let serverUseProxy=prop($$props,"serverUseProxy",3,!1),editUrl=user_derived(()=>$$props.serverUrl),editHeaders=state$1(""),editUseProxy=user_derived(serverUseProxy),urlError=user_derived(()=>{if(!get$3(editUrl).trim())return"URL is required";try{return new URL(get$3(editUrl)),null}catch{return"Invalid URL format"}}),canSave=user_derived(()=>!get$3(urlError));function handleSave(){get$3(canSave)&&$$props.onSave(get$3(editUrl).trim(),get$3(editHeaders).trim(),get$3(editUseProxy))} +function setInitialValues(url2,headers,useProxy){set$1(editUrl,url2),set$1(editHeaders,headers,!0),set$1(editUseProxy,useProxy)}var $$exports={setInitialValues},div=root$H(),node2=sibling(child(div),2);{let $0=user_derived(()=>get$3(editUrl)?get$3(urlError):null);McpServerForm(node2,{get url(){return get$3(editUrl)},get headers(){return get$3(editHeaders)},get useProxy(){return get$3(editUseProxy)},onUrlChange:v=>set$1(editUrl,v),onHeadersChange:v=>set$1(editHeaders,v,!0),onUseProxyChange:v=>set$1( +editUseProxy,v),get urlError(){return get$3($0)},get id(){return $$props.serverId}})}var div_1=sibling(node2,2),node_1=child(div_1);Button(node_1,{variant:"secondary",size:"sm",get onclick(){return $$props.onCancel},children:($$anchor2,$$slotProps)=>{next$1();var text2=text$8("Cancel");append($$anchor2,text2)},$$slots:{default:!0}});var node_2=sibling(node_1,2);{let $0=user_derived(()=>!get$3(canSave));Button(node_2,{size:"sm",onclick:handleSave,get disabled(){return get$3($0)},children:($$anchor2,$$slotProps)=>{ +next$1();var text_1=text$8();template_effect($02=>set_text(text_1,$02),[()=>$$props.serverUrl.trim()?"Update":"Add"]),append($$anchor2,text_1)},$$slots:{default:!0}})}return reset(div_1),reset(div),append($$anchor,div),pop($$exports)}function Alert_dialog_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("te\ +xt-lg font-semibold",$$props.class));component(node2,()=>Dialog_title$1,($$anchor2,AlertDialogPrimitive_Title)=>{AlertDialogPrimitive_Title($$anchor2,spread_props({"data-slot":"alert-dialog-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Alert_dialog_action($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy", +"ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(buttonVariants(),$$props.class));component(node2,()=>Alert_dialog_action$1,($$anchor2,AlertDialogPrimitive_Action)=>{AlertDialogPrimitive_Action($$anchor2,spread_props({"data-slot":"alert-dialog-action",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Alert_dialog_cancel($$anchor,$$props){push$1($$props, +!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(buttonVariants({variant:"outline"}),$$props.class));component(node2,()=>Alert_dialog_cancel$1,($$anchor2,AlertDialogPrimitive_Cancel)=>{AlertDialogPrimitive_Cancel($$anchor2,spread_props({"data-slot":"alert-dialog-cancel",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ +ref2($$value)}}))})}append($$anchor,fragment),pop()}var root$G=from_html("
");function Alert_dialog_footer($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$G();attribute_effect(div,$0=>({"data-slot":"alert-dialog-footer",class:$0,...restProps}),[()=>cn$1("mt-6 flex flex-row gap-2 sm:mt-0 sm:justify-end [&>*]:flex-1 sm:[&>*]:flex-none",$$props.class)]);var node2=child( +div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root$F=from_html("
");function Alert_dialog_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$F();attribute_effect(div,$0=>({"data-slot":"alert-dialog-header",class:$0,...restProps}),[()=>cn$1("flex flex-col gap-2 \ +text-center sm:text-left",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Alert_dialog_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[st\ +ate=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,AlertDialogPrimitive_Overlay)=>{AlertDialogPrimitive_Overlay($$anchor2,spread_props({"data-slot":"alert-dialog-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$k=from_html("\ + ",1);function Alert_dialog_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","portalProps"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,AlertDialogPrimitive_Portal)=>{AlertDialogPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$k(),node_1=first_child(fragment_1); +Alert_dialog_overlay(node_1,{});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1("fixed z-[999999] grid w-full gap-4 border bg-background p-6 shadow-lg duration-200","right-0 bottom-0 left-0 max-h-[100dvh] translate-x-0 translate-y-0 overflow-y-auto rounded-t-lg","data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:slide-out-to-bottom-full","data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:s\ +lide-in-from-bottom-full","sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-h-[100vh] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-lg","sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95","sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95",$$props.class));component(node_2,()=>Alert_dialog_content$1,($$anchor4,AlertDialogPrimitive_Content)=>{AlertDialogPrimitive_Content($$anchor4,spread_props({"data-slot":"\ +alert-dialog-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Alert_dialog_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text\ +-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,AlertDialogPrimitive_Description)=>{AlertDialogPrimitive_Description($$anchor2,spread_props({"data-slot":"alert-dialog-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$1=Alert_dialog,Portal=Portal$2;var root_5$e=from_html(`Are you sure you want to delete ? This action cannot be undone.`,1),root_3$t=from_html(" ",1),root_6$g=from_html(" ",1),root_2$B=from_html(" ",1);function McpServerCardDeleteDialog($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$1,($$anchor2,AlertDialog_Root)=>{AlertDialog_Root($$anchor2,{get onOpenChange(){return $$props.onOpenChange},get open(){return open2()},set open($$value){open2($$value)},children:($$anchor3,$$slotProps)=>{ var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Alert_dialog_content,($$anchor4,AlertDialog_Content)=>{AlertDialog_Content($$anchor4,{children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$B(),node_2=first_child(fragment_2);component(node_2,()=>Alert_dialog_header,($$anchor6,AlertDialog_Header)=>{AlertDialog_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$t(),node_3=first_child(fragment_3);component(node_3,()=>Alert_dialog_title,($$anchor8,AlertDialog_Title)=>{ AlertDialog_Title($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text2=text$8("Delete Server");append($$anchor9,text2)},$$slots:{default:!0}})});var node_4=sibling(node_3,2);component(node_4,()=>Alert_dialog_description,($$anchor8,AlertDialog_Description)=>{AlertDialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var fragment_4=root_5$e(),strong2=sibling(first_child(fragment_4)),text_1=child(strong2,!0);reset(strong2),next$1(),template_effect(()=>set_text(text_1, @@ -10843,53 +10844,54 @@ $$slots:{default:!0}}))})}append($$anchor,fragment),pop()}var root_2$i=from_html "touchmove",blockOutsideTouchMove,listenerOptions)}}),user_effect(()=>{const element2=ref2();if(cleanupInternalListeners?.(),!element2)return;const stopWheelPropagation=event2=>{event2.stopPropagation()},stopTouchPropagation=event2=>{event2.stopPropagation()};element2.addEventListener("wheel",stopWheelPropagation),element2.addEventListener("touchmove",stopTouchPropagation),cleanupInternalListeners=()=>{element2.removeEventListener("wheel",stopWheelPropagation),element2.removeEventListener("touch\ move",stopTouchPropagation)}}),onDestroy(()=>{cleanupInternalListeners?.()});var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SelectPrimitive_Portal)=>{SelectPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>cn$1("relative z-[var(--layer-popover,1000000)] max-h-(--bits-select-content-available-height) min-w-[8rem] origin-\ (--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:-translate-x-1 data-[side=left]:slide-in-from-right-2 data-[side=right]:translate-x-1 data-[side=right]:slide-in-from-left-2 data-[side=top]:-translate-y-1 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=clo\ -sed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",$$props.class));component(node_1,()=>Select_content$1,($$anchor4,SelectPrimitive_Content)=>{SelectPrimitive_Content($$anchor4,spread_props({get sideOffset(){return sideOffset()},"data-slot":"select-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$i(),node_2=first_child(fragment_2); -Select_scroll_up_button(node_2,{});var node_3=sibling(node_2,2);{let $02=user_derived(()=>cn$1("h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1"));component(node_3,()=>Select_viewport,($$anchor6,SelectPrimitive_Viewport)=>{SelectPrimitive_Viewport($$anchor6,{get class(){return get$3($02)},children:($$anchor7,$$slotProps3)=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);snippet(node_4,()=>$$props.children??noop$3),append($$anchor7,fragment_3)}, -$$slots:{default:!0}})})}var node_5=sibling(node_3,2);Select_scroll_down_button(node_5,{}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root_1$c=from_html(" ",1);function Select_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),size2=prop($$props,"size",3,"default"),variant=prop($$props,"variant",3,"default"),restProps=rest_props($$props,["$$slots","$$event\ -s","$$legacy","ref","class","children","size","variant"]);const baseClasses=user_derived(()=>variant()==="plain"?"group inline-flex w-full items-center justify-end gap-2 whitespace-nowrap px-0 py-0 text-sm font-medium text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]:pointer-events-none [&_svg\ -]:shrink-0 [&_svg:not([class*='size-'])]:size-3 [&_svg:not([class*='text-'])]:text-muted-foreground":"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placehol\ -der]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground"),chevronClasses=user_derived(()=>variant()==="plain"?"size-3 opacity-60 \ -transition-transform group-data-[state=open]:-rotate-180":"size-4 opacity-50");var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(get$3(baseClasses),$$props.class));component(node2,()=>Select_trigger$1,($$anchor2,SelectPrimitive_Trigger)=>{SelectPrimitive_Trigger($$anchor2,spread_props({"data-slot":"select-trigger",get"data-size"(){return size2()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{ -var fragment_1=root_1$c(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_down(node_2,{get class(){return get$3(chevronClasses)}}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}const Root=Select;var root$p=from_html("");function Textarea($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),value=prop($$props,"value",15),restProps=rest_props($$props, -["$$slots","$$events","$$legacy","ref","value","class"]);var textarea=root$p();remove_textarea_child(textarea),attribute_effect(textarea,$0=>({"data-slot":"textarea",class:$0,...restProps}),[()=>cn$1("flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowe\ -d disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",$$props.class)]),bind_this(textarea,$$value=>ref2($$value),()=>ref2()),bind_value(textarea,value),append($$anchor,textarea),pop()}var root_1$b=from_html(" Custom",1);function SettingsChatParameterSourceIndicator($$anchor,$$props){let className=prop($$props,"class",3,"");Badge($$anchor,{variant:"secondary",get class(){return`h-5 bg-orange-100\ - px-1.5 py-0.5 text-xs text-orange-800 dark:bg-orange-900 dark:text-orange-200 ${className()??""}`},children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$b(),node2=first_child(fragment_1);Wrench(node2,{class:"mr-1 h-3 w-3"}),next$1(),append($$anchor2,fragment_1)},$$slots:{default:!0}})}var root_3$b=from_html(" ",1),root_6$8=from_html(''),root_7$7=from_html('

'),root_2$h=from_html('
',1),root_9$5=from_html(" ",1),root_11$3=from_html('

'),root_12$2=from_html('
'),root_8$8=from_html(" ",1),root_15$2=from_html( -" ",1),root_19$2=from_html('
'),root_21$1=from_html(''),root_25$1=from_html('
'),root_18$1=from_html('
',1),root_27=from_html( -'

'),root_14$1=from_html('
',1),root_30=from_html('

'),root_28=from_html('
'),root_1$a=from_html('
');function SettingsChatFields($$anchor,$$props){ -push$1($$props,!0);let sp=user_derived(()=>{if(serverStore.isRouterMode){const m=selectedModelName();if(m)return modelsStore.getModelProps(m)?.default_generation_settings?.params??{}}return serverStore.defaultParams??{}});var fragment=comment$2(),node2=first_child(fragment);each(node2,17,()=>$$props.fields,field=>field.key,($$anchor2,field)=>{var div=root_1$a(),node_1=child(div);{var consequent_4=$$anchor3=>{const currentValue=user_derived(()=>String($$props.localConfig[get$3(field).key]??"")),serverDefault=user_derived( -()=>get$3(sp)[get$3(field).key]),isCustomRealTime=user_derived(()=>(()=>{if(get$3(serverDefault)==null||get$3(currentValue)==="")return!1;const numericInput=parseFloat(get$3(currentValue)),normalizedInput=isNaN(numericInput)?get$3(currentValue):Math.round(numericInput*1e6)/1e6,normalizedDefault=typeof get$3(serverDefault)=="number"?Math.round(get$3(serverDefault)*1e6)/1e6:get$3(serverDefault);return normalizedInput!==normalizedDefault})());var fragment_1=root_2$h(),div_1=first_child(fragment_1), -node_2=child(div_1);Label(node_2,{get for(){return get$3(field).key},class:"flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_2=root_3$b(),text2=first_child(fragment_2),node_3=sibling(text2);{var consequent=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_3,$$render=>{get$3(field).isExperimental&&$$render(consequent)})}template_effect(()=>set_text(text2,`${get$3(field).label??""} `)),append($$anchor4, -fragment_2)},$$slots:{default:!0}});var node_4=sibling(node_2,2);{var consequent_1=$$anchor4=>{SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_4,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_1)})}reset(div_1);var div_2=sibling(div_1,2),node_5=child(div_2);{let $0=user_derived(()=>get$3(sp)[get$3(field).key]!=null?`Default: ${normalizeFloatingPoint(get$3(sp)[get$3(field).key])}`:""),$1=user_derived(()=>get$3(isCustomRealTime)?"pr-8":"");Input(node_5,{get id(){return get$3( -field).key},get value(){return get$3(currentValue)},oninput:e=>{$$props.onConfigChange(get$3(field).key,e.currentTarget.value)},get placeholder(){return get$3($0)},get class(){return`w-full ${get$3($1)??""}`}})}var node_6=sibling(node_5,2);{var consequent_2=$$anchor4=>{var button=root_6$8(),node_7=child(button);Rotate_ccw(node_7,{class:"h-3 w-3"}),reset(button),delegated("click",button,()=>{settingsStore.resetParameterToServerDefault(get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}), -append($$anchor4,button)};if_block(node_6,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_2)})}reset(div_2);var node_8=sibling(div_2,2);{var consequent_3=$$anchor4=>{var p_1=root_7$7();html$6(p_1,()=>get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key],!0),reset(p_1),append($$anchor4,p_1)};if_block(node_8,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_3)})}append($$anchor3,fragment_1)},consequent_8=$$anchor3=>{var fragment_5=root_8$8(),node_9=first_child( -fragment_5);Label(node_9,{get for(){return get$3(field).key},class:"block flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_6=root_9$5(),text_1=first_child(fragment_6),node_10=sibling(text_1);{var consequent_5=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_10,$$render=>{get$3(field).isExperimental&&$$render(consequent_5)})}template_effect(()=>set_text(text_1,`${get$3(field).label??""} `)),append( -$$anchor4,fragment_6)},$$slots:{default:!0}});var node_11=sibling(node_9,2);{let $0=user_derived(()=>String($$props.localConfig[get$3(field).key]??""));Textarea(node_11,{get id(){return get$3(field).key},get value(){return get$3($0)},onchange:e=>$$props.onConfigChange(get$3(field).key,e.currentTarget.value),placeholder:"",class:"min-h-[10rem] w-full md:max-w-3xl"})}var node_12=sibling(node_11,2);{var consequent_6=$$anchor4=>{var p_2=root_11$3(),text_2=child(p_2,!0);reset(p_2),template_effect(()=>set_text( -text_2,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_2)};if_block(node_12,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_6)})}var node_13=sibling(node_12,2);{var consequent_7=$$anchor4=>{var div_3=root_12$2(),node_14=child(div_3);{let $0=user_derived(()=>!!($$props.localConfig.showSystemMessage??!0));Checkbox(node_14,{id:"showSystemMessage",get checked(){return get$3($0)},onCheckedChange:checked=>$$props.onConfigChange("\ -showSystemMessage",!!checked)})}var node_15=sibling(node_14,2);Label(node_15,{for:"showSystemMessage",class:"cursor-pointer text-sm font-normal",children:($$anchor5,$$slotProps)=>{next$1();var text_3=text$8("Show system message in conversations");append($$anchor5,text_3)},$$slots:{default:!0}}),reset(div_3),append($$anchor4,div_3)};if_block(node_13,$$render=>{get$3(field).key===SETTINGS_KEYS.SYSTEM_MESSAGE&&$$render(consequent_7)})}append($$anchor3,fragment_5)},consequent_16=$$anchor3=>{const selectedOption=user_derived( -()=>get$3(field).options?.find(opt=>opt.value===$$props.localConfig[get$3(field).key])),currentValue=user_derived(()=>$$props.localConfig[get$3(field).key]),serverDefault=user_derived(()=>get$3(sp)[get$3(field).key]),isCustomRealTime=user_derived(()=>get$3(serverDefault)==null||get$3(currentValue)===""||get$3(currentValue)===void 0?!1:get$3(currentValue)!==get$3(serverDefault));var fragment_8=root_14$1(),div_4=first_child(fragment_8),node_16=child(div_4);Label(node_16,{get for(){return get$3(field). -key},class:"flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_9=root_15$2(),text_4=first_child(fragment_9),node_17=sibling(text_4);{var consequent_9=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_17,$$render=>{get$3(field).isExperimental&&$$render(consequent_9)})}template_effect(()=>set_text(text_4,`${get$3(field).label??""} `)),append($$anchor4,fragment_9)},$$slots:{default:!0}});var node_18=sibling( -node_16,2);{var consequent_10=$$anchor4=>{SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_18,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_10)})}reset(div_4);var node_19=sibling(div_4,2);component(node_19,()=>Root,($$anchor4,Select_Root)=>{Select_Root($$anchor4,{type:"single",get value(){return get$3(currentValue)},onValueChange:value=>{get$3(field).key===SETTINGS_KEYS.THEME&&value&&$$props.onThemeChange?$$props.onThemeChange(value):$$props.onConfigChange(get$3(field). -key,value)},children:($$anchor5,$$slotProps)=>{var fragment_12=root_18$1(),div_5=first_child(fragment_12),node_20=child(div_5);component(node_20,()=>Select_trigger,($$anchor6,Select_Trigger)=>{Select_Trigger($$anchor6,{class:"w-full",children:($$anchor7,$$slotProps2)=>{var div_6=root_19$2(),node_21=child(div_6);{var consequent_11=$$anchor8=>{const IconComponent=user_derived(()=>get$3(selectedOption).icon);var fragment_13=comment$2(),node_22=first_child(fragment_13);component(node_22,()=>get$3(IconComponent), -($$anchor9,IconComponent_1)=>{IconComponent_1($$anchor9,{class:"h-4 w-4"})}),append($$anchor8,fragment_13)};if_block(node_21,$$render=>{get$3(selectedOption)?.icon&&$$render(consequent_11)})}var text_5=sibling(node_21);reset(div_6),template_effect($0=>set_text(text_5,` ${$0??""}`),[()=>get$3(selectedOption)?.label||`Select ${get$3(field).label.toLowerCase()}`]),append($$anchor7,div_6)},$$slots:{default:!0}})});var node_23=sibling(node_20,2);{var consequent_12=$$anchor6=>{var button_1=root_21$1(), -node_24=child(button_1);Rotate_ccw(node_24,{class:"h-3 w-3"}),reset(button_1),delegated("click",button_1,()=>{settingsStore.resetParameterToServerDefault(get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}),append($$anchor6,button_1)};if_block(node_23,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_12)})}reset(div_5);var node_25=sibling(div_5,2);component(node_25,()=>Select_content,($$anchor6,Select_Content)=>{Select_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{var fragment_14=comment$2(), -node_26=first_child(fragment_14);{var consequent_14=$$anchor8=>{var fragment_15=comment$2(),node_27=first_child(fragment_15);each(node_27,17,()=>get$3(field).options,option2=>option2.value,($$anchor9,option2)=>{var fragment_16=comment$2(),node_28=first_child(fragment_16);component(node_28,()=>Select_item,($$anchor10,Select_Item)=>{Select_Item($$anchor10,{get value(){return get$3(option2).value},get label(){return get$3(option2).label},children:($$anchor11,$$slotProps3)=>{var div_7=root_25$1(),node_29=child( -div_7);{var consequent_13=$$anchor12=>{const IconComponent=user_derived(()=>get$3(option2).icon);var fragment_17=comment$2(),node_30=first_child(fragment_17);component(node_30,()=>get$3(IconComponent),($$anchor13,IconComponent_2)=>{IconComponent_2($$anchor13,{class:"h-4 w-4"})}),append($$anchor12,fragment_17)};if_block(node_29,$$render=>{get$3(option2).icon&&$$render(consequent_13)})}var text_6=sibling(node_29);reset(div_7),template_effect(()=>set_text(text_6,` ${get$3(option2).label??""}`)),append( -$$anchor11,div_7)},$$slots:{default:!0}})}),append($$anchor9,fragment_16)}),append($$anchor8,fragment_15)};if_block(node_26,$$render=>{get$3(field).options&&$$render(consequent_14)})}append($$anchor7,fragment_14)},$$slots:{default:!0}})}),append($$anchor5,fragment_12)},$$slots:{default:!0}})});var node_31=sibling(node_19,2);{var consequent_15=$$anchor4=>{var p_3=root_27(),text_7=child(p_3,!0);reset(p_3),template_effect(()=>set_text(text_7,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])), -append($$anchor4,p_3)};if_block(node_31,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_15)})}append($$anchor3,fragment_8)},consequent_19=$$anchor3=>{var div_8=root_28(),node_32=child(div_8);{let $0=user_derived(()=>!!$$props.localConfig[get$3(field).key]);Checkbox(node_32,{get id(){return get$3(field).key},get checked(){return get$3($0)},onCheckedChange:checked=>$$props.onConfigChange(get$3(field).key,checked),class:"mt-1"})}var div_9=sibling(node_32,2), -label=child(div_9),text_8=child(label),node_33=sibling(text_8);{var consequent_17=$$anchor4=>{Flask_conical($$anchor4,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_33,$$render=>{get$3(field).isExperimental&&$$render(consequent_17)})}reset(label);var node_34=sibling(label,2);{var consequent_18=$$anchor4=>{var p_4=root_30(),text_9=child(p_4,!0);reset(p_4),template_effect(()=>set_text(text_9,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_4)};if_block(node_34, -$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_18)})}reset(div_9),reset(div_8),template_effect(()=>{set_attribute(label,"for",get$3(field).key),set_text(text_8,`${get$3(field).label??""} `)}),append($$anchor3,div_8)};if_block(node_1,$$render=>{get$3(field).type===SettingsFieldType.INPUT?$$render(consequent_4):get$3(field).type===SettingsFieldType.TEXTAREA?$$render(consequent_8,1):get$3(field).type===SettingsFieldType.SELECT?$$render(consequent_16,2):get$3( -field).type===SettingsFieldType.CHECKBOX&&$$render(consequent_19,3)})}reset(div),append($$anchor2,div)}),append($$anchor,fragment),pop()}delegate(["click"]);var root_1$9=from_html('
No tools available
'),root_5$7=from_html(' ',1),root_9$4=from_html('
'),root_8$7=from_html('
Tool Enabled Always allow
'),root_4$8=from_html( -" ",1),root_2$g=from_html('
');function SettingsChatToolsTab($$anchor,$$props){push$1($$props,!0);let expandedGroups=new SvelteSet,groups=user_derived(()=>toolsStore.toolGroups);function toggleExpanded(label){expandedGroups.has(label)?expandedGroups.delete(label):expandedGroups.add(label)}var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var div=root_1$9();append($$anchor2,div)},alternate_1=$$anchor2=>{var div_1=root_2$g();each(div_1, -21,()=>get$3(groups),group=>group.label,($$anchor3,group)=>{const isExpanded=user_derived(()=>expandedGroups.has(get$3(group).label));var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Collapsible,($$anchor4,Collapsible_Root)=>{Collapsible_Root($$anchor4,{get open(){return get$3(isExpanded)},onOpenChange:()=>toggleExpanded(get$3(group).label),children:($$anchor5,$$slotProps)=>{var fragment_2=root_4$8(),node_2=first_child(fragment_2);component(node_2,()=>Collapsible_trigger, -($$anchor6,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor6,{class:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-muted/50",children:($$anchor7,$$slotProps2)=>{const faviconUrl=user_derived(()=>get$3(group).serverId?mcpStore.getServerFavicon(get$3(group).serverId):null);var fragment_3=root_5$7(),node_3=first_child(fragment_3);{var consequent_1=$$anchor8=>{Chevron_down($$anchor8,{class:"h-3.5 w-3.5 shrink-0"})},alternate=$$anchor8=>{Chevron_right($$anchor8,{class:"h-3.5\ - w-3.5 shrink-0"})};if_block(node_3,$$render=>{get$3(isExpanded)?$$render(consequent_1):$$render(alternate,-1)})}var span=sibling(node_3,2),node_4=child(span);McpServerIdentity(node_4,{iconClass:"h-4 w-4",iconRounded:"rounded-sm",showVersion:!1,get displayName(){return get$3(group).label},get faviconUrl(){return get$3(faviconUrl)}}),reset(span);var span_1=sibling(span,2),text2=child(span_1);reset(span_1),template_effect(()=>set_text(text2,`${get$3(group).tools.length??""} tool${get$3(group).tools. -length!==1?"s":""}`)),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var node_5=sibling(node_2,2);component(node_5,()=>Collapsible_content,($$anchor6,Collapsible_Content)=>{Collapsible_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{var div_2=root_8$7(),node_6=sibling(child(div_2),2);each(node_6,17,()=>get$3(group).tools,tool=>tool.function.name,($$anchor8,tool)=>{const toolName=user_derived(()=>get$3(tool).function.name),isEnabled=user_derived(()=>toolsStore.isToolEnabled(get$3(toolName))), -permissionKey=user_derived(()=>toolsStore.getPermissionKey(get$3(toolName))),isAlwaysAllowed=user_derived(()=>get$3(permissionKey)?permissionsStore.hasTool(get$3(permissionKey)):!1);var div_3=root_9$4(),node_7=child(div_3);TruncatedText(node_7,{get text(){return get$3(toolName)},class:"flex-1",showTooltip:!0});var div_4=sibling(node_7,2),node_8=child(div_4);Checkbox(node_8,{get checked(){return get$3(isEnabled)},onCheckedChange:()=>toolsStore.toggleTool(get$3(toolName)),class:"h-4 w-4"}),reset(div_4); -var div_5=sibling(div_4,2),node_9=child(div_5);Checkbox(node_9,{get checked(){return get$3(isAlwaysAllowed)},onCheckedChange:()=>{get$3(isAlwaysAllowed)?permissionsStore.revokeTool(get$3(permissionKey)):permissionsStore.allowTool(get$3(permissionKey))},class:"h-4 w-4"}),reset(div_5),reset(div_3),append($$anchor8,div_3)}),reset(div_2),append($$anchor7,div_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)}),reset(div_1),append($$anchor2, -div_1)};if_block(node2,$$render=>{get$3(groups).length===0?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}const summaryList=($$anchor,show=noop$3,verb=noop$3,items2=noop$3)=>{var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var div=root_2$f(),h5=child(div),text_1=child(h5);reset(h5);var ul=sibling(h5,2),node_1=child(ul);each(node_1,17,()=>items2().slice(0,10),conv=>conv.id,($$anchor3,conv)=>{var li2=root_3$a(),text_2=child(li2);reset( -li2),template_effect(()=>set_text(text_2,`• ${(get$3(conv).name||"Untitled conversation")??""}`)),append($$anchor3,li2)});var node_2=sibling(node_1,2);{var consequent=$$anchor3=>{var li_1=root_4$7(),text_3=child(li_1);reset(li_1),template_effect(()=>set_text(text_3,`... and ${items2().length-10} more`)),append($$anchor3,li_1)};if_block(node_2,$$render=>{items2().length>10&&$$render(consequent)})}reset(ul),reset(div),template_effect(()=>set_text(text_1,`${verb()??""} +sed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",$$props.class));component(node_1,()=>Select_content$1,($$anchor4,SelectPrimitive_Content)=>{SelectPrimitive_Content($$anchor4,spread_props({get sideOffset(){return sideOffset()},"data-slot":"select-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$i(), +node_2=first_child(fragment_2);Select_scroll_up_button(node_2,{});var node_3=sibling(node_2,2);{let $02=user_derived(()=>cn$1("h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1"));component(node_3,()=>Select_viewport,($$anchor6,SelectPrimitive_Viewport)=>{SelectPrimitive_Viewport($$anchor6,{get class(){return get$3($02)},children:($$anchor7,$$slotProps3)=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);snippet(node_4,()=>$$props.children??noop$3), +append($$anchor7,fragment_3)},$$slots:{default:!0}})})}var node_5=sibling(node_3,2);Select_scroll_down_button(node_5,{}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root_1$c=from_html(" ",1);function Select_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),size2=prop($$props,"size",3,"default"),variant=prop($$props,"variant",3,"default"),restProps=rest_props( +$$props,["$$slots","$$events","$$legacy","ref","class","children","size","variant"]);const baseClasses=user_derived(()=>variant()==="plain"?"group inline-flex w-full items-center justify-end gap-2 whitespace-nowrap px-0 py-0 text-sm font-medium text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]\ +:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3 [&_svg:not([class*='text-'])]:text-muted-foreground":"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-de\ +structive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground"),chevronClasses=user_derived(()=>variant()=== +"plain"?"size-3 opacity-60 transition-transform group-data-[state=open]:-rotate-180":"size-4 opacity-50");var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(get$3(baseClasses),$$props.class));component(node2,()=>Select_trigger$1,($$anchor2,SelectPrimitive_Trigger)=>{SelectPrimitive_Trigger($$anchor2,spread_props({"data-slot":"select-trigger",get"data-size"(){return size2()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2( +$$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$c(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_down(node_2,{get class(){return get$3(chevronClasses)}}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}const Root=Select;var root$p=from_html("");function Textarea($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),value=prop($$props, +"value",15),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","value","class"]);var textarea=root$p();remove_textarea_child(textarea),attribute_effect(textarea,$0=>({"data-slot":"textarea",class:$0,...restProps}),[()=>cn$1("flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visibl\ +e:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",$$props.class)]),bind_this(textarea,$$value=>ref2($$value),()=>ref2()),bind_value(textarea,value),append($$anchor,textarea),pop()}var root_1$b=from_html(" Custom",1);function SettingsChatParameterSourceIndicator($$anchor,$$props){let className=prop($$props,"class",3,"");Badge($$anchor,{variant:"second\ +ary",get class(){return`h-5 bg-orange-100 px-1.5 py-0.5 text-xs text-orange-800 dark:bg-orange-900 dark:text-orange-200 ${className()??""}`},children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$b(),node2=first_child(fragment_1);Wrench(node2,{class:"mr-1 h-3 w-3"}),next$1(),append($$anchor2,fragment_1)},$$slots:{default:!0}})}var root_3$b=from_html(" ",1),root_6$8=from_html(''),root_7$7=from_html('

'),root_2$h=from_html('
',1),root_9$5=from_html(" ",1),root_11$3=from_html('

'),root_12$2=from_html('
'),root_8$8=from_html( +" ",1),root_15$2=from_html(" ",1),root_19$2=from_html('
'),root_21$1=from_html(''),root_25$1=from_html('
'),root_18$1=from_html('
',1),root_27=from_html('

'),root_14$1=from_html('
',1),root_30=from_html('

'),root_28=from_html('
'),root_1$a=from_html('
');function SettingsChatFields($$anchor,$$props){push$1($$props,!0);let sp=user_derived(()=>{if(serverStore.isRouterMode){const m=selectedModelName();if(m)return modelsStore.getModelProps(m)?.default_generation_settings?.params??{}}return serverStore.defaultParams??{}});var fragment=comment$2(),node2=first_child(fragment);each(node2,17,()=>$$props.fields,field=>field.key,($$anchor2,field)=>{var div=root_1$a(),node_1=child(div);{var consequent_4=$$anchor3=>{const currentValue=user_derived( +()=>String($$props.localConfig[get$3(field).key]??"")),serverDefault=user_derived(()=>get$3(sp)[get$3(field).key]),isCustomRealTime=user_derived(()=>(()=>{if(get$3(serverDefault)==null||get$3(currentValue)==="")return!1;const numericInput=parseFloat(get$3(currentValue)),normalizedInput=isNaN(numericInput)?get$3(currentValue):Math.round(numericInput*1e6)/1e6,normalizedDefault=typeof get$3(serverDefault)=="number"?Math.round(get$3(serverDefault)*1e6)/1e6:get$3(serverDefault);return normalizedInput!== +normalizedDefault})());var fragment_1=root_2$h(),div_1=first_child(fragment_1),node_2=child(div_1);Label(node_2,{get for(){return get$3(field).key},class:"flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_2=root_3$b(),text2=first_child(fragment_2),node_3=sibling(text2);{var consequent=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_3,$$render=>{get$3(field).isExperimental&&$$render(consequent)})} +template_effect(()=>set_text(text2,`${get$3(field).label??""} `)),append($$anchor4,fragment_2)},$$slots:{default:!0}});var node_4=sibling(node_2,2);{var consequent_1=$$anchor4=>{SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_4,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_1)})}reset(div_1);var div_2=sibling(div_1,2),node_5=child(div_2);{let $0=user_derived(()=>get$3(sp)[get$3(field).key]!=null?`Default: ${normalizeFloatingPoint(get$3(sp)[get$3(field).key])}`:""),$1=user_derived( +()=>get$3(isCustomRealTime)?"pr-8":"");Input(node_5,{get id(){return get$3(field).key},get value(){return get$3(currentValue)},oninput:e=>{$$props.onConfigChange(get$3(field).key,e.currentTarget.value)},get placeholder(){return get$3($0)},get class(){return`w-full ${get$3($1)??""}`}})}var node_6=sibling(node_5,2);{var consequent_2=$$anchor4=>{var button=root_6$8(),node_7=child(button);Rotate_ccw(node_7,{class:"h-3 w-3"}),reset(button),delegated("click",button,()=>{settingsStore.resetParameterToServerDefault( +get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}),append($$anchor4,button)};if_block(node_6,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_2)})}reset(div_2);var node_8=sibling(div_2,2);{var consequent_3=$$anchor4=>{var p_1=root_7$7();html$6(p_1,()=>get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key],!0),reset(p_1),append($$anchor4,p_1)};if_block(node_8,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_3)})}append($$anchor3,fragment_1)}, +consequent_8=$$anchor3=>{var fragment_5=root_8$8(),node_9=first_child(fragment_5);Label(node_9,{get for(){return get$3(field).key},class:"block flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_6=root_9$5(),text_1=first_child(fragment_6),node_10=sibling(text_1);{var consequent_5=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_10,$$render=>{get$3(field).isExperimental&&$$render(consequent_5)})}template_effect( +()=>set_text(text_1,`${get$3(field).label??""} `)),append($$anchor4,fragment_6)},$$slots:{default:!0}});var node_11=sibling(node_9,2);{let $0=user_derived(()=>String($$props.localConfig[get$3(field).key]??""));Textarea(node_11,{get id(){return get$3(field).key},get value(){return get$3($0)},onchange:e=>$$props.onConfigChange(get$3(field).key,e.currentTarget.value),placeholder:"",class:"min-h-[10rem] w-full md:max-w-3xl"})}var node_12=sibling(node_11,2);{var consequent_6=$$anchor4=>{var p_2=root_11$3(), +text_2=child(p_2,!0);reset(p_2),template_effect(()=>set_text(text_2,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_2)};if_block(node_12,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_6)})}var node_13=sibling(node_12,2);{var consequent_7=$$anchor4=>{var div_3=root_12$2(),node_14=child(div_3);{let $0=user_derived(()=>!!($$props.localConfig.showSystemMessage??!0));Checkbox(node_14,{id:"showSystemMessage",get checked(){return get$3( +$0)},onCheckedChange:checked=>$$props.onConfigChange("showSystemMessage",!!checked)})}var node_15=sibling(node_14,2);Label(node_15,{for:"showSystemMessage",class:"cursor-pointer text-sm font-normal",children:($$anchor5,$$slotProps)=>{next$1();var text_3=text$8("Show system message in conversations");append($$anchor5,text_3)},$$slots:{default:!0}}),reset(div_3),append($$anchor4,div_3)};if_block(node_13,$$render=>{get$3(field).key===SETTINGS_KEYS.SYSTEM_MESSAGE&&$$render(consequent_7)})}append($$anchor3, +fragment_5)},consequent_16=$$anchor3=>{const selectedOption=user_derived(()=>get$3(field).options?.find(opt=>opt.value===$$props.localConfig[get$3(field).key])),currentValue=user_derived(()=>$$props.localConfig[get$3(field).key]),serverDefault=user_derived(()=>get$3(sp)[get$3(field).key]),isCustomRealTime=user_derived(()=>get$3(serverDefault)==null||get$3(currentValue)===""||get$3(currentValue)===void 0?!1:get$3(currentValue)!==get$3(serverDefault));var fragment_8=root_14$1(),div_4=first_child(fragment_8), +node_16=child(div_4);Label(node_16,{get for(){return get$3(field).key},class:"flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_9=root_15$2(),text_4=first_child(fragment_9),node_17=sibling(text_4);{var consequent_9=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_17,$$render=>{get$3(field).isExperimental&&$$render(consequent_9)})}template_effect(()=>set_text(text_4,`${get$3(field).label??""} `)), +append($$anchor4,fragment_9)},$$slots:{default:!0}});var node_18=sibling(node_16,2);{var consequent_10=$$anchor4=>{SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_18,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_10)})}reset(div_4);var node_19=sibling(div_4,2);component(node_19,()=>Root,($$anchor4,Select_Root)=>{Select_Root($$anchor4,{type:"single",get value(){return get$3(currentValue)},onValueChange:value=>{get$3(field).key===SETTINGS_KEYS.THEME&&value&&$$props.onThemeChange? +$$props.onThemeChange(value):$$props.onConfigChange(get$3(field).key,value)},children:($$anchor5,$$slotProps)=>{var fragment_12=root_18$1(),div_5=first_child(fragment_12),node_20=child(div_5);component(node_20,()=>Select_trigger,($$anchor6,Select_Trigger)=>{Select_Trigger($$anchor6,{class:"w-full",children:($$anchor7,$$slotProps2)=>{var div_6=root_19$2(),node_21=child(div_6);{var consequent_11=$$anchor8=>{const IconComponent=user_derived(()=>get$3(selectedOption).icon);var fragment_13=comment$2(), +node_22=first_child(fragment_13);component(node_22,()=>get$3(IconComponent),($$anchor9,IconComponent_1)=>{IconComponent_1($$anchor9,{class:"h-4 w-4"})}),append($$anchor8,fragment_13)};if_block(node_21,$$render=>{get$3(selectedOption)?.icon&&$$render(consequent_11)})}var text_5=sibling(node_21);reset(div_6),template_effect($0=>set_text(text_5,` ${$0??""}`),[()=>get$3(selectedOption)?.label||`Select ${get$3(field).label.toLowerCase()}`]),append($$anchor7,div_6)},$$slots:{default:!0}})});var node_23=sibling( +node_20,2);{var consequent_12=$$anchor6=>{var button_1=root_21$1(),node_24=child(button_1);Rotate_ccw(node_24,{class:"h-3 w-3"}),reset(button_1),delegated("click",button_1,()=>{settingsStore.resetParameterToServerDefault(get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}),append($$anchor6,button_1)};if_block(node_23,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_12)})}reset(div_5);var node_25=sibling(div_5,2);component(node_25,()=>Select_content,($$anchor6,Select_Content)=>{ +Select_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{var fragment_14=comment$2(),node_26=first_child(fragment_14);{var consequent_14=$$anchor8=>{var fragment_15=comment$2(),node_27=first_child(fragment_15);each(node_27,17,()=>get$3(field).options,option2=>option2.value,($$anchor9,option2)=>{var fragment_16=comment$2(),node_28=first_child(fragment_16);component(node_28,()=>Select_item,($$anchor10,Select_Item)=>{Select_Item($$anchor10,{get value(){return get$3(option2).value},get label(){ +return get$3(option2).label},children:($$anchor11,$$slotProps3)=>{var div_7=root_25$1(),node_29=child(div_7);{var consequent_13=$$anchor12=>{const IconComponent=user_derived(()=>get$3(option2).icon);var fragment_17=comment$2(),node_30=first_child(fragment_17);component(node_30,()=>get$3(IconComponent),($$anchor13,IconComponent_2)=>{IconComponent_2($$anchor13,{class:"h-4 w-4"})}),append($$anchor12,fragment_17)};if_block(node_29,$$render=>{get$3(option2).icon&&$$render(consequent_13)})}var text_6=sibling( +node_29);reset(div_7),template_effect(()=>set_text(text_6,` ${get$3(option2).label??""}`)),append($$anchor11,div_7)},$$slots:{default:!0}})}),append($$anchor9,fragment_16)}),append($$anchor8,fragment_15)};if_block(node_26,$$render=>{get$3(field).options&&$$render(consequent_14)})}append($$anchor7,fragment_14)},$$slots:{default:!0}})}),append($$anchor5,fragment_12)},$$slots:{default:!0}})});var node_31=sibling(node_19,2);{var consequent_15=$$anchor4=>{var p_3=root_27(),text_7=child(p_3,!0);reset( +p_3),template_effect(()=>set_text(text_7,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_3)};if_block(node_31,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_15)})}append($$anchor3,fragment_8)},consequent_19=$$anchor3=>{var div_8=root_28(),node_32=child(div_8);{let $0=user_derived(()=>!!$$props.localConfig[get$3(field).key]);Checkbox(node_32,{get id(){return get$3(field).key},get checked(){return get$3($0)},onCheckedChange:checked=>$$props. +onConfigChange(get$3(field).key,checked),class:"mt-1"})}var div_9=sibling(node_32,2),label=child(div_9),text_8=child(label),node_33=sibling(text_8);{var consequent_17=$$anchor4=>{Flask_conical($$anchor4,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_33,$$render=>{get$3(field).isExperimental&&$$render(consequent_17)})}reset(label);var node_34=sibling(label,2);{var consequent_18=$$anchor4=>{var p_4=root_30(),text_9=child(p_4,!0);reset(p_4),template_effect(()=>set_text(text_9,get$3(field). +help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_4)};if_block(node_34,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_18)})}reset(div_9),reset(div_8),template_effect(()=>{set_attribute(label,"for",get$3(field).key),set_text(text_8,`${get$3(field).label??""} `)}),append($$anchor3,div_8)};if_block(node_1,$$render=>{get$3(field).type===SettingsFieldType.INPUT?$$render(consequent_4):get$3(field).type===SettingsFieldType.TEXTAREA?$$render(consequent_8, +1):get$3(field).type===SettingsFieldType.SELECT?$$render(consequent_16,2):get$3(field).type===SettingsFieldType.CHECKBOX&&$$render(consequent_19,3)})}reset(div),append($$anchor2,div)}),append($$anchor,fragment),pop()}delegate(["click"]);var root_1$9=from_html('
No tools available
'),root_5$7=from_html(' ',1),root_9$4=from_html('
'),root_8$7=from_html('
Tool Enabled Always allow
'),root_4$8=from_html(" ",1),root_2$g=from_html('
');function SettingsChatToolsTab($$anchor,$$props){push$1($$props,!0);let expandedGroups=new SvelteSet,groups=user_derived(()=>toolsStore.toolGroups);function toggleExpanded(label){expandedGroups.has(label)?expandedGroups.delete(label):expandedGroups.add(label)}var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var div=root_1$9(); +append($$anchor2,div)},alternate_1=$$anchor2=>{var div_1=root_2$g();each(div_1,21,()=>get$3(groups),group=>group.label,($$anchor3,group)=>{const isExpanded=user_derived(()=>expandedGroups.has(get$3(group).label));var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Collapsible,($$anchor4,Collapsible_Root)=>{Collapsible_Root($$anchor4,{get open(){return get$3(isExpanded)},onOpenChange:()=>toggleExpanded(get$3(group).label),children:($$anchor5,$$slotProps)=>{var fragment_2=root_4$8(), +node_2=first_child(fragment_2);component(node_2,()=>Collapsible_trigger,($$anchor6,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor6,{class:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-muted/50",children:($$anchor7,$$slotProps2)=>{const faviconUrl=user_derived(()=>get$3(group).serverId?mcpStore.getServerFavicon(get$3(group).serverId):null);var fragment_3=root_5$7(),node_3=first_child(fragment_3);{var consequent_1=$$anchor8=>{Chevron_down($$anchor8,{class:"h-3.5 w-3.5 \ +shrink-0"})},alternate=$$anchor8=>{Chevron_right($$anchor8,{class:"h-3.5 w-3.5 shrink-0"})};if_block(node_3,$$render=>{get$3(isExpanded)?$$render(consequent_1):$$render(alternate,-1)})}var span=sibling(node_3,2),node_4=child(span);McpServerIdentity(node_4,{iconClass:"h-4 w-4",iconRounded:"rounded-sm",showVersion:!1,get displayName(){return get$3(group).label},get faviconUrl(){return get$3(faviconUrl)}}),reset(span);var span_1=sibling(span,2),text2=child(span_1);reset(span_1),template_effect(()=>set_text( +text2,`${get$3(group).tools.length??""} tool${get$3(group).tools.length!==1?"s":""}`)),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var node_5=sibling(node_2,2);component(node_5,()=>Collapsible_content,($$anchor6,Collapsible_Content)=>{Collapsible_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{var div_2=root_8$7(),node_6=sibling(child(div_2),2);each(node_6,17,()=>get$3(group).tools,tool=>tool.function.name,($$anchor8,tool)=>{const toolName=user_derived(()=>get$3(tool).function. +name),isEnabled=user_derived(()=>toolsStore.isToolEnabled(get$3(toolName))),permissionKey=user_derived(()=>toolsStore.getPermissionKey(get$3(toolName))),isAlwaysAllowed=user_derived(()=>get$3(permissionKey)?permissionsStore.hasTool(get$3(permissionKey)):!1);var div_3=root_9$4(),node_7=child(div_3);TruncatedText(node_7,{get text(){return get$3(toolName)},class:"flex-1",showTooltip:!0});var div_4=sibling(node_7,2),node_8=child(div_4);Checkbox(node_8,{get checked(){return get$3(isEnabled)},onCheckedChange:()=>toolsStore. +toggleTool(get$3(toolName)),class:"h-4 w-4"}),reset(div_4);var div_5=sibling(div_4,2),node_9=child(div_5);Checkbox(node_9,{get checked(){return get$3(isAlwaysAllowed)},onCheckedChange:()=>{get$3(isAlwaysAllowed)?permissionsStore.revokeTool(get$3(permissionKey)):permissionsStore.allowTool(get$3(permissionKey))},class:"h-4 w-4"}),reset(div_5),reset(div_3),append($$anchor8,div_3)}),reset(div_2),append($$anchor7,div_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}), +append($$anchor3,fragment_1)}),reset(div_1),append($$anchor2,div_1)};if_block(node2,$$render=>{get$3(groups).length===0?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}const summaryList=($$anchor,show=noop$3,verb=noop$3,items2=noop$3)=>{var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var div=root_2$f(),h5=child(div),text_1=child(h5);reset(h5);var ul=sibling(h5,2),node_1=child(ul);each(node_1,17,()=>items2().slice(0,10),conv=>conv. +id,($$anchor3,conv)=>{var li2=root_3$a(),text_2=child(li2);reset(li2),template_effect(()=>set_text(text_2,`• ${(get$3(conv).name||"Untitled conversation")??""}`)),append($$anchor3,li2)});var node_2=sibling(node_1,2);{var consequent=$$anchor3=>{var li_1=root_4$7(),text_3=child(li_1);reset(li_1),template_effect(()=>set_text(text_3,`... and ${items2().length-10} more`)),append($$anchor3,li_1)};if_block(node_2,$$render=>{items2().length>10&&$$render(consequent)})}reset(ul),reset(div),template_effect( +()=>set_text(text_1,`${verb()??""} ${items2().length??""} conversation${items2().length===1?"":"s"}`)),append($$anchor2,div)};if_block(node2,$$render=>{show()&&items2().length>0&&$$render(consequent_1)})}append($$anchor,fragment)},section=($$anchor,title2=noop$3,description2=noop$3,IconComponent=noop$3,buttonText=noop$3,onclick=noop$3,opts=noop$3)=>{const buttonClass=user_derived(()=>opts()?.buttonClass??"justify-start justify-self-start md:w-auto"),buttonVariant=user_derived(()=>opts()?.buttonVariant??"outline");var div_1=root_5$6(), h4=child(div_1),text_4=child(h4,!0);reset(h4);var p2=sibling(h4,2),text_5=child(p2,!0);reset(p2);var node_3=sibling(p2,2);Button(node_3,{get class(){return get$3(buttonClass)},get onclick(){return onclick()},get variant(){return get$3(buttonVariant)},children:($$anchor2,$$slotProps)=>{var fragment_1=root_6$7(),node_4=first_child(fragment_1);component(node_4,IconComponent,($$anchor3,IconComponent_1)=>{IconComponent_1($$anchor3,{class:"mr-2 h-4 w-4"})});var text_6=sibling(node_4);template_effect(()=>set_text( text_6,` ${buttonText()??""}`)),append($$anchor2,fragment_1)},$$slots:{default:!0}});var node_5=sibling(node_3,2);{var consequent_2=$$anchor2=>{summaryList($$anchor2,()=>opts().summary.show,()=>opts().summary.verb,()=>opts().summary.items)};if_block(node_5,$$render=>{opts()?.summary&&$$render(consequent_2)})}reset(div_1),template_effect(()=>{set_class(div_1,1,`grid gap-1 ${opts()?.wrapperClass??""??""}`),set_class(h4,1,`mt-0 mb-2 text-sm font-medium ${opts()?.titleClass??""??""}`),set_text(text_4, diff --git a/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte index 2398daee70b..c0bb2a34e4f 100644 --- a/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +++ b/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -22,7 +22,7 @@ 'fixed z-[999999] grid w-full gap-4 border bg-background p-6 shadow-lg duration-200', // Mobile: Bottom sheet behavior 'right-0 bottom-0 left-0 max-h-[100dvh] translate-x-0 translate-y-0 overflow-y-auto rounded-t-lg', - 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-full', + 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:slide-out-to-bottom-full', 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-full', // Desktop: Centered dialog behavior 'sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-h-[100vh] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-lg', diff --git a/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte index 71f166debf3..b047dcf6c45 100644 --- a/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +++ b/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -13,7 +13,7 @@ bind:ref data-slot="alert-dialog-overlay" class={cn( - 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', + 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte b/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte index 74df0eacb57..0e1b07c40ed 100644 --- a/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte +++ b/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte @@ -25,7 +25,7 @@ bind:ref data-slot="dialog-content" class={cn( - `fixed top-[50%] left-[50%] z-50 grid max-h-[100dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border border-border/30 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg md:max-h-[100vh]`, + `fixed top-[50%] left-[50%] z-50 grid max-h-[100dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border border-border/30 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg md:max-h-[100vh]`, className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte b/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte index a6e9a102f5d..a7803f90368 100644 --- a/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte +++ b/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -13,7 +13,7 @@ bind:ref data-slot="dialog-overlay" class={cn( - 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', + 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte index 6013ca2661b..0ca0d3964ac 100644 --- a/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +++ b/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -19,7 +19,7 @@ data-slot="dropdown-menu-content" {sideOffset} class={cn( - 'z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20', + 'z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte index c6637366553..e26c51cdc23 100644 --- a/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +++ b/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -13,7 +13,7 @@ bind:ref data-slot="dropdown-menu-sub-content" class={cn( - 'z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20', + 'z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte b/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte index 2d3513d347f..b46e928b1b9 100644 --- a/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte +++ b/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte @@ -29,7 +29,7 @@ {collisionPadding} {avoidCollisions} class={cn( - 'z-50 w-72 origin-(--bits-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', + 'z-50 w-72 origin-(--bits-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/select/select-content.svelte b/tools/server/webui/src/lib/components/ui/select/select-content.svelte index 4050628cc87..b54bc60c227 100644 --- a/tools/server/webui/src/lib/components/ui/select/select-content.svelte +++ b/tools/server/webui/src/lib/components/ui/select/select-content.svelte @@ -93,7 +93,7 @@ {sideOffset} data-slot="select-content" class={cn( - 'relative z-[var(--layer-popover,1000000)] max-h-(--bits-select-content-available-height) min-w-[8rem] origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:-translate-x-1 data-[side=left]:slide-in-from-right-2 data-[side=right]:translate-x-1 data-[side=right]:slide-in-from-left-2 data-[side=top]:-translate-y-1 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', + 'relative z-[var(--layer-popover,1000000)] max-h-(--bits-select-content-available-height) min-w-[8rem] origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:-translate-x-1 data-[side=left]:slide-in-from-right-2 data-[side=right]:translate-x-1 data-[side=right]:slide-in-from-left-2 data-[side=top]:-translate-y-1 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', className )} {...restProps} diff --git a/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte b/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte index 1eaa6d99ff4..b616c469a91 100644 --- a/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte +++ b/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte @@ -1,7 +1,7 @@ diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte index 4830e474a83..99daa10e3e2 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte @@ -12,6 +12,8 @@ import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte'; import { AttachmentMenuItemId } from '$lib/enums'; import { PencilRuler } from '@lucide/svelte'; + import { ROUTES, SETTINGS_SECTION_SLUGS } from '$lib/constants/routes'; + import { RouterService } from '$lib/services/router.service'; interface Props { class?: string; @@ -146,13 +148,16 @@
- + MCP Servers - + Tools diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte index e555f1b43ea..3945155ff1e 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte @@ -13,6 +13,7 @@ import { conversationsStore } from '$lib/stores/conversations.svelte'; import { getFileTypeCategory } from '$lib/utils'; import { goto } from '$app/navigation'; + import { ROUTES } from '$lib/constants/routes'; interface Props { canSend?: boolean; @@ -100,7 +101,7 @@ {onSystemPromptClick} {onMcpPromptClick} {onMcpResourcesClick} - onMcpSettingsClick={() => goto('#/settings/mcp')} + onMcpSettingsClick={() => goto(ROUTES.MCP_SERVERS)} />
{/if} diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte index c593558a51b..758a84f9111 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte @@ -17,6 +17,7 @@ import { parseFilesToMessageExtras } from '$lib/utils/browser-only'; import { deriveAgenticSections } from '$lib/utils'; import type { DatabaseMessageExtraMcpPrompt } from '$lib/types'; + import { ROUTES } from '$lib/constants/routes'; interface Props { class?: string; @@ -182,7 +183,7 @@ const conversationDeleted = await chatStore.removeSystemPromptPlaceholder(message.id); if (conversationDeleted) { - goto(`#/`); + goto(ROUTES.START); } return; @@ -205,7 +206,7 @@ const conversationDeleted = await chatStore.removeSystemPromptPlaceholder(message.id); if (conversationDeleted) { - goto(`#/`); + goto(ROUTES.START); } } else { chatActions.delete(message); @@ -271,7 +272,7 @@ const conversationDeleted = await chatStore.removeSystemPromptPlaceholder(message.id); isEditing = false; if (conversationDeleted) { - goto(`#/`); + goto(ROUTES.START); } return; } diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte index ac482baa4d4..962f2a28538 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte @@ -85,10 +85,6 @@ editCtx.setUploadedFiles([...editCtx.editedUploadedFiles, ...processed]); } - function handleUploadedFilesChange(files: ChatUploadedFile[]) { - editCtx.setUploadedFiles(files); - } - $effect(() => { chatStore.setEditModeActive(handleFilesAdd); @@ -104,7 +100,7 @@ diff --git a/tools/server/webui/src/lib/components/app/dialogs/DialogExportSettings.svelte b/tools/server/webui/src/lib/components/app/dialogs/DialogExportSettings.svelte new file mode 100644 index 00000000000..c112bde9f68 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/dialogs/DialogExportSettings.svelte @@ -0,0 +1,83 @@ + + + + + + + {#if includeSensitiveData} + + {:else} + + {/if} + Export Settings + + + + {#if includeSensitiveData} +

+ Warning: This export will include sensitive data such as API keys and MCP server custom + headers (e.g., authorization tokens). Do not share this file with anyone you don't + trust. +

+ {:else} +

+ Sensitive data (API keys, MCP server custom headers) will not be included in the export + to protect your credentials. +

+ {/if} +
+
+ +
+ + + +
+ + + Cancel + + {#if includeSensitiveData} + Export Anyway + {:else} + Export Without Sensitive Data + {/if} + + +
+
diff --git a/tools/server/webui/src/lib/components/app/dialogs/index.ts b/tools/server/webui/src/lib/components/app/dialogs/index.ts index 458fc809833..5a6453b72ec 100644 --- a/tools/server/webui/src/lib/components/app/dialogs/index.ts +++ b/tools/server/webui/src/lib/components/app/dialogs/index.ts @@ -18,6 +18,37 @@ */ export { default as DialogMcpServerAddNew } from './DialogMcpServerAddNew.svelte'; +/** + * **DialogExportSettings** - Settings export dialog with sensitive data warning + * + * Dialog for exporting settings with an option to include or exclude + * sensitive data (API keys, MCP server custom headers). Defaults to excluding + * sensitive data for security. User must explicitly opt-in to include them. + * + * **Architecture:** + * - Uses ShadCN AlertDialog + * - Checkbox to toggle sensitive data inclusion (defaults to false) + * - Warning icon and message when sensitive data is included + * - Destructive variant for the action button when exporting with sensitive data + * + * **Features:** + * - Secure default: sensitive data excluded by default + * - User must explicitly opt-in to include sensitive data + * - Visual warning (ShieldOff icon) when sensitive data is included + * - Different action text based on sensitive data state + * + * @example + * ```svelte + * showExportSettings = false} + * /> + * ``` + */ +export { default as DialogExportSettings } from './DialogExportSettings.svelte'; + /** * * CONFIRMATION DIALOGS diff --git a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index f1d039142c8..105576bb4f5 100644 --- a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -11,6 +11,8 @@ import ScrollArea from '$lib/components/ui/scroll-area/scroll-area.svelte'; import * as Sidebar from '$lib/components/ui/sidebar'; import Input from '$lib/components/ui/input/input.svelte'; + import { ROUTES } from '$lib/constants/routes'; + import { RouterService } from '$lib/services/router.service'; import { conversationsStore, conversations, @@ -159,7 +161,7 @@ } handleMobileSidebarItemClick(); - await goto(`#/chat/${id}`); + await goto(RouterService.chat(id)); } function handleStopGeneration(id: string) { @@ -171,7 +173,7 @@
- +

{APP_NAME}

diff --git a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte b/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte index 46bab827461..dad8d954cbb 100644 --- a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte +++ b/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte @@ -11,6 +11,7 @@ import { DropdownMenuActions } from '$lib/components/app'; import * as Tooltip from '$lib/components/ui/tooltip'; import { FORK_TREE_DEPTH_PADDING } from '$lib/constants'; + import { RouterService } from '$lib/services/router.service'; import { getAllLoadingChats } from '$lib/stores/chat.svelte'; import { conversationsStore } from '$lib/stores/conversations.svelte'; import { TruncatedText } from '$lib/components/app'; @@ -113,7 +114,7 @@ diff --git a/tools/server/webui/src/lib/components/app/server/ServerErrorSplash.svelte b/tools/server/webui/src/lib/components/app/server/ServerErrorSplash.svelte index c7f52a7c58d..4da0d1ddfa8 100644 --- a/tools/server/webui/src/lib/components/app/server/ServerErrorSplash.svelte +++ b/tools/server/webui/src/lib/components/app/server/ServerErrorSplash.svelte @@ -7,6 +7,8 @@ import Label from '$lib/components/ui/label/label.svelte'; import { serverStore, serverLoading } from '$lib/stores/server.svelte'; import { config, settingsStore } from '$lib/stores/settings.svelte'; + import { SETTINGS_KEYS } from '$lib/constants'; + import { ROUTES } from '$lib/constants/routes'; import { fade, fly, scale } from 'svelte/transition'; import { KeyboardKey } from '$lib/enums'; @@ -63,7 +65,7 @@ try { // Update the API key in settings first - settingsStore.updateConfig('apiKey', apiKeyInput.trim()); + settingsStore.updateConfig(SETTINGS_KEYS.API_KEY, apiKeyInput.trim()); // Test the API key by making a real request to the server const response = await fetch(`${base}/props`, { @@ -79,7 +81,7 @@ // Show success state briefly, then navigate to home setTimeout(() => { - goto(`#/`); + goto(ROUTES.START); }, 1000); } else { // API key is invalid - User Story A diff --git a/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte b/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte index 08dbe467f6f..109c8ff9dac 100644 --- a/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte +++ b/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte @@ -1,11 +1,11 @@ + +
+

{title}

+ +

{description}

+ + + + {#if summary && summary.show && summary.items.length > 0} +
+
+ {summary.verb} + {summary.items.length} conversation{summary.items.length === 1 ? '' : 's'} +
+ +
    + {#each summary.items.slice(0, 10) as conv (conv.id)} +
  • • {conv.name || 'Untitled conversation'}
  • + {/each} + + {#if summary.items.length > 10} +
  • ... and {summary.items.length - 10} more
  • + {/if} +
+
+ {/if} +
diff --git a/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportTab.svelte b/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportTab.svelte index f84c3a0500a..b7b91d65b65 100644 --- a/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportTab.svelte +++ b/tools/server/webui/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportTab.svelte @@ -1,21 +1,18 @@ -{#snippet summaryList(show: boolean, verb: string, items: DatabaseConversation[])} - {#if show && items.length > 0} -
-
- {verb} - {items.length} conversation{items.length === 1 ? '' : 's'} -
- -
    - {#each items.slice(0, 10) as conv (conv.id)} -
  • • {conv.name || 'Untitled conversation'}
  • - {/each} - - {#if items.length > 10} -
  • ... and {items.length - 10} more
  • - {/if} -
-
- {/if} -{/snippet} - -{#snippet section( - title: string, - description: string, - IconComponent: Component, - buttonText: string, - onclick: () => void, - opts: SectionOpts -)} - {@const buttonClass = opts?.buttonClass ?? 'justify-start justify-self-start md:w-auto'} - {@const buttonVariant = opts?.buttonVariant ?? 'outline'} -
-

{title}

- -

{description}

- - - - {#if opts?.summary} - {@render summaryList(opts.summary.show, opts.summary.verb, opts.summary.items)} - {/if} -
-{/snippet} - -
-
- {@render section( - 'Export Conversations', - 'Download all your conversations as a JSON file. This includes all messages, attachments, and conversation history.', - Download, - 'Export conversations', - handleExportClick, - { summary: { show: showExportSummary, verb: 'Exported', items: exportedConversations } } - )} - - {@render section( - 'Import Conversations', - 'Import one or more conversations from a previously exported JSON file. This will merge with your existing conversations.', - Upload, - 'Import conversations', - handleImportClick, - { - wrapperClass: 'border-t border-border/30 pt-6', - summary: { show: showImportSummary, verb: 'Imported', items: importedConversations } - } - )} - - {@render section( - 'Delete All Conversations', - 'Permanently delete all conversations and their messages. This action cannot be undone. Consider exporting your conversations first if you want to keep a backup.', - Trash2, - 'Delete all conversations', - handleDeleteAllClick, - { - wrapperClass: 'border-t border-border/30 pt-4', - titleClass: 'text-destructive', - buttonVariant: 'destructive', - buttonClass: - 'text-destructive-foreground justify-start justify-self-start bg-destructive hover:bg-destructive/80 md:w-auto' - } - )} -
+
+ + + + + + + + + + + + +
+ + + import type { Snippet } from 'svelte'; + + interface Props { + title: string; + children: Snippet; + } + + let { title, children }: Props = $props(); + + +
+

{title}

+ +
+ {@render children()} +
+
diff --git a/tools/server/webui/src/lib/components/app/settings/index.ts b/tools/server/webui/src/lib/components/app/settings/index.ts index 50848a34426..63f9651df62 100644 --- a/tools/server/webui/src/lib/components/app/settings/index.ts +++ b/tools/server/webui/src/lib/components/app/settings/index.ts @@ -20,23 +20,44 @@ export { default as SettingsChatDesktopSidebar } from './SettingsChatDesktopSide export { default as SettingsChatMobileHeader } from './SettingsChatMobileHeader.svelte'; /** - * Settings Import/Export panel. - * Provides UI for importing and exporting chat conversations. + * Badge indicating parameter source for sampling settings. Shows one of: + * - **Custom**: User has explicitly set this value (orange badge) + * - **Server Props**: Using default from `/props` endpoint (blue badge) + * - **Default**: Using app default, server props unavailable (gray badge) + * Updates in real-time as user types to show immediate feedback. */ -export { default as SettingsChatImportExportTab } from './SettingsChat/SettingsChatImportExportTab.svelte'; +export { default as SettingsChatParameterSourceIndicator } from './SettingsChat/SettingsChatParameterSourceIndicator.svelte'; /** - * MCP Servers configuration panel. - * Provides UI for managing Model Context Protocol (MCP) server connections. + * Section wrapper for settings panels. Displays a title heading with + * child content in a structured layout. */ -export { default as SettingsMcpServers } from './SettingsMcpServers.svelte'; +export { default as SettingsGroup } from './SettingsGroup.svelte'; /** * Footer with save/cancel buttons for settings panel. Positioned at bottom * of settings dialog. Save button commits form state to config store, * cancel button triggers reset and close. */ -export { default as SettingsChatFooter } from './SettingsChat/SettingsChatFooter.svelte'; +export { default as SettingsFooter } from './SettingsFooter.svelte'; + +/** + * Settings Import/Export panel. + * Provides UI for importing and exporting chat conversations. + */ +export { default as SettingsChatImportExportTab } from './SettingsChat/SettingsChatImportExportTab.svelte'; + +/** + * Section wrapper for import/export sections. Displays a title, description, + * icon button, and optional summary of recent actions. + */ +export { default as SettingsChatImportExportSection } from './SettingsChat/SettingsChatImportExportSection.svelte'; + +/** + * MCP Servers configuration panel. + * Provides UI for managing Model Context Protocol (MCP) server connections. + */ +export { default as SettingsMcpServers } from './SettingsMcpServers.svelte'; /** * Form fields renderer for individual settings. Generates appropriate input @@ -45,15 +66,6 @@ export { default as SettingsChatFooter } from './SettingsChat/SettingsChatFooter */ export { default as SettingsChatFields } from './SettingsChat/SettingsChatFields.svelte'; -/** - * Badge indicating parameter source for sampling settings. Shows one of: - * - **Custom**: User has explicitly set this value (orange badge) - * - **Server Props**: Using default from `/props` endpoint (blue badge) - * - **Default**: Using app default, server props unavailable (gray badge) - * Updates in real-time as user types to show immediate feedback. - */ -export { default as SettingsChatParameterSourceIndicator } from './SettingsChat/SettingsChatParameterSourceIndicator.svelte'; - /** * **SettingsChatToolsTab** - Tools configuration tab for chat settings * diff --git a/tools/server/webui/src/lib/constants/index.ts b/tools/server/webui/src/lib/constants/index.ts index 898094c826b..88ff43e56c6 100644 --- a/tools/server/webui/src/lib/constants/index.ts +++ b/tools/server/webui/src/lib/constants/index.ts @@ -29,10 +29,9 @@ export * from './message-export'; export * from './model-id'; export * from './precision'; export * from './processing-info'; -export * from './settings-config'; -export * from './settings-fields'; +export * from './routes'; export * from './settings-keys'; -export * from './settings-sections'; +export * from './settings-registry'; export * from './supported-file-types'; export * from './table-html-restorer'; export * from './title-generation'; diff --git a/tools/server/webui/src/lib/constants/routes.ts b/tools/server/webui/src/lib/constants/routes.ts new file mode 100644 index 00000000000..14416478f08 --- /dev/null +++ b/tools/server/webui/src/lib/constants/routes.ts @@ -0,0 +1,26 @@ +export const NEW_CHAT_PARAM = 'new_chat'; + +/** Settings section slugs — used for routes and navigation. */ +export const SETTINGS_SECTION_SLUGS = { + GENERAL: 'general', + DISPLAY: 'display', + SAMPLING: 'sampling', + PENALTIES: 'penalties', + AGENTIC: 'agentic', + DEVELOPER: 'developer', + TOOLS: 'tools', + IMPORT_EXPORT: 'import-export' +} as const; + +export const ROUTES = { + /** Root — start of the app. */ + START: '#/', + /** New chat — root with new chat query param. */ + NEW_CHAT: `?${NEW_CHAT_PARAM}=true#/`, + /** Chat base — for dynamic chat URLs use RouterService. */ + CHAT: '#/chat', + /** MCP servers. */ + MCP_SERVERS: '#/mcp-servers', + /** Settings base — for dynamic settings URLs use RouterService. */ + SETTINGS: '#/settings' +} as const; diff --git a/tools/server/webui/src/lib/constants/settings-config.ts b/tools/server/webui/src/lib/constants/settings-config.ts deleted file mode 100644 index ddaf2f3e3db..00000000000 --- a/tools/server/webui/src/lib/constants/settings-config.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { ColorMode } from '$lib/enums/ui'; -import { Monitor, Moon, Sun } from '@lucide/svelte'; -import { TITLE } from './title-generation'; - -export const SETTING_CONFIG_DEFAULT: Record = { - // Note: in order not to introduce breaking changes, please keep the same data type (number, string, etc) if you want to change the default value. - // Do not use nested objects, keep it single level. Prefix the key if you need to group them. - apiKey: '', - systemMessage: '', - showSystemMessage: true, - theme: ColorMode.SYSTEM, - showThoughtInProgress: true, - disableReasoningParsing: false, - excludeReasoningFromContext: false, - showRawOutputSwitch: false, - keepStatsVisible: false, - showMessageStats: true, - askForTitleConfirmation: false, - titleGenerationUseFirstLine: false, - titleGenerationUseLLM: false, - titleGenerationPrompt: TITLE.DEFAULT_PROMPT, - pasteLongTextToFileLen: 2500, - copyTextAttachmentsAsPlainText: false, - pdfAsImage: false, - disableAutoScroll: false, - renderUserContentAsMarkdown: false, - alwaysShowSidebarOnDesktop: false, - autoShowSidebarOnNewChat: true, - sendOnEnter: true, - autoMicOnEmpty: false, - fullHeightCodeBlocks: false, - showRawModelNames: false, - mcpServers: '[]', - mcpServerUsageStats: '{}', // JSON object: { [serverId]: usageCount } - agenticMaxTurns: 10, - agenticMaxToolPreviewLines: 25, - showToolCallInProgress: false, - alwaysShowAgenticTurns: false, - // sampling params: empty means "use server default" - // the server / preset is the source of truth - // empty values are shown as placeholders from /props in the UI - // and are NOT sent in API requests, letting the server decide - samplers: '', - backend_sampling: false, - temperature: undefined, - dynatemp_range: undefined, - dynatemp_exponent: undefined, - top_k: undefined, - top_p: undefined, - min_p: undefined, - xtc_probability: undefined, - xtc_threshold: undefined, - typ_p: undefined, - repeat_last_n: undefined, - repeat_penalty: undefined, - presence_penalty: undefined, - frequency_penalty: undefined, - dry_multiplier: undefined, - dry_base: undefined, - dry_allowed_length: undefined, - dry_penalty_last_n: undefined, - max_tokens: undefined, - custom: '', // custom json-stringified object - preEncodeConversation: false, - // experimental features - pyInterpreterEnabled: false, - enableContinueGeneration: false -}; - -export const SETTING_CONFIG_INFO: Record = { - apiKey: 'Set the API Key if you are using --api-key option for the server.', - systemMessage: 'The starting message that defines how model should behave.', - showSystemMessage: 'Display the system message at the top of each conversation.', - theme: - 'Choose the color theme for the interface. You can choose between System (follows your device settings), Light, or Dark.', - pasteLongTextToFileLen: - 'On pasting long text, it will be converted to a file. You can control the file length by setting the value of this parameter. Value 0 means disable.', - copyTextAttachmentsAsPlainText: - 'When copying a message with text attachments, combine them into a single plain text string instead of a special format that can be pasted back as attachments.', - samplers: - 'The order at which samplers are applied, in simplified way. Default is "top_k;typ_p;top_p;min_p;temperature": top_k->typ_p->top_p->min_p->temperature', - backend_sampling: - 'Enable backend-based samplers. When enabled, supported samplers run on the accelerator backend for faster sampling.', - temperature: - 'Controls the randomness of the generated text by affecting the probability distribution of the output tokens. Higher = more random, lower = more focused.', - dynatemp_range: - 'Addon for the temperature sampler. The added value to the range of dynamic temperature, which adjusts probabilities by entropy of tokens.', - dynatemp_exponent: - 'Addon for the temperature sampler. Smoothes out the probability redistribution based on the most probable token.', - top_k: 'Keeps only k top tokens.', - top_p: 'Limits tokens to those that together have a cumulative probability of at least p', - min_p: - 'Limits tokens based on the minimum probability for a token to be considered, relative to the probability of the most likely token.', - xtc_probability: - 'XTC sampler cuts out top tokens; this parameter controls the chance of cutting tokens at all. 0 disables XTC.', - xtc_threshold: - 'XTC sampler cuts out top tokens; this parameter controls the token probability that is required to cut that token.', - typ_p: 'Sorts and limits tokens based on the difference between log-probability and entropy.', - repeat_last_n: 'Last n tokens to consider for penalizing repetition', - repeat_penalty: 'Controls the repetition of token sequences in the generated text', - presence_penalty: 'Limits tokens based on whether they appear in the output or not.', - frequency_penalty: 'Limits tokens based on how often they appear in the output.', - dry_multiplier: - 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling multiplier.', - dry_base: - 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling base value.', - dry_allowed_length: - 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the allowed length for DRY sampling.', - dry_penalty_last_n: - 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets DRY penalty for the last n tokens.', - max_tokens: 'The maximum number of token per output. Use -1 for infinite (no limit).', - custom: 'Custom JSON parameters to send to the API. Must be valid JSON format.', - showThoughtInProgress: 'Expand thought process by default when generating messages.', - disableReasoningParsing: - 'Send reasoning_format=none so the server returns thinking tokens inline instead of extracting them into a separate field.', - excludeReasoningFromContext: - 'Strip thinking from previous messages before sending. When off, thinking is sent back via the reasoning_content field so the model sees its own chain-of-thought across turns.', - showRawOutputSwitch: - 'Show toggle button to display messages as plain text instead of Markdown-formatted content', - keepStatsVisible: 'Keep processing statistics visible after generation finishes.', - showMessageStats: - 'Display generation statistics (tokens/second, token count, duration) below each assistant message.', - askForTitleConfirmation: - 'Ask for confirmation before automatically changing conversation title when editing the first message.', - titleGenerationUseFirstLine: - 'Use only the first non-empty line of the prompt to generate the conversation title.', - titleGenerationUseLLM: - 'Use the LLM to automatically generate conversation titles based on the first message exchange.', - titleGenerationPrompt: - 'Optional template for the title generation prompt. Use {{USER}} for the user message and {{ASSISTANT}} for the assistant message.', - pdfAsImage: - 'Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.', - disableAutoScroll: - 'Disable automatic scrolling while messages stream so you can control the viewport position manually.', - renderUserContentAsMarkdown: 'Render user messages using markdown formatting in the chat.', - alwaysShowSidebarOnDesktop: - 'Always keep the sidebar visible on desktop instead of auto-hiding it.', - autoShowSidebarOnNewChat: - 'Automatically show sidebar when starting a new chat. Disable to keep the sidebar hidden until you click on it.', - sendOnEnter: - 'Use Enter to send messages and Shift + Enter for new lines. When disabled, use Ctrl/Cmd + Enter.', - autoMicOnEmpty: - 'Automatically show microphone button instead of send button when textarea is empty for models with audio modality support.', - fullHeightCodeBlocks: - 'Always display code blocks at their full natural height, overriding any height limits.', - showRawModelNames: - 'Display full raw model identifiers (e.g. "ggml-org/GLM-4.7-Flash-GGUF:Q8_0") instead of parsed names with badges.', - mcpServers: - 'Configure MCP servers as a JSON list. Use the form in the MCP Client settings section to edit.', - mcpServerUsageStats: - 'Usage statistics for MCP servers. Tracks how many times tools from each server have been used.', - agenticMaxTurns: - 'Maximum number of tool execution cycles before stopping (prevents infinite loops).', - agenticMaxToolPreviewLines: - 'Number of lines shown in tool output previews (last N lines). Only these previews and the final LLM response persist after the agentic loop completes.', - showToolCallInProgress: - 'Automatically expand tool call details while executing and keep them expanded after completion.', - pyInterpreterEnabled: - 'Enable Python interpreter using Pyodide. Allows running Python code in markdown code blocks.', - preEncodeConversation: - 'After each response, re-submit the conversation to pre-fill the server KV cache. Makes the next turn faster since the prompt is already encoded while you read the response.', - enableContinueGeneration: - 'Enable "Continue" button for assistant messages. Currently works only with non-reasoning models.' -}; - -export const SETTINGS_COLOR_MODES_CONFIG = [ - { value: ColorMode.SYSTEM, label: 'System', icon: Monitor }, - { value: ColorMode.LIGHT, label: 'Light', icon: Sun }, - { value: ColorMode.DARK, label: 'Dark', icon: Moon } -]; diff --git a/tools/server/webui/src/lib/constants/settings-fields.ts b/tools/server/webui/src/lib/constants/settings-fields.ts deleted file mode 100644 index 79a6e928708..00000000000 --- a/tools/server/webui/src/lib/constants/settings-fields.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * List of all numeric fields in settings configuration. - * These fields will be converted from strings to numbers during save. - */ -export const NUMERIC_FIELDS = [ - 'temperature', - 'top_k', - 'top_p', - 'min_p', - 'max_tokens', - 'pasteLongTextToFileLen', - 'dynatemp_range', - 'dynatemp_exponent', - 'typ_p', - 'xtc_probability', - 'xtc_threshold', - 'repeat_last_n', - 'repeat_penalty', - 'presence_penalty', - 'frequency_penalty', - 'dry_multiplier', - 'dry_base', - 'dry_allowed_length', - 'dry_penalty_last_n', - 'agenticMaxTurns', - 'agenticMaxToolPreviewLines' -] as const; - -/** - * Fields that must be positive integers (>= 1). - * These will be clamped to minimum 1 and rounded during save. - */ -export const POSITIVE_INTEGER_FIELDS = ['agenticMaxTurns', 'agenticMaxToolPreviewLines'] as const; diff --git a/tools/server/webui/src/lib/constants/settings-keys.ts b/tools/server/webui/src/lib/constants/settings-keys.ts index b0cb3fe326b..b673bff278d 100644 --- a/tools/server/webui/src/lib/constants/settings-keys.ts +++ b/tools/server/webui/src/lib/constants/settings-keys.ts @@ -28,6 +28,7 @@ export const SETTINGS_KEYS = { ALWAYS_SHOW_SIDEBAR_ON_DESKTOP: 'alwaysShowSidebarOnDesktop', FULL_HEIGHT_CODE_BLOCKS: 'fullHeightCodeBlocks', SHOW_RAW_MODEL_NAMES: 'showRawModelNames', + SHOW_SYSTEM_MESSAGE: 'showSystemMessage', // Sampling TEMPERATURE: 'temperature', DYNATEMP_RANGE: 'dynatemp_range', @@ -51,6 +52,7 @@ export const SETTINGS_KEYS = { DRY_ALLOWED_LENGTH: 'dry_allowed_length', DRY_PENALTY_LAST_N: 'dry_penalty_last_n', // MCP + MCP_SERVERS: 'mcpServers', AGENTIC_MAX_TURNS: 'agenticMaxTurns', ALWAYS_SHOW_AGENTIC_TURNS: 'alwaysShowAgenticTurns', AGENTIC_MAX_TOOL_PREVIEW_LINES: 'agenticMaxToolPreviewLines', @@ -61,5 +63,6 @@ export const SETTINGS_KEYS = { DISABLE_REASONING_PARSING: 'disableReasoningParsing', EXCLUDE_REASONING_FROM_CONTEXT: 'excludeReasoningFromContext', SHOW_RAW_OUTPUT_SWITCH: 'showRawOutputSwitch', + // PY_INTERPRETER_ENABLED: 'pyInterpreterEnabled', CUSTOM: 'custom' } as const; diff --git a/tools/server/webui/src/lib/constants/settings-registry.ts b/tools/server/webui/src/lib/constants/settings-registry.ts new file mode 100644 index 00000000000..9c7cc2bf69c --- /dev/null +++ b/tools/server/webui/src/lib/constants/settings-registry.ts @@ -0,0 +1,719 @@ +import { ColorMode } from '$lib/enums/ui'; +import { SettingsFieldType } from '$lib/enums/settings'; +import { SyncableParameterType } from '$lib/enums'; +import { + Funnel, + AlertTriangle, + Code, + Monitor, + ListRestart, + Sliders, + PencilRuler, + Database, + Monitor as MonitorIcon, + Sun, + Moon +} from '@lucide/svelte'; +import type { Component } from 'svelte'; +import type { + SettingsConfigValue, + SyncableParameter, + SettingsEntry, + SettingsSectionTitle, + SettingsSectionEntry, + SettingsSection +} from '$lib/types'; +import { SETTINGS_KEYS } from './settings-keys'; +import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes'; +import { TITLE_GENERATION } from './title-generation'; + +export const SETTINGS_SECTION_TITLES = { + GENERAL: 'General', + DISPLAY: 'Display', + SAMPLING: 'Sampling', + PENALTIES: 'Penalties', + AGENTIC: 'Agentic', + TOOLS: 'Tools', + IMPORT_EXPORT: 'Import/Export', + DEVELOPER: 'Developer' +} as const; + +const STANDALONE_SECTIONS: { title: SettingsSectionTitle; slug: string; icon: Component }[] = [ + { title: SETTINGS_SECTION_TITLES.TOOLS, slug: SETTINGS_SECTION_SLUGS.TOOLS, icon: PencilRuler }, + { + title: SETTINGS_SECTION_TITLES.IMPORT_EXPORT, + slug: SETTINGS_SECTION_SLUGS.IMPORT_EXPORT, + icon: Database + } +]; + +const COLOR_MODE_OPTIONS: Array<{ value: string; label: string; icon: Component }> = [ + { value: ColorMode.SYSTEM, label: 'System', icon: MonitorIcon }, + { value: ColorMode.LIGHT, label: 'Light', icon: Sun }, + { value: ColorMode.DARK, label: 'Dark', icon: Moon } +]; + +const SETTINGS_REGISTRY: Record = { + [SETTINGS_SECTION_SLUGS.GENERAL]: { + title: SETTINGS_SECTION_TITLES.GENERAL, + slug: SETTINGS_SECTION_SLUGS.GENERAL, + icon: Sliders, + settings: [ + { + key: SETTINGS_KEYS.THEME, + label: 'Theme', + help: 'Choose the color theme for the interface. You can choose between System (follows your device settings), Light, or Dark.', + defaultValue: ColorMode.SYSTEM, + type: SettingsFieldType.SELECT, + section: SETTINGS_SECTION_SLUGS.GENERAL, + options: COLOR_MODE_OPTIONS, + sync: { serverKey: SETTINGS_KEYS.THEME, paramType: SyncableParameterType.STRING } + }, + { + key: SETTINGS_KEYS.API_KEY, + label: 'API Key', + help: 'Set the API Key if you are using --api-key option for the server.', + defaultValue: '', + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.GENERAL + }, + { + key: SETTINGS_KEYS.SYSTEM_MESSAGE, + label: 'System Message', + help: 'The starting message that defines how model should behave.', + defaultValue: '', + type: SettingsFieldType.TEXTAREA, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { serverKey: SETTINGS_KEYS.SYSTEM_MESSAGE, paramType: SyncableParameterType.STRING } + }, + { + key: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN, + label: 'Paste long text to file length', + help: 'On pasting long text, it will be converted to a file. You can control the file length by setting the value of this parameter. Value 0 means disable.', + defaultValue: 2500, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { + serverKey: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN, + paramType: SyncableParameterType.NUMBER + } + }, + { + key: SETTINGS_KEYS.SEND_ON_ENTER, + label: 'Send message on Enter', + help: 'Use Enter to send messages and Shift + Enter for new lines. When disabled, use Ctrl/Cmd + Enter.', + defaultValue: true, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { serverKey: SETTINGS_KEYS.SEND_ON_ENTER, paramType: SyncableParameterType.BOOLEAN } + }, + { + key: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT, + label: 'Copy text attachments as plain text', + help: 'When copying a message with text attachments, combine them into a single plain text string instead of a special format that can be pasted back as attachments.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { + serverKey: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION, + label: 'Enable "Continue" button', + help: 'Enable "Continue" button for assistant messages. Currently works only with non-reasoning models.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + isExperimental: true, + sync: { + serverKey: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.PDF_AS_IMAGE, + label: 'Parse PDF as image', + help: 'Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { serverKey: SETTINGS_KEYS.PDF_AS_IMAGE, paramType: SyncableParameterType.BOOLEAN } + }, + { + key: SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION, + label: 'Ask for confirmation before changing conversation title', + help: 'Ask for confirmation before automatically changing conversation title when editing the first message.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { + serverKey: SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE, + label: 'Use first non-empty line for conversation title', + help: 'Use only the first non-empty line of the prompt to generate the conversation title.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + sync: { + serverKey: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM, + label: 'Use LLM to generate conversation title', + help: 'Use the LLM to automatically generate conversation titles based on the first message exchange.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.GENERAL, + isExperimental: true + }, + { + key: SETTINGS_KEYS.TITLE_GENERATION_PROMPT, + label: 'LLM title generation prompt', + help: 'Optional template for the title generation prompt. Use {{USER}} for the user message and {{ASSISTANT}} for the assistant message.', + defaultValue: TITLE_GENERATION.DEFAULT_PROMPT, + type: SettingsFieldType.TEXTAREA, + section: SETTINGS_SECTION_SLUGS.GENERAL + } + ] + }, + [SETTINGS_SECTION_SLUGS.DISPLAY]: { + title: SETTINGS_SECTION_TITLES.DISPLAY, + slug: SETTINGS_SECTION_SLUGS.DISPLAY, + icon: Monitor, + settings: [ + { + key: SETTINGS_KEYS.SHOW_MESSAGE_STATS, + label: 'Show message generation statistics', + help: 'Display generation statistics (tokens/second, token count, duration) below each assistant message.', + defaultValue: true, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.SHOW_MESSAGE_STATS, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS, + label: 'Show thought in progress', + help: 'Expand thought process by default when generating messages.', + defaultValue: true, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS, + label: 'Show tool call in progress', + help: 'Automatically expand tool call details while executing and keep them expanded after completion.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.KEEP_STATS_VISIBLE, + label: 'Keep stats visible after generation', + help: 'Keep processing statistics visible after generation finishes.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.KEEP_STATS_VISIBLE, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY, + label: 'Show microphone on empty input', + help: 'Automatically show microphone button instead of send button when textarea is empty for models with audio modality support.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + isExperimental: true, + sync: { + serverKey: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN, + label: 'Render user content as Markdown', + help: 'Render user messages using markdown formatting in the chat.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS, + label: 'Use full height code blocks', + help: 'Always display code blocks at their full natural height, overriding any height limits.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.DISABLE_AUTO_SCROLL, + label: 'Disable automatic scroll', + help: 'Disable automatic scrolling while messages stream so you can control the viewport position manually.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.DISABLE_AUTO_SCROLL, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP, + label: 'Always show sidebar on desktop', + help: 'Always keep the sidebar visible on desktop instead of auto-hiding it.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES, + label: 'Show raw model names', + help: 'Display full raw model identifiers (e.g. "ggml-org/GLM-4.7-Flash-GGUF:Q8_0") instead of parsed names with badges.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS, + label: 'Always show agentic turns in conversation', + help: 'Always expand and display agentic loop turns in conversation messages.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DISPLAY, + sync: { + serverKey: SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS, + paramType: SyncableParameterType.BOOLEAN + } + } + ] + }, + [SETTINGS_SECTION_SLUGS.SAMPLING]: { + title: SETTINGS_SECTION_TITLES.SAMPLING, + slug: SETTINGS_SECTION_SLUGS.SAMPLING, + icon: Funnel, + settings: [ + { + key: SETTINGS_KEYS.TEMPERATURE, + label: 'Temperature', + help: 'Controls the randomness of the generated text by affecting the probability distribution of the output tokens. Higher = more random, lower = more focused.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.TEMPERATURE, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.DYNATEMP_RANGE, + label: 'Dynamic temperature range', + help: 'Addon for the temperature sampler. The added value to the range of dynamic temperature, which adjusts probabilities by entropy of tokens.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.DYNATEMP_RANGE, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.DYNATEMP_EXPONENT, + label: 'Dynamic temperature exponent', + help: 'Addon for the temperature sampler. Smoothes out the probability redistribution based on the most probable token.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { + serverKey: SETTINGS_KEYS.DYNATEMP_EXPONENT, + paramType: SyncableParameterType.NUMBER + } + }, + { + key: SETTINGS_KEYS.TOP_K, + label: 'Top K', + help: 'Keeps only k top tokens.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.TOP_K, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.TOP_P, + label: 'Top P', + help: 'Limits tokens to those that together have a cumulative probability of at least p', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.TOP_P, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.MIN_P, + label: 'Min P', + help: 'Limits tokens based on the minimum probability for a token to be considered, relative to the probability of the most likely token.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.MIN_P, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.XTC_PROBABILITY, + label: 'XTC probability', + help: 'XTC sampler cuts out top tokens; this parameter controls the chance of cutting tokens at all. 0 disables XTC.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.XTC_PROBABILITY, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.XTC_THRESHOLD, + label: 'XTC threshold', + help: 'XTC sampler cuts out top tokens; this parameter controls the token probability that is required to cut that token.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.XTC_THRESHOLD, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.TYP_P, + label: 'Typical P', + help: 'Sorts and limits tokens based on the difference between log-probability and entropy.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.TYP_P, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.MAX_TOKENS, + label: 'Max tokens', + help: 'The maximum number of token per output. Use -1 for infinite (no limit).', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.MAX_TOKENS, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.SAMPLERS, + label: 'Samplers', + help: 'The order at which samplers are applied, in simplified way. Default is "top_k;typ_p;top_p;min_p;temperature": top_k->typ_p->top_p->min_p->temperature', + defaultValue: '', + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { serverKey: SETTINGS_KEYS.SAMPLERS, paramType: SyncableParameterType.STRING } + }, + { + key: SETTINGS_KEYS.BACKEND_SAMPLING, + label: 'Backend sampling', + help: 'Enable backend-based samplers. When enabled, supported samplers run on the accelerator backend for faster sampling.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.SAMPLING, + sync: { + serverKey: SETTINGS_KEYS.BACKEND_SAMPLING, + paramType: SyncableParameterType.BOOLEAN + } + } + ] + }, + [SETTINGS_SECTION_SLUGS.PENALTIES]: { + title: SETTINGS_SECTION_TITLES.PENALTIES, + slug: SETTINGS_SECTION_SLUGS.PENALTIES, + icon: AlertTriangle, + settings: [ + { + key: SETTINGS_KEYS.REPEAT_LAST_N, + label: 'Repeat last N', + help: 'Last n tokens to consider for penalizing repetition', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { serverKey: SETTINGS_KEYS.REPEAT_LAST_N, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.REPEAT_PENALTY, + label: 'Repeat penalty', + help: 'Controls the repetition of token sequences in the generated text', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { serverKey: SETTINGS_KEYS.REPEAT_PENALTY, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.PRESENCE_PENALTY, + label: 'Presence penalty', + help: 'Limits tokens based on whether they appear in the output or not.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { serverKey: SETTINGS_KEYS.PRESENCE_PENALTY, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.FREQUENCY_PENALTY, + label: 'Frequency penalty', + help: 'Limits tokens based on how often they appear in the output.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { + serverKey: SETTINGS_KEYS.FREQUENCY_PENALTY, + paramType: SyncableParameterType.NUMBER + } + }, + { + key: SETTINGS_KEYS.DRY_MULTIPLIER, + label: 'DRY multiplier', + help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling multiplier.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { serverKey: SETTINGS_KEYS.DRY_MULTIPLIER, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.DRY_BASE, + label: 'DRY base', + help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling base value.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { serverKey: SETTINGS_KEYS.DRY_BASE, paramType: SyncableParameterType.NUMBER } + }, + { + key: SETTINGS_KEYS.DRY_ALLOWED_LENGTH, + label: 'DRY allowed length', + help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the allowed length for DRY sampling.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { + serverKey: SETTINGS_KEYS.DRY_ALLOWED_LENGTH, + paramType: SyncableParameterType.NUMBER + } + }, + { + key: SETTINGS_KEYS.DRY_PENALTY_LAST_N, + label: 'DRY penalty last N', + help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets DRY penalty for the last n tokens.', + defaultValue: undefined, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.PENALTIES, + sync: { + serverKey: SETTINGS_KEYS.DRY_PENALTY_LAST_N, + paramType: SyncableParameterType.NUMBER + } + } + ] + }, + [SETTINGS_SECTION_SLUGS.AGENTIC]: { + title: SETTINGS_SECTION_TITLES.AGENTIC, + slug: SETTINGS_SECTION_SLUGS.AGENTIC, + icon: ListRestart, + settings: [ + { + key: SETTINGS_KEYS.AGENTIC_MAX_TURNS, + label: 'Agentic turns', + help: 'Maximum number of tool execution cycles before stopping (prevents infinite loops).', + defaultValue: 10, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.AGENTIC, + isPositiveInteger: true, + sync: { + serverKey: SETTINGS_KEYS.AGENTIC_MAX_TURNS, + paramType: SyncableParameterType.NUMBER + } + }, + { + key: SETTINGS_KEYS.AGENTIC_MAX_TOOL_PREVIEW_LINES, + label: 'Max lines per tool preview', + help: 'Number of lines shown in tool output previews (last N lines). Only these previews and the final LLM response persist after the agentic loop completes.', + defaultValue: 25, + type: SettingsFieldType.INPUT, + section: SETTINGS_SECTION_SLUGS.AGENTIC, + isPositiveInteger: true, + sync: { + serverKey: SETTINGS_KEYS.AGENTIC_MAX_TOOL_PREVIEW_LINES, + paramType: SyncableParameterType.NUMBER + } + } + ] + }, + [SETTINGS_SECTION_SLUGS.DEVELOPER]: { + title: SETTINGS_SECTION_TITLES.DEVELOPER, + slug: SETTINGS_SECTION_SLUGS.DEVELOPER, + icon: Code, + settings: [ + { + key: SETTINGS_KEYS.PRE_ENCODE_CONVERSATION, + label: 'Pre-fill KV cache after response', + help: 'After each response, re-submit the conversation to pre-fill the server KV cache. Makes the next turn faster since the prompt is already encoded while you read the response.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DEVELOPER + }, + { + key: SETTINGS_KEYS.DISABLE_REASONING_PARSING, + label: 'Disable reasoning content parsing', + help: 'Send reasoning_format=none so the server returns thinking tokens inline instead of extracting them into a separate field.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DEVELOPER + }, + { + key: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT, + label: 'Exclude reasoning from context', + help: 'Strip thinking from previous messages before sending. When off, thinking is sent back via the reasoning_content field so the model sees its own chain-of-thought across turns.', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DEVELOPER, + sync: { + serverKey: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH, + label: 'Enable raw output toggle', + help: 'Show toggle button to display messages as plain text instead of Markdown-formatted content', + defaultValue: false, + type: SettingsFieldType.CHECKBOX, + section: SETTINGS_SECTION_SLUGS.DEVELOPER, + sync: { + serverKey: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH, + paramType: SyncableParameterType.BOOLEAN + } + }, + { + key: SETTINGS_KEYS.CUSTOM, + label: 'Custom JSON', + help: 'Custom JSON parameters to send to the API. Must be valid JSON format.', + defaultValue: '', + type: SettingsFieldType.TEXTAREA, + section: SETTINGS_SECTION_SLUGS.DEVELOPER + } + ] + } +} as const; + +const NON_UI_SETTINGS: SettingsEntry[] = [ + { + key: SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE, + label: 'Show system message', + help: 'Display the system message at the top of each conversation.', + defaultValue: true, + type: SettingsFieldType.CHECKBOX, + sync: { serverKey: SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE, paramType: SyncableParameterType.BOOLEAN } + }, + { + key: SETTINGS_KEYS.MCP_SERVERS, + label: 'MCP servers', + help: 'Configure MCP servers as a JSON list. Use the form in the MCP Client settings section to edit.', + defaultValue: '[]', + type: SettingsFieldType.INPUT, + sync: { serverKey: SETTINGS_KEYS.MCP_SERVERS, paramType: SyncableParameterType.STRING } + } + // { + // key: SETTINGS_KEYS.PY_INTERPRETER_ENABLED, + // label: 'Python interpreter enabled', + // help: 'Enable Python interpreter using Pyodide. Allows running Python code in markdown code blocks.', + // defaultValue: false, + // type: SettingsFieldType.CHECKBOX, + // isExperimental: true, + // sync: { serverKey: SETTINGS_KEYS.PY_INTERPRETER_ENABLED, paramType: SyncableParameterType.BOOLEAN } + // } +]; + +function getAllSettings(): SettingsEntry[] { + const result: SettingsEntry[] = []; + for (const section of Object.values(SETTINGS_REGISTRY)) { + result.push(...section.settings); + } + result.push(...NON_UI_SETTINGS); + return result; +} + +/** Flat config object stored in localStorage. */ +export const SETTING_CONFIG_DEFAULT: Record = Object.fromEntries( + getAllSettings().map((s) => [s.key, s.defaultValue]) +) as Record; + +/** Help text for every setting (including non-UI). */ +export const SETTING_CONFIG_INFO: Record = Object.fromEntries( + getAllSettings().map((s) => [s.key, s.help]) +) as Record; + +/** Theme select options. */ +export const SETTINGS_COLOR_MODES_CONFIG = COLOR_MODE_OPTIONS; + +export type { SettingsSectionTitle } from '$lib/types'; +export type { SettingsSection } from '$lib/types'; + +/** Sidebar sections + field configs (as consumed by UI). */ +export const SETTINGS_CHAT_SECTIONS: SettingsSection[] = [ + ...Object.values(SETTINGS_REGISTRY).map((section) => ({ + title: section.title, + slug: section.slug, + icon: section.icon, + fields: section.settings.map((s) => ({ + key: s.key, + label: s.label, + type: s.type, + isExperimental: s.isExperimental, + help: s.help, + options: s.options + })) + })), + ...STANDALONE_SECTIONS +]; + +/** INPUT-type settings whose value is a number. */ +export const NUMERIC_FIELDS = getAllSettings() + .filter((s) => s.type === SettingsFieldType.INPUT && typeof s.defaultValue !== 'string') + .map((s) => s.key) as readonly string[]; + +/** Numeric fields clamped to ≥ 1 and rounded. */ +export const POSITIVE_INTEGER_FIELDS = getAllSettings() + .filter((s) => s.isPositiveInteger) + .map((s) => s.key) as readonly string[]; + +/** Derived for the parameter sync service. */ +export const SYNCABLE_PARAMETERS: SyncableParameter[] = getAllSettings() + .filter((s) => s.sync !== undefined) + .map((s) => ({ + key: s.key, + serverKey: s.sync!.serverKey, + type: s.sync!.paramType, + canSync: true + })); + +export const SETTINGS_FALLBACK_EXIT_ROUTE = ROUTES.START; + +export { SETTINGS_KEYS } from './settings-keys'; diff --git a/tools/server/webui/src/lib/constants/settings-sections.ts b/tools/server/webui/src/lib/constants/settings-sections.ts deleted file mode 100644 index 26025032c89..00000000000 --- a/tools/server/webui/src/lib/constants/settings-sections.ts +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Settings section titles constants for ChatSettings component. - * - * These titles define the navigation sections in the settings dialog. - * Used for both sidebar navigation and mobile horizontal scroll menu. - */ -export const SETTINGS_SECTION_TITLES = { - GENERAL: 'General', - DISPLAY: 'Display', - SAMPLING: 'Sampling', - PENALTIES: 'Penalties', - AGENTIC: 'Agentic', - TOOLS: 'Tools', - MCP: 'MCP', - IMPORT_EXPORT: 'Import/Export', - DEVELOPER: 'Developer' -} as const; - -/** Type for settings section titles */ -export type SettingsSectionTitle = - (typeof SETTINGS_SECTION_TITLES)[keyof typeof SETTINGS_SECTION_TITLES]; - -import { - Funnel, - AlertTriangle, - Code, - Monitor, - ListRestart, - Sliders, - PencilRuler, - Database -} from '@lucide/svelte'; -import { SettingsFieldType } from '$lib/enums/settings'; -import { SETTINGS_COLOR_MODES_CONFIG } from '$lib/constants/settings-config'; -import { SETTINGS_KEYS } from '$lib/constants/settings-keys'; -import type { Component } from 'svelte'; - -export interface SettingsSection { - fields?: SettingsFieldConfig[]; - icon: Component; - slug: string; - title: SettingsSectionTitle; -} - -export const SETTINGS_CHAT_SECTIONS: SettingsSection[] = [ - { - title: SETTINGS_SECTION_TITLES.GENERAL, - slug: 'general', - icon: Sliders, - fields: [ - { - key: SETTINGS_KEYS.THEME, - label: 'Theme', - type: SettingsFieldType.SELECT, - options: SETTINGS_COLOR_MODES_CONFIG - }, - { key: SETTINGS_KEYS.API_KEY, label: 'API Key', type: SettingsFieldType.INPUT }, - { - key: SETTINGS_KEYS.SYSTEM_MESSAGE, - label: 'System Message', - type: SettingsFieldType.TEXTAREA - }, - { - key: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN, - label: 'Paste long text to file length', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.SEND_ON_ENTER, - label: 'Send message on Enter', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT, - label: 'Copy text attachments as plain text', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION, - label: 'Enable "Continue" button', - type: SettingsFieldType.CHECKBOX, - isExperimental: true - }, - { - key: SETTINGS_KEYS.PDF_AS_IMAGE, - label: 'Parse PDF as image', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION, - label: 'Ask for confirmation before changing conversation title', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE, - label: 'Use first non-empty line for conversation title', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM, - label: 'Use LLM to generate conversation title', - type: SettingsFieldType.CHECKBOX, - isExperimental: true - }, - { - key: SETTINGS_KEYS.TITLE_GENERATION_PROMPT, - type: SettingsFieldType.TEXTAREA, - isExperimental: true - } - ] - }, - { - title: SETTINGS_SECTION_TITLES.DISPLAY, - slug: 'display', - icon: Monitor, - fields: [ - { - key: SETTINGS_KEYS.SHOW_MESSAGE_STATS, - label: 'Show message generation statistics', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS, - label: 'Show thought in progress', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS, - label: 'Show tool call in progress', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.KEEP_STATS_VISIBLE, - label: 'Keep stats visible after generation', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY, - label: 'Show microphone on empty input', - type: SettingsFieldType.CHECKBOX, - isExperimental: true - }, - { - key: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN, - label: 'Render user content as Markdown', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS, - label: 'Use full height code blocks', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.DISABLE_AUTO_SCROLL, - label: 'Disable automatic scroll', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP, - label: 'Always show sidebar on desktop', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES, - label: 'Show raw model names', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS, - label: 'Always show agentic turns in conversation', - type: SettingsFieldType.CHECKBOX - } - ] - }, - { - title: SETTINGS_SECTION_TITLES.SAMPLING, - slug: 'sampling', - icon: Funnel, - fields: [ - { - key: SETTINGS_KEYS.TEMPERATURE, - label: 'Temperature', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DYNATEMP_RANGE, - label: 'Dynamic temperature range', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DYNATEMP_EXPONENT, - label: 'Dynamic temperature exponent', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.TOP_K, - label: 'Top K', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.TOP_P, - label: 'Top P', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.MIN_P, - label: 'Min P', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.XTC_PROBABILITY, - label: 'XTC probability', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.XTC_THRESHOLD, - label: 'XTC threshold', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.TYP_P, - label: 'Typical P', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.MAX_TOKENS, - label: 'Max tokens', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.SAMPLERS, - label: 'Samplers', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.BACKEND_SAMPLING, - label: 'Backend sampling', - type: SettingsFieldType.CHECKBOX - } - ] - }, - { - title: SETTINGS_SECTION_TITLES.PENALTIES, - slug: 'penalties', - icon: AlertTriangle, - fields: [ - { - key: SETTINGS_KEYS.REPEAT_LAST_N, - label: 'Repeat last N', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.REPEAT_PENALTY, - label: 'Repeat penalty', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.PRESENCE_PENALTY, - label: 'Presence penalty', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.FREQUENCY_PENALTY, - label: 'Frequency penalty', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DRY_MULTIPLIER, - label: 'DRY multiplier', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DRY_BASE, - label: 'DRY base', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DRY_ALLOWED_LENGTH, - label: 'DRY allowed length', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.DRY_PENALTY_LAST_N, - label: 'DRY penalty last N', - type: SettingsFieldType.INPUT - } - ] - }, - { - title: SETTINGS_SECTION_TITLES.AGENTIC, - slug: 'agentic', - icon: ListRestart, - fields: [ - { - key: SETTINGS_KEYS.AGENTIC_MAX_TURNS, - label: 'Agentic turns', - type: SettingsFieldType.INPUT - }, - { - key: SETTINGS_KEYS.AGENTIC_MAX_TOOL_PREVIEW_LINES, - label: 'Max lines per tool preview', - type: SettingsFieldType.INPUT - } - ] - }, - { - title: SETTINGS_SECTION_TITLES.TOOLS, - slug: 'tools', - icon: PencilRuler - }, - { - title: SETTINGS_SECTION_TITLES.IMPORT_EXPORT, - slug: 'import-export', - icon: Database - }, - { - title: SETTINGS_SECTION_TITLES.DEVELOPER, - slug: 'developer', - icon: Code, - fields: [ - { - key: SETTINGS_KEYS.PRE_ENCODE_CONVERSATION, - label: 'Pre-fill KV cache after response', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.DISABLE_REASONING_PARSING, - label: 'Disable reasoning content parsing', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT, - label: 'Exclude reasoning from context', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH, - label: 'Enable raw output toggle', - type: SettingsFieldType.CHECKBOX - }, - { - key: SETTINGS_KEYS.CUSTOM, - label: 'Custom JSON', - type: SettingsFieldType.TEXTAREA - } - ] - } -]; diff --git a/tools/server/webui/src/lib/constants/title-generation.ts b/tools/server/webui/src/lib/constants/title-generation.ts index 6a1c876e689..48ca2217a27 100644 --- a/tools/server/webui/src/lib/constants/title-generation.ts +++ b/tools/server/webui/src/lib/constants/title-generation.ts @@ -1,5 +1,5 @@ /* Title generation constants */ -export const TITLE = { +export const TITLE_GENERATION = { MIN_LENGTH: 3, FALLBACK: 'New Chat', DEFAULT_PROMPT: diff --git a/tools/server/webui/src/lib/constants/ui.ts b/tools/server/webui/src/lib/constants/ui.ts index 59a0a7fbddc..5f3c532dbd1 100644 --- a/tools/server/webui/src/lib/constants/ui.ts +++ b/tools/server/webui/src/lib/constants/ui.ts @@ -1,6 +1,7 @@ import { Settings, Search, SquarePen } from '@lucide/svelte'; import McpLogo from '$lib/components/app/mcp/McpLogo.svelte'; import type { Component } from 'svelte'; +import { ROUTES } from './routes'; export const FORK_TREE_DEPTH_PADDING = 8; export const SYSTEM_MESSAGE_PLACEHOLDER = 'System message'; @@ -19,18 +20,18 @@ export interface DesktopIconStripItem { } export const SIDEBAR_ACTIONS_ITEMS: DesktopIconStripItem[] = [ - { icon: SquarePen, tooltip: 'New chat', route: '?new_chat=true#/', keys: ['shift', 'cmd', 'o'] }, + { icon: SquarePen, tooltip: 'New chat', route: ROUTES.NEW_CHAT, keys: ['shift', 'cmd', 'o'] }, { icon: Search, tooltip: 'Search', keys: ['cmd', 'k'] }, { icon: McpLogo, tooltip: 'MCP Servers', - route: '#/settings/mcp', - activeRouteId: '/settings/mcp' + route: ROUTES.MCP_SERVERS, + activeRouteId: '/mcp-servers' }, { icon: Settings, tooltip: 'Settings', - route: '#/settings/chat/general', - activeRoutePrefix: '/settings/chat' + route: ROUTES.SETTINGS, + activeRoutePrefix: '/settings' } ]; diff --git a/tools/server/webui/src/lib/hooks/use-keyboard-shortcuts.svelte.ts b/tools/server/webui/src/lib/hooks/use-keyboard-shortcuts.svelte.ts index 3e93592e61e..05966a1a1d1 100644 --- a/tools/server/webui/src/lib/hooks/use-keyboard-shortcuts.svelte.ts +++ b/tools/server/webui/src/lib/hooks/use-keyboard-shortcuts.svelte.ts @@ -1,5 +1,6 @@ import { goto } from '$app/navigation'; import { KeyboardKey } from '$lib/enums'; +import { ROUTES } from '$lib/constants/routes'; interface KeyboardShortcutsCallbacks { activateSearchMode?: () => void; @@ -27,7 +28,7 @@ export function useKeyboardShortcuts(callbacks: KeyboardShortcutsCallbacks) { ) { event.preventDefault(); - goto('?new_chat=true#/'); + goto(ROUTES.NEW_CHAT); } if (event.shiftKey && isCmdOrCtrl && event.key === KeyboardKey.E_UPPER) { diff --git a/tools/server/webui/src/lib/hooks/use-settings-navigation.svelte.ts b/tools/server/webui/src/lib/hooks/use-settings-navigation.svelte.ts index decac5f7fca..3cbcaaeda5f 100644 --- a/tools/server/webui/src/lib/hooks/use-settings-navigation.svelte.ts +++ b/tools/server/webui/src/lib/hooks/use-settings-navigation.svelte.ts @@ -1,6 +1,7 @@ import { page } from '$app/state'; import { beforeNavigate } from '$app/navigation'; import { settingsReferrer } from '$lib/stores/settings-referrer.svelte'; +import { ROUTES } from '$lib/constants/routes'; export interface ChatSettings { reset: () => void; @@ -15,11 +16,8 @@ export function useSettingsNavigation() { const isSettingsRoute = $derived(!!page.route.id?.startsWith('/settings')); beforeNavigate(({ to, from }) => { - if ( - to?.route?.id?.startsWith('/settings/chat') && - !from?.route?.id?.startsWith('/settings/chat') - ) { - settingsReferrer.url = window.location.hash || '#/'; + if (to?.route?.id?.startsWith('/settings') && !from?.route?.id?.startsWith('/settings')) { + settingsReferrer.url = window.location.hash || ROUTES.START; } }); diff --git a/tools/server/webui/src/lib/services/chat.service.ts b/tools/server/webui/src/lib/services/chat.service.ts index 0183acee591..a26de0d5d6e 100644 --- a/tools/server/webui/src/lib/services/chat.service.ts +++ b/tools/server/webui/src/lib/services/chat.service.ts @@ -513,7 +513,7 @@ export class ChatService { const serializedToolCalls = JSON.stringify(aggregatedToolCalls); - if (import.meta.env.DEV) { + if (import.meta.env.DEV && import.meta.env.VITE_DEBUG) { console.log('[ChatService] Aggregated tool calls:', serializedToolCalls); } diff --git a/tools/server/webui/src/lib/services/index.ts b/tools/server/webui/src/lib/services/index.ts index 41382c4c19e..edfcd6b817a 100644 --- a/tools/server/webui/src/lib/services/index.ts +++ b/tools/server/webui/src/lib/services/index.ts @@ -260,3 +260,26 @@ export { ParameterSyncService } from './parameter-sync.service'; * @see MCP Protocol Specification: https://modelcontextprotocol.io/specification/2025-06-18 */ export { MCPService } from './mcp.service'; + +/** + * **RouterService** — Dynamic route URL construction utility + * + * Stateless utility for building dynamic route URLs from ROUTES base paths. + * Static routes (START, NEW_CHAT, MCP_SERVERS) live in ROUTES constants; + * dynamic routes (CHAT, SETTINGS) are constructed here by appending parameters. + * + * **Architecture & Relationships:** + * - **RouterService** (this class): Stateless URL construction + * - Builds dynamic route URLs from ROUTES base paths + * - No side effects — receives route parameters, returns route strings + * + * - **ROUTES constant** (constants/routes.ts): Static route base paths + * - **All components/stores**: Call RouterService for dynamic route URLs + * + * **Key Responsibilities:** + * - Build chat URLs for specific conversations: `RouterService.chat(id)` → `#/chat/:id` + * - Build settings URLs for sections: `RouterService.settings(section)` → `#/settings/:section` + * + * @see ROUTES in constants/routes.ts — static route base paths + */ +export { RouterService } from './router.service'; diff --git a/tools/server/webui/src/lib/services/parameter-sync.service.ts b/tools/server/webui/src/lib/services/parameter-sync.service.ts index 27742c28c67..842aff7f928 100644 --- a/tools/server/webui/src/lib/services/parameter-sync.service.ts +++ b/tools/server/webui/src/lib/services/parameter-sync.service.ts @@ -1,253 +1,8 @@ import { normalizeFloatingPoint } from '$lib/utils'; -import type { SyncableParameter, ParameterRecord, ParameterInfo, ParameterValue } from '$lib/types'; +import { SETTINGS_KEYS, SYNCABLE_PARAMETERS } from '$lib/constants'; +import type { ParameterRecord, ParameterInfo, ParameterValue } from '$lib/types'; import { SyncableParameterType, ParameterSource } from '$lib/enums'; -/** - * Mapping of webui setting keys to server parameter keys. - * Only parameters listed here can be synced from the server `/props` endpoint. - * Each entry defines the webui key, corresponding server key, value type, - * and whether sync is enabled. - */ -export const SYNCABLE_PARAMETERS: SyncableParameter[] = [ - { - key: 'temperature', - serverKey: 'temperature', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { key: 'top_k', serverKey: 'top_k', type: SyncableParameterType.NUMBER, canSync: true }, - { key: 'top_p', serverKey: 'top_p', type: SyncableParameterType.NUMBER, canSync: true }, - { key: 'min_p', serverKey: 'min_p', type: SyncableParameterType.NUMBER, canSync: true }, - { - key: 'dynatemp_range', - serverKey: 'dynatemp_range', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'dynatemp_exponent', - serverKey: 'dynatemp_exponent', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'xtc_probability', - serverKey: 'xtc_probability', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'xtc_threshold', - serverKey: 'xtc_threshold', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { key: 'typ_p', serverKey: 'typ_p', type: SyncableParameterType.NUMBER, canSync: true }, - { - key: 'repeat_last_n', - serverKey: 'repeat_last_n', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'repeat_penalty', - serverKey: 'repeat_penalty', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'presence_penalty', - serverKey: 'presence_penalty', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'frequency_penalty', - serverKey: 'frequency_penalty', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'dry_multiplier', - serverKey: 'dry_multiplier', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { key: 'dry_base', serverKey: 'dry_base', type: SyncableParameterType.NUMBER, canSync: true }, - { - key: 'dry_allowed_length', - serverKey: 'dry_allowed_length', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'dry_penalty_last_n', - serverKey: 'dry_penalty_last_n', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { key: 'max_tokens', serverKey: 'max_tokens', type: SyncableParameterType.NUMBER, canSync: true }, - { key: 'samplers', serverKey: 'samplers', type: SyncableParameterType.STRING, canSync: true }, - { - key: 'backend_sampling', - serverKey: 'backend_sampling', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'pasteLongTextToFileLen', - serverKey: 'pasteLongTextToFileLen', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'pdfAsImage', - serverKey: 'pdfAsImage', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'showThoughtInProgress', - serverKey: 'showThoughtInProgress', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'keepStatsVisible', - serverKey: 'keepStatsVisible', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'showMessageStats', - serverKey: 'showMessageStats', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'askForTitleConfirmation', - serverKey: 'askForTitleConfirmation', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'titleGenerationUseFirstLine', - serverKey: 'titleGenerationUseFirstLine', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'disableAutoScroll', - serverKey: 'disableAutoScroll', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'renderUserContentAsMarkdown', - serverKey: 'renderUserContentAsMarkdown', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'autoMicOnEmpty', - serverKey: 'autoMicOnEmpty', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'pyInterpreterEnabled', - serverKey: 'pyInterpreterEnabled', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'enableContinueGeneration', - serverKey: 'enableContinueGeneration', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'fullHeightCodeBlocks', - serverKey: 'fullHeightCodeBlocks', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'systemMessage', - serverKey: 'systemMessage', - type: SyncableParameterType.STRING, - canSync: true - }, - { - key: 'showSystemMessage', - serverKey: 'showSystemMessage', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { key: 'theme', serverKey: 'theme', type: SyncableParameterType.STRING, canSync: true }, - { - key: 'copyTextAttachmentsAsPlainText', - serverKey: 'copyTextAttachmentsAsPlainText', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'showRawOutputSwitch', - serverKey: 'showRawOutputSwitch', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'alwaysShowSidebarOnDesktop', - serverKey: 'alwaysShowSidebarOnDesktop', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'showRawModelNames', - serverKey: 'showRawModelNames', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { key: 'mcpServers', serverKey: 'mcpServers', type: SyncableParameterType.STRING, canSync: true }, - { - key: 'agenticMaxTurns', - serverKey: 'agenticMaxTurns', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'agenticMaxToolPreviewLines', - serverKey: 'agenticMaxToolPreviewLines', - type: SyncableParameterType.NUMBER, - canSync: true - }, - { - key: 'showToolCallInProgress', - serverKey: 'showToolCallInProgress', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'alwaysShowAgenticTurns', - serverKey: 'alwaysShowAgenticTurns', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'excludeReasoningFromContext', - serverKey: 'excludeReasoningFromContext', - type: SyncableParameterType.BOOLEAN, - canSync: true - }, - { - key: 'sendOnEnter', - serverKey: 'sendOnEnter', - type: SyncableParameterType.BOOLEAN, - canSync: true - } -]; - export class ParameterSyncService { /** * @@ -298,7 +53,7 @@ export class ParameterSyncService { // Handle samplers array conversion to string if (serverParams.samplers && Array.isArray(serverParams.samplers)) { - extracted.samplers = serverParams.samplers.join(';'); + extracted[SETTINGS_KEYS.SAMPLERS] = serverParams.samplers.join(';'); } } diff --git a/tools/server/webui/src/lib/services/router.service.ts b/tools/server/webui/src/lib/services/router.service.ts new file mode 100644 index 00000000000..6fa172eec23 --- /dev/null +++ b/tools/server/webui/src/lib/services/router.service.ts @@ -0,0 +1,11 @@ +import { ROUTES } from '$lib/constants/routes'; + +export class RouterService { + static chat(id: string): string { + return `${ROUTES.CHAT}/${id}`; + } + + static settings(section: string): string { + return `${ROUTES.SETTINGS}/${section}`; + } +} diff --git a/tools/server/webui/src/lib/stores/agentic.svelte.ts b/tools/server/webui/src/lib/stores/agentic.svelte.ts index af71c3da40d..3334f6c111a 100644 --- a/tools/server/webui/src/lib/stores/agentic.svelte.ts +++ b/tools/server/webui/src/lib/stores/agentic.svelte.ts @@ -413,8 +413,6 @@ class AgenticStore { const tools = toolsStore.getEnabledToolsForLLM(); if (tools.length === 0) { - console.log('[AgenticStore] No tools available, falling back to standard chat'); - return { handled: false }; } diff --git a/tools/server/webui/src/lib/stores/chat.svelte.ts b/tools/server/webui/src/lib/stores/chat.svelte.ts index dcff4f46cee..6338b8a9cae 100644 --- a/tools/server/webui/src/lib/stores/chat.svelte.ts +++ b/tools/server/webui/src/lib/stores/chat.svelte.ts @@ -37,7 +37,7 @@ import { MAX_INACTIVE_CONVERSATION_STATES, INACTIVE_CONVERSATION_STATE_MAX_AGE_MS, SYSTEM_MESSAGE_PLACEHOLDER, - TITLE + TITLE_GENERATION } from '$lib/constants'; import type { ChatMessageTimings, @@ -265,7 +265,7 @@ class ChatStore { } private isChatLoadingInternal(convId: string): boolean { - return this.chatStreamingStates.has(convId); + return this.chatLoadingStates.has(convId) || this.chatStreamingStates.has(convId); } hasPendingMessage(convId: string): boolean { @@ -950,7 +950,7 @@ class ChatStore { typeof configValue.titleGenerationPrompt === 'string' && configValue.titleGenerationPrompt.trim() ? configValue.titleGenerationPrompt - : TITLE.DEFAULT_PROMPT; + : TITLE_GENERATION.DEFAULT_PROMPT; const titlePrompt = titlePromptTemplate .replace('{{USER}}', String(userContent || '')) @@ -969,14 +969,14 @@ class ChatStore { let cleanTitle = titleResponse.trim(); cleanTitle = cleanTitle - .replace(TITLE.PREFIX_PATTERN, '') - .replace(TITLE.QUOTE_PATTERN, '') + .replace(TITLE_GENERATION.PREFIX_PATTERN, '') + .replace(TITLE_GENERATION.QUOTE_PATTERN, '') .trim(); - if (!cleanTitle || cleanTitle.length < TITLE.MIN_LENGTH) { + if (!cleanTitle || cleanTitle.length < TITLE_GENERATION.MIN_LENGTH) { const firstLine = userContent.split('\n').find((l) => l.trim().length > 0); - cleanTitle = firstLine ? firstLine.trim() : TITLE.FALLBACK; + cleanTitle = firstLine ? firstLine.trim() : TITLE_GENERATION.FALLBACK; } - if (cleanTitle && cleanTitle.length >= TITLE.MIN_LENGTH) { + if (cleanTitle && cleanTitle.length >= TITLE_GENERATION.MIN_LENGTH) { await conversationsStore.updateConversationName(convId, cleanTitle); } } diff --git a/tools/server/webui/src/lib/stores/conversations.svelte.ts b/tools/server/webui/src/lib/stores/conversations.svelte.ts index b6c95ed9baf..087301de791 100644 --- a/tools/server/webui/src/lib/stores/conversations.svelte.ts +++ b/tools/server/webui/src/lib/stores/conversations.svelte.ts @@ -44,6 +44,8 @@ import { MULTIPLE_UNDERSCORE_REGEX, MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY } from '$lib/constants'; +import { ROUTES } from '$lib/constants/routes'; +import { RouterService } from '$lib/services/router.service'; import { SvelteMap, SvelteSet } from 'svelte/reactivity'; export interface ConversationTreeItem { @@ -260,7 +262,7 @@ class ConversationsStore { this.activeConversation = conversation; this.activeMessages = []; - await goto(`#/chat/${conversation.id}`); + await goto(RouterService.chat(conversation.id)); return conversation.id; } @@ -336,7 +338,7 @@ class ConversationsStore { if (this.activeConversation && idsToRemove.has(this.activeConversation.id)) { this.clearActiveConversation(); - await goto(`?new_chat=true#/`); + await goto(ROUTES.NEW_CHAT); } } else { // Reparent direct children to deleted conv's parent (or promote to top-level) @@ -352,7 +354,7 @@ class ConversationsStore { if (this.activeConversation?.id === convId) { this.clearActiveConversation(); - await goto(`?new_chat=true#/`); + await goto(ROUTES.NEW_CHAT); } } } catch (error) { @@ -376,7 +378,7 @@ class ConversationsStore { toast.success('All conversations deleted'); - await goto(`?new_chat=true#/`); + await goto(ROUTES.NEW_CHAT); } catch (error) { console.error('Failed to delete all conversations:', error); toast.error('Failed to delete conversations'); @@ -729,7 +731,7 @@ class ConversationsStore { this.conversations = [newConv, ...this.conversations]; - await goto(`#/chat/${newConv.id}`); + await goto(RouterService.chat(newConv.id)); toast.success('Conversation forked'); diff --git a/tools/server/webui/src/lib/stores/mcp.svelte.ts b/tools/server/webui/src/lib/stores/mcp.svelte.ts index 7f97b16bf74..6fb4e076676 100644 --- a/tools/server/webui/src/lib/stores/mcp.svelte.ts +++ b/tools/server/webui/src/lib/stores/mcp.svelte.ts @@ -21,6 +21,7 @@ import { browser } from '$app/environment'; import { base } from '$app/paths'; +import { SETTINGS_KEYS } from '$lib/constants'; import { MCPService } from '$lib/services/mcp.service'; import { config, settingsStore } from '$lib/stores/settings.svelte'; import { mcpResourceStore } from '$lib/stores/mcp-resources.svelte'; @@ -556,13 +557,13 @@ class MCPStore { requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds, useProxy: serverData.useProxy }; - settingsStore.updateConfig('mcpServers', JSON.stringify([...servers, newServer])); + settingsStore.updateConfig(SETTINGS_KEYS.MCP_SERVERS, JSON.stringify([...servers, newServer])); } updateServer(id: string, updates: Partial): void { const servers = this.getServers(); settingsStore.updateConfig( - 'mcpServers', + SETTINGS_KEYS.MCP_SERVERS, JSON.stringify( servers.map((server) => (server.id === id ? { ...server, ...updates } : server)) ) @@ -571,7 +572,10 @@ class MCPStore { removeServer(id: string): void { const servers = this.getServers(); - settingsStore.updateConfig('mcpServers', JSON.stringify(servers.filter((s) => s.id !== id))); + settingsStore.updateConfig( + SETTINGS_KEYS.MCP_SERVERS, + JSON.stringify(servers.filter((s) => s.id !== id)) + ); this.clearHealthCheck(id); } diff --git a/tools/server/webui/src/lib/stores/models.svelte.ts b/tools/server/webui/src/lib/stores/models.svelte.ts index aa99b89b400..1f49f09ea67 100644 --- a/tools/server/webui/src/lib/stores/models.svelte.ts +++ b/tools/server/webui/src/lib/stores/models.svelte.ts @@ -10,6 +10,7 @@ import { MODEL_PROPS_CACHE_MAX_ENTRIES, FAVORITE_MODELS_LOCALSTORAGE_KEY } from '$lib/constants'; +import { conversationsStore } from '$lib/stores/conversations.svelte'; /** * modelsStore - Reactive store for model management in both MODEL and ROUTER modes @@ -424,6 +425,103 @@ class ModelsStore { } } + /** + * Gets the model name from the last assistant message in the active conversation. + * Iterates backward through messages to find the most recent message with a model. + * Used by both the chat page and settings page to maintain model consistency. + * @returns The model name or null if not found + */ + getModelFromLastAssistantResponse(): string | null { + const messages = conversationsStore.activeMessages; + if (!messages || messages.length === 0) return null; + + // Iterate backward to find the last message with a model + for (let i = messages.length - 1; i >= 0; i--) { + if (messages[i].model) { + return messages[i].model; + } + } + + return null; + } + + /** + * Auto-selects the model from the last assistant response if available and loaded. + * Returns true if a model was selected, false otherwise. + * This is used by the chat page to maintain model consistency across page navigation. + */ + async selectModelFromLastAssistantResponse(): Promise { + const lastModel = this.getModelFromLastAssistantResponse(); + if (!lastModel) return false; + + // Skip if already selected + if (this.selectedModelName === lastModel) return false; + + const matchingModel = this.models.find((option) => option.model === lastModel); + if (!matchingModel) return false; + + if (!this.isModelLoaded(lastModel)) { + console.log('[modelsStore] last assistant model not loaded:', lastModel); + return false; + } + + try { + await this.selectModelById(matchingModel.id); + console.log(`[modelsStore] Automatically selected model: ${lastModel} from last message`); + return true; + } catch (error) { + console.warn('[modelsStore] Failed to automatically select model from last message:', error); + return false; + } + } + + /** + * Auto-selects the first available model if none is selected, and fetches its props. + * Prioritizes: + * 1. Model from active conversation's last assistant response (if loaded) + * 2. Model from active conversation's last assistant response (if not loaded) + * 3. First loaded model (not from active conversation) + * 4. First available model + * This is used to ensure default values are populated in settings pages. + */ + async ensureFirstModelSelected(): Promise { + if (this.selectedModelName) return; + + // Filter models that are visible in webui + const availableModels = this.models.filter((option) => { + const modelProps = this.getModelProps(option.model); + return modelProps?.webui !== false; + }); + + if (availableModels.length === 0) return; + + // Try to select model from last assistant response first + const lastModel = this.getModelFromLastAssistantResponse(); + if (lastModel) { + const lastModelOption = availableModels.find((m) => m.model === lastModel); + if (lastModelOption) { + await this.selectModelById(lastModelOption.id); + if (this.isModelLoaded(lastModel)) { + await this.fetchModelProps(lastModel); + } + return; + } + } + + // Try to find a loaded model first + const loadedModel = availableModels.find((m) => this.isModelLoaded(m.model)); + if (loadedModel) { + await this.selectModelById(loadedModel.id); + await this.fetchModelProps(loadedModel.model); + return; + } + + // Fall back to the first available model + const firstModel = availableModels[0]; + await this.selectModelById(firstModel.id); + // Don't fetch props for unloaded models (will fail in ROUTER mode) + } + /** * Update modalities for a specific model * Called when a model is loaded or when we need fresh modality data diff --git a/tools/server/webui/src/lib/stores/settings-referrer.svelte.ts b/tools/server/webui/src/lib/stores/settings-referrer.svelte.ts index 48bae1170a8..297a0d6a455 100644 --- a/tools/server/webui/src/lib/stores/settings-referrer.svelte.ts +++ b/tools/server/webui/src/lib/stores/settings-referrer.svelte.ts @@ -1,4 +1,6 @@ -let _url = $state('#/'); +import { SETTINGS_FALLBACK_EXIT_ROUTE } from '$lib/constants'; + +let _url = $state(SETTINGS_FALLBACK_EXIT_ROUTE); export const settingsReferrer = { get url() { diff --git a/tools/server/webui/src/lib/stores/settings.svelte.ts b/tools/server/webui/src/lib/stores/settings.svelte.ts index e1d89844525..0177d79af87 100644 --- a/tools/server/webui/src/lib/stores/settings.svelte.ts +++ b/tools/server/webui/src/lib/stores/settings.svelte.ts @@ -32,9 +32,13 @@ */ import { browser } from '$app/environment'; +import { ColorMode } from '$lib/enums'; +import type { SettingsExportType } from '$lib/types'; +import { setMode } from 'mode-watcher'; import { CONFIG_LOCALSTORAGE_KEY, SETTING_CONFIG_DEFAULT, + SETTINGS_KEYS, USER_OVERRIDES_LOCALSTORAGE_KEY } from '$lib/constants'; import { IsMobile } from '$lib/hooks/is-mobile.svelte'; @@ -57,7 +61,6 @@ class SettingsStore { */ config = $state({ ...SETTING_CONFIG_DEFAULT }); - theme = $state('auto'); isInitialized = $state(false); userOverrides = $state>(new Set()); @@ -99,7 +102,9 @@ class SettingsStore { initialize() { try { this.loadConfig(); - this.loadTheme(); + this.migrateLegacyTheme(); + // Apply the persisted theme from config on initial load + setMode(this.config[SETTINGS_KEYS.THEME] as ColorMode); this.isInitialized = true; } catch (error) { console.error('Failed to initialize settings store:', error); @@ -124,9 +129,9 @@ class SettingsStore { }; // Default sendOnEnter to false on mobile when the user has no saved preference - if (!('sendOnEnter' in savedVal)) { + if (!(SETTINGS_KEYS.SEND_ON_ENTER in savedVal)) { if (new IsMobile().current) { - this.config.sendOnEnter = false; + this.config[SETTINGS_KEYS.SEND_ON_ENTER] = false; } } @@ -143,12 +148,21 @@ class SettingsStore { } /** - * Load theme from localStorage + * Migrate the legacy un-namespaced "theme" localStorage key into config. + * Previously theme was stored separately in localStorage("theme") — now it lives + * inside the config object alongside all other settings. + * After migration the legacy key is removed. */ - private loadTheme() { + private migrateLegacyTheme() { if (!browser) return; - this.theme = localStorage.getItem('theme') || 'auto'; + const legacyTheme = localStorage.getItem('theme'); + if (legacyTheme) { + this.config[SETTINGS_KEYS.THEME] = legacyTheme; + localStorage.removeItem('theme'); + this.saveConfig(); + setMode(legacyTheme as ColorMode); + } } /** * @@ -233,29 +247,13 @@ class SettingsStore { } /** - * Update the theme setting + * Update the theme setting. * @param newTheme - The new theme value */ updateTheme(newTheme: string) { - this.theme = newTheme; - this.saveTheme(); - } - - /** - * Save the current theme to localStorage - */ - private saveTheme() { - if (!browser) return; + this.updateConfig(SETTINGS_KEYS.THEME, newTheme); - try { - if (this.theme === 'auto') { - localStorage.removeItem('theme'); - } else { - localStorage.setItem('theme', this.theme); - } - } catch (error) { - console.error('Failed to save theme to localStorage:', error); - } + setMode(newTheme as ColorMode); } /** @@ -271,22 +269,26 @@ class SettingsStore { */ resetConfig() { this.config = { ...SETTING_CONFIG_DEFAULT }; + this.saveConfig(); } /** - * Reset theme to auto + * Reset theme to default value. + * Theme is now stored inside the config object. */ resetTheme() { - this.theme = 'auto'; - this.saveTheme(); + this.updateConfig(SETTINGS_KEYS.THEME, SETTING_CONFIG_DEFAULT[SETTINGS_KEYS.THEME]); + + setMode(SETTING_CONFIG_DEFAULT[SETTINGS_KEYS.THEME] as ColorMode); } /** - * Reset all settings to defaults + * Reset all settings to defaults. */ resetAll() { this.resetConfig(); + this.resetTheme(); } @@ -456,10 +458,86 @@ class SettingsStore { this.saveConfig(); console.log('Cleared all user overrides'); } + + /** + * + * + * Import / Export + * + * + */ + + /** + * Export all settings as a versioned JSON-compatible object. + * The export captures the full config (excluding sensitive values like API key) + * and user overrides. Sensitive fields are filtered out for security by default. + * @param includeSensitiveData - If true, include sensitive fields (apiKey, MCP server headers) in export + */ + exportSettings(includeSensitiveData: boolean = false): SettingsExportType { + // Build config excluding sensitive data unless user opts in + const configToExport: Record = + includeSensitiveData + ? { ...this.config } + : Object.fromEntries(Object.entries(this.config).filter(([key]) => key !== 'apiKey')); + + // Handle MCP servers: exclude custom headers unless user opts in + if ('mcpServers' in configToExport && !includeSensitiveData) { + try { + const mcpServers = JSON.parse(configToExport.mcpServers as string) as Array< + Record + >; + const safeServers = mcpServers.map((server) => { + delete server.headers; + return server; + }); + configToExport.mcpServers = JSON.stringify(safeServers); + } catch { + // If parsing fails, just exclude the entire mcpServers field + delete (configToExport as Record).mcpServers; + } + } + + return { + version: 1, + timestamp: Date.now(), + config: configToExport, + userOverrides: Array.from(this.userOverrides) + }; + } + + /** + * Import settings from a previously exported object. + * Restores config (including theme) and user overrides. + * @param data - The exported settings object + */ + importSettings(data: SettingsExportType): void { + if (!browser) return; + + if (!data || !data.config) { + throw new Error('Invalid settings data: missing config'); + } + + // Restore config (theme is included in config) + this.config = { + ...SETTING_CONFIG_DEFAULT, + ...data.config + }; + + // Restore user overrides (derived state — may be stale if server defaults differ) + this.userOverrides = new Set(data.userOverrides ?? []); + + // Persist to localStorage + this.saveConfig(); + + // Apply theme for immediate visual feedback + setMode(this.config[SETTINGS_KEYS.THEME] as ColorMode); + + console.log('Settings imported successfully'); + } } export const settingsStore = new SettingsStore(); export const config = () => settingsStore.config; -export const theme = () => settingsStore.theme; +export const theme = () => settingsStore.config[SETTINGS_KEYS.THEME]; export const isInitialized = () => settingsStore.isInitialized; diff --git a/tools/server/webui/src/lib/types/index.ts b/tools/server/webui/src/lib/types/index.ts index 2ce39806007..03cb9c5a562 100644 --- a/tools/server/webui/src/lib/types/index.ts +++ b/tools/server/webui/src/lib/types/index.ts @@ -76,10 +76,15 @@ export type { SettingsFieldConfig, SettingsChatServiceOptions, SettingsConfigType, + SettingsExportType, ParameterValue, ParameterRecord, ParameterInfo, - SyncableParameter + SyncableParameter, + SettingsEntry, + SettingsSectionTitle, + SettingsSectionEntry, + SettingsSection } from './settings'; // Common types diff --git a/tools/server/webui/src/lib/types/settings.d.ts b/tools/server/webui/src/lib/types/settings.d.ts index 4c545ce1dc6..8f5f164bd72 100644 --- a/tools/server/webui/src/lib/types/settings.d.ts +++ b/tools/server/webui/src/lib/types/settings.d.ts @@ -1,12 +1,42 @@ -import type { SETTING_CONFIG_DEFAULT } from '$lib/constants'; +import type { SETTING_CONFIG_DEFAULT, SETTINGS_SECTION_TITLES } from '$lib/constants'; import type { ChatMessagePromptProgress, ChatMessageTimings } from './chat'; import type { OpenAIToolDefinition } from './mcp'; import type { DatabaseMessageExtra } from './database'; import type { ParameterSource, SyncableParameterType, SettingsFieldType } from '$lib/enums'; import type { Icon } from '@lucide/svelte'; +import type { Component } from 'svelte'; export type SettingsConfigValue = string | number | boolean | undefined; +/** Section title type derived from registry section titles. */ +export type SettingsSectionTitle = + (typeof SETTINGS_SECTION_TITLES)[keyof typeof SETTINGS_SECTION_TITLES]; + +/** Per-setting metadata — one entry per setting. */ +export interface SettingsEntry { + key: string; + label: string; + help: string; + defaultValue: SettingsConfigValue; + type: SettingsFieldType; + section?: string; + options?: Array<{ value: string; label: string; icon: Component }>; + isExperimental?: boolean; + isPositiveInteger?: boolean; + sync?: { + serverKey: string; + paramType: SyncableParameterType; + }; +} + +/** A settings section with its icon, slug, title, and ordered settings. */ +export interface SettingsSectionEntry { + title: SettingsSectionTitle; + slug: string; + icon: Component; + settings: SettingsEntry[]; +} + export interface SettingsFieldConfig { key: string; label: string; @@ -16,6 +46,14 @@ export interface SettingsFieldConfig { options?: Array<{ value: string; label: string; icon?: typeof Icon }>; } +/** Re-exported for backward compatibility. */ +export interface SettingsSection { + fields?: SettingsFieldConfig[]; + icon: Component; + slug: string; + title: SettingsSectionTitle; +} + export interface SettingsChatServiceOptions { stream?: boolean; // Model (required in ROUTER mode, optional in MODEL mode) @@ -94,3 +132,18 @@ export interface SyncableParameter { type: SyncableParameterType; canSync: boolean; } + +/** + * Shape of the settings JSON export file. + * Versioned to allow future schema evolution. + */ +export interface SettingsExportType { + /** Export format version — bumped on breaking changes */ + version: number; + /** Unix timestamp of export */ + timestamp: number; + /** Full settings config (includes theme as a config key) */ + config: SettingsConfigType; + /** Keys that differ from server defaults (derived, but persisted for fidelity) */ + userOverrides: string[]; +} diff --git a/tools/server/webui/src/lib/utils/convert-files-to-extra.ts b/tools/server/webui/src/lib/utils/convert-files-to-extra.ts index 840d12c1ce3..12be45485d3 100644 --- a/tools/server/webui/src/lib/utils/convert-files-to-extra.ts +++ b/tools/server/webui/src/lib/utils/convert-files-to-extra.ts @@ -2,6 +2,7 @@ import { convertPDFToImage, convertPDFToText } from './pdf-processing'; import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png'; import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png'; import { FileTypeCategory, AttachmentType, SpecialFileType } from '$lib/enums'; +import { SETTINGS_KEYS } from '$lib/constants'; import { config, settingsStore } from '$lib/stores/settings.svelte'; import { modelsStore } from '$lib/stores/models.svelte'; import { getFileTypeCategory } from '$lib/utils'; @@ -106,7 +107,7 @@ export async function parseFilesToMessageExtras( console.log('Non-vision model detected: forcing PDF-to-text mode and updating settings'); // Update the setting in localStorage - settingsStore.updateConfig('pdfAsImage', false); + settingsStore.updateConfig(SETTINGS_KEYS.PDF_AS_IMAGE, false); // Show toast notification to user toast.warning( diff --git a/tools/server/webui/src/lib/utils/process-uploaded-files.ts b/tools/server/webui/src/lib/utils/process-uploaded-files.ts index 0342dce1f7a..1f4068aeed4 100644 --- a/tools/server/webui/src/lib/utils/process-uploaded-files.ts +++ b/tools/server/webui/src/lib/utils/process-uploaded-files.ts @@ -1,6 +1,7 @@ import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png'; import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png'; import { FileTypeCategory } from '$lib/enums'; +import { SETTINGS_KEYS } from '$lib/constants'; import { modelsStore } from '$lib/stores/models.svelte'; import { settingsStore } from '$lib/stores/settings.svelte'; import { toast } from 'svelte-sonner'; @@ -104,7 +105,7 @@ export async function processFilesToChatUploaded( action: { label: 'Enable PDF as Images', onClick: () => { - settingsStore.updateConfig('pdfAsImage', true); + settingsStore.updateConfig(SETTINGS_KEYS.PDF_AS_IMAGE, true); toast.success('PDF parsing as images enabled!', { duration: 3000 }); diff --git a/tools/server/webui/src/routes/(chat)/+page.svelte b/tools/server/webui/src/routes/(chat)/+page.svelte index b22eda0978e..c272b438ef1 100644 --- a/tools/server/webui/src/routes/(chat)/+page.svelte +++ b/tools/server/webui/src/routes/(chat)/+page.svelte @@ -7,11 +7,11 @@ import { onMount } from 'svelte'; import { page } from '$app/state'; import { replaceState } from '$app/navigation'; - import { APP_NAME } from '$lib/constants'; + import { APP_NAME, NEW_CHAT_PARAM } from '$lib/constants'; let qParam = $derived(page.url.searchParams.get('q')); let modelParam = $derived(page.url.searchParams.get('model')); - let newChatParam = $derived(page.url.searchParams.get('new_chat')); + let newChatParam = $derived(page.url.searchParams.get(NEW_CHAT_PARAM)); // Dialog state for model not available error let showModelNotAvailable = $state(false); @@ -26,7 +26,7 @@ url.searchParams.delete('q'); url.searchParams.delete('model'); - url.searchParams.delete('new_chat'); + url.searchParams.delete(NEW_CHAT_PARAM); replaceState(url.toString(), {}); } diff --git a/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte b/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte index 8a5e1be7df4..8d0981a4e16 100644 --- a/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte +++ b/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte @@ -3,13 +3,10 @@ import { page } from '$app/state'; import { afterNavigate } from '$app/navigation'; import { DialogModelNotAvailable } from '$lib/components/app'; + import { ROUTES } from '$lib/constants/routes'; import { chatStore, isLoading } from '$lib/stores/chat.svelte'; - import { - conversationsStore, - activeConversation, - activeMessages - } from '$lib/stores/conversations.svelte'; - import { modelsStore, modelOptions, selectedModelId } from '$lib/stores/models.svelte'; + import { conversationsStore, activeConversation } from '$lib/stores/conversations.svelte'; + import { modelsStore, modelOptions } from '$lib/stores/models.svelte'; let chatId = $derived(page.params.id); let currentChatId: string | undefined = undefined; @@ -73,47 +70,9 @@ urlParamsProcessed = true; } - async function selectModelFromLastAssistantResponse() { - const messages = activeMessages(); - if (messages.length === 0) return; - - let lastMessageWithModel: DatabaseMessage | undefined; - - for (let i = messages.length - 1; i >= 0; i--) { - if (messages[i].model) { - lastMessageWithModel = messages[i]; - break; - } - } - - if (!lastMessageWithModel) return; - - const currentModelId = selectedModelId(); - const currentModelName = modelOptions().find((m) => m.id === currentModelId)?.model; - - if (currentModelName === lastMessageWithModel.model) { - return; - } - - const matchingModel = modelOptions().find( - (option) => option.model === lastMessageWithModel.model - ); - - if (matchingModel && modelsStore.isModelLoaded(matchingModel.model)) { - try { - await modelsStore.selectModelById(matchingModel.id); - console.log( - `Automatically selected model: ${lastMessageWithModel.model} from last message` - ); - } catch (error) { - console.warn('Failed to automatically select model from last message:', error); - } - } - } - afterNavigate(() => { setTimeout(() => { - selectModelFromLastAssistantResponse(); + void modelsStore.selectModelFromLastAssistantResponse(); }, 100); }); @@ -141,7 +100,7 @@ await handleUrlParams(); } } else { - await goto('#/'); + await goto(ROUTES.START); } })(); } diff --git a/tools/server/webui/src/routes/+error.svelte b/tools/server/webui/src/routes/+error.svelte index faddf0bcaa2..4f8251d8679 100644 --- a/tools/server/webui/src/routes/+error.svelte +++ b/tools/server/webui/src/routes/+error.svelte @@ -2,6 +2,7 @@ import { page } from '$app/stores'; import { goto } from '$app/navigation'; import { ServerErrorSplash } from '$lib/components/app'; + import { ROUTES } from '$lib/constants/routes'; let error = $derived($page.error); let status = $derived($page.status); @@ -17,7 +18,7 @@ function handleRetry() { // Navigate back to home page after successful API key validation - goto('#/'); + goto(ROUTES.START); } @@ -60,7 +61,7 @@

"),root$1_=from_html(" ",1);function Checkbox$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let checked=prop($$props,"checked",15,!1),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),required2=prop($$props,"required", -3,!1),name=prop($$props,"name",3,void 0),value=prop($$props,"value",3,"on"),id2=prop($$props,"id",19,()=>createId(uid2)),indeterminate=prop($$props,"indeterminate",15,!1),type2=prop($$props,"type",3,"button"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","checked","ref","onCheckedChange","children","disabled","required","name","value","id","indeterminate","onIndeterminateChange","child","type","readonly"]);const group=CheckboxGroupContext.getOr(null);group&&value()&&(group.opts.value. -current.includes(value())?checked(!0):checked(!1)),watch$1.pre(()=>value(),()=>{group&&value()&&(group.opts.value.current.includes(value())?checked(!0):checked(!1))});const rootState=CheckboxRootState.create({checked:boxWith$1(()=>checked(),v=>{checked(v),$$props.onCheckedChange?.(v)}),disabled:boxWith$1(()=>disabled()??!1),required:boxWith$1(()=>required2()),name:boxWith$1(()=>name()),value:boxWith$1(()=>value()),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),indeterminate:boxWith$1( -()=>indeterminate(),v=>{indeterminate(v),$$props.onIndeterminateChange?.(v)}),type:boxWith$1(()=>type2()),readonly:boxWith$1(()=>!!$$props.readonly)},group),mergedProps=user_derived(()=>mergeProps({...restProps},rootState.props));var fragment=root$1_(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({props:get$3(mergedProps),...rootState.snippetProps}));snippet(node_1,()=>$$props.child,()=>get$3($0))}append( -$$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1y();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3,()=>rootState.snippetProps),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}var node_3=sibling(node2,2);Checkbox_input(node_3,{}),append($$anchor,fragment),pop()}const collapsibleAttrs=createBitsAttrs({component:"collapsible",parts:[ -"root","content","trigger"]}),CollapsibleRootContext=new Context$1("Collapsible.Root");class CollapsibleRootState{static create(opts){return CollapsibleRootContext.set(new CollapsibleRootState(opts))}opts;attachment;#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}contentPresence;#contentId=state$1(void 0);get contentId(){return get$3(this.#contentId)}set contentId(value){set$1(this.#contentId,value,!0)}constructor(opts){ -this.opts=opts,this.toggleOpen=this.toggleOpen.bind(this),this.attachment=attachRef(this.opts.ref),this.contentPresence=new PresenceManager({ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}})}toggleOpen(){this.opts.open.current=!this.opts.open.current}#props=user_derived(()=>({id:this.opts.id.current,"data-state":getDataOpenClosed(this.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled. -current),[collapsibleAttrs.root]:"",...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class CollapsibleContentState{static create(opts){return new CollapsibleContentState(opts,CollapsibleRootContext.get())}opts;root;attachment;#present=user_derived(()=>this.opts.hiddenUntilFound.current?this.root.opts.open.current:this.opts.forceMount.current||this.root.opts.open.current);get present(){return get$3(this.#present)}set present(value){set$1(this.#present, -value)}#originalStyles;#isMountAnimationPrevented=state$1(!1);#width=state$1(0);#height=state$1(0);constructor(opts,root2){this.opts=opts,this.root=root2,set$1(this.#isMountAnimationPrevented,root2.opts.open.current,!0),this.root.contentId=this.opts.id.current,this.attachment=attachRef(this.opts.ref,v=>this.root.contentNode=v),watch$1.pre(()=>this.opts.id.current,id2=>{this.root.contentId=id2}),user_pre_effect(()=>{const rAF=requestAnimationFrame(()=>{set$1(this.#isMountAnimationPrevented,!1)}); -return()=>{cancelAnimationFrame(rAF)}}),watch$1.pre([()=>this.opts.ref.current,()=>this.opts.hiddenUntilFound.current],([node2,hiddenUntilFound])=>!node2||!hiddenUntilFound?void 0:on(node2,"beforematch",()=>{this.root.opts.open.current||requestAnimationFrame(()=>{this.root.opts.open.current=!0})})),watch$1([()=>this.opts.ref.current,()=>this.present],([node2])=>{node2&&afterTick(()=>{if(!this.opts.ref.current)return;this.#originalStyles=this.#originalStyles||{transitionDuration:node2.style.transitionDuration, -animationName:node2.style.animationName},node2.style.transitionDuration="0s",node2.style.animationName="none";const rect=node2.getBoundingClientRect();if(set$1(this.#height,rect.height,!0),set$1(this.#width,rect.width,!0),!get$3(this.#isMountAnimationPrevented)){const{animationName,transitionDuration}=this.#originalStyles;node2.style.transitionDuration=transitionDuration,node2.style.animationName=animationName}})})}get shouldRender(){return this.root.contentPresence.shouldRender}#snippetProps=user_derived( -()=>({open:this.root.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,style:{"--bits-collapsible-content-height":get$3(this.#height)?`${get$3(this.#height)}px`:void 0,"--bits-collapsible-content-width":get$3(this.#width)?`${get$3(this.#width)}px`:void 0},hidden:this.opts.hiddenUntilFound.current&&!this.root.opts.open.current?"until-found":void 0,"data-state":getDataOpenClosed( -this.root.opts.open.current),...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),"data-disabled":boolToEmptyStrOrUndef(this.root.opts.disabled.current),[collapsibleAttrs.content]:"",...this.opts.hiddenUntilFound.current&&!this.shouldRender?{}:{hidden:this.opts.hiddenUntilFound.current?!this.shouldRender:this.opts.forceMount.current?void 0:!this.shouldRender},...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class CollapsibleTriggerState{static create(opts){ -return new CollapsibleTriggerState(opts,CollapsibleRootContext.get())}opts;root;attachment;#isDisabled=user_derived(()=>this.opts.disabled.current||this.root.opts.disabled.current);constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(this.opts.ref),this.onclick=this.onclick.bind(this),this.onkeydown=this.onkeydown.bind(this)}onclick(e){if(!get$3(this.#isDisabled)){if(e.button!==0)return e.preventDefault();this.root.toggleOpen()}}onkeydown(e){get$3(this.#isDisabled)||(e. -key===SPACE||e.key===ENTER)&&(e.preventDefault(),this.root.toggleOpen())}#props=user_derived(()=>({id:this.opts.id.current,type:"button",disabled:get$3(this.#isDisabled),"aria-controls":this.root.contentId,"aria-expanded":boolToStr(this.root.opts.open.current),"data-state":getDataOpenClosed(this.root.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(get$3(this.#isDisabled)),[collapsibleAttrs.trigger]:"",onclick:this.onclick,onkeydown:this.onkeydown,...this.attachment}));get props(){return get$3( -this.#props)}set props(value){set$1(this.#props,value)}}var root_2$1x=from_html("
");function Collapsible$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),open2=prop($$props,"open",15,!1),disabled=prop($$props,"disabled",3,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1),restProps=rest_props($$props,["$$slots","$$event\ -s","$$legacy","children","child","id","ref","open","disabled","onOpenChange","onOpenChangeComplete"]);const rootState=CollapsibleRootState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),disabled:boxWith$1(()=>disabled()),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),mergedProps=user_derived(()=>mergeProps(restProps,rootState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{ -var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1x();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1w=from_html("
");function Collapsible_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),hiddenUntilFound=prop($$props,"hiddenUntilFound",3,!1),id2=prop($$props,"id",19,()=>createId(uid2)),restProps=rest_props($$props,["$$slots","$$events","$$legacy","child","ref","forceMount","hiddenUntilFound","children","id"]);const contentState=CollapsibleContentState.create({id:boxWith$1(()=>id2()),forceMount:boxWith$1( -()=>forceMount()),hiddenUntilFound:boxWith$1(()=>hiddenUntilFound()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({...contentState.snippetProps,props:get$3(mergedProps)}));snippet(node_1,()=>$$props.child,()=>get$3($0))}append($$anchor2,fragment_1)},alternate=$$anchor2=>{ -var div=root_2$1w();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1v=from_html("");function Collapsible_trigger$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId( -uid2)),disabled=prop($$props,"disabled",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","ref","id","disabled"]);const triggerState=CollapsibleTriggerState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),disabled:boxWith$1(()=>disabled())}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1); -snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1v();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}const sides=["top","right","bottom","left"],min=Math.min,max=Math.max,round=Math. -round,floor=Math.floor,createCoords=v=>({x:v,y:v}),oppositeSideMap={left:"right",right:"left",bottom:"top",top:"bottom"},oppositeAlignmentMap={start:"end",end:"start"};function clamp$1(start2,value,end){return max(start2,min(value,end))}function evaluate(value,param){return typeof value=="function"?value(param):value}function getSide$1(placement){return placement.split("-")[0]}function getAlignment(placement){return placement.split("-")[1]}function getOppositeAxis(axis){return axis==="x"?"y":"x"} -function getAxisLength(axis){return axis==="y"?"height":"width"}const yAxisSides=new Set(["top","bottom"]);function getSideAxis(placement){return yAxisSides.has(getSide$1(placement))?"y":"x"}function getAlignmentAxis(placement){return getOppositeAxis(getSideAxis(placement))}function getAlignmentSides(placement,rects,rtl){rtl===void 0&&(rtl=!1);const alignment=getAlignment(placement),alignmentAxis=getAlignmentAxis(placement),length=getAxisLength(alignmentAxis);let mainAlignmentSide=alignmentAxis=== -"x"?alignment===(rtl?"end":"start")?"right":"left":alignment==="start"?"bottom":"top";return rects.reference[length]>rects.floating[length]&&(mainAlignmentSide=getOppositePlacement(mainAlignmentSide)),[mainAlignmentSide,getOppositePlacement(mainAlignmentSide)]}function getExpandedPlacements(placement){const oppositePlacement=getOppositePlacement(placement);return[getOppositeAlignmentPlacement(placement),oppositePlacement,getOppositeAlignmentPlacement(oppositePlacement)]}function getOppositeAlignmentPlacement(placement){ -return placement.replace(/start|end/g,alignment=>oppositeAlignmentMap[alignment])}const lrPlacement=["left","right"],rlPlacement=["right","left"],tbPlacement=["top","bottom"],btPlacement=["bottom","top"];function getSideList(side,isStart,rtl){switch(side){case"top":case"bottom":return rtl?isStart?rlPlacement:lrPlacement:isStart?lrPlacement:rlPlacement;case"left":case"right":return isStart?tbPlacement:btPlacement;default:return[]}}function getOppositeAxisPlacements(placement,flipAlignment,direction,rtl){ -const alignment=getAlignment(placement);let list2=getSideList(getSide$1(placement),direction==="start",rtl);return alignment&&(list2=list2.map(side=>side+"-"+alignment),flipAlignment&&(list2=list2.concat(list2.map(getOppositeAlignmentPlacement)))),list2}function getOppositePlacement(placement){return placement.replace(/left|right|bottom|top/g,side=>oppositeSideMap[side])}function expandPaddingObject(padding){return{top:0,right:0,bottom:0,left:0,...padding}}function getPaddingObject(padding){return typeof padding!= -"number"?expandPaddingObject(padding):{top:padding,right:padding,bottom:padding,left:padding}}function rectToClientRect(rect){const{x,y,width,height}=rect;return{width,height,top:y,left:x,right:x+width,bottom:y+height,x,y}}function computeCoordsFromPlacement(_ref,placement,rtl){let{reference,floating}=_ref;const sideAxis=getSideAxis(placement),alignmentAxis=getAlignmentAxis(placement),alignLength=getAxisLength(alignmentAxis),side=getSide$1(placement),isVertical=sideAxis==="y",commonX=reference.x+ -reference.width/2-floating.width/2,commonY=reference.y+reference.height/2-floating.height/2,commonAlign=reference[alignLength]/2-floating[alignLength]/2;let coords;switch(side){case"top":coords={x:commonX,y:reference.y-floating.height};break;case"bottom":coords={x:commonX,y:reference.y+reference.height};break;case"right":coords={x:reference.x+reference.width,y:commonY};break;case"left":coords={x:reference.x-floating.width,y:commonY};break;default:coords={x:reference.x,y:reference.y}}switch(getAlignment( -placement)){case"start":coords[alignmentAxis]-=commonAlign*(rtl&&isVertical?-1:1);break;case"end":coords[alignmentAxis]+=commonAlign*(rtl&&isVertical?-1:1);break}return coords}const computePosition$1=async(reference,floating,config2)=>{const{placement="bottom",strategy="absolute",middleware=[],platform:platform2}=config2,validMiddleware=middleware.filter(Boolean),rtl=await(platform2.isRTL==null?void 0:platform2.isRTL(floating));let rects=await platform2.getElementRects({reference,floating,strategy}), -{x,y}=computeCoordsFromPlacement(rects,placement,rtl),statefulPlacement=placement,middlewareData={},resetCount=0;for(let i=0;i({name:"arrow",options,async fn(state2){const{x,y,placement,rects,platform:platform2,elements,middlewareData}=state2,{element:element2,padding=0}=evaluate(options,state2)||{};if(element2==null)return{};const paddingObject=getPaddingObject(padding),coords={x,y},axis=getAlignmentAxis(placement),length=getAxisLength(axis),arrowDimensions=await platform2.getDimensions(element2),isYAxis=axis=== -"y",minProp=isYAxis?"top":"left",maxProp=isYAxis?"bottom":"right",clientProp=isYAxis?"clientHeight":"clientWidth",endDiff=rects.reference[length]+rects.reference[axis]-coords[axis]-rects.floating[length],startDiff=coords[axis]-rects.reference[axis],arrowOffsetParent=await(platform2.getOffsetParent==null?void 0:platform2.getOffsetParent(element2));let clientSize=arrowOffsetParent?arrowOffsetParent[clientProp]:0;(!clientSize||!await(platform2.isElement==null?void 0:platform2.isElement(arrowOffsetParent)))&& -(clientSize=elements.floating[clientProp]||rects.floating[length]);const centerToReference=endDiff/2-startDiff/2,largestPossiblePadding=clientSize/2-arrowDimensions[length]/2-1,minPadding=min(paddingObject[minProp],largestPossiblePadding),maxPadding=min(paddingObject[maxProp],largestPossiblePadding),min$1=minPadding,max2=clientSize-arrowDimensions[length]-maxPadding,center=clientSize/2-arrowDimensions[length]/2+centerToReference,offset2=clamp$1(min$1,center,max2),shouldAddOffset=!middlewareData. -arrow&&getAlignment(placement)!=null&¢er!==offset2&&rects.reference[length]/2-(centerside2<=0)){var _middlewareData$flip2,_overflowsData$filter;const nextIndex=(((_middlewareData$flip2=middlewareData.flip)==null?void 0:_middlewareData$flip2.index)||0)+1,nextPlacement=placements[nextIndex];if(nextPlacement&&(!(checkCrossAxis==="alignment"?initialSideAxis!==getSideAxis(nextPlacement):!1)||overflowsData.every(d2=>d2.overflows[0]>0&&getSideAxis(d2.placement)===initialSideAxis)))return{data:{index:nextIndex,overflows:overflowsData},reset:{placement:nextPlacement}}; -let resetPlacement=(_overflowsData$filter=overflowsData.filter(d2=>d2.overflows[0]<=0).sort((a,b)=>a.overflows[1]-b.overflows[1])[0])==null?void 0:_overflowsData$filter.placement;if(!resetPlacement)switch(fallbackStrategy){case"bestFit":{var _overflowsData$filter2;const placement2=(_overflowsData$filter2=overflowsData.filter(d2=>{if(hasFallbackAxisSideDirection){const currentSideAxis=getSideAxis(d2.placement);return currentSideAxis===initialSideAxis||currentSideAxis==="y"}return!0}).map(d2=>[d2. -placement,d2.overflows.filter(overflow2=>overflow2>0).reduce((acc,overflow2)=>acc+overflow2,0)]).sort((a,b)=>a[1]-b[1])[0])==null?void 0:_overflowsData$filter2[0];placement2&&(resetPlacement=placement2);break}case"initialPlacement":resetPlacement=initialPlacement;break}if(placement!==resetPlacement)return{reset:{placement:resetPlacement}}}return{}}}};function getSideOffsets(overflow,rect){return{top:overflow.top-rect.height,right:overflow.right-rect.width,bottom:overflow.bottom-rect.height,left:overflow. -left-rect.width}}function isAnySideFullyClipped(overflow){return sides.some(side=>overflow[side]>=0)}const hide$1=function(options){return options===void 0&&(options={}),{name:"hide",options,async fn(state2){const{rects}=state2,{strategy="referenceHidden",...detectOverflowOptions}=evaluate(options,state2);switch(strategy){case"referenceHidden":{const overflow=await detectOverflow(state2,{...detectOverflowOptions,elementContext:"reference"}),offsets=getSideOffsets(overflow,rects.reference);return{ -data:{referenceHiddenOffsets:offsets,referenceHidden:isAnySideFullyClipped(offsets)}}}case"escaped":{const overflow=await detectOverflow(state2,{...detectOverflowOptions,altBoundary:!0}),offsets=getSideOffsets(overflow,rects.floating);return{data:{escapedOffsets:offsets,escaped:isAnySideFullyClipped(offsets)}}}default:return{}}}}},originSides=new Set(["left","top"]);async function convertValueToCoords(state2,options){const{placement,platform:platform2,elements}=state2,rtl=await(platform2.isRTL== -null?void 0:platform2.isRTL(elements.floating)),side=getSide$1(placement),alignment=getAlignment(placement),isVertical=getSideAxis(placement)==="y",mainAxisMulti=originSides.has(side)?-1:1,crossAxisMulti=rtl&&isVertical?-1:1,rawValue=evaluate(options,state2);let{mainAxis,crossAxis,alignmentAxis}=typeof rawValue=="number"?{mainAxis:rawValue,crossAxis:0,alignmentAxis:null}:{mainAxis:rawValue.mainAxis||0,crossAxis:rawValue.crossAxis||0,alignmentAxis:rawValue.alignmentAxis};return alignment&&typeof alignmentAxis== -"number"&&(crossAxis=alignment==="end"?alignmentAxis*-1:alignmentAxis),isVertical?{x:crossAxis*crossAxisMulti,y:mainAxis*mainAxisMulti}:{x:mainAxis*mainAxisMulti,y:crossAxis*crossAxisMulti}}const offset$1=function(options){return options===void 0&&(options=0),{name:"offset",options,async fn(state2){var _middlewareData$offse,_middlewareData$arrow;const{x,y,placement,middlewareData}=state2,diffCoords=await convertValueToCoords(state2,options);return placement===((_middlewareData$offse=middlewareData. -offset)==null?void 0:_middlewareData$offse.placement)&&(_middlewareData$arrow=middlewareData.arrow)!=null&&_middlewareData$arrow.alignmentOffset?{}:{x:x+diffCoords.x,y:y+diffCoords.y,data:{...diffCoords,placement}}}}},shift$1=function(options){return options===void 0&&(options={}),{name:"shift",options,async fn(state2){const{x,y,placement}=state2,{mainAxis:checkMainAxis=!0,crossAxis:checkCrossAxis=!1,limiter={fn:_ref=>{let{x:x2,y:y2}=_ref;return{x:x2,y:y2}}},...detectOverflowOptions}=evaluate(options, -state2),coords={x,y},overflow=await detectOverflow(state2,detectOverflowOptions),crossAxis=getSideAxis(getSide$1(placement)),mainAxis=getOppositeAxis(crossAxis);let mainAxisCoord=coords[mainAxis],crossAxisCoord=coords[crossAxis];if(checkMainAxis){const minSide=mainAxis==="y"?"top":"left",maxSide=mainAxis==="y"?"bottom":"right",min2=mainAxisCoord+overflow[minSide],max2=mainAxisCoord-overflow[maxSide];mainAxisCoord=clamp$1(min2,mainAxisCoord,max2)}if(checkCrossAxis){const minSide=crossAxis==="y"?"\ -top":"left",maxSide=crossAxis==="y"?"bottom":"right",min2=crossAxisCoord+overflow[minSide],max2=crossAxisCoord-overflow[maxSide];crossAxisCoord=clamp$1(min2,crossAxisCoord,max2)}const limitedCoords=limiter.fn({...state2,[mainAxis]:mainAxisCoord,[crossAxis]:crossAxisCoord});return{...limitedCoords,data:{x:limitedCoords.x-x,y:limitedCoords.y-y,enabled:{[mainAxis]:checkMainAxis,[crossAxis]:checkCrossAxis}}}}}},limitShift$1=function(options){return options===void 0&&(options={}),{options,fn(state2){ -const{x,y,placement,rects,middlewareData}=state2,{offset:offset2=0,mainAxis:checkMainAxis=!0,crossAxis:checkCrossAxis=!0}=evaluate(options,state2),coords={x,y},crossAxis=getSideAxis(placement),mainAxis=getOppositeAxis(crossAxis);let mainAxisCoord=coords[mainAxis],crossAxisCoord=coords[crossAxis];const rawOffset=evaluate(offset2,state2),computedOffset=typeof rawOffset=="number"?{mainAxis:rawOffset,crossAxis:0}:{mainAxis:0,crossAxis:0,...rawOffset};if(checkMainAxis){const len=mainAxis==="y"?"heigh\ -t":"width",limitMin=rects.reference[mainAxis]-rects.floating[len]+computedOffset.mainAxis,limitMax=rects.reference[mainAxis]+rects.reference[len]-computedOffset.mainAxis;mainAxisCoordlimitMax&&(mainAxisCoord=limitMax)}if(checkCrossAxis){var _middlewareData$offse,_middlewareData$offse2;const len=mainAxis==="y"?"width":"height",isOriginSide=originSides.has(getSide$1(placement)),limitMin=rects.reference[crossAxis]-rects.floating[len]+(isOriginSide&&((_middlewareData$offse= -middlewareData.offset)==null?void 0:_middlewareData$offse[crossAxis])||0)+(isOriginSide?0:computedOffset.crossAxis),limitMax=rects.reference[crossAxis]+rects.reference[len]+(isOriginSide?0:((_middlewareData$offse2=middlewareData.offset)==null?void 0:_middlewareData$offse2[crossAxis])||0)-(isOriginSide?computedOffset.crossAxis:0);crossAxisCoordlimitMax&&(crossAxisCoord=limitMax)}return{[mainAxis]:mainAxisCoord,[crossAxis]:crossAxisCoord}}}},size$1=function(options){ -return options===void 0&&(options={}),{name:"size",options,async fn(state2){var _state$middlewareData,_state$middlewareData2;const{placement,rects,platform:platform2,elements}=state2,{apply=()=>{},...detectOverflowOptions}=evaluate(options,state2),overflow=await detectOverflow(state2,detectOverflowOptions),side=getSide$1(placement),alignment=getAlignment(placement),isYAxis=getSideAxis(placement)==="y",{width,height}=rects.floating;let heightSide,widthSide;side==="top"||side==="bottom"?(heightSide= -side,widthSide=alignment===(await(platform2.isRTL==null?void 0:platform2.isRTL(elements.floating))?"start":"end")?"left":"right"):(widthSide=side,heightSide=alignment==="end"?"top":"bottom");const maximumClippingHeight=height-overflow.top-overflow.bottom,maximumClippingWidth=width-overflow.left-overflow.right,overflowAvailableHeight=min(height-overflow[heightSide],maximumClippingHeight),overflowAvailableWidth=min(width-overflow[widthSide],maximumClippingWidth),noShift=!state2.middlewareData.shift; -let availableHeight=overflowAvailableHeight,availableWidth=overflowAvailableWidth;if((_state$middlewareData=state2.middlewareData.shift)!=null&&_state$middlewareData.enabled.x&&(availableWidth=maximumClippingWidth),(_state$middlewareData2=state2.middlewareData.shift)!=null&&_state$middlewareData2.enabled.y&&(availableHeight=maximumClippingHeight),noShift&&!alignment){const xMin=max(overflow.left,0),xMax=max(overflow.right,0),yMin=max(overflow.top,0),yMax=max(overflow.bottom,0);isYAxis?availableWidth= -width-2*(xMin!==0||xMax!==0?xMin+xMax:max(overflow.left,overflow.right)):availableHeight=height-2*(yMin!==0||yMax!==0?yMin+yMax:max(overflow.top,overflow.bottom))}await apply({...state2,availableWidth,availableHeight});const nextDimensions=await platform2.getDimensions(elements.floating);return width!==nextDimensions.width||height!==nextDimensions.height?{reset:{rects:!0}}:{}}}};function hasWindow(){return typeof window<"u"}function getNodeName(node2){return isNode(node2)?(node2.nodeName||"").toLowerCase(): -"#document"}function getWindow(node2){var _node$ownerDocument;return(node2==null||(_node$ownerDocument=node2.ownerDocument)==null?void 0:_node$ownerDocument.defaultView)||window}function getDocumentElement(node2){var _ref;return(_ref=(isNode(node2)?node2.ownerDocument:node2.document)||window.document)==null?void 0:_ref.documentElement}function isNode(value){return hasWindow()?value instanceof Node||value instanceof getWindow(value).Node:!1}function isElement(value){return hasWindow()?value instanceof -Element||value instanceof getWindow(value).Element:!1}function isHTMLElement(value){return hasWindow()?value instanceof HTMLElement||value instanceof getWindow(value).HTMLElement:!1}function isShadowRoot(value){return!hasWindow()||typeof ShadowRoot>"u"?!1:value instanceof ShadowRoot||value instanceof getWindow(value).ShadowRoot}const invalidOverflowDisplayValues=new Set(["inline","contents"]);function isOverflowElement(element2){const{overflow,overflowX,overflowY,display}=getComputedStyle$1(element2); -return/auto|scroll|overlay|hidden|clip/.test(overflow+overflowY+overflowX)&&!invalidOverflowDisplayValues.has(display)}const tableElements=new Set(["table","td","th"]);function isTableElement(element2){return tableElements.has(getNodeName(element2))}const topLayerSelectors=[":popover-open",":modal"];function isTopLayer(element2){return topLayerSelectors.some(selector=>{try{return element2.matches(selector)}catch{return!1}})}const transformProperties=["transform","translate","scale","rotate","per\ -spective"],willChangeValues=["transform","translate","scale","rotate","perspective","filter"],containValues=["paint","layout","strict","content"];function isContainingBlock(elementOrCss){const webkit=isWebKit(),css2=isElement(elementOrCss)?getComputedStyle$1(elementOrCss):elementOrCss;return transformProperties.some(value=>css2[value]?css2[value]!=="none":!1)||(css2.containerType?css2.containerType!=="normal":!1)||!webkit&&(css2.backdropFilter?css2.backdropFilter!=="none":!1)||!webkit&&(css2.filter? -css2.filter!=="none":!1)||willChangeValues.some(value=>(css2.willChange||"").includes(value))||containValues.some(value=>(css2.contain||"").includes(value))}function getContainingBlock(element2){let currentNode=getParentNode(element2);for(;isHTMLElement(currentNode)&&!isLastTraversableNode(currentNode);){if(isContainingBlock(currentNode))return currentNode;if(isTopLayer(currentNode))return null;currentNode=getParentNode(currentNode)}return null}function isWebKit(){return typeof CSS>"u"||!CSS.supports? -!1:CSS.supports("-webkit-backdrop-filter","none")}const lastTraversableNodeNames=new Set(["html","body","#document"]);function isLastTraversableNode(node2){return lastTraversableNodeNames.has(getNodeName(node2))}function getComputedStyle$1(element2){return getWindow(element2).getComputedStyle(element2)}function getNodeScroll(element2){return isElement(element2)?{scrollLeft:element2.scrollLeft,scrollTop:element2.scrollTop}:{scrollLeft:element2.scrollX,scrollTop:element2.scrollY}}function getParentNode(node2){ -if(getNodeName(node2)==="html")return node2;const result=node2.assignedSlot||node2.parentNode||isShadowRoot(node2)&&node2.host||getDocumentElement(node2);return isShadowRoot(result)?result.host:result}function getNearestOverflowAncestor(node2){const parentNode=getParentNode(node2);return isLastTraversableNode(parentNode)?node2.ownerDocument?node2.ownerDocument.body:node2.body:isHTMLElement(parentNode)&&isOverflowElement(parentNode)?parentNode:getNearestOverflowAncestor(parentNode)}function getOverflowAncestors(node2,list2,traverseIframes){ -var _node$ownerDocument2;list2===void 0&&(list2=[]),traverseIframes===void 0&&(traverseIframes=!0);const scrollableAncestor=getNearestOverflowAncestor(node2),isBody=scrollableAncestor===((_node$ownerDocument2=node2.ownerDocument)==null?void 0:_node$ownerDocument2.body),win=getWindow(scrollableAncestor);if(isBody){const frameElement=getFrameElement(win);return list2.concat(win,win.visualViewport||[],isOverflowElement(scrollableAncestor)?scrollableAncestor:[],frameElement&&traverseIframes?getOverflowAncestors( -frameElement):[])}return list2.concat(scrollableAncestor,getOverflowAncestors(scrollableAncestor,[],traverseIframes))}function getFrameElement(win){return win.parent&&Object.getPrototypeOf(win.parent)?win.frameElement:null}function getCssDimensions(element2){const css2=getComputedStyle$1(element2);let width=parseFloat(css2.width)||0,height=parseFloat(css2.height)||0;const hasOffset=isHTMLElement(element2),offsetWidth=hasOffset?element2.offsetWidth:width,offsetHeight=hasOffset?element2.offsetHeight: -height,shouldFallback=round(width)!==offsetWidth||round(height)!==offsetHeight;return shouldFallback&&(width=offsetWidth,height=offsetHeight),{width,height,$:shouldFallback}}function unwrapElement(element2){return isElement(element2)?element2:element2.contextElement}function getScale(element2){const domElement=unwrapElement(element2);if(!isHTMLElement(domElement))return createCoords(1);const rect=domElement.getBoundingClientRect(),{width,height,$}=getCssDimensions(domElement);let x=($?round(rect. -width):rect.width)/width,y=($?round(rect.height):rect.height)/height;return(!x||!Number.isFinite(x))&&(x=1),(!y||!Number.isFinite(y))&&(y=1),{x,y}}const noOffsets=createCoords(0);function getVisualOffsets(element2){const win=getWindow(element2);return!isWebKit()||!win.visualViewport?noOffsets:{x:win.visualViewport.offsetLeft,y:win.visualViewport.offsetTop}}function shouldAddVisualOffsets(element2,isFixed,floatingOffsetParent){return isFixed===void 0&&(isFixed=!1),!floatingOffsetParent||isFixed&& -floatingOffsetParent!==getWindow(element2)?!1:isFixed}function getBoundingClientRect(element2,includeScale,isFixedStrategy,offsetParent){includeScale===void 0&&(includeScale=!1),isFixedStrategy===void 0&&(isFixedStrategy=!1);const clientRect=element2.getBoundingClientRect(),domElement=unwrapElement(element2);let scale2=createCoords(1);includeScale&&(offsetParent?isElement(offsetParent)&&(scale2=getScale(offsetParent)):scale2=getScale(element2));const visualOffsets=shouldAddVisualOffsets(domElement, -isFixedStrategy,offsetParent)?getVisualOffsets(domElement):createCoords(0);let x=(clientRect.left+visualOffsets.x)/scale2.x,y=(clientRect.top+visualOffsets.y)/scale2.y,width=clientRect.width/scale2.x,height=clientRect.height/scale2.y;if(domElement){const win=getWindow(domElement),offsetWin=offsetParent&&isElement(offsetParent)?getWindow(offsetParent):offsetParent;let currentWin=win,currentIFrame=getFrameElement(currentWin);for(;currentIFrame&&offsetParent&&offsetWin!==currentWin;){const iframeScale=getScale( -currentIFrame),iframeRect=currentIFrame.getBoundingClientRect(),css2=getComputedStyle$1(currentIFrame),left=iframeRect.left+(currentIFrame.clientLeft+parseFloat(css2.paddingLeft))*iframeScale.x,top=iframeRect.top+(currentIFrame.clientTop+parseFloat(css2.paddingTop))*iframeScale.y;x*=iframeScale.x,y*=iframeScale.y,width*=iframeScale.x,height*=iframeScale.y,x+=left,y+=top,currentWin=getWindow(currentIFrame),currentIFrame=getFrameElement(currentWin)}}return rectToClientRect({width,height,x,y})}function getWindowScrollBarX(element2,rect){ -const leftScroll=getNodeScroll(element2).scrollLeft;return rect?rect.left+leftScroll:getBoundingClientRect(getDocumentElement(element2)).left+leftScroll}function getHTMLOffset(documentElement,scroll,ignoreScrollbarX){ignoreScrollbarX===void 0&&(ignoreScrollbarX=!1);const htmlRect=documentElement.getBoundingClientRect(),x=htmlRect.left+scroll.scrollLeft-(ignoreScrollbarX?0:getWindowScrollBarX(documentElement,htmlRect)),y=htmlRect.top+scroll.scrollTop;return{x,y}}function convertOffsetParentRelativeRectToViewportRelativeRect(_ref){ -let{elements,rect,offsetParent,strategy}=_ref;const isFixed=strategy==="fixed",documentElement=getDocumentElement(offsetParent),topLayer=elements?isTopLayer(elements.floating):!1;if(offsetParent===documentElement||topLayer&&isFixed)return rect;let scroll={scrollLeft:0,scrollTop:0},scale2=createCoords(1);const offsets=createCoords(0),isOffsetParentAnElement=isHTMLElement(offsetParent);if((isOffsetParentAnElement||!isOffsetParentAnElement&&!isFixed)&&((getNodeName(offsetParent)!=="body"||isOverflowElement( -documentElement))&&(scroll=getNodeScroll(offsetParent)),isHTMLElement(offsetParent))){const offsetRect=getBoundingClientRect(offsetParent);scale2=getScale(offsetParent),offsets.x=offsetRect.x+offsetParent.clientLeft,offsets.y=offsetRect.y+offsetParent.clientTop}const htmlOffset=documentElement&&!isOffsetParentAnElement&&!isFixed?getHTMLOffset(documentElement,scroll,!0):createCoords(0);return{width:rect.width*scale2.x,height:rect.height*scale2.y,x:rect.x*scale2.x-scroll.scrollLeft*scale2.x+offsets. -x+htmlOffset.x,y:rect.y*scale2.y-scroll.scrollTop*scale2.y+offsets.y+htmlOffset.y}}function getClientRects(element2){return Array.from(element2.getClientRects())}function getDocumentRect(element2){const html2=getDocumentElement(element2),scroll=getNodeScroll(element2),body2=element2.ownerDocument.body,width=max(html2.scrollWidth,html2.clientWidth,body2.scrollWidth,body2.clientWidth),height=max(html2.scrollHeight,html2.clientHeight,body2.scrollHeight,body2.clientHeight);let x=-scroll.scrollLeft+getWindowScrollBarX( -element2);const y=-scroll.scrollTop;return getComputedStyle$1(body2).direction==="rtl"&&(x+=max(html2.clientWidth,body2.clientWidth)-width),{width,height,x,y}}function getViewportRect(element2,strategy){const win=getWindow(element2),html2=getDocumentElement(element2),visualViewport=win.visualViewport;let width=html2.clientWidth,height=html2.clientHeight,x=0,y=0;if(visualViewport){width=visualViewport.width,height=visualViewport.height;const visualViewportBased=isWebKit();(!visualViewportBased||visualViewportBased&& -strategy==="fixed")&&(x=visualViewport.offsetLeft,y=visualViewport.offsetTop)}return{width,height,x,y}}const absoluteOrFixed=new Set(["absolute","fixed"]);function getInnerBoundingClientRect(element2,strategy){const clientRect=getBoundingClientRect(element2,!0,strategy==="fixed"),top=clientRect.top+element2.clientTop,left=clientRect.left+element2.clientLeft,scale2=isHTMLElement(element2)?getScale(element2):createCoords(1),width=element2.clientWidth*scale2.x,height=element2.clientHeight*scale2.y, -x=left*scale2.x,y=top*scale2.y;return{width,height,x,y}}function getClientRectFromClippingAncestor(element2,clippingAncestor,strategy){let rect;if(clippingAncestor==="viewport")rect=getViewportRect(element2,strategy);else if(clippingAncestor==="document")rect=getDocumentRect(getDocumentElement(element2));else if(isElement(clippingAncestor))rect=getInnerBoundingClientRect(clippingAncestor,strategy);else{const visualOffsets=getVisualOffsets(element2);rect={x:clippingAncestor.x-visualOffsets.x,y:clippingAncestor. -y-visualOffsets.y,width:clippingAncestor.width,height:clippingAncestor.height}}return rectToClientRect(rect)}function hasFixedPositionAncestor(element2,stopNode){const parentNode=getParentNode(element2);return parentNode===stopNode||!isElement(parentNode)||isLastTraversableNode(parentNode)?!1:getComputedStyle$1(parentNode).position==="fixed"||hasFixedPositionAncestor(parentNode,stopNode)}function getClippingElementAncestors(element2,cache2){const cachedResult=cache2.get(element2);if(cachedResult) -return cachedResult;let result=getOverflowAncestors(element2,[],!1).filter(el=>isElement(el)&&getNodeName(el)!=="body"),currentContainingBlockComputedStyle=null;const elementIsFixed=getComputedStyle$1(element2).position==="fixed";let currentNode=elementIsFixed?getParentNode(element2):element2;for(;isElement(currentNode)&&!isLastTraversableNode(currentNode);){const computedStyle=getComputedStyle$1(currentNode),currentNodeIsContaining=isContainingBlock(currentNode);!currentNodeIsContaining&&computedStyle. -position==="fixed"&&(currentContainingBlockComputedStyle=null),(elementIsFixed?!currentNodeIsContaining&&!currentContainingBlockComputedStyle:!currentNodeIsContaining&&computedStyle.position==="static"&&!!currentContainingBlockComputedStyle&&absoluteOrFixed.has(currentContainingBlockComputedStyle.position)||isOverflowElement(currentNode)&&!currentNodeIsContaining&&hasFixedPositionAncestor(element2,currentNode))?result=result.filter(ancestor=>ancestor!==currentNode):currentContainingBlockComputedStyle= -computedStyle,currentNode=getParentNode(currentNode)}return cache2.set(element2,result),result}function getClippingRect(_ref){let{element:element2,boundary:boundary2,rootBoundary,strategy}=_ref;const clippingAncestors=[...boundary2==="clippingAncestors"?isTopLayer(element2)?[]:getClippingElementAncestors(element2,this._c):[].concat(boundary2),rootBoundary],firstClippingAncestor=clippingAncestors[0],clippingRect=clippingAncestors.reduce((accRect,clippingAncestor)=>{const rect=getClientRectFromClippingAncestor( -element2,clippingAncestor,strategy);return accRect.top=max(rect.top,accRect.top),accRect.right=min(rect.right,accRect.right),accRect.bottom=min(rect.bottom,accRect.bottom),accRect.left=max(rect.left,accRect.left),accRect},getClientRectFromClippingAncestor(element2,firstClippingAncestor,strategy));return{width:clippingRect.right-clippingRect.left,height:clippingRect.bottom-clippingRect.top,x:clippingRect.left,y:clippingRect.top}}function getDimensions(element2){const{width,height}=getCssDimensions( -element2);return{width,height}}function getRectRelativeToOffsetParent(element2,offsetParent,strategy){const isOffsetParentAnElement=isHTMLElement(offsetParent),documentElement=getDocumentElement(offsetParent),isFixed=strategy==="fixed",rect=getBoundingClientRect(element2,!0,isFixed,offsetParent);let scroll={scrollLeft:0,scrollTop:0};const offsets=createCoords(0);function setLeftRTLScrollbarOffset(){offsets.x=getWindowScrollBarX(documentElement)}if(isOffsetParentAnElement||!isOffsetParentAnElement&& -!isFixed)if((getNodeName(offsetParent)!=="body"||isOverflowElement(documentElement))&&(scroll=getNodeScroll(offsetParent)),isOffsetParentAnElement){const offsetRect=getBoundingClientRect(offsetParent,!0,isFixed,offsetParent);offsets.x=offsetRect.x+offsetParent.clientLeft,offsets.y=offsetRect.y+offsetParent.clientTop}else documentElement&&setLeftRTLScrollbarOffset();isFixed&&!isOffsetParentAnElement&&documentElement&&setLeftRTLScrollbarOffset();const htmlOffset=documentElement&&!isOffsetParentAnElement&& -!isFixed?getHTMLOffset(documentElement,scroll):createCoords(0),x=rect.left+scroll.scrollLeft-offsets.x-htmlOffset.x,y=rect.top+scroll.scrollTop-offsets.y-htmlOffset.y;return{x,y,width:rect.width,height:rect.height}}function isStaticPositioned(element2){return getComputedStyle$1(element2).position==="static"}function getTrueOffsetParent(element2,polyfill){if(!isHTMLElement(element2)||getComputedStyle$1(element2).position==="fixed")return null;if(polyfill)return polyfill(element2);let rawOffsetParent=element2. -offsetParent;return getDocumentElement(element2)===rawOffsetParent&&(rawOffsetParent=rawOffsetParent.ownerDocument.body),rawOffsetParent}function getOffsetParent(element2,polyfill){const win=getWindow(element2);if(isTopLayer(element2))return win;if(!isHTMLElement(element2)){let svgOffsetParent=getParentNode(element2);for(;svgOffsetParent&&!isLastTraversableNode(svgOffsetParent);){if(isElement(svgOffsetParent)&&!isStaticPositioned(svgOffsetParent))return svgOffsetParent;svgOffsetParent=getParentNode( -svgOffsetParent)}return win}let offsetParent=getTrueOffsetParent(element2,polyfill);for(;offsetParent&&isTableElement(offsetParent)&&isStaticPositioned(offsetParent);)offsetParent=getTrueOffsetParent(offsetParent,polyfill);return offsetParent&&isLastTraversableNode(offsetParent)&&isStaticPositioned(offsetParent)&&!isContainingBlock(offsetParent)?win:offsetParent||getContainingBlock(element2)||win}const getElementRects=async function(data){const getOffsetParentFn=this.getOffsetParent||getOffsetParent, -getDimensionsFn=this.getDimensions,floatingDimensions=await getDimensionsFn(data.floating);return{reference:getRectRelativeToOffsetParent(data.reference,await getOffsetParentFn(data.floating),data.strategy),floating:{x:0,y:0,width:floatingDimensions.width,height:floatingDimensions.height}}};function isRTL(element2){return getComputedStyle$1(element2).direction==="rtl"}const platform={convertOffsetParentRelativeRectToViewportRelativeRect,getDocumentElement,getClippingRect,getOffsetParent,getElementRects, -getClientRects,getDimensions,getScale,isElement,isRTL};function rectsAreEqual(a,b){return a.x===b.x&&a.y===b.y&&a.width===b.width&&a.height===b.height}function observeMove(element2,onMove){let io=null,timeoutId;const root2=getDocumentElement(element2);function cleanup(){var _io;clearTimeout(timeoutId),(_io=io)==null||_io.disconnect(),io=null}function refresh(skip,threshold){skip===void 0&&(skip=!1),threshold===void 0&&(threshold=1),cleanup();const elementRectForRootMargin=element2.getBoundingClientRect(), -{left,top,width,height}=elementRectForRootMargin;if(skip||onMove(),!width||!height)return;const insetTop=floor(top),insetRight=floor(root2.clientWidth-(left+width)),insetBottom=floor(root2.clientHeight-(top+height)),insetLeft=floor(left),options={rootMargin:-insetTop+"px "+-insetRight+"px "+-insetBottom+"px "+-insetLeft+"px",threshold:max(0,min(1,threshold))||1};let isFirstUpdate=!0;function handleObserve(entries){const ratio=entries[0].intersectionRatio;if(ratio!==threshold){if(!isFirstUpdate)return refresh(); -ratio?refresh(!1,ratio):timeoutId=setTimeout(()=>{refresh(!1,1e-7)},1e3)}ratio===1&&!rectsAreEqual(elementRectForRootMargin,element2.getBoundingClientRect())&&refresh(),isFirstUpdate=!1}try{io=new IntersectionObserver(handleObserve,{...options,root:root2.ownerDocument})}catch{io=new IntersectionObserver(handleObserve,options)}io.observe(element2)}return refresh(!0),cleanup}function autoUpdate(reference,floating,update2,options){options===void 0&&(options={});const{ancestorScroll=!0,ancestorResize=!0, -elementResize=typeof ResizeObserver=="function",layoutShift=typeof IntersectionObserver=="function",animationFrame=!1}=options,referenceEl=unwrapElement(reference),ancestors=ancestorScroll||ancestorResize?[...referenceEl?getOverflowAncestors(referenceEl):[],...getOverflowAncestors(floating)]:[];ancestors.forEach(ancestor=>{ancestorScroll&&ancestor.addEventListener("scroll",update2,{passive:!0}),ancestorResize&&ancestor.addEventListener("resize",update2)});const cleanupIo=referenceEl&&layoutShift? -observeMove(referenceEl,update2):null;let reobserveFrame=-1,resizeObserver=null;elementResize&&(resizeObserver=new ResizeObserver(_ref=>{let[firstEntry]=_ref;firstEntry&&firstEntry.target===referenceEl&&resizeObserver&&(resizeObserver.unobserve(floating),cancelAnimationFrame(reobserveFrame),reobserveFrame=requestAnimationFrame(()=>{var _resizeObserver;(_resizeObserver=resizeObserver)==null||_resizeObserver.observe(floating)})),update2()}),referenceEl&&!animationFrame&&resizeObserver.observe(referenceEl), -resizeObserver.observe(floating));let frameId,prevRefRect=animationFrame?getBoundingClientRect(reference):null;animationFrame&&frameLoop();function frameLoop(){const nextRefRect=getBoundingClientRect(reference);prevRefRect&&!rectsAreEqual(prevRefRect,nextRefRect)&&update2(),prevRefRect=nextRefRect,frameId=requestAnimationFrame(frameLoop)}return update2(),()=>{var _resizeObserver2;ancestors.forEach(ancestor=>{ancestorScroll&&ancestor.removeEventListener("scroll",update2),ancestorResize&&ancestor. -removeEventListener("resize",update2)}),cleanupIo?.(),(_resizeObserver2=resizeObserver)==null||_resizeObserver2.disconnect(),resizeObserver=null,animationFrame&&cancelAnimationFrame(frameId)}}const offset=offset$1,shift=shift$1,flip=flip$1,size=size$1,hide=hide$1,arrow=arrow$1,limitShift=limitShift$1,computePosition=(reference,floating,options)=>{const cache2=new Map,mergedOptions={platform,...options},platformWithCache={...mergedOptions.platform,_c:cache2};return computePosition$1(reference,floating, -{...mergedOptions,platform:platformWithCache})};function get(valueOrGetValue){return typeof valueOrGetValue=="function"?valueOrGetValue():valueOrGetValue}function getDPR(element2){return typeof window>"u"?1:(element2.ownerDocument.defaultView||window).devicePixelRatio||1}function roundByDPR(element2,value){const dpr=getDPR(element2);return Math.round(value*dpr)/dpr}function getFloatingContentCSSVars(name){return{[`--bits-${name}-content-transform-origin`]:"var(--bits-floating-transform-origin)", -[`--bits-${name}-content-available-width`]:"var(--bits-floating-available-width)",[`--bits-${name}-content-available-height`]:"var(--bits-floating-available-height)",[`--bits-${name}-anchor-width`]:"var(--bits-floating-anchor-width)",[`--bits-${name}-anchor-height`]:"var(--bits-floating-anchor-height)"}}function useFloating(options){const whileElementsMountedOption=options.whileElementsMounted,openOption=user_derived(()=>get(options.open)??!0),middlewareOption=user_derived(()=>get(options.middleware)), -transformOption=user_derived(()=>get(options.transform)??!0),placementOption=user_derived(()=>get(options.placement)??"bottom"),strategyOption=user_derived(()=>get(options.strategy)??"absolute"),sideOffsetOption=user_derived(()=>get(options.sideOffset)??0),alignOffsetOption=user_derived(()=>get(options.alignOffset)??0),reference=options.reference;let x=state$1(0),y=state$1(0);const floating=simpleBox(null);let strategy=state$1(proxy(get$3(strategyOption))),placement=state$1(proxy(get$3(placementOption))), -middlewareData=state$1(proxy({})),isPositioned=state$1(!1),hasWhileMountedPosition=!1,updateRequestId=0;const floatingStyles=user_derived(()=>{const xVal=floating.current?roundByDPR(floating.current,get$3(x)):get$3(x),yVal=floating.current?roundByDPR(floating.current,get$3(y)):get$3(y);return get$3(transformOption)?{position:get$3(strategy),left:"0",top:"0",transform:`translate(${xVal}px, ${yVal}px)`,...floating.current&&getDPR(floating.current)>=1.5&&{willChange:"transform"}}:{position:get$3(strategy), -left:`${xVal}px`,top:`${yVal}px`}});let whileElementsMountedCleanup;function update2(){if(reference.current===null||floating.current===null)return;const referenceNode=reference.current,floatingNode=floating.current,requestId=++updateRequestId;computePosition(referenceNode,floatingNode,{middleware:get$3(middlewareOption),placement:get$3(placementOption),strategy:get$3(strategyOption)}).then(position2=>{if(requestId!==updateRequestId||reference.current!==referenceNode||floating.current!==floatingNode) -return;if(isReferenceHidden(referenceNode)){set$1(middlewareData,{...get$3(middlewareData),hide:{...get$3(middlewareData).hide,referenceHidden:!0}},!0);return}if(!get$3(openOption)&&get$3(x)!==0&&get$3(y)!==0){const maxExpectedOffset=Math.max(Math.abs(get$3(sideOffsetOption)),Math.abs(get$3(alignOffsetOption)),15);if(position2.x<=maxExpectedOffset&&position2.y<=maxExpectedOffset)return}set$1(x,position2.x,!0),set$1(y,position2.y,!0),set$1(strategy,position2.strategy,!0),set$1(placement,position2. -placement,!0),set$1(middlewareData,position2.middlewareData,!0),set$1(isPositioned,!0)})}function cleanup(){typeof whileElementsMountedCleanup=="function"&&(whileElementsMountedCleanup(),whileElementsMountedCleanup=void 0),updateRequestId++}function attach2(){if(cleanup(),whileElementsMountedOption===void 0){update2();return}get$3(openOption)&&(reference.current===null||floating.current===null||(whileElementsMountedCleanup=whileElementsMountedOption(reference.current,floating.current,update2)))} -function reset2(){!get$3(openOption)&&floating.current===null&&set$1(isPositioned,!1)}function trackWhileMountedDeps(){return[get$3(middlewareOption),get$3(placementOption),get$3(strategyOption),get$3(sideOffsetOption),get$3(alignOffsetOption),get$3(openOption)]}return user_effect(()=>{whileElementsMountedOption===void 0&&get$3(openOption)&&update2()}),user_effect(attach2),user_effect(()=>{if(whileElementsMountedOption!==void 0){if(trackWhileMountedDeps(),!get$3(openOption)){hasWhileMountedPosition= -!1;return}if(!get$3(isPositioned)){hasWhileMountedPosition=!1;return}if(!hasWhileMountedPosition){hasWhileMountedPosition=!0;return}update2()}}),user_effect(reset2),user_effect(()=>cleanup),{floating,reference,get strategy(){return get$3(strategy)},get placement(){return get$3(placement)},get middlewareData(){return get$3(middlewareData)},get isPositioned(){return get$3(isPositioned)},get floatingStyles(){return get$3(floatingStyles)},get update(){return update2}}}function isReferenceHidden(node2){ -return node2 instanceof Element?!node2.isConnected||node2 instanceof HTMLElement&&node2.hidden?!0:node2.getClientRects().length===0:!1}const OPPOSITE_SIDE={top:"bottom",right:"left",bottom:"top",left:"right"},FloatingRootContext=new Context$1("Floating.Root"),FloatingContentContext=new Context$1("Floating.Content"),FloatingTooltipRootContext=new Context$1("Floating.Root");class FloatingRootState{static create(tooltip=!1){return tooltip?FloatingTooltipRootContext.set(new FloatingRootState):FloatingRootContext. -set(new FloatingRootState)}anchorNode=simpleBox(null);customAnchorNode=simpleBox(null);triggerNode=simpleBox(null);constructor(){user_effect(()=>{this.customAnchorNode.current?typeof this.customAnchorNode.current=="string"?this.anchorNode.current=document.querySelector(this.customAnchorNode.current):this.anchorNode.current=this.customAnchorNode.current:this.anchorNode.current=this.triggerNode.current})}}class FloatingContentState{static create(opts,tooltip=!1){return tooltip?FloatingContentContext. -set(new FloatingContentState(opts,FloatingTooltipRootContext.get())):FloatingContentContext.set(new FloatingContentState(opts,FloatingRootContext.get()))}opts;root;contentRef=simpleBox(null);wrapperRef=simpleBox(null);arrowRef=simpleBox(null);contentAttachment=attachRef(this.contentRef);wrapperAttachment=attachRef(this.wrapperRef);arrowAttachment=attachRef(this.arrowRef);arrowId=simpleBox(useId());#transformedStyle=user_derived(()=>{if(typeof this.opts.style=="string")return cssToStyleObj(this.opts. -style);if(!this.opts.style)return{}});#updatePositionStrategy=void 0;#arrowSize=new ElementSize(()=>this.arrowRef.current??void 0);#arrowWidth=user_derived(()=>this.#arrowSize?.width??0);#arrowHeight=user_derived(()=>this.#arrowSize?.height??0);#desiredPlacement=user_derived(()=>this.opts.side?.current+(this.opts.align.current!=="center"?`-${this.opts.align.current}`:""));#boundary=user_derived(()=>Array.isArray(this.opts.collisionBoundary.current)?this.opts.collisionBoundary.current:[this.opts. -collisionBoundary.current]);#hasExplicitBoundaries=user_derived(()=>get$3(this.#boundary).length>0);get hasExplicitBoundaries(){return get$3(this.#hasExplicitBoundaries)}set hasExplicitBoundaries(value){set$1(this.#hasExplicitBoundaries,value)}#detectOverflowOptions=user_derived(()=>({padding:this.opts.collisionPadding.current,boundary:get$3(this.#boundary).filter(isNotNull),altBoundary:this.hasExplicitBoundaries}));get detectOverflowOptions(){return get$3(this.#detectOverflowOptions)}set detectOverflowOptions(value){ -set$1(this.#detectOverflowOptions,value)}#availableWidth=state$1(void 0);#availableHeight=state$1(void 0);#anchorWidth=state$1(void 0);#anchorHeight=state$1(void 0);#middleware=user_derived(()=>[offset({mainAxis:this.opts.sideOffset.current+get$3(this.#arrowHeight),alignmentAxis:this.opts.alignOffset.current}),this.opts.avoidCollisions.current&&shift({mainAxis:!0,crossAxis:!1,limiter:this.opts.sticky.current==="partial"?limitShift():void 0,...this.detectOverflowOptions}),this.opts.avoidCollisions. -current&&flip({...this.detectOverflowOptions}),size({...this.detectOverflowOptions,apply:({rects,availableWidth,availableHeight})=>{const{width:anchorWidth,height:anchorHeight}=rects.reference;set$1(this.#availableWidth,availableWidth,!0),set$1(this.#availableHeight,availableHeight,!0),set$1(this.#anchorWidth,anchorWidth,!0),set$1(this.#anchorHeight,anchorHeight,!0)}}),this.arrowRef.current&&arrow({element:this.arrowRef.current,padding:this.opts.arrowPadding.current}),transformOrigin({arrowWidth:get$3( -this.#arrowWidth),arrowHeight:get$3(this.#arrowHeight)}),this.opts.hideWhenDetached.current&&hide({strategy:"referenceHidden",...this.detectOverflowOptions})].filter(Boolean));get middleware(){return get$3(this.#middleware)}set middleware(value){set$1(this.#middleware,value)}floating;#placedSide=user_derived(()=>getSideFromPlacement(this.floating.placement));get placedSide(){return get$3(this.#placedSide)}set placedSide(value){set$1(this.#placedSide,value)}#placedAlign=user_derived(()=>getAlignFromPlacement( -this.floating.placement));get placedAlign(){return get$3(this.#placedAlign)}set placedAlign(value){set$1(this.#placedAlign,value)}#arrowX=user_derived(()=>this.floating.middlewareData.arrow?.x??0);get arrowX(){return get$3(this.#arrowX)}set arrowX(value){set$1(this.#arrowX,value)}#arrowY=user_derived(()=>this.floating.middlewareData.arrow?.y??0);get arrowY(){return get$3(this.#arrowY)}set arrowY(value){set$1(this.#arrowY,value)}#cannotCenterArrow=user_derived(()=>this.floating.middlewareData.arrow?. -centerOffset!==0);get cannotCenterArrow(){return get$3(this.#cannotCenterArrow)}set cannotCenterArrow(value){set$1(this.#cannotCenterArrow,value)}#contentZIndex=state$1();get contentZIndex(){return get$3(this.#contentZIndex)}set contentZIndex(value){set$1(this.#contentZIndex,value,!0)}#arrowBaseSide=user_derived(()=>OPPOSITE_SIDE[this.placedSide]);get arrowBaseSide(){return get$3(this.#arrowBaseSide)}set arrowBaseSide(value){set$1(this.#arrowBaseSide,value)}#wrapperProps=user_derived(()=>({id:this. -opts.wrapperId.current,"data-bits-floating-content-wrapper":"",style:{...this.floating.floatingStyles,transform:this.floating.isPositioned?this.floating.floatingStyles.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:this.contentZIndex,"--bits-floating-transform-origin":`${this.floating.middlewareData.transformOrigin?.x} ${this.floating.middlewareData.transformOrigin?.y}`,"--bits-floating-available-width":`${get$3(this.#availableWidth)}px`,"--bits-floating-available-height":`${get$3( -this.#availableHeight)}px`,"--bits-floating-anchor-width":`${get$3(this.#anchorWidth)}px`,"--bits-floating-anchor-height":`${get$3(this.#anchorHeight)}px`,...this.floating.middlewareData.hide?.referenceHidden&&{visibility:"hidden","pointer-events":"none"},...get$3(this.#transformedStyle)},dir:this.opts.dir.current,...this.wrapperAttachment}));get wrapperProps(){return get$3(this.#wrapperProps)}set wrapperProps(value){set$1(this.#wrapperProps,value)}#props=user_derived(()=>({"data-side":this.placedSide, -"data-align":this.placedAlign,style:styleToString$1({...get$3(this.#transformedStyle)}),...this.contentAttachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}#arrowStyle=user_derived(()=>({position:"absolute",left:this.arrowX?`${this.arrowX}px`:void 0,top:this.arrowY?`${this.arrowY}px`:void 0,[this.arrowBaseSide]:0,"transform-origin":{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[this.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%\ -) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[this.placedSide],visibility:this.cannotCenterArrow?"hidden":void 0}));get arrowStyle(){return get$3(this.#arrowStyle)}set arrowStyle(value){set$1(this.#arrowStyle,value)}constructor(opts,root2){this.opts=opts,this.root=root2,this.#updatePositionStrategy=opts.updatePositionStrategy,opts.customAnchor&&(this.root.customAnchorNode.current=opts.customAnchor.current),watch$1(()=>opts.customAnchor. -current,customAnchor=>{this.root.customAnchorNode.current=customAnchor}),this.floating=useFloating({strategy:()=>this.opts.strategy.current,placement:()=>get$3(this.#desiredPlacement),middleware:()=>this.middleware,reference:this.root.anchorNode,whileElementsMounted:(...args)=>autoUpdate(...args,{animationFrame:this.#updatePositionStrategy?.current==="always"}),open:()=>this.opts.enabled.current,sideOffset:()=>this.opts.sideOffset.current,alignOffset:()=>this.opts.alignOffset.current}),user_effect( -()=>{this.floating.isPositioned&&this.opts.onPlaced?.current()}),watch$1(()=>this.contentRef.current,contentNode=>{if(!contentNode||!this.opts.enabled.current)return;const win=getWindow$1(contentNode),rafId=win.requestAnimationFrame(()=>{if(this.contentRef.current!==contentNode||!this.opts.enabled.current)return;const zIndex=win.getComputedStyle(contentNode).zIndex;zIndex!==this.contentZIndex&&(this.contentZIndex=zIndex)});return()=>{win.cancelAnimationFrame(rafId)}}),user_effect(()=>{this.floating. -floating.current=this.wrapperRef.current})}}class FloatingArrowState{static create(opts){return new FloatingArrowState(opts,FloatingContentContext.get())}opts;content;constructor(opts,content2){this.opts=opts,this.content=content2}#props=user_derived(()=>({id:this.opts.id.current,style:this.content.arrowStyle,"data-side":this.content.placedSide,...this.content.arrowAttachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class FloatingAnchorState{static create(opts,tooltip=!1){ -return tooltip?new FloatingAnchorState(opts,FloatingTooltipRootContext.get()):new FloatingAnchorState(opts,FloatingRootContext.get())}opts;root;constructor(opts,root2){this.opts=opts,this.root=root2,opts.virtualEl&&opts.virtualEl.current?root2.triggerNode=boxFrom$1(opts.virtualEl.current):root2.triggerNode=opts.ref}}function transformOrigin(options){return{name:"transformOrigin",options,fn(data){const{placement,rects,middlewareData}=data,isArrowHidden=middlewareData.arrow?.centerOffset!==0,arrowWidth=isArrowHidden? -0:options.arrowWidth,arrowHeight=isArrowHidden?0:options.arrowHeight,[placedSide,placedAlign]=getSideAndAlignFromPlacement(placement),noArrowAlign={start:"0%",center:"50%",end:"100%"}[placedAlign],arrowXCenter=(middlewareData.arrow?.x??0)+arrowWidth/2,arrowYCenter=(middlewareData.arrow?.y??0)+arrowHeight/2;let x="",y="";return placedSide==="bottom"?(x=isArrowHidden?noArrowAlign:`${arrowXCenter}px`,y=`${-arrowHeight}px`):placedSide==="top"?(x=isArrowHidden?noArrowAlign:`${arrowXCenter}px`,y=`${rects. -floating.height+arrowHeight}px`):placedSide==="right"?(x=`${-arrowHeight}px`,y=isArrowHidden?noArrowAlign:`${arrowYCenter}px`):placedSide==="left"&&(x=`${rects.floating.width+arrowHeight}px`,y=isArrowHidden?noArrowAlign:`${arrowYCenter}px`),{data:{x,y}}}}}function getSideAndAlignFromPlacement(placement){const[side,align="center"]=placement.split("-");return[side,align]}function getSideFromPlacement(placement){return getSideAndAlignFromPlacement(placement)[0]}function getAlignFromPlacement(placement){ -return getSideAndAlignFromPlacement(placement)[1]}function Floating_layer($$anchor,$$props){push$1($$props,!0);let tooltip=prop($$props,"tooltip",3,!1);FloatingRootState.create(tooltip());var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}class DataTypeahead{#opts;#candidateValues=user_derived(()=>this.#opts.candidateValues());#search;constructor(opts){this.#opts=opts,this.#search=boxAutoReset("",{afterMs:1e3,getWindow:this.#opts. -getWindow}),this.handleTypeaheadSearch=this.handleTypeaheadSearch.bind(this),this.resetTypeahead=this.resetTypeahead.bind(this)}handleTypeaheadSearch(key2){if(!this.#opts.enabled()||!get$3(this.#candidateValues).length)return;this.#search.current=this.#search.current+key2;const currentItem=this.#opts.getCurrentItem(),currentMatch=get$3(this.#candidateValues).find(item=>item===currentItem)??"",values=get$3(this.#candidateValues).map(item=>item??""),nextMatch=getNextMatch(values,this.#search.current, -currentMatch),newItem=get$3(this.#candidateValues).find(item=>item===nextMatch);return newItem&&this.#opts.onMatch(newItem),newItem}resetTypeahead(){this.#search.current=""}}const FIRST_KEYS=[ARROW_DOWN,PAGE_UP,HOME],LAST_KEYS=[ARROW_UP,PAGE_DOWN,END],FIRST_LAST_KEYS=[...FIRST_KEYS,...LAST_KEYS],selectAttrs=createBitsAttrs({component:"select",parts:["trigger","content","item","viewport","scroll-up-button","scroll-down-button","group","group-label","separator","arrow","input","content-wrapper","i\ -tem-text","value"]}),SelectRootContext=new Context$1("Select.Root | Combobox.Root"),SelectContentContext=new Context$1("Select.Content | Combobox.Content");class SelectBaseRootState{opts;#touchedInput=state$1(!1);get touchedInput(){return get$3(this.#touchedInput)}set touchedInput(value){set$1(this.#touchedInput,value,!0)}#inputNode=state$1(null);get inputNode(){return get$3(this.#inputNode)}set inputNode(value){set$1(this.#inputNode,value,!0)}#contentNode=state$1(null);get contentNode(){return get$3( -this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}contentPresence;#viewportNode=state$1(null);get viewportNode(){return get$3(this.#viewportNode)}set viewportNode(value){set$1(this.#viewportNode,value,!0)}#triggerNode=state$1(null);get triggerNode(){return get$3(this.#triggerNode)}set triggerNode(value){set$1(this.#triggerNode,value,!0)}#valueNode=state$1(null);get valueNode(){return get$3(this.#valueNode)}set valueNode(value){set$1(this.#valueNode,value,!0)}#valueId=state$1( -"");get valueId(){return get$3(this.#valueId)}set valueId(value){set$1(this.#valueId,value,!0)}#highlightedNode=state$1(null);get highlightedNode(){return get$3(this.#highlightedNode)}set highlightedNode(value){set$1(this.#highlightedNode,value,!0)}#highlightedValue=user_derived(()=>this.highlightedNode?this.highlightedNode.getAttribute("data-value"):null);get highlightedValue(){return get$3(this.#highlightedValue)}set highlightedValue(value){set$1(this.#highlightedValue,value)}#highlightedId=user_derived( -()=>{if(this.highlightedNode)return this.highlightedNode.id});get highlightedId(){return get$3(this.#highlightedId)}set highlightedId(value){set$1(this.#highlightedId,value)}#highlightedLabel=user_derived(()=>this.highlightedNode?this.highlightedNode.getAttribute("data-label"):null);get highlightedLabel(){return get$3(this.#highlightedLabel)}set highlightedLabel(value){set$1(this.#highlightedLabel,value)}#contentIsPositioned=state$1(!1);get contentIsPositioned(){return get$3(this.#contentIsPositioned)}set contentIsPositioned(value){ -set$1(this.#contentIsPositioned,value,!0)}isUsingKeyboard=!1;isCombobox=!1;domContext=new DOMContext(()=>null);constructor(opts){this.opts=opts,this.isCombobox=opts.isCombobox,this.contentPresence=new PresenceManager({ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}}),user_pre_effect(()=>{this.opts.open.current||this.setHighlightedNode(null)})}setHighlightedNode(node2,initial=!1){this.highlightedNode=node2,node2&& -(this.isUsingKeyboard||initial)&&this.scrollHighlightedNodeIntoView(node2)}scrollHighlightedNodeIntoView(node2){!this.viewportNode||!this.contentIsPositioned||node2.scrollIntoView({block:this.opts.scrollAlignment.current})}getCandidateNodes(){const node2=this.contentNode;return node2?Array.from(node2.querySelectorAll(`[${this.getBitsAttr("item")}]:not([data-disabled])`)):[]}setHighlightedToFirstCandidate(initial=!1){this.setHighlightedNode(null);let nodes2=this.getCandidateNodes();if(nodes2.length){ -if(this.viewportNode){const viewportRect=this.viewportNode.getBoundingClientRect();nodes2=nodes2.filter(node2=>{if(!this.viewportNode)return!1;const nodeRect=node2.getBoundingClientRect();return nodeRect.right<=viewportRect.right&&nodeRect.left>=viewportRect.left&&nodeRect.bottom<=viewportRect.bottom&&nodeRect.top>=viewportRect.top})}this.setHighlightedNode(nodes2[0],initial)}}getNodeByValue(value){return this.getCandidateNodes().find(node2=>node2.dataset.value===value)??null}getLabelForValue(value){ -if(value==="")return"";const fromItems=this.opts.items.current.find(item=>item.value===value)?.label;if(fromItems!==void 0)return fromItems;const node2=this.getNodeByValue(value);if(node2){const dataLabel=node2.getAttribute("data-label");return dataLabel!==null&&dataLabel!==""?dataLabel:node2.textContent?.trim()??value}return value}setOpen(open2){this.opts.open.current=open2}toggleOpen(){this.opts.open.current=!this.opts.open.current}handleOpen(){this.setOpen(!0)}handleClose(){this.setHighlightedNode( -null),this.setOpen(!1)}toggleMenu(){this.toggleOpen()}getBitsAttr=part=>selectAttrs.getAttr(part,this.isCombobox?"combobox":void 0)}class SelectSingleRootState extends SelectBaseRootState{opts;isMulti=!1;#hasValue=user_derived(()=>this.opts.value.current!=="");get hasValue(){return get$3(this.#hasValue)}set hasValue(value){set$1(this.#hasValue,value)}#currentLabel=user_derived(()=>this.opts.items.current.length?this.opts.items.current.find(item=>item.value===this.opts.value.current)?.label??"":"");get currentLabel(){ -return get$3(this.#currentLabel)}set currentLabel(value){set$1(this.#currentLabel,value)}#candidateLabels=user_derived(()=>this.opts.items.current.length?this.opts.items.current.filter(item=>!item.disabled).map(item=>item.label):[]);get candidateLabels(){return get$3(this.#candidateLabels)}set candidateLabels(value){set$1(this.#candidateLabels,value)}#dataTypeaheadEnabled=user_derived(()=>!(this.isMulti||this.opts.items.current.length===0));get dataTypeaheadEnabled(){return get$3(this.#dataTypeaheadEnabled)}set dataTypeaheadEnabled(value){ -set$1(this.#dataTypeaheadEnabled,value)}constructor(opts){super(opts),this.opts=opts,user_effect(()=>{!this.opts.open.current&&this.highlightedNode&&this.setHighlightedNode(null)}),watch$1(()=>this.opts.open.current,()=>{this.opts.open.current&&this.setInitialHighlightedNode()})}includesItem(itemValue){return this.opts.value.current===itemValue}toggleItem(itemValue,itemLabel=itemValue){const newValue=this.includesItem(itemValue)?"":itemValue;this.opts.value.current=newValue,newValue!==""&&(this. -opts.inputValue.current=itemLabel)}setInitialHighlightedNode(){afterTick(()=>{if(!(this.highlightedNode&&this.domContext.getDocument().contains(this.highlightedNode))){if(this.opts.value.current!==""){const node2=this.getNodeByValue(this.opts.value.current);if(node2){this.setHighlightedNode(node2,!0);return}}this.setHighlightedToFirstCandidate(!0)}})}}class SelectMultipleRootState extends SelectBaseRootState{opts;isMulti=!0;#hasValue=user_derived(()=>this.opts.value.current.length>0);get hasValue(){ -return get$3(this.#hasValue)}set hasValue(value){set$1(this.#hasValue,value)}constructor(opts){super(opts),this.opts=opts,user_effect(()=>{!this.opts.open.current&&this.highlightedNode&&this.setHighlightedNode(null)}),watch$1(()=>this.opts.open.current,()=>{this.opts.open.current&&this.setInitialHighlightedNode()})}includesItem(itemValue){return this.opts.value.current.includes(itemValue)}toggleItem(itemValue,itemLabel=itemValue){this.includesItem(itemValue)?this.opts.value.current=this.opts.value. -current.filter(v=>v!==itemValue):this.opts.value.current=[...this.opts.value.current,itemValue],this.opts.inputValue.current=itemLabel}setInitialHighlightedNode(){afterTick(()=>{if(this.domContext&&!(this.highlightedNode&&this.domContext.getDocument().contains(this.highlightedNode))){if(this.opts.value.current.length&&this.opts.value.current[0]!==""){const node2=this.getNodeByValue(this.opts.value.current[0]);if(node2){this.setHighlightedNode(node2,!0);return}}this.setHighlightedToFirstCandidate( -!0)}})}}class SelectRootState{static create(props){const{type:type2,...rest}=props,rootState=type2==="single"?new SelectSingleRootState(rest):new SelectMultipleRootState(rest);return SelectRootContext.set(rootState)}}class SelectTriggerState{static create(opts){return new SelectTriggerState(opts,SelectRootContext.get())}opts;root;attachment;#domTypeahead;#dataTypeahead;constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(opts.ref,v=>this.root.triggerNode=v),this.root. -domContext=new DOMContext(opts.ref),this.#domTypeahead=new DOMTypeahead({getCurrentItem:()=>this.root.highlightedNode,onMatch:node2=>{this.root.setHighlightedNode(node2)},getActiveElement:()=>this.root.domContext.getActiveElement(),getWindow:()=>this.root.domContext.getWindow()}),this.#dataTypeahead=new DataTypeahead({getCurrentItem:()=>this.root.isMulti?"":this.root.currentLabel,onMatch:label=>{if(this.root.isMulti||!this.root.opts.items.current)return;const matchedItem=this.root.opts.items.current. -find(item=>item.label===label);matchedItem&&(this.root.opts.value.current=matchedItem.value)},enabled:()=>!this.root.isMulti&&this.root.dataTypeaheadEnabled,candidateValues:()=>this.root.isMulti?[]:this.root.candidateLabels,getWindow:()=>this.root.domContext.getWindow()}),this.onkeydown=this.onkeydown.bind(this),this.onpointerdown=this.onpointerdown.bind(this),this.onpointerup=this.onpointerup.bind(this),this.onclick=this.onclick.bind(this)}#handleOpen(){this.root.opts.open.current=!0,this.#dataTypeahead. -resetTypeahead(),this.#domTypeahead.resetTypeahead()}#handlePointerOpen(_){this.#handleOpen()}#handleKeyboardSelection(){const isCurrentSelectedValue=this.root.highlightedValue===this.root.opts.value.current;return!this.root.opts.allowDeselect.current&&isCurrentSelectedValue&&!this.root.isMulti?(this.root.handleClose(),!0):(this.root.highlightedValue!==null&&this.root.toggleItem(this.root.highlightedValue,this.root.highlightedLabel??void 0),!this.root.isMulti&&!isCurrentSelectedValue?(this.root. -handleClose(),!0):!1)}onkeydown(e){if(this.root.isUsingKeyboard=!0,(e.key===ARROW_UP||e.key===ARROW_DOWN)&&e.preventDefault(),!this.root.opts.open.current){if(e.key===ENTER||e.key===SPACE||e.key===ARROW_DOWN||e.key===ARROW_UP)e.preventDefault(),this.root.handleOpen();else if(!this.root.isMulti&&this.root.dataTypeaheadEnabled){this.#dataTypeahead.handleTypeaheadSearch(e.key);return}if(this.root.hasValue)return;const candidateNodes2=this.root.getCandidateNodes();if(!candidateNodes2.length)return;if(e. -key===ARROW_DOWN){const firstCandidate=candidateNodes2[0];this.root.setHighlightedNode(firstCandidate)}else if(e.key===ARROW_UP){const lastCandidate=candidateNodes2[candidateNodes2.length-1];this.root.setHighlightedNode(lastCandidate)}return}if(e.key===TAB){this.root.handleClose();return}if((e.key===ENTER||e.key===SPACE&&this.#domTypeahead.search==="")&&!e.isComposing&&(e.preventDefault(),this.#handleKeyboardSelection()))return;if(e.key===ARROW_UP&&e.altKey&&this.root.handleClose(),FIRST_LAST_KEYS. -includes(e.key)){e.preventDefault();const candidateNodes2=this.root.getCandidateNodes(),currHighlightedNode=this.root.highlightedNode,currIndex=currHighlightedNode?candidateNodes2.indexOf(currHighlightedNode):-1,loop2=this.root.opts.loop.current;let nextItem;if(e.key===ARROW_DOWN?nextItem=next(candidateNodes2,currIndex,loop2):e.key===ARROW_UP?nextItem=prev(candidateNodes2,currIndex,loop2):e.key===PAGE_DOWN?nextItem=forward(candidateNodes2,currIndex,10,loop2):e.key===PAGE_UP?nextItem=backward(candidateNodes2, -currIndex,10,loop2):e.key===HOME?nextItem=candidateNodes2[0]:e.key===END&&(nextItem=candidateNodes2[candidateNodes2.length-1]),!nextItem)return;this.root.setHighlightedNode(nextItem);return}const isModifierKey=e.ctrlKey||e.altKey||e.metaKey,isCharacterKey=e.key.length===1,isSpaceKey=e.key===SPACE,candidateNodes=this.root.getCandidateNodes();if(e.key!==TAB){if(!isModifierKey&&(isCharacterKey||isSpaceKey)){!this.#domTypeahead.handleTypeaheadSearch(e.key,candidateNodes)&&isSpaceKey&&(e.preventDefault(), -this.#handleKeyboardSelection());return}this.root.highlightedNode||this.root.setHighlightedToFirstCandidate()}}onclick(e){e.currentTarget.focus()}onpointerdown(e){if(this.root.opts.disabled.current)return;if(e.pointerType==="touch")return e.preventDefault();const target2=e.target;target2?.hasPointerCapture(e.pointerId)&&target2?.releasePointerCapture(e.pointerId),e.button===0&&e.ctrlKey===!1&&(this.root.opts.open.current===!1?this.#handlePointerOpen(e):this.root.handleClose())}onpointerup(e){this. -root.opts.disabled.current||(e.preventDefault(),e.pointerType==="touch"&&(this.root.opts.open.current===!1?this.#handlePointerOpen(e):this.root.handleClose()))}#props=user_derived(()=>({id:this.opts.id.current,disabled:this.root.opts.disabled.current?!0:void 0,"aria-haspopup":"listbox","aria-expanded":boolToStr(this.root.opts.open.current),"aria-activedescendant":this.root.highlightedId,"data-state":getDataOpenClosed(this.root.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(this.root.opts. -disabled.current),"data-placeholder":this.root.hasValue?void 0:"",[this.root.getBitsAttr("trigger")]:"",onpointerdown:this.onpointerdown,onkeydown:this.onkeydown,onclick:this.onclick,onpointerup:this.onpointerup,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectContentState{static create(opts){return SelectContentContext.set(new SelectContentState(opts,SelectRootContext.get()))}opts;root;attachment;#isPositioned=state$1(!1);get isPositioned(){ -return get$3(this.#isPositioned)}set isPositioned(value){set$1(this.#isPositioned,value,!0)}domContext;constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(opts.ref,v=>this.root.contentNode=v),this.domContext=new DOMContext(this.opts.ref),this.root.domContext===null&&(this.root.domContext=this.domContext),onDestroyEffect(()=>{this.root.contentNode=null,this.root.contentIsPositioned=!1,this.isPositioned=!1}),watch$1(()=>this.root.opts.open.current,()=>{this.root.opts.open. -current||(this.root.contentIsPositioned=!1,this.isPositioned=!1)}),watch$1([()=>this.isPositioned,()=>this.root.highlightedNode],()=>{!this.isPositioned||!this.root.highlightedNode||this.root.scrollHighlightedNodeIntoView(this.root.highlightedNode)}),this.onpointermove=this.onpointermove.bind(this)}onpointermove(_){this.root.isUsingKeyboard=!1}#styles=user_derived(()=>getFloatingContentCSSVars(this.root.isCombobox?"combobox":"select"));onInteractOutside=e=>{if(e.target===this.root.triggerNode||e. -target===this.root.inputNode){e.preventDefault();return}this.opts.onInteractOutside.current(e),!e.defaultPrevented&&this.root.handleClose()};onEscapeKeydown=e=>{this.opts.onEscapeKeydown.current(e),!e.defaultPrevented&&this.root.handleClose()};onOpenAutoFocus=e=>{e.preventDefault()};onCloseAutoFocus=e=>{e.preventDefault()};get shouldRender(){return this.root.contentPresence.shouldRender}#snippetProps=user_derived(()=>({open:this.root.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){ -set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,role:"listbox","aria-multiselectable":this.root.isMulti?"true":void 0,"data-state":getDataOpenClosed(this.root.opts.open.current),...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),[this.root.getBitsAttr("content")]:"",style:{display:"flex",flexDirection:"column",outline:"none",boxSizing:"border-box",pointerEvents:"auto",...get$3(this.#styles)},onpointermove:this.onpointermove,...this.attachment}));get props(){ -return get$3(this.#props)}set props(value){set$1(this.#props,value)}popperProps={onInteractOutside:this.onInteractOutside,onEscapeKeydown:this.onEscapeKeydown,onOpenAutoFocus:this.onOpenAutoFocus,onCloseAutoFocus:this.onCloseAutoFocus,trapFocus:!1,loop:!1,onPlaced:()=>{this.root.opts.open.current&&(this.root.contentIsPositioned=!0,this.isPositioned=!0)}}}class SelectItemState{static create(opts){return new SelectItemState(opts,SelectRootContext.get())}opts;root;attachment;#isSelected=user_derived( -()=>this.root.includesItem(this.opts.value.current));get isSelected(){return get$3(this.#isSelected)}set isSelected(value){set$1(this.#isSelected,value)}#isHighlighted=user_derived(()=>this.root.highlightedValue===this.opts.value.current);get isHighlighted(){return get$3(this.#isHighlighted)}set isHighlighted(value){set$1(this.#isHighlighted,value)}prevHighlighted=new Previous(()=>this.isHighlighted);#mounted=state$1(!1);get mounted(){return get$3(this.#mounted)}set mounted(value){set$1(this.#mounted, -value,!0)}constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(opts.ref),watch$1([()=>this.isHighlighted,()=>this.prevHighlighted.current],()=>{this.isHighlighted?this.opts.onHighlight.current():this.prevHighlighted.current&&this.opts.onUnhighlight.current()}),watch$1(()=>this.mounted,()=>{this.mounted&&this.root.setInitialHighlightedNode()}),this.onpointerdown=this.onpointerdown.bind(this),this.onpointerup=this.onpointerup.bind(this),this.onpointermove=this.onpointermove. -bind(this)}handleSelect(){if(this.opts.disabled.current)return;const isCurrentSelectedValue=this.opts.value.current===this.root.opts.value.current;if(!this.root.opts.allowDeselect.current&&isCurrentSelectedValue&&!this.root.isMulti){this.root.handleClose();return}this.root.toggleItem(this.opts.value.current,this.opts.label.current),!this.root.isMulti&&!isCurrentSelectedValue&&this.root.handleClose()}#snippetProps=user_derived(()=>({selected:this.isSelected,highlighted:this.isHighlighted}));get snippetProps(){ -return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}onpointerdown(e){e.preventDefault()}onpointerup(e){if(!(e.defaultPrevented||!this.opts.ref.current)){if(e.pointerType==="touch"&&!isIOS){on(this.opts.ref.current,"click",()=>{this.handleSelect(),this.root.setHighlightedNode(this.opts.ref.current)},{once:!0});return}e.preventDefault(),this.handleSelect(),e.pointerType==="touch"&&this.root.setHighlightedNode(this.opts.ref.current)}}onpointermove(e){e.pointerType!== -"touch"&&this.root.highlightedNode!==this.opts.ref.current&&this.root.setHighlightedNode(this.opts.ref.current)}#props=user_derived(()=>({id:this.opts.id.current,role:"option","aria-selected":this.root.includesItem(this.opts.value.current)?"true":void 0,"data-value":this.opts.value.current,"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled.current),"data-highlighted":this.root.highlightedValue===this.opts.value.current&&!this.opts.disabled.current?"":void 0,"data-selected":this.root.includesItem( -this.opts.value.current)?"":void 0,"data-label":this.opts.label.current,[this.root.getBitsAttr("item")]:"",onpointermove:this.onpointermove,onpointerdown:this.onpointerdown,onpointerup:this.onpointerup,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectHiddenInputState{static create(opts){return new SelectHiddenInputState(opts,SelectRootContext.get())}opts;root;#shouldRender=user_derived(()=>this.root.opts.name.current!=="");get shouldRender(){ -return get$3(this.#shouldRender)}set shouldRender(value){set$1(this.#shouldRender,value)}constructor(opts,root2){this.opts=opts,this.root=root2,this.onfocus=this.onfocus.bind(this)}onfocus(e){e.preventDefault(),this.root.isCombobox?this.root.inputNode?.focus():this.root.triggerNode?.focus()}#props=user_derived(()=>({disabled:boolToTrueOrUndef(this.root.opts.disabled.current),required:boolToTrueOrUndef(this.root.opts.required.current),name:this.root.opts.name.current,value:this.opts.value.current, -onfocus:this.onfocus}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectViewportState{static create(opts){return new SelectViewportState(opts,SelectContentContext.get())}opts;content;root;attachment;#prevScrollTop=state$1(0);get prevScrollTop(){return get$3(this.#prevScrollTop)}set prevScrollTop(value){set$1(this.#prevScrollTop,value,!0)}constructor(opts,content2){this.opts=opts,this.content=content2,this.root=content2.root,this.attachment=attachRef(opts. -ref,v=>{this.root.viewportNode=v})}#props=user_derived(()=>({id:this.opts.id.current,role:"presentation",[this.root.getBitsAttr("viewport")]:"",style:{position:"relative",flex:1,overflow:"auto"},...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectScrollButtonImplState{opts;content;root;attachment;autoScrollTimer=null;userScrollTimer=-1;isUserScrolling=!1;onAutoScroll=noop$1;#mounted=state$1(!1);get mounted(){return get$3(this.#mounted)}set mounted(value){ -set$1(this.#mounted,value,!0)}constructor(opts,content2){this.opts=opts,this.content=content2,this.root=content2.root,this.attachment=attachRef(opts.ref),watch$1([()=>this.mounted],()=>{if(!this.mounted){this.isUserScrolling=!1;return}this.isUserScrolling}),user_effect(()=>{this.mounted||this.clearAutoScrollInterval()}),this.onpointerdown=this.onpointerdown.bind(this),this.onpointermove=this.onpointermove.bind(this),this.onpointerleave=this.onpointerleave.bind(this)}handleUserScroll(){this.content. -domContext.clearTimeout(this.userScrollTimer),this.isUserScrolling=!0,this.userScrollTimer=this.content.domContext.setTimeout(()=>{this.isUserScrolling=!1},200)}clearAutoScrollInterval(){this.autoScrollTimer!==null&&(this.content.domContext.clearTimeout(this.autoScrollTimer),this.autoScrollTimer=null)}onpointerdown(_){if(this.autoScrollTimer!==null)return;const autoScroll=tick2=>{this.onAutoScroll(),this.autoScrollTimer=this.content.domContext.setTimeout(()=>autoScroll(tick2+1),this.opts.delay.current( -tick2))};this.autoScrollTimer=this.content.domContext.setTimeout(()=>autoScroll(1),this.opts.delay.current(0))}onpointermove(e){this.onpointerdown(e)}onpointerleave(_){this.clearAutoScrollInterval()}#props=user_derived(()=>({id:this.opts.id.current,"aria-hidden":boolToStrTrueOrUndef(!0),style:{flexShrink:0},onpointerdown:this.onpointerdown,onpointermove:this.onpointermove,onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props, -value)}}class SelectScrollDownButtonState{static create(opts){return new SelectScrollDownButtonState(new SelectScrollButtonImplState(opts,SelectContentContext.get()))}scrollButtonState;content;root;#canScrollDown=state$1(!1);get canScrollDown(){return get$3(this.#canScrollDown)}set canScrollDown(value){set$1(this.#canScrollDown,value,!0)}scrollIntoViewTimer=null;constructor(scrollButtonState){this.scrollButtonState=scrollButtonState,this.content=scrollButtonState.content,this.root=scrollButtonState. -root,this.scrollButtonState.onAutoScroll=this.handleAutoScroll,watch$1([()=>this.root.viewportNode,()=>this.content.isPositioned],()=>{if(!(!this.root.viewportNode||!this.content.isPositioned))return this.handleScroll(!0),on(this.root.viewportNode,"scroll",()=>this.handleScroll())}),watch$1([()=>this.root.opts.inputValue.current,()=>this.root.viewportNode,()=>this.content.isPositioned],()=>{!this.root.viewportNode||!this.content.isPositioned||this.handleScroll(!0)}),watch$1(()=>this.scrollButtonState. -mounted,()=>{this.scrollButtonState.mounted&&(this.scrollIntoViewTimer&&clearTimeout(this.scrollIntoViewTimer),this.scrollIntoViewTimer=afterSleep(5,()=>{const activeItem=this.root.highlightedNode;activeItem&&this.root.scrollHighlightedNodeIntoView(activeItem)}))})}handleScroll=(manual=!1)=>{if(manual||this.scrollButtonState.handleUserScroll(),!this.root.viewportNode)return;const maxScroll=this.root.viewportNode.scrollHeight-this.root.viewportNode.clientHeight,paddingTop=Number.parseInt(getComputedStyle( -this.root.viewportNode).paddingTop,10);this.canScrollDown=Math.ceil(this.root.viewportNode.scrollTop){const viewport=this.root.viewportNode,selectedItem=this.root.highlightedNode;!viewport||!selectedItem||(viewport.scrollTop=viewport.scrollTop+selectedItem.offsetHeight)};#props=user_derived(()=>({...this.scrollButtonState.props,[this.root.getBitsAttr("scroll-down-button")]:""}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}} -class SelectScrollUpButtonState{static create(opts){return new SelectScrollUpButtonState(new SelectScrollButtonImplState(opts,SelectContentContext.get()))}scrollButtonState;content;root;#canScrollUp=state$1(!1);get canScrollUp(){return get$3(this.#canScrollUp)}set canScrollUp(value){set$1(this.#canScrollUp,value,!0)}constructor(scrollButtonState){this.scrollButtonState=scrollButtonState,this.content=scrollButtonState.content,this.root=scrollButtonState.root,this.scrollButtonState.onAutoScroll=this. -handleAutoScroll,watch$1([()=>this.root.viewportNode,()=>this.content.isPositioned],()=>{if(!(!this.root.viewportNode||!this.content.isPositioned))return this.handleScroll(!0),on(this.root.viewportNode,"scroll",()=>this.handleScroll())})}handleScroll=(manual=!1)=>{if(manual||this.scrollButtonState.handleUserScroll(),!this.root.viewportNode)return;const paddingTop=Number.parseInt(getComputedStyle(this.root.viewportNode).paddingTop,10);this.canScrollUp=this.root.viewportNode.scrollTop-paddingTop>.1};handleAutoScroll=()=>{ -!this.root.viewportNode||!this.root.highlightedNode||(this.root.viewportNode.scrollTop=this.root.viewportNode.scrollTop-this.root.highlightedNode.offsetHeight)};#props=user_derived(()=>({...this.scrollButtonState.props,[this.root.getBitsAttr("scroll-up-button")]:""}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}function Select_hidden_input($$anchor,$$props){push$1($$props,!0);let value=prop($$props,"value",15);const hiddenInputState=SelectHiddenInputState.create( -{value:boxWith$1(()=>value())});var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Hidden_input($$anchor2,spread_props(()=>hiddenInputState.props,{get autocomplete(){return $$props.autocomplete},get value(){return value()},set value($$value){value($$value)}}))};if_block(node2,$$render=>{hiddenInputState.shouldRender&&$$render(consequent)})}append($$anchor,fragment),pop()}function Floating_layer_anchor($$anchor,$$props){push$1($$props,!0);let tooltip=prop($$props,"to\ -oltip",3,!1);FloatingAnchorState.create({id:boxWith$1(()=>$$props.id),virtualEl:boxWith$1(()=>$$props.virtualEl),ref:$$props.ref},tooltip());var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}var root_4$M=from_svg(''),root_2$1u=from_html("");function Arrow($$anchor,$$props){ -push$1($$props,!0);let id2=prop($$props,"id",19,useId),width=prop($$props,"width",3,10),height=prop($$props,"height",3,5),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","children","child","width","height"]);const mergedProps=user_derived(()=>mergeProps(restProps,{id:id2()}));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})), -append($$anchor2,fragment_1)},alternate_1=$$anchor2=>{var span=root_2$1u();attribute_effect(span,()=>({...get$3(mergedProps)}));var node_2=child(span);{var consequent_1=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var svg2=root_4$M();template_effect(()=>{set_attribute(svg2,"width",width()),set_attribute(svg2,"height",height())}),append($$anchor3,svg2)};if_block(node_2,$$render=>{ -$$props.children?$$render(consequent_1):$$render(alternate,-1)})}reset(span),append($$anchor2,span)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}function Floating_layer_arrow($$anchor,$$props){push$1($$props,!0);let id2=prop($$props,"id",19,useId),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref"]);const arrowState=FloatingArrowState.create({id:boxWith$1(()=>id2()), -ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,arrowState.props));Arrow($$anchor,spread_props(()=>get$3(mergedProps))),pop()}function Floating_layer_content($$anchor,$$props){push$1($$props,!0);let side=prop($$props,"side",3,"bottom"),sideOffset=prop($$props,"sideOffset",3,0),align=prop($$props,"align",3,"center"),alignOffset=prop($$props,"alignOffset",3,0),arrowPadding=prop($$props,"arrowPadding",3,0),avoidCollisions=prop($$props,"avoidCollisions",3,!0), -collisionBoundary=prop($$props,"collisionBoundary",19,()=>[]),collisionPadding=prop($$props,"collisionPadding",3,0),hideWhenDetached=prop($$props,"hideWhenDetached",3,!1),onPlaced=prop($$props,"onPlaced",3,()=>{}),sticky=prop($$props,"sticky",3,"partial"),updatePositionStrategy=prop($$props,"updatePositionStrategy",3,"optimized"),strategy=prop($$props,"strategy",3,"fixed"),dir=prop($$props,"dir",3,"ltr"),style2=prop($$props,"style",19,()=>({})),wrapperId=prop($$props,"wrapperId",19,useId),customAnchor=prop( -$$props,"customAnchor",3,null),tooltip=prop($$props,"tooltip",3,!1);const contentState=FloatingContentState.create({side:boxWith$1(()=>side()),sideOffset:boxWith$1(()=>sideOffset()),align:boxWith$1(()=>align()),alignOffset:boxWith$1(()=>alignOffset()),id:boxWith$1(()=>$$props.id),arrowPadding:boxWith$1(()=>arrowPadding()),avoidCollisions:boxWith$1(()=>avoidCollisions()),collisionBoundary:boxWith$1(()=>collisionBoundary()),collisionPadding:boxWith$1(()=>collisionPadding()),hideWhenDetached:boxWith$1( -()=>hideWhenDetached()),onPlaced:boxWith$1(()=>onPlaced()),sticky:boxWith$1(()=>sticky()),updatePositionStrategy:boxWith$1(()=>updatePositionStrategy()),strategy:boxWith$1(()=>strategy()),dir:boxWith$1(()=>dir()),style:boxWith$1(()=>style2()),enabled:boxWith$1(()=>$$props.enabled),wrapperId:boxWith$1(()=>wrapperId()),customAnchor:boxWith$1(()=>customAnchor())},tooltip()),mergedProps=user_derived(()=>mergeProps(contentState.wrapperProps,{style:{pointerEvents:"auto"}}));var fragment=comment$2(),node2=first_child( -fragment);snippet(node2,()=>$$props.content??noop$3,()=>({props:contentState.props,wrapperProps:get$3(mergedProps)})),append($$anchor,fragment),pop()}function Floating_layer_content_static($$anchor,$$props){push$1($$props,!0),onMount$1(()=>{$$props.onPlaced?.()});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.content??noop$3,()=>({props:{},wrapperProps:{}})),append($$anchor,fragment),pop()}function Popper_content($$anchor,$$props){let isStatic=prop($$props,"isStat\ -ic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","content","isStatic","onPlaced"]);var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Floating_layer_content_static($$anchor2,{get content(){return $$props.content},get onPlaced(){return $$props.onPlaced}})},alternate=$$anchor2=>{Floating_layer_content($$anchor2,spread_props({get content(){return $$props.content},get onPlaced(){return $$props.onPlaced}},()=>restProps))};if_block(node2,$$render=>{ -isStatic()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment)}var root_1$1c=from_html(" ",1);function Popper_layer_inner($$anchor,$$props){push$1($$props,!0);let interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3,null),isStatic=prop($$props,"isStatic",3,!1),tooltip=prop($$props,"tooltip",3,!1),contentPointerEvents=prop( -$$props,"contentPointerEvents",3,"auto"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","popper","onEscapeKeydown","escapeKeydownBehavior","preventOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpenAut\ -oFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","isStatic","enabled","ref","tooltip","contentPointerEvents"]);const resolvedPreventScroll=user_derived(()=>$$props.preventScroll??!0),effectiveStrategy=user_derived(()=>$$props.strategy??(get$3(resolvedPreventScroll)?"fixed":"absolute"));Popper_content($$anchor,{get isStatic(){return isStatic()},get id(){return $$props.id},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset}, -get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){return $$props.arrowPadding},get avoidCollisions(){return $$props.avoidCollisions},get collisionBoundary(){return $$props.collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){return $$props.updatePositionStrategy},get strategy(){return get$3(effectiveStrategy)}, -get dir(){return $$props.dir},get wrapperId(){return $$props.wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){return customAnchor()},get enabled(){return $$props.enabled},get tooltip(){return tooltip()},content:($$anchor2,$$arg0)=>{let floatingProps=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;var fragment_1=root_1$1c(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{Scroll_lock($$anchor3,{get preventScroll(){return get$3( -resolvedPreventScroll)}})},consequent_1=$$anchor3=>{Scroll_lock($$anchor3,{get preventScroll(){return get$3(resolvedPreventScroll)}})};if_block(node2,$$render=>{$$props.forceMount&&$$props.enabled?$$render(consequent):$$props.forceMount||$$render(consequent_1,1)})}var node_1=sibling(node2,2);Focus_scope(node_1,{get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get onCloseAutoFocus(){return $$props.onCloseAutoFocus},get loop(){return $$props.loop},get enabled(){return $$props.enabled},get trapFocus(){ -return trapFocus()},get forceMount(){return $$props.forceMount},get ref(){return $$props.ref},focusScope:($$anchor3,$$arg02)=>{let focusScopeProps=()=>$$arg02?.().props;Escape_layer($$anchor3,{get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor4,$$slotProps)=>{Dismissible_layer($$anchor4,{get id(){return $$props.id},get onInteractOutside(){return $$props. -onInteractOutside},get onFocusOutside(){return $$props.onFocusOutside},get interactOutsideBehavior(){return interactOutsideBehavior()},get isValidEvent(){return isValidEvent2()},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor5,$$arg03)=>{let dismissibleProps=()=>$$arg03?.().props;Text_selection_layer($$anchor5,{get id(){return $$props.id},get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get onPointerDown(){return $$props.onPointerDown}, -get onPointerUp(){return $$props.onPointerUp},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor6,$$slotProps2)=>{var fragment_7=comment$2(),node_2=first_child(fragment_7);{let $0=user_derived(()=>({props:mergeProps(restProps,floatingProps(),dismissibleProps(),focusScopeProps(),{style:{pointerEvents:contentPointerEvents()}}),wrapperProps:wrapperProps()}));snippet(node_2,()=>$$props.popper??noop$3,()=>get$3($0))}append($$anchor6,fragment_7)},$$slots:{default:!0}})}, -$$slots:{default:!0}})},$$slots:{default:!0}})},$$slots:{focusScope:!0}}),append($$anchor2,fragment_1)},$$slots:{content:!0}}),pop()}function Popper_layer($$anchor,$$props){let interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3,null),isStatic=prop($$props,"isStatic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","poppe\ -r","open","onEscapeKeydown","escapeKeydownBehavior","preventOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpenAutoFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","i\ -sStatic","ref","shouldRender"]);var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Popper_layer_inner($$anchor2,spread_props({get popper(){return $$props.popper},get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior},get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get id(){return $$props.id},get onPointerDown(){return $$props.onPointerDown},get onPointerUp(){return $$props. -onPointerUp},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset},get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){return $$props.arrowPadding},get avoidCollisions(){return $$props.avoidCollisions},get collisionBoundary(){return $$props.collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){ -return $$props.updatePositionStrategy},get strategy(){return $$props.strategy},get dir(){return $$props.dir},get preventScroll(){return $$props.preventScroll},get wrapperId(){return $$props.wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){return customAnchor()},get isStatic(){return isStatic()},get enabled(){return $$props.open},get onInteractOutside(){return $$props.onInteractOutside},get onCloseAutoFocus(){return $$props.onCloseAutoFocus}, -get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get interactOutsideBehavior(){return interactOutsideBehavior()},get loop(){return $$props.loop},get trapFocus(){return trapFocus()},get isValidEvent(){return isValidEvent2()},get onFocusOutside(){return $$props.onFocusOutside},forceMount:!1,get ref(){return $$props.ref}},()=>restProps))};if_block(node2,$$render=>{$$props.shouldRender&&$$render(consequent)})}append($$anchor,fragment)}function Popper_layer_force_mount($$anchor,$$props){let interactOutsideBehavior=prop( -$$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3,null),isStatic=prop($$props,"isStatic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","popper","onEscapeKeydown","escapeKeydownBehavior","preventOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary", -"collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpenAutoFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","isStatic","enabled"]);Popper_layer_inner($$anchor,spread_props({get popper(){return $$props.popper},get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior}, -get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get id(){return $$props.id},get onPointerDown(){return $$props.onPointerDown},get onPointerUp(){return $$props.onPointerUp},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset},get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){return $$props.arrowPadding},get avoidCollisions(){return $$props.avoidCollisions},get collisionBoundary(){return $$props. -collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){return $$props.updatePositionStrategy},get strategy(){return $$props.strategy},get dir(){return $$props.dir},get preventScroll(){return $$props.preventScroll},get wrapperId(){return $$props.wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){return customAnchor()}, -get isStatic(){return isStatic()},get enabled(){return $$props.enabled},get onInteractOutside(){return $$props.onInteractOutside},get onCloseAutoFocus(){return $$props.onCloseAutoFocus},get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get interactOutsideBehavior(){return interactOutsideBehavior()},get loop(){return $$props.loop},get trapFocus(){return trapFocus()},get isValidEvent(){return isValidEvent2()},get onFocusOutside(){return $$props.onFocusOutside}},()=>restProps,{forceMount:!0}))} -var root_4$L=from_html("
"),root_8$z=from_html("
");function Select_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),side=prop($$props,"side",3,"bottom"),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),preventScroll=prop($$props,"prevent\ -Scroll",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","forceMount","side","onInteractOutside","onEscapeKeydown","children","child","preventScroll","style"]);const contentState=SelectContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onInteractOutside:boxWith$1(()=>onInteractOutside()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown())}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child( -fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get side(){return side()},get enabled(){return contentState.root.opts.open.current},get id(){return id2()},get preventScroll(){return preventScroll()},forceMount:!0,get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived( -()=>mergeProps(props(),{style:contentState.props.style},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$L();attribute_effect(div,()=>({...wrapperProps()})); -var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get side(){return side()}, -get open(){return contentState.root.opts.open.current},get id(){return id2()},get preventScroll(){return preventScroll()},forceMount:!1,get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:contentState.props.style},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(), -node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$z();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4, -div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}function Mounted($$anchor,$$props){push$1($$props,!0);let mounted=prop($$props,"mounted",15,!1),onMountedChange=prop($$props,"onMountedChange",3,noop$1);onMountEffect(()=>(mounted(!0),onMountedChange()(!0),()=>{mounted( -!1),onMountedChange()(!1)})),pop()}var root_2$1t=from_html("
"),root$1Z=from_html(" ",1);function Select_item$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),label=prop($$props,"label",19,()=>$$props.value),disabled=prop($$props,"disabled",3,!1),onHighlight=prop($$props,"onHighlight",3,noop$1),onUnhighlight=prop($$props,"onUnhighlight",3,noop$1),restProps=rest_props($$props,["$$slots", -"$$events","$$legacy","id","ref","value","label","disabled","children","child","onHighlight","onUnhighlight"]);const itemState=SelectItemState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),value:boxWith$1(()=>$$props.value),disabled:boxWith$1(()=>disabled()),label:boxWith$1(()=>label()),onHighlight:boxWith$1(()=>onHighlight()),onUnhighlight:boxWith$1(()=>onUnhighlight())}),mergedProps=user_derived(()=>mergeProps(restProps,itemState.props));var fragment=root$1Z(),node2=first_child( -fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({props:get$3(mergedProps),...itemState.snippetProps}));snippet(node_1,()=>$$props.child,()=>get$3($0))}append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1t();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3,()=>itemState.snippetProps),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{ -$$props.child?$$render(consequent):$$render(alternate,-1)})}var node_3=sibling(node2,2);Mounted(node_3,{get mounted(){return itemState.mounted},set mounted($$value){itemState.mounted=$$value}}),append($$anchor,fragment),pop()}var root_2$1s=from_html("
");function Select_viewport($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id", -"ref","children","child"]);const viewportState=SelectViewportState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,viewportState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1s();attribute_effect( -div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_3$14=from_html("
"),root_1$1b=from_html(" ",1);function Select_scroll_down_button$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15, -null),delay=prop($$props,"delay",3,()=>50),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","delay","child","children"]);const scrollButtonState=SelectScrollDownButtonState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),delay:boxWith$1(()=>delay())}),mergedProps=user_derived(()=>mergeProps(restProps,scrollButtonState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var fragment_1=root_1$1b(),node_1=first_child( -fragment_1);Mounted(node_1,{get mounted(){return scrollButtonState.scrollButtonState.mounted},set mounted($$value){scrollButtonState.scrollButtonState.mounted=$$value}});var node_2=sibling(node_1,2);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.child,()=>({props:restProps})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$14();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_4=child(div);snippet(node_4, -()=>$$props.children??noop$3),reset(div),append($$anchor3,div)};if_block(node_2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{scrollButtonState.canScrollDown&&$$render(consequent_1)})}append($$anchor,fragment),pop()}var root_3$13=from_html("
"),root_1$1a=from_html(" ",1);function Select_scroll_up_button$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId( -uid2)),ref2=prop($$props,"ref",15,null),delay=prop($$props,"delay",3,()=>50),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","delay","child","children"]);const scrollButtonState=SelectScrollUpButtonState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),delay:boxWith$1(()=>delay())}),mergedProps=user_derived(()=>mergeProps(restProps,scrollButtonState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var fragment_1=root_1$1a(), -node_1=first_child(fragment_1);Mounted(node_1,{get mounted(){return scrollButtonState.scrollButtonState.mounted},set mounted($$value){scrollButtonState.scrollButtonState.mounted=$$value}});var node_2=sibling(node_1,2);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.child,()=>({props:restProps})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$13();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_4=child( -div);snippet(node_4,()=>$$props.children??noop$3),reset(div),append($$anchor3,div)};if_block(node_2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{scrollButtonState.canScrollUp&&$$render(consequent_1)})}append($$anchor,fragment),pop()}function Menu_sub($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenC\ -hangeComplete",3,noop$1);MenuSubmenuState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()?.(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),Floating_layer($$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root_2$1r=from_html("
");function Menu_item($$anchor,$$props){const uid2=props_id();push$1( -$$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),onSelect=prop($$props,"onSelect",3,noop$1),closeOnSelect=prop($$props,"closeOnSelect",3,!0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","child","children","ref","id","disabled","onSelect","closeOnSelect"]);const itemState=MenuItemState.create({id:boxWith$1(()=>id2()),disabled:boxWith$1(()=>disabled()),onSelect:boxWith$1(()=>onSelect()),ref:boxWith$1( -()=>ref2(),v=>ref2(v)),closeOnSelect:boxWith$1(()=>closeOnSelect())}),mergedProps=user_derived(()=>mergeProps(restProps,itemState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1r();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2, -()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1q=from_html("
");function Menu_separator($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","id","child","children"]);const separatorState=MenuSeparatorState. -create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,separatorState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1q();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet( -node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_4$K=from_html("
"),root_8$y=from_html("
");function Menu_sub_content($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),loop2=prop($$props,"loop",3,!0),onInteractOutside=prop( -$$props,"onInteractOutside",3,noop$1),forceMount=prop($$props,"forceMount",3,!1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"defer-otherwise-close"),escapeKeydownBehavior=prop($$props,"escapeKeydownBehavior",3,"defer-otherwise-close"),onOpenAutoFocusProp=prop($$props,"onOpenAutoFocus",3,noop$1),onCloseAutoFocusProp=prop($$props,"onCloseAutoFocus",3,noop$1),onFocusOutside=prop($$props,"onFocusOutside",3,noop$1),side=prop( -$$props,"side",3,"right"),trapFocus=prop($$props,"trapFocus",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","children","child","loop","onInteractOutside","forceMount","onEscapeKeydown","interactOutsideBehavior","escapeKeydownBehavior","onOpenAutoFocus","onCloseAutoFocus","onFocusOutside","side","trapFocus","style"]);const subContentState=MenuContentState.create({id:boxWith$1(()=>id2()),loop:boxWith$1(()=>loop2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),isSub:!0,onCloseAutoFocus:boxWith$1( -()=>handleCloseAutoFocus)});function onkeydown(e){const isKeyDownInside=e.currentTarget.contains(e.target),isCloseKey=SUB_CLOSE_KEYS[subContentState.parentMenu.root.opts.dir.current].includes(e.key);isKeyDownInside&&isCloseKey&&(subContentState.parentMenu.onClose(),subContentState.parentMenu.triggerNode?.focus(),e.preventDefault())}const dataAttr=user_derived(()=>subContentState.parentMenu.root.getBitsAttr("sub-content")),mergedProps=user_derived(()=>mergeProps(restProps,subContentState.props,{side:side(), -onkeydown,[get$3(dataAttr)]:""}));function handleOpenAutoFocus(e){onOpenAutoFocusProp()(e),!e.defaultPrevented&&(e.preventDefault(),subContentState.parentMenu.root.isUsingKeyboard&&subContentState.parentMenu.contentNode&&MenuOpenEvent.dispatch(subContentState.parentMenu.contentNode))}function handleCloseAutoFocus(e){onCloseAutoFocusProp()(e),!e.defaultPrevented&&e.preventDefault()}function handleInteractOutside(e){onInteractOutside()(e),!e.defaultPrevented&&subContentState.parentMenu.onClose()}function handleEscapeKeydown(e){ -onEscapeKeydown()(e),!e.defaultPrevented&&subContentState.parentMenu.onClose()}function handleOnFocusOutside(e){if(onFocusOutside()(e),e.defaultPrevented||!isHTMLElement$1(e.target)||e.target.id===subContentState.parentMenu.triggerNode?.id)return;if(subContentState.parentMenu.parentMenu?.contentNode?.contains(e.target)){subContentState.parentMenu.onClose(),e.preventDefault();return}const subContentSelector=`[${subContentState.parentMenu.root.getBitsAttr("sub-content")}]`;if(e.target.closest(subContentSelector)){ -e.preventDefault();return}subContentState.parentMenu.onClose()}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),{get ref(){return subContentState.opts.ref},get interactOutsideBehavior(){return interactOutsideBehavior()},get escapeKeydownBehavior(){return escapeKeydownBehavior()},onOpenAutoFocus:handleOpenAutoFocus,get enabled(){return subContentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside, -onEscapeKeydown:handleEscapeKeydown,onFocusOutside:handleOnFocusOutside,preventScroll:!1,get loop(){return loop2()},get trapFocus(){return trapFocus()},get shouldRender(){return subContentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),get$3(mergedProps),{style:getFloatingContentCSSVars("menu")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2); -{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...subContentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$K();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children?? -noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),{get ref(){return subContentState.opts.ref},get interactOutsideBehavior(){return interactOutsideBehavior()},get escapeKeydownBehavior(){return escapeKeydownBehavior()},onCloseAutoFocus:handleCloseAutoFocus,onOpenAutoFocus:handleOpenAutoFocus, -get open(){return subContentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,onFocusOutside:handleOnFocusOutside,preventScroll:!1,get loop(){return loop2()},get trapFocus(){return trapFocus()},get shouldRender(){return subContentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),get$3(mergedProps),{style:getFloatingContentCSSVars( -"menu")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...subContentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$y();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect( -div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$12=from_html("
");function Menu_sub_trigger($$anchor,$$props){ -const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),ref2=prop($$props,"ref",15,null),onSelect=prop($$props,"onSelect",3,noop$1),openDelay=prop($$props,"openDelay",3,0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","disabled","ref","children","child","onSelect","openDelay"]);const subTriggerState=MenuSubTriggerState.create({disabled:boxWith$1(()=>disabled()),onSelect:boxWith$1(()=>onSelect()),id:boxWith$1( -()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),openDelay:boxWith$1(()=>openDelay())}),mergedProps=user_derived(()=>mergeProps(restProps,subTriggerState.props));Floating_layer_anchor($$anchor,{get id(){return id2()},get ref(){return subTriggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_1=first_child(fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})), -append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$12();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor3,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}function isPointInPolygon(point2,polygon){const[x,y]=point2;let isInside=!1;const length=polygon.length;for(let i=0,j=length-1;i=y!=yj>=y&&x<=(xj-xi)*(y-yi)/(yj-yi)+xi&&(isInside=!isInside)}return isInside}function isInsideRect(point2,rect){return point2[0]>=rect.left&&point2[0]<=rect.right&&point2[1]>=rect.top&&point2[1]<=rect.bottom}function getSide(triggerRect,contentRect){const triggerCenterX=triggerRect.left+triggerRect.width/2,triggerCenterY=triggerRect.top+triggerRect.height/2,contentCenterX=contentRect.left+contentRect.width/2,contentCenterY=contentRect. -top+contentRect.height/2,deltaX=contentCenterX-triggerCenterX,deltaY=contentCenterY-triggerCenterY;return Math.abs(deltaX)>Math.abs(deltaY)?deltaX>0?"right":"left":deltaY>0?"bottom":"top"}class SafePolygon{#opts;#buffer;#transitIntentTimeout;#exitPoint=null;#exitTarget=null;#transitTargets=[];#trackedTriggerNode=null;#leaveFallbackRafId=null;#transitIntentTimeoutId=null;#cancelLeaveFallback(){this.#leaveFallbackRafId!==null&&(cancelAnimationFrame(this.#leaveFallbackRafId),this.#leaveFallbackRafId= -null)}#scheduleLeaveFallback(){this.#cancelLeaveFallback(),this.#leaveFallbackRafId=requestAnimationFrame(()=>{this.#leaveFallbackRafId=null,!(!this.#exitPoint||!this.#exitTarget)&&(this.#clearTracking(),this.#opts.onPointerExit())})}#cancelTransitIntentTimeout(){this.#transitIntentTimeoutId!==null&&(clearTimeout(this.#transitIntentTimeoutId),this.#transitIntentTimeoutId=null)}#scheduleTransitIntentTimeout(){this.#transitIntentTimeout!==null&&(this.#cancelTransitIntentTimeout(),this.#transitIntentTimeoutId= -window.setTimeout(()=>{this.#transitIntentTimeoutId=null,!(!this.#exitPoint||!this.#exitTarget)&&(this.#clearTracking(),this.#opts.onPointerExit())},this.#transitIntentTimeout))}constructor(opts){this.#opts=opts,this.#buffer=opts.buffer??1;const transitIntentTimeout=opts.transitIntentTimeout;this.#transitIntentTimeout=typeof transitIntentTimeout=="number"&&transitIntentTimeout>0?transitIntentTimeout:null,watch$1([opts.triggerNode,opts.contentNode,opts.enabled],([triggerNode,contentNode,enabled])=>{ -if(!triggerNode||!contentNode||!enabled){this.#trackedTriggerNode=null,this.#clearTracking();return}this.#trackedTriggerNode&&this.#trackedTriggerNode!==triggerNode&&this.#clearTracking(),this.#trackedTriggerNode=triggerNode;const doc=getDocument$1(triggerNode),handlePointerMove=e=>{this.#onPointerMove([e.clientX,e.clientY],triggerNode,contentNode)},handleTriggerLeave=e=>{const target2=e.relatedTarget;if(isElement$1(target2)&&contentNode.contains(target2))return;const ignoredTargets=this.#opts.ignoredTargets?.()?? -[];isElement$1(target2)&&ignoredTargets.some(n=>n===target2||n.contains(target2))||(this.#transitTargets=isElement$1(target2)&&ignoredTargets.length>0?ignoredTargets.filter(n=>target2.contains(n)):[],this.#exitPoint=[e.clientX,e.clientY],this.#exitTarget="content",this.#scheduleLeaveFallback())},handleTriggerEnter=()=>{this.#clearTracking()},handleContentEnter=()=>{this.#clearTracking()},handleContentLeave=e=>{const target2=e.relatedTarget;isElement$1(target2)&&triggerNode.contains(target2)||(this.#exitPoint= -[e.clientX,e.clientY],this.#exitTarget="trigger",this.#scheduleLeaveFallback())};return[on(doc,"pointermove",handlePointerMove),on(triggerNode,"pointerleave",handleTriggerLeave),on(triggerNode,"pointerenter",handleTriggerEnter),on(contentNode,"pointerenter",handleContentEnter),on(contentNode,"pointerleave",handleContentLeave)].reduce((acc,cleanup)=>()=>{acc(),cleanup()},()=>{})})}#onPointerMove(clientPoint,triggerNode,contentNode){if(!this.#exitPoint||!this.#exitTarget)return;this.#cancelLeaveFallback(), -this.#scheduleTransitIntentTimeout();const triggerRect=triggerNode.getBoundingClientRect(),contentRect=contentNode.getBoundingClientRect();if(this.#exitTarget==="content"&&isInsideRect(clientPoint,contentRect)){this.#clearTracking();return}if(this.#exitTarget==="trigger"&&isInsideRect(clientPoint,triggerRect)){this.#clearTracking();return}if(this.#exitTarget==="content"&&this.#transitTargets.length>0)for(const transitTarget of this.#transitTargets){const transitRect=transitTarget.getBoundingClientRect(); -if(isInsideRect(clientPoint,transitRect))return;const transitSide=getSide(triggerRect,transitRect),transitCorridor=this.#getCorridorPolygon(triggerRect,transitRect,transitSide);if(transitCorridor&&isPointInPolygon(clientPoint,transitCorridor))return}const side=getSide(triggerRect,contentRect),corridorPoly=this.#getCorridorPolygon(triggerRect,contentRect,side);if(corridorPoly&&isPointInPolygon(clientPoint,corridorPoly))return;const targetRect=this.#exitTarget==="content"?contentRect:triggerRect,safePoly=this.#getSafePolygon( -this.#exitPoint,targetRect,side,this.#exitTarget);isPointInPolygon(clientPoint,safePoly)||(this.#clearTracking(),this.#opts.onPointerExit())}#clearTracking(){this.#exitPoint=null,this.#exitTarget=null,this.#transitTargets=[],this.#cancelLeaveFallback(),this.#cancelTransitIntentTimeout()}#getCorridorPolygon(triggerRect,contentRect,side){const buffer=this.#buffer;switch(side){case"top":return[[Math.min(triggerRect.left,contentRect.left)-buffer,triggerRect.top],[Math.min(triggerRect.left,contentRect. -left)-buffer,contentRect.bottom],[Math.max(triggerRect.right,contentRect.right)+buffer,contentRect.bottom],[Math.max(triggerRect.right,contentRect.right)+buffer,triggerRect.top]];case"bottom":return[[Math.min(triggerRect.left,contentRect.left)-buffer,triggerRect.bottom],[Math.min(triggerRect.left,contentRect.left)-buffer,contentRect.top],[Math.max(triggerRect.right,contentRect.right)+buffer,contentRect.top],[Math.max(triggerRect.right,contentRect.right)+buffer,triggerRect.bottom]];case"left":return[ -[triggerRect.left,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.right,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.right,Math.max(triggerRect.bottom,contentRect.bottom)+buffer],[triggerRect.left,Math.max(triggerRect.bottom,contentRect.bottom)+buffer]];case"right":return[[triggerRect.right,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.left,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.left,Math.max(triggerRect.bottom,contentRect. -bottom)+buffer],[triggerRect.right,Math.max(triggerRect.bottom,contentRect.bottom)+buffer]]}}#getSafePolygon(exitPoint,targetRect,side,exitTarget){const buffer=this.#buffer*4,[x,y]=exitPoint;switch(exitTarget==="trigger"?this.#flipSide(side):side){case"top":return[[x-buffer,y+buffer],[x+buffer,y+buffer],[targetRect.right+buffer,targetRect.bottom],[targetRect.right+buffer,targetRect.top],[targetRect.left-buffer,targetRect.top],[targetRect.left-buffer,targetRect.bottom]];case"bottom":return[[x-buffer, -y-buffer],[x+buffer,y-buffer],[targetRect.right+buffer,targetRect.top],[targetRect.right+buffer,targetRect.bottom],[targetRect.left-buffer,targetRect.bottom],[targetRect.left-buffer,targetRect.top]];case"left":return[[x+buffer,y-buffer],[x+buffer,y+buffer],[targetRect.right,targetRect.bottom+buffer],[targetRect.left,targetRect.bottom+buffer],[targetRect.left,targetRect.top-buffer],[targetRect.right,targetRect.top-buffer]];case"right":return[[x-buffer,y-buffer],[x-buffer,y+buffer],[targetRect.left, -targetRect.bottom+buffer],[targetRect.right,targetRect.bottom+buffer],[targetRect.right,targetRect.top-buffer],[targetRect.left,targetRect.top-buffer]]}}#flipSide(side){switch(side){case"top":return"bottom";case"bottom":return"top";case"left":return"right";case"right":return"left"}}}const popoverAttrs=createBitsAttrs({component:"popover",parts:["root","trigger","content","close","overlay"]}),PopoverRootContext=new Context$1("Popover.Root");class PopoverRootState{static create(opts){return PopoverRootContext. -set(new PopoverRootState(opts))}opts;#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}contentPresence;#triggerNode=state$1(null);get triggerNode(){return get$3(this.#triggerNode)}set triggerNode(value){set$1(this.#triggerNode,value,!0)}#overlayNode=state$1(null);get overlayNode(){return get$3(this.#overlayNode)}set overlayNode(value){set$1(this.#overlayNode,value,!0)}overlayPresence;#openedViaHover=state$1(!1);get openedViaHover(){ -return get$3(this.#openedViaHover)}set openedViaHover(value){set$1(this.#openedViaHover,value,!0)}#hasInteractedWithContent=state$1(!1);get hasInteractedWithContent(){return get$3(this.#hasInteractedWithContent)}set hasInteractedWithContent(value){set$1(this.#hasInteractedWithContent,value,!0)}#hoverCooldown=state$1(!1);get hoverCooldown(){return get$3(this.#hoverCooldown)}set hoverCooldown(value){set$1(this.#hoverCooldown,value,!0)}#closeDelay=state$1(0);get closeDelay(){return get$3(this.#closeDelay)}set closeDelay(value){ -set$1(this.#closeDelay,value,!0)}#closeTimeout=null;#domContext=null;constructor(opts){this.opts=opts,this.contentPresence=new PresenceManager({ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}}),this.overlayPresence=new PresenceManager({ref:boxWith$1(()=>this.overlayNode),open:this.opts.open}),watch$1(()=>this.opts.open.current,isOpen=>{isOpen||(this.openedViaHover=!1,this.hasInteractedWithContent=!1,this.#clearCloseTimeout())})}setDomContext(ctx){ -this.#domContext=ctx}#clearCloseTimeout(){this.#closeTimeout!==null&&this.#domContext&&(this.#domContext.clearTimeout(this.#closeTimeout),this.#closeTimeout=null)}toggleOpen(){this.#clearCloseTimeout(),this.opts.open.current=!this.opts.open.current}handleClose(){this.#clearCloseTimeout(),this.opts.open.current&&(this.opts.open.current=!1)}handleHoverOpen(){this.#clearCloseTimeout(),!this.opts.open.current&&(this.openedViaHover=!0,this.opts.open.current=!0)}handleHoverClose(){this.opts.open.current&& -this.openedViaHover&&!this.hasInteractedWithContent&&(this.opts.open.current=!1)}handleDelayedHoverClose(){this.opts.open.current&&(!this.openedViaHover||this.hasInteractedWithContent||(this.#clearCloseTimeout(),this.closeDelay<=0?this.opts.open.current=!1:this.#domContext&&(this.#closeTimeout=this.#domContext.setTimeout(()=>{this.openedViaHover&&!this.hasInteractedWithContent&&(this.opts.open.current=!1),this.#closeTimeout=null},this.closeDelay))))}cancelDelayedClose(){this.#clearCloseTimeout()}markInteraction(){ -this.hasInteractedWithContent=!0,this.#clearCloseTimeout()}}class PopoverTriggerState{static create(opts){return new PopoverTriggerState(opts,PopoverRootContext.get())}opts;root;attachment;domContext;#openTimeout=null;#closeTimeout=null;#isHovering=state$1(!1);constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(this.opts.ref,v=>this.root.triggerNode=v),this.domContext=new DOMContext(opts.ref),this.root.setDomContext(this.domContext),this.onclick=this.onclick.bind(this), -this.onkeydown=this.onkeydown.bind(this),this.onpointerenter=this.onpointerenter.bind(this),this.onpointerleave=this.onpointerleave.bind(this),watch$1(()=>this.opts.closeDelay.current,delay=>{this.root.closeDelay=delay})}#clearOpenTimeout(){this.#openTimeout!==null&&(this.domContext.clearTimeout(this.#openTimeout),this.#openTimeout=null)}#clearCloseTimeout(){this.#closeTimeout!==null&&(this.domContext.clearTimeout(this.#closeTimeout),this.#closeTimeout=null)}#clearAllTimeouts(){this.#clearOpenTimeout(), -this.#clearCloseTimeout()}onpointerenter(e){if(this.opts.disabled.current||!this.opts.openOnHover.current||isTouch(e)||(set$1(this.#isHovering,!0),this.#clearCloseTimeout(),this.root.cancelDelayedClose(),this.root.opts.open.current||this.root.hoverCooldown))return;const delay=this.opts.openDelay.current;delay<=0?this.root.handleHoverOpen():this.#openTimeout=this.domContext.setTimeout(()=>{this.root.handleHoverOpen(),this.#openTimeout=null},delay)}onpointerleave(e){this.opts.disabled.current||this. -opts.openOnHover.current&&(isTouch(e)||(set$1(this.#isHovering,!1),this.#clearOpenTimeout(),this.root.hoverCooldown=!1))}onclick(e){if(!this.opts.disabled.current&&e.button===0){if(this.#clearAllTimeouts(),get$3(this.#isHovering)&&this.root.opts.open.current&&this.root.openedViaHover){this.root.openedViaHover=!1,this.root.hasInteractedWithContent=!0;return}get$3(this.#isHovering)&&this.opts.openOnHover.current&&this.root.opts.open.current&&(this.root.hoverCooldown=!0),this.root.hoverCooldown&&!this. -root.opts.open.current&&(this.root.hoverCooldown=!1),this.root.toggleOpen()}}onkeydown(e){this.opts.disabled.current||(e.key===ENTER||e.key===SPACE)&&(e.preventDefault(),this.#clearAllTimeouts(),this.root.toggleOpen())}#getAriaControls(){if(this.root.opts.open.current&&this.root.contentNode?.id)return this.root.contentNode?.id}#props=user_derived(()=>({id:this.opts.id.current,"aria-haspopup":"dialog","aria-expanded":boolToStr(this.root.opts.open.current),"data-state":getDataOpenClosed(this.root. -opts.open.current),"aria-controls":this.#getAriaControls(),[popoverAttrs.trigger]:"",disabled:this.opts.disabled.current,onkeydown:this.onkeydown,onclick:this.onclick,onpointerenter:this.onpointerenter,onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class PopoverContentState{static create(opts){return new PopoverContentState(opts,PopoverRootContext.get())}opts;root;attachment;constructor(opts,root2){this.opts= -opts,this.root=root2,this.attachment=attachRef(this.opts.ref,v=>this.root.contentNode=v),this.onpointerdown=this.onpointerdown.bind(this),this.onfocusin=this.onfocusin.bind(this),this.onpointerenter=this.onpointerenter.bind(this),this.onpointerleave=this.onpointerleave.bind(this),new SafePolygon({triggerNode:()=>this.root.triggerNode,contentNode:()=>this.root.contentNode,enabled:()=>this.root.opts.open.current&&this.root.openedViaHover&&!this.root.hasInteractedWithContent,onPointerExit:()=>{this. -root.handleDelayedHoverClose()}})}onpointerdown(_){this.root.markInteraction()}onfocusin(e){const target2=e.target;isElement$1(target2)&&isTabbable(target2)&&this.root.markInteraction()}onpointerenter(e){isTouch(e)||this.root.cancelDelayedClose()}onpointerleave(e){isTouch(e)}onInteractOutside=e=>{if(this.opts.onInteractOutside.current(e),e.defaultPrevented||!isElement$1(e.target))return;const closestTrigger=e.target.closest(popoverAttrs.selector("trigger"));if(!(closestTrigger&&closestTrigger=== -this.root.triggerNode)){if(this.opts.customAnchor.current){if(isElement$1(this.opts.customAnchor.current)){if(this.opts.customAnchor.current.contains(e.target))return}else if(typeof this.opts.customAnchor.current=="string"){const el=document.querySelector(this.opts.customAnchor.current);if(el&&el.contains(e.target))return}}this.root.handleClose()}};onEscapeKeydown=e=>{this.opts.onEscapeKeydown.current(e),!e.defaultPrevented&&this.root.handleClose()};get shouldRender(){return this.root.contentPresence. -shouldRender}get shouldTrapFocus(){return!(this.root.openedViaHover&&!this.root.hasInteractedWithContent)}#snippetProps=user_derived(()=>({open:this.root.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,tabindex:-1,"data-state":getDataOpenClosed(this.root.opts.open.current),...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),[popoverAttrs.content]:"", -style:{pointerEvents:"auto",contain:"layout style"},onpointerdown:this.onpointerdown,onfocusin:this.onfocusin,onpointerenter:this.onpointerenter,onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}popperProps={onInteractOutside:this.onInteractOutside,onEscapeKeydown:this.onEscapeKeydown}}var root_4$J=from_html("
"),root_8$x=from_html("
");function Popover_content$1($$anchor,$$props){ -const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),forceMount=prop($$props,"forceMount",3,!1),onOpenAutoFocus=prop($$props,"onOpenAutoFocus",3,noop$1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),trapFocus=prop($$props,"trapFocus",3,!0),preventScroll=prop($$props,"preventScroll",3,!1),customAnchor=prop( -$$props,"customAnchor",3,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","child","children","ref","id","forceMount","onOpenAutoFocus","onCloseAutoFocus","onEscapeKeydown","onInteractOutside","trapFocus","preventScroll","customAnchor","style"]);const contentState=PopoverContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onInteractOutside:boxWith$1(()=>onInteractOutside()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown()),customAnchor:boxWith$1(()=>customAnchor())}), -mergedProps=user_derived(()=>mergeProps(restProps,contentState.props)),effectiveTrapFocus=user_derived(()=>trapFocus()&&contentState.shouldTrapFocus);function handleOpenAutoFocus(e){contentState.shouldTrapFocus||e.preventDefault(),onOpenAutoFocus()(e)}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get enabled(){return contentState. -root.opts.open.current},get id(){return id2()},get trapFocus(){return get$3(effectiveTrapFocus)},get preventScroll(){return preventScroll()},loop:!0,forceMount:!0,get customAnchor(){return customAnchor()},onOpenAutoFocus:handleOpenAutoFocus,get onCloseAutoFocus(){return onCloseAutoFocus()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars( -"popover")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$J();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1, -()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get open(){return contentState.root.opts.open.current},get id(){ -return id2()},get trapFocus(){return get$3(effectiveTrapFocus)},get preventScroll(){return preventScroll()},loop:!0,forceMount:!1,get customAnchor(){return customAnchor()},onOpenAutoFocus:handleOpenAutoFocus,get onCloseAutoFocus(){return onCloseAutoFocus()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("po\ -pover")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$x();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect( -div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$11=from_html("");function Popover_trigger$1($$anchor,$$props){ -const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),type2=prop($$props,"type",3,"button"),disabled=prop($$props,"disabled",3,!1),openOnHover=prop($$props,"openOnHover",3,!1),openDelay=prop($$props,"openDelay",3,700),closeDelay=prop($$props,"closeDelay",3,300),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","type","disabled","openOnHover","openDelay","closeDelay"]);const triggerState=PopoverTriggerState. -create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),disabled:boxWith$1(()=>!!disabled()),openOnHover:boxWith$1(()=>openOnHover()),openDelay:boxWith$1(()=>openDelay()),closeDelay:boxWith$1(()=>closeDelay())}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));Floating_layer_anchor($$anchor,{get id(){return id2()},get ref(){return triggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{ -var fragment_2=comment$2(),node_1=first_child(fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var button=root_3$11();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor3,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)},$$slots:{default:!0}}), -pop()}function Dialog($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1);DialogRootState.create({variant:boxWith$1(()=>"dialog"),open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor, -fragment),pop()}var root_2$1p=from_html("");function Dialog_close($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","disabled"]);const closeState=DialogCloseState.create({variant:boxWith$1(()=>"close"),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2( -v)),disabled:boxWith$1(()=>!!disabled())}),mergedProps=user_derived(()=>mergeProps(restProps,closeState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1p();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props. -children??noop$3),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_6$x=from_html(" ",1),root_8$w=from_html("
",1);function Dialog_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),onCloseAutoFocus=prop($$props,"onCl\ -oseAutoFocus",3,noop$1),onOpenAutoFocus=prop($$props,"onOpenAutoFocus",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),trapFocus=prop($$props,"trapFocus",3,!0),preventScroll=prop($$props,"preventScroll",3,!0),restoreScrollDelay=prop($$props,"restoreScrollDelay",3,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","children","child","ref","forceMount","onCloseAutoFocus","onOpenAutoFocus","onEscap\ -eKeydown","onInteractOutside","trapFocus","preventScroll","restoreScrollDelay"]);const contentState=DialogContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_2=$$anchor2=>{Focus_scope($$anchor2,{get ref(){return contentState.opts.ref},loop:!0,get trapFocus(){return trapFocus()},get enabled(){return contentState.root.opts.open. -current},get onOpenAutoFocus(){return onOpenAutoFocus()},get onCloseAutoFocus(){return onCloseAutoFocus()},focusScope:($$anchor3,$$arg0)=>{let focusScopeProps=()=>$$arg0?.().props;Escape_layer($$anchor3,spread_props(()=>get$3(mergedProps),{get enabled(){return contentState.root.opts.open.current},get ref(){return contentState.opts.ref},onEscapeKeydown:e=>{onEscapeKeydown()(e),!e.defaultPrevented&&contentState.root.handleClose()},children:($$anchor4,$$slotProps)=>{Dismissible_layer($$anchor4,spread_props( -()=>get$3(mergedProps),{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},onInteractOutside:e=>{onInteractOutside()(e),!e.defaultPrevented&&contentState.root.handleClose()},children:($$anchor5,$$slotProps2)=>{Text_selection_layer($$anchor5,spread_props(()=>get$3(mergedProps),{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},children:($$anchor6,$$slotProps3)=>{var fragment_5=comment$2(),node_1=first_child( -fragment_5);{var consequent_1=$$anchor7=>{var fragment_6=root_6$x(),node_2=first_child(fragment_6);{var consequent=$$anchor8=>{Scroll_lock($$anchor8,{get preventScroll(){return preventScroll()},get restoreScrollDelay(){return restoreScrollDelay()}})};if_block(node_2,$$render=>{contentState.root.opts.open.current&&$$render(consequent)})}var node_3=sibling(node_2,2);{let $0=user_derived(()=>({props:mergeProps(get$3(mergedProps),focusScopeProps()),...contentState.snippetProps}));snippet(node_3,()=>$$props. -child,()=>get$3($0))}append($$anchor7,fragment_6)},alternate=$$anchor7=>{var fragment_8=root_8$w(),node_4=first_child(fragment_8);Scroll_lock(node_4,{get preventScroll(){return preventScroll()}});var div=sibling(node_4,2);attribute_effect(div,$0=>({...$0}),[()=>mergeProps(get$3(mergedProps),focusScopeProps())]);var node_5=child(div);snippet(node_5,()=>$$props.children??noop$3),reset(div),append($$anchor7,fragment_8)};if_block(node_1,$$render=>{$$props.child?$$render(consequent_1):$$render(alternate, --1)})}append($$anchor6,fragment_5)},$$slots:{default:!0}}))},$$slots:{default:!0}}))},$$slots:{default:!0}}))},$$slots:{focusScope:!0}})};if_block(node2,$$render=>{(contentState.shouldRender||forceMount())&&$$render(consequent_2)})}append($$anchor,fragment),pop()}function Menu($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),dir=prop($$props,"dir",3,"ltr"),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1), -variant=prop($$props,"_internal_variant",3,"dropdown-menu"),shouldSkipExitAnimation=prop($$props,"_internal_should_skip_exit_animation",3,void 0);const root2=MenuRootState.create({variant:boxWith$1(()=>variant()),dir:boxWith$1(()=>dir()),onClose:()=>{open2(!1),onOpenChange()(!1)},shouldSkipExitAnimation:()=>shouldSkipExitAnimation()?.()??!1});MenuMenuState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())},root2),Floating_layer( -$$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root_4$I=from_html("
"),root_8$v=from_html("
");function Dropdown_menu_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),loop2=prop($$props,"l\ -oop",3,!0),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),forceMount=prop($$props,"forceMount",3,!1),trapFocus=prop($$props,"trapFocus",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","child","children","ref","loop","onInteractOutside","onEscapeKeydown","onCloseAutoFocus","forceMount","trapFocus","style"]);const contentState=MenuContentState.create( -{id:boxWith$1(()=>id2()),loop:boxWith$1(()=>loop2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onCloseAutoFocus:boxWith$1(()=>onCloseAutoFocus())}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));function handleInteractOutside(e){if(contentState.handleInteractOutside(e),!e.defaultPrevented&&(onInteractOutside()(e),!e.defaultPrevented)){if(e.target&&e.target instanceof Element){const subContentSelector=`[${contentState.parentMenu.root.getBitsAttr("sub-content")}]`;if(e.target.closest( -subContentSelector))return}contentState.parentMenu.onClose()}}function handleEscapeKeydown(e){onEscapeKeydown()(e),!e.defaultPrevented&&contentState.parentMenu.onClose()}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get enabled(){return contentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown, -get trapFocus(){return trapFocus()},get loop(){return loop2()},forceMount:!0,get id(){return id2()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("dropdown-menu")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child( -fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$I();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{ -$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get open(){return contentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,get trapFocus(){return trapFocus()},get loop(){return loop2()},forceMount:!1,get id(){return id2()}, -get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("dropdown-menu")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(), -...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$v();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3, -fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$10=from_html("");function Menu_trigger($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),type2=prop($$props,"type",3,"button"),restProps=rest_props($$props,["$$slots", -"$$events","$$legacy","id","ref","child","children","disabled","type"]);const triggerState=DropdownMenuTriggerState.create({id:boxWith$1(()=>id2()),disabled:boxWith$1(()=>disabled()??!1),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));Floating_layer_anchor($$anchor,{get id(){return id2()},get ref(){return triggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{ -var consequent=$$anchor3=>{var fragment_2=comment$2(),node_1=first_child(fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var button=root_3$10();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor3,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)}, -$$slots:{default:!0}}),pop()}const labelAttrs=createBitsAttrs({component:"label",parts:["root"]});class LabelRootState{static create(opts){return new LabelRootState(opts)}opts;attachment;constructor(opts){this.opts=opts,this.attachment=attachRef(this.opts.ref),this.onmousedown=this.onmousedown.bind(this)}onmousedown(e){e.detail>1&&e.preventDefault()}#props=user_derived(()=>({id:this.opts.id.current,[labelAttrs.root]:"",onmousedown:this.onmousedown,...this.attachment}));get props(){return get$3(this.#props)}set props(value){ -set$1(this.#props,value)}}var root_2$1o=from_html("");function Label$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","for"]);const rootState=LabelRootState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,rootState.props,{for:$$props. -for}));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var label=root_2$1o();attribute_effect(label,()=>({...get$3(mergedProps),for:$$props.for}));var node_2=child(label);snippet(node_2,()=>$$props.children??noop$3),reset(label),append($$anchor2,label)};if_block(node2,$$render=>{$$props.child? -$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}class SvelteResizeObserver{#node;#onResize;constructor(node2,onResize){this.#node=node2,this.#onResize=onResize,this.handler=this.handler.bind(this),user_effect(this.handler)}handler(){let rAF=0;const _node=this.#node();if(!_node)return;const resizeObserver=new ResizeObserver(()=>{cancelAnimationFrame(rAF),rAF=window.requestAnimationFrame(this.#onResize)});return resizeObserver.observe(_node),()=>{window.cancelAnimationFrame( -rAF),resizeObserver.unobserve(_node)}}}class Presence{opts;present;#afterAnimations;#isPresent=state$1(!1);#hasMounted=!1;#transitionStatus=state$1(void 0);#transitionFrame=null;constructor(opts){this.opts=opts,this.present=this.opts.open,set$1(this.#isPresent,opts.open.current,!0),this.#afterAnimations=new AnimationsComplete({ref:this.opts.ref,afterTick:this.opts.open}),onDestroyEffect(()=>this.#clearTransitionFrame()),watch$1(()=>this.present.current,isOpen=>{if(!this.#hasMounted){this.#hasMounted= -!0;return}this.#clearTransitionFrame(),isOpen&&set$1(this.#isPresent,!0),set$1(this.#transitionStatus,isOpen?"starting":"ending",!0),isOpen&&(this.#transitionFrame=window.requestAnimationFrame(()=>{this.#transitionFrame=null,this.present.current&&set$1(this.#transitionStatus,void 0)})),this.#afterAnimations.run(()=>{isOpen===this.present.current&&(isOpen||set$1(this.#isPresent,!1),set$1(this.#transitionStatus,void 0))})})}#_isPresent=user_derived(()=>get$3(this.#isPresent));get isPresent(){return get$3( -this.#_isPresent)}set isPresent(value){set$1(this.#_isPresent,value)}get transitionStatus(){return get$3(this.#transitionStatus)}#clearTransitionFrame(){this.#transitionFrame!==null&&(window.cancelAnimationFrame(this.#transitionFrame),this.#transitionFrame=null)}}function Presence_layer($$anchor,$$props){push$1($$props,!0);const presenceState=new Presence({open:boxWith$1(()=>$$props.open),ref:$$props.ref});var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(), -node_1=first_child(fragment_1);snippet(node_1,()=>$$props.presence??noop$3,()=>({present:presenceState.isPresent,transitionStatus:presenceState.transitionStatus})),append($$anchor2,fragment_1)};if_block(node2,$$render=>{($$props.forceMount||$$props.open||presenceState.isPresent)&&$$render(consequent)})}append($$anchor,fragment),pop()}function Popover$1($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop( -$$props,"onOpenChangeComplete",3,noop$1);PopoverRootState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),Floating_layer($$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}function clamp(n,min2,max2){return Math.min(max2,Math.max(min2,n))}class StateMachine{state;#machine;constructor(initialState,machine){ +"sub-trigger"))||this.#isPointerMovingToSubmenu()||this.parentMenu.root.isUsingKeyboard.current)return;this.parentMenu.contentNode?.focus({preventScroll:!0}),this.rovingFocusGroup.setCurrentTabStopId("")}onTriggerLeave(){return!!this.#isPointerMovingToSubmenu()}handleInteractOutside(e){if(!isElementOrSVGElement(e.target))return;const triggerId=this.parentMenu.triggerNode?.id;if(e.target.id===triggerId){e.preventDefault();return}if(e.target.closest(`#${triggerId}`)){e.preventDefault();return}this. +parentMenu.root.ignoreCloseAutoFocus=!0,afterTick(()=>{this.parentMenu.root.ignoreCloseAutoFocus=!1})}get shouldRender(){return this.parentMenu.contentPresence.shouldRender}#snippetProps=user_derived(()=>({open:this.parentMenu.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,role:"menu","aria-orientation":"vertical",[this.parentMenu.root.getBitsAttr("content")]:"","da\ +ta-state":getDataOpenClosed(this.parentMenu.opts.open.current),...getDataTransitionAttrs(this.parentMenu.contentPresence.transitionStatus),onkeydown:this.onkeydown,onblur:this.onblur,onfocus:this.onfocus,dir:this.parentMenu.root.opts.dir.current,style:{pointerEvents:"auto",contain:"layout style"},...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}popperProps={onCloseAutoFocus:e=>this.onCloseAutoFocus(e)}}class MenuItemSharedState{opts;content;attachment;#isFocused=state$1( +!1);constructor(opts,content2){this.opts=opts,this.content=content2,this.attachment=attachRef(this.opts.ref),this.onpointermove=this.onpointermove.bind(this),this.onpointerleave=this.onpointerleave.bind(this),this.onfocus=this.onfocus.bind(this),this.onblur=this.onblur.bind(this)}onpointermove(e){if(!e.defaultPrevented&&isMouseEvent(e))if(this.opts.disabled.current)this.content.onItemLeave(e);else{if(this.content.onItemEnter())return;const item=e.currentTarget;if(!isHTMLElement$1(item))return;item. +focus({preventScroll:!0})}}onpointerleave(e){e.defaultPrevented||isMouseEvent(e)&&this.content.onItemLeave(e)}onfocus(e){afterTick(()=>{e.defaultPrevented||this.opts.disabled.current||set$1(this.#isFocused,!0)})}onblur(e){afterTick(()=>{e.defaultPrevented||set$1(this.#isFocused,!1)})}#props=user_derived(()=>({id:this.opts.id.current,tabindex:-1,role:"menuitem","aria-disabled":boolToStr(this.opts.disabled.current),"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled.current),"data-highlighted":get$3( +this.#isFocused)?"":void 0,[this.content.parentMenu.root.getBitsAttr("item")]:"",onpointermove:this.onpointermove,onpointerleave:this.onpointerleave,onfocus:this.onfocus,onblur:this.onblur,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class MenuItemState{static create(opts){const item=new MenuItemSharedState(opts,MenuContentContext.get());return new MenuItemState(opts,item)}opts;item;root;#isPointerDown=!1;constructor(opts,item){this.opts= +opts,this.item=item,this.root=item.content.parentMenu.root,this.onkeydown=this.onkeydown.bind(this),this.onclick=this.onclick.bind(this),this.onpointerdown=this.onpointerdown.bind(this),this.onpointerup=this.onpointerup.bind(this)}#handleSelect(){if(this.item.opts.disabled.current)return;const selectEvent=new CustomEvent("menuitemselect",{bubbles:!0,cancelable:!0});if(this.opts.onSelect.current(selectEvent),selectEvent.defaultPrevented){this.item.content.parentMenu.root.isUsingKeyboard.current=!1; +return}this.opts.closeOnSelect.current&&this.item.content.parentMenu.root.opts.onClose()}onkeydown(e){const isTypingAhead=this.item.content.search!=="";if(!(this.item.opts.disabled.current||isTypingAhead&&e.key===SPACE)&&SELECTION_KEYS.includes(e.key)){if(!isHTMLElement$1(e.currentTarget))return;e.currentTarget.click(),e.preventDefault()}}onclick(_){this.item.opts.disabled.current||this.#handleSelect()}onpointerup(e){if(!e.defaultPrevented&&!this.#isPointerDown){if(!isHTMLElement$1(e.currentTarget)) +return;e.currentTarget?.click()}}onpointerdown(_){this.#isPointerDown=!0}#props=user_derived(()=>mergeProps(this.item.props,{onclick:this.onclick,onpointerdown:this.onpointerdown,onpointerup:this.onpointerup,onkeydown:this.onkeydown}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class MenuSubTriggerState{static create(opts){const content2=MenuContentContext.get(),item=new MenuItemSharedState(opts,content2),submenu=MenuMenuContext.get();return new MenuSubTriggerState( +opts,item,content2,submenu)}opts;item;content;submenu;attachment;#openTimer=null;constructor(opts,item,content2,submenu){this.opts=opts,this.item=item,this.content=content2,this.submenu=submenu,this.attachment=attachRef(this.opts.ref,v=>this.submenu.triggerNode=v),this.onpointerleave=this.onpointerleave.bind(this),this.onpointermove=this.onpointermove.bind(this),this.onkeydown=this.onkeydown.bind(this),this.onclick=this.onclick.bind(this),onDestroyEffect(()=>{this.#clearOpenTimer()})}#clearOpenTimer(){ +this.#openTimer!==null&&(this.content.domContext.getWindow().clearTimeout(this.#openTimer),this.#openTimer=null)}onpointermove(e){if(isMouseEvent(e)){if(this.submenu.root.isPointerInTransit){this.#openTimer!==null&&this.#clearOpenTimer();return}if(!this.item.opts.disabled.current&&!this.submenu.opts.open.current&&!this.#openTimer){const openDelay=this.opts.openDelay.current;if(openDelay<=0){this.submenu.onOpen();return}this.#openTimer=this.content.domContext.setTimeout(()=>{if(this.submenu.root. +isPointerInTransit){this.#clearOpenTimer();return}this.submenu.onOpen(),this.#clearOpenTimer()},openDelay)}}}onpointerleave(e){isMouseEvent(e)&&this.#clearOpenTimer()}onkeydown(e){const isTypingAhead=this.content.search!=="";this.item.opts.disabled.current||isTypingAhead&&e.key===SPACE||SUB_OPEN_KEYS[this.submenu.root.opts.dir.current].includes(e.key)&&(e.currentTarget.click(),e.preventDefault())}onclick(e){if(this.item.opts.disabled.current||!isHTMLElement$1(e.currentTarget))return;e.currentTarget. +focus();const selectEvent=new CustomEvent("menusubtriggerselect",{bubbles:!0,cancelable:!0});this.opts.onSelect.current(selectEvent),this.submenu.opts.open.current||(this.submenu.onOpen(),afterTick(()=>{const contentNode=this.submenu.contentNode;contentNode&&MenuOpenEvent.dispatch(contentNode)}))}#props=user_derived(()=>mergeProps({"aria-haspopup":"menu","aria-expanded":boolToStr(this.submenu.opts.open.current),"data-state":getDataOpenClosed(this.submenu.opts.open.current),"aria-controls":this.submenu. +opts.open.current?this.submenu.contentId.current:void 0,[this.submenu.root.getBitsAttr("sub-trigger")]:"",onclick:this.onclick,onpointermove:this.onpointermove,onpointerleave:this.onpointerleave,onkeydown:this.onkeydown,...this.attachment},this.item.props));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class MenuSeparatorState{static create(opts){return new MenuSeparatorState(opts,MenuRootContext.get())}opts;root;attachment;constructor(opts,root2){this.opts=opts, +this.root=root2,this.attachment=attachRef(this.opts.ref)}#props=user_derived(()=>({id:this.opts.id.current,role:"group",[this.root.getBitsAttr("separator")]:"",...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class DropdownMenuTriggerState{static create(opts){return new DropdownMenuTriggerState(opts,MenuMenuContext.get())}opts;parentMenu;attachment;constructor(opts,parentMenu){this.opts=opts,this.parentMenu=parentMenu,this.attachment=attachRef( +this.opts.ref,v=>this.parentMenu.triggerNode=v)}onclick=e=>{this.opts.disabled.current||e.detail!==0||(this.parentMenu.toggleOpen(),e.preventDefault())};onpointerdown=e=>{if(!this.opts.disabled.current){if(e.pointerType==="touch")return e.preventDefault();e.button===0&&e.ctrlKey===!1&&(this.parentMenu.toggleOpen(),this.parentMenu.opts.open.current||e.preventDefault())}};onpointerup=e=>{this.opts.disabled.current||e.pointerType==="touch"&&(e.preventDefault(),this.parentMenu.toggleOpen())};onkeydown=e=>{ +if(!this.opts.disabled.current){if(e.key===SPACE||e.key===ENTER){this.parentMenu.toggleOpen(),e.preventDefault();return}e.key===ARROW_DOWN&&(this.parentMenu.onOpen(),e.preventDefault())}};#ariaControls=user_derived(()=>{if(this.parentMenu.opts.open.current&&this.parentMenu.contentId.current)return this.parentMenu.contentId.current});#props=user_derived(()=>({id:this.opts.id.current,disabled:this.opts.disabled.current,"aria-haspopup":"menu","aria-expanded":boolToStr(this.parentMenu.opts.open.current), +"aria-controls":get$3(this.#ariaControls),"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled.current),"data-state":getDataOpenClosed(this.parentMenu.opts.open.current),[this.parentMenu.root.getBitsAttr("trigger")]:"",onclick:this.onclick,onpointerdown:this.onpointerdown,onpointerup:this.onpointerup,onkeydown:this.onkeydown,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class MenuSubmenuState{static create(opts){const menu=MenuMenuContext. +get();return MenuMenuContext.set(new MenuMenuState(opts,menu.root,menu))}}globalThis.bitsDismissableLayers??=new Map;class DismissibleLayerState{static create(opts){return new DismissibleLayerState(opts)}opts;#interactOutsideProp;#behaviorType;#interceptedEvents={pointerdown:!1};#isResponsibleLayer=!1;#isFocusInsideDOMTree=!1;#documentObj=void 0;#onFocusOutside;#unsubClickListener=noop$1;constructor(opts){this.opts=opts,this.#behaviorType=opts.interactOutsideBehavior,this.#interactOutsideProp=opts. +onInteractOutside,this.#onFocusOutside=opts.onFocusOutside,user_effect(()=>{this.#documentObj=getOwnerDocument(this.opts.ref.current)});let unsubEvents=noop$1;const cleanup=()=>{this.#resetState(),globalThis.bitsDismissableLayers.delete(this),this.#handleInteractOutside.destroy(),unsubEvents()};watch$1([()=>this.opts.enabled.current,()=>this.opts.ref.current],()=>{if(!(!this.opts.enabled.current||!this.opts.ref.current))return afterSleep(1,()=>{this.opts.ref.current&&(globalThis.bitsDismissableLayers. +set(this,this.#behaviorType),unsubEvents(),unsubEvents=this.#addEventListeners())}),cleanup}),onDestroyEffect(()=>{this.#resetState.destroy(),globalThis.bitsDismissableLayers.delete(this),this.#handleInteractOutside.destroy(),this.#unsubClickListener(),unsubEvents()})}#handleFocus=event2=>{event2.defaultPrevented||this.opts.ref.current&&afterTick(()=>{!this.opts.ref.current||this.#isTargetWithinLayer(event2.target)||event2.target&&!this.#isFocusInsideDOMTree&&this.#onFocusOutside.current?.(event2)})};#addEventListeners(){ +return executeCallbacks(on(this.#documentObj,"pointerdown",executeCallbacks(this.#markInterceptedEvent,this.#markResponsibleLayer),{capture:!0}),on(this.#documentObj,"pointerdown",executeCallbacks(this.#markNonInterceptedEvent,this.#handleInteractOutside)),on(this.#documentObj,"focusin",this.#handleFocus))}#handleDismiss=e=>{let event2=e;event2.defaultPrevented&&(event2=createWrappedEvent(e)),this.#interactOutsideProp.current(e)};#handleInteractOutside=debounce$1(e=>{if(!this.opts.ref.current){this.#unsubClickListener(); +return}const isEventValid=this.opts.isValidEvent.current(e,this.opts.ref.current)||isValidEvent(e,this.opts.ref.current);if(!this.#isResponsibleLayer||this.#isAnyEventIntercepted()||!isEventValid){this.#unsubClickListener();return}let event2=e;if(event2.defaultPrevented&&(event2=createWrappedEvent(event2)),this.#behaviorType.current!=="close"&&this.#behaviorType.current!=="defer-otherwise-close"){this.#unsubClickListener();return}e.pointerType==="touch"?(this.#unsubClickListener(),this.#unsubClickListener= +on(this.#documentObj,"click",this.#handleDismiss,{once:!0})):this.#interactOutsideProp.current(event2)},10);#markInterceptedEvent=e=>{this.#interceptedEvents[e.type]=!0};#markNonInterceptedEvent=e=>{this.#interceptedEvents[e.type]=!1};#markResponsibleLayer=()=>{this.opts.ref.current&&(this.#isResponsibleLayer=isResponsibleLayer(this.opts.ref.current))};#isTargetWithinLayer=target2=>this.opts.ref.current?isOrContainsTarget(this.opts.ref.current,target2):!1;#resetState=debounce$1(()=>{for(const eventType in this.#interceptedEvents) +this.#interceptedEvents[eventType]=!1;this.#isResponsibleLayer=!1},20);#isAnyEventIntercepted(){return Object.values(this.#interceptedEvents).some(Boolean)}#onfocuscapture=()=>{this.#isFocusInsideDOMTree=!0};#onblurcapture=()=>{this.#isFocusInsideDOMTree=!1};props={onfocuscapture:this.#onfocuscapture,onblurcapture:this.#onblurcapture}}function getTopMostDismissableLayer(layersArr=[...globalThis.bitsDismissableLayers]){return layersArr.findLast(([_,{current:behaviorType}])=>behaviorType==="close"|| +behaviorType==="ignore")}function isResponsibleLayer(node2){const layersArr=[...globalThis.bitsDismissableLayers],topMostLayer=getTopMostDismissableLayer(layersArr);if(topMostLayer)return topMostLayer[0].opts.ref.current===node2;const[firstLayerNode]=layersArr[0];return firstLayerNode.opts.ref.current===node2}function isValidEvent(e,node2){const target2=e.target;if(!isElementOrSVGElement(target2))return!1;const targetIsContextMenuTrigger=!!target2.closest(`[${CONTEXT_MENU_TRIGGER_ATTR}]`),nodeIsContextMenu=!!node2. +closest(`[${CONTEXT_MENU_CONTENT_ATTR}]`);return"button"in e&&e.button>0&&!targetIsContextMenuTrigger?!1:"button"in e&&e.button===0&&targetIsContextMenuTrigger&&nodeIsContextMenu?!0:targetIsContextMenuTrigger&&nodeIsContextMenu?!1:getOwnerDocument(target2).documentElement.contains(target2)&&!isOrContainsTarget(node2,target2)&&isClickTrulyOutside(e,node2)}function createWrappedEvent(e){const capturedCurrentTarget=e.currentTarget,capturedTarget=e.target;let newEvent;e instanceof PointerEvent?newEvent= +new PointerEvent(e.type,e):newEvent=new PointerEvent("pointerdown",e);let isPrevented=!1;return new Proxy(newEvent,{get:(target2,prop2)=>prop2==="currentTarget"?capturedCurrentTarget:prop2==="target"?capturedTarget:prop2==="preventDefault"?()=>{isPrevented=!0,typeof target2.preventDefault=="function"&&target2.preventDefault()}:prop2==="defaultPrevented"?isPrevented:prop2 in target2?target2[prop2]:e[prop2]})}function Dismissible_layer($$anchor,$$props){push$1($$props,!0);let interactOutsideBehavior=prop( +$$props,"interactOutsideBehavior",3,"close"),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),onFocusOutside=prop($$props,"onFocusOutside",3,noop$1),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1);const dismissibleLayerState=DismissibleLayerState.create({id:boxWith$1(()=>$$props.id),interactOutsideBehavior:boxWith$1(()=>interactOutsideBehavior()),onInteractOutside:boxWith$1(()=>onInteractOutside()),enabled:boxWith$1(()=>$$props.enabled),onFocusOutside:boxWith$1(()=>onFocusOutside()), +isValidEvent:boxWith$1(()=>isValidEvent2()),ref:$$props.ref});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3,()=>({props:dismissibleLayerState.props})),append($$anchor,fragment),pop()}globalThis.bitsEscapeLayers??=new Map;class EscapeLayerState{static create(opts){return new EscapeLayerState(opts)}opts;domContext;constructor(opts){this.opts=opts,this.domContext=new DOMContext(this.opts.ref);let unsubEvents=noop$1;watch$1(()=>opts.enabled.current,enabled=>(enabled&& +(globalThis.bitsEscapeLayers.set(this,opts.escapeKeydownBehavior),unsubEvents=this.#addEventListener()),()=>{unsubEvents(),globalThis.bitsEscapeLayers.delete(this)}))}#addEventListener=()=>on(this.domContext.getDocument(),"keydown",this.#onkeydown,{passive:!1});#onkeydown=e=>{if(e.key!==ESCAPE||!isResponsibleEscapeLayer(this))return;const clonedEvent=new KeyboardEvent(e.type,e);e.preventDefault();const behaviorType=this.opts.escapeKeydownBehavior.current;behaviorType!=="close"&&behaviorType!=="d\ +efer-otherwise-close"||this.opts.onEscapeKeydown.current(clonedEvent)}}function isResponsibleEscapeLayer(instance){const layersArr=[...globalThis.bitsEscapeLayers],topMostLayer=layersArr.findLast(([_,{current:behaviorType}])=>behaviorType==="close"||behaviorType==="ignore");if(topMostLayer)return topMostLayer[0]===instance;const[firstLayerNode]=layersArr[0];return firstLayerNode===instance}function Escape_layer($$anchor,$$props){push$1($$props,!0);let escapeKeydownBehavior=prop($$props,"escapeKe\ +ydownBehavior",3,"close"),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1);EscapeLayerState.create({escapeKeydownBehavior:boxWith$1(()=>escapeKeydownBehavior()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown()),enabled:boxWith$1(()=>$$props.enabled),ref:$$props.ref});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}class FocusScopeManager{static instance;#scopeStack=simpleBox([]);#focusHistory=new WeakMap;#preFocusHistory=new WeakMap;static getInstance(){ +return this.instance||(this.instance=new FocusScopeManager),this.instance}register(scope2){const current2=this.getActive();current2&¤t2!==scope2&¤t2.pause();const activeElement=document.activeElement;activeElement&&activeElement!==document.body&&this.#preFocusHistory.set(scope2,activeElement),this.#scopeStack.current=this.#scopeStack.current.filter(s2=>s2!==scope2),this.#scopeStack.current.unshift(scope2)}unregister(scope2){this.#scopeStack.current=this.#scopeStack.current.filter(s2=>s2!== +scope2);const next2=this.getActive();next2&&next2.resume()}getActive(){return this.#scopeStack.current[0]}setFocusMemory(scope2,element2){this.#focusHistory.set(scope2,element2)}getFocusMemory(scope2){return this.#focusHistory.get(scope2)}isActiveScope(scope2){return this.getActive()===scope2}setPreFocusMemory(scope2,element2){this.#preFocusHistory.set(scope2,element2)}getPreFocusMemory(scope2){return this.#preFocusHistory.get(scope2)}clearPreFocusMemory(scope2){this.#preFocusHistory.delete(scope2)}} +class FocusScope{#paused=!1;#container=null;#manager=FocusScopeManager.getInstance();#cleanupFns=[];#opts;constructor(opts){this.#opts=opts}get paused(){return this.#paused}pause(){this.#paused=!0}resume(){this.#paused=!1}#cleanup(){for(const fn of this.#cleanupFns)fn();this.#cleanupFns=[]}mount(container2){this.#container&&this.unmount(),this.#container=container2,this.#manager.register(this),this.#setupEventListeners(),this.#handleOpenAutoFocus()}unmount(){this.#container&&(this.#cleanup(),this.#handleCloseAutoFocus(), +this.#manager.unregister(this),this.#manager.clearPreFocusMemory(this),this.#container=null)}#handleOpenAutoFocus(){if(!this.#container)return;const event2=new CustomEvent("focusScope.onOpenAutoFocus",{bubbles:!1,cancelable:!0});this.#opts.onOpenAutoFocus.current(event2),event2.defaultPrevented||requestAnimationFrame(()=>{if(!this.#container)return;const firstTabbable=this.#getFirstTabbable();firstTabbable?(firstTabbable.focus(),this.#manager.setFocusMemory(this,firstTabbable)):this.#container.focus()})}#handleCloseAutoFocus(){ +const event2=new CustomEvent("focusScope.onCloseAutoFocus",{bubbles:!1,cancelable:!0});if(this.#opts.onCloseAutoFocus.current?.(event2),!event2.defaultPrevented){const preFocusedElement=this.#manager.getPreFocusMemory(this);if(preFocusedElement&&document.contains(preFocusedElement))try{preFocusedElement.focus()}catch{document.body.focus()}}}#setupEventListeners(){if(!this.#container||!this.#opts.trap.current)return;const container2=this.#container,doc=container2.ownerDocument,handleFocus=e=>{if(this.#paused|| +!this.#manager.isActiveScope(this))return;const target2=e.target;if(!target2)return;if(container2.contains(target2))this.#manager.setFocusMemory(this,target2);else{const lastFocused=this.#manager.getFocusMemory(this);if(lastFocused&&container2.contains(lastFocused)&&isFocusable(lastFocused))e.preventDefault(),lastFocused.focus();else{const firstTabbable=this.#getFirstTabbable(),firstFocusable=this.#getAllFocusables()[0];(firstTabbable||firstFocusable||container2).focus()}}},handleKeydown=e=>{if(!this.#opts. +loop||this.#paused||e.key!=="Tab"||!this.#manager.isActiveScope(this))return;const tabbables=this.#getTabbables();if(tabbables.length===0)return;const first=tabbables[0],last=tabbables[tabbables.length-1];!e.shiftKey&&doc.activeElement===last?(e.preventDefault(),first.focus()):e.shiftKey&&doc.activeElement===first&&(e.preventDefault(),last.focus())};this.#cleanupFns.push(on(doc,"focusin",handleFocus,{capture:!0}),on(container2,"keydown",handleKeydown));const observer=new MutationObserver(()=>{const lastFocused=this.#manager. +getFocusMemory(this);if(lastFocused&&!container2.contains(lastFocused)){const firstTabbable=this.#getFirstTabbable(),firstFocusable=this.#getAllFocusables()[0],elementToFocus=firstTabbable||firstFocusable;elementToFocus?(elementToFocus.focus(),this.#manager.setFocusMemory(this,elementToFocus)):container2.focus()}});observer.observe(container2,{childList:!0,subtree:!0}),this.#cleanupFns.push(()=>observer.disconnect())}#getTabbables(){return this.#container?tabbable(this.#container,{includeContainer:!1, +getShadowRoot:!0}):[]}#getFirstTabbable(){return this.#getTabbables()[0]||null}#getAllFocusables(){return this.#container?focusable(this.#container,{includeContainer:!1,getShadowRoot:!0}):[]}static use(opts){let scope2=null;return watch$1([()=>opts.ref.current,()=>opts.enabled.current],([ref2,enabled])=>{ref2&&enabled?(scope2||(scope2=new FocusScope(opts)),scope2.mount(ref2)):scope2&&(scope2.unmount(),scope2=null)}),onDestroyEffect(()=>{scope2?.unmount()}),{get props(){return{tabindex:-1}}}}}function Focus_scope($$anchor,$$props){ +push$1($$props,!0);let enabled=prop($$props,"enabled",3,!1),trapFocus=prop($$props,"trapFocus",3,!1),loop2=prop($$props,"loop",3,!1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),onOpenAutoFocus=prop($$props,"onOpenAutoFocus",3,noop$1);const focusScopeState=FocusScope.use({enabled:boxWith$1(()=>enabled()),trap:boxWith$1(()=>trapFocus()),loop:loop2(),onCloseAutoFocus:boxWith$1(()=>onCloseAutoFocus()),onOpenAutoFocus:boxWith$1(()=>onOpenAutoFocus()),ref:$$props.ref});var fragment=comment$2(), +node2=first_child(fragment);snippet(node2,()=>$$props.focusScope??noop$3,()=>({props:focusScopeState.props})),append($$anchor,fragment),pop()}const noopPointer=()=>{};globalThis.bitsTextSelectionLayers??=new Map;class TextSelectionLayerState{static create(opts){return new TextSelectionLayerState(opts)}opts;domContext;#unsubSelectionLock=noop$1;#enabledSnapshot=!1;#onPointerDownSnapshot=noopPointer;#onPointerUpSnapshot=noopPointer;constructor(opts){this.opts=opts,this.domContext=new DOMContext(opts. +ref);let unsubEvents=noop$1;watch$1(()=>[this.opts.enabled.current,this.opts.onPointerDown.current,this.opts.onPointerUp.current],([enabled,onPointerDown,onPointerUp])=>(this.#enabledSnapshot=enabled,this.#onPointerDownSnapshot=onPointerDown,this.#onPointerUpSnapshot=onPointerUp,enabled&&(globalThis.bitsTextSelectionLayers.set(this,this.opts.enabled),unsubEvents(),unsubEvents=this.#addEventListeners()),()=>{this.#enabledSnapshot=!1,unsubEvents(),this.#resetSelectionLock(),globalThis.bitsTextSelectionLayers. +delete(this)}))}#addEventListeners(){return executeCallbacks(on(this.domContext.getDocument(),"pointerdown",this.#pointerdown),on(this.domContext.getDocument(),"pointerup",composeHandlers(this.#resetSelectionLock,this.#pointerupUserHandler)))}#pointerupUserHandler=e=>{this.#onPointerUpSnapshot(e)};#pointerdown=e=>{const node2=this.opts.ref.current,target2=e.target;!isHTMLElement$1(node2)||!isHTMLElement$1(target2)||!this.#enabledSnapshot||!isHighestLayer(this)||!contains$3(node2,target2)||(this.#onPointerDownSnapshot( +e),!e.defaultPrevented&&(this.#unsubSelectionLock=preventTextSelectionOverflow(node2,this.domContext.getDocument().body)))};#resetSelectionLock=()=>{this.#unsubSelectionLock(),this.#unsubSelectionLock=noop$1}}const getUserSelect=node2=>node2.style.userSelect||node2.style.webkitUserSelect;function preventTextSelectionOverflow(node2,body2){const originalBodyUserSelect=getUserSelect(body2),originalNodeUserSelect=getUserSelect(node2);return setUserSelect(body2,"none"),setUserSelect(node2,"text"),()=>{ +setUserSelect(body2,originalBodyUserSelect),setUserSelect(node2,originalNodeUserSelect)}}function setUserSelect(node2,value){node2.style.userSelect=value,node2.style.webkitUserSelect=value}function isHighestLayer(instance){const layersArr=[...globalThis.bitsTextSelectionLayers];if(!layersArr.length)return!1;const highestLayer=layersArr.at(-1);return highestLayer?highestLayer[0]===instance:!1}function Text_selection_layer($$anchor,$$props){push$1($$props,!0);let preventOverflowTextSelection=prop( +$$props,"preventOverflowTextSelection",3,!0),onPointerDown=prop($$props,"onPointerDown",3,noop$1),onPointerUp=prop($$props,"onPointerUp",3,noop$1);TextSelectionLayerState.create({id:boxWith$1(()=>$$props.id),onPointerDown:boxWith$1(()=>onPointerDown()),onPointerUp:boxWith$1(()=>onPointerUp()),enabled:boxWith$1(()=>$$props.enabled&&preventOverflowTextSelection()),ref:$$props.ref});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment), +pop()}globalThis.bitsIdCounter??={current:0};function useId(prefix="bits"){return globalThis.bitsIdCounter.current++,`${prefix}-${globalThis.bitsIdCounter.current}`}class SharedState{#factory;#subscribers=0;#state=state$1();#scope;constructor(factory){this.#factory=factory}#dispose(){this.#subscribers-=1,this.#scope&&this.#subscribers<=0&&(this.#scope(),set$1(this.#state,void 0),this.#scope=void 0)}get(...args){return this.#subscribers+=1,get$3(this.#state)===void 0&&(this.#scope=effect_root(()=>{ +set$1(this.#state,this.#factory(...args),!0)})),user_effect(()=>()=>{this.#dispose()}),get$3(this.#state)}}const lockMap=new SvelteMap;let initialBodyStyle=state$1(null),stopTouchMoveListener=null,cleanupTimeoutId=null,isInCleanupTransition=!1;const anyLocked=boxWith$1(()=>{for(const value of lockMap.values())if(value)return!0;return!1});let cleanupScheduledAt=null;const bodyLockStackCount=new SharedState(()=>{function resetBodyStyle(){document.body.setAttribute("style",get$3(initialBodyStyle)?? +""),document.body.style.removeProperty("--scrollbar-width"),isIOS&&stopTouchMoveListener?.(),set$1(initialBodyStyle,null)}function cancelPendingCleanup(){cleanupTimeoutId!==null&&(window.clearTimeout(cleanupTimeoutId),cleanupTimeoutId=null)}function scheduleCleanupIfNoNewLocks(delay,callback){cancelPendingCleanup(),isInCleanupTransition=!0,cleanupScheduledAt=Date.now();const currentCleanupId=cleanupScheduledAt,cleanupFn=()=>{cleanupTimeoutId=null,cleanupScheduledAt===currentCleanupId&&(isAnyLocked( +lockMap)?isInCleanupTransition=!1:(isInCleanupTransition=!1,callback()))},actualDelay=delay===null?24:delay;cleanupTimeoutId=window.setTimeout(cleanupFn,actualDelay)}function ensureInitialStyleCaptured(){get$3(initialBodyStyle)===null&&lockMap.size===0&&!isInCleanupTransition&&set$1(initialBodyStyle,document.body.getAttribute("style"),!0)}return watch$1(()=>anyLocked.current,()=>{if(!anyLocked.current)return;ensureInitialStyleCaptured(),isInCleanupTransition=!1;const htmlStyle=getComputedStyle(document. +documentElement),bodyStyle=getComputedStyle(document.body),hasStableGutter=htmlStyle.scrollbarGutter?.includes("stable")||bodyStyle.scrollbarGutter?.includes("stable"),verticalScrollbarWidth=window.innerWidth-document.documentElement.clientWidth,config2={padding:Number.parseInt(bodyStyle.paddingRight??"0",10)+verticalScrollbarWidth,margin:Number.parseInt(bodyStyle.marginRight??"0",10)};verticalScrollbarWidth>0&&!hasStableGutter&&(document.body.style.paddingRight=`${config2.padding}px`,document.body. +style.marginRight=`${config2.margin}px`,document.body.style.setProperty("--scrollbar-width",`${verticalScrollbarWidth}px`)),document.body.style.overflow="hidden",isIOS&&(stopTouchMoveListener=on(document,"touchmove",e=>{e.target===document.documentElement&&(e.touches.length>1||e.preventDefault())},{passive:!1})),afterTick(()=>{document.body.style.pointerEvents="none",document.body.style.overflow="hidden"})}),onDestroyEffect(()=>()=>{stopTouchMoveListener?.()}),{get lockMap(){return lockMap},resetBodyStyle, +scheduleCleanupIfNoNewLocks,cancelPendingCleanup,ensureInitialStyleCaptured}});class BodyScrollLock{#id=useId();#initialState;#restoreScrollDelay=()=>null;#countState;locked;constructor(initialState,restoreScrollDelay=()=>null){this.#initialState=initialState,this.#restoreScrollDelay=restoreScrollDelay,this.#countState=bodyLockStackCount.get(),this.#countState&&(this.#countState.cancelPendingCleanup(),this.#countState.ensureInitialStyleCaptured(),this.#countState.lockMap.set(this.#id,this.#initialState?? +!1),this.locked=boxWith$1(()=>this.#countState.lockMap.get(this.#id)??!1,v=>this.#countState.lockMap.set(this.#id,v)),onDestroyEffect(()=>{if(this.#countState.lockMap.delete(this.#id),isAnyLocked(this.#countState.lockMap))return;const restoreScrollDelay2=this.#restoreScrollDelay();this.#countState.scheduleCleanupIfNoNewLocks(restoreScrollDelay2,()=>{this.#countState.resetBodyStyle()})}))}}function isAnyLocked(map2){for(const[_,value]of map2)if(value)return!0;return!1}function Scroll_lock($$anchor,$$props){ +push$1($$props,!0);let preventScroll=prop($$props,"preventScroll",3,!0),restoreScrollDelay=prop($$props,"restoreScrollDelay",3,null);preventScroll()&&new BodyScrollLock(preventScroll(),()=>restoreScrollDelay()),pop()}var root_6$y=from_html(" ",1),root_8$A=from_html("
",1);function Alert_dialog_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceM\ +ount",3,!1),interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"ignore"),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onOpenAutoFocus=prop($$props,"onOpenAutoFocus",3,noop$1),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),preventScroll=prop($$props,"preventScroll",3,!0),trapFocus=prop($$props,"trapFocus",3,!0),restoreScrollDelay=prop($$props,"restoreScrollDelay",3,null),restProps=rest_props($$props, +["$$slots","$$events","$$legacy","id","children","child","ref","forceMount","interactOutsideBehavior","onCloseAutoFocus","onEscapeKeydown","onOpenAutoFocus","onInteractOutside","preventScroll","trapFocus","restoreScrollDelay"]);const contentState=DialogContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_2=$$anchor2=>{Focus_scope( +$$anchor2,{get ref(){return contentState.opts.ref},loop:!0,get trapFocus(){return trapFocus()},get enabled(){return contentState.root.opts.open.current},get onCloseAutoFocus(){return onCloseAutoFocus()},onOpenAutoFocus:e=>{onOpenAutoFocus()(e),!e.defaultPrevented&&(e.preventDefault(),afterSleep(0,()=>contentState.opts.ref.current?.focus()))},focusScope:($$anchor3,$$arg0)=>{let focusScopeProps=()=>$$arg0?.().props;Escape_layer($$anchor3,spread_props(()=>get$3(mergedProps),{get enabled(){return contentState. +root.opts.open.current},get ref(){return contentState.opts.ref},onEscapeKeydown:e=>{onEscapeKeydown()(e),!e.defaultPrevented&&contentState.root.handleClose()},children:($$anchor4,$$slotProps)=>{Dismissible_layer($$anchor4,spread_props(()=>get$3(mergedProps),{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},get interactOutsideBehavior(){return interactOutsideBehavior()},onInteractOutside:e=>{onInteractOutside()(e),!e.defaultPrevented&&contentState. +root.handleClose()},children:($$anchor5,$$slotProps2)=>{Text_selection_layer($$anchor5,spread_props(()=>get$3(mergedProps),{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},children:($$anchor6,$$slotProps3)=>{var fragment_5=comment$2(),node_1=first_child(fragment_5);{var consequent_1=$$anchor7=>{var fragment_6=root_6$y(),node_2=first_child(fragment_6);{var consequent=$$anchor8=>{Scroll_lock($$anchor8,{get preventScroll(){return preventScroll()},get restoreScrollDelay(){ +return restoreScrollDelay()}})};if_block(node_2,$$render=>{contentState.root.opts.open.current&&$$render(consequent)})}var node_3=sibling(node_2,2);{let $0=user_derived(()=>({props:mergeProps(get$3(mergedProps),focusScopeProps()),...contentState.snippetProps}));snippet(node_3,()=>$$props.child,()=>get$3($0))}append($$anchor7,fragment_6)},alternate=$$anchor7=>{var fragment_8=root_8$A(),node_4=first_child(fragment_8);Scroll_lock(node_4,{get preventScroll(){return preventScroll()}});var div=sibling( +node_4,2);attribute_effect(div,$0=>({...$0}),[()=>mergeProps(get$3(mergedProps),focusScopeProps())]);var node_5=child(div);snippet(node_5,()=>$$props.children??noop$3),reset(div),append($$anchor7,fragment_8)};if_block(node_1,$$render=>{$$props.child?$$render(consequent_1):$$render(alternate,-1)})}append($$anchor6,fragment_5)},$$slots:{default:!0}}))},$$slots:{default:!0}}))},$$slots:{default:!0}}))},$$slots:{focusScope:!0}})};if_block(node2,$$render=>{(contentState.shouldRender||forceMount())&&$$render( +consequent_2)})}append($$anchor,fragment),pop()}var root_3$15=from_html("
");function Dialog_overlay$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),forceMount=prop($$props,"forceMount",3,!1),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","forceMount","child","children","ref"]);const overlayState=DialogOverlayState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(), +v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,overlayState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_2=first_child(fragment_2);{let $0=user_derived(()=>({props:mergeProps(get$3(mergedProps)),...overlayState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor3,fragment_2)},alternate=$$anchor3=>{ +var div=root_3$15();attribute_effect(div,$0=>({...$0}),[()=>mergeProps(get$3(mergedProps))]);var node_3=child(div);snippet(node_3,()=>$$props.children??noop$3,()=>overlayState.snippetProps),reset(div),append($$anchor3,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{(overlayState.shouldRender||forceMount())&&$$render(consequent_1)})}append($$anchor,fragment),pop()}var root_2$1A=from_html("
\ +
");function Dialog_description$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","children","child","ref"]);const descriptionState=DialogDescriptionState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,descriptionState.props));var fragment=comment$2(),node2=first_child( +fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1A();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment), +pop()}const checkboxAttrs=createBitsAttrs({component:"checkbox",parts:["root","group","group-label","input"]}),CheckboxGroupContext=new Context$1("Checkbox.Group"),CheckboxRootContext=new Context$1("Checkbox.Root");class CheckboxRootState{static create(opts,group=null){return CheckboxRootContext.set(new CheckboxRootState(opts,group))}opts;group;#trueName=user_derived(()=>this.group&&this.group.opts.name.current?this.group.opts.name.current:this.opts.name.current);get trueName(){return get$3(this.#trueName)}set trueName(value){ +set$1(this.#trueName,value)}#trueRequired=user_derived(()=>this.group&&this.group.opts.required.current?!0:this.opts.required.current);get trueRequired(){return get$3(this.#trueRequired)}set trueRequired(value){set$1(this.#trueRequired,value)}#trueDisabled=user_derived(()=>this.group&&this.group.opts.disabled.current?!0:this.opts.disabled.current);get trueDisabled(){return get$3(this.#trueDisabled)}set trueDisabled(value){set$1(this.#trueDisabled,value)}#trueReadonly=user_derived(()=>this.group&& +this.group.opts.readonly.current?!0:this.opts.readonly.current);get trueReadonly(){return get$3(this.#trueReadonly)}set trueReadonly(value){set$1(this.#trueReadonly,value)}attachment;constructor(opts,group){this.opts=opts,this.group=group,this.attachment=attachRef(this.opts.ref),this.onkeydown=this.onkeydown.bind(this),this.onclick=this.onclick.bind(this),watch$1.pre([()=>snapshot(this.group?.opts.value.current),()=>this.opts.value.current],([groupValue,value])=>{!groupValue||!value||(this.opts. +checked.current=groupValue.includes(value))}),watch$1.pre(()=>this.opts.checked.current,checked=>{this.group&&(checked?this.group?.addValue(this.opts.value.current):this.group?.removeValue(this.opts.value.current))})}onkeydown(e){if(!(this.trueDisabled||this.trueReadonly)){if(e.key===ENTER){e.preventDefault(),this.opts.type.current==="submit"&&e.currentTarget.closest("form")?.requestSubmit();return}e.key===SPACE&&(e.preventDefault(),this.#toggle())}}#toggle(){this.opts.indeterminate.current?(this. +opts.indeterminate.current=!1,this.opts.checked.current=!0):this.opts.checked.current=!this.opts.checked.current}onclick(e){if(!(this.trueDisabled||this.trueReadonly)){if(this.opts.type.current==="submit"){this.#toggle();return}e.preventDefault(),this.#toggle()}}#snippetProps=user_derived(()=>({checked:this.opts.checked.current,indeterminate:this.opts.indeterminate.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived( +()=>({id:this.opts.id.current,role:"checkbox",type:this.opts.type.current,disabled:this.trueDisabled,"aria-checked":getAriaChecked(this.opts.checked.current,this.opts.indeterminate.current),"aria-required":boolToStr(this.trueRequired),"aria-readonly":boolToStr(this.trueReadonly),"data-disabled":boolToEmptyStrOrUndef(this.trueDisabled),"data-readonly":boolToEmptyStrOrUndef(this.trueReadonly),"data-state":getCheckboxDataState(this.opts.checked.current,this.opts.indeterminate.current),[checkboxAttrs. +root]:"",onclick:this.onclick,onkeydown:this.onkeydown,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class CheckboxInputState{static create(){return new CheckboxInputState(CheckboxRootContext.get())}root;#trueChecked=user_derived(()=>this.root.group?!!(this.root.opts.value.current!==void 0&&this.root.group.opts.value.current.includes(this.root.opts.value.current)):this.root.opts.checked.current);get trueChecked(){return get$3(this.#trueChecked)}set trueChecked(value){ +set$1(this.#trueChecked,value)}#shouldRender=user_derived(()=>!!this.root.trueName);get shouldRender(){return get$3(this.#shouldRender)}set shouldRender(value){set$1(this.#shouldRender,value)}constructor(root2){this.root=root2,this.onfocus=this.onfocus.bind(this)}onfocus(_){isHTMLElement$1(this.root.opts.ref.current)&&this.root.opts.ref.current.focus()}#props=user_derived(()=>({type:"checkbox",checked:this.root.opts.checked.current===!0,disabled:this.root.trueDisabled,required:this.root.trueRequired, +name:this.root.trueName,value:this.root.opts.value.current,readonly:this.root.trueReadonly,onfocus:this.onfocus}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}function getCheckboxDataState(checked,indeterminate){return indeterminate?"indeterminate":checked?"checked":"unchecked"}enable_legacy_mode_flag();var root_1$1d=from_html(""),root_2$1z=from_html("");function Hidden_input($$anchor,$$props){push$1($$props,!0);let value=prop($$props,"value", +15),restProps=rest_props($$props,["$$slots","$$events","$$legacy","value"]);const mergedProps=user_derived(()=>mergeProps(restProps,{"aria-hidden":"true",tabindex:-1,style:{...srOnlyStyles$1,position:"absolute",top:"0",left:"0"}}));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var input=root_1$1d();attribute_effect(input,()=>({...get$3(mergedProps),value:value()}),void 0,void 0,void 0,void 0,!0),append($$anchor2,input)},alternate=$$anchor2=>{var input_1=root_2$1z(); +attribute_effect(input_1,()=>({...get$3(mergedProps)}),void 0,void 0,void 0,void 0,!0),bind_value(input_1,value),append($$anchor2,input_1)};if_block(node2,$$render=>{get$3(mergedProps).type==="checkbox"?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}function Checkbox_input($$anchor,$$props){push$1($$props,!1);const inputState=CheckboxInputState.create();init();var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Hidden_input($$anchor2,spread_props( +()=>inputState.props))};if_block(node2,$$render=>{inputState.shouldRender&&$$render(consequent)})}append($$anchor,fragment),pop()}var root_2$1y=from_html(""),root$1_=from_html(" ",1);function Checkbox$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let checked=prop($$props,"checked",15,!1),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),required2=prop($$props,"required",3,!1),name=prop($$props,"name",3,void 0),value=prop($$props,"val\ +ue",3,"on"),id2=prop($$props,"id",19,()=>createId(uid2)),indeterminate=prop($$props,"indeterminate",15,!1),type2=prop($$props,"type",3,"button"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","checked","ref","onCheckedChange","children","disabled","required","name","value","id","indeterminate","onIndeterminateChange","child","type","readonly"]);const group=CheckboxGroupContext.getOr(null);group&&value()&&(group.opts.value.current.includes(value())?checked(!0):checked(!1)),watch$1. +pre(()=>value(),()=>{group&&value()&&(group.opts.value.current.includes(value())?checked(!0):checked(!1))});const rootState=CheckboxRootState.create({checked:boxWith$1(()=>checked(),v=>{checked(v),$$props.onCheckedChange?.(v)}),disabled:boxWith$1(()=>disabled()??!1),required:boxWith$1(()=>required2()),name:boxWith$1(()=>name()),value:boxWith$1(()=>value()),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),indeterminate:boxWith$1(()=>indeterminate(),v=>{indeterminate(v),$$props.onIndeterminateChange?.( +v)}),type:boxWith$1(()=>type2()),readonly:boxWith$1(()=>!!$$props.readonly)},group),mergedProps=user_derived(()=>mergeProps({...restProps},rootState.props));var fragment=root$1_(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({props:get$3(mergedProps),...rootState.snippetProps}));snippet(node_1,()=>$$props.child,()=>get$3($0))}append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1y();attribute_effect( +button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3,()=>rootState.snippetProps),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}var node_3=sibling(node2,2);Checkbox_input(node_3,{}),append($$anchor,fragment),pop()}const collapsibleAttrs=createBitsAttrs({component:"collapsible",parts:["root","content","trigger"]}),CollapsibleRootContext=new Context$1("Collapsible.Ro\ +ot");class CollapsibleRootState{static create(opts){return CollapsibleRootContext.set(new CollapsibleRootState(opts))}opts;attachment;#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}contentPresence;#contentId=state$1(void 0);get contentId(){return get$3(this.#contentId)}set contentId(value){set$1(this.#contentId,value,!0)}constructor(opts){this.opts=opts,this.toggleOpen=this.toggleOpen.bind(this),this.attachment= +attachRef(this.opts.ref),this.contentPresence=new PresenceManager({ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}})}toggleOpen(){this.opts.open.current=!this.opts.open.current}#props=user_derived(()=>({id:this.opts.id.current,"data-state":getDataOpenClosed(this.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled.current),[collapsibleAttrs.root]:"",...this.attachment}));get props(){return get$3( +this.#props)}set props(value){set$1(this.#props,value)}}class CollapsibleContentState{static create(opts){return new CollapsibleContentState(opts,CollapsibleRootContext.get())}opts;root;attachment;#present=user_derived(()=>this.opts.hiddenUntilFound.current?this.root.opts.open.current:this.opts.forceMount.current||this.root.opts.open.current);get present(){return get$3(this.#present)}set present(value){set$1(this.#present,value)}#originalStyles;#isMountAnimationPrevented=state$1(!1);#width=state$1( +0);#height=state$1(0);constructor(opts,root2){this.opts=opts,this.root=root2,set$1(this.#isMountAnimationPrevented,root2.opts.open.current,!0),this.root.contentId=this.opts.id.current,this.attachment=attachRef(this.opts.ref,v=>this.root.contentNode=v),watch$1.pre(()=>this.opts.id.current,id2=>{this.root.contentId=id2}),user_pre_effect(()=>{const rAF=requestAnimationFrame(()=>{set$1(this.#isMountAnimationPrevented,!1)});return()=>{cancelAnimationFrame(rAF)}}),watch$1.pre([()=>this.opts.ref.current, +()=>this.opts.hiddenUntilFound.current],([node2,hiddenUntilFound])=>!node2||!hiddenUntilFound?void 0:on(node2,"beforematch",()=>{this.root.opts.open.current||requestAnimationFrame(()=>{this.root.opts.open.current=!0})})),watch$1([()=>this.opts.ref.current,()=>this.present],([node2])=>{node2&&afterTick(()=>{if(!this.opts.ref.current)return;this.#originalStyles=this.#originalStyles||{transitionDuration:node2.style.transitionDuration,animationName:node2.style.animationName},node2.style.transitionDuration= +"0s",node2.style.animationName="none";const rect=node2.getBoundingClientRect();if(set$1(this.#height,rect.height,!0),set$1(this.#width,rect.width,!0),!get$3(this.#isMountAnimationPrevented)){const{animationName,transitionDuration}=this.#originalStyles;node2.style.transitionDuration=transitionDuration,node2.style.animationName=animationName}})})}get shouldRender(){return this.root.contentPresence.shouldRender}#snippetProps=user_derived(()=>({open:this.root.opts.open.current}));get snippetProps(){ +return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,style:{"--bits-collapsible-content-height":get$3(this.#height)?`${get$3(this.#height)}px`:void 0,"--bits-collapsible-content-width":get$3(this.#width)?`${get$3(this.#width)}px`:void 0},hidden:this.opts.hiddenUntilFound.current&&!this.root.opts.open.current?"until-found":void 0,"data-state":getDataOpenClosed(this.root.opts.open.current),...getDataTransitionAttrs( +this.root.contentPresence.transitionStatus),"data-disabled":boolToEmptyStrOrUndef(this.root.opts.disabled.current),[collapsibleAttrs.content]:"",...this.opts.hiddenUntilFound.current&&!this.shouldRender?{}:{hidden:this.opts.hiddenUntilFound.current?!this.shouldRender:this.opts.forceMount.current?void 0:!this.shouldRender},...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class CollapsibleTriggerState{static create(opts){return new CollapsibleTriggerState( +opts,CollapsibleRootContext.get())}opts;root;attachment;#isDisabled=user_derived(()=>this.opts.disabled.current||this.root.opts.disabled.current);constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(this.opts.ref),this.onclick=this.onclick.bind(this),this.onkeydown=this.onkeydown.bind(this)}onclick(e){if(!get$3(this.#isDisabled)){if(e.button!==0)return e.preventDefault();this.root.toggleOpen()}}onkeydown(e){get$3(this.#isDisabled)||(e.key===SPACE||e.key===ENTER)&&(e.preventDefault(), +this.root.toggleOpen())}#props=user_derived(()=>({id:this.opts.id.current,type:"button",disabled:get$3(this.#isDisabled),"aria-controls":this.root.contentId,"aria-expanded":boolToStr(this.root.opts.open.current),"data-state":getDataOpenClosed(this.root.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(get$3(this.#isDisabled)),[collapsibleAttrs.trigger]:"",onclick:this.onclick,onkeydown:this.onkeydown,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props, +value)}}var root_2$1x=from_html("
");function Collapsible$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),open2=prop($$props,"open",15,!1),disabled=prop($$props,"disabled",3,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","ope\ +n","disabled","onOpenChange","onOpenChangeComplete"]);const rootState=CollapsibleRootState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),disabled:boxWith$1(()=>disabled()),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),mergedProps=user_derived(()=>mergeProps(restProps,rootState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child( +fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1x();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1w=from_html("
");function Collapsible_content$1($$anchor,$$props){ +const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),hiddenUntilFound=prop($$props,"hiddenUntilFound",3,!1),id2=prop($$props,"id",19,()=>createId(uid2)),restProps=rest_props($$props,["$$slots","$$events","$$legacy","child","ref","forceMount","hiddenUntilFound","children","id"]);const contentState=CollapsibleContentState.create({id:boxWith$1(()=>id2()),forceMount:boxWith$1(()=>forceMount()),hiddenUntilFound:boxWith$1(()=>hiddenUntilFound()), +ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({...contentState.snippetProps,props:get$3(mergedProps)}));snippet(node_1,()=>$$props.child,()=>get$3($0))}append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1w();attribute_effect(div,()=>({...get$3(mergedProps)})); +var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1v=from_html("");function Collapsible_trigger$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),restProps=rest_props($$props, +["$$slots","$$events","$$legacy","children","child","ref","id","disabled"]);const triggerState=CollapsibleTriggerState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),disabled:boxWith$1(()=>disabled())}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})), +append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1v();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}const sides=["top","right","bottom","left"],min=Math.min,max=Math.max,round=Math.round,floor=Math.floor,createCoords=v=>({x:v,y:v}),oppositeSideMap={ +left:"right",right:"left",bottom:"top",top:"bottom"},oppositeAlignmentMap={start:"end",end:"start"};function clamp$1(start2,value,end){return max(start2,min(value,end))}function evaluate(value,param){return typeof value=="function"?value(param):value}function getSide$1(placement){return placement.split("-")[0]}function getAlignment(placement){return placement.split("-")[1]}function getOppositeAxis(axis){return axis==="x"?"y":"x"}function getAxisLength(axis){return axis==="y"?"height":"width"}const yAxisSides=new Set( +["top","bottom"]);function getSideAxis(placement){return yAxisSides.has(getSide$1(placement))?"y":"x"}function getAlignmentAxis(placement){return getOppositeAxis(getSideAxis(placement))}function getAlignmentSides(placement,rects,rtl){rtl===void 0&&(rtl=!1);const alignment=getAlignment(placement),alignmentAxis=getAlignmentAxis(placement),length=getAxisLength(alignmentAxis);let mainAlignmentSide=alignmentAxis==="x"?alignment===(rtl?"end":"start")?"right":"left":alignment==="start"?"bottom":"top";return rects. +reference[length]>rects.floating[length]&&(mainAlignmentSide=getOppositePlacement(mainAlignmentSide)),[mainAlignmentSide,getOppositePlacement(mainAlignmentSide)]}function getExpandedPlacements(placement){const oppositePlacement=getOppositePlacement(placement);return[getOppositeAlignmentPlacement(placement),oppositePlacement,getOppositeAlignmentPlacement(oppositePlacement)]}function getOppositeAlignmentPlacement(placement){return placement.replace(/start|end/g,alignment=>oppositeAlignmentMap[alignment])} +const lrPlacement=["left","right"],rlPlacement=["right","left"],tbPlacement=["top","bottom"],btPlacement=["bottom","top"];function getSideList(side,isStart,rtl){switch(side){case"top":case"bottom":return rtl?isStart?rlPlacement:lrPlacement:isStart?lrPlacement:rlPlacement;case"left":case"right":return isStart?tbPlacement:btPlacement;default:return[]}}function getOppositeAxisPlacements(placement,flipAlignment,direction,rtl){const alignment=getAlignment(placement);let list2=getSideList(getSide$1(placement), +direction==="start",rtl);return alignment&&(list2=list2.map(side=>side+"-"+alignment),flipAlignment&&(list2=list2.concat(list2.map(getOppositeAlignmentPlacement)))),list2}function getOppositePlacement(placement){return placement.replace(/left|right|bottom|top/g,side=>oppositeSideMap[side])}function expandPaddingObject(padding){return{top:0,right:0,bottom:0,left:0,...padding}}function getPaddingObject(padding){return typeof padding!="number"?expandPaddingObject(padding):{top:padding,right:padding, +bottom:padding,left:padding}}function rectToClientRect(rect){const{x,y,width,height}=rect;return{width,height,top:y,left:x,right:x+width,bottom:y+height,x,y}}function computeCoordsFromPlacement(_ref,placement,rtl){let{reference,floating}=_ref;const sideAxis=getSideAxis(placement),alignmentAxis=getAlignmentAxis(placement),alignLength=getAxisLength(alignmentAxis),side=getSide$1(placement),isVertical=sideAxis==="y",commonX=reference.x+reference.width/2-floating.width/2,commonY=reference.y+reference. +height/2-floating.height/2,commonAlign=reference[alignLength]/2-floating[alignLength]/2;let coords;switch(side){case"top":coords={x:commonX,y:reference.y-floating.height};break;case"bottom":coords={x:commonX,y:reference.y+reference.height};break;case"right":coords={x:reference.x+reference.width,y:commonY};break;case"left":coords={x:reference.x-floating.width,y:commonY};break;default:coords={x:reference.x,y:reference.y}}switch(getAlignment(placement)){case"start":coords[alignmentAxis]-=commonAlign* +(rtl&&isVertical?-1:1);break;case"end":coords[alignmentAxis]+=commonAlign*(rtl&&isVertical?-1:1);break}return coords}const computePosition$1=async(reference,floating,config2)=>{const{placement="bottom",strategy="absolute",middleware=[],platform:platform2}=config2,validMiddleware=middleware.filter(Boolean),rtl=await(platform2.isRTL==null?void 0:platform2.isRTL(floating));let rects=await platform2.getElementRects({reference,floating,strategy}),{x,y}=computeCoordsFromPlacement(rects,placement,rtl), +statefulPlacement=placement,middlewareData={},resetCount=0;for(let i=0;i({name:"arrow",options,async fn(state2){const{x,y,placement,rects,platform:platform2,elements,middlewareData}=state2,{element:element2,padding=0}=evaluate(options,state2)||{};if(element2==null)return{};const paddingObject=getPaddingObject(padding),coords={x,y},axis=getAlignmentAxis(placement),length=getAxisLength(axis),arrowDimensions=await platform2.getDimensions(element2),isYAxis=axis==="y",minProp=isYAxis?"top":"left",maxProp=isYAxis? +"bottom":"right",clientProp=isYAxis?"clientHeight":"clientWidth",endDiff=rects.reference[length]+rects.reference[axis]-coords[axis]-rects.floating[length],startDiff=coords[axis]-rects.reference[axis],arrowOffsetParent=await(platform2.getOffsetParent==null?void 0:platform2.getOffsetParent(element2));let clientSize=arrowOffsetParent?arrowOffsetParent[clientProp]:0;(!clientSize||!await(platform2.isElement==null?void 0:platform2.isElement(arrowOffsetParent)))&&(clientSize=elements.floating[clientProp]|| +rects.floating[length]);const centerToReference=endDiff/2-startDiff/2,largestPossiblePadding=clientSize/2-arrowDimensions[length]/2-1,minPadding=min(paddingObject[minProp],largestPossiblePadding),maxPadding=min(paddingObject[maxProp],largestPossiblePadding),min$1=minPadding,max2=clientSize-arrowDimensions[length]-maxPadding,center=clientSize/2-arrowDimensions[length]/2+centerToReference,offset2=clamp$1(min$1,center,max2),shouldAddOffset=!middlewareData.arrow&&getAlignment(placement)!=null&¢er!== +offset2&&rects.reference[length]/2-(centerside2<= +0)){var _middlewareData$flip2,_overflowsData$filter;const nextIndex=(((_middlewareData$flip2=middlewareData.flip)==null?void 0:_middlewareData$flip2.index)||0)+1,nextPlacement=placements[nextIndex];if(nextPlacement&&(!(checkCrossAxis==="alignment"?initialSideAxis!==getSideAxis(nextPlacement):!1)||overflowsData.every(d2=>d2.overflows[0]>0&&getSideAxis(d2.placement)===initialSideAxis)))return{data:{index:nextIndex,overflows:overflowsData},reset:{placement:nextPlacement}};let resetPlacement=(_overflowsData$filter= +overflowsData.filter(d2=>d2.overflows[0]<=0).sort((a,b)=>a.overflows[1]-b.overflows[1])[0])==null?void 0:_overflowsData$filter.placement;if(!resetPlacement)switch(fallbackStrategy){case"bestFit":{var _overflowsData$filter2;const placement2=(_overflowsData$filter2=overflowsData.filter(d2=>{if(hasFallbackAxisSideDirection){const currentSideAxis=getSideAxis(d2.placement);return currentSideAxis===initialSideAxis||currentSideAxis==="y"}return!0}).map(d2=>[d2.placement,d2.overflows.filter(overflow2=>overflow2> +0).reduce((acc,overflow2)=>acc+overflow2,0)]).sort((a,b)=>a[1]-b[1])[0])==null?void 0:_overflowsData$filter2[0];placement2&&(resetPlacement=placement2);break}case"initialPlacement":resetPlacement=initialPlacement;break}if(placement!==resetPlacement)return{reset:{placement:resetPlacement}}}return{}}}};function getSideOffsets(overflow,rect){return{top:overflow.top-rect.height,right:overflow.right-rect.width,bottom:overflow.bottom-rect.height,left:overflow.left-rect.width}}function isAnySideFullyClipped(overflow){ +return sides.some(side=>overflow[side]>=0)}const hide$1=function(options){return options===void 0&&(options={}),{name:"hide",options,async fn(state2){const{rects}=state2,{strategy="referenceHidden",...detectOverflowOptions}=evaluate(options,state2);switch(strategy){case"referenceHidden":{const overflow=await detectOverflow(state2,{...detectOverflowOptions,elementContext:"reference"}),offsets=getSideOffsets(overflow,rects.reference);return{data:{referenceHiddenOffsets:offsets,referenceHidden:isAnySideFullyClipped( +offsets)}}}case"escaped":{const overflow=await detectOverflow(state2,{...detectOverflowOptions,altBoundary:!0}),offsets=getSideOffsets(overflow,rects.floating);return{data:{escapedOffsets:offsets,escaped:isAnySideFullyClipped(offsets)}}}default:return{}}}}},originSides=new Set(["left","top"]);async function convertValueToCoords(state2,options){const{placement,platform:platform2,elements}=state2,rtl=await(platform2.isRTL==null?void 0:platform2.isRTL(elements.floating)),side=getSide$1(placement),alignment=getAlignment( +placement),isVertical=getSideAxis(placement)==="y",mainAxisMulti=originSides.has(side)?-1:1,crossAxisMulti=rtl&&isVertical?-1:1,rawValue=evaluate(options,state2);let{mainAxis,crossAxis,alignmentAxis}=typeof rawValue=="number"?{mainAxis:rawValue,crossAxis:0,alignmentAxis:null}:{mainAxis:rawValue.mainAxis||0,crossAxis:rawValue.crossAxis||0,alignmentAxis:rawValue.alignmentAxis};return alignment&&typeof alignmentAxis=="number"&&(crossAxis=alignment==="end"?alignmentAxis*-1:alignmentAxis),isVertical? +{x:crossAxis*crossAxisMulti,y:mainAxis*mainAxisMulti}:{x:mainAxis*mainAxisMulti,y:crossAxis*crossAxisMulti}}const offset$1=function(options){return options===void 0&&(options=0),{name:"offset",options,async fn(state2){var _middlewareData$offse,_middlewareData$arrow;const{x,y,placement,middlewareData}=state2,diffCoords=await convertValueToCoords(state2,options);return placement===((_middlewareData$offse=middlewareData.offset)==null?void 0:_middlewareData$offse.placement)&&(_middlewareData$arrow=middlewareData. +arrow)!=null&&_middlewareData$arrow.alignmentOffset?{}:{x:x+diffCoords.x,y:y+diffCoords.y,data:{...diffCoords,placement}}}}},shift$1=function(options){return options===void 0&&(options={}),{name:"shift",options,async fn(state2){const{x,y,placement}=state2,{mainAxis:checkMainAxis=!0,crossAxis:checkCrossAxis=!1,limiter={fn:_ref=>{let{x:x2,y:y2}=_ref;return{x:x2,y:y2}}},...detectOverflowOptions}=evaluate(options,state2),coords={x,y},overflow=await detectOverflow(state2,detectOverflowOptions),crossAxis=getSideAxis( +getSide$1(placement)),mainAxis=getOppositeAxis(crossAxis);let mainAxisCoord=coords[mainAxis],crossAxisCoord=coords[crossAxis];if(checkMainAxis){const minSide=mainAxis==="y"?"top":"left",maxSide=mainAxis==="y"?"bottom":"right",min2=mainAxisCoord+overflow[minSide],max2=mainAxisCoord-overflow[maxSide];mainAxisCoord=clamp$1(min2,mainAxisCoord,max2)}if(checkCrossAxis){const minSide=crossAxis==="y"?"top":"left",maxSide=crossAxis==="y"?"bottom":"right",min2=crossAxisCoord+overflow[minSide],max2=crossAxisCoord- +overflow[maxSide];crossAxisCoord=clamp$1(min2,crossAxisCoord,max2)}const limitedCoords=limiter.fn({...state2,[mainAxis]:mainAxisCoord,[crossAxis]:crossAxisCoord});return{...limitedCoords,data:{x:limitedCoords.x-x,y:limitedCoords.y-y,enabled:{[mainAxis]:checkMainAxis,[crossAxis]:checkCrossAxis}}}}}},limitShift$1=function(options){return options===void 0&&(options={}),{options,fn(state2){const{x,y,placement,rects,middlewareData}=state2,{offset:offset2=0,mainAxis:checkMainAxis=!0,crossAxis:checkCrossAxis=!0}=evaluate( +options,state2),coords={x,y},crossAxis=getSideAxis(placement),mainAxis=getOppositeAxis(crossAxis);let mainAxisCoord=coords[mainAxis],crossAxisCoord=coords[crossAxis];const rawOffset=evaluate(offset2,state2),computedOffset=typeof rawOffset=="number"?{mainAxis:rawOffset,crossAxis:0}:{mainAxis:0,crossAxis:0,...rawOffset};if(checkMainAxis){const len=mainAxis==="y"?"height":"width",limitMin=rects.reference[mainAxis]-rects.floating[len]+computedOffset.mainAxis,limitMax=rects.reference[mainAxis]+rects. +reference[len]-computedOffset.mainAxis;mainAxisCoordlimitMax&&(mainAxisCoord=limitMax)}if(checkCrossAxis){var _middlewareData$offse,_middlewareData$offse2;const len=mainAxis==="y"?"width":"height",isOriginSide=originSides.has(getSide$1(placement)),limitMin=rects.reference[crossAxis]-rects.floating[len]+(isOriginSide&&((_middlewareData$offse=middlewareData.offset)==null?void 0:_middlewareData$offse[crossAxis])||0)+(isOriginSide?0:computedOffset.crossAxis), +limitMax=rects.reference[crossAxis]+rects.reference[len]+(isOriginSide?0:((_middlewareData$offse2=middlewareData.offset)==null?void 0:_middlewareData$offse2[crossAxis])||0)-(isOriginSide?computedOffset.crossAxis:0);crossAxisCoordlimitMax&&(crossAxisCoord=limitMax)}return{[mainAxis]:mainAxisCoord,[crossAxis]:crossAxisCoord}}}},size$1=function(options){return options===void 0&&(options={}),{name:"size",options,async fn(state2){var _state$middlewareData, +_state$middlewareData2;const{placement,rects,platform:platform2,elements}=state2,{apply=()=>{},...detectOverflowOptions}=evaluate(options,state2),overflow=await detectOverflow(state2,detectOverflowOptions),side=getSide$1(placement),alignment=getAlignment(placement),isYAxis=getSideAxis(placement)==="y",{width,height}=rects.floating;let heightSide,widthSide;side==="top"||side==="bottom"?(heightSide=side,widthSide=alignment===(await(platform2.isRTL==null?void 0:platform2.isRTL(elements.floating))?"\ +start":"end")?"left":"right"):(widthSide=side,heightSide=alignment==="end"?"top":"bottom");const maximumClippingHeight=height-overflow.top-overflow.bottom,maximumClippingWidth=width-overflow.left-overflow.right,overflowAvailableHeight=min(height-overflow[heightSide],maximumClippingHeight),overflowAvailableWidth=min(width-overflow[widthSide],maximumClippingWidth),noShift=!state2.middlewareData.shift;let availableHeight=overflowAvailableHeight,availableWidth=overflowAvailableWidth;if((_state$middlewareData= +state2.middlewareData.shift)!=null&&_state$middlewareData.enabled.x&&(availableWidth=maximumClippingWidth),(_state$middlewareData2=state2.middlewareData.shift)!=null&&_state$middlewareData2.enabled.y&&(availableHeight=maximumClippingHeight),noShift&&!alignment){const xMin=max(overflow.left,0),xMax=max(overflow.right,0),yMin=max(overflow.top,0),yMax=max(overflow.bottom,0);isYAxis?availableWidth=width-2*(xMin!==0||xMax!==0?xMin+xMax:max(overflow.left,overflow.right)):availableHeight=height-2*(yMin!== +0||yMax!==0?yMin+yMax:max(overflow.top,overflow.bottom))}await apply({...state2,availableWidth,availableHeight});const nextDimensions=await platform2.getDimensions(elements.floating);return width!==nextDimensions.width||height!==nextDimensions.height?{reset:{rects:!0}}:{}}}};function hasWindow(){return typeof window<"u"}function getNodeName(node2){return isNode(node2)?(node2.nodeName||"").toLowerCase():"#document"}function getWindow(node2){var _node$ownerDocument;return(node2==null||(_node$ownerDocument= +node2.ownerDocument)==null?void 0:_node$ownerDocument.defaultView)||window}function getDocumentElement(node2){var _ref;return(_ref=(isNode(node2)?node2.ownerDocument:node2.document)||window.document)==null?void 0:_ref.documentElement}function isNode(value){return hasWindow()?value instanceof Node||value instanceof getWindow(value).Node:!1}function isElement(value){return hasWindow()?value instanceof Element||value instanceof getWindow(value).Element:!1}function isHTMLElement(value){return hasWindow()? +value instanceof HTMLElement||value instanceof getWindow(value).HTMLElement:!1}function isShadowRoot(value){return!hasWindow()||typeof ShadowRoot>"u"?!1:value instanceof ShadowRoot||value instanceof getWindow(value).ShadowRoot}const invalidOverflowDisplayValues=new Set(["inline","contents"]);function isOverflowElement(element2){const{overflow,overflowX,overflowY,display}=getComputedStyle$1(element2);return/auto|scroll|overlay|hidden|clip/.test(overflow+overflowY+overflowX)&&!invalidOverflowDisplayValues. +has(display)}const tableElements=new Set(["table","td","th"]);function isTableElement(element2){return tableElements.has(getNodeName(element2))}const topLayerSelectors=[":popover-open",":modal"];function isTopLayer(element2){return topLayerSelectors.some(selector=>{try{return element2.matches(selector)}catch{return!1}})}const transformProperties=["transform","translate","scale","rotate","perspective"],willChangeValues=["transform","translate","scale","rotate","perspective","filter"],containValues=[ +"paint","layout","strict","content"];function isContainingBlock(elementOrCss){const webkit=isWebKit(),css2=isElement(elementOrCss)?getComputedStyle$1(elementOrCss):elementOrCss;return transformProperties.some(value=>css2[value]?css2[value]!=="none":!1)||(css2.containerType?css2.containerType!=="normal":!1)||!webkit&&(css2.backdropFilter?css2.backdropFilter!=="none":!1)||!webkit&&(css2.filter?css2.filter!=="none":!1)||willChangeValues.some(value=>(css2.willChange||"").includes(value))||containValues. +some(value=>(css2.contain||"").includes(value))}function getContainingBlock(element2){let currentNode=getParentNode(element2);for(;isHTMLElement(currentNode)&&!isLastTraversableNode(currentNode);){if(isContainingBlock(currentNode))return currentNode;if(isTopLayer(currentNode))return null;currentNode=getParentNode(currentNode)}return null}function isWebKit(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}const lastTraversableNodeNames=new Set(["html","body", +"#document"]);function isLastTraversableNode(node2){return lastTraversableNodeNames.has(getNodeName(node2))}function getComputedStyle$1(element2){return getWindow(element2).getComputedStyle(element2)}function getNodeScroll(element2){return isElement(element2)?{scrollLeft:element2.scrollLeft,scrollTop:element2.scrollTop}:{scrollLeft:element2.scrollX,scrollTop:element2.scrollY}}function getParentNode(node2){if(getNodeName(node2)==="html")return node2;const result=node2.assignedSlot||node2.parentNode|| +isShadowRoot(node2)&&node2.host||getDocumentElement(node2);return isShadowRoot(result)?result.host:result}function getNearestOverflowAncestor(node2){const parentNode=getParentNode(node2);return isLastTraversableNode(parentNode)?node2.ownerDocument?node2.ownerDocument.body:node2.body:isHTMLElement(parentNode)&&isOverflowElement(parentNode)?parentNode:getNearestOverflowAncestor(parentNode)}function getOverflowAncestors(node2,list2,traverseIframes){var _node$ownerDocument2;list2===void 0&&(list2=[]), +traverseIframes===void 0&&(traverseIframes=!0);const scrollableAncestor=getNearestOverflowAncestor(node2),isBody=scrollableAncestor===((_node$ownerDocument2=node2.ownerDocument)==null?void 0:_node$ownerDocument2.body),win=getWindow(scrollableAncestor);if(isBody){const frameElement=getFrameElement(win);return list2.concat(win,win.visualViewport||[],isOverflowElement(scrollableAncestor)?scrollableAncestor:[],frameElement&&traverseIframes?getOverflowAncestors(frameElement):[])}return list2.concat(scrollableAncestor, +getOverflowAncestors(scrollableAncestor,[],traverseIframes))}function getFrameElement(win){return win.parent&&Object.getPrototypeOf(win.parent)?win.frameElement:null}function getCssDimensions(element2){const css2=getComputedStyle$1(element2);let width=parseFloat(css2.width)||0,height=parseFloat(css2.height)||0;const hasOffset=isHTMLElement(element2),offsetWidth=hasOffset?element2.offsetWidth:width,offsetHeight=hasOffset?element2.offsetHeight:height,shouldFallback=round(width)!==offsetWidth||round( +height)!==offsetHeight;return shouldFallback&&(width=offsetWidth,height=offsetHeight),{width,height,$:shouldFallback}}function unwrapElement(element2){return isElement(element2)?element2:element2.contextElement}function getScale(element2){const domElement=unwrapElement(element2);if(!isHTMLElement(domElement))return createCoords(1);const rect=domElement.getBoundingClientRect(),{width,height,$}=getCssDimensions(domElement);let x=($?round(rect.width):rect.width)/width,y=($?round(rect.height):rect.height)/ +height;return(!x||!Number.isFinite(x))&&(x=1),(!y||!Number.isFinite(y))&&(y=1),{x,y}}const noOffsets=createCoords(0);function getVisualOffsets(element2){const win=getWindow(element2);return!isWebKit()||!win.visualViewport?noOffsets:{x:win.visualViewport.offsetLeft,y:win.visualViewport.offsetTop}}function shouldAddVisualOffsets(element2,isFixed,floatingOffsetParent){return isFixed===void 0&&(isFixed=!1),!floatingOffsetParent||isFixed&&floatingOffsetParent!==getWindow(element2)?!1:isFixed}function getBoundingClientRect(element2,includeScale,isFixedStrategy,offsetParent){ +includeScale===void 0&&(includeScale=!1),isFixedStrategy===void 0&&(isFixedStrategy=!1);const clientRect=element2.getBoundingClientRect(),domElement=unwrapElement(element2);let scale2=createCoords(1);includeScale&&(offsetParent?isElement(offsetParent)&&(scale2=getScale(offsetParent)):scale2=getScale(element2));const visualOffsets=shouldAddVisualOffsets(domElement,isFixedStrategy,offsetParent)?getVisualOffsets(domElement):createCoords(0);let x=(clientRect.left+visualOffsets.x)/scale2.x,y=(clientRect. +top+visualOffsets.y)/scale2.y,width=clientRect.width/scale2.x,height=clientRect.height/scale2.y;if(domElement){const win=getWindow(domElement),offsetWin=offsetParent&&isElement(offsetParent)?getWindow(offsetParent):offsetParent;let currentWin=win,currentIFrame=getFrameElement(currentWin);for(;currentIFrame&&offsetParent&&offsetWin!==currentWin;){const iframeScale=getScale(currentIFrame),iframeRect=currentIFrame.getBoundingClientRect(),css2=getComputedStyle$1(currentIFrame),left=iframeRect.left+(currentIFrame. +clientLeft+parseFloat(css2.paddingLeft))*iframeScale.x,top=iframeRect.top+(currentIFrame.clientTop+parseFloat(css2.paddingTop))*iframeScale.y;x*=iframeScale.x,y*=iframeScale.y,width*=iframeScale.x,height*=iframeScale.y,x+=left,y+=top,currentWin=getWindow(currentIFrame),currentIFrame=getFrameElement(currentWin)}}return rectToClientRect({width,height,x,y})}function getWindowScrollBarX(element2,rect){const leftScroll=getNodeScroll(element2).scrollLeft;return rect?rect.left+leftScroll:getBoundingClientRect( +getDocumentElement(element2)).left+leftScroll}function getHTMLOffset(documentElement,scroll,ignoreScrollbarX){ignoreScrollbarX===void 0&&(ignoreScrollbarX=!1);const htmlRect=documentElement.getBoundingClientRect(),x=htmlRect.left+scroll.scrollLeft-(ignoreScrollbarX?0:getWindowScrollBarX(documentElement,htmlRect)),y=htmlRect.top+scroll.scrollTop;return{x,y}}function convertOffsetParentRelativeRectToViewportRelativeRect(_ref){let{elements,rect,offsetParent,strategy}=_ref;const isFixed=strategy==="\ +fixed",documentElement=getDocumentElement(offsetParent),topLayer=elements?isTopLayer(elements.floating):!1;if(offsetParent===documentElement||topLayer&&isFixed)return rect;let scroll={scrollLeft:0,scrollTop:0},scale2=createCoords(1);const offsets=createCoords(0),isOffsetParentAnElement=isHTMLElement(offsetParent);if((isOffsetParentAnElement||!isOffsetParentAnElement&&!isFixed)&&((getNodeName(offsetParent)!=="body"||isOverflowElement(documentElement))&&(scroll=getNodeScroll(offsetParent)),isHTMLElement( +offsetParent))){const offsetRect=getBoundingClientRect(offsetParent);scale2=getScale(offsetParent),offsets.x=offsetRect.x+offsetParent.clientLeft,offsets.y=offsetRect.y+offsetParent.clientTop}const htmlOffset=documentElement&&!isOffsetParentAnElement&&!isFixed?getHTMLOffset(documentElement,scroll,!0):createCoords(0);return{width:rect.width*scale2.x,height:rect.height*scale2.y,x:rect.x*scale2.x-scroll.scrollLeft*scale2.x+offsets.x+htmlOffset.x,y:rect.y*scale2.y-scroll.scrollTop*scale2.y+offsets.y+ +htmlOffset.y}}function getClientRects(element2){return Array.from(element2.getClientRects())}function getDocumentRect(element2){const html2=getDocumentElement(element2),scroll=getNodeScroll(element2),body2=element2.ownerDocument.body,width=max(html2.scrollWidth,html2.clientWidth,body2.scrollWidth,body2.clientWidth),height=max(html2.scrollHeight,html2.clientHeight,body2.scrollHeight,body2.clientHeight);let x=-scroll.scrollLeft+getWindowScrollBarX(element2);const y=-scroll.scrollTop;return getComputedStyle$1( +body2).direction==="rtl"&&(x+=max(html2.clientWidth,body2.clientWidth)-width),{width,height,x,y}}function getViewportRect(element2,strategy){const win=getWindow(element2),html2=getDocumentElement(element2),visualViewport=win.visualViewport;let width=html2.clientWidth,height=html2.clientHeight,x=0,y=0;if(visualViewport){width=visualViewport.width,height=visualViewport.height;const visualViewportBased=isWebKit();(!visualViewportBased||visualViewportBased&&strategy==="fixed")&&(x=visualViewport.offsetLeft, +y=visualViewport.offsetTop)}return{width,height,x,y}}const absoluteOrFixed=new Set(["absolute","fixed"]);function getInnerBoundingClientRect(element2,strategy){const clientRect=getBoundingClientRect(element2,!0,strategy==="fixed"),top=clientRect.top+element2.clientTop,left=clientRect.left+element2.clientLeft,scale2=isHTMLElement(element2)?getScale(element2):createCoords(1),width=element2.clientWidth*scale2.x,height=element2.clientHeight*scale2.y,x=left*scale2.x,y=top*scale2.y;return{width,height, +x,y}}function getClientRectFromClippingAncestor(element2,clippingAncestor,strategy){let rect;if(clippingAncestor==="viewport")rect=getViewportRect(element2,strategy);else if(clippingAncestor==="document")rect=getDocumentRect(getDocumentElement(element2));else if(isElement(clippingAncestor))rect=getInnerBoundingClientRect(clippingAncestor,strategy);else{const visualOffsets=getVisualOffsets(element2);rect={x:clippingAncestor.x-visualOffsets.x,y:clippingAncestor.y-visualOffsets.y,width:clippingAncestor. +width,height:clippingAncestor.height}}return rectToClientRect(rect)}function hasFixedPositionAncestor(element2,stopNode){const parentNode=getParentNode(element2);return parentNode===stopNode||!isElement(parentNode)||isLastTraversableNode(parentNode)?!1:getComputedStyle$1(parentNode).position==="fixed"||hasFixedPositionAncestor(parentNode,stopNode)}function getClippingElementAncestors(element2,cache2){const cachedResult=cache2.get(element2);if(cachedResult)return cachedResult;let result=getOverflowAncestors( +element2,[],!1).filter(el=>isElement(el)&&getNodeName(el)!=="body"),currentContainingBlockComputedStyle=null;const elementIsFixed=getComputedStyle$1(element2).position==="fixed";let currentNode=elementIsFixed?getParentNode(element2):element2;for(;isElement(currentNode)&&!isLastTraversableNode(currentNode);){const computedStyle=getComputedStyle$1(currentNode),currentNodeIsContaining=isContainingBlock(currentNode);!currentNodeIsContaining&&computedStyle.position==="fixed"&&(currentContainingBlockComputedStyle= +null),(elementIsFixed?!currentNodeIsContaining&&!currentContainingBlockComputedStyle:!currentNodeIsContaining&&computedStyle.position==="static"&&!!currentContainingBlockComputedStyle&&absoluteOrFixed.has(currentContainingBlockComputedStyle.position)||isOverflowElement(currentNode)&&!currentNodeIsContaining&&hasFixedPositionAncestor(element2,currentNode))?result=result.filter(ancestor=>ancestor!==currentNode):currentContainingBlockComputedStyle=computedStyle,currentNode=getParentNode(currentNode)} +return cache2.set(element2,result),result}function getClippingRect(_ref){let{element:element2,boundary:boundary2,rootBoundary,strategy}=_ref;const clippingAncestors=[...boundary2==="clippingAncestors"?isTopLayer(element2)?[]:getClippingElementAncestors(element2,this._c):[].concat(boundary2),rootBoundary],firstClippingAncestor=clippingAncestors[0],clippingRect=clippingAncestors.reduce((accRect,clippingAncestor)=>{const rect=getClientRectFromClippingAncestor(element2,clippingAncestor,strategy);return accRect. +top=max(rect.top,accRect.top),accRect.right=min(rect.right,accRect.right),accRect.bottom=min(rect.bottom,accRect.bottom),accRect.left=max(rect.left,accRect.left),accRect},getClientRectFromClippingAncestor(element2,firstClippingAncestor,strategy));return{width:clippingRect.right-clippingRect.left,height:clippingRect.bottom-clippingRect.top,x:clippingRect.left,y:clippingRect.top}}function getDimensions(element2){const{width,height}=getCssDimensions(element2);return{width,height}}function getRectRelativeToOffsetParent(element2,offsetParent,strategy){ +const isOffsetParentAnElement=isHTMLElement(offsetParent),documentElement=getDocumentElement(offsetParent),isFixed=strategy==="fixed",rect=getBoundingClientRect(element2,!0,isFixed,offsetParent);let scroll={scrollLeft:0,scrollTop:0};const offsets=createCoords(0);function setLeftRTLScrollbarOffset(){offsets.x=getWindowScrollBarX(documentElement)}if(isOffsetParentAnElement||!isOffsetParentAnElement&&!isFixed)if((getNodeName(offsetParent)!=="body"||isOverflowElement(documentElement))&&(scroll=getNodeScroll( +offsetParent)),isOffsetParentAnElement){const offsetRect=getBoundingClientRect(offsetParent,!0,isFixed,offsetParent);offsets.x=offsetRect.x+offsetParent.clientLeft,offsets.y=offsetRect.y+offsetParent.clientTop}else documentElement&&setLeftRTLScrollbarOffset();isFixed&&!isOffsetParentAnElement&&documentElement&&setLeftRTLScrollbarOffset();const htmlOffset=documentElement&&!isOffsetParentAnElement&&!isFixed?getHTMLOffset(documentElement,scroll):createCoords(0),x=rect.left+scroll.scrollLeft-offsets. +x-htmlOffset.x,y=rect.top+scroll.scrollTop-offsets.y-htmlOffset.y;return{x,y,width:rect.width,height:rect.height}}function isStaticPositioned(element2){return getComputedStyle$1(element2).position==="static"}function getTrueOffsetParent(element2,polyfill){if(!isHTMLElement(element2)||getComputedStyle$1(element2).position==="fixed")return null;if(polyfill)return polyfill(element2);let rawOffsetParent=element2.offsetParent;return getDocumentElement(element2)===rawOffsetParent&&(rawOffsetParent=rawOffsetParent. +ownerDocument.body),rawOffsetParent}function getOffsetParent(element2,polyfill){const win=getWindow(element2);if(isTopLayer(element2))return win;if(!isHTMLElement(element2)){let svgOffsetParent=getParentNode(element2);for(;svgOffsetParent&&!isLastTraversableNode(svgOffsetParent);){if(isElement(svgOffsetParent)&&!isStaticPositioned(svgOffsetParent))return svgOffsetParent;svgOffsetParent=getParentNode(svgOffsetParent)}return win}let offsetParent=getTrueOffsetParent(element2,polyfill);for(;offsetParent&& +isTableElement(offsetParent)&&isStaticPositioned(offsetParent);)offsetParent=getTrueOffsetParent(offsetParent,polyfill);return offsetParent&&isLastTraversableNode(offsetParent)&&isStaticPositioned(offsetParent)&&!isContainingBlock(offsetParent)?win:offsetParent||getContainingBlock(element2)||win}const getElementRects=async function(data){const getOffsetParentFn=this.getOffsetParent||getOffsetParent,getDimensionsFn=this.getDimensions,floatingDimensions=await getDimensionsFn(data.floating);return{ +reference:getRectRelativeToOffsetParent(data.reference,await getOffsetParentFn(data.floating),data.strategy),floating:{x:0,y:0,width:floatingDimensions.width,height:floatingDimensions.height}}};function isRTL(element2){return getComputedStyle$1(element2).direction==="rtl"}const platform={convertOffsetParentRelativeRectToViewportRelativeRect,getDocumentElement,getClippingRect,getOffsetParent,getElementRects,getClientRects,getDimensions,getScale,isElement,isRTL};function rectsAreEqual(a,b){return a. +x===b.x&&a.y===b.y&&a.width===b.width&&a.height===b.height}function observeMove(element2,onMove){let io=null,timeoutId;const root2=getDocumentElement(element2);function cleanup(){var _io;clearTimeout(timeoutId),(_io=io)==null||_io.disconnect(),io=null}function refresh(skip,threshold){skip===void 0&&(skip=!1),threshold===void 0&&(threshold=1),cleanup();const elementRectForRootMargin=element2.getBoundingClientRect(),{left,top,width,height}=elementRectForRootMargin;if(skip||onMove(),!width||!height) +return;const insetTop=floor(top),insetRight=floor(root2.clientWidth-(left+width)),insetBottom=floor(root2.clientHeight-(top+height)),insetLeft=floor(left),options={rootMargin:-insetTop+"px "+-insetRight+"px "+-insetBottom+"px "+-insetLeft+"px",threshold:max(0,min(1,threshold))||1};let isFirstUpdate=!0;function handleObserve(entries){const ratio=entries[0].intersectionRatio;if(ratio!==threshold){if(!isFirstUpdate)return refresh();ratio?refresh(!1,ratio):timeoutId=setTimeout(()=>{refresh(!1,1e-7)}, +1e3)}ratio===1&&!rectsAreEqual(elementRectForRootMargin,element2.getBoundingClientRect())&&refresh(),isFirstUpdate=!1}try{io=new IntersectionObserver(handleObserve,{...options,root:root2.ownerDocument})}catch{io=new IntersectionObserver(handleObserve,options)}io.observe(element2)}return refresh(!0),cleanup}function autoUpdate(reference,floating,update2,options){options===void 0&&(options={});const{ancestorScroll=!0,ancestorResize=!0,elementResize=typeof ResizeObserver=="function",layoutShift=typeof IntersectionObserver== +"function",animationFrame=!1}=options,referenceEl=unwrapElement(reference),ancestors=ancestorScroll||ancestorResize?[...referenceEl?getOverflowAncestors(referenceEl):[],...getOverflowAncestors(floating)]:[];ancestors.forEach(ancestor=>{ancestorScroll&&ancestor.addEventListener("scroll",update2,{passive:!0}),ancestorResize&&ancestor.addEventListener("resize",update2)});const cleanupIo=referenceEl&&layoutShift?observeMove(referenceEl,update2):null;let reobserveFrame=-1,resizeObserver=null;elementResize&& +(resizeObserver=new ResizeObserver(_ref=>{let[firstEntry]=_ref;firstEntry&&firstEntry.target===referenceEl&&resizeObserver&&(resizeObserver.unobserve(floating),cancelAnimationFrame(reobserveFrame),reobserveFrame=requestAnimationFrame(()=>{var _resizeObserver;(_resizeObserver=resizeObserver)==null||_resizeObserver.observe(floating)})),update2()}),referenceEl&&!animationFrame&&resizeObserver.observe(referenceEl),resizeObserver.observe(floating));let frameId,prevRefRect=animationFrame?getBoundingClientRect( +reference):null;animationFrame&&frameLoop();function frameLoop(){const nextRefRect=getBoundingClientRect(reference);prevRefRect&&!rectsAreEqual(prevRefRect,nextRefRect)&&update2(),prevRefRect=nextRefRect,frameId=requestAnimationFrame(frameLoop)}return update2(),()=>{var _resizeObserver2;ancestors.forEach(ancestor=>{ancestorScroll&&ancestor.removeEventListener("scroll",update2),ancestorResize&&ancestor.removeEventListener("resize",update2)}),cleanupIo?.(),(_resizeObserver2=resizeObserver)==null|| +_resizeObserver2.disconnect(),resizeObserver=null,animationFrame&&cancelAnimationFrame(frameId)}}const offset=offset$1,shift=shift$1,flip=flip$1,size=size$1,hide=hide$1,arrow=arrow$1,limitShift=limitShift$1,computePosition=(reference,floating,options)=>{const cache2=new Map,mergedOptions={platform,...options},platformWithCache={...mergedOptions.platform,_c:cache2};return computePosition$1(reference,floating,{...mergedOptions,platform:platformWithCache})};function get(valueOrGetValue){return typeof valueOrGetValue== +"function"?valueOrGetValue():valueOrGetValue}function getDPR(element2){return typeof window>"u"?1:(element2.ownerDocument.defaultView||window).devicePixelRatio||1}function roundByDPR(element2,value){const dpr=getDPR(element2);return Math.round(value*dpr)/dpr}function getFloatingContentCSSVars(name){return{[`--bits-${name}-content-transform-origin`]:"var(--bits-floating-transform-origin)",[`--bits-${name}-content-available-width`]:"var(--bits-floating-available-width)",[`--bits-${name}-content-av\ +ailable-height`]:"var(--bits-floating-available-height)",[`--bits-${name}-anchor-width`]:"var(--bits-floating-anchor-width)",[`--bits-${name}-anchor-height`]:"var(--bits-floating-anchor-height)"}}function useFloating(options){const whileElementsMountedOption=options.whileElementsMounted,openOption=user_derived(()=>get(options.open)??!0),middlewareOption=user_derived(()=>get(options.middleware)),transformOption=user_derived(()=>get(options.transform)??!0),placementOption=user_derived(()=>get(options. +placement)??"bottom"),strategyOption=user_derived(()=>get(options.strategy)??"absolute"),sideOffsetOption=user_derived(()=>get(options.sideOffset)??0),alignOffsetOption=user_derived(()=>get(options.alignOffset)??0),reference=options.reference;let x=state$1(0),y=state$1(0);const floating=simpleBox(null);let strategy=state$1(proxy(get$3(strategyOption))),placement=state$1(proxy(get$3(placementOption))),middlewareData=state$1(proxy({})),isPositioned=state$1(!1),hasWhileMountedPosition=!1,updateRequestId=0; +const floatingStyles=user_derived(()=>{const xVal=floating.current?roundByDPR(floating.current,get$3(x)):get$3(x),yVal=floating.current?roundByDPR(floating.current,get$3(y)):get$3(y);return get$3(transformOption)?{position:get$3(strategy),left:"0",top:"0",transform:`translate(${xVal}px, ${yVal}px)`,...floating.current&&getDPR(floating.current)>=1.5&&{willChange:"transform"}}:{position:get$3(strategy),left:`${xVal}px`,top:`${yVal}px`}});let whileElementsMountedCleanup;function update2(){if(reference. +current===null||floating.current===null)return;const referenceNode=reference.current,floatingNode=floating.current,requestId=++updateRequestId;computePosition(referenceNode,floatingNode,{middleware:get$3(middlewareOption),placement:get$3(placementOption),strategy:get$3(strategyOption)}).then(position2=>{if(requestId!==updateRequestId||reference.current!==referenceNode||floating.current!==floatingNode)return;if(isReferenceHidden(referenceNode)){set$1(middlewareData,{...get$3(middlewareData),hide:{ +...get$3(middlewareData).hide,referenceHidden:!0}},!0);return}if(!get$3(openOption)&&get$3(x)!==0&&get$3(y)!==0){const maxExpectedOffset=Math.max(Math.abs(get$3(sideOffsetOption)),Math.abs(get$3(alignOffsetOption)),15);if(position2.x<=maxExpectedOffset&&position2.y<=maxExpectedOffset)return}set$1(x,position2.x,!0),set$1(y,position2.y,!0),set$1(strategy,position2.strategy,!0),set$1(placement,position2.placement,!0),set$1(middlewareData,position2.middlewareData,!0),set$1(isPositioned,!0)})}function cleanup(){ +typeof whileElementsMountedCleanup=="function"&&(whileElementsMountedCleanup(),whileElementsMountedCleanup=void 0),updateRequestId++}function attach2(){if(cleanup(),whileElementsMountedOption===void 0){update2();return}get$3(openOption)&&(reference.current===null||floating.current===null||(whileElementsMountedCleanup=whileElementsMountedOption(reference.current,floating.current,update2)))}function reset2(){!get$3(openOption)&&floating.current===null&&set$1(isPositioned,!1)}function trackWhileMountedDeps(){ +return[get$3(middlewareOption),get$3(placementOption),get$3(strategyOption),get$3(sideOffsetOption),get$3(alignOffsetOption),get$3(openOption)]}return user_effect(()=>{whileElementsMountedOption===void 0&&get$3(openOption)&&update2()}),user_effect(attach2),user_effect(()=>{if(whileElementsMountedOption!==void 0){if(trackWhileMountedDeps(),!get$3(openOption)){hasWhileMountedPosition=!1;return}if(!get$3(isPositioned)){hasWhileMountedPosition=!1;return}if(!hasWhileMountedPosition){hasWhileMountedPosition= +!0;return}update2()}}),user_effect(reset2),user_effect(()=>cleanup),{floating,reference,get strategy(){return get$3(strategy)},get placement(){return get$3(placement)},get middlewareData(){return get$3(middlewareData)},get isPositioned(){return get$3(isPositioned)},get floatingStyles(){return get$3(floatingStyles)},get update(){return update2}}}function isReferenceHidden(node2){return node2 instanceof Element?!node2.isConnected||node2 instanceof HTMLElement&&node2.hidden?!0:node2.getClientRects(). +length===0:!1}const OPPOSITE_SIDE={top:"bottom",right:"left",bottom:"top",left:"right"},FloatingRootContext=new Context$1("Floating.Root"),FloatingContentContext=new Context$1("Floating.Content"),FloatingTooltipRootContext=new Context$1("Floating.Root");class FloatingRootState{static create(tooltip=!1){return tooltip?FloatingTooltipRootContext.set(new FloatingRootState):FloatingRootContext.set(new FloatingRootState)}anchorNode=simpleBox(null);customAnchorNode=simpleBox(null);triggerNode=simpleBox( +null);constructor(){user_effect(()=>{this.customAnchorNode.current?typeof this.customAnchorNode.current=="string"?this.anchorNode.current=document.querySelector(this.customAnchorNode.current):this.anchorNode.current=this.customAnchorNode.current:this.anchorNode.current=this.triggerNode.current})}}class FloatingContentState{static create(opts,tooltip=!1){return tooltip?FloatingContentContext.set(new FloatingContentState(opts,FloatingTooltipRootContext.get())):FloatingContentContext.set(new FloatingContentState( +opts,FloatingRootContext.get()))}opts;root;contentRef=simpleBox(null);wrapperRef=simpleBox(null);arrowRef=simpleBox(null);contentAttachment=attachRef(this.contentRef);wrapperAttachment=attachRef(this.wrapperRef);arrowAttachment=attachRef(this.arrowRef);arrowId=simpleBox(useId());#transformedStyle=user_derived(()=>{if(typeof this.opts.style=="string")return cssToStyleObj(this.opts.style);if(!this.opts.style)return{}});#updatePositionStrategy=void 0;#arrowSize=new ElementSize(()=>this.arrowRef.current?? +void 0);#arrowWidth=user_derived(()=>this.#arrowSize?.width??0);#arrowHeight=user_derived(()=>this.#arrowSize?.height??0);#desiredPlacement=user_derived(()=>this.opts.side?.current+(this.opts.align.current!=="center"?`-${this.opts.align.current}`:""));#boundary=user_derived(()=>Array.isArray(this.opts.collisionBoundary.current)?this.opts.collisionBoundary.current:[this.opts.collisionBoundary.current]);#hasExplicitBoundaries=user_derived(()=>get$3(this.#boundary).length>0);get hasExplicitBoundaries(){ +return get$3(this.#hasExplicitBoundaries)}set hasExplicitBoundaries(value){set$1(this.#hasExplicitBoundaries,value)}#detectOverflowOptions=user_derived(()=>({padding:this.opts.collisionPadding.current,boundary:get$3(this.#boundary).filter(isNotNull),altBoundary:this.hasExplicitBoundaries}));get detectOverflowOptions(){return get$3(this.#detectOverflowOptions)}set detectOverflowOptions(value){set$1(this.#detectOverflowOptions,value)}#availableWidth=state$1(void 0);#availableHeight=state$1(void 0);#anchorWidth=state$1( +void 0);#anchorHeight=state$1(void 0);#middleware=user_derived(()=>[offset({mainAxis:this.opts.sideOffset.current+get$3(this.#arrowHeight),alignmentAxis:this.opts.alignOffset.current}),this.opts.avoidCollisions.current&&shift({mainAxis:!0,crossAxis:!1,limiter:this.opts.sticky.current==="partial"?limitShift():void 0,...this.detectOverflowOptions}),this.opts.avoidCollisions.current&&flip({...this.detectOverflowOptions}),size({...this.detectOverflowOptions,apply:({rects,availableWidth,availableHeight})=>{ +const{width:anchorWidth,height:anchorHeight}=rects.reference;set$1(this.#availableWidth,availableWidth,!0),set$1(this.#availableHeight,availableHeight,!0),set$1(this.#anchorWidth,anchorWidth,!0),set$1(this.#anchorHeight,anchorHeight,!0)}}),this.arrowRef.current&&arrow({element:this.arrowRef.current,padding:this.opts.arrowPadding.current}),transformOrigin({arrowWidth:get$3(this.#arrowWidth),arrowHeight:get$3(this.#arrowHeight)}),this.opts.hideWhenDetached.current&&hide({strategy:"referenceHidden", +...this.detectOverflowOptions})].filter(Boolean));get middleware(){return get$3(this.#middleware)}set middleware(value){set$1(this.#middleware,value)}floating;#placedSide=user_derived(()=>getSideFromPlacement(this.floating.placement));get placedSide(){return get$3(this.#placedSide)}set placedSide(value){set$1(this.#placedSide,value)}#placedAlign=user_derived(()=>getAlignFromPlacement(this.floating.placement));get placedAlign(){return get$3(this.#placedAlign)}set placedAlign(value){set$1(this.#placedAlign, +value)}#arrowX=user_derived(()=>this.floating.middlewareData.arrow?.x??0);get arrowX(){return get$3(this.#arrowX)}set arrowX(value){set$1(this.#arrowX,value)}#arrowY=user_derived(()=>this.floating.middlewareData.arrow?.y??0);get arrowY(){return get$3(this.#arrowY)}set arrowY(value){set$1(this.#arrowY,value)}#cannotCenterArrow=user_derived(()=>this.floating.middlewareData.arrow?.centerOffset!==0);get cannotCenterArrow(){return get$3(this.#cannotCenterArrow)}set cannotCenterArrow(value){set$1(this.#cannotCenterArrow, +value)}#contentZIndex=state$1();get contentZIndex(){return get$3(this.#contentZIndex)}set contentZIndex(value){set$1(this.#contentZIndex,value,!0)}#arrowBaseSide=user_derived(()=>OPPOSITE_SIDE[this.placedSide]);get arrowBaseSide(){return get$3(this.#arrowBaseSide)}set arrowBaseSide(value){set$1(this.#arrowBaseSide,value)}#wrapperProps=user_derived(()=>({id:this.opts.wrapperId.current,"data-bits-floating-content-wrapper":"",style:{...this.floating.floatingStyles,transform:this.floating.isPositioned? +this.floating.floatingStyles.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:this.contentZIndex,"--bits-floating-transform-origin":`${this.floating.middlewareData.transformOrigin?.x} ${this.floating.middlewareData.transformOrigin?.y}`,"--bits-floating-available-width":`${get$3(this.#availableWidth)}px`,"--bits-floating-available-height":`${get$3(this.#availableHeight)}px`,"--bits-floating-anchor-width":`${get$3(this.#anchorWidth)}px`,"--bits-floating-anchor-height":`${get$3(this.#anchorHeight)}\ +px`,...this.floating.middlewareData.hide?.referenceHidden&&{visibility:"hidden","pointer-events":"none"},...get$3(this.#transformedStyle)},dir:this.opts.dir.current,...this.wrapperAttachment}));get wrapperProps(){return get$3(this.#wrapperProps)}set wrapperProps(value){set$1(this.#wrapperProps,value)}#props=user_derived(()=>({"data-side":this.placedSide,"data-align":this.placedAlign,style:styleToString$1({...get$3(this.#transformedStyle)}),...this.contentAttachment}));get props(){return get$3(this.#props)}set props(value){ +set$1(this.#props,value)}#arrowStyle=user_derived(()=>({position:"absolute",left:this.arrowX?`${this.arrowX}px`:void 0,top:this.arrowY?`${this.arrowY}px`:void 0,[this.arrowBaseSide]:0,"transform-origin":{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[this.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[this.placedSide],visibility:this.cannotCenterArrow?"hidden": +void 0}));get arrowStyle(){return get$3(this.#arrowStyle)}set arrowStyle(value){set$1(this.#arrowStyle,value)}constructor(opts,root2){this.opts=opts,this.root=root2,this.#updatePositionStrategy=opts.updatePositionStrategy,opts.customAnchor&&(this.root.customAnchorNode.current=opts.customAnchor.current),watch$1(()=>opts.customAnchor.current,customAnchor=>{this.root.customAnchorNode.current=customAnchor}),this.floating=useFloating({strategy:()=>this.opts.strategy.current,placement:()=>get$3(this.#desiredPlacement), +middleware:()=>this.middleware,reference:this.root.anchorNode,whileElementsMounted:(...args)=>autoUpdate(...args,{animationFrame:this.#updatePositionStrategy?.current==="always"}),open:()=>this.opts.enabled.current,sideOffset:()=>this.opts.sideOffset.current,alignOffset:()=>this.opts.alignOffset.current}),user_effect(()=>{this.floating.isPositioned&&this.opts.onPlaced?.current()}),watch$1(()=>this.contentRef.current,contentNode=>{if(!contentNode||!this.opts.enabled.current)return;const win=getWindow$1( +contentNode),rafId=win.requestAnimationFrame(()=>{if(this.contentRef.current!==contentNode||!this.opts.enabled.current)return;const zIndex=win.getComputedStyle(contentNode).zIndex;zIndex!==this.contentZIndex&&(this.contentZIndex=zIndex)});return()=>{win.cancelAnimationFrame(rafId)}}),user_effect(()=>{this.floating.floating.current=this.wrapperRef.current})}}class FloatingArrowState{static create(opts){return new FloatingArrowState(opts,FloatingContentContext.get())}opts;content;constructor(opts,content2){ +this.opts=opts,this.content=content2}#props=user_derived(()=>({id:this.opts.id.current,style:this.content.arrowStyle,"data-side":this.content.placedSide,...this.content.arrowAttachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class FloatingAnchorState{static create(opts,tooltip=!1){return tooltip?new FloatingAnchorState(opts,FloatingTooltipRootContext.get()):new FloatingAnchorState(opts,FloatingRootContext.get())}opts;root;constructor(opts,root2){this.opts= +opts,this.root=root2,opts.virtualEl&&opts.virtualEl.current?root2.triggerNode=boxFrom$1(opts.virtualEl.current):root2.triggerNode=opts.ref}}function transformOrigin(options){return{name:"transformOrigin",options,fn(data){const{placement,rects,middlewareData}=data,isArrowHidden=middlewareData.arrow?.centerOffset!==0,arrowWidth=isArrowHidden?0:options.arrowWidth,arrowHeight=isArrowHidden?0:options.arrowHeight,[placedSide,placedAlign]=getSideAndAlignFromPlacement(placement),noArrowAlign={start:"0%", +center:"50%",end:"100%"}[placedAlign],arrowXCenter=(middlewareData.arrow?.x??0)+arrowWidth/2,arrowYCenter=(middlewareData.arrow?.y??0)+arrowHeight/2;let x="",y="";return placedSide==="bottom"?(x=isArrowHidden?noArrowAlign:`${arrowXCenter}px`,y=`${-arrowHeight}px`):placedSide==="top"?(x=isArrowHidden?noArrowAlign:`${arrowXCenter}px`,y=`${rects.floating.height+arrowHeight}px`):placedSide==="right"?(x=`${-arrowHeight}px`,y=isArrowHidden?noArrowAlign:`${arrowYCenter}px`):placedSide==="left"&&(x=`${rects. +floating.width+arrowHeight}px`,y=isArrowHidden?noArrowAlign:`${arrowYCenter}px`),{data:{x,y}}}}}function getSideAndAlignFromPlacement(placement){const[side,align="center"]=placement.split("-");return[side,align]}function getSideFromPlacement(placement){return getSideAndAlignFromPlacement(placement)[0]}function getAlignFromPlacement(placement){return getSideAndAlignFromPlacement(placement)[1]}function Floating_layer($$anchor,$$props){push$1($$props,!0);let tooltip=prop($$props,"tooltip",3,!1);FloatingRootState. +create(tooltip());var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}class DataTypeahead{#opts;#candidateValues=user_derived(()=>this.#opts.candidateValues());#search;constructor(opts){this.#opts=opts,this.#search=boxAutoReset("",{afterMs:1e3,getWindow:this.#opts.getWindow}),this.handleTypeaheadSearch=this.handleTypeaheadSearch.bind(this),this.resetTypeahead=this.resetTypeahead.bind(this)}handleTypeaheadSearch(key2){if(!this.#opts. +enabled()||!get$3(this.#candidateValues).length)return;this.#search.current=this.#search.current+key2;const currentItem=this.#opts.getCurrentItem(),currentMatch=get$3(this.#candidateValues).find(item=>item===currentItem)??"",values=get$3(this.#candidateValues).map(item=>item??""),nextMatch=getNextMatch(values,this.#search.current,currentMatch),newItem=get$3(this.#candidateValues).find(item=>item===nextMatch);return newItem&&this.#opts.onMatch(newItem),newItem}resetTypeahead(){this.#search.current= +""}}const FIRST_KEYS=[ARROW_DOWN,PAGE_UP,HOME],LAST_KEYS=[ARROW_UP,PAGE_DOWN,END],FIRST_LAST_KEYS=[...FIRST_KEYS,...LAST_KEYS],selectAttrs=createBitsAttrs({component:"select",parts:["trigger","content","item","viewport","scroll-up-button","scroll-down-button","group","group-label","separator","arrow","input","content-wrapper","item-text","value"]}),SelectRootContext=new Context$1("Select.Root | Combobox.Root"),SelectContentContext=new Context$1("Select.Content | Combobox.Content");class SelectBaseRootState{opts;#touchedInput=state$1( +!1);get touchedInput(){return get$3(this.#touchedInput)}set touchedInput(value){set$1(this.#touchedInput,value,!0)}#inputNode=state$1(null);get inputNode(){return get$3(this.#inputNode)}set inputNode(value){set$1(this.#inputNode,value,!0)}#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}contentPresence;#viewportNode=state$1(null);get viewportNode(){return get$3(this.#viewportNode)}set viewportNode(value){set$1(this.#viewportNode, +value,!0)}#triggerNode=state$1(null);get triggerNode(){return get$3(this.#triggerNode)}set triggerNode(value){set$1(this.#triggerNode,value,!0)}#valueNode=state$1(null);get valueNode(){return get$3(this.#valueNode)}set valueNode(value){set$1(this.#valueNode,value,!0)}#valueId=state$1("");get valueId(){return get$3(this.#valueId)}set valueId(value){set$1(this.#valueId,value,!0)}#highlightedNode=state$1(null);get highlightedNode(){return get$3(this.#highlightedNode)}set highlightedNode(value){set$1( +this.#highlightedNode,value,!0)}#highlightedValue=user_derived(()=>this.highlightedNode?this.highlightedNode.getAttribute("data-value"):null);get highlightedValue(){return get$3(this.#highlightedValue)}set highlightedValue(value){set$1(this.#highlightedValue,value)}#highlightedId=user_derived(()=>{if(this.highlightedNode)return this.highlightedNode.id});get highlightedId(){return get$3(this.#highlightedId)}set highlightedId(value){set$1(this.#highlightedId,value)}#highlightedLabel=user_derived(()=>this. +highlightedNode?this.highlightedNode.getAttribute("data-label"):null);get highlightedLabel(){return get$3(this.#highlightedLabel)}set highlightedLabel(value){set$1(this.#highlightedLabel,value)}#contentIsPositioned=state$1(!1);get contentIsPositioned(){return get$3(this.#contentIsPositioned)}set contentIsPositioned(value){set$1(this.#contentIsPositioned,value,!0)}isUsingKeyboard=!1;isCombobox=!1;domContext=new DOMContext(()=>null);constructor(opts){this.opts=opts,this.isCombobox=opts.isCombobox, +this.contentPresence=new PresenceManager({ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}}),user_pre_effect(()=>{this.opts.open.current||this.setHighlightedNode(null)})}setHighlightedNode(node2,initial=!1){this.highlightedNode=node2,node2&&(this.isUsingKeyboard||initial)&&this.scrollHighlightedNodeIntoView(node2)}scrollHighlightedNodeIntoView(node2){!this.viewportNode||!this.contentIsPositioned||node2.scrollIntoView( +{block:this.opts.scrollAlignment.current})}getCandidateNodes(){const node2=this.contentNode;return node2?Array.from(node2.querySelectorAll(`[${this.getBitsAttr("item")}]:not([data-disabled])`)):[]}setHighlightedToFirstCandidate(initial=!1){this.setHighlightedNode(null);let nodes2=this.getCandidateNodes();if(nodes2.length){if(this.viewportNode){const viewportRect=this.viewportNode.getBoundingClientRect();nodes2=nodes2.filter(node2=>{if(!this.viewportNode)return!1;const nodeRect=node2.getBoundingClientRect(); +return nodeRect.right<=viewportRect.right&&nodeRect.left>=viewportRect.left&&nodeRect.bottom<=viewportRect.bottom&&nodeRect.top>=viewportRect.top})}this.setHighlightedNode(nodes2[0],initial)}}getNodeByValue(value){return this.getCandidateNodes().find(node2=>node2.dataset.value===value)??null}getLabelForValue(value){if(value==="")return"";const fromItems=this.opts.items.current.find(item=>item.value===value)?.label;if(fromItems!==void 0)return fromItems;const node2=this.getNodeByValue(value);if(node2){ +const dataLabel=node2.getAttribute("data-label");return dataLabel!==null&&dataLabel!==""?dataLabel:node2.textContent?.trim()??value}return value}setOpen(open2){this.opts.open.current=open2}toggleOpen(){this.opts.open.current=!this.opts.open.current}handleOpen(){this.setOpen(!0)}handleClose(){this.setHighlightedNode(null),this.setOpen(!1)}toggleMenu(){this.toggleOpen()}getBitsAttr=part=>selectAttrs.getAttr(part,this.isCombobox?"combobox":void 0)}class SelectSingleRootState extends SelectBaseRootState{opts;isMulti=!1;#hasValue=user_derived( +()=>this.opts.value.current!=="");get hasValue(){return get$3(this.#hasValue)}set hasValue(value){set$1(this.#hasValue,value)}#currentLabel=user_derived(()=>this.opts.items.current.length?this.opts.items.current.find(item=>item.value===this.opts.value.current)?.label??"":"");get currentLabel(){return get$3(this.#currentLabel)}set currentLabel(value){set$1(this.#currentLabel,value)}#candidateLabels=user_derived(()=>this.opts.items.current.length?this.opts.items.current.filter(item=>!item.disabled). +map(item=>item.label):[]);get candidateLabels(){return get$3(this.#candidateLabels)}set candidateLabels(value){set$1(this.#candidateLabels,value)}#dataTypeaheadEnabled=user_derived(()=>!(this.isMulti||this.opts.items.current.length===0));get dataTypeaheadEnabled(){return get$3(this.#dataTypeaheadEnabled)}set dataTypeaheadEnabled(value){set$1(this.#dataTypeaheadEnabled,value)}constructor(opts){super(opts),this.opts=opts,user_effect(()=>{!this.opts.open.current&&this.highlightedNode&&this.setHighlightedNode( +null)}),watch$1(()=>this.opts.open.current,()=>{this.opts.open.current&&this.setInitialHighlightedNode()})}includesItem(itemValue){return this.opts.value.current===itemValue}toggleItem(itemValue,itemLabel=itemValue){const newValue=this.includesItem(itemValue)?"":itemValue;this.opts.value.current=newValue,newValue!==""&&(this.opts.inputValue.current=itemLabel)}setInitialHighlightedNode(){afterTick(()=>{if(!(this.highlightedNode&&this.domContext.getDocument().contains(this.highlightedNode))){if(this. +opts.value.current!==""){const node2=this.getNodeByValue(this.opts.value.current);if(node2){this.setHighlightedNode(node2,!0);return}}this.setHighlightedToFirstCandidate(!0)}})}}class SelectMultipleRootState extends SelectBaseRootState{opts;isMulti=!0;#hasValue=user_derived(()=>this.opts.value.current.length>0);get hasValue(){return get$3(this.#hasValue)}set hasValue(value){set$1(this.#hasValue,value)}constructor(opts){super(opts),this.opts=opts,user_effect(()=>{!this.opts.open.current&&this.highlightedNode&& +this.setHighlightedNode(null)}),watch$1(()=>this.opts.open.current,()=>{this.opts.open.current&&this.setInitialHighlightedNode()})}includesItem(itemValue){return this.opts.value.current.includes(itemValue)}toggleItem(itemValue,itemLabel=itemValue){this.includesItem(itemValue)?this.opts.value.current=this.opts.value.current.filter(v=>v!==itemValue):this.opts.value.current=[...this.opts.value.current,itemValue],this.opts.inputValue.current=itemLabel}setInitialHighlightedNode(){afterTick(()=>{if(this. +domContext&&!(this.highlightedNode&&this.domContext.getDocument().contains(this.highlightedNode))){if(this.opts.value.current.length&&this.opts.value.current[0]!==""){const node2=this.getNodeByValue(this.opts.value.current[0]);if(node2){this.setHighlightedNode(node2,!0);return}}this.setHighlightedToFirstCandidate(!0)}})}}class SelectRootState{static create(props){const{type:type2,...rest}=props,rootState=type2==="single"?new SelectSingleRootState(rest):new SelectMultipleRootState(rest);return SelectRootContext. +set(rootState)}}class SelectTriggerState{static create(opts){return new SelectTriggerState(opts,SelectRootContext.get())}opts;root;attachment;#domTypeahead;#dataTypeahead;constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(opts.ref,v=>this.root.triggerNode=v),this.root.domContext=new DOMContext(opts.ref),this.#domTypeahead=new DOMTypeahead({getCurrentItem:()=>this.root.highlightedNode,onMatch:node2=>{this.root.setHighlightedNode(node2)},getActiveElement:()=>this.root. +domContext.getActiveElement(),getWindow:()=>this.root.domContext.getWindow()}),this.#dataTypeahead=new DataTypeahead({getCurrentItem:()=>this.root.isMulti?"":this.root.currentLabel,onMatch:label=>{if(this.root.isMulti||!this.root.opts.items.current)return;const matchedItem=this.root.opts.items.current.find(item=>item.label===label);matchedItem&&(this.root.opts.value.current=matchedItem.value)},enabled:()=>!this.root.isMulti&&this.root.dataTypeaheadEnabled,candidateValues:()=>this.root.isMulti?[]: +this.root.candidateLabels,getWindow:()=>this.root.domContext.getWindow()}),this.onkeydown=this.onkeydown.bind(this),this.onpointerdown=this.onpointerdown.bind(this),this.onpointerup=this.onpointerup.bind(this),this.onclick=this.onclick.bind(this)}#handleOpen(){this.root.opts.open.current=!0,this.#dataTypeahead.resetTypeahead(),this.#domTypeahead.resetTypeahead()}#handlePointerOpen(_){this.#handleOpen()}#handleKeyboardSelection(){const isCurrentSelectedValue=this.root.highlightedValue===this.root. +opts.value.current;return!this.root.opts.allowDeselect.current&&isCurrentSelectedValue&&!this.root.isMulti?(this.root.handleClose(),!0):(this.root.highlightedValue!==null&&this.root.toggleItem(this.root.highlightedValue,this.root.highlightedLabel??void 0),!this.root.isMulti&&!isCurrentSelectedValue?(this.root.handleClose(),!0):!1)}onkeydown(e){if(this.root.isUsingKeyboard=!0,(e.key===ARROW_UP||e.key===ARROW_DOWN)&&e.preventDefault(),!this.root.opts.open.current){if(e.key===ENTER||e.key===SPACE|| +e.key===ARROW_DOWN||e.key===ARROW_UP)e.preventDefault(),this.root.handleOpen();else if(!this.root.isMulti&&this.root.dataTypeaheadEnabled){this.#dataTypeahead.handleTypeaheadSearch(e.key);return}if(this.root.hasValue)return;const candidateNodes2=this.root.getCandidateNodes();if(!candidateNodes2.length)return;if(e.key===ARROW_DOWN){const firstCandidate=candidateNodes2[0];this.root.setHighlightedNode(firstCandidate)}else if(e.key===ARROW_UP){const lastCandidate=candidateNodes2[candidateNodes2.length- +1];this.root.setHighlightedNode(lastCandidate)}return}if(e.key===TAB){this.root.handleClose();return}if((e.key===ENTER||e.key===SPACE&&this.#domTypeahead.search==="")&&!e.isComposing&&(e.preventDefault(),this.#handleKeyboardSelection()))return;if(e.key===ARROW_UP&&e.altKey&&this.root.handleClose(),FIRST_LAST_KEYS.includes(e.key)){e.preventDefault();const candidateNodes2=this.root.getCandidateNodes(),currHighlightedNode=this.root.highlightedNode,currIndex=currHighlightedNode?candidateNodes2.indexOf( +currHighlightedNode):-1,loop2=this.root.opts.loop.current;let nextItem;if(e.key===ARROW_DOWN?nextItem=next(candidateNodes2,currIndex,loop2):e.key===ARROW_UP?nextItem=prev(candidateNodes2,currIndex,loop2):e.key===PAGE_DOWN?nextItem=forward(candidateNodes2,currIndex,10,loop2):e.key===PAGE_UP?nextItem=backward(candidateNodes2,currIndex,10,loop2):e.key===HOME?nextItem=candidateNodes2[0]:e.key===END&&(nextItem=candidateNodes2[candidateNodes2.length-1]),!nextItem)return;this.root.setHighlightedNode(nextItem); +return}const isModifierKey=e.ctrlKey||e.altKey||e.metaKey,isCharacterKey=e.key.length===1,isSpaceKey=e.key===SPACE,candidateNodes=this.root.getCandidateNodes();if(e.key!==TAB){if(!isModifierKey&&(isCharacterKey||isSpaceKey)){!this.#domTypeahead.handleTypeaheadSearch(e.key,candidateNodes)&&isSpaceKey&&(e.preventDefault(),this.#handleKeyboardSelection());return}this.root.highlightedNode||this.root.setHighlightedToFirstCandidate()}}onclick(e){e.currentTarget.focus()}onpointerdown(e){if(this.root.opts. +disabled.current)return;if(e.pointerType==="touch")return e.preventDefault();const target2=e.target;target2?.hasPointerCapture(e.pointerId)&&target2?.releasePointerCapture(e.pointerId),e.button===0&&e.ctrlKey===!1&&(this.root.opts.open.current===!1?this.#handlePointerOpen(e):this.root.handleClose())}onpointerup(e){this.root.opts.disabled.current||(e.preventDefault(),e.pointerType==="touch"&&(this.root.opts.open.current===!1?this.#handlePointerOpen(e):this.root.handleClose()))}#props=user_derived( +()=>({id:this.opts.id.current,disabled:this.root.opts.disabled.current?!0:void 0,"aria-haspopup":"listbox","aria-expanded":boolToStr(this.root.opts.open.current),"aria-activedescendant":this.root.highlightedId,"data-state":getDataOpenClosed(this.root.opts.open.current),"data-disabled":boolToEmptyStrOrUndef(this.root.opts.disabled.current),"data-placeholder":this.root.hasValue?void 0:"",[this.root.getBitsAttr("trigger")]:"",onpointerdown:this.onpointerdown,onkeydown:this.onkeydown,onclick:this.onclick, +onpointerup:this.onpointerup,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectContentState{static create(opts){return SelectContentContext.set(new SelectContentState(opts,SelectRootContext.get()))}opts;root;attachment;#isPositioned=state$1(!1);get isPositioned(){return get$3(this.#isPositioned)}set isPositioned(value){set$1(this.#isPositioned,value,!0)}domContext;constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment= +attachRef(opts.ref,v=>this.root.contentNode=v),this.domContext=new DOMContext(this.opts.ref),this.root.domContext===null&&(this.root.domContext=this.domContext),onDestroyEffect(()=>{this.root.contentNode=null,this.root.contentIsPositioned=!1,this.isPositioned=!1}),watch$1(()=>this.root.opts.open.current,()=>{this.root.opts.open.current||(this.root.contentIsPositioned=!1,this.isPositioned=!1)}),watch$1([()=>this.isPositioned,()=>this.root.highlightedNode],()=>{!this.isPositioned||!this.root.highlightedNode|| +this.root.scrollHighlightedNodeIntoView(this.root.highlightedNode)}),this.onpointermove=this.onpointermove.bind(this)}onpointermove(_){this.root.isUsingKeyboard=!1}#styles=user_derived(()=>getFloatingContentCSSVars(this.root.isCombobox?"combobox":"select"));onInteractOutside=e=>{if(e.target===this.root.triggerNode||e.target===this.root.inputNode){e.preventDefault();return}this.opts.onInteractOutside.current(e),!e.defaultPrevented&&this.root.handleClose()};onEscapeKeydown=e=>{this.opts.onEscapeKeydown. +current(e),!e.defaultPrevented&&this.root.handleClose()};onOpenAutoFocus=e=>{e.preventDefault()};onCloseAutoFocus=e=>{e.preventDefault()};get shouldRender(){return this.root.contentPresence.shouldRender}#snippetProps=user_derived(()=>({open:this.root.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,role:"listbox","aria-multiselectable":this.root.isMulti?"true":void 0, +"data-state":getDataOpenClosed(this.root.opts.open.current),...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),[this.root.getBitsAttr("content")]:"",style:{display:"flex",flexDirection:"column",outline:"none",boxSizing:"border-box",pointerEvents:"auto",...get$3(this.#styles)},onpointermove:this.onpointermove,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}popperProps={onInteractOutside:this.onInteractOutside,onEscapeKeydown:this. +onEscapeKeydown,onOpenAutoFocus:this.onOpenAutoFocus,onCloseAutoFocus:this.onCloseAutoFocus,trapFocus:!1,loop:!1,onPlaced:()=>{this.root.opts.open.current&&(this.root.contentIsPositioned=!0,this.isPositioned=!0)}}}class SelectItemState{static create(opts){return new SelectItemState(opts,SelectRootContext.get())}opts;root;attachment;#isSelected=user_derived(()=>this.root.includesItem(this.opts.value.current));get isSelected(){return get$3(this.#isSelected)}set isSelected(value){set$1(this.#isSelected, +value)}#isHighlighted=user_derived(()=>this.root.highlightedValue===this.opts.value.current);get isHighlighted(){return get$3(this.#isHighlighted)}set isHighlighted(value){set$1(this.#isHighlighted,value)}prevHighlighted=new Previous(()=>this.isHighlighted);#mounted=state$1(!1);get mounted(){return get$3(this.#mounted)}set mounted(value){set$1(this.#mounted,value,!0)}constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(opts.ref),watch$1([()=>this.isHighlighted,()=>this. +prevHighlighted.current],()=>{this.isHighlighted?this.opts.onHighlight.current():this.prevHighlighted.current&&this.opts.onUnhighlight.current()}),watch$1(()=>this.mounted,()=>{this.mounted&&this.root.setInitialHighlightedNode()}),this.onpointerdown=this.onpointerdown.bind(this),this.onpointerup=this.onpointerup.bind(this),this.onpointermove=this.onpointermove.bind(this)}handleSelect(){if(this.opts.disabled.current)return;const isCurrentSelectedValue=this.opts.value.current===this.root.opts.value. +current;if(!this.root.opts.allowDeselect.current&&isCurrentSelectedValue&&!this.root.isMulti){this.root.handleClose();return}this.root.toggleItem(this.opts.value.current,this.opts.label.current),!this.root.isMulti&&!isCurrentSelectedValue&&this.root.handleClose()}#snippetProps=user_derived(()=>({selected:this.isSelected,highlighted:this.isHighlighted}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}onpointerdown(e){e.preventDefault()}onpointerup(e){ +if(!(e.defaultPrevented||!this.opts.ref.current)){if(e.pointerType==="touch"&&!isIOS){on(this.opts.ref.current,"click",()=>{this.handleSelect(),this.root.setHighlightedNode(this.opts.ref.current)},{once:!0});return}e.preventDefault(),this.handleSelect(),e.pointerType==="touch"&&this.root.setHighlightedNode(this.opts.ref.current)}}onpointermove(e){e.pointerType!=="touch"&&this.root.highlightedNode!==this.opts.ref.current&&this.root.setHighlightedNode(this.opts.ref.current)}#props=user_derived(()=>({ +id:this.opts.id.current,role:"option","aria-selected":this.root.includesItem(this.opts.value.current)?"true":void 0,"data-value":this.opts.value.current,"data-disabled":boolToEmptyStrOrUndef(this.opts.disabled.current),"data-highlighted":this.root.highlightedValue===this.opts.value.current&&!this.opts.disabled.current?"":void 0,"data-selected":this.root.includesItem(this.opts.value.current)?"":void 0,"data-label":this.opts.label.current,[this.root.getBitsAttr("item")]:"",onpointermove:this.onpointermove, +onpointerdown:this.onpointerdown,onpointerup:this.onpointerup,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectHiddenInputState{static create(opts){return new SelectHiddenInputState(opts,SelectRootContext.get())}opts;root;#shouldRender=user_derived(()=>this.root.opts.name.current!=="");get shouldRender(){return get$3(this.#shouldRender)}set shouldRender(value){set$1(this.#shouldRender,value)}constructor(opts,root2){this.opts=opts, +this.root=root2,this.onfocus=this.onfocus.bind(this)}onfocus(e){e.preventDefault(),this.root.isCombobox?this.root.inputNode?.focus():this.root.triggerNode?.focus()}#props=user_derived(()=>({disabled:boolToTrueOrUndef(this.root.opts.disabled.current),required:boolToTrueOrUndef(this.root.opts.required.current),name:this.root.opts.name.current,value:this.opts.value.current,onfocus:this.onfocus}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectViewportState{static create(opts){ +return new SelectViewportState(opts,SelectContentContext.get())}opts;content;root;attachment;#prevScrollTop=state$1(0);get prevScrollTop(){return get$3(this.#prevScrollTop)}set prevScrollTop(value){set$1(this.#prevScrollTop,value,!0)}constructor(opts,content2){this.opts=opts,this.content=content2,this.root=content2.root,this.attachment=attachRef(opts.ref,v=>{this.root.viewportNode=v})}#props=user_derived(()=>({id:this.opts.id.current,role:"presentation",[this.root.getBitsAttr("viewport")]:"",style:{ +position:"relative",flex:1,overflow:"auto"},...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectScrollButtonImplState{opts;content;root;attachment;autoScrollTimer=null;userScrollTimer=-1;isUserScrolling=!1;onAutoScroll=noop$1;#mounted=state$1(!1);get mounted(){return get$3(this.#mounted)}set mounted(value){set$1(this.#mounted,value,!0)}constructor(opts,content2){this.opts=opts,this.content=content2,this.root=content2.root,this.attachment= +attachRef(opts.ref),watch$1([()=>this.mounted],()=>{if(!this.mounted){this.isUserScrolling=!1;return}this.isUserScrolling}),user_effect(()=>{this.mounted||this.clearAutoScrollInterval()}),this.onpointerdown=this.onpointerdown.bind(this),this.onpointermove=this.onpointermove.bind(this),this.onpointerleave=this.onpointerleave.bind(this)}handleUserScroll(){this.content.domContext.clearTimeout(this.userScrollTimer),this.isUserScrolling=!0,this.userScrollTimer=this.content.domContext.setTimeout(()=>{ +this.isUserScrolling=!1},200)}clearAutoScrollInterval(){this.autoScrollTimer!==null&&(this.content.domContext.clearTimeout(this.autoScrollTimer),this.autoScrollTimer=null)}onpointerdown(_){if(this.autoScrollTimer!==null)return;const autoScroll=tick2=>{this.onAutoScroll(),this.autoScrollTimer=this.content.domContext.setTimeout(()=>autoScroll(tick2+1),this.opts.delay.current(tick2))};this.autoScrollTimer=this.content.domContext.setTimeout(()=>autoScroll(1),this.opts.delay.current(0))}onpointermove(e){ +this.onpointerdown(e)}onpointerleave(_){this.clearAutoScrollInterval()}#props=user_derived(()=>({id:this.opts.id.current,"aria-hidden":boolToStrTrueOrUndef(!0),style:{flexShrink:0},onpointerdown:this.onpointerdown,onpointermove:this.onpointermove,onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectScrollDownButtonState{static create(opts){return new SelectScrollDownButtonState(new SelectScrollButtonImplState( +opts,SelectContentContext.get()))}scrollButtonState;content;root;#canScrollDown=state$1(!1);get canScrollDown(){return get$3(this.#canScrollDown)}set canScrollDown(value){set$1(this.#canScrollDown,value,!0)}scrollIntoViewTimer=null;constructor(scrollButtonState){this.scrollButtonState=scrollButtonState,this.content=scrollButtonState.content,this.root=scrollButtonState.root,this.scrollButtonState.onAutoScroll=this.handleAutoScroll,watch$1([()=>this.root.viewportNode,()=>this.content.isPositioned], +()=>{if(!(!this.root.viewportNode||!this.content.isPositioned))return this.handleScroll(!0),on(this.root.viewportNode,"scroll",()=>this.handleScroll())}),watch$1([()=>this.root.opts.inputValue.current,()=>this.root.viewportNode,()=>this.content.isPositioned],()=>{!this.root.viewportNode||!this.content.isPositioned||this.handleScroll(!0)}),watch$1(()=>this.scrollButtonState.mounted,()=>{this.scrollButtonState.mounted&&(this.scrollIntoViewTimer&&clearTimeout(this.scrollIntoViewTimer),this.scrollIntoViewTimer= +afterSleep(5,()=>{const activeItem=this.root.highlightedNode;activeItem&&this.root.scrollHighlightedNodeIntoView(activeItem)}))})}handleScroll=(manual=!1)=>{if(manual||this.scrollButtonState.handleUserScroll(),!this.root.viewportNode)return;const maxScroll=this.root.viewportNode.scrollHeight-this.root.viewportNode.clientHeight,paddingTop=Number.parseInt(getComputedStyle(this.root.viewportNode).paddingTop,10);this.canScrollDown=Math.ceil(this.root.viewportNode.scrollTop){ +const viewport=this.root.viewportNode,selectedItem=this.root.highlightedNode;!viewport||!selectedItem||(viewport.scrollTop=viewport.scrollTop+selectedItem.offsetHeight)};#props=user_derived(()=>({...this.scrollButtonState.props,[this.root.getBitsAttr("scroll-down-button")]:""}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class SelectScrollUpButtonState{static create(opts){return new SelectScrollUpButtonState(new SelectScrollButtonImplState(opts,SelectContentContext. +get()))}scrollButtonState;content;root;#canScrollUp=state$1(!1);get canScrollUp(){return get$3(this.#canScrollUp)}set canScrollUp(value){set$1(this.#canScrollUp,value,!0)}constructor(scrollButtonState){this.scrollButtonState=scrollButtonState,this.content=scrollButtonState.content,this.root=scrollButtonState.root,this.scrollButtonState.onAutoScroll=this.handleAutoScroll,watch$1([()=>this.root.viewportNode,()=>this.content.isPositioned],()=>{if(!(!this.root.viewportNode||!this.content.isPositioned)) +return this.handleScroll(!0),on(this.root.viewportNode,"scroll",()=>this.handleScroll())})}handleScroll=(manual=!1)=>{if(manual||this.scrollButtonState.handleUserScroll(),!this.root.viewportNode)return;const paddingTop=Number.parseInt(getComputedStyle(this.root.viewportNode).paddingTop,10);this.canScrollUp=this.root.viewportNode.scrollTop-paddingTop>.1};handleAutoScroll=()=>{!this.root.viewportNode||!this.root.highlightedNode||(this.root.viewportNode.scrollTop=this.root.viewportNode.scrollTop-this. +root.highlightedNode.offsetHeight)};#props=user_derived(()=>({...this.scrollButtonState.props,[this.root.getBitsAttr("scroll-up-button")]:""}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}function Select_hidden_input($$anchor,$$props){push$1($$props,!0);let value=prop($$props,"value",15);const hiddenInputState=SelectHiddenInputState.create({value:boxWith$1(()=>value())});var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Hidden_input( +$$anchor2,spread_props(()=>hiddenInputState.props,{get autocomplete(){return $$props.autocomplete},get value(){return value()},set value($$value){value($$value)}}))};if_block(node2,$$render=>{hiddenInputState.shouldRender&&$$render(consequent)})}append($$anchor,fragment),pop()}function Floating_layer_anchor($$anchor,$$props){push$1($$props,!0);let tooltip=prop($$props,"tooltip",3,!1);FloatingAnchorState.create({id:boxWith$1(()=>$$props.id),virtualEl:boxWith$1(()=>$$props.virtualEl),ref:$$props.ref}, +tooltip());var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}var root_4$M=from_svg(''),root_2$1u=from_html("");function Arrow($$anchor,$$props){push$1($$props,!0);let id2=prop($$props,"id",19,useId),width=prop($$props,"width",3,10),height=prop($$props,"height",3,5),restProps=rest_props( +$$props,["$$slots","$$events","$$legacy","id","children","child","width","height"]);const mergedProps=user_derived(()=>mergeProps(restProps,{id:id2()}));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate_1=$$anchor2=>{var span=root_2$1u();attribute_effect(span,()=>({...get$3(mergedProps)}));var node_2=child( +span);{var consequent_1=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var svg2=root_4$M();template_effect(()=>{set_attribute(svg2,"width",width()),set_attribute(svg2,"height",height())}),append($$anchor3,svg2)};if_block(node_2,$$render=>{$$props.children?$$render(consequent_1):$$render(alternate,-1)})}reset(span),append($$anchor2,span)};if_block(node2,$$render=>{$$props.child? +$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}function Floating_layer_arrow($$anchor,$$props){push$1($$props,!0);let id2=prop($$props,"id",19,useId),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref"]);const arrowState=FloatingArrowState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,arrowState.props));Arrow($$anchor,spread_props(()=>get$3( +mergedProps))),pop()}function Floating_layer_content($$anchor,$$props){push$1($$props,!0);let side=prop($$props,"side",3,"bottom"),sideOffset=prop($$props,"sideOffset",3,0),align=prop($$props,"align",3,"center"),alignOffset=prop($$props,"alignOffset",3,0),arrowPadding=prop($$props,"arrowPadding",3,0),avoidCollisions=prop($$props,"avoidCollisions",3,!0),collisionBoundary=prop($$props,"collisionBoundary",19,()=>[]),collisionPadding=prop($$props,"collisionPadding",3,0),hideWhenDetached=prop($$props, +"hideWhenDetached",3,!1),onPlaced=prop($$props,"onPlaced",3,()=>{}),sticky=prop($$props,"sticky",3,"partial"),updatePositionStrategy=prop($$props,"updatePositionStrategy",3,"optimized"),strategy=prop($$props,"strategy",3,"fixed"),dir=prop($$props,"dir",3,"ltr"),style2=prop($$props,"style",19,()=>({})),wrapperId=prop($$props,"wrapperId",19,useId),customAnchor=prop($$props,"customAnchor",3,null),tooltip=prop($$props,"tooltip",3,!1);const contentState=FloatingContentState.create({side:boxWith$1(()=>side()), +sideOffset:boxWith$1(()=>sideOffset()),align:boxWith$1(()=>align()),alignOffset:boxWith$1(()=>alignOffset()),id:boxWith$1(()=>$$props.id),arrowPadding:boxWith$1(()=>arrowPadding()),avoidCollisions:boxWith$1(()=>avoidCollisions()),collisionBoundary:boxWith$1(()=>collisionBoundary()),collisionPadding:boxWith$1(()=>collisionPadding()),hideWhenDetached:boxWith$1(()=>hideWhenDetached()),onPlaced:boxWith$1(()=>onPlaced()),sticky:boxWith$1(()=>sticky()),updatePositionStrategy:boxWith$1(()=>updatePositionStrategy()), +strategy:boxWith$1(()=>strategy()),dir:boxWith$1(()=>dir()),style:boxWith$1(()=>style2()),enabled:boxWith$1(()=>$$props.enabled),wrapperId:boxWith$1(()=>wrapperId()),customAnchor:boxWith$1(()=>customAnchor())},tooltip()),mergedProps=user_derived(()=>mergeProps(contentState.wrapperProps,{style:{pointerEvents:"auto"}}));var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.content??noop$3,()=>({props:contentState.props,wrapperProps:get$3(mergedProps)})),append($$anchor,fragment), +pop()}function Floating_layer_content_static($$anchor,$$props){push$1($$props,!0),onMount$1(()=>{$$props.onPlaced?.()});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.content??noop$3,()=>({props:{},wrapperProps:{}})),append($$anchor,fragment),pop()}function Popper_content($$anchor,$$props){let isStatic=prop($$props,"isStatic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","content","isStatic","onPlaced"]);var fragment=comment$2(),node2=first_child( +fragment);{var consequent=$$anchor2=>{Floating_layer_content_static($$anchor2,{get content(){return $$props.content},get onPlaced(){return $$props.onPlaced}})},alternate=$$anchor2=>{Floating_layer_content($$anchor2,spread_props({get content(){return $$props.content},get onPlaced(){return $$props.onPlaced}},()=>restProps))};if_block(node2,$$render=>{isStatic()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment)}var root_1$1c=from_html(" ",1);function Popper_layer_inner($$anchor,$$props){ +push$1($$props,!0);let interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3,null),isStatic=prop($$props,"isStatic",3,!1),tooltip=prop($$props,"tooltip",3,!1),contentPointerEvents=prop($$props,"contentPointerEvents",3,"auto"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","popper","onEscapeKeydown","escapeKeydownBehavior","prev\ +entOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpenAutoFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","isStatic","enabled","ref","tooltip","contentPointerEvents"]); +const resolvedPreventScroll=user_derived(()=>$$props.preventScroll??!0),effectiveStrategy=user_derived(()=>$$props.strategy??(get$3(resolvedPreventScroll)?"fixed":"absolute"));Popper_content($$anchor,{get isStatic(){return isStatic()},get id(){return $$props.id},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset},get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){return $$props.arrowPadding},get avoidCollisions(){return $$props. +avoidCollisions},get collisionBoundary(){return $$props.collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){return $$props.updatePositionStrategy},get strategy(){return get$3(effectiveStrategy)},get dir(){return $$props.dir},get wrapperId(){return $$props.wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){ +return customAnchor()},get enabled(){return $$props.enabled},get tooltip(){return tooltip()},content:($$anchor2,$$arg0)=>{let floatingProps=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;var fragment_1=root_1$1c(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{Scroll_lock($$anchor3,{get preventScroll(){return get$3(resolvedPreventScroll)}})},consequent_1=$$anchor3=>{Scroll_lock($$anchor3,{get preventScroll(){return get$3(resolvedPreventScroll)}})};if_block(node2,$$render=>{ +$$props.forceMount&&$$props.enabled?$$render(consequent):$$props.forceMount||$$render(consequent_1,1)})}var node_1=sibling(node2,2);Focus_scope(node_1,{get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get onCloseAutoFocus(){return $$props.onCloseAutoFocus},get loop(){return $$props.loop},get enabled(){return $$props.enabled},get trapFocus(){return trapFocus()},get forceMount(){return $$props.forceMount},get ref(){return $$props.ref},focusScope:($$anchor3,$$arg02)=>{let focusScopeProps=()=>$$arg02?.(). +props;Escape_layer($$anchor3,{get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor4,$$slotProps)=>{Dismissible_layer($$anchor4,{get id(){return $$props.id},get onInteractOutside(){return $$props.onInteractOutside},get onFocusOutside(){return $$props.onFocusOutside},get interactOutsideBehavior(){return interactOutsideBehavior()},get isValidEvent(){ +return isValidEvent2()},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor5,$$arg03)=>{let dismissibleProps=()=>$$arg03?.().props;Text_selection_layer($$anchor5,{get id(){return $$props.id},get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get onPointerDown(){return $$props.onPointerDown},get onPointerUp(){return $$props.onPointerUp},get enabled(){return $$props.enabled},get ref(){return $$props.ref},children:($$anchor6,$$slotProps2)=>{ +var fragment_7=comment$2(),node_2=first_child(fragment_7);{let $0=user_derived(()=>({props:mergeProps(restProps,floatingProps(),dismissibleProps(),focusScopeProps(),{style:{pointerEvents:contentPointerEvents()}}),wrapperProps:wrapperProps()}));snippet(node_2,()=>$$props.popper??noop$3,()=>get$3($0))}append($$anchor6,fragment_7)},$$slots:{default:!0}})},$$slots:{default:!0}})},$$slots:{default:!0}})},$$slots:{focusScope:!0}}),append($$anchor2,fragment_1)},$$slots:{content:!0}}),pop()}function Popper_layer($$anchor,$$props){ +let interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3,null),isStatic=prop($$props,"isStatic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","popper","open","onEscapeKeydown","escapeKeydownBehavior","preventOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding", +"avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpenAutoFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","isStatic","ref","shouldRender"]);var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{Popper_layer_inner($$anchor2,spread_props({get popper(){return $$props. +popper},get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior},get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get id(){return $$props.id},get onPointerDown(){return $$props.onPointerDown},get onPointerUp(){return $$props.onPointerUp},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset},get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){ +return $$props.arrowPadding},get avoidCollisions(){return $$props.avoidCollisions},get collisionBoundary(){return $$props.collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){return $$props.updatePositionStrategy},get strategy(){return $$props.strategy},get dir(){return $$props.dir},get preventScroll(){return $$props.preventScroll},get wrapperId(){return $$props. +wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){return customAnchor()},get isStatic(){return isStatic()},get enabled(){return $$props.open},get onInteractOutside(){return $$props.onInteractOutside},get onCloseAutoFocus(){return $$props.onCloseAutoFocus},get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get interactOutsideBehavior(){return interactOutsideBehavior()},get loop(){return $$props.loop},get trapFocus(){return trapFocus()},get isValidEvent(){ +return isValidEvent2()},get onFocusOutside(){return $$props.onFocusOutside},forceMount:!1,get ref(){return $$props.ref}},()=>restProps))};if_block(node2,$$render=>{$$props.shouldRender&&$$render(consequent)})}append($$anchor,fragment)}function Popper_layer_force_mount($$anchor,$$props){let interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"close"),trapFocus=prop($$props,"trapFocus",3,!0),isValidEvent2=prop($$props,"isValidEvent",3,()=>!1),customAnchor=prop($$props,"customAnchor",3, +null),isStatic=prop($$props,"isStatic",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","popper","onEscapeKeydown","escapeKeydownBehavior","preventOverflowTextSelection","id","onPointerDown","onPointerUp","side","sideOffset","align","alignOffset","arrowPadding","avoidCollisions","collisionBoundary","collisionPadding","sticky","hideWhenDetached","updatePositionStrategy","strategy","dir","preventScroll","wrapperId","style","onPlaced","onInteractOutside","onCloseAutoFocus","onOpe\ +nAutoFocus","onFocusOutside","interactOutsideBehavior","loop","trapFocus","isValidEvent","customAnchor","isStatic","enabled"]);Popper_layer_inner($$anchor,spread_props({get popper(){return $$props.popper},get onEscapeKeydown(){return $$props.onEscapeKeydown},get escapeKeydownBehavior(){return $$props.escapeKeydownBehavior},get preventOverflowTextSelection(){return $$props.preventOverflowTextSelection},get id(){return $$props.id},get onPointerDown(){return $$props.onPointerDown},get onPointerUp(){ +return $$props.onPointerUp},get side(){return $$props.side},get sideOffset(){return $$props.sideOffset},get align(){return $$props.align},get alignOffset(){return $$props.alignOffset},get arrowPadding(){return $$props.arrowPadding},get avoidCollisions(){return $$props.avoidCollisions},get collisionBoundary(){return $$props.collisionBoundary},get collisionPadding(){return $$props.collisionPadding},get sticky(){return $$props.sticky},get hideWhenDetached(){return $$props.hideWhenDetached},get updatePositionStrategy(){ +return $$props.updatePositionStrategy},get strategy(){return $$props.strategy},get dir(){return $$props.dir},get preventScroll(){return $$props.preventScroll},get wrapperId(){return $$props.wrapperId},get style(){return $$props.style},get onPlaced(){return $$props.onPlaced},get customAnchor(){return customAnchor()},get isStatic(){return isStatic()},get enabled(){return $$props.enabled},get onInteractOutside(){return $$props.onInteractOutside},get onCloseAutoFocus(){return $$props.onCloseAutoFocus}, +get onOpenAutoFocus(){return $$props.onOpenAutoFocus},get interactOutsideBehavior(){return interactOutsideBehavior()},get loop(){return $$props.loop},get trapFocus(){return trapFocus()},get isValidEvent(){return isValidEvent2()},get onFocusOutside(){return $$props.onFocusOutside}},()=>restProps,{forceMount:!0}))}var root_4$L=from_html("
"),root_8$z=from_html("
");function Select_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop( +$$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),side=prop($$props,"side",3,"bottom"),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),preventScroll=prop($$props,"preventScroll",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","forceMount","side","onInteractOutside","onEscapeKeydown","children","child","preventScroll","style"]);const contentState=SelectContentState. +create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onInteractOutside:boxWith$1(()=>onInteractOutside()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown())}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get side(){return side()},get enabled(){ +return contentState.root.opts.open.current},get id(){return id2()},get preventScroll(){return preventScroll()},forceMount:!0,get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:contentState.props.style},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(), +node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$L();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block( +node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get side(){return side()},get open(){return contentState.root.opts.open.current},get id(){return id2()},get preventScroll(){return preventScroll()},forceMount:!1,get shouldRender(){return contentState.shouldRender}, +popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:contentState.props.style},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_5,()=>$$props.child, +()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$z();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{ +forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}function Mounted($$anchor,$$props){push$1($$props,!0);let mounted=prop($$props,"mounted",15,!1),onMountedChange=prop($$props,"onMountedChange",3,noop$1);onMountEffect(()=>(mounted(!0),onMountedChange()(!0),()=>{mounted(!1),onMountedChange()(!1)})),pop()}var root_2$1t=from_html("
"),root$1Z=from_html(" ",1);function Select_item$1($$anchor,$$props){const uid2=props_id();push$1( +$$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),label=prop($$props,"label",19,()=>$$props.value),disabled=prop($$props,"disabled",3,!1),onHighlight=prop($$props,"onHighlight",3,noop$1),onUnhighlight=prop($$props,"onUnhighlight",3,noop$1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","value","label","disabled","children","child","onHighlight","onUnhighlight"]);const itemState=SelectItemState.create({id:boxWith$1(()=>id2()),ref:boxWith$1( +()=>ref2(),v=>ref2(v)),value:boxWith$1(()=>$$props.value),disabled:boxWith$1(()=>disabled()),label:boxWith$1(()=>label()),onHighlight:boxWith$1(()=>onHighlight()),onUnhighlight:boxWith$1(()=>onUnhighlight())}),mergedProps=user_derived(()=>mergeProps(restProps,itemState.props));var fragment=root$1Z(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({props:get$3(mergedProps),...itemState.snippetProps}));snippet( +node_1,()=>$$props.child,()=>get$3($0))}append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1t();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3,()=>itemState.snippetProps),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}var node_3=sibling(node2,2);Mounted(node_3,{get mounted(){return itemState.mounted},set mounted($$value){itemState.mounted= +$$value}}),append($$anchor,fragment),pop()}var root_2$1s=from_html("
");function Select_viewport($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","children","child"]);const viewportState=SelectViewportState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps, +viewportState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1s();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render( +consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_3$14=from_html("
"),root_1$1b=from_html(" ",1);function Select_scroll_down_button$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),delay=prop($$props,"delay",3,()=>50),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","delay","child","children"]);const scrollButtonState=SelectScrollDownButtonState. +create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),delay:boxWith$1(()=>delay())}),mergedProps=user_derived(()=>mergeProps(restProps,scrollButtonState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var fragment_1=root_1$1b(),node_1=first_child(fragment_1);Mounted(node_1,{get mounted(){return scrollButtonState.scrollButtonState.mounted},set mounted($$value){scrollButtonState.scrollButtonState.mounted=$$value}});var node_2=sibling(node_1, +2);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.child,()=>({props:restProps})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$14();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_4=child(div);snippet(node_4,()=>$$props.children??noop$3),reset(div),append($$anchor3,div)};if_block(node_2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)};if_block(node2, +$$render=>{scrollButtonState.canScrollDown&&$$render(consequent_1)})}append($$anchor,fragment),pop()}var root_3$13=from_html("
"),root_1$1a=from_html(" ",1);function Select_scroll_up_button$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),delay=prop($$props,"delay",3,()=>50),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","delay","child","children"]);const scrollButtonState=SelectScrollUpButtonState. +create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),delay:boxWith$1(()=>delay())}),mergedProps=user_derived(()=>mergeProps(restProps,scrollButtonState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{var fragment_1=root_1$1a(),node_1=first_child(fragment_1);Mounted(node_1,{get mounted(){return scrollButtonState.scrollButtonState.mounted},set mounted($$value){scrollButtonState.scrollButtonState.mounted=$$value}});var node_2=sibling(node_1, +2);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.child,()=>({props:restProps})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$13();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_4=child(div);snippet(node_4,()=>$$props.children??noop$3),reset(div),append($$anchor3,div)};if_block(node_2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)};if_block(node2, +$$render=>{scrollButtonState.canScrollUp&&$$render(consequent_1)})}append($$anchor,fragment),pop()}function Menu_sub($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1);MenuSubmenuState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()?.(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),Floating_layer($$anchor,{children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root_2$1r=from_html("
");function Menu_item($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),onSelect=prop($$props,"onSelect",3,noop$1),closeOnSelect=prop($$props,"closeOnSelect",3,!0),restProps=rest_props( +$$props,["$$slots","$$events","$$legacy","child","children","ref","id","disabled","onSelect","closeOnSelect"]);const itemState=MenuItemState.create({id:boxWith$1(()=>id2()),disabled:boxWith$1(()=>disabled()),onSelect:boxWith$1(()=>onSelect()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),closeOnSelect:boxWith$1(()=>closeOnSelect())}),mergedProps=user_derived(()=>mergeProps(restProps,itemState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(), +node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1r();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_2$1q=from_html("
");function Menu_separator($$anchor,$$props){ +const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","id","child","children"]);const separatorState=MenuSeparatorState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,separatorState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(), +node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$1q();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_4$K=from_html("
"),root_8$y=from_html( +"
");function Menu_sub_content($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),loop2=prop($$props,"loop",3,!0),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),forceMount=prop($$props,"forceMount",3,!1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),interactOutsideBehavior=prop($$props,"interactOutsideBehavior",3,"defer-otherwise-close"),escapeKeydownBehavior=prop( +$$props,"escapeKeydownBehavior",3,"defer-otherwise-close"),onOpenAutoFocusProp=prop($$props,"onOpenAutoFocus",3,noop$1),onCloseAutoFocusProp=prop($$props,"onCloseAutoFocus",3,noop$1),onFocusOutside=prop($$props,"onFocusOutside",3,noop$1),side=prop($$props,"side",3,"right"),trapFocus=prop($$props,"trapFocus",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","children","child","loop","onInteractOutside","forceMount","onEscapeKeydown","interactOutsideBehavior","escapeKe\ +ydownBehavior","onOpenAutoFocus","onCloseAutoFocus","onFocusOutside","side","trapFocus","style"]);const subContentState=MenuContentState.create({id:boxWith$1(()=>id2()),loop:boxWith$1(()=>loop2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),isSub:!0,onCloseAutoFocus:boxWith$1(()=>handleCloseAutoFocus)});function onkeydown(e){const isKeyDownInside=e.currentTarget.contains(e.target),isCloseKey=SUB_CLOSE_KEYS[subContentState.parentMenu.root.opts.dir.current].includes(e.key);isKeyDownInside&&isCloseKey&&(subContentState. +parentMenu.onClose(),subContentState.parentMenu.triggerNode?.focus(),e.preventDefault())}const dataAttr=user_derived(()=>subContentState.parentMenu.root.getBitsAttr("sub-content")),mergedProps=user_derived(()=>mergeProps(restProps,subContentState.props,{side:side(),onkeydown,[get$3(dataAttr)]:""}));function handleOpenAutoFocus(e){onOpenAutoFocusProp()(e),!e.defaultPrevented&&(e.preventDefault(),subContentState.parentMenu.root.isUsingKeyboard&&subContentState.parentMenu.contentNode&&MenuOpenEvent. +dispatch(subContentState.parentMenu.contentNode))}function handleCloseAutoFocus(e){onCloseAutoFocusProp()(e),!e.defaultPrevented&&e.preventDefault()}function handleInteractOutside(e){onInteractOutside()(e),!e.defaultPrevented&&subContentState.parentMenu.onClose()}function handleEscapeKeydown(e){onEscapeKeydown()(e),!e.defaultPrevented&&subContentState.parentMenu.onClose()}function handleOnFocusOutside(e){if(onFocusOutside()(e),e.defaultPrevented||!isHTMLElement$1(e.target)||e.target.id===subContentState. +parentMenu.triggerNode?.id)return;if(subContentState.parentMenu.parentMenu?.contentNode?.contains(e.target)){subContentState.parentMenu.onClose(),e.preventDefault();return}const subContentSelector=`[${subContentState.parentMenu.root.getBitsAttr("sub-content")}]`;if(e.target.closest(subContentSelector)){e.preventDefault();return}subContentState.parentMenu.onClose()}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3( +mergedProps),{get ref(){return subContentState.opts.ref},get interactOutsideBehavior(){return interactOutsideBehavior()},get escapeKeydownBehavior(){return escapeKeydownBehavior()},onOpenAutoFocus:handleOpenAutoFocus,get enabled(){return subContentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,onFocusOutside:handleOnFocusOutside,preventScroll:!1,get loop(){return loop2()},get trapFocus(){return trapFocus()},get shouldRender(){return subContentState. +shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),get$3(mergedProps),{style:getFloatingContentCSSVars("menu")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...subContentState.snippetProps})); +snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$K();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{ +Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),{get ref(){return subContentState.opts.ref},get interactOutsideBehavior(){return interactOutsideBehavior()},get escapeKeydownBehavior(){return escapeKeydownBehavior()},onCloseAutoFocus:handleCloseAutoFocus,onOpenAutoFocus:handleOpenAutoFocus,get open(){return subContentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,onFocusOutside:handleOnFocusOutside,preventScroll:!1,get loop(){ +return loop2()},get trapFocus(){return trapFocus()},get shouldRender(){return subContentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),get$3(mergedProps),{style:getFloatingContentCSSVars("menu")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived( +()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...subContentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$y();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child? +$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$12=from_html("
");function Menu_sub_trigger($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),ref2=prop($$props,"ref",15,null),onSelect=prop( +$$props,"onSelect",3,noop$1),openDelay=prop($$props,"openDelay",3,0),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","disabled","ref","children","child","onSelect","openDelay"]);const subTriggerState=MenuSubTriggerState.create({disabled:boxWith$1(()=>disabled()),onSelect:boxWith$1(()=>onSelect()),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),openDelay:boxWith$1(()=>openDelay())}),mergedProps=user_derived(()=>mergeProps(restProps,subTriggerState.props));Floating_layer_anchor( +$$anchor,{get id(){return id2()},get ref(){return subTriggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_1=first_child(fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var div=root_3$12();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children?? +noop$3),reset(div),append($$anchor3,div)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}function isPointInPolygon(point2,polygon){const[x,y]=point2;let isInside=!1;const length=polygon.length;for(let i=0,j=length-1;i=y!=yj>=y&&x<=(xj-xi)*(y-yi)/(yj-yi)+xi&&(isInside=!isInside)}return isInside}function isInsideRect(point2,rect){ +return point2[0]>=rect.left&&point2[0]<=rect.right&&point2[1]>=rect.top&&point2[1]<=rect.bottom}function getSide(triggerRect,contentRect){const triggerCenterX=triggerRect.left+triggerRect.width/2,triggerCenterY=triggerRect.top+triggerRect.height/2,contentCenterX=contentRect.left+contentRect.width/2,contentCenterY=contentRect.top+contentRect.height/2,deltaX=contentCenterX-triggerCenterX,deltaY=contentCenterY-triggerCenterY;return Math.abs(deltaX)>Math.abs(deltaY)?deltaX>0?"right":"left":deltaY>0? +"bottom":"top"}class SafePolygon{#opts;#buffer;#transitIntentTimeout;#exitPoint=null;#exitTarget=null;#transitTargets=[];#trackedTriggerNode=null;#leaveFallbackRafId=null;#transitIntentTimeoutId=null;#cancelLeaveFallback(){this.#leaveFallbackRafId!==null&&(cancelAnimationFrame(this.#leaveFallbackRafId),this.#leaveFallbackRafId=null)}#scheduleLeaveFallback(){this.#cancelLeaveFallback(),this.#leaveFallbackRafId=requestAnimationFrame(()=>{this.#leaveFallbackRafId=null,!(!this.#exitPoint||!this.#exitTarget)&& +(this.#clearTracking(),this.#opts.onPointerExit())})}#cancelTransitIntentTimeout(){this.#transitIntentTimeoutId!==null&&(clearTimeout(this.#transitIntentTimeoutId),this.#transitIntentTimeoutId=null)}#scheduleTransitIntentTimeout(){this.#transitIntentTimeout!==null&&(this.#cancelTransitIntentTimeout(),this.#transitIntentTimeoutId=window.setTimeout(()=>{this.#transitIntentTimeoutId=null,!(!this.#exitPoint||!this.#exitTarget)&&(this.#clearTracking(),this.#opts.onPointerExit())},this.#transitIntentTimeout))}constructor(opts){ +this.#opts=opts,this.#buffer=opts.buffer??1;const transitIntentTimeout=opts.transitIntentTimeout;this.#transitIntentTimeout=typeof transitIntentTimeout=="number"&&transitIntentTimeout>0?transitIntentTimeout:null,watch$1([opts.triggerNode,opts.contentNode,opts.enabled],([triggerNode,contentNode,enabled])=>{if(!triggerNode||!contentNode||!enabled){this.#trackedTriggerNode=null,this.#clearTracking();return}this.#trackedTriggerNode&&this.#trackedTriggerNode!==triggerNode&&this.#clearTracking(),this.#trackedTriggerNode= +triggerNode;const doc=getDocument$1(triggerNode),handlePointerMove=e=>{this.#onPointerMove([e.clientX,e.clientY],triggerNode,contentNode)},handleTriggerLeave=e=>{const target2=e.relatedTarget;if(isElement$1(target2)&&contentNode.contains(target2))return;const ignoredTargets=this.#opts.ignoredTargets?.()??[];isElement$1(target2)&&ignoredTargets.some(n=>n===target2||n.contains(target2))||(this.#transitTargets=isElement$1(target2)&&ignoredTargets.length>0?ignoredTargets.filter(n=>target2.contains(n)): +[],this.#exitPoint=[e.clientX,e.clientY],this.#exitTarget="content",this.#scheduleLeaveFallback())},handleTriggerEnter=()=>{this.#clearTracking()},handleContentEnter=()=>{this.#clearTracking()},handleContentLeave=e=>{const target2=e.relatedTarget;isElement$1(target2)&&triggerNode.contains(target2)||(this.#exitPoint=[e.clientX,e.clientY],this.#exitTarget="trigger",this.#scheduleLeaveFallback())};return[on(doc,"pointermove",handlePointerMove),on(triggerNode,"pointerleave",handleTriggerLeave),on(triggerNode, +"pointerenter",handleTriggerEnter),on(contentNode,"pointerenter",handleContentEnter),on(contentNode,"pointerleave",handleContentLeave)].reduce((acc,cleanup)=>()=>{acc(),cleanup()},()=>{})})}#onPointerMove(clientPoint,triggerNode,contentNode){if(!this.#exitPoint||!this.#exitTarget)return;this.#cancelLeaveFallback(),this.#scheduleTransitIntentTimeout();const triggerRect=triggerNode.getBoundingClientRect(),contentRect=contentNode.getBoundingClientRect();if(this.#exitTarget==="content"&&isInsideRect( +clientPoint,contentRect)){this.#clearTracking();return}if(this.#exitTarget==="trigger"&&isInsideRect(clientPoint,triggerRect)){this.#clearTracking();return}if(this.#exitTarget==="content"&&this.#transitTargets.length>0)for(const transitTarget of this.#transitTargets){const transitRect=transitTarget.getBoundingClientRect();if(isInsideRect(clientPoint,transitRect))return;const transitSide=getSide(triggerRect,transitRect),transitCorridor=this.#getCorridorPolygon(triggerRect,transitRect,transitSide); +if(transitCorridor&&isPointInPolygon(clientPoint,transitCorridor))return}const side=getSide(triggerRect,contentRect),corridorPoly=this.#getCorridorPolygon(triggerRect,contentRect,side);if(corridorPoly&&isPointInPolygon(clientPoint,corridorPoly))return;const targetRect=this.#exitTarget==="content"?contentRect:triggerRect,safePoly=this.#getSafePolygon(this.#exitPoint,targetRect,side,this.#exitTarget);isPointInPolygon(clientPoint,safePoly)||(this.#clearTracking(),this.#opts.onPointerExit())}#clearTracking(){ +this.#exitPoint=null,this.#exitTarget=null,this.#transitTargets=[],this.#cancelLeaveFallback(),this.#cancelTransitIntentTimeout()}#getCorridorPolygon(triggerRect,contentRect,side){const buffer=this.#buffer;switch(side){case"top":return[[Math.min(triggerRect.left,contentRect.left)-buffer,triggerRect.top],[Math.min(triggerRect.left,contentRect.left)-buffer,contentRect.bottom],[Math.max(triggerRect.right,contentRect.right)+buffer,contentRect.bottom],[Math.max(triggerRect.right,contentRect.right)+buffer, +triggerRect.top]];case"bottom":return[[Math.min(triggerRect.left,contentRect.left)-buffer,triggerRect.bottom],[Math.min(triggerRect.left,contentRect.left)-buffer,contentRect.top],[Math.max(triggerRect.right,contentRect.right)+buffer,contentRect.top],[Math.max(triggerRect.right,contentRect.right)+buffer,triggerRect.bottom]];case"left":return[[triggerRect.left,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.right,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.right,Math. +max(triggerRect.bottom,contentRect.bottom)+buffer],[triggerRect.left,Math.max(triggerRect.bottom,contentRect.bottom)+buffer]];case"right":return[[triggerRect.right,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.left,Math.min(triggerRect.top,contentRect.top)-buffer],[contentRect.left,Math.max(triggerRect.bottom,contentRect.bottom)+buffer],[triggerRect.right,Math.max(triggerRect.bottom,contentRect.bottom)+buffer]]}}#getSafePolygon(exitPoint,targetRect,side,exitTarget){const buffer=this.#buffer* +4,[x,y]=exitPoint;switch(exitTarget==="trigger"?this.#flipSide(side):side){case"top":return[[x-buffer,y+buffer],[x+buffer,y+buffer],[targetRect.right+buffer,targetRect.bottom],[targetRect.right+buffer,targetRect.top],[targetRect.left-buffer,targetRect.top],[targetRect.left-buffer,targetRect.bottom]];case"bottom":return[[x-buffer,y-buffer],[x+buffer,y-buffer],[targetRect.right+buffer,targetRect.top],[targetRect.right+buffer,targetRect.bottom],[targetRect.left-buffer,targetRect.bottom],[targetRect. +left-buffer,targetRect.top]];case"left":return[[x+buffer,y-buffer],[x+buffer,y+buffer],[targetRect.right,targetRect.bottom+buffer],[targetRect.left,targetRect.bottom+buffer],[targetRect.left,targetRect.top-buffer],[targetRect.right,targetRect.top-buffer]];case"right":return[[x-buffer,y-buffer],[x-buffer,y+buffer],[targetRect.left,targetRect.bottom+buffer],[targetRect.right,targetRect.bottom+buffer],[targetRect.right,targetRect.top-buffer],[targetRect.left,targetRect.top-buffer]]}}#flipSide(side){ +switch(side){case"top":return"bottom";case"bottom":return"top";case"left":return"right";case"right":return"left"}}}const popoverAttrs=createBitsAttrs({component:"popover",parts:["root","trigger","content","close","overlay"]}),PopoverRootContext=new Context$1("Popover.Root");class PopoverRootState{static create(opts){return PopoverRootContext.set(new PopoverRootState(opts))}opts;#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode, +value,!0)}contentPresence;#triggerNode=state$1(null);get triggerNode(){return get$3(this.#triggerNode)}set triggerNode(value){set$1(this.#triggerNode,value,!0)}#overlayNode=state$1(null);get overlayNode(){return get$3(this.#overlayNode)}set overlayNode(value){set$1(this.#overlayNode,value,!0)}overlayPresence;#openedViaHover=state$1(!1);get openedViaHover(){return get$3(this.#openedViaHover)}set openedViaHover(value){set$1(this.#openedViaHover,value,!0)}#hasInteractedWithContent=state$1(!1);get hasInteractedWithContent(){ +return get$3(this.#hasInteractedWithContent)}set hasInteractedWithContent(value){set$1(this.#hasInteractedWithContent,value,!0)}#hoverCooldown=state$1(!1);get hoverCooldown(){return get$3(this.#hoverCooldown)}set hoverCooldown(value){set$1(this.#hoverCooldown,value,!0)}#closeDelay=state$1(0);get closeDelay(){return get$3(this.#closeDelay)}set closeDelay(value){set$1(this.#closeDelay,value,!0)}#closeTimeout=null;#domContext=null;constructor(opts){this.opts=opts,this.contentPresence=new PresenceManager( +{ref:boxWith$1(()=>this.contentNode),open:this.opts.open,onComplete:()=>{this.opts.onOpenChangeComplete.current(this.opts.open.current)}}),this.overlayPresence=new PresenceManager({ref:boxWith$1(()=>this.overlayNode),open:this.opts.open}),watch$1(()=>this.opts.open.current,isOpen=>{isOpen||(this.openedViaHover=!1,this.hasInteractedWithContent=!1,this.#clearCloseTimeout())})}setDomContext(ctx){this.#domContext=ctx}#clearCloseTimeout(){this.#closeTimeout!==null&&this.#domContext&&(this.#domContext. +clearTimeout(this.#closeTimeout),this.#closeTimeout=null)}toggleOpen(){this.#clearCloseTimeout(),this.opts.open.current=!this.opts.open.current}handleClose(){this.#clearCloseTimeout(),this.opts.open.current&&(this.opts.open.current=!1)}handleHoverOpen(){this.#clearCloseTimeout(),!this.opts.open.current&&(this.openedViaHover=!0,this.opts.open.current=!0)}handleHoverClose(){this.opts.open.current&&this.openedViaHover&&!this.hasInteractedWithContent&&(this.opts.open.current=!1)}handleDelayedHoverClose(){ +this.opts.open.current&&(!this.openedViaHover||this.hasInteractedWithContent||(this.#clearCloseTimeout(),this.closeDelay<=0?this.opts.open.current=!1:this.#domContext&&(this.#closeTimeout=this.#domContext.setTimeout(()=>{this.openedViaHover&&!this.hasInteractedWithContent&&(this.opts.open.current=!1),this.#closeTimeout=null},this.closeDelay))))}cancelDelayedClose(){this.#clearCloseTimeout()}markInteraction(){this.hasInteractedWithContent=!0,this.#clearCloseTimeout()}}class PopoverTriggerState{static create(opts){ +return new PopoverTriggerState(opts,PopoverRootContext.get())}opts;root;attachment;domContext;#openTimeout=null;#closeTimeout=null;#isHovering=state$1(!1);constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(this.opts.ref,v=>this.root.triggerNode=v),this.domContext=new DOMContext(opts.ref),this.root.setDomContext(this.domContext),this.onclick=this.onclick.bind(this),this.onkeydown=this.onkeydown.bind(this),this.onpointerenter=this.onpointerenter.bind(this),this.onpointerleave= +this.onpointerleave.bind(this),watch$1(()=>this.opts.closeDelay.current,delay=>{this.root.closeDelay=delay})}#clearOpenTimeout(){this.#openTimeout!==null&&(this.domContext.clearTimeout(this.#openTimeout),this.#openTimeout=null)}#clearCloseTimeout(){this.#closeTimeout!==null&&(this.domContext.clearTimeout(this.#closeTimeout),this.#closeTimeout=null)}#clearAllTimeouts(){this.#clearOpenTimeout(),this.#clearCloseTimeout()}onpointerenter(e){if(this.opts.disabled.current||!this.opts.openOnHover.current|| +isTouch(e)||(set$1(this.#isHovering,!0),this.#clearCloseTimeout(),this.root.cancelDelayedClose(),this.root.opts.open.current||this.root.hoverCooldown))return;const delay=this.opts.openDelay.current;delay<=0?this.root.handleHoverOpen():this.#openTimeout=this.domContext.setTimeout(()=>{this.root.handleHoverOpen(),this.#openTimeout=null},delay)}onpointerleave(e){this.opts.disabled.current||this.opts.openOnHover.current&&(isTouch(e)||(set$1(this.#isHovering,!1),this.#clearOpenTimeout(),this.root.hoverCooldown= +!1))}onclick(e){if(!this.opts.disabled.current&&e.button===0){if(this.#clearAllTimeouts(),get$3(this.#isHovering)&&this.root.opts.open.current&&this.root.openedViaHover){this.root.openedViaHover=!1,this.root.hasInteractedWithContent=!0;return}get$3(this.#isHovering)&&this.opts.openOnHover.current&&this.root.opts.open.current&&(this.root.hoverCooldown=!0),this.root.hoverCooldown&&!this.root.opts.open.current&&(this.root.hoverCooldown=!1),this.root.toggleOpen()}}onkeydown(e){this.opts.disabled.current|| +(e.key===ENTER||e.key===SPACE)&&(e.preventDefault(),this.#clearAllTimeouts(),this.root.toggleOpen())}#getAriaControls(){if(this.root.opts.open.current&&this.root.contentNode?.id)return this.root.contentNode?.id}#props=user_derived(()=>({id:this.opts.id.current,"aria-haspopup":"dialog","aria-expanded":boolToStr(this.root.opts.open.current),"data-state":getDataOpenClosed(this.root.opts.open.current),"aria-controls":this.#getAriaControls(),[popoverAttrs.trigger]:"",disabled:this.opts.disabled.current, +onkeydown:this.onkeydown,onclick:this.onclick,onpointerenter:this.onpointerenter,onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}class PopoverContentState{static create(opts){return new PopoverContentState(opts,PopoverRootContext.get())}opts;root;attachment;constructor(opts,root2){this.opts=opts,this.root=root2,this.attachment=attachRef(this.opts.ref,v=>this.root.contentNode=v),this.onpointerdown=this.onpointerdown. +bind(this),this.onfocusin=this.onfocusin.bind(this),this.onpointerenter=this.onpointerenter.bind(this),this.onpointerleave=this.onpointerleave.bind(this),new SafePolygon({triggerNode:()=>this.root.triggerNode,contentNode:()=>this.root.contentNode,enabled:()=>this.root.opts.open.current&&this.root.openedViaHover&&!this.root.hasInteractedWithContent,onPointerExit:()=>{this.root.handleDelayedHoverClose()}})}onpointerdown(_){this.root.markInteraction()}onfocusin(e){const target2=e.target;isElement$1( +target2)&&isTabbable(target2)&&this.root.markInteraction()}onpointerenter(e){isTouch(e)||this.root.cancelDelayedClose()}onpointerleave(e){isTouch(e)}onInteractOutside=e=>{if(this.opts.onInteractOutside.current(e),e.defaultPrevented||!isElement$1(e.target))return;const closestTrigger=e.target.closest(popoverAttrs.selector("trigger"));if(!(closestTrigger&&closestTrigger===this.root.triggerNode)){if(this.opts.customAnchor.current){if(isElement$1(this.opts.customAnchor.current)){if(this.opts.customAnchor. +current.contains(e.target))return}else if(typeof this.opts.customAnchor.current=="string"){const el=document.querySelector(this.opts.customAnchor.current);if(el&&el.contains(e.target))return}}this.root.handleClose()}};onEscapeKeydown=e=>{this.opts.onEscapeKeydown.current(e),!e.defaultPrevented&&this.root.handleClose()};get shouldRender(){return this.root.contentPresence.shouldRender}get shouldTrapFocus(){return!(this.root.openedViaHover&&!this.root.hasInteractedWithContent)}#snippetProps=user_derived( +()=>({open:this.root.opts.open.current}));get snippetProps(){return get$3(this.#snippetProps)}set snippetProps(value){set$1(this.#snippetProps,value)}#props=user_derived(()=>({id:this.opts.id.current,tabindex:-1,"data-state":getDataOpenClosed(this.root.opts.open.current),...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),[popoverAttrs.content]:"",style:{pointerEvents:"auto",contain:"layout style"},onpointerdown:this.onpointerdown,onfocusin:this.onfocusin,onpointerenter:this.onpointerenter, +onpointerleave:this.onpointerleave,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}popperProps={onInteractOutside:this.onInteractOutside,onEscapeKeydown:this.onEscapeKeydown}}var root_4$J=from_html("
"),root_8$x=from_html("
");function Popover_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let ref2=prop($$props,"ref",15,null),id2=prop($$props,"id",19,()=>createId(uid2)),forceMount=prop( +$$props,"forceMount",3,!1),onOpenAutoFocus=prop($$props,"onOpenAutoFocus",3,noop$1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),trapFocus=prop($$props,"trapFocus",3,!0),preventScroll=prop($$props,"preventScroll",3,!1),customAnchor=prop($$props,"customAnchor",3,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","child","children","ref","id","forceMo\ +unt","onOpenAutoFocus","onCloseAutoFocus","onEscapeKeydown","onInteractOutside","trapFocus","preventScroll","customAnchor","style"]);const contentState=PopoverContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),onInteractOutside:boxWith$1(()=>onInteractOutside()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown()),customAnchor:boxWith$1(()=>customAnchor())}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props)),effectiveTrapFocus=user_derived(()=>trapFocus()&& +contentState.shouldTrapFocus);function handleOpenAutoFocus(e){contentState.shouldTrapFocus||e.preventDefault(),onOpenAutoFocus()(e)}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},get id(){return id2()},get trapFocus(){return get$3(effectiveTrapFocus)},get preventScroll(){ +return preventScroll()},loop:!0,forceMount:!0,get customAnchor(){return customAnchor()},onOpenAutoFocus:handleOpenAutoFocus,get onCloseAutoFocus(){return onCloseAutoFocus()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("popover")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2); +{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$J();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3), +reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get open(){return contentState.root.opts.open.current},get id(){return id2()},get trapFocus(){return get$3(effectiveTrapFocus)},get preventScroll(){return preventScroll()}, +loop:!0,forceMount:!1,get customAnchor(){return customAnchor()},onOpenAutoFocus:handleOpenAutoFocus,get onCloseAutoFocus(){return onCloseAutoFocus()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("popover")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{ +var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$x();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3), +reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$11=from_html("");function Popover_trigger$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)), +ref2=prop($$props,"ref",15,null),type2=prop($$props,"type",3,"button"),disabled=prop($$props,"disabled",3,!1),openOnHover=prop($$props,"openOnHover",3,!1),openDelay=prop($$props,"openDelay",3,700),closeDelay=prop($$props,"closeDelay",3,300),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","type","disabled","openOnHover","openDelay","closeDelay"]);const triggerState=PopoverTriggerState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)), +disabled:boxWith$1(()=>!!disabled()),openOnHover:boxWith$1(()=>openOnHover()),openDelay:boxWith$1(()=>openDelay()),closeDelay:boxWith$1(()=>closeDelay())}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));Floating_layer_anchor($$anchor,{get id(){return id2()},get ref(){return triggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var fragment_2=comment$2(),node_1=first_child( +fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var button=root_3$11();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor3,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}function Dialog($$anchor,$$props){push$1( +$$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1);DialogRootState.create({variant:boxWith$1(()=>"dialog"),open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}var root_2$1p=from_html("");function Dialog_close($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","disabled"]);const closeState=DialogCloseState.create({variant:boxWith$1(()=>"close"),id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)),disabled:boxWith$1(()=>!!disabled())}), +mergedProps=user_derived(()=>mergeProps(restProps,closeState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1p();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor2, +button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_6$x=from_html(" ",1),root_8$w=from_html("
",1);function Dialog_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),forceMount=prop($$props,"forceMount",3,!1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),onOpenAutoFocus=prop($$props, +"onOpenAutoFocus",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),trapFocus=prop($$props,"trapFocus",3,!0),preventScroll=prop($$props,"preventScroll",3,!0),restoreScrollDelay=prop($$props,"restoreScrollDelay",3,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","children","child","ref","forceMount","onCloseAutoFocus","onOpenAutoFocus","onEscapeKeydown","onInteractOutside","trapFocus","preventScr\ +oll","restoreScrollDelay"]);const contentState=DialogContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_2=$$anchor2=>{Focus_scope($$anchor2,{get ref(){return contentState.opts.ref},loop:!0,get trapFocus(){return trapFocus()},get enabled(){return contentState.root.opts.open.current},get onOpenAutoFocus(){return onOpenAutoFocus()}, +get onCloseAutoFocus(){return onCloseAutoFocus()},focusScope:($$anchor3,$$arg0)=>{let focusScopeProps=()=>$$arg0?.().props;Escape_layer($$anchor3,spread_props(()=>get$3(mergedProps),{get enabled(){return contentState.root.opts.open.current},get ref(){return contentState.opts.ref},onEscapeKeydown:e=>{onEscapeKeydown()(e),!e.defaultPrevented&&contentState.root.handleClose()},children:($$anchor4,$$slotProps)=>{Dismissible_layer($$anchor4,spread_props(()=>get$3(mergedProps),{get ref(){return contentState. +opts.ref},get enabled(){return contentState.root.opts.open.current},onInteractOutside:e=>{onInteractOutside()(e),!e.defaultPrevented&&contentState.root.handleClose()},children:($$anchor5,$$slotProps2)=>{Text_selection_layer($$anchor5,spread_props(()=>get$3(mergedProps),{get ref(){return contentState.opts.ref},get enabled(){return contentState.root.opts.open.current},children:($$anchor6,$$slotProps3)=>{var fragment_5=comment$2(),node_1=first_child(fragment_5);{var consequent_1=$$anchor7=>{var fragment_6=root_6$x(), +node_2=first_child(fragment_6);{var consequent=$$anchor8=>{Scroll_lock($$anchor8,{get preventScroll(){return preventScroll()},get restoreScrollDelay(){return restoreScrollDelay()}})};if_block(node_2,$$render=>{contentState.root.opts.open.current&&$$render(consequent)})}var node_3=sibling(node_2,2);{let $0=user_derived(()=>({props:mergeProps(get$3(mergedProps),focusScopeProps()),...contentState.snippetProps}));snippet(node_3,()=>$$props.child,()=>get$3($0))}append($$anchor7,fragment_6)},alternate=$$anchor7=>{ +var fragment_8=root_8$w(),node_4=first_child(fragment_8);Scroll_lock(node_4,{get preventScroll(){return preventScroll()}});var div=sibling(node_4,2);attribute_effect(div,$0=>({...$0}),[()=>mergeProps(get$3(mergedProps),focusScopeProps())]);var node_5=child(div);snippet(node_5,()=>$$props.children??noop$3),reset(div),append($$anchor7,fragment_8)};if_block(node_1,$$render=>{$$props.child?$$render(consequent_1):$$render(alternate,-1)})}append($$anchor6,fragment_5)},$$slots:{default:!0}}))},$$slots:{ +default:!0}}))},$$slots:{default:!0}}))},$$slots:{focusScope:!0}})};if_block(node2,$$render=>{(contentState.shouldRender||forceMount())&&$$render(consequent_2)})}append($$anchor,fragment),pop()}function Menu($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),dir=prop($$props,"dir",3,"ltr"),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComplete",3,noop$1),variant=prop($$props,"_internal_variant",3,"dropdown-menu"),shouldSkipExitAnimation=prop( +$$props,"_internal_should_skip_exit_animation",3,void 0);const root2=MenuRootState.create({variant:boxWith$1(()=>variant()),dir:boxWith$1(()=>dir()),onClose:()=>{open2(!1),onOpenChange()(!1)},shouldSkipExitAnimation:()=>shouldSkipExitAnimation()?.()??!1});MenuMenuState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())},root2),Floating_layer($$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}var root_4$I=from_html("
"),root_8$v=from_html("
");function Dropdown_menu_content$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),loop2=prop($$props,"loop",3,!0),onInteractOutside=prop($$props,"onInteractOutside",3,noop$1),onEscapeKeydown=prop( +$$props,"onEscapeKeydown",3,noop$1),onCloseAutoFocus=prop($$props,"onCloseAutoFocus",3,noop$1),forceMount=prop($$props,"forceMount",3,!1),trapFocus=prop($$props,"trapFocus",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","child","children","ref","loop","onInteractOutside","onEscapeKeydown","onCloseAutoFocus","forceMount","trapFocus","style"]);const contentState=MenuContentState.create({id:boxWith$1(()=>id2()),loop:boxWith$1(()=>loop2()),ref:boxWith$1(()=>ref2(),v=>ref2(v)), +onCloseAutoFocus:boxWith$1(()=>onCloseAutoFocus())}),mergedProps=user_derived(()=>mergeProps(restProps,contentState.props));function handleInteractOutside(e){if(contentState.handleInteractOutside(e),!e.defaultPrevented&&(onInteractOutside()(e),!e.defaultPrevented)){if(e.target&&e.target instanceof Element){const subContentSelector=`[${contentState.parentMenu.root.getBitsAttr("sub-content")}]`;if(e.target.closest(subContentSelector))return}contentState.parentMenu.onClose()}}function handleEscapeKeydown(e){ +onEscapeKeydown()(e),!e.defaultPrevented&&contentState.parentMenu.onClose()}var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get enabled(){return contentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,get trapFocus(){return trapFocus()},get loop(){return loop2()}, +forceMount:!0,get id(){return id2()},get shouldRender(){return contentState.shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("dropdown-menu")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $0=user_derived(()=>({props:get$3( +finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$I();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render( +alternate,-1)})}append($$anchor3,fragment_2)},$$slots:{popper:!0}}))},consequent_3=$$anchor2=>{Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get ref(){return contentState.opts.ref},get open(){return contentState.parentMenu.opts.open.current},onInteractOutside:handleInteractOutside,onEscapeKeydown:handleEscapeKeydown,get trapFocus(){return trapFocus()},get loop(){return loop2()},forceMount:!1,get id(){return id2()},get shouldRender(){return contentState. +shouldRender},popper:($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("dropdown-menu")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $0=user_derived(()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet( +node_5,()=>$$props.child,()=>get$3($0))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$v();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)},$$slots:{popper:!0}}))}; +if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_3$10=from_html("");function Menu_trigger($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),disabled=prop($$props,"disabled",3,!1),type2=prop($$props,"type",3,"button"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","id","ref","chi\ +ld","children","disabled","type"]);const triggerState=DropdownMenuTriggerState.create({id:boxWith$1(()=>id2()),disabled:boxWith$1(()=>disabled()??!1),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));Floating_layer_anchor($$anchor,{get id(){return id2()},get ref(){return triggerState.opts.ref},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var fragment_2=comment$2(), +node_1=first_child(fragment_2);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor3,fragment_2)},alternate=$$anchor3=>{var button=root_3$10();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor3,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}const labelAttrs=createBitsAttrs( +{component:"label",parts:["root"]});class LabelRootState{static create(opts){return new LabelRootState(opts)}opts;attachment;constructor(opts){this.opts=opts,this.attachment=attachRef(this.opts.ref),this.onmousedown=this.onmousedown.bind(this)}onmousedown(e){e.detail>1&&e.preventDefault()}#props=user_derived(()=>({id:this.opts.id.current,[labelAttrs.root]:"",onmousedown:this.onmousedown,...this.attachment}));get props(){return get$3(this.#props)}set props(value){set$1(this.#props,value)}}var root_2$1o=from_html( +"");function Label$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","for"]);const rootState=LabelRootState.create({id:boxWith$1(()=>id2()),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,rootState.props,{for:$$props.for}));var fragment=comment$2(),node2=first_child( +fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var label=root_2$1o();attribute_effect(label,()=>({...get$3(mergedProps),for:$$props.for}));var node_2=child(label);snippet(node_2,()=>$$props.children??noop$3),reset(label),append($$anchor2,label)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})} +append($$anchor,fragment),pop()}class SvelteResizeObserver{#node;#onResize;constructor(node2,onResize){this.#node=node2,this.#onResize=onResize,this.handler=this.handler.bind(this),user_effect(this.handler)}handler(){let rAF=0;const _node=this.#node();if(!_node)return;const resizeObserver=new ResizeObserver(()=>{cancelAnimationFrame(rAF),rAF=window.requestAnimationFrame(this.#onResize)});return resizeObserver.observe(_node),()=>{window.cancelAnimationFrame(rAF),resizeObserver.unobserve(_node)}}} +class Presence{opts;present;#afterAnimations;#isPresent=state$1(!1);#hasMounted=!1;#transitionStatus=state$1(void 0);#transitionFrame=null;constructor(opts){this.opts=opts,this.present=this.opts.open,set$1(this.#isPresent,opts.open.current,!0),this.#afterAnimations=new AnimationsComplete({ref:this.opts.ref,afterTick:this.opts.open}),onDestroyEffect(()=>this.#clearTransitionFrame()),watch$1(()=>this.present.current,isOpen=>{if(!this.#hasMounted){this.#hasMounted=!0;return}this.#clearTransitionFrame(), +isOpen&&set$1(this.#isPresent,!0),set$1(this.#transitionStatus,isOpen?"starting":"ending",!0),isOpen&&(this.#transitionFrame=window.requestAnimationFrame(()=>{this.#transitionFrame=null,this.present.current&&set$1(this.#transitionStatus,void 0)})),this.#afterAnimations.run(()=>{isOpen===this.present.current&&(isOpen||set$1(this.#isPresent,!1),set$1(this.#transitionStatus,void 0))})})}#_isPresent=user_derived(()=>get$3(this.#isPresent));get isPresent(){return get$3(this.#_isPresent)}set isPresent(value){ +set$1(this.#_isPresent,value)}get transitionStatus(){return get$3(this.#transitionStatus)}#clearTransitionFrame(){this.#transitionFrame!==null&&(window.cancelAnimationFrame(this.#transitionFrame),this.#transitionFrame=null)}}function Presence_layer($$anchor,$$props){push$1($$props,!0);const presenceState=new Presence({open:boxWith$1(()=>$$props.open),ref:$$props.ref});var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1); +snippet(node_1,()=>$$props.presence??noop$3,()=>({present:presenceState.isPresent,transitionStatus:presenceState.transitionStatus})),append($$anchor2,fragment_1)};if_block(node2,$$render=>{($$props.forceMount||$$props.open||presenceState.isPresent)&&$$render(consequent)})}append($$anchor,fragment),pop()}function Popover$1($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),onOpenChange=prop($$props,"onOpenChange",3,noop$1),onOpenChangeComplete=prop($$props,"onOpenChangeComp\ +lete",3,noop$1);PopoverRootState.create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),onOpenChangeComplete:boxWith$1(()=>onOpenChangeComplete())}),Floating_layer($$anchor,{children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}}),pop()}function clamp(n,min2,max2){return Math.min(max2,Math.max(min2,n))}class StateMachine{state;#machine;constructor(initialState,machine){ this.state=simpleBox(initialState),this.#machine=machine,this.dispatch=this.dispatch.bind(this)}#reducer(event2){return this.#machine[this.state.current][event2]??this.state.current}dispatch(event2){this.state.current=this.#reducer(event2)}}const scrollAreaAttrs=createBitsAttrs({component:"scroll-area",parts:["root","viewport","corner","thumb","scrollbar"]}),ScrollAreaRootContext=new Context$1("ScrollArea.Root"),ScrollAreaScrollbarContext=new Context$1("ScrollArea.Scrollbar"),ScrollAreaScrollbarVisibleContext=new Context$1( "ScrollArea.ScrollbarVisible"),ScrollAreaScrollbarAxisContext=new Context$1("ScrollArea.ScrollbarAxis"),ScrollAreaScrollbarSharedContext=new Context$1("ScrollArea.ScrollbarShared");class ScrollAreaRootState{static create(opts){return ScrollAreaRootContext.set(new ScrollAreaRootState(opts))}opts;attachment;#scrollAreaNode=state$1(null);get scrollAreaNode(){return get$3(this.#scrollAreaNode)}set scrollAreaNode(value){set$1(this.#scrollAreaNode,value,!0)}#viewportNode=state$1(null);get viewportNode(){ return get$3(this.#viewportNode)}set viewportNode(value){set$1(this.#viewportNode,value,!0)}#contentNode=state$1(null);get contentNode(){return get$3(this.#contentNode)}set contentNode(value){set$1(this.#contentNode,value,!0)}#scrollbarXNode=state$1(null);get scrollbarXNode(){return get$3(this.#scrollbarXNode)}set scrollbarXNode(value){set$1(this.#scrollbarXNode,value,!0)}#scrollbarYNode=state$1(null);get scrollbarYNode(){return get$3(this.#scrollbarYNode)}set scrollbarYNode(value){set$1(this.#scrollbarYNode, @@ -1134,160 +1136,160 @@ create({open:boxWith$1(()=>open2(),v=>{open2(v),onOpenChange()(v)}),triggerId:bo const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),ref2=prop($$props,"ref",15,null),side=prop($$props,"side",3,"top"),sideOffset=prop($$props,"sideOffset",3,0),align=prop($$props,"align",3,"center"),avoidCollisions=prop($$props,"avoidCollisions",3,!0),arrowPadding=prop($$props,"arrowPadding",3,0),sticky=prop($$props,"sticky",3,"partial"),hideWhenDetached=prop($$props,"hideWhenDetached",3,!1),collisionPadding=prop($$props,"collisionPadding",3,0),onInteractOutside=prop( $$props,"onInteractOutside",3,noop$1),onEscapeKeydown=prop($$props,"onEscapeKeydown",3,noop$1),forceMount=prop($$props,"forceMount",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","ref","side","sideOffset","align","avoidCollisions","arrowPadding","sticky","strategy","hideWhenDetached","customAnchor","collisionPadding","onInteractOutside","onEscapeKeydown","forceMount","style"]);const contentState=TooltipContentState.create({id:boxWith$1(()=>id2()),ref:boxWith$1( ()=>ref2(),v=>ref2(v)),onInteractOutside:boxWith$1(()=>onInteractOutside()),onEscapeKeydown:boxWith$1(()=>onEscapeKeydown())}),floatingProps=user_derived(()=>({side:side(),sideOffset:sideOffset(),align:align(),avoidCollisions:avoidCollisions(),arrowPadding:arrowPadding(),sticky:sticky(),hideWhenDetached:hideWhenDetached(),collisionPadding:collisionPadding(),strategy:$$props.strategy,customAnchor:$$props.customAnchor??contentState.root.triggerNode})),mergedProps=user_derived(()=>mergeProps(restProps, -get$3(floatingProps),contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{{const popper=($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("tooltip")},{style:$$props.style}));var fragment_2=comment$2(),node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $02=user_derived( -()=>({props:get$3(finalProps),wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($02))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$H();attribute_effect(div,()=>({...wrapperProps()}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child(div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent): -$$render(alternate,-1)})}append($$anchor3,fragment_2)};let $0=user_derived(()=>contentState.root.disableHoverableContent?"none":"auto");Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get enabled(){return contentState.root.opts.open.current},get id(){return id2()},trapFocus:!1,loop:!1,preventScroll:!1,forceMount:!0,get ref(){return contentState.opts.ref},tooltip:!0,get shouldRender(){return contentState.shouldRender},get contentPointerEvents(){ -return get$3($0)},popper,$$slots:{popper:!0}}))}},consequent_3=$$anchor2=>{{const popper=($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("tooltip")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $02=user_derived(()=>({props:get$3(finalProps), -wrapperProps:wrapperProps(),...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($02))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$u();attribute_effect(div_2,()=>({...wrapperProps()}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render( -alternate_1,-1)})}append($$anchor3,fragment_5)};let $0=user_derived(()=>contentState.root.disableHoverableContent?"none":"auto");Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get open(){return contentState.root.opts.open.current},get id(){return id2()},trapFocus:!1,loop:!1,preventScroll:!1,forceMount:!1,get ref(){return contentState.opts.ref},tooltip:!0,get shouldRender(){return contentState.shouldRender},get contentPointerEvents(){return get$3($0)},popper, -$$slots:{popper:!0}}))}};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_2$1h=from_html("");function Tooltip_trigger$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),type2=prop($$props,"type",3,"button"),tabindex=prop($$props,"tabindex",3,0),ref2=prop($$props,"ref",15,null),restProps=rest_props( -$$props,["$$slots","$$events","$$legacy","children","child","id","disabled","payload","tether","type","tabindex","ref"]);const triggerState=TooltipTriggerState.create({id:boxWith$1(()=>id2()),disabled:boxWith$1(()=>disabled()??!1),tabindex:boxWith$1(()=>tabindex()??0),payload:boxWith$1(()=>$$props.payload),tether:boxWith$1(()=>$$props.tether),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));var fragment=comment$2(),node2=first_child( -fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1h();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor, -fragment),pop()}function Tooltip_arrow($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);Floating_layer_arrow($$anchor,spread_props(()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}})),pop()}function Tooltip_provider($$anchor,$$props){push$1($$props,!0);let delayDuration=prop($$props,"delayDuration",3,700),disableCloseOnTriggerClick=prop($$props,"disableCloseOnTriggerClick",3,!1),disableHoverableContent=prop( -$$props,"disableHoverableContent",3,!1),disabled=prop($$props,"disabled",3,!1),ignoreNonKeyboardFocus=prop($$props,"ignoreNonKeyboardFocus",3,!1),skipDelayDuration=prop($$props,"skipDelayDuration",3,300);TooltipProviderState.create({delayDuration:boxWith$1(()=>delayDuration()),disableCloseOnTriggerClick:boxWith$1(()=>disableCloseOnTriggerClick()),disableHoverableContent:boxWith$1(()=>disableHoverableContent()),disabled:boxWith$1(()=>disabled()),ignoreNonKeyboardFocus:boxWith$1(()=>ignoreNonKeyboardFocus()), -skipDelayDuration:boxWith$1(()=>skipDelayDuration())});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}function Tooltip_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Tooltip_trigger$1,($$anchor2,TooltipPrimitive_Trigger)=>{TooltipPrimitive_Trigger( -$$anchor2,spread_props({"data-slot":"tooltip-trigger",class:"cursor-pointer"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}var root_3$_=from_html("
"),root_2$1g=from_html(" ",1);function Tooltip_content($$anchor,$$props){push$1($$props,!0);const tooltipContent=$$anchor2=>{var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Tooltip_content$1,($$anchor3,TooltipPrimitive_Content)=>{TooltipPrimitive_Content( -$$anchor3,spread_props({"data-slot":"tooltip-content",get sideOffset(){return sideOffset()},get side(){return side()},get class(){return get$3(contentClass)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor4,$$slotProps)=>{var fragment_1=root_2$1g(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);{const child2=($$anchor5,$$arg0)=>{let props=()=>$$arg0?.().props;var div=root_3$_();attribute_effect( -div,$0=>({class:$0,...props()}),[()=>cn$1("z-50 size-2.5 rotate-45 rounded-[2px] bg-primary","data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]","data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]","data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2","data-[side=left]:-translate-y-[calc(50%_-_3px)]",$$props.arrowClasses)]),append($$anchor5,div)};component(node_2,()=>Tooltip_arrow,($$anchor5,TooltipPrimitive_Arrow)=>{ -TooltipPrimitive_Arrow($$anchor5,{child:child2,$$slots:{child:!0}})})}append($$anchor4,fragment_1)},$$slots:{default:!0}}))}),append($$anchor2,fragment)};let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,0),side=prop($$props,"side",3,"top"),noPortal=prop($$props,"noPortal",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","sideOffset","side","children","arrowClasses","noPortal"]);const contentClass=user_derived(()=>cn$1("z-50 w-fit origin\ --(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:fill-mode-forwards",$$props.class));var fragment_2=comment$2(),node_3=first_child( -fragment_2);{var consequent=$$anchor2=>{tooltipContent($$anchor2)},alternate=$$anchor2=>{var fragment_4=comment$2(),node_4=first_child(fragment_4);component(node_4,()=>Portal$2,($$anchor3,TooltipPrimitive_Portal)=>{TooltipPrimitive_Portal($$anchor3,{children:($$anchor4,$$slotProps)=>{tooltipContent($$anchor4)},$$slots:{default:!0}})}),append($$anchor2,fragment_4)};if_block(node_3,$$render=>{noPortal()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2),pop()}const Root$5=Tooltip, -Provider=Tooltip_provider;var AttachmentType=(AttachmentType2=>(AttachmentType2.AUDIO="AUDIO",AttachmentType2.IMAGE="IMAGE",AttachmentType2.MCP_PROMPT="MCP_PROMPT",AttachmentType2.MCP_RESOURCE="MCP_RESOURCE",AttachmentType2.PDF="PDF",AttachmentType2.TEXT="TEXT",AttachmentType2.LEGACY_CONTEXT="context",AttachmentType2))(AttachmentType||{}),AttachmentMenuItemId=(AttachmentMenuItemId2=>(AttachmentMenuItemId2.IMAGES="images",AttachmentMenuItemId2.AUDIO="audio",AttachmentMenuItemId2.TEXT="text",AttachmentMenuItemId2. -PDF="pdf",AttachmentMenuItemId2.SYSTEM_MESSAGE="system-message",AttachmentMenuItemId2.MCP_PROMPT="mcp-prompt",AttachmentMenuItemId2.MCP_RESOURCES="mcp-resources",AttachmentMenuItemId2))(AttachmentMenuItemId||{}),AttachmentItemEnabledWhen=(AttachmentItemEnabledWhen2=>(AttachmentItemEnabledWhen2.ALWAYS="always",AttachmentItemEnabledWhen2.HAS_VISION_MODALITY="hasVisionModality",AttachmentItemEnabledWhen2.HAS_AUDIO_MODALITY="hasAudioModality",AttachmentItemEnabledWhen2))(AttachmentItemEnabledWhen||{}), -AttachmentAction=(AttachmentAction2=>(AttachmentAction2.FILE_UPLOAD="onFileUpload",AttachmentAction2.SYSTEM_PROMPT_CLICK="onSystemPromptClick",AttachmentAction2.MCP_PROMPT_CLICK="onMcpPromptClick",AttachmentAction2.MCP_RESOURCES_CLICK="onMcpResourcesClick",AttachmentAction2))(AttachmentAction||{}),AttachmentItemVisibleWhen=(AttachmentItemVisibleWhen2=>(AttachmentItemVisibleWhen2.HAS_MCP_PROMPTS_SUPPORT="hasMcpPromptsSupport",AttachmentItemVisibleWhen2.HAS_MCP_RESOURCES_SUPPORT="hasMcpResourcesSu\ -pport",AttachmentItemVisibleWhen2))(AttachmentItemVisibleWhen||{}),ToolCallType=(ToolCallType2=>(ToolCallType2.FUNCTION="function",ToolCallType2))(ToolCallType||{}),AgenticSectionType=(AgenticSectionType2=>(AgenticSectionType2.TEXT="text",AgenticSectionType2.TOOL_CALL="tool_call",AgenticSectionType2.TOOL_CALL_PENDING="tool_call_pending",AgenticSectionType2.TOOL_CALL_STREAMING="tool_call_streaming",AgenticSectionType2.REASONING="reasoning",AgenticSectionType2.REASONING_PENDING="reasoning_pending", -AgenticSectionType2))(AgenticSectionType||{}),ChatMessageStatsView=(ChatMessageStatsView2=>(ChatMessageStatsView2.GENERATION="generation",ChatMessageStatsView2.READING="reading",ChatMessageStatsView2.TOOLS="tools",ChatMessageStatsView2.SUMMARY="summary",ChatMessageStatsView2))(ChatMessageStatsView||{}),ReasoningFormat=(ReasoningFormat2=>(ReasoningFormat2.NONE="none",ReasoningFormat2.AUTO="auto",ReasoningFormat2))(ReasoningFormat||{}),MessageRole=(MessageRole2=>(MessageRole2.USER="user",MessageRole2. -ASSISTANT="assistant",MessageRole2.SYSTEM="system",MessageRole2.TOOL="tool",MessageRole2))(MessageRole||{}),MessageType=(MessageType2=>(MessageType2.ROOT="root",MessageType2.TEXT="text",MessageType2.THINK="think",MessageType2.SYSTEM="system",MessageType2))(MessageType||{}),ContentPartType=(ContentPartType2=>(ContentPartType2.TEXT="text",ContentPartType2.IMAGE_URL="image_url",ContentPartType2.INPUT_AUDIO="input_audio",ContentPartType2))(ContentPartType||{}),ErrorDialogType=(ErrorDialogType2=>(ErrorDialogType2. -TIMEOUT="timeout",ErrorDialogType2.SERVER="server",ErrorDialogType2))(ErrorDialogType||{}),ConversationSelectionMode=(ConversationSelectionMode2=>(ConversationSelectionMode2.EXPORT="export",ConversationSelectionMode2.IMPORT="import",ConversationSelectionMode2))(ConversationSelectionMode||{}),PdfViewMode=(PdfViewMode2=>(PdfViewMode2.TEXT="text",PdfViewMode2.PAGES="pages",PdfViewMode2))(PdfViewMode||{}),FileTypeCategory=(FileTypeCategory2=>(FileTypeCategory2.IMAGE="image",FileTypeCategory2.AUDIO="\ -audio",FileTypeCategory2.PDF="pdf",FileTypeCategory2.TEXT="text",FileTypeCategory2))(FileTypeCategory||{}),SpecialFileType=(SpecialFileType2=>(SpecialFileType2.MCP_PROMPT="mcp-prompt",SpecialFileType2))(SpecialFileType||{}),FileTypeImage=(FileTypeImage2=>(FileTypeImage2.JPEG="jpeg",FileTypeImage2.PNG="png",FileTypeImage2.GIF="gif",FileTypeImage2.WEBP="webp",FileTypeImage2.SVG="svg",FileTypeImage2))(FileTypeImage||{}),FileTypeAudio=(FileTypeAudio2=>(FileTypeAudio2.MP3="mp3",FileTypeAudio2.WAV="wa\ -v",FileTypeAudio2.WEBM="webm",FileTypeAudio2))(FileTypeAudio||{}),FileTypePdf=(FileTypePdf2=>(FileTypePdf2.PDF="pdf",FileTypePdf2))(FileTypePdf||{}),FileTypeText=(FileTypeText2=>(FileTypeText2.PLAIN_TEXT="plainText",FileTypeText2.MARKDOWN="md",FileTypeText2.ASCIIDOC="asciidoc",FileTypeText2.JAVASCRIPT="js",FileTypeText2.TYPESCRIPT="ts",FileTypeText2.JSX="jsx",FileTypeText2.TSX="tsx",FileTypeText2.CSS="css",FileTypeText2.HTML="html",FileTypeText2.JSON="json",FileTypeText2.XML="xml",FileTypeText2. -YAML="yaml",FileTypeText2.CSV="csv",FileTypeText2.LOG="log",FileTypeText2.PYTHON="python",FileTypeText2.JAVA="java",FileTypeText2.CPP="cpp",FileTypeText2.PHP="php",FileTypeText2.RUBY="ruby",FileTypeText2.GO="go",FileTypeText2.RUST="rust",FileTypeText2.SHELL="shell",FileTypeText2.SQL="sql",FileTypeText2.R="r",FileTypeText2.SCALA="scala",FileTypeText2.KOTLIN="kotlin",FileTypeText2.SWIFT="swift",FileTypeText2.DART="dart",FileTypeText2.VUE="vue",FileTypeText2.SVELTE="svelte",FileTypeText2.LATEX="lat\ -ex",FileTypeText2.BIBTEX="bibtex",FileTypeText2.CUDA="cuda",FileTypeText2.VULKAN="vulkan",FileTypeText2.HASKELL="haskell",FileTypeText2.CSHARP="csharp",FileTypeText2.PROPERTIES="properties",FileTypeText2))(FileTypeText||{}),FileExtensionImage=(FileExtensionImage2=>(FileExtensionImage2.JPG=".jpg",FileExtensionImage2.JPEG=".jpeg",FileExtensionImage2.PNG=".png",FileExtensionImage2.GIF=".gif",FileExtensionImage2.WEBP=".webp",FileExtensionImage2.SVG=".svg",FileExtensionImage2))(FileExtensionImage||{}), -FileExtensionAudio=(FileExtensionAudio2=>(FileExtensionAudio2.MP3=".mp3",FileExtensionAudio2.WAV=".wav",FileExtensionAudio2))(FileExtensionAudio||{}),FileExtensionPdf=(FileExtensionPdf2=>(FileExtensionPdf2.PDF=".pdf",FileExtensionPdf2))(FileExtensionPdf||{}),FileExtensionText=(FileExtensionText2=>(FileExtensionText2.TXT=".txt",FileExtensionText2.MD=".md",FileExtensionText2.ADOC=".adoc",FileExtensionText2.JS=".js",FileExtensionText2.TS=".ts",FileExtensionText2.JSX=".jsx",FileExtensionText2.TSX=".\ -tsx",FileExtensionText2.CSS=".css",FileExtensionText2.HTML=".html",FileExtensionText2.HTM=".htm",FileExtensionText2.JSON=".json",FileExtensionText2.XML=".xml",FileExtensionText2.YAML=".yaml",FileExtensionText2.YML=".yml",FileExtensionText2.CSV=".csv",FileExtensionText2.LOG=".log",FileExtensionText2.PY=".py",FileExtensionText2.JAVA=".java",FileExtensionText2.CPP=".cpp",FileExtensionText2.C=".c",FileExtensionText2.H=".h",FileExtensionText2.PHP=".php",FileExtensionText2.RB=".rb",FileExtensionText2. -GO=".go",FileExtensionText2.RS=".rs",FileExtensionText2.SH=".sh",FileExtensionText2.BAT=".bat",FileExtensionText2.SQL=".sql",FileExtensionText2.R=".r",FileExtensionText2.SCALA=".scala",FileExtensionText2.KT=".kt",FileExtensionText2.SWIFT=".swift",FileExtensionText2.DART=".dart",FileExtensionText2.VUE=".vue",FileExtensionText2.SVELTE=".svelte",FileExtensionText2.TEX=".tex",FileExtensionText2.BIB=".bib",FileExtensionText2.CU=".cu",FileExtensionText2.CUH=".cuh",FileExtensionText2.COMP=".comp",FileExtensionText2. -HPP=".hpp",FileExtensionText2.HS=".hs",FileExtensionText2.PROPERTIES=".properties",FileExtensionText2.CS=".cs",FileExtensionText2))(FileExtensionText||{}),MimeTypePrefix=(MimeTypePrefix2=>(MimeTypePrefix2.IMAGE="image/",MimeTypePrefix2.TEXT="text",MimeTypePrefix2))(MimeTypePrefix||{}),MimeTypeIncludes=(MimeTypeIncludes2=>(MimeTypeIncludes2.JSON="json",MimeTypeIncludes2.JAVASCRIPT="javascript",MimeTypeIncludes2.TYPESCRIPT="typescript",MimeTypeIncludes2))(MimeTypeIncludes||{}),UriPattern=(UriPattern2=>(UriPattern2. -DATABASE_KEYWORD="database",UriPattern2.DATABASE_SCHEME="db://",UriPattern2))(UriPattern||{}),MimeTypeApplication=(MimeTypeApplication2=>(MimeTypeApplication2.PDF="application/pdf",MimeTypeApplication2.OCTET_STREAM="application/octet-stream",MimeTypeApplication2))(MimeTypeApplication||{}),MimeTypeAudio=(MimeTypeAudio2=>(MimeTypeAudio2.MP3_MPEG="audio/mpeg",MimeTypeAudio2.MP3="audio/mp3",MimeTypeAudio2.MP4="audio/mp4",MimeTypeAudio2.WAV="audio/wav",MimeTypeAudio2.WEBM="audio/webm",MimeTypeAudio2. -WEBM_OPUS="audio/webm;codecs=opus",MimeTypeAudio2))(MimeTypeAudio||{}),MimeTypeImage=(MimeTypeImage2=>(MimeTypeImage2.JPEG="image/jpeg",MimeTypeImage2.JPG="image/jpg",MimeTypeImage2.PNG="image/png",MimeTypeImage2.GIF="image/gif",MimeTypeImage2.WEBP="image/webp",MimeTypeImage2.SVG="image/svg+xml",MimeTypeImage2.ICO="image/x-icon",MimeTypeImage2.ICO_MICROSOFT="image/vnd.microsoft.icon",MimeTypeImage2))(MimeTypeImage||{}),MimeTypeText=(MimeTypeText2=>(MimeTypeText2.PLAIN="text/plain",MimeTypeText2. -MARKDOWN="text/markdown",MimeTypeText2.ASCIIDOC="text/asciidoc",MimeTypeText2.JAVASCRIPT="text/javascript",MimeTypeText2.JAVASCRIPT_APP="application/javascript",MimeTypeText2.TYPESCRIPT="text/typescript",MimeTypeText2.JSX="text/jsx",MimeTypeText2.TSX="text/tsx",MimeTypeText2.CSS="text/css",MimeTypeText2.HTML="text/html",MimeTypeText2.JSON="application/json",MimeTypeText2.XML_TEXT="text/xml",MimeTypeText2.XML_APP="application/xml",MimeTypeText2.YAML_TEXT="text/yaml",MimeTypeText2.YAML_APP="applic\ -ation/yaml",MimeTypeText2.CSV="text/csv",MimeTypeText2.PYTHON="text/x-python",MimeTypeText2.JAVA="text/x-java-source",MimeTypeText2.CPP_HDR="text/x-c++hdr",MimeTypeText2.CPP_SRC="text/x-c++src",MimeTypeText2.CSHARP="text/x-csharp",MimeTypeText2.HASKELL="text/x-haskell",MimeTypeText2.C_SRC="text/x-csrc",MimeTypeText2.C_HDR="text/x-chdr",MimeTypeText2.PHP="text/x-php",MimeTypeText2.RUBY="text/x-ruby",MimeTypeText2.GO="text/x-go",MimeTypeText2.RUST="text/x-rust",MimeTypeText2.SHELL="text/x-shellscr\ -ipt",MimeTypeText2.BAT="application/x-bat",MimeTypeText2.SQL="text/x-sql",MimeTypeText2.R="text/x-r",MimeTypeText2.SCALA="text/x-scala",MimeTypeText2.KOTLIN="text/x-kotlin",MimeTypeText2.SWIFT="text/x-swift",MimeTypeText2.DART="text/x-dart",MimeTypeText2.VUE="text/x-vue",MimeTypeText2.SVELTE="text/x-svelte",MimeTypeText2.TEX="text/x-tex",MimeTypeText2.TEX_APP="application/x-tex",MimeTypeText2.LATEX="application/x-latex",MimeTypeText2.BIBTEX="text/x-bibtex",MimeTypeText2.CUDA="text/x-cuda",MimeTypeText2. -PROPERTIES="text/properties",MimeTypeText2))(MimeTypeText||{}),MCPConnectionPhase=(MCPConnectionPhase2=>(MCPConnectionPhase2.IDLE="idle",MCPConnectionPhase2.TRANSPORT_CREATING="transport_creating",MCPConnectionPhase2.TRANSPORT_READY="transport_ready",MCPConnectionPhase2.INITIALIZING="initializing",MCPConnectionPhase2.CAPABILITIES_EXCHANGED="capabilities_exchanged",MCPConnectionPhase2.LISTING_TOOLS="listing_tools",MCPConnectionPhase2.CONNECTED="connected",MCPConnectionPhase2.ERROR="error",MCPConnectionPhase2. -DISCONNECTED="disconnected",MCPConnectionPhase2))(MCPConnectionPhase||{}),MCPLogLevel=(MCPLogLevel2=>(MCPLogLevel2.INFO="info",MCPLogLevel2.WARN="warn",MCPLogLevel2.ERROR="error",MCPLogLevel2))(MCPLogLevel||{}),MCPTransportType=(MCPTransportType2=>(MCPTransportType2.WEBSOCKET="websocket",MCPTransportType2.STREAMABLE_HTTP="streamable_http",MCPTransportType2.SSE="sse",MCPTransportType2))(MCPTransportType||{}),HealthCheckStatus=(HealthCheckStatus2=>(HealthCheckStatus2.IDLE="idle",HealthCheckStatus2. -CONNECTING="connecting",HealthCheckStatus2.SUCCESS="success",HealthCheckStatus2.ERROR="error",HealthCheckStatus2))(HealthCheckStatus||{}),MCPContentType=(MCPContentType2=>(MCPContentType2.TEXT="text",MCPContentType2.IMAGE="image",MCPContentType2.RESOURCE="resource",MCPContentType2))(MCPContentType||{}),JsonSchemaType=(JsonSchemaType2=>(JsonSchemaType2.OBJECT="object",JsonSchemaType2))(JsonSchemaType||{}),MCPRefType=(MCPRefType2=>(MCPRefType2.PROMPT="ref/prompt",MCPRefType2.RESOURCE="ref/resource", -MCPRefType2))(MCPRefType||{}),ModelModality=(ModelModality2=>(ModelModality2.TEXT="TEXT",ModelModality2.AUDIO="AUDIO",ModelModality2.VISION="VISION",ModelModality2))(ModelModality||{}),ServerRole=(ServerRole2=>(ServerRole2.MODEL="model",ServerRole2.ROUTER="router",ServerRole2))(ServerRole||{}),ServerModelStatus=(ServerModelStatus2=>(ServerModelStatus2.UNLOADED="unloaded",ServerModelStatus2.LOADING="loading",ServerModelStatus2.LOADED="loaded",ServerModelStatus2.SLEEPING="sleeping",ServerModelStatus2. -FAILED="failed",ServerModelStatus2))(ServerModelStatus||{}),ParameterSource=(ParameterSource2=>(ParameterSource2.DEFAULT="default",ParameterSource2.CUSTOM="custom",ParameterSource2))(ParameterSource||{}),SyncableParameterType=(SyncableParameterType2=>(SyncableParameterType2.NUMBER="number",SyncableParameterType2.STRING="string",SyncableParameterType2.BOOLEAN="boolean",SyncableParameterType2))(SyncableParameterType||{}),SettingsFieldType=(SettingsFieldType2=>(SettingsFieldType2.INPUT="input",SettingsFieldType2. -TEXTAREA="textarea",SettingsFieldType2.CHECKBOX="checkbox",SettingsFieldType2.SELECT="select",SettingsFieldType2))(SettingsFieldType||{}),ColorMode=(ColorMode2=>(ColorMode2.LIGHT="light",ColorMode2.DARK="dark",ColorMode2.SYSTEM="system",ColorMode2))(ColorMode||{}),TooltipSide=(TooltipSide2=>(TooltipSide2.TOP="top",TooltipSide2.RIGHT="right",TooltipSide2.BOTTOM="bottom",TooltipSide2.LEFT="left",TooltipSide2))(TooltipSide||{}),McpPromptVariant=(McpPromptVariant2=>(McpPromptVariant2.MESSAGE="messag\ -e",McpPromptVariant2.ATTACHMENT="attachment",McpPromptVariant2))(McpPromptVariant||{}),UrlProtocol=(UrlProtocol2=>(UrlProtocol2.DATA="data:",UrlProtocol2.HTTP="http:",UrlProtocol2.HTTPS="https:",UrlProtocol2.WEBSOCKET="ws:",UrlProtocol2.WEBSOCKET_SECURE="wss:",UrlProtocol2))(UrlProtocol||{}),HtmlInputType=(HtmlInputType2=>(HtmlInputType2.FILE="file",HtmlInputType2))(HtmlInputType||{}),KeyboardKey=(KeyboardKey2=>(KeyboardKey2.ENTER="Enter",KeyboardKey2.ESCAPE="Escape",KeyboardKey2.ARROW_UP="Arrow\ -Up",KeyboardKey2.ARROW_DOWN="ArrowDown",KeyboardKey2.ARROW_LEFT="ArrowLeft",KeyboardKey2.ARROW_RIGHT="ArrowRight",KeyboardKey2.TAB="Tab",KeyboardKey2.D_LOWER="d",KeyboardKey2.D_UPPER="D",KeyboardKey2.E_UPPER="E",KeyboardKey2.K_LOWER="k",KeyboardKey2.O_LOWER="o",KeyboardKey2.O_UPPER="O",KeyboardKey2.SPACE=" ",KeyboardKey2))(KeyboardKey||{}),ToolSource=(ToolSource2=>(ToolSource2.BUILTIN="builtin",ToolSource2.MCP="mcp",ToolSource2.CUSTOM="custom",ToolSource2))(ToolSource||{}),ToolPermissionDecision=(ToolPermissionDecision2=>(ToolPermissionDecision2. -ALWAYS="always",ToolPermissionDecision2.ALWAYS_SERVER="always_server",ToolPermissionDecision2.ONCE="once",ToolPermissionDecision2.DENY="deny",ToolPermissionDecision2))(ToolPermissionDecision||{}),ToolResponseField=(ToolResponseField2=>(ToolResponseField2.PLAIN_TEXT="plain_text_response",ToolResponseField2.ERROR="error",ToolResponseField2))(ToolResponseField||{}),root_5$v=from_html("

"),root_1$19=from_html(" ",1);function ActionIcon($$anchor,$$props){push$1($$props,!0);let variant=prop( -$$props,"variant",3,"ghost"),size2=prop($$props,"size",3,"sm"),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),iconSize=prop($$props,"iconSize",3,"h-3 w-3"),tooltipSide=prop($$props,"tooltipSide",19,()=>TooltipSide.TOP),stopPropagationOnClick=prop($$props,"stopPropagationOnClick",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$19(), -node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>$$props.ariaLabel||$$props.tooltip);Button($$anchor5,{get variant(){return variant()},get size(){return size2()},get disabled(){return disabled()},onclick:e=>{stopPropagationOnClick()&&e.stopPropagation(),$$props.onclick?.(e)},get class(){return`h-6 w-6 p-0 ${className()??""} flex hover:bg-transparent data-[state\ -=open]:bg-transparent!`},get"aria-label"(){return get$3($0)},children:($$anchor6,$$slotProps3)=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{var consequent=$$anchor7=>{const IconComponent=user_derived(()=>$$props.icon);var fragment_4=comment$2(),node_3=first_child(fragment_4);component(node_3,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{IconComponent_1($$anchor8,{get class(){return iconSize()}})}),append($$anchor7,fragment_4)};if_block(node_2,$$render=>{$$props.icon&&$$render( -consequent)})}append($$anchor6,fragment_3)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_4=sibling(node_1,2);component(node_4,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{get side(){return tooltipSide()},children:($$anchor5,$$slotProps2)=>{var p2=root_5$v(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltip)),append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor, -fragment),pop()}const defaultAttributes={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};var root$1V=from_svg("");function Icon($$anchor,$$props){push$1($$props,!0);const color=prop($$props,"color",3,"currentColor"),size2=prop($$props,"size",3,24),strokeWidth=prop($$props,"strokeWidth",3,2),absoluteStrokeWidth=prop($$props,"absoluteStrokeWidth",3,!1),iconNode=prop( -$$props,"iconNode",19,()=>[]),props=rest_props($$props,["$$slots","$$events","$$legacy","name","color","size","strokeWidth","absoluteStrokeWidth","iconNode","children"]);var svg2=root$1V();attribute_effect(svg2,$0=>({...defaultAttributes,...props,width:size2(),height:size2(),stroke:color(),"stroke-width":$0,class:["lucide-icon lucide",$$props.name&&`lucide-${$$props.name}`,$$props.class]}),[()=>absoluteStrokeWidth()?Number(strokeWidth())*24/Number(size2()):strokeWidth()]);var node2=child(svg2);each( -node2,17,iconNode,index$2,($$anchor2,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let tag=()=>get$3($$array)[0],attrs=()=>get$3($$array)[1];var fragment=comment$2(),node_1=first_child(fragment);element$4(node_1,tag,!0,($$element,$$anchor3)=>{attribute_effect($$element,()=>({...attrs()}))}),append($$anchor2,fragment)});var node_2=sibling(node2);snippet(node_2,()=>$$props.children??noop$3),reset(svg2),append($$anchor,svg2),pop()}function Arrow_big_up($$anchor,$$props){push$1($$props, -!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18v-6H5l7-7 7 7h-4v6H9z"}]];Icon($$anchor,spread_props({name:"arrow-big-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, -["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"m12 5 7 7-7 7"}]];Icon($$anchor,spread_props({name:"arrow-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$\ -$legacy"]);const iconNode=[["path",{d:"m5 12 7-7 7 7"}],["path",{d:"M12 19V5"}]];Icon($$anchor,spread_props({name:"arrow-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Book_open_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["path",{d:"M12 7v14"}],["path",{d:"M16 12h2"}],["path",{d:"M16 8h2"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],["path",{d:"M6 12h2"}],["path",{d:"M6 8h2"}]];Icon($$anchor,spread_props({name:"book-open-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( -$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Braces($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"}],["path",{d:"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"}]];Icon($$anchor,spread_props({name:"braces"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Brain($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3\ --4 4.5 4.5 0 0 1-3 4"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516"}],["path",{d:"M19.967 17.484A4 4 0 0 1 18 18"}]];Icon($$anchor,spread_props({name:"brain"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Check($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 6 9 17l-5-5"}]];Icon($$anchor,spread_props({name:"check"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ -default:!0}})),pop()}function Chevron_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 9 6 6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_left($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15 18-6-6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, -["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m18 15-6-6-6 6"}]];Icon($$anchor,spread_props({name:"chevron-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["path",{d:"m9 18 6-6-6-6"}]];Icon($$anchor,spread_props({name:"chevron-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevrons_up_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m7 15 5 5 5-5"}],["path",{ -d:"m7 9 5-5 5 5"}]];Icon($$anchor,spread_props({name:"chevrons-up-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"\ -12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"circle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_check_big($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["path",{d:"M21.801 10A10 10 0 1 1 17 3.335"}],["path",{d:"m9 11 3 3L22 4"}]];Icon($$anchor,spread_props({name:"circle-check-big"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]];Icon($$anchor,spread_props({name:"circle-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Clock($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["circle",{cx:"12",cy:"12",r:"10"}],["polyline",{points:"12 6 12 12 16 14"}]];Icon($$anchor,spread_props({name:"clock"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Code($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ -m16 18 6-6-6-6"}],["path",{d:"m8 6-6 6 6 6"}]];Icon($$anchor,spread_props({name:"code"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Copy($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"14",height:"14",x:"8",y:"8", -rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]];Icon($$anchor,spread_props({name:"copy"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Database($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}]];Icon($$anchor,spread_props({name:"database"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Download($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots", -"$$events","$$legacy"]);const iconNode=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]];Icon($$anchor,spread_props({name:"download"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Ellipsis($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]];Icon($$anchor,spread_props({name:"ellipsis"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function External_link($$anchor,$$props){push$1($$props, -!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 3h6v6"}],["path",{d:"M10 14 21 3"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}]];Icon($$anchor,spread_props({name:"external-link"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Eye($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"eye"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, -$$slots:{default:!0}})),pop()}function File_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"M10 9H8"}],["path",{d:"M16 13H8"}],["path",{d:"M16 17H8"}]];Icon($$anchor,spread_props({name:"file-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"m14.5 12.5-5 5"}],["path",{d:"m9.5 12.5 5 5"}]];Icon($$anchor,spread_props({name:"file-x"},()=>props,{get iconNode(){ -return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}]];Icon($$anchor,spread_props({name:"file"}, -()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Flask_conical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"}],["\ -path",{d:"M6.453 15h11.094"}],["path",{d:"M8.5 2h7"}]];Icon($$anchor,spread_props({name:"flask-conical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Folder_open($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 14 \ -1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"}]];Icon($$anchor,spread_props({name:"folder-open"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Funnel($$anchor,$$props){push$1( -$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"}]];Icon($$anchor,spread_props({name:"funnel"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, -$$slots:{default:!0}})),pop()}function Gauge($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m12 14 4-4"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0"}]];Icon($$anchor,spread_props({name:"gauge"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), -pop()}function Git_branch($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"6",x2:"6",y1:"3",y2:"15"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}],["path",{d:"M18 9a9 9 0 0 1-9 9"}]];Icon($$anchor,spread_props({name:"git-branch"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? -noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Globe($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["path",{d:"M2 12h20"}]];Icon($$anchor,spread_props({name:"globe"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1); -snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"2",y1:"2",x2:"22",y2:"22"}],["path",{d:"M16.5 16.5 12 21l-7-7c-1.5-1.45-3-3.2-3-5.5a5.5 5.5 0 0 1 2.14-4.35"}],["path",{d:"M8.76 3.1c1.15.22 2.13.78 3.24 1.9 1.5-1.5 2.74-2 4.5-2A5.5 5.5 0 0 1 22 8.5c0 2.12-1.3 3.78-2.67 5.17"}]];Icon($$anchor,spread_props( -{name:"heart-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5\ - 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"}]];Icon($$anchor,spread_props({name:"heart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Image$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3", -rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]];Icon($$anchor,spread_props({name:"image"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Info$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legac\ -y"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 16v-4"}],["path",{d:"M12 8h.01"}]];Icon($$anchor,spread_props({name:"info"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Key($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]); -const iconNode=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"}],["path",{d:"m21 2-9.6 9.6"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5"}]];Icon($$anchor,spread_props({name:"key"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Layers($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"}]];Icon($$anchor,spread_props({name:"layers"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_checks($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m3 17 2 2 4-4"}],["path",{d:"m3 7 2 2 4-4"}],["path",{d:"M13 6h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 18h8"}]];Icon($$anchor,spread_props({name:"list-checks"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_restart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 6H3"}],["path",{d:"M7 12H3"}],["path",{d:"M7 18H3"}],["path",{d:"M12 18a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L11 14"}],["path",{d:"M11 10v4h4"}]];Icon($$anchor, -spread_props({name:"list-restart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Loader_circle($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]];Icon($$anchor,spread_props({name:"loader-c\ -ircle"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Message_square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"}]];Icon($$anchor,spread_props({name:"\ -message-square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Mic($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"}],["li\ -ne",{x1:"12",x2:"12",y1:"19",y2:"22"}]];Icon($$anchor,spread_props({name:"mic"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Minus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}]];Icon($$anchor,spread_props( -{name:"minus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Monitor($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21"}],["line",{x1:"12", -x2:"12",y1:"17",y2:"21"}]];Icon($$anchor,spread_props({name:"monitor"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Moon($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"}]];Icon($$anchor, -spread_props({name:"moon"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Music($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18V5l12-2v13"}],["circle",{cx:"6",cy:"18",r:"3"}],["circle",{cx:"18",cy:"16",r:"3"}]];Icon( -$$anchor,spread_props({name:"music"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Package($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-\ -2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"}],["path",{d:"M12 22V12"}],["polyline",{points:"3.29 7 12 12 20.71 7"}],["path",{d:"m7.5 4.27 9 5.15"}]];Icon($$anchor,spread_props({name:"package"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left_close($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m16 15-3-3 3-3"}]];Icon($$anchor,spread_props({name:"panel-left-close"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left($$anchor,$$props){push$1( -$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}]];Icon($$anchor,spread_props({name:"panel-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil_ruler($$anchor,$$props){push$1( -$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"}],["path",{d:"m8 6 2-2"}],["path",{d:"m18 16 2-2"}],["path",{d:"m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor, -spread_props({name:"pencil-ruler"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.\ -5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Plus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ -M5 12h14"}],["path",{d:"M12 5v14"}]];Icon($$anchor,spread_props({name:"plus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15"}],[ -"path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68"}],["path",{d:"M12 2v4"}],["path",{d:"m2 2 20 20"}]];Icon($$anchor,spread_props({name:"power-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["path",{d:"M12 2v10"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04"}]];Icon($$anchor,spread_props({name:"power"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Radio($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4.9 1\ -9.1C1 15.2 1 8.8 4.9 4.9"}],["path",{d:"M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"}],["path",{d:"M19.1 4.9C23 8.8 23 15.1 19.1 19"}]];Icon($$anchor,spread_props({name:"radio"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Refresh_cw($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]];Icon($$anchor,spread_props({name:"refresh-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? -noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_ccw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}]];Icon($$anchor,spread_props({name:"rotate-ccw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}]];Icon($$anchor,spread_props({name:"rotate-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2, -()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Search($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]];Icon($$anchor,spread_props({name:"search"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? -noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Server($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18"}]];Icon($$anchor,spread_props({name:"server"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Settings$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73\ - 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"settings"},()=>props,{get iconNode(){return iconNode}, -children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m2 2 20 20"}],["path",{d:"M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71"}],["path",{d:"M9.309 3.652A12.252 12.252\ - 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264"}]];Icon($$anchor,spread_props({name:"shield-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_question($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$\ -$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"shield-question"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. -children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}]];Icon($$anchor,spread_props({name:"shield"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ -var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sliders_vertical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"4",x2:"4",y1:"21",y2:"14"}],["line",{x1:"4",x2:"4",y1:"10",y2:"3"}],["line",{x1:"12",x2:"12",y1:"21",y2:"12"}],["line",{x1:"12",x2:"12",y1:"8",y2:"3"}],["line",{x1:"20",x2:"20",y1:"21", -y2:"16"}],["line",{x1:"20",x2:"20",y1:"12",y2:"3"}],["line",{x1:"2",x2:"6",y1:"14",y2:"14"}],["line",{x1:"10",x2:"14",y1:"8",y2:"8"}],["line",{x1:"18",x2:"22",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"sliders-vertical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sparkles($$anchor,$$props){ -push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"}],["path",{d:"M20 3v4"}],["path",{d:"M22 5h-4"}],["path",{d:"M4 17v2"}],["path",{d:"M5 18H3"}]];Icon($$anchor,spread_props({name:"sparkl\ -es"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square_pen($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.01\ -3 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"}]];Icon($$anchor,spread_props({name:"square-pen"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ -["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]];Icon($$anchor,spread_props({name:"square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sun($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"4"}], -["path",{d:"M12 2v2"}],["path",{d:"M12 20v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"m17.66 17.66 1.41 1.41"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"m6.34 17.66-1.41 1.41"}],["path",{d:"m19.07 4.93-1.41 1.41"}]];Icon($$anchor,spread_props({name:"sun"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ -default:!0}})),pop()}function Timer_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 2h4"}],["path",{d:"M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7"}],["path",{d:"M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0 0 1 .9 10.2"}],["path",{d:"m2 2 20 20"}],["path",{d:"M12 12v-2"}]];Icon($$anchor,spread_props({name:"timer-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), -node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Trash_2($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 6h18"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17"}]];Icon($$anchor,spread_props( -{name:"trash-2"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Triangle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"}],["path",{d:"M12\ - 9v4"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"triangle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Upload($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3v12"}],["path",{d:"m17 8-\ -5-5-5 5"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}]];Icon($$anchor,spread_props({name:"upload"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Whole_word($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"\ -7",cy:"12",r:"3"}],["path",{d:"M10 9v6"}],["circle",{cx:"17",cy:"12",r:"3"}],["path",{d:"M14 7v8"}],["path",{d:"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1"}]];Icon($$anchor,spread_props({name:"whole-word"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Wrench($$anchor,$$props){push$1($$props,!0);let props=rest_props( -$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"}]];Icon($$anchor,spread_props({name:"wrench"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), -pop()}function X($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]];Icon($$anchor,spread_props({name:"x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Zap($$anchor,$$props){push$1( -$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}]];Icon($$anchor,spread_props({name:"zap"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, -fragment_1)},$$slots:{default:!0}})),pop()}const defaultWindow$2=typeof window<"u"?window:void 0;function getActiveElement$2(document2){let activeElement=document2.activeElement;for(;activeElement?.shadowRoot;){const node2=activeElement.shadowRoot.activeElement;if(node2===activeElement)break;activeElement=node2}return activeElement}let ActiveElement$2=class{#document;#subscribe;constructor(options={}){const{window:window2=defaultWindow$2,document:document2=window2?.document}=options;window2!==void 0&& -(this.#document=document2,this.#subscribe=createSubscriber(update2=>{const cleanupFocusIn=on(window2,"focusin",update2),cleanupFocusOut=on(window2,"focusout",update2);return()=>{cleanupFocusIn(),cleanupFocusOut()}}))}get current(){return this.#subscribe?.(),this.#document?getActiveElement$2(this.#document):null}};new ActiveElement$2;function runEffect(flush,effect2){switch(flush){case"post":user_effect(effect2);break;case"pre":user_pre_effect(effect2);break}}function runWatcher(sources,flush,effect2,options={}){ -const{lazy=!1}=options;let active=!lazy,previousValues=Array.isArray(sources)?[]:void 0;runEffect(flush,()=>{const values=Array.isArray(sources)?sources.map(source2=>source2()):sources();if(!active){active=!0,previousValues=values;return}const cleanup=untrack$1(()=>effect2(values,previousValues));return previousValues=values,cleanup})}function watch(sources,effect2,options){runWatcher(sources,"post",effect2,options)}function watchPre(sources,effect2,options){runWatcher(sources,"pre",effect2,options)} -watch.pre=watchPre;function getStorage(storageType,window2){switch(storageType){case"local":return window2.localStorage;case"session":return window2.sessionStorage}}class PersistedState{#current;#key;#serializer;#storage;#subscribe;#version=state$1(0);constructor(key2,initialValue,options={}){const{storage:storageType="local",serializer:serializer2={serialize:JSON.stringify,deserialize:JSON.parse},syncTabs=!0,window:window2=defaultWindow$2}=options;if(this.#current=initialValue,this.#key=key2,this.#serializer= -serializer2,window2===void 0)return;const storage=getStorage(storageType,window2);this.#storage=storage;const existingValue=storage.getItem(key2);existingValue!==null?this.#current=this.#deserialize(existingValue):this.#serialize(initialValue),syncTabs&&storageType==="local"&&(this.#subscribe=createSubscriber(()=>on(window2,"storage",this.#handleStorageEvent)))}get current(){this.#subscribe?.(),get$3(this.#version);const root2=this.#deserialize(this.#storage?.getItem(this.#key))??this.#current,proxies=new WeakMap, -proxy2=value=>{if(value===null||value?.constructor.name==="Date"||typeof value!="object")return value;let p2=proxies.get(value);return p2||(p2=new Proxy(value,{get:(target2,property)=>(get$3(this.#version),proxy2(Reflect.get(target2,property))),set:(target2,property,value2)=>(set$1(this.#version,get$3(this.#version)+1),Reflect.set(target2,property,value2),this.#serialize(root2),!0)}),proxies.set(value,p2)),p2};return proxy2(root2)}set current(newValue){this.#serialize(newValue),set$1(this.#version, -get$3(this.#version)+1)}#handleStorageEvent=event2=>{event2.key!==this.#key||event2.newValue===null||(this.#current=this.#deserialize(event2.newValue),set$1(this.#version,get$3(this.#version)+1))};#deserialize(value){try{return this.#serializer.deserialize(value)}catch(error2){console.error(`Error when parsing "${value}" from persisted store "${this.#key}"`,error2);return}}#serialize(value){try{value!=null&&this.#storage?.setItem(this.#key,this.#serializer.serialize(value))}catch(error2){console. -error(`Error when writing value from persisted store "${this.#key}" to ${this.#storage}`,error2)}}}function sanitizeClassNames(classNames){return classNames.filter(className=>className.length>0)}const noopStorage={getItem:_key=>null,setItem:(_key,_value)=>{}},isBrowser$1=typeof document<"u";function isFunction(value){return typeof value=="function"}function isObject$1(value){return value!==null&&typeof value=="object"}const BoxSymbol=Symbol("box"),isWritableSymbol=Symbol("is-writable");function isBox(value){ -return isObject$1(value)&&BoxSymbol in value}function isWritableBox(value){return box.isBox(value)&&isWritableSymbol in value}function box(initialValue){let current2=state$1(proxy(initialValue));return{[BoxSymbol]:!0,[isWritableSymbol]:!0,get current(){return get$3(current2)},set current(v){set$1(current2,v,!0)}}}function boxWith(getter,setter){const derived2=user_derived(getter);return setter?{[BoxSymbol]:!0,[isWritableSymbol]:!0,get current(){return get$3(derived2)},set current(v){setter(v)}}: -{[BoxSymbol]:!0,get current(){return getter()}}}function boxFrom(value){return box.isBox(value)?value:isFunction(value)?box.with(value):box(value)}function boxFlatten(boxes){return Object.entries(boxes).reduce((acc,[key2,b])=>box.isBox(b)?(box.isWritableBox(b)?Object.defineProperty(acc,key2,{get(){return b.current},set(v){b.current=v}}):Object.defineProperty(acc,key2,{get(){return b.current}}),acc):Object.assign(acc,{[key2]:b}),{})}function toReadonlyBox(b){return box.isWritableBox(b)?{[BoxSymbol]:!0, -get current(){return b.current}}:b}box.from=boxFrom;box.with=boxWith;box.flatten=boxFlatten;box.readonly=toReadonlyBox;box.isBox=isBox;box.isWritableBox=isWritableBox;function createParser$1(matcher,replacer){const regex=RegExp(matcher,"g");return str=>{if(typeof str!="string")throw new TypeError(`expected an argument of type string, but got ${typeof str}`);return str.match(regex)?str.replace(regex,replacer):str}}const camelToKebab=createParser$1(/[A-Z]/,match=>`-${match.toLowerCase()}`);function styleToCSS(styleObj){ -if(!styleObj||typeof styleObj!="object"||Array.isArray(styleObj))throw new TypeError(`expected an argument of type object, but got ${typeof styleObj}`);return Object.keys(styleObj).map(property=>`${camelToKebab(property)}: ${styleObj[property]};`).join(` +get$3(floatingProps),contentState.props));var fragment=comment$2(),node2=first_child(fragment);{var consequent_1=$$anchor2=>{{const popper=($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalWrapperProps=user_derived(()=>mergeProps(wrapperProps(),{style:{pointerEvents:contentState.root.disableHoverableContent?"none":void 0}})),finalProps=user_derived(()=>mergeProps(props(),{style:getFloatingContentCSSVars("tooltip")},{style:$$props.style}));var fragment_2=comment$2(), +node_1=first_child(fragment_2);{var consequent=$$anchor4=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{let $02=user_derived(()=>({props:get$3(finalProps),wrapperProps:get$3(finalWrapperProps),...contentState.snippetProps}));snippet(node_2,()=>$$props.child,()=>get$3($02))}append($$anchor4,fragment_3)},alternate=$$anchor4=>{var div=root_4$H();attribute_effect(div,()=>({...get$3(finalWrapperProps)}));var div_1=child(div);attribute_effect(div_1,()=>({...get$3(finalProps)}));var node_3=child( +div_1);snippet(node_3,()=>$$props.children??noop$3),reset(div_1),reset(div),append($$anchor4,div)};if_block(node_1,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor3,fragment_2)};let $0=user_derived(()=>contentState.root.disableHoverableContent?"none":"auto");Popper_layer_force_mount($$anchor2,spread_props(()=>get$3(mergedProps),()=>contentState.popperProps,{get enabled(){return contentState.root.opts.open.current},get id(){return id2()},trapFocus:!1,loop:!1, +preventScroll:!1,forceMount:!0,get ref(){return contentState.opts.ref},tooltip:!0,get shouldRender(){return contentState.shouldRender},get contentPointerEvents(){return get$3($0)},popper,$$slots:{popper:!0}}))}},consequent_3=$$anchor2=>{{const popper=($$anchor3,$$arg0)=>{let props=()=>$$arg0?.().props,wrapperProps=()=>$$arg0?.().wrapperProps;const finalWrapperProps=user_derived(()=>mergeProps(wrapperProps(),{style:{pointerEvents:contentState.root.disableHoverableContent?"none":void 0}})),finalProps=user_derived( +()=>mergeProps(props(),{style:getFloatingContentCSSVars("tooltip")},{style:$$props.style}));var fragment_5=comment$2(),node_4=first_child(fragment_5);{var consequent_2=$$anchor4=>{var fragment_6=comment$2(),node_5=first_child(fragment_6);{let $02=user_derived(()=>({props:get$3(finalProps),wrapperProps:get$3(finalWrapperProps),...contentState.snippetProps}));snippet(node_5,()=>$$props.child,()=>get$3($02))}append($$anchor4,fragment_6)},alternate_1=$$anchor4=>{var div_2=root_8$u();attribute_effect( +div_2,()=>({...get$3(finalWrapperProps)}));var div_3=child(div_2);attribute_effect(div_3,()=>({...get$3(finalProps)}));var node_6=child(div_3);snippet(node_6,()=>$$props.children??noop$3),reset(div_3),reset(div_2),append($$anchor4,div_2)};if_block(node_4,$$render=>{$$props.child?$$render(consequent_2):$$render(alternate_1,-1)})}append($$anchor3,fragment_5)};let $0=user_derived(()=>contentState.root.disableHoverableContent?"none":"auto");Popper_layer($$anchor2,spread_props(()=>get$3(mergedProps), +()=>contentState.popperProps,{get open(){return contentState.root.opts.open.current},get id(){return id2()},trapFocus:!1,loop:!1,preventScroll:!1,forceMount:!1,get ref(){return contentState.opts.ref},tooltip:!0,get shouldRender(){return contentState.shouldRender},get contentPointerEvents(){return get$3($0)},popper,$$slots:{popper:!0}}))}};if_block(node2,$$render=>{forceMount()?$$render(consequent_1):forceMount()||$$render(consequent_3,1)})}append($$anchor,fragment),pop()}var root_2$1h=from_html( +"");function Tooltip_trigger$1($$anchor,$$props){const uid2=props_id();push$1($$props,!0);let id2=prop($$props,"id",19,()=>createId(uid2)),disabled=prop($$props,"disabled",3,!1),type2=prop($$props,"type",3,"button"),tabindex=prop($$props,"tabindex",3,0),ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","children","child","id","disabled","payload","tether","type","tabindex","ref"]);const triggerState=TooltipTriggerState.create({id:boxWith$1( +()=>id2()),disabled:boxWith$1(()=>disabled()??!1),tabindex:boxWith$1(()=>tabindex()??0),payload:boxWith$1(()=>$$props.payload),tether:boxWith$1(()=>$$props.tether),ref:boxWith$1(()=>ref2(),v=>ref2(v))}),mergedProps=user_derived(()=>mergeProps(restProps,triggerState.props,{type:type2()}));var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append( +$$anchor2,fragment_1)},alternate=$$anchor2=>{var button=root_2$1h();attribute_effect(button,()=>({...get$3(mergedProps)}));var node_2=child(button);snippet(node_2,()=>$$props.children??noop$3),reset(button),append($$anchor2,button)};if_block(node2,$$render=>{$$props.child?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}function Tooltip_arrow($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","\ +$$legacy","ref"]);Floating_layer_arrow($$anchor,spread_props(()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}})),pop()}function Tooltip_provider($$anchor,$$props){push$1($$props,!0);let delayDuration=prop($$props,"delayDuration",3,700),disableCloseOnTriggerClick=prop($$props,"disableCloseOnTriggerClick",3,!1),disableHoverableContent=prop($$props,"disableHoverableContent",3,!1),disabled=prop($$props,"disabled",3,!1),ignoreNonKeyboardFocus=prop($$props,"ignoreNonKeyboardFocu\ +s",3,!1),skipDelayDuration=prop($$props,"skipDelayDuration",3,300);TooltipProviderState.create({delayDuration:boxWith$1(()=>delayDuration()),disableCloseOnTriggerClick:boxWith$1(()=>disableCloseOnTriggerClick()),disableHoverableContent:boxWith$1(()=>disableHoverableContent()),disabled:boxWith$1(()=>disabled()),ignoreNonKeyboardFocus:boxWith$1(()=>ignoreNonKeyboardFocus()),skipDelayDuration:boxWith$1(()=>skipDelayDuration())});var fragment=comment$2(),node2=first_child(fragment);snippet(node2,()=>$$props. +children??noop$3),append($$anchor,fragment),pop()}function Tooltip_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Tooltip_trigger$1,($$anchor2,TooltipPrimitive_Trigger)=>{TooltipPrimitive_Trigger($$anchor2,spread_props({"data-slot":"tooltip-trigger",class:"cursor-pointer"},()=>restProps,{get ref(){return ref2()},set ref($$value){ +ref2($$value)}}))}),append($$anchor,fragment),pop()}var root_3$_=from_html("
"),root_2$1g=from_html(" ",1);function Tooltip_content($$anchor,$$props){push$1($$props,!0);const tooltipContent=$$anchor2=>{var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Tooltip_content$1,($$anchor3,TooltipPrimitive_Content)=>{TooltipPrimitive_Content($$anchor3,spread_props({"data-slot":"tooltip-content",get sideOffset(){return sideOffset()},get side(){return side()},get class(){ +return get$3(contentClass)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor4,$$slotProps)=>{var fragment_1=root_2$1g(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);{const child2=($$anchor5,$$arg0)=>{let props=()=>$$arg0?.().props;var div=root_3$_();attribute_effect(div,$0=>({class:$0,...props()}),[()=>cn$1("z-50 size-2.5 rotate-45 rounded-[2px] bg-primary","data-[side=top]:translate-x-1/2 data\ +-[side=top]:translate-y-[calc(-50%_+_2px)]","data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]","data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2","data-[side=left]:-translate-y-[calc(50%_-_3px)]",$$props.arrowClasses)]),append($$anchor5,div)};component(node_2,()=>Tooltip_arrow,($$anchor5,TooltipPrimitive_Arrow)=>{TooltipPrimitive_Arrow($$anchor5,{child:child2,$$slots:{child:!0}})})}append($$anchor4,fragment_1)},$$slots:{default:!0}}))}), +append($$anchor2,fragment)};let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,0),side=prop($$props,"side",3,"top"),noPortal=prop($$props,"noPortal",3,!1),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","sideOffset","side","children","arrowClasses","noPortal"]);const contentClass=user_derived(()=>cn$1("z-50 w-fit origin-(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-fore\ +ground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:fill-mode-forwards",$$props.class));var fragment_2=comment$2(),node_3=first_child(fragment_2);{var consequent=$$anchor2=>{tooltipContent($$anchor2)},alternate=$$anchor2=>{var fragment_4=comment$2(),node_4=first_child( +fragment_4);component(node_4,()=>Portal$2,($$anchor3,TooltipPrimitive_Portal)=>{TooltipPrimitive_Portal($$anchor3,{children:($$anchor4,$$slotProps)=>{tooltipContent($$anchor4)},$$slots:{default:!0}})}),append($$anchor2,fragment_4)};if_block(node_3,$$render=>{noPortal()?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment_2),pop()}const Root$5=Tooltip,Provider=Tooltip_provider;var AttachmentType=(AttachmentType2=>(AttachmentType2.AUDIO="AUDIO",AttachmentType2.IMAGE="IMAGE",AttachmentType2. +MCP_PROMPT="MCP_PROMPT",AttachmentType2.MCP_RESOURCE="MCP_RESOURCE",AttachmentType2.PDF="PDF",AttachmentType2.TEXT="TEXT",AttachmentType2.LEGACY_CONTEXT="context",AttachmentType2))(AttachmentType||{}),AttachmentMenuItemId=(AttachmentMenuItemId2=>(AttachmentMenuItemId2.IMAGES="images",AttachmentMenuItemId2.AUDIO="audio",AttachmentMenuItemId2.TEXT="text",AttachmentMenuItemId2.PDF="pdf",AttachmentMenuItemId2.SYSTEM_MESSAGE="system-message",AttachmentMenuItemId2.MCP_PROMPT="mcp-prompt",AttachmentMenuItemId2. +MCP_RESOURCES="mcp-resources",AttachmentMenuItemId2))(AttachmentMenuItemId||{}),AttachmentItemEnabledWhen=(AttachmentItemEnabledWhen2=>(AttachmentItemEnabledWhen2.ALWAYS="always",AttachmentItemEnabledWhen2.HAS_VISION_MODALITY="hasVisionModality",AttachmentItemEnabledWhen2.HAS_AUDIO_MODALITY="hasAudioModality",AttachmentItemEnabledWhen2))(AttachmentItemEnabledWhen||{}),AttachmentAction=(AttachmentAction2=>(AttachmentAction2.FILE_UPLOAD="onFileUpload",AttachmentAction2.SYSTEM_PROMPT_CLICK="onSyste\ +mPromptClick",AttachmentAction2.MCP_PROMPT_CLICK="onMcpPromptClick",AttachmentAction2.MCP_RESOURCES_CLICK="onMcpResourcesClick",AttachmentAction2))(AttachmentAction||{}),AttachmentItemVisibleWhen=(AttachmentItemVisibleWhen2=>(AttachmentItemVisibleWhen2.HAS_MCP_PROMPTS_SUPPORT="hasMcpPromptsSupport",AttachmentItemVisibleWhen2.HAS_MCP_RESOURCES_SUPPORT="hasMcpResourcesSupport",AttachmentItemVisibleWhen2))(AttachmentItemVisibleWhen||{}),ToolCallType=(ToolCallType2=>(ToolCallType2.FUNCTION="function", +ToolCallType2))(ToolCallType||{}),AgenticSectionType=(AgenticSectionType2=>(AgenticSectionType2.TEXT="text",AgenticSectionType2.TOOL_CALL="tool_call",AgenticSectionType2.TOOL_CALL_PENDING="tool_call_pending",AgenticSectionType2.TOOL_CALL_STREAMING="tool_call_streaming",AgenticSectionType2.REASONING="reasoning",AgenticSectionType2.REASONING_PENDING="reasoning_pending",AgenticSectionType2))(AgenticSectionType||{}),ChatMessageStatsView=(ChatMessageStatsView2=>(ChatMessageStatsView2.GENERATION="gene\ +ration",ChatMessageStatsView2.READING="reading",ChatMessageStatsView2.TOOLS="tools",ChatMessageStatsView2.SUMMARY="summary",ChatMessageStatsView2))(ChatMessageStatsView||{}),ReasoningFormat=(ReasoningFormat2=>(ReasoningFormat2.NONE="none",ReasoningFormat2.AUTO="auto",ReasoningFormat2))(ReasoningFormat||{}),MessageRole=(MessageRole2=>(MessageRole2.USER="user",MessageRole2.ASSISTANT="assistant",MessageRole2.SYSTEM="system",MessageRole2.TOOL="tool",MessageRole2))(MessageRole||{}),MessageType=(MessageType2=>(MessageType2. +ROOT="root",MessageType2.TEXT="text",MessageType2.THINK="think",MessageType2.SYSTEM="system",MessageType2))(MessageType||{}),ContentPartType=(ContentPartType2=>(ContentPartType2.TEXT="text",ContentPartType2.IMAGE_URL="image_url",ContentPartType2.INPUT_AUDIO="input_audio",ContentPartType2))(ContentPartType||{}),ErrorDialogType=(ErrorDialogType2=>(ErrorDialogType2.TIMEOUT="timeout",ErrorDialogType2.SERVER="server",ErrorDialogType2))(ErrorDialogType||{}),ConversationSelectionMode=(ConversationSelectionMode2=>(ConversationSelectionMode2. +EXPORT="export",ConversationSelectionMode2.IMPORT="import",ConversationSelectionMode2))(ConversationSelectionMode||{}),PdfViewMode=(PdfViewMode2=>(PdfViewMode2.TEXT="text",PdfViewMode2.PAGES="pages",PdfViewMode2))(PdfViewMode||{}),FileTypeCategory=(FileTypeCategory2=>(FileTypeCategory2.IMAGE="image",FileTypeCategory2.AUDIO="audio",FileTypeCategory2.PDF="pdf",FileTypeCategory2.TEXT="text",FileTypeCategory2))(FileTypeCategory||{}),SpecialFileType=(SpecialFileType2=>(SpecialFileType2.MCP_PROMPT="mc\ +p-prompt",SpecialFileType2))(SpecialFileType||{}),FileTypeImage=(FileTypeImage2=>(FileTypeImage2.JPEG="jpeg",FileTypeImage2.PNG="png",FileTypeImage2.GIF="gif",FileTypeImage2.WEBP="webp",FileTypeImage2.SVG="svg",FileTypeImage2))(FileTypeImage||{}),FileTypeAudio=(FileTypeAudio2=>(FileTypeAudio2.MP3="mp3",FileTypeAudio2.WAV="wav",FileTypeAudio2.WEBM="webm",FileTypeAudio2))(FileTypeAudio||{}),FileTypePdf=(FileTypePdf2=>(FileTypePdf2.PDF="pdf",FileTypePdf2))(FileTypePdf||{}),FileTypeText=(FileTypeText2=>(FileTypeText2. +PLAIN_TEXT="plainText",FileTypeText2.MARKDOWN="md",FileTypeText2.ASCIIDOC="asciidoc",FileTypeText2.JAVASCRIPT="js",FileTypeText2.TYPESCRIPT="ts",FileTypeText2.JSX="jsx",FileTypeText2.TSX="tsx",FileTypeText2.CSS="css",FileTypeText2.HTML="html",FileTypeText2.JSON="json",FileTypeText2.XML="xml",FileTypeText2.YAML="yaml",FileTypeText2.CSV="csv",FileTypeText2.LOG="log",FileTypeText2.PYTHON="python",FileTypeText2.JAVA="java",FileTypeText2.CPP="cpp",FileTypeText2.PHP="php",FileTypeText2.RUBY="ruby",FileTypeText2. +GO="go",FileTypeText2.RUST="rust",FileTypeText2.SHELL="shell",FileTypeText2.SQL="sql",FileTypeText2.R="r",FileTypeText2.SCALA="scala",FileTypeText2.KOTLIN="kotlin",FileTypeText2.SWIFT="swift",FileTypeText2.DART="dart",FileTypeText2.VUE="vue",FileTypeText2.SVELTE="svelte",FileTypeText2.LATEX="latex",FileTypeText2.BIBTEX="bibtex",FileTypeText2.CUDA="cuda",FileTypeText2.VULKAN="vulkan",FileTypeText2.HASKELL="haskell",FileTypeText2.CSHARP="csharp",FileTypeText2.PROPERTIES="properties",FileTypeText2))( +FileTypeText||{}),FileExtensionImage=(FileExtensionImage2=>(FileExtensionImage2.JPG=".jpg",FileExtensionImage2.JPEG=".jpeg",FileExtensionImage2.PNG=".png",FileExtensionImage2.GIF=".gif",FileExtensionImage2.WEBP=".webp",FileExtensionImage2.SVG=".svg",FileExtensionImage2))(FileExtensionImage||{}),FileExtensionAudio=(FileExtensionAudio2=>(FileExtensionAudio2.MP3=".mp3",FileExtensionAudio2.WAV=".wav",FileExtensionAudio2))(FileExtensionAudio||{}),FileExtensionPdf=(FileExtensionPdf2=>(FileExtensionPdf2. +PDF=".pdf",FileExtensionPdf2))(FileExtensionPdf||{}),FileExtensionText=(FileExtensionText2=>(FileExtensionText2.TXT=".txt",FileExtensionText2.MD=".md",FileExtensionText2.ADOC=".adoc",FileExtensionText2.JS=".js",FileExtensionText2.TS=".ts",FileExtensionText2.JSX=".jsx",FileExtensionText2.TSX=".tsx",FileExtensionText2.CSS=".css",FileExtensionText2.HTML=".html",FileExtensionText2.HTM=".htm",FileExtensionText2.JSON=".json",FileExtensionText2.XML=".xml",FileExtensionText2.YAML=".yaml",FileExtensionText2. +YML=".yml",FileExtensionText2.CSV=".csv",FileExtensionText2.LOG=".log",FileExtensionText2.PY=".py",FileExtensionText2.JAVA=".java",FileExtensionText2.CPP=".cpp",FileExtensionText2.C=".c",FileExtensionText2.H=".h",FileExtensionText2.PHP=".php",FileExtensionText2.RB=".rb",FileExtensionText2.GO=".go",FileExtensionText2.RS=".rs",FileExtensionText2.SH=".sh",FileExtensionText2.BAT=".bat",FileExtensionText2.SQL=".sql",FileExtensionText2.R=".r",FileExtensionText2.SCALA=".scala",FileExtensionText2.KT=".k\ +t",FileExtensionText2.SWIFT=".swift",FileExtensionText2.DART=".dart",FileExtensionText2.VUE=".vue",FileExtensionText2.SVELTE=".svelte",FileExtensionText2.TEX=".tex",FileExtensionText2.BIB=".bib",FileExtensionText2.CU=".cu",FileExtensionText2.CUH=".cuh",FileExtensionText2.COMP=".comp",FileExtensionText2.HPP=".hpp",FileExtensionText2.HS=".hs",FileExtensionText2.PROPERTIES=".properties",FileExtensionText2.CS=".cs",FileExtensionText2))(FileExtensionText||{}),MimeTypePrefix=(MimeTypePrefix2=>(MimeTypePrefix2. +IMAGE="image/",MimeTypePrefix2.TEXT="text",MimeTypePrefix2))(MimeTypePrefix||{}),MimeTypeIncludes=(MimeTypeIncludes2=>(MimeTypeIncludes2.JSON="json",MimeTypeIncludes2.JAVASCRIPT="javascript",MimeTypeIncludes2.TYPESCRIPT="typescript",MimeTypeIncludes2))(MimeTypeIncludes||{}),UriPattern=(UriPattern2=>(UriPattern2.DATABASE_KEYWORD="database",UriPattern2.DATABASE_SCHEME="db://",UriPattern2))(UriPattern||{}),MimeTypeApplication=(MimeTypeApplication2=>(MimeTypeApplication2.PDF="application/pdf",MimeTypeApplication2. +OCTET_STREAM="application/octet-stream",MimeTypeApplication2))(MimeTypeApplication||{}),MimeTypeAudio=(MimeTypeAudio2=>(MimeTypeAudio2.MP3_MPEG="audio/mpeg",MimeTypeAudio2.MP3="audio/mp3",MimeTypeAudio2.MP4="audio/mp4",MimeTypeAudio2.WAV="audio/wav",MimeTypeAudio2.WEBM="audio/webm",MimeTypeAudio2.WEBM_OPUS="audio/webm;codecs=opus",MimeTypeAudio2))(MimeTypeAudio||{}),MimeTypeImage=(MimeTypeImage2=>(MimeTypeImage2.JPEG="image/jpeg",MimeTypeImage2.JPG="image/jpg",MimeTypeImage2.PNG="image/png",MimeTypeImage2. +GIF="image/gif",MimeTypeImage2.WEBP="image/webp",MimeTypeImage2.SVG="image/svg+xml",MimeTypeImage2.ICO="image/x-icon",MimeTypeImage2.ICO_MICROSOFT="image/vnd.microsoft.icon",MimeTypeImage2))(MimeTypeImage||{}),MimeTypeText=(MimeTypeText2=>(MimeTypeText2.PLAIN="text/plain",MimeTypeText2.MARKDOWN="text/markdown",MimeTypeText2.ASCIIDOC="text/asciidoc",MimeTypeText2.JAVASCRIPT="text/javascript",MimeTypeText2.JAVASCRIPT_APP="application/javascript",MimeTypeText2.TYPESCRIPT="text/typescript",MimeTypeText2. +JSX="text/jsx",MimeTypeText2.TSX="text/tsx",MimeTypeText2.CSS="text/css",MimeTypeText2.HTML="text/html",MimeTypeText2.JSON="application/json",MimeTypeText2.XML_TEXT="text/xml",MimeTypeText2.XML_APP="application/xml",MimeTypeText2.YAML_TEXT="text/yaml",MimeTypeText2.YAML_APP="application/yaml",MimeTypeText2.CSV="text/csv",MimeTypeText2.PYTHON="text/x-python",MimeTypeText2.JAVA="text/x-java-source",MimeTypeText2.CPP_HDR="text/x-c++hdr",MimeTypeText2.CPP_SRC="text/x-c++src",MimeTypeText2.CSHARP="te\ +xt/x-csharp",MimeTypeText2.HASKELL="text/x-haskell",MimeTypeText2.C_SRC="text/x-csrc",MimeTypeText2.C_HDR="text/x-chdr",MimeTypeText2.PHP="text/x-php",MimeTypeText2.RUBY="text/x-ruby",MimeTypeText2.GO="text/x-go",MimeTypeText2.RUST="text/x-rust",MimeTypeText2.SHELL="text/x-shellscript",MimeTypeText2.BAT="application/x-bat",MimeTypeText2.SQL="text/x-sql",MimeTypeText2.R="text/x-r",MimeTypeText2.SCALA="text/x-scala",MimeTypeText2.KOTLIN="text/x-kotlin",MimeTypeText2.SWIFT="text/x-swift",MimeTypeText2. +DART="text/x-dart",MimeTypeText2.VUE="text/x-vue",MimeTypeText2.SVELTE="text/x-svelte",MimeTypeText2.TEX="text/x-tex",MimeTypeText2.TEX_APP="application/x-tex",MimeTypeText2.LATEX="application/x-latex",MimeTypeText2.BIBTEX="text/x-bibtex",MimeTypeText2.CUDA="text/x-cuda",MimeTypeText2.PROPERTIES="text/properties",MimeTypeText2))(MimeTypeText||{}),MCPConnectionPhase=(MCPConnectionPhase2=>(MCPConnectionPhase2.IDLE="idle",MCPConnectionPhase2.TRANSPORT_CREATING="transport_creating",MCPConnectionPhase2. +TRANSPORT_READY="transport_ready",MCPConnectionPhase2.INITIALIZING="initializing",MCPConnectionPhase2.CAPABILITIES_EXCHANGED="capabilities_exchanged",MCPConnectionPhase2.LISTING_TOOLS="listing_tools",MCPConnectionPhase2.CONNECTED="connected",MCPConnectionPhase2.ERROR="error",MCPConnectionPhase2.DISCONNECTED="disconnected",MCPConnectionPhase2))(MCPConnectionPhase||{}),MCPLogLevel=(MCPLogLevel2=>(MCPLogLevel2.INFO="info",MCPLogLevel2.WARN="warn",MCPLogLevel2.ERROR="error",MCPLogLevel2))(MCPLogLevel|| +{}),MCPTransportType=(MCPTransportType2=>(MCPTransportType2.WEBSOCKET="websocket",MCPTransportType2.STREAMABLE_HTTP="streamable_http",MCPTransportType2.SSE="sse",MCPTransportType2))(MCPTransportType||{}),HealthCheckStatus=(HealthCheckStatus2=>(HealthCheckStatus2.IDLE="idle",HealthCheckStatus2.CONNECTING="connecting",HealthCheckStatus2.SUCCESS="success",HealthCheckStatus2.ERROR="error",HealthCheckStatus2))(HealthCheckStatus||{}),MCPContentType=(MCPContentType2=>(MCPContentType2.TEXT="text",MCPContentType2. +IMAGE="image",MCPContentType2.RESOURCE="resource",MCPContentType2))(MCPContentType||{}),JsonSchemaType=(JsonSchemaType2=>(JsonSchemaType2.OBJECT="object",JsonSchemaType2))(JsonSchemaType||{}),MCPRefType=(MCPRefType2=>(MCPRefType2.PROMPT="ref/prompt",MCPRefType2.RESOURCE="ref/resource",MCPRefType2))(MCPRefType||{}),ModelModality=(ModelModality2=>(ModelModality2.TEXT="TEXT",ModelModality2.AUDIO="AUDIO",ModelModality2.VISION="VISION",ModelModality2))(ModelModality||{}),ServerRole=(ServerRole2=>(ServerRole2. +MODEL="model",ServerRole2.ROUTER="router",ServerRole2))(ServerRole||{}),ServerModelStatus=(ServerModelStatus2=>(ServerModelStatus2.UNLOADED="unloaded",ServerModelStatus2.LOADING="loading",ServerModelStatus2.LOADED="loaded",ServerModelStatus2.SLEEPING="sleeping",ServerModelStatus2.FAILED="failed",ServerModelStatus2))(ServerModelStatus||{}),ParameterSource=(ParameterSource2=>(ParameterSource2.DEFAULT="default",ParameterSource2.CUSTOM="custom",ParameterSource2))(ParameterSource||{}),SyncableParameterType=(SyncableParameterType2=>(SyncableParameterType2. +NUMBER="number",SyncableParameterType2.STRING="string",SyncableParameterType2.BOOLEAN="boolean",SyncableParameterType2))(SyncableParameterType||{}),SettingsFieldType=(SettingsFieldType2=>(SettingsFieldType2.INPUT="input",SettingsFieldType2.TEXTAREA="textarea",SettingsFieldType2.CHECKBOX="checkbox",SettingsFieldType2.SELECT="select",SettingsFieldType2))(SettingsFieldType||{}),ColorMode=(ColorMode2=>(ColorMode2.LIGHT="light",ColorMode2.DARK="dark",ColorMode2.SYSTEM="system",ColorMode2))(ColorMode|| +{}),TooltipSide=(TooltipSide2=>(TooltipSide2.TOP="top",TooltipSide2.RIGHT="right",TooltipSide2.BOTTOM="bottom",TooltipSide2.LEFT="left",TooltipSide2))(TooltipSide||{}),McpPromptVariant=(McpPromptVariant2=>(McpPromptVariant2.MESSAGE="message",McpPromptVariant2.ATTACHMENT="attachment",McpPromptVariant2))(McpPromptVariant||{}),UrlProtocol=(UrlProtocol2=>(UrlProtocol2.DATA="data:",UrlProtocol2.HTTP="http:",UrlProtocol2.HTTPS="https:",UrlProtocol2.WEBSOCKET="ws:",UrlProtocol2.WEBSOCKET_SECURE="wss:", +UrlProtocol2))(UrlProtocol||{}),HtmlInputType=(HtmlInputType2=>(HtmlInputType2.FILE="file",HtmlInputType2))(HtmlInputType||{}),KeyboardKey=(KeyboardKey2=>(KeyboardKey2.ENTER="Enter",KeyboardKey2.ESCAPE="Escape",KeyboardKey2.ARROW_UP="ArrowUp",KeyboardKey2.ARROW_DOWN="ArrowDown",KeyboardKey2.ARROW_LEFT="ArrowLeft",KeyboardKey2.ARROW_RIGHT="ArrowRight",KeyboardKey2.TAB="Tab",KeyboardKey2.D_LOWER="d",KeyboardKey2.D_UPPER="D",KeyboardKey2.E_UPPER="E",KeyboardKey2.K_LOWER="k",KeyboardKey2.O_LOWER="o", +KeyboardKey2.O_UPPER="O",KeyboardKey2.SPACE=" ",KeyboardKey2))(KeyboardKey||{}),ToolSource=(ToolSource2=>(ToolSource2.BUILTIN="builtin",ToolSource2.MCP="mcp",ToolSource2.CUSTOM="custom",ToolSource2))(ToolSource||{}),ToolPermissionDecision=(ToolPermissionDecision2=>(ToolPermissionDecision2.ALWAYS="always",ToolPermissionDecision2.ALWAYS_SERVER="always_server",ToolPermissionDecision2.ONCE="once",ToolPermissionDecision2.DENY="deny",ToolPermissionDecision2))(ToolPermissionDecision||{}),ToolResponseField=(ToolResponseField2=>(ToolResponseField2. +PLAIN_TEXT="plain_text_response",ToolResponseField2.ERROR="error",ToolResponseField2))(ToolResponseField||{}),root_5$v=from_html("

"),root_1$19=from_html(" ",1);function ActionIcon($$anchor,$$props){push$1($$props,!0);let variant=prop($$props,"variant",3,"ghost"),size2=prop($$props,"size",3,"sm"),className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),iconSize=prop($$props,"iconSize",3,"h-3 w-3"),tooltipSide=prop($$props,"tooltipSide",19,()=>TooltipSide.TOP),stopPropagationOnClick=prop( +$$props,"stopPropagationOnClick",3,!1);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$5,($$anchor2,Tooltip_Root)=>{Tooltip_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$19(),node_1=first_child(fragment_1);component(node_1,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor4,{children:($$anchor5,$$slotProps2)=>{{let $0=user_derived(()=>$$props.ariaLabel||$$props.tooltip);Button($$anchor5,{get variant(){return variant()}, +get size(){return size2()},get disabled(){return disabled()},onclick:e=>{stopPropagationOnClick()&&e.stopPropagation(),$$props.onclick?.(e)},get class(){return`h-6 w-6 p-0 ${className()??""} flex hover:bg-transparent data-[state=open]:bg-transparent!`},get"aria-label"(){return get$3($0)},children:($$anchor6,$$slotProps3)=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);{var consequent=$$anchor7=>{const IconComponent=user_derived(()=>$$props.icon);var fragment_4=comment$2(),node_3=first_child( +fragment_4);component(node_3,()=>get$3(IconComponent),($$anchor8,IconComponent_1)=>{IconComponent_1($$anchor8,{get class(){return iconSize()}})}),append($$anchor7,fragment_4)};if_block(node_2,$$render=>{$$props.icon&&$$render(consequent)})}append($$anchor6,fragment_3)},$$slots:{default:!0}})}},$$slots:{default:!0}})});var node_4=sibling(node_1,2);component(node_4,()=>Tooltip_content,($$anchor4,Tooltip_Content)=>{Tooltip_Content($$anchor4,{get side(){return tooltipSide()},children:($$anchor5,$$slotProps2)=>{ +var p2=root_5$v(),text2=child(p2,!0);reset(p2),template_effect(()=>set_text(text2,$$props.tooltip)),append($$anchor5,p2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}const defaultAttributes={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};var root$1V=from_svg("");function Icon($$anchor,$$props){ +push$1($$props,!0);const color=prop($$props,"color",3,"currentColor"),size2=prop($$props,"size",3,24),strokeWidth=prop($$props,"strokeWidth",3,2),absoluteStrokeWidth=prop($$props,"absoluteStrokeWidth",3,!1),iconNode=prop($$props,"iconNode",19,()=>[]),props=rest_props($$props,["$$slots","$$events","$$legacy","name","color","size","strokeWidth","absoluteStrokeWidth","iconNode","children"]);var svg2=root$1V();attribute_effect(svg2,$0=>({...defaultAttributes,...props,width:size2(),height:size2(),stroke:color(), +"stroke-width":$0,class:["lucide-icon lucide",$$props.name&&`lucide-${$$props.name}`,$$props.class]}),[()=>absoluteStrokeWidth()?Number(strokeWidth())*24/Number(size2()):strokeWidth()]);var node2=child(svg2);each(node2,17,iconNode,index$2,($$anchor2,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let tag=()=>get$3($$array)[0],attrs=()=>get$3($$array)[1];var fragment=comment$2(),node_1=first_child(fragment);element$4(node_1,tag,!0,($$element,$$anchor3)=>{attribute_effect($$element, +()=>({...attrs()}))}),append($$anchor2,fragment)});var node_2=sibling(node2);snippet(node_2,()=>$$props.children??noop$3),reset(svg2),append($$anchor,svg2),pop()}function Arrow_big_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18v-6H5l7-7 7 7h-4v6H9z"}]];Icon($$anchor,spread_props({name:"arrow-big-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"m12 5 7 7-7 7"}]];Icon($$anchor,spread_props({name:"arrow-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2, +()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Arrow_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m5 12 7-7 7 7"}],["path",{d:"M12 19V5"}]];Icon($$anchor,spread_props({name:"arrow-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), +append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Book_open_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 7v14"}],["path",{d:"M16 12h2"}],["path",{d:"M16 8h2"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],["path",{d:"M6 12h2"}],["path",{d:"M6 8h2"}]];Icon($$anchor,spread_props({name:"bo\ +ok-open-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Braces($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"}],["path",{d:"M16 21h1a2 2 0 0 \ +0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"}]];Icon($$anchor,spread_props({name:"braces"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Brain($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 5a3 3 0 1 \ +0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"}],["path",{d:"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"}],["path",{d:"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"}],["path",{d:"M17.599 6.5a3 3 0 0 0 .399-1.375"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396"}],["path",{d:"M19.938 10.5a4 4 0 0 1 .585.396"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516"}],["path",{d:"M19.967 17.484A4 4 0 0\ + 1 18 18"}]];Icon($$anchor,spread_props({name:"brain"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Check($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 6 9 17l-5-5"}]];Icon($$anchor,spread_props({name:"check"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 9 6 6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_left($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15 18-6-6 6-6"}]];Icon($$anchor,spread_props({name:"chevron-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_up($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m18 15-6-6-6 6"}]];Icon($$anchor,spread_props({name:"chevron-up"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children?? +noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Chevron_right($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m9 18 6-6-6-6"}]];Icon($$anchor,spread_props({name:"chevron-right"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Chevrons_up_down($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m7 15 5 5 5-5"}],["path",{d:"m7 9 5-5 5 5"}]];Icon($$anchor,spread_props({name:"chevrons-up-down"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), +pop()}function Circle_alert($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"circle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( +$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_check_big($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335"}],["path",{d:"m9 11 3 3L22 4"}]];Icon($$anchor,spread_props({name:"circle-check-big"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), +append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Circle_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"m15 9-6 6"}],["path",{d:"m9 9 6 6"}]];Icon($$anchor,spread_props({name:"circle-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3), +append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Clock($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["polyline",{points:"12 6 12 12 16 14"}]];Icon($$anchor,spread_props({name:"clock"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, +fragment_1)},$$slots:{default:!0}})),pop()}function Code($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m16 18 6-6-6-6"}],["path",{d:"m8 6-6 6 6 6"}]];Icon($$anchor,spread_props({name:"code"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), +pop()}function Copy($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]];Icon($$anchor,spread_props({name:"copy"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2, +fragment_1)},$$slots:{default:!0}})),pop()}function Database($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5"}],["path",{d:"M3 12A9 3 0 0 0 21 12"}]];Icon($$anchor,spread_props({name:"database"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. +children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Download($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]];Icon($$anchor,spread_props({name:"download"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1); +snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Ellipsis($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]];Icon($$anchor,spread_props({name:"ellipsis"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function External_link($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 3h6v6"}],["path",{d:"M10 14 21 3"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}]];Icon($$anchor,spread_props({name:"external-link"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Eye($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"eye"},()=>props,{get iconNode(){ +return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_text($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"M10 9H8"}],["path",{d:"M16\ + 13H8"}],["path",{d:"M16 17H8"}]];Icon($$anchor,spread_props({name:"file-text"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File_x($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2\ + 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}],["path",{d:"m14.5 12.5-5 5"}],["path",{d:"m9.5 12.5 5 5"}]];Icon($$anchor,spread_props({name:"file-x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function File$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots", +"$$events","$$legacy"]);const iconNode=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4"}]];Icon($$anchor,spread_props({name:"file"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Flask_conical($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"}],["path",{d:"M6.453 15h11.094"}],["path",{d:"M8.5 2h7"}]];Icon($$anchor,spread_props({name:"flask-conical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Folder_open($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"}]];Icon($$anchor,spread_props({name:"folder-open"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Funnel($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"}]];Icon($$anchor,spread_props({name:"funnel"},()=>props,{get iconNode(){return iconNode}, +children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Gauge($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m12 14 4-4"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0"}]];Icon($$anchor,spread_props({name:"gauge"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Git_branch($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"6",x2:"6",y1:"3",y2:"15"}],["circle",{cx:"18",cy:"6",r:"3"}],["circle",{cx:"6",cy:"18",r:"3"}],["path",{d:"M18 9a9 9 0 0 1-9 9"}]];Icon($$anchor,spread_props({name:"git-branch"},()=>props,{ +get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Globe($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"}],["path",{d:"M2 12h20"}]];Icon($$anchor,spread_props( +{name:"globe"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["line",{x1:"2",y1:"2",x2:"22",y2:"22"}],["path",{d:"M16.5 16.5 12 21l-7-7c-1.5-1.45-3-3.2-3-5.5a5.5 5.5 0 0 1\ + 2.14-4.35"}],["path",{d:"M8.76 3.1c1.15.22 2.13.78 3.24 1.9 1.5-1.5 2.74-2 4.5-2A5.5 5.5 0 0 1 22 8.5c0 2.12-1.3 3.78-2.67 5.17"}]];Icon($$anchor,spread_props({name:"heart-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Heart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,[ +"$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"}]];Icon($$anchor,spread_props({name:"heart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Image$1($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]];Icon($$anchor,spread_props({name:"image"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, +$$slots:{default:!0}})),pop()}function Info$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 16v-4"}],["path",{d:"M12 8h.01"}]];Icon($$anchor,spread_props({name:"info"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, +$$slots:{default:!0}})),pop()}function Key($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"}],["path",{d:"m21 2-9.6 9.6"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5"}]];Icon($$anchor,spread_props({name:"key"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. +children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Layers($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 \ +0 0 22 17"}]];Icon($$anchor,spread_props({name:"layers"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_checks($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m3 17 2 2 4-4"}],["path",{d:"m3 7 2 2 4-4"}],["path",{ +d:"M13 6h8"}],["path",{d:"M13 12h8"}],["path",{d:"M13 18h8"}]];Icon($$anchor,spread_props({name:"list-checks"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function List_restart($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"\ +M21 6H3"}],["path",{d:"M7 12H3"}],["path",{d:"M7 18H3"}],["path",{d:"M12 18a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L11 14"}],["path",{d:"M11 10v4h4"}]];Icon($$anchor,spread_props({name:"list-restart"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Loader_circle($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]];Icon($$anchor,spread_props({name:"loader-circle"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Message_square($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"}]];Icon($$anchor,spread_props({name:"message-square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Mic($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, +["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2"}],["line",{x1:"12",x2:"12",y1:"19",y2:"22"}]];Icon($$anchor,spread_props({name:"mic"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Minus($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}]];Icon($$anchor,spread_props({name:"minus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Monitor($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","\ +$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21"}]];Icon($$anchor,spread_props({name:"monitor"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Moon($$anchor,$$props){push$1( +$$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"}]];Icon($$anchor,spread_props({name:"moon"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Music($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props, +["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9 18V5l12-2v13"}],["circle",{cx:"6",cy:"18",r:"3"}],["circle",{cx:"18",cy:"16",r:"3"}]];Icon($$anchor,spread_props({name:"music"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Package($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"}],["path",{d:"M12 22V12"}],["polyline",{points:"3.29 7 12 12 20.71 7"}],["path",{d:"m7.5 4.27 9 5.15"}]];Icon($$anchor,spread_props({name:"package"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props. +children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left_close($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}],["path",{d:"m16 15-3-3 3-3"}]];Icon($$anchor,spread_props({name:"panel-left-close"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Panel_left($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}],["path",{d:"M9 3v18"}]];Icon($$anchor,spread_props({name:"panel-left"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil_ruler($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"}],["path",{d:"m8 6 2-2"}],["path",{d:"m18 16 2-2"}],["path",{d:"m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"}],["path",{d:"M21.174 6\ +.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil-ruler"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Pencil($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"}],["path",{d:"m15 5 4 4"}]];Icon($$anchor,spread_props({name:"pencil"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), +pop()}function Plus($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M5 12h14"}],["path",{d:"M12 5v14"}]];Icon($$anchor,spread_props({name:"plus"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Power_off($$anchor,$$props){ +push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15"}],["path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68"}],["path",{d:"M12 2v4"}],["path",{d:"m2 2 20 20"}]];Icon($$anchor,spread_props({name:"power-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Power($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 2v10"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04"}]];Icon($$anchor,spread_props({name:"power"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()} +function Radio($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4.9 19.1C1 15.2 1 8.8 4.9 4.9"}],["path",{d:"M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"}],["circle",{cx:"12",cy:"12",r:"2"}],["path",{d:"M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"}],["path",{d:"M19.1 4.9C23 8.8 23 15.1 19.1 19"}]];Icon($$anchor,spread_props({name:"radio"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Refresh_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]];Icon($$anchor,spread_props({name:"refresh-cw"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_ccw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"}],["path",{d:"M3 3v5h5"}]];Icon($$anchor,spread_props({name:"\ +rotate-ccw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Rotate_cw($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}]];Icon($$anchor,spread_props( +{name:"rotate-cw"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Search($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]];Icon($$anchor,spread_props({name:"search"}, +()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Server($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2"}],["line",{ +x1:"6",x2:"6.01",y1:"6",y2:"6"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18"}]];Icon($$anchor,spread_props({name:"server"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Settings$1($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["p\ +ath",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0\ +-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["circle",{cx:"12",cy:"12",r:"3"}]];Icon($$anchor,spread_props({name:"settings"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$ev\ +ents","$$legacy"]);const iconNode=[["path",{d:"m2 2 20 20"}],["path",{d:"M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71"}],["path",{d:"M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264"}]];Icon($$anchor,spread_props({name:"shield-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet( +node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield_question($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3"}],["path",{d:"M12 17h.01"}]];Icon($$anchor, +spread_props({name:"shield-question"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Shield($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 \ +1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}]];Icon($$anchor,spread_props({name:"shield"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sliders_vertical($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[ +["line",{x1:"4",x2:"4",y1:"21",y2:"14"}],["line",{x1:"4",x2:"4",y1:"10",y2:"3"}],["line",{x1:"12",x2:"12",y1:"21",y2:"12"}],["line",{x1:"12",x2:"12",y1:"8",y2:"3"}],["line",{x1:"20",x2:"20",y1:"21",y2:"16"}],["line",{x1:"20",x2:"20",y1:"12",y2:"3"}],["line",{x1:"2",x2:"6",y1:"14",y2:"14"}],["line",{x1:"10",x2:"14",y1:"8",y2:"8"}],["line",{x1:"18",x2:"22",y1:"16",y2:"16"}]];Icon($$anchor,spread_props({name:"sliders-vertical"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{ +var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Sparkles($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 1\ +4.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"}],["path",{d:"M20 3v4"}],["path",{d:"M22 5h-4"}],["path",{d:"M4 17v2"}],["path",{d:"M5 18H3"}]];Icon($$anchor,spread_props({name:"sparkles"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square_pen($$anchor,$$props){push$1($$props,!0);let props=rest_props( +$$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}],["path",{d:"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"}]];Icon($$anchor,spread_props({name:"square-pen"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append( +$$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Square($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2"}]];Icon($$anchor,spread_props({name:"square"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{ +default:!0}})),pop()}function Sun($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"12",cy:"12",r:"4"}],["path",{d:"M12 2v2"}],["path",{d:"M12 20v2"}],["path",{d:"m4.93 4.93 1.41 1.41"}],["path",{d:"m17.66 17.66 1.41 1.41"}],["path",{d:"M2 12h2"}],["path",{d:"M20 12h2"}],["path",{d:"m6.34 17.66-1.41 1.41"}],["path",{d:"m19.07 4.93-1.41 1.41"}]];Icon($$anchor,spread_props({name:"sun"},()=>props,{get iconNode(){return iconNode}, +children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Timer_off($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M10 2h4"}],["path",{d:"M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7"}],["path",{d:"M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0 0 1 .9 10.2"}],["path",{d:"m2 2 20 20"}],[ +"path",{d:"M12 12v-2"}]];Icon($$anchor,spread_props({name:"timer-off"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Trash_2($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M3 6h18"}],["path",{d:"M19 6v14c0 1-1 2-2 2H\ +7c-1 0-2-1-2-2V6"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17"}]];Icon($$anchor,spread_props({name:"trash-2"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Triangle_alert($$anchor,$$props){push$1($$props,!0); +let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"}],["path",{d:"M12 9v4"}],["path",{d:"M12 17h.01"}]];Icon($$anchor,spread_props({name:"triangle-alert"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})), +pop()}function Upload($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M12 3v12"}],["path",{d:"m17 8-5-5-5 5"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}]];Icon($$anchor,spread_props({name:"upload"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)}, +$$slots:{default:!0}})),pop()}function Whole_word($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["circle",{cx:"7",cy:"12",r:"3"}],["path",{d:"M10 9v6"}],["circle",{cx:"17",cy:"12",r:"3"}],["path",{d:"M14 7v8"}],["path",{d:"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1"}]];Icon($$anchor,spread_props({name:"whole-word"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child( +fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Wrench($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"}]];Icon($$anchor,spread_props({name:"wrench"},()=>props,{get iconNode(){return iconNode}, +children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function X($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]];Icon($$anchor,spread_props({name:"x"},()=>props,{get iconNode(){return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(), +node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}function Zap($$anchor,$$props){push$1($$props,!0);let props=rest_props($$props,["$$slots","$$events","$$legacy"]);const iconNode=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}]];Icon($$anchor,spread_props({name:"zap"},()=>props,{get iconNode(){ +return iconNode},children:($$anchor2,$$slotProps)=>{var fragment_1=comment$2(),node2=first_child(fragment_1);snippet(node2,()=>$$props.children??noop$3),append($$anchor2,fragment_1)},$$slots:{default:!0}})),pop()}const defaultWindow$2=typeof window<"u"?window:void 0;function getActiveElement$2(document2){let activeElement=document2.activeElement;for(;activeElement?.shadowRoot;){const node2=activeElement.shadowRoot.activeElement;if(node2===activeElement)break;activeElement=node2}return activeElement} +let ActiveElement$2=class{#document;#subscribe;constructor(options={}){const{window:window2=defaultWindow$2,document:document2=window2?.document}=options;window2!==void 0&&(this.#document=document2,this.#subscribe=createSubscriber(update2=>{const cleanupFocusIn=on(window2,"focusin",update2),cleanupFocusOut=on(window2,"focusout",update2);return()=>{cleanupFocusIn(),cleanupFocusOut()}}))}get current(){return this.#subscribe?.(),this.#document?getActiveElement$2(this.#document):null}};new ActiveElement$2; +function runEffect(flush,effect2){switch(flush){case"post":user_effect(effect2);break;case"pre":user_pre_effect(effect2);break}}function runWatcher(sources,flush,effect2,options={}){const{lazy=!1}=options;let active=!lazy,previousValues=Array.isArray(sources)?[]:void 0;runEffect(flush,()=>{const values=Array.isArray(sources)?sources.map(source2=>source2()):sources();if(!active){active=!0,previousValues=values;return}const cleanup=untrack$1(()=>effect2(values,previousValues));return previousValues= +values,cleanup})}function watch(sources,effect2,options){runWatcher(sources,"post",effect2,options)}function watchPre(sources,effect2,options){runWatcher(sources,"pre",effect2,options)}watch.pre=watchPre;function getStorage(storageType,window2){switch(storageType){case"local":return window2.localStorage;case"session":return window2.sessionStorage}}class PersistedState{#current;#key;#serializer;#storage;#subscribe;#version=state$1(0);constructor(key2,initialValue,options={}){const{storage:storageType="\ +local",serializer:serializer2={serialize:JSON.stringify,deserialize:JSON.parse},syncTabs=!0,window:window2=defaultWindow$2}=options;if(this.#current=initialValue,this.#key=key2,this.#serializer=serializer2,window2===void 0)return;const storage=getStorage(storageType,window2);this.#storage=storage;const existingValue=storage.getItem(key2);existingValue!==null?this.#current=this.#deserialize(existingValue):this.#serialize(initialValue),syncTabs&&storageType==="local"&&(this.#subscribe=createSubscriber( +()=>on(window2,"storage",this.#handleStorageEvent)))}get current(){this.#subscribe?.(),get$3(this.#version);const root2=this.#deserialize(this.#storage?.getItem(this.#key))??this.#current,proxies=new WeakMap,proxy2=value=>{if(value===null||value?.constructor.name==="Date"||typeof value!="object")return value;let p2=proxies.get(value);return p2||(p2=new Proxy(value,{get:(target2,property)=>(get$3(this.#version),proxy2(Reflect.get(target2,property))),set:(target2,property,value2)=>(set$1(this.#version, +get$3(this.#version)+1),Reflect.set(target2,property,value2),this.#serialize(root2),!0)}),proxies.set(value,p2)),p2};return proxy2(root2)}set current(newValue){this.#serialize(newValue),set$1(this.#version,get$3(this.#version)+1)}#handleStorageEvent=event2=>{event2.key!==this.#key||event2.newValue===null||(this.#current=this.#deserialize(event2.newValue),set$1(this.#version,get$3(this.#version)+1))};#deserialize(value){try{return this.#serializer.deserialize(value)}catch(error2){console.error(`E\ +rror when parsing "${value}" from persisted store "${this.#key}"`,error2);return}}#serialize(value){try{value!=null&&this.#storage?.setItem(this.#key,this.#serializer.serialize(value))}catch(error2){console.error(`Error when writing value from persisted store "${this.#key}" to ${this.#storage}`,error2)}}}function sanitizeClassNames(classNames){return classNames.filter(className=>className.length>0)}const noopStorage={getItem:_key=>null,setItem:(_key,_value)=>{}},isBrowser$1=typeof document<"u";function isFunction(value){ +return typeof value=="function"}function isObject$1(value){return value!==null&&typeof value=="object"}const BoxSymbol=Symbol("box"),isWritableSymbol=Symbol("is-writable");function isBox(value){return isObject$1(value)&&BoxSymbol in value}function isWritableBox(value){return box.isBox(value)&&isWritableSymbol in value}function box(initialValue){let current2=state$1(proxy(initialValue));return{[BoxSymbol]:!0,[isWritableSymbol]:!0,get current(){return get$3(current2)},set current(v){set$1(current2, +v,!0)}}}function boxWith(getter,setter){const derived2=user_derived(getter);return setter?{[BoxSymbol]:!0,[isWritableSymbol]:!0,get current(){return get$3(derived2)},set current(v){setter(v)}}:{[BoxSymbol]:!0,get current(){return getter()}}}function boxFrom(value){return box.isBox(value)?value:isFunction(value)?box.with(value):box(value)}function boxFlatten(boxes){return Object.entries(boxes).reduce((acc,[key2,b])=>box.isBox(b)?(box.isWritableBox(b)?Object.defineProperty(acc,key2,{get(){return b. +current},set(v){b.current=v}}):Object.defineProperty(acc,key2,{get(){return b.current}}),acc):Object.assign(acc,{[key2]:b}),{})}function toReadonlyBox(b){return box.isWritableBox(b)?{[BoxSymbol]:!0,get current(){return b.current}}:b}box.from=boxFrom;box.with=boxWith;box.flatten=boxFlatten;box.readonly=toReadonlyBox;box.isBox=isBox;box.isWritableBox=isWritableBox;function createParser$1(matcher,replacer){const regex=RegExp(matcher,"g");return str=>{if(typeof str!="string")throw new TypeError(`exp\ +ected an argument of type string, but got ${typeof str}`);return str.match(regex)?str.replace(regex,replacer):str}}const camelToKebab=createParser$1(/[A-Z]/,match=>`-${match.toLowerCase()}`);function styleToCSS(styleObj){if(!styleObj||typeof styleObj!="object"||Array.isArray(styleObj))throw new TypeError(`expected an argument of type object, but got ${typeof styleObj}`);return Object.keys(styleObj).map(property=>`${camelToKebab(property)}: ${styleObj[property]};`).join(` `)}function styleToString(style2={}){return styleToCSS(style2).replace(` `," ")}const srOnlyStyles={position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",transform:"translateX(-100%)"};styleToString(srOnlyStyles);const defaultWindow$1=typeof window<"u"?window:void 0;function getActiveElement$1(document2){let activeElement=document2.activeElement;for(;activeElement?.shadowRoot;){const node2=activeElement.shadowRoot.activeElement;if(node2===activeElement)break;activeElement=node2} return activeElement}let ActiveElement$1=class{#document;#subscribe;constructor(options={}){const{window:window2=defaultWindow$1,document:document2=window2?.document}=options;window2!==void 0&&(this.#document=document2,this.#subscribe=createSubscriber(update2=>{const cleanupFocusIn=on(window2,"focusin",update2),cleanupFocusOut=on(window2,"focusout",update2);return()=>{cleanupFocusIn(),cleanupFocusOut()}}))}get current(){return this.#subscribe?.(),this.#document?getActiveElement$1(this.#document): @@ -6776,22 +6778,24 @@ next$1(),append($$anchor5,fragment_8)},$$slots:{default:!0}})};if_block(node_1,$ get$3(promptPickerRef)?.handleKeydown(event2)||$$props.isInlineResourcePickerOpen&&get$3(resourcePickerRef)?.handleKeydown(event2))}var $$exports={handleKeydown},fragment=root$1h(),node2=first_child(fragment);bind_this(ChatFormPickerMcpPrompts(node2,{get isOpen(){return $$props.isPromptPickerOpen},get searchQuery(){return $$props.promptSearchQuery},get onClose(){return $$props.onPromptPickerClose},get onPromptLoadStart(){return $$props.onPromptLoadStart},get onPromptLoadComplete(){return $$props. onPromptLoadComplete},get onPromptLoadError(){return $$props.onPromptLoadError}}),$$value=>set$1(promptPickerRef,$$value,!0),()=>get$3(promptPickerRef));var node_1=sibling(node2,2);return bind_this(ChatFormPickerMcpResources(node_1,{get isOpen(){return $$props.isInlineResourcePickerOpen},get searchQuery(){return $$props.resourceSearchQuery},get onClose(){return $$props.onInlineResourcePickerClose},get onResourceSelect(){return $$props.onInlineResourceSelect},get onBrowse(){return $$props.onInlineResourceBrowse}}), $$value=>set$1(resourcePickerRef,$$value,!0),()=>get$3(resourcePickerRef)),append($$anchor,fragment),pop($$exports)}const MESSAGE_EDIT_KEY=Symbol.for(CONTEXT_KEY_MESSAGE_EDIT);function setMessageEditContext(ctx){return setContext(MESSAGE_EDIT_KEY,ctx)}function getMessageEditContext(){return getContext(MESSAGE_EDIT_KEY)}const CHAT_ACTIONS_KEY=Symbol.for(CONTEXT_KEY_CHAT_ACTIONS);function setChatActionsContext(ctx){return setContext(CHAT_ACTIONS_KEY,ctx)}function getChatActionsContext(){return getContext( -CHAT_ACTIONS_KEY)}const CHAT_SETTINGS_CONFIG_KEY=Symbol.for(CONTEXT_KEY_CHAT_SETTINGS_CONFIG);function setChatSettingsConfigContext(ctx){return setContext(CHAT_SETTINGS_CONFIG_KEY,ctx)}const PROCESSING_INFO_KEY=Symbol.for(CONTEXT_KEY_PROCESSING_INFO);function setProcessingInfoContext(ctx){return setContext(PROCESSING_INFO_KEY,ctx)}function getProcessingInfoContext(){return getContext(PROCESSING_INFO_KEY)}var root$1g=from_html(" ",1);function ChatMessages($$anchor,$$props){push$1($$props,!0); -let messages=prop($$props,"messages",19,()=>[]),allConversationMessages=state$1(proxy([]));const currentConfig=config$1();setChatActionsContext({copy:async message=>{const asPlainText=!!currentConfig.copyTextAttachmentsAsPlainText,clipboardContent=formatMessageForClipboard(message.content,message.extra,asPlainText);await copyToClipboard(clipboardContent,"Message copied to clipboard")},delete:async message=>{await chatStore.deleteMessage(message.id),refreshAllMessages()},navigateToSibling:async siblingId=>{ -await conversationsStore.navigateToSibling(siblingId)},editWithBranching:async(message,newContent,newExtras)=>{$$props.onUserAction?.(),await chatStore.editMessageWithBranching(message.id,newContent,newExtras),refreshAllMessages()},editWithReplacement:async(message,newContent,shouldBranch)=>{$$props.onUserAction?.(),await chatStore.editAssistantMessage(message.id,newContent,shouldBranch),refreshAllMessages()},editUserMessagePreserveResponses:async(message,newContent,newExtras)=>{$$props.onUserAction?.(), -await chatStore.editUserMessagePreserveResponses(message.id,newContent,newExtras),refreshAllMessages()},regenerateWithBranching:async(message,modelOverride)=>{$$props.onUserAction?.(),await chatStore.regenerateMessageWithBranching(message.id,modelOverride),refreshAllMessages()},continueAssistantMessage:async message=>{$$props.onUserAction?.(),await chatStore.continueAssistantMessage(message.id),refreshAllMessages()},forkConversation:async(message,options)=>{await conversationsStore.forkConversation( -message.id,options)}});function refreshAllMessages(){const conversation=activeConversation();conversation?conversationsStore.getConversationMessages(conversation.id).then(messages2=>{set$1(allConversationMessages,messages2,!0)}):set$1(allConversationMessages,[],!0)}user_effect(()=>{activeConversation()&&refreshAllMessages()});let displayMessages=user_derived(()=>{if(!messages().length)return[];const filteredMessages=currentConfig.showSystemMessage?messages():messages().filter(msg=>msg.type!==MessageRole. -SYSTEM),result=[];for(let i=0;i=0;i--)if(result[i].message.role===MessageRole.ASSISTANT){result[i].isLastAssistantMessage=!0;break}return result});var fragment=root$1g(),node2=first_child( -fragment);each(node2,17,()=>get$3(displayMessages),({message,toolMessages,isLastAssistantMessage,siblingInfo})=>message.id,($$anchor2,$$item)=>{let message=()=>get$3($$item).message,toolMessages=()=>get$3($$item).toolMessages,isLastAssistantMessage=()=>get$3($$item).isLastAssistantMessage,siblingInfo=()=>get$3($$item).siblingInfo;ChatMessage($$anchor2,{class:"mx-auto mt-12 w-full max-w-[48rem]",get message(){return message()},get toolMessages(){return toolMessages()},get isLastAssistantMessage(){ -return isLastAssistantMessage()},get siblingInfo(){return siblingInfo()}})});var node_1=sibling(node2,2);{var consequent_1=$$anchor2=>{const convId=user_derived(()=>activeConversation().id),pendingContent=user_derived(()=>agenticPendingSteeringMessageContent(get$3(convId)));var fragment_2=comment$2(),node_2=first_child(fragment_2);{var consequent=$$anchor3=>{{let $0=user_derived(()=>agenticPendingSteeringMessageExtras(get$3(convId)));ChatMessageUserPending($$anchor3,{class:"mx-auto mt-12 w-full \ -max-w-[48rem]",get content(){return get$3(pendingContent)},get extras(){return get$3($0)},onSendImmediately:()=>chatStore.abortCurrentFlow(get$3(convId)),onEdit:(newContent,extras)=>agenticInjectSteeringMessage(get$3(convId),newContent,extras),onDelete:()=>agenticClearSteeringMessage(get$3(convId))})}};if_block(node_2,$$render=>{get$3(pendingContent)&&$$render(consequent)})}append($$anchor2,fragment_2)},d2=user_derived(()=>activeConversation()&&agenticPendingSteeringMessageContent(activeConversation(). -id)),consequent_3=$$anchor2=>{const convId=user_derived(()=>activeConversation().id),pendingContent=user_derived(()=>chatPendingMessageContent(get$3(convId)));var fragment_4=comment$2(),node_3=first_child(fragment_4);{var consequent_2=$$anchor3=>{{let $0=user_derived(()=>chatPendingMessageExtras(get$3(convId)));ChatMessageUserPending($$anchor3,{class:"mx-auto mt-12 w-full max-w-[48rem]",get content(){return get$3(pendingContent)},get extras(){return get$3($0)},onSendImmediately:()=>chatStore.abortCurrentFlow( -get$3(convId)),onEdit:(newContent,extras)=>chatInjectPendingMessage(get$3(convId),newContent,extras),onDelete:()=>chatClearPendingMessage(get$3(convId))})}};if_block(node_3,$$render=>{get$3(pendingContent)&&$$render(consequent_2)})}append($$anchor2,fragment_4)},d_12=user_derived(()=>activeConversation()&&chatPendingMessageContent(activeConversation().id));if_block(node_1,$$render=>{get$3(d2)?$$render(consequent_1):get$3(d_12)&&$$render(consequent_3,1)})}append($$anchor,fragment),pop()}function isElementInViewport(node2){ -const rect=node2.getBoundingClientRect();return rect.top0&&rect.left0}function fadeInView(node2,options={}){const{duration:duration2=300,y=0,skipIfVisible=!1}=options;skipIfVisible&&isElementInViewport(node2)||(node2.style.opacity="0",node2.style.transform=`translateY(${y}px)`,node2.style.transition=`opacity ${duration2}ms ease-out, transform ${duration2}ms ease-out`,user_effect(()=>{const observer=new IntersectionObserver(entries=>{ -for(const entry of entries)entry.isIntersecting&&(requestAnimationFrame(()=>{node2.style.opacity="1",node2.style.transform="translateY(0)"}),observer.disconnect())},{threshold:.05});return observer.observe(node2),()=>{observer.disconnect()}}))}var root$1f=from_html("
");function ChatMessage($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),toolMessages=prop($$props,"toolMessages",19,()=>[]),isLastAssistantMessage=prop($$props,"isLastAssistantMessage",3,!1), -siblingInfo=prop($$props,"siblingInfo",3,null);const chatActions=getChatActionsContext();let deletionInfo=state$1(null),editedContent=user_derived(()=>$$props.message.content),rawEditContent=user_derived(()=>{if($$props.message.role!==MessageRole.ASSISTANT)return;const sections=deriveAgenticSections($$props.message,toolMessages(),[],!1),parts=[];for(const section of sections)switch(section.type){case AgenticSectionType.REASONING:case AgenticSectionType.REASONING_PENDING:parts.push(`${REASONING_TAGS. -START} +CHAT_ACTIONS_KEY)}const CHAT_SETTINGS_CONFIG_KEY=Symbol.for(CONTEXT_KEY_CHAT_SETTINGS_CONFIG);function setChatSettingsConfigContext(ctx){return setContext(CHAT_SETTINGS_CONFIG_KEY,ctx)}const PROCESSING_INFO_KEY=Symbol.for(CONTEXT_KEY_PROCESSING_INFO);function setProcessingInfoContext(ctx){return setContext(PROCESSING_INFO_KEY,ctx)}function getProcessingInfoContext(){return getContext(PROCESSING_INFO_KEY)}var root$1g=from_html("
");function ChatMessages($$anchor,$$props){push$1($$props, +!0);let messages=prop($$props,"messages",19,()=>[]),allConversationMessages=state$1(proxy([])),isVisible=state$1(!1),previousConversationId=state$1(null);const currentConfig=config$1();setChatActionsContext({copy:async message=>{const asPlainText=!!currentConfig.copyTextAttachmentsAsPlainText,clipboardContent=formatMessageForClipboard(message.content,message.extra,asPlainText);await copyToClipboard(clipboardContent,"Message copied to clipboard")},delete:async message=>{await chatStore.deleteMessage( +message.id),refreshAllMessages()},navigateToSibling:async siblingId=>{await conversationsStore.navigateToSibling(siblingId)},editWithBranching:async(message,newContent,newExtras)=>{$$props.onUserAction?.(),await chatStore.editMessageWithBranching(message.id,newContent,newExtras),refreshAllMessages()},editWithReplacement:async(message,newContent,shouldBranch)=>{$$props.onUserAction?.(),await chatStore.editAssistantMessage(message.id,newContent,shouldBranch),refreshAllMessages()},editUserMessagePreserveResponses:async(message,newContent,newExtras)=>{ +$$props.onUserAction?.(),await chatStore.editUserMessagePreserveResponses(message.id,newContent,newExtras),refreshAllMessages()},regenerateWithBranching:async(message,modelOverride)=>{$$props.onUserAction?.(),await chatStore.regenerateMessageWithBranching(message.id,modelOverride),refreshAllMessages()},continueAssistantMessage:async message=>{$$props.onUserAction?.(),await chatStore.continueAssistantMessage(message.id),refreshAllMessages()},forkConversation:async(message,options)=>{await conversationsStore. +forkConversation(message.id,options)}});function refreshAllMessages(){const conversation=activeConversation();conversation?conversationsStore.getConversationMessages(conversation.id).then(messages2=>{set$1(allConversationMessages,messages2,!0)}):set$1(allConversationMessages,[],!0)}user_effect(()=>{const conversation=activeConversation(),currentId=conversation?.id??null;currentId!==get$3(previousConversationId)&&get$3(previousConversationId)!==null?(set$1(isVisible,!1),requestAnimationFrame(()=>{ +refreshAllMessages(),set$1(previousConversationId,currentId,!0),requestAnimationFrame(()=>{set$1(isVisible,!0)})})):(set$1(previousConversationId,currentId,!0),conversation&&refreshAllMessages())}),user_effect(()=>{get$3(allConversationMessages),$$props.onMessagesReady?.(get$3(displayMessages).length)}),onMount$1(()=>{requestAnimationFrame(()=>{set$1(isVisible,!0)})}),beforeNavigate(()=>{set$1(isVisible,!1)}),afterNavigate(()=>{requestAnimationFrame(()=>{set$1(isVisible,!0)})});let displayMessages=user_derived( +()=>{if(!messages().length)return[];const filteredMessages=currentConfig.showSystemMessage?messages():messages().filter(msg=>msg.type!==MessageRole.SYSTEM),result=[];for(let i=0;i=0;i--)if(result[i].message.role===MessageRole.ASSISTANT){result[i].isLastAssistantMessage=!0;break}return result});var div=root$1g(),node2=child(div);each(node2,17,()=>get$3(displayMessages),({message,toolMessages,isLastAssistantMessage,siblingInfo})=>message.id,($$anchor2,$$item)=>{let message=()=>get$3($$item).message,toolMessages=()=>get$3($$item).toolMessages,isLastAssistantMessage=()=>get$3($$item).isLastAssistantMessage,siblingInfo=()=>get$3($$item).siblingInfo;ChatMessage($$anchor2,{ +class:"mx-auto mt-12 w-full max-w-[48rem]",get message(){return message()},get toolMessages(){return toolMessages()},get isLastAssistantMessage(){return isLastAssistantMessage()},get siblingInfo(){return siblingInfo()}})});var node_1=sibling(node2,2);{var consequent_1=$$anchor2=>{const convId=user_derived(()=>activeConversation().id),pendingContent=user_derived(()=>agenticPendingSteeringMessageContent(get$3(convId)));var fragment_1=comment$2(),node_2=first_child(fragment_1);{var consequent=$$anchor3=>{ +{let $0=user_derived(()=>agenticPendingSteeringMessageExtras(get$3(convId)));ChatMessageUserPending($$anchor3,{class:"mx-auto mt-12 w-full max-w-[48rem]",get content(){return get$3(pendingContent)},get extras(){return get$3($0)},onSendImmediately:()=>chatStore.abortCurrentFlow(get$3(convId)),onEdit:(newContent,extras)=>agenticInjectSteeringMessage(get$3(convId),newContent,extras),onDelete:()=>agenticClearSteeringMessage(get$3(convId))})}};if_block(node_2,$$render=>{get$3(pendingContent)&&$$render( +consequent)})}append($$anchor2,fragment_1)},d2=user_derived(()=>activeConversation()&&agenticPendingSteeringMessageContent(activeConversation().id)),consequent_3=$$anchor2=>{const convId=user_derived(()=>activeConversation().id),pendingContent=user_derived(()=>chatPendingMessageContent(get$3(convId)));var fragment_3=comment$2(),node_3=first_child(fragment_3);{var consequent_2=$$anchor3=>{{let $0=user_derived(()=>chatPendingMessageExtras(get$3(convId)));ChatMessageUserPending($$anchor3,{class:"mx\ +-auto mt-12 w-full max-w-[48rem]",get content(){return get$3(pendingContent)},get extras(){return get$3($0)},onSendImmediately:()=>chatStore.abortCurrentFlow(get$3(convId)),onEdit:(newContent,extras)=>chatInjectPendingMessage(get$3(convId),newContent,extras),onDelete:()=>chatClearPendingMessage(get$3(convId))})}};if_block(node_3,$$render=>{get$3(pendingContent)&&$$render(consequent_2)})}append($$anchor2,fragment_3)},d_12=user_derived(()=>activeConversation()&&chatPendingMessageContent(activeConversation(). +id));if_block(node_1,$$render=>{get$3(d2)?$$render(consequent_1):get$3(d_12)&&$$render(consequent_3,1)})}reset(div),template_effect(()=>set_class(div,1,`transition-opacity delay-300 duration-500 ease-out + ${get$3(isVisible)?"opacity-100":"opacity-0"}`)),append($$anchor,div),pop()}function isElementInViewport(node2){const rect=node2.getBoundingClientRect();return rect.top0&&rect.left0}function fadeInView(node2,options={}){const{duration:duration2=300,y=0,skipIfVisible=!1}=options;skipIfVisible&&isElementInViewport(node2)||(node2.style.opacity="0",node2.style.transform=`translateY(${y}px)`,node2.style.transition=`opacity ${duration2}\ +ms ease-out, transform ${duration2}ms ease-out`,user_effect(()=>{const observer=new IntersectionObserver(entries=>{for(const entry of entries)entry.isIntersecting&&(requestAnimationFrame(()=>{node2.style.opacity="1",node2.style.transform="translateY(0)"}),observer.disconnect())},{threshold:.05});return observer.observe(node2),()=>{observer.disconnect()}}))}var root$1f=from_html("
");function ChatMessage($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),toolMessages=prop( +$$props,"toolMessages",19,()=>[]),isLastAssistantMessage=prop($$props,"isLastAssistantMessage",3,!1),siblingInfo=prop($$props,"siblingInfo",3,null);const chatActions=getChatActionsContext();let deletionInfo=state$1(null),editedContent=user_derived(()=>$$props.message.content),rawEditContent=user_derived(()=>{if($$props.message.role!==MessageRole.ASSISTANT)return;const sections=deriveAgenticSections($$props.message,toolMessages(),[],!1),parts=[];for(const section of sections)switch(section.type){case AgenticSectionType. +REASONING:case AgenticSectionType.REASONING_PENDING:parts.push(`${REASONING_TAGS.START} ${section.content} ${REASONING_TAGS.END}`);break;case AgenticSectionType.TEXT:parts.push(section.content);break;case AgenticSectionType.TOOL_CALL:case AgenticSectionType.TOOL_CALL_PENDING:case AgenticSectionType.TOOL_CALL_STREAMING:{const callObj={name:section.toolName};if(section.toolArgs)try{callObj.arguments=JSON.parse(section.toolArgs)}catch{callObj.arguments=section.toolArgs}parts.push(JSON.stringify(callObj,null,2)),section.toolResult&&parts.push(`[Tool Result] ${section.toolResult}`);break}}return parts.join(` @@ -7001,130 +7005,130 @@ $$slots:{default:!0}}),reset(div_7),append($$anchor5,div_7)};if_block(node_11,$$ ()=>set_class(button,1,`group/expand w-full text-left ${!get$3(isExpanded)&&get$3(showExpandButton)?"cursor-pointer":"cursor-auto"}`)),delegated("click",button,function(...$$args){(get$3(showExpandButton)&&!get$3(isExpanded)?toggleExpand:void 0)?.apply(this,$$args)}),append($$anchor3,div_3)},d2=user_derived(()=>$$props.message.content.trim());if_block(node_5,$$render=>{get$3(d2)&&$$render(consequent_4)})}var node_13=sibling(node_5,2);{var consequent_5=$$anchor3=>{var div_8=root_13$8(),node_14=child( div_8);ChatMessageActionIcons(node_14,{actionsPosition:"right",get deletionInfo(){return $$props.deletionInfo},justify:"end",get onConfirmDelete(){return $$props.onConfirmDelete},get onCopy(){return $$props.onCopy},get onDelete(){return $$props.onDelete},get onEdit(){return $$props.onEdit},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props. showDeleteDialog},get role(){return MessageRole.USER}}),reset(div_8),append($$anchor3,div_8)};if_block(node_13,$$render=>{$$props.message.timestamp&&$$render(consequent_5)})}append($$anchor2,fragment_2)};if_block(node2,$$render=>{editCtx.isEditing?$$render(consequent):$$render(alternate_1,-1)})}reset(div),template_effect(()=>set_class(div,1,`group flex flex-col items-end gap-3 md:gap-2 ${className()??""}`)),append($$anchor,div),pop()}delegate(["keydown","input","click"]);class AutoScrollController{#_autoScrollEnabled=state$1( -!0);get _autoScrollEnabled(){return get$3(this.#_autoScrollEnabled)}set _autoScrollEnabled(value){set$1(this.#_autoScrollEnabled,value,!0)}#_userScrolledUp=state$1(!1);get _userScrolledUp(){return get$3(this.#_userScrolledUp)}set _userScrolledUp(value){set$1(this.#_userScrolledUp,value,!0)}#_lastScrollTop=state$1(0);get _lastScrollTop(){return get$3(this.#_lastScrollTop)}set _lastScrollTop(value){set$1(this.#_lastScrollTop,value,!0)}_scrollInterval;_scrollTimeout;_container;_disabled;_isColumnReverse;_mutationObserver=null;_rafPending=!1;_observerEnabled=!1;constructor(options={}){ -this._disabled=options.disabled??!1,this._isColumnReverse=options.isColumnReverse??!1}get autoScrollEnabled(){return this._autoScrollEnabled}get userScrolledUp(){return this._userScrolledUp}setContainer(container2){this._doStopObserving(),this._container=container2,this._observerEnabled&&container2&&!this._disabled&&this._doStartObserving()}setDisabled(disabled){this._disabled=disabled,disabled?(this._autoScrollEnabled=!1,this.stopInterval(),this._doStopObserving()):this._observerEnabled&&this._container&& -!this._mutationObserver&&this._doStartObserving()}handleScroll(){if(this._disabled||!this._container)return;const{scrollTop,scrollHeight,clientHeight}=this._container;let distanceFromBottom,isScrollingUp;this._isColumnReverse?(distanceFromBottom=Math.abs(scrollTop),isScrollingUp=scrollTop{isAtBottom&&(this._userScrolledUp=!1,this._autoScrollEnabled=!0)},AUTO_SCROLL_INTERVAL),this._lastScrollTop=scrollTop}scrollToBottom(behavior="smooth"){this._disabled||!this._container||(this._isColumnReverse?this._container.scrollTo({top:0,behavior}):this._container.scrollTo( -{top:this._container.scrollHeight,behavior}))}enable(){this._disabled||(this._userScrolledUp=!1,this._autoScrollEnabled=!0)}startInterval(){this._disabled||this._scrollInterval||(this._scrollInterval=setInterval(()=>{this.scrollToBottom()},AUTO_SCROLL_INTERVAL))}stopInterval(){this._scrollInterval&&(clearInterval(this._scrollInterval),this._scrollInterval=void 0)}updateInterval(isStreaming){if(this._disabled){this.stopInterval();return}isStreaming&&this._autoScrollEnabled?this._scrollInterval||this. -startInterval():this.stopInterval()}destroy(){this.stopInterval(),this._doStopObserving(),this._scrollTimeout&&(clearTimeout(this._scrollTimeout),this._scrollTimeout=void 0)}startObserving(){this._observerEnabled=!0,this._container&&!this._disabled&&!this._mutationObserver&&this._doStartObserving()}stopObserving(){this._observerEnabled=!1,this._doStopObserving()}_doStartObserving(){if(!this._container||this._mutationObserver)return;const isReverse=this._isColumnReverse;this._mutationObserver=new MutationObserver( -()=>{!this._autoScrollEnabled||this._rafPending||(this._rafPending=!0,requestAnimationFrame(()=>{this._rafPending=!1,this._autoScrollEnabled&&this._container&&(isReverse?this._container.scrollTop=0:this._container.scrollTop=this._container.scrollHeight)}))}),this._mutationObserver.observe(this._container,{childList:!0,subtree:!0,characterData:!0})}_doStopObserving(){this._mutationObserver&&(this._mutationObserver.disconnect(),this._mutationObserver=null),this._rafPending=!1}}function createAutoScrollController(options={}){ -return new AutoScrollController(options)}function useKeyboardShortcuts(callbacks){function handleKeydown(event2){const isCmdOrCtrl=event2.metaKey||event2.ctrlKey;isCmdOrCtrl&&event2.key===KeyboardKey.K_LOWER&&(event2.preventDefault(),callbacks.activateSearchMode?.(),callbacks.onSearchActivated?.()),isCmdOrCtrl&&event2.shiftKey&&(event2.key===KeyboardKey.O_LOWER||event2.key===KeyboardKey.O_UPPER)&&(event2.preventDefault(),goto(ROUTES.NEW_CHAT)),event2.shiftKey&&isCmdOrCtrl&&event2.key===KeyboardKey. -E_UPPER&&(event2.preventDefault(),callbacks.editActiveConversation?.()),isCmdOrCtrl&&event2.shiftKey&&(event2.key===KeyboardKey.D_LOWER||event2.key===KeyboardKey.D_UPPER)&&(event2.preventDefault(),callbacks.deleteActiveConversation?.()),isCmdOrCtrl&&event2.shiftKey&&event2.key===KeyboardKey.ARROW_UP&&(event2.preventDefault(),callbacks.navigateToPrevConversation?.()),isCmdOrCtrl&&event2.shiftKey&&event2.key===KeyboardKey.ARROW_DOWN&&(event2.preventDefault(),callbacks.navigateToNextConversation?.())} -return{handleKeydown}}var root_5$i=from_html('

Hello there

'),root_9$d=from_html('Server unavailable ',1),root_8$j=from_html(" ",1),root_7$h=from_html('
'),root_3$D=from_html('
'),root$$=from_html(" ",1);function ChatScreen($$anchor,$$props){push$1($$props,!0);let showCenteredEmpty=prop( -$$props,"showCenteredEmpty",3,!1),disableAutoScroll=user_derived(()=>!!config$1().disableAutoScroll),chatScrollContainer=state$1(void 0),dragCounter=state$1(0),isDragOver=state$1(!1),showFileErrorDialog=state$1(!1),uploadedFiles=state$1(proxy([]));const autoScroll=createAutoScrollController({isColumnReverse:!0});let fileErrorData=state$1(proxy({generallyUnsupported:[],modalityUnsupported:[],modalityReasons:{},supportedTypes:[]})),showDeleteDialog=state$1(!1),showEmptyFileDialog=state$1(!1),emptyFileNames=state$1( -proxy([])),initialMessage=state$1(""),isEmpty=user_derived(()=>showCenteredEmpty()&&!activeConversation()&&activeMessages().length===0&&!isLoading()),activeErrorDialog=user_derived(errorDialog),isServerLoading=user_derived(serverLoading),hasPropsError=user_derived(()=>!!serverError()),isCurrentConversationLoading=user_derived(()=>isLoading()||isChatStreaming()),showProcessingInfo=user_derived(()=>get$3(isCurrentConversationLoading)||config$1().keepStatsVisible&&!!page$1.params.id||activeProcessingState()!== -null),isRouter=user_derived(isRouterMode),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId();if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3(conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model)return model.model} -return null}),modelPropsVersion=state$1(0);setProcessingInfoContext({get showProcessingInfo(){return get$3(showProcessingInfo)}}),user_effect(()=>{get$3(activeModelId)&&(modelsStore.getModelProps(get$3(activeModelId))||modelsStore.fetchModelProps(get$3(activeModelId)).then(()=>{update$1(modelPropsVersion)}))});let hasAudioModality=user_derived(()=>get$3(activeModelId)?(get$3(modelPropsVersion),modelsStore.modelSupportsAudio(get$3(activeModelId))):!1),hasVisionModality=user_derived(()=>get$3(activeModelId)? -(get$3(modelPropsVersion),modelsStore.modelSupportsVision(get$3(activeModelId))):!1);async function handleDeleteConfirm(){const conversation=activeConversation();conversation&&await conversationsStore.deleteConversation(conversation.id),set$1(showDeleteDialog,!1)}function handleDragEnter(event2){event2.preventDefault(),update$1(dragCounter),event2.dataTransfer?.types.includes("Files")&&set$1(isDragOver,!0)}function handleDragLeave(event2){event2.preventDefault(),update$1(dragCounter,-1),get$3(dragCounter)=== -0&&set$1(isDragOver,!1)}function handleErrorDialogOpenChange(open2){open2||chatStore.dismissErrorDialog()}function handleDragOver(event2){event2.preventDefault()}function handleDrop(event2){if(event2.preventDefault(),set$1(isDragOver,!1),set$1(dragCounter,0),event2.dataTransfer?.files){const files=Array.from(event2.dataTransfer.files);if(isEditing()){const handler=getAddFilesHandler();if(handler){handler(files);return}}processFiles(files)}}function handleFileRemove(fileId){set$1(uploadedFiles,get$3( -uploadedFiles).filter(f=>f.id!==fileId),!0)}function handleFileUpload(files){processFiles(files)}const{handleKeydown}=useKeyboardShortcuts({deleteActiveConversation:()=>{activeConversation()&&set$1(showDeleteDialog,!0)}});async function handleSystemPromptAdd(draft){(draft.message||draft.files.length>0)&&chatStore.savePendingDraft(draft.message,draft.files),await chatStore.addSystemPrompt()}function handleScroll(){autoScroll.handleScroll()}async function handleSendMessage(message,files){const plainFiles=files? -snapshot(files):void 0,result=plainFiles?await parseFilesToMessageExtras(plainFiles,get$3(activeModelId)??void 0):void 0;if(result?.emptyFiles&&result.emptyFiles.length>0){if(set$1(emptyFileNames,result.emptyFiles,!0),set$1(showEmptyFileDialog,!0),files){const emptyFileNamesSet=new Set(result.emptyFiles);set$1(uploadedFiles,get$3(uploadedFiles).filter(file=>!emptyFileNamesSet.has(file.name)),!0)}return!1}const extras=result?.extras;return autoScroll.enable(),await chatStore.sendMessage(message,extras), -autoScroll.scrollToBottom(),!0}async function processFiles(files){const generallySupported=[],generallyUnsupported=[];for(const file of files)isFileTypeSupported(file.name,file.type)?generallySupported.push(file):generallyUnsupported.push(file);const capabilities={hasVision:get$3(hasVisionModality),hasAudio:get$3(hasAudioModality)},{supportedFiles,unsupportedFiles,modalityReasons}=filterFilesByModalities(generallySupported,capabilities);if([...generallyUnsupported,...unsupportedFiles].length>0){ -const supportedTypes=["text files","PDFs"];get$3(hasVisionModality)&&supportedTypes.push("images"),get$3(hasAudioModality)&&supportedTypes.push("audio files"),set$1(fileErrorData,{generallyUnsupported,modalityUnsupported:unsupportedFiles,modalityReasons,supportedTypes},!0),set$1(showFileErrorDialog,!0)}if(supportedFiles.length>0){const processed=await processFilesToChatUploaded(supportedFiles,get$3(activeModelId)??void 0);set$1(uploadedFiles,[...get$3(uploadedFiles),...processed],!0)}}afterNavigate( -()=>{get$3(disableAutoScroll)||autoScroll.enable()}),onMount$1(()=>{autoScroll.startObserving(),get$3(disableAutoScroll)||autoScroll.enable();const pendingDraft=chatStore.consumePendingDraft();pendingDraft&&(set$1(initialMessage,pendingDraft.message,!0),set$1(uploadedFiles,pendingDraft.files,!0))}),user_effect(()=>{autoScroll.setContainer(get$3(chatScrollContainer))}),user_effect(()=>{autoScroll.setDisabled(get$3(disableAutoScroll))});var fragment=root$$();event("keydown",$window,handleKeydown); -var node2=first_child(fragment);{var consequent=$$anchor2=>{ChatScreenDragOverlay($$anchor2)};if_block(node2,$$render=>{get$3(isDragOver)&&$$render(consequent)})}var node_1=sibling(node2,2);{var consequent_1=$$anchor2=>{ServerLoadingSplash($$anchor2,{})},alternate=$$anchor2=>{var div=root_3$D(),div_1=child(div),node_2=child(div_1);{var consequent_2=$$anchor3=>{{let $0=user_derived(activeMessages);ChatMessages($$anchor3,{get messages(){return get$3($0)},onUserAction:()=>{autoScroll.enable(),autoScroll. -scrollToBottom()}})}};if_block(node_2,$$render=>{get$3(isEmpty)||$$render(consequent_2)})}var div_2=sibling(node_2,2),node_3=child(div_2);{var consequent_3=$$anchor3=>{var div_3=root_5$i(),p2=sibling(child(div_3),2),text2=child(p2);reset(p2),reset(div_3),action(div_3,($$node,$$action_arg)=>fadeInView?.($$node,$$action_arg),()=>({duration:300})),template_effect(()=>set_text(text2,`${serverStore.props?.modalities?.audio?"Record audio, type a message ":"Type a message"} or upload files to get start\ -ed`)),append($$anchor3,div_3)};if_block(node_3,$$render=>{get$3(isEmpty)&&$$render(consequent_3)})}var node_4=sibling(node_3,2);{var consequent_4=$$anchor3=>{ChatScreenProcessingInfo($$anchor3,{})};if_block(node_4,$$render=>{page$1.params.id&&$$render(consequent_4)})}var node_5=sibling(node_4,2);{var consequent_5=$$anchor3=>{var div_4=root_7$h(),node_6=child(div_4);component(node_6,()=>Alert,($$anchor4,Alert_Root)=>{Alert_Root($$anchor4,{variant:"destructive",children:($$anchor5,$$slotProps)=>{var fragment_5=root_8$j(), -node_7=first_child(fragment_5);Triangle_alert(node_7,{class:"h-4 w-4"});var node_8=sibling(node_7,2);component(node_8,()=>Alert_title,($$anchor6,Alert_Title)=>{Alert_Title($$anchor6,{class:"flex items-center justify-between",children:($$anchor7,$$slotProps2)=>{var fragment_6=root_9$d(),button=sibling(first_child(fragment_6),2),node_9=child(button);{let $0=user_derived(()=>get$3(isServerLoading)?"animate-spin":"");Refresh_cw(node_9,{get class(){return`h-3 w-3 ${get$3($0)??""}`}})}var text_1=sibling( -node_9);reset(button),template_effect(()=>{button.disabled=get$3(isServerLoading),set_text(text_1,` ${get$3(isServerLoading)?"Retrying...":"Retry"}`)}),delegated("click",button,()=>serverStore.fetch()),append($$anchor7,fragment_6)},$$slots:{default:!0}})});var node_10=sibling(node_8,2);component(node_10,()=>Alert_description,($$anchor6,Alert_Description)=>{Alert_Description($$anchor6,{children:($$anchor7,$$slotProps2)=>{next$1();var text_2=text$8();template_effect($0=>set_text(text_2,$0),[()=>serverError()]), -append($$anchor7,text_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_5)},$$slots:{default:!0}})}),reset(div_4),action(div_4,($$node,$$action_arg)=>fadeInView?.($$node,$$action_arg),()=>({y:10,duration:250})),append($$anchor3,div_4)};if_block(node_5,$$render=>{get$3(hasPropsError)&&$$render(consequent_5)})}var div_5=sibling(node_5,2),node_11=child(div_5);{let $0=user_derived(()=>get$3(hasPropsError)||isEditing());ChatScreenForm(node_11,{get disabled(){return get$3($0)},get initialMessage(){ -return get$3(initialMessage)},get isLoading(){return get$3(isCurrentConversationLoading)},onFileRemove:handleFileRemove,onFileUpload:handleFileUpload,onSend:handleSendMessage,onStop:()=>chatStore.stopGeneration(),onSystemPromptAdd:handleSystemPromptAdd,get uploadedFiles(){return get$3(uploadedFiles)},set uploadedFiles($$value){set$1(uploadedFiles,$$value,!0)}})}reset(div_5),reset(div_2),reset(div_1),reset(div),bind_this(div,$$value=>set$1(chatScrollContainer,$$value),()=>get$3(chatScrollContainer)), -template_effect(()=>set_class(div_2,1,`pointer-events-none ${get$3(isEmpty)?"absolute bottom-[calc(50dvh-7rem)]":"sticky bottom-4"} right-4 left-4 mt-auto pt-16 transition-all duration-200`)),event("dragenter",div,handleDragEnter),event("dragleave",div,handleDragLeave),event("dragover",div,handleDragOver),event("drop",div,handleDrop),event("scroll",div,handleScroll),append($$anchor2,div)};if_block(node_1,$$render=>{get$3(isServerLoading)?$$render(consequent_1):$$render(alternate,-1)})}var node_12=sibling( -node_1,2);DialogFileUploadError(node_12,{get fileErrorData(){return get$3(fileErrorData)},get open(){return get$3(showFileErrorDialog)},set open($$value){set$1(showFileErrorDialog,$$value,!0)}});var node_13=sibling(node_12,2);DialogConfirmation(node_13,{title:"Delete Conversation",description:"Are you sure you want to delete this conversation? This action cannot be undone and will permanently remove all messages in this conversation.",confirmText:"Delete",cancelText:"Cancel",variant:"destructive", -get icon(){return Trash_2},onConfirm:handleDeleteConfirm,onCancel:()=>set$1(showDeleteDialog,!1),get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)}});var node_14=sibling(node_13,2);DialogEmptyFileAlert(node_14,{get emptyFiles(){return get$3(emptyFileNames)},onOpenChange:open2=>{open2||set$1(emptyFileNames,[],!0)},get open(){return get$3(showEmptyFileDialog)},set open($$value){set$1(showEmptyFileDialog,$$value,!0)}});var node_15=sibling(node_14,2);{let $0=user_derived( -()=>get$3(activeErrorDialog)?.message??""),$1=user_derived(()=>get$3(activeErrorDialog)?.contextInfo),$2=user_derived(()=>!!get$3(activeErrorDialog)),$3=user_derived(()=>get$3(activeErrorDialog)?.type??ErrorDialogType.SERVER);DialogChatError(node_15,{get message(){return get$3($0)},get contextInfo(){return get$3($1)},onOpenChange:handleErrorDialogOpenChange,get open(){return get$3($2)},get type(){return get$3($3)}})}append($$anchor,fragment),pop()}delegate(["click"]);var root$_=from_html('

Attach a file

Drop your files here to upload

');function ChatScreenDragOverlay($$anchor){var div=root$_(),div_1=child(div),node2=child(div_1);Upload( -node2,{class:"mb-4 h-12 w-12 text-muted-foreground"}),next$1(4),reset(div_1),reset(div),append($$anchor,div)}class DraftMessagesStore{drafts=new Map;getDraftMessage(chatId){const key2=chatId??NEW_CHAT_DRAFT_KEY;return this.drafts.get(key2)??{message:"",files:[]}}saveDraftMessage(chatId,message,files){const key2=chatId??NEW_CHAT_DRAFT_KEY;message||files.length>0?this.drafts.set(key2,{message,files:[...files]}):this.drafts.delete(key2)}clearDraftMessage(chatId){const key2=chatId??NEW_CHAT_DRAFT_KEY; -this.drafts.delete(key2)}}const draftMessagesStore=new DraftMessagesStore;function useDraftMessages(options){onMount$1(()=>{const chatId=options.getChatId(),draft=draftMessagesStore.getDraftMessage(chatId);(draft.message||draft.files.length>0)&&!options.getInitialMessage()&&(options.setMessage(draft.message),options.setFiles(draft.files))}),beforeNavigate(()=>{const chatId=options.getChatId();draftMessagesStore.saveDraftMessage(chatId,options.getMessage(),options.getFiles())}),afterNavigate(navigation=>{ -if(navigation?.from!=null){const chatId=options.getChatId(),draft=draftMessagesStore.getDraftMessage(chatId);options.setMessage(draft.message),options.setFiles(draft.files)}});function clearDraft(){const chatId=options.getChatId();draftMessagesStore.clearDraftMessage(chatId)}return{clearDraft}}var root$Z=from_html('
');function ChatScreenForm($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),initialMessage=prop($$props, -"initialMessage",3,""),isLoading2=prop($$props,"isLoading",3,!1),uploadedFiles=prop($$props,"uploadedFiles",31,()=>proxy([])),chatFormRef=state$1(void 0),chatId=user_derived(()=>page$1.params.id),hasLoadingAttachments=user_derived(()=>uploadedFiles().some(f=>f.isLoading)),message=user_derived(initialMessage),previousIsLoading=user_derived(isLoading2),previousInitialMessage=user_derived(initialMessage);const{clearDraft}=useDraftMessages({getChatId:()=>get$3(chatId),getMessage:()=>get$3(message),getFiles:()=>uploadedFiles(), -setMessage:m=>set$1(message,m),setFiles:f=>uploadedFiles(f),getInitialMessage:()=>initialMessage()});function handleFilesAdd(files){$$props.onFileUpload?.(files)}async function handleSubmit(){if(!get$3(message).trim()&&uploadedFiles().length===0||disabled()||get$3(hasLoadingAttachments)||!get$3(chatFormRef)?.checkModelSelected())return;const messageToSend=get$3(message).trim(),filesToSend=[...uploadedFiles()];set$1(message,""),uploadedFiles([]),clearDraft(),get$3(chatFormRef)?.resetTextareaHeight(), -await $$props.onSend?.(messageToSend,filesToSend)||(set$1(message,messageToSend),uploadedFiles(filesToSend))}function handleSystemPromptClick(){$$props.onSystemPromptAdd?.({message:get$3(message),files:uploadedFiles()})}function handleUploadedFileRemove(fileId){$$props.onFileRemove?.(fileId)}onMount$1(()=>{setTimeout(()=>get$3(chatFormRef)?.focus(),10)}),afterNavigate(navigation=>{navigation?.from!=null&&setTimeout(()=>get$3(chatFormRef)?.focus(),10)}),user_effect(()=>{initialMessage()!==get$3(previousInitialMessage)&& -(set$1(message,initialMessage()),set$1(previousInitialMessage,initialMessage()))}),user_effect(()=>{get$3(previousIsLoading)&&!isLoading2()&&setTimeout(()=>get$3(chatFormRef)?.focus(),10),set$1(previousIsLoading,isLoading2())});var div=root$Z(),node2=child(div);bind_this(ChatForm(node2,{get class(){return $$props.class},get disabled(){return disabled()},get isLoading(){return isLoading2()},showMcpPromptButton:!0,onFilesAdd:handleFilesAdd,get onStop(){return $$props.onStop},onSubmit:handleSubmit, -onSystemPromptClick:handleSystemPromptClick,onUploadedFileRemove:handleUploadedFileRemove,get value(){return get$3(message)},set value($$value){set$1(message,$$value)},get uploadedFiles(){return uploadedFiles()},set uploadedFiles($$value){uploadedFiles($$value)}}),$$value=>set$1(chatFormRef,$$value,!0),()=>get$3(chatFormRef)),reset(div),append($$anchor,div),pop()}var root_1$u=from_html(' '),root$Y=from_html( -'
');function ChatScreenProcessingInfo($$anchor,$$props){push$1($$props,!0);const processingState=useProcessingState(),processingInfoCtx=getProcessingInfoContext();let showProcessingInfo=user_derived(()=>processingInfoCtx.showProcessingInfo),isCurrentConversationLoading=user_derived(isLoading),isStreaming=user_derived(isChatStreaming),processingDetails=user_derived(()=>processingState.getTechnicalDetails());user_effect(()=>{ -const conversation=activeConversation();untrack$1(()=>chatStore.setActiveProcessingConversation(conversation?.id??null))}),user_effect(()=>{const keepStatsVisible=config$1().keepStatsVisible;if((keepStatsVisible||get$3(isCurrentConversationLoading)||get$3(isStreaming))&&processingState.startMonitoring(),!get$3(isCurrentConversationLoading)&&!get$3(isStreaming)&&!keepStatsVisible){const timeout=setTimeout(()=>{!config$1().keepStatsVisible&&!isChatStreaming()&&processingState.stopMonitoring()},PROCESSING_INFO_TIMEOUT); -return()=>clearTimeout(timeout)}}),user_effect(()=>{const conversation=activeConversation(),messages=activeMessages();if(config$1().keepStatsVisible&&conversation){if(messages.length===0){untrack$1(()=>chatStore.clearProcessingState(conversation.id));return}!get$3(isCurrentConversationLoading)&&!get$3(isStreaming)&&untrack$1(()=>chatStore.restoreProcessingStateFromMessages(messages,conversation.id))}});var div=root$Y(),div_1=child(div);each(div_1,20,()=>get$3(processingDetails),detail=>detail,($$anchor2,detail)=>{ -var span=root_1$u(),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,detail)),append($$anchor2,span)}),reset(div_1),reset(div),template_effect(()=>set_class(div,1,clsx(["chat-processing-info-container pointer-events-none",get$3(showProcessingInfo)&&"visible"]),"svelte-1ktvj8d")),append($$anchor,div),pop()}const emptyOptions$7={};function toString$1(value,options){const settings=emptyOptions$7,includeImageAlt=typeof settings.includeImageAlt=="boolean"?settings.includeImageAlt:!0, -includeHtml=typeof settings.includeHtml=="boolean"?settings.includeHtml:!0;return one$2(value,includeImageAlt,includeHtml)}function one$2(value,includeImageAlt,includeHtml){if(node(value)){if("value"in value)return value.type==="html"&&!includeHtml?"":value.value;if(includeImageAlt&&"alt"in value&&value.alt)return value.alt;if("children"in value)return all$2(value.children,includeImageAlt,includeHtml)}return Array.isArray(value)?all$2(value,includeImageAlt,includeHtml):""}function all$2(values,includeImageAlt,includeHtml){ -const result=[];let index2=-1;for(;++index2end?0:end+start2:start2=start2>end?end:start2,remove2=remove2>0?remove2:0,items2.length<1e4)parameters=Array.from(items2),parameters.unshift(start2,remove2),list2.splice(...parameters);else for(remove2&&list2.splice(start2,remove2);chunkStart0?(splice(list2,list2.length,0,items2),list2):items2}const hasOwnProperty={}.hasOwnProperty;function combineExtensions(extensions){const all2={};let index2=-1;for(;++index213&&code2< -32||code2>126&&code2<160||code2>55295&&code2<57344||code2>64975&&code2<65008||(code2&65535)===65535||(code2&65535)===65534||code2>1114111?"�":String.fromCodePoint(code2)}function normalizeIdentifier(value){return value.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const asciiAlpha=regexCheck(/[A-Za-z]/),asciiAlphanumeric=regexCheck(/[\dA-Za-z]/),asciiAtext=regexCheck(/[#-'*+\--9=?A-Z^-~]/);function asciiControl(code2){return code2!==null&&(code2<32||code2===127)}const asciiDigit=regexCheck( -/\d/),asciiHexDigit=regexCheck(/[\dA-Fa-f]/),asciiPunctuation=regexCheck(/[!-/:-@[-`{-~]/);function markdownLineEnding(code2){return code2!==null&&code2<-2}function markdownLineEndingOrSpace(code2){return code2!==null&&(code2<0||code2===32)}function markdownSpace(code2){return code2===-2||code2===-1||code2===32}const unicodePunctuation=regexCheck(new RegExp("\\p{P}|\\p{S}","u")),unicodeWhitespace=regexCheck(/\s/);function regexCheck(regex){return check;function check(code2){return code2!==null&& -code2>-1&®ex.test(String.fromCharCode(code2))}}function normalizeUri(value){const result=[];let index2=-1,start2=0,skip=0;for(;++index255295&&code2<57344){const next2=value.charCodeAt(index2+ -1);code2<56320&&next2>56319&&next2<57344?(replace2=String.fromCharCode(code2,next2),skip=1):replace2="�"}else replace2=String.fromCharCode(code2);replace2&&(result.push(value.slice(start2,index2),encodeURIComponent(replace2)),start2=index2+skip+1,replace2=""),skip&&(index2+=skip,skip=0)}return result.join("")+value.slice(start2)}function factorySpace(effects,ok,type2,max2){const limit2=max2?max2-1:Number.POSITIVE_INFINITY;let size2=0;return start2;function start2(code2){return markdownSpace(code2)? -(effects.enter(type2),prefix(code2)):ok(code2)}function prefix(code2){return markdownSpace(code2)&&size2++lineStartOffset))return;const indexBeforeExits=self2.events.length;let indexBeforeFlow=indexBeforeExits,seen2,point2;for(;indexBeforeFlow--;)if(self2.events[indexBeforeFlow][0]==="exit"&&self2.events[indexBeforeFlow][1].type==="chunkFlow"){ -if(seen2){point2=self2.events[indexBeforeFlow][1].end;break}seen2=!0}for(exitContainers(continued),index2=indexBeforeExits;index2size2;){const entry=stack[index2];self2.containerState=entry[1],entry[0].exit.call(self2,effects)}stack.length=size2}function closeFlow(){ -childFlow.write([null]),childToken=void 0,childFlow=void 0,self2.containerState._closeFlow=void 0}}function tokenizeContainer(effects,ok,nok){return factorySpace(effects,effects.attempt(this.parser.constructs.document,ok,nok),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function classifyCharacter(code2){if(code2===null||markdownLineEndingOrSpace(code2)||unicodeWhitespace(code2))return 1;if(unicodePunctuation(code2))return 2}function resolveAll(constructs2,events,context){ -const called=[];let index2=-1;for(;++index21&&events[index2][1].end.offset-events[index2][1].start.offset>1?2:1;const start2={...events[open2][1].end},end={...events[index2][1].start};movePoint(start2,-use),movePoint(end,use),openingSequence={type:use>1?"strongSequence":"emphasisSequence",start:start2,end:{...events[open2][1].end}},closingSequence={type:use>1?"strongSequence":"emphasisSequence",start:{...events[index2][1]. -start},end},text2={type:use>1?"strongText":"emphasisText",start:{...events[open2][1].end},end:{...events[index2][1].start}},group={type:use>1?"strong":"emphasis",start:{...openingSequence.start},end:{...closingSequence.end}},events[open2][1].end={...openingSequence.start},events[index2][1].start={...closingSequence.end},nextEvents=[],events[open2][1].end.offset-events[open2][1].start.offset&&(nextEvents=push(nextEvents,[["enter",events[open2][1],context],["exit",events[open2][1],context]])),nextEvents= -push(nextEvents,[["enter",group,context],["enter",openingSequence,context],["exit",openingSequence,context],["enter",text2,context]]),nextEvents=push(nextEvents,resolveAll(context.parser.constructs.insideSpan.null,events.slice(open2+1,index2),context)),nextEvents=push(nextEvents,[["exit",text2,context],["enter",closingSequence,context],["exit",closingSequence,context],["exit",group,context]]),events[index2][1].end.offset-events[index2][1].start.offset?(offset2=2,nextEvents=push(nextEvents,[["ent\ -er",events[index2][1],context],["exit",events[index2][1],context]])):offset2=0,splice(events,open2-1,index2-open2+3,nextEvents),index2=open2+nextEvents.length-offset2-2;break}}for(index2=-1;++index20&&markdownSpace(code2)?factorySpace(effects,beforeContentChunk,"linePrefix",initialPrefix+1)(code2):beforeContentChunk(code2)}function beforeContentChunk(code2){return code2===null||markdownLineEnding(code2)?effects.check(nonLazyContinuation$1,atNonLazyBreak,after)(code2):(effects.enter("codeFlowValue"),contentChunk(code2))}function contentChunk(code2){return code2===null||markdownLineEnding(code2)?(effects.exit("codeFlowVa\ -lue"),beforeContentChunk(code2)):(effects.consume(code2),contentChunk)}function after(code2){return effects.exit("codeFenced"),ok(code2)}function tokenizeCloseStart(effects2,ok2,nok2){let size2=0;return startBefore;function startBefore(code2){return effects2.enter("lineEnding"),effects2.consume(code2),effects2.exit("lineEnding"),start3}function start3(code2){return effects2.enter("codeFencedFence"),markdownSpace(code2)?factorySpace(effects2,beforeSequenceClose,"linePrefix",self2.parser.constructs. -disable.null.includes("codeIndented")?void 0:4)(code2):beforeSequenceClose(code2)}function beforeSequenceClose(code2){return code2===marker?(effects2.enter("codeFencedFenceSequence"),sequenceClose(code2)):nok2(code2)}function sequenceClose(code2){return code2===marker?(size2++,effects2.consume(code2),sequenceClose):size2>=sizeOpen?(effects2.exit("codeFencedFenceSequence"),markdownSpace(code2)?factorySpace(effects2,sequenceCloseAfter,"whitespace")(code2):sequenceCloseAfter(code2)):nok2(code2)}function sequenceCloseAfter(code2){ -return code2===null||markdownLineEnding(code2)?(effects2.exit("codeFencedFence"),ok2(code2)):nok2(code2)}}}function tokenizeNonLazyContinuation$1(effects,ok,nok){const self2=this;return start2;function start2(code2){return code2===null?nok(code2):(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),lineStart)}function lineStart(code2){return self2.parser.lazy[self2.now().line]?nok(code2):ok(code2)}}const codeIndented={name:"codeIndented",tokenize:tokenizeCodeIndented},furtherStart={ -partial:!0,tokenize:tokenizeFurtherStart};function tokenizeCodeIndented(effects,ok,nok){const self2=this;return start2;function start2(code2){return effects.enter("codeIndented"),factorySpace(effects,afterPrefix,"linePrefix",5)(code2)}function afterPrefix(code2){const tail=self2.events[self2.events.length-1];return tail&&tail[1].type==="linePrefix"&&tail[2].sliceSerialize(tail[1],!0).length>=4?atBreak(code2):nok(code2)}function atBreak(code2){return code2===null?after(code2):markdownLineEnding(code2)? -effects.attempt(furtherStart,atBreak,after)(code2):(effects.enter("codeFlowValue"),inside(code2))}function inside(code2){return code2===null||markdownLineEnding(code2)?(effects.exit("codeFlowValue"),atBreak(code2)):(effects.consume(code2),inside)}function after(code2){return effects.exit("codeIndented"),ok(code2)}}function tokenizeFurtherStart(effects,ok,nok){const self2=this;return furtherStart2;function furtherStart2(code2){return self2.parser.lazy[self2.now().line]?nok(code2):markdownLineEnding( -code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),furtherStart2):factorySpace(effects,afterPrefix,"linePrefix",5)(code2)}function afterPrefix(code2){const tail=self2.events[self2.events.length-1];return tail&&tail[1].type==="linePrefix"&&tail[2].sliceSerialize(tail[1],!0).length>=4?ok(code2):markdownLineEnding(code2)?furtherStart2(code2):nok(code2)}}const codeText={name:"codeText",previous:previous$2,resolve:resolveCodeText,tokenize:tokenizeCodeText};function resolveCodeText(events){ -let tailExitIndex=events.length-4,headEnterIndex=3,index2,enter;if((events[headEnterIndex][1].type==="lineEnding"||events[headEnterIndex][1].type==="space")&&(events[tailExitIndex][1].type==="lineEnding"||events[tailExitIndex][1].type==="space")){for(index2=headEnterIndex;++index2=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+index2+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return index2this.left.length?this.right.slice(this.right.length-stop+this.left.length,this.right.length-start2+this.left.length).reverse():this.left.slice(start2).concat(this.right.slice(this.right.length-stop+this.left.length).reverse())}splice(start2,deleteCount,items2){ -const count=deleteCount||0;this.setCursor(Math.trunc(start2));const removed=this.right.splice(this.right.length-count,Number.POSITIVE_INFINITY);return items2&&chunkedPush(this.left,items2),removed.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(item){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(item)}pushMany(items2){this.setCursor(Number.POSITIVE_INFINITY),chunkedPush(this.left,items2)}unshift(item){this.setCursor(0),this.right.push(item)}unshiftMany(items2){ -this.setCursor(0),chunkedPush(this.right,items2.reverse())}setCursor(n){if(!(n===this.left.length||n>this.left.length&&this.right.length===0||n<0&&this.left.length===0))if(n=4?ok(code2):effects.interrupt(self2.parser.constructs.flow,nok,ok)(code2)}}function factoryDestination(effects,ok,nok,type2,literalType,literalMarkerType,rawType,stringType,max2){ -const limit2=max2||Number.POSITIVE_INFINITY;let balance=0;return start2;function start2(code2){return code2===60?(effects.enter(type2),effects.enter(literalType),effects.enter(literalMarkerType),effects.consume(code2),effects.exit(literalMarkerType),enclosedBefore):code2===null||code2===32||code2===41||asciiControl(code2)?nok(code2):(effects.enter(type2),effects.enter(rawType),effects.enter(stringType),effects.enter("chunkString",{contentType:"string"}),raw2(code2))}function enclosedBefore(code2){ -return code2===62?(effects.enter(literalMarkerType),effects.consume(code2),effects.exit(literalMarkerType),effects.exit(literalType),effects.exit(type2),ok):(effects.enter(stringType),effects.enter("chunkString",{contentType:"string"}),enclosed(code2))}function enclosed(code2){return code2===62?(effects.exit("chunkString"),effects.exit(stringType),enclosedBefore(code2)):code2===null||code2===60||markdownLineEnding(code2)?nok(code2):(effects.consume(code2),code2===92?enclosedEscape:enclosed)}function enclosedEscape(code2){ -return code2===60||code2===62||code2===92?(effects.consume(code2),enclosed):enclosed(code2)}function raw2(code2){return!balance&&(code2===null||code2===41||markdownLineEndingOrSpace(code2))?(effects.exit("chunkString"),effects.exit(stringType),effects.exit(rawType),effects.exit(type2),ok(code2)):balance999||code2===null||code2===91||code2===93&&!seen2|| -code2===94&&!size2&&"_hiddenFootnoteSupport"in self2.parser.constructs?nok(code2):code2===93?(effects.exit(stringType),effects.enter(markerType),effects.consume(code2),effects.exit(markerType),effects.exit(type2),ok):markdownLineEnding(code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),atBreak):(effects.enter("chunkString",{contentType:"string"}),labelInside(code2))}function labelInside(code2){return code2===null||code2===91||code2===93||markdownLineEnding(code2)|| -size2++>999?(effects.exit("chunkString"),atBreak(code2)):(effects.consume(code2),seen2||(seen2=!markdownSpace(code2)),code2===92?labelEscape:labelInside)}function labelEscape(code2){return code2===91||code2===92||code2===93?(effects.consume(code2),size2++,labelInside):labelInside(code2)}}function factoryTitle(effects,ok,nok,type2,markerType,stringType){let marker;return start2;function start2(code2){return code2===34||code2===39||code2===40?(effects.enter(type2),effects.enter(markerType),effects. -consume(code2),effects.exit(markerType),marker=code2===40?41:code2,begin):nok(code2)}function begin(code2){return code2===marker?(effects.enter(markerType),effects.consume(code2),effects.exit(markerType),effects.exit(type2),ok):(effects.enter(stringType),atBreak(code2))}function atBreak(code2){return code2===marker?(effects.exit(stringType),begin(marker)):code2===null?nok(code2):markdownLineEnding(code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),factorySpace( -effects,atBreak,"linePrefix")):(effects.enter("chunkString",{contentType:"string"}),inside(code2))}function inside(code2){return code2===marker||code2===null||markdownLineEnding(code2)?(effects.exit("chunkString"),atBreak(code2)):(effects.consume(code2),code2===92?escape2:inside)}function escape2(code2){return code2===marker||code2===92?(effects.consume(code2),inside):inside(code2)}}function factoryWhitespace(effects,ok){let seen2;return start2;function start2(code2){return markdownLineEnding(code2)? -(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),seen2=!0,start2):markdownSpace(code2)?factorySpace(effects,start2,seen2?"linePrefix":"lineSuffix")(code2):ok(code2)}}const definition$1={name:"definition",tokenize:tokenizeDefinition},titleBefore={partial:!0,tokenize:tokenizeTitleBefore};function tokenizeDefinition(effects,ok,nok){const self2=this;let identifier2;return start2;function start2(code2){return effects.enter("definition"),before(code2)}function before(code2){ -return factoryLabel.call(self2,effects,labelAfter,nok,"definitionLabel","definitionLabelMarker","definitionLabelString")(code2)}function labelAfter(code2){return identifier2=normalizeIdentifier(self2.sliceSerialize(self2.events[self2.events.length-1][1]).slice(1,-1)),code2===58?(effects.enter("definitionMarker"),effects.consume(code2),effects.exit("definitionMarker"),markerAfter):nok(code2)}function markerAfter(code2){return markdownLineEndingOrSpace(code2)?factoryWhitespace(effects,destinationBefore)( -code2):destinationBefore(code2)}function destinationBefore(code2){return factoryDestination(effects,destinationAfter,nok,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(code2)}function destinationAfter(code2){return effects.attempt(titleBefore,after,after)(code2)}function after(code2){return markdownSpace(code2)?factorySpace(effects,afterWhitespace,"whitespace")(code2):afterWhitespace(code2)}function afterWhitespace(code2){ +!0);get _autoScrollEnabled(){return get$3(this.#_autoScrollEnabled)}set _autoScrollEnabled(value){set$1(this.#_autoScrollEnabled,value,!0)}#_userScrolledUp=state$1(!1);get _userScrolledUp(){return get$3(this.#_userScrolledUp)}set _userScrolledUp(value){set$1(this.#_userScrolledUp,value,!0)}#_lastScrollTop=state$1(0);get _lastScrollTop(){return get$3(this.#_lastScrollTop)}set _lastScrollTop(value){set$1(this.#_lastScrollTop,value,!0)}_scrollInterval;_scrollTimeout;_container;_disabled;_mutationObserver=null;_rafPending=!1;_observerEnabled=!1;constructor(options={}){ +this._disabled=options.disabled??!1}get autoScrollEnabled(){return this._autoScrollEnabled}get userScrolledUp(){return this._userScrolledUp}setContainer(container2){this._doStopObserving(),this._container=container2,this._observerEnabled&&container2&&!this._disabled&&this._doStartObserving()}setDisabled(disabled){this._disabled=disabled,disabled?(this._autoScrollEnabled=!1,this.stopInterval(),this._doStopObserving()):this._observerEnabled&&this._container&&!this._mutationObserver&&this._doStartObserving()}handleScroll(){ +if(this._disabled||!this._container)return;const{scrollTop,scrollHeight,clientHeight}=this._container,distanceFromBottom=scrollHeight-clientHeight-scrollTop,isScrollingUp=scrollTop{isAtBottom&&(this._userScrolledUp=!1,this._autoScrollEnabled=!0)},AUTO_SCROLL_INTERVAL),this._lastScrollTop=scrollTop}scrollToBottom(behavior="smooth"){this._disabled||!this._container||this._container.scrollTo({top:this._container.scrollHeight,behavior})}enable(){this._disabled||(this._userScrolledUp=!1,this._autoScrollEnabled=!0)}startInterval(){this._disabled||this._scrollInterval||(this._scrollInterval=setInterval(()=>{this.scrollToBottom()},AUTO_SCROLL_INTERVAL))}stopInterval(){ +this._scrollInterval&&(clearInterval(this._scrollInterval),this._scrollInterval=void 0)}updateInterval(isStreaming){if(this._disabled){this.stopInterval();return}isStreaming&&this._autoScrollEnabled?this._scrollInterval||this.startInterval():this.stopInterval()}destroy(){this.stopInterval(),this._doStopObserving(),this._scrollTimeout&&(clearTimeout(this._scrollTimeout),this._scrollTimeout=void 0)}startObserving(){this._observerEnabled=!0,this._container&&!this._disabled&&!this._mutationObserver&& +this._doStartObserving()}stopObserving(){this._observerEnabled=!1,this._doStopObserving()}_doStartObserving(){!this._container||this._mutationObserver||(this._mutationObserver=new MutationObserver(()=>{!this._autoScrollEnabled||this._rafPending||(this._rafPending=!0,requestAnimationFrame(()=>{this._rafPending=!1,this._autoScrollEnabled&&this._container&&(this._container.scrollTop=this._container.scrollHeight)}))}),this._mutationObserver.observe(this._container,{childList:!0,subtree:!0,characterData:!0}))}_doStopObserving(){ +this._mutationObserver&&(this._mutationObserver.disconnect(),this._mutationObserver=null),this._rafPending=!1}}function createAutoScrollController(options={}){return new AutoScrollController(options)}function useKeyboardShortcuts(callbacks){function handleKeydown(event2){const isCmdOrCtrl=event2.metaKey||event2.ctrlKey;isCmdOrCtrl&&event2.key===KeyboardKey.K_LOWER&&(event2.preventDefault(),callbacks.activateSearchMode?.(),callbacks.onSearchActivated?.()),isCmdOrCtrl&&event2.shiftKey&&(event2.key=== +KeyboardKey.O_LOWER||event2.key===KeyboardKey.O_UPPER)&&(event2.preventDefault(),goto(ROUTES.NEW_CHAT)),event2.shiftKey&&isCmdOrCtrl&&event2.key===KeyboardKey.E_UPPER&&(event2.preventDefault(),callbacks.editActiveConversation?.()),isCmdOrCtrl&&event2.shiftKey&&(event2.key===KeyboardKey.D_LOWER||event2.key===KeyboardKey.D_UPPER)&&(event2.preventDefault(),callbacks.deleteActiveConversation?.()),isCmdOrCtrl&&event2.shiftKey&&event2.key===KeyboardKey.ARROW_UP&&(event2.preventDefault(),callbacks.navigateToPrevConversation?.()), +isCmdOrCtrl&&event2.shiftKey&&event2.key===KeyboardKey.ARROW_DOWN&&(event2.preventDefault(),callbacks.navigateToNextConversation?.())}return{handleKeydown}}var root_5$i=from_html('

Hello there

'),root_9$d=from_html('Server unavailable ',1),root_8$j=from_html(" ",1),root_7$h=from_html('
'),root_3$D=from_html('
'),root$$=from_html(" ",1);function ChatScreen($$anchor,$$props){push$1($$props,!0);let showCenteredEmpty=prop($$props,"showCenteredEmpty",3,!1);const autoScroll=createAutoScrollController();let disableAutoScroll=user_derived(()=>!!config$1().disableAutoScroll),chatScrollContainer=state$1(void 0),dragCounter=state$1(0),isDragOver=state$1(!1),showFileErrorDialog=state$1(!1),uploadedFiles=state$1(proxy([])),fileErrorData=state$1(proxy({generallyUnsupported:[],modalityUnsupported:[], +modalityReasons:{},supportedTypes:[]})),showDeleteDialog=state$1(!1),showEmptyFileDialog=state$1(!1),emptyFileNames=state$1(proxy([])),initialMessage=state$1(""),isEmpty=user_derived(()=>showCenteredEmpty()&&!activeConversation()&&activeMessages().length===0&&!isLoading()),activeErrorDialog=user_derived(errorDialog),isServerLoading=user_derived(serverLoading),hasPropsError=user_derived(()=>!!serverError()),isCurrentConversationLoading=user_derived(()=>isLoading()||isChatStreaming()),showProcessingInfo=user_derived( +()=>get$3(isCurrentConversationLoading)||config$1().keepStatsVisible&&!!page$1.params.id||activeProcessingState()!==null),isRouter=user_derived(isRouterMode),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId();if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3( +conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model)return model.model}return null}),modelPropsVersion=state$1(0);setProcessingInfoContext({get showProcessingInfo(){return get$3(showProcessingInfo)}}),user_effect(()=>{get$3(activeModelId)&&(modelsStore.getModelProps(get$3(activeModelId))||modelsStore.fetchModelProps(get$3(activeModelId)).then(()=>{update$1(modelPropsVersion)}))});let hasAudioModality=user_derived(()=>get$3(activeModelId)?(get$3(modelPropsVersion), +modelsStore.modelSupportsAudio(get$3(activeModelId))):!1),hasVisionModality=user_derived(()=>get$3(activeModelId)?(get$3(modelPropsVersion),modelsStore.modelSupportsVision(get$3(activeModelId))):!1);async function handleDeleteConfirm(){const conversation=activeConversation();conversation&&await conversationsStore.deleteConversation(conversation.id),set$1(showDeleteDialog,!1)}function handleDragEnter(event2){event2.preventDefault(),update$1(dragCounter),event2.dataTransfer?.types.includes("Files")&& +set$1(isDragOver,!0)}function handleDragLeave(event2){event2.preventDefault(),update$1(dragCounter,-1),get$3(dragCounter)===0&&set$1(isDragOver,!1)}function handleErrorDialogOpenChange(open2){open2||chatStore.dismissErrorDialog()}function handleDragOver(event2){event2.preventDefault()}function handleDrop(event2){if(event2.preventDefault(),set$1(isDragOver,!1),set$1(dragCounter,0),event2.dataTransfer?.files){const files=Array.from(event2.dataTransfer.files);if(isEditing()){const handler=getAddFilesHandler(); +if(handler){handler(files);return}}processFiles(files)}}function handleFileRemove(fileId){set$1(uploadedFiles,get$3(uploadedFiles).filter(f=>f.id!==fileId),!0)}function handleFileUpload(files){processFiles(files)}const{handleKeydown}=useKeyboardShortcuts({deleteActiveConversation:()=>{activeConversation()&&set$1(showDeleteDialog,!0)}});async function handleSystemPromptAdd(draft){(draft.message||draft.files.length>0)&&chatStore.savePendingDraft(draft.message,draft.files),await chatStore.addSystemPrompt()} +function handleScroll(){autoScroll.handleScroll()}async function handleSendMessage(message,files){const plainFiles=files?snapshot(files):void 0,result=plainFiles?await parseFilesToMessageExtras(plainFiles,get$3(activeModelId)??void 0):void 0;if(result?.emptyFiles&&result.emptyFiles.length>0){if(set$1(emptyFileNames,result.emptyFiles,!0),set$1(showEmptyFileDialog,!0),files){const emptyFileNamesSet=new Set(result.emptyFiles);set$1(uploadedFiles,get$3(uploadedFiles).filter(file=>!emptyFileNamesSet. +has(file.name)),!0)}return!1}const extras=result?.extras;return autoScroll.enable(),await chatStore.sendMessage(message,extras),autoScroll.scrollToBottom(),!0}async function processFiles(files){const generallySupported=[],generallyUnsupported=[];for(const file of files)isFileTypeSupported(file.name,file.type)?generallySupported.push(file):generallyUnsupported.push(file);const capabilities={hasVision:get$3(hasVisionModality),hasAudio:get$3(hasAudioModality)},{supportedFiles,unsupportedFiles,modalityReasons}=filterFilesByModalities( +generallySupported,capabilities);if([...generallyUnsupported,...unsupportedFiles].length>0){const supportedTypes=["text files","PDFs"];get$3(hasVisionModality)&&supportedTypes.push("images"),get$3(hasAudioModality)&&supportedTypes.push("audio files"),set$1(fileErrorData,{generallyUnsupported,modalityUnsupported:unsupportedFiles,modalityReasons,supportedTypes},!0),set$1(showFileErrorDialog,!0)}if(supportedFiles.length>0){const processed=await processFilesToChatUploaded(supportedFiles,get$3(activeModelId)?? +void 0);set$1(uploadedFiles,[...get$3(uploadedFiles),...processed],!0)}}afterNavigate(()=>{get$3(disableAutoScroll)||autoScroll.enable()});function handleMessagesReady(){get$3(disableAutoScroll)||requestAnimationFrame(()=>{autoScroll.scrollToBottom("instant")})}onMount$1(()=>{autoScroll.startObserving(),get$3(disableAutoScroll)||autoScroll.enable();const pendingDraft=chatStore.consumePendingDraft();pendingDraft&&(set$1(initialMessage,pendingDraft.message,!0),set$1(uploadedFiles,pendingDraft.files, +!0))}),user_effect(()=>{autoScroll.setContainer(get$3(chatScrollContainer))}),user_effect(()=>{autoScroll.setDisabled(get$3(disableAutoScroll))});var fragment=root$$();event("keydown",$window,handleKeydown);var node2=first_child(fragment);{var consequent=$$anchor2=>{ChatScreenDragOverlay($$anchor2)};if_block(node2,$$render=>{get$3(isDragOver)&&$$render(consequent)})}var node_1=sibling(node2,2);{var consequent_1=$$anchor2=>{ServerLoadingSplash($$anchor2,{})},alternate=$$anchor2=>{var div=root_3$D(), +div_1=child(div),node_2=child(div_1);{var consequent_2=$$anchor3=>{{let $0=user_derived(activeMessages);ChatMessages($$anchor3,{get messages(){return get$3($0)},onUserAction:()=>{autoScroll.enable(),autoScroll.scrollToBottom()},onMessagesReady:handleMessagesReady})}};if_block(node_2,$$render=>{get$3(isEmpty)||$$render(consequent_2)})}var div_2=sibling(node_2,2),node_3=child(div_2);{var consequent_3=$$anchor3=>{var div_3=root_5$i(),p2=sibling(child(div_3),2),text2=child(p2);reset(p2),reset(div_3), +action(div_3,($$node,$$action_arg)=>fadeInView?.($$node,$$action_arg),()=>({duration:300})),template_effect(()=>set_text(text2,`${serverStore.props?.modalities?.audio?"Record audio, type a message ":"Type a message"} or upload files to get started`)),append($$anchor3,div_3)};if_block(node_3,$$render=>{get$3(isEmpty)&&$$render(consequent_3)})}var node_4=sibling(node_3,2);{var consequent_4=$$anchor3=>{ChatScreenProcessingInfo($$anchor3,{})};if_block(node_4,$$render=>{page$1.params.id&&$$render(consequent_4)})} +var node_5=sibling(node_4,2);{var consequent_5=$$anchor3=>{var div_4=root_7$h(),node_6=child(div_4);component(node_6,()=>Alert,($$anchor4,Alert_Root)=>{Alert_Root($$anchor4,{variant:"destructive",children:($$anchor5,$$slotProps)=>{var fragment_5=root_8$j(),node_7=first_child(fragment_5);Triangle_alert(node_7,{class:"h-4 w-4"});var node_8=sibling(node_7,2);component(node_8,()=>Alert_title,($$anchor6,Alert_Title)=>{Alert_Title($$anchor6,{class:"flex items-center justify-between",children:($$anchor7,$$slotProps2)=>{ +var fragment_6=root_9$d(),button=sibling(first_child(fragment_6),2),node_9=child(button);{let $0=user_derived(()=>get$3(isServerLoading)?"animate-spin":"");Refresh_cw(node_9,{get class(){return`h-3 w-3 ${get$3($0)??""}`}})}var text_1=sibling(node_9);reset(button),template_effect(()=>{button.disabled=get$3(isServerLoading),set_text(text_1,` ${get$3(isServerLoading)?"Retrying...":"Retry"}`)}),delegated("click",button,()=>serverStore.fetch()),append($$anchor7,fragment_6)},$$slots:{default:!0}})});var node_10=sibling( +node_8,2);component(node_10,()=>Alert_description,($$anchor6,Alert_Description)=>{Alert_Description($$anchor6,{children:($$anchor7,$$slotProps2)=>{next$1();var text_2=text$8();template_effect($0=>set_text(text_2,$0),[()=>serverError()]),append($$anchor7,text_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_5)},$$slots:{default:!0}})}),reset(div_4),action(div_4,($$node,$$action_arg)=>fadeInView?.($$node,$$action_arg),()=>({y:10,duration:250})),append($$anchor3,div_4)};if_block(node_5,$$render=>{ +get$3(hasPropsError)&&$$render(consequent_5)})}var div_5=sibling(node_5,2),node_11=child(div_5);{let $0=user_derived(()=>get$3(hasPropsError)||isEditing());ChatScreenForm(node_11,{get disabled(){return get$3($0)},get initialMessage(){return get$3(initialMessage)},get isLoading(){return get$3(isCurrentConversationLoading)},onFileRemove:handleFileRemove,onFileUpload:handleFileUpload,onSend:handleSendMessage,onStop:()=>chatStore.stopGeneration(),onSystemPromptAdd:handleSystemPromptAdd,get uploadedFiles(){ +return get$3(uploadedFiles)},set uploadedFiles($$value){set$1(uploadedFiles,$$value,!0)}})}reset(div_5),reset(div_2),reset(div_1),reset(div),bind_this(div,$$value=>set$1(chatScrollContainer,$$value),()=>get$3(chatScrollContainer)),template_effect(()=>set_class(div_2,1,`pointer-events-none ${get$3(isEmpty)?"absolute bottom-[calc(50dvh-7rem)]":"sticky bottom-4"} right-4 left-4 mt-auto pt-16 transition-all duration-200`)),event("dragenter",div,handleDragEnter),event("dragleave",div,handleDragLeave), +event("dragover",div,handleDragOver),event("drop",div,handleDrop),event("scroll",div,handleScroll),append($$anchor2,div)};if_block(node_1,$$render=>{get$3(isServerLoading)?$$render(consequent_1):$$render(alternate,-1)})}var node_12=sibling(node_1,2);DialogFileUploadError(node_12,{get fileErrorData(){return get$3(fileErrorData)},get open(){return get$3(showFileErrorDialog)},set open($$value){set$1(showFileErrorDialog,$$value,!0)}});var node_13=sibling(node_12,2);DialogConfirmation(node_13,{title:"\ +Delete Conversation",description:"Are you sure you want to delete this conversation? This action cannot be undone and will permanently remove all messages in this conversation.",confirmText:"Delete",cancelText:"Cancel",variant:"destructive",get icon(){return Trash_2},onConfirm:handleDeleteConfirm,onCancel:()=>set$1(showDeleteDialog,!1),get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)}});var node_14=sibling(node_13,2);DialogEmptyFileAlert(node_14,{get emptyFiles(){ +return get$3(emptyFileNames)},onOpenChange:open2=>{open2||set$1(emptyFileNames,[],!0)},get open(){return get$3(showEmptyFileDialog)},set open($$value){set$1(showEmptyFileDialog,$$value,!0)}});var node_15=sibling(node_14,2);{let $0=user_derived(()=>get$3(activeErrorDialog)?.message??""),$1=user_derived(()=>get$3(activeErrorDialog)?.contextInfo),$2=user_derived(()=>!!get$3(activeErrorDialog)),$3=user_derived(()=>get$3(activeErrorDialog)?.type??ErrorDialogType.SERVER);DialogChatError(node_15,{get message(){ +return get$3($0)},get contextInfo(){return get$3($1)},onOpenChange:handleErrorDialogOpenChange,get open(){return get$3($2)},get type(){return get$3($3)}})}append($$anchor,fragment),pop()}delegate(["click"]);var root$_=from_html('

Attach a file

Drop your files here to upload

');function ChatScreenDragOverlay($$anchor){var div=root$_(),div_1=child(div),node2=child(div_1);Upload(node2,{class:"mb-4 h-12 w-12 text-muted-foreground"}),next$1(4),reset(div_1),reset(div),append($$anchor,div)}class DraftMessagesStore{drafts=new Map;getDraftMessage(chatId){const key2=chatId??NEW_CHAT_DRAFT_KEY;return this.drafts.get(key2)??{message:"",files:[]}}saveDraftMessage(chatId,message,files){ +const key2=chatId??NEW_CHAT_DRAFT_KEY;message||files.length>0?this.drafts.set(key2,{message,files:[...files]}):this.drafts.delete(key2)}clearDraftMessage(chatId){const key2=chatId??NEW_CHAT_DRAFT_KEY;this.drafts.delete(key2)}}const draftMessagesStore=new DraftMessagesStore;function useDraftMessages(options){onMount$1(()=>{const chatId=options.getChatId(),draft=draftMessagesStore.getDraftMessage(chatId);(draft.message||draft.files.length>0)&&!options.getInitialMessage()&&(options.setMessage(draft. +message),options.setFiles(draft.files))}),beforeNavigate(()=>{const chatId=options.getChatId();draftMessagesStore.saveDraftMessage(chatId,options.getMessage(),options.getFiles())}),afterNavigate(navigation=>{if(navigation?.from!=null){const chatId=options.getChatId(),draft=draftMessagesStore.getDraftMessage(chatId);options.setMessage(draft.message),options.setFiles(draft.files)}});function clearDraft(){const chatId=options.getChatId();draftMessagesStore.clearDraftMessage(chatId)}return{clearDraft}} +var root$Z=from_html('
');function ChatScreenForm($$anchor,$$props){push$1($$props,!0);let disabled=prop($$props,"disabled",3,!1),initialMessage=prop($$props,"initialMessage",3,""),isLoading2=prop($$props,"isLoading",3,!1),uploadedFiles=prop($$props,"uploadedFiles",31,()=>proxy([])),chatFormRef=state$1(void 0),chatId=user_derived(()=>page$1.params.id),hasLoadingAttachments=user_derived(()=>uploadedFiles().some(f=>f.isLoading)),message=user_derived( +initialMessage),previousIsLoading=user_derived(isLoading2),previousInitialMessage=user_derived(initialMessage);const{clearDraft}=useDraftMessages({getChatId:()=>get$3(chatId),getMessage:()=>get$3(message),getFiles:()=>uploadedFiles(),setMessage:m=>set$1(message,m),setFiles:f=>uploadedFiles(f),getInitialMessage:()=>initialMessage()});function handleFilesAdd(files){$$props.onFileUpload?.(files)}async function handleSubmit(){if(!get$3(message).trim()&&uploadedFiles().length===0||disabled()||get$3(hasLoadingAttachments)|| +!get$3(chatFormRef)?.checkModelSelected())return;const messageToSend=get$3(message).trim(),filesToSend=[...uploadedFiles()];set$1(message,""),uploadedFiles([]),clearDraft(),get$3(chatFormRef)?.resetTextareaHeight(),await $$props.onSend?.(messageToSend,filesToSend)||(set$1(message,messageToSend),uploadedFiles(filesToSend))}function handleSystemPromptClick(){$$props.onSystemPromptAdd?.({message:get$3(message),files:uploadedFiles()})}function handleUploadedFileRemove(fileId){$$props.onFileRemove?.( +fileId)}onMount$1(()=>{setTimeout(()=>get$3(chatFormRef)?.focus(),10)}),afterNavigate(navigation=>{navigation?.from!=null&&setTimeout(()=>get$3(chatFormRef)?.focus(),10)}),user_effect(()=>{initialMessage()!==get$3(previousInitialMessage)&&(set$1(message,initialMessage()),set$1(previousInitialMessage,initialMessage()))}),user_effect(()=>{get$3(previousIsLoading)&&!isLoading2()&&setTimeout(()=>get$3(chatFormRef)?.focus(),10),set$1(previousIsLoading,isLoading2())});var div=root$Z(),node2=child(div); +bind_this(ChatForm(node2,{get class(){return $$props.class},get disabled(){return disabled()},get isLoading(){return isLoading2()},showMcpPromptButton:!0,onFilesAdd:handleFilesAdd,get onStop(){return $$props.onStop},onSubmit:handleSubmit,onSystemPromptClick:handleSystemPromptClick,onUploadedFileRemove:handleUploadedFileRemove,get value(){return get$3(message)},set value($$value){set$1(message,$$value)},get uploadedFiles(){return uploadedFiles()},set uploadedFiles($$value){uploadedFiles($$value)}}), +$$value=>set$1(chatFormRef,$$value,!0),()=>get$3(chatFormRef)),reset(div),append($$anchor,div),pop()}var root_1$u=from_html(' '),root$Y=from_html('
');function ChatScreenProcessingInfo($$anchor,$$props){push$1($$props,!0);const processingState=useProcessingState(),processingInfoCtx=getProcessingInfoContext();let showProcessingInfo=user_derived( +()=>processingInfoCtx.showProcessingInfo),isCurrentConversationLoading=user_derived(isLoading),isStreaming=user_derived(isChatStreaming),processingDetails=user_derived(()=>processingState.getTechnicalDetails());user_effect(()=>{const conversation=activeConversation();untrack$1(()=>chatStore.setActiveProcessingConversation(conversation?.id??null))}),user_effect(()=>{const keepStatsVisible=config$1().keepStatsVisible;if((keepStatsVisible||get$3(isCurrentConversationLoading)||get$3(isStreaming))&&processingState. +startMonitoring(),!get$3(isCurrentConversationLoading)&&!get$3(isStreaming)&&!keepStatsVisible){const timeout=setTimeout(()=>{!config$1().keepStatsVisible&&!isChatStreaming()&&processingState.stopMonitoring()},PROCESSING_INFO_TIMEOUT);return()=>clearTimeout(timeout)}}),user_effect(()=>{const conversation=activeConversation(),messages=activeMessages();if(config$1().keepStatsVisible&&conversation){if(messages.length===0){untrack$1(()=>chatStore.clearProcessingState(conversation.id));return}!get$3( +isCurrentConversationLoading)&&!get$3(isStreaming)&&untrack$1(()=>chatStore.restoreProcessingStateFromMessages(messages,conversation.id))}});var div=root$Y(),div_1=child(div);each(div_1,20,()=>get$3(processingDetails),detail=>detail,($$anchor2,detail)=>{var span=root_1$u(),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,detail)),append($$anchor2,span)}),reset(div_1),reset(div),template_effect(()=>set_class(div,1,clsx(["chat-processing-info-container pointer-events-none",get$3( +showProcessingInfo)&&"visible"]),"svelte-1ktvj8d")),append($$anchor,div),pop()}const emptyOptions$7={};function toString$1(value,options){const settings=emptyOptions$7,includeImageAlt=typeof settings.includeImageAlt=="boolean"?settings.includeImageAlt:!0,includeHtml=typeof settings.includeHtml=="boolean"?settings.includeHtml:!0;return one$2(value,includeImageAlt,includeHtml)}function one$2(value,includeImageAlt,includeHtml){if(node(value)){if("value"in value)return value.type==="html"&&!includeHtml? +"":value.value;if(includeImageAlt&&"alt"in value&&value.alt)return value.alt;if("children"in value)return all$2(value.children,includeImageAlt,includeHtml)}return Array.isArray(value)?all$2(value,includeImageAlt,includeHtml):""}function all$2(values,includeImageAlt,includeHtml){const result=[];let index2=-1;for(;++index2end?0:end+start2:start2=start2>end?end:start2,remove2=remove2>0?remove2:0,items2. +length<1e4)parameters=Array.from(items2),parameters.unshift(start2,remove2),list2.splice(...parameters);else for(remove2&&list2.splice(start2,remove2);chunkStart0?(splice(list2,list2.length,0,items2),list2):items2}const hasOwnProperty={}.hasOwnProperty;function combineExtensions(extensions){const all2={}; +let index2=-1;for(;++index213&&code2<32||code2>126&&code2<160||code2>55295&&code2<57344||code2>64975&&code2<65008||(code2&65535)===65535||(code2&65535)===65534||code2>1114111?"�":String.fromCodePoint(code2)}function normalizeIdentifier(value){return value.replace( +/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const asciiAlpha=regexCheck(/[A-Za-z]/),asciiAlphanumeric=regexCheck(/[\dA-Za-z]/),asciiAtext=regexCheck(/[#-'*+\--9=?A-Z^-~]/);function asciiControl(code2){return code2!==null&&(code2<32||code2===127)}const asciiDigit=regexCheck(/\d/),asciiHexDigit=regexCheck(/[\dA-Fa-f]/),asciiPunctuation=regexCheck(/[!-/:-@[-`{-~]/);function markdownLineEnding(code2){return code2!==null&&code2<-2}function markdownLineEndingOrSpace(code2){return code2!== +null&&(code2<0||code2===32)}function markdownSpace(code2){return code2===-2||code2===-1||code2===32}const unicodePunctuation=regexCheck(new RegExp("\\p{P}|\\p{S}","u")),unicodeWhitespace=regexCheck(/\s/);function regexCheck(regex){return check;function check(code2){return code2!==null&&code2>-1&®ex.test(String.fromCharCode(code2))}}function normalizeUri(value){const result=[];let index2=-1,start2=0,skip=0;for(;++index255295&&code2<57344){const next2=value.charCodeAt(index2+1);code2<56320&&next2>56319&&next2<57344?(replace2=String.fromCharCode(code2,next2),skip=1):replace2="�"}else replace2=String.fromCharCode(code2);replace2&&(result.push(value.slice(start2,index2),encodeURIComponent( +replace2)),start2=index2+skip+1,replace2=""),skip&&(index2+=skip,skip=0)}return result.join("")+value.slice(start2)}function factorySpace(effects,ok,type2,max2){const limit2=max2?max2-1:Number.POSITIVE_INFINITY;let size2=0;return start2;function start2(code2){return markdownSpace(code2)?(effects.enter(type2),prefix(code2)):ok(code2)}function prefix(code2){return markdownSpace(code2)&&size2++lineStartOffset))return;const indexBeforeExits=self2.events.length;let indexBeforeFlow=indexBeforeExits,seen2,point2;for(;indexBeforeFlow--;)if(self2.events[indexBeforeFlow][0]==="exit"&&self2.events[indexBeforeFlow][1].type==="chunkFlow"){if(seen2){point2=self2.events[indexBeforeFlow][1].end;break}seen2=!0}for(exitContainers(continued),index2=indexBeforeExits;index2size2;){const entry=stack[index2];self2.containerState=entry[1],entry[0].exit.call(self2,effects)}stack.length=size2}function closeFlow(){childFlow.write([null]),childToken=void 0,childFlow=void 0,self2.containerState._closeFlow=void 0}}function tokenizeContainer(effects,ok,nok){return factorySpace(effects,effects.attempt(this.parser.constructs.document,ok,nok),"linePrefix", +this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function classifyCharacter(code2){if(code2===null||markdownLineEndingOrSpace(code2)||unicodeWhitespace(code2))return 1;if(unicodePunctuation(code2))return 2}function resolveAll(constructs2,events,context){const called=[];let index2=-1;for(;++index21&&events[index2][1].end.offset-events[index2][1].start.offset>1?2:1;const start2={...events[open2][1].end},end={ +...events[index2][1].start};movePoint(start2,-use),movePoint(end,use),openingSequence={type:use>1?"strongSequence":"emphasisSequence",start:start2,end:{...events[open2][1].end}},closingSequence={type:use>1?"strongSequence":"emphasisSequence",start:{...events[index2][1].start},end},text2={type:use>1?"strongText":"emphasisText",start:{...events[open2][1].end},end:{...events[index2][1].start}},group={type:use>1?"strong":"emphasis",start:{...openingSequence.start},end:{...closingSequence.end}},events[open2][1]. +end={...openingSequence.start},events[index2][1].start={...closingSequence.end},nextEvents=[],events[open2][1].end.offset-events[open2][1].start.offset&&(nextEvents=push(nextEvents,[["enter",events[open2][1],context],["exit",events[open2][1],context]])),nextEvents=push(nextEvents,[["enter",group,context],["enter",openingSequence,context],["exit",openingSequence,context],["enter",text2,context]]),nextEvents=push(nextEvents,resolveAll(context.parser.constructs.insideSpan.null,events.slice(open2+1, +index2),context)),nextEvents=push(nextEvents,[["exit",text2,context],["enter",closingSequence,context],["exit",closingSequence,context],["exit",group,context]]),events[index2][1].end.offset-events[index2][1].start.offset?(offset2=2,nextEvents=push(nextEvents,[["enter",events[index2][1],context],["exit",events[index2][1],context]])):offset2=0,splice(events,open2-1,index2-open2+3,nextEvents),index2=open2+nextEvents.length-offset2-2;break}}for(index2=-1;++index20&&markdownSpace(code2)?factorySpace(effects,beforeContentChunk,"linePrefix",initialPrefix+1)(code2):beforeContentChunk( +code2)}function beforeContentChunk(code2){return code2===null||markdownLineEnding(code2)?effects.check(nonLazyContinuation$1,atNonLazyBreak,after)(code2):(effects.enter("codeFlowValue"),contentChunk(code2))}function contentChunk(code2){return code2===null||markdownLineEnding(code2)?(effects.exit("codeFlowValue"),beforeContentChunk(code2)):(effects.consume(code2),contentChunk)}function after(code2){return effects.exit("codeFenced"),ok(code2)}function tokenizeCloseStart(effects2,ok2,nok2){let size2=0; +return startBefore;function startBefore(code2){return effects2.enter("lineEnding"),effects2.consume(code2),effects2.exit("lineEnding"),start3}function start3(code2){return effects2.enter("codeFencedFence"),markdownSpace(code2)?factorySpace(effects2,beforeSequenceClose,"linePrefix",self2.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(code2):beforeSequenceClose(code2)}function beforeSequenceClose(code2){return code2===marker?(effects2.enter("codeFencedFenceSequence"),sequenceClose( +code2)):nok2(code2)}function sequenceClose(code2){return code2===marker?(size2++,effects2.consume(code2),sequenceClose):size2>=sizeOpen?(effects2.exit("codeFencedFenceSequence"),markdownSpace(code2)?factorySpace(effects2,sequenceCloseAfter,"whitespace")(code2):sequenceCloseAfter(code2)):nok2(code2)}function sequenceCloseAfter(code2){return code2===null||markdownLineEnding(code2)?(effects2.exit("codeFencedFence"),ok2(code2)):nok2(code2)}}}function tokenizeNonLazyContinuation$1(effects,ok,nok){const self2=this; +return start2;function start2(code2){return code2===null?nok(code2):(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),lineStart)}function lineStart(code2){return self2.parser.lazy[self2.now().line]?nok(code2):ok(code2)}}const codeIndented={name:"codeIndented",tokenize:tokenizeCodeIndented},furtherStart={partial:!0,tokenize:tokenizeFurtherStart};function tokenizeCodeIndented(effects,ok,nok){const self2=this;return start2;function start2(code2){return effects.enter("co\ +deIndented"),factorySpace(effects,afterPrefix,"linePrefix",5)(code2)}function afterPrefix(code2){const tail=self2.events[self2.events.length-1];return tail&&tail[1].type==="linePrefix"&&tail[2].sliceSerialize(tail[1],!0).length>=4?atBreak(code2):nok(code2)}function atBreak(code2){return code2===null?after(code2):markdownLineEnding(code2)?effects.attempt(furtherStart,atBreak,after)(code2):(effects.enter("codeFlowValue"),inside(code2))}function inside(code2){return code2===null||markdownLineEnding( +code2)?(effects.exit("codeFlowValue"),atBreak(code2)):(effects.consume(code2),inside)}function after(code2){return effects.exit("codeIndented"),ok(code2)}}function tokenizeFurtherStart(effects,ok,nok){const self2=this;return furtherStart2;function furtherStart2(code2){return self2.parser.lazy[self2.now().line]?nok(code2):markdownLineEnding(code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),furtherStart2):factorySpace(effects,afterPrefix,"linePrefix",5)(code2)}function afterPrefix(code2){ +const tail=self2.events[self2.events.length-1];return tail&&tail[1].type==="linePrefix"&&tail[2].sliceSerialize(tail[1],!0).length>=4?ok(code2):markdownLineEnding(code2)?furtherStart2(code2):nok(code2)}}const codeText={name:"codeText",previous:previous$2,resolve:resolveCodeText,tokenize:tokenizeCodeText};function resolveCodeText(events){let tailExitIndex=events.length-4,headEnterIndex=3,index2,enter;if((events[headEnterIndex][1].type==="lineEnding"||events[headEnterIndex][1].type==="space")&&(events[tailExitIndex][1]. +type==="lineEnding"||events[tailExitIndex][1].type==="space")){for(index2=headEnterIndex;++index2=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+index2+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return index2this.left.length?this.right.slice(this.right.length-stop+this.left.length,this.right.length-start2+this.left.length).reverse():this.left.slice(start2).concat(this.right.slice(this.right.length-stop+this.left.length).reverse())}splice(start2,deleteCount,items2){const count=deleteCount||0;this.setCursor(Math.trunc(start2));const removed=this.right.splice(this.right.length-count,Number.POSITIVE_INFINITY);return items2&&chunkedPush(this. +left,items2),removed.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(item){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(item)}pushMany(items2){this.setCursor(Number.POSITIVE_INFINITY),chunkedPush(this.left,items2)}unshift(item){this.setCursor(0),this.right.push(item)}unshiftMany(items2){this.setCursor(0),chunkedPush(this.right,items2.reverse())}setCursor(n){if(!(n===this.left.length||n>this.left.length&&this.right.length===0||n<0&&this.left.length=== +0))if(n=4?ok(code2):effects.interrupt(self2.parser.constructs.flow,nok,ok)(code2)}}function factoryDestination(effects,ok,nok,type2,literalType,literalMarkerType,rawType,stringType,max2){const limit2=max2||Number.POSITIVE_INFINITY; +let balance=0;return start2;function start2(code2){return code2===60?(effects.enter(type2),effects.enter(literalType),effects.enter(literalMarkerType),effects.consume(code2),effects.exit(literalMarkerType),enclosedBefore):code2===null||code2===32||code2===41||asciiControl(code2)?nok(code2):(effects.enter(type2),effects.enter(rawType),effects.enter(stringType),effects.enter("chunkString",{contentType:"string"}),raw2(code2))}function enclosedBefore(code2){return code2===62?(effects.enter(literalMarkerType), +effects.consume(code2),effects.exit(literalMarkerType),effects.exit(literalType),effects.exit(type2),ok):(effects.enter(stringType),effects.enter("chunkString",{contentType:"string"}),enclosed(code2))}function enclosed(code2){return code2===62?(effects.exit("chunkString"),effects.exit(stringType),enclosedBefore(code2)):code2===null||code2===60||markdownLineEnding(code2)?nok(code2):(effects.consume(code2),code2===92?enclosedEscape:enclosed)}function enclosedEscape(code2){return code2===60||code2=== +62||code2===92?(effects.consume(code2),enclosed):enclosed(code2)}function raw2(code2){return!balance&&(code2===null||code2===41||markdownLineEndingOrSpace(code2))?(effects.exit("chunkString"),effects.exit(stringType),effects.exit(rawType),effects.exit(type2),ok(code2)):balance999||code2===null||code2===91||code2===93&&!seen2||code2===94&&!size2&&"\ +_hiddenFootnoteSupport"in self2.parser.constructs?nok(code2):code2===93?(effects.exit(stringType),effects.enter(markerType),effects.consume(code2),effects.exit(markerType),effects.exit(type2),ok):markdownLineEnding(code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),atBreak):(effects.enter("chunkString",{contentType:"string"}),labelInside(code2))}function labelInside(code2){return code2===null||code2===91||code2===93||markdownLineEnding(code2)||size2++>999?(effects. +exit("chunkString"),atBreak(code2)):(effects.consume(code2),seen2||(seen2=!markdownSpace(code2)),code2===92?labelEscape:labelInside)}function labelEscape(code2){return code2===91||code2===92||code2===93?(effects.consume(code2),size2++,labelInside):labelInside(code2)}}function factoryTitle(effects,ok,nok,type2,markerType,stringType){let marker;return start2;function start2(code2){return code2===34||code2===39||code2===40?(effects.enter(type2),effects.enter(markerType),effects.consume(code2),effects. +exit(markerType),marker=code2===40?41:code2,begin):nok(code2)}function begin(code2){return code2===marker?(effects.enter(markerType),effects.consume(code2),effects.exit(markerType),effects.exit(type2),ok):(effects.enter(stringType),atBreak(code2))}function atBreak(code2){return code2===marker?(effects.exit(stringType),begin(marker)):code2===null?nok(code2):markdownLineEnding(code2)?(effects.enter("lineEnding"),effects.consume(code2),effects.exit("lineEnding"),factorySpace(effects,atBreak,"linePr\ +efix")):(effects.enter("chunkString",{contentType:"string"}),inside(code2))}function inside(code2){return code2===marker||code2===null||markdownLineEnding(code2)?(effects.exit("chunkString"),atBreak(code2)):(effects.consume(code2),code2===92?escape2:inside)}function escape2(code2){return code2===marker||code2===92?(effects.consume(code2),inside):inside(code2)}}function factoryWhitespace(effects,ok){let seen2;return start2;function start2(code2){return markdownLineEnding(code2)?(effects.enter("li\ +neEnding"),effects.consume(code2),effects.exit("lineEnding"),seen2=!0,start2):markdownSpace(code2)?factorySpace(effects,start2,seen2?"linePrefix":"lineSuffix")(code2):ok(code2)}}const definition$1={name:"definition",tokenize:tokenizeDefinition},titleBefore={partial:!0,tokenize:tokenizeTitleBefore};function tokenizeDefinition(effects,ok,nok){const self2=this;let identifier2;return start2;function start2(code2){return effects.enter("definition"),before(code2)}function before(code2){return factoryLabel. +call(self2,effects,labelAfter,nok,"definitionLabel","definitionLabelMarker","definitionLabelString")(code2)}function labelAfter(code2){return identifier2=normalizeIdentifier(self2.sliceSerialize(self2.events[self2.events.length-1][1]).slice(1,-1)),code2===58?(effects.enter("definitionMarker"),effects.consume(code2),effects.exit("definitionMarker"),markerAfter):nok(code2)}function markerAfter(code2){return markdownLineEndingOrSpace(code2)?factoryWhitespace(effects,destinationBefore)(code2):destinationBefore( +code2)}function destinationBefore(code2){return factoryDestination(effects,destinationAfter,nok,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(code2)}function destinationAfter(code2){return effects.attempt(titleBefore,after,after)(code2)}function after(code2){return markdownSpace(code2)?factorySpace(effects,afterWhitespace,"whitespace")(code2):afterWhitespace(code2)}function afterWhitespace(code2){ return code2===null||markdownLineEnding(code2)?(effects.exit("definition"),self2.parser.defined.push(identifier2),ok(code2)):nok(code2)}}function tokenizeTitleBefore(effects,ok,nok){return titleBefore2;function titleBefore2(code2){return markdownLineEndingOrSpace(code2)?factoryWhitespace(effects,beforeMarker)(code2):nok(code2)}function beforeMarker(code2){return factoryTitle(effects,titleAfter,nok,"definitionTitle","definitionTitleMarker","definitionTitleString")(code2)}function titleAfter(code2){ return markdownSpace(code2)?factorySpace(effects,titleAfterOptionalWhitespace,"whitespace")(code2):titleAfterOptionalWhitespace(code2)}function titleAfterOptionalWhitespace(code2){return code2===null||markdownLineEnding(code2)?ok(code2):nok(code2)}}const hardBreakEscape={name:"hardBreakEscape",tokenize:tokenizeHardBreakEscape};function tokenizeHardBreakEscape(effects,ok,nok){return start2;function start2(code2){return effects.enter("hardBreakEscape"),effects.consume(code2),after}function after(code2){ return markdownLineEnding(code2)?(effects.exit("hardBreakEscape"),ok(code2)):nok(code2)}}const headingAtx={name:"headingAtx",resolve:resolveHeadingAtx,tokenize:tokenizeHeadingAtx};function resolveHeadingAtx(events,context){let contentEnd=events.length-2,contentStart=3,content2,text2;return events[contentStart][1].type==="whitespace"&&(contentStart+=2),contentEnd-2>contentStart&&events[contentEnd][1].type==="whitespace"&&(contentEnd-=2),events[contentEnd][1].type==="atxHeadingSequence"&&(contentStart=== diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte index 550631a9e30..281e6ad0c54 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte @@ -1,4 +1,6 @@ -{#each displayMessages as { message, toolMessages, isLastAssistantMessage, siblingInfo } (message.id)} - -{/each} - -{#if activeConversation() && agenticPendingSteeringMessageContent(activeConversation()!.id)} - {@const convId = activeConversation()!.id} - {@const pendingContent = agenticPendingSteeringMessageContent(convId)} - - {#if pendingContent} - chatStore.abortCurrentFlow(convId)} - onEdit={(newContent, extras) => agenticInjectSteeringMessage(convId, newContent, extras)} - onDelete={() => agenticClearSteeringMessage(convId)} - /> - {/if} -{:else if activeConversation() && chatPendingMessageContent(activeConversation()!.id)} - {@const convId = activeConversation()!.id} - {@const pendingContent = chatPendingMessageContent(convId)} - - {#if pendingContent} - + {#each displayMessages as { message, toolMessages, isLastAssistantMessage, siblingInfo } (message.id)} + chatStore.abortCurrentFlow(convId)} - onEdit={(newContent, extras) => chatInjectPendingMessage(convId, newContent, extras)} - onDelete={() => chatClearPendingMessage(convId)} + {message} + {toolMessages} + {isLastAssistantMessage} + {siblingInfo} /> + {/each} + + {#if activeConversation() && agenticPendingSteeringMessageContent(activeConversation()!.id)} + {@const convId = activeConversation()!.id} + {@const pendingContent = agenticPendingSteeringMessageContent(convId)} + + {#if pendingContent} + chatStore.abortCurrentFlow(convId)} + onEdit={(newContent, extras) => agenticInjectSteeringMessage(convId, newContent, extras)} + onDelete={() => agenticClearSteeringMessage(convId)} + /> + {/if} + {:else if activeConversation() && chatPendingMessageContent(activeConversation()!.id)} + {@const convId = activeConversation()!.id} + {@const pendingContent = chatPendingMessageContent(convId)} + + {#if pendingContent} + chatStore.abortCurrentFlow(convId)} + onEdit={(newContent, extras) => chatInjectPendingMessage(convId, newContent, extras)} + onDelete={() => chatClearPendingMessage(convId)} + /> + {/if} {/if} -{/if} +
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index bc114d484be..85ce536af29 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -42,6 +42,8 @@ let { showCenteredEmpty = false } = $props(); + const autoScroll = createAutoScrollController(); + let disableAutoScroll = $derived(Boolean(config().disableAutoScroll)); let chatScrollContainer: HTMLDivElement | undefined = $state(); let dragCounter = $state(0); @@ -49,8 +51,6 @@ let showFileErrorDialog = $state(false); let uploadedFiles = $state([]); - const autoScroll = createAutoScrollController({ isColumnReverse: true }); - let fileErrorData = $state<{ generallyUnsupported: File[]; modalityUnsupported: File[]; @@ -322,6 +322,14 @@ } }); + function handleMessagesReady() { + if (!disableAutoScroll) { + requestAnimationFrame(() => { + autoScroll.scrollToBottom('instant'); + }); + } + } + onMount(() => { autoScroll.startObserving(); @@ -357,7 +365,7 @@
{/if} diff --git a/tools/server/webui/src/lib/hooks/use-auto-scroll.svelte.ts b/tools/server/webui/src/lib/hooks/use-auto-scroll.svelte.ts index 204d71a90a0..f4b9a0fb059 100644 --- a/tools/server/webui/src/lib/hooks/use-auto-scroll.svelte.ts +++ b/tools/server/webui/src/lib/hooks/use-auto-scroll.svelte.ts @@ -2,7 +2,6 @@ import { AUTO_SCROLL_AT_BOTTOM_THRESHOLD, AUTO_SCROLL_INTERVAL } from '$lib/cons export interface AutoScrollOptions { disabled?: boolean; - isColumnReverse?: boolean; } /** @@ -12,7 +11,6 @@ export interface AutoScrollOptions { * - Auto-scrolls to bottom during streaming/loading * - Stops auto-scroll when user manually scrolls up * - Resumes auto-scroll when user scrolls back to bottom - * - Supports both normal and column-reverse scroll containers */ export class AutoScrollController { private _autoScrollEnabled = $state(true); @@ -22,14 +20,11 @@ export class AutoScrollController { private _scrollTimeout: ReturnType | undefined; private _container: HTMLElement | undefined; private _disabled: boolean; - private _isColumnReverse: boolean; private _mutationObserver: MutationObserver | null = null; private _rafPending = false; private _observerEnabled = false; - constructor(options: AutoScrollOptions = {}) { this._disabled = options.disabled ?? false; - this._isColumnReverse = options.isColumnReverse ?? false; } get autoScrollEnabled(): boolean { @@ -73,20 +68,8 @@ export class AutoScrollController { if (this._disabled || !this._container) return; const { scrollTop, scrollHeight, clientHeight } = this._container; - - let distanceFromBottom: number; - let isScrollingUp: boolean; - - if (this._isColumnReverse) { - // column-reverse: scrollTop=0 at bottom, negative when scrolled up - distanceFromBottom = Math.abs(scrollTop); - isScrollingUp = scrollTop < this._lastScrollTop; - } else { - // normal: scrollTop=0 at top, increases when scrolled down - distanceFromBottom = scrollHeight - clientHeight - scrollTop; - isScrollingUp = scrollTop < this._lastScrollTop; - } - + const distanceFromBottom = scrollHeight - clientHeight - scrollTop; + const isScrollingUp = scrollTop < this._lastScrollTop; const isAtBottom = distanceFromBottom < AUTO_SCROLL_AT_BOTTOM_THRESHOLD; if (isScrollingUp && !isAtBottom) { @@ -116,13 +99,7 @@ export class AutoScrollController { */ scrollToBottom(behavior: ScrollBehavior = 'smooth'): void { if (this._disabled || !this._container) return; - - if (this._isColumnReverse) { - // column-reverse: scrollTop=0 is the bottom - this._container.scrollTo({ top: 0, behavior }); - } else { - this._container.scrollTo({ top: this._container.scrollHeight, behavior }); - } + this._container.scrollTo({ top: this._container.scrollHeight, behavior }); } /** @@ -210,20 +187,13 @@ export class AutoScrollController { private _doStartObserving(): void { if (!this._container || this._mutationObserver) return; - const isReverse = this._isColumnReverse; - this._mutationObserver = new MutationObserver(() => { if (!this._autoScrollEnabled || this._rafPending) return; this._rafPending = true; requestAnimationFrame(() => { this._rafPending = false; if (this._autoScrollEnabled && this._container) { - if (isReverse) { - // column-reverse: scrollTop=0 is the bottom - this._container.scrollTop = 0; - } else { - this._container.scrollTop = this._container.scrollHeight; - } + this._container.scrollTop = this._container.scrollHeight; } }); }); From cce09f0b2b37028caf6f549c976ba16b3e8703d8 Mon Sep 17 00:00:00 2001 From: fredzillman Date: Tue, 12 May 2026 17:16:01 -0230 Subject: [PATCH 078/158] convert : fix Pixtral 12B --mistral-format conversion (3 bugs) (#22981) --- convert_hf_to_gguf.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/convert_hf_to_gguf.py b/convert_hf_to_gguf.py index d79372ceac7..5cff8684856 100755 --- a/convert_hf_to_gguf.py +++ b/convert_hf_to_gguf.py @@ -2865,8 +2865,12 @@ def __init__(self, *args, **kwargs): # fix for SmolVLM2, missing `num_attention_heads` in config.json if self.hf_arch == "VLlama3ForCausalLM": self.hparams["num_attention_heads"] = self.hparams.get("num_attention_heads", 32) - hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) - self.origin_hf_arch = hparams.get('architectures', [None])[0] + # Mistral consolidated format has no config.json; origin_hf_arch is HF-only. + if self.is_mistral_format: + self.origin_hf_arch = None + else: + hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) + self.origin_hf_arch = hparams.get('architectures', [None])[0] def set_vocab(self): if self.origin_hf_arch == "GlmasrModel": @@ -13409,7 +13413,7 @@ def set_gguf_parameters(self): self.gguf_writer.add_vision_use_silu(True) # spatial_merge_size - if self.find_vparam(["mm_projector_id"]) == "patch_merge": + if self.find_vparam(["mm_projector_id"], optional=True) == "patch_merge": self.gguf_writer.add_vision_spatial_merge_size( self.find_vparam(["spatial_merge_size"]) ) @@ -13417,8 +13421,12 @@ def set_gguf_parameters(self): def map_tensor_name(self, name: str, try_suffixes: Sequence[str] = (".weight", ".bias")) -> str: if name == "vision_language_adapter.w_in.weight": return "mm.1.weight" + elif name == "vision_language_adapter.w_in.bias": + return "mm.1.bias" elif name == "vision_language_adapter.w_out.weight": return "mm.2.weight" + elif name == "vision_language_adapter.w_out.bias": + return "mm.2.bias" return super().map_tensor_name(name, try_suffixes) From a9883db8ee021cf16783016a60996d41820b5195 Mon Sep 17 00:00:00 2001 From: yzyyzyhhh <96101183+happyyzy@users.noreply.github.com> Date: Wed, 13 May 2026 04:10:37 +0800 Subject: [PATCH 079/158] opencl: add opt-in Adreno xmem F16xF32 GEMM for prefill (#22755) * ggml-opencl: add Adreno xmem F16xF32 GEMM for prefill * ggml-opencl: address Adreno xmem review comments * ggml-opencl: align xmem gemm kernel naming --------- Co-authored-by: Your Name --- ggml/src/ggml-opencl/CMakeLists.txt | 4 + ggml/src/ggml-opencl/ggml-opencl.cpp | 220 +++++++++++++++++ .../kernels/gemm_xmem_f16_f32_os8.cl | 233 ++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 ggml/src/ggml-opencl/kernels/gemm_xmem_f16_f32_os8.cl diff --git a/ggml/src/ggml-opencl/CMakeLists.txt b/ggml/src/ggml-opencl/CMakeLists.txt index 7edb3eb4e9c..0b39c011371 100644 --- a/ggml/src/ggml-opencl/CMakeLists.txt +++ b/ggml/src/ggml-opencl/CMakeLists.txt @@ -176,6 +176,10 @@ set(GGML_OPENCL_KERNELS flash_attn_f32 ) +if (GGML_OPENCL_USE_ADRENO_KERNELS) + list(APPEND GGML_OPENCL_KERNELS gemm_xmem_f16_f32_os8) +endif () + foreach (K ${GGML_OPENCL_KERNELS}) ggml_opencl_add_kernel(${K}) endforeach() diff --git a/ggml/src/ggml-opencl/ggml-opencl.cpp b/ggml/src/ggml-opencl/ggml-opencl.cpp index 73a58f74a94..61bdc62cd10 100644 --- a/ggml/src/ggml-opencl/ggml-opencl.cpp +++ b/ggml/src/ggml-opencl/ggml-opencl.cpp @@ -407,6 +407,8 @@ struct ggml_backend_opencl_context { cl_bool non_uniform_workgroups; size_t image_max_buffer_size; + size_t image2d_max_width; + size_t image2d_max_height; cl_context context; cl_command_queue queue; @@ -420,6 +422,11 @@ struct ggml_backend_opencl_context { ggml_cl_buffer prealloc_src0; ggml_cl_buffer prealloc_src1; +#ifdef GGML_OPENCL_USE_ADRENO_KERNELS + ggml_cl_buffer prealloc_adreno_xmem_const; + bool adreno_xmem_gemm_enabled = false; +#endif + // prealloc buffers for MoE router table preprocess bool toggle_reorder = false; ggml_cl_buffer prealloc_post_router; @@ -538,6 +545,10 @@ struct ggml_backend_opencl_context { cl_kernel kernel_mul_mat_f16_f32; cl_kernel kernel_mul_mat_f16_f32_l4; cl_kernel kernel_mul_mat_f16_f32_tiled; + cl_kernel kernel_adreno_xmem_pack_src_f32; + cl_kernel kernel_adreno_xmem_prepack_weight_f16; + cl_kernel kernel_gemm_xmem_f16_f32_os8; + cl_kernel kernel_adreno_xmem_store_dst_f32; cl_kernel kernel_mul_mm_f16_f32_kqv; cl_kernel kernel_mul_mm_f16_f32_kq; cl_kernel kernel_mul_mat_q4_0_f32, kernel_mul_mat_q4_0_f32_v; @@ -1554,6 +1565,32 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx, ggml_cl_ve GGML_LOG_CONT("."); } +#ifdef GGML_OPENCL_USE_ADRENO_KERNELS + // gemm_xmem_f16_f32_os8 + { +#ifdef GGML_OPENCL_EMBED_KERNELS + const std::string kernel_src { + #include "gemm_xmem_f16_f32_os8.cl.h" + }; +#else + const std::string kernel_src = read_file("gemm_xmem_f16_f32_os8.cl"); +#endif + cl_program prog = + build_program_from_source(backend_ctx->context, backend_ctx->device, kernel_src.c_str(), compile_opts); + + CL_CHECK((backend_ctx->kernel_adreno_xmem_pack_src_f32 = + clCreateKernel(prog, "adreno_xmem_pack_src_f32", &err), err)); + CL_CHECK((backend_ctx->kernel_adreno_xmem_prepack_weight_f16 = + clCreateKernel(prog, "adreno_xmem_prepack_weight_f16", &err), err)); + CL_CHECK((backend_ctx->kernel_gemm_xmem_f16_f32_os8 = + clCreateKernel(prog, "kernel_gemm_xmem_f16_f32_os8", &err), err)); + CL_CHECK((backend_ctx->kernel_adreno_xmem_store_dst_f32 = + clCreateKernel(prog, "adreno_xmem_store_dst_f32", &err), err)); + CL_CHECK(clReleaseProgram(prog)); + GGML_LOG_CONT("."); + } +#endif // GGML_OPENCL_USE_ADRENO_KERNELS + // mul_mm_f32_f32_l4_lm { #ifdef GGML_OPENCL_EMBED_KERNELS @@ -3473,6 +3510,10 @@ static ggml_backend_opencl_context * ggml_cl2_init(ggml_backend_dev_t dev) { clGetDeviceInfo(device, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, sizeof(size_t), &backend_ctx->image_max_buffer_size, NULL); GGML_LOG_INFO("ggml_opencl: device max image buffer size (pixels): %lu\n", backend_ctx->image_max_buffer_size); + clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(size_t), &backend_ctx->image2d_max_width, NULL); + clGetDeviceInfo(device, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(size_t), &backend_ctx->image2d_max_height, NULL); + GGML_LOG_INFO("ggml_opencl: device max image2d size: %lu x %lu\n", backend_ctx->image2d_max_width, backend_ctx->image2d_max_height); + clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &backend_ctx->max_workgroup_size, NULL); GGML_LOG_INFO("ggml_opencl: device max workgroup size: %lu\n", backend_ctx->max_workgroup_size); @@ -3511,6 +3552,16 @@ static ggml_backend_opencl_context * ggml_cl2_init(ggml_backend_dev_t dev) { GGML_LOG_INFO("ggml_opencl: using kernels optimized for Adreno (GGML_OPENCL_USE_ADRENO_KERNELS)\n"); #endif // GGML_OPENCL_USE_ADRENO_KERNELS +#ifdef GGML_OPENCL_USE_ADRENO_KERNELS + backend_ctx->adreno_xmem_gemm_enabled = getenv("GGML_OPENCL_ADRENO_XMEM_GEMM") != nullptr && + backend_ctx->gpu_family == GPU_FAMILY::ADRENO; + if (getenv("GGML_OPENCL_ADRENO_XMEM_GEMM") != nullptr) { + GGML_LOG_INFO("ggml_opencl: Adreno xmem F16xF32 GEMM %s\n", + backend_ctx->adreno_xmem_gemm_enabled ? + "enabled (temporary weight prepack)" : "requested but unsupported by this driver"); + } +#endif // GGML_OPENCL_USE_ADRENO_KERNELS + // determine whether to use large buffer for Adreno backend_ctx->adreno_use_large_buffer = getenv("GGML_OPENCL_ADRENO_USE_LARGE_BUFFER") != nullptr && backend_ctx->gpu_family == GPU_FAMILY::ADRENO; @@ -9920,6 +9971,169 @@ static void ggml_cl_mul_mat_f16_f32_tiled(ggml_backend_t backend, const ggml_ten backend_ctx->enqueue_ndrange_kernel(kernel, 2, global_work_size, local_work_size, dst); } +#ifdef GGML_OPENCL_USE_ADRENO_KERNELS +static bool ggml_cl_can_use_adreno_xmem_gemm_f16_f32( + const ggml_backend_opencl_context * backend_ctx, + const ggml_tensor * src0, + const ggml_tensor * src1, + const ggml_tensor * dst) { + if (!backend_ctx->adreno_xmem_gemm_enabled) { + return false; + } + if (backend_ctx->gpu_family != GPU_FAMILY::ADRENO) { + return false; + } + if (src0->type != GGML_TYPE_F16 || src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) { + return false; + } + if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1) || !ggml_is_contiguous(dst)) { + return false; + } + if (src0->ne[2] != 1 || src0->ne[3] != 1 || + src1->ne[2] != 1 || src1->ne[3] != 1 || + dst->ne[2] != 1 || dst->ne[3] != 1) { + return false; + } + const int K = src0->ne[0]; + const int M = src0->ne[1]; + const int N = src1->ne[1]; + if (src1->ne[0] != K || dst->ne[0] != M || dst->ne[1] != N) { + return false; + } + if (N <= 1 || M < 64 || N < 16 || K < 64) { + return false; + } + if ((K % 8) != 0) { + return false; + } + const int kpack = K / 4; + const int npack = CEIL_DIV(M, 4); + if (static_cast(N) > backend_ctx->image2d_max_width || + static_cast(kpack) > backend_ctx->image2d_max_height) { + return false; + } + if (static_cast(N) > backend_ctx->image2d_max_width || + static_cast(npack) > backend_ctx->image2d_max_height) { + return false; + } + return true; +} + +static void ggml_cl_mul_mat_f16_f32_adreno_xmem( + ggml_backend_t backend, + const ggml_tensor * src0, + const ggml_tensor * src1, + ggml_tensor * dst) { + ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context; + + ggml_tensor_extra_cl * extra0 = (ggml_tensor_extra_cl *)src0->extra; + ggml_tensor_extra_cl * extra1 = (ggml_tensor_extra_cl *)src1->extra; + ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *)dst->extra; + + const cl_ulong offset0 = extra0->offset + src0->view_offs; + const cl_ulong offset1 = extra1->offset + src1->view_offs; + const cl_ulong offsetd = extrad->offset + dst->view_offs; + + const int K = src0->ne[0]; + const int M = src0->ne[1]; + const int N = src1->ne[1]; + const int kpack = K / 4; + const int npack = CEIL_DIV(M, 4); + const int os = 8; + + const size_t xmem_bytes = 6144; + const size_t weight_bytes = static_cast(kpack) * static_cast(npack) * 4u * sizeof(cl_half4); + + backend_ctx->prealloc_adreno_xmem_const.allocate(backend_ctx->context, xmem_bytes); + + cl_int err = CL_SUCCESS; + cl_image_format fmt = {}; + fmt.image_channel_order = CL_RGBA; + fmt.image_channel_data_type = CL_HALF_FLOAT; + + cl_image_desc desc_src = {}; + desc_src.image_type = CL_MEM_OBJECT_IMAGE2D; + desc_src.image_width = static_cast(N); + desc_src.image_height = static_cast(kpack); + cl_mem src_img = clCreateImage(backend_ctx->context, CL_MEM_READ_WRITE, &fmt, &desc_src, nullptr, &err); + CL_CHECK(err); + + cl_image_desc desc_dst = {}; + desc_dst.image_type = CL_MEM_OBJECT_IMAGE2D; + desc_dst.image_width = static_cast(N); + desc_dst.image_height = static_cast(npack); + cl_mem dst_img = clCreateImage(backend_ctx->context, CL_MEM_READ_WRITE, &fmt, &desc_dst, nullptr, &err); + CL_CHECK(err); + + cl_mem weights = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, weight_bytes, nullptr, &err); + CL_CHECK(err); + + cl_kernel prepack = backend_ctx->kernel_adreno_xmem_prepack_weight_f16; + CL_CHECK(clSetKernelArg(prepack, 0, sizeof(cl_mem), &weights)); + CL_CHECK(clSetKernelArg(prepack, 1, sizeof(cl_mem), &extra0->data_device)); + CL_CHECK(clSetKernelArg(prepack, 2, sizeof(cl_ulong), &offset0)); + CL_CHECK(clSetKernelArg(prepack, 3, sizeof(int), &K)); + CL_CHECK(clSetKernelArg(prepack, 4, sizeof(int), &M)); + CL_CHECK(clSetKernelArg(prepack, 5, sizeof(int), &kpack)); + CL_CHECK(clSetKernelArg(prepack, 6, sizeof(int), &npack)); + CL_CHECK(clSetKernelArg(prepack, 7, sizeof(int), &os)); + size_t lws = 256; + size_t max_wg = backend_ctx->get_kernel_workgroup_size(prepack); + if (lws > max_wg) { + lws = max_wg; + } + size_t gws = CEIL_DIV(static_cast(kpack) * static_cast(npack), lws) * lws; + backend_ctx->enqueue_ndrange_kernel(prepack, 1, &gws, &lws, dst); + + cl_kernel pack_src = backend_ctx->kernel_adreno_xmem_pack_src_f32; + CL_CHECK(clSetKernelArg(pack_src, 0, sizeof(cl_mem), &extra1->data_device)); + CL_CHECK(clSetKernelArg(pack_src, 1, sizeof(cl_ulong), &offset1)); + CL_CHECK(clSetKernelArg(pack_src, 2, sizeof(cl_mem), &src_img)); + CL_CHECK(clSetKernelArg(pack_src, 3, sizeof(int), &K)); + CL_CHECK(clSetKernelArg(pack_src, 4, sizeof(int), &N)); + size_t pack_src_lws[2] = { 16, 16 }; + size_t pack_src_gws[2] = { + CEIL_DIV(static_cast(N), pack_src_lws[0])*pack_src_lws[0], + CEIL_DIV(static_cast(kpack), pack_src_lws[1])*pack_src_lws[1] + }; + backend_ctx->enqueue_ndrange_kernel(pack_src, 2, pack_src_gws, pack_src_lws, dst); + + cl_kernel gemm = backend_ctx->kernel_gemm_xmem_f16_f32_os8; + CL_CHECK(clSetKernelArg(gemm, 0, sizeof(cl_mem), &weights)); + CL_CHECK(clSetKernelArg(gemm, 1, sizeof(cl_mem), &backend_ctx->prealloc_adreno_xmem_const.buffer)); + CL_CHECK(clSetKernelArg(gemm, 2, sizeof(cl_mem), &src_img)); + CL_CHECK(clSetKernelArg(gemm, 3, sizeof(cl_mem), &dst_img)); + CL_CHECK(clSetKernelArg(gemm, 4, sizeof(int), &N)); + CL_CHECK(clSetKernelArg(gemm, 5, sizeof(int), &npack)); + CL_CHECK(clSetKernelArg(gemm, 6, sizeof(int), &kpack)); + const size_t z_values = CEIL_DIV(static_cast(npack), static_cast(os)); + size_t gemm_lws[3] = { 64, 1, 1 }; + size_t gemm_gws[3] = { + z_values*gemm_lws[0], + CEIL_DIV(static_cast(N), gemm_lws[0]), + 1 + }; + backend_ctx->enqueue_ndrange_kernel(gemm, 3, gemm_gws, gemm_lws, dst); + + cl_kernel store_dst = backend_ctx->kernel_adreno_xmem_store_dst_f32; + CL_CHECK(clSetKernelArg(store_dst, 0, sizeof(cl_mem), &dst_img)); + CL_CHECK(clSetKernelArg(store_dst, 1, sizeof(cl_mem), &extrad->data_device)); + CL_CHECK(clSetKernelArg(store_dst, 2, sizeof(cl_ulong), &offsetd)); + CL_CHECK(clSetKernelArg(store_dst, 3, sizeof(int), &M)); + CL_CHECK(clSetKernelArg(store_dst, 4, sizeof(int), &N)); + size_t store_lws[2] = { 16, 16 }; + size_t store_gws[2] = { + CEIL_DIV(static_cast(N), store_lws[0])*store_lws[0], + CEIL_DIV(static_cast(npack), store_lws[1])*store_lws[1] + }; + backend_ctx->enqueue_ndrange_kernel(store_dst, 2, store_gws, store_lws, dst); + + CL_CHECK(clReleaseMemObject(weights)); + CL_CHECK(clReleaseMemObject(dst_img)); + CL_CHECK(clReleaseMemObject(src_img)); +} +#endif // GGML_OPENCL_USE_ADRENO_KERNELS + static void ggml_cl_conv_2d(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { GGML_TENSOR_BINARY_OP_LOCALS; ggml_backend_opencl_context *backend_ctx = (ggml_backend_opencl_context *)backend->context; @@ -11681,6 +11895,12 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co return; } case GGML_TYPE_F16: { +#ifdef GGML_OPENCL_USE_ADRENO_KERNELS + if (ggml_cl_can_use_adreno_xmem_gemm_f16_f32(backend_ctx, src0, src1, dst)) { + ggml_cl_mul_mat_f16_f32_adreno_xmem(backend, src0, src1, dst); + return; + } +#endif kernel = backend_ctx->kernel_mul_mm_f16_f32_l4_lm; nth0 = 128; // calculated as (BM*BN)/(TM*TN) diff --git a/ggml/src/ggml-opencl/kernels/gemm_xmem_f16_f32_os8.cl b/ggml/src/ggml-opencl/kernels/gemm_xmem_f16_f32_os8.cl new file mode 100644 index 00000000000..df9d9aed067 --- /dev/null +++ b/ggml/src/ggml-opencl/kernels/gemm_xmem_f16_f32_os8.cl @@ -0,0 +1,233 @@ +#pragma OPENCL EXTENSION cl_khr_fp16 : enable +#pragma OPENCL EXTENSION cl_qcom_subgroup_uniform_load : enable +#pragma OPENCL EXTENSION cl_qcom_subgroup_constant_load : enable + +__constant sampler_t smp_zero = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST; + +__kernel void adreno_xmem_pack_src_f32( + __global const void * src_void, + ulong offset, + __write_only image2d_t src_img, + int K, + int N) { + const int x = get_global_id(0); + const int y = get_global_id(1); + const int kpack = K / 4; + + if (x >= N || y >= kpack) { + return; + } + + __global const float * src = (__global const float *)((__global const char *)src_void + offset); + const int base = x*K + y*4; + const half4 v = (half4)((half)src[base + 0], (half)src[base + 1], (half)src[base + 2], (half)src[base + 3]); + write_imageh(src_img, (int2)(x, y), v); +} + +__kernel void adreno_xmem_prepack_weight_f16( + __global half4 * dst, + __global const void * src_void, + ulong offset, + int K, + int M, + int kpack, + int npack, + int os) { + const int linear = get_global_id(0); + const int total = kpack*npack; + if (linear >= total) { + return; + } + + __global const half * src = (__global const half *)((__global const char *)src_void + offset); + + const int dst_ogroup = linear % os; + const int dst_o_sp_i = linear / os; + const int dst_i = dst_o_sp_i % kpack; + const int dst_o = dst_o_sp_i / kpack; + const int o_slice = dst_o*os + dst_ogroup; + const int k_base = dst_i*4; + + half4 w0 = (half4)(0.0h); + half4 w1 = (half4)(0.0h); + half4 w2 = (half4)(0.0h); + half4 w3 = (half4)(0.0h); + + const int o0 = o_slice*4 + 0; + const int o1 = o_slice*4 + 1; + const int o2 = o_slice*4 + 2; + const int o3 = o_slice*4 + 3; + + if (k_base + 0 < K) { + if (o0 < M) w0.s0 = src[o0*K + k_base + 0]; + if (o1 < M) w0.s1 = src[o1*K + k_base + 0]; + if (o2 < M) w0.s2 = src[o2*K + k_base + 0]; + if (o3 < M) w0.s3 = src[o3*K + k_base + 0]; + } + if (k_base + 1 < K) { + if (o0 < M) w1.s0 = src[o0*K + k_base + 1]; + if (o1 < M) w1.s1 = src[o1*K + k_base + 1]; + if (o2 < M) w1.s2 = src[o2*K + k_base + 1]; + if (o3 < M) w1.s3 = src[o3*K + k_base + 1]; + } + if (k_base + 2 < K) { + if (o0 < M) w2.s0 = src[o0*K + k_base + 2]; + if (o1 < M) w2.s1 = src[o1*K + k_base + 2]; + if (o2 < M) w2.s2 = src[o2*K + k_base + 2]; + if (o3 < M) w2.s3 = src[o3*K + k_base + 2]; + } + if (k_base + 3 < K) { + if (o0 < M) w3.s0 = src[o0*K + k_base + 3]; + if (o1 < M) w3.s1 = src[o1*K + k_base + 3]; + if (o2 < M) w3.s2 = src[o2*K + k_base + 3]; + if (o3 < M) w3.s3 = src[o3*K + k_base + 3]; + } + + dst[linear*4 + 0] = w0; + dst[linear*4 + 1] = w1; + dst[linear*4 + 2] = w2; + dst[linear*4 + 3] = w3; +} + +__attribute__((qcom_max_concurrent_subgroups(12))) +__kernel void kernel_gemm_xmem_f16_f32_os8( + __constant half8 * weights_buffer __attribute__((sub_group_uniform)), + __constant half8 * xmem_buffer __attribute__((max_constant_size((6144)))), + __read_only image2d_t src_img, + __write_only image2d_t dst_img, + int N, + int npack, + int kpack) { + const int X = get_group_id(1)*get_local_size(0) + get_local_id(0); + const int Z = get_group_id(0)*get_local_size(2) + get_local_id(2); + + if (X >= N || Z*8 >= npack) { + return; + } + + half4 r0 = (half4)(0.0h); + half4 r1 = (half4)(0.0h); + half4 r2 = (half4)(0.0h); + half4 r3 = (half4)(0.0h); + half4 r4 = (half4)(0.0h); + half4 r5 = (half4)(0.0h); + half4 r6 = (half4)(0.0h); + half4 r7 = (half4)(0.0h); + + int f_offset = Z*kpack*32; + int subgroup_id = (int)(0x1F & qcom_get_physical_sub_group_id()); + subgroup_id = subgroup_id % 12; + const int c_offset = subgroup_id*32; + __constant half16 * weights_cache = (__constant half16 *)&xmem_buffer[c_offset]; + + int coord_s = 0; + do { + const half4 src0 = read_imageh(src_img, smp_zero, (int2)(X, coord_s)); + coord_s++; + const half4 src1 = read_imageh(src_img, smp_zero, (int2)(X, coord_s)); + coord_s++; + + qcom_sub_group_constant_load8(xmem_buffer, weights_buffer, c_offset, f_offset >> 1, 32); + f_offset += 64; + qcom_sub_group_sync(QCOM_CLK_CONST_LOAD_SYNC); + + r0 += src0.x * weights_cache[0].s0123; + r0 += src0.y * weights_cache[0].s4567; + r0 += src0.z * weights_cache[0].s89ab; + r0 += src0.w * weights_cache[0].scdef; + r1 += src0.x * weights_cache[1].s0123; + r1 += src0.y * weights_cache[1].s4567; + r1 += src0.z * weights_cache[1].s89ab; + r1 += src0.w * weights_cache[1].scdef; + r2 += src0.x * weights_cache[2].s0123; + r2 += src0.y * weights_cache[2].s4567; + r2 += src0.z * weights_cache[2].s89ab; + r2 += src0.w * weights_cache[2].scdef; + r3 += src0.x * weights_cache[3].s0123; + r3 += src0.y * weights_cache[3].s4567; + r3 += src0.z * weights_cache[3].s89ab; + r3 += src0.w * weights_cache[3].scdef; + r4 += src0.x * weights_cache[4].s0123; + r4 += src0.y * weights_cache[4].s4567; + r4 += src0.z * weights_cache[4].s89ab; + r4 += src0.w * weights_cache[4].scdef; + r5 += src0.x * weights_cache[5].s0123; + r5 += src0.y * weights_cache[5].s4567; + r5 += src0.z * weights_cache[5].s89ab; + r5 += src0.w * weights_cache[5].scdef; + r6 += src0.x * weights_cache[6].s0123; + r6 += src0.y * weights_cache[6].s4567; + r6 += src0.z * weights_cache[6].s89ab; + r6 += src0.w * weights_cache[6].scdef; + r7 += src0.x * weights_cache[7].s0123; + r7 += src0.y * weights_cache[7].s4567; + r7 += src0.z * weights_cache[7].s89ab; + r7 += src0.w * weights_cache[7].scdef; + + r0 += src1.x * weights_cache[8].s0123; + r0 += src1.y * weights_cache[8].s4567; + r0 += src1.z * weights_cache[8].s89ab; + r0 += src1.w * weights_cache[8].scdef; + r1 += src1.x * weights_cache[9].s0123; + r1 += src1.y * weights_cache[9].s4567; + r1 += src1.z * weights_cache[9].s89ab; + r1 += src1.w * weights_cache[9].scdef; + r2 += src1.x * weights_cache[10].s0123; + r2 += src1.y * weights_cache[10].s4567; + r2 += src1.z * weights_cache[10].s89ab; + r2 += src1.w * weights_cache[10].scdef; + r3 += src1.x * weights_cache[11].s0123; + r3 += src1.y * weights_cache[11].s4567; + r3 += src1.z * weights_cache[11].s89ab; + r3 += src1.w * weights_cache[11].scdef; + r4 += src1.x * weights_cache[12].s0123; + r4 += src1.y * weights_cache[12].s4567; + r4 += src1.z * weights_cache[12].s89ab; + r4 += src1.w * weights_cache[12].scdef; + r5 += src1.x * weights_cache[13].s0123; + r5 += src1.y * weights_cache[13].s4567; + r5 += src1.z * weights_cache[13].s89ab; + r5 += src1.w * weights_cache[13].scdef; + r6 += src1.x * weights_cache[14].s0123; + r6 += src1.y * weights_cache[14].s4567; + r6 += src1.z * weights_cache[14].s89ab; + r6 += src1.w * weights_cache[14].scdef; + r7 += src1.x * weights_cache[15].s0123; + r7 += src1.y * weights_cache[15].s4567; + r7 += src1.z * weights_cache[15].s89ab; + r7 += src1.w * weights_cache[15].scdef; + } while (coord_s < kpack); + + int coord_s_out = Z*8; + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r0); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r1); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r2); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r3); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r4); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r5); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r6); coord_s_out++; } + if (coord_s_out < npack) { write_imageh(dst_img, (int2)(X, coord_s_out), r7); } +} + +__kernel void adreno_xmem_store_dst_f32( + __read_only image2d_t dst_img, + __global void * dst_void, + ulong offset, + int M, + int N) { + const int x = get_global_id(0); + const int y = get_global_id(1); + const int npack = (M + 3) / 4; + + if (x >= N || y >= npack) { + return; + } + + __global float * dst = (__global float *)((__global char *)dst_void + offset); + const half4 hv = read_imageh(dst_img, smp_zero, (int2)(x, y)); + const int m = y*4; + if (m + 0 < M) dst[x*M + m + 0] = (float)hv.s0; + if (m + 1 < M) dst[x*M + m + 1] = (float)hv.s1; + if (m + 2 < M) dst[x*M + m + 2] = (float)hv.s2; + if (m + 3 < M) dst[x*M + m + 3] = (float)hv.s3; +} From 856c3adac1709be15e1ea2529a0e89f742d25fe0 Mon Sep 17 00:00:00 2001 From: Trivikram Reddy <127072883+trivikram-reddy1@users.noreply.github.com> Date: Tue, 12 May 2026 19:28:02 -0500 Subject: [PATCH 080/158] hexagon: eliminate scalar VTCM loads via HVX splat helpers (#22993) * hexagon: add hvx_vec_repl helpers and use those for splat-from-vtcm usecase * hmx-mm: optimize per-group scale handling * hmx-fa: optimize slope load from vtcm * hmx-fa: use aligned access where possible in hmx-utils * hexagon: add hvx_vec_repl_2x_f16 helper and consolidate repl helpers --------- Co-authored-by: Max Krasnyansky --- .../src/ggml-hexagon/htp/hmx-flash-attn-ops.c | 5 +- ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c | 29 +++----- ggml/src/ggml-hexagon/htp/hmx-utils.h | 34 ++++----- ggml/src/ggml-hexagon/htp/hvx-repl.h | 74 +++++++++++++++++++ ggml/src/ggml-hexagon/htp/hvx-utils.h | 1 + scripts/snapdragon/adb/run-completion.sh | 2 +- 6 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 ggml/src/ggml-hexagon/htp/hvx-repl.h diff --git a/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c b/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c index 8a6d7c14edf..4a4ff0b331d 100644 --- a/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c +++ b/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c @@ -760,8 +760,9 @@ static void fa_softmax_thread(unsigned int n, unsigned int i, void * data) { // ALiBi slopes — only needed when has_alibi (scheme A) HVX_Vector v_slope0, v_slope1; if (args->has_alibi) { - v_slope0 = hvx_vec_splat_f16(args->slopes[r + 0]); - v_slope1 = (r + 1 < (int) n_rows_g) ? hvx_vec_splat_f16(args->slopes[r + 1]) : Q6_V_vzero(); + HVX_Vector v_s = hvx_vmemu(args->slopes + r); + v_slope0 = hvx_vec_repl_f16(v_s); + v_slope1 = (r + 1 < (int) n_rows_g) ? hvx_vec_repl_f16(Q6_V_vror_VR(v_s, 2)) : Q6_V_vzero(); } const HVX_Vector v_threshold = Q6_Vh_vsplat_R(0xcc00); // fp16 -16.0 (hoisted outside for-c) diff --git a/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c b/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c index 9e8c9966e04..e05ccfd5fc7 100644 --- a/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c +++ b/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c @@ -180,12 +180,10 @@ static int hmx_compute_chunks(size_t vtcm_total, // Dequantize one x4x2 Q4_0 group (32 elements from 32 packed bytes) -> 32 FP16 in first 64 bytes. // In x4x2, sub-blocks 0..3 use lower nibbles, sub-blocks 4..7 use upper nibbles // of the same 32 packed bytes. -static inline HVX_Vector dequantize_x4x2_q4_0_group_hvx( - const uint8_t *packed_32, bool upper_nibbles, - const __fp16 *scale, const HVX_Vector vlut_cvt) { +static inline HVX_Vector dequantize_x4x2_q4_0_group_hvx(const uint8_t *packed_32, bool upper_nibbles, const __fp16 *scale, const HVX_Vector vlut_cvt) { HVX_Vector vq = hvx_vmemu(packed_32); const HVX_Vector mask_h4 = Q6_Vb_vsplat_R(0x0F); - HVX_Vector v_scales = hvx_vec_splat_f16(*scale); + HVX_Vector v_scales = hvx_vec_repl_f16(hvx_vmemu(scale)); // q4x4x2 stores two int4 values per byte. Keep only the selected nibble. HVX_Vector v_quants = Q6_Vub_vlsr_VubR(vq, 4 * upper_nibbles); v_quants = Q6_V_vand_VV(v_quants, mask_h4); @@ -223,9 +221,10 @@ static inline void dequantize_x4x2_q4_0_x4groups_hvx( HVX_Vector v_hi = Q6_V_hi_W(vp); // [group2: 32 fp16 | group3: 32 fp16] // Build per-group scale vectors: first 64 bytes use scale_a, last 64 use scale_b - HVX_VectorPred q64 = Q6_Q_vsetq_R(64); - HVX_Vector v_sc01 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f16(scales_4[0]), hvx_vec_splat_f16(scales_4[1])); - HVX_Vector v_sc23 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f16(scales_4[2]), hvx_vec_splat_f16(scales_4[3])); + volatile HVX_Vector vscale = hvx_vmemu(scales_4); + + HVX_Vector v_sc01 = hvx_vec_repl_2x_f16(vscale); + HVX_Vector v_sc23 = hvx_vec_repl_2x_f16(Q6_V_vror_VR(vscale, 4)); v_lo = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vmpy_VhfVhf(v_lo, v_sc01)); v_hi = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vmpy_VhfVhf(v_hi, v_sc23)); @@ -237,10 +236,10 @@ static inline void dequantize_x4x2_q4_0_x4groups_hvx( // Dequantize one x4x2 Q8_0 group (32 int8 quants) -> 32 FP16 in first 64 bytes. static inline HVX_Vector dequantize_x4x2_q8_0_group_hvx(const int8_t *quants_32, const __fp16 *scale) { - HVX_Vector vq = hvx_vmemu(quants_32); - HVX_Vector v_scales = hvx_vec_splat_f16(*scale); - HVX_Vector v0 = Q6_V_lo_W(Q6_Wh_vunpack_Vb(vq)); - HVX_Vector v_hf = Q6_Vhf_equals_Vh(v0); + HVX_Vector vq = hvx_vmemu(quants_32); + HVX_Vector v_scales = hvx_vec_repl_f16(hvx_vmemu(scale)); + HVX_Vector v0 = Q6_V_lo_W(Q6_Wh_vunpack_Vb(vq)); + HVX_Vector v_hf = Q6_Vhf_equals_Vh(v0); return Q6_Vhf_equals_Vqf16(Q6_Vqf16_vmpy_VhfVhf(v_hf, v_scales)); } @@ -521,12 +520,8 @@ static void dequantize_x4x2_weight_to_fp16_tiles_task( const uint8_t *r0 = vtcm_src + row0 * row_stride; const uint8_t *r1 = vtcm_src + row1 * row_stride; - HVX_Vector v0 = dequantize_x4x2_q8_0_group_hvx( - (const int8_t *)(r0 + byte_off), (const __fp16 *)(r0 + scale_off)); - HVX_Vector v1 = (row1 < n_cols) - ? dequantize_x4x2_q8_0_group_hvx( - (const int8_t *)(r1 + byte_off), (const __fp16 *)(r1 + scale_off)) - : Q6_V_vzero(); + HVX_Vector v0 = dequantize_x4x2_q8_0_group_hvx((const int8_t *)(r0 + byte_off), (const __fp16 *)(r0 + scale_off)); + HVX_Vector v1 = (row1 < n_cols) ? dequantize_x4x2_q8_0_group_hvx((const int8_t *)(r1 + byte_off), (const __fp16 *)(r1 + scale_off)) : Q6_V_vzero(); Q6_vscatter_QRMVwV(q_mask64, (size_t)tile_base, HMX_FP16_TILE_SIZE - 1, v_off, v0); v_off = Q6_Vw_vadd_VwVw(v_off, v_scat_step); diff --git a/ggml/src/ggml-hexagon/htp/hmx-utils.h b/ggml/src/ggml-hexagon/htp/hmx-utils.h index 68f174d6937..f448ee3372a 100644 --- a/ggml/src/ggml-hexagon/htp/hmx-utils.h +++ b/ggml/src/ggml-hexagon/htp/hmx-utils.h @@ -77,16 +77,18 @@ static inline void hmx_interleave_rows_to_tiles(__fp16 * restrict vtcm_dst, const HVX_Vector v_off0 = Q6_Vw_vadd_VwVw(v_scat_base, Q6_V_vsplat_R(local_r * 4)); const HVX_Vector v_off1 = Q6_Vw_vadd_VwVw(v_off0, v_scat_step); - __fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS; - const uint8_t * p0 = (const uint8_t *) (vtcm_src + r * src_stride); - const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL; + __fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS; + const uint8_t * p0 = (const uint8_t *) (vtcm_src + r * src_stride); + const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL; + + assert(hex_is_aligned(p0, 128)); + assert(hex_is_aligned(p1, 128)); + assert(c_byte_step % 128 == 0); if (p1) { for (int i = 0; i < n_c_iters; ++i) { - HVX_Vector v0 = hvx_vmemu(p0); - p0 += c_byte_step; - HVX_Vector v1 = hvx_vmemu(p1); - p1 += c_byte_step; + HVX_Vector v0 = hvx_vmem(p0); p0 += c_byte_step; + HVX_Vector v1 = hvx_vmem(p1); p1 += c_byte_step; Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off0, v0); Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off1, v1); tile_base += dst_step; @@ -94,8 +96,7 @@ static inline void hmx_interleave_rows_to_tiles(__fp16 * restrict vtcm_dst, } else { const HVX_Vector vzero = Q6_V_vzero(); for (int i = 0; i < n_c_iters; ++i) { - HVX_Vector v0 = hvx_vmemu(p0); - p0 += c_byte_step; + HVX_Vector v0 = hvx_vmem(p0); p0 += c_byte_step; Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off0, v0); Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off1, vzero); tile_base += dst_step; @@ -116,16 +117,14 @@ static inline void hmx_interleave_rows_to_tiles(__fp16 * restrict vtcm_dst, const HVX_Vector v_off0 = Q6_Vw_vadd_VwVw(v_scat_base, Q6_V_vsplat_R(local_r * 4)); const HVX_Vector v_off1 = Q6_Vw_vadd_VwVw(v_off0, v_scat_step); - __fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS; - const uint8_t * p0 = (const uint8_t *) (vtcm_src + r * src_stride); - const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL; + __fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS; + const uint8_t * p0 = (const uint8_t *) (vtcm_src + r * src_stride); + const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL; if (p1) { for (int i = 0; i < n_c_iters; ++i) { - HVX_Vector v0 = hvx_vmemu(p0); - p0 += c_byte_step; - HVX_Vector v1 = hvx_vmemu(p1); - p1 += c_byte_step; + HVX_Vector v0 = hvx_vmemu(p0); p0 += c_byte_step; + HVX_Vector v1 = hvx_vmemu(p1); p1 += c_byte_step; Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off0, v0); Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off1, v1); tile_base += dst_step; @@ -133,8 +132,7 @@ static inline void hmx_interleave_rows_to_tiles(__fp16 * restrict vtcm_dst, } else { const HVX_Vector vzero = Q6_V_vzero(); for (int i = 0; i < n_c_iters; ++i) { - HVX_Vector v0 = hvx_vmemu(p0); - p0 += c_byte_step; + HVX_Vector v0 = hvx_vmemu(p0); p0 += c_byte_step; Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off0, v0); Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off1, vzero); tile_base += dst_step; diff --git a/ggml/src/ggml-hexagon/htp/hvx-repl.h b/ggml/src/ggml-hexagon/htp/hvx-repl.h new file mode 100644 index 00000000000..fdc7e6c7d2f --- /dev/null +++ b/ggml/src/ggml-hexagon/htp/hvx-repl.h @@ -0,0 +1,74 @@ +#ifndef HVX_REPL_H +#define HVX_REPL_H + +#include +#include +#include + +#include "hvx-base.h" + +static inline HVX_Vector hvx_vec_repl(HVX_Vector v, const uint8_t * ctrl) { + return Q6_V_vdelta_VV(v, hvx_vmem(ctrl)); +} + +static inline HVX_Vector hvx_vec_repl_u32(HVX_Vector v) { + // vdelta control to replicate first 4 bytes across all lanes + static const uint8_t __attribute__((aligned(128))) repl[128] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + }; + return hvx_vec_repl(v, repl); +} + +static inline HVX_Vector hvx_vec_repl_f32(HVX_Vector v) { + // vdelta control to replicate first 4 bytes across all lanes + static const uint8_t __attribute__((aligned(128))) repl[128] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, + }; + return hvx_vec_repl(v, repl); +} + +static inline HVX_Vector hvx_vec_repl_f16(HVX_Vector v) { + // vdelta control to replicate first two bytes across all lanes + static const uint8_t __attribute__((aligned(128))) repl[128] = { + 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x40, 0x40, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + }; + return hvx_vec_repl(v, repl); +} + +static inline HVX_Vector hvx_vec_repl_2x_f16(HVX_Vector v) { + // vdelta control to splat a pair of f16s: first half = f16[0], second half = f16[1] + static const uint8_t __attribute__((aligned(128))) repl[128] = { + 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, + 0x02, 0x02, 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, + 0x02, 0x02, 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, + 0x02, 0x02, 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, + }; + return hvx_vec_repl(v, repl); +} + +#endif // HVX_REPL_H diff --git a/ggml/src/ggml-hexagon/htp/hvx-utils.h b/ggml/src/ggml-hexagon/htp/hvx-utils.h index a518ad37331..e0452811ec3 100644 --- a/ggml/src/ggml-hexagon/htp/hvx-utils.h +++ b/ggml/src/ggml-hexagon/htp/hvx-utils.h @@ -5,6 +5,7 @@ #include "hvx-types.h" #include "hvx-copy.h" +#include "hvx-repl.h" #include "hvx-scale.h" #include "hvx-exp.h" #include "hvx-inverse.h" diff --git a/scripts/snapdragon/adb/run-completion.sh b/scripts/snapdragon/adb/run-completion.sh index 638823b8824..1b904c77b3f 100755 --- a/scripts/snapdragon/adb/run-completion.sh +++ b/scripts/snapdragon/adb/run-completion.sh @@ -70,5 +70,5 @@ adb $adbserial $adbhost shell " \ ./$branch/bin/llama-completion --no-mmap -m $basedir/../gguf/$model \ --poll 1000 -t 6 --cpu-mask 0xfc --cpu-strict 1 \ --ctx-size 8192 --ubatch-size 256 -fa on \ - -ngl 99 -no-cnv --device $device $cli_opts $@ \ + -ngl 99 --device $device $cli_opts $@ \ " From 61af07c22df7e06d07905a74f39d3809e6c0522f Mon Sep 17 00:00:00 2001 From: Sachin Sharma Date: Wed, 13 May 2026 11:43:47 +0530 Subject: [PATCH 081/158] ggml-zendnn : adaptive fallback to CPU backend for small batch sizes (#22681) * ggml-zendnn : add runtime env var GGML_ZENDNN_ADAPTIVE_FALLBACK to control adaptive fallback (default: enabled) * ggml-zendnn : restore original fallback logic when adaptive fallback is disabled --- ggml/src/ggml-zendnn/CMakeLists.txt | 2 +- ggml/src/ggml-zendnn/ggml-zendnn.cpp | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ggml/src/ggml-zendnn/CMakeLists.txt b/ggml/src/ggml-zendnn/CMakeLists.txt index 4f321a25257..f1e4f991fae 100644 --- a/ggml/src/ggml-zendnn/CMakeLists.txt +++ b/ggml/src/ggml-zendnn/CMakeLists.txt @@ -28,7 +28,7 @@ if (NOT ZENDNN_ROOT OR ZENDNN_ROOT STREQUAL "" OR ZENDNN_ROOT STREQUAL "OFF") ExternalProject_Add( zendnn GIT_REPOSITORY https://github.com/amd/ZenDNN.git - GIT_TAG f79f7321a1add65ced6397a6bfab7edba6e3e14e # ZenDNN-2026-WW13 + GIT_TAG ac9e580d9434b7b98985f2627a7ebfb5eba4bb0d # ZenDNN-2026-WW17 PREFIX ${ZENDNN_PREFIX} SOURCE_DIR ${ZENDNN_SOURCE_DIR} BINARY_DIR ${ZENDNN_BUILD_DIR} diff --git a/ggml/src/ggml-zendnn/ggml-zendnn.cpp b/ggml/src/ggml-zendnn/ggml-zendnn.cpp index 2b82c7c1dbb..6a83bb6b1ec 100644 --- a/ggml/src/ggml-zendnn/ggml-zendnn.cpp +++ b/ggml/src/ggml-zendnn/ggml-zendnn.cpp @@ -47,6 +47,7 @@ static bool ggml_zendnn_matmul(ggml_backend_zendnn_context * ctx, int64_t m, int params.dtypes.dst = ggml_to_zendnn_type(); params.num_threads = ctx->n_threads; + zendnnl::lowoha::matmul::matmul_batch_params_t batch_params; zendnnl::error_handling::status_t status = zendnnl::lowoha::matmul::matmul_direct( 'r', false, true, // row-major, don't transpose B, transpose A (because it's column-major) n, // M: rows of B and C @@ -59,7 +60,7 @@ static bool ggml_zendnn_matmul(ggml_backend_zendnn_context * ctx, int64_t m, int 0.0f, // beta C, ldc, // output C[n,m] true, // is_weights_const - {}, // batch_params + batch_params, // batch_params params // params ); @@ -520,6 +521,12 @@ static ggml_backend_buffer_t ggml_backend_zendnn_device_buffer_from_host_ptr(ggm GGML_UNUSED(max_tensor_size); } +static bool ggml_zendnn_adaptive_fallback_enabled() { + static const bool enabled = std::getenv("GGML_ZENDNN_ADAPTIVE_FALLBACK") == nullptr || + std::atoi(std::getenv("GGML_ZENDNN_ADAPTIVE_FALLBACK")) != 0; + return enabled; +} + static bool ggml_backend_zendnn_device_supports_op(ggml_backend_dev_t dev, const struct ggml_tensor * op) { switch (op->op) { case GGML_OP_NONE: @@ -538,12 +545,24 @@ static bool ggml_backend_zendnn_device_supports_op(ggml_backend_dev_t dev, const const int64_t ne10 = inputs->ne[0]; const int64_t ne0 = op->ne[0]; const int64_t ne1 = op->ne[1]; - const int64_t min_batch = 1; - if (!ggml_is_contiguous(weights) || !ggml_is_contiguous(inputs) || - ne0 < min_batch || ne1 < min_batch || ne10 < min_batch) { + + if(!ggml_is_contiguous(weights) || !ggml_is_contiguous(inputs)) { + return false; + } + + if (ggml_zendnn_adaptive_fallback_enabled()) { + const int64_t K = inputs->ne[0]; + const int64_t N = (inputs->ne[1]*inputs->ne[2]*inputs->ne[3]); + const int64_t M = weights->ne[1]; + if(K <= 256 || N <= 128 || M <= 96) { return false; + } } + else if (ne0 < min_batch || ne1 < min_batch || ne10 < min_batch) { + return false; + } + // MUL_MAT_ID performs best with a moderate number of experts due to its // gather + batched matmul + scatter approach. Future versions will leverage // ZenDNN's grouped_gemm for better scalability with larger expert counts: From bcfe63fc53e331b8b5f24aa5e9e9528e745cfae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigbj=C3=B8rn=20Skj=C3=A6ret?= Date: Wed, 13 May 2026 08:14:24 +0200 Subject: [PATCH 082/158] llama-eval : enable type check (#22988) --- examples/llama-eval/llama-eval.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/llama-eval/llama-eval.py b/examples/llama-eval/llama-eval.py index b33a3615be3..a2948273512 100755 --- a/examples/llama-eval/llama-eval.py +++ b/examples/llama-eval/llama-eval.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# type: ignore import argparse import json @@ -100,6 +99,8 @@ def wilson_interval(correct: int, total: int, z: float = 1.96) -> Tuple[float, f class BaseDataset(ABC): + questions: List[Dict] + @abstractmethod def get_question(self, index: int) -> Dict: pass @@ -573,7 +574,7 @@ def normalize_number(s: str) -> Optional[int]: class AimeDataset(BaseDataset): def __init__(self, split: str = "train"): self.split = split - self.questions: List[Dict] = [] + self.questions = [] self._load_dataset() def _load_dataset(self): @@ -618,7 +619,7 @@ def get_prompt(self, question: Dict) -> str: class Aime2025Dataset(BaseDataset): def __init__(self): - self.questions: List[Dict] = [] + self.questions = [] self._load_dataset() def _load_dataset(self): @@ -681,7 +682,7 @@ def get_prompt(self, question: Dict) -> str: class Gsm8kDataset(BaseDataset): def __init__(self, split: str = "test"): self.split = split - self.questions: List[Dict] = [] + self.questions = [] self._load_dataset() def _load_dataset(self): @@ -742,7 +743,7 @@ class GpqaDataset(BaseDataset): def __init__(self, variant: str = "diamond", seed: int = 1234): self.variant = variant self.seed = seed - self.questions: List[Dict] = [] + self.questions = [] self._load_dataset() def _load_dataset(self): From 634275fbbb0a01b95e86e30c5e740e7f44d10d2f Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Wed, 13 May 2026 09:15:39 +0300 Subject: [PATCH 083/158] spec : update CLI arguments for better consistency (#22964) * spec : update CLI arguments for better consistency * cont : fix CLI arg message --- common/arg.cpp | 4 +- common/common.h | 7 ++-- common/speculative.cpp | 78 +++++++++++++++++++------------------- tools/cli/README.md | 3 +- tools/completion/README.md | 1 + tools/server/README.md | 3 +- 6 files changed, 50 insertions(+), 46 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index 07ba7193525..f1f4c12a3ce 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2223,7 +2223,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex if (llama_supports_rpc()) { add_opt(common_arg( {"--rpc"}, "SERVERS", - "comma separated list of RPC servers (host:port)", + "comma-separated list of RPC servers (host:port)", [](common_params & params, const std::string & value) { add_rpc_devices(value); GGML_UNUSED(params); @@ -3555,7 +3555,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex ).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_DRAFT_MODEL")); add_opt(common_arg( {"--spec-type"}, common_speculative_all_types_str(), - string_format("type of speculative decoding to use when no draft model is provided (default: %s)\n", + string_format("comma-separated list of types of speculative decoding to use (default: %s)\n", common_speculative_type_name_str(params.speculative.types).c_str()), [](common_params & params, const std::string & value) { const auto enabled_types = string_split(value, ','); diff --git a/common/common.h b/common/common.h index aafc376f2e7..a3cd1743957 100644 --- a/common/common.h +++ b/common/common.h @@ -157,9 +157,9 @@ enum common_params_sampling_config : uint64_t { enum common_speculative_type { COMMON_SPECULATIVE_TYPE_NONE, // no speculative decoding - COMMON_SPECULATIVE_TYPE_DRAFT, // draft model - COMMON_SPECULATIVE_TYPE_EAGLE3, // eagle draft model - COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE, // simple self-speculative decoding + COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE, // standalone draft model speculative decoding + COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3, // Eagle3 speculative decoding + COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE, // simple self-speculative decoding based on n-grams COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K, // self-speculative decoding with n-gram keys only COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V, // self-speculative decoding with n-gram keys and 4 m-gram values COMMON_SPECULATIVE_TYPE_NGRAM_MOD, @@ -342,6 +342,7 @@ struct common_params_speculative_ngram_cache { struct common_params_speculative { std::vector types = { COMMON_SPECULATIVE_TYPE_NONE }; + // used by Simple, MTP, Eagle3, etc. - all methods that require some kind of draft model common_params_speculative_draft draft; common_params_speculative_ngram_mod ngram_mod; diff --git a/common/speculative.cpp b/common/speculative.cpp index e487e003d39..0eebcb3dcfe 100644 --- a/common/speculative.cpp +++ b/common/speculative.cpp @@ -21,8 +21,8 @@ const std::map common_speculative_type_from_name_map = { {"none", COMMON_SPECULATIVE_TYPE_NONE}, - {"draft", COMMON_SPECULATIVE_TYPE_DRAFT}, - {"eagle3", COMMON_SPECULATIVE_TYPE_EAGLE3}, + {"draft-simple", COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE}, + {"draft-eagle3", COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3}, {"ngram-simple", COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE}, {"ngram-map-k", COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K}, {"ngram-map-k4v", COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V}, @@ -145,15 +145,15 @@ struct common_speculative_impl { virtual void accept(llama_seq_id seq_id, uint16_t n_accepted) = 0; }; -struct common_speculative_state_draft : public common_speculative_impl { +struct common_speculative_impl_draft_simple : public common_speculative_impl { common_params_speculative_draft params; llama_batch batch; std::vector smpls; - common_speculative_state_draft(const common_params_speculative & params, uint32_t n_seq) - : common_speculative_impl(COMMON_SPECULATIVE_TYPE_DRAFT, n_seq) + common_speculative_impl_draft_simple(const common_params_speculative & params, uint32_t n_seq) + : common_speculative_impl(COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE, n_seq) , params(params.draft) { auto * ctx_dft = this->params.ctx_dft; @@ -206,7 +206,7 @@ struct common_speculative_state_draft : public common_speculative_impl { } } - ~common_speculative_state_draft() override { + ~common_speculative_impl_draft_simple() override { llama_batch_free(batch); } @@ -340,11 +340,11 @@ struct common_speculative_state_draft : public common_speculative_impl { } }; -struct common_speculative_state_eagle3 : public common_speculative_impl { +struct common_speculative_impl_draft_eagle3 : public common_speculative_impl { //common_params_speculative_eagle3 params; - common_speculative_state_eagle3(const common_params_speculative & /*params*/, uint32_t n_seq) - : common_speculative_impl(COMMON_SPECULATIVE_TYPE_EAGLE3, n_seq) {} + common_speculative_impl_draft_eagle3(const common_params_speculative & /*params*/, uint32_t n_seq) + : common_speculative_impl(COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3, n_seq) {} void begin(llama_seq_id /*seq_id*/, const llama_tokens & /*prompt*/) override { // noop @@ -365,13 +365,13 @@ struct common_speculative_state_eagle3 : public common_speculative_impl { }; // state of self-speculation (simple implementation, not ngram-map) -struct common_speculative_state_ngram_simple : public common_speculative_impl { +struct common_speculative_impl_ngram_simple : public common_speculative_impl { common_params_speculative_ngram_map params; // shared across all sequences common_ngram_simple_config config; - common_speculative_state_ngram_simple( + common_speculative_impl_ngram_simple( const common_params_speculative & params, uint32_t n_seq, common_ngram_simple_config config) : common_speculative_impl(COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE, n_seq) @@ -405,13 +405,13 @@ struct common_speculative_state_ngram_simple : public common_speculative_impl { } }; -struct common_speculative_state_ngram_map_k : public common_speculative_impl { +struct common_speculative_impl_ngram_map_k : public common_speculative_impl { common_params_speculative_ngram_map params; // n_seq configs std::vector config; - common_speculative_state_ngram_map_k( + common_speculative_impl_ngram_map_k( const common_params_speculative & params, const common_ngram_map & config, uint32_t n_seq) @@ -453,7 +453,7 @@ struct common_speculative_state_ngram_map_k : public common_speculative_impl { } }; -struct common_speculative_state_ngram_mod : public common_speculative_impl { +struct common_speculative_impl_ngram_mod : public common_speculative_impl { common_params_speculative_ngram_mod params; // shared across all sequences @@ -475,7 +475,7 @@ struct common_speculative_state_ngram_mod : public common_speculative_impl { std::vector sinfos; - common_speculative_state_ngram_mod( + common_speculative_impl_ngram_mod( const common_params_speculative & params, uint32_t n_seq) : common_speculative_impl(COMMON_SPECULATIVE_TYPE_NGRAM_MOD, n_seq) @@ -621,7 +621,7 @@ struct common_speculative_state_ngram_mod : public common_speculative_impl { } }; -struct common_speculative_state_ngram_cache : public common_speculative_impl { +struct common_speculative_impl_ngram_cache : public common_speculative_impl { common_params_speculative_ngram_cache params; uint16_t n_draft; @@ -639,7 +639,7 @@ struct common_speculative_state_ngram_cache : public common_speculative_impl { std::vector sinfos; - common_speculative_state_ngram_cache( + common_speculative_impl_ngram_cache( const common_params_speculative & params, uint32_t n_seq, uint16_t n_draft, @@ -775,7 +775,7 @@ static common_ngram_map get_common_ngram_map( return common_ngram_map(size_key, size_value, key_only, min_hits); } -static common_speculative_state_ngram_cache create_state_ngram_cache( +static common_speculative_impl_ngram_cache create_state_ngram_cache( const common_speculative_config & config, uint32_t n_seq, const std::string & path_static, @@ -786,7 +786,7 @@ static common_speculative_state_ngram_cache create_state_ngram_cache( bool save_static = false; bool save_dynamic = false; - common_speculative_state_ngram_cache state(config.params, n_seq, n_draft, path_static, path_dynamic, save_static, save_dynamic); + common_speculative_impl_ngram_cache state(config.params, n_seq, n_draft, path_static, path_dynamic, save_static, save_dynamic); return state; } @@ -818,8 +818,8 @@ const char * common_speculative_all_types_str() { std::string common_speculative_type_to_str(common_speculative_type type) { switch (type) { case COMMON_SPECULATIVE_TYPE_NONE: return "none"; - case COMMON_SPECULATIVE_TYPE_DRAFT: return "draft"; - case COMMON_SPECULATIVE_TYPE_EAGLE3: return "eagle3"; + case COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE: return "draft-simple"; + case COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3: return "draft-eagle3"; case COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE: return "ngram-simple"; case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K: return "ngram-map-k"; case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V: return "ngram-map-k4v"; @@ -872,9 +872,9 @@ common_speculative * common_speculative_init(common_params_speculative & params, { uint32_t enabled_configs = common_get_enabled_speculative_configs(params.types); - bool has_draft = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT)); - bool has_draft_model = !params.draft.mparams.path.empty(); + bool has_draft_model_path = !params.draft.mparams.path.empty(); + bool has_draft_simple = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE)); // bool has_mtp = false; // TODO: add MTP here bool has_draft_eagle3 = false; // TODO PR-18039: if params.speculative.eagle3 @@ -906,22 +906,22 @@ common_speculative * common_speculative_init(common_params_speculative & params, if (has_ngram_cache) { configs.push_back(common_speculative_config(COMMON_SPECULATIVE_TYPE_NGRAM_CACHE, params)); } - if (has_draft) { - if (!has_draft_model) { + if (has_draft_simple) { + if (!has_draft_model_path) { LOG_WRN("%s: draft model is not specified - cannot use 'draft' type\n", __func__); - has_draft = false; + has_draft_simple = false; } - } else if (has_draft_model) { + } else if (has_draft_model_path) { LOG_WRN("%s: draft model is specified but 'draft' speculative type is not explicitly enabled - enabling it\n", __func__); - has_draft = true; + has_draft_simple = true; } - if (has_draft) { - configs.push_back(common_speculative_config(COMMON_SPECULATIVE_TYPE_DRAFT, params)); + if (has_draft_simple) { + configs.push_back(common_speculative_config(COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE, params)); } // TODO: add MTP here if (has_draft_eagle3) { - configs.push_back(common_speculative_config(COMMON_SPECULATIVE_TYPE_EAGLE3, params)); + configs.push_back(common_speculative_config(COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3, params)); } } @@ -932,12 +932,12 @@ common_speculative * common_speculative_init(common_params_speculative & params, switch (config.type) { case COMMON_SPECULATIVE_TYPE_NONE: break; - case COMMON_SPECULATIVE_TYPE_DRAFT: { - impls.push_back(std::make_unique(config.params, n_seq)); + case COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE: { + impls.push_back(std::make_unique(config.params, n_seq)); break; } - case COMMON_SPECULATIVE_TYPE_EAGLE3: { - impls.push_back(std::make_unique(config.params, n_seq)); + case COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3: { + impls.push_back(std::make_unique(config.params, n_seq)); break; } case COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE: { @@ -950,7 +950,7 @@ common_speculative * common_speculative_init(common_params_speculative & params, /* .size_ngram = */ ngram_size_key, /* .size_mgram = */ mgram_size_value }; - auto state = std::make_unique( + auto state = std::make_unique( /* .params = */ config.params, /* .n_seq = */ n_seq, /* .state = */ config_simple @@ -961,13 +961,13 @@ common_speculative * common_speculative_init(common_params_speculative & params, case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K: case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V: { impls.push_back( - std::make_unique( + std::make_unique( config.params, get_common_ngram_map(config.type, config.params.ngram_map_k), n_seq)); break; } case COMMON_SPECULATIVE_TYPE_NGRAM_MOD: { impls.push_back( - std::make_unique(config.params, n_seq)); + std::make_unique(config.params, n_seq)); break; } case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE: { @@ -975,7 +975,7 @@ common_speculative * common_speculative_init(common_params_speculative & params, config, n_seq, params.ngram_cache.lookup_cache_static, params.ngram_cache.lookup_cache_dynamic); - impls.push_back(std::make_unique(state)); + impls.push_back(std::make_unique(state)); break; } default: diff --git a/tools/cli/README.md b/tools/cli/README.md index 02c564a2906..9f0574d25d3 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -55,6 +55,7 @@ | `-ctv, --cache-type-v TYPE` | KV cache data type for V
allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1
(default: f16)
(env: LLAMA_ARG_CACHE_TYPE_V) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)
(env: LLAMA_ARG_DEFRAG_THOLD) | | `-np, --parallel N` | number of parallel sequences to decode (default: 1)
(env: LLAMA_ARG_N_PARALLEL) | +| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)
(env: LLAMA_ARG_RPC) | | `--mlock` | force system to keep model in RAM rather than swapping or compressing
(env: LLAMA_ARG_MLOCK) | | `--mmap, --no-mmap` | whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock) (default: enabled)
(env: LLAMA_ARG_MMAP) | | `-dio, --direct-io, -ndio, --no-direct-io` | use DirectIO if available. (default: disabled)
(env: LLAMA_ARG_DIO) | @@ -198,7 +199,7 @@ | `--spec-draft-device, -devd, --device-draft ` | comma-separated list of devices to use for offloading the draft model (none = don't offload)
use --list-devices to see a list of available devices | | `--spec-draft-ngl, -ngld, --gpu-layers-draft, --n-gpu-layers-draft N` | max. number of draft model layers to store in VRAM, either an exact number, 'auto', or 'all' (default: auto)
(env: LLAMA_ARG_N_GPU_LAYERS_DRAFT) | | `--spec-draft-model, -md, --model-draft FNAME` | draft model for speculative decoding (default: unused)
(env: LLAMA_ARG_SPEC_DRAFT_MODEL) | -| `--spec-type [none\|ngram-cache\|ngram-simple\|ngram-map-k\|ngram-map-k4v\|ngram-mod]` | type of speculative decoding to use when no draft model is provided (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--spec-type none,draft-simple,draft-eagle3,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | | `--spec-ngram-mod-n-min N` | minimum number of ngram tokens to use for ngram-based speculative decoding (default: 48) | | `--spec-ngram-mod-n-max N` | maximum number of ngram tokens to use for ngram-based speculative decoding (default: 64) | | `--spec-ngram-mod-n-match N` | ngram-mod lookup length (default: 24) | diff --git a/tools/completion/README.md b/tools/completion/README.md index 7042889db13..048cf7416fc 100644 --- a/tools/completion/README.md +++ b/tools/completion/README.md @@ -138,6 +138,7 @@ llama-completion.exe -m models\gemma-1.1-7b-it.Q4_K_M.gguf --ignore-eos -n -1 | `-ctv, --cache-type-v TYPE` | KV cache data type for V
allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1
(default: f16)
(env: LLAMA_ARG_CACHE_TYPE_V) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)
(env: LLAMA_ARG_DEFRAG_THOLD) | | `-np, --parallel N` | number of parallel sequences to decode (default: 1)
(env: LLAMA_ARG_N_PARALLEL) | +| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)
(env: LLAMA_ARG_RPC) | | `--mlock` | force system to keep model in RAM rather than swapping or compressing
(env: LLAMA_ARG_MLOCK) | | `--mmap, --no-mmap` | whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock) (default: enabled)
(env: LLAMA_ARG_MMAP) | | `-dio, --direct-io, -ndio, --no-direct-io` | use DirectIO if available. (default: disabled)
(env: LLAMA_ARG_DIO) | diff --git a/tools/server/README.md b/tools/server/README.md index 7f856faa813..07e180929e5 100644 --- a/tools/server/README.md +++ b/tools/server/README.md @@ -72,6 +72,7 @@ For the full list of features, please refer to [server's changelog](https://gith | `-ctk, --cache-type-k TYPE` | KV cache data type for K
allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1
(default: f16)
(env: LLAMA_ARG_CACHE_TYPE_K) | | `-ctv, --cache-type-v TYPE` | KV cache data type for V
allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1
(default: f16)
(env: LLAMA_ARG_CACHE_TYPE_V) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)
(env: LLAMA_ARG_DEFRAG_THOLD) | +| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)
(env: LLAMA_ARG_RPC) | | `--mlock` | force system to keep model in RAM rather than swapping or compressing
(env: LLAMA_ARG_MLOCK) | | `--mmap, --no-mmap` | whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock) (default: enabled)
(env: LLAMA_ARG_MMAP) | | `-dio, --direct-io, -ndio, --no-direct-io` | use DirectIO if available. (default: disabled)
(env: LLAMA_ARG_DIO) | @@ -247,7 +248,7 @@ For the full list of features, please refer to [server's changelog](https://gith | `--spec-draft-device, -devd, --device-draft ` | comma-separated list of devices to use for offloading the draft model (none = don't offload)
use --list-devices to see a list of available devices | | `--spec-draft-ngl, -ngld, --gpu-layers-draft, --n-gpu-layers-draft N` | max. number of draft model layers to store in VRAM, either an exact number, 'auto', or 'all' (default: auto)
(env: LLAMA_ARG_N_GPU_LAYERS_DRAFT) | | `--spec-draft-model, -md, --model-draft FNAME` | draft model for speculative decoding (default: unused)
(env: LLAMA_ARG_SPEC_DRAFT_MODEL) | -| `--spec-type [none\|ngram-cache\|ngram-simple\|ngram-map-k\|ngram-map-k4v\|ngram-mod]` | type of speculative decoding to use when no draft model is provided (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--spec-type none,draft-simple,draft-eagle3,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | | `--spec-ngram-mod-n-min N` | minimum number of ngram tokens to use for ngram-based speculative decoding (default: 48) | | `--spec-ngram-mod-n-max N` | maximum number of ngram tokens to use for ngram-based speculative decoding (default: 64) | | `--spec-ngram-mod-n-match N` | ngram-mod lookup length (default: 24) | From 3796c94bad989f82d16594b5202e8adb51b979a4 Mon Sep 17 00:00:00 2001 From: Xuan-Son Nguyen Date: Wed, 13 May 2026 10:59:37 +0200 Subject: [PATCH 084/158] ci: validate model naming convention (#22680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: validate model naming convention * bring back dedicated ec workflow * add missing jobs --------- Co-authored-by: Sigbjørn Skjæret --- .github/workflows/code-style.yml | 51 ++++++++++++++++++++++++++++++ .github/workflows/editorconfig.yml | 5 --- 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/code-style.yml diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 00000000000..c88396c0a7d --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,51 @@ +name: Code Style Checker + +on: + workflow_dispatch: # allows manual triggering + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + model-naming: + runs-on: ubuntu-slim + steps: + - uses: actions/checkout@v6 + - name: Check model naming conventions + run: | + python3 - << 'EOF' + import re, os, sys + + pairs = re.findall( + r'case\s+(LLM_ARCH_\w+)\s*:\s*\n\s+return new (llama_model_\w+)\s*\(', + open("src/llama-model.cpp").read()) + + errors = [] + for arch, cls in pairs: + suffix = arch[len("LLM_ARCH_"):] + csuffix = cls[len("llama_model_"):] + fname = csuffix.replace("_", "-") + ".cpp" + + if not re.fullmatch(r'[A-Z][A-Z0-9_]*', suffix): + errors.append(f"{arch}: suffix not upper snake case, example: LLM_ARCH_MY_MODEL") + + if not re.fullmatch(r'[a-z][a-z0-9_]*', csuffix): + errors.append(f"{arch}: class suffix not lower snake case, example: llama_model_my_model") + + elif suffix.lower() != csuffix: + errors.append(f"{arch}: arch/class name mismatch, expected class 'llama_model_{suffix.lower()}' but got '{cls}'") + + elif not os.path.isfile(f"src/models/{fname}"): + errors.append(f"{arch}: expects model file name to be src/models/{fname}, but not found") + + if errors: + print('\n'.join(f" - {e}" for e in errors)); sys.exit(1) + print(f"OK: {len(pairs)} mappings validated.") + EOF diff --git a/.github/workflows/editorconfig.yml b/.github/workflows/editorconfig.yml index a2d4d0a3a78..53f6a0ccfda 100644 --- a/.github/workflows/editorconfig.yml +++ b/.github/workflows/editorconfig.yml @@ -2,11 +2,6 @@ name: EditorConfig Checker on: workflow_dispatch: # allows manual triggering - inputs: - create_release: - description: 'Create new release' - required: true - type: boolean push: branches: - master From 5d44db60089b0381cdbf7c45ce9ded43fc0c7f4c Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 13 May 2026 11:09:51 +0200 Subject: [PATCH 085/158] server, webui: support continue generation on reasoning models (#22727) * server, webui : support continue generation on reasoning models (#22727) Remove the throw blocking assistant prefill on reasoning models and orchestrate thinking tags around the prefilled message so the parser routes the next stream chunks correctly. WebUI drops the reasoning guard on the Continue button, sends reasoning_content with the prefilled message and persists partial reasoning on stop so the CoT survives reload and resume. Scope : templates with a simple thinking_start_tag / thinking_end_tag pair. Channel-based templates like GPT-OSS are out of scope, pending a per-template prefill API in common/chat. First step toward #21754. * chore: update webui build output * server: reject reasoning prefill on channel based templates --- tools/server/public/bundle.js | 317 +++++++++--------- tools/server/server-common.cpp | 47 ++- .../ChatMessageAssistant.svelte | 3 +- .../src/lib/constants/settings-registry.ts | 2 +- .../webui/src/lib/stores/chat.svelte.ts | 72 ++-- 5 files changed, 249 insertions(+), 192 deletions(-) diff --git a/tools/server/public/bundle.js b/tools/server/public/bundle.js index 97013850822..8c902da9db7 100644 --- a/tools/server/public/bundle.js +++ b/tools/server/public/bundle.js @@ -1371,15 +1371,15 @@ type:SettingsFieldType.SELECT,section:SETTINGS_SECTION_SLUGS.GENERAL,options:COL behave.",defaultValue:"",type:SettingsFieldType.TEXTAREA,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.SYSTEM_MESSAGE,paramType:SyncableParameterType.STRING}},{key:SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,label:"Paste long text to file length",help:"On pasting long text, it will be converted to a file. You can control the file length by setting the value of this parameter. Value 0 means disable.",defaultValue:2500,type:SettingsFieldType.INPUT,section:SETTINGS_SECTION_SLUGS. GENERAL,sync:{serverKey:SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,paramType:SyncableParameterType.NUMBER}},{key:SETTINGS_KEYS.SEND_ON_ENTER,label:"Send message on Enter",help:"Use Enter to send messages and Shift + Enter for new lines. When disabled, use Ctrl/Cmd + Enter.",defaultValue:!0,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.SEND_ON_ENTER,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT, label:"Copy text attachments as plain text",help:"When copying a message with text attachments, combine them into a single plain text string instead of a special format that can be pasted back as attachments.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,label:'Enable "Continue" button',help:'Enable "Co\ -ntinue" button for assistant messages. Currently works only with non-reasoning models.',defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,isExperimental:!0,sync:{serverKey:SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.PDF_AS_IMAGE,label:"Parse PDF as image",help:"Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.",defaultValue:!1,type:SettingsFieldType. -CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.PDF_AS_IMAGE,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,label:"Ask for confirmation before changing conversation title",help:"Ask for confirmation before automatically changing conversation title when editing the first message.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,paramType:SyncableParameterType. -BOOLEAN}},{key:SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,label:"Use first non-empty line for conversation title",help:"Use only the first non-empty line of the prompt to generate the conversation title.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,label:"Use LLM to generate conversation title",help:"Use \ -the LLM to automatically generate conversation titles based on the first message exchange.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,isExperimental:!0},{key:SETTINGS_KEYS.TITLE_GENERATION_PROMPT,label:"LLM title generation prompt",help:"Optional template for the title generation prompt. Use {{USER}} for the user message and {{ASSISTANT}} for the assistant message.",defaultValue:TITLE_GENERATION.DEFAULT_PROMPT,type:SettingsFieldType.TEXTAREA,section:SETTINGS_SECTION_SLUGS. -GENERAL}]},[SETTINGS_SECTION_SLUGS.DISPLAY]:{title:SETTINGS_SECTION_TITLES.DISPLAY,slug:SETTINGS_SECTION_SLUGS.DISPLAY,icon:Monitor,settings:[{key:SETTINGS_KEYS.SHOW_MESSAGE_STATS,label:"Show message generation statistics",help:"Display generation statistics (tokens/second, token count, duration) below each assistant message.",defaultValue:!0,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_MESSAGE_STATS,paramType:SyncableParameterType.BOOLEAN}}, -{key:SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,label:"Show thought in progress",help:"Expand thought process by default when generating messages.",defaultValue:!0,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,label:"Show tool call in progress",help:"Automatically expand tool call details while executing and keep them expanded after c\ -ompletion.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.KEEP_STATS_VISIBLE,label:"Keep stats visible after generation",help:"Keep processing statistics visible after generation finishes.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.KEEP_STATS_VISIBLE,paramType:SyncableParameterType. -BOOLEAN}},{key:SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,label:"Show microphone on empty input",help:"Automatically show microphone button instead of send button when textarea is empty for models with audio modality support.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,isExperimental:!0,sync:{serverKey:SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,label:"Render user content as Markdown", -help:"Render user messages using markdown formatting in the chat.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,label:"Use full height code blocks",help:"Always display code blocks at their full natural height, overriding any height limits.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS. +ntinue" button for assistant messages, including reasoning models.',defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,isExperimental:!0,sync:{serverKey:SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.PDF_AS_IMAGE,label:"Parse PDF as image",help:"Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS. +GENERAL,sync:{serverKey:SETTINGS_KEYS.PDF_AS_IMAGE,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,label:"Ask for confirmation before changing conversation title",help:"Ask for confirmation before automatically changing conversation title when editing the first message.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS. +TITLE_GENERATION_USE_FIRST_LINE,label:"Use first non-empty line for conversation title",help:"Use only the first non-empty line of the prompt to generate the conversation title.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,sync:{serverKey:SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,label:"Use LLM to generate conversation title",help:"Use the LLM to automatically gene\ +rate conversation titles based on the first message exchange.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.GENERAL,isExperimental:!0},{key:SETTINGS_KEYS.TITLE_GENERATION_PROMPT,label:"LLM title generation prompt",help:"Optional template for the title generation prompt. Use {{USER}} for the user message and {{ASSISTANT}} for the assistant message.",defaultValue:TITLE_GENERATION.DEFAULT_PROMPT,type:SettingsFieldType.TEXTAREA,section:SETTINGS_SECTION_SLUGS.GENERAL}]}, +[SETTINGS_SECTION_SLUGS.DISPLAY]:{title:SETTINGS_SECTION_TITLES.DISPLAY,slug:SETTINGS_SECTION_SLUGS.DISPLAY,icon:Monitor,settings:[{key:SETTINGS_KEYS.SHOW_MESSAGE_STATS,label:"Show message generation statistics",help:"Display generation statistics (tokens/second, token count, duration) below each assistant message.",defaultValue:!0,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_MESSAGE_STATS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS. +SHOW_THOUGHT_IN_PROGRESS,label:"Show thought in progress",help:"Expand thought process by default when generating messages.",defaultValue:!0,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,label:"Show tool call in progress",help:"Automatically expand tool call details while executing and keep them expanded after completion.",defaultValue:!1, +type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.KEEP_STATS_VISIBLE,label:"Keep stats visible after generation",help:"Keep processing statistics visible after generation finishes.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.KEEP_STATS_VISIBLE,paramType:SyncableParameterType.BOOLEAN}},{ +key:SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,label:"Show microphone on empty input",help:"Automatically show microphone button instead of send button when textarea is empty for models with audio modality support.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,isExperimental:!0,sync:{serverKey:SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,label:"Render user content as Markdown",help:"Ren\ +der user messages using markdown formatting in the chat.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,label:"Use full height code blocks",help:"Always display code blocks at their full natural height, overriding any height limits.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS. DISPLAY,sync:{serverKey:SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.DISABLE_AUTO_SCROLL,label:"Disable automatic scroll",help:"Disable automatic scrolling while messages stream so you can control the viewport position manually.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.DISABLE_AUTO_SCROLL,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP, label:"Always show sidebar on desktop",help:"Always keep the sidebar visible on desktop instead of auto-hiding it.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,label:"Show raw model names",help:'Display full raw model identifiers (e.g. "ggml-org/GLM-4.7-Flash-GGUF:Q8_0") instead of parsed names with badges.',defaultValue:!1, type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,paramType:SyncableParameterType.BOOLEAN}},{key:SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS,label:"Always show agentic turns in conversation",help:"Always expand and display agentic loop turns in conversation messages.",defaultValue:!1,type:SettingsFieldType.CHECKBOX,section:SETTINGS_SECTION_SLUGS.DISPLAY,sync:{serverKey:SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS,paramType:SyncableParameterType. @@ -6473,140 +6473,141 @@ id,!1);return}console.error("Failed to send message:",error2),this.setChatLoadin if(isRouterMode()&&!effectiveModel){const conversationModel=this.getConversationModel(allMessages);effectiveModel=selectedModelName()||conversationModel}isRouterMode()&&effectiveModel&&(modelsStore.getModelProps(effectiveModel)||await modelsStore.fetchModelProps(effectiveModel));let currentMessageId=assistantMessage.id,streamedContent="",streamedReasoningContent="",resolvedModel=null,modelPersisted=!1;const convId=assistantMessage.convId,recordModel=(modelName,persistImmediately=!0)=>{if(!modelName) return;const n=normalizeModelName(modelName);if(!n||n===resolvedModel)return;resolvedModel=n;const idx=conversationsStore.findMessageIndex(currentMessageId);conversationsStore.updateMessageAtIndex(idx,{model:n}),persistImmediately&&!modelPersisted&&(modelPersisted=!0,DatabaseService.updateMessage(currentMessageId,{model:n}).catch(()=>{modelPersisted=!1,resolvedModel=null}))},updateStreamingUI=()=>{this.setChatStreaming(convId,streamedContent,currentMessageId);const idx=conversationsStore.findMessageIndex( currentMessageId);conversationsStore.updateMessageAtIndex(idx,{content:streamedContent})},cleanupStreamingState=()=>{this.setStreamingActive(!1),this.setChatLoading(convId,!1),this.clearChatStreaming(convId),this.setProcessingState(convId,null)};this.setStreamingActive(!0),this.setActiveProcessingConversation(convId);const abortController=this.getOrCreateAbortController(convId),streamCallbacks={onChunk:chunk=>{streamedContent+=chunk,updateStreamingUI()},onReasoningChunk:chunk=>{streamedReasoningContent+= -chunk;const idx=conversationsStore.findMessageIndex(currentMessageId);conversationsStore.updateMessageAtIndex(idx,{reasoningContent:streamedReasoningContent})},onToolCallsStreaming:toolCalls=>{const idx=conversationsStore.findMessageIndex(currentMessageId);conversationsStore.updateMessageAtIndex(idx,{toolCalls:JSON.stringify(toolCalls)})},onAttachments:(messageId,extras)=>{if(!extras.length)return;const idx=conversationsStore.findMessageIndex(messageId);if(idx===-1)return;const updatedExtras=[...conversationsStore. -activeMessages[idx].extra||[],...extras];conversationsStore.updateMessageAtIndex(idx,{extra:updatedExtras}),DatabaseService.updateMessage(messageId,{extra:updatedExtras}).catch(console.error)},onModel:modelName=>recordModel(modelName),onTurnComplete:intermediateTimings=>{const idx=conversationsStore.findMessageIndex(assistantMessage.id);conversationsStore.updateMessageAtIndex(idx,{timings:intermediateTimings})},onTimings:(timings,promptProgress)=>{const tokensPerSecond=timings?.predicted_ms&&timings?. -predicted_n?timings.predicted_n/timings.predicted_ms*1e3:0;this.updateProcessingStateFromTimings({prompt_n:timings?.prompt_n||0,prompt_ms:timings?.prompt_ms,predicted_n:timings?.predicted_n||0,predicted_per_second:tokensPerSecond,cache_n:timings?.cache_n||0,prompt_progress:promptProgress},convId)},onAssistantTurnComplete:async(content2,reasoningContent,timings,toolCalls)=>{const updateData={content:content2,reasoningContent:reasoningContent||void 0,toolCalls:toolCalls?JSON.stringify(toolCalls):"", -timings};resolvedModel&&!modelPersisted&&(updateData.model=resolvedModel),await DatabaseService.updateMessage(currentMessageId,updateData);const idx=conversationsStore.findMessageIndex(currentMessageId),uiUpdate={content:content2,reasoningContent:reasoningContent||void 0,toolCalls:toolCalls?JSON.stringify(toolCalls):""};timings&&(uiUpdate.timings=timings),resolvedModel&&(uiUpdate.model=resolvedModel),conversationsStore.updateMessageAtIndex(idx,uiUpdate),await conversationsStore.updateCurrentNode( -currentMessageId)},createToolResultMessage:async(toolCallId,content2,extras)=>{const msg=await DatabaseService.createMessageBranch({convId,type:MessageType.TEXT,role:MessageRole.TOOL,content:content2,toolCallId,timestamp:Date.now(),toolCalls:"",children:[],extra:extras},currentMessageId);return conversationsStore.addMessageToActive(msg),await conversationsStore.updateCurrentNode(msg.id),msg},createAssistantMessage:async()=>{streamedContent="",streamedReasoningContent="";const lastMsg=conversationsStore. -activeMessages[conversationsStore.activeMessages.length-1],msg=await DatabaseService.createMessageBranch({convId,type:MessageType.TEXT,role:MessageRole.ASSISTANT,content:"",timestamp:Date.now(),toolCalls:"",children:[],model:resolvedModel},lastMsg.id);return conversationsStore.addMessageToActive(msg),currentMessageId=msg.id,msg},onFlowComplete:finalTimings=>{if(finalTimings){const idx=conversationsStore.findMessageIndex(assistantMessage.id);conversationsStore.updateMessageAtIndex(idx,{timings:finalTimings}), -DatabaseService.updateMessage(assistantMessage.id,{timings:finalTimings}).catch(console.error)}cleanupStreamingState(),onComplete&&onComplete(streamedContent),isRouterMode()&&modelsStore.fetchRouterModels().catch(console.error),config$1().preEncodeConversation&&this.triggerPreEncode(allMessages,assistantMessage,streamedContent,effectiveModel,!!config$1().excludeReasoningFromContext)},onError:error2=>{if(this.setStreamingActive(!1),isAbortError(error2)){cleanupStreamingState();const pending=this. -consumePendingMessage(convId);pending&&this.sendMessage(pending.content,pending.extras);return}console.error("Streaming error:",error2),cleanupStreamingState(),this.clearPendingMessage(convId);const idx=conversationsStore.findMessageIndex(assistantMessage.id);if(idx!==-1){const failedMessage=conversationsStore.removeMessageAtIndex(idx);failedMessage&&DatabaseService.deleteMessage(failedMessage.id).catch(console.error)}const contextInfo=error2.contextInfo;this.showErrorDialog({type:error2.name=== -"TimeoutError"?ErrorDialogType.TIMEOUT:ErrorDialogType.SERVER,message:error2.message,contextInfo}),onError&&onError(error2)}},perChatOverrides=conversationsStore.activeConversation?.mcpServerOverrides;if((await agenticStore.runAgenticFlow({conversationId:convId,messages:allMessages,options:{...this.getApiOptions(),...effectiveModel?{model:effectiveModel}:{}},callbacks:streamCallbacks,signal:abortController.signal,perChatOverrides})).handled){firstUserMessageContent&&await this.generateTitleWithLLM( -firstUserMessageContent,streamedContent,convId);const pending=agenticStore.consumePendingSteeringMessage(convId);pending&&await this.sendMessage(pending.content,pending.extras);return}await ChatService.sendMessage(allMessages,{...this.getApiOptions(),...effectiveModel?{model:effectiveModel}:{},stream:!0,onChunk:streamCallbacks.onChunk,onReasoningChunk:streamCallbacks.onReasoningChunk,onModel:streamCallbacks.onModel,onTimings:streamCallbacks.onTimings,onComplete:async(finalContent,reasoningContent,timings,toolCalls)=>{ +chunk,this.setChatStreaming(convId,streamedContent,currentMessageId);const idx=conversationsStore.findMessageIndex(currentMessageId);conversationsStore.updateMessageAtIndex(idx,{reasoningContent:streamedReasoningContent})},onToolCallsStreaming:toolCalls=>{const idx=conversationsStore.findMessageIndex(currentMessageId);conversationsStore.updateMessageAtIndex(idx,{toolCalls:JSON.stringify(toolCalls)})},onAttachments:(messageId,extras)=>{if(!extras.length)return;const idx=conversationsStore.findMessageIndex( +messageId);if(idx===-1)return;const updatedExtras=[...conversationsStore.activeMessages[idx].extra||[],...extras];conversationsStore.updateMessageAtIndex(idx,{extra:updatedExtras}),DatabaseService.updateMessage(messageId,{extra:updatedExtras}).catch(console.error)},onModel:modelName=>recordModel(modelName),onTurnComplete:intermediateTimings=>{const idx=conversationsStore.findMessageIndex(assistantMessage.id);conversationsStore.updateMessageAtIndex(idx,{timings:intermediateTimings})},onTimings:(timings,promptProgress)=>{ +const tokensPerSecond=timings?.predicted_ms&&timings?.predicted_n?timings.predicted_n/timings.predicted_ms*1e3:0;this.updateProcessingStateFromTimings({prompt_n:timings?.prompt_n||0,prompt_ms:timings?.prompt_ms,predicted_n:timings?.predicted_n||0,predicted_per_second:tokensPerSecond,cache_n:timings?.cache_n||0,prompt_progress:promptProgress},convId)},onAssistantTurnComplete:async(content2,reasoningContent,timings,toolCalls)=>{const updateData={content:content2,reasoningContent:reasoningContent|| +void 0,toolCalls:toolCalls?JSON.stringify(toolCalls):"",timings};resolvedModel&&!modelPersisted&&(updateData.model=resolvedModel),await DatabaseService.updateMessage(currentMessageId,updateData);const idx=conversationsStore.findMessageIndex(currentMessageId),uiUpdate={content:content2,reasoningContent:reasoningContent||void 0,toolCalls:toolCalls?JSON.stringify(toolCalls):""};timings&&(uiUpdate.timings=timings),resolvedModel&&(uiUpdate.model=resolvedModel),conversationsStore.updateMessageAtIndex( +idx,uiUpdate),await conversationsStore.updateCurrentNode(currentMessageId)},createToolResultMessage:async(toolCallId,content2,extras)=>{const msg=await DatabaseService.createMessageBranch({convId,type:MessageType.TEXT,role:MessageRole.TOOL,content:content2,toolCallId,timestamp:Date.now(),toolCalls:"",children:[],extra:extras},currentMessageId);return conversationsStore.addMessageToActive(msg),await conversationsStore.updateCurrentNode(msg.id),msg},createAssistantMessage:async()=>{streamedContent= +"",streamedReasoningContent="";const lastMsg=conversationsStore.activeMessages[conversationsStore.activeMessages.length-1],msg=await DatabaseService.createMessageBranch({convId,type:MessageType.TEXT,role:MessageRole.ASSISTANT,content:"",timestamp:Date.now(),toolCalls:"",children:[],model:resolvedModel},lastMsg.id);return conversationsStore.addMessageToActive(msg),currentMessageId=msg.id,msg},onFlowComplete:finalTimings=>{if(finalTimings){const idx=conversationsStore.findMessageIndex(assistantMessage. +id);conversationsStore.updateMessageAtIndex(idx,{timings:finalTimings}),DatabaseService.updateMessage(assistantMessage.id,{timings:finalTimings}).catch(console.error)}cleanupStreamingState(),onComplete&&onComplete(streamedContent),isRouterMode()&&modelsStore.fetchRouterModels().catch(console.error),config$1().preEncodeConversation&&this.triggerPreEncode(allMessages,assistantMessage,streamedContent,effectiveModel,!!config$1().excludeReasoningFromContext)},onError:error2=>{if(this.setStreamingActive( +!1),isAbortError(error2)){cleanupStreamingState();const pending=this.consumePendingMessage(convId);pending&&this.sendMessage(pending.content,pending.extras);return}console.error("Streaming error:",error2),cleanupStreamingState(),this.clearPendingMessage(convId);const idx=conversationsStore.findMessageIndex(assistantMessage.id);if(idx!==-1){const failedMessage=conversationsStore.removeMessageAtIndex(idx);failedMessage&&DatabaseService.deleteMessage(failedMessage.id).catch(console.error)}const contextInfo=error2. +contextInfo;this.showErrorDialog({type:error2.name==="TimeoutError"?ErrorDialogType.TIMEOUT:ErrorDialogType.SERVER,message:error2.message,contextInfo}),onError&&onError(error2)}},perChatOverrides=conversationsStore.activeConversation?.mcpServerOverrides;if((await agenticStore.runAgenticFlow({conversationId:convId,messages:allMessages,options:{...this.getApiOptions(),...effectiveModel?{model:effectiveModel}:{}},callbacks:streamCallbacks,signal:abortController.signal,perChatOverrides})).handled){firstUserMessageContent&& +await this.generateTitleWithLLM(firstUserMessageContent,streamedContent,convId);const pending=agenticStore.consumePendingSteeringMessage(convId);pending&&await this.sendMessage(pending.content,pending.extras);return}await ChatService.sendMessage(allMessages,{...this.getApiOptions(),...effectiveModel?{model:effectiveModel}:{},stream:!0,onChunk:streamCallbacks.onChunk,onReasoningChunk:streamCallbacks.onReasoningChunk,onModel:streamCallbacks.onModel,onTimings:streamCallbacks.onTimings,onComplete:async(finalContent,reasoningContent,timings,toolCalls)=>{ const content2=streamedContent||finalContent||"",reasoning=streamedReasoningContent||reasoningContent,updateData={content:content2,reasoningContent:reasoning||void 0,toolCalls:toolCalls||"",timings};resolvedModel&&!modelPersisted&&(updateData.model=resolvedModel),await DatabaseService.updateMessage(currentMessageId,updateData);const idx=conversationsStore.findMessageIndex(currentMessageId),uiUpdate={content:content2,reasoningContent:reasoning||void 0,toolCalls:toolCalls||""};timings&&(uiUpdate.timings= timings),resolvedModel&&(uiUpdate.model=resolvedModel),conversationsStore.updateMessageAtIndex(idx,uiUpdate),await conversationsStore.updateCurrentNode(currentMessageId),cleanupStreamingState(),onComplete&&await onComplete(content2),isRouterMode()&&modelsStore.fetchRouterModels().catch(console.error),firstUserMessageContent&&await this.generateTitleWithLLM(firstUserMessageContent,streamedContent,convId);const pending=this.consumePendingMessage(convId);pending&&await this.sendMessage(pending.content, pending.extras)},onError:streamCallbacks.onError},convId,abortController.signal)}async stopGeneration(){const activeConv=conversationsStore.activeConversation;activeConv&&await this.stopGenerationForChat(activeConv.id)}async stopGenerationForChat(convId){await this.savePartialResponseIfNeeded(convId),this.setStreamingActive(!1),this.abortRequest(convId),this.setChatLoading(convId,!1),this.clearChatStreaming(convId),this.setProcessingState(convId,null),this.clearPendingMessage(convId)}async generateTitleWithLLM(userContent,assistantContent,convId){ const effectiveModel=isRouterMode()&&selectedModelName()?selectedModelName():void 0,configValue=config$1(),titlePrompt=(typeof configValue.titleGenerationPrompt=="string"&&configValue.titleGenerationPrompt.trim()?configValue.titleGenerationPrompt:TITLE_GENERATION.DEFAULT_PROMPT).replace("{{USER}}",String(userContent||"")).replace("{{ASSISTANT}}",String(assistantContent||"")),titleMessage={role:MessageRole.USER,content:titlePrompt},titleResponse=await ChatService.generateTitle(titleMessage,effectiveModel); if(!titleResponse)return;let cleanTitle=titleResponse.trim();if(cleanTitle=cleanTitle.replace(TITLE_GENERATION.PREFIX_PATTERN,"").replace(TITLE_GENERATION.QUOTE_PATTERN,"").trim(),!cleanTitle||cleanTitle.lengthl.trim().length>0);cleanTitle=firstLine?firstLine.trim():TITLE_GENERATION.FALLBACK}cleanTitle&&cleanTitle.length>=TITLE_GENERATION.MIN_LENGTH&&await conversationsStore.updateConversationName(convId,cleanTitle)}async savePartialResponseIfNeeded(convId){const conversationId=convId||conversationsStore.activeConversation?.id;if(!conversationId)return;const streamingState=this.getChatStreaming(conversationId);if(!streamingState||!streamingState.response.trim())return;const messages=conversationId=== -conversationsStore.activeConversation?.id?conversationsStore.activeMessages:await conversationsStore.getConversationMessages(conversationId);if(!messages.length)return;const lastMessage=messages[messages.length-1];if(lastMessage?.role===MessageRole.ASSISTANT)try{const updateData={content:streamingState.response},lastKnownState=this.getProcessingState(conversationId);lastKnownState&&(updateData.timings={prompt_n:lastKnownState.promptTokens||0,prompt_ms:lastKnownState.promptMs,predicted_n:lastKnownState. -tokensDecoded||0,cache_n:lastKnownState.cacheTokens||0,predicted_ms:lastKnownState.tokensPerSecond&&lastKnownState.tokensDecoded?lastKnownState.tokensDecoded/lastKnownState.tokensPerSecond*1e3:void 0}),await DatabaseService.updateMessage(lastMessage.id,updateData),lastMessage.content=streamingState.response,updateData.timings&&(lastMessage.timings=updateData.timings)}catch(error2){lastMessage.content=streamingState.response,console.error("Failed to save partial response:",error2)}}async updateMessage(messageId,newContent){ -const activeConv=conversationsStore.activeConversation;if(!activeConv)return;this.isChatLoadingInternal(activeConv.id)&&await this.stopGeneration();const result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(!result)return;const{message:messageToUpdate,index:messageIndex}=result,originalContent=messageToUpdate.content;try{const rootMessage=(await conversationsStore.getConversationMessages(activeConv.id)).find(m=>m.type==="root"&&m.parent===null),isFirstUserMessage=rootMessage&&messageToUpdate. -parent===rootMessage.id;conversationsStore.updateMessageAtIndex(messageIndex,{content:newContent}),await DatabaseService.updateMessage(messageId,{content:newContent}),isFirstUserMessage&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation(activeConv.id,generateConversationTitle(newContent,!!config$1().titleGenerationUseFirstLine));const messagesToRemove=conversationsStore.activeMessages.slice(messageIndex+1);for(const message of messagesToRemove)await DatabaseService. -deleteMessage(message.id);conversationsStore.sliceActiveMessages(messageIndex+1),conversationsStore.updateConversationTimestamp(),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const assistantMessage=await this.createAssistantMessage();conversationsStore.addMessageToActive(assistantMessage),await conversationsStore.updateCurrentNode(assistantMessage.id),await this.streamChatCompletion(conversationsStore.activeMessages.slice(0,-1),assistantMessage,void 0,()=>{conversationsStore. -updateMessageAtIndex(conversationsStore.findMessageIndex(messageId),{content:originalContent})})}catch(error2){isAbortError(error2)||console.error("Failed to update message:",error2)}}async regenerateMessage(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;this.cancelPreEncode();const result=this.getMessageByIdWithRole(messageId,MessageRole.ASSISTANT);if(!result)return;const{index:messageIndex}=result;try{const messagesToRemove=conversationsStore. -activeMessages.slice(messageIndex);for(const message of messagesToRemove)await DatabaseService.deleteMessage(message.id);conversationsStore.sliceActiveMessages(messageIndex),conversationsStore.updateConversationTimestamp(),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const parentMessageId=conversationsStore.activeMessages.length>0?conversationsStore.activeMessages[conversationsStore.activeMessages.length-1].id:void 0,assistantMessage=await this.createAssistantMessage( -parentMessageId);conversationsStore.addMessageToActive(assistantMessage),await this.streamChatCompletion(conversationsStore.activeMessages.slice(0,-1),assistantMessage)}catch(error2){isAbortError(error2)||console.error("Failed to regenerate message:",error2),this.setChatLoading(activeConv?.id||"",!1)}}async regenerateMessageWithBranching(messageId,modelOverride){const activeConv=conversationsStore.activeConversation;if(!(!activeConv||this.isChatLoadingInternal(activeConv.id))){this.cancelPreEncode(); -try{const idx=conversationsStore.findMessageIndex(messageId);if(idx===-1)return;const msg=conversationsStore.activeMessages[idx];if(msg.role!==MessageRole.ASSISTANT)return;const allMessages=await conversationsStore.getConversationMessages(activeConv.id),parentMessage=findMessageById(allMessages,msg.parent);if(!parentMessage)return;this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const newAssistantMessage=await DatabaseService.createMessageBranch({convId:msg.convId,type:msg. -type,timestamp:Date.now(),role:msg.role,content:"",toolCalls:"",children:[],model:null},parentMessage.id);await conversationsStore.updateCurrentNode(newAssistantMessage.id),conversationsStore.updateConversationTimestamp(),await conversationsStore.refreshActiveMessages();const conversationPath=filterByLeafNodeId(allMessages,parentMessage.id,!1),modelToUse=modelOverride||msg.model||void 0;await this.streamChatCompletion(conversationPath,newAssistantMessage,void 0,void 0,modelToUse)}catch(error2){isAbortError( -error2)||console.error("Failed to regenerate message with branching:",error2),this.setChatLoading(activeConv?.id||"",!1)}}}async getDeletionInfo(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv)return{totalCount:0,userMessages:0,assistantMessages:0,messageTypes:[]};const allMessages=await conversationsStore.getConversationMessages(activeConv.id);if(findMessageById(allMessages,messageId)?.role===MessageRole.SYSTEM){const messagesToDelete2=allMessages.filter(m=>m.id=== -messageId);let userMessages2=0,assistantMessages2=0;const messageTypes2=[];for(const msg of messagesToDelete2)msg.role===MessageRole.USER?(userMessages2++,messageTypes2.includes("user message")||messageTypes2.push("user message")):msg.role===MessageRole.ASSISTANT&&(assistantMessages2++,messageTypes2.includes("assistant response")||messageTypes2.push("assistant response"));return{totalCount:1,userMessages:userMessages2,assistantMessages:assistantMessages2,messageTypes:messageTypes2}}const descendants=findDescendantMessages( -allMessages,messageId),allToDelete=[messageId,...descendants],messagesToDelete=allMessages.filter(m=>allToDelete.includes(m.id));let userMessages=0,assistantMessages=0;const messageTypes=[];for(const msg of messagesToDelete)msg.role===MessageRole.USER?(userMessages++,messageTypes.includes("user message")||messageTypes.push("user message")):msg.role===MessageRole.ASSISTANT&&(assistantMessages++,messageTypes.includes("assistant response")||messageTypes.push("assistant response"));return{totalCount:allToDelete. -length,userMessages,assistantMessages,messageTypes}}async deleteMessage(messageId){const activeConv=conversationsStore.activeConversation;if(activeConv)try{const allMessages=await conversationsStore.getConversationMessages(activeConv.id),messageToDelete=findMessageById(allMessages,messageId);if(!messageToDelete)return;if(filterByLeafNodeId(allMessages,activeConv.currNode||"",!1).some(m=>m.id===messageId)&&messageToDelete.parent){const siblings2=allMessages.filter(m=>m.parent===messageToDelete.parent&& -m.id!==messageId);if(siblings2.length>0){const latestSibling=siblings2.reduce((latest,sibling2)=>sibling2.timestamp>latest.timestamp?sibling2:latest);await conversationsStore.updateCurrentNode(findLeafNode(allMessages,latestSibling.id))}else messageToDelete.parent&&await conversationsStore.updateCurrentNode(findLeafNode(allMessages,messageToDelete.parent))}await DatabaseService.deleteMessageCascading(activeConv.id,messageId),await conversationsStore.refreshActiveMessages(),conversationsStore.updateConversationTimestamp()}catch(error2){ -console.error("Failed to delete message:",error2)}}async continueAssistantMessage(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;const result=this.getMessageByIdWithRole(messageId,MessageRole.ASSISTANT);if(!result)return;const{message:msg,index:idx}=result;try{this.showErrorDialog(null),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const allMessages=await conversationsStore.getConversationMessages( -activeConv.id),dbMessage=findMessageById(allMessages,messageId);if(!dbMessage){this.setChatLoading(activeConv.id,!1);return}const originalContent=dbMessage.content,originalReasoning=dbMessage.reasoningContent||"",contextWithContinue=[...conversationsStore.activeMessages.slice(0,idx),{role:MessageRole.ASSISTANT,content:originalContent}];let appendedContent="",appendedReasoning="",hasReceivedContent=!1;const updateStreamingContent=fullContent=>{this.setChatStreaming(msg.convId,fullContent,msg.id), -conversationsStore.updateMessageAtIndex(idx,{content:fullContent})},abortController=this.getOrCreateAbortController(msg.convId);await ChatService.sendMessage(contextWithContinue,{...this.getApiOptions(),onChunk:chunk=>{appendedContent+=chunk,hasReceivedContent=!0,updateStreamingContent(originalContent+appendedContent)},onReasoningChunk:chunk=>{appendedReasoning+=chunk,hasReceivedContent=!0,conversationsStore.updateMessageAtIndex(idx,{reasoningContent:originalReasoning+appendedReasoning})},onTimings:(timings,promptProgress)=>{ -const tokensPerSecond=timings?.predicted_ms&&timings?.predicted_n?timings.predicted_n/timings.predicted_ms*1e3:0;this.updateProcessingStateFromTimings({prompt_n:timings?.prompt_n||0,prompt_ms:timings?.prompt_ms,predicted_n:timings?.predicted_n||0,predicted_per_second:tokensPerSecond,cache_n:timings?.cache_n||0,prompt_progress:promptProgress},msg.convId)},onComplete:async(finalContent,reasoningContent,timings)=>{const finalAppendedContent=hasReceivedContent?appendedContent:finalContent||"",finalAppendedReasoning=hasReceivedContent? -appendedReasoning:reasoningContent||"",fullContent=originalContent+finalAppendedContent,fullReasoning=originalReasoning+finalAppendedReasoning||void 0;await DatabaseService.updateMessage(msg.id,{content:fullContent,reasoningContent:fullReasoning,timestamp:Date.now(),timings}),conversationsStore.updateMessageAtIndex(idx,{content:fullContent,reasoningContent:fullReasoning,timestamp:Date.now(),timings}),conversationsStore.updateConversationTimestamp(),this.setChatLoading(msg.convId,!1),this.clearChatStreaming( -msg.convId),this.setProcessingState(msg.convId,null)},onError:async error2=>{if(isAbortError(error2)){hasReceivedContent&&appendedContent&&(await DatabaseService.updateMessage(msg.id,{content:originalContent+appendedContent,reasoningContent:originalReasoning+appendedReasoning||void 0,timestamp:Date.now()}),conversationsStore.updateMessageAtIndex(idx,{content:originalContent+appendedContent,reasoningContent:originalReasoning+appendedReasoning||void 0,timestamp:Date.now()})),this.setChatLoading(msg. -convId,!1),this.clearChatStreaming(msg.convId),this.setProcessingState(msg.convId,null);return}console.error("Continue generation error:",error2),conversationsStore.updateMessageAtIndex(idx,{content:originalContent}),await DatabaseService.updateMessage(msg.id,{content:originalContent}),this.setChatLoading(msg.convId,!1),this.clearChatStreaming(msg.convId),this.setProcessingState(msg.convId,null),this.showErrorDialog({type:error2.name==="TimeoutError"?ErrorDialogType.TIMEOUT:ErrorDialogType.SERVER, -message:error2.message})}},msg.convId,abortController.signal)}catch(error2){isAbortError(error2)||console.error("Failed to continue message:",error2),activeConv&&this.setChatLoading(activeConv.id,!1)}}async editAssistantMessage(messageId,newContent,shouldBranch){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;const result=this.getMessageByIdWithRole(messageId,MessageRole.ASSISTANT);if(!result)return;const{message:msg,index:idx}=result; -try{if(shouldBranch){const newMessage=await DatabaseService.createMessageBranch({convId:msg.convId,type:msg.type,timestamp:Date.now(),role:msg.role,content:newContent,toolCalls:msg.toolCalls||"",children:[],model:msg.model},msg.parent);await conversationsStore.updateCurrentNode(newMessage.id)}else await DatabaseService.updateMessage(msg.id,{content:newContent}),conversationsStore.updateMessageAtIndex(idx,{content:newContent});conversationsStore.updateConversationTimestamp(),await conversationsStore. -refreshActiveMessages()}catch(error2){console.error("Failed to edit assistant message:",error2)}}async editUserMessagePreserveResponses(messageId,newContent,newExtras){const activeConv=conversationsStore.activeConversation;if(!activeConv)return;const result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(!result)return;const{message:msg,index:idx}=result;try{const updateData={content:newContent};newExtras!==void 0&&(updateData.extra=JSON.parse(JSON.stringify(newExtras))),await DatabaseService. -updateMessage(messageId,updateData),conversationsStore.updateMessageAtIndex(idx,updateData);const rootMessage=(await conversationsStore.getConversationMessages(activeConv.id)).find(m=>m.type==="root"&&m.parent===null);rootMessage&&msg.parent===rootMessage.id&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation(activeConv.id,generateConversationTitle(newContent,!!config$1().titleGenerationUseFirstLine)),conversationsStore.updateConversationTimestamp()}catch(error2){ -console.error("Failed to edit user message:",error2)}}async editMessageWithBranching(messageId,newContent,newExtras){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;let result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(result||(result=this.getMessageByIdWithRole(messageId,MessageRole.SYSTEM)),!result)return;const{message:msg,index:idx}=result;try{const allMessages=await conversationsStore.getConversationMessages( -activeConv.id),rootMessage=allMessages.find(m=>m.type==="root"&&m.parent===null),isFirstUserMessage=msg.role===MessageRole.USER&&rootMessage&&msg.parent===rootMessage.id,extrasToUse=newExtras!==void 0?JSON.parse(JSON.stringify(newExtras)):msg.extra?JSON.parse(JSON.stringify(msg.extra)):void 0;let messageIdForResponse;const dbMsg=findMessageById(allMessages,msg.id);if(dbMsg?dbMsg.children.length>0:msg.children.length>0){const parentId=msg.parent||rootMessage?.id;if(!parentId)return;const newMessage=await DatabaseService. -createMessageBranch({convId:msg.convId,type:msg.type,timestamp:Date.now(),role:msg.role,content:newContent,toolCalls:msg.toolCalls||"",children:[],extra:extrasToUse,model:msg.model},parentId);await conversationsStore.updateCurrentNode(newMessage.id),messageIdForResponse=newMessage.id}else{const updates={content:newContent,timestamp:Date.now(),extra:extrasToUse};await DatabaseService.updateMessage(msg.id,updates),conversationsStore.updateMessageAtIndex(idx,updates),messageIdForResponse=msg.id}conversationsStore. -updateConversationTimestamp(),isFirstUserMessage&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation(activeConv.id,generateConversationTitle(newContent,!!config$1().titleGenerationUseFirstLine)),await conversationsStore.refreshActiveMessages(),msg.role===MessageRole.USER&&await this.generateResponseForMessage(messageIdForResponse)}catch(error2){console.error("Failed to edit message with branching:",error2)}}async generateResponseForMessage(userMessageId){const activeConv=conversationsStore. -activeConversation;if(activeConv){this.showErrorDialog(null),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);try{const allMessages=await conversationsStore.getConversationMessages(activeConv.id),conversationPath=filterByLeafNodeId(allMessages,userMessageId,!1),assistantMessage=await DatabaseService.createMessageBranch({convId:activeConv.id,type:MessageType.TEXT,timestamp:Date.now(),role:MessageRole.ASSISTANT,content:"",toolCalls:"",children:[],model:null},userMessageId); -conversationsStore.addMessageToActive(assistantMessage),await this.streamChatCompletion(conversationPath,assistantMessage)}catch(error2){console.error("Failed to generate response:",error2),this.setChatLoading(activeConv.id,!1)}}}getContextTotal(){const activeConvId=this.activeConversationId,activeState=activeConvId?this.getProcessingState(activeConvId):null;if(activeState&&typeof activeState.contextTotal=="number"&&activeState.contextTotal>0)return activeState.contextTotal;if(isRouterMode()){const modelContextSize=selectedModelContextSize(); -if(typeof modelContextSize=="number"&&modelContextSize>0)return modelContextSize}else{const propsContextSize=contextSize();if(typeof propsContextSize=="number"&&propsContextSize>0)return propsContextSize}return null}updateProcessingStateFromTimings(timingData,conversationId){const processingState=this.parseTimingData(timingData);if(processingState===null){console.warn("Failed to parse timing data - skipping update");return}const targetId=conversationId||this.activeConversationId;targetId&&this.setProcessingState( -targetId,processingState)}parseTimingData(timingData){const promptTokens=timingData.prompt_n||0,promptMs=timingData.prompt_ms||void 0,predictedTokens=timingData.predicted_n||0,tokensPerSecond=timingData.predicted_per_second||0,cacheTokens=timingData.cache_n||0,promptProgress=timingData.prompt_progress,contextTotal=this.getContextTotal(),currentConfig=config$1(),outputTokensMax=currentConfig.max_tokens||-1,contextUsed=promptTokens+cacheTokens+predictedTokens,outputTokensUsed=predictedTokens,progressCache=promptProgress?. -cache||0,progressActualDone=(promptProgress?.processed??0)-progressCache,progressActualTotal=(promptProgress?.total??0)-progressCache,progressPercent=promptProgress?Math.round(progressActualDone/progressActualTotal*100):void 0;return{status:predictedTokens>0?"generating":promptProgress?"preparing":"idle",tokensDecoded:predictedTokens,tokensRemaining:outputTokensMax-predictedTokens,contextUsed,contextTotal,outputTokensUsed,outputTokensMax,hasNextToken:predictedTokens>0,tokensPerSecond,temperature:currentConfig. -temperature??.8,topP:currentConfig.top_p??.95,speculative:!1,progressPercent,promptProgress,promptTokens,promptMs,cacheTokens}}restoreProcessingStateFromMessages(messages,conversationId){for(let i=messages.length-1;i>=0;i--){const message=messages[i];if(message.role===MessageRole.ASSISTANT&&message.timings){const restoredState=this.parseTimingData({prompt_n:message.timings.prompt_n||0,prompt_ms:message.timings.prompt_ms,predicted_n:message.timings.predicted_n||0,predicted_per_second:message.timings. -predicted_n&&message.timings.predicted_ms?message.timings.predicted_n/message.timings.predicted_ms*1e3:0,cache_n:message.timings.cache_n||0});if(restoredState){this.setProcessingState(conversationId,restoredState);return}}}}getConversationModel(messages){for(let i=messages.length-1;i>=0;i--){const message=messages[i];if(message.role===MessageRole.ASSISTANT&&message.model)return message.model}return null}getApiOptions(){const currentConfig=config$1(),hasValue=value=>value!=null&&value!=="",apiOptions={ -stream:!0,timings_per_token:!0};if(isRouterMode()){const modelName=selectedModelName();modelName&&(apiOptions.model=modelName)}return currentConfig.systemMessage&&(apiOptions.systemMessage=currentConfig.systemMessage),currentConfig.disableReasoningParsing&&(apiOptions.disableReasoningParsing=!0),currentConfig.excludeReasoningFromContext&&(apiOptions.excludeReasoningFromContext=!0),hasValue(currentConfig.temperature)&&(apiOptions.temperature=Number(currentConfig.temperature)),hasValue(currentConfig. -max_tokens)&&(apiOptions.max_tokens=Number(currentConfig.max_tokens)),hasValue(currentConfig.dynatemp_range)&&(apiOptions.dynatemp_range=Number(currentConfig.dynatemp_range)),hasValue(currentConfig.dynatemp_exponent)&&(apiOptions.dynatemp_exponent=Number(currentConfig.dynatemp_exponent)),hasValue(currentConfig.top_k)&&(apiOptions.top_k=Number(currentConfig.top_k)),hasValue(currentConfig.top_p)&&(apiOptions.top_p=Number(currentConfig.top_p)),hasValue(currentConfig.min_p)&&(apiOptions.min_p=Number( -currentConfig.min_p)),hasValue(currentConfig.xtc_probability)&&(apiOptions.xtc_probability=Number(currentConfig.xtc_probability)),hasValue(currentConfig.xtc_threshold)&&(apiOptions.xtc_threshold=Number(currentConfig.xtc_threshold)),hasValue(currentConfig.typ_p)&&(apiOptions.typ_p=Number(currentConfig.typ_p)),hasValue(currentConfig.repeat_last_n)&&(apiOptions.repeat_last_n=Number(currentConfig.repeat_last_n)),hasValue(currentConfig.repeat_penalty)&&(apiOptions.repeat_penalty=Number(currentConfig. -repeat_penalty)),hasValue(currentConfig.presence_penalty)&&(apiOptions.presence_penalty=Number(currentConfig.presence_penalty)),hasValue(currentConfig.frequency_penalty)&&(apiOptions.frequency_penalty=Number(currentConfig.frequency_penalty)),hasValue(currentConfig.dry_multiplier)&&(apiOptions.dry_multiplier=Number(currentConfig.dry_multiplier)),hasValue(currentConfig.dry_base)&&(apiOptions.dry_base=Number(currentConfig.dry_base)),hasValue(currentConfig.dry_allowed_length)&&(apiOptions.dry_allowed_length= -Number(currentConfig.dry_allowed_length)),hasValue(currentConfig.dry_penalty_last_n)&&(apiOptions.dry_penalty_last_n=Number(currentConfig.dry_penalty_last_n)),currentConfig.samplers&&(apiOptions.samplers=currentConfig.samplers),apiOptions.backend_sampling=currentConfig.backend_sampling,currentConfig.custom&&(apiOptions.custom=currentConfig.custom),apiOptions}cancelPreEncode(){this.preEncodeAbortController&&(this.preEncodeAbortController.abort(),this.preEncodeAbortController=null)}async triggerPreEncode(allMessages,assistantMessage,assistantContent,model,excludeReasoning){ -this.cancelPreEncode(),this.preEncodeAbortController=new AbortController;const signal=this.preEncodeAbortController.signal;try{if(!await ChatService.areAllSlotsIdle(model,signal)||signal.aborted)return;const messagesWithAssistant=[...allMessages,{...assistantMessage,content:assistantContent}];await ChatService.preEncode(messagesWithAssistant,model,excludeReasoning,signal)}catch(err){isAbortError(err)||console.warn("[ChatStore] Pre-encode failed:",err)}}}const chatStore=new ChatStore,activeProcessingState=()=>chatStore. -activeProcessingState,errorDialog=()=>chatStore.errorDialogState,getAddFilesHandler=()=>chatStore.getAddFilesHandler(),getAllLoadingChats=()=>chatStore.getAllLoadingChats(),isChatStreaming=()=>chatStore.isStreaming(),isEditing=()=>chatStore.isEditing(),isLoading=()=>chatStore.isLoading,pendingEditMessageId=()=>chatStore.pendingEditMessageId,chatPendingMessageContent=convId=>chatStore.pendingMessageContent(convId),chatPendingMessageExtras=convId=>chatStore.pendingMessageExtras(convId),chatClearPendingMessage=convId=>chatStore. -clearPendingMessage(convId),chatInjectPendingMessage=(convId,content2,extras)=>chatStore.injectPendingMessage(convId,content2,extras);var root$1u=from_html('
',1);function ChatForm($$anchor,$$props){push$1($$props,!0);let attachments=prop($$props,"attachments",19,()=>[]),className=prop( -$$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),isLoading2=prop($$props,"isLoading",3,!1),placeholder=prop($$props,"placeholder",3,"Type a message..."),showMcpPromptButton=prop($$props,"showMcpPromptButton",3,!1),showAddButton=prop($$props,"showAddButton",3,!0),showModelSelector=prop($$props,"showModelSelector",3,!0),uploadedFiles=prop($$props,"uploadedFiles",31,()=>proxy([])),value=prop($$props,"value",15,""),audioRecorder,chatFormActionsRef=state$1(void 0),fileInputRef=state$1(void 0), -pickersRef=state$1(void 0),textareaRef=state$1(void 0),isRecording=state$1(!1),recordingSupported=state$1(!1),isPromptPickerOpen=state$1(!1),promptSearchQuery=state$1(""),isInlineResourcePickerOpen=state$1(!1),resourceSearchQuery=state$1(""),isResourceDialogOpen=state$1(!1),preSelectedResourceUri=state$1(void 0),currentConfig=user_derived(config$1),pasteLongTextToFileLength=user_derived(()=>{const n=Number(get$3(currentConfig).pasteLongTextToFileLen);return Number.isNaN(n)?Number(SETTING_CONFIG_DEFAULT. -pasteLongTextToFileLen):n}),isRouter=user_derived(isRouterMode),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId();if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3(conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model) -return model.model}return null}),hasModelSelected=user_derived(()=>!get$3(isRouter)||!!get$3(conversationModel)||!!selectedModelId()),hasLoadingAttachments=user_derived(()=>uploadedFiles().some(f=>f.isLoading)),hasAttachments=user_derived(()=>attachments()&&attachments().length>0||uploadedFiles()&&uploadedFiles().length>0),canSubmit=user_derived(()=>value().trim().length>0||get$3(hasAttachments));onMount$1(()=>{set$1(recordingSupported,isAudioRecordingSupported(),!0),audioRecorder=new AudioRecorder}); -function focus2(){get$3(textareaRef)?.focus()}function resetTextareaHeight(){get$3(textareaRef)?.resetHeight()}function openModelSelector(){get$3(chatFormActionsRef)?.openModelSelector()}function checkModelSelected(){return get$3(hasModelSelected)?!0:(get$3(chatFormActionsRef)?.openModelSelector(),!1)}function handleFileSelect(files){$$props.onFilesAdd?.(files)}function handleFileUpload(){get$3(fileInputRef)?.click()}function handleFileRemove(fileId){if(fileId.startsWith("attachment-")){const index2=parseInt( -fileId.replace("attachment-",""),10);!isNaN(index2)&&index2>=0&&index2item.kind==="file").map(item=>item.getAsFile()).filter(file=>file!==null);if(files.length>0){event2.preventDefault(),$$props.onFilesAdd?.(files);return}const text2=event2.clipboardData.getData(MimeTypeText.PLAIN);if(text2.startsWith(CLIPBOARD_CONTENT_QUOTE_PREFIX)){const parsed=parseClipboardContent(text2);if(parsed.textAttachments.length>0||parsed. -mcpPromptAttachments.length>0){if(event2.preventDefault(),value(parsed.message),$$props.onValueChange?.(parsed.message),parsed.textAttachments.length>0){const attachmentFiles=parsed.textAttachments.map(att=>new File([att.content],att.name,{type:MimeTypeText.PLAIN}));$$props.onFilesAdd?.(attachmentFiles)}if(parsed.mcpPromptAttachments.length>0){const mcpPromptFiles=parsed.mcpPromptAttachments.map(att=>({id:uuid$1(),name:att.name,size:att.content.length,type:SpecialFileType.MCP_PROMPT,file:new File( -[att.content],`${att.name}${FileExtensionText.TXT}`,{type:MimeTypeText.PLAIN}),isLoading:!1,textContent:att.content,mcpPrompt:{serverName:att.serverName,promptName:att.promptName,arguments:att.arguments}}));uploadedFiles([...uploadedFiles(),...mcpPromptFiles]),$$props.onUploadedFilesChange?.(uploadedFiles())}setTimeout(()=>{get$3(textareaRef)?.focus()},10);return}}if(text2.length>0&&get$3(pasteLongTextToFileLength)>0&&text2.length>get$3(pasteLongTextToFileLength)){event2.preventDefault();const textFile=new File( -[text2],"Pasted",{type:MimeTypeText.PLAIN});$$props.onFilesAdd?.([textFile])}}function handlePromptLoadStart(placeholderId,promptInfo,args){value().startsWith(PROMPT_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1(isPromptPickerOpen,!1),set$1(promptSearchQuery,"");const promptName=promptInfo.title||promptInfo.name,placeholder2={id:placeholderId,name:promptName,size:INITIAL_FILE_SIZE,type:SpecialFileType.MCP_PROMPT,file:new File([],"loading"),isLoading:!0,mcpPrompt:{serverName:promptInfo. -serverName,promptName:promptInfo.name,arguments:args?{...args}:void 0}};uploadedFiles([...uploadedFiles(),placeholder2]),$$props.onUploadedFilesChange?.(uploadedFiles()),get$3(textareaRef)?.focus()}function handlePromptLoadComplete(placeholderId,result){const promptText=result.messages?.map(msg=>typeof msg.content=="string"?msg.content:msg.content.type===ContentPartType.TEXT?msg.content.text:"").filter(Boolean).join(PROMPT_CONTENT_SEPARATOR);uploadedFiles(uploadedFiles().map(f=>f.id===placeholderId? -{...f,isLoading:!1,textContent:promptText,size:promptText.length,file:new File([promptText],`${f.name}${FileExtensionText.TXT}`,{type:MimeTypeText.PLAIN})}:f)),$$props.onUploadedFilesChange?.(uploadedFiles())}function handlePromptLoadError(placeholderId,error2){uploadedFiles(uploadedFiles().map(f=>f.id===placeholderId?{...f,isLoading:!1,loadError:error2}:f)),$$props.onUploadedFilesChange?.(uploadedFiles())}function handlePromptPickerClose(){set$1(isPromptPickerOpen,!1),set$1(promptSearchQuery,""), -get$3(textareaRef)?.focus()}function handleInlineResourcePickerClose(){set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),get$3(textareaRef)?.focus()}function handleInlineResourceSelect(){value().startsWith(RESOURCE_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),get$3(textareaRef)?.focus()}function handleBrowseResources(){set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),value().startsWith( -RESOURCE_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1(isResourceDialogOpen,!0)}async function handleMicClick(){if(!audioRecorder||!get$3(recordingSupported)){console.warn("Audio recording not supported");return}if(get$3(isRecording)){set$1(isRecording,!1);try{const audioBlob=await audioRecorder.stopRecording(),wavBlob=await convertToWav(audioBlob),audioFile=createAudioFile(wavBlob);$$props.onFilesAdd?.([audioFile])}catch(error2){console.error("Failed to stop recording:",error2)}}else -try{await audioRecorder.startRecording(),set$1(isRecording,!0)}catch(error2){console.error("Failed to start recording:",error2)}}var $$exports={focus:focus2,resetTextareaHeight,openModelSelector,checkModelSelected},fragment=root$1u(),node2=first_child(fragment);bind_this(ChatFormFileInputInvisible(node2,{onFileSelect:handleFileSelect}),$$value=>set$1(fileInputRef,$$value,!0),()=>get$3(fileInputRef));var form=sibling(node2,2),node_1=child(form);bind_this(ChatFormPickers(node_1,{get isPromptPickerOpen(){ -return get$3(isPromptPickerOpen)},get promptSearchQuery(){return get$3(promptSearchQuery)},get isInlineResourcePickerOpen(){return get$3(isInlineResourcePickerOpen)},get resourceSearchQuery(){return get$3(resourceSearchQuery)},onPromptPickerClose:handlePromptPickerClose,onInlineResourcePickerClose:handleInlineResourcePickerClose,onInlineResourceSelect:handleInlineResourceSelect,onPromptLoadStart:handlePromptLoadStart,onPromptLoadComplete:handlePromptLoadComplete,onPromptLoadError:handlePromptLoadError, -onInlineResourceBrowse:handleBrowseResources}),$$value=>set$1(pickersRef,$$value,!0),()=>get$3(pickersRef));var div=sibling(node_1,2),node_2=child(div);{let $0=user_derived(()=>get$3(activeModelId)??void 0);ChatAttachmentsList(node_2,{get attachments(){return attachments()},onFileRemove:handleFileRemove,limitToSingleRow:!0,class:"py-5",style:"scroll-padding: 1rem;",get activeModelId(){return get$3($0)},get uploadedFiles(){return uploadedFiles()},set uploadedFiles($$value){uploadedFiles($$value)}})} -var div_1=sibling(node_2,2),node_3=child(div_1);bind_this(ChatFormTextarea(node_3,{class:"px-5 py-1.5 md:pt-0",onKeydown:handleKeydown,onInput:()=>{handleInput(),$$props.onValueChange?.(value())},get disabled(){return disabled()},get placeholder(){return placeholder()},get value(){return value()},set value($$value){value($$value)}}),$$value=>set$1(textareaRef,$$value,!0),()=>get$3(textareaRef));var node_4=sibling(node_3,2);{var consequent=$$anchor2=>{ChatFormMcpResourcesList($$anchor2,{class:"mb\ --3",onResourceClick:uri2=>{set$1(preSelectedResourceUri,uri2,!0),set$1(isResourceDialogOpen,!0)}})},d2=user_derived(()=>mcpHasResourceAttachments());if_block(node_4,$$render=>{get$3(d2)&&$$render(consequent)})}var node_5=sibling(node_4,2);{let $0=user_derived(()=>showMcpPromptButton()?()=>set$1(isPromptPickerOpen,!0):void 0);bind_this(ChatFormActions(node_5,{class:"px-3",get canSend(){return get$3(canSubmit)},get disabled(){return disabled()},get isLoading(){return isLoading2()},get isRecording(){ -return get$3(isRecording)},get showAddButton(){return showAddButton()},get showModelSelector(){return showModelSelector()},get uploadedFiles(){return uploadedFiles()},onFileUpload:handleFileUpload,onMicClick:handleMicClick,get onStop(){return $$props.onStop},onSystemPromptClick:()=>$$props.onSystemPromptClick?.({message:value(),files:uploadedFiles()}),get onMcpPromptClick(){return get$3($0)},onMcpResourcesClick:()=>set$1(isResourceDialogOpen,!0)}),$$value=>set$1(chatFormActionsRef,$$value,!0),()=>get$3( -chatFormActionsRef))}reset(div_1),reset(div),reset(form);var node_6=sibling(form,2);return DialogMcpResourcesBrowser(node_6,{get preSelectedUri(){return get$3(preSelectedResourceUri)},onAttach:resource=>{mcpStore.attachResource(resource.uri)},onOpenChange:newOpen=>{newOpen||set$1(preSelectedResourceUri,void 0)},get open(){return get$3(isResourceDialogOpen)},set open($$value){set$1(isResourceDialogOpen,$$value,!0)}}),template_effect(()=>{set_class(form,1,`relative ${className()??""}`),set_class(div, -1,`${INPUT_CLASSES??""} overflow-hidden rounded-3xl backdrop-blur-md ${disabled()?"cursor-not-allowed opacity-60":""}`)}),event("submit",form,event2=>{event2.preventDefault(),!(!get$3(canSubmit)||disabled()||get$3(hasLoadingAttachments))&&$$props.onSubmit?.()}),event("paste",div_1,handlePaste),append($$anchor,fragment),pop($$exports)}function Dropdown_menu_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,4),restProps=rest_props( -$$props,["$$slots","$$events","$$legacy","ref","sideOffset","portalProps","class"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,DropdownMenuPrimitive_Portal)=>{DropdownMenuPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dro\ -pdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=op\ -en]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node_1,()=>Dropdown_menu_content$1,($$anchor4,DropdownMenuPrimitive_Content)=>{DropdownMenuPrimitive_Content($$anchor4,spread_props({"data-slot":"dropdown-menu-content",get sideOffset(){return sideOffset()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_item($$anchor,$$props){ -push$1($$props,!0);let ref2=prop($$props,"ref",15,null),variant=prop($$props,"variant",3,"default"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","variant"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disab\ -led]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 data-[variant=destructive]:data-highlighted:text-destructive dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:!text-destructive",$$props.class));component(node2,()=>Menu_item, -($$anchor2,DropdownMenuPrimitive_Item)=>{DropdownMenuPrimitive_Item($$anchor2,spread_props({"data-slot":"dropdown-menu-item",get"data-inset"(){return $$props.inset},get"data-variant"(){return variant()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_separator($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events", -"$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("-mx-1 my-1 h-px bg-border/20",$$props.class));component(node2,()=>Menu_separator,($$anchor2,DropdownMenuPrimitive_Separator)=>{DropdownMenuPrimitive_Separator($$anchor2,spread_props({"data-slot":"dropdown-menu-separator",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_trigger($$anchor,$$props){ -push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Menu_trigger,($$anchor2,DropdownMenuPrimitive_Trigger)=>{DropdownMenuPrimitive_Trigger($$anchor2,spread_props({"data-slot":"dropdown-menu-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_sub_content($$anchor,$$props){ -push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-\ -in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node2,()=>Menu_sub_content,($$anchor2,DropdownMenuPrimitive_SubContent)=>{DropdownMenuPrimitive_SubContent( -$$anchor2,spread_props({"data-slot":"dropdown-menu-sub-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$P=from_html(" ",1);function Dropdown_menu_sub_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","children"]);var fragment=comment$2(),node2=first_child(fragment);{ -let $0=user_derived(()=>cn$1("flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",$$props.class));component( -node2,()=>Menu_sub_trigger,($$anchor2,DropdownMenuPrimitive_SubTrigger)=>{DropdownMenuPrimitive_SubTrigger($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-trigger",get"data-inset"(){return $$props.inset},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$P(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_right(node_2, -{class:"ml-auto size-4"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}const Sub=Menu_sub,Root$4=Menu;function useAttachmentMenu(getFlags,getCallbacks,close2){const modalityFlags=user_derived(getFlags),callbacks=user_derived(()=>{const cbs=getCallbacks(),wrap2=fn=>()=>{close2(),fn?.()};return{[AttachmentAction.FILE_UPLOAD]:wrap2(cbs.onFileUpload),[AttachmentAction.SYSTEM_PROMPT_CLICK]:wrap2(cbs.onSystemPromptClick),[AttachmentAction.MCP_PROMPT_CLICK]:wrap2( -cbs.onMcpPromptClick),[AttachmentAction.MCP_RESOURCES_CLICK]:wrap2(cbs.onMcpResourcesClick)}});function isItemEnabled(enabledWhen){return!enabledWhen||enabledWhen==="always"?!0:!!get$3(modalityFlags)[enabledWhen]}function isItemVisible(visibleWhen){return visibleWhen?!!get$3(modalityFlags)[visibleWhen]:!0}function getSystemMessageTooltip(){return page$1.params.id?"Inject custom system message at the beginning of the conversation":"Add custom system message for a new conversation"}return{get callbacks(){ -return get$3(callbacks)},isItemEnabled,isItemVisible,getSystemMessageTooltip}}var root_6$s=from_html(" ",1),root_10$f=from_html(" ",1),root_11$e=from_html("

"),root_8$r=from_html(" ",1),root_16$6=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$d=from_html(" ",1),root_22$2=from_html(" ",1),root_23$5=from_html("

"),root_20$5=from_html( -" ",1),root_26$2=from_html(" ",1),root_3$S=from_html(" ",1),root_1$O=from_html(" ",1),root$1t=from_html("
");function ChatFormActionAddDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3, -!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),dropdownOpen=state$1(!1);function handleMcpSettingsClick(){set$1(dropdownOpen,!1),$$props.onMcpSettingsClick?.()}const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props. -onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(dropdownOpen,!1)});var div=root$1t(),node2=child(div);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$O(),node_1=first_child(fragment);component(node_1,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor4, -{name:"Attach files",get disabled(){return disabled()},children:($$anchor5,$$slotProps2)=>{var fragment_1=comment$2(),node_2=first_child(fragment_1);snippet(node_2,()=>$$props.trigger,()=>({disabled:disabled()})),append($$anchor5,fragment_1)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_content,($$anchor4,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor4,{align:"start",class:"w-48",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_3$S(),node_4=first_child( -fragment_2);each(node_4,17,()=>ATTACHMENT_FILE_ITEMS,item=>item.id,($$anchor6,item)=>{const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_5=first_child(fragment_3);{var consequent=$$anchor7=>{var fragment_4=comment$2(),node_6=first_child(fragment_4);{let $0=user_derived(()=>get$3(item).class??"");component(node_6,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor8,{get class(){return`${get$3($0)??""}\ - flex cursor-pointer items-center gap-2`},onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{var fragment_5=root_6$s(),node_7=first_child(fragment_5);component(node_7,()=>get$3(item).icon,($$anchor10,item_icon)=>{item_icon($$anchor10,{class:"h-4 w-4"})});var span=sibling(node_7,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,get$3(item).label)),append($$anchor9,fragment_5)},$$slots:{default:!0}})})}append($$anchor7,fragment_4)},consequent_1=$$anchor7=>{ -var fragment_6=comment$2(),node_8=first_child(fragment_6);component(node_8,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_7=root_8$r(),node_9=first_child(fragment_7);component(node_9,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);{let $0=user_derived( -()=>get$3(item).class??"");component(node_10,()=>Dropdown_menu_item,($$anchor12,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor12,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},disabled:!0,children:($$anchor13,$$slotProps5)=>{var fragment_9=root_10$f(),node_11=first_child(fragment_9);component(node_11,()=>get$3(item).icon,($$anchor14,item_icon_1)=>{item_icon_1($$anchor14,{class:"h-4 w-4"})});var span_1=sibling(node_11,2),text_1=child(span_1,!0);reset(span_1),template_effect( -()=>set_text(text_1,get$3(item).label)),append($$anchor13,fragment_9)},$$slots:{default:!0}})})}append($$anchor11,fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9, -fragment_7)},$$slots:{default:!0}})}),append($$anchor7,fragment_6)};if_block(node_5,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_4,2);{var consequent_3=$$anchor6=>{var fragment_10=comment$2(),node_14=first_child(fragment_10);component(node_14,()=>Root$5,($$anchor7,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor7,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor8,$$slotProps3)=>{ -var fragment_11=root_13$d(),node_15=first_child(fragment_11);component(node_15,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor9,{class:"w-full",children:($$anchor10,$$slotProps4)=>{var fragment_12=comment$2(),node_16=first_child(fragment_12);component(node_16,()=>Dropdown_menu_item,($$anchor11,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor11,{class:"flex cursor-pointer items-center gap-2",get onclick(){return attachmentMenu.callbacks.onFileUpload},children:($$anchor12,$$slotProps5)=>{ -const pdfItem=user_derived(()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_13=comment$2(),node_17=first_child(fragment_13);{var consequent_2=$$anchor13=>{var fragment_14=root_16$6(),node_18=first_child(fragment_14);component(node_18,()=>get$3(pdfItem).icon,($$anchor14,pdfItem_icon)=>{pdfItem_icon($$anchor14,{class:"h-4 w-4"})});var span_2=sibling(node_18,2),text_3=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_3,get$3(pdfItem).label)),append($$anchor13, -fragment_14)};if_block(node_17,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor12,fragment_13)},$$slots:{default:!0}})}),append($$anchor10,fragment_12)},$$slots:{default:!0}})});var node_19=sibling(node_15,2);component(node_19,()=>Tooltip_content,($$anchor9,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor9,{side:"right",children:($$anchor10,$$slotProps4)=>{var p_1=root_17$8();append($$anchor10,p_1)},$$slots:{default:!0}})}),append($$anchor8,fragment_11)},$$slots:{default:!0}})}), -append($$anchor6,fragment_10)},d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13,$$render=>{get$3(d2)&&$$render(consequent_3)})}var node_20=sibling(node_13,2);component(node_20,()=>Dropdown_menu_separator,($$anchor6,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor6,{})});var node_21=sibling(node_20,2);each(node_21,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_15=comment$2(),node_22=first_child(fragment_15);{var consequent_4=$$anchor7=>{ -var fragment_16=comment$2(),node_23=first_child(fragment_16);component(node_23,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_17=root_20$5(),node_24=first_child(fragment_17);component(node_24,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_18=comment$2(),node_25=first_child(fragment_18); -component(node_25,()=>Dropdown_menu_item,($$anchor12,DropdownMenu_Item_3)=>{DropdownMenu_Item_3($$anchor12,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor13,$$slotProps5)=>{var fragment_19=root_22$2(),node_26=first_child(fragment_19);component(node_26,()=>get$3(item).icon,($$anchor14,item_icon_2)=>{item_icon_2($$anchor14,{class:"h-4 w-4"})});var span_3=sibling(node_26,2),text_4=child(span_3,!0);reset(span_3),template_effect( -()=>set_text(text_4,get$3(item).label)),append($$anchor13,fragment_19)},$$slots:{default:!0}})}),append($$anchor11,fragment_18)},$$slots:{default:!0}})});var node_27=sibling(node_24,2);component(node_27,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_2=root_23$5(),text_5=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_5,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)}, -$$slots:{default:!0}})}),append($$anchor9,fragment_17)},$$slots:{default:!0}})}),append($$anchor7,fragment_16)};if_block(node_22,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_15)});var node_28=sibling(node_21,2);ChatFormActionAddToolsSubmenu(node_28,{});var node_29=sibling(node_28,2);ChatFormActionAddMcpServersSubmenu(node_29,{onMcpSettingsClick:handleMcpSettingsClick});var node_30=sibling(node_29,2);each(node_30,17,()=>ATTACHMENT_MCP_ITEMS, -item=>item.id,($$anchor6,item)=>{var fragment_20=comment$2(),node_31=first_child(fragment_20);{var consequent_5=$$anchor7=>{var fragment_21=comment$2(),node_32=first_child(fragment_21);component(node_32,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_4)=>{DropdownMenu_Item_4($$anchor8,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{var fragment_22=root_26$2(),node_33=first_child(fragment_22);component( -node_33,()=>get$3(item).icon,($$anchor10,item_icon_3)=>{item_icon_3($$anchor10,{class:"h-4 w-4"})});var span_4=sibling(node_33,2),text_6=child(span_4,!0);reset(span_4),template_effect(()=>set_text(text_6,get$3(item).label)),append($$anchor9,fragment_22)},$$slots:{default:!0}})}),append($$anchor7,fragment_21)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_31,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_20)}),append($$anchor5, -fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}function Sheet_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/5\ -0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,SheetPrimitive_Overlay)=>{SheetPrimitive_Overlay($$anchor2,spread_props({"data-slot":"sheet-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const sheetVariants=tv({base:`bor\ -der-border/30 dark:border-border/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fixed z-50 flex flex-col gap-4 shadow-sm transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 ${PANEL_CLASSES}`,variants:{side:{top:"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",bottom:"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inse\ -t-x-0 bottom-0 h-auto border-t",left:"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",right:"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"}},defaultVariants:{side:"right"}});var root_3$R=from_html(' Close',1),root_2$12=from_html(" ",1),root_1$N=from_html(" ",1);function Sheet_content($$anchor,$$props){ -push$1($$props,!0);let ref2=prop($$props,"ref",15,null),side=prop($$props,"side",3,"right"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","side","portalProps","children"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SheetPrimitive_Portal)=>{SheetPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$N(),node_1=first_child(fragment_1);Sheet_overlay(node_1, -{});var node_2=sibling(node_1,2);{let $0=user_derived(()=>cn$1(sheetVariants({side:side()}),$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,SheetPrimitive_Content)=>{SheetPrimitive_Content($$anchor4,spread_props({"data-slot":"sheet-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$12(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3); -var node_4=sibling(node_3,2);component(node_4,()=>Dialog_close,($$anchor6,SheetPrimitive_Close)=>{SheetPrimitive_Close($$anchor6,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none",children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$R(),node_5=first_child(fragment_3);X(node_5,{class:"size-4"}),next$1( -2),append($$anchor7,fragment_3)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root$1s=from_html("
");function Sheet_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$1s();attribute_effect(div,$0=>({"data-slot":"sheet-header",class:$0, -...restProps}),[()=>cn$1("flex flex-col gap-1.5 p-4",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Sheet_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("font-semibold text-fo\ -reground",$$props.class));component(node2,()=>Dialog_title$1,($$anchor2,SheetPrimitive_Title)=>{SheetPrimitive_Title($$anchor2,spread_props({"data-slot":"sheet-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Sheet_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(), -node2=first_child(fragment);{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,SheetPrimitive_Description)=>{SheetPrimitive_Description($$anchor2,spread_props({"data-slot":"sheet-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$3=Dialog;var root_3$Q=from_html(" ",1),root_7$r=from_html(''),root_10$e=from_html(''),root_11$d=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(''),root_16$5=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$5=from_html(" ",1),root_20$4=from_html(''),root_21$3=from_html("

"), -root_19$9=from_html(" ",1),root_23$4=from_html(''),root_2$11=from_html('
',1),root_1$M=from_html(" ",1),root$1r=from_html("
");function ChatFormActionAddSheet($$anchor,$$props){ +`).find(l=>l.trim().length>0);cleanTitle=firstLine?firstLine.trim():TITLE_GENERATION.FALLBACK}cleanTitle&&cleanTitle.length>=TITLE_GENERATION.MIN_LENGTH&&await conversationsStore.updateConversationName(convId,cleanTitle)}async savePartialResponseIfNeeded(convId){const conversationId=convId||conversationsStore.activeConversation?.id;if(!conversationId)return;const streamingState=this.getChatStreaming(conversationId);if(!streamingState)return;const messages=conversationId===conversationsStore.activeConversation?. +id?conversationsStore.activeMessages:await conversationsStore.getConversationMessages(conversationId);if(!messages.length)return;const lastMessage=messages[messages.length-1];if(lastMessage?.role!==MessageRole.ASSISTANT)return;const partialContent=streamingState.response,partialReasoning=lastMessage.reasoningContent||"";if(!(!partialContent.trim()&&!partialReasoning.trim()))try{const updateData={content:partialContent};partialReasoning&&(updateData.reasoningContent=partialReasoning);const lastKnownState=this. +getProcessingState(conversationId);lastKnownState&&(updateData.timings={prompt_n:lastKnownState.promptTokens||0,prompt_ms:lastKnownState.promptMs,predicted_n:lastKnownState.tokensDecoded||0,cache_n:lastKnownState.cacheTokens||0,predicted_ms:lastKnownState.tokensPerSecond&&lastKnownState.tokensDecoded?lastKnownState.tokensDecoded/lastKnownState.tokensPerSecond*1e3:void 0}),await DatabaseService.updateMessage(lastMessage.id,updateData),lastMessage.content=partialContent,updateData.timings&&(lastMessage. +timings=updateData.timings)}catch(error2){lastMessage.content=partialContent,console.error("Failed to save partial response:",error2)}}async updateMessage(messageId,newContent){const activeConv=conversationsStore.activeConversation;if(!activeConv)return;this.isChatLoadingInternal(activeConv.id)&&await this.stopGeneration();const result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(!result)return;const{message:messageToUpdate,index:messageIndex}=result,originalContent=messageToUpdate. +content;try{const rootMessage=(await conversationsStore.getConversationMessages(activeConv.id)).find(m=>m.type==="root"&&m.parent===null),isFirstUserMessage=rootMessage&&messageToUpdate.parent===rootMessage.id;conversationsStore.updateMessageAtIndex(messageIndex,{content:newContent}),await DatabaseService.updateMessage(messageId,{content:newContent}),isFirstUserMessage&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation(activeConv.id,generateConversationTitle(newContent, +!!config$1().titleGenerationUseFirstLine));const messagesToRemove=conversationsStore.activeMessages.slice(messageIndex+1);for(const message of messagesToRemove)await DatabaseService.deleteMessage(message.id);conversationsStore.sliceActiveMessages(messageIndex+1),conversationsStore.updateConversationTimestamp(),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const assistantMessage=await this.createAssistantMessage();conversationsStore.addMessageToActive(assistantMessage), +await conversationsStore.updateCurrentNode(assistantMessage.id),await this.streamChatCompletion(conversationsStore.activeMessages.slice(0,-1),assistantMessage,void 0,()=>{conversationsStore.updateMessageAtIndex(conversationsStore.findMessageIndex(messageId),{content:originalContent})})}catch(error2){isAbortError(error2)||console.error("Failed to update message:",error2)}}async regenerateMessage(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal( +activeConv.id))return;this.cancelPreEncode();const result=this.getMessageByIdWithRole(messageId,MessageRole.ASSISTANT);if(!result)return;const{index:messageIndex}=result;try{const messagesToRemove=conversationsStore.activeMessages.slice(messageIndex);for(const message of messagesToRemove)await DatabaseService.deleteMessage(message.id);conversationsStore.sliceActiveMessages(messageIndex),conversationsStore.updateConversationTimestamp(),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming( +activeConv.id);const parentMessageId=conversationsStore.activeMessages.length>0?conversationsStore.activeMessages[conversationsStore.activeMessages.length-1].id:void 0,assistantMessage=await this.createAssistantMessage(parentMessageId);conversationsStore.addMessageToActive(assistantMessage),await this.streamChatCompletion(conversationsStore.activeMessages.slice(0,-1),assistantMessage)}catch(error2){isAbortError(error2)||console.error("Failed to regenerate message:",error2),this.setChatLoading(activeConv?. +id||"",!1)}}async regenerateMessageWithBranching(messageId,modelOverride){const activeConv=conversationsStore.activeConversation;if(!(!activeConv||this.isChatLoadingInternal(activeConv.id))){this.cancelPreEncode();try{const idx=conversationsStore.findMessageIndex(messageId);if(idx===-1)return;const msg=conversationsStore.activeMessages[idx];if(msg.role!==MessageRole.ASSISTANT)return;const allMessages=await conversationsStore.getConversationMessages(activeConv.id),parentMessage=findMessageById(allMessages, +msg.parent);if(!parentMessage)return;this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const newAssistantMessage=await DatabaseService.createMessageBranch({convId:msg.convId,type:msg.type,timestamp:Date.now(),role:msg.role,content:"",toolCalls:"",children:[],model:null},parentMessage.id);await conversationsStore.updateCurrentNode(newAssistantMessage.id),conversationsStore.updateConversationTimestamp(),await conversationsStore.refreshActiveMessages();const conversationPath=filterByLeafNodeId( +allMessages,parentMessage.id,!1),modelToUse=modelOverride||msg.model||void 0;await this.streamChatCompletion(conversationPath,newAssistantMessage,void 0,void 0,modelToUse)}catch(error2){isAbortError(error2)||console.error("Failed to regenerate message with branching:",error2),this.setChatLoading(activeConv?.id||"",!1)}}}async getDeletionInfo(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv)return{totalCount:0,userMessages:0,assistantMessages:0,messageTypes:[]};const allMessages=await conversationsStore. +getConversationMessages(activeConv.id);if(findMessageById(allMessages,messageId)?.role===MessageRole.SYSTEM){const messagesToDelete2=allMessages.filter(m=>m.id===messageId);let userMessages2=0,assistantMessages2=0;const messageTypes2=[];for(const msg of messagesToDelete2)msg.role===MessageRole.USER?(userMessages2++,messageTypes2.includes("user message")||messageTypes2.push("user message")):msg.role===MessageRole.ASSISTANT&&(assistantMessages2++,messageTypes2.includes("assistant response")||messageTypes2. +push("assistant response"));return{totalCount:1,userMessages:userMessages2,assistantMessages:assistantMessages2,messageTypes:messageTypes2}}const descendants=findDescendantMessages(allMessages,messageId),allToDelete=[messageId,...descendants],messagesToDelete=allMessages.filter(m=>allToDelete.includes(m.id));let userMessages=0,assistantMessages=0;const messageTypes=[];for(const msg of messagesToDelete)msg.role===MessageRole.USER?(userMessages++,messageTypes.includes("user message")||messageTypes. +push("user message")):msg.role===MessageRole.ASSISTANT&&(assistantMessages++,messageTypes.includes("assistant response")||messageTypes.push("assistant response"));return{totalCount:allToDelete.length,userMessages,assistantMessages,messageTypes}}async deleteMessage(messageId){const activeConv=conversationsStore.activeConversation;if(activeConv)try{const allMessages=await conversationsStore.getConversationMessages(activeConv.id),messageToDelete=findMessageById(allMessages,messageId);if(!messageToDelete) +return;if(filterByLeafNodeId(allMessages,activeConv.currNode||"",!1).some(m=>m.id===messageId)&&messageToDelete.parent){const siblings2=allMessages.filter(m=>m.parent===messageToDelete.parent&&m.id!==messageId);if(siblings2.length>0){const latestSibling=siblings2.reduce((latest,sibling2)=>sibling2.timestamp>latest.timestamp?sibling2:latest);await conversationsStore.updateCurrentNode(findLeafNode(allMessages,latestSibling.id))}else messageToDelete.parent&&await conversationsStore.updateCurrentNode( +findLeafNode(allMessages,messageToDelete.parent))}await DatabaseService.deleteMessageCascading(activeConv.id,messageId),await conversationsStore.refreshActiveMessages(),conversationsStore.updateConversationTimestamp()}catch(error2){console.error("Failed to delete message:",error2)}}async continueAssistantMessage(messageId){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;const result=this.getMessageByIdWithRole(messageId,MessageRole. +ASSISTANT);if(!result)return;const{message:msg,index:idx}=result;try{this.showErrorDialog(null),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming(activeConv.id);const allMessages=await conversationsStore.getConversationMessages(activeConv.id),dbMessage=findMessageById(allMessages,messageId);if(!dbMessage){this.setChatLoading(activeConv.id,!1);return}const originalContent=dbMessage.content,originalReasoning=dbMessage.reasoningContent||"",contextWithContinue=[...conversationsStore.activeMessages. +slice(0,idx),{role:MessageRole.ASSISTANT,content:originalContent,reasoning_content:originalReasoning||void 0}];let appendedContent="",appendedReasoning="",hasReceivedContent=!1;const updateStreamingContent=fullContent=>{this.setChatStreaming(msg.convId,fullContent,msg.id),conversationsStore.updateMessageAtIndex(idx,{content:fullContent})},abortController=this.getOrCreateAbortController(msg.convId);await ChatService.sendMessage(contextWithContinue,{...this.getApiOptions(),onChunk:chunk=>{appendedContent+= +chunk,hasReceivedContent=!0,updateStreamingContent(originalContent+appendedContent)},onReasoningChunk:chunk=>{appendedReasoning+=chunk,hasReceivedContent=!0,this.setChatStreaming(msg.convId,originalContent+appendedContent,msg.id),conversationsStore.updateMessageAtIndex(idx,{reasoningContent:originalReasoning+appendedReasoning})},onTimings:(timings,promptProgress)=>{const tokensPerSecond=timings?.predicted_ms&&timings?.predicted_n?timings.predicted_n/timings.predicted_ms*1e3:0;this.updateProcessingStateFromTimings( +{prompt_n:timings?.prompt_n||0,prompt_ms:timings?.prompt_ms,predicted_n:timings?.predicted_n||0,predicted_per_second:tokensPerSecond,cache_n:timings?.cache_n||0,prompt_progress:promptProgress},msg.convId)},onComplete:async(finalContent,reasoningContent,timings)=>{const finalAppendedContent=hasReceivedContent?appendedContent:finalContent||"",finalAppendedReasoning=hasReceivedContent?appendedReasoning:reasoningContent||"",fullContent=originalContent+finalAppendedContent,fullReasoning=originalReasoning+ +finalAppendedReasoning||void 0;await DatabaseService.updateMessage(msg.id,{content:fullContent,reasoningContent:fullReasoning,timestamp:Date.now(),timings}),conversationsStore.updateMessageAtIndex(idx,{content:fullContent,reasoningContent:fullReasoning,timestamp:Date.now(),timings}),conversationsStore.updateConversationTimestamp(),this.setChatLoading(msg.convId,!1),this.clearChatStreaming(msg.convId),this.setProcessingState(msg.convId,null)},onError:async error2=>{if(isAbortError(error2)){hasReceivedContent&& +appendedContent&&(await DatabaseService.updateMessage(msg.id,{content:originalContent+appendedContent,reasoningContent:originalReasoning+appendedReasoning||void 0,timestamp:Date.now()}),conversationsStore.updateMessageAtIndex(idx,{content:originalContent+appendedContent,reasoningContent:originalReasoning+appendedReasoning||void 0,timestamp:Date.now()})),this.setChatLoading(msg.convId,!1),this.clearChatStreaming(msg.convId),this.setProcessingState(msg.convId,null);return}console.error("Continue g\ +eneration error:",error2),conversationsStore.updateMessageAtIndex(idx,{content:originalContent}),await DatabaseService.updateMessage(msg.id,{content:originalContent}),this.setChatLoading(msg.convId,!1),this.clearChatStreaming(msg.convId),this.setProcessingState(msg.convId,null),this.showErrorDialog({type:error2.name==="TimeoutError"?ErrorDialogType.TIMEOUT:ErrorDialogType.SERVER,message:error2.message})}},msg.convId,abortController.signal)}catch(error2){isAbortError(error2)||console.error("Faile\ +d to continue message:",error2),activeConv&&this.setChatLoading(activeConv.id,!1)}}async editAssistantMessage(messageId,newContent,shouldBranch){const activeConv=conversationsStore.activeConversation;if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;const result=this.getMessageByIdWithRole(messageId,MessageRole.ASSISTANT);if(!result)return;const{message:msg,index:idx}=result;try{if(shouldBranch){const newMessage=await DatabaseService.createMessageBranch({convId:msg.convId,type:msg. +type,timestamp:Date.now(),role:msg.role,content:newContent,toolCalls:msg.toolCalls||"",children:[],model:msg.model},msg.parent);await conversationsStore.updateCurrentNode(newMessage.id)}else await DatabaseService.updateMessage(msg.id,{content:newContent}),conversationsStore.updateMessageAtIndex(idx,{content:newContent});conversationsStore.updateConversationTimestamp(),await conversationsStore.refreshActiveMessages()}catch(error2){console.error("Failed to edit assistant message:",error2)}}async editUserMessagePreserveResponses(messageId,newContent,newExtras){ +const activeConv=conversationsStore.activeConversation;if(!activeConv)return;const result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(!result)return;const{message:msg,index:idx}=result;try{const updateData={content:newContent};newExtras!==void 0&&(updateData.extra=JSON.parse(JSON.stringify(newExtras))),await DatabaseService.updateMessage(messageId,updateData),conversationsStore.updateMessageAtIndex(idx,updateData);const rootMessage=(await conversationsStore.getConversationMessages( +activeConv.id)).find(m=>m.type==="root"&&m.parent===null);rootMessage&&msg.parent===rootMessage.id&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation(activeConv.id,generateConversationTitle(newContent,!!config$1().titleGenerationUseFirstLine)),conversationsStore.updateConversationTimestamp()}catch(error2){console.error("Failed to edit user message:",error2)}}async editMessageWithBranching(messageId,newContent,newExtras){const activeConv=conversationsStore.activeConversation; +if(!activeConv||this.isChatLoadingInternal(activeConv.id))return;let result=this.getMessageByIdWithRole(messageId,MessageRole.USER);if(result||(result=this.getMessageByIdWithRole(messageId,MessageRole.SYSTEM)),!result)return;const{message:msg,index:idx}=result;try{const allMessages=await conversationsStore.getConversationMessages(activeConv.id),rootMessage=allMessages.find(m=>m.type==="root"&&m.parent===null),isFirstUserMessage=msg.role===MessageRole.USER&&rootMessage&&msg.parent===rootMessage.id, +extrasToUse=newExtras!==void 0?JSON.parse(JSON.stringify(newExtras)):msg.extra?JSON.parse(JSON.stringify(msg.extra)):void 0;let messageIdForResponse;const dbMsg=findMessageById(allMessages,msg.id);if(dbMsg?dbMsg.children.length>0:msg.children.length>0){const parentId=msg.parent||rootMessage?.id;if(!parentId)return;const newMessage=await DatabaseService.createMessageBranch({convId:msg.convId,type:msg.type,timestamp:Date.now(),role:msg.role,content:newContent,toolCalls:msg.toolCalls||"",children:[], +extra:extrasToUse,model:msg.model},parentId);await conversationsStore.updateCurrentNode(newMessage.id),messageIdForResponse=newMessage.id}else{const updates={content:newContent,timestamp:Date.now(),extra:extrasToUse};await DatabaseService.updateMessage(msg.id,updates),conversationsStore.updateMessageAtIndex(idx,updates),messageIdForResponse=msg.id}conversationsStore.updateConversationTimestamp(),isFirstUserMessage&&newContent.trim()&&await conversationsStore.updateConversationTitleWithConfirmation( +activeConv.id,generateConversationTitle(newContent,!!config$1().titleGenerationUseFirstLine)),await conversationsStore.refreshActiveMessages(),msg.role===MessageRole.USER&&await this.generateResponseForMessage(messageIdForResponse)}catch(error2){console.error("Failed to edit message with branching:",error2)}}async generateResponseForMessage(userMessageId){const activeConv=conversationsStore.activeConversation;if(activeConv){this.showErrorDialog(null),this.setChatLoading(activeConv.id,!0),this.clearChatStreaming( +activeConv.id);try{const allMessages=await conversationsStore.getConversationMessages(activeConv.id),conversationPath=filterByLeafNodeId(allMessages,userMessageId,!1),assistantMessage=await DatabaseService.createMessageBranch({convId:activeConv.id,type:MessageType.TEXT,timestamp:Date.now(),role:MessageRole.ASSISTANT,content:"",toolCalls:"",children:[],model:null},userMessageId);conversationsStore.addMessageToActive(assistantMessage),await this.streamChatCompletion(conversationPath,assistantMessage)}catch(error2){ +console.error("Failed to generate response:",error2),this.setChatLoading(activeConv.id,!1)}}}getContextTotal(){const activeConvId=this.activeConversationId,activeState=activeConvId?this.getProcessingState(activeConvId):null;if(activeState&&typeof activeState.contextTotal=="number"&&activeState.contextTotal>0)return activeState.contextTotal;if(isRouterMode()){const modelContextSize=selectedModelContextSize();if(typeof modelContextSize=="number"&&modelContextSize>0)return modelContextSize}else{const propsContextSize=contextSize(); +if(typeof propsContextSize=="number"&&propsContextSize>0)return propsContextSize}return null}updateProcessingStateFromTimings(timingData,conversationId){const processingState=this.parseTimingData(timingData);if(processingState===null){console.warn("Failed to parse timing data - skipping update");return}const targetId=conversationId||this.activeConversationId;targetId&&this.setProcessingState(targetId,processingState)}parseTimingData(timingData){const promptTokens=timingData.prompt_n||0,promptMs=timingData. +prompt_ms||void 0,predictedTokens=timingData.predicted_n||0,tokensPerSecond=timingData.predicted_per_second||0,cacheTokens=timingData.cache_n||0,promptProgress=timingData.prompt_progress,contextTotal=this.getContextTotal(),currentConfig=config$1(),outputTokensMax=currentConfig.max_tokens||-1,contextUsed=promptTokens+cacheTokens+predictedTokens,outputTokensUsed=predictedTokens,progressCache=promptProgress?.cache||0,progressActualDone=(promptProgress?.processed??0)-progressCache,progressActualTotal=(promptProgress?. +total??0)-progressCache,progressPercent=promptProgress?Math.round(progressActualDone/progressActualTotal*100):void 0;return{status:predictedTokens>0?"generating":promptProgress?"preparing":"idle",tokensDecoded:predictedTokens,tokensRemaining:outputTokensMax-predictedTokens,contextUsed,contextTotal,outputTokensUsed,outputTokensMax,hasNextToken:predictedTokens>0,tokensPerSecond,temperature:currentConfig.temperature??.8,topP:currentConfig.top_p??.95,speculative:!1,progressPercent,promptProgress,promptTokens, +promptMs,cacheTokens}}restoreProcessingStateFromMessages(messages,conversationId){for(let i=messages.length-1;i>=0;i--){const message=messages[i];if(message.role===MessageRole.ASSISTANT&&message.timings){const restoredState=this.parseTimingData({prompt_n:message.timings.prompt_n||0,prompt_ms:message.timings.prompt_ms,predicted_n:message.timings.predicted_n||0,predicted_per_second:message.timings.predicted_n&&message.timings.predicted_ms?message.timings.predicted_n/message.timings.predicted_ms*1e3: +0,cache_n:message.timings.cache_n||0});if(restoredState){this.setProcessingState(conversationId,restoredState);return}}}}getConversationModel(messages){for(let i=messages.length-1;i>=0;i--){const message=messages[i];if(message.role===MessageRole.ASSISTANT&&message.model)return message.model}return null}getApiOptions(){const currentConfig=config$1(),hasValue=value=>value!=null&&value!=="",apiOptions={stream:!0,timings_per_token:!0};if(isRouterMode()){const modelName=selectedModelName();modelName&& +(apiOptions.model=modelName)}return currentConfig.systemMessage&&(apiOptions.systemMessage=currentConfig.systemMessage),currentConfig.disableReasoningParsing&&(apiOptions.disableReasoningParsing=!0),currentConfig.excludeReasoningFromContext&&(apiOptions.excludeReasoningFromContext=!0),hasValue(currentConfig.temperature)&&(apiOptions.temperature=Number(currentConfig.temperature)),hasValue(currentConfig.max_tokens)&&(apiOptions.max_tokens=Number(currentConfig.max_tokens)),hasValue(currentConfig.dynatemp_range)&& +(apiOptions.dynatemp_range=Number(currentConfig.dynatemp_range)),hasValue(currentConfig.dynatemp_exponent)&&(apiOptions.dynatemp_exponent=Number(currentConfig.dynatemp_exponent)),hasValue(currentConfig.top_k)&&(apiOptions.top_k=Number(currentConfig.top_k)),hasValue(currentConfig.top_p)&&(apiOptions.top_p=Number(currentConfig.top_p)),hasValue(currentConfig.min_p)&&(apiOptions.min_p=Number(currentConfig.min_p)),hasValue(currentConfig.xtc_probability)&&(apiOptions.xtc_probability=Number(currentConfig. +xtc_probability)),hasValue(currentConfig.xtc_threshold)&&(apiOptions.xtc_threshold=Number(currentConfig.xtc_threshold)),hasValue(currentConfig.typ_p)&&(apiOptions.typ_p=Number(currentConfig.typ_p)),hasValue(currentConfig.repeat_last_n)&&(apiOptions.repeat_last_n=Number(currentConfig.repeat_last_n)),hasValue(currentConfig.repeat_penalty)&&(apiOptions.repeat_penalty=Number(currentConfig.repeat_penalty)),hasValue(currentConfig.presence_penalty)&&(apiOptions.presence_penalty=Number(currentConfig.presence_penalty)), +hasValue(currentConfig.frequency_penalty)&&(apiOptions.frequency_penalty=Number(currentConfig.frequency_penalty)),hasValue(currentConfig.dry_multiplier)&&(apiOptions.dry_multiplier=Number(currentConfig.dry_multiplier)),hasValue(currentConfig.dry_base)&&(apiOptions.dry_base=Number(currentConfig.dry_base)),hasValue(currentConfig.dry_allowed_length)&&(apiOptions.dry_allowed_length=Number(currentConfig.dry_allowed_length)),hasValue(currentConfig.dry_penalty_last_n)&&(apiOptions.dry_penalty_last_n=Number( +currentConfig.dry_penalty_last_n)),currentConfig.samplers&&(apiOptions.samplers=currentConfig.samplers),apiOptions.backend_sampling=currentConfig.backend_sampling,currentConfig.custom&&(apiOptions.custom=currentConfig.custom),apiOptions}cancelPreEncode(){this.preEncodeAbortController&&(this.preEncodeAbortController.abort(),this.preEncodeAbortController=null)}async triggerPreEncode(allMessages,assistantMessage,assistantContent,model,excludeReasoning){this.cancelPreEncode(),this.preEncodeAbortController= +new AbortController;const signal=this.preEncodeAbortController.signal;try{if(!await ChatService.areAllSlotsIdle(model,signal)||signal.aborted)return;const messagesWithAssistant=[...allMessages,{...assistantMessage,content:assistantContent}];await ChatService.preEncode(messagesWithAssistant,model,excludeReasoning,signal)}catch(err){isAbortError(err)||console.warn("[ChatStore] Pre-encode failed:",err)}}}const chatStore=new ChatStore,activeProcessingState=()=>chatStore.activeProcessingState,errorDialog=()=>chatStore. +errorDialogState,getAddFilesHandler=()=>chatStore.getAddFilesHandler(),getAllLoadingChats=()=>chatStore.getAllLoadingChats(),isChatStreaming=()=>chatStore.isStreaming(),isEditing=()=>chatStore.isEditing(),isLoading=()=>chatStore.isLoading,pendingEditMessageId=()=>chatStore.pendingEditMessageId,chatPendingMessageContent=convId=>chatStore.pendingMessageContent(convId),chatPendingMessageExtras=convId=>chatStore.pendingMessageExtras(convId),chatClearPendingMessage=convId=>chatStore.clearPendingMessage( +convId),chatInjectPendingMessage=(convId,content2,extras)=>chatStore.injectPendingMessage(convId,content2,extras);var root$1u=from_html('
',1);function ChatForm($$anchor,$$props){push$1($$props,!0);let attachments=prop($$props,"attachments",19,()=>[]),className=prop($$props,"cla\ +ss",3,""),disabled=prop($$props,"disabled",3,!1),isLoading2=prop($$props,"isLoading",3,!1),placeholder=prop($$props,"placeholder",3,"Type a message..."),showMcpPromptButton=prop($$props,"showMcpPromptButton",3,!1),showAddButton=prop($$props,"showAddButton",3,!0),showModelSelector=prop($$props,"showModelSelector",3,!0),uploadedFiles=prop($$props,"uploadedFiles",31,()=>proxy([])),value=prop($$props,"value",15,""),audioRecorder,chatFormActionsRef=state$1(void 0),fileInputRef=state$1(void 0),pickersRef=state$1( +void 0),textareaRef=state$1(void 0),isRecording=state$1(!1),recordingSupported=state$1(!1),isPromptPickerOpen=state$1(!1),promptSearchQuery=state$1(""),isInlineResourcePickerOpen=state$1(!1),resourceSearchQuery=state$1(""),isResourceDialogOpen=state$1(!1),preSelectedResourceUri=state$1(void 0),currentConfig=user_derived(config$1),pasteLongTextToFileLength=user_derived(()=>{const n=Number(get$3(currentConfig).pasteLongTextToFileLen);return Number.isNaN(n)?Number(SETTING_CONFIG_DEFAULT.pasteLongTextToFileLen): +n}),isRouter=user_derived(isRouterMode),conversationModel=user_derived(()=>chatStore.getConversationModel(activeMessages())),activeModelId=user_derived(()=>{const options=modelOptions();if(!get$3(isRouter))return options.length>0?options[0].model:null;const selectedId=selectedModelId();if(selectedId){const model=options.find(m=>m.id===selectedId);if(model)return model.model}if(get$3(conversationModel)){const model=options.find(m=>m.model===get$3(conversationModel));if(model)return model.model}return null}), +hasModelSelected=user_derived(()=>!get$3(isRouter)||!!get$3(conversationModel)||!!selectedModelId()),hasLoadingAttachments=user_derived(()=>uploadedFiles().some(f=>f.isLoading)),hasAttachments=user_derived(()=>attachments()&&attachments().length>0||uploadedFiles()&&uploadedFiles().length>0),canSubmit=user_derived(()=>value().trim().length>0||get$3(hasAttachments));onMount$1(()=>{set$1(recordingSupported,isAudioRecordingSupported(),!0),audioRecorder=new AudioRecorder});function focus2(){get$3(textareaRef)?. +focus()}function resetTextareaHeight(){get$3(textareaRef)?.resetHeight()}function openModelSelector(){get$3(chatFormActionsRef)?.openModelSelector()}function checkModelSelected(){return get$3(hasModelSelected)?!0:(get$3(chatFormActionsRef)?.openModelSelector(),!1)}function handleFileSelect(files){$$props.onFilesAdd?.(files)}function handleFileUpload(){get$3(fileInputRef)?.click()}function handleFileRemove(fileId){if(fileId.startsWith("attachment-")){const index2=parseInt(fileId.replace("attachme\ +nt-",""),10);!isNaN(index2)&&index2>=0&&index2item.kind==="file").map(item=>item.getAsFile()).filter(file=>file!==null);if(files.length>0){event2.preventDefault(),$$props.onFilesAdd?.(files);return}const text2=event2.clipboardData.getData(MimeTypeText.PLAIN);if(text2.startsWith(CLIPBOARD_CONTENT_QUOTE_PREFIX)){const parsed=parseClipboardContent(text2);if(parsed.textAttachments.length>0||parsed.mcpPromptAttachments.length> +0){if(event2.preventDefault(),value(parsed.message),$$props.onValueChange?.(parsed.message),parsed.textAttachments.length>0){const attachmentFiles=parsed.textAttachments.map(att=>new File([att.content],att.name,{type:MimeTypeText.PLAIN}));$$props.onFilesAdd?.(attachmentFiles)}if(parsed.mcpPromptAttachments.length>0){const mcpPromptFiles=parsed.mcpPromptAttachments.map(att=>({id:uuid$1(),name:att.name,size:att.content.length,type:SpecialFileType.MCP_PROMPT,file:new File([att.content],`${att.name}${FileExtensionText. +TXT}`,{type:MimeTypeText.PLAIN}),isLoading:!1,textContent:att.content,mcpPrompt:{serverName:att.serverName,promptName:att.promptName,arguments:att.arguments}}));uploadedFiles([...uploadedFiles(),...mcpPromptFiles]),$$props.onUploadedFilesChange?.(uploadedFiles())}setTimeout(()=>{get$3(textareaRef)?.focus()},10);return}}if(text2.length>0&&get$3(pasteLongTextToFileLength)>0&&text2.length>get$3(pasteLongTextToFileLength)){event2.preventDefault();const textFile=new File([text2],"Pasted",{type:MimeTypeText. +PLAIN});$$props.onFilesAdd?.([textFile])}}function handlePromptLoadStart(placeholderId,promptInfo,args){value().startsWith(PROMPT_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1(isPromptPickerOpen,!1),set$1(promptSearchQuery,"");const promptName=promptInfo.title||promptInfo.name,placeholder2={id:placeholderId,name:promptName,size:INITIAL_FILE_SIZE,type:SpecialFileType.MCP_PROMPT,file:new File([],"loading"),isLoading:!0,mcpPrompt:{serverName:promptInfo.serverName,promptName:promptInfo. +name,arguments:args?{...args}:void 0}};uploadedFiles([...uploadedFiles(),placeholder2]),$$props.onUploadedFilesChange?.(uploadedFiles()),get$3(textareaRef)?.focus()}function handlePromptLoadComplete(placeholderId,result){const promptText=result.messages?.map(msg=>typeof msg.content=="string"?msg.content:msg.content.type===ContentPartType.TEXT?msg.content.text:"").filter(Boolean).join(PROMPT_CONTENT_SEPARATOR);uploadedFiles(uploadedFiles().map(f=>f.id===placeholderId?{...f,isLoading:!1,textContent:promptText, +size:promptText.length,file:new File([promptText],`${f.name}${FileExtensionText.TXT}`,{type:MimeTypeText.PLAIN})}:f)),$$props.onUploadedFilesChange?.(uploadedFiles())}function handlePromptLoadError(placeholderId,error2){uploadedFiles(uploadedFiles().map(f=>f.id===placeholderId?{...f,isLoading:!1,loadError:error2}:f)),$$props.onUploadedFilesChange?.(uploadedFiles())}function handlePromptPickerClose(){set$1(isPromptPickerOpen,!1),set$1(promptSearchQuery,""),get$3(textareaRef)?.focus()}function handleInlineResourcePickerClose(){ +set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),get$3(textareaRef)?.focus()}function handleInlineResourceSelect(){value().startsWith(RESOURCE_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),get$3(textareaRef)?.focus()}function handleBrowseResources(){set$1(isInlineResourcePickerOpen,!1),set$1(resourceSearchQuery,""),value().startsWith(RESOURCE_TRIGGER_PREFIX)&&(value(""),$$props.onValueChange?.("")),set$1( +isResourceDialogOpen,!0)}async function handleMicClick(){if(!audioRecorder||!get$3(recordingSupported)){console.warn("Audio recording not supported");return}if(get$3(isRecording)){set$1(isRecording,!1);try{const audioBlob=await audioRecorder.stopRecording(),wavBlob=await convertToWav(audioBlob),audioFile=createAudioFile(wavBlob);$$props.onFilesAdd?.([audioFile])}catch(error2){console.error("Failed to stop recording:",error2)}}else try{await audioRecorder.startRecording(),set$1(isRecording,!0)}catch(error2){ +console.error("Failed to start recording:",error2)}}var $$exports={focus:focus2,resetTextareaHeight,openModelSelector,checkModelSelected},fragment=root$1u(),node2=first_child(fragment);bind_this(ChatFormFileInputInvisible(node2,{onFileSelect:handleFileSelect}),$$value=>set$1(fileInputRef,$$value,!0),()=>get$3(fileInputRef));var form=sibling(node2,2),node_1=child(form);bind_this(ChatFormPickers(node_1,{get isPromptPickerOpen(){return get$3(isPromptPickerOpen)},get promptSearchQuery(){return get$3( +promptSearchQuery)},get isInlineResourcePickerOpen(){return get$3(isInlineResourcePickerOpen)},get resourceSearchQuery(){return get$3(resourceSearchQuery)},onPromptPickerClose:handlePromptPickerClose,onInlineResourcePickerClose:handleInlineResourcePickerClose,onInlineResourceSelect:handleInlineResourceSelect,onPromptLoadStart:handlePromptLoadStart,onPromptLoadComplete:handlePromptLoadComplete,onPromptLoadError:handlePromptLoadError,onInlineResourceBrowse:handleBrowseResources}),$$value=>set$1(pickersRef, +$$value,!0),()=>get$3(pickersRef));var div=sibling(node_1,2),node_2=child(div);{let $0=user_derived(()=>get$3(activeModelId)??void 0);ChatAttachmentsList(node_2,{get attachments(){return attachments()},onFileRemove:handleFileRemove,limitToSingleRow:!0,class:"py-5",style:"scroll-padding: 1rem;",get activeModelId(){return get$3($0)},get uploadedFiles(){return uploadedFiles()},set uploadedFiles($$value){uploadedFiles($$value)}})}var div_1=sibling(node_2,2),node_3=child(div_1);bind_this(ChatFormTextarea( +node_3,{class:"px-5 py-1.5 md:pt-0",onKeydown:handleKeydown,onInput:()=>{handleInput(),$$props.onValueChange?.(value())},get disabled(){return disabled()},get placeholder(){return placeholder()},get value(){return value()},set value($$value){value($$value)}}),$$value=>set$1(textareaRef,$$value,!0),()=>get$3(textareaRef));var node_4=sibling(node_3,2);{var consequent=$$anchor2=>{ChatFormMcpResourcesList($$anchor2,{class:"mb-3",onResourceClick:uri2=>{set$1(preSelectedResourceUri,uri2,!0),set$1(isResourceDialogOpen, +!0)}})},d2=user_derived(()=>mcpHasResourceAttachments());if_block(node_4,$$render=>{get$3(d2)&&$$render(consequent)})}var node_5=sibling(node_4,2);{let $0=user_derived(()=>showMcpPromptButton()?()=>set$1(isPromptPickerOpen,!0):void 0);bind_this(ChatFormActions(node_5,{class:"px-3",get canSend(){return get$3(canSubmit)},get disabled(){return disabled()},get isLoading(){return isLoading2()},get isRecording(){return get$3(isRecording)},get showAddButton(){return showAddButton()},get showModelSelector(){ +return showModelSelector()},get uploadedFiles(){return uploadedFiles()},onFileUpload:handleFileUpload,onMicClick:handleMicClick,get onStop(){return $$props.onStop},onSystemPromptClick:()=>$$props.onSystemPromptClick?.({message:value(),files:uploadedFiles()}),get onMcpPromptClick(){return get$3($0)},onMcpResourcesClick:()=>set$1(isResourceDialogOpen,!0)}),$$value=>set$1(chatFormActionsRef,$$value,!0),()=>get$3(chatFormActionsRef))}reset(div_1),reset(div),reset(form);var node_6=sibling(form,2);return DialogMcpResourcesBrowser( +node_6,{get preSelectedUri(){return get$3(preSelectedResourceUri)},onAttach:resource=>{mcpStore.attachResource(resource.uri)},onOpenChange:newOpen=>{newOpen||set$1(preSelectedResourceUri,void 0)},get open(){return get$3(isResourceDialogOpen)},set open($$value){set$1(isResourceDialogOpen,$$value,!0)}}),template_effect(()=>{set_class(form,1,`relative ${className()??""}`),set_class(div,1,`${INPUT_CLASSES??""} overflow-hidden rounded-3xl backdrop-blur-md ${disabled()?"cursor-not-allowed opacity-60": +""}`)}),event("submit",form,event2=>{event2.preventDefault(),!(!get$3(canSubmit)||disabled()||get$3(hasLoadingAttachments))&&$$props.onSubmit?.()}),event("paste",div_1,handlePaste),append($$anchor,fragment),pop($$exports)}function Dropdown_menu_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,4),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","sideOffset","portalProps","class"]);var fragment=comment$2(),node2=first_child( +fragment);component(node2,()=>Portal$2,($$anchor2,DropdownMenuPrimitive_Portal)=>{DropdownMenuPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-po\ +pover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node_1,()=>Dropdown_menu_content$1, +($$anchor4,DropdownMenuPrimitive_Content)=>{DropdownMenuPrimitive_Content($$anchor4,spread_props({"data-slot":"dropdown-menu-content",get sideOffset(){return sideOffset()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_item($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),variant=prop($$props,"variant", +3,"default"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","variant"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[var\ +iant=destructive]:data-highlighted:bg-destructive/10 data-[variant=destructive]:data-highlighted:text-destructive dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:!text-destructive",$$props.class));component(node2,()=>Menu_item,($$anchor2,DropdownMenuPrimitive_Item)=>{DropdownMenuPrimitive_Item($$anchor2,spread_props( +{"data-slot":"dropdown-menu-item",get"data-inset"(){return $$props.inset},get"data-variant"(){return variant()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_separator($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived( +()=>cn$1("-mx-1 my-1 h-px bg-border/20",$$props.class));component(node2,()=>Menu_separator,($$anchor2,DropdownMenuPrimitive_Separator)=>{DropdownMenuPrimitive_Separator($$anchor2,spread_props({"data-slot":"dropdown-menu-separator",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Dropdown_menu_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props, +["$$slots","$$events","$$legacy","ref"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Menu_trigger,($$anchor2,DropdownMenuPrimitive_Trigger)=>{DropdownMenuPrimitive_Trigger($$anchor2,spread_props({"data-slot":"dropdown-menu-trigger"},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))}),append($$anchor,fragment),pop()}function Dropdown_menu_sub_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props( +$$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-\ +in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20",$$props.class));component(node2,()=>Menu_sub_content,($$anchor2,DropdownMenuPrimitive_SubContent)=>{DropdownMenuPrimitive_SubContent($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-content",get class(){ +return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}var root_1$P=from_html(" ",1);function Dropdown_menu_sub_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","inset","children"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("flex cursor-default items-center gap-2 rounded\ +-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",$$props.class));component(node2,()=>Menu_sub_trigger,($$anchor2,DropdownMenuPrimitive_SubTrigger)=>{ +DropdownMenuPrimitive_SubTrigger($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-trigger",get"data-inset"(){return $$props.inset},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$P(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_right(node_2,{class:"ml-auto size-4"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})} +append($$anchor,fragment),pop()}const Sub=Menu_sub,Root$4=Menu;function useAttachmentMenu(getFlags,getCallbacks,close2){const modalityFlags=user_derived(getFlags),callbacks=user_derived(()=>{const cbs=getCallbacks(),wrap2=fn=>()=>{close2(),fn?.()};return{[AttachmentAction.FILE_UPLOAD]:wrap2(cbs.onFileUpload),[AttachmentAction.SYSTEM_PROMPT_CLICK]:wrap2(cbs.onSystemPromptClick),[AttachmentAction.MCP_PROMPT_CLICK]:wrap2(cbs.onMcpPromptClick),[AttachmentAction.MCP_RESOURCES_CLICK]:wrap2(cbs.onMcpResourcesClick)}}); +function isItemEnabled(enabledWhen){return!enabledWhen||enabledWhen==="always"?!0:!!get$3(modalityFlags)[enabledWhen]}function isItemVisible(visibleWhen){return visibleWhen?!!get$3(modalityFlags)[visibleWhen]:!0}function getSystemMessageTooltip(){return page$1.params.id?"Inject custom system message at the beginning of the conversation":"Add custom system message for a new conversation"}return{get callbacks(){return get$3(callbacks)},isItemEnabled,isItemVisible,getSystemMessageTooltip}}var root_6$s=from_html( +" ",1),root_10$f=from_html(" ",1),root_11$e=from_html("

"),root_8$r=from_html(" ",1),root_16$6=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$d=from_html(" ",1),root_22$2=from_html(" ",1),root_23$5=from_html("

"),root_20$5=from_html(" ",1),root_26$2=from_html(" ",1),root_3$S=from_html(" \ + ",1),root_1$O=from_html(" ",1),root$1t=from_html("
");function ChatFormActionAddDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),dropdownOpen=state$1(!1); +function handleMcpSettingsClick(){set$1(dropdownOpen,!1),$$props.onMcpSettingsClick?.()}const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(dropdownOpen, +!1)});var div=root$1t(),node2=child(div);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$O(),node_1=first_child(fragment);component(node_1,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor4,{name:"Attach files",get disabled(){return disabled()},children:($$anchor5,$$slotProps2)=>{ +var fragment_1=comment$2(),node_2=first_child(fragment_1);snippet(node_2,()=>$$props.trigger,()=>({disabled:disabled()})),append($$anchor5,fragment_1)},$$slots:{default:!0}})});var node_3=sibling(node_1,2);component(node_3,()=>Dropdown_menu_content,($$anchor4,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor4,{align:"start",class:"w-48",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_3$S(),node_4=first_child(fragment_2);each(node_4,17,()=>ATTACHMENT_FILE_ITEMS,item=>item.id,($$anchor6,item)=>{ +const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledWhen));var fragment_3=comment$2(),node_5=first_child(fragment_3);{var consequent=$$anchor7=>{var fragment_4=comment$2(),node_6=first_child(fragment_4);{let $0=user_derived(()=>get$3(item).class??"");component(node_6,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor8,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},onclick:()=>attachmentMenu.callbacks[get$3(item). +action](),children:($$anchor9,$$slotProps3)=>{var fragment_5=root_6$s(),node_7=first_child(fragment_5);component(node_7,()=>get$3(item).icon,($$anchor10,item_icon)=>{item_icon($$anchor10,{class:"h-4 w-4"})});var span=sibling(node_7,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,get$3(item).label)),append($$anchor9,fragment_5)},$$slots:{default:!0}})})}append($$anchor7,fragment_4)},consequent_1=$$anchor7=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);component( +node_8,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_7=root_8$r(),node_9=first_child(fragment_7);component(node_9,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);{let $0=user_derived(()=>get$3(item).class??"");component(node_10,()=>Dropdown_menu_item, +($$anchor12,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor12,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},disabled:!0,children:($$anchor13,$$slotProps5)=>{var fragment_9=root_10$f(),node_11=first_child(fragment_9);component(node_11,()=>get$3(item).icon,($$anchor14,item_icon_1)=>{item_icon_1($$anchor14,{class:"h-4 w-4"})});var span_1=sibling(node_11,2),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,get$3(item).label)),append($$anchor13, +fragment_9)},$$slots:{default:!0}})})}append($$anchor11,fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7, +fragment_6)};if_block(node_5,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_4,2);{var consequent_3=$$anchor6=>{var fragment_10=comment$2(),node_14=first_child(fragment_10);component(node_14,()=>Root$5,($$anchor7,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor7,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor8,$$slotProps3)=>{var fragment_11=root_13$d(),node_15=first_child( +fragment_11);component(node_15,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor9,{class:"w-full",children:($$anchor10,$$slotProps4)=>{var fragment_12=comment$2(),node_16=first_child(fragment_12);component(node_16,()=>Dropdown_menu_item,($$anchor11,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor11,{class:"flex cursor-pointer items-center gap-2",get onclick(){return attachmentMenu.callbacks.onFileUpload},children:($$anchor12,$$slotProps5)=>{const pdfItem=user_derived( +()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_13=comment$2(),node_17=first_child(fragment_13);{var consequent_2=$$anchor13=>{var fragment_14=root_16$6(),node_18=first_child(fragment_14);component(node_18,()=>get$3(pdfItem).icon,($$anchor14,pdfItem_icon)=>{pdfItem_icon($$anchor14,{class:"h-4 w-4"})});var span_2=sibling(node_18,2),text_3=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_3,get$3(pdfItem).label)),append($$anchor13,fragment_14)};if_block( +node_17,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append($$anchor12,fragment_13)},$$slots:{default:!0}})}),append($$anchor10,fragment_12)},$$slots:{default:!0}})});var node_19=sibling(node_15,2);component(node_19,()=>Tooltip_content,($$anchor9,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor9,{side:"right",children:($$anchor10,$$slotProps4)=>{var p_1=root_17$8();append($$anchor10,p_1)},$$slots:{default:!0}})}),append($$anchor8,fragment_11)},$$slots:{default:!0}})}),append($$anchor6,fragment_10)}, +d2=user_derived(()=>!attachmentMenu.isItemEnabled("hasVisionModality"));if_block(node_13,$$render=>{get$3(d2)&&$$render(consequent_3)})}var node_20=sibling(node_13,2);component(node_20,()=>Dropdown_menu_separator,($$anchor6,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor6,{})});var node_21=sibling(node_20,2);each(node_21,17,()=>ATTACHMENT_EXTRA_ITEMS,item=>item.id,($$anchor6,item)=>{var fragment_15=comment$2(),node_22=first_child(fragment_15);{var consequent_4=$$anchor7=>{var fragment_16=comment$2(), +node_23=first_child(fragment_16);component(node_23,()=>Root$5,($$anchor8,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_17=root_20$5(),node_24=first_child(fragment_17);component(node_24,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_18=comment$2(),node_25=first_child(fragment_18);component(node_25,()=>Dropdown_menu_item, +($$anchor12,DropdownMenu_Item_3)=>{DropdownMenu_Item_3($$anchor12,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor13,$$slotProps5)=>{var fragment_19=root_22$2(),node_26=first_child(fragment_19);component(node_26,()=>get$3(item).icon,($$anchor14,item_icon_2)=>{item_icon_2($$anchor14,{class:"h-4 w-4"})});var span_3=sibling(node_26,2),text_4=child(span_3,!0);reset(span_3),template_effect(()=>set_text(text_4,get$3(item).label)), +append($$anchor13,fragment_19)},$$slots:{default:!0}})}),append($$anchor11,fragment_18)},$$slots:{default:!0}})});var node_27=sibling(node_24,2);component(node_27,()=>Tooltip_content,($$anchor10,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_2=root_23$5(),text_5=child(p_2,!0);reset(p_2),template_effect($0=>set_text(text_5,$0),[()=>attachmentMenu.getSystemMessageTooltip()]),append($$anchor11,p_2)},$$slots:{default:!0}})}),append($$anchor9, +fragment_17)},$$slots:{default:!0}})}),append($$anchor7,fragment_16)};if_block(node_22,$$render=>{get$3(item).id===AttachmentMenuItemId.SYSTEM_MESSAGE&&$$render(consequent_4)})}append($$anchor6,fragment_15)});var node_28=sibling(node_21,2);ChatFormActionAddToolsSubmenu(node_28,{});var node_29=sibling(node_28,2);ChatFormActionAddMcpServersSubmenu(node_29,{onMcpSettingsClick:handleMcpSettingsClick});var node_30=sibling(node_29,2);each(node_30,17,()=>ATTACHMENT_MCP_ITEMS,item=>item.id,($$anchor6,item)=>{ +var fragment_20=comment$2(),node_31=first_child(fragment_20);{var consequent_5=$$anchor7=>{var fragment_21=comment$2(),node_32=first_child(fragment_21);component(node_32,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_4)=>{DropdownMenu_Item_4($$anchor8,{class:"flex cursor-pointer items-center gap-2",onclick:()=>attachmentMenu.callbacks[get$3(item).action](),children:($$anchor9,$$slotProps3)=>{var fragment_22=root_26$2(),node_33=first_child(fragment_22);component(node_33,()=>get$3(item).icon, +($$anchor10,item_icon_3)=>{item_icon_3($$anchor10,{class:"h-4 w-4"})});var span_4=sibling(node_33,2),text_6=child(span_4,!0);reset(span_4),template_effect(()=>set_text(text_6,get$3(item).label)),append($$anchor9,fragment_22)},$$slots:{default:!0}})}),append($$anchor7,fragment_21)},d_12=user_derived(()=>attachmentMenu.isItemVisible(get$3(item).visibleWhen));if_block(node_31,$$render=>{get$3(d_12)&&$$render(consequent_5)})}append($$anchor6,fragment_20)}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}), +append($$anchor3,fragment)},$$slots:{default:!0}})}),reset(div),template_effect(()=>set_class(div,1,`flex items-center gap-1 ${className()??""}`)),append($$anchor,div),pop()}function Sheet_overlay($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data\ +-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0",$$props.class));component(node2,()=>Dialog_overlay$1,($$anchor2,SheetPrimitive_Overlay)=>{SheetPrimitive_Overlay($$anchor2,spread_props({"data-slot":"sheet-overlay",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const sheetVariants=tv({base:`border-border/30 dark:border-border/20 da\ +ta-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fixed z-50 flex flex-col gap-4 shadow-sm transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 ${PANEL_CLASSES}`,variants:{side:{top:"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",bottom:"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",left:"\ +data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",right:"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"}},defaultVariants:{side:"right"}});var root_3$R=from_html(' Close',1),root_2$12=from_html(" ",1),root_1$N=from_html(" ",1);function Sheet_content($$anchor,$$props){push$1($$props,!0);let ref2=prop( +$$props,"ref",15,null),side=prop($$props,"side",3,"right"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","side","portalProps","children"]);var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SheetPrimitive_Portal)=>{SheetPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps,{children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$N(),node_1=first_child(fragment_1);Sheet_overlay(node_1,{});var node_2=sibling(node_1, +2);{let $0=user_derived(()=>cn$1(sheetVariants({side:side()}),$$props.class));component(node_2,()=>Dialog_content$1,($$anchor4,SheetPrimitive_Content)=>{SheetPrimitive_Content($$anchor4,spread_props({"data-slot":"sheet-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$12(),node_3=first_child(fragment_2);snippet(node_3,()=>$$props.children??noop$3);var node_4=sibling(node_3,2);component( +node_4,()=>Dialog_close,($$anchor6,SheetPrimitive_Close)=>{SheetPrimitive_Close($$anchor6,{class:"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none",children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$R(),node_5=first_child(fragment_3);X(node_5,{class:"size-4"}),next$1(2),append($$anchor7,fragment_3)},$$slots:{ +default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment),pop()}var root$1s=from_html("
");function Sheet_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$1s();attribute_effect(div,$0=>({"data-slot":"sheet-header",class:$0,...restProps}),[()=>cn$1("flex fle\ +x-col gap-1.5 p-4",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Sheet_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("font-semibold text-foreground",$$props.class));component( +node2,()=>Dialog_title$1,($$anchor2,SheetPrimitive_Title)=>{SheetPrimitive_Title($$anchor2,spread_props({"data-slot":"sheet-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Sheet_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment); +{let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,SheetPrimitive_Description)=>{SheetPrimitive_Description($$anchor2,spread_props({"data-slot":"sheet-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$3=Dialog;var root_3$Q=from_html(" ",1),root_7$r=from_html(''),root_11$d=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(''),root_16$5=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$5=from_html(" ",1),root_20$4=from_html(''),root_21$3=from_html("

"),root_19$9=from_html(" ",1),root_23$4=from_html(''),root_2$11=from_html(' ',1),root_1$M=from_html(" ",1),root$1r=from_html("
");function ChatFormActionAddSheet($$anchor,$$props){ push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),sheetOpen=state$1(!1);const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(), hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(sheetOpen,!1)}),sheetItemClass="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allowed disabled:opacity-50";var div=root$1r(),node2=child(div);component(node2,()=>Root$3, ($$anchor2,Sheet_Root)=>{Sheet_Root($$anchor2,{get open(){return get$3(sheetOpen)},set open($$value){set$1(sheetOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$M(),node_1=first_child(fragment);snippet(node_1,()=>$$props.trigger,()=>({disabled:disabled(),onclick:()=>set$1(sheetOpen,!0)}));var node_2=sibling(node_1,2);component(node_2,()=>Sheet_content,($$anchor4,Sheet_Content)=>{Sheet_Content($$anchor4,{side:"bottom",class:"max-h-[85vh] gap-0 overflow-y-auto",children:($$anchor5,$$slotProps2)=>{ @@ -6938,8 +6939,8 @@ cache2,percent=Math.round(actualProcessed/actualTotal*100),eta=getETASecs(actual 0&&time_ms>0){const tokensPerSecond=actualProcessed/(time_ms/1e3);return{tokensProcessed:actualProcessed,totalTokens:actualTotal,timeMs:time_ms,tokensPerSecond}}}return get$3(lastKnownProcessingStats)}function getLiveGenerationStats(){if(!get$3(processingState))return null;const{tokensDecoded,tokensPerSecond}=get$3(processingState);if(tokensDecoded<=0)return null;const timeMs=tokensPerSecond&&tokensPerSecond>0?tokensDecoded/tokensPerSecond*1e3:0;return{tokensGenerated:tokensDecoded,timeMs,tokensPerSecond:tokensPerSecond|| 0}}return{get processingState(){return get$3(processingState)},getProcessingDetails,getTechnicalDetails,getProcessingMessage,getPromptProgressText,getLiveProcessingStats,getLiveGenerationStats,shouldShowDetails,startMonitoring,stopMonitoring}}var root_1$y=from_html('
'),root_4$s=from_html('
 
'),root_6$l=from_html( '
'),root_7$j=from_html('
'),root_8$l=from_html('
'),root$15=from_html('
');function ChatMessageAssistant($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),isLastAssistantMessage=prop($$props,"isLastAssistantMessage",3,!1),toolMessages=prop($$props,"toolMessages",19,()=>[]),siblingInfo=prop($$props,"siblingInfo",3,null);const editCtx=getMessageEditContext(),isAgentic=user_derived(()=>hasAgenticContent($$props.message,toolMessages())),hasReasoning=user_derived(()=>!!$$props.message.reasoningContent),processingState=useProcessingState(); -let currentConfig=user_derived(config$1),isRouter=user_derived(isRouterMode),showRawOutput=state$1(!1),rawOutputContent=user_derived(()=>{const sections=deriveAgenticSections($$props.message,toolMessages(),[],!1),parts=[];for(const section of sections)switch(section.type){case AgenticSectionType.REASONING:case AgenticSectionType.REASONING_PENDING:parts.push(`${REASONING_TAGS.START} +div>');function ChatMessageAssistant($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),isLastAssistantMessage=prop($$props,"isLastAssistantMessage",3,!1),toolMessages=prop($$props,"toolMessages",19,()=>[]),siblingInfo=prop($$props,"siblingInfo",3,null);const editCtx=getMessageEditContext(),isAgentic=user_derived(()=>hasAgenticContent($$props.message,toolMessages())),processingState=useProcessingState();let currentConfig=user_derived(config$1),isRouter=user_derived(isRouterMode), +showRawOutput=state$1(!1),rawOutputContent=user_derived(()=>{const sections=deriveAgenticSections($$props.message,toolMessages(),[],!1),parts=[];for(const section of sections)switch(section.type){case AgenticSectionType.REASONING:case AgenticSectionType.REASONING_PENDING:parts.push(`${REASONING_TAGS.START} ${section.content} ${REASONING_TAGS.END}`);break;case AgenticSectionType.TEXT:parts.push(section.content);break;case AgenticSectionType.TOOL_CALL:case AgenticSectionType.TOOL_CALL_PENDING:case AgenticSectionType.TOOL_CALL_STREAMING:{const callObj={name:section.toolName};if(section.toolArgs)try{callObj.arguments=JSON.parse(section.toolArgs)}catch{callObj.arguments=section.toolArgs}parts.push(JSON.stringify(callObj,null,2)),section.toolResult&&parts.push(`[Tool Result] ${section.toolResult}`);break}}return parts.join(` @@ -6959,24 +6960,24 @@ isRouter)?$$render(consequent_5):$$render(alternate_2,-1)})}var node_6=sibling(n promptProgress=user_derived(()=>processingState.processingState?.promptProgress),isStillProcessingPrompt=user_derived(()=>get$3(promptProgress)&&get$3(promptProgress).processed{{let $0=user_derived(()=>!!get$3(isStillProcessingPrompt)),$1=user_derived(()=>get$3(liveStats)?.tokensProcessed),$2=user_derived(()=>get$3(liveStats)?.timeMs),$3=user_derived(()=>get$3(genStats)?.tokensGenerated), $4=user_derived(()=>get$3(genStats)?.timeMs);ChatMessageStatistics($$anchor4,{isLive:!0,get isProcessingPrompt(){return get$3($0)},get promptTokens(){return get$3($1)},get promptMs(){return get$3($2)},get predictedTokens(){return get$3($3)},get predictedMs(){return get$3($4)}})}};if_block(node_7,$$render=>{(get$3(liveStats)||get$3(genStats))&&$$render(consequent_7)})}append($$anchor3,fragment_6)},d2=user_derived(()=>isLoading()&&get$3(currentConfig).showMessageStats);if_block(node_6,$$render=>{get$3( currentConfig).showMessageStats&&$$props.message.timings&&$$props.message.timings.predicted_n&&$$props.message.timings.predicted_ms?$$render(consequent_6):get$3(d2)&&$$render(consequent_8,1)})}reset(div_7),bind_this(div_7,$$value=>set$1(statsContainerEl,$$value),()=>get$3(statsContainerEl)),append($$anchor2,div_7)};if_block(node_4,$$render=>{get$3(displayedModel)&&$$render(consequent_9)})}reset(div_6);var node_8=sibling(div_6,2);{var consequent_10=$$anchor2=>{{let $0=user_derived(()=>get$3(currentConfig). -enableContinueGeneration&&!get$3(hasReasoning)?$$props.onContinue:void 0);ChatMessageActionIcons($$anchor2,{get role(){return MessageRole.ASSISTANT},justify:"start",actionsPosition:"left",get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props.showDeleteDialog},get deletionInfo(){return $$props.deletionInfo},get onCopy(){return $$props.onCopy},get onEdit(){return $$props.onEdit},get onRegenerate(){return $$props.onRegenerate},get onContinue(){return get$3($0)},get onForkConversation(){ -return $$props.onForkConversation},get onDelete(){return $$props.onDelete},get onConfirmDelete(){return $$props.onConfirmDelete},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get showRawOutputSwitch(){return get$3(currentConfig).showRawOutputSwitch},get rawOutputEnabled(){return get$3(showRawOutput)},onRawOutputToggle:enabled=>set$1(showRawOutput,enabled,!0)})}};if_block(node_8,$$render=>{$$props.message.timestamp&& -!editCtx.isEditing&&$$render(consequent_10)})}reset(div),template_effect(()=>set_class(div,1,`text-md group w-full leading-7.5 ${className()??""}`,"svelte-3902tz")),append($$anchor,div),pop()}var root_1$x=from_html('
'),root_2$O=from_html('
'),root_3$G=from_html("
"),root_4$r=from_html(" Cancel",1),root$14=from_html('
',1);function ChatMessageEditForm($$anchor,$$props){push$1($$props,!0);const editCtx=getMessageEditContext();let saveWithoutRegenerate=state$1(!1),showDiscardDialog=state$1(!1),branchAfterEdit=state$1( -!1),isUserMessage=user_derived(()=>editCtx.messageRole===MessageRole.USER),isAssistantMessage=user_derived(()=>editCtx.messageRole===MessageRole.ASSISTANT),hasUnsavedChanges=user_derived(()=>!!(editCtx.editedContent!==editCtx.originalContent||editCtx.editedUploadedFiles.length>0||editCtx.editedExtras.length!==editCtx.originalExtras.length||editCtx.editedExtras.some((extra,i)=>extra!==editCtx.originalExtras[i]))),hasAttachments=user_derived(()=>editCtx.editedExtras&&editCtx.editedExtras.length>0|| -editCtx.editedUploadedFiles&&editCtx.editedUploadedFiles.length>0),canSubmit=user_derived(()=>editCtx.editedContent.trim().length>0||get$3(hasAttachments));function handleGlobalKeydown(event2){event2.key===KeyboardKey.ESCAPE&&(event2.preventDefault(),attemptCancel())}function attemptCancel(){get$3(hasUnsavedChanges)?set$1(showDiscardDialog,!0):editCtx.cancel()}function handleSubmit(){get$3(canSubmit)&&(get$3(isUserMessage)&&get$3(saveWithoutRegenerate)&&editCtx.showSaveOnlyOption?editCtx.saveOnly(): -(get$3(isAssistantMessage)&&editCtx.setShouldBranchAfterEdit&&editCtx.setShouldBranchAfterEdit(get$3(branchAfterEdit)),editCtx.save()),set$1(saveWithoutRegenerate,!1),set$1(branchAfterEdit,!1))}function handleAttachmentRemove(index2){const newExtras=[...editCtx.editedExtras];newExtras.splice(index2,1),editCtx.setExtras(newExtras)}function handleUploadedFileRemove(fileId){const newFiles=editCtx.editedUploadedFiles.filter(f=>f.id!==fileId);editCtx.setUploadedFiles(newFiles)}async function handleFilesAdd(files){ -const processed=await processFilesToChatUploaded(files);editCtx.setUploadedFiles([...editCtx.editedUploadedFiles,...processed])}user_effect(()=>(chatStore.setEditModeActive(handleFilesAdd),()=>{chatStore.clearEditMode()}));var fragment=root$14();event("keydown",$window,handleGlobalKeydown);var div=first_child(fragment),node2=child(div);{let $0=user_derived(()=>editCtx.messageRole===MessageRole.USER),$1=user_derived(()=>editCtx.messageRole===MessageRole.USER);ChatForm(node2,{get value(){return editCtx. -editedContent},get attachments(){return editCtx.editedExtras},placeholder:"Edit your message...",showMcpPromptButton:!0,get showAddButton(){return get$3($0)},get showModelSelector(){return get$3($1)},get onValueChange(){return editCtx.setContent},onAttachmentRemove:handleAttachmentRemove,onUploadedFileRemove:handleUploadedFileRemove,onFilesAdd:handleFilesAdd,onSubmit:handleSubmit,get uploadedFiles(){return editCtx.editedUploadedFiles},set uploadedFiles($$value){editCtx.editedUploadedFiles=$$value}})} -reset(div);var div_1=sibling(div,2),node_1=child(div_1);{var consequent=$$anchor2=>{var div_2=root_1$x(),node_2=child(div_2);Switch(node_2,{id:"save-only-switch",class:"scale-75",get checked(){return get$3(saveWithoutRegenerate)},set checked($$value){set$1(saveWithoutRegenerate,$$value,!0)}}),next$1(2),reset(div_2),append($$anchor2,div_2)},consequent_1=$$anchor2=>{var div_3=root_2$O(),node_3=child(div_3);Switch(node_3,{id:"branch-after-edit",class:"scale-75",get checked(){return get$3(branchAfterEdit)}, -set checked($$value){set$1(branchAfterEdit,$$value,!0)}}),next$1(2),reset(div_3),append($$anchor2,div_3)},alternate=$$anchor2=>{var div_4=root_3$G();append($$anchor2,div_4)};if_block(node_1,$$render=>{get$3(isUserMessage)&&editCtx.showSaveOnlyOption?$$render(consequent):get$3(isAssistantMessage)?$$render(consequent_1,1):$$render(alternate,-1)})}var node_4=sibling(node_1,2);Button(node_4,{class:"h-7 px-3 text-xs",onclick:attemptCancel,size:"sm",variant:"ghost",children:($$anchor2,$$slotProps)=>{var fragment_1=root_4$r(), -node_5=first_child(fragment_1);X(node_5,{class:"mr-1 h-3 w-3"}),next$1(),append($$anchor2,fragment_1)},$$slots:{default:!0}}),reset(div_1);var node_6=sibling(div_1,2);DialogConfirmation(node_6,{title:"Discard changes?",description:"You have unsaved changes. Are you sure you want to discard them?",confirmText:"Discard",cancelText:"Keep editing",variant:"destructive",get icon(){return Triangle_alert},get onConfirm(){return editCtx.cancel},onCancel:()=>set$1(showDiscardDialog,!1),get open(){return get$3( -showDiscardDialog)},set open($$value){set$1(showDiscardDialog,$$value,!0)}}),append($$anchor,fragment),pop()}var root_3$F=from_html('
'),root_2$N=from_html(" ",1),root$13=from_html('
');function ChatMessageUser($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),siblingInfo=prop($$props,"siblingInfo",3,null);const editCtx=getMessageEditContext();var div=root$13(),node2=child( -div);{var consequent=$$anchor2=>{ChatMessageEditForm($$anchor2,{})},alternate=$$anchor2=>{var fragment_1=root_2$N(),node_1=first_child(fragment_1);ChatMessageUserBubble(node_1,{get content(){return $$props.message.content},get attachments(){return $$props.message.extra},renderMarkdown:!0});var node_2=sibling(node_1,2);{var consequent_1=$$anchor3=>{var div_1=root_3$F(),node_3=child(div_1);ChatMessageActionIcons(node_3,{actionsPosition:"right",get deletionInfo(){return $$props.deletionInfo},justify:"\ -end",get onConfirmDelete(){return $$props.onConfirmDelete},get onCopy(){return $$props.onCopy},get onDelete(){return $$props.onDelete},get onEdit(){return $$props.onEdit},get onForkConversation(){return $$props.onForkConversation},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props.showDeleteDialog},get role(){return MessageRole.USER}}),reset( -div_1),append($$anchor3,div_1)};if_block(node_2,$$render=>{$$props.message.timestamp&&$$render(consequent_1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{editCtx.isEditing?$$render(consequent):$$render(alternate,-1)})}reset(div),template_effect(()=>set_class(div,1,`group flex flex-col items-end gap-3 md:gap-2 ${className()??""}`)),append($$anchor,div),pop()}var root_1$w=from_html('
'),root_4$q=from_html("
"),root_5$k=from_html(' '),root$12=from_html(" ",1);function ChatMessageUserBubble($$anchor,$$props){push$1($$props,!0);let attachments=prop($$props,"attachments",19,()=>[]),renderMarkdown=prop($$props,"renderMarkdown",3,!1),textColorClass=prop($$props,"textColorClass",3,"text-foreground"),cardBgClass=prop($$props,"cardBgClass",3,"dark:bg-primary/15"),maxHeightStyle=prop($$props,"maxHeightStyle",3,"max-height: var(--max-message-height);"),isMultiline=state$1(!1),messageElement=state$1( -void 0);const currentConfig=config$1();user_effect(()=>{if(!get$3(messageElement)||!$$props.content.trim())return;if($$props.content.includes(` +enableContinueGeneration?$$props.onContinue:void 0);ChatMessageActionIcons($$anchor2,{get role(){return MessageRole.ASSISTANT},justify:"start",actionsPosition:"left",get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props.showDeleteDialog},get deletionInfo(){return $$props.deletionInfo},get onCopy(){return $$props.onCopy},get onEdit(){return $$props.onEdit},get onRegenerate(){return $$props.onRegenerate},get onContinue(){return get$3($0)},get onForkConversation(){return $$props. +onForkConversation},get onDelete(){return $$props.onDelete},get onConfirmDelete(){return $$props.onConfirmDelete},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get showRawOutputSwitch(){return get$3(currentConfig).showRawOutputSwitch},get rawOutputEnabled(){return get$3(showRawOutput)},onRawOutputToggle:enabled=>set$1(showRawOutput,enabled,!0)})}};if_block(node_8,$$render=>{$$props.message.timestamp&&!editCtx. +isEditing&&$$render(consequent_10)})}reset(div),template_effect(()=>set_class(div,1,`text-md group w-full leading-7.5 ${className()??""}`,"svelte-3902tz")),append($$anchor,div),pop()}var root_1$x=from_html('
'),root_2$O=from_html('
'),root_3$G=from_html("
"),root_4$r=from_html(" Cancel",1),root$14=from_html('
',1);function ChatMessageEditForm($$anchor,$$props){push$1($$props,!0);const editCtx=getMessageEditContext();let saveWithoutRegenerate=state$1(!1),showDiscardDialog=state$1(!1),branchAfterEdit=state$1(!1),isUserMessage=user_derived( +()=>editCtx.messageRole===MessageRole.USER),isAssistantMessage=user_derived(()=>editCtx.messageRole===MessageRole.ASSISTANT),hasUnsavedChanges=user_derived(()=>!!(editCtx.editedContent!==editCtx.originalContent||editCtx.editedUploadedFiles.length>0||editCtx.editedExtras.length!==editCtx.originalExtras.length||editCtx.editedExtras.some((extra,i)=>extra!==editCtx.originalExtras[i]))),hasAttachments=user_derived(()=>editCtx.editedExtras&&editCtx.editedExtras.length>0||editCtx.editedUploadedFiles&&editCtx. +editedUploadedFiles.length>0),canSubmit=user_derived(()=>editCtx.editedContent.trim().length>0||get$3(hasAttachments));function handleGlobalKeydown(event2){event2.key===KeyboardKey.ESCAPE&&(event2.preventDefault(),attemptCancel())}function attemptCancel(){get$3(hasUnsavedChanges)?set$1(showDiscardDialog,!0):editCtx.cancel()}function handleSubmit(){get$3(canSubmit)&&(get$3(isUserMessage)&&get$3(saveWithoutRegenerate)&&editCtx.showSaveOnlyOption?editCtx.saveOnly():(get$3(isAssistantMessage)&&editCtx. +setShouldBranchAfterEdit&&editCtx.setShouldBranchAfterEdit(get$3(branchAfterEdit)),editCtx.save()),set$1(saveWithoutRegenerate,!1),set$1(branchAfterEdit,!1))}function handleAttachmentRemove(index2){const newExtras=[...editCtx.editedExtras];newExtras.splice(index2,1),editCtx.setExtras(newExtras)}function handleUploadedFileRemove(fileId){const newFiles=editCtx.editedUploadedFiles.filter(f=>f.id!==fileId);editCtx.setUploadedFiles(newFiles)}async function handleFilesAdd(files){const processed=await processFilesToChatUploaded( +files);editCtx.setUploadedFiles([...editCtx.editedUploadedFiles,...processed])}user_effect(()=>(chatStore.setEditModeActive(handleFilesAdd),()=>{chatStore.clearEditMode()}));var fragment=root$14();event("keydown",$window,handleGlobalKeydown);var div=first_child(fragment),node2=child(div);{let $0=user_derived(()=>editCtx.messageRole===MessageRole.USER),$1=user_derived(()=>editCtx.messageRole===MessageRole.USER);ChatForm(node2,{get value(){return editCtx.editedContent},get attachments(){return editCtx. +editedExtras},placeholder:"Edit your message...",showMcpPromptButton:!0,get showAddButton(){return get$3($0)},get showModelSelector(){return get$3($1)},get onValueChange(){return editCtx.setContent},onAttachmentRemove:handleAttachmentRemove,onUploadedFileRemove:handleUploadedFileRemove,onFilesAdd:handleFilesAdd,onSubmit:handleSubmit,get uploadedFiles(){return editCtx.editedUploadedFiles},set uploadedFiles($$value){editCtx.editedUploadedFiles=$$value}})}reset(div);var div_1=sibling(div,2),node_1=child( +div_1);{var consequent=$$anchor2=>{var div_2=root_1$x(),node_2=child(div_2);Switch(node_2,{id:"save-only-switch",class:"scale-75",get checked(){return get$3(saveWithoutRegenerate)},set checked($$value){set$1(saveWithoutRegenerate,$$value,!0)}}),next$1(2),reset(div_2),append($$anchor2,div_2)},consequent_1=$$anchor2=>{var div_3=root_2$O(),node_3=child(div_3);Switch(node_3,{id:"branch-after-edit",class:"scale-75",get checked(){return get$3(branchAfterEdit)},set checked($$value){set$1(branchAfterEdit, +$$value,!0)}}),next$1(2),reset(div_3),append($$anchor2,div_3)},alternate=$$anchor2=>{var div_4=root_3$G();append($$anchor2,div_4)};if_block(node_1,$$render=>{get$3(isUserMessage)&&editCtx.showSaveOnlyOption?$$render(consequent):get$3(isAssistantMessage)?$$render(consequent_1,1):$$render(alternate,-1)})}var node_4=sibling(node_1,2);Button(node_4,{class:"h-7 px-3 text-xs",onclick:attemptCancel,size:"sm",variant:"ghost",children:($$anchor2,$$slotProps)=>{var fragment_1=root_4$r(),node_5=first_child( +fragment_1);X(node_5,{class:"mr-1 h-3 w-3"}),next$1(),append($$anchor2,fragment_1)},$$slots:{default:!0}}),reset(div_1);var node_6=sibling(div_1,2);DialogConfirmation(node_6,{title:"Discard changes?",description:"You have unsaved changes. Are you sure you want to discard them?",confirmText:"Discard",cancelText:"Keep editing",variant:"destructive",get icon(){return Triangle_alert},get onConfirm(){return editCtx.cancel},onCancel:()=>set$1(showDiscardDialog,!1),get open(){return get$3(showDiscardDialog)}, +set open($$value){set$1(showDiscardDialog,$$value,!0)}}),append($$anchor,fragment),pop()}var root_3$F=from_html('
'),root_2$N=from_html(" ",1),root$13=from_html('
');function ChatMessageUser($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),siblingInfo=prop($$props,"siblingInfo",3,null);const editCtx=getMessageEditContext();var div=root$13(),node2=child(div);{var consequent=$$anchor2=>{ +ChatMessageEditForm($$anchor2,{})},alternate=$$anchor2=>{var fragment_1=root_2$N(),node_1=first_child(fragment_1);ChatMessageUserBubble(node_1,{get content(){return $$props.message.content},get attachments(){return $$props.message.extra},renderMarkdown:!0});var node_2=sibling(node_1,2);{var consequent_1=$$anchor3=>{var div_1=root_3$F(),node_3=child(div_1);ChatMessageActionIcons(node_3,{actionsPosition:"right",get deletionInfo(){return $$props.deletionInfo},justify:"end",get onConfirmDelete(){return $$props. +onConfirmDelete},get onCopy(){return $$props.onCopy},get onDelete(){return $$props.onDelete},get onEdit(){return $$props.onEdit},get onForkConversation(){return $$props.onForkConversation},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props.showDeleteDialog},get role(){return MessageRole.USER}}),reset(div_1),append($$anchor3,div_1)};if_block( +node_2,$$render=>{$$props.message.timestamp&&$$render(consequent_1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{editCtx.isEditing?$$render(consequent):$$render(alternate,-1)})}reset(div),template_effect(()=>set_class(div,1,`group flex flex-col items-end gap-3 md:gap-2 ${className()??""}`)),append($$anchor,div),pop()}var root_1$w=from_html('
'),root_4$q=from_html("
"),root_5$k=from_html(' '),root$12=from_html(" ",1);function ChatMessageUserBubble($$anchor,$$props){push$1($$props,!0);let attachments=prop($$props,"attachments",19,()=>[]),renderMarkdown=prop($$props,"renderMarkdown",3,!1),textColorClass=prop($$props,"textColorClass",3,"text-foreground"),cardBgClass=prop($$props,"cardBgClass",3,"dark:bg-primary/15"),maxHeightStyle=prop($$props,"maxHeightStyle",3,"max-height: var(--max-message-height);"),isMultiline=state$1(!1),messageElement=state$1(void 0);const currentConfig=config$1(); +user_effect(()=>{if(!get$3(messageElement)||!$$props.content.trim())return;if($$props.content.includes(` `)){set$1(isMultiline,!0);return}const resizeObserver=new ResizeObserver(entries=>{for(const entry of entries){const element2=entry.target;set$1(isMultiline,element2.offsetHeight>24*1.5)}});return resizeObserver.observe(get$3(messageElement)),()=>{resizeObserver.disconnect()}});var fragment=root$12(),node2=first_child(fragment);{var consequent=$$anchor2=>{var div=root_1$w(),node_1=child(div);ChatAttachmentsList(node_1,{get attachments(){return attachments()},readonly:!0,imageHeight:"h-40"}),reset( div),append($$anchor2,div)};if_block(node2,$$render=>{attachments()&&attachments().length>0&&$$render(consequent)})}var node_2=sibling(node2,2);{var consequent_2=$$anchor2=>{{let $0=user_derived(()=>get$3(isMultiline)?"":void 0);Card($$anchor2,{get class(){return`max-w-[80%] overflow-y-auto rounded-[1.125rem] border-none bg-primary/5 px-3.75 py-1.5 ${textColorClass()??""} backdrop-blur-md data-[multiline]:py-2.5 ${cardBgClass()??""}`},get"data-multiline"(){return get$3($0)},get style(){return`${maxHeightStyle()?? ""} overflow-wrap: anywhere; word-break: break-word;`},children:($$anchor3,$$slotProps)=>{var fragment_2=comment$2(),node_3=first_child(fragment_2);{var consequent_1=$$anchor4=>{var div_1=root_4$q(),node_4=child(div_1);MarkdownContent(node_4,{class:"markdown-user-content -my-4",get content(){return $$props.content}}),reset(div_1),bind_this(div_1,$$value=>set$1(messageElement,$$value),()=>get$3(messageElement)),append($$anchor4,div_1)},alternate=$$anchor4=>{var span=root_5$k(),text2=child(span,!0); diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index 21c843c0d69..0675ce31d06 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -1082,11 +1082,12 @@ json oaicompat_chat_params_parse( throw std::invalid_argument("Cannot have 2 or more assistant messages at the end of the list."); } - /* TODO: test this properly */ - inputs.reasoning_format = COMMON_REASONING_FORMAT_NONE; - - if ( inputs.enable_thinking ) { - throw std::invalid_argument("Assistant response prefill is incompatible with enable_thinking."); + // reject reasoning prefill on channel based templates that do not expose explicit thinking tags + if (!last_message.reasoning_content.empty() && inputs.enable_thinking) { + auto probe_params = common_chat_templates_apply(opt.tmpls.get(), inputs); + if (probe_params.supports_thinking && probe_params.thinking_end_tag.empty()) { + throw std::invalid_argument("Assistant prefill with reasoning_content is not supported yet for this template."); + } } inputs.add_generation_prompt = true; @@ -1098,6 +1099,42 @@ json oaicompat_chat_params_parse( /* Append assistant prefilled message */ if (prefill_assistant_message) { + const bool thinking_active = chat_params.supports_thinking && !chat_params.thinking_end_tag.empty(); + const bool has_reasoning = !last_message.reasoning_content.empty(); + const bool has_content = !last_message.content.empty() || !last_message.content_parts.empty(); + const bool mid_reasoning = has_reasoning && !has_content; + + // some templates inject thinking_start in generation_prompt, others let the model emit it + const bool gp_has_think = thinking_active + && chat_params.generation_prompt.find(chat_params.thinking_start_tag) != std::string::npos; + + // open the thinking block when reasoning is present and the template did not inject it + if (has_reasoning) { + if (thinking_active && !gp_has_think) { + chat_params.prompt += chat_params.thinking_start_tag; + } + chat_params.prompt += last_message.reasoning_content; + } + + if (thinking_active) { + if (mid_reasoning) { + // model continues inside the thinking block, keep generation_prompt open on think + if (!gp_has_think) { + chat_params.generation_prompt += chat_params.thinking_start_tag; + } + } else { + // close thinking block when reasoning is followed by content, or when the template forced it open + if (has_reasoning || gp_has_think) { + chat_params.prompt += chat_params.thinking_end_tag; + } + // strip thinking_start from generation_prompt so the parser routes model output as content + auto pos = chat_params.generation_prompt.rfind(chat_params.thinking_start_tag); + if (pos != std::string::npos) { + chat_params.generation_prompt = chat_params.generation_prompt.substr(0, pos); + } + } + } + if (!last_message.content_parts.empty()) { for (auto & p : last_message.content_parts) { chat_params.prompt += p.text; diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte index 2fb6066d9e8..b4d69b932ab 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte @@ -74,7 +74,6 @@ const editCtx = getMessageEditContext(); const isAgentic = $derived(hasAgenticContent(message, toolMessages)); - const hasReasoning = $derived(!!message.reasoningContent); const processingState = useProcessingState(); let currentConfig = $derived(config()); @@ -329,7 +328,7 @@ {onCopy} {onEdit} {onRegenerate} - onContinue={currentConfig.enableContinueGeneration && !hasReasoning ? onContinue : undefined} + onContinue={currentConfig.enableContinueGeneration ? onContinue : undefined} {onForkConversation} {onDelete} {onConfirmDelete} diff --git a/tools/server/webui/src/lib/constants/settings-registry.ts b/tools/server/webui/src/lib/constants/settings-registry.ts index 9c7cc2bf69c..809f780640c 100644 --- a/tools/server/webui/src/lib/constants/settings-registry.ts +++ b/tools/server/webui/src/lib/constants/settings-registry.ts @@ -122,7 +122,7 @@ const SETTINGS_REGISTRY: Record = { { key: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION, label: 'Enable "Continue" button', - help: 'Enable "Continue" button for assistant messages. Currently works only with non-reasoning models.', + help: 'Enable "Continue" button for assistant messages, including reasoning models.', defaultValue: false, type: SettingsFieldType.CHECKBOX, section: SETTINGS_SECTION_SLUGS.GENERAL, diff --git a/tools/server/webui/src/lib/stores/chat.svelte.ts b/tools/server/webui/src/lib/stores/chat.svelte.ts index 7157068e9ba..7b4a4e04293 100644 --- a/tools/server/webui/src/lib/stores/chat.svelte.ts +++ b/tools/server/webui/src/lib/stores/chat.svelte.ts @@ -674,7 +674,8 @@ class ChatStore { }, onReasoningChunk: (chunk: string) => { streamedReasoningContent += chunk; - // Update UI to show reasoning is being received + // mark streaming state so a stop mid-thinking can persist the partial reasoning + this.setChatStreaming(convId, streamedContent, currentMessageId); const idx = conversationsStore.findMessageIndex(currentMessageId); conversationsStore.updateMessageAtIndex(idx, { reasoningContent: streamedReasoningContent @@ -989,38 +990,51 @@ class ChatStore { const conversationId = convId || conversationsStore.activeConversation?.id; if (!conversationId) return; const streamingState = this.getChatStreaming(conversationId); - if (!streamingState || !streamingState.response.trim()) return; + if (!streamingState) return; const messages = conversationId === conversationsStore.activeConversation?.id ? conversationsStore.activeMessages : await conversationsStore.getConversationMessages(conversationId); if (!messages.length) return; const lastMessage = messages[messages.length - 1]; - if (lastMessage?.role === MessageRole.ASSISTANT) { - try { - const updateData: { content: string; timings?: ChatMessageTimings } = { - content: streamingState.response + if (lastMessage?.role !== MessageRole.ASSISTANT) return; + + const partialContent = streamingState.response; + const partialReasoning = lastMessage.reasoningContent || ''; + + // nothing to persist when both content and reasoning are empty (e.g. stop before any token) + if (!partialContent.trim() && !partialReasoning.trim()) return; + + try { + const updateData: { + content: string; + reasoningContent?: string; + timings?: ChatMessageTimings; + } = { + content: partialContent + }; + if (partialReasoning) { + updateData.reasoningContent = partialReasoning; + } + const lastKnownState = this.getProcessingState(conversationId); + if (lastKnownState) { + updateData.timings = { + prompt_n: lastKnownState.promptTokens || 0, + prompt_ms: lastKnownState.promptMs, + predicted_n: lastKnownState.tokensDecoded || 0, + cache_n: lastKnownState.cacheTokens || 0, + predicted_ms: + lastKnownState.tokensPerSecond && lastKnownState.tokensDecoded + ? (lastKnownState.tokensDecoded / lastKnownState.tokensPerSecond) * 1000 + : undefined }; - const lastKnownState = this.getProcessingState(conversationId); - if (lastKnownState) { - updateData.timings = { - prompt_n: lastKnownState.promptTokens || 0, - prompt_ms: lastKnownState.promptMs, - predicted_n: lastKnownState.tokensDecoded || 0, - cache_n: lastKnownState.cacheTokens || 0, - predicted_ms: - lastKnownState.tokensPerSecond && lastKnownState.tokensDecoded - ? (lastKnownState.tokensDecoded / lastKnownState.tokensPerSecond) * 1000 - : undefined - }; - } - await DatabaseService.updateMessage(lastMessage.id, updateData); - lastMessage.content = streamingState.response; - if (updateData.timings) lastMessage.timings = updateData.timings; - } catch (error) { - lastMessage.content = streamingState.response; - console.error('Failed to save partial response:', error); } + await DatabaseService.updateMessage(lastMessage.id, updateData); + lastMessage.content = partialContent; + if (updateData.timings) lastMessage.timings = updateData.timings; + } catch (error) { + lastMessage.content = partialContent; + console.error('Failed to save partial response:', error); } } @@ -1265,7 +1279,11 @@ class ChatStore { const conversationContext = conversationsStore.activeMessages.slice(0, idx); const contextWithContinue = [ ...conversationContext, - { role: MessageRole.ASSISTANT as const, content: originalContent } + { + role: MessageRole.ASSISTANT as const, + content: originalContent, + reasoning_content: originalReasoning || undefined + } ]; let appendedContent = ''; @@ -1291,6 +1309,8 @@ class ChatStore { onReasoningChunk: (chunk: string) => { appendedReasoning += chunk; hasReceivedContent = true; + // mark streaming state so a stop mid-thinking can persist the partial reasoning + this.setChatStreaming(msg.convId, originalContent + appendedContent, msg.id); conversationsStore.updateMessageAtIndex(idx, { reasoningContent: originalReasoning + appendedReasoning }); From e75cd5efb5bd88328725cf6d19da4d26f2b5059e Mon Sep 17 00:00:00 2001 From: Xuan-Son Nguyen Date: Wed, 13 May 2026 15:14:58 +0200 Subject: [PATCH 086/158] download: do not exit() on error (#23008) --- common/arg.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index f1f4c12a3ce..75a4d38d3da 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -357,8 +357,7 @@ static handle_model_result common_params_handle_model(struct common_params_model auto download_result = common_download_model(model, opts, true); if (download_result.model_path.empty()) { - LOG_ERR("error: failed to download model from Hugging Face\n"); - exit(1); + throw std::runtime_error("failed to download model from Hugging Face"); } model.name = model.hf_repo; @@ -380,8 +379,7 @@ static handle_model_result common_params_handle_model(struct common_params_model opts.offline = offline; auto download_result = common_download_model(model, opts); if (download_result.model_path.empty()) { - LOG_ERR("error: failed to download model from %s\n", model.url.c_str()); - exit(1); + throw std::runtime_error("failed to download model from " + model.url); } } From ad96bb8c0c5d40ebd05a04eb93b32f88f77d7025 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Wed, 13 May 2026 06:59:28 -0700 Subject: [PATCH 087/158] hexagon: add unary tanh op (#22999) --- ggml/src/ggml-hexagon/ggml-hexagon.cpp | 2 ++ ggml/src/ggml-hexagon/htp/htp-ops.h | 1 + ggml/src/ggml-hexagon/htp/main.c | 1 + ggml/src/ggml-hexagon/htp/unary-ops.c | 22 +++++++++++++++++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ggml/src/ggml-hexagon/ggml-hexagon.cpp b/ggml/src/ggml-hexagon/ggml-hexagon.cpp index d3c125dbc3d..3d1c9da8329 100644 --- a/ggml/src/ggml-hexagon/ggml-hexagon.cpp +++ b/ggml/src/ggml-hexagon/ggml-hexagon.cpp @@ -2865,6 +2865,7 @@ static htp_op_code op_remap_to_htp(const ggml_tensor * t) { case GGML_UNARY_OP_NEG: return HTP_OP_UNARY_NEG; case GGML_UNARY_OP_EXP: return HTP_OP_UNARY_EXP; case GGML_UNARY_OP_SOFTPLUS: return HTP_OP_UNARY_SOFTPLUS; + case GGML_UNARY_OP_TANH: return HTP_OP_UNARY_TANH; default: break; } @@ -3335,6 +3336,7 @@ static bool ggml_backend_hexagon_device_supports_op(ggml_backend_dev_t dev, cons case GGML_UNARY_OP_EXP: case GGML_UNARY_OP_SIGMOID: case GGML_UNARY_OP_SOFTPLUS: + case GGML_UNARY_OP_TANH: supp = ggml_hexagon_supported_unary(sess, op); break; case GGML_UNARY_OP_SILU: diff --git a/ggml/src/ggml-hexagon/htp/htp-ops.h b/ggml/src/ggml-hexagon/htp/htp-ops.h index 6203e3848b9..98db864dd42 100644 --- a/ggml/src/ggml-hexagon/htp/htp-ops.h +++ b/ggml/src/ggml-hexagon/htp/htp-ops.h @@ -62,6 +62,7 @@ enum htp_op_code { HTP_OP_UNARY_EXP, HTP_OP_UNARY_NEG, HTP_OP_UNARY_SOFTPLUS, + HTP_OP_UNARY_TANH, HTP_OP_GLU_SWIGLU, HTP_OP_GLU_SWIGLU_OAI, HTP_OP_GLU_GEGLU, diff --git a/ggml/src/ggml-hexagon/htp/main.c b/ggml/src/ggml-hexagon/htp/main.c index fa1e0698f4a..883a31d6163 100644 --- a/ggml/src/ggml-hexagon/htp/main.c +++ b/ggml/src/ggml-hexagon/htp/main.c @@ -542,6 +542,7 @@ static int execute_op(struct htp_ops_context * octx) { case HTP_OP_UNARY_SIGMOID: case HTP_OP_UNARY_NEG: case HTP_OP_UNARY_EXP: + case HTP_OP_UNARY_TANH: case HTP_OP_L2_NORM: return op_unary(octx); diff --git a/ggml/src/ggml-hexagon/htp/unary-ops.c b/ggml/src/ggml-hexagon/htp/unary-ops.c index 26a0e0bd793..d4ae89ee6f0 100644 --- a/ggml/src/ggml-hexagon/htp/unary-ops.c +++ b/ggml/src/ggml-hexagon/htp/unary-ops.c @@ -373,6 +373,21 @@ static void l2_norm_f32(const float * restrict src, } } +static void tanh_f32(const float * restrict src, + float * restrict dst, + uint8_t * restrict spad, + const uint32_t num_rows, + const uint32_t row_elems, + const size_t row_size, + int32_t * op_params) { + for (uint32_t ir = 0; ir < num_rows; ir++) { + const uint8_t * restrict src_local = (const uint8_t *)src + (ir * row_size); + uint8_t * restrict dst_local = (uint8_t *)dst + (ir * row_size); + + hvx_tanh_f32_aa(dst_local, src_local, row_elems); + } +} + static void unary_job_f32_per_thread(unsigned int nth, unsigned int ith, void * data) { const struct htp_unary_context * uctx = (const struct htp_unary_context *) data; struct htp_ops_context * octx = uctx->octx; @@ -477,6 +492,9 @@ static void unary_job_f32_per_thread(unsigned int nth, unsigned int ith, void * case HTP_OP_UNARY_SOFTPLUS: softplus_f32(src0_spad, dst_spad, NULL, block_size, ne0, src0_row_size_aligned, op_params); break; + case HTP_OP_UNARY_TANH: + tanh_f32(src0_spad, dst_spad, NULL, block_size, ne0, src0_row_size_aligned, op_params); + break; case HTP_OP_L2_NORM: l2_norm_f32(src0_spad, dst_spad, NULL, block_size, ne0, src0_row_size_aligned, op_params); break; @@ -547,10 +565,12 @@ static int execute_op_unary_f32(struct htp_ops_context * octx) { case HTP_OP_UNARY_SOFTPLUS: op_type = "softplus-f32"; break; + case HTP_OP_UNARY_TANH: + op_type = "tanh-f32"; + break; case HTP_OP_L2_NORM: op_type = "l2norm-f32"; break; - default: FARF(ERROR, "Unsupported unary Op %u\n", octx->op); return HTP_STATUS_NO_SUPPORT; From 7e16646015aa88d8c1ebf8fd3ddbe3c8cab7bef5 Mon Sep 17 00:00:00 2001 From: Ravi Panchumarthy Date: Wed, 13 May 2026 07:12:15 -0700 Subject: [PATCH 088/158] docs : Update OPENVINO.md (#22959) Updated OPENVINO.md with Validated models and quantizations Co-authored-by: Haarika Madaka --- docs/backend/OPENVINO.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/backend/OPENVINO.md b/docs/backend/OPENVINO.md index c9c005a9981..b0e19abb090 100644 --- a/docs/backend/OPENVINO.md +++ b/docs/backend/OPENVINO.md @@ -57,17 +57,22 @@ Although OpenVINO supports a wide range of [Intel hardware](https://docs.openvin ## Validated Models -The following models have been validated for functionality on Intel® Core™ Ultra Series 1 and Series 2: - -- [Llama-3.2-1B-Instruct-GGUF](https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/) -- [Llama-3.1-8B-Instruct](https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF) -- [microsoft/Phi-3-mini-4k-instruct-gguf](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf) -- [Qwen/Qwen2.5-1.5B-Instruct-GGUF](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF) -- [Qwen/Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B-GGUF) -- [openbmb/MiniCPM-1B-sft-bf16](https://huggingface.co/openbmb/MiniCPM-S-1B-sft-gguf) -- [tencent/Hunyuan-7B-Instruct](https://huggingface.co/bartowski/tencent_Hunyuan-7B-Instruct-GGUF) -- [mistralai/Mistral-7B-Instruct-v0.3](https://huggingface.co/bartowski/Mistral-7B-Instruct-v0.3-GGUF) -- [bartowski/DeepSeek-R1-Distill-Llama-8B-GGUF](https://huggingface.co/bartowski/DeepSeek-R1-Distill-Llama-8B-GGUF) +The following models were validated on Intel® Core™ Ultra Series 2. While our testing was limited, the OpenVINO backend is expected to work across a broad range of [Intel hardware](https://docs.openvino.ai/2026/about-openvino/release-notes-openvino/system-requirements.html). +- Use `GGML_OPENVINO_STATEFUL_EXECUTION=1` when using GPU device. +- `-fa 1` is required when running llama-bench with the OpenVINO backend. +- Additional model support, quantization formats and validations are work in progress. + +| Model | Validated | Known Issues | +| :------| :---------- | :-------------| +| [Llama-3.2-1B-Instruct](https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/) | `FP16`, `Q8_0`, `Q4_0`, `Q4_1`, `Q4_K_M` on CPU/GPU/NPU | — | +| [Meta-Llama-3.1-8B-Instruct](https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF) | `Q8_0`, `Q4_K_M` on CPU/GPU/NPU | `Q4_0_8_8`, `Q4_0_4_8`, `Q4_0_4_4` fail | +| [Phi-3-mini-4k-instruct](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf) | `FP16`, `Q4` on CPU/NPU | GPU unsupported for `FP16` and `Q4` (`llama-cli`, `llama-bench`) | +| [Qwen2.5-1.5B-Instruct](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF) | `FP16`, `Q8_0`, `Q4_0`, `Q4_1`, `Q4_K_M` on CPU/GPU/NPU | — | +| [Qwen3-8B-Instruct](https://huggingface.co/Qwen/Qwen3-8B-GGUF) | `FP16`, `Q8_0`, `Q4_0`, `Q4_1`, `Q4_K_M` on CPU/NPU; GPU works via `llama-bench` | GPU `llama-cli` unsupported for all quantizations | +| [MiniCPM-V-2_6-GGUF](https://huggingface.co/openbmb/MiniCPM-V-2_6-gguf) | `Q4_0` on CPU/GPU/NPU | — | +| [DeepSeek-R1-Distill-Llama-8B](https://huggingface.co/bartowski/DeepSeek-R1-Distill-Llama-8B-GGUF) | `Q8_0`, `Q4_0`, `Q4_1`, `Q4_K_M` on CPU/GPU/NPU | — | +| [Hunyuan-7B-Instruct](https://huggingface.co/bartowski/tencent_Hunyuan-7B-Instruct-GGUF) | CPU: `Q8_0`, `Q4_0`, `Q4_1`, `Q4_K_M`; GPU: `Q8_0`, `Q4_0`, `Q4_1`; NPU (`llama-bench` only): `Q4_0`, `Q4_1`, `Q4_K_M` | GPU `Q4_K_M` unsupported; NPU `llama-cli` unsupported | +| [Mistral-7B-Instruct-v0.3](https://huggingface.co/bartowski/Mistral-7B-Instruct-v0.3-GGUF/) | CPU/GPU: `Q8_0`, `Q4_K_M`; NPU: `Q8_0`, `Q4_K_M` (via `llama-bench`) | NPU `llama-cli` unsupported for `Q8_0`, `Q4_K_M` | ## Build Instructions From 46be24d12175f53f61051f450a289dc2926a9088 Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 13 May 2026 16:16:02 +0200 Subject: [PATCH 089/158] webui: preserve system message on edit cancel (#22911) * webui: preserve system message on edit cancel when content is not the placeholder * chore: update webui build output --- tools/server/public/bundle.js | 112 +++++++++--------- .../ChatMessage/ChatMessage.svelte | 2 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/tools/server/public/bundle.js b/tools/server/public/bundle.js index 8c902da9db7..c2a9942a797 100644 --- a/tools/server/public/bundle.js +++ b/tools/server/public/bundle.js @@ -6805,62 +6805,62 @@ ${section.toolResult}`);break}}return parts.join(` `)}),editedExtras=user_derived(()=>$$props.message.extra?[...$$props.message.extra]:[]),editedUploadedFiles=state$1(proxy([])),isEditing2=state$1(!1),showDeleteDialog=state$1(!1),shouldBranchAfterEdit=state$1(!1),textareaElement=state$1(void 0),showSaveOnlyOption=user_derived(()=>$$props.message.role===MessageRole.USER),showBranchAfterEditOption=user_derived(()=>$$props.message.role===MessageRole.ASSISTANT);setMessageEditContext({get isEditing(){return get$3(isEditing2)},get editedContent(){return get$3( editedContent)},get editedExtras(){return get$3(editedExtras)},get editedUploadedFiles(){return get$3(editedUploadedFiles)},get originalContent(){return $$props.message.role===MessageRole.ASSISTANT?get$3(rawEditContent)??$$props.message.content:$$props.message.content},get originalExtras(){return $$props.message.extra||[]},get showSaveOnlyOption(){return get$3(showSaveOnlyOption)},get showBranchAfterEditOption(){return get$3(showBranchAfterEditOption)},get shouldBranchAfterEdit(){return get$3(shouldBranchAfterEdit)}, get messageRole(){return $$props.message.role},get rawEditContent(){return get$3(rawEditContent)},setContent:content2=>{set$1(editedContent,content2)},setExtras:extras=>{set$1(editedExtras,extras)},setUploadedFiles:files=>{set$1(editedUploadedFiles,files,!0)},setShouldBranchAfterEdit:value=>{set$1(shouldBranchAfterEdit,value,!0)},save:handleSaveEdit,saveOnly:handleSaveEditOnly,cancel:handleCancelEdit,startEdit:handleEdit});let mcpPromptExtra=user_derived(()=>{if($$props.message.role!==MessageRole. -USER||$$props.message.content.trim()||!$$props.message.extra||$$props.message.extra.length!==1)return null;const extra=$$props.message.extra[0];return extra.type===AttachmentType.MCP_PROMPT?extra:null});user_effect(()=>{const pendingId=pendingEditMessageId();pendingId&&pendingId===$$props.message.id&&!get$3(isEditing2)&&(handleEdit(),chatStore.clearPendingEditMessageId())});async function handleCancelEdit(){if(set$1(isEditing2,!1),$$props.message.role===MessageRole.SYSTEM){await chatStore.removeSystemPromptPlaceholder( -$$props.message.id)&&goto(ROUTES.START);return}set$1(editedContent,$$props.message.role===MessageRole.ASSISTANT?get$3(rawEditContent)||$$props.message.content||"":$$props.message.content),set$1(editedExtras,$$props.message.extra?[...$$props.message.extra]:[]),set$1(editedUploadedFiles,[],!0)}function handleCopy(){chatActions.copy($$props.message)}async function handleConfirmDelete(){$$props.message.role===MessageRole.SYSTEM?await chatStore.removeSystemPromptPlaceholder($$props.message.id)&&goto( -ROUTES.START):chatActions.delete($$props.message),set$1(showDeleteDialog,!1)}async function handleDelete2(){set$1(deletionInfo,await chatStore.getDeletionInfo($$props.message.id),!0),set$1(showDeleteDialog,!0)}function handleEdit(){set$1(isEditing2,!0),$$props.message.role===MessageRole.SYSTEM&&$$props.message.content===SYSTEM_MESSAGE_PLACEHOLDER?set$1(editedContent,""):$$props.message.role===MessageRole.ASSISTANT?set$1(editedContent,get$3(rawEditContent)||$$props.message.content||""):set$1(editedContent, -$$props.message.content),get$3(textareaElement)?.focus(),set$1(editedExtras,$$props.message.extra?[...$$props.message.extra]:[]),set$1(editedUploadedFiles,[],!0),setTimeout(()=>{get$3(textareaElement)&&(get$3(textareaElement).focus(),get$3(textareaElement).setSelectionRange(get$3(textareaElement).value.length,get$3(textareaElement).value.length))},0)}function handleRegenerate(modelOverride){chatActions.regenerateWithBranching($$props.message,modelOverride)}function handleContinue(){chatActions.continueAssistantMessage( -$$props.message)}function handleForkConversation(options){chatActions.forkConversation($$props.message,options)}function handleNavigateToSibling(siblingId){chatActions.navigateToSibling(siblingId)}async function handleSaveEdit(){if($$props.message.role===MessageRole.SYSTEM){const newContent=get$3(editedContent).trim();if(!newContent){const conversationDeleted=await chatStore.removeSystemPromptPlaceholder($$props.message.id);set$1(isEditing2,!1),conversationDeleted&&goto(ROUTES.START);return}await DatabaseService. -updateMessage($$props.message.id,{content:newContent});const index2=conversationsStore.findMessageIndex($$props.message.id);index2!==-1&&conversationsStore.updateMessageAtIndex(index2,{content:newContent})}else if($$props.message.role===MessageRole.USER){const finalExtras=await getMergedExtras();chatActions.editWithBranching($$props.message,get$3(editedContent).trim(),finalExtras)}else chatActions.editWithReplacement($$props.message,get$3(editedContent),get$3(shouldBranchAfterEdit));set$1(isEditing2, -!1),set$1(shouldBranchAfterEdit,!1),set$1(editedUploadedFiles,[],!0)}async function handleSaveEditOnly(){if($$props.message.role===MessageRole.USER){const finalExtras=await getMergedExtras();chatActions.editUserMessagePreserveResponses($$props.message,get$3(editedContent).trim(),finalExtras)}set$1(isEditing2,!1),set$1(editedUploadedFiles,[],!0)}async function getMergedExtras(){if(get$3(editedUploadedFiles).length===0)return get$3(editedExtras);const plainFiles=snapshot(get$3(editedUploadedFiles)), -newExtras=(await parseFilesToMessageExtras(plainFiles))?.extras||[];return[...get$3(editedExtras),...newExtras]}function handleShowDeleteDialogChange(show){set$1(showDeleteDialog,show,!0)}var div=root$1f(),node2=child(div);{var consequent=$$anchor2=>{ChatMessageSystem($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)},get message(){return $$props.message},onConfirmDelete:handleConfirmDelete,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onNavigateToSibling:handleNavigateToSibling, -onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()},get textareaElement(){return get$3(textareaElement)},set textareaElement($$value){set$1(textareaElement,$$value,!0)}})},consequent_1=$$anchor2=>{ChatMessageMcpPrompt($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)},get message(){return $$props.message},get mcpPrompt(){return get$3(mcpPromptExtra)},onConfirmDelete:handleConfirmDelete, -onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()}})},consequent_2=$$anchor2=>{ChatMessageUser($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)},get message(){return $$props.message},onConfirmDelete:handleConfirmDelete,onCopy:handleCopy,onDelete:handleDelete2, -onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()}})},alternate=$$anchor2=>{ChatMessageAssistant($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)},get isLastAssistantMessage(){return isLastAssistantMessage()},get message(){return $$props.message},get toolMessages(){ -return toolMessages()},get messageContent(){return $$props.message.content},onConfirmDelete:handleConfirmDelete,onContinue:handleContinue,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onRegenerate:handleRegenerate,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()},get textareaElement(){return get$3(textareaElement)}, -set textareaElement($$value){set$1(textareaElement,$$value,!0)}})};if_block(node2,$$render=>{$$props.message.role===MessageRole.SYSTEM?$$render(consequent):get$3(mcpPromptExtra)?$$render(consequent_1,1):$$props.message.role===MessageRole.USER?$$render(consequent_2,2):$$render(alternate,-1)})}reset(div),action(div,$$node=>fadeInView?.($$node)),append($$anchor,div),pop()}var root_2$U=from_html('
'),root_7$n=from_html('
Receiving arguments...
'),root_8$o=from_html('
Response was truncated
'),root_4$w=from_html('
Arguments:
'),root_11$b=from_html('
Arguments:
'),root_13$b=from_html( -'
Waiting for result...
'),root_16$3=from_html(''),root_15$8=from_html('
',1),root_14$4=from_html('
'),root_17$6=from_html('
No\ - output
'),root_10$a=from_html('
Result:
',1),root_19$7=from_html('
'),root_21$1=from_html('
'),root_25$2=from_html('
'),root_23$3=from_html( -'
'),root$1e=from_html('
');function ChatMessageAgenticContent($$anchor,$$props){push$1($$props,!0);const renderSection=($$anchor2,section=noop$3,index2=noop$3)=>{var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor3=>{var div=root_2$U(),node_1=child(div);{let $0=user_derived( -()=>$$props.message?.extra);MarkdownContent(node_1,{get content(){return section().content},get attachments(){return get$3($0)}})}reset(div),append($$anchor3,div)},consequent_4=$$anchor3=>{const streamingIcon=user_derived(()=>(isStreaming(),Loader_circle)),streamingIconClass=user_derived(()=>isStreaming()?"h-4 w-4 animate-spin":"h-4 w-4");{let $0=user_derived(()=>isExpanded(index2(),section())),$1=user_derived(()=>section().toolName||"Tool call"),$2=user_derived(()=>isStreaming()?"":"incomplete"); -CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return get$3(streamingIcon)},get iconClass(){return get$3(streamingIconClass)},get title(){return get$3($1)},get subtitle(){return get$3($2)},get isStreaming(){return isStreaming()},onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var div_1=root_4$w(),div_2=child(div_1),node_2=sibling(child(div_2),2);{var consequent_1=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animat\ -e-spin"})};if_block(node_2,$$render=>{isStreaming()&&$$render(consequent_1)})}reset(div_2);var node_3=sibling(div_2,2);{var consequent_2=$$anchor5=>{{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode($$anchor5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}},consequent_3=$$anchor5=>{var div_3=root_7$n();append($$anchor5,div_3)},alternate=$$anchor5=>{var div_4=root_8$o();append($$anchor5,div_4)};if_block( -node_3,$$render=>{section().toolArgs?$$render(consequent_2):isStreaming()?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_1),append($$anchor4,div_1)},$$slots:{default:!0}})}},consequent_10=$$anchor3=>{const isPending=user_derived(()=>section().type===AgenticSectionType.TOOL_CALL_PENDING),toolIcon=user_derived(()=>get$3(isPending)?Loader_circle:Wrench),toolIconClass=user_derived(()=>get$3(isPending)?"h-4 w-4 animate-spin":"h-4 w-4");{let $0=user_derived(()=>isExpanded(index2(),section())), -$1=user_derived(()=>section().toolName||""),$2=user_derived(()=>get$3(isPending)?"executing...":void 0);CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return get$3(toolIcon)},get iconClass(){return get$3(toolIconClass)},get title(){return get$3($1)},get subtitle(){return get$3($2)},get isStreaming(){return get$3(isPending)},onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var fragment_5=root_10$a(),node_4=first_child(fragment_5); -{var consequent_5=$$anchor5=>{var div_5=root_11$b(),node_5=sibling(child(div_5),2);{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode(node_5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}reset(div_5),append($$anchor5,div_5)};if_block(node_4,$$render=>{section().toolArgs&§ion().toolArgs!=="{}"&&$$render(consequent_5)})}var div_6=sibling(node_4,2),div_7=child(div_6),node_6=sibling(child(div_7),2);{ -var consequent_6=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animate-spin"})};if_block(node_6,$$render=>{get$3(isPending)&&$$render(consequent_6)})}reset(div_7);var node_7=sibling(div_7,2);{var consequent_7=$$anchor5=>{var div_8=root_13$b();append($$anchor5,div_8)},consequent_9=$$anchor5=>{var div_9=root_14$4();each(div_9,21,()=>section().parsedLines,index$2,($$anchor6,line)=>{var fragment_7=root_15$8(),div_10=first_child(fragment_7),text2=child(div_10,!0);reset(div_10);var node_8=sibling( -div_10,2);{var consequent_8=$$anchor7=>{var img=root_16$3();template_effect(()=>{set_attribute(img,"src",get$3(line).image.base64Url),set_attribute(img,"alt",get$3(line).image.name)}),append($$anchor7,img)};if_block(node_8,$$render=>{get$3(line).image&&$$render(consequent_8)})}template_effect(()=>set_text(text2,get$3(line).text)),append($$anchor6,fragment_7)}),reset(div_9),append($$anchor5,div_9)},alternate_1=$$anchor5=>{var div_11=root_17$6();append($$anchor5,div_11)};if_block(node_7,$$render=>{ -get$3(isPending)?$$render(consequent_7):section().toolResult?$$render(consequent_9,1):$$render(alternate_1,-1)})}reset(div_6),append($$anchor4,fragment_5)},$$slots:{default:!0}})}},consequent_11=$$anchor3=>{{let $0=user_derived(()=>isExpanded(index2(),section()));CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return Brain},title:"Reasoning",onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var div_12=root_19$7(),div_13=child( -div_12),text_1=child(div_13,!0);reset(div_13),reset(div_12),template_effect(()=>set_text(text_1,section().content)),append($$anchor4,div_12)},$$slots:{default:!0}})}},consequent_12=$$anchor3=>{const reasoningTitle=user_derived(()=>isStreaming()?"Reasoning...":"Reasoning"),reasoningSubtitle=user_derived(()=>isStreaming()?"":"incomplete");{let $0=user_derived(()=>isExpanded(index2(),section()));CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return Brain},get title(){ -return get$3(reasoningTitle)},get subtitle(){return get$3(reasoningSubtitle)},get isStreaming(){return isStreaming()},onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var div_14=root_21$1(),div_15=child(div_14),text_2=child(div_15,!0);reset(div_15),reset(div_14),template_effect(()=>set_text(text_2,section().content)),append($$anchor4,div_14)},$$slots:{default:!0}})}};if_block(node2,$$render=>{section().type===AgenticSectionType.TEXT?$$render(consequent):section(). -type===AgenticSectionType.TOOL_CALL_STREAMING?$$render(consequent_4,1):section().type===AgenticSectionType.TOOL_CALL||section().type===AgenticSectionType.TOOL_CALL_PENDING?$$render(consequent_10,2):section().type===AgenticSectionType.REASONING?$$render(consequent_11,3):section().type===AgenticSectionType.REASONING_PENDING&&$$render(consequent_12,4)})}append($$anchor2,fragment)};let toolMessages=prop($$props,"toolMessages",19,()=>[]),isStreaming=prop($$props,"isStreaming",3,!1),isLastAssistantMessage=prop( -$$props,"isLastAssistantMessage",3,!1),highlightTurns=prop($$props,"highlightTurns",3,!1),expandedStates=proxy({});const showToolCallInProgress=user_derived(()=>config$1().showToolCallInProgress),showThoughtInProgress=user_derived(()=>config$1().showThoughtInProgress);let permissionDismissed=state$1(!1);const pendingPermission=user_derived(()=>isStreaming()&&isLastAssistantMessage()?agenticPendingPermissionRequest($$props.message.convId):null);let prevPendingRef=null;user_effect(()=>{get$3(pendingPermission)!== -prevPendingRef&&(prevPendingRef=get$3(pendingPermission),get$3(pendingPermission)&&set$1(permissionDismissed,!1))});function handlePermission(decision){set$1(permissionDismissed,!0),agenticResolvePermission($$props.message.convId,decision)}let continueDismissed=state$1(!1);const pendingContinue=user_derived(()=>isStreaming()&&isLastAssistantMessage()?agenticPendingContinueRequest($$props.message.convId):!1);let prevContinueRef=!1;user_effect(()=>{get$3(pendingContinue)!==prevContinueRef&&(prevContinueRef= -get$3(pendingContinue),get$3(pendingContinue)&&set$1(continueDismissed,!1))});function handleContinue(shouldContinue){set$1(continueDismissed,!0),agenticResolveContinue($$props.message.convId,shouldContinue)}const sections=user_derived(()=>deriveAgenticSections($$props.message,toolMessages(),[],isStreaming())),sectionsParsed=user_derived(()=>get$3(sections).map(section=>({...section,parsedLines:section.toolResult?parseToolResultWithImages(section.toolResult,section.toolResultExtras||$$props.message?. -extra):[]}))),turnGroups=user_derived(()=>{const turns=[];let currentTurn=[],currentIndices=[],prevWasTool=!1;for(let i=0;i0&&(turns.push({sections:currentTurn,flatIndices:currentIndices}),currentTurn=[],currentIndices=[]),currentTurn. -push(section),currentIndices.push(i),prevWasTool=isTool}return currentTurn.length>0&&turns.push({sections:currentTurn,flatIndices:currentIndices}),turns});function getDefaultExpanded(section){return section.type===AgenticSectionType.TOOL_CALL_PENDING||section.type===AgenticSectionType.TOOL_CALL_STREAMING?get$3(showToolCallInProgress):section.type===AgenticSectionType.REASONING_PENDING?get$3(showThoughtInProgress):!1}function isExpanded(index2,section){return expandedStates[index2]!==void 0?expandedStates[index2]: -getDefaultExpanded(section)}function toggleExpanded(index2,section){const currentState=isExpanded(index2,section);expandedStates[index2]=!currentState}function buildTurnAgenticTimings(stats){return{turns:1,toolCallsCount:stats.toolCalls.length,toolsMs:stats.toolsMs,toolCalls:stats.toolCalls,llm:stats.llm}}var div_16=root$1e(),node_9=child(div_16);{var consequent_14=$$anchor2=>{var fragment_10=comment$2(),node_10=first_child(fragment_10);each(node_10,17,()=>get$3(turnGroups),index$2,($$anchor3,turn,turnIndex)=>{ -const turnStats=user_derived(()=>$$props.message?.timings?.agentic?.perTurn?.[turnIndex]);var div_17=root_23$3(),span=child(div_17);span.textContent=`Turn ${turnIndex+1}`;var node_11=sibling(span,2);each(node_11,19,()=>get$3(turn).sections,(section,sIdx)=>get$3(turn).flatIndices[sIdx],($$anchor4,section,sIdx)=>{renderSection($$anchor4,()=>get$3(section),()=>get$3(turn).flatIndices[get$3(sIdx)])});var node_12=sibling(node_11,2);{var consequent_13=$$anchor4=>{var div_18=root_25$2(),node_13=child(div_18); -{let $0=user_derived(()=>get$3(turnStats).toolCalls.length>0?buildTurnAgenticTimings(get$3(turnStats)):void 0);ChatMessageStatistics(node_13,{get promptTokens(){return get$3(turnStats).llm.prompt_n},get promptMs(){return get$3(turnStats).llm.prompt_ms},get predictedTokens(){return get$3(turnStats).llm.predicted_n},get predictedMs(){return get$3(turnStats).llm.predicted_ms},get agenticTimings(){return get$3($0)},get initialView(){return ChatMessageStatsView.GENERATION},hideSummary:!0})}reset(div_18), -append($$anchor4,div_18)};if_block(node_12,$$render=>{get$3(turnStats)&&$$render(consequent_13)})}reset(div_17),append($$anchor3,div_17)}),append($$anchor2,fragment_10)},alternate_2=$$anchor2=>{var fragment_12=comment$2(),node_14=first_child(fragment_12);each(node_14,17,()=>get$3(sectionsParsed),index$2,($$anchor3,section,index2)=>{renderSection($$anchor3,()=>get$3(section),()=>index2)}),append($$anchor2,fragment_12)};if_block(node_9,$$render=>{highlightTurns()&&get$3(turnGroups).length>1?$$render( -consequent_14):$$render(alternate_2,-1)})}var node_15=sibling(node_9,2);{var consequent_15=$$anchor2=>{ChatMessageActionCardPermissionRequest($$anchor2,{get toolName(){return get$3(pendingPermission).toolName},get serverLabel(){return get$3(pendingPermission).serverLabel},onDecision:handlePermission})};if_block(node_15,$$render=>{get$3(pendingPermission)&&!get$3(permissionDismissed)&&$$render(consequent_15)})}var node_16=sibling(node_15,2);{var consequent_16=$$anchor2=>{ChatMessageActionCardContinueRequest( -$$anchor2,{onDecision:handleContinue})};if_block(node_16,$$render=>{get$3(pendingContinue)&&!get$3(continueDismissed)&&$$render(consequent_16)})}reset(div_16),append($$anchor,div_16),pop()}var root$1d=from_html("
");function Button_group_root($$anchor,$$props){push$1($$props,!0);let restProps=rest_props($$props,["$$slots","$$events","$$legacy","class","children"]);var div=root$1d();attribute_effect(div,$0=>({class:$0,...restProps}),[()=>cn$1("flex items-center [&>*:first-child]:roun\ -ded-r-none [&>*:last-child]:rounded-l-none [&>*:not(:first-child):not(:last-child)]:rounded-none",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children),reset(div),append($$anchor,div),pop()}var root$1c=from_html("
");function Button_group_separator($$anchor,$$props){push$1($$props,!0);let restProps=rest_props($$props,["$$slots","$$events","$$legacy"]);var div=root$1c();attribute_effect(div,$0=>({class:$0,...restProps}),[()=>cn$1("shrink-0 self-stretch bg-border","w-\ -px")]),append($$anchor,div),pop()}var root_2$T=from_html('from ',1),root_1$B=from_html('Allow use of ?',1),root_5$n=from_html(" ",1),root_10$9=from_html("Always allow
 
tool",1),root_9$h=from_html(" ",1),root_4$v=from_html(" ",1),root_3$K=from_html(" ",1);function ChatMessageActionCardPermissionRequest($$anchor,$$props){push$1($$props,!0),ChatMessageActionCard($$anchor,{get icon(){ -return Shield_question},message:$$anchor2=>{next$1();var fragment_1=root_1$B(),span=sibling(first_child(fragment_1)),text2=child(span,!0);reset(span);var node2=sibling(span,2);{var consequent=$$anchor3=>{var fragment_2=root_2$T(),span_1=sibling(first_child(fragment_2)),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,$$props.serverLabel)),append($$anchor3,fragment_2)};if_block(node2,$$render=>{$$props.serverLabel&&$$render(consequent)})}next$1(),template_effect(()=>set_text( -text2,$$props.toolName)),append($$anchor2,fragment_1)},actions:$$anchor2=>{var fragment_3=root_3$K(),node_1=first_child(fragment_3);component(node_1,()=>Root$4,($$anchor3,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_4=root_4$v(),node_2=first_child(fragment_4);component(node_2,()=>Button_group_root,($$anchor5,ButtonGroup_Root)=>{ButtonGroup_Root($$anchor5,{class:"overflow-hidden rounded-md bg-foreground text-white shadow-sm dark:bg-secondary da\ -rk:text-foreground",children:($$anchor6,$$slotProps2)=>{var fragment_5=root_5$n(),node_3=first_child(fragment_5);Button(node_3,{class:"rounded-none! shadow-none!",size:"sm",onclick:()=>$$props.onDecision(ToolPermissionDecision.ONCE),children:($$anchor7,$$slotProps3)=>{next$1();var text_2=text$8("Allow once");append($$anchor7,text_2)},$$slots:{default:!0}});var node_4=sibling(node_3,2);component(node_4,()=>Button_group_separator,($$anchor7,ButtonGroup_Separator)=>{ButtonGroup_Separator($$anchor7, -{})});var node_5=sibling(node_4,2);component(node_5,()=>Dropdown_menu_trigger,($$anchor7,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor7,{children:($$anchor8,$$slotProps3)=>{Button($$anchor8,{size:"sm",class:"rounded-none! !ps-2 shadow-none!",children:($$anchor9,$$slotProps4)=>{Chevron_down($$anchor9,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}})},$$slots:{default:!0}})}),append($$anchor6,fragment_5)},$$slots:{default:!0}})});var node_6=sibling(node_2,2);component(node_6,()=>Dropdown_menu_content, -($$anchor5,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor5,{align:"start",class:"min-w-[8rem]",children:($$anchor6,$$slotProps2)=>{var fragment_8=root_9$h(),node_7=first_child(fragment_8);component(node_7,()=>Dropdown_menu_item,($$anchor7,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor7,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS),children:($$anchor8,$$slotProps3)=>{next$1();var fragment_9=root_10$9(),pre=sibling(first_child(fragment_9)),text_3=child(pre,!0);reset(pre),next$1(), -template_effect(()=>set_text(text_3,$$props.toolName)),append($$anchor8,fragment_9)},$$slots:{default:!0}})});var node_8=sibling(node_7,2);{var consequent_1=$$anchor7=>{var fragment_10=comment$2(),node_9=first_child(fragment_10);component(node_9,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor8,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS_SERVER),children:($$anchor9,$$slotProps3)=>{next$1();var text_4=text$8();template_effect(()=>set_text(text_4, -`Always allow all tools from ${$$props.serverLabel??""}`)),append($$anchor9,text_4)},$$slots:{default:!0}})}),append($$anchor7,fragment_10)},alternate=$$anchor7=>{const source2=user_derived(()=>toolsStore.getToolSource($$props.toolName)),providerName=user_derived(()=>get$3(source2)===ToolSource.BUILTIN?TOOL_SERVER_LABELS[ToolSource.BUILTIN]:get$3(source2)===ToolSource.CUSTOM?TOOL_SERVER_LABELS[ToolSource.CUSTOM]:"MCP Tools");var fragment_12=comment$2(),node_10=first_child(fragment_12);component( -node_10,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor8,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS_SERVER),children:($$anchor9,$$slotProps3)=>{next$1();var text_5=text$8();template_effect(()=>set_text(text_5,`Approve all tools from ${get$3(providerName)??""}`)),append($$anchor9,text_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_12)};if_block(node_8,$$render=>{$$props.serverLabel?$$render(consequent_1):$$render(alternate,-1)})}append( -$$anchor6,fragment_8)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})});var node_11=sibling(node_1,2);Button(node_11,{variant:"destructive",size:"sm",class:"text-destructive hover:text-destructive",onclick:()=>$$props.onDecision(ToolPermissionDecision.DENY),children:($$anchor3,$$slotProps)=>{next$1();var text_6=text$8("Deny");append($$anchor3,text_6)},$$slots:{default:!0}}),append($$anchor2,fragment_3)}}),pop()}var root$1b=from_html('
');function ChatMessageActionCard($$anchor,$$props){var div=root$1b(),div_1=child(div),node2=child(div_1);component(node2,()=>$$props.icon,($$anchor2,IconComponent_1)=>{IconComponent_1($$anchor2,{class:"h-4 w-4 shrink-0 text-muted-foreground"})});var span=sibling(node2,2),node_1=child(span);snippet(node_1,()=>$$props.message),reset( -span),reset(div_1);var div_2=sibling(div_1,2),node_2=child(div_2);snippet(node_2,()=>$$props.actions),reset(div_2),reset(div),append($$anchor,div)}var root_2$S=from_html(" ",1);function ChatMessageActionCardContinueRequest($$anchor,$$props){push$1($$props,!0),ChatMessageActionCard($$anchor,{get icon(){return Rotate_cw},message:$$anchor2=>{next$1();var text2=text$8("Agentic turn limit reached. Continue?");append($$anchor2,text2)},actions:$$anchor2=>{var fragment_1=root_2$S(),node2=first_child( -fragment_1);Button(node2,{size:"sm",onclick:()=>$$props.onDecision(!0),children:($$anchor3,$$slotProps)=>{next$1();var text_1=text$8("Continue");append($$anchor3,text_1)},$$slots:{default:!0}});var node_1=sibling(node2,2);Button(node_1,{variant:"destructive",size:"sm",class:"text-destructive hover:text-destructive",onclick:()=>$$props.onDecision(!1),children:($$anchor3,$$slotProps)=>{next$1();var text_2=text$8("Stop");append($$anchor3,text_2)},$$slots:{default:!0}}),append($$anchor2,fragment_1)}}), -pop()}var root_6$o=from_html('
Show raw output
'),root_7$m=from_html('
'),root$1a=from_html('
',1);function ChatMessageActionIcons($$anchor,$$props){push$1($$props,!0);let siblingInfo=prop($$props,"siblingInfo",3,null),showDeleteDialog=prop($$props,"showDeleteDialog",7),showRawOutputSwitch=prop($$props,"showRawOutputSwitch",3,!1),rawOutputEnabled=prop($$props,"rawOutputEnabled",3,!1),showForkDialog=state$1(!1),forkName=state$1(""),forkIncludeAttachments=state$1(!0);function handleConfirmDelete(){$$props.onConfirmDelete(),$$props.onShowDeleteDialogChange(!1)}function handleOpenForkDialog(){ +USER||$$props.message.content.trim()||!$$props.message.extra||$$props.message.extra.length!==1)return null;const extra=$$props.message.extra[0];return extra.type===AttachmentType.MCP_PROMPT?extra:null});user_effect(()=>{const pendingId=pendingEditMessageId();pendingId&&pendingId===$$props.message.id&&!get$3(isEditing2)&&(handleEdit(),chatStore.clearPendingEditMessageId())});async function handleCancelEdit(){if(set$1(isEditing2,!1),$$props.message.role===MessageRole.SYSTEM&&$$props.message.content=== +SYSTEM_MESSAGE_PLACEHOLDER){await chatStore.removeSystemPromptPlaceholder($$props.message.id)&&goto(ROUTES.START);return}set$1(editedContent,$$props.message.role===MessageRole.ASSISTANT?get$3(rawEditContent)||$$props.message.content||"":$$props.message.content),set$1(editedExtras,$$props.message.extra?[...$$props.message.extra]:[]),set$1(editedUploadedFiles,[],!0)}function handleCopy(){chatActions.copy($$props.message)}async function handleConfirmDelete(){$$props.message.role===MessageRole.SYSTEM? +await chatStore.removeSystemPromptPlaceholder($$props.message.id)&&goto(ROUTES.START):chatActions.delete($$props.message),set$1(showDeleteDialog,!1)}async function handleDelete2(){set$1(deletionInfo,await chatStore.getDeletionInfo($$props.message.id),!0),set$1(showDeleteDialog,!0)}function handleEdit(){set$1(isEditing2,!0),$$props.message.role===MessageRole.SYSTEM&&$$props.message.content===SYSTEM_MESSAGE_PLACEHOLDER?set$1(editedContent,""):$$props.message.role===MessageRole.ASSISTANT?set$1(editedContent, +get$3(rawEditContent)||$$props.message.content||""):set$1(editedContent,$$props.message.content),get$3(textareaElement)?.focus(),set$1(editedExtras,$$props.message.extra?[...$$props.message.extra]:[]),set$1(editedUploadedFiles,[],!0),setTimeout(()=>{get$3(textareaElement)&&(get$3(textareaElement).focus(),get$3(textareaElement).setSelectionRange(get$3(textareaElement).value.length,get$3(textareaElement).value.length))},0)}function handleRegenerate(modelOverride){chatActions.regenerateWithBranching( +$$props.message,modelOverride)}function handleContinue(){chatActions.continueAssistantMessage($$props.message)}function handleForkConversation(options){chatActions.forkConversation($$props.message,options)}function handleNavigateToSibling(siblingId){chatActions.navigateToSibling(siblingId)}async function handleSaveEdit(){if($$props.message.role===MessageRole.SYSTEM){const newContent=get$3(editedContent).trim();if(!newContent){const conversationDeleted=await chatStore.removeSystemPromptPlaceholder( +$$props.message.id);set$1(isEditing2,!1),conversationDeleted&&goto(ROUTES.START);return}await DatabaseService.updateMessage($$props.message.id,{content:newContent});const index2=conversationsStore.findMessageIndex($$props.message.id);index2!==-1&&conversationsStore.updateMessageAtIndex(index2,{content:newContent})}else if($$props.message.role===MessageRole.USER){const finalExtras=await getMergedExtras();chatActions.editWithBranching($$props.message,get$3(editedContent).trim(),finalExtras)}else chatActions. +editWithReplacement($$props.message,get$3(editedContent),get$3(shouldBranchAfterEdit));set$1(isEditing2,!1),set$1(shouldBranchAfterEdit,!1),set$1(editedUploadedFiles,[],!0)}async function handleSaveEditOnly(){if($$props.message.role===MessageRole.USER){const finalExtras=await getMergedExtras();chatActions.editUserMessagePreserveResponses($$props.message,get$3(editedContent).trim(),finalExtras)}set$1(isEditing2,!1),set$1(editedUploadedFiles,[],!0)}async function getMergedExtras(){if(get$3(editedUploadedFiles). +length===0)return get$3(editedExtras);const plainFiles=snapshot(get$3(editedUploadedFiles)),newExtras=(await parseFilesToMessageExtras(plainFiles))?.extras||[];return[...get$3(editedExtras),...newExtras]}function handleShowDeleteDialogChange(show){set$1(showDeleteDialog,show,!0)}var div=root$1f(),node2=child(div);{var consequent=$$anchor2=>{ChatMessageSystem($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)},get message(){return $$props.message},onConfirmDelete:handleConfirmDelete, +onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()},get textareaElement(){return get$3(textareaElement)},set textareaElement($$value){set$1(textareaElement,$$value,!0)}})},consequent_1=$$anchor2=>{ChatMessageMcpPrompt($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)}, +get message(){return $$props.message},get mcpPrompt(){return get$3(mcpPromptExtra)},onConfirmDelete:handleConfirmDelete,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()}})},consequent_2=$$anchor2=>{ChatMessageUser($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)}, +get message(){return $$props.message},onConfirmDelete:handleConfirmDelete,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()}})},alternate=$$anchor2=>{ChatMessageAssistant($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)}, +get isLastAssistantMessage(){return isLastAssistantMessage()},get message(){return $$props.message},get toolMessages(){return toolMessages()},get messageContent(){return $$props.message.content},onConfirmDelete:handleConfirmDelete,onContinue:handleContinue,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onRegenerate:handleRegenerate,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){ +return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()},get textareaElement(){return get$3(textareaElement)},set textareaElement($$value){set$1(textareaElement,$$value,!0)}})};if_block(node2,$$render=>{$$props.message.role===MessageRole.SYSTEM?$$render(consequent):get$3(mcpPromptExtra)?$$render(consequent_1,1):$$props.message.role===MessageRole.USER?$$render(consequent_2,2):$$render(alternate,-1)})}reset(div),action(div,$$node=>fadeInView?.($$node)),append($$anchor,div),pop()}var root_2$U=from_html( +'
'),root_7$n=from_html('
Receiving arguments...
'),root_8$o=from_html('
Response was truncated
'),root_4$w=from_html('
Arguments:
'),root_11$b=from_html( +'
Arguments:
'),root_13$b=from_html('
Waiting for result...
'),root_16$3=from_html(''),root_15$8=from_html('
',1),root_14$4=from_html('
'),root_17$6=from_html('
No output
'),root_10$a=from_html('
Result:
',1),root_19$7=from_html('
'),root_21$1=from_html('
'),root_25$2=from_html('
'),root_23$3=from_html('
'),root$1e=from_html('
');function ChatMessageAgenticContent($$anchor,$$props){push$1($$props,!0);const renderSection=($$anchor2,section=noop$3,index2=noop$3)=>{ +var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor3=>{var div=root_2$U(),node_1=child(div);{let $0=user_derived(()=>$$props.message?.extra);MarkdownContent(node_1,{get content(){return section().content},get attachments(){return get$3($0)}})}reset(div),append($$anchor3,div)},consequent_4=$$anchor3=>{const streamingIcon=user_derived(()=>(isStreaming(),Loader_circle)),streamingIconClass=user_derived(()=>isStreaming()?"h-4 w-4 animate-spin":"h-4 w-4");{let $0=user_derived( +()=>isExpanded(index2(),section())),$1=user_derived(()=>section().toolName||"Tool call"),$2=user_derived(()=>isStreaming()?"":"incomplete");CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return get$3(streamingIcon)},get iconClass(){return get$3(streamingIconClass)},get title(){return get$3($1)},get subtitle(){return get$3($2)},get isStreaming(){return isStreaming()},onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var div_1=root_4$w(), +div_2=child(div_1),node_2=sibling(child(div_2),2);{var consequent_1=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animate-spin"})};if_block(node_2,$$render=>{isStreaming()&&$$render(consequent_1)})}reset(div_2);var node_3=sibling(div_2,2);{var consequent_2=$$anchor5=>{{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode($$anchor5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}},consequent_3=$$anchor5=>{ +var div_3=root_7$n();append($$anchor5,div_3)},alternate=$$anchor5=>{var div_4=root_8$o();append($$anchor5,div_4)};if_block(node_3,$$render=>{section().toolArgs?$$render(consequent_2):isStreaming()?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_1),append($$anchor4,div_1)},$$slots:{default:!0}})}},consequent_10=$$anchor3=>{const isPending=user_derived(()=>section().type===AgenticSectionType.TOOL_CALL_PENDING),toolIcon=user_derived(()=>get$3(isPending)?Loader_circle:Wrench),toolIconClass=user_derived( +()=>get$3(isPending)?"h-4 w-4 animate-spin":"h-4 w-4");{let $0=user_derived(()=>isExpanded(index2(),section())),$1=user_derived(()=>section().toolName||""),$2=user_derived(()=>get$3(isPending)?"executing...":void 0);CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return get$3(toolIcon)},get iconClass(){return get$3(toolIconClass)},get title(){return get$3($1)},get subtitle(){return get$3($2)},get isStreaming(){return get$3(isPending)},onToggle:()=>toggleExpanded( +index2(),section()),children:($$anchor4,$$slotProps)=>{var fragment_5=root_10$a(),node_4=first_child(fragment_5);{var consequent_5=$$anchor5=>{var div_5=root_11$b(),node_5=sibling(child(div_5),2);{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode(node_5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}reset(div_5),append($$anchor5,div_5)};if_block(node_4,$$render=>{section().toolArgs&§ion().toolArgs!== +"{}"&&$$render(consequent_5)})}var div_6=sibling(node_4,2),div_7=child(div_6),node_6=sibling(child(div_7),2);{var consequent_6=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animate-spin"})};if_block(node_6,$$render=>{get$3(isPending)&&$$render(consequent_6)})}reset(div_7);var node_7=sibling(div_7,2);{var consequent_7=$$anchor5=>{var div_8=root_13$b();append($$anchor5,div_8)},consequent_9=$$anchor5=>{var div_9=root_14$4();each(div_9,21,()=>section().parsedLines,index$2,($$anchor6,line)=>{var fragment_7=root_15$8(), +div_10=first_child(fragment_7),text2=child(div_10,!0);reset(div_10);var node_8=sibling(div_10,2);{var consequent_8=$$anchor7=>{var img=root_16$3();template_effect(()=>{set_attribute(img,"src",get$3(line).image.base64Url),set_attribute(img,"alt",get$3(line).image.name)}),append($$anchor7,img)};if_block(node_8,$$render=>{get$3(line).image&&$$render(consequent_8)})}template_effect(()=>set_text(text2,get$3(line).text)),append($$anchor6,fragment_7)}),reset(div_9),append($$anchor5,div_9)},alternate_1=$$anchor5=>{ +var div_11=root_17$6();append($$anchor5,div_11)};if_block(node_7,$$render=>{get$3(isPending)?$$render(consequent_7):section().toolResult?$$render(consequent_9,1):$$render(alternate_1,-1)})}reset(div_6),append($$anchor4,fragment_5)},$$slots:{default:!0}})}},consequent_11=$$anchor3=>{{let $0=user_derived(()=>isExpanded(index2(),section()));CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return Brain},title:"Reasoning",onToggle:()=>toggleExpanded(index2(),section()), +children:($$anchor4,$$slotProps)=>{var div_12=root_19$7(),div_13=child(div_12),text_1=child(div_13,!0);reset(div_13),reset(div_12),template_effect(()=>set_text(text_1,section().content)),append($$anchor4,div_12)},$$slots:{default:!0}})}},consequent_12=$$anchor3=>{const reasoningTitle=user_derived(()=>isStreaming()?"Reasoning...":"Reasoning"),reasoningSubtitle=user_derived(()=>isStreaming()?"":"incomplete");{let $0=user_derived(()=>isExpanded(index2(),section()));CollapsibleContentBlock($$anchor3, +{get open(){return get$3($0)},class:"my-2",get icon(){return Brain},get title(){return get$3(reasoningTitle)},get subtitle(){return get$3(reasoningSubtitle)},get isStreaming(){return isStreaming()},onToggle:()=>toggleExpanded(index2(),section()),children:($$anchor4,$$slotProps)=>{var div_14=root_21$1(),div_15=child(div_14),text_2=child(div_15,!0);reset(div_15),reset(div_14),template_effect(()=>set_text(text_2,section().content)),append($$anchor4,div_14)},$$slots:{default:!0}})}};if_block(node2,$$render=>{ +section().type===AgenticSectionType.TEXT?$$render(consequent):section().type===AgenticSectionType.TOOL_CALL_STREAMING?$$render(consequent_4,1):section().type===AgenticSectionType.TOOL_CALL||section().type===AgenticSectionType.TOOL_CALL_PENDING?$$render(consequent_10,2):section().type===AgenticSectionType.REASONING?$$render(consequent_11,3):section().type===AgenticSectionType.REASONING_PENDING&&$$render(consequent_12,4)})}append($$anchor2,fragment)};let toolMessages=prop($$props,"toolMessages",19, +()=>[]),isStreaming=prop($$props,"isStreaming",3,!1),isLastAssistantMessage=prop($$props,"isLastAssistantMessage",3,!1),highlightTurns=prop($$props,"highlightTurns",3,!1),expandedStates=proxy({});const showToolCallInProgress=user_derived(()=>config$1().showToolCallInProgress),showThoughtInProgress=user_derived(()=>config$1().showThoughtInProgress);let permissionDismissed=state$1(!1);const pendingPermission=user_derived(()=>isStreaming()&&isLastAssistantMessage()?agenticPendingPermissionRequest($$props. +message.convId):null);let prevPendingRef=null;user_effect(()=>{get$3(pendingPermission)!==prevPendingRef&&(prevPendingRef=get$3(pendingPermission),get$3(pendingPermission)&&set$1(permissionDismissed,!1))});function handlePermission(decision){set$1(permissionDismissed,!0),agenticResolvePermission($$props.message.convId,decision)}let continueDismissed=state$1(!1);const pendingContinue=user_derived(()=>isStreaming()&&isLastAssistantMessage()?agenticPendingContinueRequest($$props.message.convId):!1); +let prevContinueRef=!1;user_effect(()=>{get$3(pendingContinue)!==prevContinueRef&&(prevContinueRef=get$3(pendingContinue),get$3(pendingContinue)&&set$1(continueDismissed,!1))});function handleContinue(shouldContinue){set$1(continueDismissed,!0),agenticResolveContinue($$props.message.convId,shouldContinue)}const sections=user_derived(()=>deriveAgenticSections($$props.message,toolMessages(),[],isStreaming())),sectionsParsed=user_derived(()=>get$3(sections).map(section=>({...section,parsedLines:section. +toolResult?parseToolResultWithImages(section.toolResult,section.toolResultExtras||$$props.message?.extra):[]}))),turnGroups=user_derived(()=>{const turns=[];let currentTurn=[],currentIndices=[],prevWasTool=!1;for(let i=0;i0&&(turns. +push({sections:currentTurn,flatIndices:currentIndices}),currentTurn=[],currentIndices=[]),currentTurn.push(section),currentIndices.push(i),prevWasTool=isTool}return currentTurn.length>0&&turns.push({sections:currentTurn,flatIndices:currentIndices}),turns});function getDefaultExpanded(section){return section.type===AgenticSectionType.TOOL_CALL_PENDING||section.type===AgenticSectionType.TOOL_CALL_STREAMING?get$3(showToolCallInProgress):section.type===AgenticSectionType.REASONING_PENDING?get$3(showThoughtInProgress): +!1}function isExpanded(index2,section){return expandedStates[index2]!==void 0?expandedStates[index2]:getDefaultExpanded(section)}function toggleExpanded(index2,section){const currentState=isExpanded(index2,section);expandedStates[index2]=!currentState}function buildTurnAgenticTimings(stats){return{turns:1,toolCallsCount:stats.toolCalls.length,toolsMs:stats.toolsMs,toolCalls:stats.toolCalls,llm:stats.llm}}var div_16=root$1e(),node_9=child(div_16);{var consequent_14=$$anchor2=>{var fragment_10=comment$2(), +node_10=first_child(fragment_10);each(node_10,17,()=>get$3(turnGroups),index$2,($$anchor3,turn,turnIndex)=>{const turnStats=user_derived(()=>$$props.message?.timings?.agentic?.perTurn?.[turnIndex]);var div_17=root_23$3(),span=child(div_17);span.textContent=`Turn ${turnIndex+1}`;var node_11=sibling(span,2);each(node_11,19,()=>get$3(turn).sections,(section,sIdx)=>get$3(turn).flatIndices[sIdx],($$anchor4,section,sIdx)=>{renderSection($$anchor4,()=>get$3(section),()=>get$3(turn).flatIndices[get$3(sIdx)])}); +var node_12=sibling(node_11,2);{var consequent_13=$$anchor4=>{var div_18=root_25$2(),node_13=child(div_18);{let $0=user_derived(()=>get$3(turnStats).toolCalls.length>0?buildTurnAgenticTimings(get$3(turnStats)):void 0);ChatMessageStatistics(node_13,{get promptTokens(){return get$3(turnStats).llm.prompt_n},get promptMs(){return get$3(turnStats).llm.prompt_ms},get predictedTokens(){return get$3(turnStats).llm.predicted_n},get predictedMs(){return get$3(turnStats).llm.predicted_ms},get agenticTimings(){ +return get$3($0)},get initialView(){return ChatMessageStatsView.GENERATION},hideSummary:!0})}reset(div_18),append($$anchor4,div_18)};if_block(node_12,$$render=>{get$3(turnStats)&&$$render(consequent_13)})}reset(div_17),append($$anchor3,div_17)}),append($$anchor2,fragment_10)},alternate_2=$$anchor2=>{var fragment_12=comment$2(),node_14=first_child(fragment_12);each(node_14,17,()=>get$3(sectionsParsed),index$2,($$anchor3,section,index2)=>{renderSection($$anchor3,()=>get$3(section),()=>index2)}),append( +$$anchor2,fragment_12)};if_block(node_9,$$render=>{highlightTurns()&&get$3(turnGroups).length>1?$$render(consequent_14):$$render(alternate_2,-1)})}var node_15=sibling(node_9,2);{var consequent_15=$$anchor2=>{ChatMessageActionCardPermissionRequest($$anchor2,{get toolName(){return get$3(pendingPermission).toolName},get serverLabel(){return get$3(pendingPermission).serverLabel},onDecision:handlePermission})};if_block(node_15,$$render=>{get$3(pendingPermission)&&!get$3(permissionDismissed)&&$$render( +consequent_15)})}var node_16=sibling(node_15,2);{var consequent_16=$$anchor2=>{ChatMessageActionCardContinueRequest($$anchor2,{onDecision:handleContinue})};if_block(node_16,$$render=>{get$3(pendingContinue)&&!get$3(continueDismissed)&&$$render(consequent_16)})}reset(div_16),append($$anchor,div_16),pop()}var root$1d=from_html("
");function Button_group_root($$anchor,$$props){push$1($$props,!0);let restProps=rest_props($$props,["$$slots","$$events","$$legacy","class","children"]);var div=root$1d(); +attribute_effect(div,$0=>({class:$0,...restProps}),[()=>cn$1("flex items-center [&>*:first-child]:rounded-r-none [&>*:last-child]:rounded-l-none [&>*:not(:first-child):not(:last-child)]:rounded-none",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children),reset(div),append($$anchor,div),pop()}var root$1c=from_html("
");function Button_group_separator($$anchor,$$props){push$1($$props,!0);let restProps=rest_props($$props,["$$slots","$$events","$$legacy"]);var div=root$1c(); +attribute_effect(div,$0=>({class:$0,...restProps}),[()=>cn$1("shrink-0 self-stretch bg-border","w-px")]),append($$anchor,div),pop()}var root_2$T=from_html('from ',1),root_1$B=from_html('Allow use of ?',1),root_5$n=from_html(" ",1),root_10$9=from_html("Always allow
 
tool",1),root_9$h=from_html(" ",1),root_4$v=from_html(" ",1),root_3$K=from_html(" ",1);function ChatMessageActionCardPermissionRequest($$anchor,$$props){ +push$1($$props,!0),ChatMessageActionCard($$anchor,{get icon(){return Shield_question},message:$$anchor2=>{next$1();var fragment_1=root_1$B(),span=sibling(first_child(fragment_1)),text2=child(span,!0);reset(span);var node2=sibling(span,2);{var consequent=$$anchor3=>{var fragment_2=root_2$T(),span_1=sibling(first_child(fragment_2)),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,$$props.serverLabel)),append($$anchor3,fragment_2)};if_block(node2,$$render=>{$$props.serverLabel&& +$$render(consequent)})}next$1(),template_effect(()=>set_text(text2,$$props.toolName)),append($$anchor2,fragment_1)},actions:$$anchor2=>{var fragment_3=root_3$K(),node_1=first_child(fragment_3);component(node_1,()=>Root$4,($$anchor3,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_4=root_4$v(),node_2=first_child(fragment_4);component(node_2,()=>Button_group_root,($$anchor5,ButtonGroup_Root)=>{ButtonGroup_Root($$anchor5,{class:"overflow-hidden round\ +ed-md bg-foreground text-white shadow-sm dark:bg-secondary dark:text-foreground",children:($$anchor6,$$slotProps2)=>{var fragment_5=root_5$n(),node_3=first_child(fragment_5);Button(node_3,{class:"rounded-none! shadow-none!",size:"sm",onclick:()=>$$props.onDecision(ToolPermissionDecision.ONCE),children:($$anchor7,$$slotProps3)=>{next$1();var text_2=text$8("Allow once");append($$anchor7,text_2)},$$slots:{default:!0}});var node_4=sibling(node_3,2);component(node_4,()=>Button_group_separator,($$anchor7,ButtonGroup_Separator)=>{ +ButtonGroup_Separator($$anchor7,{})});var node_5=sibling(node_4,2);component(node_5,()=>Dropdown_menu_trigger,($$anchor7,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor7,{children:($$anchor8,$$slotProps3)=>{Button($$anchor8,{size:"sm",class:"rounded-none! !ps-2 shadow-none!",children:($$anchor9,$$slotProps4)=>{Chevron_down($$anchor9,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}})},$$slots:{default:!0}})}),append($$anchor6,fragment_5)},$$slots:{default:!0}})});var node_6=sibling(node_2,2);component( +node_6,()=>Dropdown_menu_content,($$anchor5,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor5,{align:"start",class:"min-w-[8rem]",children:($$anchor6,$$slotProps2)=>{var fragment_8=root_9$h(),node_7=first_child(fragment_8);component(node_7,()=>Dropdown_menu_item,($$anchor7,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor7,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS),children:($$anchor8,$$slotProps3)=>{next$1();var fragment_9=root_10$9(),pre=sibling(first_child(fragment_9)), +text_3=child(pre,!0);reset(pre),next$1(),template_effect(()=>set_text(text_3,$$props.toolName)),append($$anchor8,fragment_9)},$$slots:{default:!0}})});var node_8=sibling(node_7,2);{var consequent_1=$$anchor7=>{var fragment_10=comment$2(),node_9=first_child(fragment_10);component(node_9,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor8,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS_SERVER),children:($$anchor9,$$slotProps3)=>{next$1();var text_4=text$8(); +template_effect(()=>set_text(text_4,`Always allow all tools from ${$$props.serverLabel??""}`)),append($$anchor9,text_4)},$$slots:{default:!0}})}),append($$anchor7,fragment_10)},alternate=$$anchor7=>{const source2=user_derived(()=>toolsStore.getToolSource($$props.toolName)),providerName=user_derived(()=>get$3(source2)===ToolSource.BUILTIN?TOOL_SERVER_LABELS[ToolSource.BUILTIN]:get$3(source2)===ToolSource.CUSTOM?TOOL_SERVER_LABELS[ToolSource.CUSTOM]:"MCP Tools");var fragment_12=comment$2(),node_10=first_child( +fragment_12);component(node_10,()=>Dropdown_menu_item,($$anchor8,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor8,{onclick:()=>$$props.onDecision(ToolPermissionDecision.ALWAYS_SERVER),children:($$anchor9,$$slotProps3)=>{next$1();var text_5=text$8();template_effect(()=>set_text(text_5,`Approve all tools from ${get$3(providerName)??""}`)),append($$anchor9,text_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_12)};if_block(node_8,$$render=>{$$props.serverLabel?$$render(consequent_1):$$render( +alternate,-1)})}append($$anchor6,fragment_8)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})});var node_11=sibling(node_1,2);Button(node_11,{variant:"destructive",size:"sm",class:"text-destructive hover:text-destructive",onclick:()=>$$props.onDecision(ToolPermissionDecision.DENY),children:($$anchor3,$$slotProps)=>{next$1();var text_6=text$8("Deny");append($$anchor3,text_6)},$$slots:{default:!0}}),append($$anchor2,fragment_3)}}),pop()}var root$1b=from_html('
');function ChatMessageActionCard($$anchor,$$props){var div=root$1b(),div_1=child(div),node2=child(div_1);component(node2,()=>$$props.icon,($$anchor2,IconComponent_1)=>{IconComponent_1($$anchor2,{class:"h-4 w-4 shrink-0 text-muted-foreground"})});var span=sibling(node2,2),node_1=child(span);snippet(node_1, +()=>$$props.message),reset(span),reset(div_1);var div_2=sibling(div_1,2),node_2=child(div_2);snippet(node_2,()=>$$props.actions),reset(div_2),reset(div),append($$anchor,div)}var root_2$S=from_html(" ",1);function ChatMessageActionCardContinueRequest($$anchor,$$props){push$1($$props,!0),ChatMessageActionCard($$anchor,{get icon(){return Rotate_cw},message:$$anchor2=>{next$1();var text2=text$8("Agentic turn limit reached. Continue?");append($$anchor2,text2)},actions:$$anchor2=>{var fragment_1=root_2$S(), +node2=first_child(fragment_1);Button(node2,{size:"sm",onclick:()=>$$props.onDecision(!0),children:($$anchor3,$$slotProps)=>{next$1();var text_1=text$8("Continue");append($$anchor3,text_1)},$$slots:{default:!0}});var node_1=sibling(node2,2);Button(node_1,{variant:"destructive",size:"sm",class:"text-destructive hover:text-destructive",onclick:()=>$$props.onDecision(!1),children:($$anchor3,$$slotProps)=>{next$1();var text_2=text$8("Stop");append($$anchor3,text_2)},$$slots:{default:!0}}),append($$anchor2, +fragment_1)}}),pop()}var root_6$o=from_html('
Show raw output
'),root_7$m=from_html('
'),root$1a=from_html('
<\ +!>
',1);function ChatMessageActionIcons($$anchor,$$props){push$1($$props,!0);let siblingInfo=prop($$props,"siblingInfo",3,null),showDeleteDialog=prop($$props,"showDeleteDialog",7),showRawOutputSwitch=prop($$props,"showRawOutputSwitch",3,!1),rawOutputEnabled=prop($$props,"rawOutputEnabled",3,!1),showForkDialog=state$1(!1),forkName=state$1(""),forkIncludeAttachments=state$1(!0);function handleConfirmDelete(){$$props.onConfirmDelete(),$$props.onShowDeleteDialogChange(!1)}function handleOpenForkDialog(){ const conv=activeConversation();set$1(forkName,`Fork of ${conv?.name??"Conversation"}`),set$1(forkIncludeAttachments,!0),set$1(showForkDialog,!0)}function handleConfirmFork(){$$props.onForkConversation?.({name:get$3(forkName).trim(),includeAttachments:get$3(forkIncludeAttachments)}),set$1(showForkDialog,!1)}var fragment=root$1a(),div=first_child(fragment),div_1=child(div),node2=child(div_1);{var consequent=$$anchor2=>{ChatMessageActionIconsBranchingControls($$anchor2,{get siblingInfo(){return siblingInfo()}, get onNavigateToSibling(){return $$props.onNavigateToSibling}})};if_block(node2,$$render=>{siblingInfo()&&siblingInfo().totalSiblings>1&&$$render(consequent)})}var div_2=sibling(node2,2),node_1=child(div_2);ActionIcon(node_1,{get icon(){return Copy},tooltip:"Copy",get onclick(){return $$props.onCopy}});var node_2=sibling(node_1,2);{var consequent_1=$$anchor2=>{ActionIcon($$anchor2,{get icon(){return Square_pen},tooltip:"Edit",get onclick(){return $$props.onEdit}})};if_block(node_2,$$render=>{$$props. onEdit&&$$render(consequent_1)})}var node_3=sibling(node_2,2);{var consequent_2=$$anchor2=>{ActionIcon($$anchor2,{get icon(){return Refresh_cw},tooltip:"Regenerate",onclick:()=>$$props.onRegenerate()})};if_block(node_3,$$render=>{$$props.role===MessageRole.ASSISTANT&&$$props.onRegenerate&&$$render(consequent_2)})}var node_4=sibling(node_3,2);{var consequent_3=$$anchor2=>{ActionIcon($$anchor2,{get icon(){return Arrow_right},tooltip:"Continue",get onclick(){return $$props.onContinue}})};if_block(node_4, diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte index 758a84f9111..4d0b302d272 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte @@ -179,7 +179,7 @@ isEditing = false; // If canceling a new system message with placeholder content, remove it without deleting children - if (message.role === MessageRole.SYSTEM) { + if (message.role === MessageRole.SYSTEM && message.content === SYSTEM_MESSAGE_PLACEHOLDER) { const conversationDeleted = await chatStore.removeSystemPromptPlaceholder(message.id); if (conversationDeleted) { From 2dfeca31cc34ffa03c510e75438455d9c6773ffb Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Wed, 13 May 2026 16:39:36 +0200 Subject: [PATCH 090/158] webui: Deduplicate model aliases in data + handle single/multiple aliases in UI (#22979) * fix: Deduplicate aliases + display single alias instead of default name or 2+ aliases as tags * refactor: Address review comments --- tools/server/public/bundle.js | 4512 +++++++++-------- .../lib/components/app/models/ModelId.svelte | 21 +- 2 files changed, 2271 insertions(+), 2262 deletions(-) diff --git a/tools/server/public/bundle.js b/tools/server/public/bundle.js index c2a9942a797..5a42071e5f2 100644 --- a/tools/server/public/bundle.js +++ b/tools/server/public/bundle.js @@ -5160,13 +5160,13 @@ d-md border px-2 py-0.75 text-sm transition-colors",getStatusClass($$props.attac var div=root_7$u(),node_6=child(div);{var consequent_3=$$anchor6=>{var img=root_8$t();template_effect(()=>{set_attribute(img,"alt",$$props.attachment.resource.serverName),set_attribute(img,"src",get$3(favicon))}),event("error",img,e=>{e.currentTarget.style.display="none"}),replay_events(img),append($$anchor6,img)};if_block(node_6,$$render=>{get$3(favicon)&&$$render(consequent_3)})}var span_1=sibling(node_6,2),text_1=child(span_1,!0);reset(span_1),reset(div),template_effect(()=>set_text(text_1,get$3( serverName))),append($$anchor5,div)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}delegate(["click"]);const info$1=($$anchor,text2=noop$3)=>{var fragment=comment$2(),node_2=first_child(fragment);{var consequent_1=$$anchor2=>{var span=root_6$u(),text_3=child(span,!0);reset(span),template_effect(()=>set_text(text_3,text2())),append($$anchor2,span)};if_block(node_2,$$render=>{text2()&&$$render(consequent_1)})}append($$anchor,fragment)}; var root_2$19=from_html("
"),root_1$13=from_html('
'),root_3$Y=from_html('
'),root_4$E=from_html('
'),root_6$u=from_html(' '),root_9$m=from_html('
'),root_11$f=from_html(' ',1),root_7$t=from_html(''),root_13$f=from_html('');function ChatAttachmentsListItemThumbnailFile($$anchor,$$props){ +iv class="flex min-w-0 flex-1 flex-col items-start text-left">
'),root_11$g=from_html(' ',1),root_7$t=from_html(''),root_13$f=from_html('');function ChatAttachmentsListItemThumbnailFile($$anchor,$$props){ push$1($$props,!0);const textPreview=($$anchor2,content2=noop$3)=>{var div=root_1$13(),div_1=child(div),text_1=child(div_1,!0);reset(div_1);var node2=sibling(div_1,2);{var consequent=$$anchor3=>{var div_2=root_2$19();template_effect(()=>set_class(div_2,1,`pointer-events-none absolute right-0 bottom-0 left-0 h-4 bg-gradient-to-t from-muted to-transparent ${readonly2()?"h-6":""}`)),append($$anchor3,div_2)};if_block(node2,$$render=>{content2().length>150&&$$render(consequent)})}reset(div),template_effect( $0=>{set_class(div_1,1,`font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-muted-foreground ${readonly2()?"":"max-h-3rem line-height-1.2"}`),set_text(text_1,$0)},[()=>getPreviewText(content2())]),append($$anchor2,div)},removeButton=$$anchor2=>{var div_3=root_3$Y(),node_1=child(div_3);ActionIcon(node_1,{get icon(){return X},tooltip:"Remove",stopPropagationOnClick:!0,onclick:()=>$$props.onRemove?.($$props.id)}),reset(div_3),append($$anchor2,div_3)},fileIcon=$$anchor2=>{var div_4=root_4$E(), text_2=child(div_4,!0);reset(div_4),template_effect(()=>set_text(text_2,get$3(fileTypeLabel))),append($$anchor2,div_4)};let className=prop($$props,"class",3,""),readonly2=prop($$props,"readonly",3,!1),isPdf=user_derived(()=>isPdfFile$1($$props.attachment,$$props.uploadedFile)),isPdfWithContent=user_derived(()=>get$3(isPdf)&&!!$$props.textContent),isText=user_derived(()=>isTextFile($$props.attachment,$$props.uploadedFile)),isTextWithContent=user_derived(()=>get$3(isText)&&!!$$props.textContent),fileTypeLabel=user_derived( ()=>{if($$props.uploadedFile?.type)return getFileTypeLabel($$props.uploadedFile.type);if($$props.attachment){if("mimeType"in $$props.attachment&&$$props.attachment.mimeType)return getFileTypeLabel($$props.attachment.mimeType);if($$props.attachment.type)return getFileTypeLabel($$props.attachment.type)}return getFileTypeLabel($$props.name)}),pdfProcessingMode=user_derived(()=>$$props.attachment?.type===AttachmentType.PDF?$$props.attachment.processedAsImages?"Sent as Image":"Sent as Text":null);var fragment_1=comment$2(), node_3=first_child(fragment_1);{var consequent_6=$$anchor2=>{var button=root_7$t(),node_4=child(button);{var consequent_2=$$anchor3=>{removeButton($$anchor3)};if_block(node_4,$$render=>{readonly2()||$$render(consequent_2)})}var div_5=sibling(node_4,2),node_5=child(div_5);{var consequent_4=$$anchor3=>{var div_6=root_9$m(),div_7=child(div_6),span_1=child(div_7),text_4=child(span_1,!0);reset(span_1);var node_6=sibling(span_1,2);{let $0=user_derived(()=>get$3(pdfProcessingMode)||($$props.size?formatFileSize( -$$props.size):void 0));info$1(node_6,()=>get$3($0))}var node_7=sibling(node_6,2);{var consequent_3=$$anchor4=>{textPreview($$anchor4,()=>$$props.textContent)};if_block(node_7,$$render=>{$$props.textContent&&$$render(consequent_3)})}reset(div_7),reset(div_6),template_effect(()=>set_text(text_4,$$props.name)),append($$anchor3,div_6)},alternate=$$anchor3=>{var fragment_4=root_11$f(),span_2=first_child(fragment_4),text_5=child(span_2,!0);reset(span_2);var node_8=sibling(span_2,2);{var consequent_5=$$anchor4=>{ +$$props.size):void 0));info$1(node_6,()=>get$3($0))}var node_7=sibling(node_6,2);{var consequent_3=$$anchor4=>{textPreview($$anchor4,()=>$$props.textContent)};if_block(node_7,$$render=>{$$props.textContent&&$$render(consequent_3)})}reset(div_7),reset(div_6),template_effect(()=>set_text(text_4,$$props.name)),append($$anchor3,div_6)},alternate=$$anchor3=>{var fragment_4=root_11$g(),span_2=first_child(fragment_4),text_5=child(span_2,!0);reset(span_2);var node_8=sibling(span_2,2);{var consequent_5=$$anchor4=>{ textPreview($$anchor4,()=>$$props.textContent)};if_block(node_8,$$render=>{$$props.textContent&&$$render(consequent_5)})}template_effect(()=>set_text(text_5,$$props.name)),append($$anchor3,fragment_4)};if_block(node_5,$$render=>{readonly2()?$$render(consequent_4):$$render(alternate,-1)})}reset(div_5),reset(button),template_effect(()=>{set_attribute(button,"aria-label",readonly2()?`Preview ${$$props.name}`:void 0),set_class(button,1,`rounded-lg border border-border bg-muted p-3 ${className()??""}\ cursor-pointer ${readonly2()?"w-full max-w-2xl transition-shadow hover:shadow-md":`group relative text-left ${$$props.textContent?"max-h-24 max-w-72":"max-w-36"}`} overflow-hidden`),set_class(div_5,1,clsx([!readonly2()&&"pr-8","overflow-hidden"]))}),delegated("click",button,function(...$$args){$$props.onclick?.apply(this,$$args)}),append($$anchor2,button)},alternate_1=$$anchor2=>{var button_1=root_13$f(),node_9=child(button_1);fileIcon(node_9);var div_8=sibling(node_9,2),span_3=child(div_8),text_6=child( span_3,!0);reset(span_3);var node_10=sibling(span_3,2);{let $0=user_derived(()=>get$3(pdfProcessingMode)||($$props.size?formatFileSize($$props.size):void 0));info$1(node_10,()=>get$3($0))}reset(div_8);var node_11=sibling(div_8,2);{var consequent_7=$$anchor3=>{removeButton($$anchor3)};if_block(node_11,$$render=>{readonly2()||$$render(consequent_7)})}reset(button_1),template_effect(()=>{set_class(button_1,1,`group flex items-center gap-3 rounded-lg border border-border bg-muted p-3 ${className()?? @@ -6574,7 +6574,7 @@ return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2 DropdownMenuPrimitive_SubTrigger($$anchor2,spread_props({"data-slot":"dropdown-menu-sub-trigger",get"data-inset"(){return $$props.inset},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$P(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_right(node_2,{class:"ml-auto size-4"}),append($$anchor3,fragment_1)},$$slots:{default:!0}}))})} append($$anchor,fragment),pop()}const Sub=Menu_sub,Root$4=Menu;function useAttachmentMenu(getFlags,getCallbacks,close2){const modalityFlags=user_derived(getFlags),callbacks=user_derived(()=>{const cbs=getCallbacks(),wrap2=fn=>()=>{close2(),fn?.()};return{[AttachmentAction.FILE_UPLOAD]:wrap2(cbs.onFileUpload),[AttachmentAction.SYSTEM_PROMPT_CLICK]:wrap2(cbs.onSystemPromptClick),[AttachmentAction.MCP_PROMPT_CLICK]:wrap2(cbs.onMcpPromptClick),[AttachmentAction.MCP_RESOURCES_CLICK]:wrap2(cbs.onMcpResourcesClick)}}); function isItemEnabled(enabledWhen){return!enabledWhen||enabledWhen==="always"?!0:!!get$3(modalityFlags)[enabledWhen]}function isItemVisible(visibleWhen){return visibleWhen?!!get$3(modalityFlags)[visibleWhen]:!0}function getSystemMessageTooltip(){return page$1.params.id?"Inject custom system message at the beginning of the conversation":"Add custom system message for a new conversation"}return{get callbacks(){return get$3(callbacks)},isItemEnabled,isItemVisible,getSystemMessageTooltip}}var root_6$s=from_html( -" ",1),root_10$f=from_html(" ",1),root_11$e=from_html("

"),root_8$r=from_html(" ",1),root_16$6=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$d=from_html(" ",1),root_22$2=from_html(" ",1),root_23$5=from_html("

"),root_20$5=from_html(" ",1),root_26$2=from_html(" ",1),root_3$S=from_html(" \ +" ",1),root_10$f=from_html(" ",1),root_11$f=from_html("

"),root_8$r=from_html(" ",1),root_16$6=from_html(" ",1),root_17$8=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_13$d=from_html(" ",1),root_22$2=from_html(" ",1),root_23$5=from_html("

"),root_20$5=from_html(" ",1),root_26$2=from_html(" ",1),root_3$S=from_html(" \ ",1),root_1$O=from_html(" ",1),root$1t=from_html("
");function ChatFormActionAddDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),dropdownOpen=state$1(!1); function handleMcpSettingsClick(){set$1(dropdownOpen,!1),$$props.onMcpSettingsClick?.()}const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(),hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(dropdownOpen, !1)});var div=root$1t(),node2=child(div);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)},children:($$anchor3,$$slotProps)=>{var fragment=root_1$O(),node_1=first_child(fragment);component(node_1,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{DropdownMenu_Trigger($$anchor4,{name:"Attach files",get disabled(){return disabled()},children:($$anchor5,$$slotProps2)=>{ @@ -6583,7 +6583,7 @@ const enabled=user_derived(()=>attachmentMenu.isItemEnabled(get$3(item).enabledW action](),children:($$anchor9,$$slotProps3)=>{var fragment_5=root_6$s(),node_7=first_child(fragment_5);component(node_7,()=>get$3(item).icon,($$anchor10,item_icon)=>{item_icon($$anchor10,{class:"h-4 w-4"})});var span=sibling(node_7,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,get$3(item).label)),append($$anchor9,fragment_5)},$$slots:{default:!0}})})}append($$anchor7,fragment_4)},consequent_1=$$anchor7=>{var fragment_6=comment$2(),node_8=first_child(fragment_6);component( node_8,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_7=root_8$r(),node_9=first_child(fragment_7);component(node_9,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{class:"w-full",children:($$anchor11,$$slotProps4)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);{let $0=user_derived(()=>get$3(item).class??"");component(node_10,()=>Dropdown_menu_item, ($$anchor12,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor12,{get class(){return`${get$3($0)??""} flex cursor-pointer items-center gap-2`},disabled:!0,children:($$anchor13,$$slotProps5)=>{var fragment_9=root_10$f(),node_11=first_child(fragment_9);component(node_11,()=>get$3(item).icon,($$anchor14,item_icon_1)=>{item_icon_1($$anchor14,{class:"h-4 w-4"})});var span_1=sibling(node_11,2),text_1=child(span_1,!0);reset(span_1),template_effect(()=>set_text(text_1,get$3(item).label)),append($$anchor13, -fragment_9)},$$slots:{default:!0}})})}append($$anchor11,fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7, +fragment_9)},$$slots:{default:!0}})})}append($$anchor11,fragment_8)},$$slots:{default:!0}})});var node_12=sibling(node_9,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p2=root_11$f(),text_2=child(p2,!0);reset(p2),template_effect(()=>set_text(text_2,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7, fragment_6)};if_block(node_5,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_4,2);{var consequent_3=$$anchor6=>{var fragment_10=comment$2(),node_14=first_child(fragment_10);component(node_14,()=>Root$5,($$anchor7,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor7,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor8,$$slotProps3)=>{var fragment_11=root_13$d(),node_15=first_child( fragment_11);component(node_15,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor9,{class:"w-full",children:($$anchor10,$$slotProps4)=>{var fragment_12=comment$2(),node_16=first_child(fragment_12);component(node_16,()=>Dropdown_menu_item,($$anchor11,DropdownMenu_Item_2)=>{DropdownMenu_Item_2($$anchor11,{class:"flex cursor-pointer items-center gap-2",get onclick(){return attachmentMenu.callbacks.onFileUpload},children:($$anchor12,$$slotProps5)=>{const pdfItem=user_derived( ()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_13=comment$2(),node_17=first_child(fragment_13);{var consequent_2=$$anchor13=>{var fragment_14=root_16$6(),node_18=first_child(fragment_14);component(node_18,()=>get$3(pdfItem).icon,($$anchor14,pdfItem_icon)=>{pdfItem_icon($$anchor14,{class:"h-4 w-4"})});var span_2=sibling(node_18,2),text_3=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_3,get$3(pdfItem).label)),append($$anchor13,fragment_14)};if_block( @@ -6606,7 +6606,7 @@ default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($ x-col gap-1.5 p-4",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}function Sheet_title($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("font-semibold text-foreground",$$props.class));component( node2,()=>Dialog_title$1,($$anchor2,SheetPrimitive_Title)=>{SheetPrimitive_Title($$anchor2,spread_props({"data-slot":"sheet-title",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}function Sheet_description($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment); {let $0=user_derived(()=>cn$1("text-sm text-muted-foreground",$$props.class));component(node2,()=>Dialog_description$1,($$anchor2,SheetPrimitive_Description)=>{SheetPrimitive_Description($$anchor2,spread_props({"data-slot":"sheet-description",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)}}))})}append($$anchor,fragment),pop()}const Root$3=Dialog;var root_3$Q=from_html(" ",1),root_7$r=from_html(''),root_11$d=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(''),root_16$5=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$5=from_html(" ",1),root_20$4=from_html(''),root_21$3=from_html("

"),root_19$9=from_html(" '),root_10$e=from_html(''),root_11$e=from_html("

"),root_9$k=from_html(" ",1),root_15$9=from_html(''),root_16$5=from_html("

PDFs will be converted to text. Image-based PDFs may not work properly.

"),root_14$5=from_html(" ",1),root_20$4=from_html(''),root_21$3=from_html("

"),root_19$9=from_html(" ",1),root_23$4=from_html(''),root_2$11=from_html(' ',1),root_1$M=from_html(" ",1),root$1r=from_html("
");function ChatFormActionAddSheet($$anchor,$$props){ push$1($$props,!0);let className=prop($$props,"class",3,""),disabled=prop($$props,"disabled",3,!1),hasAudioModality=prop($$props,"hasAudioModality",3,!1),hasVisionModality=prop($$props,"hasVisionModality",3,!1),hasMcpPromptsSupport=prop($$props,"hasMcpPromptsSupport",3,!1),hasMcpResourcesSupport=prop($$props,"hasMcpResourcesSupport",3,!1),sheetOpen=state$1(!1);const attachmentMenu=useAttachmentMenu(()=>({hasVisionModality:hasVisionModality(),hasAudioModality:hasAudioModality(),hasMcpPromptsSupport:hasMcpPromptsSupport(), hasMcpResourcesSupport:hasMcpResourcesSupport()}),()=>({onFileUpload:$$props.onFileUpload,onSystemPromptClick:$$props.onSystemPromptClick,onMcpPromptClick:$$props.onMcpPromptClick,onMcpResourcesClick:$$props.onMcpResourcesClick}),()=>{set$1(sheetOpen,!1)}),sheetItemClass="flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allowed disabled:opacity-50";var div=root$1r(),node2=child(div);component(node2,()=>Root$3, @@ -6616,7 +6616,7 @@ var fragment_1=root_2$11(),node_3=first_child(fragment_1);component(node_3,()=>S var fragment_3=comment$2(),node_7=first_child(fragment_3);{var consequent=$$anchor7=>{var button=root_7$r();set_class(button,1,clsx(sheetItemClass));var node_8=child(button);component(node_8,()=>get$3(item).icon,($$anchor8,item_icon)=>{item_icon($$anchor8,{class:"h-4 w-4 shrink-0"})});var span=sibling(node_8,2),text_2=child(span,!0);reset(span),reset(button),template_effect(()=>set_text(text_2,get$3(item).label)),delegated("click",button,()=>attachmentMenu.callbacks[get$3(item).action]()),append( $$anchor7,button)},consequent_1=$$anchor7=>{var fragment_4=comment$2(),node_9=first_child(fragment_4);component(node_9,()=>Root$5,($$anchor8,Tooltip_Root)=>{Tooltip_Root($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_5=root_9$k(),node_10=first_child(fragment_5);component(node_10,()=>Tooltip_trigger,($$anchor10,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_1=root_10$e();set_class(button_1, 1,clsx(sheetItemClass));var node_11=child(button_1);component(node_11,()=>get$3(item).icon,($$anchor12,item_icon_1)=>{item_icon_1($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_1=sibling(node_11,2),text_3=child(span_1,!0);reset(span_1),reset(button_1),template_effect(()=>set_text(text_3,get$3(item).label)),append($$anchor11,button_1)},$$slots:{default:!0}})});var node_12=sibling(node_10,2);component(node_12,()=>Tooltip_content,($$anchor10,Tooltip_Content)=>{Tooltip_Content($$anchor10,{side:"r\ -ight",children:($$anchor11,$$slotProps4)=>{var p2=root_11$d(),text_4=child(p2,!0);reset(p2),template_effect(()=>set_text(text_4,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_4)};if_block(node_7,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_6,2);{var consequent_3=$$anchor6=>{ +ight",children:($$anchor11,$$slotProps4)=>{var p2=root_11$e(),text_4=child(p2,!0);reset(p2),template_effect(()=>set_text(text_4,get$3(item).disabledTooltip)),append($$anchor11,p2)},$$slots:{default:!0}})}),append($$anchor9,fragment_5)},$$slots:{default:!0}})}),append($$anchor7,fragment_4)};if_block(node_7,$$render=>{get$3(enabled)?$$render(consequent):get$3(item).disabledTooltip&&$$render(consequent_1,1)})}append($$anchor6,fragment_3)});var node_13=sibling(node_6,2);{var consequent_3=$$anchor6=>{ const pdfItem=user_derived(()=>ATTACHMENT_FILE_ITEMS.find(i=>i.id===AttachmentMenuItemId.PDF));var fragment_6=comment$2(),node_14=first_child(fragment_6);{var consequent_2=$$anchor7=>{var fragment_7=comment$2(),node_15=first_child(fragment_7);component(node_15,()=>Root$5,($$anchor8,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor8,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor9,$$slotProps3)=>{var fragment_8=root_14$5(),node_16=first_child(fragment_8);component(node_16,()=>Tooltip_trigger, ($$anchor10,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor10,{children:($$anchor11,$$slotProps4)=>{var button_2=root_15$9();set_class(button_2,1,clsx(sheetItemClass));var node_17=child(button_2);component(node_17,()=>get$3(pdfItem).icon,($$anchor12,pdfItem_icon)=>{pdfItem_icon($$anchor12,{class:"h-4 w-4 shrink-0"})});var span_2=sibling(node_17,2),text_5=child(span_2,!0);reset(span_2),reset(button_2),template_effect(()=>set_text(text_5,get$3(pdfItem).label)),delegated("click",button_2,()=>attachmentMenu. callbacks[get$3(pdfItem).action]()),append($$anchor11,button_2)},$$slots:{default:!0}})});var node_18=sibling(node_16,2);component(node_18,()=>Tooltip_content,($$anchor10,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor10,{side:"right",children:($$anchor11,$$slotProps4)=>{var p_1=root_16$5();append($$anchor11,p_1)},$$slots:{default:!0}})}),append($$anchor9,fragment_8)},$$slots:{default:!0}})}),append($$anchor7,fragment_7)};if_block(node_14,$$render=>{get$3(pdfItem)&&$$render(consequent_2)})}append( @@ -6688,7 +6688,7 @@ div_7),append($$anchor11,div_7)},$$slots:{default:!0}})}),append($$anchor9,fragm wed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",$$props.class));component(node2,()=>Switch$1,($$anchor2,SwitchPrimitive_Root)=>{SwitchPrimitive_Root($$anchor2,spread_props({"data-slot":"switch",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},get checked(){return checked()},set checked($$value){checked($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(), node_1=first_child(fragment_1);{let $02=user_derived(()=>cn$1("pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"));component(node_1,()=>Switch_thumb,($$anchor4,SwitchPrimitive_Thumb)=>{SwitchPrimitive_Thumb($$anchor4,{"data-slot":"switch-thumb",get class(){return get$3($02)}})})}append( $$anchor3,fragment_1)},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}var root_3$N=from_html(" MCP Servers",1),root_7$o=from_html(" Manage MCP Servers",1),root_10$c=from_html('Error'),root_9$i=from_html(''),root_8$p=from_html('
'),root_12$8=from_html(" Add MCP Servers",1),root_11$c=from_html('
No MCP servers configured
',1),root_2$_=from_html(" ",1);function ChatFormActionAddMcpServersSubmenu($$anchor,$$props){push$1( +ot-allowed disabled:opacity-50">
'),root_8$p=from_html('
'),root_12$8=from_html(" Add MCP Servers",1),root_11$d=from_html('
No MCP servers configured
',1),root_2$_=from_html(" ",1);function ChatFormActionAddMcpServersSubmenu($$anchor,$$props){push$1( $$props,!0);let mcpSearchQuery=state$1(""),allMcpServers=user_derived(()=>mcpStore.getServersSorted()),mcpServers=user_derived(()=>get$3(allMcpServers).filter(s2=>s2.enabled)),hasMcpServers=user_derived(()=>get$3(mcpServers).length>0),filteredMcpServers=user_derived(()=>{const query=get$3(mcpSearchQuery).toLowerCase().trim();return query?get$3(mcpServers).filter(s2=>{const name=getServerLabel(s2).toLowerCase(),url2=s2.url.toLowerCase();return name.includes(query)||url2.includes(query)}):get$3(mcpServers)}); function getServerLabel(server){return mcpStore.getServerLabel(server)}function isServerEnabledForChat(serverId){return conversationsStore.isMcpServerEnabledForChat(serverId)}async function toggleServerForChat(serverId){await conversationsStore.toggleMcpServerForChat(serverId)}function handleMcpSubMenuOpen(open2){open2&&(set$1(mcpSearchQuery,""),mcpStore.runHealthChecksForServers(get$3(allMcpServers)))}function handleMcpSettingsClick(){$$props.onMcpSettingsClick?.(),goto(`${get$3(hasMcpServers)? "":"?add"}${ROUTES.MCP_SERVERS}`)}var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Sub,($$anchor4,DropdownMenu_Sub)=>{DropdownMenu_Sub($$anchor4,{onOpenChange:handleMcpSubMenuOpen,children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$_(),node_2=first_child(fragment_2);component(node_2,()=>Dropdown_menu_sub_trigger, @@ -6697,7 +6697,7 @@ var fragment_4=comment$2(),node_5=first_child(fragment_4);{var consequent_1=$$an $$anchor11,fragment_7)},$$slots:{default:!0}})}),append($$anchor9,fragment_6)};let $0=user_derived(()=>get$3(filteredMcpServers).length===0);DropdownMenuSearchable($$anchor8,{placeholder:"Search servers...",emptyMessage:"No servers found",get isEmpty(){return get$3($0)},get searchValue(){return get$3(mcpSearchQuery)},set searchValue($$value){set$1(mcpSearchQuery,$$value,!0)},footer:footer2,children:($$anchor9,$$slotProps4)=>{var div=root_8$p();each(div,21,()=>get$3(filteredMcpServers),server=>server. id,($$anchor10,server)=>{const healthState=user_derived(()=>mcpStore.getHealthCheckState(get$3(server).id)),hasError=user_derived(()=>get$3(healthState).status===HealthCheckStatus.ERROR),isEnabledForChat=user_derived(()=>isServerEnabledForChat(get$3(server).id)),displayName=user_derived(()=>getServerLabel(get$3(server))),faviconUrl=user_derived(()=>mcpStore.getServerFavicon(get$3(server).id));var button=root_9$i(),div_1=child(button),div_2=child(div_1),node_8=child(div_2);McpServerIdentity(node_8, {get displayName(){return get$3(displayName)},get faviconUrl(){return get$3(faviconUrl)},iconClass:"h-4 w-4",iconRounded:"rounded-sm",showVersion:!1,nameClass:"text-sm"}),reset(div_2);var node_9=sibling(div_2,2);{var consequent=$$anchor11=>{var span=root_10$c();append($$anchor11,span)};if_block(node_9,$$render=>{get$3(hasError)&&$$render(consequent)})}reset(div_1);var node_10=sibling(div_1,2);Switch(node_10,{get checked(){return get$3(isEnabledForChat)},get disabled(){return get$3(hasError)},onclick:e=>e. -stopPropagation(),onCheckedChange:()=>toggleServerForChat(get$3(server).id)}),reset(button),template_effect(()=>button.disabled=get$3(hasError)),delegated("click",button,()=>!get$3(hasError)&&toggleServerForChat(get$3(server).id)),append($$anchor10,button)}),reset(div),append($$anchor9,div)},$$slots:{footer:!0,default:!0}})}},alternate=$$anchor8=>{var fragment_8=root_11$c(),node_11=sibling(first_child(fragment_8),2);component(node_11,()=>Dropdown_menu_separator,($$anchor9,DropdownMenu_Separator)=>{ +stopPropagation(),onCheckedChange:()=>toggleServerForChat(get$3(server).id)}),reset(button),template_effect(()=>button.disabled=get$3(hasError)),delegated("click",button,()=>!get$3(hasError)&&toggleServerForChat(get$3(server).id)),append($$anchor10,button)}),reset(div),append($$anchor9,div)},$$slots:{footer:!0,default:!0}})}},alternate=$$anchor8=>{var fragment_8=root_11$d(),node_11=sibling(first_child(fragment_8),2);component(node_11,()=>Dropdown_menu_separator,($$anchor9,DropdownMenu_Separator)=>{ DropdownMenu_Separator($$anchor9,{})});var node_12=sibling(node_11,2);component(node_12,()=>Dropdown_menu_item,($$anchor9,DropdownMenu_Item_1)=>{DropdownMenu_Item_1($$anchor9,{class:"flex cursor-pointer items-center gap-2",onclick:handleMcpSettingsClick,children:($$anchor10,$$slotProps4)=>{var fragment_9=root_12$8(),node_13=first_child(fragment_9);Plus(node_13,{class:"h-4 w-4"}),next$1(2),append($$anchor10,fragment_9)},$$slots:{default:!0}})}),append($$anchor8,fragment_8)};if_block(node_5,$$render=>{ get$3(hasMcpServers)?$$render(consequent_1):$$render(alternate,-1)})}append($$anchor7,fragment_4)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}delegate(["click"]);var root$1o=from_html('');function ChatFormFileInputInvisible($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),multiple=prop($$props,"multiple",3,!0),fileInputElement; function click(){fileInputElement?.click()}function handleFileSelect(event2){const input=event2.target;input.files&&$$props.onFileSelect?.(Array.from(input.files))}var $$exports={click},input_1=root$1o();return bind_this(input_1,$$value=>fileInputElement=$$value,()=>fileInputElement),template_effect(()=>{input_1.multiple=multiple(),set_class(input_1,1,`hidden ${className()??""}`)}),delegated("change",input_1,handleFileSelect),append($$anchor,input_1),pop($$exports)}delegate(["change"]);var root_1$H=from_html( @@ -6818,7 +6818,7 @@ get message(){return $$props.message},get mcpPrompt(){return get$3(mcpPromptExtr get message(){return $$props.message},onConfirmDelete:handleConfirmDelete,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()}})},alternate=$$anchor2=>{ChatMessageAssistant($$anchor2,{get class(){return className()},get deletionInfo(){return get$3(deletionInfo)}, get isLastAssistantMessage(){return isLastAssistantMessage()},get message(){return $$props.message},get toolMessages(){return toolMessages()},get messageContent(){return $$props.message.content},onConfirmDelete:handleConfirmDelete,onContinue:handleContinue,onCopy:handleCopy,onDelete:handleDelete2,onEdit:handleEdit,onForkConversation:handleForkConversation,onNavigateToSibling:handleNavigateToSibling,onRegenerate:handleRegenerate,onShowDeleteDialogChange:handleShowDeleteDialogChange,get showDeleteDialog(){ return get$3(showDeleteDialog)},get siblingInfo(){return siblingInfo()},get textareaElement(){return get$3(textareaElement)},set textareaElement($$value){set$1(textareaElement,$$value,!0)}})};if_block(node2,$$render=>{$$props.message.role===MessageRole.SYSTEM?$$render(consequent):get$3(mcpPromptExtra)?$$render(consequent_1,1):$$props.message.role===MessageRole.USER?$$render(consequent_2,2):$$render(alternate,-1)})}reset(div),action(div,$$node=>fadeInView?.($$node)),append($$anchor,div),pop()}var root_2$U=from_html( -'
'),root_7$n=from_html('
Receiving arguments...
'),root_8$o=from_html('
Response was truncated
'),root_4$w=from_html('
Arguments:
'),root_11$b=from_html( +'
'),root_7$n=from_html('
Receiving arguments...
'),root_8$o=from_html('
Response was truncated
'),root_4$w=from_html('
Arguments:
'),root_11$c=from_html( '
Arguments:
'),root_13$b=from_html('
Waiting for result...
'),root_16$3=from_html(''),root_15$8=from_html('
',1),root_14$4=from_html('
'),root_17$6=from_html('
No output
'),root_10$a=from_html('
Result:
',1),root_19$7=from_html('
'),root_21$1=from_html('
'),root_25$2=from_html('
'),root_23$3=from_html('
'),root$1e=from_html('
');function ChatMessageAgenticContent($$anchor,$$props){push$1($$props,!0);const renderSection=($$anchor2,section=noop$3,index2=noop$3)=>{ @@ -6827,7 +6827,7 @@ var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor3=> div_2=child(div_1),node_2=sibling(child(div_2),2);{var consequent_1=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animate-spin"})};if_block(node_2,$$render=>{isStreaming()&&$$render(consequent_1)})}reset(div_2);var node_3=sibling(div_2,2);{var consequent_2=$$anchor5=>{{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode($$anchor5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}},consequent_3=$$anchor5=>{ var div_3=root_7$n();append($$anchor5,div_3)},alternate=$$anchor5=>{var div_4=root_8$o();append($$anchor5,div_4)};if_block(node_3,$$render=>{section().toolArgs?$$render(consequent_2):isStreaming()?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_1),append($$anchor4,div_1)},$$slots:{default:!0}})}},consequent_10=$$anchor3=>{const isPending=user_derived(()=>section().type===AgenticSectionType.TOOL_CALL_PENDING),toolIcon=user_derived(()=>get$3(isPending)?Loader_circle:Wrench),toolIconClass=user_derived( ()=>get$3(isPending)?"h-4 w-4 animate-spin":"h-4 w-4");{let $0=user_derived(()=>isExpanded(index2(),section())),$1=user_derived(()=>section().toolName||""),$2=user_derived(()=>get$3(isPending)?"executing...":void 0);CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return get$3(toolIcon)},get iconClass(){return get$3(toolIconClass)},get title(){return get$3($1)},get subtitle(){return get$3($2)},get isStreaming(){return get$3(isPending)},onToggle:()=>toggleExpanded( -index2(),section()),children:($$anchor4,$$slotProps)=>{var fragment_5=root_10$a(),node_4=first_child(fragment_5);{var consequent_5=$$anchor5=>{var div_5=root_11$b(),node_5=sibling(child(div_5),2);{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode(node_5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}reset(div_5),append($$anchor5,div_5)};if_block(node_4,$$render=>{section().toolArgs&§ion().toolArgs!== +index2(),section()),children:($$anchor4,$$slotProps)=>{var fragment_5=root_10$a(),node_4=first_child(fragment_5);{var consequent_5=$$anchor5=>{var div_5=root_11$c(),node_5=sibling(child(div_5),2);{let $02=user_derived(()=>formatJsonPretty(section().toolArgs));SyntaxHighlightedCode(node_5,{get code(){return get$3($02)},get language(){return FileTypeText.JSON},maxHeight:"20rem",class:"text-xs"})}reset(div_5),append($$anchor5,div_5)};if_block(node_4,$$render=>{section().toolArgs&§ion().toolArgs!== "{}"&&$$render(consequent_5)})}var div_6=sibling(node_4,2),div_7=child(div_6),node_6=sibling(child(div_7),2);{var consequent_6=$$anchor5=>{Loader_circle($$anchor5,{class:"h-3 w-3 animate-spin"})};if_block(node_6,$$render=>{get$3(isPending)&&$$render(consequent_6)})}reset(div_7);var node_7=sibling(div_7,2);{var consequent_7=$$anchor5=>{var div_8=root_13$b();append($$anchor5,div_8)},consequent_9=$$anchor5=>{var div_9=root_14$4();each(div_9,21,()=>section().parsedLines,index$2,($$anchor6,line)=>{var fragment_7=root_15$8(), div_10=first_child(fragment_7),text2=child(div_10,!0);reset(div_10);var node_8=sibling(div_10,2);{var consequent_8=$$anchor7=>{var img=root_16$3();template_effect(()=>{set_attribute(img,"src",get$3(line).image.base64Url),set_attribute(img,"alt",get$3(line).image.name)}),append($$anchor7,img)};if_block(node_8,$$render=>{get$3(line).image&&$$render(consequent_8)})}template_effect(()=>set_text(text2,get$3(line).text)),append($$anchor6,fragment_7)}),reset(div_9),append($$anchor5,div_9)},alternate_1=$$anchor5=>{ var div_11=root_17$6();append($$anchor5,div_11)};if_block(node_7,$$render=>{get$3(isPending)?$$render(consequent_7):section().toolResult?$$render(consequent_9,1):$$render(alternate_1,-1)})}reset(div_6),append($$anchor4,fragment_5)},$$slots:{default:!0}})}},consequent_11=$$anchor3=>{{let $0=user_derived(()=>isExpanded(index2(),section()));CollapsibleContentBlock($$anchor3,{get open(){return get$3($0)},class:"my-2",get icon(){return Brain},title:"Reasoning",onToggle:()=>toggleExpanded(index2(),section()), @@ -6876,7 +6876,7 @@ siblingInfo.totalSiblings-1),nextSiblingId=user_derived(()=>get$3(hasNext)?$$pro d opacity-30");ActionIcon(node_1,{get icon(){return Chevron_left},tooltip:"Previous version",get disabled(){return get$3($0)},get class(){return`h-5 w-5 p-0 ${get$3($1)??""}`},onclick:()=>$$props.onNavigateToSibling?.(get$3(previousSiblingId))})}var span=sibling(node_1,2),text2=child(span);reset(span);var node_2=sibling(span,2);{let $0=user_derived(()=>!get$3(hasNext)),$1=user_derived(()=>get$3(hasNext)?"":"opacity-30");ActionIcon(node_2,{get icon(){return Chevron_right},tooltip:"Next version",get disabled(){ return get$3($0)},get class(){return`h-5 w-5 p-0 ${get$3($1)??""}`},onclick:()=>$$props.onNavigateToSibling?.(get$3(nextSiblingId))})}reset(div),template_effect(()=>{set_attribute(div,"aria-label",`Message version ${$$props.siblingInfo.currentIndex+1} of ${$$props.siblingInfo.totalSiblings??""}`),set_class(div,1,`flex items-center gap-1 text-xs text-muted-foreground ${className()??""}`),set_text(text2,`${$$props.siblingInfo.currentIndex+1}/${$$props.siblingInfo.totalSiblings??""}`)}),append($$anchor2, div)};if_block(node2,$$render=>{$$props.siblingInfo&&$$props.siblingInfo.totalSiblings>1&&$$render(consequent)})}append($$anchor,fragment),pop()}var root_3$J=from_html(''),root_4$u=from_html("

Reading (prompt processing)

"),root_2$R=from_html(" ",1),root_6$n=from_html(''),root_7$l=from_html("

"),root_5$m=from_html(" ",1),root_10$8=from_html( -''),root_11$a=from_html("

Tool calls

"),root_9$g=from_html(" ",1),root_14$3=from_html(''),root_15$7=from_html("

Agentic summary

"),root_13$a=from_html(" ",1),root_8$n=from_html(" ",1),root_16$2=from_html(" ",1),root_17$5=from_html(" ",1),root_18$4=from_html(" ",1),root_19$6=from_html(" ",1), +''),root_11$b=from_html("

Tool calls

"),root_9$g=from_html(" ",1),root_14$3=from_html(''),root_15$7=from_html("

Agentic summary

"),root_13$a=from_html(" ",1),root_8$n=from_html(" ",1),root_16$2=from_html(" ",1),root_17$5=from_html(" ",1),root_18$4=from_html(" ",1),root_19$6=from_html(" ",1), root$19=from_html('
');function ChatMessageStatistics($$anchor,$$props){push$1($$props,!0);let isLive=prop($$props,"isLive",3,!1),isProcessingPrompt=prop($$props,"isProcessingPrompt",3,!1),initialView=prop($$props,"initialView",19,()=>ChatMessageStatsView.GENERATION),hideSummary=prop( $$props,"hideSummary",3,!1),activeView=user_derived(initialView),hasAutoSwitchedToGeneration=state$1(!1);user_effect(()=>{$$props.onActiveViewChange?.(get$3(activeView))}),user_effect(()=>{isLive()&&(!get$3(hasAutoSwitchedToGeneration)&&!isProcessingPrompt()&&$$props.predictedTokens&&$$props.predictedTokens>0?(set$1(activeView,ChatMessageStatsView.GENERATION),set$1(hasAutoSwitchedToGeneration,!0)):get$3(hasAutoSwitchedToGeneration)||set$1(activeView,ChatMessageStatsView.READING))});let hasGenerationStats=user_derived( ()=>$$props.predictedTokens!==void 0&&$$props.predictedTokens>0&&$$props.predictedMs!==void 0&&$$props.predictedMs>0),tokensPerSecond=user_derived(()=>get$3(hasGenerationStats)?$$props.predictedTokens/$$props.predictedMs*MS_PER_SECOND:0),formattedTime=user_derived(()=>$$props.predictedMs!==void 0?formatPerformanceTime($$props.predictedMs):DEFAULT_PERFORMANCE_TIME),promptTokensPerSecond=user_derived(()=>$$props.promptTokens!==void 0&&$$props.promptMs!==void 0&&$$props.promptMs>0?$$props.promptTokens/ @@ -6889,7 +6889,7 @@ fragment_2);component(node_6,()=>Tooltip_trigger,($$anchor4,Tooltip_Trigger_1)=> ursor-not-allowed opacity-40":"hover:text-foreground"}`),button_1.disabled=get$3(isGenerationDisabled)}),delegated("click",button_1,()=>!get$3(isGenerationDisabled)&&set$1(activeView,ChatMessageStatsView.GENERATION)),append($$anchor5,button_1)},$$slots:{default:!0}})});var node_8=sibling(node_6,2);component(node_8,()=>Tooltip_content,($$anchor4,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor4,{children:($$anchor5,$$slotProps2)=>{var p_1=root_7$l(),text2=child(p_1,!0);reset(p_1),template_effect(()=>set_text( text2,get$3(isGenerationDisabled)?"Generation (waiting for tokens...)":"Generation (token output)")),append($$anchor5,p_1)},$$slots:{default:!0}})}),append($$anchor3,fragment_2)},$$slots:{default:!0}})});var node_9=sibling(node_5,2);{var consequent_2=$$anchor2=>{var fragment_3=root_8$n(),node_10=first_child(fragment_3);component(node_10,()=>Root$5,($$anchor3,Tooltip_Root_2)=>{Tooltip_Root_2($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_4=root_9$g(),node_11=first_child(fragment_4);component( node_11,()=>Tooltip_trigger,($$anchor5,Tooltip_Trigger_2)=>{Tooltip_Trigger_2($$anchor5,{children:($$anchor6,$$slotProps2)=>{var button_2=root_10$8(),node_12=child(button_2);Wrench(node_12,{class:"h-3 w-3"}),next$1(2),reset(button_2),template_effect(()=>set_class(button_2,1,`inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors ${get$3(activeView)===ChatMessageStatsView.TOOLS?"bg-background text-foreground shadow-sm":"hover:text-foreground"}`)),delegated("click",button_2,()=>set$1( -activeView,ChatMessageStatsView.TOOLS)),append($$anchor6,button_2)},$$slots:{default:!0}})});var node_13=sibling(node_11,2);component(node_13,()=>Tooltip_content,($$anchor5,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p_2=root_11$a();append($$anchor6,p_2)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})});var node_14=sibling(node_10,2);{var consequent_1=$$anchor3=>{var fragment_5=comment$2(),node_15=first_child(fragment_5); +activeView,ChatMessageStatsView.TOOLS)),append($$anchor6,button_2)},$$slots:{default:!0}})});var node_13=sibling(node_11,2);component(node_13,()=>Tooltip_content,($$anchor5,Tooltip_Content_2)=>{Tooltip_Content_2($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p_2=root_11$b();append($$anchor6,p_2)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})});var node_14=sibling(node_10,2);{var consequent_1=$$anchor3=>{var fragment_5=comment$2(),node_15=first_child(fragment_5); component(node_15,()=>Root$5,($$anchor4,Tooltip_Root_3)=>{Tooltip_Root_3($$anchor4,{children:($$anchor5,$$slotProps)=>{var fragment_6=root_13$a(),node_16=first_child(fragment_6);component(node_16,()=>Tooltip_trigger,($$anchor6,Tooltip_Trigger_3)=>{Tooltip_Trigger_3($$anchor6,{children:($$anchor7,$$slotProps2)=>{var button_3=root_14$3(),node_17=child(button_3);Layers(node_17,{class:"h-3 w-3"}),next$1(2),reset(button_3),template_effect(()=>set_class(button_3,1,`inline-flex h-5 w-5 items-center jus\ tify-center rounded-sm transition-colors ${get$3(activeView)===ChatMessageStatsView.SUMMARY?"bg-background text-foreground shadow-sm":"hover:text-foreground"}`)),delegated("click",button_3,()=>set$1(activeView,ChatMessageStatsView.SUMMARY)),append($$anchor7,button_3)},$$slots:{default:!0}})});var node_18=sibling(node_16,2);component(node_18,()=>Tooltip_content,($$anchor6,Tooltip_Content_3)=>{Tooltip_Content_3($$anchor6,{children:($$anchor7,$$slotProps2)=>{var p_3=root_15$7();append($$anchor7,p_3)}, $$slots:{default:!0}})}),append($$anchor5,fragment_6)},$$slots:{default:!0}})}),append($$anchor3,fragment_5)};if_block(node_14,$$render=>{hideSummary()||$$render(consequent_1)})}append($$anchor2,fragment_3)};if_block(node_9,$$render=>{get$3(hasAgenticStats)&&$$render(consequent_2)})}reset(div_1);var div_2=sibling(div_1,2),node_19=child(div_2);{var consequent_3=$$anchor2=>{var fragment_7=root_16$2(),node_20=first_child(fragment_7);{let $0=user_derived(()=>$$props.predictedTokens?.toLocaleString()); @@ -6909,7 +6909,7 @@ var fragment_1=root_2$P(),node_1=first_child(fragment_1);ChatMessageMcpPromptCon onCopy},get onDelete(){return $$props.onDelete},get onEdit(){return $$props.onEdit},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props.showDeleteDialog},get role(){return MessageRole.USER}}),reset(div_1),append($$anchor3,div_1)};if_block(node_2,$$render=>{$$props.message.timestamp&&$$render(consequent_1)})}append($$anchor2,fragment_1)};if_block( node2,$$render=>{editCtx.isEditing?$$render(consequent):$$render(alternate,-1)})}reset(div),template_effect(()=>set_class(div,1,`group flex flex-col items-end gap-3 md:gap-2 ${className()??""}`)),append($$anchor,div),pop()}var root$17=from_html("
");function Card($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$17();attribute_effect(div,$0=>({"data-slot":"ca\ rd",class:$0,...restProps}),[()=>cn$1("flex flex-col gap-6 rounded-xl bg-card py-6 text-card-foreground shadow-sm",BOX_BORDER,$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root_3$H=from_html(''),root_4$t=from_html(" "),root_1$z=from_html(" ",1),root_8$m=from_html(" "),root_9$f=from_html( -' '),root_7$k=from_html(" ",1),root_5$l=from_html('
'),root_11$9=from_html("
"),root_13$9=from_html('
'),root_17$4=from_html(" \ +' '),root_7$k=from_html(" ",1),root_5$l=from_html('
'),root_11$a=from_html("
"),root_13$9=from_html('
'),root_17$4=from_html(" \ "),root_18$3=from_html(" "),root_15$6=from_html("
"),root$16=from_html('
');function ChatMessageMcpPromptContent($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),variant=prop($$props,"variant",19,()=>McpPromptVariant.MESSAGE),isLoading2=prop($$props,"\ isLoading",3,!1),hoveredArgKey=state$1(null),argumentEntries=user_derived(()=>Object.entries($$props.prompt.arguments??{})),hasArguments=user_derived(()=>$$props.prompt.arguments&&Object.keys($$props.prompt.arguments).length>0),hasContent=user_derived(()=>$$props.prompt.content&&$$props.prompt.content.trim().length>0),contentParts=user_derived(()=>{if(!$$props.prompt.content||!get$3(hasArguments))return[{text:$$props.prompt.content||"",argKey:null}];const parts=[];let remaining=$$props.prompt.content; const valueToKey=new SvelteMap;for(const[key2,value]of get$3(argumentEntries))value&&value.trim()&&valueToKey.set(value,key2);const sortedValues=[...valueToKey.keys()].sort((a,b)=>b.length-a.length);for(;remaining.length>0;){let earliestMatch=null;for(const value of sortedValues){const index2=remaining.indexOf(value);index2!==-1&&(earliestMatch===null||index20&&parts.push({text:remaining. @@ -6920,7 +6920,7 @@ node_2,$$render=>{get$3(serverFavicon)&&$$render(consequent)})}append($$anchor5, node2,2);TruncatedText(node_4,{get text(){return $$props.prompt.name}}),reset(div_2);var node_5=sibling(div_2,2);{var consequent_1=$$anchor2=>{var div_3=root_5$l();each(div_3,21,()=>get$3(argumentEntries),([key2,value])=>key2,($$anchor3,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let key2=()=>get$3($$array)[0],value=()=>get$3($$array)[1];var fragment_2=comment$2(),node_6=first_child(fragment_2);component(node_6,()=>Root$5,($$anchor4,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor4, {children:($$anchor5,$$slotProps)=>{var fragment_3=root_7$k(),node_7=first_child(fragment_3);component(node_7,()=>Tooltip_trigger,($$anchor6,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor6,{children:($$anchor7,$$slotProps2)=>{var span_1=root_8$m(),text_1=child(span_1,!0);reset(span_1),template_effect(()=>{set_class(span_1,1,`rounded-sm bg-purple-200/60 px-1.5 py-0.5 text-[10px] leading-none text-purple-700 transition-opacity dark:bg-purple-800/40 dark:text-purple-300 ${get$3(hoveredArgKey)&&get$3( hoveredArgKey)!==key2()?"opacity-30":""}`),set_text(text_1,key2())}),event("mouseenter",span_1,()=>set$1(hoveredArgKey,key2(),!0)),event("mouseleave",span_1,()=>set$1(hoveredArgKey,null)),append($$anchor7,span_1)},$$slots:{default:!0}})});var node_8=sibling(node_7,2);component(node_8,()=>Tooltip_content,($$anchor6,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor6,{children:($$anchor7,$$slotProps2)=>{var span_2=root_9$f(),text_2=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_2,value())), -append($$anchor7,span_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_3)},$$slots:{default:!0}})}),append($$anchor3,fragment_2)}),reset(div_3),append($$anchor2,div_3)};if_block(node_5,$$render=>{get$3(showArgBadges)&&$$render(consequent_1)})}reset(div_1);var node_9=sibling(div_1,2);{var consequent_2=$$anchor2=>{Card($$anchor2,{class:"relative overflow-hidden rounded-[1.125rem] border border-destructive/50 bg-destructive/10 backdrop-blur-md",children:($$anchor3,$$slotProps)=>{var div_4=root_11$9(), +append($$anchor7,span_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_3)},$$slots:{default:!0}})}),append($$anchor3,fragment_2)}),reset(div_3),append($$anchor2,div_3)};if_block(node_5,$$render=>{get$3(showArgBadges)&&$$render(consequent_1)})}reset(div_1);var node_9=sibling(div_1,2);{var consequent_2=$$anchor2=>{Card($$anchor2,{class:"relative overflow-hidden rounded-[1.125rem] border border-destructive/50 bg-destructive/10 backdrop-blur-md",children:($$anchor3,$$slotProps)=>{var div_4=root_11$a(), span_3=child(div_4),text_3=child(span_3,!0);reset(span_3),reset(div_4),template_effect(()=>{set_class(div_4,1,`overflow-y-auto ${get$3(paddingClass)??""}`),set_style(div_4,`${get$3(maxHeightStyle)??""} overflow-wrap: anywhere; word-break: break-word;`),set_class(span_3,1,`${get$3(textSizeClass)??""} text-destructive`),set_text(text_3,$$props.loadError)}),append($$anchor3,div_4)},$$slots:{default:!0}})},consequent_3=$$anchor2=>{Card($$anchor2,{class:"relative overflow-hidden rounded-[1.125rem] bo\ rder border-purple-200 bg-purple-500/10 px-1 py-2 backdrop-blur-md dark:border-purple-800 dark:bg-purple-500/20",children:($$anchor3,$$slotProps)=>{var div_5=root_13$9();template_effect(()=>{set_class(div_5,1,`overflow-y-auto ${get$3(paddingClass)??""}`),set_style(div_5,`${get$3(maxHeightStyle)??""} overflow-wrap: anywhere; word-break: break-word;`)}),append($$anchor3,div_5)},$$slots:{default:!0}})},consequent_5=$$anchor2=>{Card($$anchor2,{class:"relative overflow-hidden rounded-[1.125rem] borde\ r border-purple-200 bg-purple-500/10 py-0 text-foreground backdrop-blur-md dark:border-purple-800 dark:bg-purple-500/20",children:($$anchor3,$$slotProps)=>{var div_6=root_15$6(),span_4=child(div_6);each(span_4,21,()=>get$3(contentParts),index$2,($$anchor4,part)=>{var fragment_7=comment$2(),node_10=first_child(fragment_7);{var consequent_4=$$anchor5=>{var span_5=root_17$4(),text_4=child(span_5,!0);reset(span_5),template_effect(()=>{set_class(span_5,1,`rounded-sm bg-purple-300/50 px-0.5 text-purpl\ @@ -6991,7 +6991,7 @@ onEdit(content2,extras2)});var div=root$11(),node2=child(div);{var consequent=$$ div_2),div_4=child(div_3),node_2=child(div_4);ActionIcon(node_2,{get icon(){return Square_pen},tooltip:"Edit",get onclick(){return editCtx.handleEdit}});var node_3=sibling(node_2,2);ActionIcon(node_3,{get icon(){return Trash_2},tooltip:"Delete",get onclick(){return $$props.onDelete}});var node_4=sibling(node_3,2);ActionIcon(node_4,{get icon(){return Arrow_up},tooltip:"Send immediately",get onclick(){return $$props.onSendImmediately}}),reset(div_4),reset(div_3),reset(div_2),reset(div_1),append($$anchor2, fragment_1)};if_block(node2,$$render=>{editCtx.isEditing?$$render(consequent):$$render(alternate,-1)})}reset(div),action(div,$$node=>fadeInView?.($$node)),template_effect(()=>set_class(div,1,`group flex flex-col items-end gap-3 transition-opacity hover:opacity-80 md:gap-2 ${className()??""} sticky ${get$3(showProcessingInfo)?"bottom-44":"bottom-32"}`)),append($$anchor,div),pop()}var root_2$L=from_html(" Cancel",1),root_3$E=from_html(" Save",1),root_1$v=from_html('
'),root_7$i=from_html("
"),root_8$k=from_html(" "),root_9$e=from_html('
', -1),root_11$8=from_html('
'),root_6$k=from_html("
",1),root_5$j=from_html('
'),root_13$8=from_html('
'),root_4$p=from_html(" ",1),root$10=from_html('
');function ChatMessageSystem($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""), +1),root_11$9=from_html('
'),root_6$k=from_html("
",1),root_5$j=from_html('
'),root_13$8=from_html('
'),root_4$p=from_html(" ",1),root$10=from_html('
');function ChatMessageSystem($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""), siblingInfo=prop($$props,"siblingInfo",3,null),textareaElement=prop($$props,"textareaElement",15);const editCtx=getMessageEditContext();function handleEditKeydown(event2){event2.key===KeyboardKey.ENTER&&!event2.shiftKey&&!isIMEComposing(event2)?(event2.preventDefault(),editCtx.save()):event2.key===KeyboardKey.ESCAPE&&(event2.preventDefault(),editCtx.cancel())}let isMultiline=state$1(!1),messageElement=state$1(void 0),isExpanded=state$1(!1),contentHeight=state$1(0);const MAX_HEIGHT=200,currentConfig=config$1(); let showExpandButton=user_derived(()=>get$3(contentHeight)>MAX_HEIGHT);user_effect(()=>{if(!get$3(messageElement)||!$$props.message.content.trim())return;$$props.message.content.includes(` `)&&set$1(isMultiline,!0);const resizeObserver=new ResizeObserver(entries=>{for(const entry of entries){const element2=entry.target;set$1(isMultiline,element2.offsetHeight>24*1.5),set$1(contentHeight,element2.scrollHeight,!0)}});return resizeObserver.observe(get$3(messageElement)),()=>{resizeObserver.disconnect()}});function toggleExpand(){set$1(isExpanded,!get$3(isExpanded))}var div=root$10(),node2=child(div);{var consequent=$$anchor2=>{var div_1=root_1$v(),textarea=child(div_1);remove_textarea_child( @@ -7001,7 +7001,7 @@ node_3,{class:"h-8 px-3",get onclick(){return editCtx.save},get disabled(){retur e]:py-2.5",get"data-multiline"(){return get$3($0)},style:"border: 2px dashed hsl(var(--border)); max-height: var(--max-message-height); overflow-wrap: anywhere; word-break: break-word;",children:($$anchor4,$$slotProps)=>{var fragment_3=root_6$k(),div_4=first_child(fragment_3),node_7=child(div_4);{var consequent_1=$$anchor5=>{var div_5=root_7$i(),node_8=child(div_5);MarkdownContent(node_8,{class:"markdown-system-content -my-4",get content(){return $$props.message.content}}),reset(div_5),bind_this( div_5,$$value=>set$1(messageElement,$$value),()=>get$3(messageElement)),template_effect(()=>set_class(div_5,1,clsx(get$3(isExpanded)?"cursor-text":""))),append($$anchor5,div_5)},alternate=$$anchor5=>{var span=root_8$k(),text2=child(span,!0);reset(span),bind_this(span,$$value=>set$1(messageElement,$$value),()=>get$3(messageElement)),template_effect(()=>{set_class(span,1,`text-md whitespace-pre-wrap ${get$3(isExpanded)?"cursor-text":""}`),set_text(text2,$$props.message.content)}),append($$anchor5, span)};if_block(node_7,$$render=>{currentConfig.renderUserContentAsMarkdown?$$render(consequent_1):$$render(alternate,-1)})}var node_9=sibling(node_7,2);{var consequent_2=$$anchor5=>{var fragment_4=root_9$e(),div_6=sibling(first_child(fragment_4),2),node_10=child(div_6);Button(node_10,{class:"rounded-full px-4 py-1.5 text-xs shadow-md",size:"sm",variant:"outline",children:($$anchor6,$$slotProps2)=>{next$1();var text_1=text$8("Show full system message");append($$anchor6,text_1)},$$slots:{default:!0}}), -reset(div_6),append($$anchor5,fragment_4)};if_block(node_9,$$render=>{!get$3(isExpanded)&&get$3(showExpandButton)&&$$render(consequent_2)})}reset(div_4);var node_11=sibling(div_4,2);{var consequent_3=$$anchor5=>{var div_7=root_11$8(),node_12=child(div_7);Button(node_12,{class:"rounded-full px-4 py-1.5 text-xs",onclick:e=>{e.stopPropagation(),toggleExpand()},size:"sm",variant:"outline",children:($$anchor6,$$slotProps2)=>{next$1();var text_2=text$8("Collapse System Message");append($$anchor6,text_2)}, +reset(div_6),append($$anchor5,fragment_4)};if_block(node_9,$$render=>{!get$3(isExpanded)&&get$3(showExpandButton)&&$$render(consequent_2)})}reset(div_4);var node_11=sibling(div_4,2);{var consequent_3=$$anchor5=>{var div_7=root_11$9(),node_12=child(div_7);Button(node_12,{class:"rounded-full px-4 py-1.5 text-xs",onclick:e=>{e.stopPropagation(),toggleExpand()},size:"sm",variant:"outline",children:($$anchor6,$$slotProps2)=>{next$1();var text_2=text$8("Collapse System Message");append($$anchor6,text_2)}, $$slots:{default:!0}}),reset(div_7),append($$anchor5,div_7)};if_block(node_11,$$render=>{get$3(isExpanded)&&get$3(showExpandButton)&&$$render(consequent_3)})}template_effect(()=>{set_class(div_4,1,`relative transition-all duration-300 ${get$3(isExpanded)?"cursor-text select-text":"select-none"}`),set_style(div_4,!get$3(isExpanded)&&get$3(showExpandButton)?`max-height: ${MAX_HEIGHT}px;`:"max-height: none;")}),append($$anchor4,fragment_3)},$$slots:{default:!0}})}reset(button),reset(div_3),template_effect( ()=>set_class(button,1,`group/expand w-full text-left ${!get$3(isExpanded)&&get$3(showExpandButton)?"cursor-pointer":"cursor-auto"}`)),delegated("click",button,function(...$$args){(get$3(showExpandButton)&&!get$3(isExpanded)?toggleExpand:void 0)?.apply(this,$$args)}),append($$anchor3,div_3)},d2=user_derived(()=>$$props.message.content.trim());if_block(node_5,$$render=>{get$3(d2)&&$$render(consequent_4)})}var node_13=sibling(node_5,2);{var consequent_5=$$anchor3=>{var div_8=root_13$8(),node_14=child( div_8);ChatMessageActionIcons(node_14,{actionsPosition:"right",get deletionInfo(){return $$props.deletionInfo},justify:"end",get onConfirmDelete(){return $$props.onConfirmDelete},get onCopy(){return $$props.onCopy},get onDelete(){return $$props.onDelete},get onEdit(){return $$props.onEdit},get onNavigateToSibling(){return $$props.onNavigateToSibling},get onShowDeleteDialogChange(){return $$props.onShowDeleteDialogChange},get siblingInfo(){return siblingInfo()},get showDeleteDialog(){return $$props. @@ -10537,12 +10537,12 @@ McpLogo(node2,{class:"h-5 w-5 md:h-6 md:w-6"}),next$1(2),reset(div_1);var div_2= set open($$value){set$1(isAddingServer,$$value,!0)}});var div_3=sibling(node_3,2),node_4=child(div_3);{var consequent=$$anchor2=>{var div_4=root_2$H();append($$anchor2,div_4)};if_block(node_4,$$render=>{get$3(servers).length===0&&!get$3(isAddingServer)&&$$render(consequent)})}var node_5=sibling(node_4,2);{var consequent_2=$$anchor2=>{var div_5=root_3$A();each(div_5,21,()=>get$3(servers),server=>server.id,($$anchor3,server)=>{var fragment_1=comment$2(),node_6=first_child(fragment_1);{var consequent_1=$$anchor4=>{ McpServerCardSkeleton($$anchor4)},alternate=$$anchor4=>{{let $0=user_derived(()=>conversationsStore.isMcpServerEnabledForChat(get$3(server).id));McpServerCard($$anchor4,{get server(){return get$3(server)},get enabled(){return get$3($0)},onToggle:async()=>{const wasEnabled=conversationsStore.isMcpServerEnabledForChat(get$3(server).id);await conversationsStore.toggleMcpServerForChat(get$3(server).id),wasEnabled||toolsStore.enableAllToolsForServer(get$3(server).id)},onUpdate:updates=>mcpStore.updateServer( get$3(server).id,updates),onDelete:()=>mcpStore.removeServer(get$3(server).id)})}};if_block(node_6,$$render=>{get$3(initialLoadComplete)?$$render(alternate,-1):$$render(consequent_1)})}append($$anchor3,fragment_1)}),reset(div_5),append($$anchor2,div_5)};if_block(node_5,$$render=>{get$3(servers).length>0&&$$render(consequent_2)})}reset(div_3),reset(div),template_effect(()=>set_class(div_3,1,`grid gap-5 md:space-y-4 ${$$props.class??""}`)),transition(1,div,()=>fade,()=>({duration:150})),append($$anchor, -div),pop()}delegate(["click"]);var root_3$z=from_html(" Tools",1),root_5$g=from_html(" Resources",1),root_7$g=from_html(" Prompts",1),root_9$c=from_html(" Logging",1),root_11$7=from_html(" Completions",1),root_13$7=from_html(" Tasks",1),root_1$q=from_html(" ",1);function McpCapabilitiesBadges($$anchor,$$props){push$1($$props,!0);var fragment=comment$2(),node2=first_child(fragment);{var consequent_6=$$anchor2=>{var fragment_1=root_1$q(),node_1=first_child(fragment_1); +div),pop()}delegate(["click"]);var root_3$z=from_html(" Tools",1),root_5$g=from_html(" Resources",1),root_7$g=from_html(" Prompts",1),root_9$c=from_html(" Logging",1),root_11$8=from_html(" Completions",1),root_13$7=from_html(" Tasks",1),root_1$q=from_html(" ",1);function McpCapabilitiesBadges($$anchor,$$props){push$1($$props,!0);var fragment=comment$2(),node2=first_child(fragment);{var consequent_6=$$anchor2=>{var fragment_1=root_1$q(),node_1=first_child(fragment_1); {var consequent=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-green-50 px-1.5 text-[10px] dark:bg-green-950",children:($$anchor4,$$slotProps)=>{var fragment_3=root_3$z(),node_2=first_child(fragment_3);Wrench(node_2,{class:"h-3 w-3 text-green-600 dark:text-green-400"}),next$1(),append($$anchor4,fragment_3)},$$slots:{default:!0}})};if_block(node_1,$$render=>{$$props.capabilities.server.tools&&$$render(consequent)})}var node_3=sibling(node_1,2);{var consequent_1=$$anchor3=>{Badge( $$anchor3,{variant:"outline",class:"h-5 gap-1 bg-blue-50 px-1.5 text-[10px] dark:bg-blue-950",children:($$anchor4,$$slotProps)=>{var fragment_5=root_5$g(),node_4=first_child(fragment_5);Database(node_4,{class:"h-3 w-3 text-blue-600 dark:text-blue-400"}),next$1(),append($$anchor4,fragment_5)},$$slots:{default:!0}})};if_block(node_3,$$render=>{$$props.capabilities.server.resources&&$$render(consequent_1)})}var node_5=sibling(node_3,2);{var consequent_2=$$anchor3=>{Badge($$anchor3,{variant:"outline", class:"h-5 gap-1 bg-purple-50 px-1.5 text-[10px] dark:bg-purple-950",children:($$anchor4,$$slotProps)=>{var fragment_7=root_7$g(),node_6=first_child(fragment_7);Message_square(node_6,{class:"h-3 w-3 text-purple-600 dark:text-purple-400"}),next$1(),append($$anchor4,fragment_7)},$$slots:{default:!0}})};if_block(node_5,$$render=>{$$props.capabilities.server.prompts&&$$render(consequent_2)})}var node_7=sibling(node_5,2);{var consequent_3=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-\ 1 bg-orange-50 px-1.5 text-[10px] dark:bg-orange-950",children:($$anchor4,$$slotProps)=>{var fragment_9=root_9$c(),node_8=first_child(fragment_9);File_text(node_8,{class:"h-3 w-3 text-orange-600 dark:text-orange-400"}),next$1(),append($$anchor4,fragment_9)},$$slots:{default:!0}})};if_block(node_7,$$render=>{$$props.capabilities.server.logging&&$$render(consequent_3)})}var node_9=sibling(node_7,2);{var consequent_4=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-cyan-50 px-1.5 \ -text-[10px] dark:bg-cyan-950",children:($$anchor4,$$slotProps)=>{var fragment_11=root_11$7(),node_10=first_child(fragment_11);Sparkles(node_10,{class:"h-3 w-3 text-cyan-600 dark:text-cyan-400"}),next$1(),append($$anchor4,fragment_11)},$$slots:{default:!0}})};if_block(node_9,$$render=>{$$props.capabilities.server.completions&&$$render(consequent_4)})}var node_11=sibling(node_9,2);{var consequent_5=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-pink-50 px-1.5 text-[10px] dark:b\ +text-[10px] dark:bg-cyan-950",children:($$anchor4,$$slotProps)=>{var fragment_11=root_11$8(),node_10=first_child(fragment_11);Sparkles(node_10,{class:"h-3 w-3 text-cyan-600 dark:text-cyan-400"}),next$1(),append($$anchor4,fragment_11)},$$slots:{default:!0}})};if_block(node_9,$$render=>{$$props.capabilities.server.completions&&$$render(consequent_4)})}var node_11=sibling(node_9,2);{var consequent_5=$$anchor3=>{Badge($$anchor3,{variant:"outline",class:"h-5 gap-1 bg-pink-50 px-1.5 text-[10px] dark:b\ g-pink-950",children:($$anchor4,$$slotProps)=>{var fragment_13=root_13$7(),node_12=first_child(fragment_13);List_checks(node_12,{class:"h-3 w-3 text-pink-600 dark:text-pink-400"}),next$1(),append($$anchor4,fragment_13)},$$slots:{default:!0}})};if_block(node_11,$$render=>{$$props.capabilities.server.tasks&&$$render(consequent_5)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{$$props.capabilities&&$$render(consequent_6)})}append($$anchor,fragment),pop()}var root_6$i=from_html(' '),root_3$y=from_html(" ",1),root_9$b=from_html('
details
 
'),root_8$i=from_html('
',1),root_7$f=from_html('
'),root_2$G=from_html('
',1);function McpConnectionLogs($$anchor,$$props){push$1($$props,!0);let defaultExpanded=prop($$props,"defaultExpanded",3,!1),isExpanded=user_derived(defaultExpanded);function formatLogDetails(details){if(details==null)return"";try{return JSON.stringify(details,null,2)}catch{return String(details)}}var fragment=comment$2(),node2=first_child(fragment); @@ -10641,7 +10641,7 @@ toLowerCase();for(const resource of resourceList){if(!resourceMatchesSearch(reso children:new Map,isFiltered:!0})}function cleanupEmptyFolders(node2){if(node2.resource)return!0;const toDelete=[];for(const[name,child2]of node2.children.entries())cleanupEmptyFolders(child2)||toDelete.push(name);for(const name of toDelete)node2.children.delete(name);return node2.children.size>0}return cleanupEmptyFolders(root2),root2}function countTreeResources(node2){if(node2.resource)return 1;let count=0;for(const child2 of node2.children.values())count+=countTreeResources(child2);return count} function sortTreeChildren(children){return children.sort((a,b)=>{const aIsFolder=!a.resource&&a.children.size>0,bIsFolder=!b.resource&&b.children.size>0;return aIsFolder&&!bIsFolder?-1:!aIsFolder&&bIsFolder?1:a.name.localeCompare(b.name)})}var root_4$j=from_html(' ',1),root_7$d=from_html('
'),root_3$q=from_html(" ",1),root_9$a=from_html( '
'),root_12$6=from_html('
)
',1),root_18$2=from_html('
'),root_19$5=from_html('
No resources
'),root_24=from_html('
'),root_25$1=from_html(''),root_23$2=from_html('
Templates
',1),root_20$2=from_html(" ",1),root_17$3=from_html('
'),root_11$6=from_html("No resources
'),root_24=from_html('
'),root_25$1=from_html(''),root_23$2=from_html('
Templates
',1),root_20$2=from_html(" ",1),root_17$3=from_html('
'),root_11$7=from_html(" ",1);function McpResourcesBrowserServerItem($$anchor,$$props){push$1($$props,!0);const renderTreeNode=($$anchor2,node2=noop$3,depth=noop$3,parentPath=noop$3)=>{const isFolder=user_derived(()=>!node2().resource&&node2().children.size>0),folderId=user_derived(()=>`${$$props.serverName}:${parentPath()}/${node2().name}`),isFolderExpanded=user_derived(()=>$$props.expandedFolders.has(get$3(folderId)));var fragment=comment$2(),node_1=first_child(fragment);{var consequent_1=$$anchor3=>{const folderCount=user_derived( ()=>countTreeResources(node2()));var fragment_1=comment$2(),node_2=first_child(fragment_1);component(node_2,()=>Collapsible,($$anchor4,Collapsible_Root)=>{Collapsible_Root($$anchor4,{get open(){return get$3(isFolderExpanded)},onOpenChange:()=>$$props.onToggleFolder(get$3(folderId)),children:($$anchor5,$$slotProps)=>{var fragment_2=root_3$q(),node_3=first_child(fragment_2);component(node_3,()=>Collapsible_trigger,($$anchor6,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor6,{class:"flex w-full \ items-center gap-2 rounded px-2 py-1 text-sm hover:bg-muted/50",children:($$anchor7,$$slotProps2)=>{var fragment_3=root_4$j(),node_4=first_child(fragment_3);{var consequent=$$anchor8=>{Chevron_down($$anchor8,{class:"h-3 w-3"})},alternate=$$anchor8=>{Chevron_right($$anchor8,{class:"h-3 w-3"})};if_block(node_4,$$render=>{get$3(isFolderExpanded)?$$render(consequent):$$render(alternate,-1)})}var node_5=sibling(node_4,2);Folder_open(node_5,{class:"h-3.5 w-3.5 text-muted-foreground"});var span=sibling( @@ -10652,7 +10652,7 @@ node_7,2),node_8=child(button);component(node_8,()=>get$3(ResourceIcon),($$ancho set_text(text_2,get$3(resourceDisplayName))}),delegated("click",button,e=>handleResourceClick(get$3(resource),e)),append($$anchor3,div_1)};if_block(node_1,$$render=>{get$3(isFolder)?$$render(consequent_1):node2().resource&&$$render(consequent_3,1)})}append($$anchor2,fragment)};let searchQuery=prop($$props,"searchQuery",3,""),serverDisplayName=user_derived(()=>mcpStore.getServerDisplayName($$props.serverName)),serverFaviconUrl=user_derived(()=>mcpStore.getServerFavicon($$props.serverName));const hasResources=user_derived( ()=>$$props.serverRes.resources.length>0),hasTemplates=user_derived(()=>$$props.serverRes.templates.length>0),hasContent=user_derived(()=>get$3(hasResources)||get$3(hasTemplates)),resourceTree=user_derived(()=>buildResourceTree($$props.serverRes.resources,$$props.serverName,searchQuery())),templateInfos=user_derived(()=>$$props.serverRes.templates.map(t=>({uriTemplate:t.uriTemplate,name:t.name,title:t.title,description:t.description,mimeType:t.mimeType,serverName:$$props.serverName,annotations:t. annotations,icons:t.icons})));function handleResourceClick(resource,event2){$$props.onSelect?.(resource,event2.shiftKey)}function handleCheckboxChange(resource,checked){$$props.onToggle?.(resource,checked)}function isResourceSelected(resource){return $$props.selectedUris.has(resource.uri)}var fragment_8=comment$2(),node_9=first_child(fragment_8);component(node_9,()=>Collapsible,($$anchor2,Collapsible_Root_1)=>{Collapsible_Root_1($$anchor2,{get open(){return $$props.isExpanded},get onOpenChange(){ -return $$props.onToggleServer},children:($$anchor3,$$slotProps)=>{var fragment_9=root_11$6(),node_10=first_child(fragment_9);component(node_10,()=>Collapsible_trigger,($$anchor4,Collapsible_Trigger_1)=>{Collapsible_Trigger_1($$anchor4,{class:"flex w-full items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50",children:($$anchor5,$$slotProps2)=>{var fragment_10=root_12$6(),node_11=first_child(fragment_10);{var consequent_4=$$anchor6=>{Chevron_down($$anchor6,{class:"h-3.5 w-3.5"})},alternate_1=$$anchor6=>{ +return $$props.onToggleServer},children:($$anchor3,$$slotProps)=>{var fragment_9=root_11$7(),node_10=first_child(fragment_9);component(node_10,()=>Collapsible_trigger,($$anchor4,Collapsible_Trigger_1)=>{Collapsible_Trigger_1($$anchor4,{class:"flex w-full items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50",children:($$anchor5,$$slotProps2)=>{var fragment_10=root_12$6(),node_11=first_child(fragment_10);{var consequent_4=$$anchor6=>{Chevron_down($$anchor6,{class:"h-3.5 w-3.5"})},alternate_1=$$anchor6=>{ Chevron_right($$anchor6,{class:"h-3.5 w-3.5"})};if_block(node_11,$$render=>{$$props.isExpanded?$$render(consequent_4):$$render(alternate_1,-1)})}var span_3=sibling(node_11,2),div_2=child(span_3),node_12=child(div_2);McpServerIdentity(node_12,{get displayName(){return get$3(serverDisplayName)},get faviconUrl(){return get$3(serverFaviconUrl)},iconClass:"h-4 w-4",showVersion:!1}),reset(div_2);var span_4=sibling(div_2,2),text_3=child(span_4),node_13=sibling(text_3);{var consequent_5=$$anchor6=>{var text_4=text$8(); template_effect(()=>set_text(text_4,`, ${$$props.serverRes.templates.length??""} template${$$props.serverRes.templates.length!==1?"s":""}`)),append($$anchor6,text_4)};if_block(node_13,$$render=>{get$3(hasTemplates)&&$$render(consequent_5)})}next$1(),reset(span_4),reset(span_3);var node_14=sibling(span_3,2);{var consequent_6=$$anchor6=>{Loader_circle($$anchor6,{class:"ml-auto h-3 w-3 animate-spin text-muted-foreground"})};if_block(node_14,$$render=>{$$props.serverRes.loading&&$$render(consequent_6)})} template_effect(()=>set_text(text_3,`(${$$props.serverRes.resources.length??""} resource${$$props.serverRes.resources.length!==1?"s":""}`)),append($$anchor5,fragment_10)},$$slots:{default:!0}})});var node_15=sibling(node_10,2);component(node_15,()=>Collapsible_content,($$anchor4,Collapsible_Content_1)=>{Collapsible_Content_1($$anchor4,{children:($$anchor5,$$slotProps2)=>{var div_3=root_17$3(),node_16=child(div_3);{var consequent_7=$$anchor6=>{var div_4=root_18$2(),text_5=child(div_4);reset(div_4), @@ -10668,7 +10668,7 @@ add(serverName)}function toggleFolder(folderId){expandedFolders.has(folderId)?ex $$anchor2,{get isLoading(){return get$3(isLoading2)}})},alternate=$$anchor2=>{var fragment_1=comment$2(),node_2=first_child(fragment_1);each(node_2,17,()=>[...get$3(filteredResources).entries()],([serverName,serverRes])=>serverName,($$anchor3,$$item)=>{var $$array=user_derived(()=>to_array(get$3($$item),2));let serverName=()=>get$3($$array)[0],serverRes=()=>get$3($$array)[1];{let $0=user_derived(()=>expandedServers.has(serverName()));McpResourcesBrowserServerItem($$anchor3,{get serverName(){return serverName()}, get serverRes(){return serverRes()},get isExpanded(){return get$3($0)},get selectedUris(){return selectedUris()},get selectedTemplateUri(){return $$props.selectedTemplateUri},get expandedFolders(){return expandedFolders},onToggleServer:()=>toggleServer(serverName()),onToggleFolder:toggleFolder,get onSelect(){return $$props.onSelect},get onToggle(){return $$props.onToggle},get onTemplateSelect(){return $$props.onTemplateSelect},get searchQuery(){return get$3(searchQuery)}})}}),append($$anchor2,fragment_1)}; if_block(node_1,$$render=>{get$3(filteredResources).size===0?$$render(consequent):$$render(alternate,-1)})}reset(div_1),reset(div),template_effect(()=>set_class(div,1,clsx(["flex flex-col gap-2",$$props.class]))),append($$anchor,div),pop()}var root_1$h=from_html('
Select a resource to preview
'),root_3$p=from_html('

'),root_5$b=from_html( -'
'),root_6$d=from_html('
'),root_8$g=from_html('
 
'),root_10$7=from_html('Resource content'),root_11$5=from_html('
<\ +'
'),root_6$d=from_html('
'),root_8$g=from_html('
 
'),root_10$7=from_html('Resource content'),root_11$6=from_html('
<\ /span>
'),root_12$5=from_html('
No content available
'),root_7$c=from_html(" ",1),root_14$2=from_html(' '),root_15$5=from_html(' '),root_13$6=from_html('
'),root_2$z=from_html('

',1),root$A=from_html("
");function McpResourcePreview($$anchor,$$props){push$1($$props,!0);let content2=state$1(null),isLoading2=state$1(!1),error2=state$1( null);user_effect(()=>{$$props.resource?$$props.preloadedContent?(set$1(content2,$$props.preloadedContent,!0),set$1(isLoading2,!1),set$1(error2,null)):loadContent($$props.resource.uri):(set$1(content2,null),set$1(error2,null))});async function loadContent(uri2){set$1(isLoading2,!0),set$1(error2,null);try{const result=await mcpStore.readResource(uri2);result?set$1(content2,result,!0):set$1(error2,"Failed to load resource content")}catch(e){set$1(error2,e instanceof Error?e.message:"Unknown error", @@ -10678,7 +10678,7 @@ div_2=first_child(fragment),div_3=child(div_2),h3=child(div_3),text_1=child(h3,! ntent",children:($$anchor3,$$slotProps)=>{Download($$anchor3,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}})}reset(div_4),reset(div_2);var div_5=sibling(div_2,2),node_5=child(div_5);{var consequent_2=$$anchor3=>{var div_6=root_5$b(),node_6=child(div_6);Loader_circle(node_6,{class:"h-6 w-6 animate-spin text-muted-foreground"}),reset(div_6),append($$anchor3,div_6)},consequent_3=$$anchor3=>{var div_7=root_6$d(),node_7=child(div_7);Circle_alert(node_7,{class:"h-6 w-6"});var span=sibling(node_7,2),text_4=child( span,!0);reset(span),reset(div_7),template_effect(()=>set_text(text_4,get$3(error2))),append($$anchor3,div_7)},consequent_7=$$anchor3=>{const textContent=user_derived(()=>getResourceTextContent(get$3(content2))),blobContent=user_derived(()=>getResourceBlobContent(get$3(content2)));var fragment_2=root_7$c(),node_8=first_child(fragment_2);{var consequent_4=$$anchor4=>{var pre=root_8$g(),text_5=child(pre,!0);reset(pre),template_effect(()=>set_text(text_5,get$3(textContent))),append($$anchor4,pre)}; if_block(node_8,$$render=>{get$3(textContent)&&$$render(consequent_4)})}var node_9=sibling(node_8,2);each(node_9,17,()=>get$3(blobContent),blob=>blob.uri,($$anchor4,blob)=>{var fragment_3=comment$2(),node_10=first_child(fragment_3);{var consequent_5=$$anchor5=>{var img=root_10$7();template_effect($0=>set_attribute(img,"src",$0),[()=>createBase64DataUrl(get$3(blob).mimeType??MimeTypeApplication.OCTET_STREAM,get$3(blob).blob)]),append($$anchor5,img)},d2=user_derived(()=>isImageMimeType(get$3(blob). -mimeType??MimeTypeApplication.OCTET_STREAM)),alternate=$$anchor5=>{var div_8=root_11$5(),node_11=child(div_8);File_text(node_11,{class:"h-4 w-4"});var span_1=sibling(node_11,2),text_6=child(span_1);reset(span_1),reset(div_8),template_effect(()=>set_text(text_6,`Binary content (${(get$3(blob).mimeType||"unknown type")??""})`)),append($$anchor5,div_8)};if_block(node_10,$$render=>{get$3(d2)?$$render(consequent_5):$$render(alternate,-1)})}append($$anchor4,fragment_3)});var node_12=sibling(node_9,2); +mimeType??MimeTypeApplication.OCTET_STREAM)),alternate=$$anchor5=>{var div_8=root_11$6(),node_11=child(div_8);File_text(node_11,{class:"h-4 w-4"});var span_1=sibling(node_11,2),text_6=child(span_1);reset(span_1),reset(div_8),template_effect(()=>set_text(text_6,`Binary content (${(get$3(blob).mimeType||"unknown type")??""})`)),append($$anchor5,div_8)};if_block(node_10,$$render=>{get$3(d2)?$$render(consequent_5):$$render(alternate,-1)})}append($$anchor4,fragment_3)});var node_12=sibling(node_9,2); {var consequent_6=$$anchor4=>{var div_9=root_12$5();append($$anchor4,div_9)};if_block(node_12,$$render=>{!get$3(textContent)&&get$3(blobContent).length===0&&$$render(consequent_6)})}append($$anchor3,fragment_2)};if_block(node_5,$$render=>{get$3(isLoading2)?$$render(consequent_2):get$3(error2)?$$render(consequent_3,1):get$3(content2)&&$$render(consequent_7,2)})}reset(div_5);var node_13=sibling(div_5,2);{var consequent_10=$$anchor3=>{var div_10=root_13$6(),node_14=child(div_10);{var consequent_8=$$anchor4=>{ var span_2=root_14$2(),text_7=child(span_2,!0);reset(span_2),template_effect(()=>set_text(text_7,$$props.resource.mimeType)),append($$anchor4,span_2)};if_block(node_14,$$render=>{$$props.resource.mimeType&&$$render(consequent_8)})}var node_15=sibling(node_14,2);{var consequent_9=$$anchor4=>{var span_3=root_15$5(),text_8=child(span_3);reset(span_3),template_effect(()=>set_text(text_8,`Priority: ${$$props.resource.annotations.priority??""}`)),append($$anchor4,span_3)};if_block(node_15,$$render=>{$$props. resource.annotations?.priority!==void 0&&$$render(consequent_9)})}var span_4=sibling(node_15,2),text_9=child(span_4);reset(span_4),reset(div_10),template_effect(()=>set_text(text_9,`Server: ${$$props.resource.serverName??""}`)),append($$anchor3,div_10)};if_block(node_13,$$render=>{($$props.resource.mimeType||$$props.resource.annotations)&&$$render(consequent_10)})}template_effect(()=>{set_text(text_1,$$props.resource.title||$$props.resource.name),set_text(text_2,$$props.resource.uri)}),append($$anchor2, @@ -10701,12 +10701,12 @@ ry",size:"sm",onclick:()=>handleOpenChange(!1),children:($$anchor8,$$slotProps4) fragment_4)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_4$i=from_html(" Export Settings",1),root_8$f=from_html(`

Warning: This export will include sensitive data such as API keys and MCP server custom headers (e.g., authorization tokens). Do not share this file with anyone you don't trust.

`),root_9$9=from_html(`

Sensitive data (API keys, MCP server custom headers) will not be included in the export - to protect your credentials.

`),root_3$o=from_html(" ",1),root_11$4=from_html('Include sensitive data (not recommended)'),root_12$4=from_html("Include sensitive data"),root_13$5=from_html(" ",1),root_2$w=from_html('
',1);function DialogExportSettings($$anchor,$$props){push$1($$props,!0);let includeSensitiveData=prop($$props,"includeSensitiveData",15,!1);function handleOpenChange(newOpen){ + to protect your credentials.

`),root_3$o=from_html(" ",1),root_11$5=from_html('Include sensitive data (not recommended)'),root_12$4=from_html("Include sensitive data"),root_13$5=from_html(" ",1),root_2$w=from_html('
',1);function DialogExportSettings($$anchor,$$props){push$1($$props,!0);let includeSensitiveData=prop($$props,"includeSensitiveData",15,!1);function handleOpenChange(newOpen){ newOpen||$$props.onCancel()}var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$1,($$anchor2,AlertDialog_Root)=>{AlertDialog_Root($$anchor2,{get open(){return $$props.open},onOpenChange:handleOpenChange,children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Alert_dialog_content,($$anchor4,AlertDialog_Content)=>{AlertDialog_Content($$anchor4,{children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$w(),node_2=first_child( fragment_2);component(node_2,()=>Alert_dialog_header,($$anchor6,AlertDialog_Header)=>{AlertDialog_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$o(),node_3=first_child(fragment_3);component(node_3,()=>Alert_dialog_title,($$anchor8,AlertDialog_Title)=>{AlertDialog_Title($$anchor8,{class:"flex items-center gap-2",children:($$anchor9,$$slotProps4)=>{var fragment_4=root_4$i(),node_4=first_child(fragment_4);{var consequent=$$anchor10=>{Shield_off($$anchor10,{class:"h-5 w-\ 5 text-destructive"})},alternate=$$anchor10=>{Shield($$anchor10,{class:"h-5 w-5 text-destructive"})};if_block(node_4,$$render=>{includeSensitiveData()?$$render(consequent):$$render(alternate,-1)})}next$1(),append($$anchor9,fragment_4)},$$slots:{default:!0}})});var node_5=sibling(node_3,2);component(node_5,()=>Alert_dialog_description,($$anchor8,AlertDialog_Description)=>{AlertDialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{var fragment_7=comment$2(),node_6=first_child(fragment_7); {var consequent_1=$$anchor10=>{var p2=root_8$f();append($$anchor10,p2)},alternate_1=$$anchor10=>{var p_1=root_9$9();append($$anchor10,p_1)};if_block(node_6,$$render=>{includeSensitiveData()?$$render(consequent_1):$$render(alternate_1,-1)})}append($$anchor9,fragment_7)},$$slots:{default:!0}})}),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var div=sibling(node_2,2),node_7=child(div);Checkbox(node_7,{id:"include-sensitive",get checked(){return includeSensitiveData()},set checked($$value){ -includeSensitiveData($$value)}});var node_8=sibling(node_7,2);Label(node_8,{for:"include-sensitive",class:"text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",children:($$anchor6,$$slotProps3)=>{var fragment_8=comment$2(),node_9=first_child(fragment_8);{var consequent_2=$$anchor7=>{var span=root_11$4();append($$anchor7,span)},alternate_2=$$anchor7=>{var span_1=root_12$4();append($$anchor7,span_1)};if_block(node_9,$$render=>{includeSensitiveData()?$$render(consequent_2): +includeSensitiveData($$value)}});var node_8=sibling(node_7,2);Label(node_8,{for:"include-sensitive",class:"text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",children:($$anchor6,$$slotProps3)=>{var fragment_8=comment$2(),node_9=first_child(fragment_8);{var consequent_2=$$anchor7=>{var span=root_11$5();append($$anchor7,span)},alternate_2=$$anchor7=>{var span_1=root_12$4();append($$anchor7,span_1)};if_block(node_9,$$render=>{includeSensitiveData()?$$render(consequent_2): $$render(alternate_2,-1)})}append($$anchor6,fragment_8)},$$slots:{default:!0}}),reset(div);var node_10=sibling(div,2);component(node_10,()=>Alert_dialog_footer,($$anchor6,AlertDialog_Footer)=>{AlertDialog_Footer($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_9=root_13$5(),node_11=first_child(fragment_9);component(node_11,()=>Alert_dialog_cancel,($$anchor8,AlertDialog_Cancel)=>{AlertDialog_Cancel($$anchor8,{get onclick(){return $$props.onCancel},children:($$anchor9,$$slotProps4)=>{next$1(); var text2=text$8("Cancel");append($$anchor9,text2)},$$slots:{default:!0}})});var node_12=sibling(node_11,2);component(node_12,()=>Alert_dialog_action,($$anchor8,AlertDialog_Action)=>{AlertDialog_Action($$anchor8,{get onclick(){return $$props.onConfirm},class:"bg-destructive text-white hover:bg-destructive/80",children:($$anchor9,$$slotProps4)=>{var fragment_10=comment$2(),node_13=first_child(fragment_10);{var consequent_3=$$anchor10=>{var text_1=text$8("Export Anyway");append($$anchor10,text_1)}, alternate_3=$$anchor10=>{var text_2=text$8("Export Without Sensitive Data");append($$anchor10,text_2)};if_block(node_13,$$render=>{includeSensitiveData()?$$render(consequent_3):$$render(alternate_3,-1)})}append($$anchor9,fragment_10)},$$slots:{default:!0}})}),append($$anchor7,fragment_9)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_4$h=from_html(" ",1),root_3$n=from_html( @@ -10783,7 +10783,7 @@ noop$3),reset(td),bind_this(td,$$value=>ref2($$value),()=>ref2()),append($$ancho reground [&:has([role=checkbox])]:pe-0",$$props.class)]);var node2=child(th);snippet(node2,()=>$$props.children??noop$3),reset(th),bind_this(th,$$value=>ref2($$value),()=>ref2()),append($$anchor,th),pop()}var root$u=from_html("");function Table_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var thead2=root$u();attribute_effect(thead2,$0=>({"data-slot":"tabl\ e-header",class:$0,...restProps}),[()=>cn$1("[&_tr]:border-b",$$props.class)]);var node2=child(thead2);snippet(node2,()=>$$props.children??noop$3),reset(thead2),bind_this(thead2,$$value=>ref2($$value),()=>ref2()),append($$anchor,thead2),pop()}var root$t=from_html("");function Table_row($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var tr2=root$t();attribute_effect(tr2, $0=>({"data-slot":"table-row",class:$0,...restProps}),[()=>cn$1("border-b transition-colors data-[state=selected]:bg-muted hover:[&,&>svelte-css-wrapper]:[&>th,td]:bg-muted/50",$$props.class)]);var node2=child(tr2);snippet(node2,()=>$$props.children??noop$3),reset(tr2),bind_this(tr2,$$value=>ref2($$value),()=>ref2()),append($$anchor,tr2),pop()}var root_3$e=from_html(" ",1),root_6$9=from_html('
Load\ -ing model information...
'),root_13$4=from_html('
'),root_11$3=from_html(" ",1),root_17$2=from_html(' ',1),root_15$4=from_html(" ",1),root_19$4=from_html(" ",1),root_23$1=from_html(" ",1),root_27=from_html(" ",1),root_31$1=from_html(" ",1),root_35=from_html( +ing model information...
'),root_13$4=from_html('
'),root_11$4=from_html(" ",1),root_17$2=from_html(' ',1),root_15$4=from_html(" ",1),root_19$4=from_html(" ",1),root_23$1=from_html(" ",1),root_27=from_html(" ",1),root_31$1=from_html(" ",1),root_35=from_html( " ",1),root_39=from_html(" ",1),root_43=from_html(" ",1),root_47=from_html(" ",1),root_50=from_html(" ",1),root_56=from_html('
'),root_54=from_html(" ",1),root_57=from_html(" ",1),root_63=from_html('
 
'),root_61=from_html(" ",1),root_14$1=from_html(" ",1),root_9$6=from_html( " ",1),root_64=from_html('
No model information available
'),root_2$m=from_html(`
`,1);function DialogModelInformation($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15),modelId=prop($$props,"modelId",3,null),isRouter=user_derived(()=>serverStore.isRouterMode),routerModelProps=state$1(null),isLoadingRouterProps=state$1(!1),serverProps2=user_derived(()=>get$3(isRouter)&&modelId()?get$3(routerModelProps):serverStore.props),modelName=user_derived(()=>get$3(isRouter)&&modelId()?modelId():modelsStore.singleModelName), -models=user_derived(modelOptions),isLoadingModels=user_derived(modelsLoading),firstModel=user_derived(()=>get$3(isRouter)&&modelId()?get$3(models).find(m=>m.model===modelId())??null:get$3(models)[0]??null),modalities=user_derived(()=>get$3(firstModel)?.id?modelsStore.getModelModalitiesArray(get$3(firstModel).id):[]);user_effect(()=>{open2()&&get$3(models).length===0&&modelsStore.fetch()}),user_effect(()=>{open2()&&get$3(isRouter)&&modelId()&&(set$1(isLoadingRouterProps,!0),modelsStore.fetchModelProps( -modelId()).then(props=>{set$1(routerModelProps,props,!0)}).catch(()=>{set$1(routerModelProps,null)}).finally(()=>{set$1(isLoadingRouterProps,!1)})),open2()||set$1(routerModelProps,null)});var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$2,($$anchor2,Dialog_Root)=>{Dialog_Root($$anchor2,{get onOpenChange(){return $$props.onOpenChange},get open(){return open2()},set open($$value){open2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child( -fragment_1);component(node_1,()=>Dialog_content,($$anchor4,Dialog_Content)=>{Dialog_Content($$anchor4,{class:"@container z-9999 !max-h-[80dvh] !max-w-[60rem] max-w-full",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$m(),node_2=sibling(first_child(fragment_2),2);component(node_2,()=>Dialog_header,($$anchor6,Dialog_Header)=>{Dialog_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$e(),node_3=first_child(fragment_3);component(node_3,()=>Dialog_title,($$anchor8,Dialog_Title)=>{ -Dialog_Title($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text2=text$8("Model Information");append($$anchor9,text2)},$$slots:{default:!0}})});var node_4=sibling(node_3,2);component(node_4,()=>Dialog_description,($$anchor8,Dialog_Description)=>{Dialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text_1=text$8("Current model details and capabilities");append($$anchor9,text_1)},$$slots:{default:!0}})}),append($$anchor7,fragment_3)},$$slots:{default:!0}})}); -var div=sibling(node_2,2),node_5=child(div);{var consequent=$$anchor6=>{var div_1=root_6$9();append($$anchor6,div_1)},consequent_11=$$anchor6=>{const modelMeta=user_derived(()=>get$3(firstModel).meta);var fragment_4=comment$2(),node_6=first_child(fragment_4);{var consequent_10=$$anchor7=>{var fragment_5=comment$2(),node_7=first_child(fragment_5);component(node_7,()=>Table,($$anchor8,Table_Root)=>{Table_Root($$anchor8,{children:($$anchor9,$$slotProps3)=>{var fragment_6=root_9$6(),node_8=first_child( -fragment_6);component(node_8,()=>Table_header,($$anchor10,Table_Header)=>{Table_Header($$anchor10,{children:($$anchor11,$$slotProps4)=>{var fragment_7=comment$2(),node_9=first_child(fragment_7);component(node_9,()=>Table_row,($$anchor12,Table_Row)=>{Table_Row($$anchor12,{children:($$anchor13,$$slotProps5)=>{var fragment_8=root_11$4(),node_10=first_child(fragment_8);component(node_10,()=>Table_head,($$anchor14,Table_Head)=>{Table_Head($$anchor14,{class:"w-[10rem]",children:($$anchor15,$$slotProps6)=>{ -next$1();var text_2=text$8("Model");append($$anchor15,text_2)},$$slots:{default:!0}})});var node_11=sibling(node_10,2);component(node_11,()=>Table_head,($$anchor14,Table_Head_1)=>{Table_Head_1($$anchor14,{children:($$anchor15,$$slotProps6)=>{var div_2=root_13$4(),span=child(div_2);set_style(span,"",{},{"--threshold":"12rem"});var text_3=child(span,!0);reset(span);var node_12=sibling(span,2);{let $0=user_derived(()=>get$3(modelName)||""),$1=user_derived(()=>!!get$3(modelName));ActionIconCopyToClipboard( -node_12,{get text(){return get$3($0)},get canCopy(){return get$3($1)},ariaLabel:"Copy model name to clipboard"})}reset(div_2),template_effect(()=>set_text(text_3,get$3(modelName))),append($$anchor15,div_2)},$$slots:{default:!0}})}),append($$anchor13,fragment_8)},$$slots:{default:!0}})}),append($$anchor11,fragment_7)},$$slots:{default:!0}})});var node_13=sibling(node_8,2);component(node_13,()=>Table_body,($$anchor10,Table_Body)=>{Table_Body($$anchor10,{children:($$anchor11,$$slotProps4)=>{var fragment_9=root_14$1(), -node_14=first_child(fragment_9);component(node_14,()=>Table_row,($$anchor12,Table_Row_1)=>{Table_Row_1($$anchor12,{children:($$anchor13,$$slotProps5)=>{var fragment_10=root_15$4(),node_15=first_child(fragment_10);component(node_15,()=>Table_cell,($$anchor14,Table_Cell)=>{Table_Cell($$anchor14,{class:"h-10 align-middle font-medium",children:($$anchor15,$$slotProps6)=>{next$1();var text_4=text$8("File Path");append($$anchor15,text_4)},$$slots:{default:!0}})});var node_16=sibling(node_15,2);component( -node_16,()=>Table_cell,($$anchor14,Table_Cell_1)=>{Table_Cell_1($$anchor14,{class:"inline-flex h-10 items-center gap-2 align-middle font-mono text-xs",children:($$anchor15,$$slotProps6)=>{var fragment_11=root_17$2(),span_1=first_child(fragment_11);set_style(span_1,"",{},{"--threshold":"14rem"});var text_5=child(span_1,!0);reset(span_1);var node_17=sibling(span_1,2);ActionIconCopyToClipboard(node_17,{get text(){return get$3(serverProps2).model_path},ariaLabel:"Copy model path to clipboard"}),template_effect( -()=>set_text(text_5,get$3(serverProps2).model_path)),append($$anchor15,fragment_11)},$$slots:{default:!0}})}),append($$anchor13,fragment_10)},$$slots:{default:!0}})});var node_18=sibling(node_14,2);{var consequent_1=$$anchor12=>{var fragment_12=comment$2(),node_19=first_child(fragment_12);component(node_19,()=>Table_row,($$anchor13,Table_Row_2)=>{Table_Row_2($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_13=root_19$4(),node_20=first_child(fragment_13);component(node_20,()=>Table_cell, -($$anchor15,Table_Cell_2)=>{Table_Cell_2($$anchor15,{class:"h-10 align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_6=text$8("Context Size");append($$anchor16,text_6)},$$slots:{default:!0}})});var node_21=sibling(node_20,2);component(node_21,()=>Table_cell,($$anchor15,Table_Cell_3)=>{Table_Cell_3($$anchor15,{children:($$anchor16,$$slotProps6)=>{next$1();var text_7=text$8();template_effect($0=>set_text(text_7,`${$0??""} tokens`),[()=>formatNumber(get$3(serverProps2). -default_generation_settings.n_ctx)]),append($$anchor16,text_7)},$$slots:{default:!0}})}),append($$anchor14,fragment_13)},$$slots:{default:!0}})}),append($$anchor12,fragment_12)},alternate=$$anchor12=>{var fragment_15=comment$2(),node_22=first_child(fragment_15);component(node_22,()=>Table_row,($$anchor13,Table_Row_3)=>{Table_Row_3($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_16=root_23$1(),node_23=first_child(fragment_16);component(node_23,()=>Table_cell,($$anchor15,Table_Cell_4)=>{ -Table_Cell_4($$anchor15,{class:"h-10 align-middle font-medium text-red-500",children:($$anchor16,$$slotProps6)=>{next$1();var text_8=text$8("Context Size");append($$anchor16,text_8)},$$slots:{default:!0}})});var node_24=sibling(node_23,2);component(node_24,()=>Table_cell,($$anchor15,Table_Cell_5)=>{Table_Cell_5($$anchor15,{class:"text-red-500",children:($$anchor16,$$slotProps6)=>{next$1();var text_9=text$8("Not available");append($$anchor16,text_9)},$$slots:{default:!0}})}),append($$anchor14,fragment_16)}, -$$slots:{default:!0}})}),append($$anchor12,fragment_15)};if_block(node_18,$$render=>{get$3(serverProps2)?.default_generation_settings?.n_ctx?$$render(consequent_1):$$render(alternate,-1)})}var node_25=sibling(node_18,2);{var consequent_2=$$anchor12=>{var fragment_17=comment$2(),node_26=first_child(fragment_17);component(node_26,()=>Table_row,($$anchor13,Table_Row_4)=>{Table_Row_4($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_18=root_27(),node_27=first_child(fragment_18);component( -node_27,()=>Table_cell,($$anchor15,Table_Cell_6)=>{Table_Cell_6($$anchor15,{class:"h-10 align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_10=text$8("Training Context");append($$anchor16,text_10)},$$slots:{default:!0}})});var node_28=sibling(node_27,2);component(node_28,()=>Table_cell,($$anchor15,Table_Cell_7)=>{Table_Cell_7($$anchor15,{children:($$anchor16,$$slotProps6)=>{next$1();var text_11=text$8();template_effect($0=>set_text(text_11,`${$0??""} tokens`),[()=>formatNumber( -get$3(modelMeta).n_ctx_train)]),append($$anchor16,text_11)},$$slots:{default:!0}})}),append($$anchor14,fragment_18)},$$slots:{default:!0}})}),append($$anchor12,fragment_17)};if_block(node_25,$$render=>{get$3(modelMeta)?.n_ctx_train&&$$render(consequent_2)})}var node_29=sibling(node_25,2);{var consequent_3=$$anchor12=>{var fragment_20=comment$2(),node_30=first_child(fragment_20);component(node_30,()=>Table_row,($$anchor13,Table_Row_5)=>{Table_Row_5($$anchor13,{children:($$anchor14,$$slotProps5)=>{ -var fragment_21=root_31$1(),node_31=first_child(fragment_21);component(node_31,()=>Table_cell,($$anchor15,Table_Cell_8)=>{Table_Cell_8($$anchor15,{class:"h-10 align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_12=text$8("Model Size");append($$anchor16,text_12)},$$slots:{default:!0}})});var node_32=sibling(node_31,2);component(node_32,()=>Table_cell,($$anchor15,Table_Cell_9)=>{Table_Cell_9($$anchor15,{children:($$anchor16,$$slotProps6)=>{next$1();var text_13=text$8(); -template_effect($0=>set_text(text_13,$0),[()=>formatFileSize(get$3(modelMeta).size)]),append($$anchor16,text_13)},$$slots:{default:!0}})}),append($$anchor14,fragment_21)},$$slots:{default:!0}})}),append($$anchor12,fragment_20)};if_block(node_29,$$render=>{get$3(modelMeta)?.size&&$$render(consequent_3)})}var node_33=sibling(node_29,2);{var consequent_4=$$anchor12=>{var fragment_23=comment$2(),node_34=first_child(fragment_23);component(node_34,()=>Table_row,($$anchor13,Table_Row_6)=>{Table_Row_6($$anchor13, -{children:($$anchor14,$$slotProps5)=>{var fragment_24=root_35(),node_35=first_child(fragment_24);component(node_35,()=>Table_cell,($$anchor15,Table_Cell_10)=>{Table_Cell_10($$anchor15,{class:"h-10 align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_14=text$8("Parameters");append($$anchor16,text_14)},$$slots:{default:!0}})});var node_36=sibling(node_35,2);component(node_36,()=>Table_cell,($$anchor15,Table_Cell_11)=>{Table_Cell_11($$anchor15,{children:($$anchor16,$$slotProps6)=>{ -next$1();var text_15=text$8();template_effect($0=>set_text(text_15,$0),[()=>formatParameters(get$3(modelMeta).n_params)]),append($$anchor16,text_15)},$$slots:{default:!0}})}),append($$anchor14,fragment_24)},$$slots:{default:!0}})}),append($$anchor12,fragment_23)};if_block(node_33,$$render=>{get$3(modelMeta)?.n_params&&$$render(consequent_4)})}var node_37=sibling(node_33,2);{var consequent_5=$$anchor12=>{var fragment_26=comment$2(),node_38=first_child(fragment_26);component(node_38,()=>Table_row, -($$anchor13,Table_Row_7)=>{Table_Row_7($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_27=root_39(),node_39=first_child(fragment_27);component(node_39,()=>Table_cell,($$anchor15,Table_Cell_12)=>{Table_Cell_12($$anchor15,{class:"align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_16=text$8("Embedding Size");append($$anchor16,text_16)},$$slots:{default:!0}})});var node_40=sibling(node_39,2);component(node_40,()=>Table_cell,($$anchor15,Table_Cell_13)=>{Table_Cell_13( -$$anchor15,{children:($$anchor16,$$slotProps6)=>{next$1();var text_17=text$8();template_effect($0=>set_text(text_17,$0),[()=>formatNumber(get$3(modelMeta).n_embd)]),append($$anchor16,text_17)},$$slots:{default:!0}})}),append($$anchor14,fragment_27)},$$slots:{default:!0}})}),append($$anchor12,fragment_26)};if_block(node_37,$$render=>{get$3(modelMeta)?.n_embd&&$$render(consequent_5)})}var node_41=sibling(node_37,2);{var consequent_6=$$anchor12=>{var fragment_29=comment$2(),node_42=first_child(fragment_29); -component(node_42,()=>Table_row,($$anchor13,Table_Row_8)=>{Table_Row_8($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_30=root_43(),node_43=first_child(fragment_30);component(node_43,()=>Table_cell,($$anchor15,Table_Cell_14)=>{Table_Cell_14($$anchor15,{class:"align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_18=text$8("Vocabulary Size");append($$anchor16,text_18)},$$slots:{default:!0}})});var node_44=sibling(node_43,2);component(node_44,()=>Table_cell, -($$anchor15,Table_Cell_15)=>{Table_Cell_15($$anchor15,{children:($$anchor16,$$slotProps6)=>{next$1();var text_19=text$8();template_effect($0=>set_text(text_19,`${$0??""} tokens`),[()=>formatNumber(get$3(modelMeta).n_vocab)]),append($$anchor16,text_19)},$$slots:{default:!0}})}),append($$anchor14,fragment_30)},$$slots:{default:!0}})}),append($$anchor12,fragment_29)};if_block(node_41,$$render=>{get$3(modelMeta)?.n_vocab&&$$render(consequent_6)})}var node_45=sibling(node_41,2);{var consequent_7=$$anchor12=>{ -var fragment_32=comment$2(),node_46=first_child(fragment_32);component(node_46,()=>Table_row,($$anchor13,Table_Row_9)=>{Table_Row_9($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_33=root_47(),node_47=first_child(fragment_33);component(node_47,()=>Table_cell,($$anchor15,Table_Cell_16)=>{Table_Cell_16($$anchor15,{class:"align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_20=text$8("Vocabulary Type");append($$anchor16,text_20)},$$slots:{default:!0}})});var node_48=sibling( -node_47,2);component(node_48,()=>Table_cell,($$anchor15,Table_Cell_17)=>{Table_Cell_17($$anchor15,{class:"align-middle capitalize",children:($$anchor16,$$slotProps6)=>{next$1();var text_21=text$8();template_effect(()=>set_text(text_21,get$3(modelMeta).vocab_type)),append($$anchor16,text_21)},$$slots:{default:!0}})}),append($$anchor14,fragment_33)},$$slots:{default:!0}})}),append($$anchor12,fragment_32)};if_block(node_45,$$render=>{get$3(modelMeta)?.vocab_type&&$$render(consequent_7)})}var node_49=sibling( -node_45,2);component(node_49,()=>Table_row,($$anchor12,Table_Row_10)=>{Table_Row_10($$anchor12,{children:($$anchor13,$$slotProps5)=>{var fragment_35=root_50(),node_50=first_child(fragment_35);component(node_50,()=>Table_cell,($$anchor14,Table_Cell_18)=>{Table_Cell_18($$anchor14,{class:"align-middle font-medium",children:($$anchor15,$$slotProps6)=>{next$1();var text_22=text$8("Parallel Slots");append($$anchor15,text_22)},$$slots:{default:!0}})});var node_51=sibling(node_50,2);component(node_51,()=>Table_cell, -($$anchor14,Table_Cell_19)=>{Table_Cell_19($$anchor14,{children:($$anchor15,$$slotProps6)=>{next$1();var text_23=text$8();template_effect(()=>set_text(text_23,get$3(serverProps2).total_slots)),append($$anchor15,text_23)},$$slots:{default:!0}})}),append($$anchor13,fragment_35)},$$slots:{default:!0}})});var node_52=sibling(node_49,2);{var consequent_8=$$anchor12=>{var fragment_37=comment$2(),node_53=first_child(fragment_37);component(node_53,()=>Table_row,($$anchor13,Table_Row_11)=>{Table_Row_11($$anchor13, -{children:($$anchor14,$$slotProps5)=>{var fragment_38=root_54(),node_54=first_child(fragment_38);component(node_54,()=>Table_cell,($$anchor15,Table_Cell_20)=>{Table_Cell_20($$anchor15,{class:"align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_24=text$8("Modalities");append($$anchor16,text_24)},$$slots:{default:!0}})});var node_55=sibling(node_54,2);component(node_55,()=>Table_cell,($$anchor15,Table_Cell_21)=>{Table_Cell_21($$anchor15,{children:($$anchor16,$$slotProps6)=>{ -var div_3=root_56(),node_56=child(div_3);BadgesModality(node_56,{get modalities(){return get$3(modalities)}}),reset(div_3),append($$anchor16,div_3)},$$slots:{default:!0}})}),append($$anchor14,fragment_38)},$$slots:{default:!0}})}),append($$anchor12,fragment_37)};if_block(node_52,$$render=>{get$3(modalities).length>0&&$$render(consequent_8)})}var node_57=sibling(node_52,2);component(node_57,()=>Table_row,($$anchor12,Table_Row_12)=>{Table_Row_12($$anchor12,{children:($$anchor13,$$slotProps5)=>{var fragment_39=root_57(), -node_58=first_child(fragment_39);component(node_58,()=>Table_cell,($$anchor14,Table_Cell_22)=>{Table_Cell_22($$anchor14,{class:"align-middle font-medium",children:($$anchor15,$$slotProps6)=>{next$1();var text_25=text$8("Build Info");append($$anchor15,text_25)},$$slots:{default:!0}})});var node_59=sibling(node_58,2);component(node_59,()=>Table_cell,($$anchor14,Table_Cell_23)=>{Table_Cell_23($$anchor14,{class:"align-middle font-mono text-xs",children:($$anchor15,$$slotProps6)=>{next$1();var text_26=text$8(); -template_effect(()=>set_text(text_26,get$3(serverProps2).build_info)),append($$anchor15,text_26)},$$slots:{default:!0}})}),append($$anchor13,fragment_39)},$$slots:{default:!0}})});var node_60=sibling(node_57,2);{var consequent_9=$$anchor12=>{var fragment_41=comment$2(),node_61=first_child(fragment_41);component(node_61,()=>Table_row,($$anchor13,Table_Row_13)=>{Table_Row_13($$anchor13,{children:($$anchor14,$$slotProps5)=>{var fragment_42=root_61(),node_62=first_child(fragment_42);component(node_62, -()=>Table_cell,($$anchor15,Table_Cell_24)=>{Table_Cell_24($$anchor15,{class:"align-middle font-medium",children:($$anchor16,$$slotProps6)=>{next$1();var text_27=text$8("Chat Template");append($$anchor16,text_27)},$$slots:{default:!0}})});var node_63=sibling(node_62,2);component(node_63,()=>Table_cell,($$anchor15,Table_Cell_25)=>{Table_Cell_25($$anchor15,{class:"py-10",children:($$anchor16,$$slotProps6)=>{var div_4=root_63(),pre=child(div_4),text_28=child(pre,!0);reset(pre),reset(div_4),template_effect( -()=>set_text(text_28,get$3(serverProps2).chat_template)),append($$anchor16,div_4)},$$slots:{default:!0}})}),append($$anchor14,fragment_42)},$$slots:{default:!0}})}),append($$anchor12,fragment_41)};if_block(node_60,$$render=>{get$3(serverProps2).chat_template&&$$render(consequent_9)})}append($$anchor11,fragment_9)},$$slots:{default:!0}})}),append($$anchor9,fragment_6)},$$slots:{default:!0}})}),append($$anchor7,fragment_5)};if_block(node_6,$$render=>{get$3(serverProps2)&&$$render(consequent_10)})} -append($$anchor6,fragment_4)},consequent_12=$$anchor6=>{var div_5=root_64();append($$anchor6,div_5)};if_block(node_5,$$render=>{get$3(isLoadingModels)||get$3(isLoadingRouterProps)?$$render(consequent):get$3(firstModel)?$$render(consequent_11,1):get$3(isLoadingModels)||$$render(consequent_12,2)})}reset(div),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_5$9=from_html(' '),root_4$a=from_html(" MCP Resources ",1),root_3$d=from_html(" ",1),root_8$b=from_html('

'),root_9$5=from_html('
'),root_10$5=from_html('
'),root_7$9=from_html('

'),root_15$3=from_html('
'),root_18$1=from_html('
Select a resource to preview
'),root_22$1=from_html(" Attach Resource",1),root_26$1=from_html( -" ",1),root_19$3=from_html(" ",1),root_2$l=from_html('
',1);function DialogMcpResourcesBrowser($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15,!1),selectedResources=new SvelteSet,lastSelectedUri=state$1(null),isAttaching=state$1(!1),selectedTemplate=state$1(null),templatePreviewUri=state$1( -null),templatePreviewContent=state$1(null),templatePreviewLoading=state$1(!1),templatePreviewError=state$1(null);const totalCount=user_derived(mcpTotalResourceCount);user_effect(()=>{open2()&&(loadResources(),$$props.preSelectedUri&&(selectedResources.clear(),selectedResources.add($$props.preSelectedUri),set$1(lastSelectedUri,$$props.preSelectedUri,!0)))});async function loadResources(){const perChatOverrides=conversationsStore.getAllMcpServerOverrides();await mcpStore.ensureInitialized(perChatOverrides)&& -await mcpStore.fetchAllResources()}function handleOpenChange(newOpen){open2(newOpen),$$props.onOpenChange?.(newOpen),newOpen||(selectedResources.clear(),set$1(lastSelectedUri,null),clearTemplateState())}function clearTemplateState(){set$1(selectedTemplate,null),set$1(templatePreviewUri,null),set$1(templatePreviewContent,null),set$1(templatePreviewLoading,!1),set$1(templatePreviewError,null)}function handleTemplateSelect(template){if(selectedResources.clear(),set$1(lastSelectedUri,null),get$3(selectedTemplate)?. -uriTemplate===template.uriTemplate&&get$3(selectedTemplate)?.serverName===template.serverName){clearTemplateState();return}set$1(selectedTemplate,template,!0),set$1(templatePreviewUri,null),set$1(templatePreviewContent,null),set$1(templatePreviewLoading,!1),set$1(templatePreviewError,null)}async function handleTemplateResolve(uri2,serverName){set$1(templatePreviewUri,uri2,!0),set$1(templatePreviewContent,null),set$1(templatePreviewLoading,!0),set$1(templatePreviewError,null);try{const content2=await mcpStore. -readResourceByUri(serverName,uri2);content2?set$1(templatePreviewContent,content2,!0):set$1(templatePreviewError,"Failed to read resource")}catch(error2){set$1(templatePreviewError,error2 instanceof Error?error2.message:"Unknown error",!0)}finally{set$1(templatePreviewLoading,!1)}}function handleTemplateCancelForm(){clearTemplateState()}async function handleAttachTemplateResource(){if(!(!get$3(templatePreviewUri)||!get$3(selectedTemplate)||!get$3(templatePreviewContent))){set$1(isAttaching,!0);try{ -const knownResource=mcpResourceStore.findResourceByUri(get$3(templatePreviewUri));if(knownResource)mcpResourceStore.isAttached(knownResource.uri)||await mcpStore.attachResource(knownResource.uri),toast.success(`Resource attached: ${knownResource.title||knownResource.name}`);else{if(mcpResourceStore.isAttached(get$3(templatePreviewUri))){toast.info("Resource already attached"),handleOpenChange(!1);return}const resourceInfo={uri:get$3(templatePreviewUri),name:get$3(templatePreviewUri).split("/").pop()|| -get$3(templatePreviewUri),serverName:get$3(selectedTemplate).serverName},attachment=mcpResourceStore.addAttachment(resourceInfo);mcpResourceStore.updateAttachmentContent(attachment.id,get$3(templatePreviewContent)),toast.success(`Resource attached: ${resourceInfo.name}`)}handleOpenChange(!1)}catch(error2){console.error("Failed to attach template resource:",error2)}finally{set$1(isAttaching,!1)}}}function handleResourceSelect(resource,shiftKey=!1){if(clearTemplateState(),shiftKey&&get$3(lastSelectedUri)){ -const allResources=getAllResourcesFlatInTreeOrder(),lastIndex=allResources.findIndex(r2=>r2.uri===get$3(lastSelectedUri)),currentIndex=allResources.findIndex(r2=>r2.uri===resource.uri);if(lastIndex!==-1&¤tIndex!==-1){const start2=Math.min(lastIndex,currentIndex),end=Math.max(lastIndex,currentIndex);for(let i=start2;i<=end;i++)selectedResources.add(allResources[i].uri)}}else selectedResources.clear(),selectedResources.add(resource.uri),set$1(lastSelectedUri,resource.uri,!0)}function handleResourceToggle(resource,checked){ -clearTemplateState(),checked?selectedResources.add(resource.uri):selectedResources.delete(resource.uri),set$1(lastSelectedUri,resource.uri,!0)}function getAllResourcesFlatInTreeOrder(){const allResources=[],resourcesMap=mcpResources();for(const[serverName,serverRes]of resourcesMap.entries())for(const resource of serverRes.resources)allResources.push({...resource,serverName});return allResources.sort((a,b)=>{const aName=getResourceDisplayName(a),bName=getResourceDisplayName(b);return aName.localeCompare( -bName)})}async function handleAttach(){if(selectedResources.size!==0){set$1(isAttaching,!0);try{const resourcesToAttach=getAllResourcesFlatInTreeOrder().filter(r2=>selectedResources.has(r2.uri));for(const resource of resourcesToAttach)await mcpStore.attachResource(resource.uri),$$props.onAttach?.(resource);const count=resourcesToAttach.length;toast.success(count===1?`Resource attached: ${resourcesToAttach[0].name}`:`${count} resources attached`),handleOpenChange(!1)}catch(error2){console.error("\ -Failed to attach resources:",error2)}finally{set$1(isAttaching,!1)}}}const selectedTemplateUri=user_derived(()=>get$3(selectedTemplate)?.uriTemplate??null),hasTemplateResult=user_derived(()=>!!get$3(selectedTemplate)&&!!get$3(templatePreviewContent)&&!!get$3(templatePreviewUri));var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$2,($$anchor2,Dialog_Root)=>{Dialog_Root($$anchor2,{get open(){return open2()},onOpenChange:handleOpenChange,children:($$anchor3,$$slotProps)=>{ -var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Dialog_content,($$anchor4,Dialog_Content)=>{Dialog_Content($$anchor4,{class:"max-h-[80vh] !max-w-4xl overflow-hidden p-0",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$l(),node_2=first_child(fragment_2);component(node_2,()=>Dialog_header,($$anchor6,Dialog_Header)=>{Dialog_Header($$anchor6,{class:"border-b border-border/30 px-6 py-4",children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$d(),node_3=first_child( -fragment_3);component(node_3,()=>Dialog_title,($$anchor8,Dialog_Title)=>{Dialog_Title($$anchor8,{class:"flex items-center gap-2",children:($$anchor9,$$slotProps4)=>{var fragment_4=root_4$a(),node_4=first_child(fragment_4);Folder_open(node_4,{class:"h-5 w-5"});var node_5=sibling(node_4,4);{var consequent=$$anchor10=>{var span=root_5$9(),text2=child(span);reset(span),template_effect(()=>set_text(text2,`(${get$3(totalCount)??""})`)),append($$anchor10,span)};if_block(node_5,$$render=>{get$3(totalCount)> -0&&$$render(consequent)})}append($$anchor9,fragment_4)},$$slots:{default:!0}})});var node_6=sibling(node_3,2);component(node_6,()=>Dialog_description,($$anchor8,Dialog_Description)=>{Dialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text_1=text$8("Browse and attach resources from connected MCP servers to your chat context.");append($$anchor9,text_1)},$$slots:{default:!0}})}),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var div=sibling(node_2,2),div_1=child( -div),node_7=child(div_1);McpResourcesBrowser(node_7,{onSelect:handleResourceSelect,onToggle:handleResourceToggle,onTemplateSelect:handleTemplateSelect,get selectedUris(){return selectedResources},get selectedTemplateUri(){return get$3(selectedTemplateUri)},get expandToUri(){return $$props.preSelectedUri}}),reset(div_1);var div_2=sibling(div_1,2),node_8=child(div_2);{var consequent_4=$$anchor6=>{var div_3=root_7$9(),div_4=child(div_3),node_9=child(div_4);Braces(node_9,{class:"h-4 w-4 text-muted-f\ -oreground"});var span_1=sibling(node_9,2),text_2=child(span_1,!0);reset(span_1),reset(div_4);var node_10=sibling(div_4,2);{var consequent_1=$$anchor7=>{var p2=root_8$b(),text_3=child(p2,!0);reset(p2),template_effect(()=>set_text(text_3,get$3(selectedTemplate).description)),append($$anchor7,p2)};if_block(node_10,$$render=>{get$3(selectedTemplate).description&&$$render(consequent_1)})}var div_5=sibling(node_10,2),p_1=child(div_5),text_4=child(p_1,!0);reset(p_1),reset(div_5);var node_11=sibling(div_5, -2);{var consequent_2=$$anchor7=>{var div_6=root_9$5(),node_12=child(div_6);Loader_circle(node_12,{class:"h-6 w-6 animate-spin text-muted-foreground"}),reset(div_6),append($$anchor7,div_6)},consequent_3=$$anchor7=>{var div_7=root_10$5(),span_2=child(div_7),text_5=child(span_2,!0);reset(span_2);var node_13=sibling(span_2,2);Button(node_13,{size:"sm",variant:"outline",onclick:()=>{set$1(templatePreviewError,null)},children:($$anchor8,$$slotProps3)=>{next$1();var text_6=text$8("Try again");append($$anchor8, -text_6)},$$slots:{default:!0}}),reset(div_7),template_effect(()=>set_text(text_5,get$3(templatePreviewError))),append($$anchor7,div_7)},alternate=$$anchor7=>{McpResourceTemplateForm($$anchor7,{get template(){return get$3(selectedTemplate)},onResolve:handleTemplateResolve,onCancel:handleTemplateCancelForm})};if_block(node_11,$$render=>{get$3(templatePreviewLoading)?$$render(consequent_2):get$3(templatePreviewError)?$$render(consequent_3,1):$$render(alternate,-1)})}reset(div_3),template_effect(()=>{ -set_text(text_2,get$3(selectedTemplate).title||get$3(selectedTemplate).name),set_text(text_4,get$3(selectedTemplate).uriTemplate)}),append($$anchor6,div_3)},consequent_5=$$anchor6=>{{let $0=user_derived(()=>({uri:get$3(templatePreviewUri)??"",name:get$3(templatePreviewUri)?.split("/").pop()||(get$3(templatePreviewUri)??""),serverName:get$3(selectedTemplate)?.serverName||""}));McpResourcePreview($$anchor6,{get resource(){return get$3($0)},get preloadedContent(){return get$3(templatePreviewContent)}})}}, -consequent_6=$$anchor6=>{const allResources=user_derived(getAllResourcesFlatInTreeOrder),selectedResource=user_derived(()=>get$3(allResources).find(r2=>selectedResources.has(r2.uri)));{let $0=user_derived(()=>get$3(selectedResource)??null);McpResourcePreview($$anchor6,{get resource(){return get$3($0)}})}},consequent_8=$$anchor6=>{var div_8=root_15$3();each(div_8,21,getAllResourcesFlatInTreeOrder,resource=>resource.uri,($$anchor7,resource)=>{var fragment_8=comment$2(),node_14=first_child(fragment_8); -{var consequent_7=$$anchor8=>{McpResourcePreview($$anchor8,{get resource(){return get$3(resource)}})},d2=user_derived(()=>selectedResources.has(get$3(resource).uri));if_block(node_14,$$render=>{get$3(d2)&&$$render(consequent_7)})}append($$anchor7,fragment_8)}),reset(div_8),append($$anchor6,div_8)},alternate_1=$$anchor6=>{var div_9=root_18$1();append($$anchor6,div_9)};if_block(node_8,$$render=>{get$3(selectedTemplate)&&!get$3(templatePreviewContent)?$$render(consequent_4):get$3(hasTemplateResult)? -$$render(consequent_5,1):selectedResources.size===1?$$render(consequent_6,2):selectedResources.size>1?$$render(consequent_8,3):$$render(alternate_1,-1)})}reset(div_2),reset(div);var node_15=sibling(div,2);component(node_15,()=>Dialog_footer,($$anchor6,Dialog_Footer)=>{Dialog_Footer($$anchor6,{class:"border-t border-border/30 px-6 py-4",children:($$anchor7,$$slotProps3)=>{var fragment_10=root_19$3(),node_16=first_child(fragment_10);Button(node_16,{variant:"outline",onclick:()=>handleOpenChange(!1), -children:($$anchor8,$$slotProps4)=>{next$1();var text_7=text$8("Cancel");append($$anchor8,text_7)},$$slots:{default:!0}});var node_17=sibling(node_16,2);{var consequent_10=$$anchor8=>{Button($$anchor8,{onclick:handleAttachTemplateResource,get disabled(){return get$3(isAttaching)},children:($$anchor9,$$slotProps4)=>{var fragment_12=root_22$1(),node_18=first_child(fragment_12);{var consequent_9=$$anchor10=>{Loader_circle($$anchor10,{class:"mr-2 h-4 w-4 animate-spin"})},alternate_2=$$anchor10=>{Plus( -$$anchor10,{class:"mr-2 h-4 w-4"})};if_block(node_18,$$render=>{get$3(isAttaching)?$$render(consequent_9):$$render(alternate_2,-1)})}next$1(),append($$anchor9,fragment_12)},$$slots:{default:!0}})},alternate_4=$$anchor8=>{{let $0=user_derived(()=>selectedResources.size===0||get$3(isAttaching));Button($$anchor8,{onclick:handleAttach,get disabled(){return get$3($0)},children:($$anchor9,$$slotProps4)=>{var fragment_16=root_26$1(),node_19=first_child(fragment_16);{var consequent_11=$$anchor10=>{Loader_circle( -$$anchor10,{class:"mr-2 h-4 w-4 animate-spin"})},alternate_3=$$anchor10=>{Plus($$anchor10,{class:"mr-2 h-4 w-4"})};if_block(node_19,$$render=>{get$3(isAttaching)?$$render(consequent_11):$$render(alternate_3,-1)})}var text_8=sibling(node_19);template_effect(()=>set_text(text_8,` Attach ${selectedResources.size>0?`(${selectedResources.size})`:"Resource"}`)),append($$anchor9,fragment_16)},$$slots:{default:!0}})}};if_block(node_17,$$render=>{get$3(hasTemplateResult)?$$render(consequent_10):$$render( -alternate_4,-1)})}append($$anchor7,fragment_10)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_7$8=from_html(''),root_6$8=from_html('· '),root_8$a=from_html(' '),root_5$8=from_html( -'
'),root_3$c=from_html(" ",1),root_10$4=from_html('
'),root_12$3=from_html('
 
'),root_13$3=from_html('
No content available
'),root_2$k=from_html('
',1);function DialogMcpResourcePreview($$anchor,$$props){push$1($$props,!0);let open2=prop($$props,"open",15);const serverName=user_derived(()=>mcpStore.getServerDisplayName($$props.extra.serverName)),favicon=user_derived(()=>mcpStore.getServerFavicon($$props.extra.serverName));function getLanguage(){if($$props.extra.mimeType?.includes( -MimeTypeIncludes.JSON))return MimeTypeIncludes.JSON;if($$props.extra.mimeType?.includes(MimeTypeIncludes.JAVASCRIPT))return MimeTypeIncludes.JAVASCRIPT;if($$props.extra.mimeType?.includes(MimeTypeIncludes.TYPESCRIPT))return MimeTypeIncludes.TYPESCRIPT;const name=$$props.extra.name||$$props.extra.uri||"";return getLanguageFromFilename(name)||"plaintext"}function handleDownload(){$$props.extra.content&&downloadResourceContent($$props.extra.content,$$props.extra.mimeType||MimeTypeText.PLAIN,$$props. -extra.name||DEFAULT_RESOURCE_FILENAME)}var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Root$2,($$anchor2,Dialog_Root)=>{Dialog_Root($$anchor2,{get onOpenChange(){return $$props.onOpenChange},get open(){return open2()},set open($$value){open2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Dialog_content,($$anchor4,Dialog_Content)=>{Dialog_Content($$anchor4,{class:"grid max-h-[90vh] max-w-5xl over\ -flow-hidden sm:w-auto sm:max-w-6xl",children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$k(),node_2=first_child(fragment_2);component(node_2,()=>Dialog_header,($$anchor6,Dialog_Header)=>{Dialog_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_3=root_3$c(),node_3=first_child(fragment_3);component(node_3,()=>Dialog_title,($$anchor8,Dialog_Title)=>{Dialog_Title($$anchor8,{class:"pr-8",children:($$anchor9,$$slotProps4)=>{next$1();var text2=text$8();template_effect(()=>set_text( -text2,$$props.extra.name)),append($$anchor9,text2)},$$slots:{default:!0}})});var node_4=sibling(node_3,2);component(node_4,()=>Dialog_description,($$anchor8,Dialog_Description)=>{Dialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{var div=root_5$8(),span=child(div),text_1=child(span,!0);reset(span);var node_5=sibling(span,2);{var consequent_1=$$anchor10=>{var span_1=root_6$8(),node_6=sibling(child(span_1));{var consequent=$$anchor11=>{var img=root_7$8();template_effect(()=>set_attribute( -img,"src",get$3(favicon))),event("error",img,e=>{e.currentTarget.style.display="none"}),replay_events(img),append($$anchor11,img)};if_block(node_6,$$render=>{get$3(favicon)&&$$render(consequent)})}var text_2=sibling(node_6);reset(span_1),template_effect(()=>set_text(text_2,` ${get$3(serverName)??""}`)),append($$anchor10,span_1)};if_block(node_5,$$render=>{get$3(serverName)&&$$render(consequent_1)})}var node_7=sibling(node_5,2);{var consequent_2=$$anchor10=>{var span_2=root_8$a(),text_3=child(span_2, -!0);reset(span_2),template_effect(()=>set_text(text_3,$$props.extra.mimeType)),append($$anchor10,span_2)};if_block(node_7,$$render=>{$$props.extra.mimeType&&$$render(consequent_2)})}reset(div),template_effect(()=>set_text(text_1,$$props.extra.uri)),append($$anchor9,div)},$$slots:{default:!0}})}),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var div_1=sibling(node_2,2),node_8=child(div_1);{let $0=user_derived(()=>!!$$props.extra.content);ActionIconCopyToClipboard(node_8,{get text(){return $$props. -extra.content},get canCopy(){return get$3($0)},ariaLabel:"Copy content"})}var node_9=sibling(node_8,2);{let $0=user_derived(()=>!$$props.extra.content);Button(node_9,{variant:"ghost",size:"sm",class:"h-7 w-7 p-0",onclick:handleDownload,get disabled(){return get$3($0)},title:"Download content",children:($$anchor6,$$slotProps3)=>{Download($$anchor6,{class:"h-3.5 w-3.5"})},$$slots:{default:!0}})}reset(div_1);var div_2=sibling(div_1,2),node_10=child(div_2);{var consequent_3=$$anchor6=>{var div_3=root_10$4(), -img_1=child(div_3);reset(div_3),template_effect($0=>{set_attribute(img_1,"src",$0),set_attribute(img_1,"alt",$$props.extra.name)},[()=>$$props.extra.content.startsWith("data:")?$$props.extra.content:`data:${$$props.extra.mimeType||"image/png"};base64,${$$props.extra.content}`]),append($$anchor6,div_3)},d2=user_derived(()=>isImageResource($$props.extra.mimeType,$$props.extra.uri)&&$$props.extra.content),consequent_4=$$anchor6=>{{let $0=user_derived(getLanguage);SyntaxHighlightedCode($$anchor6,{get code(){ -return $$props.extra.content},get language(){return get$3($0)},maxHeight:"70vh"})}},d_12=user_derived(()=>isCodeResource($$props.extra.mimeType,$$props.extra.uri)&&$$props.extra.content),consequent_5=$$anchor6=>{var pre=root_12$3(),text_4=child(pre,!0);reset(pre),template_effect(()=>set_text(text_4,$$props.extra.content)),append($$anchor6,pre)},alternate=$$anchor6=>{var div_4=root_13$3();append($$anchor6,div_4)};if_block(node_10,$$render=>{get$3(d2)?$$render(consequent_3):get$3(d_12)?$$render(consequent_4, -1):$$props.extra.content?$$render(consequent_5,2):$$render(alternate,-1)})}reset(div_2),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}let _url=state$1(proxy(SETTINGS_FALLBACK_EXIT_ROUTE));const settingsReferrer={get url(){return get$3(_url)},set url(value){set$1(_url,value,!0)}};var root_3$b=from_html('
'),root$s=from_html(`

Settings are saved in browser's localStorage

`);function SettingsChat($$anchor,$$props){push$1($$props,!0);let activeSlug=user_derived(()=>$$props.initialSection??page$1.params.section??"general"),currentSection=user_derived(()=>SETTINGS_CHAT_SECTIONS.find(section=>section.slug===get$3(activeSlug))||SETTINGS_CHAT_SECTIONS[0]),localConfig=state$1(proxy({...config$1()})),mobileHeader,fetchInitiated=!1;user_effect(()=>{isRouterMode()&&get$3(currentSection).fields&&!fetchInitiated&&(fetchInitiated=!0,modelsStore.fetch().then(()=>modelsStore. -fetchRouterModels()).then(()=>modelsStore.fetchModalitiesForLoadedModels()).then(()=>modelsStore.ensureFirstModelSelected()))});function handleThemeChange(newTheme){get$3(localConfig).theme=newTheme,setMode(newTheme)}function handleConfigChange(key2,value){get$3(localConfig)[key2]=value}function handleReset(){set$1(localConfig,{...config$1()},!0),setMode(get$3(localConfig).theme),mobileHeader?.updateCarousel()}function handleSave(){if(get$3(localConfig).custom&&typeof get$3(localConfig).custom== -"string"&&get$3(localConfig).custom.trim())try{JSON.parse(get$3(localConfig).custom)}catch(error2){alert("Invalid JSON in custom parameters. Please check the format and try again."),console.error(error2);return}const processedConfig={...get$3(localConfig)};for(const field of NUMERIC_FIELDS)if(processedConfig[field]!==void 0&&processedConfig[field]!==""){const numValue=Number(processedConfig[field]);if(!isNaN(numValue))POSITIVE_INTEGER_FIELDS.includes(field)?processedConfig[field]=Math.max(1,Math. -round(numValue)):processedConfig[field]=numValue;else{alert(`Invalid numeric value for ${field}. Please enter a valid number.`);return}}settingsStore.updateMultipleConfig(processedConfig),goto(settingsReferrer.url)}function reset$1(){set$1(localConfig,{...config$1()},!0)}setChatSettingsConfigContext({get localConfig(){return get$3(localConfig)},handleConfigChange,handleThemeChange});var $$exports={reset:reset$1},div=root$s(),div_1=child(div),node2=child(div_1);{let $0=user_derived(()=>$$props.getSectionHref?? -(section=>RouterService.settings(section.slug)));SettingsChatDesktopSidebar(node2,{get sections(){return SETTINGS_CHAT_SECTIONS},isActive:section=>section.slug===get$3(activeSlug),get getHref(){return get$3($0)}})}var node_1=sibling(node2,2);{let $0=user_derived(()=>$$props.getSectionHref??(section=>RouterService.settings(section.slug)));bind_this(SettingsChatMobileHeader(node_1,{get sections(){return SETTINGS_CHAT_SECTIONS},isActive:section=>section.slug===get$3(activeSlug),get getHref(){return get$3( -$0)}}),$$value=>mobileHeader=$$value,()=>mobileHeader)}var div_2=sibling(node_1,2),div_3=child(div_2),div_4=child(div_3),div_5=child(div_4),node_2=child(div_5);component(node_2,()=>get$3(currentSection).icon,($$anchor2,currentSection_icon)=>{currentSection_icon($$anchor2,{class:"h-5 w-5"})});var h3=sibling(node_2,2),text2=child(h3,!0);reset(h3),reset(div_5);var node_3=sibling(div_5,2);{var consequent=$$anchor2=>{SettingsChatToolsTab($$anchor2,{})},consequent_1=$$anchor2=>{SettingsChatImportExportTab( -$$anchor2,{})},consequent_2=$$anchor2=>{var div_6=root_3$b(),node_4=child(div_6);SettingsChatFields(node_4,{get fields(){return get$3(currentSection).fields},get localConfig(){return get$3(localConfig)},onConfigChange:handleConfigChange,onThemeChange:handleThemeChange}),reset(div_6),append($$anchor2,div_6)};if_block(node_3,$$render=>{get$3(currentSection).title===SETTINGS_SECTION_TITLES.TOOLS?$$render(consequent):get$3(currentSection).title===SETTINGS_SECTION_TITLES.IMPORT_EXPORT?$$render(consequent_1, -1):get$3(currentSection).fields&&$$render(consequent_2,2)})}reset(div_4),next$1(2),reset(div_3);var node_5=sibling(div_3,2);return SettingsFooter(node_5,{onReset:handleReset,onSave:handleSave}),reset(div_2),reset(div_1),reset(div),template_effect(()=>set_text(text2,get$3(currentSection).title)),transition(1,div,()=>fade,()=>({duration:150})),append($$anchor,div),pop($$exports)}var root_2$j=from_html(' '),root_3$a=from_html(''),root$r=from_html('');function SettingsChatDesktopSidebar($$anchor,$$props){push$1($$props,!0);var div=root$r(),div_1=child(div),node2=child(div_1);Settings$1(node2,{class:"h-6 w-6"}),next$1(2),reset(div_1);var nav=sibling(div_1,2);each(nav,21,()=>$$props.sections, -section=>section.title,($$anchor2,section)=>{var fragment=comment$2(),node_1=first_child(fragment);{var consequent=$$anchor3=>{var a=root_2$j(),node_2=child(a);component(node_2,()=>get$3(section).icon,($$anchor4,section_icon)=>{section_icon($$anchor4,{class:"h-4 w-4"})});var span=sibling(node_2,2),text2=child(span,!0);reset(span),reset(a),template_effect(($0,$1)=>{set_class(a,1,`flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-2 text-left text-sm no-underline transition-colors ho\ -ver:bg-accent ${$0??""}`),set_attribute(a,"href",$1),set_text(text2,get$3(section).title)},[()=>$$props.isActive(get$3(section))?"bg-accent text-accent-foreground":"text-muted-foreground",()=>$$props.getHref(get$3(section))]),append($$anchor3,a)},alternate=$$anchor3=>{var button=root_3$a(),node_3=child(button);component(node_3,()=>get$3(section).icon,($$anchor4,section_icon_1)=>{section_icon_1($$anchor4,{class:"h-4 w-4"})});var span_1=sibling(node_3,2),text_1=child(span_1,!0);reset(span_1),reset( -button),template_effect($0=>{set_class(button,1,`flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-2 text-left text-sm transition-colors hover:bg-accent ${$0??""}`),set_text(text_1,get$3(section).title)},[()=>$$props.isActive(get$3(section))?"bg-accent text-accent-foreground":"text-muted-foreground"]),delegated("click",button,()=>$$props.onSectionChange?.(get$3(section).title)),append($$anchor3,button)};if_block(node_1,$$render=>{$$props.getHref?$$render(consequent):$$render(alternate, --1)})}append($$anchor2,fragment)}),reset(nav),reset(div),append($$anchor,div),pop()}delegate(["click"]);function useScrollCarousel(){let canScrollLeft=state$1(!1),canScrollRight=state$1(!1),scrollContainer=state$1(void 0);function scrollToCenter(element2){if(!get$3(scrollContainer))return;const containerRect=get$3(scrollContainer).getBoundingClientRect(),elementRect=element2.getBoundingClientRect(),elementCenter=elementRect.left+elementRect.width/2,containerCenter=containerRect.left+containerRect. -width/2,scrollOffset=elementCenter-containerCenter;get$3(scrollContainer).scrollBy({left:scrollOffset,behavior:"smooth"})}function scrollLeft(){get$3(scrollContainer)&&get$3(scrollContainer).scrollBy({left:-250,behavior:"smooth"})}function scrollRight(){get$3(scrollContainer)&&get$3(scrollContainer).scrollBy({left:250,behavior:"smooth"})}function updateScrollButtons(){if(!get$3(scrollContainer))return;const{scrollLeft:sl,scrollWidth,clientWidth}=get$3(scrollContainer);set$1(canScrollLeft,sl>0),set$1( -canScrollRight,sl{get$3(scrollContainer)&&updateScrollButtons()}),{get canScrollLeft(){return get$3(canScrollLeft)},get canScrollRight(){return get$3(canScrollRight)},get scrollContainer(){return get$3(scrollContainer)},set scrollContainer(el){set$1(scrollContainer,el,!0)},scrollToCenter,scrollLeft,scrollRight,updateScrollButtons}}var root_2$i=from_html(" "),root_3$9=from_html(""),root$q=from_html( -'

Settings

');function SettingsChatMobileHeader($$anchor,$$props){push$1($$props,!0);const carousel=useScrollCarousel();onMount$1(async()=>{if(await tick(),carousel.scrollContainer){const activeTab=carousel.scrollContainer.querySelector('[data-active="true"]');activeTab instanceof HTMLElement&&carousel.scrollToCenter(activeTab)}});function updateCarousel(){setTimeout(carousel.updateScrollButtons,100)}var $$exports={updateCarousel},div=root$q(),div_1=child(div),node2=child(div_1);Settings$1( -node2,{class:"h-5 w-5 md:h-6 md:w-6"}),next$1(2),reset(div_1);var div_2=sibling(div_1,2),div_3=child(div_2),button=child(div_3),node_1=child(button);Chevron_left(node_1,{class:"h-4 w-4"}),reset(button);var div_4=sibling(button,2),div_5=child(div_4);each(div_5,21,()=>$$props.sections,section=>section.title,($$anchor2,section)=>{var fragment=comment$2(),node_2=first_child(fragment);{var consequent=$$anchor3=>{var a=root_2$i(),node_3=child(a);component(node_3,()=>get$3(section).icon,($$anchor4,section_icon)=>{ -section_icon($$anchor4,{class:"h-4 w-4 flex-shrink-0"})});var span=sibling(node_3,2),text2=child(span,!0);reset(span),reset(a),template_effect(($0,$1,$2)=>{set_class(a,1,`flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm whitespace-nowrap no-underline transition-colors first:ml-4 last:mr-4 hover:bg-accent ${$0??""}`),set_attribute(a,"data-active",$1),set_attribute(a,"href",$2),set_text(text2,get$3(section).title)},[()=>$$props.isActive(get$3(section))?"bg-accent text-accent-for\ -eground":"text-muted-foreground",()=>$$props.isActive(get$3(section)),()=>$$props.getHref(get$3(section))]),delegated("click",a,e=>{carousel.scrollToCenter(e.currentTarget)}),append($$anchor3,a)},alternate=$$anchor3=>{var button_1=root_3$9(),node_4=child(button_1);component(node_4,()=>get$3(section).icon,($$anchor4,section_icon_1)=>{section_icon_1($$anchor4,{class:"h-4 w-4 flex-shrink-0"})});var span_1=sibling(node_4,2),text_1=child(span_1,!0);reset(span_1),reset(button_1),template_effect(($0,$1)=>{ -set_class(button_1,1,`flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm whitespace-nowrap transition-colors first:ml-4 last:mr-4 hover:bg-accent ${$0??""}`),set_attribute(button_1,"data-active",$1),set_text(text_1,get$3(section).title)},[()=>$$props.isActive(get$3(section))?"bg-accent text-accent-foreground":"text-muted-foreground",()=>$$props.isActive(get$3(section))]),delegated("click",button_1,e=>{$$props.onSectionChange?.(get$3(section).title),carousel.scrollToCenter(e.currentTarget)}), -append($$anchor3,button_1)};if_block(node_2,$$render=>{$$props.getHref?$$render(consequent):$$render(alternate,-1)})}append($$anchor2,fragment)}),reset(div_5),reset(div_4),bind_this(div_4,$$value=>carousel.scrollContainer=$$value,()=>carousel?.scrollContainer);var button_2=sibling(div_4,2),node_5=child(button_2);return Chevron_right(node_5,{class:"h-4 w-4"}),reset(button_2),reset(div_3),reset(div_2),reset(div),template_effect(()=>{set_class(button,1,`absolute left-2 z-10 flex h-6 w-6 items-cente\ -r justify-center rounded-full bg-muted shadow-md backdrop-blur-sm transition-opacity hover:bg-accent ${carousel.canScrollLeft?"opacity-100":"pointer-events-none opacity-0"}`),set_class(button_2,1,`absolute right-2 z-10 flex h-6 w-6 items-center justify-center rounded-full bg-muted shadow-md backdrop-blur-sm transition-opacity hover:bg-accent ${carousel.canScrollRight?"opacity-100":"pointer-events-none opacity-0"}`)}),delegated("click",button,function(...$$args){carousel.scrollLeft?.apply(this,$$args)}), -event("scroll",div_4,function(...$$args){carousel.updateScrollButtons?.apply(this,$$args)}),delegated("click",button_2,function(...$$args){carousel.scrollRight?.apply(this,$$args)}),append($$anchor,div),pop($$exports)}delegate(["click"]);var root_1$g=from_html(" Custom",1);function SettingsChatParameterSourceIndicator($$anchor,$$props){let className=prop($$props,"class",3,"");Badge($$anchor,{variant:"secondary",get class(){return`h-5 bg-orange-100 px-1.5 py-0.5 text-xs text-orange-800 dark:bg\ --orange-900 dark:text-orange-200 ${className()??""}`},children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$g(),node2=first_child(fragment_1);Wrench(node2,{class:"mr-1 h-3 w-3"}),next$1(),append($$anchor2,fragment_1)},$$slots:{default:!0}})}var root$p=from_html('

');function SettingsGroup($$anchor,$$props){var div=root$p(),h3=child(div),text2=child(h3,!0);reset(h3);var div_1=sibling(h3,2),node2=child(div_1); -snippet(node2,()=>$$props.children),reset(div_1),reset(div),template_effect(()=>set_text(text2,$$props.title)),append($$anchor,div)}var root_1$f=from_html(" Reset to default",1),root_5$7=from_html(" ",1),root_8$9=from_html(" ",1),root_4$9=from_html(" ",1),root$o=from_html('
',1);function SettingsFooter($$anchor,$$props){push$1($$props,!0);let showResetDialog=state$1( -!1);function handleResetClick(){set$1(showResetDialog,!0)}function handleConfirmReset(){settingsStore.forceSyncWithServerDefaults(),$$props.onReset?.(),set$1(showResetDialog,!1)}function handleSave(){$$props.onSave?.()}var fragment=root$o(),div=first_child(fragment),div_1=child(div),node2=child(div_1);Button(node2,{variant:"outline",onclick:handleResetClick,children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$f(),node_1=first_child(fragment_1);Rotate_ccw(node_1,{class:"h-3 w-3"}),next$1(),append( -$$anchor2,fragment_1)},$$slots:{default:!0}}),reset(div_1);var node_2=sibling(div_1,2);Button(node_2,{onclick:handleSave,children:($$anchor2,$$slotProps)=>{next$1();var text2=text$8("Save settings");append($$anchor2,text2)},$$slots:{default:!0}}),reset(div);var node_3=sibling(div,2);component(node_3,()=>Root$1,($$anchor2,AlertDialog_Root)=>{AlertDialog_Root($$anchor2,{get open(){return get$3(showResetDialog)},set open($$value){set$1(showResetDialog,$$value,!0)},children:($$anchor3,$$slotProps)=>{ -var fragment_2=comment$2(),node_4=first_child(fragment_2);component(node_4,()=>Alert_dialog_content,($$anchor4,AlertDialog_Content)=>{AlertDialog_Content($$anchor4,{children:($$anchor5,$$slotProps2)=>{var fragment_3=root_4$9(),node_5=first_child(fragment_3);component(node_5,()=>Alert_dialog_header,($$anchor6,AlertDialog_Header)=>{AlertDialog_Header($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_4=root_5$7(),node_6=first_child(fragment_4);component(node_6,()=>Alert_dialog_title,($$anchor8,AlertDialog_Title)=>{ -AlertDialog_Title($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text_1=text$8("Reset Settings to Default");append($$anchor9,text_1)},$$slots:{default:!0}})});var node_7=sibling(node_6,2);component(node_7,()=>Alert_dialog_description,($$anchor8,AlertDialog_Description)=>{AlertDialog_Description($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1();var text_2=text$8(`Are you sure you want to reset all settings to their default values? This will reset all - parameters to the values provided by the server's /props endpoint and remove all your custom - configurations.`);append($$anchor9,text_2)},$$slots:{default:!0}})}),append($$anchor7,fragment_4)},$$slots:{default:!0}})});var node_8=sibling(node_5,2);component(node_8,()=>Alert_dialog_footer,($$anchor6,AlertDialog_Footer)=>{AlertDialog_Footer($$anchor6,{children:($$anchor7,$$slotProps3)=>{var fragment_5=root_8$9(),node_9=first_child(fragment_5);component(node_9,()=>Alert_dialog_cancel,($$anchor8,AlertDialog_Cancel)=>{AlertDialog_Cancel($$anchor8,{children:($$anchor9,$$slotProps4)=>{next$1(); -var text_3=text$8("Cancel");append($$anchor9,text_3)},$$slots:{default:!0}})});var node_10=sibling(node_9,2);component(node_10,()=>Alert_dialog_action,($$anchor8,AlertDialog_Action)=>{AlertDialog_Action($$anchor8,{onclick:handleConfirmReset,children:($$anchor9,$$slotProps4)=>{next$1();var text_4=text$8("Reset to Default");append($$anchor9,text_4)},$$slots:{default:!0}})}),append($$anchor7,fragment_5)},$$slots:{default:!0}})}),append($$anchor5,fragment_3)},$$slots:{default:!0}})}),append($$anchor3, -fragment_2)},$$slots:{default:!0}})}),append($$anchor,fragment),pop()}var root_1$e=from_html(" ",1),root_3$8=from_html('
  • '),root_4$8=from_html('
  • '),root_2$h=from_html('
    '),root$n=from_html('

    ');function SettingsChatImportExportSection($$anchor,$$props){push$1($$props,!0);let sectionButtonClass=user_derived(()=>$$props.buttonClass??"justify-start justify-self-start md:w-auto"),sectionButtonVariant=user_derived(()=>$$props.buttonVariant??"outline");var div=root$n(),h4=child(div),text2=child(h4,!0);reset(h4);var p2=sibling(h4,2),text_1=child(p2,!0);reset(p2);var node2=sibling(p2,2);Button(node2,{get class(){return get$3(sectionButtonClass)},get onclick(){return $$props. -onclick},get variant(){return get$3(sectionButtonVariant)},children:($$anchor2,$$slotProps)=>{var fragment=root_1$e(),node_1=first_child(fragment);component(node_1,()=>$$props.IconComponent,($$anchor3,IconComponent_1)=>{IconComponent_1($$anchor3,{class:"mr-2 h-4 w-4"})});var text_2=sibling(node_1);template_effect(()=>set_text(text_2,` ${$$props.buttonText??""}`)),append($$anchor2,fragment)},$$slots:{default:!0}});var node_2=sibling(node2,2);{var consequent_1=$$anchor2=>{var div_1=root_2$h(),h5=child( -div_1),text_3=child(h5);reset(h5);var ul=sibling(h5,2),node_3=child(ul);each(node_3,17,()=>$$props.summary.items.slice(0,10),conv=>conv.id,($$anchor3,conv)=>{var li2=root_3$8(),text_4=child(li2);reset(li2),template_effect(()=>set_text(text_4,`• ${(get$3(conv).name||"Untitled conversation")??""}`)),append($$anchor3,li2)});var node_4=sibling(node_3,2);{var consequent=$$anchor3=>{var li_1=root_4$8(),text_5=child(li_1);reset(li_1),template_effect(()=>set_text(text_5,`... and ${$$props.summary.items. -length-10} more`)),append($$anchor3,li_1)};if_block(node_4,$$render=>{$$props.summary.items.length>10&&$$render(consequent)})}reset(ul),reset(div_1),template_effect(()=>set_text(text_3,`${$$props.summary.verb??""} - ${$$props.summary.items.length??""} conversation${$$props.summary.items.length===1?"":"s"}`)),append($$anchor2,div_1)};if_block(node_2,$$render=>{$$props.summary&&$$props.summary.show&&$$props.summary.items.length>0&&$$render(consequent_1)})}reset(div),template_effect(()=>{set_class(div,1,`grid gap-1 ${$$props.wrapperClass??""??""}`),set_class(h4,1,`mt-0 mb-2 text-sm font-medium ${$$props.titleClass??""??""}`),set_text(text2,$$props.title),set_text(text_1,$$props.description)}),append($$anchor, -div),pop()}var root_1$d=from_html(" ",1),root_2$g=from_html(" ",1),root$m=from_html('
    ',1);function SettingsChatImportExportTab($$anchor,$$props){push$1($$props,!0);let exportedConversations=state$1(proxy([])),importedConversations=state$1(proxy([])),showExportSummary=state$1(!1),showImportSummary=state$1(!1),showExportDialog=state$1(!1),showImportDialog=state$1(!1),availableConversations=state$1(proxy([])),messageCountMap=state$1( -proxy(new Map)),fullImportData=state$1(proxy([])),showDeleteDialog=state$1(!1),showSettingsExportSummary=state$1(!1),showSettingsImportSummary=state$1(!1),showSettingsExportDialog=state$1(!1),includeSensitiveData=state$1(!1);function handleSettingsExport(){set$1(showSettingsExportDialog,!0),set$1(includeSensitiveData,!1)}function handleSettingsExportConfirm(){set$1(showSettingsExportDialog,!1);try{const data=settingsStore.exportSettings(get$3(includeSensitiveData)),blob=new Blob([JSON.stringify( -data,null,2)],{type:"application/json"}),url2=URL.createObjectURL(blob),a=document.createElement("a");a.href=url2,a.download=`llama_settings_${new Date().toISOString().split("T")[0]}.json`,document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(url2),set$1(showSettingsExportSummary,!0),set$1(showSettingsImportSummary,!1),toast.success("Settings exported")}catch(err){console.error("Failed to export settings:",err),toast.error("Failed to export settings")}}function handleSettingsExportCancel(){ -set$1(showSettingsExportDialog,!1)}function handleSettingsImport(){try{const input=document.createElement("input");input.type=HtmlInputType.FILE,input.accept=FileExtensionText.JSON,input.onchange=async e=>{const file=e.target?.files?.[0];if(file)try{const text2=await file.text(),data=JSON.parse(text2);if(!data||typeof data!="object"||!data.config){toast.error("Invalid settings file: missing config");return}settingsStore.importSettings(data),set$1(showSettingsImportSummary,!0),set$1(showSettingsExportSummary, -!1),toast.success("Settings imported successfully")}catch(err){console.error("Failed to import settings:",err),toast.error("Failed to import settings")}},input.click()}catch(err){console.error("Failed to open file picker:",err),toast.error("Failed to open file picker")}}async function handleExportClick(){try{const allConversations=conversations();if(allConversations.length===0){toast.info("No conversations to export");return}const conversationsWithMessages=await Promise.all(allConversations.map( -async conv=>{const messages=await conversationsStore.getConversationMessages(conv.id);return{conv,messages}}));set$1(messageCountMap,createMessageCountMap(conversationsWithMessages),!0),set$1(availableConversations,allConversations,!0),set$1(showExportDialog,!0)}catch(err){console.error("Failed to load conversations:",err),alert("Failed to load conversations")}}async function handleExportConfirm(selectedConversations){try{const allData=await Promise.all(selectedConversations.map(async conv=>{const messages=await conversationsStore. -getConversationMessages(conv.id);return{conv:snapshot(conv),messages:snapshot(messages)}}));conversationsStore.downloadConversationFile(allData),set$1(exportedConversations,selectedConversations,!0),set$1(showExportSummary,!0),set$1(showImportSummary,!1),set$1(showExportDialog,!1)}catch(err){console.error("Export failed:",err),alert("Failed to export conversations")}}async function handleImportClick(){try{const input=document.createElement("input");input.type=HtmlInputType.FILE,input.accept=FileExtensionText. -JSON,input.onchange=async e=>{const file=e.target?.files?.[0];if(file)try{const text2=await file.text(),parsedData=JSON.parse(text2);let importedData;if(Array.isArray(parsedData))importedData=parsedData;else if(parsedData&&typeof parsedData=="object"&&"conv"in parsedData&&"messages"in parsedData)importedData=[parsedData];else throw new Error("Invalid file format: expected array of conversations or single conversation object");set$1(fullImportData,importedData,!0),set$1(availableConversations,importedData. -map(item=>item.conv),!0),set$1(messageCountMap,createMessageCountMap(importedData),!0),set$1(showImportDialog,!0)}catch(err){const message=err instanceof Error?err.message:"Unknown error";console.error("Failed to parse file:",err),alert(`Failed to parse file: ${message}`)}},input.click()}catch(err){console.error("Import failed:",err),alert("Failed to import conversations")}}async function handleImportConfirm(selectedConversations){try{const selectedIds=new Set(selectedConversations.map(c2=>c2.id)), -selectedData=snapshot(get$3(fullImportData)).filter(item=>selectedIds.has(item.conv.id));await conversationsStore.importConversationsData(selectedData),set$1(importedConversations,selectedConversations,!0),set$1(showImportSummary,!0),set$1(showExportSummary,!1),set$1(showImportDialog,!1)}catch(err){console.error("Import failed:",err),alert("Failed to import conversations. Please check the file format.")}}async function handleDeleteAllClick(){try{if(conversations().length===0){toast.info("No conv\ -ersations to delete");return}set$1(showDeleteDialog,!0)}catch(err){console.error("Failed to load conversations for deletion:",err),toast.error("Failed to load conversations")}}async function handleDeleteAllConfirm(){try{await conversationsStore.deleteAll(),set$1(showDeleteDialog,!1)}catch(err){console.error("Failed to delete conversations:",err)}}function handleDeleteAllCancel(){set$1(showDeleteDialog,!1)}var fragment=root$m(),div=first_child(fragment),node2=child(div);SettingsGroup(node2,{title:"\ -Conversations",children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$d(),node_1=first_child(fragment_1);{let $0=user_derived(()=>({show:get$3(showExportSummary),verb:"Exported",items:get$3(exportedConversations)}));SettingsChatImportExportSection(node_1,{title:"Export",description:"Download your conversations as a JSON file. This includes all messages, attachments, and conversation history.",get IconComponent(){return Download},buttonText:"Export conversations",onclick:handleExportClick,get summary(){ -return get$3($0)}})}var node_2=sibling(node_1,2);{let $0=user_derived(()=>({show:get$3(showImportSummary),verb:"Imported",items:get$3(importedConversations)}));SettingsChatImportExportSection(node_2,{title:"Import",description:"Import one or more conversations from a previously exported JSON file. This will merge with your existing conversations.",get IconComponent(){return Upload},buttonText:"Import conversations",onclick:handleImportClick,get summary(){return get$3($0)}})}var node_3=sibling(node_2, -2);SettingsChatImportExportSection(node_3,{title:"Delete All",description:"Permanently delete all conversations and their messages. This action cannot be undone. Consider exporting your conversations first if you want to keep a backup.",get IconComponent(){return Trash_2},buttonText:"Delete all conversations",onclick:handleDeleteAllClick,titleClass:"text-destructive",buttonVariant:"destructive",buttonClass:"text-destructive-foreground justify-start justify-self-start bg-destructive hover:bg-dest\ -ructive/80 md:w-auto"}),append($$anchor2,fragment_1)}});var node_4=sibling(node2,2);SettingsGroup(node_4,{title:"Settings",children:($$anchor2,$$slotProps)=>{var fragment_2=root_2$g(),node_5=first_child(fragment_2);{let $0=user_derived(()=>({show:get$3(showSettingsExportSummary),verb:"Exported",items:[]}));SettingsChatImportExportSection(node_5,{title:"Export",description:"Export your chat settings and preferences as a JSON file.",get IconComponent(){return Download},buttonText:"Export settings", -onclick:handleSettingsExport,get summary(){return get$3($0)}})}var node_6=sibling(node_5,2);{let $0=user_derived(()=>({show:get$3(showSettingsImportSummary),verb:"Imported",items:[]}));SettingsChatImportExportSection(node_6,{title:"Import",description:"Import chat settings from a previously exported JSON file. This will merge with your existing settings.",get IconComponent(){return Upload},buttonText:"Import settings",onclick:handleSettingsImport,get summary(){return get$3($0)}})}append($$anchor2, -fragment_2)}}),reset(div);var node_7=sibling(div,2);DialogExportSettings(node_7,{onConfirm:handleSettingsExportConfirm,onCancel:handleSettingsExportCancel,get open(){return get$3(showSettingsExportDialog)},set open($$value){set$1(showSettingsExportDialog,$$value,!0)},get includeSensitiveData(){return get$3(includeSensitiveData)},set includeSensitiveData($$value){set$1(includeSensitiveData,$$value,!0)}});var node_8=sibling(node_7,2);DialogConversationSelection(node_8,{get conversations(){return get$3( -availableConversations)},get messageCountMap(){return get$3(messageCountMap)},get mode(){return ConversationSelectionMode.EXPORT},onCancel:()=>set$1(showExportDialog,!1),onConfirm:handleExportConfirm,get open(){return get$3(showExportDialog)},set open($$value){set$1(showExportDialog,$$value,!0)}});var node_9=sibling(node_8,2);DialogConversationSelection(node_9,{get conversations(){return get$3(availableConversations)},get messageCountMap(){return get$3(messageCountMap)},get mode(){return ConversationSelectionMode. -IMPORT},onCancel:()=>set$1(showImportDialog,!1),onConfirm:handleImportConfirm,get open(){return get$3(showImportDialog)},set open($$value){set$1(showImportDialog,$$value,!0)}});var node_10=sibling(node_9,2);DialogConfirmation(node_10,{title:"Delete all conversations",description:"Are you sure you want to delete all conversations? This action cannot be undone and will permanently remove all your conversations and messages.",confirmText:"Delete All",cancelText:"Cancel",variant:"destructive",get icon(){ -return Trash_2},onConfirm:handleDeleteAllConfirm,onCancel:handleDeleteAllCancel,get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)}}),transition(1,div,()=>fade,()=>({duration:150})),append($$anchor,fragment),pop()}var root_1$c=from_html(' ',1);function Select_item($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,[ -"$$slots","$$events","$$legacy","ref","class","value","label","children"]);var fragment=comment$2(),node2=first_child(fragment);{const children=($$anchor2,$$arg0)=>{let selected=()=>$$arg0?.().selected,highlighted=()=>$$arg0?.().highlighted;var fragment_1=root_1$c(),span=first_child(fragment_1),node_1=child(span);{var consequent=$$anchor3=>{Check($$anchor3,{class:"size-4"})};if_block(node_1,$$render=>{selected()&&$$render(consequent)})}reset(span);var node_2=sibling(span,2);{var consequent_1=$$anchor3=>{ -var fragment_3=comment$2(),node_3=first_child(fragment_3);snippet(node_3,()=>$$props.children,()=>({selected:selected(),highlighted:highlighted()})),append($$anchor3,fragment_3)},alternate=$$anchor3=>{var text2=text$8();template_effect(()=>set_text(text2,$$props.label||$$props.value)),append($$anchor3,text2)};if_block(node_2,$$render=>{$$props.children?$$render(consequent_1):$$render(alternate,-1)})}append($$anchor2,fragment_1)};let $0=user_derived(()=>cn$1("relative flex w-full cursor-default i\ -tems-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",$$props.class));component(node2,()=>Select_item$1,($$anchor2,SelectPrimitive_Item)=>{ -SelectPrimitive_Item($$anchor2,spread_props({get value(){return $$props.value},"data-slot":"select-item",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children,$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}function Select_scroll_up_button($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child( -fragment);{let $0=user_derived(()=>cn$1("flex cursor-default items-center justify-center py-1",$$props.class));component(node2,()=>Select_scroll_up_button$1,($$anchor2,SelectPrimitive_ScrollUpButton)=>{SelectPrimitive_ScrollUpButton($$anchor2,spread_props({"data-slot":"select-scroll-up-button",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{Chevron_up($$anchor3,{class:"size-4"})},$$slots:{default:!0}}))})}append( -$$anchor,fragment),pop()}function Select_scroll_down_button($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class"]);var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1("flex cursor-default items-center justify-center py-1",$$props.class));component(node2,()=>Select_scroll_down_button$1,($$anchor2,SelectPrimitive_ScrollDownButton)=>{SelectPrimitive_ScrollDownButton($$anchor2, -spread_props({"data-slot":"select-scroll-down-button",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{Chevron_down($$anchor3,{class:"size-4"})},$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}var root_2$f=from_html(" ",1);function Select_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),sideOffset=prop($$props,"sideOffset",3,4),restProps=rest_props($$props, -["$$slots","$$events","$$legacy","ref","class","sideOffset","portalProps","children"]),cleanupInternalListeners;onMount$1(()=>{const listenerOptions={passive:!1},blockOutsideWheel=event2=>{if(!ref2())return;const target2=event2.target;(!target2||!ref2().contains(target2))&&(event2.preventDefault(),event2.stopPropagation())},blockOutsideTouchMove=event2=>{if(!ref2())return;const target2=event2.target;(!target2||!ref2().contains(target2))&&(event2.preventDefault(),event2.stopPropagation())};return document. -addEventListener("wheel",blockOutsideWheel,listenerOptions),document.addEventListener("touchmove",blockOutsideTouchMove,listenerOptions),()=>{document.removeEventListener("wheel",blockOutsideWheel,listenerOptions),document.removeEventListener("touchmove",blockOutsideTouchMove,listenerOptions)}}),user_effect(()=>{const element2=ref2();if(cleanupInternalListeners?.(),!element2)return;const stopWheelPropagation=event2=>{event2.stopPropagation()},stopTouchPropagation=event2=>{event2.stopPropagation()}; -element2.addEventListener("wheel",stopWheelPropagation),element2.addEventListener("touchmove",stopTouchPropagation),cleanupInternalListeners=()=>{element2.removeEventListener("wheel",stopWheelPropagation),element2.removeEventListener("touchmove",stopTouchPropagation)}}),onDestroy(()=>{cleanupInternalListeners?.()});var fragment=comment$2(),node2=first_child(fragment);component(node2,()=>Portal$2,($$anchor2,SelectPrimitive_Portal)=>{SelectPrimitive_Portal($$anchor2,spread_props(()=>$$props.portalProps, -{children:($$anchor3,$$slotProps)=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);{let $0=user_derived(()=>cn$1("relative z-[var(--layer-popover,1000000)] max-h-(--bits-select-content-available-height) min-w-[8rem] origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:-translate-x-1 data-[side=left]:slide-i\ -n-from-right-2 data-[side=right]:translate-x-1 data-[side=right]:slide-in-from-left-2 data-[side=top]:-translate-y-1 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",$$props.class));component(node_1,()=>Select_content$1,($$anchor4,SelectPrimitive_Content)=>{SelectPrimitive_Content($$anchor4, -spread_props({get sideOffset(){return sideOffset()},"data-slot":"select-content",get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor5,$$slotProps2)=>{var fragment_2=root_2$f(),node_2=first_child(fragment_2);Select_scroll_up_button(node_2,{});var node_3=sibling(node_2,2);{let $02=user_derived(()=>cn$1("h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1"));component(node_3,()=>Select_viewport, -($$anchor6,SelectPrimitive_Viewport)=>{SelectPrimitive_Viewport($$anchor6,{get class(){return get$3($02)},children:($$anchor7,$$slotProps3)=>{var fragment_3=comment$2(),node_4=first_child(fragment_3);snippet(node_4,()=>$$props.children??noop$3),append($$anchor7,fragment_3)},$$slots:{default:!0}})})}var node_5=sibling(node_3,2);Select_scroll_down_button(node_5,{}),append($$anchor5,fragment_2)},$$slots:{default:!0}}))})}append($$anchor3,fragment_1)},$$slots:{default:!0}}))}),append($$anchor,fragment), -pop()}var root_1$b=from_html(" ",1);function Select_trigger($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),size2=prop($$props,"size",3,"default"),variant=prop($$props,"variant",3,"default"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children","size","variant"]);const baseClasses=user_derived(()=>variant()==="plain"?"group inline-flex w-full items-center justify-end gap-2 whitespace-nowrap px-0 py-0 text-sm font-medium text-muted-\ -foreground transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3 [&_svg:not([class*='text-'])]:text-muted-foreground":"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap\ - shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/\ -30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground"),chevronClasses=user_derived(()=>variant()==="plain"?"size-3 opacity-60 transition-transform group-data-[state=open]:-rotate-180":"size-4 opacity-50");var fragment=comment$2(),node2=first_child(fragment);{let $0=user_derived(()=>cn$1(get$3(baseClasses),$$props.class));component(node2,()=>Select_trigger$1, -($$anchor2,SelectPrimitive_Trigger)=>{SelectPrimitive_Trigger($$anchor2,spread_props({"data-slot":"select-trigger",get"data-size"(){return size2()},get class(){return get$3($0)}},()=>restProps,{get ref(){return ref2()},set ref($$value){ref2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1$b(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.children??noop$3);var node_2=sibling(node_1,2);Chevron_down(node_2,{get class(){return get$3(chevronClasses)}}),append($$anchor3,fragment_1)}, -$$slots:{default:!0}}))})}append($$anchor,fragment),pop()}const Root=Select;var root$l=from_html("");function Textarea($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),value=prop($$props,"value",15),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","value","class"]);var textarea=root$l();remove_textarea_child(textarea),attribute_effect(textarea,$0=>({"data-slot":"textarea",class:$0,...restProps}),[()=>cn$1("flex field-sizing-content\ - min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",$$props.class)]),bind_this(textarea,$$value=>ref2($$value),()=>ref2()),bind_value( -textarea,value),append($$anchor,textarea),pop()}var root_3$7=from_html(" ",1),root_6$7=from_html(''),root_7$7=from_html('

    '),root_2$e=from_html('
    ',1),root_10$3=from_html(" ",1),root_12$2=from_html('

    '),root_13$2=from_html('
    '),root_8$8=from_html(" ",1),root_16$1=from_html(" ",1),root_20$1=from_html('
    '),root_22=from_html(''),root_26=from_html('
    '),root_19$2=from_html('
    ',1),root_28=from_html('

    '),root_15$2=from_html('
    ',1),root_31=from_html('

    '),root_29=from_html('
    '),root_1$a=from_html('
    ');function SettingsChatFields($$anchor,$$props){push$1($$props,!0);let currentModelParams=user_derived(()=>{if(propsCacheVersion(),serverStore.isRouterMode){const currentModelName=selectedModelName();if(currentModelName)return modelsStore.getModelProps( -currentModelName)?.default_generation_settings?.params??{}}return serverStore.defaultParams??{}});var fragment=comment$2(),node2=first_child(fragment);each(node2,17,()=>$$props.fields,field=>field.key,($$anchor2,field)=>{var div=root_1$a(),node_1=child(div);{var consequent_4=$$anchor3=>{const currentValue=user_derived(()=>String($$props.localConfig[get$3(field).key]??"")),serverDefault=user_derived(()=>get$3(currentModelParams)[get$3(field).key]),isCustomRealTime=user_derived(()=>(()=>{if(get$3( -serverDefault)==null||get$3(currentValue)==="")return!1;const numericInput=parseFloat(get$3(currentValue)),normalizedInput=isNaN(numericInput)?get$3(currentValue):Math.round(numericInput*1e6)/1e6,normalizedDefault=typeof get$3(serverDefault)=="number"?Math.round(get$3(serverDefault)*1e6)/1e6:get$3(serverDefault);return normalizedInput!==normalizedDefault})());var fragment_1=root_2$e(),div_1=first_child(fragment_1),node_2=child(div_1);Label(node_2,{get for(){return get$3(field).key},class:"flex i\ -tems-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_2=root_3$7(),text2=first_child(fragment_2),node_3=sibling(text2);{var consequent=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_3,$$render=>{get$3(field).isExperimental&&$$render(consequent)})}template_effect(()=>set_text(text2,`${get$3(field).label??""} `)),append($$anchor4,fragment_2)},$$slots:{default:!0}});var node_4=sibling(node_2,2);{var consequent_1=$$anchor4=>{ -SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_4,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_1)})}reset(div_1);var div_2=sibling(div_1,2),node_5=child(div_2);{let $0=user_derived(()=>get$3(currentModelParams)[get$3(field).key]!=null?`Default: ${normalizeFloatingPoint(get$3(currentModelParams)[get$3(field).key])}`:""),$1=user_derived(()=>get$3(isCustomRealTime)?"pr-8":"");Input(node_5,{get id(){return get$3(field).key},get value(){return get$3(currentValue)},oninput:e=>{ -$$props.onConfigChange(get$3(field).key,e.currentTarget.value)},get placeholder(){return get$3($0)},get class(){return`w-full ${get$3($1)??""}`}})}var node_6=sibling(node_5,2);{var consequent_2=$$anchor4=>{var button=root_6$7(),node_7=child(button);Rotate_ccw(node_7,{class:"h-3 w-3"}),reset(button),delegated("click",button,()=>{settingsStore.resetParameterToServerDefault(get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}),append($$anchor4,button)};if_block(node_6,$$render=>{get$3(isCustomRealTime)&& -$$render(consequent_2)})}reset(div_2);var node_8=sibling(div_2,2);{var consequent_3=$$anchor4=>{var p2=root_7$7();html$6(p2,()=>get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key],!0),reset(p2),append($$anchor4,p2)};if_block(node_8,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_3)})}append($$anchor3,fragment_1)},consequent_9=$$anchor3=>{var fragment_5=root_8$8(),node_9=first_child(fragment_5);{var consequent_6=$$anchor4=>{Label($$anchor4,{get for(){ -return get$3(field).key},class:"block flex items-center gap-1.5 text-sm font-medium",children:($$anchor5,$$slotProps)=>{next$1();var fragment_7=root_10$3(),text_1=first_child(fragment_7),node_10=sibling(text_1);{var consequent_5=$$anchor6=>{Flask_conical($$anchor6,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_10,$$render=>{get$3(field).isExperimental&&$$render(consequent_5)})}template_effect(()=>set_text(text_1,`${get$3(field).label??""} `)),append($$anchor5,fragment_7)},$$slots:{default:!0}})}; -if_block(node_9,$$render=>{get$3(field).label&&$$render(consequent_6)})}var node_11=sibling(node_9,2);{let $0=user_derived(()=>String($$props.localConfig[get$3(field).key]??""));Textarea(node_11,{get id(){return get$3(field).key},get value(){return get$3($0)},onchange:e=>$$props.onConfigChange(get$3(field).key,e.currentTarget.value),placeholder:"",class:"min-h-[10rem] w-full md:max-w-3xl"})}var node_12=sibling(node_11,2);{var consequent_7=$$anchor4=>{var p_1=root_12$2(),text_2=child(p_1,!0);reset( -p_1),template_effect(()=>set_text(text_2,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_1)};if_block(node_12,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_7)})}var node_13=sibling(node_12,2);{var consequent_8=$$anchor4=>{var div_3=root_13$2(),node_14=child(div_3);{let $0=user_derived(()=>!!($$props.localConfig.showSystemMessage??!0));Checkbox(node_14,{id:"showSystemMessage",get checked(){return get$3($0)},onCheckedChange:checked=>$$props. -onConfigChange(SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE,!!checked)})}var node_15=sibling(node_14,2);Label(node_15,{for:"showSystemMessage",class:"cursor-pointer text-sm font-normal",children:($$anchor5,$$slotProps)=>{next$1();var text_3=text$8("Show system message in conversations");append($$anchor5,text_3)},$$slots:{default:!0}}),reset(div_3),append($$anchor4,div_3)};if_block(node_13,$$render=>{get$3(field).key===SETTINGS_KEYS.SYSTEM_MESSAGE&&$$render(consequent_8)})}append($$anchor3,fragment_5)},consequent_17=$$anchor3=>{ -const selectedOption=user_derived(()=>get$3(field).options?.find(opt=>opt.value===$$props.localConfig[get$3(field).key])),currentValue=user_derived(()=>$$props.localConfig[get$3(field).key]),serverDefault=user_derived(()=>get$3(currentModelParams)[get$3(field).key]),isCustomRealTime=user_derived(()=>get$3(serverDefault)==null||get$3(currentValue)===""||get$3(currentValue)===void 0?!1:get$3(currentValue)!==get$3(serverDefault));var fragment_9=root_15$2(),div_4=first_child(fragment_9),node_16=child( -div_4);Label(node_16,{get for(){return get$3(field).key},class:"flex items-center gap-1.5 text-sm font-medium",children:($$anchor4,$$slotProps)=>{next$1();var fragment_10=root_16$1(),text_4=first_child(fragment_10),node_17=sibling(text_4);{var consequent_10=$$anchor5=>{Flask_conical($$anchor5,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_17,$$render=>{get$3(field).isExperimental&&$$render(consequent_10)})}template_effect(()=>set_text(text_4,`${get$3(field).label??""} `)),append($$anchor4, -fragment_10)},$$slots:{default:!0}});var node_18=sibling(node_16,2);{var consequent_11=$$anchor4=>{SettingsChatParameterSourceIndicator($$anchor4,{})};if_block(node_18,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_11)})}reset(div_4);var node_19=sibling(div_4,2);component(node_19,()=>Root,($$anchor4,Select_Root)=>{Select_Root($$anchor4,{type:"single",get value(){return get$3(currentValue)},onValueChange:value=>{get$3(field).key===SETTINGS_KEYS.THEME&&value&&$$props.onThemeChange?$$props. -onThemeChange(value):$$props.onConfigChange(get$3(field).key,value)},children:($$anchor5,$$slotProps)=>{var fragment_13=root_19$2(),div_5=first_child(fragment_13),node_20=child(div_5);component(node_20,()=>Select_trigger,($$anchor6,Select_Trigger)=>{Select_Trigger($$anchor6,{class:"w-full",children:($$anchor7,$$slotProps2)=>{var div_6=root_20$1(),node_21=child(div_6);{var consequent_12=$$anchor8=>{const IconComponent=user_derived(()=>get$3(selectedOption).icon);var fragment_14=comment$2(),node_22=first_child( -fragment_14);component(node_22,()=>get$3(IconComponent),($$anchor9,IconComponent_1)=>{IconComponent_1($$anchor9,{class:"h-4 w-4"})}),append($$anchor8,fragment_14)};if_block(node_21,$$render=>{get$3(selectedOption)?.icon&&$$render(consequent_12)})}var text_5=sibling(node_21);reset(div_6),template_effect($0=>set_text(text_5,` ${$0??""}`),[()=>get$3(selectedOption)?.label||`Select ${get$3(field).label.toLowerCase()}`]),append($$anchor7,div_6)},$$slots:{default:!0}})});var node_23=sibling(node_20,2); -{var consequent_13=$$anchor6=>{var button_1=root_22(),node_24=child(button_1);Rotate_ccw(node_24,{class:"h-3 w-3"}),reset(button_1),delegated("click",button_1,()=>{settingsStore.resetParameterToServerDefault(get$3(field).key),$$props.onConfigChange(get$3(field).key,"")}),append($$anchor6,button_1)};if_block(node_23,$$render=>{get$3(isCustomRealTime)&&$$render(consequent_13)})}reset(div_5);var node_25=sibling(div_5,2);component(node_25,()=>Select_content,($$anchor6,Select_Content)=>{Select_Content( -$$anchor6,{children:($$anchor7,$$slotProps2)=>{var fragment_15=comment$2(),node_26=first_child(fragment_15);{var consequent_15=$$anchor8=>{var fragment_16=comment$2(),node_27=first_child(fragment_16);each(node_27,17,()=>get$3(field).options,option2=>option2.value,($$anchor9,option2)=>{var fragment_17=comment$2(),node_28=first_child(fragment_17);component(node_28,()=>Select_item,($$anchor10,Select_Item)=>{Select_Item($$anchor10,{get value(){return get$3(option2).value},get label(){return get$3(option2). -label},children:($$anchor11,$$slotProps3)=>{var div_7=root_26(),node_29=child(div_7);{var consequent_14=$$anchor12=>{const IconComponent=user_derived(()=>get$3(option2).icon);var fragment_18=comment$2(),node_30=first_child(fragment_18);component(node_30,()=>get$3(IconComponent),($$anchor13,IconComponent_2)=>{IconComponent_2($$anchor13,{class:"h-4 w-4"})}),append($$anchor12,fragment_18)};if_block(node_29,$$render=>{get$3(option2).icon&&$$render(consequent_14)})}var text_6=sibling(node_29);reset(div_7), -template_effect(()=>set_text(text_6,` ${get$3(option2).label??""}`)),append($$anchor11,div_7)},$$slots:{default:!0}})}),append($$anchor9,fragment_17)}),append($$anchor8,fragment_16)};if_block(node_26,$$render=>{get$3(field).options&&$$render(consequent_15)})}append($$anchor7,fragment_15)},$$slots:{default:!0}})}),append($$anchor5,fragment_13)},$$slots:{default:!0}})});var node_31=sibling(node_19,2);{var consequent_16=$$anchor4=>{var p_2=root_28(),text_7=child(p_2,!0);reset(p_2),template_effect(()=>set_text( -text_7,get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])),append($$anchor4,p_2)};if_block(node_31,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_16)})}append($$anchor3,fragment_9)},consequent_20=$$anchor3=>{var div_8=root_29(),node_32=child(div_8);{let $0=user_derived(()=>!!$$props.localConfig[get$3(field).key]);Checkbox(node_32,{get id(){return get$3(field).key},get checked(){return get$3($0)},onCheckedChange:checked=>$$props.onConfigChange(get$3( -field).key,checked),class:"mt-1"})}var div_9=sibling(node_32,2),label=child(div_9),text_8=child(label),node_33=sibling(text_8);{var consequent_18=$$anchor4=>{Flask_conical($$anchor4,{class:"h-3.5 w-3.5 text-muted-foreground"})};if_block(node_33,$$render=>{get$3(field).isExperimental&&$$render(consequent_18)})}reset(label);var node_34=sibling(label,2);{var consequent_19=$$anchor4=>{var p_3=root_31(),text_9=child(p_3,!0);reset(p_3),template_effect(()=>set_text(text_9,get$3(field).help||SETTING_CONFIG_INFO[get$3( -field).key])),append($$anchor4,p_3)};if_block(node_34,$$render=>{(get$3(field).help||SETTING_CONFIG_INFO[get$3(field).key])&&$$render(consequent_19)})}reset(div_9),reset(div_8),template_effect(()=>{set_attribute(label,"for",get$3(field).key),set_text(text_8,`${get$3(field).label??""} `)}),append($$anchor3,div_8)};if_block(node_1,$$render=>{get$3(field).type===SettingsFieldType.INPUT?$$render(consequent_4):get$3(field).type===SettingsFieldType.TEXTAREA?$$render(consequent_9,1):get$3(field).type=== -SettingsFieldType.SELECT?$$render(consequent_17,2):get$3(field).type===SettingsFieldType.CHECKBOX&&$$render(consequent_20,3)})}reset(div),append($$anchor2,div)}),append($$anchor,fragment),pop()}delegate(["click"]);var root_1$9=from_html('
    No tools available
    '),root_5$6=from_html(' ',1), -root_9$4=from_html('
    '),root_8$7=from_html('
    Tool Enabled Always allow
    '),root_4$7=from_html(" ",1),root_2$d=from_html('
    ');function SettingsChatToolsTab($$anchor,$$props){push$1($$props,!0);let expandedGroups=new SvelteSet,groups=user_derived(()=>toolsStore.toolGroups);function toggleExpanded(label){expandedGroups.has(label)?expandedGroups.delete(label):expandedGroups.add(label)}var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var div=root_1$9();append($$anchor2, -div)},alternate_1=$$anchor2=>{var div_1=root_2$d();each(div_1,21,()=>get$3(groups),group=>group.label,($$anchor3,group)=>{const isExpanded=user_derived(()=>expandedGroups.has(get$3(group).label));var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Collapsible,($$anchor4,Collapsible_Root)=>{Collapsible_Root($$anchor4,{get open(){return get$3(isExpanded)},onOpenChange:()=>toggleExpanded(get$3(group).label),children:($$anchor5,$$slotProps)=>{var fragment_2=root_4$7(),node_2=first_child( -fragment_2);component(node_2,()=>Collapsible_trigger,($$anchor6,Collapsible_Trigger)=>{Collapsible_Trigger($$anchor6,{class:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-muted/50",children:($$anchor7,$$slotProps2)=>{const faviconUrl=user_derived(()=>get$3(group).serverId?mcpStore.getServerFavicon(get$3(group).serverId):null);var fragment_3=root_5$6(),node_3=first_child(fragment_3);{var consequent_1=$$anchor8=>{Chevron_down($$anchor8,{class:"h-3.5 w-3.5 shrink-0"})},alternate=$$anchor8=>{ -Chevron_right($$anchor8,{class:"h-3.5 w-3.5 shrink-0"})};if_block(node_3,$$render=>{get$3(isExpanded)?$$render(consequent_1):$$render(alternate,-1)})}var span=sibling(node_3,2),node_4=child(span);McpServerIdentity(node_4,{iconClass:"h-4 w-4",iconRounded:"rounded-sm",showVersion:!1,get displayName(){return get$3(group).label},get faviconUrl(){return get$3(faviconUrl)}}),reset(span);var span_1=sibling(span,2),text2=child(span_1);reset(span_1),template_effect(()=>set_text(text2,`${get$3(group).tools. -length??""} tool${get$3(group).tools.length!==1?"s":""}`)),append($$anchor7,fragment_3)},$$slots:{default:!0}})});var node_5=sibling(node_2,2);component(node_5,()=>Collapsible_content,($$anchor6,Collapsible_Content)=>{Collapsible_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{var div_2=root_8$7(),node_6=sibling(child(div_2),2);each(node_6,17,()=>get$3(group).tools,tool=>tool.function.name,($$anchor8,tool)=>{const toolName=user_derived(()=>get$3(tool).function.name),isEnabled=user_derived( -()=>toolsStore.isToolEnabled(get$3(toolName))),permissionKey=user_derived(()=>toolsStore.getPermissionKey(get$3(toolName))),isAlwaysAllowed=user_derived(()=>get$3(permissionKey)?permissionsStore.hasTool(get$3(permissionKey)):!1);var div_3=root_9$4(),node_7=child(div_3);TruncatedText(node_7,{get text(){return get$3(toolName)},class:"flex-1",showTooltip:!0});var div_4=sibling(node_7,2),node_8=child(div_4);Checkbox(node_8,{get checked(){return get$3(isEnabled)},onCheckedChange:()=>toolsStore.toggleTool( -get$3(toolName)),class:"h-4 w-4"}),reset(div_4);var div_5=sibling(div_4,2),node_9=child(div_5);Checkbox(node_9,{get checked(){return get$3(isAlwaysAllowed)},onCheckedChange:()=>{get$3(isAlwaysAllowed)?permissionsStore.revokeTool(get$3(permissionKey)):permissionsStore.allowTool(get$3(permissionKey))},class:"h-4 w-4"}),reset(div_5),reset(div_3),append($$anchor8,div_3)}),reset(div_2),append($$anchor7,div_2)},$$slots:{default:!0}})}),append($$anchor5,fragment_2)},$$slots:{default:!0}})}),append($$anchor3, -fragment_1)}),reset(div_1),append($$anchor2,div_1)};if_block(node2,$$render=>{get$3(groups).length===0?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}function filterModelOptions(options,searchTerm){const term=searchTerm.trim().toLowerCase();return term?options.filter(option2=>option2.model.toLowerCase().includes(term)||option2.name?.toLowerCase().includes(term)||option2.aliases?.some(alias=>alias.toLowerCase().includes(term))||option2.tags?.some(tag=>tag.toLowerCase(). -includes(term))):options}function groupModelOptions(filteredOptions,favoriteIds,isModelLoaded){const loaded=[];for(let i=0;iitem.option.model)),favorites=[];for(let i=0;imodelOptions().filter(option2=>modelsStore.getModelProps(option2.model)?.webui!==!1)),loading=user_derived(modelsLoading),updating=user_derived(modelsUpdating),activeId=user_derived(selectedModelId),isRouter=user_derived(isRouterMode),serverModel=user_derived(singleModelName),currentModel=user_derived(()=>opts.currentModel()),useGlobalSelection=user_derived(()=>opts.useGlobalSelection?.()??!1),onModelChange=user_derived(()=>opts.onModelChange?.()),isHighlightedCurrentModelActive=user_derived( -()=>{if(!get$3(isRouter)||!get$3(currentModel))return!1;const currentOption=get$3(options).find(option2=>option2.model===get$3(currentModel));return currentOption?currentOption.id===get$3(activeId):!1}),isCurrentModelInCache=user_derived(()=>!get$3(isRouter)||!get$3(currentModel)?!0:get$3(options).some(option2=>option2.model===get$3(currentModel)));let isLoadingModel=state$1(!1),searchTerm=state$1(""),showModelDialog=state$1(!1),infoModelId=state$1(null);const filteredOptions=user_derived(()=>filterModelOptions( -get$3(options),get$3(searchTerm))),groupedFilteredOptions=user_derived(()=>groupModelOptions(get$3(filteredOptions),modelsStore.favoriteModelIds,m=>modelsStore.isModelLoaded(m)));function handleInfoClick(modelName){set$1(infoModelId,modelName,!0),set$1(showModelDialog,!0)}onMount$1(()=>{modelsStore.fetch().catch(error2=>{console.error("Unable to load models:",error2)})});function handleOpenChange(open2){get$3(loading)||get$3(updating)||(get$3(isRouter)?(set$1(searchTerm,""),open2&&modelsStore.fetchRouterModels(). -then(()=>{modelsStore.fetchModalitiesForLoadedModels()}),opts.onOpenChange?.(open2)):set$1(showModelDialog,open2,!0))}async function handleSelect(modelId){const option2=get$3(options).find(opt=>opt.id===modelId);if(!option2)return;let shouldCloseMenu=!0;get$3(onModelChange)?await get$3(onModelChange)(option2.id,option2.model)===!1&&(shouldCloseMenu=!1):await modelsStore.selectModelById(option2.id),shouldCloseMenu&&(handleOpenChange(!1),requestAnimationFrame(()=>{document.querySelector('[data-slo\ -t="chat-form"] textarea')?.focus()})),!get$3(onModelChange)&&get$3(isRouter)&&!modelsStore.isModelLoaded(option2.model)&&(set$1(isLoadingModel,!0),modelsStore.loadModel(option2.model).catch(error2=>console.error("Failed to load model:",error2)).finally(()=>set$1(isLoadingModel,!1)))}function getDisplayOption(){if(!get$3(isRouter)){const displayModel=get$3(serverModel)||get$3(currentModel);return displayModel?{id:get$3(serverModel)?"current":"offline-current",model:displayModel,name:displayModel. -split("/").pop()||displayModel,capabilities:[]}:void 0}if(get$3(useGlobalSelection)&&get$3(activeId)){const selected=get$3(options).find(option2=>option2.id===get$3(activeId));if(selected)return selected}if(get$3(currentModel))return get$3(isCurrentModelInCache)?get$3(options).find(option2=>option2.model===get$3(currentModel)):{id:"not-in-cache",model:get$3(currentModel),name:get$3(currentModel).split("/").pop()||get$3(currentModel),capabilities:[]};if(get$3(activeId))return get$3(options).find( -option2=>option2.id===get$3(activeId))}return{get options(){return get$3(options)},get loading(){return get$3(loading)},get updating(){return get$3(updating)},get activeId(){return get$3(activeId)},get isRouter(){return get$3(isRouter)},get serverModel(){return get$3(serverModel)},get isHighlightedCurrentModelActive(){return get$3(isHighlightedCurrentModelActive)},get isCurrentModelInCache(){return get$3(isCurrentModelInCache)},get filteredOptions(){return get$3(filteredOptions)},get groupedFilteredOptions(){ -return get$3(groupedFilteredOptions)},get isLoadingModel(){return get$3(isLoadingModel)},get searchTerm(){return get$3(searchTerm)},get showModelDialog(){return get$3(showModelDialog)},get infoModelId(){return get$3(infoModelId)},setSearchTerm(value){set$1(searchTerm,value,!0)},setShowModelDialog(value){set$1(showModelDialog,value,!0)},handleInfoClick,handleSelect,handleOpenChange,isFavorite(model){return modelsStore.favoriteModelIds.has(model)},getDisplayOption}}var root_1$8=from_html('
    Loading models…
    '),root_3$6=from_html(' '),root_4$6=from_html('

    No models available.

    '),root_12$1=from_html('

    '),root_10$2=from_html(" ",1),root_13$1=from_html('Select model'),root_8$6=from_html(" ",1),root_19$1=from_html(''),root_20=from_html('

    No models found.

    '),root_17$1=from_html('
    '),root_7$6=from_html(" ",1),root_25=from_html('

    '),root_23=from_html(" ",1),root_21=from_html(''),root$k=from_html("
    ",1);function ModelsSelectorDropdown($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),currentModel=prop($$props,"currentModel",3,null),disabled=prop($$props,"disabled",3,!1),forceForegroundText=prop($$props,"forceForegroundText",3,!1),useGlobalSelection=prop($$props,"useGlobalSelection", -3,!1),isOpen=state$1(!1),highlightedIndex=state$1(-1);const ms=useModelsSelector({currentModel:()=>currentModel(),useGlobalSelection:()=>useGlobalSelection(),onModelChange:()=>$$props.onModelChange,onOpenChange:open3=>{set$1(isOpen,open3,!0),set$1(highlightedIndex,-1)}});user_effect(()=>{ms.searchTerm,set$1(highlightedIndex,-1)});function open2(){ms.handleOpenChange(!0)}function handleSearchKeyDown(event2){if(!event2.isComposing){if(event2.key===KeyboardKey.ARROW_DOWN){if(event2.preventDefault(), -ms.filteredOptions.length===0)return;get$3(highlightedIndex)===-1||get$3(highlightedIndex)===ms.filteredOptions.length-1?set$1(highlightedIndex,0):set$1(highlightedIndex,get$3(highlightedIndex)+1)}else if(event2.key===KeyboardKey.ARROW_UP){if(event2.preventDefault(),ms.filteredOptions.length===0)return;get$3(highlightedIndex)===-1||get$3(highlightedIndex)===0?set$1(highlightedIndex,ms.filteredOptions.length-1):set$1(highlightedIndex,get$3(highlightedIndex)-1)}else if(event2.key===KeyboardKey.ENTER) -if(event2.preventDefault(),get$3(highlightedIndex)>=0&&get$3(highlightedIndex)0&&set$1(highlightedIndex,0)}}var $$exports={open:open2},fragment=root$k(),div=first_child(fragment),node2=child(div);{var consequent=$$anchor2=>{var div_1=root_1$8(),node_1=child(div_1);Loader_circle(node_1,{class:"h-3.5 w-3.5 animate-spin"}),next$1(),reset(div_1),append($$anchor2, -div_1)},consequent_2=$$anchor2=>{var fragment_1=comment$2(),node_2=first_child(fragment_1);{var consequent_1=$$anchor3=>{var span=root_3$6(),node_3=child(span);Package(node_3,{class:"h-3.5 w-3.5"});var node_4=sibling(node_3,2);ModelId(node_4,{get modelId(){return currentModel()},class:"min-w-0",hideQuantization:!0}),reset(span),template_effect(()=>set_class(span,1,clsx(["inline-flex items-center gap-1.5 rounded-sm bg-muted-foreground/10 px-1.5 py-1 text-xs text-muted-foreground",className()]))), -append($$anchor3,span)},alternate=$$anchor3=>{var p2=root_4$6();append($$anchor3,p2)};if_block(node_2,$$render=>{currentModel()?$$render(consequent_1):$$render(alternate,-1)})}append($$anchor2,fragment_1)},alternate_4=$$anchor2=>{const selectedOption=user_derived(()=>ms.getDisplayOption());var fragment_2=comment$2(),node_5=first_child(fragment_2);{var consequent_7=$$anchor3=>{var fragment_3=comment$2(),node_6=first_child(fragment_3);component(node_6,()=>Root$4,($$anchor4,DropdownMenu_Root)=>{DropdownMenu_Root( -$$anchor4,{get onOpenChange(){return ms.handleOpenChange},get open(){return get$3(isOpen)},set open($$value){set$1(isOpen,$$value,!0)},children:($$anchor5,$$slotProps)=>{var fragment_4=root_7$6(),node_7=first_child(fragment_4);{let $0=user_derived(()=>["inline-grid cursor-pointer grid-cols-[1fr_auto_1fr] items-center gap-1.5 rounded-sm bg-background px-1.5 py-1 text-xs shadow-sm transition hover:bg-muted-foreground/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:\ -ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-muted-foreground/15 dark:text-secondary-foreground",ms.isCurrentModelInCache?(forceForegroundText()||ms.isHighlightedCurrentModelActive,"text-foreground"):"bg-red-400/10 !text-red-400 hover:bg-red-400/20 hover:text-red-400",get$3(isOpen)&&"text-foreground","max-w-[min(calc(100vw-4rem) md:max-w-[min(calc(100cqw-9rem),25rem)]"]),$1=user_derived(()=>disabled()||ms.updating);component(node_7,()=>Dropdown_menu_trigger,($$anchor6,DropdownMenu_Trigger)=>{ -DropdownMenu_Trigger($$anchor6,{get class(){return get$3($0)},get disabled(){return get$3($1)},children:($$anchor7,$$slotProps2)=>{var fragment_5=root_8$6(),node_8=first_child(fragment_5);Package(node_8,{class:"h-3.5 w-3.5"});var node_9=sibling(node_8,2);{var consequent_3=$$anchor8=>{var fragment_6=comment$2(),node_10=first_child(fragment_6);component(node_10,()=>Root$5,($$anchor9,Tooltip_Root)=>{Tooltip_Root($$anchor9,{children:($$anchor10,$$slotProps3)=>{var fragment_7=root_10$2(),node_11=first_child( -fragment_7);{const child2=($$anchor11,$$arg0)=>{ModelId($$anchor11,spread_props({get modelId(){return get$3(selectedOption).model},class:"min-w-0 overflow-hidden",hideOrgName:!1},()=>$$arg0?.().props))};component(node_11,()=>Tooltip_trigger,($$anchor11,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor11,{child:child2,$$slots:{child:!0}})})}var node_12=sibling(node_11,2);component(node_12,()=>Tooltip_content,($$anchor11,Tooltip_Content)=>{Tooltip_Content($$anchor11,{children:($$anchor12,$$slotProps4)=>{ -var p_1=root_12$1(),text2=child(p_1,!0);reset(p_1),template_effect(()=>set_text(text2,get$3(selectedOption).model)),append($$anchor12,p_1)},$$slots:{default:!0}})}),append($$anchor10,fragment_7)},$$slots:{default:!0}})}),append($$anchor8,fragment_6)},alternate_1=$$anchor8=>{var span_1=root_13$1();append($$anchor8,span_1)};if_block(node_9,$$render=>{get$3(selectedOption)?$$render(consequent_3):$$render(alternate_1,-1)})}var node_13=sibling(node_9,2);{var consequent_4=$$anchor8=>{Loader_circle($$anchor8, -{class:"h-3 w-3.5 animate-spin"})},alternate_2=$$anchor8=>{Chevron_down($$anchor8,{class:"h-3 w-3.5"})};if_block(node_13,$$render=>{ms.updating||ms.isLoadingModel?$$render(consequent_4):$$render(alternate_2,-1)})}append($$anchor7,fragment_5)},$$slots:{default:!0}})})}var node_14=sibling(node_7,2);component(node_14,()=>Dropdown_menu_content,($$anchor6,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor6,{align:"end",class:"w-full max-w-[100vw] pt-0 sm:w-max sm:max-w-[calc(100vw-2rem)]",children:($$anchor7,$$slotProps2)=>{ -{let $0=user_derived(()=>ms.filteredOptions.length===0&&ms.isCurrentModelInCache);DropdownMenuSearchable($$anchor7,{get searchValue(){return ms.searchTerm},onSearchChange:v=>ms.setSearchTerm(v),placeholder:"Search models...",onSearchKeyDown:handleSearchKeyDown,emptyMessage:"No models found.",get isEmpty(){return get$3($0)},children:($$anchor8,$$slotProps3)=>{var div_2=root_17$1();{const modelOption=($$anchor9,item=noop$3,hideOrgName=noop$3)=>{const computed_const=user_derived(()=>{const{option:option2, -flatIndex}=item();return{option:option2,flatIndex}}),isSelected=user_derived(()=>currentModel()===get$3(computed_const).option.model||ms.activeId===get$3(computed_const).option.id),isHighlighted=user_derived(()=>get$3(computed_const).flatIndex===get$3(highlightedIndex)),isFav=user_derived(()=>ms.isFavorite(get$3(computed_const).option.model));ModelsSelectorOption($$anchor9,{get option(){return get$3(computed_const).option},get isSelected(){return get$3(isSelected)},get isHighlighted(){return get$3( -isHighlighted)},get isFav(){return get$3(isFav)},get hideOrgName(){return hideOrgName()},get onSelect(){return ms.handleSelect},get onInfoClick(){return ms.handleInfoClick},onMouseEnter:()=>set$1(highlightedIndex,get$3(computed_const).flatIndex,!0),onKeyDown:event2=>{(event2.key===KeyboardKey.ENTER||event2.key===KeyboardKey.SPACE)&&(event2.preventDefault(),ms.handleSelect(get$3(computed_const).option.id))}})};var node_15=child(div_2);{var consequent_5=$$anchor9=>{var button=root_19$1(),node_16=child( -button);ModelId(node_16,{get modelId(){return currentModel()},class:"flex-1",hideQuantization:!0}),next$1(2),reset(button),append($$anchor9,button)};if_block(node_15,$$render=>{!ms.isCurrentModelInCache&¤tModel()&&$$render(consequent_5)})}var node_17=sibling(node_15,2);{var consequent_6=$$anchor9=>{var p_2=root_20();append($$anchor9,p_2)};if_block(node_17,$$render=>{ms.filteredOptions.length===0&&$$render(consequent_6)})}var node_18=sibling(node_17,2);ModelsSelectorList(node_18,{get groups(){ -return ms.groupedFilteredOptions},get currentModel(){return currentModel()},get activeId(){return ms.activeId},sectionHeaderClass:"my-1.5 px-2 py-2 text-[13px] font-semibold text-muted-foreground/70 select-none",get onSelect(){return ms.handleSelect},get onInfoClick(){return ms.handleInfoClick},get renderOption(){return modelOption}}),reset(div_2)}append($$anchor8,div_2)},$$slots:{default:!0}})}},$$slots:{default:!0}})}),append($$anchor5,fragment_4)},$$slots:{default:!0}})}),append($$anchor3,fragment_3)}, -alternate_3=$$anchor3=>{var button_1=root_21(),node_19=child(button_1);Package(node_19,{class:"h-3.5 w-3.5"});var node_20=sibling(node_19,2);{var consequent_8=$$anchor4=>{var fragment_13=comment$2(),node_21=first_child(fragment_13);component(node_21,()=>Root$5,($$anchor5,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor5,{children:($$anchor6,$$slotProps)=>{var fragment_14=root_23(),node_22=first_child(fragment_14);{const child2=($$anchor7,$$arg0)=>{ModelId($$anchor7,spread_props({get modelId(){return get$3( -selectedOption).model},class:"min-w-0 overflow-hidden",hideOrgName:!1},()=>$$arg0?.().props))};component(node_22,()=>Tooltip_trigger,($$anchor7,Tooltip_Trigger_1)=>{Tooltip_Trigger_1($$anchor7,{child:child2,$$slots:{child:!0}})})}var node_23=sibling(node_22,2);component(node_23,()=>Tooltip_content,($$anchor7,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor7,{children:($$anchor8,$$slotProps2)=>{var p_3=root_25(),text_1=child(p_3,!0);reset(p_3),template_effect(()=>set_text(text_1,get$3(selectedOption). -model)),append($$anchor8,p_3)},$$slots:{default:!0}})}),append($$anchor6,fragment_14)},$$slots:{default:!0}})}),append($$anchor4,fragment_13)};if_block(node_20,$$render=>{get$3(selectedOption)&&$$render(consequent_8)})}var node_24=sibling(node_20,2);{var consequent_9=$$anchor4=>{Loader_circle($$anchor4,{class:"h-3 w-3.5 animate-spin"})};if_block(node_24,$$render=>{ms.updating&&$$render(consequent_9)})}reset(button_1),template_effect(()=>{set_class(button_1,1,clsx(["inline-flex cursor-pointer ite\ -ms-center gap-1.5 rounded-sm bg-background px-1.5 py-1 text-xs shadow-sm transition hover:bg-muted-foreground/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-muted-foreground/15 dark:text-secondary-foreground",ms.isCurrentModelInCache?(forceForegroundText()||ms.isHighlightedCurrentModelActive,"text-foreground"):"bg-red-400/10 !text-red-400 hover:bg-red-400/20 hover:text-red-400",get$3(isOpen)&& -"text-foreground"])),button_1.disabled=disabled()||ms.updating}),delegated("click",button_1,()=>ms.handleOpenChange(!0)),append($$anchor3,button_1)};if_block(node_5,$$render=>{ms.isRouter?$$render(consequent_7):$$render(alternate_3,-1)})}append($$anchor2,fragment_2)};if_block(node2,$$render=>{ms.loading&&ms.options.length===0&&ms.isRouter?$$render(consequent):ms.options.length===0&&ms.isRouter?$$render(consequent_2,1):$$render(alternate_4,-1)})}reset(div);var node_25=sibling(div,2);{var consequent_10=$$anchor2=>{ -DialogModelInformation($$anchor2,{get open(){return ms.showModelDialog},onOpenChange:v=>ms.setShowModelDialog(v),get modelId(){return ms.infoModelId}})};if_block(node_25,$$render=>{ms.showModelDialog&&$$render(consequent_10)})}return template_effect(()=>set_class(div,1,clsx(["relative inline-flex flex-col items-end gap-1",className()]))),append($$anchor,fragment),pop($$exports)}delegate(["click"]);var root_2$c=from_html("

    Loaded models

    ",1),root_4$5=from_html("

    Favorite models

    ", -1),root_8$5=from_html("

    "),root_7$5=from_html(" ",1),root_6$6=from_html("

    Available models

    ",1),root$j=from_html(" ",1);function ModelsSelectorList($$anchor,$$props){push$1($$props,!0);const defaultOption=($$anchor2,item=noop$3,hideOrgName=noop$3)=>{const computed_const=user_derived(()=>{const{option:option2}=item();return{option:option2}}),isSelected=user_derived(()=>$$props.currentModel===get$3(computed_const).option.model||$$props.activeId===get$3(computed_const). -option.id),isFav=user_derived(()=>modelsStore.favoriteModelIds.has(get$3(computed_const).option.model));ModelsSelectorOption($$anchor2,{get option(){return get$3(computed_const).option},get isSelected(){return get$3(isSelected)},isHighlighted:!1,get isFav(){return get$3(isFav)},get hideOrgName(){return hideOrgName()},get onSelect(){return $$props.onSelect},get onInfoClick(){return $$props.onInfoClick},onMouseEnter:()=>{},onKeyDown:()=>{}})};let sectionHeaderClass=prop($$props,"sectionHeaderClass", -3,"my-1 px-2 py-2 text-[13px] font-semibold text-muted-foreground/70 select-none"),orgHeaderClass=prop($$props,"orgHeaderClass",3,"px-2 py-2 text-[11px] font-semibold text-muted-foreground/50 select-none [&:not(:first-child)]:mt-1"),render3=user_derived(()=>$$props.renderOption??defaultOption);var fragment_1=root$j(),node2=first_child(fragment_1);{var consequent=$$anchor2=>{var fragment_2=root_2$c(),p2=first_child(fragment_2),node_1=sibling(p2,2);each(node_1,17,()=>$$props.groups.loaded,item=>`l\ -oaded-${item.option.id}`,($$anchor3,item)=>{var fragment_3=comment$2(),node_2=first_child(fragment_3);snippet(node_2,()=>get$3(render3),()=>get$3(item),()=>!1),append($$anchor3,fragment_3)}),template_effect(()=>set_class(p2,1,clsx(sectionHeaderClass()))),append($$anchor2,fragment_2)};if_block(node2,$$render=>{$$props.groups.loaded.length>0&&$$render(consequent)})}var node_3=sibling(node2,2);{var consequent_1=$$anchor2=>{var fragment_4=root_4$5(),p_1=first_child(fragment_4),node_4=sibling(p_1,2); -each(node_4,17,()=>$$props.groups.favorites,item=>`fav-${item.option.id}`,($$anchor3,item)=>{var fragment_5=comment$2(),node_5=first_child(fragment_5);snippet(node_5,()=>get$3(render3),()=>get$3(item),()=>!0),append($$anchor3,fragment_5)}),template_effect(()=>set_class(p_1,1,clsx(sectionHeaderClass()))),append($$anchor2,fragment_4)};if_block(node_3,$$render=>{$$props.groups.favorites.length>0&&$$render(consequent_1)})}var node_6=sibling(node_3,2);{var consequent_3=$$anchor2=>{var fragment_6=root_6$6(), -p_2=first_child(fragment_6),node_7=sibling(p_2,2);each(node_7,17,()=>$$props.groups.available,group=>group.orgName,($$anchor3,group)=>{var fragment_7=root_7$5(),node_8=first_child(fragment_7);{var consequent_2=$$anchor4=>{var p_3=root_8$5(),text2=child(p_3,!0);reset(p_3),template_effect(()=>{set_class(p_3,1,clsx(orgHeaderClass())),set_text(text2,get$3(group).orgName)}),append($$anchor4,p_3)};if_block(node_8,$$render=>{get$3(group).orgName&&$$render(consequent_2)})}var node_9=sibling(node_8,2);each( -node_9,17,()=>get$3(group).items,item=>item.option.id,($$anchor4,item)=>{var fragment_8=comment$2(),node_10=first_child(fragment_8);snippet(node_10,()=>get$3(render3),()=>get$3(item),()=>!0),append($$anchor4,fragment_8)}),append($$anchor3,fragment_7)}),template_effect(()=>set_class(p_2,1,clsx(sectionHeaderClass()))),append($$anchor2,fragment_6)};if_block(node_6,$$render=>{$$props.groups.available.length>0&&$$render(consequent_3)})}append($$anchor,fragment_1),pop()}var root_5$5=from_html('
    '),root_6$5=from_html('
    '),root_7$4=from_html('
    '), -root_8$4=from_html('
    '),root$i=from_html('
    ');function ModelsSelectorOption($$anchor,$$props){ -push$1($$props,!0);let hideOrgName=prop($$props,"hideOrgName",3,!1),currentRouterModels=user_derived(routerModels),serverStatus=user_derived(()=>get$3(currentRouterModels).find(m=>m.id===$$props.option.model)?.status?.value??null),isOperationInProgress=user_derived(()=>modelsStore.isModelOperationInProgress($$props.option.model)),isFailed=user_derived(()=>get$3(serverStatus)===ServerModelStatus.FAILED),isSleeping=user_derived(()=>get$3(serverStatus)===ServerModelStatus.SLEEPING),isLoaded=user_derived( -()=>(get$3(serverStatus)===ServerModelStatus.LOADED||get$3(isSleeping))&&!get$3(isOperationInProgress)),isLoading2=user_derived(()=>get$3(serverStatus)===ServerModelStatus.LOADING||get$3(isOperationInProgress));var div=root$i(),node2=child(div);ModelId(node2,{get modelId(){return $$props.option.model},get hideOrgName(){return hideOrgName()},get aliases(){return $$props.option.aliases},get tags(){return $$props.option.tags},class:"flex-1"});var div_1=sibling(node2,2),div_2=child(div_1),node_1=child( -div_2);{var consequent=$$anchor2=>{ActionIcon($$anchor2,{iconSize:"h-2.5 w-2.5",get icon(){return Heart_off},tooltip:"Remove from favorites",class:"h-3 w-3 hover:text-foreground",onclick:()=>modelsStore.toggleFavorite($$props.option.model)})},alternate=$$anchor2=>{ActionIcon($$anchor2,{iconSize:"h-2.5 w-2.5",get icon(){return Heart},tooltip:"Add to favorites",class:"h-3 w-3 hover:text-foreground",onclick:()=>modelsStore.toggleFavorite($$props.option.model)})};if_block(node_1,$$render=>{$$props.isFav? -$$render(consequent):$$render(alternate,-1)})}var node_2=sibling(node_1,2);{var consequent_1=$$anchor2=>{ActionIcon($$anchor2,{iconSize:"h-2.5 w-2.5",get icon(){return Info$1},tooltip:"Model information",class:"h-3 w-3 hover:text-foreground",onclick:()=>$$props.onInfoClick($$props.option.model)})};if_block(node_2,$$render=>{get$3(isLoaded)&&$$props.onInfoClick&&$$render(consequent_1)})}reset(div_2);var node_3=sibling(div_2,2);{var consequent_2=$$anchor2=>{Loader_circle($$anchor2,{class:"h-4 w-4 \ -animate-spin text-muted-foreground"})},consequent_3=$$anchor2=>{var div_3=root_5$5(),node_4=child(div_3);Circle_alert(node_4,{class:"h-3.5 w-3.5 text-red-500 group-hover:hidden"});var div_4=sibling(node_4,2),node_5=child(div_4);ActionIcon(node_5,{iconSize:"h-2.5 w-2.5",get icon(){return Rotate_cw},tooltip:"Retry loading model",class:"h-3 w-3 text-red-500 hover:text-foreground",onclick:()=>modelsStore.loadModel($$props.option.model),stopPropagationOnClick:!0}),reset(div_4),reset(div_3),append($$anchor2, -div_3)},consequent_4=$$anchor2=>{var div_5=root_6$5(),div_6=sibling(child(div_5),2),node_6=child(div_6);ActionIcon(node_6,{iconSize:"h-2.5 w-2.5",get icon(){return Power_off},tooltip:"Unload model",class:"h-3 w-3 text-red-500 hover:text-red-600",onclick:e=>{e?.stopPropagation(),modelsStore.unloadModel($$props.option.model)}}),reset(div_6),reset(div_5),append($$anchor2,div_5)},consequent_5=$$anchor2=>{var div_7=root_7$4(),div_8=sibling(child(div_7),2),node_7=child(div_8);ActionIcon(node_7,{iconSize:"\ -h-2.5 w-2.5",get icon(){return Power_off},tooltip:"Unload model",class:"h-3 w-3 text-red-500 hover:text-red-600",onclick:()=>modelsStore.unloadModel($$props.option.model),stopPropagationOnClick:!0}),reset(div_8),reset(div_7),append($$anchor2,div_7)},alternate_1=$$anchor2=>{var div_9=root_8$4(),div_10=sibling(child(div_9),2),node_8=child(div_10);ActionIcon(node_8,{iconSize:"h-2.5 w-2.5",get icon(){return Power},tooltip:"Load model",class:"h-3 w-3",onclick:()=>modelsStore.loadModel($$props.option. -model),stopPropagationOnClick:!0}),reset(div_10),reset(div_9),append($$anchor2,div_9)};if_block(node_3,$$render=>{get$3(isLoading2)?$$render(consequent_2):get$3(isFailed)?$$render(consequent_3,1):get$3(isSleeping)?$$render(consequent_4,2):get$3(isLoaded)?$$render(consequent_5,3):$$render(alternate_1,-1)})}reset(div_1),reset(div),template_effect(()=>{set_class(div,1,clsx(["group flex w-full items-center gap-2 rounded-sm p-2 text-left text-sm transition focus:outline-none","cursor-pointer hover:bg\ --muted focus:bg-muted",($$props.isSelected||$$props.isHighlighted)&&"bg-accent text-accent-foreground",!($$props.isSelected||$$props.isHighlighted)&&"hover:bg-accent hover:text-accent-foreground",get$3(isLoaded)?"text-popover-foreground":"text-muted-foreground"])),set_attribute(div,"aria-selected",$$props.isSelected||$$props.isHighlighted)}),delegated("click",div,()=>$$props.onSelect($$props.option.id)),event("mouseenter",div,function(...$$args){$$props.onMouseEnter?.apply(this,$$args)}),delegated( -"keydown",div,function(...$$args){$$props.onKeyDown?.apply(this,$$args)}),delegated("click",div_2,e=>e.stopPropagation()),append($$anchor,div),pop()}delegate(["click","keydown"]);var root_1$7=from_html('
    Loading models…
    '),root_2$b=from_html('

    No models available.

    '),root_5$4=from_html('Select model'),root_11$3=from_html(" ",1),root_14=from_html( -'
    ',1),root_15$1=from_html('

    No models found.

    '),root_10$1=from_html('
    ',1),root_4$4=from_html(' ',1),root_16=from_html(''),root$h=from_html("
    ",1);function ModelsSelectorSheet($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),currentModel=prop( -$$props,"currentModel",3,null),disabled=prop($$props,"disabled",3,!1),forceForegroundText=prop($$props,"forceForegroundText",3,!1),useGlobalSelection=prop($$props,"useGlobalSelection",3,!1),sheetOpen=state$1(!1);const ms=useModelsSelector({currentModel:()=>currentModel(),useGlobalSelection:()=>useGlobalSelection(),onModelChange:()=>$$props.onModelChange,onOpenChange:open3=>{set$1(sheetOpen,open3,!0)}});function open2(){ms.handleOpenChange(!0)}function handleSheetOpenChange(open3){open3||ms.handleOpenChange( -!1)}var $$exports={open:open2},fragment=root$h(),div=first_child(fragment),node2=child(div);{var consequent=$$anchor2=>{var div_1=root_1$7(),node_1=child(div_1);Loader_circle(node_1,{class:"h-3.5 w-3.5 animate-spin"}),next$1(),reset(div_1),append($$anchor2,div_1)},consequent_1=$$anchor2=>{var p2=root_2$b();append($$anchor2,p2)},alternate_3=$$anchor2=>{const selectedOption=user_derived(()=>ms.getDisplayOption());var fragment_1=comment$2(),node_2=first_child(fragment_1);{var consequent_6=$$anchor3=>{ -var fragment_2=root_4$4(),button=first_child(fragment_2),node_3=child(button);Package(node_3,{class:"h-3.5 w-3.5"});var node_4=sibling(node_3,2);{var consequent_2=$$anchor4=>{var span=root_5$4();append($$anchor4,span)},alternate=$$anchor4=>{{let $0=user_derived(()=>get$3(selectedOption)?.model||"");ModelId($$anchor4,{class:"text-xs",get modelId(){return get$3($0)},hideQuantization:!0,hideOrgName:!0})}};if_block(node_4,$$render=>{get$3(selectedOption)?$$render(alternate,-1):$$render(consequent_2)})} -var node_5=sibling(node_4,2);{var consequent_3=$$anchor4=>{Loader_circle($$anchor4,{class:"h-3 w-3.5 animate-spin"})},alternate_1=$$anchor4=>{Chevron_down($$anchor4,{class:"h-3 w-3.5"})};if_block(node_5,$$render=>{ms.updating||ms.isLoadingModel?$$render(consequent_3):$$render(alternate_1,-1)})}reset(button);var node_6=sibling(button,2);component(node_6,()=>Root$3,($$anchor4,Sheet_Root)=>{Sheet_Root($$anchor4,{onOpenChange:handleSheetOpenChange,get open(){return get$3(sheetOpen)},set open($$value){ -set$1(sheetOpen,$$value,!0)},children:($$anchor5,$$slotProps)=>{var fragment_6=comment$2(),node_7=first_child(fragment_6);component(node_7,()=>Sheet_content,($$anchor6,Sheet_Content)=>{Sheet_Content($$anchor6,{side:"bottom",class:"max-h-[85vh] gap-1",children:($$anchor7,$$slotProps2)=>{var fragment_7=root_10$1(),node_8=first_child(fragment_7);component(node_8,()=>Sheet_header,($$anchor8,Sheet_Header)=>{Sheet_Header($$anchor8,{children:($$anchor9,$$slotProps3)=>{var fragment_8=root_11$3(),node_9=first_child( -fragment_8);component(node_9,()=>Sheet_title,($$anchor10,Sheet_Title)=>{Sheet_Title($$anchor10,{children:($$anchor11,$$slotProps4)=>{next$1();var text2=text$8("Select Model");append($$anchor11,text2)},$$slots:{default:!0}})});var node_10=sibling(node_9,2);component(node_10,()=>Sheet_description,($$anchor10,Sheet_Description)=>{Sheet_Description($$anchor10,{class:"sr-only",children:($$anchor11,$$slotProps4)=>{next$1();var text_1=text$8("Choose a model to use for the conversation");append($$anchor11, -text_1)},$$slots:{default:!0}})}),append($$anchor9,fragment_8)},$$slots:{default:!0}})});var div_2=sibling(node_8,2),div_3=child(div_2),node_11=child(div_3);SearchInput(node_11,{placeholder:"Search models...",get value(){return ms.searchTerm},onInput:v=>ms.setSearchTerm(v)}),reset(div_3);var div_4=sibling(div_3,2),node_12=child(div_4);{var consequent_4=$$anchor8=>{var fragment_9=root_14(),button_1=first_child(fragment_9),span_1=child(button_1),text_2=child(span_1,!0);reset(span_1),next$1(2),reset( -button_1),next$1(2),template_effect(()=>set_text(text_2,get$3(selectedOption)?.name||currentModel())),append($$anchor8,fragment_9)};if_block(node_12,$$render=>{!ms.isCurrentModelInCache&¤tModel()&&$$render(consequent_4)})}var node_13=sibling(node_12,2);{var consequent_5=$$anchor8=>{var p_1=root_15$1();append($$anchor8,p_1)};if_block(node_13,$$render=>{ms.filteredOptions.length===0&&$$render(consequent_5)})}var node_14=sibling(node_13,2);ModelsSelectorList(node_14,{get groups(){return ms.groupedFilteredOptions}, -get currentModel(){return currentModel()},get activeId(){return ms.activeId},sectionHeaderClass:"px-2 py-2 text-xs font-semibold text-muted-foreground/60 select-none",orgHeaderClass:"px-2 py-2 text-xs font-semibold text-muted-foreground/60 select-none [&:not(:first-child)]:mt-2",get onSelect(){return ms.handleSelect},get onInfoClick(){return ms.handleInfoClick}}),reset(div_4),reset(div_2),append($$anchor7,fragment_7)},$$slots:{default:!0}})}),append($$anchor5,fragment_6)},$$slots:{default:!0}})}), -template_effect(()=>{set_class(button,1,clsx(["inline-grid cursor-pointer grid-cols-[1fr_auto_1fr] items-center gap-1.5 rounded-sm bg-background px-1.5 py-1 text-xs shadow-sm transition hover:bg-muted-foreground/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-muted-foreground/15 dark:text-secondary-foreground",ms.isCurrentModelInCache?(forceForegroundText()||ms.isHighlightedCurrentModelActive,"\ -text-foreground"):"bg-red-400/10 !text-red-400 hover:bg-red-400/20 hover:text-red-400",get$3(sheetOpen)&&"text-foreground"])),button.disabled=disabled()||ms.updating}),delegated("click",button,()=>ms.handleOpenChange(!0)),append($$anchor3,fragment_2)},alternate_2=$$anchor3=>{var button_2=root_16(),node_15=child(button_2);Package(node_15,{class:"h-3.5 w-3.5"});var node_16=sibling(node_15,2);{let $0=user_derived(()=>get$3(selectedOption)?.model||"");TruncatedText(node_16,{get text(){return get$3($0)}, -class:"font-medium"})}var node_17=sibling(node_16,2);{var consequent_7=$$anchor4=>{Loader_circle($$anchor4,{class:"h-3 w-3.5 animate-spin"})};if_block(node_17,$$render=>{ms.updating&&$$render(consequent_7)})}reset(button_2),template_effect(()=>{set_class(button_2,1,clsx(["inline-flex cursor-pointer items-center gap-1.5 rounded-sm bg-background px-1.5 py-1 text-xs shadow-sm transition hover:bg-muted-foreground/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-o\ -ffset-2 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-muted-foreground/15 dark:text-secondary-foreground",ms.isCurrentModelInCache?(forceForegroundText()||ms.isHighlightedCurrentModelActive,"text-foreground"):"bg-red-400/10 !text-red-400 hover:bg-red-400/20 hover:text-red-400"])),button_2.disabled=disabled()||ms.updating}),delegated("click",button_2,()=>ms.handleOpenChange(!0)),append($$anchor3,button_2)};if_block(node_2,$$render=>{ms.isRouter?$$render(consequent_6):$$render(alternate_2, --1)})}append($$anchor2,fragment_1)};if_block(node2,$$render=>{ms.loading&&ms.options.length===0&&ms.isRouter?$$render(consequent):ms.options.length===0&&ms.isRouter?$$render(consequent_1,1):$$render(alternate_3,-1)})}reset(div);var node_18=sibling(div,2);{var consequent_8=$$anchor2=>{DialogModelInformation($$anchor2,{get open(){return ms.showModelDialog},onOpenChange:v=>ms.setShowModelDialog(v),get modelId(){return ms.infoModelId}})};if_block(node_18,$$render=>{ms.showModelDialog&&$$render(consequent_8)})} -return template_effect(()=>set_class(div,1,clsx(["relative inline-flex flex-col items-end gap-1",className()]))),append($$anchor,fragment),pop($$exports)}delegate(["click"]);var root_4$3=from_html(" "),root_5$3=from_html(" "),root_7$3=from_html(" "),root_9$3=from_html(" "),root_11$2=from_html(" "),root_2$a=from_html(' ');function ModelId($$anchor,$$props){ -push$1($$props,!0);let hideOrgName=prop($$props,"hideOrgName",3,!1),showRaw=prop($$props,"showRaw",3,void 0),hideQuantization=prop($$props,"hideQuantization",3,!1),className=prop($$props,"class",3,""),rest=rest_props($$props,["$$slots","$$events","$$legacy","modelId","hideOrgName","showRaw","hideQuantization","aliases","tags","class"]);const badgeClass="inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap rounded-md border border-border/50 px-1 py-0 text-[10px] font-mono bg-f\ -oreground/15 dark:bg-foreground/10 text-foreground [a&]:hover:bg-foreground/25",tagBadgeClass="inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap rounded-md border border-border/50 px-1 py-0 text-[10px] font-mono text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";let parsed=user_derived(()=>ModelsService.parseModelId($$props.modelId)),resolvedShowRaw=user_derived(()=>showRaw()??config$1().showRawModelNames??!1),uniqueAliases=user_derived(()=>[...new Set( -$$props.aliases??[])]),uniqueTags=user_derived(()=>[...new Set([...get$3(parsed).tags??[],...$$props.tags??[]])]),primaryAlias=user_derived(()=>get$3(uniqueAliases).length===1?get$3(uniqueAliases)[0]:null),displayName=user_derived(()=>get$3(primaryAlias)??get$3(parsed).modelName??$$props.modelId);var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{TruncatedText($$anchor2,spread_props({get class(){return`font-medium ${className()??""}`},showTooltip:!1,get text(){return $$props. -modelId}},()=>rest))},alternate=$$anchor2=>{var span=root_2$a();attribute_effect(span,()=>({class:`flex min-w-0 flex-wrap items-center gap-1 ${className()??""}`,...rest}));var span_1=child(span),node_1=child(span_1);{var consequent_1=$$anchor3=>{var text2=text$8();template_effect(()=>set_text(text2,`${get$3(parsed).orgName??""}/`)),append($$anchor3,text2)};if_block(node_1,$$render=>{!hideOrgName()&&get$3(parsed).orgName&&$$render(consequent_1)})}var text_1=sibling(node_1,1,!0);reset(span_1);var node_2=sibling( -span_1,2);{var consequent_2=$$anchor3=>{var span_2=root_4$3();set_class(span_2,1,clsx(badgeClass));var text_2=child(span_2);reset(span_2),template_effect(()=>set_text(text_2,`${get$3(parsed).params??""}${get$3(parsed).activatedParams?`-${get$3(parsed).activatedParams}`:""}`)),append($$anchor3,span_2)};if_block(node_2,$$render=>{get$3(parsed).params&&$$render(consequent_2)})}var node_3=sibling(node_2,2);{var consequent_3=$$anchor3=>{var span_3=root_5$3();set_class(span_3,1,clsx(badgeClass));var text_3=child( -span_3,!0);reset(span_3),template_effect(()=>set_text(text_3,get$3(parsed).quantization)),append($$anchor3,span_3)};if_block(node_3,$$render=>{get$3(parsed).quantization&&!hideQuantization()&&$$render(consequent_3)})}var node_4=sibling(node_3,2);{var consequent_5=$$anchor3=>{var fragment_3=comment$2(),node_5=first_child(fragment_3);{var consequent_4=$$anchor4=>{var span_4=root_7$3();set_class(span_4,1,clsx(badgeClass));var text_4=child(span_4,!0);reset(span_4),template_effect(()=>set_text(text_4, -get$3(parsed).modelName??$$props.modelId)),append($$anchor4,span_4)};if_block(node_5,$$render=>{get$3(primaryAlias)!==get$3(parsed).modelName&&$$render(consequent_4)})}append($$anchor3,fragment_3)},consequent_6=$$anchor3=>{var fragment_4=comment$2(),node_6=first_child(fragment_4);each(node_6,16,()=>get$3(uniqueAliases),alias=>alias,($$anchor4,alias)=>{var span_5=root_9$3();set_class(span_5,1,clsx(badgeClass));var text_5=child(span_5,!0);reset(span_5),template_effect(()=>set_text(text_5,alias)),append( -$$anchor4,span_5)}),append($$anchor3,fragment_4)};if_block(node_4,$$render=>{get$3(primaryAlias)?$$render(consequent_5):get$3(uniqueAliases).length>1&&$$render(consequent_6,1)})}var node_7=sibling(node_4,2);{var consequent_7=$$anchor3=>{var fragment_5=comment$2(),node_8=first_child(fragment_5);each(node_8,16,()=>get$3(uniqueTags),tag=>tag,($$anchor4,tag)=>{var span_6=root_11$2();set_class(span_6,1,clsx(tagBadgeClass));var text_6=child(span_6,!0);reset(span_6),template_effect(()=>set_text(text_6, -tag)),append($$anchor4,span_6)}),append($$anchor3,fragment_5)};if_block(node_7,$$render=>{get$3(uniqueTags).length>0&&$$render(consequent_7)})}reset(span),template_effect(()=>set_text(text_1,get$3(displayName))),append($$anchor2,span)};if_block(node2,$$render=>{get$3(resolvedShowRaw)?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root_3$5=from_html(" ",1),root_8$3=from_html(" ",1);function ModelBadge($$anchor,$$props){push$1($$props,!0);const badgeContent=$$anchor2=>{ -BadgeInfo($$anchor2,{get class(){return className()},get onclick(){return $$props.onclick},icon:$$anchor3=>{Package($$anchor3,{class:"h-3 w-3"})},children:($$anchor3,$$slotProps)=>{var fragment_2=root_3$5(),node2=first_child(fragment_2);{var consequent=$$anchor4=>{ModelId($$anchor4,{get modelId(){return get$3(model)}})};if_block(node2,$$render=>{get$3(model)&&$$render(consequent)})}var node_1=sibling(node2,2);{var consequent_1=$$anchor4=>{{let $0=user_derived(()=>get$3(model)||"");ActionIconCopyToClipboard( -$$anchor4,{get text(){return get$3($0)},ariaLabel:"Copy model name"})}};if_block(node_1,$$render=>{showCopyIcon()&&$$render(consequent_1)})}append($$anchor3,fragment_2)},$$slots:{icon:!0,default:!0}})};let className=prop($$props,"class",3,""),showCopyIcon=prop($$props,"showCopyIcon",3,!1),showTooltip=prop($$props,"showTooltip",3,!1),model=user_derived(()=>$$props.model||modelsStore.singleModelName),isModelMode=user_derived(()=>serverStore.isModelMode),shouldShow=user_derived(()=>get$3(model)&&($$props. -model!==void 0||get$3(isModelMode)));var fragment_5=comment$2(),node_2=first_child(fragment_5);{var consequent_3=$$anchor2=>{var fragment_6=comment$2(),node_3=first_child(fragment_6);{var consequent_2=$$anchor3=>{var fragment_7=comment$2(),node_4=first_child(fragment_7);component(node_4,()=>Root$5,($$anchor4,Tooltip_Root)=>{Tooltip_Root($$anchor4,{children:($$anchor5,$$slotProps)=>{var fragment_8=root_8$3(),node_5=first_child(fragment_8);component(node_5,()=>Tooltip_trigger,($$anchor6,Tooltip_Trigger)=>{ -Tooltip_Trigger($$anchor6,{children:($$anchor7,$$slotProps2)=>{badgeContent($$anchor7)},$$slots:{default:!0}})});var node_6=sibling(node_5,2);component(node_6,()=>Tooltip_content,($$anchor6,Tooltip_Content)=>{Tooltip_Content($$anchor6,{children:($$anchor7,$$slotProps2)=>{next$1();var text2=text$8();template_effect(()=>set_text(text2,$$props.onclick?"Click for model details":get$3(model))),append($$anchor7,text2)},$$slots:{default:!0}})}),append($$anchor5,fragment_8)},$$slots:{default:!0}})}),append( -$$anchor3,fragment_7)},alternate=$$anchor3=>{badgeContent($$anchor3)};if_block(node_3,$$render=>{showTooltip()?$$render(consequent_2):$$render(alternate,-1)})}append($$anchor2,fragment_6)};if_block(node_2,$$render=>{get$3(shouldShow)&&$$render(consequent_3)})}append($$anchor,fragment_5),pop()}var root_1$6=from_html('
    '),root_2$9=from_html(" ",1),root$g=from_html('
    <\ -!>
    ',1);function DropdownMenuSearchable($$anchor,$$props){push$1($$props,!0);let placeholder=prop($$props,"placeholder",3,"Search..."),searchValue=prop($$props,"searchValue",15,""),emptyMessage=prop($$props,"emptyMessage",3,"No items found"),isEmpty=prop($$props,"isEmpty",3,!1);var fragment=root$g(),div=first_child(fragment),node2=child(div);SearchInput(node2,{get placeholder(){return placeholder()},get onInput(){return $$props.onSearchChange}, -get onKeyDown(){return $$props.onSearchKeyDown},get value(){return searchValue()},set value($$value){searchValue($$value)}}),reset(div);var div_1=sibling(div,2),node_1=child(div_1);snippet(node_1,()=>$$props.children);var node_2=sibling(node_1,2);{var consequent=$$anchor2=>{var div_2=root_1$6(),text2=child(div_2,!0);reset(div_2),template_effect(()=>set_text(text2,emptyMessage())),append($$anchor2,div_2)};if_block(node_2,$$render=>{isEmpty()&&$$render(consequent)})}reset(div_1);var node_3=sibling( -div_1,2);{var consequent_1=$$anchor2=>{var fragment_1=root_2$9(),node_4=first_child(fragment_1);component(node_4,()=>Dropdown_menu_separator,($$anchor3,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor3,{})});var node_5=sibling(node_4,2);snippet(node_5,()=>$$props.footer),append($$anchor2,fragment_1)};if_block(node_3,$$render=>{$$props.footer&&$$render(consequent_1)})}append($$anchor,fragment),pop()}const iconComponent=($$anchor,IconComponent=noop$3,className=noop$3)=>{var fragment=comment$2(), -node2=first_child(fragment);component(node2,IconComponent,($$anchor2,IconComponent_1)=>{IconComponent_1($$anchor2,{get class(){return className()}})}),append($$anchor,fragment)};var root_6$4=from_html(' ',1),root_7$2=from_html("

    "),root_5$2=from_html(" ",1),root_12=from_html('
    ',1),root_10=from_html(" ",1),root_2$8=from_html(" ",1);function DropdownMenuActions($$anchor,$$props){push$1($$props, -!0);let triggerClass=prop($$props,"triggerClass",3,""),align=prop($$props,"align",3,"end"),open2=prop($$props,"open",15,!1);var fragment_1=comment$2(),node_1=first_child(fragment_1);component(node_1,()=>Root$4,($$anchor2,DropdownMenu_Root)=>{DropdownMenu_Root($$anchor2,{get open(){return open2()},set open($$value){open2($$value)},children:($$anchor3,$$slotProps)=>{var fragment_2=root_2$8(),node_2=first_child(fragment_2);component(node_2,()=>Dropdown_menu_trigger,($$anchor4,DropdownMenu_Trigger)=>{ -DropdownMenu_Trigger($$anchor4,{get class(){return`flex h-6 w-6 cursor-pointer items-center justify-center rounded-md p-0 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground ${triggerClass()??""}`},onclick:e=>e.stopPropagation(),children:($$anchor5,$$slotProps2)=>{var fragment_3=comment$2(), -node_3=first_child(fragment_3);{var consequent=$$anchor6=>{var fragment_4=comment$2(),node_4=first_child(fragment_4);component(node_4,()=>Root$5,($$anchor7,Tooltip_Root)=>{Tooltip_Root($$anchor7,{children:($$anchor8,$$slotProps3)=>{var fragment_5=root_5$2(),node_5=first_child(fragment_5);component(node_5,()=>Tooltip_trigger,($$anchor9,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor9,{children:($$anchor10,$$slotProps4)=>{var fragment_6=root_6$4(),node_6=first_child(fragment_6);iconComponent(node_6,()=>$$props. -triggerIcon,()=>"h-3 w-3");var span=sibling(node_6,2),text2=child(span,!0);reset(span),template_effect(()=>set_text(text2,$$props.triggerTooltip)),append($$anchor10,fragment_6)},$$slots:{default:!0}})});var node_7=sibling(node_5,2);component(node_7,()=>Tooltip_content,($$anchor9,Tooltip_Content)=>{Tooltip_Content($$anchor9,{children:($$anchor10,$$slotProps4)=>{var p2=root_7$2(),text_1=child(p2,!0);reset(p2),template_effect(()=>set_text(text_1,$$props.triggerTooltip)),append($$anchor10,p2)},$$slots:{ -default:!0}})}),append($$anchor8,fragment_5)},$$slots:{default:!0}})}),append($$anchor6,fragment_4)},alternate=$$anchor6=>{iconComponent($$anchor6,()=>$$props.triggerIcon,()=>"h-3 w-3")};if_block(node_3,$$render=>{$$props.triggerTooltip?$$render(consequent):$$render(alternate,-1)})}append($$anchor5,fragment_3)},$$slots:{default:!0}})});var node_8=sibling(node_2,2);component(node_8,()=>Dropdown_menu_content,($$anchor4,DropdownMenu_Content)=>{DropdownMenu_Content($$anchor4,{get align(){return align()}, -class:"z-[999999] w-48",children:($$anchor5,$$slotProps2)=>{var fragment_8=comment$2(),node_9=first_child(fragment_8);each(node_9,19,()=>$$props.actions,action2=>action2.label,($$anchor6,action2,index2)=>{var fragment_9=root_10(),node_10=first_child(fragment_9);{var consequent_1=$$anchor7=>{var fragment_10=comment$2(),node_11=first_child(fragment_10);component(node_11,()=>Dropdown_menu_separator,($$anchor8,DropdownMenu_Separator)=>{DropdownMenu_Separator($$anchor8,{})}),append($$anchor7,fragment_10)}; -if_block(node_10,$$render=>{get$3(action2).separator&&get$3(index2)>0&&$$render(consequent_1)})}var node_12=sibling(node_10,2);component(node_12,()=>Dropdown_menu_item,($$anchor7,DropdownMenu_Item)=>{DropdownMenu_Item($$anchor7,{get onclick(){return get$3(action2).onclick},get variant(){return get$3(action2).variant},get disabled(){return get$3(action2).disabled},class:"flex items-center justify-between hover:[&>kbd]:opacity-100",children:($$anchor8,$$slotProps3)=>{var fragment_11=root_12(),div=first_child( -fragment_11),node_13=child(div);iconComponent(node_13,()=>get$3(action2).icon,()=>`h-4 w-4 ${get$3(action2).variant==="destructive"?"text-destructive":""}`);var text_2=sibling(node_13);reset(div);var node_14=sibling(div,2);{var consequent_2=$$anchor9=>{KeyboardShortcutInfo($$anchor9,{get keys(){return get$3(action2).shortcut},get variant(){return get$3(action2).variant}})};if_block(node_14,$$render=>{get$3(action2).shortcut&&$$render(consequent_2)})}template_effect(()=>set_text(text_2,` ${get$3( -action2).label??""}`)),append($$anchor8,fragment_11)},$$slots:{default:!0}})}),append($$anchor6,fragment_9)}),append($$anchor5,fragment_8)},$$slots:{default:!0}})}),append($$anchor3,fragment_2)},$$slots:{default:!0}})}),append($$anchor,fragment_1),pop()}function circIn(t){return 1-Math.sqrt(1-t*t)}var root_2$7=from_html("
    "),root$f=from_html('
    ',1);function DesktopIconStrip($$anchor,$$props){push$1($$props, -!0);let sidebarOpen=prop($$props,"sidebarOpen",3,!1);const{handleKeydown}=useKeyboardShortcuts({activateSearchMode:()=>$$props.onSearchClick()});let initialized=state$1(!1),showIcons=user_derived(()=>!sidebarOpen());set$1(showIcons,!1),onMount$1(()=>{set$1(showIcons,!sidebarOpen()),setTimeout(()=>{set$1(initialized,!0)},ICON_STRIP_TRANSITION_DELAY_MULTIPLIER*SIDEBAR_ACTIONS_ITEMS.length)});var fragment=root$f();event("keydown",$window,handleKeydown);var div=first_child(fragment),aside=sibling(div, -2),div_1=child(aside);each(div_1,23,()=>SIDEBAR_ACTIONS_ITEMS,item=>item.tooltip,($$anchor2,item,i)=>{const onclick=user_derived(()=>get$3(item).route?()=>goto(get$3(item).route):$$props.onSearchClick),isActive=user_derived(()=>get$3(item).activeRouteId?page$1.route.id===get$3(item).activeRouteId:get$3(item).activeRoutePrefix?!!page$1.route.id?.startsWith(get$3(item).activeRoutePrefix):!1);var fragment_1=comment$2(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{var div_2=root_2$7(),node_1=child( -div_2);{let $0=user_derived(()=>get$3(isActive)?"bg-accent text-accent-foreground":"");ActionIcon(node_1,{get icon(){return get$3(item).icon},get tooltip(){return get$3(item).tooltip},get tooltipSide(){return TooltipSide.RIGHT},size:"lg",iconSize:"h-4 w-4",get class(){return`h-9 w-9 rounded-full hover:bg-accent! ${get$3($0)??""}`},get onclick(){return get$3(onclick)}})}reset(div_2),transition(1,div_2,()=>fade,()=>({duration:ICON_STRIP_TRANSITION_DURATION,delay:get$3(initialized)?0:ICON_STRIP_TRANSITION_DELAY_MULTIPLIER+ -get$3(i)*ICON_STRIP_TRANSITION_DELAY_MULTIPLIER,easing:circIn})),append($$anchor3,div_2)};if_block(node2,$$render=>{get$3(showIcons)&&$$render(consequent)})}append($$anchor2,fragment_1)}),reset(div_1),reset(aside),template_effect(()=>{set_class(div,1,`hidden shrink-0 transition-[width] duration-200 ease-linear md:block ${sidebarOpen()?"w-0":"w-[calc(var(--sidebar-width-icon)+1.5rem)]"}`),set_class(aside,1,`fixed top-0 bottom-0 left-0 z-10 hidden w-[calc(var(--sidebar-width-icon)+1.5rem)] flex-co\ -l items-center justify-between py-3 transition-opacity duration-200 ease-linear md:flex ${sidebarOpen()?"pointer-events-none opacity-0":"opacity-100"}`)}),append($$anchor,fragment),pop()}const itemIcon=($$anchor,IconComponent=noop$3)=>{var fragment=comment$2(),node2=first_child(fragment);component(node2,IconComponent,($$anchor2,IconComponent_1)=>{IconComponent_1($$anchor2,{class:"h-4 w-4"})}),append($$anchor,fragment)};var root_6$3=from_html('
    ', -1),root_9$2=from_html('
    ',1),root$e=from_html('
    ');function SidebarNavigationActions($$anchor,$$props){push$1($$props,!0);let isSearchModeActive=prop($$props,"isSearchModeActive",15),searchQuery=prop($$props,"searchQuery",15),isCancelAlwaysVisible=prop($$props,"isCancelAlwaysVisible",3,!1),searchInputRef=state$1(null);function handleSearchModeDeactivate(){isSearchModeActive(!1),searchQuery(""),$$props.onSearchDeactivated?.()} -function activateSearch(){isSearchModeActive(!0),queueMicrotask(()=>get$3(searchInputRef)?.focus())}var $$exports={activateSearch},div=root$e(),node_1=child(div);{var consequent=$$anchor2=>{SearchInput($$anchor2,{onClose:handleSearchModeDeactivate,onKeyDown:e=>e.key==="Escape"&&handleSearchModeDeactivate(),placeholder:"Search conversations...",get isCancelAlwaysVisible(){return isCancelAlwaysVisible()},get value(){return searchQuery()},set value($$value){searchQuery($$value)},get ref(){return get$3( -searchInputRef)},set ref($$value){set$1(searchInputRef,$$value,!0)}})},alternate_1=$$anchor2=>{var fragment_2=comment$2(),node_2=first_child(fragment_2);each(node_2,17,()=>SIDEBAR_ACTIONS_ITEMS,item=>item.route,($$anchor3,item)=>{var fragment_3=comment$2(),node_3=first_child(fragment_3);{var consequent_2=$$anchor4=>{Button($$anchor4,{class:"w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100",onclick:activateSearch,variant:"ghost",children:($$anchor5,$$slotProps)=>{var fragment_5=root_6$3(), -div_1=first_child(fragment_5),node_4=child(div_1);itemIcon(node_4,()=>get$3(item).icon);var text2=sibling(node_4);reset(div_1);var node_5=sibling(div_1,2);{var consequent_1=$$anchor6=>{KeyboardShortcutInfo($$anchor6,{get keys(){return get$3(item).keys}})};if_block(node_5,$$render=>{get$3(item).keys&&$$render(consequent_1)})}template_effect(()=>set_text(text2,` ${get$3(item).tooltip??""}`)),append($$anchor5,fragment_5)},$$slots:{default:!0}})},alternate=$$anchor4=>{{let $0=user_derived(()=>get$3( -item).activeRouteId&&page$1.route.id===get$3(item).activeRouteId||get$3(item).activeRoutePrefix&&page$1.route.id?.startsWith(get$3(item).activeRoutePrefix)?"bg-accent text-accent-foreground":"");Button($$anchor4,{get class(){return`w-full justify-between px-2 backdrop-blur-none! hover:[&>kbd]:opacity-100 ${get$3($0)??""}`},get href(){return get$3(item).route},get onclick(){return $$props.handleMobileSidebarItemClick},variant:"ghost",children:($$anchor5,$$slotProps)=>{var fragment_8=root_9$2(),div_2=first_child( -fragment_8),node_6=child(div_2);itemIcon(node_6,()=>get$3(item).icon);var text_1=sibling(node_6);reset(div_2);var node_7=sibling(div_2,2);{var consequent_3=$$anchor6=>{KeyboardShortcutInfo($$anchor6,{get keys(){return get$3(item).keys}})};if_block(node_7,$$render=>{get$3(item).keys&&$$render(consequent_3)})}template_effect(()=>set_text(text_1,` ${get$3(item).tooltip??""}`)),append($$anchor5,fragment_8)},$$slots:{default:!0}})}};if_block(node_3,$$render=>{get$3(item).route?$$render(alternate,-1): -$$render(consequent_2)})}append($$anchor3,fragment_3)}),append($$anchor2,fragment_2)};if_block(node_1,$$render=>{isSearchModeActive()?$$render(consequent):$$render(alternate_1,-1)})}return reset(div),append($$anchor,div),pop($$exports)}var root_3$4=from_html(''),root_4$2=from_html("

    See parent conversation

    "),root_2$6=from_html(" ",1),root_7$1=from_html('
    '),root_8$2=from_html("

    Stop generation

    "),root_6$2=from_html(" ",1),root_9$1=from_html('
    '),root$d=from_html('');function SidebarNavigationConversationItem($$anchor,$$props){ -push$1($$props,!0);let isActive=prop($$props,"isActive",3,!1),depth=prop($$props,"depth",3,0),renderActionsDropdown=state$1(!1),dropdownOpen=state$1(!1),isLoading2=user_derived(()=>getAllLoadingChats().includes($$props.conversation.id));function handleEdit(event2){event2.stopPropagation(),$$props.onEdit?.($$props.conversation.id)}function handleDelete2(event2){event2.stopPropagation(),$$props.onDelete?.($$props.conversation.id)}function handleStop(event2){event2.stopPropagation(),$$props.onStop?.( -$$props.conversation.id)}function handleGlobalEditEvent(event2){event2.detail.conversationId===$$props.conversation.id&&isActive()&&handleEdit(event2)}function handleMouseLeave(){get$3(dropdownOpen)||set$1(renderActionsDropdown,!1)}function handleMouseOver(){set$1(renderActionsDropdown,!0)}function handleSelect(){$$props.onSelect?.($$props.conversation.id)}user_effect(()=>{get$3(dropdownOpen)||set$1(renderActionsDropdown,!1)}),onMount$1(()=>(document.addEventListener("edit-active-conversation",handleGlobalEditEvent), -()=>{document.removeEventListener("edit-active-conversation",handleGlobalEditEvent)}));var button=root$d(),div=child(button);let styles2;var node2=child(div);{var consequent=$$anchor2=>{var fragment=comment$2(),node_1=first_child(fragment);component(node_1,()=>Root$5,($$anchor3,Tooltip_Root)=>{Tooltip_Root($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_1=root_2$6(),node_2=first_child(fragment_1);component(node_2,()=>Tooltip_trigger,($$anchor5,Tooltip_Trigger)=>{Tooltip_Trigger($$anchor5, -{children:($$anchor6,$$slotProps2)=>{var a=root_3$4(),node_3=child(a);Git_branch(node_3,{class:"h-3.5 w-3.5"}),reset(a),template_effect($0=>set_attribute(a,"href",$0),[()=>RouterService.chat($$props.conversation.forkedFromConversationId)]),append($$anchor6,a)},$$slots:{default:!0}})});var node_4=sibling(node_2,2);component(node_4,()=>Tooltip_content,($$anchor5,Tooltip_Content)=>{Tooltip_Content($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p2=root_4$2();append($$anchor6,p2)},$$slots:{default:!0}})}), -append($$anchor4,fragment_1)},$$slots:{default:!0}})}),append($$anchor2,fragment)};if_block(node2,$$render=>{depth()>0&&$$render(consequent)})}var node_5=sibling(node2,2);{var consequent_1=$$anchor2=>{var fragment_2=comment$2(),node_6=first_child(fragment_2);component(node_6,()=>Root$5,($$anchor3,Tooltip_Root_1)=>{Tooltip_Root_1($$anchor3,{children:($$anchor4,$$slotProps)=>{var fragment_3=root_6$2(),node_7=first_child(fragment_3);component(node_7,()=>Tooltip_trigger,($$anchor5,Tooltip_Trigger_1)=>{ -Tooltip_Trigger_1($$anchor5,{children:($$anchor6,$$slotProps2)=>{var div_1=root_7$1(),node_8=child(div_1);Loader_circle(node_8,{class:"loading-icon h-3.5 w-3.5 animate-spin"});var node_9=sibling(node_8,2);Square(node_9,{class:"stop-icon hidden h-3 w-3 fill-current text-destructive"}),reset(div_1),delegated("click",div_1,handleStop),delegated("keydown",div_1,e=>e.key==="Enter"&&handleStop(e)),append($$anchor6,div_1)},$$slots:{default:!0}})});var node_10=sibling(node_7,2);component(node_10,()=>Tooltip_content, -($$anchor5,Tooltip_Content_1)=>{Tooltip_Content_1($$anchor5,{children:($$anchor6,$$slotProps2)=>{var p_1=root_8$2();append($$anchor6,p_1)},$$slots:{default:!0}})}),append($$anchor4,fragment_3)},$$slots:{default:!0}})}),append($$anchor2,fragment_2)};if_block(node_5,$$render=>{get$3(isLoading2)&&$$render(consequent_1)})}var node_11=sibling(node_5,2);TruncatedText(node_11,{get text(){return $$props.conversation.name},class:"text-sm font-medium",showTooltip:!1}),reset(div);var node_12=sibling(div,2); -{var consequent_2=$$anchor2=>{var div_2=root_9$1(),node_13=child(div_2);{let $0=user_derived(()=>[{icon:Pencil,label:"Edit",onclick:handleEdit,shortcut:["shift","cmd","e"]},{icon:Download,label:"Export",onclick:e=>{e.stopPropagation(),conversationsStore.downloadConversation($$props.conversation.id)},shortcut:["shift","cmd","s"]},{icon:Trash_2,label:"Delete",onclick:handleDelete2,variant:"destructive",shortcut:["shift","cmd","d"],separator:!0}]);DropdownMenuActions(node_13,{get triggerIcon(){return Ellipsis}, -triggerTooltip:"More actions",get actions(){return get$3($0)},get open(){return get$3(dropdownOpen)},set open($$value){set$1(dropdownOpen,$$value,!0)}})}reset(div_2),append($$anchor2,div_2)};if_block(node_12,$$render=>{get$3(renderActionsDropdown)&&$$render(consequent_2)})}reset(button),template_effect(()=>{set_class(button,1,`group flex min-h-9 w-full cursor-pointer items-center justify-between space-x-3 rounded-lg py-1.5 text-left transition-colors hover:bg-foreground/10 ${isActive()?"bg-foreg\ -round/5 text-accent-foreground":""} px-3`,"svelte-19ekn76"),styles2=set_style(div,"",styles2,{"padding-left":`${depth()*FORK_TREE_DEPTH_PADDING}px`})}),delegated("click",button,handleSelect),delegated("mouseover",button,handleMouseOver),event("mouseleave",button,handleMouseLeave),append($$anchor,button),pop()}delegate(["click","mouseover","keydown"]);const SIDEBAR_COOKIE_NAME="sidebar:state",SIDEBAR_COOKIE_MAX_AGE=3600*24*7,SIDEBAR_MIN_WIDTH="18rem",SIDEBAR_MAX_WIDTH="32rem",SIDEBAR_WIDTH_ICON="\ -3rem",SIDEBAR_KEYBOARD_SHORTCUT="b";class SidebarState{props;#open=user_derived(()=>this.props.open());get open(){return get$3(this.#open)}set open(value){set$1(this.#open,value)}#openMobile=state$1(!1);get openMobile(){return get$3(this.#openMobile)}set openMobile(value){set$1(this.#openMobile,value,!0)}#sidebarWidth=state$1(proxy(SIDEBAR_MIN_WIDTH));get sidebarWidth(){return get$3(this.#sidebarWidth)}set sidebarWidth(value){set$1(this.#sidebarWidth,value,!0)}#isResizing=state$1(!1);get isResizing(){ -return get$3(this.#isResizing)}set isResizing(value){set$1(this.#isResizing,value,!0)}setOpen;#isMobile;#state=user_derived(()=>this.open?"expanded":"collapsed");get state(){return get$3(this.#state)}set state(value){set$1(this.#state,value)}constructor(props){this.setOpen=props.setOpen,this.#isMobile=new IsMobile,this.props=props}get isMobile(){return this.#isMobile.current}handleShortcutKeydown=e=>{e.key===SIDEBAR_KEYBOARD_SHORTCUT&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),this.toggle())};setOpenMobile=value=>{ -this.openMobile=value};toggle=()=>{this.setOpen(!this.open)}}const SYMBOL_KEY="scn-sidebar";function setSidebar(props){return setContext(Symbol.for(SYMBOL_KEY),new SidebarState(props))}function useSidebar(){return getContext(Symbol.for(SYMBOL_KEY))}var root$c=from_html("
    ");function Sidebar_group_content($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$c();attribute_effect( -div,$0=>({"data-slot":"sidebar-group-content","data-sidebar":"group-content",class:$0,...restProps}),[()=>cn$1("w-full text-sm",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root_2$5=from_html("
    ");function Sidebar_group_label($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legac\ -y","ref","children","child","class"]);const mergedProps=user_derived(()=>({class:cn$1("text-sidebar-foreground/70 ring-sidebar-ring outline-hidden flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0","group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",$$props.class),"data-slot":"sidebar-group-label","data-sidebar":"group-label",...restProps}));var fragment=comment$2(), -node2=first_child(fragment);{var consequent=$$anchor2=>{var fragment_1=comment$2(),node_1=first_child(fragment_1);snippet(node_1,()=>$$props.child,()=>({props:get$3(mergedProps)})),append($$anchor2,fragment_1)},alternate=$$anchor2=>{var div=root_2$5();attribute_effect(div,()=>({...get$3(mergedProps)}));var node_2=child(div);snippet(node_2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor2,div)};if_block(node2,$$render=>{$$props.child?$$render( -consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop()}var root$b=from_html("
    ");function Sidebar_group($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$b();attribute_effect(div,$0=>({"data-slot":"sidebar-group","data-sidebar":"group",class:$0,...restProps}),[()=>cn$1("relative flex w-full min-w-0 flex-col p-2",$$props.class)]);var node2=child( -div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root$a=from_html("
    ");function Sidebar_header($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var div=root$a();attribute_effect(div,$0=>({"data-slot":"sidebar-header","data-sidebar":"header",class:$0,...restProps}),[()=>cn$1("flex f\ -lex-col gap-2 p-2",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()}var root$9=from_html("
    ");function Sidebar_inset($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var main2=root$9();attribute_effect(main2,$0=>({"data-slot":"sidebar-inset",class:$0, -...restProps}),[()=>cn$1("relative flex w-full flex-1 flex-col","md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",$$props.class)]);var node2=child(main2);snippet(node2,()=>$$props.children??noop$3),reset(main2),bind_this(main2,$$value=>ref2($$value),()=>ref2()),append($$anchor,main2),pop()}tv({base:"peer/menu-button outline-hidden ring-si\ -debar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground group-has-data-[sidebar=menu-action]/menu-item:pr-8 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! flex w-full items-center gap-2 overflow-hidden rounded-m\ -d py-2 px-1 text-left text-sm transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",variants:{variant:{default:"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",outline:"bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_var(--sidebar-border)]\ - hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"},size:{default:"h-8 text-sm",sm:"h-7 text-xs",lg:"group-data-[collapsible=icon]:p-0! h-12 text-sm"}},defaultVariants:{variant:"default",size:"default"}});var root$8=from_html("
  • ");function Sidebar_menu_item($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var li2=root$8();attribute_effect(li2,$0=>({"data-slot":"sidebar-menu-\ -item","data-sidebar":"menu-item",class:$0,...restProps}),[()=>cn$1("group/menu-item relative",$$props.class)]);var node2=child(li2);snippet(node2,()=>$$props.children??noop$3),reset(li2),bind_this(li2,$$value=>ref2($$value),()=>ref2()),append($$anchor,li2),pop()}var root$7=from_html("
    ");function Sidebar_menu($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","children"]);var ul=root$7(); -attribute_effect(ul,$0=>({"data-slot":"sidebar-menu","data-sidebar":"menu",class:$0,...restProps}),[()=>cn$1("flex w-full min-w-0 flex-col gap-1",$$props.class)]);var node2=child(ul);snippet(node2,()=>$$props.children??noop$3),reset(ul),bind_this(ul,$$value=>ref2($$value),()=>ref2()),append($$anchor,ul),pop()}var root$6=from_html("
    ");function Sidebar_provider($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),open2=prop($$props,"open",15,!0),onOpenChange=prop( -$$props,"onOpenChange",3,()=>{}),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","open","onOpenChange","class","style","children"]);const sidebar=setSidebar({open:()=>open2(),setOpen:value=>{open2(value),onOpenChange()(value),document.cookie=`${SIDEBAR_COOKIE_NAME}=${open2()}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`}});var div=root$6();event("keydown",$window,function(...$$args){sidebar.handleShortcutKeydown?.apply(this,$$args)}),attribute_effect(div,$0=>({"data-slot":"sid\ -ebar-wrapper",style:`--sidebar-width: ${sidebar.sidebarWidth??""}; --sidebar-min-width: ${SIDEBAR_MIN_WIDTH}; --sidebar-max-width: ${SIDEBAR_MAX_WIDTH}; --sidebar-width-icon: ${SIDEBAR_WIDTH_ICON}; ${$$props.style??""}`,class:$0,...restProps}),[()=>cn$1("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",$$props.class)]);var node2=child(div);snippet(node2,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()),append($$anchor,div),pop()} -var root_1$5=from_html(' Toggle Sidebar',1);function Sidebar_trigger($$anchor,$$props){push$1($$props,!0),prop($$props,"ref",11,null);let restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","class","onclick"]);const sidebar=useSidebar();{let $0=user_derived(()=>$$props.class),$1=user_derived(()=>sidebar.open?"top-1.5":"top-0"),$2=user_derived(()=>sidebar.isResizing?"!duration-0":"");Button($$anchor,spread_props({"data-sidebar":"trigger","data-slot":"\ -sidebar-trigger",variant:"ghost",size:"icon-lg",get class(){return`rounded-full backdrop-blur-lg ${get$3($0)??""} ${get$3($1)??""} md:left-[calc(var(--sidebar-width)-3.25rem)] ${get$3($2)??""}`},type:"button",onclick:e=>{$$props.onclick?.(e),sidebar.toggle()}},()=>restProps,{children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$5(),node2=first_child(fragment_1);{var consequent=$$anchor3=>{Panel_left_close($$anchor3,{})},alternate=$$anchor3=>{Panel_left($$anchor3,{})};if_block(node2,$$render=>{ -sidebar.open?$$render(consequent):$$render(alternate,-1)})}next$1(2),append($$anchor2,fragment_1)},$$slots:{default:!0}}))}pop()}var root_1$4=from_html("
    "),root_3$3=from_html('
    '),root_4$1=from_html('
    '),root_2$4=from_html('
    ');function Sidebar($$anchor,$$props){push$1($$props,!0);let ref2=prop($$props,"ref",15,null),side=prop($$props,"side",3,"left"),variant=prop($$props,"variant",3,"sidebar"),collapsible=prop($$props,"collapsible",3,"offcanvas"),restProps=rest_props($$props,["$$slots","$$events","$$legacy","ref","side","variant","collapsible","class","children"]);const sidebar=useSidebar();function handleResizePointerDown(e){if(sidebar.isMobile)return; -e.preventDefault();const target2=e.currentTarget;target2.setPointerCapture(e.pointerId);const minPx=remToPx(SIDEBAR_MIN_WIDTH),maxPx=remToPx(SIDEBAR_MAX_WIDTH);sidebar.isResizing=!0;function onPointerMove(ev){const newWidth=side()==="left"?ev.clientX:window.innerWidth-ev.clientX,clamped=Math.min(maxPx,Math.max(minPx,newWidth));sidebar.sidebarWidth=`${clamped}px`}function onPointerUp(){sidebar.isResizing=!1,target2.removeEventListener("pointermove",onPointerMove),target2.removeEventListener("poin\ -terup",onPointerUp)}target2.addEventListener("pointermove",onPointerMove),target2.addEventListener("pointerup",onPointerUp)}var fragment=comment$2(),node2=first_child(fragment);{var consequent=$$anchor2=>{var div=root_1$4();attribute_effect(div,$0=>({class:$0,...restProps}),[()=>cn$1("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",$$props.class)]);var node_1=child(div);snippet(node_1,()=>$$props.children??noop$3),reset(div),bind_this(div,$$value=>ref2($$value),()=>ref2()), -append($$anchor2,div)},alternate_1=$$anchor2=>{var div_1=root_2$4(),div_2=child(div_1),div_3=sibling(div_2,2);attribute_effect(div_3,$0=>({"data-slot":"sidebar-container",class:$0,style:variant()==="floating"?"height: calc(100dvh - 1.5rem);":void 0,...restProps}),[()=>cn$1("fixed inset-y-0 z-[900] flex w-[calc(100dvw-1.5rem)] duration-200 ease-linear md:z-0 md:w-(--sidebar-width)","group-data-[collapsible=offcanvas]:pointer-events-none md:group-data-[collapsible=offcanvas]:pointer-events-auto",sidebar. -isResizing&&"!duration-0",variant()==="floating"?["transition-[left,right,width,opacity]",side()==="left"?"left-3 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-0.775)] group-data-[collapsible=offcanvas]:opacity-0":"right-3 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-0.775)] group-data-[collapsible=offcanvas]:opacity-0","my-3 overflow-hidden rounded-3xl border border-sidebar-border shadow-md"]:["h-svh transition-[left,right,width]",side()==="left"?"l\ -eft-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]":"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"],variant()==="inset"?"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]":variant()==="floating"?"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon)",$$props.class)]);var div_4=child(div_3),node_2=child(div_4); -snippet(node_2,()=>$$props.children??noop$3),reset(div_4);var node_3=sibling(div_4,2);{var consequent_1=$$anchor3=>{var div_5=root_3$3();let classes;template_effect(()=>classes=set_class(div_5,1,"absolute inset-y-0 right-0 z-50 hidden w-1.5 cursor-ew-resize touch-none select-none hover:bg-sidebar-border/50 active:bg-sidebar-border md:block",null,classes,{"bg-sidebar-border":sidebar.isResizing})),delegated("pointerdown",div_5,handleResizePointerDown),append($$anchor3,div_5)},alternate=$$anchor3=>{ -var div_6=root_4$1();let classes_1;template_effect(()=>classes_1=set_class(div_6,1,"absolute inset-y-0 left-0 z-50 hidden w-1.5 cursor-ew-resize touch-none select-none hover:bg-sidebar-border/50 active:bg-sidebar-border md:block",null,classes_1,{"bg-sidebar-border":sidebar.isResizing})),delegated("pointerdown",div_6,handleResizePointerDown),append($$anchor3,div_6)};if_block(node_3,$$render=>{side()==="left"?$$render(consequent_1):$$render(alternate,-1)})}reset(div_3),reset(div_1),bind_this(div_1, -$$value=>ref2($$value),()=>ref2()),template_effect($0=>{set_attribute(div_1,"data-state",sidebar.state),set_attribute(div_1,"data-collapsible",sidebar.state==="collapsed"?collapsible():""),set_attribute(div_1,"data-variant",variant()),set_attribute(div_1,"data-side",side()),set_class(div_2,1,$0)},[()=>clsx(cn$1("relative bg-transparent transition-[width] duration-200 ease-linear",sidebar.isResizing&&"!duration-0","w-0",variant()==="floating"?"md:w-[calc(var(--sidebar-width)+0.75rem)]":"md:w-(--s\ -idebar-width)","md:group-data-[collapsible=offcanvas]:w-0","group-data-[side=right]:rotate-180",variant()==="floating"||variant()==="inset"?"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]":"group-data-[collapsible=icon]:w-(--sidebar-width-icon)"))]),append($$anchor2,div_1)};if_block(node2,$$render=>{collapsible()==="none"?$$render(consequent):$$render(alternate_1,-1)})}append($$anchor,fragment),pop()}delegate(["pointerdown"]);var root_3$2=from_html(' Close sidebar',1),root_2$3=from_html(' ',1),root_11$1=from_html('

    '),root_8$1=from_html(" ",1),root_4=from_html(" ",1),root_1$3=from_html(" ",1),root_13=from_html('
    '), -root$5=from_html('
    ',1);function SidebarNavigation($$anchor,$$props){push$1($$props,!0);const sidebar=useSidebar();let currentChatId=user_derived(()=>page$1.params.id),isSearchModeActive=state$1(!1),searchQuery=state$1(""),showDeleteDialog=state$1(!1),deleteWithForks=state$1(!1),showEditDialog=state$1(!1),selectedConversation=state$1(null),editedName=state$1(""),selectedConversationNamePreview=user_derived(()=>get$3(selectedConversation)?getPreviewText( -get$3(selectedConversation).name):""),filteredConversations=user_derived(()=>get$3(isSearchModeActive)?get$3(searchQuery).trim().length>0?conversations().filter(conversation=>conversation.name.toLowerCase().includes(get$3(searchQuery).toLowerCase())):[]:conversations()),conversationTree=user_derived(()=>buildConversationTree(get$3(filteredConversations))),selectedConversationHasDescendants=user_derived(()=>{if(!get$3(selectedConversation))return!1;const allConvs=conversations(),queue=[get$3(selectedConversation). -id];for(;queue.length>0;){const parentId=queue.pop();for(const c2 of allConvs)if(c2.forkedFromConversationId===parentId)return!0}return!1});async function handleDeleteConversation(id2){const conversation=conversations().find(conv=>conv.id===id2);conversation&&(set$1(selectedConversation,conversation,!0),set$1(deleteWithForks,!1),set$1(showDeleteDialog,!0))}async function handleEditConversation(id2){const conversation=conversations().find(conv=>conv.id===id2);conversation&&(set$1(selectedConversation, -conversation,!0),set$1(editedName,conversation.name,!0),set$1(showEditDialog,!0))}function handleConfirmDelete(){if(get$3(selectedConversation)){const convId=get$3(selectedConversation).id,withForks=get$3(deleteWithForks);set$1(showDeleteDialog,!1),setTimeout(()=>{conversationsStore.deleteConversation(convId,{deleteWithForks:withForks})},100)}}function handleConfirmEdit(){!get$3(editedName).trim()||!get$3(selectedConversation)||(set$1(showEditDialog,!1),conversationsStore.updateConversationName( -get$3(selectedConversation).id,get$3(editedName)),set$1(selectedConversation,null))}function handleMobileSidebarItemClick(){sidebar.isMobile&&sidebar.toggle()}let chatSidebarActions=state$1(void 0),openedForSearch=state$1(!1);function activateSearchMode(){sidebar.open||set$1(openedForSearch,!0),get$3(chatSidebarActions)?.activateSearch?.()}function handleSearchDeactivated(){get$3(openedForSearch)&&(set$1(openedForSearch,!1),sidebar.toggle())}user_effect(()=>{sidebar.open||(set$1(isSearchModeActive, -!1),set$1(searchQuery,""),set$1(openedForSearch,!1))});function editActiveConversation(){if(get$3(currentChatId)&&get$3(filteredConversations).find(conv=>conv.id===get$3(currentChatId))){const event2=new CustomEvent("edit-active-conversation",{detail:{conversationId:get$3(currentChatId)}});document.dispatchEvent(event2)}}async function selectConversation(id2){get$3(isSearchModeActive)&&(set$1(isSearchModeActive,!1),set$1(searchQuery,"")),handleMobileSidebarItemClick(),await goto(RouterService.chat( -id2))}function handleStopGeneration(id2){chatStore.stopGenerationForChat(id2)}var $$exports={handleMobileSidebarItemClick,activateSearchMode,editActiveConversation},fragment=root$5(),div=first_child(fragment),node2=child(div);Scroll_area(node2,{class:"h-full flex-1",children:($$anchor2,$$slotProps)=>{var fragment_1=root_1$3(),node_1=first_child(fragment_1);component(node_1,()=>Sidebar_header,($$anchor3,Sidebar_Header)=>{Sidebar_Header($$anchor3,{class:"gap-4 bg-sidebar/50 p-3 backdrop-blur-lg md\ -:pt-4 md:pb-2",children:($$anchor4,$$slotProps2)=>{var fragment_2=root_2$3(),div_1=first_child(fragment_2),a=child(div_1),h1=child(a),text2=child(h1,!0);reset(h1),reset(a);var node_2=sibling(a,2);Button(node_2,{class:"rounded-full md:hidden",variant:"ghost",size:"icon",onclick:()=>sidebar.toggle(),children:($$anchor5,$$slotProps3)=>{var fragment_3=root_3$2(),node_3=first_child(fragment_3);X(node_3,{class:"h-4 w-4"}),next$1(2),append($$anchor5,fragment_3)},$$slots:{default:!0}}),reset(div_1);var node_4=sibling( -div_1,2);bind_this(SidebarNavigationActions(node_4,{handleMobileSidebarItemClick,onSearchDeactivated:handleSearchDeactivated,get isSearchModeActive(){return get$3(isSearchModeActive)},set isSearchModeActive($$value){set$1(isSearchModeActive,$$value,!0)},get searchQuery(){return get$3(searchQuery)},set searchQuery($$value){set$1(searchQuery,$$value,!0)}}),$$value=>set$1(chatSidebarActions,$$value,!0),()=>get$3(chatSidebarActions)),template_effect(()=>{set_attribute(a,"href",ROUTES.START),set_text( -text2,APP_NAME)}),delegated("click",a,handleMobileSidebarItemClick),append($$anchor4,fragment_2)},$$slots:{default:!0}})});var node_5=sibling(node_1,2);component(node_5,()=>Sidebar_group,($$anchor3,Sidebar_Group)=>{Sidebar_Group($$anchor3,{class:"mt-2 h-[calc(100vh-21rem)] space-y-2 p-0 px-3",children:($$anchor4,$$slotProps2)=>{var fragment_4=root_4(),node_6=first_child(fragment_4);{var consequent=$$anchor5=>{var fragment_5=comment$2(),node_7=first_child(fragment_5);component(node_7,()=>Sidebar_group_label, -($$anchor6,Sidebar_GroupLabel)=>{Sidebar_GroupLabel($$anchor6,{children:($$anchor7,$$slotProps3)=>{next$1();var text_1=text$8();template_effect(()=>set_text(text_1,get$3(isSearchModeActive)?"Search results":"Recent conversations")),append($$anchor7,text_1)},$$slots:{default:!0}})}),append($$anchor5,fragment_5)};if_block(node_6,$$render=>{(get$3(filteredConversations).length>0&&get$3(isSearchModeActive)||!get$3(isSearchModeActive))&&$$render(consequent)})}var node_8=sibling(node_6,2);component(node_8, -()=>Sidebar_group_content,($$anchor5,Sidebar_GroupContent)=>{Sidebar_GroupContent($$anchor5,{children:($$anchor6,$$slotProps3)=>{var fragment_7=comment$2(),node_9=first_child(fragment_7);component(node_9,()=>Sidebar_menu,($$anchor7,Sidebar_Menu)=>{Sidebar_Menu($$anchor7,{children:($$anchor8,$$slotProps4)=>{var fragment_8=root_8$1(),node_10=first_child(fragment_8);each(node_10,17,()=>get$3(conversationTree),({conversation,depth})=>conversation.id,($$anchor9,$$item)=>{let conversation=()=>get$3($$item). -conversation,depth=()=>get$3($$item).depth;var fragment_9=comment$2(),node_11=first_child(fragment_9);component(node_11,()=>Sidebar_menu_item,($$anchor10,Sidebar_MenuItem)=>{Sidebar_MenuItem($$anchor10,{class:"mb-1 p-0",children:($$anchor11,$$slotProps5)=>{{let $0=user_derived(()=>({id:conversation().id,name:conversation().name,lastModified:conversation().lastModified,currNode:conversation().currNode,forkedFromConversationId:conversation().forkedFromConversationId})),$1=user_derived(()=>get$3(currentChatId)=== -conversation().id);SidebarNavigationConversationItem($$anchor11,{get conversation(){return get$3($0)},get depth(){return depth()},get isActive(){return get$3($1)},onSelect:selectConversation,onEdit:handleEditConversation,onDelete:handleDeleteConversation,onStop:handleStopGeneration})}},$$slots:{default:!0}})}),append($$anchor9,fragment_9)});var node_12=sibling(node_10,2);{var consequent_1=$$anchor9=>{var div_2=root_11$1(),p2=child(div_2),text_2=child(p2,!0);reset(p2),reset(div_2),template_effect( -()=>set_text(text_2,get$3(searchQuery).length>0?"No results found":get$3(isSearchModeActive)?"Start typing to see results":"No conversations yet")),append($$anchor9,div_2)};if_block(node_12,$$render=>{get$3(conversationTree).length===0&&$$render(consequent_1)})}append($$anchor8,fragment_8)},$$slots:{default:!0}})}),append($$anchor6,fragment_7)},$$slots:{default:!0}})}),append($$anchor4,fragment_4)},$$slots:{default:!0}})}),append($$anchor2,fragment_1)},$$slots:{default:!0}}),reset(div);var node_13=sibling( -div,2);{let $0=user_derived(()=>get$3(selectedConversation)?`Are you sure you want to delete "${get$3(selectedConversationNamePreview)}"? This action cannot be undone and will permanently remove all messages in this conversation.`:"");DialogConfirmation(node_13,{title:"Delete Conversation",get description(){return get$3($0)},confirmText:"Delete",cancelText:"Cancel",variant:"destructive",get icon(){return Trash_2},onConfirm:handleConfirmDelete,onCancel:()=>{set$1(showDeleteDialog,!1),set$1(selectedConversation, -null)},get open(){return get$3(showDeleteDialog)},set open($$value){set$1(showDeleteDialog,$$value,!0)},children:($$anchor2,$$slotProps)=>{var fragment_11=comment$2(),node_14=first_child(fragment_11);{var consequent_2=$$anchor3=>{var div_3=root_13(),node_15=child(div_3);Checkbox(node_15,{id:"delete-with-forks",get checked(){return get$3(deleteWithForks)},set checked($$value){set$1(deleteWithForks,$$value,!0)}});var node_16=sibling(node_15,2);Label(node_16,{for:"delete-with-forks",class:"text-sm", -children:($$anchor4,$$slotProps2)=>{next$1();var text_3=text$8("Also delete all forked conversations");append($$anchor4,text_3)},$$slots:{default:!0}}),reset(div_3),append($$anchor3,div_3)};if_block(node_14,$$render=>{get$3(selectedConversationHasDescendants)&&$$render(consequent_2)})}append($$anchor2,fragment_11)},$$slots:{default:!0}})}var node_17=sibling(node_13,2);return DialogConfirmation(node_17,{title:"Edit Conversation Name",description:"",confirmText:"Save",cancelText:"Cancel",get icon(){ -return Pencil},onConfirm:handleConfirmEdit,onCancel:()=>{set$1(showEditDialog,!1),set$1(selectedConversation,null)},onKeydown:event2=>{event2.key==="Enter"&&(event2.preventDefault(),event2.stopImmediatePropagation(),handleConfirmEdit())},get open(){return get$3(showEditDialog)},set open($$value){set$1(showEditDialog,$$value,!0)},children:($$anchor2,$$slotProps)=>{Input($$anchor2,{class:"text-foreground",placeholder:"Enter a new name",type:"text",get value(){return get$3(editedName)},set value($$value){ -set$1(editedName,$$value,!0)}})},$$slots:{default:!0}}),append($$anchor,fragment),pop($$exports)}delegate(["click"]);var root_2$2=from_html(" ",1),root_1$2=from_html(" ",1),root_6$1=from_html(" ",1),root$4=from_html('
    ');function ServerStatus($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),showActions=prop($$props,"showActions", -3,!1),error2=user_derived(serverError),loading=user_derived(serverLoading),model=user_derived(singleModelName),serverData=user_derived(serverProps);function getStatusColor(){return get$3(loading)?"bg-yellow-500":get$3(error2)?"bg-red-500":get$3(serverData)?"bg-green-500":"bg-gray-500"}function getStatusText(){return get$3(loading)?"Connecting...":get$3(error2)?"Connection Error":get$3(serverData)?"Connected":"Unknown"}var div=root$4(),div_1=child(div),div_2=child(div_1),span=sibling(div_2,2),text2=child( -span,!0);reset(span),reset(div_1);var node2=sibling(div_1,2);{var consequent_1=$$anchor2=>{var fragment=root_1$2(),node_1=first_child(fragment);Badge(node_1,{variant:"outline",class:"text-xs",children:($$anchor3,$$slotProps)=>{var fragment_1=root_2$2(),node_2=first_child(fragment_1);Server(node_2,{class:"mr-1 h-3 w-3"});var text_1=sibling(node_2);template_effect(()=>set_text(text_1,` ${(get$3(model)||"Unknown Model")??""}`)),append($$anchor3,fragment_1)},$$slots:{default:!0}});var node_3=sibling( -node_1,2);{var consequent=$$anchor3=>{Badge($$anchor3,{variant:"secondary",class:"text-xs",children:($$anchor4,$$slotProps)=>{next$1();var text_2=text$8();template_effect($0=>set_text(text_2,`ctx: ${$0??""}`),[()=>get$3(serverData).default_generation_settings.n_ctx.toLocaleString()]),append($$anchor4,text_2)},$$slots:{default:!0}})};if_block(node_3,$$render=>{get$3(serverData)?.default_generation_settings?.n_ctx&&$$render(consequent)})}append($$anchor2,fragment)};if_block(node2,$$render=>{get$3( -serverData)&&!get$3(error2)&&$$render(consequent_1)})}var node_4=sibling(node2,2);{var consequent_2=$$anchor2=>{Button($$anchor2,{variant:"outline",size:"sm",class:"text-destructive",children:($$anchor3,$$slotProps)=>{var fragment_5=root_6$1(),node_5=first_child(fragment_5);Triangle_alert(node_5,{class:"h-4 w-4"});var text_3=sibling(node_5);template_effect(()=>set_text(text_3,` ${get$3(error2)??""}`)),append($$anchor3,fragment_5)},$$slots:{default:!0}})};if_block(node_4,$$render=>{showActions()&& -get$3(error2)&&$$render(consequent_2)})}reset(div),template_effect(($0,$1)=>{set_class(div,1,`flex items-center space-x-3 ${className()??""}`),set_class(div_2,1,`h-2 w-2 rounded-full ${$0??""}`),set_text(text2,$1)},[()=>getStatusColor(),()=>getStatusText()]),append($$anchor,div),pop()}var root_2$1=from_html(" Enter API Key",1),root_1$1=from_html('
    '),root_5$1=from_html('
    '),root_6=from_html('
    '),root_7=from_html('
    '),root_8=from_html('

    '),root_9=from_html('

    ✓ API key validated successfully! Connecting...

    '),root_11=from_html(" Validating...",1),root_3$1=from_html('
    '),root_17=from_html(" Connecting...",1),root_18=from_html(" Retry Connection",1),root_15=from_html("
    "),root_19=from_html('
    Troubleshooting

    Start the llama-server:

    llama-server -hf ggml-org/gemma-3-4b-it-GGUF

    or

    llama-server -m locally-stored-model.gguf

    • Check that the server is accessible at the correct URL
    • Verify your network connection
    • Check server logs for any error messages
    '),root$3=from_html('

    Server Connection Error

    ');function ServerErrorSplash($$anchor,$$props){push$1($$props,!0);let className=prop($$props,"class",3,""),showRetry=prop($$props,"showRetry",3,!0),showTroubleshooting=prop($$props,"showTroubleshooting", -3,!1),isServerLoading=user_derived(serverLoading),isAccessDeniedError=user_derived(()=>$$props.error.toLowerCase().includes("access denied")||$$props.error.toLowerCase().includes("invalid api key")||$$props.error.toLowerCase().includes("unauthorized")||$$props.error.toLowerCase().includes("401")||$$props.error.toLowerCase().includes("403")),apiKeyInput=state$1(""),showApiKeyInput=state$1(!1),apiKeyState=state$1("idle"),apiKeyError=state$1("");function handleRetryConnection(){$$props.onRetry?$$props. -onRetry():serverStore.fetch()}function handleShowApiKeyInput(){set$1(showApiKeyInput,!0);const currentConfig=config$1();set$1(apiKeyInput,currentConfig.apiKey?.toString()||"",!0)}async function handleSaveApiKey(){if(get$3(apiKeyInput).trim()){set$1(apiKeyState,"validating"),set$1(apiKeyError,"");try{settingsStore.updateConfig(SETTINGS_KEYS.API_KEY,get$3(apiKeyInput).trim());const response=await fetch(`${base}/props`,{headers:{"Content-Type":"application/json",Authorization:`Bearer ${get$3(apiKeyInput). -trim()}`}});response.ok?(set$1(apiKeyState,"success"),setTimeout(()=>{goto(ROUTES.START)},1e3)):(set$1(apiKeyState,"error"),response.status===401||response.status===403?set$1(apiKeyError,"Invalid API key - please check and try again"):set$1(apiKeyError,`Authentication failed (${response.status})`),setTimeout(()=>{set$1(apiKeyState,"idle")},3e3))}catch(error2){set$1(apiKeyState,"error"),error2 instanceof Error?error2.message.includes("fetch")?set$1(apiKeyError,"Cannot connect to server - check if\ - server is running"):set$1(apiKeyError,error2.message,!0):set$1(apiKeyError,"Connection error - please try again"),setTimeout(()=>{set$1(apiKeyState,"idle")},3e3)}}}function handleApiKeyKeydown(event2){event2.key===KeyboardKey.ENTER&&handleSaveApiKey()}var div=root$3(),div_1=child(div),div_2=child(div_1),div_3=child(div_2),node2=child(div_3);Triangle_alert(node2,{class:"h-8 w-8 text-destructive"}),reset(div_3);var p2=sibling(div_3,4),text2=child(p2,!0);reset(p2),reset(div_2);var node_1=sibling(div_2, -2);{var consequent=$$anchor2=>{var div_4=root_1$1(),node_2=child(div_4);Button(node_2,{onclick:handleShowApiKeyInput,variant:"outline",class:"w-full",children:($$anchor3,$$slotProps)=>{var fragment=root_2$1(),node_3=first_child(fragment);Key(node_3,{class:"h-4 w-4"}),next$1(),append($$anchor3,fragment)},$$slots:{default:!0}}),reset(div_4),transition(1,div_4,()=>fly,()=>({y:10,duration:300,delay:200})),append($$anchor2,div_4)};if_block(node_1,$$render=>{get$3(isAccessDeniedError)&&!get$3(showApiKeyInput)&& -$$render(consequent)})}var node_4=sibling(node_1,2);{var consequent_8=$$anchor2=>{var div_5=root_3$1(),div_6=child(div_5),node_5=child(div_6);Label(node_5,{for:"api-key-input",class:"text-sm font-medium",children:($$anchor3,$$slotProps)=>{next$1();var text_1=text$8("API Key");append($$anchor3,text_1)},$$slots:{default:!0}});var div_7=sibling(node_5,2),node_6=child(div_7);{let $0=user_derived(()=>get$3(apiKeyState)==="error"?"border-destructive":get$3(apiKeyState)==="success"?"border-green-500":""), -$1=user_derived(()=>get$3(apiKeyState)==="validating");Input(node_6,{id:"api-key-input",placeholder:"Enter your API key...",onkeydown:handleApiKeyKeydown,get class(){return`w-full pr-10 ${get$3($0)??""}`},get disabled(){return get$3($1)},get value(){return get$3(apiKeyInput)},set value($$value){set$1(apiKeyInput,$$value,!0)}})}var node_7=sibling(node_6,2);{var consequent_1=$$anchor3=>{var div_8=root_5$1(),node_8=child(div_8);Refresh_cw(node_8,{class:"h-4 w-4 animate-spin text-muted-foreground"}), -reset(div_8),append($$anchor3,div_8)},consequent_2=$$anchor3=>{var div_9=root_6(),node_9=child(div_9);Circle_check_big(node_9,{class:"h-4 w-4 text-green-500"}),reset(div_9),transition(1,div_9,()=>scale,()=>({duration:200,start:.8})),append($$anchor3,div_9)},consequent_3=$$anchor3=>{var div_10=root_7(),node_10=child(div_10);Circle_x(node_10,{class:"h-4 w-4 text-destructive"}),reset(div_10),transition(1,div_10,()=>scale,()=>({duration:200,start:.8})),append($$anchor3,div_10)};if_block(node_7,$$render=>{ -get$3(apiKeyState)==="validating"?$$render(consequent_1):get$3(apiKeyState)==="success"?$$render(consequent_2,1):get$3(apiKeyState)==="error"&&$$render(consequent_3,2)})}reset(div_7);var node_11=sibling(div_7,2);{var consequent_4=$$anchor3=>{var p_1=root_8(),text_2=child(p_1,!0);reset(p_1),template_effect(()=>set_text(text_2,get$3(apiKeyError))),transition(1,p_1,()=>fly,()=>({y:-10,duration:200})),append($$anchor3,p_1)};if_block(node_11,$$render=>{get$3(apiKeyError)&&$$render(consequent_4)})}var node_12=sibling( -node_11,2);{var consequent_5=$$anchor3=>{var p_2=root_9();transition(1,p_2,()=>fly,()=>({y:-10,duration:200})),append($$anchor3,p_2)};if_block(node_12,$$render=>{get$3(apiKeyState)==="success"&&$$render(consequent_5)})}reset(div_6);var div_11=sibling(div_6,2),node_13=child(div_11);{let $0=user_derived(()=>!get$3(apiKeyInput).trim()||get$3(apiKeyState)==="validating"||get$3(apiKeyState)==="success");Button(node_13,{onclick:handleSaveApiKey,get disabled(){return get$3($0)},class:"flex-1",children:($$anchor3,$$slotProps)=>{ -var fragment_1=comment$2(),node_14=first_child(fragment_1);{var consequent_6=$$anchor4=>{var fragment_2=root_11(),node_15=first_child(fragment_2);Refresh_cw(node_15,{class:"h-4 w-4 animate-spin"}),next$1(),append($$anchor4,fragment_2)},consequent_7=$$anchor4=>{var text_3=text$8("Success!");append($$anchor4,text_3)},alternate=$$anchor4=>{var text_4=text$8("Save & Retry");append($$anchor4,text_4)};if_block(node_14,$$render=>{get$3(apiKeyState)==="validating"?$$render(consequent_6):get$3(apiKeyState)=== -"success"?$$render(consequent_7,1):$$render(alternate,-1)})}append($$anchor3,fragment_1)},$$slots:{default:!0}})}var node_16=sibling(node_13,2);{let $0=user_derived(()=>get$3(apiKeyState)==="validating");Button(node_16,{onclick:()=>{set$1(showApiKeyInput,!1),set$1(apiKeyState,"idle"),set$1(apiKeyError,"")},variant:"outline",class:"flex-1",get disabled(){return get$3($0)},children:($$anchor3,$$slotProps)=>{next$1();var text_5=text$8("Cancel");append($$anchor3,text_5)},$$slots:{default:!0}})}reset( -div_11),reset(div_5),transition(1,div_5,()=>fly,()=>({y:10,duration:300,delay:200})),append($$anchor2,div_5)};if_block(node_4,$$render=>{get$3(showApiKeyInput)&&$$render(consequent_8)})}var node_17=sibling(node_4,2);{var consequent_10=$$anchor2=>{var div_12=root_15(),node_18=child(div_12);Button(node_18,{onclick:handleRetryConnection,get disabled(){return get$3(isServerLoading)},class:"w-full",children:($$anchor3,$$slotProps)=>{var fragment_3=comment$2(),node_19=first_child(fragment_3);{var consequent_9=$$anchor4=>{ -var fragment_4=root_17(),node_20=first_child(fragment_4);Refresh_cw(node_20,{class:"h-4 w-4 animate-spin"}),next$1(),append($$anchor4,fragment_4)},alternate_1=$$anchor4=>{var fragment_5=root_18(),node_21=first_child(fragment_5);Refresh_cw(node_21,{class:"h-4 w-4"}),next$1(),append($$anchor4,fragment_5)};if_block(node_19,$$render=>{get$3(isServerLoading)?$$render(consequent_9):$$render(alternate_1,-1)})}append($$anchor3,fragment_3)},$$slots:{default:!0}}),reset(div_12),transition(1,div_12,()=>fly, -()=>({y:10,duration:300,delay:200})),append($$anchor2,div_12)};if_block(node_17,$$render=>{showRetry()&&$$render(consequent_10)})}var node_22=sibling(node_17,2);{var consequent_11=$$anchor2=>{var div_13=root_19();transition(1,div_13,()=>fly,()=>({y:10,duration:300,delay:400})),append($$anchor2,div_13)};if_block(node_22,$$render=>{showTroubleshooting()&&$$render(consequent_11)})}reset(div_1),reset(div),template_effect(()=>{set_class(div,1,`flex h-full items-center justify-center ${className()??""}`), -set_text(text2,$$props.error)}),transition(1,div_2,()=>fade,()=>({duration:300})),append($$anchor,div),pop()}var root$2=from_html('

    Connecting to Server

    ');function ServerLoadingSplash($$anchor,$$props){let className=prop( -$$props,"class",3,""),message=prop($$props,"message",3,"Initializing connection to llama.cpp server...");var div=root$2(),div_1=child(div),div_2=child(div_1),div_3=child(div_2),node2=child(div_3);Server(node2,{class:"h-8 w-8 animate-pulse text-muted-foreground"}),reset(div_3);var p2=sibling(div_3,4),text2=child(p2,!0);reset(p2),reset(div_2);var div_4=sibling(div_2,2),node_1=child(div_4);ServerStatus(node_1,{class:"justify-center"}),reset(div_4),reset(div_1),reset(div),template_effect(()=>{set_class( -div,1,`flex h-full items-center justify-center ${className()??""}`),set_text(text2,message())}),transition(1,div_2,()=>fade,()=>({duration:300})),append($$anchor,div)}function useSettingsNavigation(){const subroute=proxy({activePanel:"chat",chatSettingsRef:void 0}),isSettingsRoute=user_derived(()=>!!page$1.route.id?.startsWith("/settings"));return beforeNavigate(({to,from})=>{to?.route?.id?.startsWith("/settings")&&!from?.route?.id?.startsWith("/settings")&&(settingsReferrer.url=window.location. -hash||ROUTES.START)}),user_effect(()=>{subroute.activePanel==="settings"&&subroute.chatSettingsRef&&subroute.chatSettingsRef.reset()}),user_effect(()=>{page$1.url,subroute.activePanel="chat"}),{get panel(){return subroute},get isSettingsRoute(){return get$3(isSettingsRoute)}}}var root_5=from_html("
    "),root_2=from_html('
    '),root_1=from_html(" ",1);function _layout$2($$anchor,$$props){push$1($$props,!0);let alwaysShowSidebarOnDesktop=user_derived( -()=>config$1().alwaysShowSidebarOnDesktop),isMobile=new IsMobile,isDesktop=user_derived(()=>!isMobile.current),sidebarOpen=state$1(!1),mounted=state$1(!1),innerHeight=state$1(void 0),chatSidebar=state$1(void 0),titleUpdateDialogOpen=state$1(!1),titleUpdateCurrentTitle=state$1(""),titleUpdateNewTitle=state$1(""),titleUpdateResolve=null;const panelNav=useSettingsNavigation();function navigateToConversation(direction){const allConvs=conversations();if(allConvs.length===0)return;const currentId=page$1. -params.id;if(!currentId){goto(RouterService.chat(allConvs[direction===1?0:allConvs.length-1].id));return}const idx=allConvs.findIndex(c2=>c2.id===currentId);if(idx===-1)return;const targetIdx=idx+direction;targetIdx>=0&&targetIdxget$3(chatSidebar)?.editActiveConversation?.(),navigateToPrevConversation:()=>navigateToConversation(-1),navigateToNextConversation:()=>navigateToConversation( -1)});function checkApiKey(){const apiKey=config$1().apiKey;if((page$1.route.id==="/(chat)"||page$1.route.id==="/(chat)/chat/[id]")&&page$1.status!==401&&page$1.status!==403){const headers={"Content-Type":"application/json"};apiKey&&apiKey.trim()!==""&&(headers.Authorization=`Bearer ${apiKey.trim()}`),fetch(`${base}/props`,{headers}).then(response=>{(response.status===401||response.status===403)&&window.location.reload()}).catch(e=>{console.error("Error checking API key:",e)})}}function handleTitleUpdateCancel(){ -set$1(titleUpdateDialogOpen,!1),titleUpdateResolve&&(titleUpdateResolve(!1),titleUpdateResolve=null)}function handleTitleUpdateConfirm(){set$1(titleUpdateDialogOpen,!1),titleUpdateResolve&&(titleUpdateResolve(!0),titleUpdateResolve=null)}onMount$1(()=>{set$1(mounted,!0)}),user_effect(()=>{if(get$3(alwaysShowSidebarOnDesktop)&&get$3(isDesktop)){set$1(sidebarOpen,!0);return}}),user_effect(()=>{serverStore.props||untrack$1(()=>{serverStore.fetch()})}),user_effect(()=>{serverStore.props&&settingsStore. -syncWithServerDefaults()});let routerModelsFetched=!1;user_effect(()=>{const isRouter=isRouterMode(),modelsCount=modelsStore.models.length;isRouter&&modelsCount>0&&!routerModelsFetched&&(routerModelsFetched=!0,untrack$1(()=>{modelsStore.fetchRouterModels()}))}),user_effect(()=>{const enabledServers=mcpStore.getServers().filter(s2=>s2.enabled&&s2.url.trim());enabledServers.length>0&&untrack$1(()=>{mcpStore.runHealthChecksForServers(enabledServers,!1).catch(error2=>{console.warn("[layout] MCP heal\ -th checks failed:",error2)})})}),user_effect(()=>{checkApiKey()}),user_effect(()=>{conversationsStore.setTitleUpdateConfirmationCallback(async(currentTitle,newTitle)=>new Promise(resolve2=>{set$1(titleUpdateCurrentTitle,currentTitle,!0),set$1(titleUpdateNewTitle,newTitle,!0),titleUpdateResolve=resolve2,set$1(titleUpdateDialogOpen,!0)}))});var fragment=comment$2();event("keydown",$window,handleKeydown);var node2=first_child(fragment);component(node2,()=>Provider,($$anchor2,Tooltip_Provider)=>{Tooltip_Provider( -$$anchor2,{get delayDuration(){return TOOLTIP_DELAY_DURATION},children:($$anchor3,$$slotProps)=>{var fragment_1=root_1(),node_1=first_child(fragment_1);Mode_watcher(node_1,{});var node_2=sibling(node_1,2);Toaster(node_2,{richColors:!0});var node_3=sibling(node_2,2);DialogConversationTitleUpdate(node_3,{get currentTitle(){return get$3(titleUpdateCurrentTitle)},get newTitle(){return get$3(titleUpdateNewTitle)},onConfirm:handleTitleUpdateConfirm,onCancel:handleTitleUpdateCancel,get open(){return get$3( -titleUpdateDialogOpen)},set open($$value){set$1(titleUpdateDialogOpen,$$value,!0)}});var node_4=sibling(node_3,2);component(node_4,()=>Sidebar_provider,($$anchor4,Sidebar_Provider)=>{Sidebar_Provider($$anchor4,{get open(){return get$3(sidebarOpen)},set open($$value){set$1(sidebarOpen,$$value,!0)},children:($$anchor5,$$slotProps2)=>{var div=root_2();let styles2;var node_5=child(div);component(node_5,()=>Sidebar,($$anchor6,Sidebar_Root)=>{Sidebar_Root($$anchor6,{variant:"floating",class:"h-full",children:($$anchor7,$$slotProps3)=>{ -bind_this(SidebarNavigation($$anchor7,{}),$$value=>set$1(chatSidebar,$$value,!0),()=>get$3(chatSidebar))},$$slots:{default:!0}})});var node_6=sibling(node_5,2);{var consequent_1=$$anchor6=>{var fragment_3=comment$2(),node_7=first_child(fragment_3);{var consequent=$$anchor7=>{var div_1=root_5(),node_8=child(div_1);{let $0=user_derived(()=>get$3(sidebarOpen)?"left-[calc(var(--sidebar-width)+0.75rem)] max-md:hidden":"left-0!");component(node_8,()=>Sidebar_trigger,($$anchor8,Sidebar_Trigger)=>{Sidebar_Trigger( -$$anchor8,{get class(){return`transition-left absolute left-0 z-[900] duration-200 ease-linear ${get$3($0)??""}`},style:"translate: 1rem 1rem;"})})}reset(div_1),transition(1,div_1,()=>fade,()=>({duration:200})),append($$anchor7,div_1)};if_block(node_7,$$render=>{get$3(mounted)&&$$render(consequent)})}append($$anchor6,fragment_3)};if_block(node_6,$$render=>{!(get$3(alwaysShowSidebarOnDesktop)&&get$3(isDesktop))&&!(panelNav.isSettingsRoute&&!get$3(isDesktop))&&$$render(consequent_1)})}var node_9=sibling( -node_6,2);{var consequent_2=$$anchor6=>{DesktopIconStrip($$anchor6,{get sidebarOpen(){return get$3(sidebarOpen)},onSearchClick:()=>{get$3(chatSidebar)?.activateSearchMode&&get$3(chatSidebar).activateSearchMode(),set$1(sidebarOpen,!0)}})};if_block(node_9,$$render=>{get$3(isDesktop)&&!get$3(alwaysShowSidebarOnDesktop)&&$$render(consequent_2)})}var node_10=sibling(node_9,2);component(node_10,()=>Sidebar_inset,($$anchor6,Sidebar_Inset)=>{Sidebar_Inset($$anchor6,{class:"flex flex-1 flex-col overflow-\ -hidden",children:($$anchor7,$$slotProps3)=>{var fragment_5=comment$2(),node_11=first_child(fragment_5);snippet(node_11,()=>$$props.children??noop$3),append($$anchor7,fragment_5)},$$slots:{default:!0}})}),reset(div),template_effect(()=>styles2=set_style(div,"",styles2,{height:`${get$3(innerHeight)??""}px`})),append($$anchor5,div)},$$slots:{default:!0}})}),append($$anchor3,fragment_1)},$$slots:{default:!0}})}),bind_window_size("innerHeight",$$value=>set$1(innerHeight,$$value,!0)),append($$anchor,fragment), -pop()}const _0=Object.freeze(Object.defineProperty({__proto__:null,component:_layout$2},Symbol.toStringTag,{value:"Module"})),getStores=()=>{const stores$1=stores;return{page:{subscribe:stores$1.page.subscribe},navigating:{subscribe:stores$1.navigating.subscribe},updated:stores$1.updated}},page={subscribe(fn){return getStores().page.subscribe(fn)}};var root_3=from_html('

    ');function _error($$anchor,$$props){push$1($$props,!0);const $page=()=>store_get(page,"$page",$$stores),[$$stores,$$cleanup]=setup_stores();let error2=user_derived(()=>$page().error),status=user_derived(()=>$page().status),isApiKeyError=user_derived(()=>get$3(status)===401||get$3(status)===403||get$3(error2)?.message?.toLowerCase().includes("access denied")||get$3(error2)?.message?. -toLowerCase().includes("unauthorized")||get$3(error2)?.message?.toLowerCase().includes("invalid api key"));function handleRetry(){goto(ROUTES.START)}var fragment=comment$2();head$1("1j96wlh",$$anchor2=>{deferred_template_effect(()=>{$document.title=`Error ${get$3(status)??""} - WebUI`})});var node2=first_child(fragment);{var consequent=$$anchor2=>{{let $0=user_derived(()=>get$3(error2)?.message||"Access denied - check server permissions");ServerErrorSplash($$anchor2,{get error(){return get$3($0)}, -onRetry:handleRetry,showRetry:!1,showTroubleshooting:!1})}},alternate=$$anchor2=>{var div=root_3(),div_1=child(div),div_2=child(div_1),h1=sibling(child(div_2),2),text2=child(h1);reset(h1);var p2=sibling(h1,2),text_1=child(p2,!0);reset(p2),reset(div_2);var button=sibling(div_2,2);reset(div_1),reset(div),template_effect(()=>{set_text(text2,`Error ${get$3(status)??""}`),set_text(text_1,get$3(error2)?.message||"Something went wrong")}),delegated("click",button,()=>goto(ROUTES.START)),append($$anchor2, -div)};if_block(node2,$$render=>{get$3(isApiKeyError)?$$render(consequent):$$render(alternate,-1)})}append($$anchor,fragment),pop(),$$cleanup()}delegate(["click"]);const _1=Object.freeze(Object.defineProperty({__proto__:null,component:_error},Symbol.toStringTag,{value:"Module"}));var root$1=from_html(" ",1);function _layout$1($$anchor,$$props){push$1($$props,!0);let showCenteredEmpty=user_derived(()=>!page$1.params.id);var fragment=root$1(),node2=first_child(fragment);ChatScreen(node2,{get showCenteredEmpty(){ -return get$3(showCenteredEmpty)}});var node_1=sibling(node2,2);snippet(node_1,()=>$$props.children??noop$3),append($$anchor,fragment),pop()}const _2=Object.freeze(Object.defineProperty({__proto__:null,component:_layout$1},Symbol.toStringTag,{value:"Module"}));var root=from_html('
    ');function _layout($$anchor,$$props){push$1($$props,!0);let previousRouteId=state$1(null);user_effect( -()=>{const currentId=page$1.route.id;return()=>{set$1(previousRouteId,currentId,!0)}});function handleClose(){const prevIsSettings=get$3(previousRouteId)?.startsWith("/settings");window.history.length>1&&!prevIsSettings?history.back():goto(SETTINGS_FALLBACK_EXIT_ROUTE)}var div=root(),div_1=child(div),node2=child(div_1);ActionIcon(node2,{get icon(){return X},tooltip:"Close",onclick:handleClose}),reset(div_1);var div_2=sibling(div_1,2),node_1=child(div_2);snippet(node_1,()=>$$props.children??noop$3), -reset(div_2),reset(div),append($$anchor,div),pop()}const _3=Object.freeze(Object.defineProperty({__proto__:null,component:_layout},Symbol.toStringTag,{value:"Module"})),load$1=async({fetch:fetch2})=>{await validateApiKey(fetch2)},_page$5=Object.freeze(Object.defineProperty({__proto__:null,load:load$1},Symbol.toStringTag,{value:"Module"}));function _page$4($$anchor,$$props){push$1($$props,!0);let qParam=user_derived(()=>page$1.url.searchParams.get("q")),modelParam=user_derived(()=>page$1.url.searchParams. -get("model")),newChatParam=user_derived(()=>page$1.url.searchParams.get(NEW_CHAT_PARAM)),showModelNotAvailable=state$1(!1),requestedModelName=state$1(""),availableModelNames=user_derived(()=>modelOptions().map(m=>m.model));function clearUrlParams(){const url2=new URL(page$1.url);url2.searchParams.delete("q"),url2.searchParams.delete("model"),url2.searchParams.delete(NEW_CHAT_PARAM),replaceState(url2.toString(),{})}async function handleUrlParams(){if(await modelsStore.fetch(),get$3(modelParam)){const model=modelsStore. -findModelByName(get$3(modelParam));if(model)try{await modelsStore.selectModelById(model.id)}catch(error2){console.error("Failed to select model:",error2),set$1(requestedModelName,get$3(modelParam),!0),set$1(showModelNotAvailable,!0);return}else{set$1(requestedModelName,get$3(modelParam),!0),set$1(showModelNotAvailable,!0);return}}get$3(qParam)!==null?(await conversationsStore.createConversation(),clearUrlParams()):(get$3(modelParam)||get$3(newChatParam)==="true")&&clearUrlParams()}onMount$1(async()=>{ -if(isConversationsInitialized()||await conversationsStore.initialize(),conversationsStore.clearActiveConversation(),chatStore.clearUIState(),isRouterMode()&&modelsStore.selectedModelName&&!modelsStore.isModelLoaded(modelsStore.selectedModelName)){modelsStore.clearSelection();const first=modelOptions().find(m=>modelsStore.loadedModelIds.includes(m.model));first&&await modelsStore.selectModelById(first.id)}(get$3(qParam)!==null||get$3(modelParam)!==null||get$3(newChatParam)==="true")&&await handleUrlParams()}), -head$1("ku01nm",$$anchor2=>{deferred_template_effect(()=>{$document.title=APP_NAME})}),DialogModelNotAvailable($$anchor,{get modelName(){return get$3(requestedModelName)},get availableModels(){return get$3(availableModelNames)},get open(){return get$3(showModelNotAvailable)},set open($$value){set$1(showModelNotAvailable,$$value,!0)}}),pop()}const _4=Object.freeze(Object.defineProperty({__proto__:null,component:_page$4,universal:_page$5},Symbol.toStringTag,{value:"Module"})),load=async({fetch:fetch2})=>{ -await validateApiKey(fetch2)},_page$3=Object.freeze(Object.defineProperty({__proto__:null,load},Symbol.toStringTag,{value:"Module"}));function _page$2($$anchor,$$props){push$1($$props,!0);let chatId=user_derived(()=>page$1.params.id),currentChatId,qParam=user_derived(()=>page$1.url.searchParams.get("q")),modelParam=user_derived(()=>page$1.url.searchParams.get("model")),showModelNotAvailable=state$1(!1),requestedModelName=state$1(""),availableModelNames=user_derived(()=>modelOptions().map(m=>m.model)), -urlParamsProcessed=state$1(!1);function clearUrlParams(){const url2=new URL(page$1.url);url2.searchParams.delete("q"),url2.searchParams.delete("model"),replaceState(url2.toString(),{})}async function handleUrlParams(){if(await modelsStore.fetch(),get$3(modelParam)){const model=modelsStore.findModelByName(get$3(modelParam));if(model)try{await modelsStore.selectModelById(model.id)}catch(error2){console.error("Failed to select model:",error2),set$1(requestedModelName,get$3(modelParam),!0),set$1(showModelNotAvailable, -!0);return}else{set$1(requestedModelName,get$3(modelParam),!0),set$1(showModelNotAvailable,!0);return}}get$3(qParam)!==null?(await chatStore.sendMessage(get$3(qParam)),clearUrlParams()):get$3(modelParam)&&clearUrlParams(),set$1(urlParamsProcessed,!0)}afterNavigate(()=>{setTimeout(()=>{modelsStore.selectModelFromLastAssistantResponse()},100)}),user_effect(()=>{if(get$3(chatId)&&get$3(chatId)!==currentChatId){if(currentChatId=get$3(chatId),set$1(urlParamsProcessed,!1),activeConversation()?.id===get$3( -chatId)){(get$3(qParam)!==null||get$3(modelParam)!==null)&&!get$3(urlParamsProcessed)&&handleUrlParams();return}(async()=>await conversationsStore.loadConversation(get$3(chatId))?(chatStore.syncLoadingStateForChat(get$3(chatId)),(get$3(qParam)!==null||get$3(modelParam)!==null)&&!get$3(urlParamsProcessed)&&await handleUrlParams()):await goto(ROUTES.START))()}}),user_effect(()=>{if(typeof window<"u"){const handleBeforeUnload=()=>{isLoading()&&(console.log("Page unload detected while streaming - ab\ -orting stream"),chatStore.stopGeneration())};return window.addEventListener("beforeunload",handleBeforeUnload),()=>{window.removeEventListener("beforeunload",handleBeforeUnload)}}}),head$1("jib4ir",$$anchor2=>{deferred_template_effect($0=>{$document.title=`${$0??""} - llama.cpp`},[()=>activeConversation()?.name||"Chat"])}),DialogModelNotAvailable($$anchor,{get modelName(){return get$3(requestedModelName)},get availableModels(){return get$3(availableModelNames)},get open(){return get$3(showModelNotAvailable)}, -set open($$value){set$1(showModelNotAvailable,$$value,!0)}}),pop()}const _5=Object.freeze(Object.defineProperty({__proto__:null,component:_page$2,universal:_page$3},Symbol.toStringTag,{value:"Module"}));function _page$1($$anchor){SettingsMcpServers($$anchor,{class:"mx-auto w-full p-4 md:p-8 md:py-8"})}const _6=Object.freeze(Object.defineProperty({__proto__:null,component:_page$1},Symbol.toStringTag,{value:"Module"}));function _page($$anchor,$$props){push$1($$props,!1),onMount$1(()=>{page$1.params. -section||replaceState(RouterService.settings(SETTINGS_SECTION_SLUGS.GENERAL),{})}),init(),SettingsChat($$anchor,{get initialSection(){return page$1.params.section}}),pop()}const _7=Object.freeze(Object.defineProperty({__proto__:null,component:_page},Symbol.toStringTag,{value:"Module"})),pdf_worker_min='/**\n * @licstart The following is the entire license notice for the\n * JavaScript code in this page\n *\n * Copyright 2024 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (th\ -e "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License\ -.\n *\n * @licend The above is the entire license notice for the\n * JavaScript code in this page\n */\n/**\n * pdfjsVersion = 5.4.54\n * pdfjsBuild = 295fb3ec4\n */\nconst e=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),t=[.001,0,0,.001,0,0],a=1.35,r=.35,i=.25925925925925924,n=1,s=2,o=4,c=8,l=16,h=64,u=128,d=256,f="pdfjs_internal_editor_",g=3,p=9,m=13,b=15,y=101,w={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODI\ -FY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},x=0,S=4,k=1,C=2,v=3,F=1,T=2,O=3,M=4,D=5,R=6,N=7,E=8,L=9,j=10,_=11,U=12,X=13,q=14,H=15,W=16,z=17,$=20,G="Group",V="R",K=1,J=2,Y=4,Z=16,Q=32,ee=128,te=512,ae=1,re=2,ie=4096,ne=8192,se=32768,oe=65536,ce=131072,le=1048576,he=2097152,ue=8388608,de=16777216,fe=1,ge=2,pe=3,me=4,be=5,ye={E:"Mouse Enter",X:"Mouse Exit",D:"Mouse Down",U:"Mouse Up",Fo:"Focus",Bl:"Blur",PO:"PageOpen",PC:"PageClose"\ -,PV:"PageVisible",PI:"PageInvisible",K:"Keystroke",F:"Format",V:"Validate",C:"Calculate"},we={WC:"WillClose",WS:"WillSave",DS:"DidSave",WP:"WillPrint",DP:"DidPrint"},xe={O:"PageOpen",C:"PageClose"},Se=1,Ae=5,ke=1,Ce=2,ve=3,Fe=4,Ie=5,Te=6,Oe=7,Me=8,De=9,Be=10,Re=11,Ne=12,Ee=13,Pe=14,Le=15,je=16,_e=17,Ue=18,Xe=19,qe=20,He=21,We=22,ze=23,$e=24,Ge=25,Ve=26,Ke=27,Je=28,Ye=29,Ze=30,Qe=31,et=32,tt=33,at=34,rt=35,it=36,nt=37,st=38,ot=39,ct=40,lt=41,ht=42,ut=43,dt=44,ft=45,gt=46,pt=47,mt=48,bt=49,yt=50,w\ -t=51,xt=52,St=53,At=54,kt=55,Ct=56,vt=57,Ft=58,It=59,Tt=60,Ot=61,Mt=62,Dt=63,Bt=64,Rt=65,Nt=66,Et=67,Pt=68,Lt=69,jt=70,_t=71,Ut=72,Xt=73,qt=74,Ht=75,Wt=76,zt=77,$t=80,Gt=81,Vt=83,Kt=84,Jt=85,Yt=86,Zt=87,Qt=88,ea=89,ta=90,aa=91,ra=92,ia=93,na=94,sa=0,oa=1,ca=2,la=3,ha=1,ua=2;let da=Se;function getVerbosityLevel(){return da}function info(e){da>=Ae&&console.log(`Info: ${e}`)}function warn(e){da>=Se&&console.log(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unre\ -achable(t)}function createValidAbsoluteUrl(e,t=null,a=null){if(!e)return null;if(a&&"string"==typeof e){if(a.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\\./g);t?.length>=2&&(e=`http://${e}`)}if(a.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const r=t?URL.parse(e,t):URL.parse(e);return function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(r)?r:null}function shadow(e,t,a,r=!1){Object.define\ -Property(e,t,{value:a,enumerable:!r,configurable:!0,writable:!1});return a}const fa=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends fa{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends fa{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extend\ -s fa{constructor(e){super(e,"InvalidPDFException")}}class ResponseException extends fa{constructor(e,t,a){super(e,"ResponseException");this.status=t;this.missing=a}}class FormatError extends fa{constructor(e){super(e,"FormatError")}}class AbortException extends fa{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,a=8192;if(t>24&255,e>>16&255,e>>8&255,255&e)}function objectSize(e){return Object.keys(e).length}class FeatureTest{static get isLittleEndian(){r\ -eturn shadow(this,"isLittleEndian",function isLittleEndian(){const e=new Uint8Array(4);e[0]=1;return 1===new Uint32Array(e.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get isImageDecoderSupported(){return shadow(this,"isImageDecoderSupported","undefin\ -ed"!=typeof ImageDecoder)}static get platform(){const{platform:e,userAgent:t}=navigator;return shadow(this,"platform",{isAndroid:t.includes("Android"),isLinux:e.includes("Linux"),isMac:e.includes("Mac"),isWindows:e.includes("Win"),isFirefox:t.includes("Firefox")})}static get isCSSRoundSupported(){return shadow(this,"isCSSRoundSupported",globalThis.CSS?.supports?.("width: round(1.5px, 1px)"))}}const ga=Array.from(Array(256).keys(),(e=>e.toString(16).padStart(2,"0")));class Util{static makeHexColo\ -r(e,t,a){return`#${ga[e]}${ga[t]}${ga[a]}`}static scaleMinMax(e,t){let a;if(e[0]){if(e[0]<0){a=t[0];t[0]=t[2];t[2]=a}t[0]*=e[0];t[2]*=e[0];if(e[3]<0){a=t[1];t[1]=t[3];t[3]=a}t[1]*=e[3];t[3]*=e[3]}else{a=t[0];t[0]=t[1];t[1]=a;a=t[2];t[2]=t[3];t[3]=a;if(e[1]<0){a=t[1];t[1]=t[3];t[3]=a}t[1]*=e[1];t[3]*=e[1];if(e[2]<0){a=t[0];t[0]=t[2];t[2]=a}t[0]*=e[2];t[2]*=e[2]}t[0]+=e[4];t[1]+=e[5];t[2]+=e[4];t[3]+=e[5]}static transform(e,t){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]\ -*t[2]+e[3]*t[3],e[0]*t[4]+e[2]*t[5]+e[4],e[1]*t[4]+e[3]*t[5]+e[5]]}static applyTransform(e,t,a=0){const r=e[a],i=e[a+1];e[a]=r*t[0]+i*t[2]+t[4];e[a+1]=r*t[1]+i*t[3]+t[5]}static applyTransformToBezier(e,t,a=0){const r=t[0],i=t[1],n=t[2],s=t[3],o=t[4],c=t[5];for(let t=0;t<6;t+=2){const l=e[a+t],h=e[a+t+1];e[a+t]=l*r+h*n+o;e[a+t+1]=l*i+h*s+c}}static applyInverseTransform(e,t){const a=e[0],r=e[1],i=t[0]*t[3]-t[1]*t[2];e[0]=(a*t[3]-r*t[2]+t[2]*t[5]-t[4]*t[3])/i;e[1]=(-a*t[1]+r*t[0]+t[4]*t[1]-t[5]*t[0\ -])/i}static axialAlignedBoundingBox(e,t,a){const r=t[0],i=t[1],n=t[2],s=t[3],o=t[4],c=t[5],l=e[0],h=e[1],u=e[2],d=e[3];let f=r*l+o,g=f,p=r*u+o,m=p,b=s*h+c,y=b,w=s*d+c,x=w;if(0!==i||0!==n){const e=i*l,t=i*u,a=n*h,r=n*d;f+=a;m+=a;p+=r;g+=r;b+=e;x+=e;w+=t;y+=t}a[0]=Math.min(a[0],f,p,g,m);a[1]=Math.min(a[1],b,w,y,x);a[2]=Math.max(a[2],f,p,g,m);a[3]=Math.max(a[3],b,w,y,x)}static inverseTransform(e){const t=e[0]*e[3]-e[1]*e[2];return[e[3]/t,-e[1]/t,-e[2]/t,e[0]/t,(e[2]*e[5]-e[4]*e[3])/t,(e[4]*e[1]-e[5\ -]*e[0])/t]}static singularValueDecompose2dScale(e,t){const a=e[0],r=e[1],i=e[2],n=e[3],s=a**2+r**2,o=a*i+r*n,c=i**2+n**2,l=(s+c)/2,h=Math.sqrt(l**2-(s*c-o**2));t[0]=Math.sqrt(l+h||1);t[1]=Math.sqrt(l-h||1)}static normalizeRect(e){const t=e.slice(0);if(e[0]>e[2]){t[0]=e[2];t[2]=e[0]}if(e[1]>e[3]){t[1]=e[3];t[3]=e[1]}return t}static intersect(e,t){const a=Math.max(Math.min(e[0],e[2]),Math.min(t[0],t[2])),r=Math.min(Math.max(e[0],e[2]),Math.max(t[0],t[2]));if(a>r)return null;const i=Math.max(Math.m\ -in(e[1],e[3]),Math.min(t[1],t[3])),n=Math.min(Math.max(e[1],e[3]),Math.max(t[1],t[3]));return i>n?null:[a,i,r,n]}static pointBoundingBox(e,t,a){a[0]=Math.min(a[0],e);a[1]=Math.min(a[1],t);a[2]=Math.max(a[2],e);a[3]=Math.max(a[3],t)}static rectBoundingBox(e,t,a,r,i){i[0]=Math.min(i[0],e,a);i[1]=Math.min(i[1],t,r);i[2]=Math.max(i[2],e,a);i[3]=Math.max(i[3],t,r)}static#e(e,t,a,r,i,n,s,o,c,l){if(c<=0||c>=1)return;const h=1-c,u=c*c,d=u*c,f=h*(h*(h*e+3*c*t)+3*u*a)+d*r,g=h*(h*(h*i+3*c*n)+3*u*s)+d*o;l[0\ -]=Math.min(l[0],f);l[1]=Math.min(l[1],g);l[2]=Math.max(l[2],f);l[3]=Math.max(l[3],g)}static#t(e,t,a,r,i,n,s,o,c,l,h,u){if(Math.abs(c)<1e-12){Math.abs(l)>=1e-12&&this.#e(e,t,a,r,i,n,s,o,-h/l,u);return}const d=l**2-4*h*c;if(d<0)return;const f=Math.sqrt(d),g=2*c;this.#e(e,t,a,r,i,n,s,o,(-l+f)/g,u);this.#e(e,t,a,r,i,n,s,o,(-l-f)/g,u)}static bezierBoundingBox(e,t,a,r,i,n,s,o,c){c[0]=Math.min(c[0],e,s);c[1]=Math.min(c[1],t,o);c[2]=Math.max(c[2],e,s);c[3]=Math.max(c[3],t,o);this.#t(e,a,i,s,t,r,n,o,3*(3\ -*(a-i)-e+s),6*(e-2*a+i),3*(a-e),c);this.#t(e,a,i,s,t,r,n,o,3*(3*(r-n)-t+o),6*(t-2*r+n),3*(r-t),c)}}const pa=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,728,711,710,729,733,731,730,732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8226,8224,8225,8230,8212,8211,402,8260,8249,8250,8722,8240,8222,8220,8221,8216,8217,8218,8482,64257,64258,321,338,352,3\ -76,381,305,322,339,353,382,0,8364];function stringToPDFString(e,t=!1){if(e[0]>="ï"){let a;if("þ"===e[0]&&"ÿ"===e[1]){a="utf-16be";e.length%2==1&&(e=e.slice(0,-1))}else if("ÿ"===e[0]&&"þ"===e[1]){a="utf-16le";e.length%2==1&&(e=e.slice(0,-1))}else"ï"===e[0]&&"»"===e[1]&&"¿"===e[2]&&(a="utf-8");if(a)try{const r=new TextDecoder(a,{fatal:!0}),i=stringToBytes(e),n=r.decode(i);return t||!n.includes("\x1B")?n:n.replaceAll(/\\x1b[^\\x1b]*(?:\\x1b|$)/g,"")}catch(e){warn(`stringToPDFString: "${e}".`)}}const a=[]\ -;for(let r=0,i=e.length;rga[e])).join("")}"function"!=typeof Pro\ -mise.try&&(Promise.try=function(e,...t){return new Promise((a=>{a(e(...t))}))});"function"!=typeof Math.sumPrecise&&(Math.sumPrecise=function(e){return e.reduce(((e,t)=>e+t),0)});const ya=Symbol("CIRCULAR_REF"),wa=Symbol("EOF");let xa=Object.create(null),Sa=Object.create(null),Aa=Object.create(null);class Name{constructor(e){this.name=e}static get(e){return Sa[e]||=new Name(e)}}class Cmd{constructor(e){this.cmd=e}static get(e){return xa[e]||=new Cmd(e)}}const ka=function nonSerializableClosure()\ -{return ka};class Dict{constructor(e=null){this._map=new Map;this.xref=e;this.objId=null;this.suppressEncryption=!1;this.__nonSerializable__=ka}assignXref(e){this.xref=e}get size(){return this._map.size}get(e,t,a){let r=this._map.get(e);if(void 0===r&&void 0!==t){r=this._map.get(t);void 0===r&&void 0!==a&&(r=this._map.get(a))}return r instanceof Ref&&this.xref?this.xref.fetch(r,this.suppressEncryption):r}async getAsync(e,t,a){let r=this._map.get(e);if(void 0===r&&void 0!==t){r=this._map.get(t);v\ -oid 0===r&&void 0!==a&&(r=this._map.get(a))}return r instanceof Ref&&this.xref?this.xref.fetchAsync(r,this.suppressEncryption):r}getArray(e,t,a){let r=this._map.get(e);if(void 0===r&&void 0!==t){r=this._map.get(t);void 0===r&&void 0!==a&&(r=this._map.get(a))}r instanceof Ref&&this.xref&&(r=this.xref.fetch(r,this.suppressEncryption));if(Array.isArray(r)){r=r.slice();for(let e=0,t=r.length;e{unreachable("Should not call `set` on the empty dictionary.")};return shadow(this,"empty",e)}static merge({xref:e,dictArray:t,mergeSubDicts:a=!1}){const r=new Dict(e),i=new Map;for(const e of t)if(e instanceof Dict)for(const[t,r]of e._map){let e=i.get(t);if(void 0===e){e=[];i.set(t,e)}else if(!(a&&r instanceof Dict))continue;e.push(r)}for(co\ -nst[t,a]of i){if(1===a.length||!(a[0]instanceof Dict)){r._map.set(t,a[0]);continue}const i=new Dict(e);for(const e of a)for(const[t,a]of e._map)i._map.has(t)||i._map.set(t,a);i.size>0&&r._map.set(t,i)}i.clear();return r.size>0?r:Dict.empty}clone(){const e=new Dict(this.xref);for(const t of this.getKeys())e.set(t,this.getRaw(t));return e}delete(e){delete this._map[e]}}class Ref{constructor(e,t){this.num=e;this.gen=t}toString(){return 0===this.gen?`${this.num}R`:`${this.num}R${this.gen}`}static fr\ -omString(e){const t=Aa[e];if(t)return t;const a=/^(\\d+)R(\\d*)$/.exec(e);return a&&"0"!==a[1]?Aa[e]=new Ref(parseInt(a[1]),a[2]?parseInt(a[2]):0):null}static get(e,t){const a=0===t?`${e}R`:`${e}R${t}`;return Aa[a]||=new Ref(e,t)}}class RefSet{constructor(e=null){this._set=new Set(e?._set)}has(e){return this._set.has(e.toString())}put(e){this._set.add(e.toString())}remove(e){this._set.delete(e.toString())}[Symbol.iterator](){return this._set.values()}clear(){this._set.clear()}}class RefSetCache{co\ -nstructor(){this._map=new Map}get size(){return this._map.size}get(e){return this._map.get(e.toString())}has(e){return this._map.has(e.toString())}put(e,t){this._map.set(e.toString(),t)}putAlias(e,t){this._map.set(e.toString(),this.get(t))}[Symbol.iterator](){return this._map.values()}clear(){this._map.clear()}*values(){yield*this._map.values()}*items(){for(const[e,t]of this._map)yield[Ref.fromString(e),t]}}function isName(e,t){return e instanceof Name&&(void 0===t||e.name===t)}function isCmd(e,\ -t){return e instanceof Cmd&&(void 0===t||e.cmd===t)}function isDict(e,t){return e instanceof Dict&&(void 0===t||isName(e.get("Type"),t))}function isRefsEqual(e,t){return e.num===t.num&&e.gen===t.gen}class BaseStream{get length(){unreachable("Abstract getter `length` accessed")}get isEmpty(){unreachable("Abstract getter `isEmpty` accessed")}get isDataLoaded(){return shadow(this,"isDataLoaded",!0)}getByte(){unreachable("Abstract method `getByte` called")}getBytes(e){unreachable("Abstract method `g\ -etBytes` called")}async getImageData(e,t){return this.getBytes(e,t)}async asyncGetBytes(){unreachable("Abstract method `asyncGetBytes` called")}get isAsync(){return!1}get isAsyncDecoder(){return!1}get canAsyncDecodeImageFromBuffer(){return!1}async getTransferableImage(){return null}peekByte(){const e=this.getByte();-1!==e&&this.pos--;return e}peekBytes(e){const t=this.getBytes(e);this.pos-=t.length;return t}getUint16(){const e=this.getByte(),t=this.getByte();return-1===e||-1===t?-1:(e<<8)+t}getI\ -nt32(){return(this.getByte()<<24)+(this.getByte()<<16)+(this.getByte()<<8)+this.getByte()}getByteRange(e,t){unreachable("Abstract method `getByteRange` called")}getString(e){return bytesToString(this.getBytes(e))}skip(e){this.pos+=e||1}reset(){unreachable("Abstract method `reset` called")}moveStart(){unreachable("Abstract method `moveStart` called")}makeSubStream(e,t,a=null){unreachable("Abstract method `makeSubStream` called")}getBaseStreams(){return null}}const Ca=/^[1-9]\\.\\d$/,va=2**31-1,Fa=[\ -1,0,0,1,0,0],Ia=["ColorSpace","ExtGState","Font","Pattern","Properties","Shading","XObject"],Ta=["ExtGState","Font","Properties","XObject"];function getLookupTableFactory(e){let t;return function(){if(e){t=Object.create(null);e(t);e=null}return t}}class MissingDataException extends fa{constructor(e,t){super(`Missing data [${e}, ${t})`,"MissingDataException");this.begin=e;this.end=t}}class ParserEOFException extends fa{constructor(e){super(e,"ParserEOFException")}}class XRefEntryException extends\ - fa{constructor(e){super(e,"XRefEntryException")}}class XRefParseException extends fa{constructor(e){super(e,"XRefParseException")}}function arrayBuffersToBytes(e){const t=e.length;if(0===t)return new Uint8Array(0);if(1===t)return new Uint8Array(e[0]);let a=0;for(let r=0;r0,"The number should be a positive integer.");const a="M".repeat(e/1e3|0)+Oa[e%1e3/100|0]+Oa[10+(e%100/10|0)]+Oa[20+e%10];return t?a.toLowerCase():a}function log2(e){return e>0?Math.ceil(Math.log2(e)):0}function readInt8(e,t){return e[t]<<24>>24}function readInt16(e,t){return(e[t]<<24|e[t+1]<<16)>>16}function readUint16(e,t){return e[t]<<8|e[t+1]}function readUint32(e,t){return(e[t]\ -<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function isWhiteSpace(e){return 32===e||9===e||13===e||10===e}function isNumberArray(e,t){return Array.isArray(e)?(null===t||e.length===t)&&e.every((e=>"number"==typeof e)):ArrayBuffer.isView(e)&&!(e instanceof BigInt64Array||e instanceof BigUint64Array)&&(null===t||e.length===t)}function lookupMatrix(e,t){return isNumberArray(e,6)?e:t}function lookupRect(e,t){return isNumberArray(e,4)?e:t}function lookupNormalRect(e,t){return isNumberArray(e,4)?Util.normali\ -zeRect(e):t}function parseXFAPath(e){const t=/(.+)\\[(\\d+)\\]$/;return e.split(".").map((e=>{const a=e.match(t);return a?{name:a[1],pos:parseInt(a[2],10)}:{name:e,pos:0}}))}function escapePDFName(e){const t=[];let a=0;for(let r=0,i=e.length;r126||35===i||40===i||41===i||60===i||62===i||91===i||93===i||123===i||125===i||47===i||37===i){a"\\n"===e?"\\\\n":"\\r"===e?"\\\\r":`\\\\${e}`))}function _collectJS(e,t,a,r){if(!e)return;let i=null;if(e instanceof Ref){if(r.has(e))return;i=e;r.put(i);e=t.fetch(e)}if(Array.isArray(e))for(const i of e)_collectJS(i,t,a,r);else if(e instanceof Dict){if(isName(e.get("S"),"JavaScript")){const t=e.get("JS");let r;t instanceof BaseStream?r=t.getString():"string"==typeof t&&(r=t);r&&=stringToPDFString(r,!0).repl\ -aceAll("\\0","");r&&a.push(r.trim())}_collectJS(e.getRaw("Next"),t,a,r)}i&&r.remove(i)}function collectActions(e,t,a){const r=Object.create(null),i=getInheritableProperty({dict:t,key:"AA",stopWhenFound:!1});if(i)for(let t=i.length-1;t>=0;t--){const n=i[t];if(n instanceof Dict)for(const t of n.getKeys()){const i=a[t];if(!i)continue;const s=[];_collectJS(n.getRaw(t),e,s,new RefSet);s.length>0&&(r[i]=s)}}if(t.has("A")){const a=[];_collectJS(t.get("A"),e,a,new RefSet);a.length>0&&(r.Action=a)}return \ -objectSize(r)>0?r:null}const Ma={60:"<",62:">",38:"&",34:""",39:"'"};function*codePointIter(e){for(let t=0,a=e.length;t55295&&(a<57344||a>65533)&&t++;yield a}}function encodeToXmlString(e){const t=[];let a=0;for(let r=0,i=e.length;r55295&&(i<57\ -344||i>65533)&&r++;a=r+1}}if(0===t.length)return e;a: ${e}.`);return!1}return!0}function val\ -idateCSSFont(e){const t=new Set(["100","200","300","400","500","600","700","800","900","1000","normal","bold","bolder","lighter"]),{fontFamily:a,fontWeight:r,italicAngle:i}=e;if(!validateFontName(a,!0))return!1;const n=r?r.toString():"";e.fontWeight=t.has(n)?n:"400";const s=parseFloat(i);e.italicAngle=isNaN(s)||s<-90||s>90?"14":i.toString();return!0}function recoverJsURL(e){const t=new RegExp("^\\\\s*("+["app.launchURL","window.open","xfa.host.gotoURL"].join("|").replaceAll(".","\\\\.")+")\\\\((?:\'|\\"\ -)([^\'\\"]*)(?:\'|\\")(?:,\\\\s*(\\\\w+)\\\\)|\\\\))","i").exec(e);return t?.[2]?{url:t[2],newWindow:"app.launchURL"===t[1]&&"true"===t[3]}:null}function numberToString(e){if(Number.isInteger(e))return e.toString();const t=Math.round(100*e);return t%100==0?(t/100).toString():t%10==0?e.toFixed(1):e.toFixed(2)}function getNewAnnotationsMap(e){if(!e)return null;const t=new Map;for(const[a,r]of e){if(!a.startsWith(f))continue;let e=t.get(r.pageIndex);if(!e){e=[];t.set(r.pageIndex,e)}e.push(r)}return t.size>0?t:\ -null}function stringToAsciiOrUTF16BE(e){return null==e||function isAscii(e){if("string"!=typeof e)return!1;return!e||/^[\\x00-\\x7F]*$/.test(e)}(e)?e:stringToUTF16String(e,!0)}function stringToUTF16HexString(e){const t=[];for(let a=0,r=e.length;a>8&255],ga[255&r])}return t.join("")}function stringToUTF16String(e,t=!1){const a=[];t&&a.push("þÿ");for(let t=0,r=e.length;t>8&255),String.fromCharCod\ -e(255&r))}return a.join("")}function getRotationMatrix(e,t,a){switch(e){case 90:return[0,1,-1,0,t,0];case 180:return[-1,0,0,-1,t,a];case 270:return[0,-1,1,0,0,a];default:throw new Error("Invalid rotation")}}function getSizeInBytes(e){return Math.ceil(Math.ceil(Math.log2(1+e))/8)}class QCMS{static#a=null;static _memory=null;static _mustAddAlpha=!1;static _destBuffer=null;static _destOffset=0;static _destLength=0;static _cssColor="";static _makeHexColor=null;static get _memoryArray(){const e=this.\ -#a;return e?.byteLength?e:this.#a=new Uint8Array(this._memory.buffer)}}let Da;const Ba="undefined"!=typeof TextDecoder?new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}):{decode:()=>{throw Error("TextDecoder not available")}};"undefined"!=typeof TextDecoder&&Ba.decode();let Ra=null;function getUint8ArrayMemory0(){null!==Ra&&0!==Ra.byteLength||(Ra=new Uint8Array(Da.memory.buffer));return Ra}let Na=0;function passArray8ToWasm0(e,t){const a=t(1*e.length,1)>>>0;getUint8ArrayMemory0().set(e,a/1);Na=e.l\ -ength;return a}const Ea=Object.freeze({RGB8:0,0:"RGB8",RGBA8:1,1:"RGBA8",BGRA8:2,2:"BGRA8",Gray8:3,3:"Gray8",GrayA8:4,4:"GrayA8",CMYK:5,5:"CMYK"}),Pa=Object.freeze({Perceptual:0,0:"Perceptual",RelativeColorimetric:1,1:"RelativeColorimetric",Saturation:2,2:"Saturation",AbsoluteColorimetric:3,3:"AbsoluteColorimetric"});function __wbg_get_imports(){const e={wbg:{}};e.wbg.__wbg_copyresult_b08ee7d273f295dd=function(e,t){!function copy_result(e,t){const{_mustAddAlpha:a,_destBuffer:r,_destOffset:i,_des\ -tLength:n,_memoryArray:s}=QCMS;if(t!==n)if(a)for(let a=e,n=e+t,o=i;a>>0,t>>>0)};e.wbg.__wbg_copyrgb_d60ce17bb05d9b67=function(e){!function copy_rgb(e){const{_destBuffer:t,_destOffset:a,_memoryArray:r}=QCMS;t[a]=r[e];t[a+1]=r[e+1];t[a+2]=r[e+2]}(e>>>0)};e.wbg.__wbg_makecssRGB_893bf0cd9fdb302d=function(e){!function make_cs\ -sRGB(e){const{_memoryArray:t}=QCMS;QCMS._cssColor=QCMS._makeHexColor(t[e],t[e+1],t[e+2])}(e>>>0)};e.wbg.__wbindgen_init_externref_table=function(){const e=Da.__wbindgen_export_0,t=e.grow(4);e.set(0,void 0);e.set(t+0,void 0);e.set(t+1,null);e.set(t+2,!0);e.set(t+3,!1)};e.wbg.__wbindgen_throw=function(e,t){throw new Error(function getStringFromWasm0(e,t){e>>>=0;return Ba.decode(getUint8ArrayMemory0().subarray(e,e+t))}(e,t))};return e}function __wbg_finalize_init(e,t){Da=e.exports;__wbg_init.__wbin\ -dgen_wasm_module=t;Ra=null;Da.__wbindgen_start();return Da}async function __wbg_init(e){if(void 0!==Da)return Da;void 0!==e&&(Object.getPrototypeOf(e)===Object.prototype?({module_or_path:e}=e):console.warn("using deprecated parameters for the initialization function; pass a single object instead"));const t=__wbg_get_imports();("string"==typeof e||"function"==typeof Request&&e instanceof Request||"function"==typeof URL&&e instanceof URL)&&(e=fetch(e));const{instance:a,module:r}=await async functi\ -on __wbg_load(e,t){if("function"==typeof Response&&e instanceof Response){if("function"==typeof WebAssembly.instantiateStreaming)try{return await WebAssembly.instantiateStreaming(e,t)}catch(t){if("application/wasm"==e.headers.get("Content-Type"))throw t;console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\\n",t)}const a=await e.arrayBuffer();retu\ -rn await WebAssembly.instantiate(a,t)}{const a=await WebAssembly.instantiate(e,t);return a instanceof WebAssembly.Instance?{instance:a,module:e}:a}}(await e,t);return __wbg_finalize_init(a,r)}class ColorSpace{static#r=new Uint8ClampedArray(3);constructor(e,t){this.name=e;this.numComps=t}getRgb(e,t,a=new Uint8ClampedArray(3)){this.getRgbItem(e,t,a,0);return a}getRgbHex(e,t){const a=this.getRgb(e,t,ColorSpace.#r);return Util.makeHexColor(a[0],a[1],a[2])}getRgbItem(e,t,a,r){unreachable("Should not \ -call ColorSpace.getRgbItem")}getRgbBuffer(e,t,a,r,i,n,s){unreachable("Should not call ColorSpace.getRgbBuffer")}getOutputLength(e,t){unreachable("Should not call ColorSpace.getOutputLength")}isPassthrough(e){return!1}isDefaultDecode(e,t){return ColorSpace.isDefaultDecode(e,this.numComps)}fillRgb(e,t,a,r,i,n,s,o,c){const l=t*a;let h=null;const u=1<u&&"DeviceGray"!==this.name&&"DeviceRGB"!==this.name){const t=s<=8?new Uint\ -8Array(u):new Uint16Array(u);for(let e=0;e=.99554525?1:MathClamp(1.055*e**(1/2.4)-.055,0,1)}#b(e){return e<0?-this.#b(-e):e>8?((e+16)/116)**3:e*CalRGBCS.#d}#y(e,t,a){if(0===e[0]&&0===e[1]&&0===e[2]){a[0]=t[0];a[1]=t[1];a[2]=t[2];return}const r=this.#b(0),i=(1-r)/(1-this.#b(e[0])),n=1-i,s=(1-r)/(1-this.#b(e[1])),o=1-s,c=(1-r)/(1-this.#b(e[2]\ -)),l=1-c;a[0]=t[0]*i+n;a[1]=t[1]*s+o;a[2]=t[2]*c+l}#w(e,t,a){if(1===e[0]&&1===e[2]){a[0]=t[0];a[1]=t[1];a[2]=t[2];return}const r=a;this.#f(CalRGBCS.#n,t,r);const i=CalRGBCS.#l;this.#g(e,r,i);this.#f(CalRGBCS.#s,i,a)}#x(e,t,a){const r=a;this.#f(CalRGBCS.#n,t,r);const i=CalRGBCS.#l;this.#p(e,r,i);this.#f(CalRGBCS.#s,i,a)}#i(e,t,a,r,i){const n=MathClamp(e[t]*i,0,1),s=MathClamp(e[t+1]*i,0,1),o=MathClamp(e[t+2]*i,0,1),c=1===n?1:n**this.GR,l=1===s?1:s**this.GG,h=1===o?1:o**this.GB,u=this.MXA*c+this.MX\ -B*l+this.MXC*h,d=this.MYA*c+this.MYB*l+this.MYC*h,f=this.MZA*c+this.MZB*l+this.MZC*h,g=CalRGBCS.#h;g[0]=u;g[1]=d;g[2]=f;const p=CalRGBCS.#u;this.#w(this.whitePoint,g,p);const m=CalRGBCS.#h;this.#y(this.blackPoint,p,m);const b=CalRGBCS.#u;this.#x(CalRGBCS.#c,m,b);const y=CalRGBCS.#h;this.#f(CalRGBCS.#o,b,y);a[r]=255*this.#m(y[0]);a[r+1]=255*this.#m(y[1]);a[r+2]=255*this.#m(y[2])}getRgbItem(e,t,a,r){this.#i(e,t,a,r,1)}getRgbBuffer(e,t,a,r,i,n,s){const o=1/((1<this.amax||this.bmin>this.bmax){info("Invalid Range, falling back to defaults");this.amin=-100;this.amax=100;this.bmin=-100;this.bmax=100}}#S(e){return e>=6/29?e**3:108/841*(e-4/29)}#A(e,t,a,r){return a+e*(r-a)/t}#i(e,t,a,r,i){let n=e[t],s=e[t+1],o=e[t+2];if(!1!==a){n=this.#A(n,a,0,100);s=this.#A(s,a,this.amin,this.amax);o=this.#A(o,a,this.bmin,this.bmax)}s>this.amax?s=this.amax:sthis.bma\ -x?o=this.bmax:o{!function qcms_drop_transformer(e){Da.qcms_drop_transformer(e)}(e)}));constr\ -uctor(e,t,a){if(!IccColorSpace.isUsable)throw new Error("No ICC color space support");super(t,a);let r;switch(a){case 1:r=Ea.Gray8;this.#C=(e,t,a)=>function qcms_convert_one(e,t,a){Da.qcms_convert_one(e,t,a)}(this.#k,255*e[t],a);break;case 3:r=Ea.RGB8;this.#C=(e,t,a)=>function qcms_convert_three(e,t,a,r,i){Da.qcms_convert_three(e,t,a,r,i)}(this.#k,255*e[t],255*e[t+1],255*e[t+2],a);break;case 4:r=Ea.CMYK;this.#C=(e,t,a)=>function qcms_convert_four(e,t,a,r,i,n){Da.qcms_convert_four(e,t,a,r,i,n)}(t\ -his.#k,255*e[t],255*e[t+1],255*e[t+2],255*e[t+3],a);break;default:throw new Error(`Unsupported number of components: ${a}`)}this.#k=function qcms_transformer_from_memory(e,t,a){const r=passArray8ToWasm0(e,Da.__wbindgen_malloc),i=Na;return Da.qcms_transformer_from_memory(r,i,t,a)>>>0}(e,r,Pa.Perceptual);if(!this.#k)throw new Error("Failed to create ICC color space");IccColorSpace.#I.register(this,this.#k)}getRgbHex(e,t){this.#C(e,t,!0);return QCMS._cssColor}getRgbItem(e,t,a,r){QCMS._destBuffer=a;\ -QCMS._destOffset=r;QCMS._destLength=3;this.#C(e,t,!1);QCMS._destBuffer=null}getRgbBuffer(e,t,a,r,i,n,s){e=e.subarray(t,t+a*this.numComps);if(8!==n){const t=255/((1<=this.end?-1:this.bytes[this.pos++]}getBytes(e){const t=this.bytes,a=this.pos,r=this.end;if(!e)return t.subarray(a,r);let i=a+e;i>r&&(i=r);this.pos=i;return t.subarray(a,i)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);return this.bytes.subarray(e,t)}reset(){this.pos=this.start}moveStart(){this.start=this.pos}makeSubStream(e,t,a=null){return new Stream(this.bytes.buffer,e,t,a)}}class StringStream extends Stream{constructor(e){super(stringToBytes(\ -e))}}class NullStream extends Stream{constructor(){super(new Uint8Array(0))}}class ChunkedStream extends Stream{constructor(e,t,a){super(new Uint8Array(e),0,e,null);this.chunkSize=t;this._loadedChunks=new Set;this.numChunks=Math.ceil(e/t);this.manager=a;this.progressiveDataLength=0;this.lastSuccessfulEnsureByteChunk=-1}getMissingChunks(){const e=[];for(let t=0,a=this.numChunks;t=this.end?this.numChunks:Math.floor(t/this.chunkSize);for(let e=a;ethis.numChunks)&&t!==this.lastSuccessfulEnsureByteChunk){if(!this._loadedChunks.has(t))throw new MissingDataException(e,e+1);this.lastSuccessfulEnsureByteChunk=t}}ensureRange(e,t){if(e>=t)return;if(t<=this.progressiveDataLength)return;const a=\ -Math.floor(e/this.chunkSize);if(a>this.numChunks)return;const r=Math.min(Math.floor((t-1)/this.chunkSize)+1,this.numChunks);for(let i=a;i=this.end)return-1;e>=this.progressiveDataLength&&this.ensureByte(e);return this.byt\ -es[this.pos++]}getBytes(e){const t=this.bytes,a=this.pos,r=this.end;if(!e){r>this.progressiveDataLength&&this.ensureRange(a,r);return t.subarray(a,r)}let i=a+e;i>r&&(i=r);i>this.progressiveDataLength&&this.ensureRange(a,i);this.pos=i;return t.subarray(a,i)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);t>this.progressiveDataLength&&this.ensureRange(e,t);return this.bytes.subarray(e,t)}makeSubStream(e,t,a=null){t?e+t>this.progressiveDataLength&&this.ensureRange(e,e+t):e>=this.progressiveDa\ -taLength&&this.ensureByte(e);function ChunkedStreamSubstream(){}ChunkedStreamSubstream.prototype=Object.create(this);ChunkedStreamSubstream.prototype.getMissingChunks=function(){const e=this.chunkSize,t=Math.floor(this.start/e),a=Math.floor((this.end-1)/e)+1,r=[];for(let e=t;e{const readChunk=({value:n,done:s})=>{try{if(s){const t=arrayBuffersToBytes(r);r=null;e(t);return}i+=n.byteLength;a.isStreamingSupported&&this.onProgress({loaded:i});r.push(n);a.read().then(readChunk,t)}catch(e){t(e)}};a.read().\ -then(readChunk,t)})).then((t=>{this.aborted||this.onReceiveData({chunk:t,begin:e})}))}requestAllChunks(e=!1){if(!e){const e=this.stream.getMissingChunks();this._requestChunks(e)}return this._loadedStreamCapability.promise}_requestChunks(e){const t=this.currRequestId++,a=new Set;this._chunksNeededByRequest.set(t,a);for(const t of e)this.stream.hasChunk(t)||a.add(t);if(0===a.size)return Promise.resolve();const r=Promise.withResolvers();this._promisesByRequest.set(t,r);const i=[];for(const e of a){\ -let a=this._requestsByChunk.get(e);if(!a){a=[];this._requestsByChunk.set(e,a);i.push(e)}a.push(t)}if(i.length>0){const e=this.groupChunks(i);for(const t of e){const e=t.beginChunk*this.chunkSize,a=Math.min(t.endChunk*this.chunkSize,this.length);this.sendRequest(e,a).catch(r.reject)}}return r.promise.catch((e=>{if(!this.aborted)throw e}))}getStream(){return this.stream}requestRange(e,t){t=Math.min(t,this.length);const a=this.getBeginChunk(e),r=this.getEndChunk(t),i=[];for(let e=a;ee-t));return this._requestChunks(t)}groupChunks(e){const t=[];let a=-1,r=-1;for(let i=0,n=e.length;i=0&&r+1!==n){t.push({beginChunk:a,endChunk:r+1});a=n}i+1===e.length&&t.push({beginChunk:a,endChunk:n+1});r=n}return t}onProgress(e){this.msgHandler.send("DocPro\ -gress",{loaded:this.stream.numChunksLoaded*this.chunkSize+e.loaded,total:this.length})}onReceiveData(e){const t=e.chunk,a=void 0===e.begin,r=a?this.progressiveDataLength:e.begin,i=r+t.byteLength,n=Math.floor(r/this.chunkSize),s=i0||o.push(a)}}}if(!this.disableAutoFetch&&0===this._requestsByChunk.size){let e;if(1===this.stream.numChunksLoaded){const t=this.stream.numChunks-1;this.stream.hasChunk(t)||(e=t)}else e=this.stream.nextEmptyChunk(s);Number.isInteger(e)&&this._requestChunks([e])}for(const e of o){const t=this._promisesByRequest.get(e\ -);this._promisesByRequest.delete(e);t.resolve()}this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize,total:this.length})}onError(e){this._loadedStreamCapability.reject(e)}getBeginChunk(e){return Math.floor(e/this.chunkSize)}getEndChunk(e){return Math.floor((e-1)/this.chunkSize)+1}abort(e){this.aborted=!0;this.pdfNetworkStream?.cancelAllRequests(e);for(const t of this._promisesByRequest.values())t.reject(e)}}function convertToRGBA(e){switch(e.kind){case k:return c\ -onvertBlackAndWhiteToRGBA(e);case C:return function convertRGBToRGBA({src:e,srcPos:t=0,dest:a,destPos:r=0,width:i,height:n}){let s=0;const o=i*n*3,c=o>>2,l=new Uint32Array(e.buffer,t,c);if(FeatureTest.isLittleEndian){for(;s>>24|t<<8|4278190080;a[r+2]=t>>>16|i<<16|4278190080;a[r+3]=i>>>8|4278190080}for(let i=4*s,n=t+o;i>>8|255;a[r+2]=t<<16|i>>>16|255;a[r+3]=i<<8|255}for(let i=4*s,n=t+o;i>3,u=7&r,d=e.length;a=new Uint32Array(a.buffer);let f=0;for(let r=0;ra||t>a)return!0;const r=e*t;if(this._hasMaxArea)return r>this.MAX_AREA;if(r(this.MAX_AREA=this.#O**2)}static getReducePowerForJPX(e,t,a){const r=e*t,i=2**30/(4*a);if(!this.needsToBeResized(e,t))return r>i?Math.ceil(Math.log2(r/i)):0\ -;const{MAX_DIM:n,MAX_AREA:s}=this,o=Math.max(e/n,t/n,Math.sqrt(r/Math.min(i,s)));return Math.ceil(Math.log2(o))}static get MAX_DIM(){return shadow(this,"MAX_DIM",this._guessMax(2048,65537,0,1))}static get MAX_AREA(){this._hasMaxArea=!0;return shadow(this,"MAX_AREA",this._guessMax(this.#O,this.MAX_DIM,128,0)**2)}static set MAX_AREA(e){if(e>=0){this._hasMaxArea=!0;shadow(this,"MAX_AREA",e)}}static setOptions({canvasMaxAreaInBytes:e=-1,isImageDecoderSupported:t=!1}){this._hasMaxArea||(this.MAX_AREA\ -=e>>2);this.#M=t}static _areGoodDims(e,t){try{const a=new OffscreenCanvas(e,t),r=a.getContext("2d");r.fillRect(0,0,1,1);const i=r.getImageData(0,0,1,1).data[3];a.width=a.height=1;return 0!==i}catch{return!1}}static _guessMax(e,t,a,r){for(;e+a+1va){const e=this.#D();if(\ -e)return e}const r=this._encodeBMP();let i,n;if(await ImageResizer.canUseImageDecoder){i=new ImageDecoder({data:r,type:"image/bmp",preferAnimation:!1,transfer:[r.buffer]});n=i.decode().catch((e=>{warn(`BMP image decoding failed: ${e}`);return createImageBitmap(new Blob([this._encodeBMP().buffer],{type:"image/bmp"}))})).finally((()=>{i.close()}))}else n=createImageBitmap(new Blob([r.buffer],{type:"image/bmp"}));const{MAX_AREA:s,MAX_DIM:o}=ImageResizer,c=Math.max(t/o,a/o,Math.sqrt(t*a/s)),l=Math.m\ -ax(c,2),h=Math.round(10*(c+1.25))/10/l,u=Math.floor(Math.log2(h)),d=new Array(u+2).fill(2);d[0]=l;d.splice(-1,1,h/(1<>s,c=r>>s;let l,h=r;try{l=new Uint8Array(n)}catch{let e=Math.floor(Math.log2(n+1));for(;;)try{l=new Uint8Array(2**e-1);break}catch{e-=1}h=Math.floor((2**e-1)/(4*a));const t=a*h*4;t>s;e>3,s=a+3&-4;if(a!==s){const e=new Uint8Array(s*t);let r=0;for(let n=0,o=t*a;ni&&(r=i)}else{for(;!this.eof;)this.readBlock(t);r=this.bufferLength}this.pos=r;return this.buffer.subarray(a,r)}async getImageData(e,t){if(!this.canAsyncDecodeImageFromBuffer)return this.isAsyncDecoder?this.decodeImage(null,t):this.getBytes(e,t);const a=awa\ -it this.stream.asyncGetBytes();return this.decodeImage(a,t)}reset(){this.pos=0}makeSubStream(e,t,a=null){if(void 0===t)for(;!this.eof;)this.readBlock();else{const a=e+t;for(;this.bufferLength<=a&&!this.eof;)this.readBlock()}return new Stream(this.buffer,e,t,a)}getBaseStreams(){return this.str?this.str.getBaseStreams():null}}class StreamsSequenceStream extends DecodeStream{constructor(e,t=null){e=e.filter((e=>e instanceof BaseStream));let a=0;for(const t of e)a+=t instanceof DecodeStream?t._rawMi\ -nBufferLength:t.length;super(a);this.streams=e;this._onError=t}readBlock(){const e=this.streams;if(0===e.length){this.eof=!0;return}const t=e.shift();let a;try{a=t.getBytes()}catch(e){if(this._onError){this._onError(e,t.dict?.objId);return}throw e}const r=this.bufferLength,i=r+a.length;this.ensureBuffer(i).set(a,r);this.bufferLength=i}getBaseStreams(){const e=[];for(const t of this.streams){const a=t.getBaseStreams();a&&e.push(...a)}return e.length>0?e:null}}class ColorSpaceUtils{static parse({c\ -s:e,xref:t,resources:a=null,pdfFunctionFactory:r,globalColorSpaceCache:i,localColorSpaceCache:n,asyncIfNotCached:s=!1}){const o={xref:t,resources:a,pdfFunctionFactory:r,globalColorSpaceCache:i,localColorSpaceCache:n};let c,l,h;if(e instanceof Ref){l=e;const a=i.getByRef(l)||n.getByRef(l);if(a)return a;e=t.fetch(e)}if(e instanceof Name){c=e.name;const t=n.getByName(c);if(t)return t}try{h=this.#B(e,o)}catch(e){if(s&&!(e instanceof MissingDataException))return Promise.reject(e);throw e}if(c||l){n.s\ -et(c,l,h);l&&i.set(null,l,h)}return s?Promise.resolve(h):h}static#R(e,t){const{globalColorSpaceCache:a}=t;let r;if(e instanceof Ref){r=e;const t=a.getByRef(r);if(t)return t}const i=this.#B(e,t);r&&a.set(null,r,i);return i}static#B(e,t){const{xref:a,resources:r,pdfFunctionFactory:i,globalColorSpaceCache:n}=t;if((e=a.fetchIfRef(e))instanceof Name)switch(e.name){case"G":case"DeviceGray":return this.gray;case"RGB":case"DeviceRGB":return this.rgb;case"DeviceRGBA":return this.rgba;case"CMYK":case"Devi\ -ceCMYK":return this.cmyk;case"Pattern":return new PatternCS(null);default:if(r instanceof Dict){const a=r.get("ColorSpace");if(a instanceof Dict){const r=a.get(e.name);if(r){if(r instanceof Name)return this.#B(r,t);e=r;break}}}warn(`Unrecognized ColorSpace: ${e.name}`);return this.gray}if(Array.isArray(e)){const r=a.fetchIfRef(e[0]).name;let s,o,c,l,h,u;switch(r){case"G":case"DeviceGray":return this.gray;case"RGB":case"DeviceRGB":return this.rgb;case"CMYK":case"DeviceCMYK":return this.cmyk;case"\ -CalGray":s=a.fetchIfRef(e[1]);l=s.getArray("WhitePoint");h=s.getArray("BlackPoint");u=s.get("Gamma");return new CalGrayCS(l,h,u);case"CalRGB":s=a.fetchIfRef(e[1]);l=s.getArray("WhitePoint");h=s.getArray("BlackPoint");u=s.getArray("Gamma");const d=s.getArray("Matrix");return new CalRGBCS(l,h,u,d);case"ICCBased":const f=e[1]instanceof Ref;if(f){const t=n.getByRef(e[1]);if(t)return t}const g=a.fetchIfRef(e[1]),p=g.dict;o=p.get("N");if(IccColorSpace.isUsable)try{const t=new IccColorSpace(g.getBytes(\ -),"ICCBased",o);f&&n.set(null,e[1],t);return t}catch(t){if(t instanceof MissingDataException)throw t;warn(`ICCBased color space (${e[1]}): "${t}".`)}const m=p.getRaw("Alternate");if(m){const e=this.#R(m,t);if(e.numComps===o)return e;warn("ICCBased color space: Ignoring incorrect /Alternate entry.")}if(1===o)return this.gray;if(3===o)return this.rgb;if(4===o)return this.cmyk;break;case"Pattern":c=e[1]||null;c&&(c=this.#R(c,t));return new PatternCS(c);case"I":case"Indexed":c=this.#R(e[1],t);const \ -b=MathClamp(a.fetchIfRef(e[2]),0,255),y=a.fetchIfRef(e[3]);return new IndexedCS(c,b,y);case"Separation":case"DeviceN":const w=a.fetchIfRef(e[1]);o=Array.isArray(w)?w.length:1;c=this.#R(e[2],t);const x=i.create(e[3]);return new AlternateCS(o,c,x);case"Lab":s=a.fetchIfRef(e[1]);l=s.getArray("WhitePoint");h=s.getArray("BlackPoint");const S=s.getArray("Range");return new LabCS(l,h,S);default:warn(`Unimplemented ColorSpace object: ${r}`);return this.gray}}warn(`Unrecognized ColorSpace object: ${e}`);\ -return this.gray}static get gray(){return shadow(this,"gray",new DeviceGrayCS)}static get rgb(){return shadow(this,"rgb",new DeviceRgbCS)}static get rgba(){return shadow(this,"rgba",new DeviceRgbaCS)}static get cmyk(){if(CmykICCBasedCS.isUsable)try{return shadow(this,"cmyk",new CmykICCBasedCS)}catch{warn("CMYK fallback: DeviceCMYK")}return shadow(this,"cmyk",new DeviceCmykCS)}}class JpegError extends fa{constructor(e){super(e,"JpegError")}}class DNLMarkerError extends fa{constructor(e,t){super(e\ -,"DNLMarkerError");this.scanLines=t}}class EOIMarkerError extends fa{constructor(e){super(e,"EOIMarkerError")}}const ja=new Uint8Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]),_a=4017,Ua=799,Xa=3406,qa=2276,Ha=1567,Wa=3784,za=5793,$a=2896;function buildHuffmanTable(e,t){let a,r,i=0,n=16;for(;n>0&&!e[n-1];)n--;const s=[{children:[],index:0}];let o,c=s[0];\ -for(a=0;a0;)c=s.pop();c.index++;s.push(c);for(;s.length<=a;){s.push(o={children:[],index:0});c.children[c.index]=o.children;c=o}i++}if(a+10){g--;return f>>g&1}f=e[t++];if(255===f){const r=e[t++];if(r){if(220===r&&l){const r=readUint16(e,t+=2);t+=2;if(r>0&&r!==a.scanLines)throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data",r)}else if(217===r){if(l){const e=y*(8===a.precision?8:0);if(e>0&&Math.round(a.scanLines/e)>=5)throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, possibly caused by incorrect `scanLines` parameter",e)}throw new EOIMarkerError("Found EOI marker (0xFFD9) while pa\ -rsing scan data")}throw new JpegError(`unexpected marker ${(f<<8|r).toString(16)}`)}}g=7;return f>>>7}function decodeHuffman(e){let t=e;for(;;){t=t[readBit()];switch(typeof t){case"number":return t;case"object":continue}throw new JpegError("invalid huffman sequence")}}function receive(e){let t=0;for(;e>0;){t=t<<1|readBit();e--}return t}function receiveAndExtend(e){if(1===e)return 1===readBit()?1:-1;const t=receive(e);return t>=1<0){p--;return}let a=n;const \ -r=s;for(;a<=r;){const r=decodeHuffman(e.huffmanTableAC),i=15&r,n=r>>4;if(0===i){if(n<15){p=receive(n)+(1<>4;if(0===i)if(l<15){p=receive(l)+(1<>4;if(0===r){if(n<15)break;i+=16;continue}i+=n;const s=ja[i];e.blockData[t+s]=receiveAndExtend(r);i++}};let T,O=0;const M=1===w?r[0].blocksPerLine*r[0].blocksPerColumn:h*a.mcusPerColumn;let D,R;for(;O<=M;){const a=i?Math.min(M-O,i):M;if(a>0){for(S=0;S0?"unexpected":"excessive"} MCU data, current marker is: ${T.invalid}`);t=T.offset}if(!(T.marker>=65488&&T.marker<=65495))break;t+=2}return t-d}function quantizeAndInverse(e,t,a){const r=e.quantizationTable,i=e.blockData;let n,s,o,c,l,h,u,d,f,g,p,m,b,y,w,x,S;if(!r)throw new JpegError("missing required Quantization Table.");for(let e=0;e<64;e+=8){f=i[t+e];g=i[t+e+1];p=i[t+e+2];m=i[t+e+3];b=i[t+e+4];y=i[t+e+5];w=i[t+e+6];x=i[t+e+7];f*=r[e];if(g|p|m|b|y|w|x)\ -{g*=r[e+1];p*=r[e+2];m*=r[e+3];b*=r[e+4];y*=r[e+5];w*=r[e+6];x*=r[e+7];n=za*f+128>>8;s=za*b+128>>8;o=p;c=w;l=$a*(g-x)+128>>8;d=$a*(g+x)+128>>8;h=m<<4;u=y<<4;n=n+s+1>>1;s=n-s;S=o*Wa+c*Ha+128>>8;o=o*Ha-c*Wa+128>>8;c=S;l=l+u+1>>1;u=l-u;d=d+h+1>>1;h=d-h;n=n+c+1>>1;c=n-c;s=s+o+1>>1;o=s-o;S=l*qa+d*Xa+2048>>12;l=l*Xa-d*qa+2048>>12;d=S;S=h*Ua+u*_a+2048>>12;h=h*_a-u*Ua+2048>>12;u=S;a[e]=n+d;a[e+7]=n-d;a[e+1]=s+u;a[e+6]=s-u;a[e+2]=o+h;a[e+5]=o-h;a[e+3]=c+l;a[e+4]=c-l}else{S=za*f+512>>10;a[e]=S;a[e+1]=S;a[\ -e+2]=S;a[e+3]=S;a[e+4]=S;a[e+5]=S;a[e+6]=S;a[e+7]=S}}for(let e=0;e<8;++e){f=a[e];g=a[e+8];p=a[e+16];m=a[e+24];b=a[e+32];y=a[e+40];w=a[e+48];x=a[e+56];if(g|p|m|b|y|w|x){n=za*f+2048>>12;s=za*b+2048>>12;o=p;c=w;l=$a*(g-x)+2048>>12;d=$a*(g+x)+2048>>12;h=m;u=y;n=4112+(n+s+1>>1);s=n-s;S=o*Wa+c*Ha+2048>>12;o=o*Ha-c*Wa+2048>>12;c=S;l=l+u+1>>1;u=l-u;d=d+h+1>>1;h=d-h;n=n+c+1>>1;c=n-c;s=s+o+1>>1;o=s-o;S=l*qa+d*Xa+2048>>12;l=l*Xa-d*qa+2048>>12;d=S;S=h*Ua+u*_a+2048>>12;h=h*_a-u*Ua+2048>>12;u=S;f=n+d;x=n-d;g=\ -s+u;w=s-u;p=o+h;y=o-h;m=c+l;b=c-l;f<16?f=0:f>=4080?f=255:f>>=4;g<16?g=0:g>=4080?g=255:g>>=4;p<16?p=0:p>=4080?p=255:p>>=4;m<16?m=0:m>=4080?m=255:m>>=4;b<16?b=0:b>=4080?b=255:b>>=4;y<16?y=0:y>=4080?y=255:y>>=4;w<16?w=0:w>=4080?w=255:w>>=4;x<16?x=0:x>=4080?x=255:x>>=4;i[t+e]=f;i[t+e+8]=g;i[t+e+16]=p;i[t+e+24]=m;i[t+e+32]=b;i[t+e+40]=y;i[t+e+48]=w;i[t+e+56]=x}else{S=za*f+8192>>14;S=S<-2040?0:S>=2024?255:S+2056>>4;i[t+e]=S;i[t+e+8]=S;i[t+e+16]=S;i[t+e+24]=S;i[t+e+32]=S;i[t+e+40]=S;i[t+e+48]=S;i[t+e+5\ -6]=S}}}function buildComponentData(e,t){const a=t.blocksPerLine,r=t.blocksPerColumn,i=new Int16Array(64);for(let e=0;e=r)return null;const n=readUint16(e,t);if(n>=65472&&n<=65534)return{invalid:null,marker:n,offset:t};let s=readUint16(e,i);for(;!(s>=65472&&s<=65534);){if(++i>=r)return null;s=readUint16(e,i)}return{invalid:n.\ -toString(16),marker:s,offset:i}}function prepareComponents(e){const t=Math.ceil(e.samplesPerLine/8/e.maxH),a=Math.ceil(e.scanLines/8/e.maxV);for(const r of e.components){const i=Math.ceil(Math.ceil(e.samplesPerLine/8)*r.h/e.maxH),n=Math.ceil(Math.ceil(e.scanLines/8)*r.v/e.maxV),s=t*r.h,o=64*(a*r.v)*(s+1);r.blockData=new Int16Array(o);r.blocksPerLine=i;r.blocksPerColumn=n}e.mcusPerLine=t;e.mcusPerColumn=a}function readDataBlock(e,t){const a=readUint16(e,t);let r=(t+=2)+a-2;const i=findNextFileMar\ -ker(e,r,t);if(i?.invalid){warn("readDataBlock - incorrect length, current marker is: "+i.invalid);r=i.offset}const n=e.subarray(t,r);return{appData:n,oldOffset:t,newOffset:t+n.length}}function skipData(e,t){const a=readUint16(e,t),r=(t+=2)+a-2,i=findNextFileMarker(e,r,t);return i?.invalid?i.offset:r}class JpegImage{constructor({decodeTransform:e=null,colorTransform:t=-1}={}){this._decodeTransform=e;this._colorTransform=t}static canUseImageDecoder(e,t=-1){let a=null,r=0,i=null,n=readUint16(e,r);r\ -+=2;if(65496!==n)throw new JpegError("SOI not found");n=readUint16(e,r);r+=2;e:for(;65497!==n;){switch(n){case 65505:const{appData:t,oldOffset:s,newOffset:o}=readDataBlock(e,r);r=o;if(69===t[0]&&120===t[1]&&105===t[2]&&102===t[3]&&0===t[4]&&0===t[5]){if(a)throw new JpegError("Duplicate EXIF-blocks found.");a={exifStart:s+6,exifEnd:o}}n=readUint16(e,r);r+=2;continue;case 65472:case 65473:case 65474:i=e[r+7];break e;case 65535:255!==e[r]&&r--}r=skipData(e,r);n=readUint16(e,r);r+=2}return 4===i||3=\ -==i&&0===t?null:a||{}}parse(e,{dnlScanLines:t=null}={}){let a,r,i=0,n=null,s=null,o=0;const c=[],l=[],h=[];let u=readUint16(e,i);i+=2;if(65496!==u)throw new JpegError("SOI not found");u=readUint16(e,i);i+=2;e:for(;65497!==u;){let d,f,g;switch(u){case 65504:case 65505:case 65506:case 65507:case 65508:case 65509:case 65510:case 65511:case 65512:case 65513:case 65514:case 65515:case 65516:case 65517:case 65518:case 65519:case 65534:const{appData:p,newOffset:m}=readDataBlock(e,i);i=m;65504===u&&74==\ -=p[0]&&70===p[1]&&73===p[2]&&70===p[3]&&0===p[4]&&(n={version:{major:p[5],minor:p[6]},densityUnits:p[7],xDensity:p[8]<<8|p[9],yDensity:p[10]<<8|p[11],thumbWidth:p[12],thumbHeight:p[13],thumbData:p.subarray(14,14+3*p[12]*p[13])});65518===u&&65===p[0]&&100===p[1]&&111===p[2]&&98===p[3]&&101===p[4]&&(s={version:p[5]<<8|p[6],flags0:p[7]<<8|p[8],flags1:p[9]<<8|p[10],transformCode:p[11]});break;case 65499:const b=readUint16(e,i);i+=2;const y=b+i-2;let w;for(;i>4){if(t>>4!=1)throw new JpegError("DQT - invalid table spec");for(f=0;f<64;f++){w=ja[f];a[w]=readUint16(e,i);i+=2}}else for(f=0;f<64;f++){w=ja[f];a[w]=e[i++]}c[15&t]=a}break;case 65472:case 65473:case 65474:if(a)throw new JpegError("Only single frame JPEGs supported");i+=2;a={};a.extended=65473===u;a.progressive=65474===u;a.precision=e[i++];const x=readUint16(e,i);i+=2;a.scanLines=t||x;a.samplesPerLine=readUint16(e,i);i+=2;a.components=[];a.componentIds={};const S=e[i++];let k=0,C=0;for(d=\ -0;d>4,n=15&e[i+1];k>4?l:h)[15&t]=buildHuffmanTable(a,n)}break;case 65501:i+=2;r=readUint16(e\ -,i);i+=2;break;case 65498:const F=1==++o&&!t;i+=2;const T=e[i++],O=[];for(d=0;d>4];n.huffmanTableAC=l[15&s];O.push(n)}const M=e[i++],D=e[i++],R=e[i++];try{i+=decodeScan(e,i,a,O,r,M,D,R>>4,15&R,F)}catch(t){if(t instanceof DNLMarkerError){warn(`${t.message} -- attempting to re-parse the JPEG image.`);return this.parse(e,{dnlScanLines:t.scanLines})}if(t instanceof EOIMarkerError){warn(`${t.mes\ -sage} -- ignoring the rest of the image data.`);break e}throw t}break;case 65500:i+=4;break;case 65535:255!==e[i]&&i--;break;default:const N=findNextFileMarker(e,i-2,i-3);if(N?.invalid){warn("JpegImage.parse - unexpected data, current marker is: "+N.invalid);i=N.offset;break}if(!N||i>=e.length-1){warn("JpegImage.parse - reached the end of the image data without finding an EOI marker (0xFFD9).");break e}throw new JpegError("JpegImage.parse - unknown marker: "+u.toString(16))}u=readUint16(e,i);i+=\ -2}if(!a)throw new JpegError("JpegImage.parse - no frame data found.");this.width=a.samplesPerLine;this.height=a.scanLines;this.jfif=n;this.adobe=s;this.components=[];for(const e of a.components){const t=c[e.quantizationId];t&&(e.quantizationTable=t);this.components.push({index:e.index,output:buildComponentData(0,e),scaleX:e.h/a.maxH,scaleY:e.v/a.maxV,blocksPerLine:e.blocksPerLine,blocksPerColumn:e.blocksPerColumn})}this.numComponents=this.components.length}_getLinearizedBlockData(e,t,a=!1){const\ - r=this.width/e,i=this.height/t;let n,s,o,c,l,h,u,d,f,g,p,m=0;const b=this.components.length,y=e*t*b,w=new Uint8ClampedArray(y),x=new Uint32Array(e),S=4294967288;let k;for(u=0;u>8)+C[f+1];return w}get _isColorConversionNeeded(){return this.adobe?!!this.adobe.transformCode:3===this.numComponents?0!==this._colorTransform&&(82!==this.components[0].index||71!==this.components[1].index||66!==this.components[2].index):1===this._colorTransform}_convertYccToRgb(e){let t,a,r;for(let i=0,n=e.length;i4)throw new JpegError("Unsupported color mode");const n=this._g\ -etLinearizedBlockData(e,t,i);if(1===this.numComponents&&(a||r)){const e=n.length*(a?4:3),t=new Uint8ClampedArray(e);let r=0;if(a)!function grayToRGBA(e,t){if(FeatureTest.isLittleEndian)for(let a=0,r=e.length;a0&&(e=e.subarray(t));break}return e}decodeImage(e){if(this.eof)return this.buff\ -er;e=this.#N(e||this.bytes);const t=new JpegImage(this.jpegOptions);t.parse(e);const a=t.getData({width:this.drawWidth,height:this.drawHeight,forceRGBA:this.forceRGBA,forceRGB:this.forceRGB,isSourcePDF:!0});this.buffer=a;this.bufferLength=a.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}async getTransferableImage(){if(!await JpegStream.canUseImageDecoder)return null;const e=this.jpegOptions;if(e.decodeTransform)return null;let t;try{const a=t\ -his.canAsyncDecodeImageFromBuffer&&await this.stream.asyncGetBytes()||this.bytes;if(!a)return null;let r=this.#N(a);const i=JpegImage.canUseImageDecoder(r,e.colorTransform);if(!i)return null;if(i.exifStart){r=r.slice();r.fill(0,i.exifStart,i.exifEnd)}t=new ImageDecoder({data:r,type:"image/jpeg",preferAnimation:!1});return(await t.decode()).image}catch(e){warn(`getTransferableImage - failed: "${e}".`);return null}finally{t?.close()}}}var OpenJPEG=async function(e={}){var t,a,r=e,i=new Promise(((e\ -,r)=>{t=e;a=r})),n="./this.program",quit_=(e,t)=>{throw t},s=import.meta.url;try{new URL(".",s).href}catch{}var o,c,l,h,u,d,f=console.log.bind(console),g=console.error.bind(console),p=!1;function updateMemoryViews(){var e=o.buffer;l=new Int8Array(e);new Int16Array(e);h=new Uint8Array(e);new Uint16Array(e);u=new Int32Array(e);d=new Uint32Array(e);new Float32Array(e);new Float64Array(e);new BigInt64Array(e);new BigUint64Array(e)}var m=0,b=null;class ExitStatus{name="ExitStatus";constructor(e){this\ -.message=`Program terminated with exit(${e})`;this.status=e}}var callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(r)},y=[],addOnPostRun=e=>y.push(e),w=[],addOnPreRun=e=>w.push(e),x=!0,S=0,k={},handleException=e=>{if(e instanceof ExitStatus||"unwind"==e)return c;quit_(0,e)},keepRuntimeAlive=()=>x||S>0,_proc_exit=e=>{c=e;if(!keepRuntimeAlive()){r.onExit?.(e);p=!0}quit_(0,new ExitStatus(e))},_exit=(e,t)=>{c=e;_proc_exit(e)},callUserCallback=e=>{if(!p)try{e();(()=>{if(!keepRuntimeAlive())try{_exi\ -t(c)}catch(e){handleException(e)}})()}catch(e){handleException(e)}},growMemory=e=>{var t=(e-o.buffer.byteLength+65535)/65536|0;try{o.grow(t);updateMemoryViews();return 1}catch(e){}},C={},getEnvStrings=()=>{if(!getEnvStrings.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:n||"./this.program"};for(var t in C)void 0===C[t]?delete e[t]:e[t]=C[t];va\ -r a=[];for(var t in e)a.push(`${t}=${e[t]}`);getEnvStrings.strings=a}return getEnvStrings.strings},lengthBytesUTF8=e=>{for(var t=0,a=0;a=55296&&r<=57343){t+=4;++a}else t+=3}return t},v=[null,[],[]],F="undefined"!=typeof TextDecoder?new TextDecoder:void 0,UTF8ArrayToString=(e,t=0,a=NaN)=>{for(var r=t+a,i=t;e[i]&&!(i>=r);)++i;if(i-t>16&&e.buffer&&F)return F.decode(e.subarray(t,i));for(var n="";t>10,56320|1023&l)}}else n+=String.fromCharCode((31&s)<<6|o)}else n+=String.fromCharCode(s)}return n},printChar=(e,t)=>{var a=v[e];if(0===t||10===t){(1===e?f:g)(UTF8ArrayToString(a));a.length=0}else a.push(t)},UTF8ToString=(e,t)=>e?UTF8ArrayToString(h,e,t):"";r.noExitRuntime&&(x=r.noExitRuntime\ -);r.print&&(f=r.print);r.printErr&&(g=r.printErr);r.wasmBinary&&r.wasmBinary;r.arguments&&r.arguments;r.thisProgram&&(n=r.thisProgram);r.writeArrayToMemory=(e,t)=>{l.set(e,t)};var T={l:()=>function abort(e){r.onAbort?.(e);g(e="Aborted("+e+")");p=!0;e+=". Build with -sASSERTIONS for more info.";var t=new WebAssembly.RuntimeError(e);a(t);throw t}(""),k:()=>{x=!1;S=0},m:(e,t)=>{if(k[e]){clearTimeout(k[e].id);delete k[e]}if(!t)return 0;var a=setTimeout((()=>{delete k[e];callUserCallback((()=>M(e,per\ -formance.now())))}),t);k[e]={id:a,timeout_ms:t};return 0},g:function _copy_pixels_1(e,t){e>>=2;const a=r.imageData=new Uint8ClampedArray(t),i=u.subarray(e,e+t);a.set(i)},f:function _copy_pixels_3(e,t,a,i){e>>=2;t>>=2;a>>=2;const n=r.imageData=new Uint8ClampedArray(3*i),s=u.subarray(e,e+i),o=u.subarray(t,t+i),c=u.subarray(a,a+i);for(let e=0;e>=2;t>>=2;a>>=2;i>>=2;const s=r.imageData=new Uint8ClampedArray(4*n)\ -,o=u.subarray(e,e+n),c=u.subarray(t,t+n),l=u.subarray(a,a+n),h=u.subarray(i,i+n);for(let e=0;e{var t,a,r=h.length,i=2147483648;if((e>>>=0)>i)return!1;for(var n=1;n<=4;n*=2){var s=r*(1+.2/n);s=Math.min(s,e+100663296);var o=Math.min(i,(t=Math.max(e,s),a=65536,Math.ceil(t/a)*a));if(growMemory(o))return!0}return!1},p:(e,t)=>{var a=0,r=0;for(var i of getEnvStrings()){var n=t+a;d[e+r>>2]=n;a+=((e,t,a,r)=>{if(!(r>0))return 0;for(var i\ -=a,n=a+r-1,s=0;s=55296&&o<=57343&&(o=65536+((1023&o)<<10)|1023&e.charCodeAt(++s));if(o<=127){if(a>=n)break;t[a++]=o}else if(o<=2047){if(a+1>=n)break;t[a++]=192|o>>6;t[a++]=128|63&o}else if(o<=65535){if(a+2>=n)break;t[a++]=224|o>>12;t[a++]=128|o>>6&63;t[a++]=128|63&o}else{if(a+3>=n)break;t[a++]=240|o>>18;t[a++]=128|o>>12&63;t[a++]=128|o>>6&63;t[a++]=128|63&o}}t[a]=0;return a-i})(i,h,n,1/0)+1;r+=4}return 0},q:(e,t)=>{var a=getEnvStrings();d[e>>2]=a.length;var\ - r=0;for(var i of a)r+=lengthBytesUTF8(i)+1;d[t>>2]=r;return 0},b:e=>52,o:function _fd_seek(e,t,a,r){t=(i=t)<-9007199254740992||i>9007199254740992?NaN:Number(i);var i;return 70},c:(e,t,a,r)=>{for(var i=0,n=0;n>2],o=d[t+4>>2];t+=8;for(var c=0;c>2]=i;return 0},r:function _gray_to_rgba(e,t){e>>=2;const a=r.imageData=new Uint8ClampedArray(4*t),i=u.subarray(e,e+t);for(let e=0;e>=2;t>>=2;const i=r.imageData=new Uint8ClampedArray(4*a),n=u.subarray(e,e+a),s=u.subarray(t,t+a);for(let e=0;e>=2;t>>=2;a>>=2;const n=r.imageData=new Uint8ClampedArray(4*i),s=u.subarray(e,e+i),o=u.subarray(t,t+i),c=u.subarray(a,a+i);for(let e=0;e{r.\ -instantiateWasm(e,((e,a)=>{t(receiveInstance(e))}))}))}(),M=(O.t,r._malloc=O.u,r._free=O.v,r._jp2_decode=O.w,O.x);!function preInit(){if(r.preInit){"function"==typeof r.preInit&&(r.preInit=[r.preInit]);for(;r.preInit.length>0;)r.preInit.shift()()}}();!function run(){if(m>0)b=run;else{!function preRun(){if(r.preRun){"function"==typeof r.preRun&&(r.preRun=[r.preRun]);for(;r.preRun.length;)addOnPreRun(r.preRun.shift())}callRuntimeCallbacks(w)}();if(m>0)b=run;else if(r.setStatus){r.setStatus("Runnin\ -g...");setTimeout((()=>{setTimeout((()=>r.setStatus("")),1);doRun()}),1)}else doRun()}function doRun(){r.calledRun=!0;if(!p){!function initRuntime(){O.t()}();t(r);r.onRuntimeInitialized?.();!function postRun(){if(r.postRun){"function"==typeof r.postRun&&(r.postRun=[r.postRun]);for(;r.postRun.length;)addOnPostRun(r.postRun.shift())}callRuntimeCallbacks(y)}()}}}();return i};const Ga=OpenJPEG;class JpxError extends fa{constructor(e){super(e,"JpxError")}}class JpxImage{static#E=null;static#P=null;st\ -atic#L=null;static#v=!0;static#j=!0;static#F=null;static setOptions({handler:e,useWasm:t,useWorkerFetch:a,wasmUrl:r}){this.#v=t;this.#j=a;this.#F=r;a||(this.#P=e)}static async#_(e){const t=`${this.#F}openjpeg_nowasm_fallback.js`;let a=null;try{a=(await import(\n/*webpackIgnore: true*/\n/*@vite-ignore*/\nt)).default()}catch(e){warn(`JpxImage#getJsModule: ${e}`)}e(a)}static async#U(e,t,a){const r="openjpeg.wasm";try{this.#E||(this.#j?this.#E=await fetchBinaryData(`${this.#F}${r}`):this.#E=await this.\ -#P.sendWithPromise("FetchBinaryData",{type:"wasmFactory",filename:r}));return a((await WebAssembly.instantiate(this.#E,t)).instance)}catch(t){warn(`JpxImage#instantiateWasm: ${t}`);this.#_(e);return null}finally{this.#P=null}}static async decode(e,{numComponents:t=4,isIndexedColormap:a=!1,smaskInData:r=!1,reducePower:i=0}={}){if(!this.#L){const{promise:e,resolve:t}=Promise.withResolvers(),a=[e];this.#v?a.push(Ga({warn,instantiateWasm:this.#U.bind(this,t)})):this.#_(t);this.#L=Promise.race(a)}con\ -st n=await this.#L;if(!n)throw new JpxError("OpenJPEG failed to initialize");let s;try{const o=e.length;s=n._malloc(o);n.writeArrayToMemory(e,s);if(n._jp2_decode(s,o,t>0?t:0,!!a,!!r,i)){const{errorMessages:e}=n;if(e){delete n.errorMessages;throw new JpxError(e)}throw new JpxError("Unknown error")}const{imageData:c}=n;n.imageData=null;return c}finally{s&&n._free(s)}}static cleanup(){this.#L=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const a=t;t=e.getByte();if(65361===(a<<8|\ -t)){e.skip(4);const t=e.getInt32()>>>0,a=e.getInt32()>>>0,r=e.getInt32()>>>0,i=e.getInt32()>>>0;e.skip(16);return{width:t-r,height:a-i,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}function addState(e,t,a,r,i){let n=e;for(let e=0,a=t.length-1;e1e3){l=Math.max(l,d);f+=u+2;d=0;u=0}h.push({transform:t,x\ -:d,y:f,w:a.width,h:a.height});d+=a.width+2;u=Math.max(u,a.height)}const g=Math.max(l,d)+1,p=f+u+1,m=new Uint8Array(g*p*4),b=g<<2;for(let e=0;e=0;){t[n-4]=t[n];t[n-3]=t[n+1];t[n-2]=t[n+2];t[n-1]=t[n+3];t[n+a]=t[n+a-4];t[n+a+1]=t[n+a-3];t[n+a+2]=t[n+a-2];t[n+a+3]=t[n+a-1];n-=b}}const y={width:g,h\ -eight:p};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(g,p);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(m.buffer),g,p),0,0);y.bitmap=e.transferToImageBitmap();y.data=null}else{y.kind=v;y.data=m}a.splice(n,4*c,Zt);r.splice(n,4*c,[y,h]);return n+1}));addState(Va,[Be,Ne,Vt,Re],null,(function iterateImageMaskGroup(e,t){const a=e.fnArray,r=(t-(e.iCurr-3))%4;switch(r){case 0:return a[t]===Be;case 1:return a[t]===Ne;case 2:return a[t]===Vt;case 3:return a[t]===Re}\ -throw new Error(`iterateImageMaskGroup - invalid pos: ${r}`)}),(function foundImageMaskGroup(e,t){const a=e.fnArray,r=e.argsArray,i=e.iCurr,n=i-3,s=i-2,o=i-1;let c=Math.floor((t-n)/4);if(c<10)return t-(t-n)%4;let l,h,u=!1;const d=r[o][0],f=r[s][0],g=r[s][1],p=r[s][2],m=r[s][3];if(g===p){u=!0;l=s+4;let e=o+4;for(let t=1;t=4&&a[n-4]===a[s]&&a[n-3]===a[o]&&a[n-2]===a[c]&&a[n-1]===a[l]&&r[n-4][0]===h&&r[n-4][1]===u){d++;f-=5}let g=f+4;for(let e=1;e{const t=e.argsArray,a=t[e.iCurr-\ -1][0];if(a!==qe&&a!==He&&a!==$e&&a!==Ge&&a!==Ve&&a!==Ke)return!0;const r=t[e.iCurr-2];return 1===r[0]&&0===r[1]&&0===r[2]&&1===r[3]}),(()=>!1),((e,t)=>{const{fnArray:a,argsArray:r}=e,i=e.iCurr,n=i-3,s=i-2,o=r[i-1],c=r[s],[,[l],h]=o;if(h){Util.scaleMinMax(c,h);for(let e=0,t=l.length;e=a)break}r=(r||Va)[e[t]];if(r&&!Array.isArray(r)){n.iCurr=t;t++;if(!r.checkFn||(0,r.checkFn)(n)){i=r;r=null}else r=null}else t++}this.state=r;this.match=i;this.lastProcessed=t}flush(){for(;this.match;){const e=this.queue.fnArray.length;this.lastProcessed=(0,this.match.processFn)(this.context,e);this.match=null;this.state=null;this._optimize(\ -)}}reset(){this.state=null;this.match=null;this.lastProcessed=0}}class OperatorList{static CHUNK_SIZE=1e3;static CHUNK_SIZE_ABOUT=this.CHUNK_SIZE-5;static isOffscreenCanvasSupported=!1;constructor(e=0,t){this._streamSink=t;this.fnArray=[];this.argsArray=[];this.optimizer=!t||e&d?new NullOptimizer(this):new QueueOptimizer(this);this.dependencies=new Set;this._totalLength=0;this.weight=0;this._resolved=t?null:Promise.resolve()}static setOptions({isOffscreenCanvasSupported:e}){this.isOffscreenCanva\ -sSupported=e}get length(){return this.argsArray.length}get ready(){return this._resolved||this._streamSink.ready}get totalLength(){return this._totalLength+this.length}addOp(e,t){this.optimizer.push(e,t);this.weight++;this._streamSink&&(this.weight>=OperatorList.CHUNK_SIZE||this.weight>=OperatorList.CHUNK_SIZE_ABOUT&&(e===Re||e===et))&&this.flush()}addImageOps(e,t,a,r=!1){if(r){this.addOp(Be);this.addOp(De,[[["SMask",!1]]])}void 0!==a&&this.addOp(jt,["OC",a]);this.addOp(e,t);void 0!==a&&this.add\ -Op(_t,[]);r&&this.addOp(Re)}addDependency(e){if(!this.dependencies.has(e)){this.dependencies.add(e);this.addOp(ke,[e])}}addDependencies(e){for(const t of e)this.addDependency(t)}addOpList(e){if(e instanceof OperatorList){for(const t of e.dependencies)this.dependencies.add(t);for(let t=0,a=e.length;t>>0}function hexToStr(e,t){return 1===t?String.fromCharCode(e[0],e[1]):3===t?String.fromCharCode(e[0],e[1],e[2],e[3]):String.fromCharCode(...e.subarray(0,t+1))}function addHex(e,t,a){let r=0;for(let i=a;i>=0;i--){r+=e[i]+t[i];e[i]=255&r;r>>=8}}\ -function incHex(e,t){let a=1;for(let r=t;r>=0&&a>0;r--){a+=e[r];e[r]=255&a;a>>=8}}const Ka=16;class BinaryCMapStream{constructor(e){this.buffer=e;this.pos=0;this.end=e.length;this.tmpBuf=new Uint8Array(19)}readByte(){return this.pos>=this.end?-1:this.buffer[this.pos++]}readNumber(){let e,t=0;do{const a=this.readByte();if(a<0)throw new FormatError("unexpected EOF in bcmap");e=!(128&a);t=t<<7|127&a}while(!e);return t}readSigned(){const e=this.readNumber();return 1&e?~(e>>>1):e>>>1}readHex(e,t){e.s\ -et(this.buffer.subarray(this.pos,this.pos+t+1));this.pos+=t+1}readHexNumber(e,t){let a;const r=this.tmpBuf;let i=0;do{const e=this.readByte();if(e<0)throw new FormatError("unexpected EOF in bcmap");a=!(128&e);r[i++]=127&e}while(!a);let n=t,s=0,o=0;for(;n>=0;){for(;o<8&&r.length>0;){s|=r[--i]<>=8;o-=8}}readHexSigned(e,t){this.readHexNumber(e,t);const a=1&e[t]?255:0;let r=0;for(let i=0;i<=t;i++){r=(1&r)<<8|e[i];e[i]=r>>1^a}}readString(){const e=this.readNumber(),t=new Arra\ -y(e);for(let a=0;a=0;){const e=d>>5;if(7===e){switch(31&d){case 0:r.readString();break;case 1:n=r.readString()}continue}const a=!!(16&d),i=15&d;if(i+1>Ka)throw new Error("Bin\ -aryCMapReader.process: Invalid dataSize.");const f=1,g=r.readNumber();switch(e){case 0:r.readHex(s,i);r.readHexNumber(o,i);addHex(o,s,i);t.addCodespaceRange(i+1,hexToInt(s,i),hexToInt(o,i));for(let e=1;e=0;--i){r[a+i]=255&s;s>>=8}}}}class AsciiHexStream extends DecodeStream{constructor(e,t){t&&(t*=.5);super(t);this.str=e;this.dict=e.dict;this.\ -firstDigit=-1}readBlock(){const e=this.str.getBytes(8e3);if(!e.length){this.eof=!0;return}const t=e.length+1>>1,a=this.ensureBuffer(this.bufferLength+t);let r=this.bufferLength,i=this.firstDigit;for(const t of e){let e;if(t>=48&&t<=57)e=15&t;else{if(!(t>=65&&t<=70||t>=97&&t<=102)){if(62===t){this.eof=!0;break}continue}e=9+(15&t)}if(i<0)i=e;else{a[r++]=i<<4|e;i=-1}}if(i>=0&&this.eof){a[r++]=i<<4;i=-1}this.firstDigit=i;this.bufferLength=r}}const Ja=-1,Ya=[[-1,-1],[-1,-1],[7,8],[7,7],[6,6],[6,6],[6\ -,5],[6,5],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2\ -],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2]],Za=[[-1,-1],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[12,1984],[12,2048],[12,2112],[12,2176],[12,2240],[12,2304],[11,1856],[11,1856],[11,1920],[11,1920],[12,2368],[12,243\ -2],[12,2496],[12,2560]],Qa=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[8,29],[8,29],[8,30],[8,30],[8,45],[8,45],[8,46],[8,46],[7,22],[7,22],[7,22],[7,22],[7,23],[7,23],[7,23],[7,23],[8,47],[8,47],[8,48],[8,48],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[7,20],[7,20],[7,20],[7,20],[8,33],[8,33],[8,34],[8,34],[8,35],[8,35],[8,36],[8,36],[8,37],[8,37],[8,38],[8,38],[7,19],[7,19],[7,19],[7,19],[8,31],[8,31],[8,32],[8,32],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,12],[6,12],[6,12],[6,12],\ -[6,12],[6,12],[6,12],[6,12],[8,53],[8,53],[8,54],[8,54],[7,26],[7,26],[7,26],[7,26],[8,39],[8,39],[8,40],[8,40],[8,41],[8,41],[8,42],[8,42],[8,43],[8,43],[8,44],[8,44],[7,21],[7,21],[7,21],[7,21],[7,28],[7,28],[7,28],[7,28],[8,61],[8,61],[8,62],[8,62],[8,63],[8,63],[8,0],[8,0],[8,320],[8,320],[8,384],[8,384],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[\ -5,11],[5,11],[5,11],[5,11],[5,11],[7,27],[7,27],[7,27],[7,27],[8,59],[8,59],[8,60],[8,60],[9,1472],[9,1536],[9,1600],[9,1728],[7,18],[7,18],[7,18],[7,18],[7,24],[7,24],[7,24],[7,24],[8,49],[8,49],[8,50],[8,50],[8,51],[8,51],[8,52],[8,52],[7,25],[7,25],[7,25],[7,25],[8,55],[8,55],[8,56],[8,56],[8,57],[8,57],[8,58],[8,58],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[8,448],[8,448],[8,512],[8,512],[9,704],[9\ -,768],[8,640],[8,640],[8,576],[8,576],[9,832],[9,896],[9,960],[9,1024],[9,1088],[9,1152],[9,1216],[9,1280],[9,1344],[9,1408],[7,256],[7,256],[7,256],[7,256],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[\ -4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[4,4],[4,4],[4,4],[4,4],[4,\ -4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[5,64],[5,64],[5,64],[5,64],[5,\ -64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7]],er=[[-1,-1],[-1,-1],[12,-2],[12,-2\ -],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[11,1792],[11,1792],[12,1984],[12,1984],[12,2048],[12,2048],[12,2112],[12,2112],[12,2176],[12,2176],[12,2240],[12,2240],[12,2304],[12,2304],[11,1856],[11,1856],[11,1856],[11,1856],[11,1920],[11,1920],[11,1920],[11,1920],[12,2368],[12,2368],[12,2432],[12,\ -2432],[12,2496],[12,2496],[12,2560],[12,2560],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[12,52],[12,52],[13,640],[13,704],[13,768],[13,832],[12,55],[12,55],[12,56],[12,56],[13,1280],[13,1344],[13,1408],[13,1472],[12,59],[12,59],[12,60],[12,60],[13,1536],[13,1600],[11,24],[11,24],[11,24],[11,24],[11,25],[11,25],[11,25],[11,25],[13,1664],[13,1728],[12,320],[12,320],[12,384],[12,384],[12,448],[12,448],[13,512],[13,576],[12,53],[12,53],[12,54],[12,54],[13,896],[13,960],[13,1024\ -],[13,1088],[13,1152],[13,1216],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64]],tr=[[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[11,23],[11,23],[12,50],[12,51],[12,44],[12,45],[12,46],[12,47],[12,57],[12,58],[12,61],[12,256],[10,16],[10,16],[10,16],[10,16],[10,17],[10,17],[10,17],[10,17],[12,48],[12,49],[12,62],[12,63],[12,30],[12,31],[12,32],[12,33],[12,40],[12,41],[11,22],[11,22],[8,14],[8,14],[8,14],[8,14],[8\ -,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11\ -],[7,11],[7,11],[7,11],[7,11],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[12,128],[12,192],[12,26],[12,27],[12,28],[12,29],[11,19],[11,19],[11,20],[11,20],[12,34],[12,35],[12,36],[12,37],[12,38],[12,39],[11,21],[11,21],[12,42],[12,43],[10,0],[10,0],[10,0],[10,0],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12]]\ -,ar=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[6,9],[6,8],[5,7],[5,7],[4,6],[4,6],[4,6],[4,6],[4,5],[4,5],[4,5],[4,5],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2]];class CCITTFaxDecoder{constructor(e,t={}){if("function"!=typeof e?.next)throw new Error(\'CCITTFaxDecod\ -er - invalid "source" parameter.\');this.source=e;this.eof=!1;this.encoding=t.K||0;this.eoline=t.EndOfLine||!1;this.byteAlign=t.EncodedByteAlign||!1;this.columns=t.Columns||1728;this.rows=t.Rows||0;this.eoblock=t.EndOfBlock??!0;this.black=t.BlackIs1||!1;this.codingLine=new Uint32Array(this.columns+1);this.refLine=new Uint32Array(this.columns+2);this.codingLine[0]=this.columns;this.codingPos=0;this.row=0;this.nextLine2D=this.encoding<0;this.inputBits=0;this.inputBuf=0;this.outputBits=0;this.rowsDo\ -ne=!1;let a;for(;0===(a=this._lookBits(12));)this._eatBits(1);1===a&&this._eatBits(12);if(this.encoding>0){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}}readNextChar(){if(this.eof)return-1;const e=this.refLine,t=this.codingLine,a=this.columns;let r,i,n,s,o;if(0===this.outputBits){this.rowsDone&&(this.eof=!0);if(this.eof)return-1;this.err=!1;let n,o,c;if(this.nextLine2D){for(s=0;t[s]=64);do{o+=c=this._getWhiteCode()}while(c>=64)}else{do{n+=c=this._getWhiteCode()}while(c>=64);do{o+=c=this._getBlackCode()}while(c>=64)}this._addPixels(t[this.codingPos]+n,i);t[this.codingPos]0?--r:++r;for(;e[r]<=t[this.codingPos]&&e[r]0?--r:++r;for(;e[r]<=t[this.codingPos]&&e[r]0?--r:++r;for(;e[r]<=t[this.codingPos]&&e[r]=64);else do{n+=c=this._getWhite\ -Code()}while(c>=64);this._addPixels(t[this.codingPos]+n,i);i^=1}}let l=!1;this.byteAlign&&(this.inputBits&=-8);if(this.eoblock||this.row!==this.rows-1){n=this._lookBits(12);if(this.eoline)for(;n!==Ja&&1!==n;){this._eatBits(1);n=this._lookBits(12)}else for(;0===n;){this._eatBits(1);n=this._lookBits(12)}if(1===n){this._eatBits(12);l=!0}else n===Ja&&(this.eof=!0)}else this.rowsDone=!0;if(!this.eof&&this.encoding>0&&!this.rowsDone){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}if(this.eoblock&\ -&l&&this.byteAlign){n=this._lookBits(12);if(1===n){this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}if(this.encoding>=0)for(s=0;s<4;++s){n=this._lookBits(12);1!==n&&info("bad rtc code: "+n);this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}}this.eof=!0}}else if(this.err&&this.eoline){for(;;){n=this._lookBits(13);if(n===Ja){this.eof=!0;return-1}if(n>>1==1)break;this._eatBits(1)}this._eatBits(12);if(this.encoding>0){this._eatBits(1);this.nextLine2D=!(\ -1&n)}}this.outputBits=t[0]>0?t[this.codingPos=0]:t[this.codingPos=1];this.row++}if(this.outputBits>=8){o=1&this.codingPos?0:255;this.outputBits-=8;if(0===this.outputBits&&t[this.codingPos]n){o<<=n;1&this.codingPos||(o|=255>>8-n);this.outputBits-=n;n=0}else{o<<=this.o\ -utputBits;1&this.codingPos||(o|=255>>8-this.outputBits);n-=this.outputBits;this.outputBits=0;if(t[this.codingPos]0){o<<=n;n=0}}}while(n)}this.black&&(o^=255);return o}_addPixels(e,t){const a=this.codingLine;let r=this.codingPos;if(e>a[r]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&r^t&&++r;a[r]=e}this.codingPos=r}_addPixelsNeg(e,t){const a=this.codingLine;let r=this.codingPos;if(\ -e>a[r]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&r^t&&++r;a[r]=e}else if(e0&&e=i){const t=a[e-i];if(t[0]===r){this._eatBits(r);return[!0,t[1],!0]}}}return[!1,0,!1]}_getTwoDimCode(){let e,t=0;if(this.eoblock){t=this._lookBits(7);e=Ya[t];\ -if(e?.[0]>0){this._eatBits(e[0]);return e[1]}}else{const e=this._findTableCode(1,7,Ya);if(e[0]&&e[2])return e[1]}info("Bad two dim code");return Ja}_getWhiteCode(){let e,t=0;if(this.eoblock){t=this._lookBits(12);if(t===Ja)return 1;e=t>>5?Qa[t>>3]:Za[t];if(e[0]>0){this._eatBits(e[0]);return e[1]}}else{let e=this._findTableCode(1,9,Qa);if(e[0])return e[1];e=this._findTableCode(11,12,Za);if(e[0])return e[1]}info("bad white code");this._eatBits(1);return 1}_getBlackCode(){let e,t;if(this.eoblock){e=\ -this._lookBits(13);if(e===Ja)return 1;t=e>>7?!(e>>9)&&e>>7?tr[(e>>1)-64]:ar[e>>7]:er[e];if(t[0]>0){this._eatBits(t[0]);return t[1]}}else{let e=this._findTableCode(2,6,ar);if(e[0])return e[1];e=this._findTableCode(7,12,tr,64);if(e[0])return e[1];e=this._findTableCode(10,13,er);if(e[0])return e[1]}info("bad black code");this._eatBits(1);return 1}_lookBits(e){let t;for(;this.inputBits>16-e;this.in\ -putBuf=this.inputBuf<<8|t;this.inputBits+=8}return this.inputBuf>>this.inputBits-e&65535>>16-e}_eatBits(e){(this.inputBits-=e)<0&&(this.inputBits=0)}}class CCITTFaxStream extends DecodeStream{constructor(e,t,a){super(t);this.str=e;this.dict=e.dict;a instanceof Dict||(a=Dict.empty);const r={next:()=>e.getByte()};this.ccittFaxDecoder=new CCITTFaxDecoder(r,{K:a.get("K"),EndOfLine:a.get("EndOfLine"),EncodedByteAlign:a.get("EncodedByteAlign"),Columns:a.get("Columns"),Rows:a.get("Rows"),EndOfBlock:a.g\ -et("EndOfBlock"),BlackIs1:a.get("BlackIs1")})}readBlock(){for(;!this.eof;){const e=this.ccittFaxDecoder.readNextChar();if(-1===e){this.eof=!0;return}this.ensureBuffer(this.bufferLength+1);this.buffer[this.bufferLength++]=e}}}const rr=new Int32Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),ir=new Int32Array([3,4,5,6,7,8,9,10,65547,65549,65551,65553,131091,131095,131099,131103,196643,196651,196659,196667,262211,262227,262243,262259,327811,327843,327875,327907,258,258,258]),nr=new Int32Arr\ -ay([1,2,3,4,65541,65543,131081,131085,196625,196633,262177,262193,327745,327777,393345,393409,459009,459137,524801,525057,590849,591361,657409,658433,724993,727041,794625,798721,868353,876545]),sr=[new Int32Array([459008,524368,524304,524568,459024,524400,524336,590016,459016,524384,524320,589984,524288,524416,524352,590048,459012,524376,524312,589968,459028,524408,524344,590032,459020,524392,524328,59e4,524296,524424,524360,590064,459010,524372,524308,524572,459026,524404,524340,590024,459018,5\ -24388,524324,589992,524292,524420,524356,590056,459014,524380,524316,589976,459030,524412,524348,590040,459022,524396,524332,590008,524300,524428,524364,590072,459009,524370,524306,524570,459025,524402,524338,590020,459017,524386,524322,589988,524290,524418,524354,590052,459013,524378,524314,589972,459029,524410,524346,590036,459021,524394,524330,590004,524298,524426,524362,590068,459011,524374,524310,524574,459027,524406,524342,590028,459019,524390,524326,589996,524294,524422,524358,590060,4590\ -15,524382,524318,589980,459031,524414,524350,590044,459023,524398,524334,590012,524302,524430,524366,590076,459008,524369,524305,524569,459024,524401,524337,590018,459016,524385,524321,589986,524289,524417,524353,590050,459012,524377,524313,589970,459028,524409,524345,590034,459020,524393,524329,590002,524297,524425,524361,590066,459010,524373,524309,524573,459026,524405,524341,590026,459018,524389,524325,589994,524293,524421,524357,590058,459014,524381,524317,589978,459030,524413,524349,590042,\ -459022,524397,524333,590010,524301,524429,524365,590074,459009,524371,524307,524571,459025,524403,524339,590022,459017,524387,524323,589990,524291,524419,524355,590054,459013,524379,524315,589974,459029,524411,524347,590038,459021,524395,524331,590006,524299,524427,524363,590070,459011,524375,524311,524575,459027,524407,524343,590030,459019,524391,524327,589998,524295,524423,524359,590062,459015,524383,524319,589982,459031,524415,524351,590046,459023,524399,524335,590014,524303,524431,524367,590\ -078,459008,524368,524304,524568,459024,524400,524336,590017,459016,524384,524320,589985,524288,524416,524352,590049,459012,524376,524312,589969,459028,524408,524344,590033,459020,524392,524328,590001,524296,524424,524360,590065,459010,524372,524308,524572,459026,524404,524340,590025,459018,524388,524324,589993,524292,524420,524356,590057,459014,524380,524316,589977,459030,524412,524348,590041,459022,524396,524332,590009,524300,524428,524364,590073,459009,524370,524306,524570,459025,524402,524338\ -,590021,459017,524386,524322,589989,524290,524418,524354,590053,459013,524378,524314,589973,459029,524410,524346,590037,459021,524394,524330,590005,524298,524426,524362,590069,459011,524374,524310,524574,459027,524406,524342,590029,459019,524390,524326,589997,524294,524422,524358,590061,459015,524382,524318,589981,459031,524414,524350,590045,459023,524398,524334,590013,524302,524430,524366,590077,459008,524369,524305,524569,459024,524401,524337,590019,459016,524385,524321,589987,524289,524417,52\ -4353,590051,459012,524377,524313,589971,459028,524409,524345,590035,459020,524393,524329,590003,524297,524425,524361,590067,459010,524373,524309,524573,459026,524405,524341,590027,459018,524389,524325,589995,524293,524421,524357,590059,459014,524381,524317,589979,459030,524413,524349,590043,459022,524397,524333,590011,524301,524429,524365,590075,459009,524371,524307,524571,459025,524403,524339,590023,459017,524387,524323,589991,524291,524419,524355,590055,459013,524379,524315,589975,459029,52441\ -1,524347,590039,459021,524395,524331,590007,524299,524427,524363,590071,459011,524375,524311,524575,459027,524407,524343,590031,459019,524391,524327,589999,524295,524423,524359,590063,459015,524383,524319,589983,459031,524415,524351,590047,459023,524399,524335,590015,524303,524431,524367,590079]),9],or=[new Int32Array([327680,327696,327688,327704,327684,327700,327692,327708,327682,327698,327690,327706,327686,327702,327694,0,327681,327697,327689,327705,327685,327701,327693,327709,327683,327699,32\ -7691,327707,327687,327703,327695,0]),5];class FlateStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict;const a=e.getByte(),r=e.getByte();if(-1===a||-1===r)throw new FormatError(`Invalid header in flate stream: ${a}, ${r}`);if(8!=(15&a))throw new FormatError(`Unknown compression method in flate stream: ${a}, ${r}`);if(((a<<8)+r)%31!=0)throw new FormatError(`Bad FCHECK in flate stream: ${a}, ${r}`);if(32&r)throw new FormatError(`FDICT bit set in flate stream: ${a}, ${\ -r}`);this.codeSize=0;this.codeBuf=0}async getImageData(e,t){const a=await this.asyncGetBytes();return a?a.length<=e?a:a.subarray(0,e):this.getBytes(e)}async asyncGetBytes(){this.str.reset();const e=this.str.getBytes();try{const{readable:t,writable:a}=new DecompressionStream("deflate"),r=a.getWriter();await r.ready;r.write(e).then((async()=>{await r.ready;await r.close()})).catch((()=>{}));const i=[];let n=0;for await(const e of t){i.push(e);n+=e.byteLength}const s=new Uint8Array(n);let o=0;for(c\ -onst e of i){s.set(e,o);o+=e.byteLength}return s}catch{this.str=new Stream(e,2,e.length,this.str.dict);this.reset();return null}}get isAsync(){return!0}getBits(e){const t=this.str;let a,r=this.codeSize,i=this.codeBuf;for(;r>e;this.codeSize=r-=e;return a}getCode(e){const t=this.str,a=e[0],r=e[1];let i,n=this.codeSize,s=this.codeBuf;for(;n>16,l=65535&o;if(c<1||n>c;this.codeSize=n-c;return l}generateHuffmanTable(e){const t=e.length;let a,r=0;for(a=0;ar&&(r=e[a]);const i=1<>=1}for(a=e;a>=1;if(0===t){let t;if(-1===(t=r.getByte())){this.#X("Bad block header in flate stream");return}let a=t;if(-1===(t=r.getByte())){this.#X("Bad block header in flate stream");return}a|=t<<8;if(-1===(t=r.getByte())){this.#X("Bad block header in flate stream");return}let i=t;if(-1===(t=r.getByte())){this.#X("Bad block header in flate stream");return}i|=t<<8;if(i!==(65535&~a)&&(0!==a||0!==i))throw new FormatError("Bad unc\ -ompressed block length in flate stream");this.codeBuf=0;this.codeSize=0;const n=this.bufferLength,s=n+a;e=this.ensureBuffer(s);this.bufferLength=s;if(0===a)-1===r.peekByte()&&(this.eof=!0);else{const t=r.getBytes(a);e.set(t,n);t.length0;)h[o++]=f}i=this.generateHuffmanTable(h.subarray(0,e));n=this.generateHuffmanTable(h.subarray(e,l))}}e=this.buffer;let s=e?e.length:0,o=this.bufferLength;for(;;){let t=this.getCode(i);if(t<256){if(o+1>=s){e=this.ensureBuffer(o+1);s=\ -e.length}e[o++]=t;continue}if(256===t){this.bufferLength=o;return}t-=257;t=ir[t];let r=t>>16;r>0&&(r=this.getBits(r));a=(65535&t)+r;t=this.getCode(n);t=nr[t];r=t>>16;r>0&&(r=this.getBits(r));const c=(65535&t)+r;if(o+a>=s){e=this.ensureBuffer(o+a);s=e.length}for(let t=0;t>9&127;this.clow=this.clow<<7&65535;this.ct-=7;this.a=32768}byteIn(){const e=this.data;let t=this.bp;if(255===e[t])if(e[t+1]>143){this.clow+=65280;this.ct=8}else{t++;this.clow+=e[t]<<9;this.ct=7;this.bp=t}else{t++;\ -this.clow+=t65535){this.chigh+=this.clow>>16;this.clow&=65535}}readBit(e,t){let a=e[t]>>1,r=1&e[t];const i=cr[a],n=i.qe;let s,o=this.a-n;if(this.chigh>15&1;this.clow=this.\ -clow<<1&65535;this.ct--}while(!(32768&o));this.a=o;e[t]=a<<1|r;return s}}class Jbig2Error extends fa{constructor(e){super(e,"Jbig2Error")}}class ContextCache{getContexts(e){return e in this?this[e]:this[e]=new Int8Array(65536)}}class DecodingContext{constructor(e,t,a){this.data=e;this.start=t;this.end=a}get decoder(){return shadow(this,"decoder",new ArithmeticDecoder(this.data,this.start,this.end))}get contextCache(){return shadow(this,"contextCache",new ContextCache)}}function decodeInteger(e,t\ -,a){const r=e.getContexts(t);let i=1;function readBits(e){let t=0;for(let n=0;n>>0}const n=readBits(1),s=readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(32)+4436:readBits(12)+340:readBits(8)+84:readBits(6)+20:readBits(4)+4:readBits(2);let o;0===n?o=s:s>0&&(o=-s);return o>=-2147483648&&o<=va?o:null}function decodeIAID(e,t,a){const r=e.getContexts("IAID");let i=1;for(let e=0;ee.y-t.y||e.x-t.x));const h=l.length,u=new Int8Array(h),d=new Int8Array(h),f=[];let g,p,m=0,b=0,y=0,w=0;for(p=0;p=v&&E=F){q=q<<1&m;for(p=0;p=0&&j=0){_=D[L][j];_&&(q|=_<=e?l<<=1:l=l<<1|S[o][c]}for(f=0;f=w||c<0||c>=y?l<<=1:l=l<<1|r[o][c]}const g=k.readBit(C,l);t[s]=g}}return S}function decodeTextRegion(e,t,a,r,i,n,s,o,c,l,h,\ -u,d,f,g,p,m,b,y){if(e&&t)throw new Jbig2Error("refinement with Huffman is not supported");const w=[];let x,S;for(x=0;x1&&(i=e?y.readBits(b):decodeInteger(C,"IAIT",k));const n=s*v+i,F=e?f.symbolIDTabl\ -e.decode(y):decodeIAID(C,k,c),T=t&&(e?y.readBit():decodeInteger(C,"IARI",k));let O=o[F],M=O[0].length,D=O.length;if(T){const e=decodeInteger(C,"IARDW",k),t=decodeInteger(C,"IARDH",k);M+=e;D+=t;O=decodeRefinement(M,D,g,O,(e>>1)+decodeInteger(C,"IARDX",k),(t>>1)+decodeInteger(C,"IARDY",k),!1,p,m)}let R=0;l?1&u?R=D-1:r+=D-1:u>1?r+=M-1:R=M-1;const N=n-(1&u?0:D-1),E=r-(2&u?M-1:0);let L,j,_;if(l)for(L=0;L>5&7;const c=[31&s];let l=t+6;if(7===s){o=536870911&readUint32(e,l-1);l+=3;let t=o+7>>3;c[0]=e[l++];for(;--t>0;)c.push(e[l++])}else if(5===s||6===s)throw new Jbig2Error("invalid referred-to flags");a.retainBits=c;let h=4;a.number<=256?h=1:a.number<=65536&&(h=2);const u=[];let d,f;for(d=0;d>>24&255;n[3]=t.height>>16&255;n[4]=t.height>>8&255;n[5]=255&t.height;for(d=l,f=e.length;d>2&3;e.huffmanDWSelector=t>>4&3;e.bitmapSizeSelector=t>>6&1;e.aggregationInstancesSelector=t>>7&1;e.bitmapCodingContextUsed=!!(256&t);e.bitmapCodingContextRetained=!!(512&\ -t);e.template=t>>10&3;e.refinementTemplate=t>>12&1;l+=2;if(!e.huffman){c=0===e.template?4:1;s=[];for(o=0;o>2&3;h.stripSize=1<>4&3;h.transposed=!!(64&u);h.combinationOperator=u>>7&3;h.defaultPixelValue=u>>9&1;h.dsOffset=u<<17>>27;h.refinementTemplate=u>>15&1;if(h.huffman){const e=readUint16(r,l);l+=2;h.huffmanFS=3&e;h.huffmanDS=e>>2&3;h.huffmanDT=e>>4&3;h.huffmanRefinementDW=e>>6&3;h.huffmanRefinementDH=e>>8&3;h.huffmanRefinementDX=e>>10&3;h.huffmanRefinementDY=e>>12&3;h.huffmanRefi\ -nementSizeSelector=!!(16384&e)}if(h.refinement&&!h.refinementTemplate){s=[];for(o=0;o<2;o++){s.push({x:readInt8(r,l),y:readInt8(r,l+1)});l+=2}h.refinementAt=s}h.numberOfSymbolInstances=readUint32(r,l);l+=4;n=[h,a.referredTo,r,l,i];break;case 16:const d={},f=r[l++];d.mmr=!!(1&f);d.template=f>>1&3;d.patternWidth=r[l++];d.patternHeight=r[l++];d.maxPatternIndex=readUint32(r,l);l+=4;n=[d,a.number,r,l,i];break;case 22:case 23:const g={};g.info=readRegionSegmentInformation(r,l);l+=gr;const p=r[l++];g.m\ -mr=!!(1&p);g.template=p>>1&3;g.enableSkip=!!(8&p);g.combinationOperator=p>>4&7;g.defaultPixelValue=p>>7&1;g.gridWidth=readUint32(r,l);l+=4;g.gridHeight=readUint32(r,l);l+=4;g.gridOffsetX=4294967295&readUint32(r,l);l+=4;g.gridOffsetY=4294967295&readUint32(r,l);l+=4;g.gridVectorX=readUint16(r,l);l+=2;g.gridVectorY=readUint16(r,l);l+=2;n=[g,a.referredTo,r,l,i];break;case 38:case 39:const m={};m.info=readRegionSegmentInformation(r,l);l+=gr;const b=r[l++];m.mmr=!!(1&b);m.template=b>>1&3;m.prediction=\ -!!(8&b);if(!m.mmr){c=0===m.template?4:1;s=[];for(o=0;o>2&1;y.combinationOperator=w>>3&3;y.requiresBuffer=!!(32&w);y.combinationOperatorOverride=!!(64&w);n=[\ -y];break;case 49:case 50:case 51:case 62:break;case 53:n=[a.number,r,l,i];break;default:throw new Jbig2Error(`segment type ${a.typeName}(${a.type}) is not implemented`)}const h="on"+a.typeName;h in t&&t[h].apply(t,n)}function processSegments(e,t){for(let a=0,r=e.length;a>3,a=new Uint8ClampedArray(t*e.height);e.defaultPixelValue&&a.fill(255);this.buffer=a}drawBitmap(e,t){const a=\ -this.currentPageInfo,r=e.width,i=e.height,n=a.width+7>>3,s=a.combinationOperatorOverride?e.combinationOperator:a.combinationOperator,o=this.buffer,c=128>>(7&e.x);let l,h,u,d,f=e.y*n+(e.x>>3);switch(s){case 0:for(l=0;l>=1;if(!u){u=128;d++}}f+=n}break;case 2:for(l=0;l>=1;if(!u){u=128;d++}}f+=n}break;default:throw new Jbig2Error(`operator ${s} is not supported`)}}onImmediateGenericRegion(e,t,\ -a,r){const i=e.info,n=new DecodingContext(t,a,r),s=decodeBitmap(e.mmr,i.width,i.height,e.template,e.prediction,null,e.at,n);this.drawBitmap(i,s)}onImmediateLosslessGenericRegion(){this.onImmediateGenericRegion(...arguments)}onSymbolDictionary(e,t,a,r,i,n){let s,o;if(e.huffman){s=function getSymbolDictionaryHuffmanTables(e,t,a){let r,i,n,s,o=0;switch(e.huffmanDHSelector){case 0:case 1:r=getStandardTable(e.huffmanDHSelector+4);break;case 3:r=getCustomHuffmanTable(o,t,a);o++;break;default:throw new\ - Jbig2Error("invalid Huffman DH selector")}switch(e.huffmanDWSelector){case 0:case 1:i=getStandardTable(e.huffmanDWSelector+2);break;case 3:i=getCustomHuffmanTable(o,t,a);o++;break;default:throw new Jbig2Error("invalid Huffman DW selector")}if(e.bitmapSizeSelector){n=getCustomHuffmanTable(o,t,a);o++}else n=getStandardTable(1);s=e.aggregationInstancesSelector?getCustomHuffmanTable(o,t,a):getStandardTable(1);return{tableDeltaHeight:r,tableDeltaWidth:i,tableBitmapSize:n,tableAggregateInstances:s}}(\ -e,a,this.customTables);o=new Reader(r,i,n)}let c=this.symbols;c||(this.symbols=c={});const l=[];for(const e of a){const t=c[e];t&&l.push(...t)}const h=new DecodingContext(r,i,n);c[t]=function decodeSymbolDictionary(e,t,a,r,i,n,s,o,c,l,h,u){if(e&&t)throw new Jbig2Error("symbol refinement with Huffman is not supported");const d=[];let f=0,g=log2(a.length+r);const p=h.decoder,m=h.contextCache;let b,y;if(e){b=getStandardTable(1);y=[];g=Math.max(g,1)}for(;d.length1)w=decodeTextRegion(e,t,r,f,0,i,1,a.concat(d),g,0,0,1,0,n,c,l,h,0,u);else{const e=decodeIAID(m,p,g),t=decodeInteger(m,"IARDX",p),i=decodeInteger(m,"IARDY",p);w=decodeRefinement(r,f,c,e=32){let a,r,s;switch(t){case 32:if(0===e)throw new Jbig2Error("no previous value in symbol ID table");r=i.readBits(2)+3;a=n[e-1].prefixLength;break;case 33:r=i.readBits(3)+3;a=0;break;case 34:r=i.readBits(7)+11;a=0;break;default:throw new Jbig2Error("invalid code length in symbol ID table")}for(s=0;s=0;m--){O=e?decodeMMRBitmap(T,c,l,!0):decodeBitmap(!1,c,l,a,!1,null,v,g);F[m]=O}for(M=0;M=0;b--){R^=F[b][M][D];N|=R<>8;j=u+M*d-D*f>>8;if(L>=0&&L+S<=r&&j>=0&&j+k<=i)for(m=0;m=i)){U=p[t];_=E[m];for(b=0;b=0&&e>1&7),c=1+(r>>4&7),l=[];let h,u,d=i;do{h=s.readBits(o);u=s.readBits(c);l.push(new HuffmanLine([d,h,u,0]));d+=1<>t&1;if(t<=0)this.children[a]=new HuffmanTreeNode(e);else{let r=this.children[a];r||(this.children[a]=r=new HuffmanTreeNode(null));r.buildTree(e,t-1)}}decodeNode(e){if(this.isLeaf){if(this.isOOB)return null;const t=e.readBits(this.rangeLength);return this.rangeLow+(this.isLowerRange?-t:t)}const t=this.children[e.readBit()];if(!t)throw new Jbig2Error("invalid Huffman data");return t.decod\ -eNode(e)}}class HuffmanTable{constructor(e,t){t||this.assignPrefixCodes(e);this.rootNode=new HuffmanTreeNode(null);for(let t=0,a=e.length;t0&&this.rootNode.buildTree(a,a.prefixLength-1)}}decode(e){return this.rootNode.decodeNode(e)}assignPrefixCodes(e){const t=e.length;let a=0;for(let r=0;r=this.end)throw new Jbig2Error("end of data while reading bit");this.currentByte=this.data[this.position++];this.shift=7}const e=this.currentByte>>this.shift&1;this.shift--;return e}readBits(e){let t,a=0;for(t=e-1;t>=0;t--)a|=this.readBit()<=this.end?-1:this.data[this.position++]}}function getCustomHuffmanTable(e,t,a){let r=0;for(let i=0,n=t.length;i>a&1;a--}}if(r&&!o){const e=5;for(let t=0;t>>t&(1<0;if(e<256){d[0]=e;f=1}else{if(!(e>=258)){if(256===e){h=9;s=258;f=0;continue}this.eof=!0;delete this.lzwState;break}if(e=0;t--){d[t]=o[a];a=l[a]}}else d[f++]=d[0]}if(i){l[s]=u;c[s]=c[u]+1;o[s]=d[0];s++;h=s+n&s+n-1?h:0|Math.min(Math.log(s+n)/.6931471805599453+1,12)}u=e\ -;g+=f;if(r15))throw new FormatError(`Unsupported predictor: ${r}`);this.readBlock=2===r?this.readBlockTiff:this.readBlockPng;this.str\ -=e;this.dict=e.dict;const i=this.colors=a.get("Colors")||1,n=this.bits=a.get("BPC","BitsPerComponent")||8,s=this.columns=a.get("Columns")||1;this.pixBytes=i*n+7>>3;this.rowBytes=s*i*n+7>>3;return this}readBlockTiff(){const e=this.rowBytes,t=this.bufferLength,a=this.ensureBuffer(t+e),r=this.bits,i=this.colors,n=this.str.getBytes(e);this.eof=!n.length;if(this.eof)return;let s,o=0,c=0,l=0,h=0,u=t;if(1===r&&1===i)for(s=0;s>1;e^=e>>2;e^=e>>4;o=(1&e)<<7;a[u++]=e}else if(8===r\ -){for(s=0;s>8&255;a[u++]=255&e}}else{const e=new Uint8Array(i+1),u=(1<>l-r)&u;l-=r;c=c<=8){a[f++]=c>>h-8&255;h-=8}}h>0&&(a[f++]=(c<<8-h)+(o&(1<<8-h)-1))}this\ -.bufferLength+=e}readBlockPng(){const e=this.rowBytes,t=this.pixBytes,a=this.str.getByte(),r=this.str.getBytes(e);this.eof=!r.length;if(this.eof)return;const i=this.bufferLength,n=this.ensureBuffer(i+e);let s=n.subarray(i-e,i);0===s.length&&(s=new Uint8Array(e));let o,c,l,h=i;switch(a){case 0:for(o=0;o>1)+r[\ -o];for(;o>1)+r[o]&255;h++}break;case 4:for(o=0;o0){const e=this.str.getBytes(r);t.set(e,a);a+=r}}else{r=257-r;t=this.ensureBuffer(a+r+1);t.fill(e[1],a,a+r);a+=r}this.bufferLength=a}}class Parser{constructor({lexer:e,xref:t,allowStreams:a=!1,recoveryMode:r=!1}){this.lexer=e;this.xref=t;this.allowStreams=a;this.recoveryMode=r;this.imageCache=Object.create(null);this._imageId=0;this.refill()}refill(){this.buf1=this.\ -lexer.getObj();this.buf2=this.lexer.getObj()}shift(){if(this.buf2 instanceof Cmd&&"ID"===this.buf2.cmd){this.buf1=this.buf2;this.buf2=null}else{this.buf1=this.buf2;this.buf2=this.lexer.getObj()}}tryShift(){try{this.shift();return!0}catch(e){if(e instanceof MissingDataException)throw e;return!1}}getObj(e=null){const t=this.buf1;this.shift();if(t instanceof Cmd)switch(t.cmd){case"BI":return this.makeInlineImage(e);case"[":const a=[];for(;!isCmd(this.buf1,"]")&&this.buf1!==wa;)a.push(this.getObj(e)\ -);if(this.buf1===wa){if(this.recoveryMode)return a;throw new ParserEOFException("End of file inside array.")}this.shift();return a;case"<<":const r=new Dict(this.xref);for(;!isCmd(this.buf1,">>")&&this.buf1!==wa;){if(!(this.buf1 instanceof Name)){info("Malformed dictionary: key must be a name object");this.shift();continue}const t=this.buf1.name;this.shift();if(this.buf1===wa)break;r.set(t,this.getObj(e))}if(this.buf1===wa){if(this.recoveryMode)return r;throw new ParserEOFException("End of file \ -inside dictionary.")}if(isCmd(this.buf2,"stream"))return this.allowStreams?this.makeStream(r,e):r;this.shift();return r;default:return t}if(Number.isInteger(t)){if(Number.isInteger(this.buf1)&&isCmd(this.buf2,"R")){const e=Ref.get(t,this.buf1);this.shift();this.shift();return e}return t}return"string"==typeof t&&e?e.decryptString(t):t}findDefaultInlineStreamEnd(e){const{knownCommands:t}=this.lexer,a=e.pos;let r,i,n=0;for(;-1!==(r=e.getByte());)if(0===n)n=69===r?1:0;else if(1===n)n=73===r?2:0;els\ -e if(32===r||10===r||13===r){i=e.pos;const a=e.peekBytes(15),s=a.length;if(0===s)break;for(let e=0;e127))){n=0;break}}if(2!==n)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const o=new Lexer(new Stream(e.peekBytes(75)),t);o._hexStringWarn=()=>{};let c=0;for(;;){const e=o.getObj();if(e===wa){n=0;break}if(e instanceof Cmd){const a=t[e.cmd];if(!a){n=0;break}if(a.variableArgs?c<=a.num\ -Args:c===a.numArgs)break;c=0}else c++}if(2===n)break}else n=0;if(-1===r){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(i){warn(\'... trying to recover by using the last "EI" occurrence.\');e.skip(-(e.pos-i))}}let s=4;e.skip(-s);r=e.peekByte();e.skip(s);isWhiteSpace(r)||s--;return e.pos-s-a}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let a,r,i=!1;for(;-1!==(a=e.getByte());)if(255===a){switch(e.getByte()){case 0:break;case 255:e.skip(-1);bre\ -ak;case 217:i=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:r=e.getUint16();r>2?e.skip(r-2):e.skip(-2)}if(i)break}const n=e.pos-t;if(-1===a){warn("Inline DCTDecode image stream: EOI marker not fo\ -und, searching for /EI/ instead.");e.skip(-n);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return n}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte());)if(126===a){const t=e.pos;a=e.peekByte();for(;isWhiteSpace(a);){e.skip();a=e.peekByte()}if(62===a){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const r=e.pos-t;if(-1===a){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ inst\ -ead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte())&&62!==a;);const r=e.pos-t;if(-1===a){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}inlineStreamSkipEI(e){let t,a=0;for(;-1!==(t=e.getByte());)if(0===a)a=69===t?1:0;else if(1===a)a=73==\ -=t?2:0;else if(2===a)break}makeInlineImage(e){const t=this.lexer,a=t.stream,r=Object.create(null);let i;for(;!isCmd(this.buf1,"ID")&&this.buf1!==wa;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===wa)break;r[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(i=a.pos-t.beginInlineImagePos);const n=this.xref.fetchIfRef(r.F||r.Filter);let s;if(n instanceof Name)s=n.name;else if(Array.isArray(n)){const e=th\ -is.xref.fetchIfRef(n[0]);e instanceof Name&&(s=e.name)}const o=a.pos;let c,l;switch(s){case"DCT":case"DCTDecode":c=this.findDCTDecodeInlineStreamEnd(a);break;case"A85":case"ASCII85Decode":c=this.findASCII85DecodeInlineStreamEnd(a);break;case"AHx":case"ASCIIHexDecode":c=this.findASCIIHexDecodeInlineStreamEnd(a);break;default:c=this.findDefaultInlineStreamEnd(a)}if(c<1e3&&i>0){const e=a.pos;a.pos=t.beginInlineImagePos;l=function getInlineImageCacheKey(e){const t=[],a=e.length;let r=0;for(;r=r){let r=!1;for(const e of i){const t=e.length;let i=0;for(;i=n){r=!0;break}if(i>=t){if(isWhiteSpace(s[c+o+i])){info(`Found "${bytesToStri\ -ng([...a,...e])}" when searching for endstream command.`);r=!0}break}}if(r){t.pos+=c;return t.pos-e}}c++}t.pos+=o}return-1}makeStream(e,t){const a=this.lexer;let r=a.stream;a.skipToNextLine();const i=r.pos-1;let n=e.get("Length");if(!Number.isInteger(n)){info(`Bad length "${n&&n.toString()}" in stream.`);n=0}r.pos=i+n;a.nextChar();if(this.tryShift()&&isCmd(this.buf2,"endstream"))this.shift();else{n=this.#q(i);if(n<0)throw new FormatError("Missing endstream command.");a.nextChar();this.shift();th\ -is.shift()}this.shift();r=r.makeSubStream(i,n,e);t&&(r=t.createStream(r,n));r=this.filter(r,e,n);r.dict=e;return r}filter(e,t,a){let r=t.get("F","Filter"),i=t.get("DP","DecodeParms");if(r instanceof Name){Array.isArray(i)&&warn("/DecodeParms should not be an Array, when /Filter is a Name.");return this.makeFilter(e,r.name,a,i)}let n=a;if(Array.isArray(r)){const t=r,a=i;for(let s=0,o=t.length;s=48&&e<=57?15&e:e>=65&&e<=70||e>=97&&e<=102?9+(15&e):-1}class Lexer{constructor(e,t=null){this.stream=e;this.nextChar();this.strBuf=[];this.knownCommands=t;this._hexStringNumWarn=0;this.beginInlineImagePos=-1}nextChar(){return this.currentChar=this.stream.getByte()}peekChar(){return this.stream.peekByte()}getNumber(){let e=this.currentChar,t=!1,a=0,r=1;if(45===e){r=-1;e=this.nextChar();45===e&&\ -(e=this.nextChar())}else 43===e&&(e=this.nextChar());if(10===e||13===e)do{e=this.nextChar()}while(10===e||13===e);if(46===e){a=10;e=this.nextChar()}if(e<48||e>57){const t=`Invalid number: ${String.fromCharCode(e)} (charCode ${e})`;if(isWhiteSpace(e)||40===e||60===e||-1===e){info(`Lexer.getNumber - "${t}".`);return 0}throw new FormatError(t)}let i=e-48,n=0,s=1;for(;(e=this.nextChar())>=0;)if(e>=48&&e<=57){const r=e-48;if(t)n=10*n+r;else{0!==a&&(a*=10);i=10*i+r}}else if(46===e){if(0!==a)break;a=1}\ -else if(45===e)warn("Badly formatted number: minus sign in the middle");else{if(69!==e&&101!==e)break;e=this.peekChar();if(43===e||45===e){s=45===e?-1:1;this.nextChar()}else if(e<48||e>57)break;t=!0}0!==a&&(i/=a);t&&(i*=10**(s*n));return r*i}getString(){let e=1,t=!1;const a=this.strBuf;a.length=0;let r=this.nextChar();for(;;){let i=!1;switch(0|r){case-1:warn("Unterminated string");t=!0;break;case 40:++e;a.push("(");break;case 41:if(0==--e){this.nextChar();t=!0}else a.push(")");break;case 92:r=th\ -is.nextChar();switch(r){case-1:warn("Unterminated string");t=!0;break;case 110:a.push("\\n");break;case 114:a.push("\\r");break;case 116:a.push("\\t");break;case 98:a.push("\\b");break;case 102:a.push("\\f");break;case 92:case 40:case 41:a.push(String.fromCharCode(r));break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:let e=15&r;r=this.nextChar();i=!0;if(r>=48&&r<=55){e=(e<<3)+(15&r);r=this.nextChar();if(r>=48&&r<=55){i=!1;e=(e<<3)+(15&r)}}a.push(String.fromCharCode(e));break;case \ -13:10===this.peekChar()&&this.nextChar();break;case 10:break;default:a.push(String.fromCharCode(r))}break;default:a.push(String.fromCharCode(r))}if(t)break;i||(r=this.nextChar())}return a.join("")}getName(){let e,t;const a=this.strBuf;a.length=0;for(;(e=this.nextChar())>=0&&!mr[e];)if(35===e){e=this.nextChar();if(mr[e]){warn("Lexer_getName: NUMBER SIGN (#) should be followed by a hexadecimal number.");a.push("#");break}const r=toHexDigit(e);if(-1!==r){t=e;e=this.nextChar();const i=toHexDigit(e);\ -if(-1===i){warn(`Lexer_getName: Illegal digit (${String.fromCharCode(e)}) in hexadecimal number.`);a.push("#",String.fromCharCode(t));if(mr[e])break;a.push(String.fromCharCode(e));continue}a.push(String.fromCharCode(r<<4|i))}else a.push("#",String.fromCharCode(e))}else a.push(String.fromCharCode(e));a.length>127&&warn(`Name token is longer than allowed by the spec: ${a.length}`);return Name.get(a.join(""))}_hexStringWarn(e){5!=this._hexStringNumWarn++?this._hexStringNumWarn>5||warn(`getHexString\ - - ignoring invalid character: ${e}`):warn("getHexString - ignoring additional invalid characters.")}getHexString(){const e=this.strBuf;e.length=0;let t=this.currentChar,a=-1,r=-1;this._hexStringNumWarn=0;for(;;){if(t<0){warn("Unterminated hex string");break}if(62===t){this.nextChar();break}if(1!==mr[t]){r=toHexDigit(t);if(-1===r)this._hexStringWarn(t);else if(-1===a)a=r;else{e.push(String.fromCharCode(a<<4|r));a=-1}t=this.nextChar()}else t=this.nextChar()}-1!==a&&e.push(String.fromCharCode(a<<4\ -));return e.join("")}getObj(){let e=!1,t=this.currentChar;for(;;){if(t<0)return wa;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(1!==mr[t])break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return this.getNumber();case 40:return this.getString();case 47:return this.getName();case 91:this.nextChar();return Cmd.get("[");case 93:this.nextChar();return Cmd.get("]");case 60:t=this.nextChar();if(60===t){\ -this.nextChar();return Cmd.get("<<")}return this.getHexString();case 62:t=this.nextChar();if(62===t){this.nextChar();return Cmd.get(">>")}return Cmd.get(">");case 123:this.nextChar();return Cmd.get("{");case 125:this.nextChar();return Cmd.get("}");case 41:this.nextChar();throw new FormatError(`Illegal character: ${t}`)}let a=String.fromCharCode(t);if(t<32||t>127){const e=this.peekChar();if(e>=32&&e<=127){this.nextChar();return Cmd.get(a)}}const r=this.knownCommands;let i=void 0!==r?.[a];for(;(t=\ -this.nextChar())>=0&&!mr[t];){const e=a+String.fromCharCode(t);if(i&&void 0===r[e])break;if(128===a.length)throw new FormatError(`Command token too long: ${a.length}`);a=e;i=void 0!==r?.[a]}if("true"===a)return!0;if("false"===a)return!1;if("null"===a)return null;"BI"===a&&(this.beginInlineImagePos=this.stream.pos);return Cmd.get(a)}skipToNextLine(){let e=this.currentChar;for(;e>=0;){if(13===e){e=this.nextChar();10===e&&this.nextChar();break}if(10===e){this.nextChar();break}e=this.nextChar()}}}cl\ -ass Linearization{static create(e){function getInt(e,t,a=!1){const r=e.get(t);if(Number.isInteger(r)&&(a?r>=0:r>0))return r;throw new Error(`The "${t}" parameter in the linearization dictionary is invalid.`)}const t=new Parser({lexer:new Lexer(e),xref:null}),a=t.getObj(),r=t.getObj(),i=t.getObj(),n=t.getObj();let s,o;if(!(Number.isInteger(a)&&Number.isInteger(r)&&isCmd(i,"obj")&&n instanceof Dict&&"number"==typeof(s=n.get("Linearized"))&&s>0))return null;if((o=getInt(n,"L"))!==e.length)throw new\ - Error(\'The "L" parameter in the linearization dictionary does not equal the stream length.\');return{length:o,hints:function getHints(e){const t=e.get("H");let a;if(Array.isArray(t)&&(2===(a=t.length)||4===a)){for(let e=0;e0))throw new Error(`Hint (${e}) in the linearization dictionary is invalid.`)}return t}throw new Error("Hint array in the linearization dictionary is invalid.")}(n),objectNumberFirst:getInt(n,"O"),endFirst:getInt(n,"E"),numPages\ -:getInt(n,"N"),mainXRefEntriesOffset:getInt(n,"T"),pageFirst:n.has("P")?getInt(n,"P",!0):0}}}const br=["Adobe-GB1-UCS2","Adobe-CNS1-UCS2","Adobe-Japan1-UCS2","Adobe-Korea1-UCS2","78-EUC-H","78-EUC-V","78-H","78-RKSJ-H","78-RKSJ-V","78-V","78ms-RKSJ-H","78ms-RKSJ-V","83pv-RKSJ-H","90ms-RKSJ-H","90ms-RKSJ-V","90msp-RKSJ-H","90msp-RKSJ-V","90pv-RKSJ-H","90pv-RKSJ-V","Add-H","Add-RKSJ-H","Add-RKSJ-V","Add-V","Adobe-CNS1-0","Adobe-CNS1-1","Adobe-CNS1-2","Adobe-CNS1-3","Adobe-CNS1-4","Adobe-CNS1-5","A\ -dobe-CNS1-6","Adobe-GB1-0","Adobe-GB1-1","Adobe-GB1-2","Adobe-GB1-3","Adobe-GB1-4","Adobe-GB1-5","Adobe-Japan1-0","Adobe-Japan1-1","Adobe-Japan1-2","Adobe-Japan1-3","Adobe-Japan1-4","Adobe-Japan1-5","Adobe-Japan1-6","Adobe-Korea1-0","Adobe-Korea1-1","Adobe-Korea1-2","B5-H","B5-V","B5pc-H","B5pc-V","CNS-EUC-H","CNS-EUC-V","CNS1-H","CNS1-V","CNS2-H","CNS2-V","ETHK-B5-H","ETHK-B5-V","ETen-B5-H","ETen-B5-V","ETenms-B5-H","ETenms-B5-V","EUC-H","EUC-V","Ext-H","Ext-RKSJ-H","Ext-RKSJ-V","Ext-V","GB-EUC\ --H","GB-EUC-V","GB-H","GB-V","GBK-EUC-H","GBK-EUC-V","GBK2K-H","GBK2K-V","GBKp-EUC-H","GBKp-EUC-V","GBT-EUC-H","GBT-EUC-V","GBT-H","GBT-V","GBTpc-EUC-H","GBTpc-EUC-V","GBpc-EUC-H","GBpc-EUC-V","H","HKdla-B5-H","HKdla-B5-V","HKdlb-B5-H","HKdlb-B5-V","HKgccs-B5-H","HKgccs-B5-V","HKm314-B5-H","HKm314-B5-V","HKm471-B5-H","HKm471-B5-V","HKscs-B5-H","HKscs-B5-V","Hankaku","Hiragana","KSC-EUC-H","KSC-EUC-V","KSC-H","KSC-Johab-H","KSC-Johab-V","KSC-V","KSCms-UHC-H","KSCms-UHC-HW-H","KSCms-UHC-HW-V","KSC\ -ms-UHC-V","KSCpc-EUC-H","KSCpc-EUC-V","Katakana","NWP-H","NWP-V","RKSJ-H","RKSJ-V","Roman","UniCNS-UCS2-H","UniCNS-UCS2-V","UniCNS-UTF16-H","UniCNS-UTF16-V","UniCNS-UTF32-H","UniCNS-UTF32-V","UniCNS-UTF8-H","UniCNS-UTF8-V","UniGB-UCS2-H","UniGB-UCS2-V","UniGB-UTF16-H","UniGB-UTF16-V","UniGB-UTF32-H","UniGB-UTF32-V","UniGB-UTF8-H","UniGB-UTF8-V","UniJIS-UCS2-H","UniJIS-UCS2-HW-H","UniJIS-UCS2-HW-V","UniJIS-UCS2-V","UniJIS-UTF16-H","UniJIS-UTF16-V","UniJIS-UTF32-H","UniJIS-UTF32-V","UniJIS-UTF8-H"\ -,"UniJIS-UTF8-V","UniJIS2004-UTF16-H","UniJIS2004-UTF16-V","UniJIS2004-UTF32-H","UniJIS2004-UTF32-V","UniJIS2004-UTF8-H","UniJIS2004-UTF8-V","UniJISPro-UCS2-HW-V","UniJISPro-UCS2-V","UniJISPro-UTF8-V","UniJISX0213-UTF32-H","UniJISX0213-UTF32-V","UniJISX02132004-UTF32-H","UniJISX02132004-UTF32-V","UniKS-UCS2-H","UniKS-UCS2-V","UniKS-UTF16-H","UniKS-UTF16-V","UniKS-UTF32-H","UniKS-UTF32-V","UniKS-UTF8-H","UniKS-UTF8-V","V","WP-Symbol"],yr=2**24-1;class CMap{constructor(e=!1){this.codespaceRanges=[\ -[],[],[],[]];this.numCodespaceRanges=0;this._map=[];this.name="";this.vertical=!1;this.useCMap=null;this.builtInCMap=e}addCodespaceRange(e,t,a){this.codespaceRanges[e-1].push(t,a);this.numCodespaceRanges++}mapCidRange(e,t,a){if(t-e>yr)throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE.");for(;e<=t;)this._map[e++]=a++}mapBfRange(e,t,a){if(t-e>yr)throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE.");const r=a.length-1;for(;e<=t;){this._map[e++]=a;const t=a.charCodeAt(r)+\ -1;t>255?a=a.substring(0,r-1)+String.fromCharCode(a.charCodeAt(r-1)+1)+"\\0":a=a.substring(0,r)+String.fromCharCode(t)}}mapBfRangeToArray(e,t,a){if(t-e>yr)throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.");const r=a.length;let i=0;for(;e<=t&&i>>0;const s=i[n];for(let e=0,t=s.length;e=t&&r<=i){a.charcode=r;a.length=n+1;return}}}a.charcode=0;a.length=1}getCharCodeLength(e){const t=this.codespaceRanges;for(let a=0,r=t.length;\ -a=i&&e<=n)return a+1}}return 1}get length(){return this._map.length}get isIdentityCMap(){if("Identity-H"!==this.name&&"Identity-V"!==this.name)return!1;if(65536!==this._map.length)return!1;for(let e=0;e<65536;e++)if(this._map[e]!==e)return!1;return!0}}class IdentityCMap extends CMap{constructor(e,t){super();this.vertical=e;this.addCodespaceRange(t,0,65535)}mapCidRange(e,t,a){unreachable("should not call mapCidRange")}\ -mapBfRange(e,t,a){unreachable("should not call mapBfRange")}mapBfRangeToArray(e,t,a){unreachable("should not call mapBfRangeToArray")}mapOne(e,t){unreachable("should not call mapCidOne")}lookup(e){return Number.isInteger(e)&&e<=65535?e:void 0}contains(e){return Number.isInteger(e)&&e<=65535}forEach(e){for(let t=0;t<=65535;t++)e(t,t)}charCodeOf(e){return Number.isInteger(e)&&e<=65535?e:-1}getMap(){const e=new Array(65536);for(let t=0;t<=65535;t++)e[t]=t;return e}get length(){return 65536}get isId\ -entityCMap(){unreachable("should not access .isIdentityCMap")}}function strToInt(e){let t=0;for(let a=0;a>>0}function expectString(e){if("string"!=typeof e)throw new FormatError("Malformed CMap: expected string.")}function expectInt(e){if(!Number.isInteger(e))throw new FormatError("Malformed CMap: expected int.")}function parseBfChar(e,t){for(;;){let a=t.getObj();if(a===wa)break;if(isCmd(a,"endbfchar"))return;expectString(a);const r=strToInt(a);a=t.g\ -etObj();expectString(a);const i=a;e.mapOne(r,i)}}function parseBfRange(e,t){for(;;){let a=t.getObj();if(a===wa)break;if(isCmd(a,"endbfrange"))return;expectString(a);const r=strToInt(a);a=t.getObj();expectString(a);const i=strToInt(a);a=t.getObj();if(Number.isInteger(a)||"string"==typeof a){const t=Number.isInteger(a)?String.fromCharCode(a):a;e.mapBfRange(r,i,t)}else{if(!isCmd(a,"["))break;{a=t.getObj();const n=[];for(;!isCmd(a,"]")&&a!==wa;){n.push(a);a=t.getObj()}e.mapBfRangeToArray(r,i,n)}}}th\ -row new FormatError("Invalid bf range.")}function parseCidChar(e,t){for(;;){let a=t.getObj();if(a===wa)break;if(isCmd(a,"endcidchar"))return;expectString(a);const r=strToInt(a);a=t.getObj();expectInt(a);const i=a;e.mapOne(r,i)}}function parseCidRange(e,t){for(;;){let a=t.getObj();if(a===wa)break;if(isCmd(a,"endcidrange"))return;expectString(a);const r=strToInt(a);a=t.getObj();expectString(a);const i=strToInt(a);a=t.getObj();expectInt(a);const n=a;e.mapCidRange(r,i,n)}}function parseCodespaceRang\ -e(e,t){for(;;){let a=t.getObj();if(a===wa)break;if(isCmd(a,"endcodespacerange"))return;if("string"!=typeof a)break;const r=strToInt(a);a=t.getObj();if("string"!=typeof a)break;const i=strToInt(a);e.addCodespaceRange(a.length,r,i)}throw new FormatError("Invalid codespace range.")}function parseWMode(e,t){const a=t.getObj();Number.isInteger(a)&&(e.vertical=!!a)}function parseCMapName(e,t){const a=t.getObj();a instanceof Name&&(e.name=a.name)}async function parseCMap(e,t,a,r){let i,n;e:for(;;)try{c\ -onst a=t.getObj();if(a===wa)break;if(a instanceof Name){"WMode"===a.name?parseWMode(e,t):"CMapName"===a.name&&parseCMapName(e,t);i=a}else if(a instanceof Cmd)switch(a.cmd){case"endcmap":break e;case"usecmap":i instanceof Name&&(n=i.name);break;case"begincodespacerange":parseCodespaceRange(e,t);break;case"beginbfchar":parseBfChar(e,t);break;case"begincidchar":parseCidChar(e,t);break;case"beginbfrange":parseBfRange(e,t);break;case"begincidrange":parseCidRange(e,t)}}catch(e){if(e instanceof Missing\ -DataException)throw e;warn("Invalid cMap data: "+e);continue}!r&&n&&(r=n);return r?extendCMap(e,a,r):e}async function extendCMap(e,t,a){e.useCMap=await createBuiltInCMap(a,t);if(0===e.numCodespaceRanges){const t=e.useCMap.codespaceRanges;for(let a=0;aextendCMap(i,t,e)));const n=new Lexer(new Stream(a));return parseCMap(i,n,t,null)}class CMapFactory{static async create({encoding:e,fetchBuiltInCMap:t,useCMap:a}){if(e instanceof Name)return createBuilt\ -InCMap(e.name,t);if(e instanceof BaseStream){const r=await parseCMap(new CMap,new Lexer(e),t,a);return r.isIdentityCMap?createBuiltInCMap(r.name,t):r}throw new Error("Encoding required.")}}const wr=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","per\ -iod","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenr\ -ightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","",""\ -,"Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","ze\ -roinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oac\ -utesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],xr=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","centoldstyle","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hy\ -phen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","","threequartersemdash","","questionsmall","","","","","Ethsmall","","","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","","","","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphe\ -ninferior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","asuperior","centsuperior","","","","","Aacutesmall","Agravesmall","Acircumflexsmall","Adieresissmall","Atildesmall","Aringsmall","Ccedillasmall","Eacutesmall","Egravesmall","Ecircumflex\ -small","Edieresissmall","Iacutesmall","Igravesmall","Icircumflexsmall","Idieresissmall","Ntildesmall","Oacutesmall","Ogravesmall","Ocircumflexsmall","Odieresissmall","Otildesmall","Uacutesmall","Ugravesmall","Ucircumflexsmall","Udieresissmall","","eightsuperior","fourinferior","threeinferior","sixinferior","eightinferior","seveninferior","Scaronsmall","","centinferior","twoinferior","","Dieresissmall","","Caronsmall","osuperior","fiveinferior","","commainferior","periodinferior","Yacutesmall",""\ -,"dollarinferior","","","Thornsmall","","nineinferior","zeroinferior","Zcaronsmall","AEsmall","Oslashsmall","questiondownsmall","oneinferior","Lslashsmall","","","","","","","Cedillasmall","","","","","","OEsmall","figuredash","hyphensuperior","","","","","exclamdownsmall","","Ydieresissmall","","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","ninesuperior","zerosuperior","","esuperior","rsuperior","tsuperior","","","isuperior","ssuperior",\ -"dsuperior","","","","","","lsuperior","Ogoneksmall","Brevesmall","Macronsmall","bsuperior","nsuperior","msuperior","commasuperior","periodsuperior","Dotaccentsmall","Ringsmall","","","",""],Sr=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six"\ -,"seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","a\ -grave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summatio\ -n","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","space","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perth\ -ousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron"],Ar=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenrigh\ -t","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceri\ -ght","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex",\ -"tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls","","","",""],kr=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","\ -quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y"\ -,"z","braceleft","bar","braceright","asciitilde","bullet","Euro","bullet","quotesinglbase","florin","quotedblbase","ellipsis","dagger","daggerdbl","circumflex","perthousand","Scaron","guilsinglleft","OE","bullet","Zcaron","bullet","bullet","quoteleft","quoteright","quotedblleft","quotedblright","bullet","endash","emdash","tilde","trademark","scaron","guilsinglright","oe","bullet","zcaron","Ydieresis","space","exclamdown","cent","sterling","currency","yen","brokenbar","section","dieresis","copyri\ -ght","ordfeminine","guillemotleft","logicalnot","hyphen","registered","macron","degree","plusminus","twosuperior","threesuperior","acute","mu","paragraph","periodcentered","cedilla","onesuperior","ordmasculine","guillemotright","onequarter","onehalf","threequarters","questiondown","Agrave","Aacute","Acircumflex","Atilde","Adieresis","Aring","AE","Ccedilla","Egrave","Eacute","Ecircumflex","Edieresis","Igrave","Iacute","Icircumflex","Idieresis","Eth","Ntilde","Ograve","Oacute","Ocircumflex","Otild\ -e","Odieresis","multiply","Oslash","Ugrave","Uacute","Ucircumflex","Udieresis","Yacute","Thorn","germandbls","agrave","aacute","acircumflex","atilde","adieresis","aring","ae","ccedilla","egrave","eacute","ecircumflex","edieresis","igrave","iacute","icircumflex","idieresis","eth","ntilde","ograve","oacute","ocircumflex","otilde","odieresis","divide","oslash","ugrave","uacute","ucircumflex","udieresis","yacute","thorn","ydieresis"],Cr=["","","","","","","","","","","","","","","","","","","","",""\ -,"","","","","","","","","","","","space","exclam","universal","numbersign","existential","percent","ampersand","suchthat","parenleft","parenright","asteriskmath","plus","comma","minus","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","congruent","Alpha","Beta","Chi","Delta","Epsilon","Phi","Gamma","Eta","Iota","theta1","Kappa","Lambda","Mu","Nu","Omicron","Pi","Theta","Rho","Sigma","Tau","Upsilon","sig\ -ma1","Omega","Xi","Psi","Zeta","bracketleft","therefore","bracketright","perpendicular","underscore","radicalex","alpha","beta","chi","delta","epsilon","phi","gamma","eta","iota","phi1","kappa","lambda","mu","nu","omicron","pi","theta","rho","sigma","tau","upsilon","omega1","omega","xi","psi","zeta","braceleft","bar","braceright","similar","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Euro","Upsilon1","minute","lessequal","fraction","infinity\ -","florin","club","diamond","heart","spade","arrowboth","arrowleft","arrowup","arrowright","arrowdown","degree","plusminus","second","greaterequal","multiply","proportional","partialdiff","bullet","divide","notequal","equivalence","approxequal","ellipsis","arrowvertex","arrowhorizex","carriagereturn","aleph","Ifraktur","Rfraktur","weierstrass","circlemultiply","circleplus","emptyset","intersection","union","propersuperset","reflexsuperset","notsubset","propersubset","reflexsubset","element","not\ -element","angle","gradient","registerserif","copyrightserif","trademarkserif","product","radical","dotmath","logicalnot","logicaland","logicalor","arrowdblboth","arrowdblleft","arrowdblup","arrowdblright","arrowdbldown","lozenge","angleleft","registersans","copyrightsans","trademarksans","summation","parenlefttp","parenleftex","parenleftbt","bracketlefttp","bracketleftex","bracketleftbt","bracelefttp","braceleftmid","braceleftbt","braceex","","angleright","integral","integraltp","integralex","in\ -tegralbt","parenrighttp","parenrightex","parenrightbt","bracketrighttp","bracketrightex","bracketrightbt","bracerighttp","bracerightmid","bracerightbt",""],vr=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","a1","a2","a202","a3","a4","a5","a119","a118","a117","a11","a12","a13","a14","a15","a16","a105","a17","a18","a19","a20","a21","a22","a23","a24","a25","a26","a27","a28","a6","a7","a8","a9","a10","a29","a30","a31","a32","a33","a34","a35",\ -"a36","a37","a38","a39","a40","a41","a42","a43","a44","a45","a46","a47","a48","a49","a50","a51","a52","a53","a54","a55","a56","a57","a58","a59","a60","a61","a62","a63","a64","a65","a66","a67","a68","a69","a70","a71","a72","a73","a74","a203","a75","a204","a76","a77","a78","a79","a81","a82","a83","a84","a97","a98","a99","a100","","a89","a90","a93","a94","a91","a92","a205","a85","a206","a86","a87","a88","a95","a96","","","","","","","","","","","","","","","","","","","","a101","a102","a103","a104"\ -,"a106","a107","a108","a112","a111","a110","a109","a120","a121","a122","a123","a124","a125","a126","a127","a128","a129","a130","a131","a132","a133","a134","a135","a136","a137","a138","a139","a140","a141","a142","a143","a144","a145","a146","a147","a148","a149","a150","a151","a152","a153","a154","a155","a156","a157","a158","a159","a160","a161","a163","a164","a196","a165","a192","a166","a167","a168","a169","a170","a171","a172","a173","a162","a174","a175","a176","a177","a178","a179","a193","a180","a\ -199","a181","a200","a182","","a201","a183","a184","a197","a185","a194","a198","a186","a195","a187","a188","a189","a190","a191",""];function getEncoding(e){switch(e){case"WinAnsiEncoding":return kr;case"StandardEncoding":return Ar;case"MacRomanEncoding":return Sr;case"SymbolSetEncoding":return Cr;case"ZapfDingbatsEncoding":return vr;case"ExpertEncoding":return wr;case"MacExpertEncoding":return xr;default:return null}}const Fr=getLookupTableFactory((function(e){e.A=65;e.AE=198;e.AEacute=508;e.AEma\ -cron=482;e.AEsmall=63462;e.Aacute=193;e.Aacutesmall=63457;e.Abreve=258;e.Abreveacute=7854;e.Abrevecyrillic=1232;e.Abrevedotbelow=7862;e.Abrevegrave=7856;e.Abrevehookabove=7858;e.Abrevetilde=7860;e.Acaron=461;e.Acircle=9398;e.Acircumflex=194;e.Acircumflexacute=7844;e.Acircumflexdotbelow=7852;e.Acircumflexgrave=7846;e.Acircumflexhookabove=7848;e.Acircumflexsmall=63458;e.Acircumflextilde=7850;e.Acute=63177;e.Acutesmall=63412;e.Acyrillic=1040;e.Adblgrave=512;e.Adieresis=196;e.Adieresiscyrillic=1234;\ -e.Adieresismacron=478;e.Adieresissmall=63460;e.Adotbelow=7840;e.Adotmacron=480;e.Agrave=192;e.Agravesmall=63456;e.Ahookabove=7842;e.Aiecyrillic=1236;e.Ainvertedbreve=514;e.Alpha=913;e.Alphatonos=902;e.Amacron=256;e.Amonospace=65313;e.Aogonek=260;e.Aring=197;e.Aringacute=506;e.Aringbelow=7680;e.Aringsmall=63461;e.Asmall=63329;e.Atilde=195;e.Atildesmall=63459;e.Aybarmenian=1329;e.B=66;e.Bcircle=9399;e.Bdotaccent=7682;e.Bdotbelow=7684;e.Becyrillic=1041;e.Benarmenian=1330;e.Beta=914;e.Bhook=385;e.Bl\ -inebelow=7686;e.Bmonospace=65314;e.Brevesmall=63220;e.Bsmall=63330;e.Btopbar=386;e.C=67;e.Caarmenian=1342;e.Cacute=262;e.Caron=63178;e.Caronsmall=63221;e.Ccaron=268;e.Ccedilla=199;e.Ccedillaacute=7688;e.Ccedillasmall=63463;e.Ccircle=9400;e.Ccircumflex=264;e.Cdot=266;e.Cdotaccent=266;e.Cedillasmall=63416;e.Chaarmenian=1353;e.Cheabkhasiancyrillic=1212;e.Checyrillic=1063;e.Chedescenderabkhasiancyrillic=1214;e.Chedescendercyrillic=1206;e.Chedieresiscyrillic=1268;e.Cheharmenian=1347;e.Chekhakassiancy\ -rillic=1227;e.Cheverticalstrokecyrillic=1208;e.Chi=935;e.Chook=391;e.Circumflexsmall=63222;e.Cmonospace=65315;e.Coarmenian=1361;e.Csmall=63331;e.D=68;e.DZ=497;e.DZcaron=452;e.Daarmenian=1332;e.Dafrican=393;e.Dcaron=270;e.Dcedilla=7696;e.Dcircle=9401;e.Dcircumflexbelow=7698;e.Dcroat=272;e.Ddotaccent=7690;e.Ddotbelow=7692;e.Decyrillic=1044;e.Deicoptic=1006;e.Delta=8710;e.Deltagreek=916;e.Dhook=394;e.Dieresis=63179;e.DieresisAcute=63180;e.DieresisGrave=63181;e.Dieresissmall=63400;e.Digammagreek=988\ -;e.Djecyrillic=1026;e.Dlinebelow=7694;e.Dmonospace=65316;e.Dotaccentsmall=63223;e.Dslash=272;e.Dsmall=63332;e.Dtopbar=395;e.Dz=498;e.Dzcaron=453;e.Dzeabkhasiancyrillic=1248;e.Dzecyrillic=1029;e.Dzhecyrillic=1039;e.E=69;e.Eacute=201;e.Eacutesmall=63465;e.Ebreve=276;e.Ecaron=282;e.Ecedillabreve=7708;e.Echarmenian=1333;e.Ecircle=9402;e.Ecircumflex=202;e.Ecircumflexacute=7870;e.Ecircumflexbelow=7704;e.Ecircumflexdotbelow=7878;e.Ecircumflexgrave=7872;e.Ecircumflexhookabove=7874;e.Ecircumflexsmall=634\ -66;e.Ecircumflextilde=7876;e.Ecyrillic=1028;e.Edblgrave=516;e.Edieresis=203;e.Edieresissmall=63467;e.Edot=278;e.Edotaccent=278;e.Edotbelow=7864;e.Efcyrillic=1060;e.Egrave=200;e.Egravesmall=63464;e.Eharmenian=1335;e.Ehookabove=7866;e.Eightroman=8551;e.Einvertedbreve=518;e.Eiotifiedcyrillic=1124;e.Elcyrillic=1051;e.Elevenroman=8554;e.Emacron=274;e.Emacronacute=7702;e.Emacrongrave=7700;e.Emcyrillic=1052;e.Emonospace=65317;e.Encyrillic=1053;e.Endescendercyrillic=1186;e.Eng=330;e.Enghecyrillic=1188;e\ -.Enhookcyrillic=1223;e.Eogonek=280;e.Eopen=400;e.Epsilon=917;e.Epsilontonos=904;e.Ercyrillic=1056;e.Ereversed=398;e.Ereversedcyrillic=1069;e.Escyrillic=1057;e.Esdescendercyrillic=1194;e.Esh=425;e.Esmall=63333;e.Eta=919;e.Etarmenian=1336;e.Etatonos=905;e.Eth=208;e.Ethsmall=63472;e.Etilde=7868;e.Etildebelow=7706;e.Euro=8364;e.Ezh=439;e.Ezhcaron=494;e.Ezhreversed=440;e.F=70;e.Fcircle=9403;e.Fdotaccent=7710;e.Feharmenian=1366;e.Feicoptic=996;e.Fhook=401;e.Fitacyrillic=1138;e.Fiveroman=8548;e.Fmonosp\ -ace=65318;e.Fourroman=8547;e.Fsmall=63334;e.G=71;e.GBsquare=13191;e.Gacute=500;e.Gamma=915;e.Gammaafrican=404;e.Gangiacoptic=1002;e.Gbreve=286;e.Gcaron=486;e.Gcedilla=290;e.Gcircle=9404;e.Gcircumflex=284;e.Gcommaaccent=290;e.Gdot=288;e.Gdotaccent=288;e.Gecyrillic=1043;e.Ghadarmenian=1346;e.Ghemiddlehookcyrillic=1172;e.Ghestrokecyrillic=1170;e.Gheupturncyrillic=1168;e.Ghook=403;e.Gimarmenian=1331;e.Gjecyrillic=1027;e.Gmacron=7712;e.Gmonospace=65319;e.Grave=63182;e.Gravesmall=63328;e.Gsmall=63335;\ -e.Gsmallhook=667;e.Gstroke=484;e.H=72;e.H18533=9679;e.H18543=9642;e.H18551=9643;e.H22073=9633;e.HPsquare=13259;e.Haabkhasiancyrillic=1192;e.Hadescendercyrillic=1202;e.Hardsigncyrillic=1066;e.Hbar=294;e.Hbrevebelow=7722;e.Hcedilla=7720;e.Hcircle=9405;e.Hcircumflex=292;e.Hdieresis=7718;e.Hdotaccent=7714;e.Hdotbelow=7716;e.Hmonospace=65320;e.Hoarmenian=1344;e.Horicoptic=1e3;e.Hsmall=63336;e.Hungarumlaut=63183;e.Hungarumlautsmall=63224;e.Hzsquare=13200;e.I=73;e.IAcyrillic=1071;e.IJ=306;e.IUcyrillic=\ -1070;e.Iacute=205;e.Iacutesmall=63469;e.Ibreve=300;e.Icaron=463;e.Icircle=9406;e.Icircumflex=206;e.Icircumflexsmall=63470;e.Icyrillic=1030;e.Idblgrave=520;e.Idieresis=207;e.Idieresisacute=7726;e.Idieresiscyrillic=1252;e.Idieresissmall=63471;e.Idot=304;e.Idotaccent=304;e.Idotbelow=7882;e.Iebrevecyrillic=1238;e.Iecyrillic=1045;e.Ifraktur=8465;e.Igrave=204;e.Igravesmall=63468;e.Ihookabove=7880;e.Iicyrillic=1048;e.Iinvertedbreve=522;e.Iishortcyrillic=1049;e.Imacron=298;e.Imacroncyrillic=1250;e.Imono\ -space=65321;e.Iniarmenian=1339;e.Iocyrillic=1025;e.Iogonek=302;e.Iota=921;e.Iotaafrican=406;e.Iotadieresis=938;e.Iotatonos=906;e.Ismall=63337;e.Istroke=407;e.Itilde=296;e.Itildebelow=7724;e.Izhitsacyrillic=1140;e.Izhitsadblgravecyrillic=1142;e.J=74;e.Jaarmenian=1345;e.Jcircle=9407;e.Jcircumflex=308;e.Jecyrillic=1032;e.Jheharmenian=1355;e.Jmonospace=65322;e.Jsmall=63338;e.K=75;e.KBsquare=13189;e.KKsquare=13261;e.Kabashkircyrillic=1184;e.Kacute=7728;e.Kacyrillic=1050;e.Kadescendercyrillic=1178;e.K\ -ahookcyrillic=1219;e.Kappa=922;e.Kastrokecyrillic=1182;e.Kaverticalstrokecyrillic=1180;e.Kcaron=488;e.Kcedilla=310;e.Kcircle=9408;e.Kcommaaccent=310;e.Kdotbelow=7730;e.Keharmenian=1364;e.Kenarmenian=1343;e.Khacyrillic=1061;e.Kheicoptic=998;e.Khook=408;e.Kjecyrillic=1036;e.Klinebelow=7732;e.Kmonospace=65323;e.Koppacyrillic=1152;e.Koppagreek=990;e.Ksicyrillic=1134;e.Ksmall=63339;e.L=76;e.LJ=455;e.LL=63167;e.Lacute=313;e.Lambda=923;e.Lcaron=317;e.Lcedilla=315;e.Lcircle=9409;e.Lcircumflexbelow=7740;\ -e.Lcommaaccent=315;e.Ldot=319;e.Ldotaccent=319;e.Ldotbelow=7734;e.Ldotbelowmacron=7736;e.Liwnarmenian=1340;e.Lj=456;e.Ljecyrillic=1033;e.Llinebelow=7738;e.Lmonospace=65324;e.Lslash=321;e.Lslashsmall=63225;e.Lsmall=63340;e.M=77;e.MBsquare=13190;e.Macron=63184;e.Macronsmall=63407;e.Macute=7742;e.Mcircle=9410;e.Mdotaccent=7744;e.Mdotbelow=7746;e.Menarmenian=1348;e.Mmonospace=65325;e.Msmall=63341;e.Mturned=412;e.Mu=924;e.N=78;e.NJ=458;e.Nacute=323;e.Ncaron=327;e.Ncedilla=325;e.Ncircle=9411;e.Ncircum\ -flexbelow=7754;e.Ncommaaccent=325;e.Ndotaccent=7748;e.Ndotbelow=7750;e.Nhookleft=413;e.Nineroman=8552;e.Nj=459;e.Njecyrillic=1034;e.Nlinebelow=7752;e.Nmonospace=65326;e.Nowarmenian=1350;e.Nsmall=63342;e.Ntilde=209;e.Ntildesmall=63473;e.Nu=925;e.O=79;e.OE=338;e.OEsmall=63226;e.Oacute=211;e.Oacutesmall=63475;e.Obarredcyrillic=1256;e.Obarreddieresiscyrillic=1258;e.Obreve=334;e.Ocaron=465;e.Ocenteredtilde=415;e.Ocircle=9412;e.Ocircumflex=212;e.Ocircumflexacute=7888;e.Ocircumflexdotbelow=7896;e.Ocirc\ -umflexgrave=7890;e.Ocircumflexhookabove=7892;e.Ocircumflexsmall=63476;e.Ocircumflextilde=7894;e.Ocyrillic=1054;e.Odblacute=336;e.Odblgrave=524;e.Odieresis=214;e.Odieresiscyrillic=1254;e.Odieresissmall=63478;e.Odotbelow=7884;e.Ogoneksmall=63227;e.Ograve=210;e.Ogravesmall=63474;e.Oharmenian=1365;e.Ohm=8486;e.Ohookabove=7886;e.Ohorn=416;e.Ohornacute=7898;e.Ohorndotbelow=7906;e.Ohorngrave=7900;e.Ohornhookabove=7902;e.Ohorntilde=7904;e.Ohungarumlaut=336;e.Oi=418;e.Oinvertedbreve=526;e.Omacron=332;e.O\ -macronacute=7762;e.Omacrongrave=7760;e.Omega=8486;e.Omegacyrillic=1120;e.Omegagreek=937;e.Omegaroundcyrillic=1146;e.Omegatitlocyrillic=1148;e.Omegatonos=911;e.Omicron=927;e.Omicrontonos=908;e.Omonospace=65327;e.Oneroman=8544;e.Oogonek=490;e.Oogonekmacron=492;e.Oopen=390;e.Oslash=216;e.Oslashacute=510;e.Oslashsmall=63480;e.Osmall=63343;e.Ostrokeacute=510;e.Otcyrillic=1150;e.Otilde=213;e.Otildeacute=7756;e.Otildedieresis=7758;e.Otildesmall=63477;e.P=80;e.Pacute=7764;e.Pcircle=9413;e.Pdotaccent=776\ -6;e.Pecyrillic=1055;e.Peharmenian=1354;e.Pemiddlehookcyrillic=1190;e.Phi=934;e.Phook=420;e.Pi=928;e.Piwrarmenian=1363;e.Pmonospace=65328;e.Psi=936;e.Psicyrillic=1136;e.Psmall=63344;e.Q=81;e.Qcircle=9414;e.Qmonospace=65329;e.Qsmall=63345;e.R=82;e.Raarmenian=1356;e.Racute=340;e.Rcaron=344;e.Rcedilla=342;e.Rcircle=9415;e.Rcommaaccent=342;e.Rdblgrave=528;e.Rdotaccent=7768;e.Rdotbelow=7770;e.Rdotbelowmacron=7772;e.Reharmenian=1360;e.Rfraktur=8476;e.Rho=929;e.Ringsmall=63228;e.Rinvertedbreve=530;e.Rli\ -nebelow=7774;e.Rmonospace=65330;e.Rsmall=63346;e.Rsmallinverted=641;e.Rsmallinvertedsuperior=694;e.S=83;e.SF010000=9484;e.SF020000=9492;e.SF030000=9488;e.SF040000=9496;e.SF050000=9532;e.SF060000=9516;e.SF070000=9524;e.SF080000=9500;e.SF090000=9508;e.SF100000=9472;e.SF110000=9474;e.SF190000=9569;e.SF200000=9570;e.SF210000=9558;e.SF220000=9557;e.SF230000=9571;e.SF240000=9553;e.SF250000=9559;e.SF260000=9565;e.SF270000=9564;e.SF280000=9563;e.SF360000=9566;e.SF370000=9567;e.SF380000=9562;e.SF390000=9\ -556;e.SF400000=9577;e.SF410000=9574;e.SF420000=9568;e.SF430000=9552;e.SF440000=9580;e.SF450000=9575;e.SF460000=9576;e.SF470000=9572;e.SF480000=9573;e.SF490000=9561;e.SF500000=9560;e.SF510000=9554;e.SF520000=9555;e.SF530000=9579;e.SF540000=9578;e.Sacute=346;e.Sacutedotaccent=7780;e.Sampigreek=992;e.Scaron=352;e.Scarondotaccent=7782;e.Scaronsmall=63229;e.Scedilla=350;e.Schwa=399;e.Schwacyrillic=1240;e.Schwadieresiscyrillic=1242;e.Scircle=9416;e.Scircumflex=348;e.Scommaaccent=536;e.Sdotaccent=7776;\ -e.Sdotbelow=7778;e.Sdotbelowdotaccent=7784;e.Seharmenian=1357;e.Sevenroman=8550;e.Shaarmenian=1351;e.Shacyrillic=1064;e.Shchacyrillic=1065;e.Sheicoptic=994;e.Shhacyrillic=1210;e.Shimacoptic=1004;e.Sigma=931;e.Sixroman=8549;e.Smonospace=65331;e.Softsigncyrillic=1068;e.Ssmall=63347;e.Stigmagreek=986;e.T=84;e.Tau=932;e.Tbar=358;e.Tcaron=356;e.Tcedilla=354;e.Tcircle=9417;e.Tcircumflexbelow=7792;e.Tcommaaccent=354;e.Tdotaccent=7786;e.Tdotbelow=7788;e.Tecyrillic=1058;e.Tedescendercyrillic=1196;e.Tenro\ -man=8553;e.Tetsecyrillic=1204;e.Theta=920;e.Thook=428;e.Thorn=222;e.Thornsmall=63486;e.Threeroman=8546;e.Tildesmall=63230;e.Tiwnarmenian=1359;e.Tlinebelow=7790;e.Tmonospace=65332;e.Toarmenian=1337;e.Tonefive=444;e.Tonesix=388;e.Tonetwo=423;e.Tretroflexhook=430;e.Tsecyrillic=1062;e.Tshecyrillic=1035;e.Tsmall=63348;e.Twelveroman=8555;e.Tworoman=8545;e.U=85;e.Uacute=218;e.Uacutesmall=63482;e.Ubreve=364;e.Ucaron=467;e.Ucircle=9418;e.Ucircumflex=219;e.Ucircumflexbelow=7798;e.Ucircumflexsmall=63483;e.\ -Ucyrillic=1059;e.Udblacute=368;e.Udblgrave=532;e.Udieresis=220;e.Udieresisacute=471;e.Udieresisbelow=7794;e.Udieresiscaron=473;e.Udieresiscyrillic=1264;e.Udieresisgrave=475;e.Udieresismacron=469;e.Udieresissmall=63484;e.Udotbelow=7908;e.Ugrave=217;e.Ugravesmall=63481;e.Uhookabove=7910;e.Uhorn=431;e.Uhornacute=7912;e.Uhorndotbelow=7920;e.Uhorngrave=7914;e.Uhornhookabove=7916;e.Uhorntilde=7918;e.Uhungarumlaut=368;e.Uhungarumlautcyrillic=1266;e.Uinvertedbreve=534;e.Ukcyrillic=1144;e.Umacron=362;e.U\ -macroncyrillic=1262;e.Umacrondieresis=7802;e.Umonospace=65333;e.Uogonek=370;e.Upsilon=933;e.Upsilon1=978;e.Upsilonacutehooksymbolgreek=979;e.Upsilonafrican=433;e.Upsilondieresis=939;e.Upsilondieresishooksymbolgreek=980;e.Upsilonhooksymbol=978;e.Upsilontonos=910;e.Uring=366;e.Ushortcyrillic=1038;e.Usmall=63349;e.Ustraightcyrillic=1198;e.Ustraightstrokecyrillic=1200;e.Utilde=360;e.Utildeacute=7800;e.Utildebelow=7796;e.V=86;e.Vcircle=9419;e.Vdotbelow=7806;e.Vecyrillic=1042;e.Vewarmenian=1358;e.Vhoo\ -k=434;e.Vmonospace=65334;e.Voarmenian=1352;e.Vsmall=63350;e.Vtilde=7804;e.W=87;e.Wacute=7810;e.Wcircle=9420;e.Wcircumflex=372;e.Wdieresis=7812;e.Wdotaccent=7814;e.Wdotbelow=7816;e.Wgrave=7808;e.Wmonospace=65335;e.Wsmall=63351;e.X=88;e.Xcircle=9421;e.Xdieresis=7820;e.Xdotaccent=7818;e.Xeharmenian=1341;e.Xi=926;e.Xmonospace=65336;e.Xsmall=63352;e.Y=89;e.Yacute=221;e.Yacutesmall=63485;e.Yatcyrillic=1122;e.Ycircle=9422;e.Ycircumflex=374;e.Ydieresis=376;e.Ydieresissmall=63487;e.Ydotaccent=7822;e.Ydot\ -below=7924;e.Yericyrillic=1067;e.Yerudieresiscyrillic=1272;e.Ygrave=7922;e.Yhook=435;e.Yhookabove=7926;e.Yiarmenian=1349;e.Yicyrillic=1031;e.Yiwnarmenian=1362;e.Ymonospace=65337;e.Ysmall=63353;e.Ytilde=7928;e.Yusbigcyrillic=1130;e.Yusbigiotifiedcyrillic=1132;e.Yuslittlecyrillic=1126;e.Yuslittleiotifiedcyrillic=1128;e.Z=90;e.Zaarmenian=1334;e.Zacute=377;e.Zcaron=381;e.Zcaronsmall=63231;e.Zcircle=9423;e.Zcircumflex=7824;e.Zdot=379;e.Zdotaccent=379;e.Zdotbelow=7826;e.Zecyrillic=1047;e.Zedescendercy\ -rillic=1176;e.Zedieresiscyrillic=1246;e.Zeta=918;e.Zhearmenian=1338;e.Zhebrevecyrillic=1217;e.Zhecyrillic=1046;e.Zhedescendercyrillic=1174;e.Zhedieresiscyrillic=1244;e.Zlinebelow=7828;e.Zmonospace=65338;e.Zsmall=63354;e.Zstroke=437;e.a=97;e.aabengali=2438;e.aacute=225;e.aadeva=2310;e.aagujarati=2694;e.aagurmukhi=2566;e.aamatragurmukhi=2622;e.aarusquare=13059;e.aavowelsignbengali=2494;e.aavowelsigndeva=2366;e.aavowelsigngujarati=2750;e.abbreviationmarkarmenian=1375;e.abbreviationsigndeva=2416;e.a\ -bengali=2437;e.abopomofo=12570;e.abreve=259;e.abreveacute=7855;e.abrevecyrillic=1233;e.abrevedotbelow=7863;e.abrevegrave=7857;e.abrevehookabove=7859;e.abrevetilde=7861;e.acaron=462;e.acircle=9424;e.acircumflex=226;e.acircumflexacute=7845;e.acircumflexdotbelow=7853;e.acircumflexgrave=7847;e.acircumflexhookabove=7849;e.acircumflextilde=7851;e.acute=180;e.acutebelowcmb=791;e.acutecmb=769;e.acutecomb=769;e.acutedeva=2388;e.acutelowmod=719;e.acutetonecmb=833;e.acyrillic=1072;e.adblgrave=513;e.addakgu\ -rmukhi=2673;e.adeva=2309;e.adieresis=228;e.adieresiscyrillic=1235;e.adieresismacron=479;e.adotbelow=7841;e.adotmacron=481;e.ae=230;e.aeacute=509;e.aekorean=12624;e.aemacron=483;e.afii00208=8213;e.afii08941=8356;e.afii10017=1040;e.afii10018=1041;e.afii10019=1042;e.afii10020=1043;e.afii10021=1044;e.afii10022=1045;e.afii10023=1025;e.afii10024=1046;e.afii10025=1047;e.afii10026=1048;e.afii10027=1049;e.afii10028=1050;e.afii10029=1051;e.afii10030=1052;e.afii10031=1053;e.afii10032=1054;e.afii10033=1055;\ -e.afii10034=1056;e.afii10035=1057;e.afii10036=1058;e.afii10037=1059;e.afii10038=1060;e.afii10039=1061;e.afii10040=1062;e.afii10041=1063;e.afii10042=1064;e.afii10043=1065;e.afii10044=1066;e.afii10045=1067;e.afii10046=1068;e.afii10047=1069;e.afii10048=1070;e.afii10049=1071;e.afii10050=1168;e.afii10051=1026;e.afii10052=1027;e.afii10053=1028;e.afii10054=1029;e.afii10055=1030;e.afii10056=1031;e.afii10057=1032;e.afii10058=1033;e.afii10059=1034;e.afii10060=1035;e.afii10061=1036;e.afii10062=1038;e.afii1\ -0063=63172;e.afii10064=63173;e.afii10065=1072;e.afii10066=1073;e.afii10067=1074;e.afii10068=1075;e.afii10069=1076;e.afii10070=1077;e.afii10071=1105;e.afii10072=1078;e.afii10073=1079;e.afii10074=1080;e.afii10075=1081;e.afii10076=1082;e.afii10077=1083;e.afii10078=1084;e.afii10079=1085;e.afii10080=1086;e.afii10081=1087;e.afii10082=1088;e.afii10083=1089;e.afii10084=1090;e.afii10085=1091;e.afii10086=1092;e.afii10087=1093;e.afii10088=1094;e.afii10089=1095;e.afii10090=1096;e.afii10091=1097;e.afii10092=\ -1098;e.afii10093=1099;e.afii10094=1100;e.afii10095=1101;e.afii10096=1102;e.afii10097=1103;e.afii10098=1169;e.afii10099=1106;e.afii10100=1107;e.afii10101=1108;e.afii10102=1109;e.afii10103=1110;e.afii10104=1111;e.afii10105=1112;e.afii10106=1113;e.afii10107=1114;e.afii10108=1115;e.afii10109=1116;e.afii10110=1118;e.afii10145=1039;e.afii10146=1122;e.afii10147=1138;e.afii10148=1140;e.afii10192=63174;e.afii10193=1119;e.afii10194=1123;e.afii10195=1139;e.afii10196=1141;e.afii10831=63175;e.afii10832=63176\ -;e.afii10846=1241;e.afii299=8206;e.afii300=8207;e.afii301=8205;e.afii57381=1642;e.afii57388=1548;e.afii57392=1632;e.afii57393=1633;e.afii57394=1634;e.afii57395=1635;e.afii57396=1636;e.afii57397=1637;e.afii57398=1638;e.afii57399=1639;e.afii57400=1640;e.afii57401=1641;e.afii57403=1563;e.afii57407=1567;e.afii57409=1569;e.afii57410=1570;e.afii57411=1571;e.afii57412=1572;e.afii57413=1573;e.afii57414=1574;e.afii57415=1575;e.afii57416=1576;e.afii57417=1577;e.afii57418=1578;e.afii57419=1579;e.afii57420=\ -1580;e.afii57421=1581;e.afii57422=1582;e.afii57423=1583;e.afii57424=1584;e.afii57425=1585;e.afii57426=1586;e.afii57427=1587;e.afii57428=1588;e.afii57429=1589;e.afii57430=1590;e.afii57431=1591;e.afii57432=1592;e.afii57433=1593;e.afii57434=1594;e.afii57440=1600;e.afii57441=1601;e.afii57442=1602;e.afii57443=1603;e.afii57444=1604;e.afii57445=1605;e.afii57446=1606;e.afii57448=1608;e.afii57449=1609;e.afii57450=1610;e.afii57451=1611;e.afii57452=1612;e.afii57453=1613;e.afii57454=1614;e.afii57455=1615;e.\ -afii57456=1616;e.afii57457=1617;e.afii57458=1618;e.afii57470=1607;e.afii57505=1700;e.afii57506=1662;e.afii57507=1670;e.afii57508=1688;e.afii57509=1711;e.afii57511=1657;e.afii57512=1672;e.afii57513=1681;e.afii57514=1722;e.afii57519=1746;e.afii57534=1749;e.afii57636=8362;e.afii57645=1470;e.afii57658=1475;e.afii57664=1488;e.afii57665=1489;e.afii57666=1490;e.afii57667=1491;e.afii57668=1492;e.afii57669=1493;e.afii57670=1494;e.afii57671=1495;e.afii57672=1496;e.afii57673=1497;e.afii57674=1498;e.afii576\ -75=1499;e.afii57676=1500;e.afii57677=1501;e.afii57678=1502;e.afii57679=1503;e.afii57680=1504;e.afii57681=1505;e.afii57682=1506;e.afii57683=1507;e.afii57684=1508;e.afii57685=1509;e.afii57686=1510;e.afii57687=1511;e.afii57688=1512;e.afii57689=1513;e.afii57690=1514;e.afii57694=64298;e.afii57695=64299;e.afii57700=64331;e.afii57705=64287;e.afii57716=1520;e.afii57717=1521;e.afii57718=1522;e.afii57723=64309;e.afii57793=1460;e.afii57794=1461;e.afii57795=1462;e.afii57796=1467;e.afii57797=1464;e.afii57798\ -=1463;e.afii57799=1456;e.afii57800=1458;e.afii57801=1457;e.afii57802=1459;e.afii57803=1474;e.afii57804=1473;e.afii57806=1465;e.afii57807=1468;e.afii57839=1469;e.afii57841=1471;e.afii57842=1472;e.afii57929=700;e.afii61248=8453;e.afii61289=8467;e.afii61352=8470;e.afii61573=8236;e.afii61574=8237;e.afii61575=8238;e.afii61664=8204;e.afii63167=1645;e.afii64937=701;e.agrave=224;e.agujarati=2693;e.agurmukhi=2565;e.ahiragana=12354;e.ahookabove=7843;e.aibengali=2448;e.aibopomofo=12574;e.aideva=2320;e.aiec\ -yrillic=1237;e.aigujarati=2704;e.aigurmukhi=2576;e.aimatragurmukhi=2632;e.ainarabic=1593;e.ainfinalarabic=65226;e.aininitialarabic=65227;e.ainmedialarabic=65228;e.ainvertedbreve=515;e.aivowelsignbengali=2504;e.aivowelsigndeva=2376;e.aivowelsigngujarati=2760;e.akatakana=12450;e.akatakanahalfwidth=65393;e.akorean=12623;e.alef=1488;e.alefarabic=1575;e.alefdageshhebrew=64304;e.aleffinalarabic=65166;e.alefhamzaabovearabic=1571;e.alefhamzaabovefinalarabic=65156;e.alefhamzabelowarabic=1573;e.alefhamzab\ -elowfinalarabic=65160;e.alefhebrew=1488;e.aleflamedhebrew=64335;e.alefmaddaabovearabic=1570;e.alefmaddaabovefinalarabic=65154;e.alefmaksuraarabic=1609;e.alefmaksurafinalarabic=65264;e.alefmaksurainitialarabic=65267;e.alefmaksuramedialarabic=65268;e.alefpatahhebrew=64302;e.alefqamatshebrew=64303;e.aleph=8501;e.allequal=8780;e.alpha=945;e.alphatonos=940;e.amacron=257;e.amonospace=65345;e.ampersand=38;e.ampersandmonospace=65286;e.ampersandsmall=63270;e.amsquare=13250;e.anbopomofo=12578;e.angbopomof\ -o=12580;e.angbracketleft=12296;e.angbracketright=12297;e.angkhankhuthai=3674;e.angle=8736;e.anglebracketleft=12296;e.anglebracketleftvertical=65087;e.anglebracketright=12297;e.anglebracketrightvertical=65088;e.angleleft=9001;e.angleright=9002;e.angstrom=8491;e.anoteleia=903;e.anudattadeva=2386;e.anusvarabengali=2434;e.anusvaradeva=2306;e.anusvaragujarati=2690;e.aogonek=261;e.apaatosquare=13056;e.aparen=9372;e.apostrophearmenian=1370;e.apostrophemod=700;e.apple=63743;e.approaches=8784;e.approxequ\ -al=8776;e.approxequalorimage=8786;e.approximatelyequal=8773;e.araeaekorean=12686;e.araeakorean=12685;e.arc=8978;e.arighthalfring=7834;e.aring=229;e.aringacute=507;e.aringbelow=7681;e.arrowboth=8596;e.arrowdashdown=8675;e.arrowdashleft=8672;e.arrowdashright=8674;e.arrowdashup=8673;e.arrowdblboth=8660;e.arrowdbldown=8659;e.arrowdblleft=8656;e.arrowdblright=8658;e.arrowdblup=8657;e.arrowdown=8595;e.arrowdownleft=8601;e.arrowdownright=8600;e.arrowdownwhite=8681;e.arrowheaddownmod=709;e.arrowheadleft\ -mod=706;e.arrowheadrightmod=707;e.arrowheadupmod=708;e.arrowhorizex=63719;e.arrowleft=8592;e.arrowleftdbl=8656;e.arrowleftdblstroke=8653;e.arrowleftoverright=8646;e.arrowleftwhite=8678;e.arrowright=8594;e.arrowrightdblstroke=8655;e.arrowrightheavy=10142;e.arrowrightoverleft=8644;e.arrowrightwhite=8680;e.arrowtableft=8676;e.arrowtabright=8677;e.arrowup=8593;e.arrowupdn=8597;e.arrowupdnbse=8616;e.arrowupdownbase=8616;e.arrowupleft=8598;e.arrowupleftofdown=8645;e.arrowupright=8599;e.arrowupwhite=86\ -79;e.arrowvertex=63718;e.asciicircum=94;e.asciicircummonospace=65342;e.asciitilde=126;e.asciitildemonospace=65374;e.ascript=593;e.ascriptturned=594;e.asmallhiragana=12353;e.asmallkatakana=12449;e.asmallkatakanahalfwidth=65383;e.asterisk=42;e.asteriskaltonearabic=1645;e.asteriskarabic=1645;e.asteriskmath=8727;e.asteriskmonospace=65290;e.asterisksmall=65121;e.asterism=8258;e.asuperior=63209;e.asymptoticallyequal=8771;e.at=64;e.atilde=227;e.atmonospace=65312;e.atsmall=65131;e.aturned=592;e.aubengal\ -i=2452;e.aubopomofo=12576;e.audeva=2324;e.augujarati=2708;e.augurmukhi=2580;e.aulengthmarkbengali=2519;e.aumatragurmukhi=2636;e.auvowelsignbengali=2508;e.auvowelsigndeva=2380;e.auvowelsigngujarati=2764;e.avagrahadeva=2365;e.aybarmenian=1377;e.ayin=1506;e.ayinaltonehebrew=64288;e.ayinhebrew=1506;e.b=98;e.babengali=2476;e.backslash=92;e.backslashmonospace=65340;e.badeva=2348;e.bagujarati=2732;e.bagurmukhi=2604;e.bahiragana=12400;e.bahtthai=3647;e.bakatakana=12496;e.bar=124;e.barmonospace=65372;e.b\ -bopomofo=12549;e.bcircle=9425;e.bdotaccent=7683;e.bdotbelow=7685;e.beamedsixteenthnotes=9836;e.because=8757;e.becyrillic=1073;e.beharabic=1576;e.behfinalarabic=65168;e.behinitialarabic=65169;e.behiragana=12409;e.behmedialarabic=65170;e.behmeeminitialarabic=64671;e.behmeemisolatedarabic=64520;e.behnoonfinalarabic=64621;e.bekatakana=12505;e.benarmenian=1378;e.bet=1489;e.beta=946;e.betasymbolgreek=976;e.betdagesh=64305;e.betdageshhebrew=64305;e.bethebrew=1489;e.betrafehebrew=64332;e.bhabengali=2477\ -;e.bhadeva=2349;e.bhagujarati=2733;e.bhagurmukhi=2605;e.bhook=595;e.bihiragana=12403;e.bikatakana=12499;e.bilabialclick=664;e.bindigurmukhi=2562;e.birusquare=13105;e.blackcircle=9679;e.blackdiamond=9670;e.blackdownpointingtriangle=9660;e.blackleftpointingpointer=9668;e.blackleftpointingtriangle=9664;e.blacklenticularbracketleft=12304;e.blacklenticularbracketleftvertical=65083;e.blacklenticularbracketright=12305;e.blacklenticularbracketrightvertical=65084;e.blacklowerlefttriangle=9699;e.blacklowe\ -rrighttriangle=9698;e.blackrectangle=9644;e.blackrightpointingpointer=9658;e.blackrightpointingtriangle=9654;e.blacksmallsquare=9642;e.blacksmilingface=9787;e.blacksquare=9632;e.blackstar=9733;e.blackupperlefttriangle=9700;e.blackupperrighttriangle=9701;e.blackuppointingsmalltriangle=9652;e.blackuppointingtriangle=9650;e.blank=9251;e.blinebelow=7687;e.block=9608;e.bmonospace=65346;e.bobaimaithai=3610;e.bohiragana=12412;e.bokatakana=12508;e.bparen=9373;e.bqsquare=13251;e.braceex=63732;e.braceleft\ -=123;e.braceleftbt=63731;e.braceleftmid=63730;e.braceleftmonospace=65371;e.braceleftsmall=65115;e.bracelefttp=63729;e.braceleftvertical=65079;e.braceright=125;e.bracerightbt=63742;e.bracerightmid=63741;e.bracerightmonospace=65373;e.bracerightsmall=65116;e.bracerighttp=63740;e.bracerightvertical=65080;e.bracketleft=91;e.bracketleftbt=63728;e.bracketleftex=63727;e.bracketleftmonospace=65339;e.bracketlefttp=63726;e.bracketright=93;e.bracketrightbt=63739;e.bracketrightex=63738;e.bracketrightmonospac\ -e=65341;e.bracketrighttp=63737;e.breve=728;e.brevebelowcmb=814;e.brevecmb=774;e.breveinvertedbelowcmb=815;e.breveinvertedcmb=785;e.breveinverteddoublecmb=865;e.bridgebelowcmb=810;e.bridgeinvertedbelowcmb=826;e.brokenbar=166;e.bstroke=384;e.bsuperior=63210;e.btopbar=387;e.buhiragana=12406;e.bukatakana=12502;e.bullet=8226;e.bulletinverse=9688;e.bulletoperator=8729;e.bullseye=9678;e.c=99;e.caarmenian=1390;e.cabengali=2458;e.cacute=263;e.cadeva=2330;e.cagujarati=2714;e.cagurmukhi=2586;e.calsquare=13\ -192;e.candrabindubengali=2433;e.candrabinducmb=784;e.candrabindudeva=2305;e.candrabindugujarati=2689;e.capslock=8682;e.careof=8453;e.caron=711;e.caronbelowcmb=812;e.caroncmb=780;e.carriagereturn=8629;e.cbopomofo=12568;e.ccaron=269;e.ccedilla=231;e.ccedillaacute=7689;e.ccircle=9426;e.ccircumflex=265;e.ccurl=597;e.cdot=267;e.cdotaccent=267;e.cdsquare=13253;e.cedilla=184;e.cedillacmb=807;e.cent=162;e.centigrade=8451;e.centinferior=63199;e.centmonospace=65504;e.centoldstyle=63394;e.centsuperior=6320\ -0;e.chaarmenian=1401;e.chabengali=2459;e.chadeva=2331;e.chagujarati=2715;e.chagurmukhi=2587;e.chbopomofo=12564;e.cheabkhasiancyrillic=1213;e.checkmark=10003;e.checyrillic=1095;e.chedescenderabkhasiancyrillic=1215;e.chedescendercyrillic=1207;e.chedieresiscyrillic=1269;e.cheharmenian=1395;e.chekhakassiancyrillic=1228;e.cheverticalstrokecyrillic=1209;e.chi=967;e.chieuchacirclekorean=12919;e.chieuchaparenkorean=12823;e.chieuchcirclekorean=12905;e.chieuchkorean=12618;e.chieuchparenkorean=12809;e.choc\ -hangthai=3594;e.chochanthai=3592;e.chochingthai=3593;e.chochoethai=3596;e.chook=392;e.cieucacirclekorean=12918;e.cieucaparenkorean=12822;e.cieuccirclekorean=12904;e.cieuckorean=12616;e.cieucparenkorean=12808;e.cieucuparenkorean=12828;e.circle=9675;e.circlecopyrt=169;e.circlemultiply=8855;e.circleot=8857;e.circleplus=8853;e.circlepostalmark=12342;e.circlewithlefthalfblack=9680;e.circlewithrighthalfblack=9681;e.circumflex=710;e.circumflexbelowcmb=813;e.circumflexcmb=770;e.clear=8999;e.clickalveola\ -r=450;e.clickdental=448;e.clicklateral=449;e.clickretroflex=451;e.club=9827;e.clubsuitblack=9827;e.clubsuitwhite=9831;e.cmcubedsquare=13220;e.cmonospace=65347;e.cmsquaredsquare=13216;e.coarmenian=1409;e.colon=58;e.colonmonetary=8353;e.colonmonospace=65306;e.colonsign=8353;e.colonsmall=65109;e.colontriangularhalfmod=721;e.colontriangularmod=720;e.comma=44;e.commaabovecmb=787;e.commaaboverightcmb=789;e.commaaccent=63171;e.commaarabic=1548;e.commaarmenian=1373;e.commainferior=63201;e.commamonospace\ -=65292;e.commareversedabovecmb=788;e.commareversedmod=701;e.commasmall=65104;e.commasuperior=63202;e.commaturnedabovecmb=786;e.commaturnedmod=699;e.compass=9788;e.congruent=8773;e.contourintegral=8750;e.control=8963;e.controlACK=6;e.controlBEL=7;e.controlBS=8;e.controlCAN=24;e.controlCR=13;e.controlDC1=17;e.controlDC2=18;e.controlDC3=19;e.controlDC4=20;e.controlDEL=127;e.controlDLE=16;e.controlEM=25;e.controlENQ=5;e.controlEOT=4;e.controlESC=27;e.controlETB=23;e.controlETX=3;e.controlFF=12;e.con\ -trolFS=28;e.controlGS=29;e.controlHT=9;e.controlLF=10;e.controlNAK=21;e.controlNULL=0;e.controlRS=30;e.controlSI=15;e.controlSO=14;e.controlSOT=2;e.controlSTX=1;e.controlSUB=26;e.controlSYN=22;e.controlUS=31;e.controlVT=11;e.copyright=169;e.copyrightsans=63721;e.copyrightserif=63193;e.cornerbracketleft=12300;e.cornerbracketlefthalfwidth=65378;e.cornerbracketleftvertical=65089;e.cornerbracketright=12301;e.cornerbracketrighthalfwidth=65379;e.cornerbracketrightvertical=65090;e.corporationsquare=131\ -83;e.cosquare=13255;e.coverkgsquare=13254;e.cparen=9374;e.cruzeiro=8354;e.cstretched=663;e.curlyand=8911;e.curlyor=8910;e.currency=164;e.cyrBreve=63185;e.cyrFlex=63186;e.cyrbreve=63188;e.cyrflex=63189;e.d=100;e.daarmenian=1380;e.dabengali=2470;e.dadarabic=1590;e.dadeva=2342;e.dadfinalarabic=65214;e.dadinitialarabic=65215;e.dadmedialarabic=65216;e.dagesh=1468;e.dageshhebrew=1468;e.dagger=8224;e.daggerdbl=8225;e.dagujarati=2726;e.dagurmukhi=2598;e.dahiragana=12384;e.dakatakana=12480;e.dalarabic=15\ -83;e.dalet=1491;e.daletdagesh=64307;e.daletdageshhebrew=64307;e.dalethebrew=1491;e.dalfinalarabic=65194;e.dammaarabic=1615;e.dammalowarabic=1615;e.dammatanaltonearabic=1612;e.dammatanarabic=1612;e.danda=2404;e.dargahebrew=1447;e.dargalefthebrew=1447;e.dasiapneumatacyrilliccmb=1157;e.dblGrave=63187;e.dblanglebracketleft=12298;e.dblanglebracketleftvertical=65085;e.dblanglebracketright=12299;e.dblanglebracketrightvertical=65086;e.dblarchinvertedbelowcmb=811;e.dblarrowleft=8660;e.dblarrowright=8658;\ -e.dbldanda=2405;e.dblgrave=63190;e.dblgravecmb=783;e.dblintegral=8748;e.dbllowline=8215;e.dbllowlinecmb=819;e.dbloverlinecmb=831;e.dblprimemod=698;e.dblverticalbar=8214;e.dblverticallineabovecmb=782;e.dbopomofo=12553;e.dbsquare=13256;e.dcaron=271;e.dcedilla=7697;e.dcircle=9427;e.dcircumflexbelow=7699;e.dcroat=273;e.ddabengali=2465;e.ddadeva=2337;e.ddagujarati=2721;e.ddagurmukhi=2593;e.ddalarabic=1672;e.ddalfinalarabic=64393;e.dddhadeva=2396;e.ddhabengali=2466;e.ddhadeva=2338;e.ddhagujarati=2722;\ -e.ddhagurmukhi=2594;e.ddotaccent=7691;e.ddotbelow=7693;e.decimalseparatorarabic=1643;e.decimalseparatorpersian=1643;e.decyrillic=1076;e.degree=176;e.dehihebrew=1453;e.dehiragana=12391;e.deicoptic=1007;e.dekatakana=12487;e.deleteleft=9003;e.deleteright=8998;e.delta=948;e.deltaturned=397;e.denominatorminusonenumeratorbengali=2552;e.dezh=676;e.dhabengali=2471;e.dhadeva=2343;e.dhagujarati=2727;e.dhagurmukhi=2599;e.dhook=599;e.dialytikatonos=901;e.dialytikatonoscmb=836;e.diamond=9830;e.diamondsuitwhi\ -te=9826;e.dieresis=168;e.dieresisacute=63191;e.dieresisbelowcmb=804;e.dieresiscmb=776;e.dieresisgrave=63192;e.dieresistonos=901;e.dihiragana=12386;e.dikatakana=12482;e.dittomark=12291;e.divide=247;e.divides=8739;e.divisionslash=8725;e.djecyrillic=1106;e.dkshade=9619;e.dlinebelow=7695;e.dlsquare=13207;e.dmacron=273;e.dmonospace=65348;e.dnblock=9604;e.dochadathai=3598;e.dodekthai=3604;e.dohiragana=12393;e.dokatakana=12489;e.dollar=36;e.dollarinferior=63203;e.dollarmonospace=65284;e.dollaroldstyle=\ -63268;e.dollarsmall=65129;e.dollarsuperior=63204;e.dong=8363;e.dorusquare=13094;e.dotaccent=729;e.dotaccentcmb=775;e.dotbelowcmb=803;e.dotbelowcomb=803;e.dotkatakana=12539;e.dotlessi=305;e.dotlessj=63166;e.dotlessjstrokehook=644;e.dotmath=8901;e.dottedcircle=9676;e.doubleyodpatah=64287;e.doubleyodpatahhebrew=64287;e.downtackbelowcmb=798;e.downtackmod=725;e.dparen=9375;e.dsuperior=63211;e.dtail=598;e.dtopbar=396;e.duhiragana=12389;e.dukatakana=12485;e.dz=499;e.dzaltone=675;e.dzcaron=454;e.dzcurl=\ -677;e.dzeabkhasiancyrillic=1249;e.dzecyrillic=1109;e.dzhecyrillic=1119;e.e=101;e.eacute=233;e.earth=9793;e.ebengali=2447;e.ebopomofo=12572;e.ebreve=277;e.ecandradeva=2317;e.ecandragujarati=2701;e.ecandravowelsigndeva=2373;e.ecandravowelsigngujarati=2757;e.ecaron=283;e.ecedillabreve=7709;e.echarmenian=1381;e.echyiwnarmenian=1415;e.ecircle=9428;e.ecircumflex=234;e.ecircumflexacute=7871;e.ecircumflexbelow=7705;e.ecircumflexdotbelow=7879;e.ecircumflexgrave=7873;e.ecircumflexhookabove=7875;e.ecircumf\ -lextilde=7877;e.ecyrillic=1108;e.edblgrave=517;e.edeva=2319;e.edieresis=235;e.edot=279;e.edotaccent=279;e.edotbelow=7865;e.eegurmukhi=2575;e.eematragurmukhi=2631;e.efcyrillic=1092;e.egrave=232;e.egujarati=2703;e.eharmenian=1383;e.ehbopomofo=12573;e.ehiragana=12360;e.ehookabove=7867;e.eibopomofo=12575;e.eight=56;e.eightarabic=1640;e.eightbengali=2542;e.eightcircle=9319;e.eightcircleinversesansserif=10129;e.eightdeva=2414;e.eighteencircle=9329;e.eighteenparen=9349;e.eighteenperiod=9369;e.eightguja\ -rati=2798;e.eightgurmukhi=2670;e.eighthackarabic=1640;e.eighthangzhou=12328;e.eighthnotebeamed=9835;e.eightideographicparen=12839;e.eightinferior=8328;e.eightmonospace=65304;e.eightoldstyle=63288;e.eightparen=9339;e.eightperiod=9359;e.eightpersian=1784;e.eightroman=8567;e.eightsuperior=8312;e.eightthai=3672;e.einvertedbreve=519;e.eiotifiedcyrillic=1125;e.ekatakana=12456;e.ekatakanahalfwidth=65396;e.ekonkargurmukhi=2676;e.ekorean=12628;e.elcyrillic=1083;e.element=8712;e.elevencircle=9322;e.eleven\ -paren=9342;e.elevenperiod=9362;e.elevenroman=8570;e.ellipsis=8230;e.ellipsisvertical=8942;e.emacron=275;e.emacronacute=7703;e.emacrongrave=7701;e.emcyrillic=1084;e.emdash=8212;e.emdashvertical=65073;e.emonospace=65349;e.emphasismarkarmenian=1371;e.emptyset=8709;e.enbopomofo=12579;e.encyrillic=1085;e.endash=8211;e.endashvertical=65074;e.endescendercyrillic=1187;e.eng=331;e.engbopomofo=12581;e.enghecyrillic=1189;e.enhookcyrillic=1224;e.enspace=8194;e.eogonek=281;e.eokorean=12627;e.eopen=603;e.eope\ -nclosed=666;e.eopenreversed=604;e.eopenreversedclosed=606;e.eopenreversedhook=605;e.eparen=9376;e.epsilon=949;e.epsilontonos=941;e.equal=61;e.equalmonospace=65309;e.equalsmall=65126;e.equalsuperior=8316;e.equivalence=8801;e.erbopomofo=12582;e.ercyrillic=1088;e.ereversed=600;e.ereversedcyrillic=1101;e.escyrillic=1089;e.esdescendercyrillic=1195;e.esh=643;e.eshcurl=646;e.eshortdeva=2318;e.eshortvowelsigndeva=2374;e.eshreversedloop=426;e.eshsquatreversed=645;e.esmallhiragana=12359;e.esmallkatakana=1\ -2455;e.esmallkatakanahalfwidth=65386;e.estimated=8494;e.esuperior=63212;e.eta=951;e.etarmenian=1384;e.etatonos=942;e.eth=240;e.etilde=7869;e.etildebelow=7707;e.etnahtafoukhhebrew=1425;e.etnahtafoukhlefthebrew=1425;e.etnahtahebrew=1425;e.etnahtalefthebrew=1425;e.eturned=477;e.eukorean=12641;e.euro=8364;e.evowelsignbengali=2503;e.evowelsigndeva=2375;e.evowelsigngujarati=2759;e.exclam=33;e.exclamarmenian=1372;e.exclamdbl=8252;e.exclamdown=161;e.exclamdownsmall=63393;e.exclammonospace=65281;e.exclam\ -small=63265;e.existential=8707;e.ezh=658;e.ezhcaron=495;e.ezhcurl=659;e.ezhreversed=441;e.ezhtail=442;e.f=102;e.fadeva=2398;e.fagurmukhi=2654;e.fahrenheit=8457;e.fathaarabic=1614;e.fathalowarabic=1614;e.fathatanarabic=1611;e.fbopomofo=12552;e.fcircle=9429;e.fdotaccent=7711;e.feharabic=1601;e.feharmenian=1414;e.fehfinalarabic=65234;e.fehinitialarabic=65235;e.fehmedialarabic=65236;e.feicoptic=997;e.female=9792;e.ff=64256;e.f_f=64256;e.ffi=64259;e.f_f_i=64259;e.ffl=64260;e.f_f_l=64260;e.fi=64257;e.\ -f_i=64257;e.fifteencircle=9326;e.fifteenparen=9346;e.fifteenperiod=9366;e.figuredash=8210;e.filledbox=9632;e.filledrect=9644;e.finalkaf=1498;e.finalkafdagesh=64314;e.finalkafdageshhebrew=64314;e.finalkafhebrew=1498;e.finalmem=1501;e.finalmemhebrew=1501;e.finalnun=1503;e.finalnunhebrew=1503;e.finalpe=1507;e.finalpehebrew=1507;e.finaltsadi=1509;e.finaltsadihebrew=1509;e.firsttonechinese=713;e.fisheye=9673;e.fitacyrillic=1139;e.five=53;e.fivearabic=1637;e.fivebengali=2539;e.fivecircle=9316;e.fiveci\ -rcleinversesansserif=10126;e.fivedeva=2411;e.fiveeighths=8541;e.fivegujarati=2795;e.fivegurmukhi=2667;e.fivehackarabic=1637;e.fivehangzhou=12325;e.fiveideographicparen=12836;e.fiveinferior=8325;e.fivemonospace=65301;e.fiveoldstyle=63285;e.fiveparen=9336;e.fiveperiod=9356;e.fivepersian=1781;e.fiveroman=8564;e.fivesuperior=8309;e.fivethai=3669;e.fl=64258;e.f_l=64258;e.florin=402;e.fmonospace=65350;e.fmsquare=13209;e.fofanthai=3615;e.fofathai=3613;e.fongmanthai=3663;e.forall=8704;e.four=52;e.fourar\ -abic=1636;e.fourbengali=2538;e.fourcircle=9315;e.fourcircleinversesansserif=10125;e.fourdeva=2410;e.fourgujarati=2794;e.fourgurmukhi=2666;e.fourhackarabic=1636;e.fourhangzhou=12324;e.fourideographicparen=12835;e.fourinferior=8324;e.fourmonospace=65300;e.fournumeratorbengali=2551;e.fouroldstyle=63284;e.fourparen=9335;e.fourperiod=9355;e.fourpersian=1780;e.fourroman=8563;e.foursuperior=8308;e.fourteencircle=9325;e.fourteenparen=9345;e.fourteenperiod=9365;e.fourthai=3668;e.fourthtonechinese=715;e.f\ -paren=9377;e.fraction=8260;e.franc=8355;e.g=103;e.gabengali=2455;e.gacute=501;e.gadeva=2327;e.gafarabic=1711;e.gaffinalarabic=64403;e.gafinitialarabic=64404;e.gafmedialarabic=64405;e.gagujarati=2711;e.gagurmukhi=2583;e.gahiragana=12364;e.gakatakana=12460;e.gamma=947;e.gammalatinsmall=611;e.gammasuperior=736;e.gangiacoptic=1003;e.gbopomofo=12557;e.gbreve=287;e.gcaron=487;e.gcedilla=291;e.gcircle=9430;e.gcircumflex=285;e.gcommaaccent=291;e.gdot=289;e.gdotaccent=289;e.gecyrillic=1075;e.gehiragana=1\ -2370;e.gekatakana=12466;e.geometricallyequal=8785;e.gereshaccenthebrew=1436;e.gereshhebrew=1523;e.gereshmuqdamhebrew=1437;e.germandbls=223;e.gershayimaccenthebrew=1438;e.gershayimhebrew=1524;e.getamark=12307;e.ghabengali=2456;e.ghadarmenian=1394;e.ghadeva=2328;e.ghagujarati=2712;e.ghagurmukhi=2584;e.ghainarabic=1594;e.ghainfinalarabic=65230;e.ghaininitialarabic=65231;e.ghainmedialarabic=65232;e.ghemiddlehookcyrillic=1173;e.ghestrokecyrillic=1171;e.gheupturncyrillic=1169;e.ghhadeva=2394;e.ghhagur\ -mukhi=2650;e.ghook=608;e.ghzsquare=13203;e.gihiragana=12366;e.gikatakana=12462;e.gimarmenian=1379;e.gimel=1490;e.gimeldagesh=64306;e.gimeldageshhebrew=64306;e.gimelhebrew=1490;e.gjecyrillic=1107;e.glottalinvertedstroke=446;e.glottalstop=660;e.glottalstopinverted=662;e.glottalstopmod=704;e.glottalstopreversed=661;e.glottalstopreversedmod=705;e.glottalstopreversedsuperior=740;e.glottalstopstroke=673;e.glottalstopstrokereversed=674;e.gmacron=7713;e.gmonospace=65351;e.gohiragana=12372;e.gokatakana=1\ -2468;e.gparen=9378;e.gpasquare=13228;e.gradient=8711;e.grave=96;e.gravebelowcmb=790;e.gravecmb=768;e.gravecomb=768;e.gravedeva=2387;e.gravelowmod=718;e.gravemonospace=65344;e.gravetonecmb=832;e.greater=62;e.greaterequal=8805;e.greaterequalorless=8923;e.greatermonospace=65310;e.greaterorequivalent=8819;e.greaterorless=8823;e.greateroverequal=8807;e.greatersmall=65125;e.gscript=609;e.gstroke=485;e.guhiragana=12368;e.guillemotleft=171;e.guillemotright=187;e.guilsinglleft=8249;e.guilsinglright=8250;\ -e.gukatakana=12464;e.guramusquare=13080;e.gysquare=13257;e.h=104;e.haabkhasiancyrillic=1193;e.haaltonearabic=1729;e.habengali=2489;e.hadescendercyrillic=1203;e.hadeva=2361;e.hagujarati=2745;e.hagurmukhi=2617;e.haharabic=1581;e.hahfinalarabic=65186;e.hahinitialarabic=65187;e.hahiragana=12399;e.hahmedialarabic=65188;e.haitusquare=13098;e.hakatakana=12495;e.hakatakanahalfwidth=65418;e.halantgurmukhi=2637;e.hamzaarabic=1569;e.hamzalowarabic=1569;e.hangulfiller=12644;e.hardsigncyrillic=1098;e.harpoon\ -leftbarbup=8636;e.harpoonrightbarbup=8640;e.hasquare=13258;e.hatafpatah=1458;e.hatafpatah16=1458;e.hatafpatah23=1458;e.hatafpatah2f=1458;e.hatafpatahhebrew=1458;e.hatafpatahnarrowhebrew=1458;e.hatafpatahquarterhebrew=1458;e.hatafpatahwidehebrew=1458;e.hatafqamats=1459;e.hatafqamats1b=1459;e.hatafqamats28=1459;e.hatafqamats34=1459;e.hatafqamatshebrew=1459;e.hatafqamatsnarrowhebrew=1459;e.hatafqamatsquarterhebrew=1459;e.hatafqamatswidehebrew=1459;e.hatafsegol=1457;e.hatafsegol17=1457;e.hatafsegol2\ -4=1457;e.hatafsegol30=1457;e.hatafsegolhebrew=1457;e.hatafsegolnarrowhebrew=1457;e.hatafsegolquarterhebrew=1457;e.hatafsegolwidehebrew=1457;e.hbar=295;e.hbopomofo=12559;e.hbrevebelow=7723;e.hcedilla=7721;e.hcircle=9431;e.hcircumflex=293;e.hdieresis=7719;e.hdotaccent=7715;e.hdotbelow=7717;e.he=1492;e.heart=9829;e.heartsuitblack=9829;e.heartsuitwhite=9825;e.hedagesh=64308;e.hedageshhebrew=64308;e.hehaltonearabic=1729;e.heharabic=1607;e.hehebrew=1492;e.hehfinalaltonearabic=64423;e.hehfinalalttwoara\ -bic=65258;e.hehfinalarabic=65258;e.hehhamzaabovefinalarabic=64421;e.hehhamzaaboveisolatedarabic=64420;e.hehinitialaltonearabic=64424;e.hehinitialarabic=65259;e.hehiragana=12408;e.hehmedialaltonearabic=64425;e.hehmedialarabic=65260;e.heiseierasquare=13179;e.hekatakana=12504;e.hekatakanahalfwidth=65421;e.hekutaarusquare=13110;e.henghook=615;e.herutusquare=13113;e.het=1495;e.hethebrew=1495;e.hhook=614;e.hhooksuperior=689;e.hieuhacirclekorean=12923;e.hieuhaparenkorean=12827;e.hieuhcirclekorean=12909\ -;e.hieuhkorean=12622;e.hieuhparenkorean=12813;e.hihiragana=12402;e.hikatakana=12498;e.hikatakanahalfwidth=65419;e.hiriq=1460;e.hiriq14=1460;e.hiriq21=1460;e.hiriq2d=1460;e.hiriqhebrew=1460;e.hiriqnarrowhebrew=1460;e.hiriqquarterhebrew=1460;e.hiriqwidehebrew=1460;e.hlinebelow=7830;e.hmonospace=65352;e.hoarmenian=1392;e.hohipthai=3627;e.hohiragana=12411;e.hokatakana=12507;e.hokatakanahalfwidth=65422;e.holam=1465;e.holam19=1465;e.holam26=1465;e.holam32=1465;e.holamhebrew=1465;e.holamnarrowhebrew=14\ -65;e.holamquarterhebrew=1465;e.holamwidehebrew=1465;e.honokhukthai=3630;e.hookabovecomb=777;e.hookcmb=777;e.hookpalatalizedbelowcmb=801;e.hookretroflexbelowcmb=802;e.hoonsquare=13122;e.horicoptic=1001;e.horizontalbar=8213;e.horncmb=795;e.hotsprings=9832;e.house=8962;e.hparen=9379;e.hsuperior=688;e.hturned=613;e.huhiragana=12405;e.huiitosquare=13107;e.hukatakana=12501;e.hukatakanahalfwidth=65420;e.hungarumlaut=733;e.hungarumlautcmb=779;e.hv=405;e.hyphen=45;e.hypheninferior=63205;e.hyphenmonospace\ -=65293;e.hyphensmall=65123;e.hyphensuperior=63206;e.hyphentwo=8208;e.i=105;e.iacute=237;e.iacyrillic=1103;e.ibengali=2439;e.ibopomofo=12583;e.ibreve=301;e.icaron=464;e.icircle=9432;e.icircumflex=238;e.icyrillic=1110;e.idblgrave=521;e.ideographearthcircle=12943;e.ideographfirecircle=12939;e.ideographicallianceparen=12863;e.ideographiccallparen=12858;e.ideographiccentrecircle=12965;e.ideographicclose=12294;e.ideographiccomma=12289;e.ideographiccommaleft=65380;e.ideographiccongratulationparen=12855\ -;e.ideographiccorrectcircle=12963;e.ideographicearthparen=12847;e.ideographicenterpriseparen=12861;e.ideographicexcellentcircle=12957;e.ideographicfestivalparen=12864;e.ideographicfinancialcircle=12950;e.ideographicfinancialparen=12854;e.ideographicfireparen=12843;e.ideographichaveparen=12850;e.ideographichighcircle=12964;e.ideographiciterationmark=12293;e.ideographiclaborcircle=12952;e.ideographiclaborparen=12856;e.ideographicleftcircle=12967;e.ideographiclowcircle=12966;e.ideographicmedicineci\ -rcle=12969;e.ideographicmetalparen=12846;e.ideographicmoonparen=12842;e.ideographicnameparen=12852;e.ideographicperiod=12290;e.ideographicprintcircle=12958;e.ideographicreachparen=12867;e.ideographicrepresentparen=12857;e.ideographicresourceparen=12862;e.ideographicrightcircle=12968;e.ideographicsecretcircle=12953;e.ideographicselfparen=12866;e.ideographicsocietyparen=12851;e.ideographicspace=12288;e.ideographicspecialparen=12853;e.ideographicstockparen=12849;e.ideographicstudyparen=12859;e.ideo\ -graphicsunparen=12848;e.ideographicsuperviseparen=12860;e.ideographicwaterparen=12844;e.ideographicwoodparen=12845;e.ideographiczero=12295;e.ideographmetalcircle=12942;e.ideographmooncircle=12938;e.ideographnamecircle=12948;e.ideographsuncircle=12944;e.ideographwatercircle=12940;e.ideographwoodcircle=12941;e.ideva=2311;e.idieresis=239;e.idieresisacute=7727;e.idieresiscyrillic=1253;e.idotbelow=7883;e.iebrevecyrillic=1239;e.iecyrillic=1077;e.ieungacirclekorean=12917;e.ieungaparenkorean=12821;e.ieu\ -ngcirclekorean=12903;e.ieungkorean=12615;e.ieungparenkorean=12807;e.igrave=236;e.igujarati=2695;e.igurmukhi=2567;e.ihiragana=12356;e.ihookabove=7881;e.iibengali=2440;e.iicyrillic=1080;e.iideva=2312;e.iigujarati=2696;e.iigurmukhi=2568;e.iimatragurmukhi=2624;e.iinvertedbreve=523;e.iishortcyrillic=1081;e.iivowelsignbengali=2496;e.iivowelsigndeva=2368;e.iivowelsigngujarati=2752;e.ij=307;e.ikatakana=12452;e.ikatakanahalfwidth=65394;e.ikorean=12643;e.ilde=732;e.iluyhebrew=1452;e.imacron=299;e.imacronc\ -yrillic=1251;e.imageorapproximatelyequal=8787;e.imatragurmukhi=2623;e.imonospace=65353;e.increment=8710;e.infinity=8734;e.iniarmenian=1387;e.integral=8747;e.integralbottom=8993;e.integralbt=8993;e.integralex=63733;e.integraltop=8992;e.integraltp=8992;e.intersection=8745;e.intisquare=13061;e.invbullet=9688;e.invcircle=9689;e.invsmileface=9787;e.iocyrillic=1105;e.iogonek=303;e.iota=953;e.iotadieresis=970;e.iotadieresistonos=912;e.iotalatin=617;e.iotatonos=943;e.iparen=9380;e.irigurmukhi=2674;e.ism\ -allhiragana=12355;e.ismallkatakana=12451;e.ismallkatakanahalfwidth=65384;e.issharbengali=2554;e.istroke=616;e.isuperior=63213;e.iterationhiragana=12445;e.iterationkatakana=12541;e.itilde=297;e.itildebelow=7725;e.iubopomofo=12585;e.iucyrillic=1102;e.ivowelsignbengali=2495;e.ivowelsigndeva=2367;e.ivowelsigngujarati=2751;e.izhitsacyrillic=1141;e.izhitsadblgravecyrillic=1143;e.j=106;e.jaarmenian=1393;e.jabengali=2460;e.jadeva=2332;e.jagujarati=2716;e.jagurmukhi=2588;e.jbopomofo=12560;e.jcaron=496;e.\ -jcircle=9433;e.jcircumflex=309;e.jcrossedtail=669;e.jdotlessstroke=607;e.jecyrillic=1112;e.jeemarabic=1580;e.jeemfinalarabic=65182;e.jeeminitialarabic=65183;e.jeemmedialarabic=65184;e.jeharabic=1688;e.jehfinalarabic=64395;e.jhabengali=2461;e.jhadeva=2333;e.jhagujarati=2717;e.jhagurmukhi=2589;e.jheharmenian=1403;e.jis=12292;e.jmonospace=65354;e.jparen=9381;e.jsuperior=690;e.k=107;e.kabashkircyrillic=1185;e.kabengali=2453;e.kacute=7729;e.kacyrillic=1082;e.kadescendercyrillic=1179;e.kadeva=2325;e.k\ -af=1499;e.kafarabic=1603;e.kafdagesh=64315;e.kafdageshhebrew=64315;e.kaffinalarabic=65242;e.kafhebrew=1499;e.kafinitialarabic=65243;e.kafmedialarabic=65244;e.kafrafehebrew=64333;e.kagujarati=2709;e.kagurmukhi=2581;e.kahiragana=12363;e.kahookcyrillic=1220;e.kakatakana=12459;e.kakatakanahalfwidth=65398;e.kappa=954;e.kappasymbolgreek=1008;e.kapyeounmieumkorean=12657;e.kapyeounphieuphkorean=12676;e.kapyeounpieupkorean=12664;e.kapyeounssangpieupkorean=12665;e.karoriisquare=13069;e.kashidaautoarabic=1\ -600;e.kashidaautonosidebearingarabic=1600;e.kasmallkatakana=12533;e.kasquare=13188;e.kasraarabic=1616;e.kasratanarabic=1613;e.kastrokecyrillic=1183;e.katahiraprolongmarkhalfwidth=65392;e.kaverticalstrokecyrillic=1181;e.kbopomofo=12558;e.kcalsquare=13193;e.kcaron=489;e.kcedilla=311;e.kcircle=9434;e.kcommaaccent=311;e.kdotbelow=7731;e.keharmenian=1412;e.kehiragana=12369;e.kekatakana=12465;e.kekatakanahalfwidth=65401;e.kenarmenian=1391;e.kesmallkatakana=12534;e.kgreenlandic=312;e.khabengali=2454;e.\ -khacyrillic=1093;e.khadeva=2326;e.khagujarati=2710;e.khagurmukhi=2582;e.khaharabic=1582;e.khahfinalarabic=65190;e.khahinitialarabic=65191;e.khahmedialarabic=65192;e.kheicoptic=999;e.khhadeva=2393;e.khhagurmukhi=2649;e.khieukhacirclekorean=12920;e.khieukhaparenkorean=12824;e.khieukhcirclekorean=12906;e.khieukhkorean=12619;e.khieukhparenkorean=12810;e.khokhaithai=3586;e.khokhonthai=3589;e.khokhuatthai=3587;e.khokhwaithai=3588;e.khomutthai=3675;e.khook=409;e.khorakhangthai=3590;e.khzsquare=13201;e.\ -kihiragana=12365;e.kikatakana=12461;e.kikatakanahalfwidth=65399;e.kiroguramusquare=13077;e.kiromeetorusquare=13078;e.kirosquare=13076;e.kiyeokacirclekorean=12910;e.kiyeokaparenkorean=12814;e.kiyeokcirclekorean=12896;e.kiyeokkorean=12593;e.kiyeokparenkorean=12800;e.kiyeoksioskorean=12595;e.kjecyrillic=1116;e.klinebelow=7733;e.klsquare=13208;e.kmcubedsquare=13222;e.kmonospace=65355;e.kmsquaredsquare=13218;e.kohiragana=12371;e.kohmsquare=13248;e.kokaithai=3585;e.kokatakana=12467;e.kokatakanahalfwid\ -th=65402;e.kooposquare=13086;e.koppacyrillic=1153;e.koreanstandardsymbol=12927;e.koroniscmb=835;e.kparen=9382;e.kpasquare=13226;e.ksicyrillic=1135;e.ktsquare=13263;e.kturned=670;e.kuhiragana=12367;e.kukatakana=12463;e.kukatakanahalfwidth=65400;e.kvsquare=13240;e.kwsquare=13246;e.l=108;e.labengali=2482;e.lacute=314;e.ladeva=2354;e.lagujarati=2738;e.lagurmukhi=2610;e.lakkhangyaothai=3653;e.lamaleffinalarabic=65276;e.lamalefhamzaabovefinalarabic=65272;e.lamalefhamzaaboveisolatedarabic=65271;e.lamal\ -efhamzabelowfinalarabic=65274;e.lamalefhamzabelowisolatedarabic=65273;e.lamalefisolatedarabic=65275;e.lamalefmaddaabovefinalarabic=65270;e.lamalefmaddaaboveisolatedarabic=65269;e.lamarabic=1604;e.lambda=955;e.lambdastroke=411;e.lamed=1500;e.lameddagesh=64316;e.lameddageshhebrew=64316;e.lamedhebrew=1500;e.lamfinalarabic=65246;e.lamhahinitialarabic=64714;e.laminitialarabic=65247;e.lamjeeminitialarabic=64713;e.lamkhahinitialarabic=64715;e.lamlamhehisolatedarabic=65010;e.lammedialarabic=65248;e.lamm\ -eemhahinitialarabic=64904;e.lammeeminitialarabic=64716;e.largecircle=9711;e.lbar=410;e.lbelt=620;e.lbopomofo=12556;e.lcaron=318;e.lcedilla=316;e.lcircle=9435;e.lcircumflexbelow=7741;e.lcommaaccent=316;e.ldot=320;e.ldotaccent=320;e.ldotbelow=7735;e.ldotbelowmacron=7737;e.leftangleabovecmb=794;e.lefttackbelowcmb=792;e.less=60;e.lessequal=8804;e.lessequalorgreater=8922;e.lessmonospace=65308;e.lessorequivalent=8818;e.lessorgreater=8822;e.lessoverequal=8806;e.lesssmall=65124;e.lezh=622;e.lfblock=9612\ -;e.lhookretroflex=621;e.lira=8356;e.liwnarmenian=1388;e.lj=457;e.ljecyrillic=1113;e.ll=63168;e.lladeva=2355;e.llagujarati=2739;e.llinebelow=7739;e.llladeva=2356;e.llvocalicbengali=2529;e.llvocalicdeva=2401;e.llvocalicvowelsignbengali=2531;e.llvocalicvowelsigndeva=2403;e.lmiddletilde=619;e.lmonospace=65356;e.lmsquare=13264;e.lochulathai=3628;e.logicaland=8743;e.logicalnot=172;e.logicalnotreversed=8976;e.logicalor=8744;e.lolingthai=3621;e.longs=383;e.lowlinecenterline=65102;e.lowlinecmb=818;e.lowl\ -inedashed=65101;e.lozenge=9674;e.lparen=9383;e.lslash=322;e.lsquare=8467;e.lsuperior=63214;e.ltshade=9617;e.luthai=3622;e.lvocalicbengali=2444;e.lvocalicdeva=2316;e.lvocalicvowelsignbengali=2530;e.lvocalicvowelsigndeva=2402;e.lxsquare=13267;e.m=109;e.mabengali=2478;e.macron=175;e.macronbelowcmb=817;e.macroncmb=772;e.macronlowmod=717;e.macronmonospace=65507;e.macute=7743;e.madeva=2350;e.magujarati=2734;e.magurmukhi=2606;e.mahapakhhebrew=1444;e.mahapakhlefthebrew=1444;e.mahiragana=12414;e.maichatt\ -awalowleftthai=63637;e.maichattawalowrightthai=63636;e.maichattawathai=3659;e.maichattawaupperleftthai=63635;e.maieklowleftthai=63628;e.maieklowrightthai=63627;e.maiekthai=3656;e.maiekupperleftthai=63626;e.maihanakatleftthai=63620;e.maihanakatthai=3633;e.maitaikhuleftthai=63625;e.maitaikhuthai=3655;e.maitholowleftthai=63631;e.maitholowrightthai=63630;e.maithothai=3657;e.maithoupperleftthai=63629;e.maitrilowleftthai=63634;e.maitrilowrightthai=63633;e.maitrithai=3658;e.maitriupperleftthai=63632;e.\ -maiyamokthai=3654;e.makatakana=12510;e.makatakanahalfwidth=65423;e.male=9794;e.mansyonsquare=13127;e.maqafhebrew=1470;e.mars=9794;e.masoracirclehebrew=1455;e.masquare=13187;e.mbopomofo=12551;e.mbsquare=13268;e.mcircle=9436;e.mcubedsquare=13221;e.mdotaccent=7745;e.mdotbelow=7747;e.meemarabic=1605;e.meemfinalarabic=65250;e.meeminitialarabic=65251;e.meemmedialarabic=65252;e.meemmeeminitialarabic=64721;e.meemmeemisolatedarabic=64584;e.meetorusquare=13133;e.mehiragana=12417;e.meizierasquare=13182;e.m\ -ekatakana=12513;e.mekatakanahalfwidth=65426;e.mem=1502;e.memdagesh=64318;e.memdageshhebrew=64318;e.memhebrew=1502;e.menarmenian=1396;e.merkhahebrew=1445;e.merkhakefulahebrew=1446;e.merkhakefulalefthebrew=1446;e.merkhalefthebrew=1445;e.mhook=625;e.mhzsquare=13202;e.middledotkatakanahalfwidth=65381;e.middot=183;e.mieumacirclekorean=12914;e.mieumaparenkorean=12818;e.mieumcirclekorean=12900;e.mieumkorean=12609;e.mieumpansioskorean=12656;e.mieumparenkorean=12804;e.mieumpieupkorean=12654;e.mieumsiosko\ -rean=12655;e.mihiragana=12415;e.mikatakana=12511;e.mikatakanahalfwidth=65424;e.minus=8722;e.minusbelowcmb=800;e.minuscircle=8854;e.minusmod=727;e.minusplus=8723;e.minute=8242;e.miribaarusquare=13130;e.mirisquare=13129;e.mlonglegturned=624;e.mlsquare=13206;e.mmcubedsquare=13219;e.mmonospace=65357;e.mmsquaredsquare=13215;e.mohiragana=12418;e.mohmsquare=13249;e.mokatakana=12514;e.mokatakanahalfwidth=65427;e.molsquare=13270;e.momathai=3617;e.moverssquare=13223;e.moverssquaredsquare=13224;e.mparen=93\ -84;e.mpasquare=13227;e.mssquare=13235;e.msuperior=63215;e.mturned=623;e.mu=181;e.mu1=181;e.muasquare=13186;e.muchgreater=8811;e.muchless=8810;e.mufsquare=13196;e.mugreek=956;e.mugsquare=13197;e.muhiragana=12416;e.mukatakana=12512;e.mukatakanahalfwidth=65425;e.mulsquare=13205;e.multiply=215;e.mumsquare=13211;e.munahhebrew=1443;e.munahlefthebrew=1443;e.musicalnote=9834;e.musicalnotedbl=9835;e.musicflatsign=9837;e.musicsharpsign=9839;e.mussquare=13234;e.muvsquare=13238;e.muwsquare=13244;e.mvmegasqu\ -are=13241;e.mvsquare=13239;e.mwmegasquare=13247;e.mwsquare=13245;e.n=110;e.nabengali=2472;e.nabla=8711;e.nacute=324;e.nadeva=2344;e.nagujarati=2728;e.nagurmukhi=2600;e.nahiragana=12394;e.nakatakana=12490;e.nakatakanahalfwidth=65413;e.napostrophe=329;e.nasquare=13185;e.nbopomofo=12555;e.nbspace=160;e.ncaron=328;e.ncedilla=326;e.ncircle=9437;e.ncircumflexbelow=7755;e.ncommaaccent=326;e.ndotaccent=7749;e.ndotbelow=7751;e.nehiragana=12397;e.nekatakana=12493;e.nekatakanahalfwidth=65416;e.newsheqelsig\ -n=8362;e.nfsquare=13195;e.ngabengali=2457;e.ngadeva=2329;e.ngagujarati=2713;e.ngagurmukhi=2585;e.ngonguthai=3591;e.nhiragana=12435;e.nhookleft=626;e.nhookretroflex=627;e.nieunacirclekorean=12911;e.nieunaparenkorean=12815;e.nieuncieuckorean=12597;e.nieuncirclekorean=12897;e.nieunhieuhkorean=12598;e.nieunkorean=12596;e.nieunpansioskorean=12648;e.nieunparenkorean=12801;e.nieunsioskorean=12647;e.nieuntikeutkorean=12646;e.nihiragana=12395;e.nikatakana=12491;e.nikatakanahalfwidth=65414;e.nikhahitleftt\ -hai=63641;e.nikhahitthai=3661;e.nine=57;e.ninearabic=1641;e.ninebengali=2543;e.ninecircle=9320;e.ninecircleinversesansserif=10130;e.ninedeva=2415;e.ninegujarati=2799;e.ninegurmukhi=2671;e.ninehackarabic=1641;e.ninehangzhou=12329;e.nineideographicparen=12840;e.nineinferior=8329;e.ninemonospace=65305;e.nineoldstyle=63289;e.nineparen=9340;e.nineperiod=9360;e.ninepersian=1785;e.nineroman=8568;e.ninesuperior=8313;e.nineteencircle=9330;e.nineteenparen=9350;e.nineteenperiod=9370;e.ninethai=3673;e.nj=46\ -0;e.njecyrillic=1114;e.nkatakana=12531;e.nkatakanahalfwidth=65437;e.nlegrightlong=414;e.nlinebelow=7753;e.nmonospace=65358;e.nmsquare=13210;e.nnabengali=2467;e.nnadeva=2339;e.nnagujarati=2723;e.nnagurmukhi=2595;e.nnnadeva=2345;e.nohiragana=12398;e.nokatakana=12494;e.nokatakanahalfwidth=65417;e.nonbreakingspace=160;e.nonenthai=3603;e.nonuthai=3609;e.noonarabic=1606;e.noonfinalarabic=65254;e.noonghunnaarabic=1722;e.noonghunnafinalarabic=64415;e.nooninitialarabic=65255;e.noonjeeminitialarabic=64722\ -;e.noonjeemisolatedarabic=64587;e.noonmedialarabic=65256;e.noonmeeminitialarabic=64725;e.noonmeemisolatedarabic=64590;e.noonnoonfinalarabic=64653;e.notcontains=8716;e.notelement=8713;e.notelementof=8713;e.notequal=8800;e.notgreater=8815;e.notgreaternorequal=8817;e.notgreaternorless=8825;e.notidentical=8802;e.notless=8814;e.notlessnorequal=8816;e.notparallel=8742;e.notprecedes=8832;e.notsubset=8836;e.notsucceeds=8833;e.notsuperset=8837;e.nowarmenian=1398;e.nparen=9385;e.nssquare=13233;e.nsuperior\ -=8319;e.ntilde=241;e.nu=957;e.nuhiragana=12396;e.nukatakana=12492;e.nukatakanahalfwidth=65415;e.nuktabengali=2492;e.nuktadeva=2364;e.nuktagujarati=2748;e.nuktagurmukhi=2620;e.numbersign=35;e.numbersignmonospace=65283;e.numbersignsmall=65119;e.numeralsigngreek=884;e.numeralsignlowergreek=885;e.numero=8470;e.nun=1504;e.nundagesh=64320;e.nundageshhebrew=64320;e.nunhebrew=1504;e.nvsquare=13237;e.nwsquare=13243;e.nyabengali=2462;e.nyadeva=2334;e.nyagujarati=2718;e.nyagurmukhi=2590;e.o=111;e.oacute=24\ -3;e.oangthai=3629;e.obarred=629;e.obarredcyrillic=1257;e.obarreddieresiscyrillic=1259;e.obengali=2451;e.obopomofo=12571;e.obreve=335;e.ocandradeva=2321;e.ocandragujarati=2705;e.ocandravowelsigndeva=2377;e.ocandravowelsigngujarati=2761;e.ocaron=466;e.ocircle=9438;e.ocircumflex=244;e.ocircumflexacute=7889;e.ocircumflexdotbelow=7897;e.ocircumflexgrave=7891;e.ocircumflexhookabove=7893;e.ocircumflextilde=7895;e.ocyrillic=1086;e.odblacute=337;e.odblgrave=525;e.odeva=2323;e.odieresis=246;e.odieresiscyr\ -illic=1255;e.odotbelow=7885;e.oe=339;e.oekorean=12634;e.ogonek=731;e.ogonekcmb=808;e.ograve=242;e.ogujarati=2707;e.oharmenian=1413;e.ohiragana=12362;e.ohookabove=7887;e.ohorn=417;e.ohornacute=7899;e.ohorndotbelow=7907;e.ohorngrave=7901;e.ohornhookabove=7903;e.ohorntilde=7905;e.ohungarumlaut=337;e.oi=419;e.oinvertedbreve=527;e.okatakana=12458;e.okatakanahalfwidth=65397;e.okorean=12631;e.olehebrew=1451;e.omacron=333;e.omacronacute=7763;e.omacrongrave=7761;e.omdeva=2384;e.omega=969;e.omega1=982;e.o\ -megacyrillic=1121;e.omegalatinclosed=631;e.omegaroundcyrillic=1147;e.omegatitlocyrillic=1149;e.omegatonos=974;e.omgujarati=2768;e.omicron=959;e.omicrontonos=972;e.omonospace=65359;e.one=49;e.onearabic=1633;e.onebengali=2535;e.onecircle=9312;e.onecircleinversesansserif=10122;e.onedeva=2407;e.onedotenleader=8228;e.oneeighth=8539;e.onefitted=63196;e.onegujarati=2791;e.onegurmukhi=2663;e.onehackarabic=1633;e.onehalf=189;e.onehangzhou=12321;e.oneideographicparen=12832;e.oneinferior=8321;e.onemonospac\ -e=65297;e.onenumeratorbengali=2548;e.oneoldstyle=63281;e.oneparen=9332;e.oneperiod=9352;e.onepersian=1777;e.onequarter=188;e.oneroman=8560;e.onesuperior=185;e.onethai=3665;e.onethird=8531;e.oogonek=491;e.oogonekmacron=493;e.oogurmukhi=2579;e.oomatragurmukhi=2635;e.oopen=596;e.oparen=9386;e.openbullet=9702;e.option=8997;e.ordfeminine=170;e.ordmasculine=186;e.orthogonal=8735;e.oshortdeva=2322;e.oshortvowelsigndeva=2378;e.oslash=248;e.oslashacute=511;e.osmallhiragana=12361;e.osmallkatakana=12457;e.\ -osmallkatakanahalfwidth=65387;e.ostrokeacute=511;e.osuperior=63216;e.otcyrillic=1151;e.otilde=245;e.otildeacute=7757;e.otildedieresis=7759;e.oubopomofo=12577;e.overline=8254;e.overlinecenterline=65098;e.overlinecmb=773;e.overlinedashed=65097;e.overlinedblwavy=65100;e.overlinewavy=65099;e.overscore=175;e.ovowelsignbengali=2507;e.ovowelsigndeva=2379;e.ovowelsigngujarati=2763;e.p=112;e.paampssquare=13184;e.paasentosquare=13099;e.pabengali=2474;e.pacute=7765;e.padeva=2346;e.pagedown=8671;e.pageup=86\ -70;e.pagujarati=2730;e.pagurmukhi=2602;e.pahiragana=12401;e.paiyannoithai=3631;e.pakatakana=12497;e.palatalizationcyrilliccmb=1156;e.palochkacyrillic=1216;e.pansioskorean=12671;e.paragraph=182;e.parallel=8741;e.parenleft=40;e.parenleftaltonearabic=64830;e.parenleftbt=63725;e.parenleftex=63724;e.parenleftinferior=8333;e.parenleftmonospace=65288;e.parenleftsmall=65113;e.parenleftsuperior=8317;e.parenlefttp=63723;e.parenleftvertical=65077;e.parenright=41;e.parenrightaltonearabic=64831;e.parenrightb\ -t=63736;e.parenrightex=63735;e.parenrightinferior=8334;e.parenrightmonospace=65289;e.parenrightsmall=65114;e.parenrightsuperior=8318;e.parenrighttp=63734;e.parenrightvertical=65078;e.partialdiff=8706;e.paseqhebrew=1472;e.pashtahebrew=1433;e.pasquare=13225;e.patah=1463;e.patah11=1463;e.patah1d=1463;e.patah2a=1463;e.patahhebrew=1463;e.patahnarrowhebrew=1463;e.patahquarterhebrew=1463;e.patahwidehebrew=1463;e.pazerhebrew=1441;e.pbopomofo=12550;e.pcircle=9439;e.pdotaccent=7767;e.pe=1508;e.pecyrillic=\ -1087;e.pedagesh=64324;e.pedageshhebrew=64324;e.peezisquare=13115;e.pefinaldageshhebrew=64323;e.peharabic=1662;e.peharmenian=1402;e.pehebrew=1508;e.pehfinalarabic=64343;e.pehinitialarabic=64344;e.pehiragana=12410;e.pehmedialarabic=64345;e.pekatakana=12506;e.pemiddlehookcyrillic=1191;e.perafehebrew=64334;e.percent=37;e.percentarabic=1642;e.percentmonospace=65285;e.percentsmall=65130;e.period=46;e.periodarmenian=1417;e.periodcentered=183;e.periodhalfwidth=65377;e.periodinferior=63207;e.periodmonosp\ -ace=65294;e.periodsmall=65106;e.periodsuperior=63208;e.perispomenigreekcmb=834;e.perpendicular=8869;e.perthousand=8240;e.peseta=8359;e.pfsquare=13194;e.phabengali=2475;e.phadeva=2347;e.phagujarati=2731;e.phagurmukhi=2603;e.phi=966;e.phi1=981;e.phieuphacirclekorean=12922;e.phieuphaparenkorean=12826;e.phieuphcirclekorean=12908;e.phieuphkorean=12621;e.phieuphparenkorean=12812;e.philatin=632;e.phinthuthai=3642;e.phisymbolgreek=981;e.phook=421;e.phophanthai=3614;e.phophungthai=3612;e.phosamphaothai=3\ -616;e.pi=960;e.pieupacirclekorean=12915;e.pieupaparenkorean=12819;e.pieupcieuckorean=12662;e.pieupcirclekorean=12901;e.pieupkiyeokkorean=12658;e.pieupkorean=12610;e.pieupparenkorean=12805;e.pieupsioskiyeokkorean=12660;e.pieupsioskorean=12612;e.pieupsiostikeutkorean=12661;e.pieupthieuthkorean=12663;e.pieuptikeutkorean=12659;e.pihiragana=12404;e.pikatakana=12500;e.pisymbolgreek=982;e.piwrarmenian=1411;e.planckover2pi=8463;e.planckover2pi1=8463;e.plus=43;e.plusbelowcmb=799;e.pluscircle=8853;e.plusm\ -inus=177;e.plusmod=726;e.plusmonospace=65291;e.plussmall=65122;e.plussuperior=8314;e.pmonospace=65360;e.pmsquare=13272;e.pohiragana=12413;e.pointingindexdownwhite=9759;e.pointingindexleftwhite=9756;e.pointingindexrightwhite=9758;e.pointingindexupwhite=9757;e.pokatakana=12509;e.poplathai=3611;e.postalmark=12306;e.postalmarkface=12320;e.pparen=9387;e.precedes=8826;e.prescription=8478;e.primemod=697;e.primereversed=8245;e.product=8719;e.projective=8965;e.prolongedkana=12540;e.propellor=8984;e.prope\ -rsubset=8834;e.propersuperset=8835;e.proportion=8759;e.proportional=8733;e.psi=968;e.psicyrillic=1137;e.psilipneumatacyrilliccmb=1158;e.pssquare=13232;e.puhiragana=12407;e.pukatakana=12503;e.pvsquare=13236;e.pwsquare=13242;e.q=113;e.qadeva=2392;e.qadmahebrew=1448;e.qafarabic=1602;e.qaffinalarabic=65238;e.qafinitialarabic=65239;e.qafmedialarabic=65240;e.qamats=1464;e.qamats10=1464;e.qamats1a=1464;e.qamats1c=1464;e.qamats27=1464;e.qamats29=1464;e.qamats33=1464;e.qamatsde=1464;e.qamatshebrew=1464;e\ -.qamatsnarrowhebrew=1464;e.qamatsqatanhebrew=1464;e.qamatsqatannarrowhebrew=1464;e.qamatsqatanquarterhebrew=1464;e.qamatsqatanwidehebrew=1464;e.qamatsquarterhebrew=1464;e.qamatswidehebrew=1464;e.qarneyparahebrew=1439;e.qbopomofo=12561;e.qcircle=9440;e.qhook=672;e.qmonospace=65361;e.qof=1511;e.qofdagesh=64327;e.qofdageshhebrew=64327;e.qofhebrew=1511;e.qparen=9388;e.quarternote=9833;e.qubuts=1467;e.qubuts18=1467;e.qubuts25=1467;e.qubuts31=1467;e.qubutshebrew=1467;e.qubutsnarrowhebrew=1467;e.qubuts\ -quarterhebrew=1467;e.qubutswidehebrew=1467;e.question=63;e.questionarabic=1567;e.questionarmenian=1374;e.questiondown=191;e.questiondownsmall=63423;e.questiongreek=894;e.questionmonospace=65311;e.questionsmall=63295;e.quotedbl=34;e.quotedblbase=8222;e.quotedblleft=8220;e.quotedblmonospace=65282;e.quotedblprime=12318;e.quotedblprimereversed=12317;e.quotedblright=8221;e.quoteleft=8216;e.quoteleftreversed=8219;e.quotereversed=8219;e.quoteright=8217;e.quoterightn=329;e.quotesinglbase=8218;e.quotesin\ -gle=39;e.quotesinglemonospace=65287;e.r=114;e.raarmenian=1404;e.rabengali=2480;e.racute=341;e.radeva=2352;e.radical=8730;e.radicalex=63717;e.radoverssquare=13230;e.radoverssquaredsquare=13231;e.radsquare=13229;e.rafe=1471;e.rafehebrew=1471;e.ragujarati=2736;e.ragurmukhi=2608;e.rahiragana=12425;e.rakatakana=12521;e.rakatakanahalfwidth=65431;e.ralowerdiagonalbengali=2545;e.ramiddlediagonalbengali=2544;e.ramshorn=612;e.ratio=8758;e.rbopomofo=12566;e.rcaron=345;e.rcedilla=343;e.rcircle=9441;e.rcomma\ -accent=343;e.rdblgrave=529;e.rdotaccent=7769;e.rdotbelow=7771;e.rdotbelowmacron=7773;e.referencemark=8251;e.reflexsubset=8838;e.reflexsuperset=8839;e.registered=174;e.registersans=63720;e.registerserif=63194;e.reharabic=1585;e.reharmenian=1408;e.rehfinalarabic=65198;e.rehiragana=12428;e.rekatakana=12524;e.rekatakanahalfwidth=65434;e.resh=1512;e.reshdageshhebrew=64328;e.reshhebrew=1512;e.reversedtilde=8765;e.reviahebrew=1431;e.reviamugrashhebrew=1431;e.revlogicalnot=8976;e.rfishhook=638;e.rfishho\ -okreversed=639;e.rhabengali=2525;e.rhadeva=2397;e.rho=961;e.rhook=637;e.rhookturned=635;e.rhookturnedsuperior=693;e.rhosymbolgreek=1009;e.rhotichookmod=734;e.rieulacirclekorean=12913;e.rieulaparenkorean=12817;e.rieulcirclekorean=12899;e.rieulhieuhkorean=12608;e.rieulkiyeokkorean=12602;e.rieulkiyeoksioskorean=12649;e.rieulkorean=12601;e.rieulmieumkorean=12603;e.rieulpansioskorean=12652;e.rieulparenkorean=12803;e.rieulphieuphkorean=12607;e.rieulpieupkorean=12604;e.rieulpieupsioskorean=12651;e.rieu\ -lsioskorean=12605;e.rieulthieuthkorean=12606;e.rieultikeutkorean=12650;e.rieulyeorinhieuhkorean=12653;e.rightangle=8735;e.righttackbelowcmb=793;e.righttriangle=8895;e.rihiragana=12426;e.rikatakana=12522;e.rikatakanahalfwidth=65432;e.ring=730;e.ringbelowcmb=805;e.ringcmb=778;e.ringhalfleft=703;e.ringhalfleftarmenian=1369;e.ringhalfleftbelowcmb=796;e.ringhalfleftcentered=723;e.ringhalfright=702;e.ringhalfrightbelowcmb=825;e.ringhalfrightcentered=722;e.rinvertedbreve=531;e.rittorusquare=13137;e.rli\ -nebelow=7775;e.rlongleg=636;e.rlonglegturned=634;e.rmonospace=65362;e.rohiragana=12429;e.rokatakana=12525;e.rokatakanahalfwidth=65435;e.roruathai=3619;e.rparen=9389;e.rrabengali=2524;e.rradeva=2353;e.rragurmukhi=2652;e.rreharabic=1681;e.rrehfinalarabic=64397;e.rrvocalicbengali=2528;e.rrvocalicdeva=2400;e.rrvocalicgujarati=2784;e.rrvocalicvowelsignbengali=2500;e.rrvocalicvowelsigndeva=2372;e.rrvocalicvowelsigngujarati=2756;e.rsuperior=63217;e.rtblock=9616;e.rturned=633;e.rturnedsuperior=692;e.ruh\ -iragana=12427;e.rukatakana=12523;e.rukatakanahalfwidth=65433;e.rupeemarkbengali=2546;e.rupeesignbengali=2547;e.rupiah=63197;e.ruthai=3620;e.rvocalicbengali=2443;e.rvocalicdeva=2315;e.rvocalicgujarati=2699;e.rvocalicvowelsignbengali=2499;e.rvocalicvowelsigndeva=2371;e.rvocalicvowelsigngujarati=2755;e.s=115;e.sabengali=2488;e.sacute=347;e.sacutedotaccent=7781;e.sadarabic=1589;e.sadeva=2360;e.sadfinalarabic=65210;e.sadinitialarabic=65211;e.sadmedialarabic=65212;e.sagujarati=2744;e.sagurmukhi=2616;e\ -.sahiragana=12373;e.sakatakana=12469;e.sakatakanahalfwidth=65403;e.sallallahoualayhewasallamarabic=65018;e.samekh=1505;e.samekhdagesh=64321;e.samekhdageshhebrew=64321;e.samekhhebrew=1505;e.saraaathai=3634;e.saraaethai=3649;e.saraaimaimalaithai=3652;e.saraaimaimuanthai=3651;e.saraamthai=3635;e.saraathai=3632;e.saraethai=3648;e.saraiileftthai=63622;e.saraiithai=3637;e.saraileftthai=63621;e.saraithai=3636;e.saraothai=3650;e.saraueeleftthai=63624;e.saraueethai=3639;e.saraueleftthai=63623;e.sarauetha\ -i=3638;e.sarauthai=3640;e.sarauuthai=3641;e.sbopomofo=12569;e.scaron=353;e.scarondotaccent=7783;e.scedilla=351;e.schwa=601;e.schwacyrillic=1241;e.schwadieresiscyrillic=1243;e.schwahook=602;e.scircle=9442;e.scircumflex=349;e.scommaaccent=537;e.sdotaccent=7777;e.sdotbelow=7779;e.sdotbelowdotaccent=7785;e.seagullbelowcmb=828;e.second=8243;e.secondtonechinese=714;e.section=167;e.seenarabic=1587;e.seenfinalarabic=65202;e.seeninitialarabic=65203;e.seenmedialarabic=65204;e.segol=1462;e.segol13=1462;e.s\ -egol1f=1462;e.segol2c=1462;e.segolhebrew=1462;e.segolnarrowhebrew=1462;e.segolquarterhebrew=1462;e.segoltahebrew=1426;e.segolwidehebrew=1462;e.seharmenian=1405;e.sehiragana=12379;e.sekatakana=12475;e.sekatakanahalfwidth=65406;e.semicolon=59;e.semicolonarabic=1563;e.semicolonmonospace=65307;e.semicolonsmall=65108;e.semivoicedmarkkana=12444;e.semivoicedmarkkanahalfwidth=65439;e.sentisquare=13090;e.sentosquare=13091;e.seven=55;e.sevenarabic=1639;e.sevenbengali=2541;e.sevencircle=9318;e.sevencirclei\ -nversesansserif=10128;e.sevendeva=2413;e.seveneighths=8542;e.sevengujarati=2797;e.sevengurmukhi=2669;e.sevenhackarabic=1639;e.sevenhangzhou=12327;e.sevenideographicparen=12838;e.seveninferior=8327;e.sevenmonospace=65303;e.sevenoldstyle=63287;e.sevenparen=9338;e.sevenperiod=9358;e.sevenpersian=1783;e.sevenroman=8566;e.sevensuperior=8311;e.seventeencircle=9328;e.seventeenparen=9348;e.seventeenperiod=9368;e.seventhai=3671;e.sfthyphen=173;e.shaarmenian=1399;e.shabengali=2486;e.shacyrillic=1096;e.sha\ -ddaarabic=1617;e.shaddadammaarabic=64609;e.shaddadammatanarabic=64606;e.shaddafathaarabic=64608;e.shaddakasraarabic=64610;e.shaddakasratanarabic=64607;e.shade=9618;e.shadedark=9619;e.shadelight=9617;e.shademedium=9618;e.shadeva=2358;e.shagujarati=2742;e.shagurmukhi=2614;e.shalshelethebrew=1427;e.shbopomofo=12565;e.shchacyrillic=1097;e.sheenarabic=1588;e.sheenfinalarabic=65206;e.sheeninitialarabic=65207;e.sheenmedialarabic=65208;e.sheicoptic=995;e.sheqel=8362;e.sheqelhebrew=8362;e.sheva=1456;e.sh\ -eva115=1456;e.sheva15=1456;e.sheva22=1456;e.sheva2e=1456;e.shevahebrew=1456;e.shevanarrowhebrew=1456;e.shevaquarterhebrew=1456;e.shevawidehebrew=1456;e.shhacyrillic=1211;e.shimacoptic=1005;e.shin=1513;e.shindagesh=64329;e.shindageshhebrew=64329;e.shindageshshindot=64300;e.shindageshshindothebrew=64300;e.shindageshsindot=64301;e.shindageshsindothebrew=64301;e.shindothebrew=1473;e.shinhebrew=1513;e.shinshindot=64298;e.shinshindothebrew=64298;e.shinsindot=64299;e.shinsindothebrew=64299;e.shook=642;\ -e.sigma=963;e.sigma1=962;e.sigmafinal=962;e.sigmalunatesymbolgreek=1010;e.sihiragana=12375;e.sikatakana=12471;e.sikatakanahalfwidth=65404;e.siluqhebrew=1469;e.siluqlefthebrew=1469;e.similar=8764;e.sindothebrew=1474;e.siosacirclekorean=12916;e.siosaparenkorean=12820;e.sioscieuckorean=12670;e.sioscirclekorean=12902;e.sioskiyeokkorean=12666;e.sioskorean=12613;e.siosnieunkorean=12667;e.siosparenkorean=12806;e.siospieupkorean=12669;e.siostikeutkorean=12668;e.six=54;e.sixarabic=1638;e.sixbengali=2540;\ -e.sixcircle=9317;e.sixcircleinversesansserif=10127;e.sixdeva=2412;e.sixgujarati=2796;e.sixgurmukhi=2668;e.sixhackarabic=1638;e.sixhangzhou=12326;e.sixideographicparen=12837;e.sixinferior=8326;e.sixmonospace=65302;e.sixoldstyle=63286;e.sixparen=9337;e.sixperiod=9357;e.sixpersian=1782;e.sixroman=8565;e.sixsuperior=8310;e.sixteencircle=9327;e.sixteencurrencydenominatorbengali=2553;e.sixteenparen=9347;e.sixteenperiod=9367;e.sixthai=3670;e.slash=47;e.slashmonospace=65295;e.slong=383;e.slongdotaccent=\ -7835;e.smileface=9786;e.smonospace=65363;e.sofpasuqhebrew=1475;e.softhyphen=173;e.softsigncyrillic=1100;e.sohiragana=12381;e.sokatakana=12477;e.sokatakanahalfwidth=65407;e.soliduslongoverlaycmb=824;e.solidusshortoverlaycmb=823;e.sorusithai=3625;e.sosalathai=3624;e.sosothai=3595;e.sosuathai=3626;e.space=32;e.spacehackarabic=32;e.spade=9824;e.spadesuitblack=9824;e.spadesuitwhite=9828;e.sparen=9390;e.squarebelowcmb=827;e.squarecc=13252;e.squarecm=13213;e.squarediagonalcrosshatchfill=9641;e.squareho\ -rizontalfill=9636;e.squarekg=13199;e.squarekm=13214;e.squarekmcapital=13262;e.squareln=13265;e.squarelog=13266;e.squaremg=13198;e.squaremil=13269;e.squaremm=13212;e.squaremsquared=13217;e.squareorthogonalcrosshatchfill=9638;e.squareupperlefttolowerrightfill=9639;e.squareupperrighttolowerleftfill=9640;e.squareverticalfill=9637;e.squarewhitewithsmallblack=9635;e.srsquare=13275;e.ssabengali=2487;e.ssadeva=2359;e.ssagujarati=2743;e.ssangcieuckorean=12617;e.ssanghieuhkorean=12677;e.ssangieungkorean=1\ -2672;e.ssangkiyeokkorean=12594;e.ssangnieunkorean=12645;e.ssangpieupkorean=12611;e.ssangsioskorean=12614;e.ssangtikeutkorean=12600;e.ssuperior=63218;e.sterling=163;e.sterlingmonospace=65505;e.strokelongoverlaycmb=822;e.strokeshortoverlaycmb=821;e.subset=8834;e.subsetnotequal=8842;e.subsetorequal=8838;e.succeeds=8827;e.suchthat=8715;e.suhiragana=12377;e.sukatakana=12473;e.sukatakanahalfwidth=65405;e.sukunarabic=1618;e.summation=8721;e.sun=9788;e.superset=8835;e.supersetnotequal=8843;e.supersetore\ -qual=8839;e.svsquare=13276;e.syouwaerasquare=13180;e.t=116;e.tabengali=2468;e.tackdown=8868;e.tackleft=8867;e.tadeva=2340;e.tagujarati=2724;e.tagurmukhi=2596;e.taharabic=1591;e.tahfinalarabic=65218;e.tahinitialarabic=65219;e.tahiragana=12383;e.tahmedialarabic=65220;e.taisyouerasquare=13181;e.takatakana=12479;e.takatakanahalfwidth=65408;e.tatweelarabic=1600;e.tau=964;e.tav=1514;e.tavdages=64330;e.tavdagesh=64330;e.tavdageshhebrew=64330;e.tavhebrew=1514;e.tbar=359;e.tbopomofo=12554;e.tcaron=357;e.\ -tccurl=680;e.tcedilla=355;e.tcheharabic=1670;e.tchehfinalarabic=64379;e.tchehinitialarabic=64380;e.tchehmedialarabic=64381;e.tcircle=9443;e.tcircumflexbelow=7793;e.tcommaaccent=355;e.tdieresis=7831;e.tdotaccent=7787;e.tdotbelow=7789;e.tecyrillic=1090;e.tedescendercyrillic=1197;e.teharabic=1578;e.tehfinalarabic=65174;e.tehhahinitialarabic=64674;e.tehhahisolatedarabic=64524;e.tehinitialarabic=65175;e.tehiragana=12390;e.tehjeeminitialarabic=64673;e.tehjeemisolatedarabic=64523;e.tehmarbutaarabic=157\ -7;e.tehmarbutafinalarabic=65172;e.tehmedialarabic=65176;e.tehmeeminitialarabic=64676;e.tehmeemisolatedarabic=64526;e.tehnoonfinalarabic=64627;e.tekatakana=12486;e.tekatakanahalfwidth=65411;e.telephone=8481;e.telephoneblack=9742;e.telishagedolahebrew=1440;e.telishaqetanahebrew=1449;e.tencircle=9321;e.tenideographicparen=12841;e.tenparen=9341;e.tenperiod=9361;e.tenroman=8569;e.tesh=679;e.tet=1496;e.tetdagesh=64312;e.tetdageshhebrew=64312;e.tethebrew=1496;e.tetsecyrillic=1205;e.tevirhebrew=1435;e.t\ -evirlefthebrew=1435;e.thabengali=2469;e.thadeva=2341;e.thagujarati=2725;e.thagurmukhi=2597;e.thalarabic=1584;e.thalfinalarabic=65196;e.thanthakhatlowleftthai=63640;e.thanthakhatlowrightthai=63639;e.thanthakhatthai=3660;e.thanthakhatupperleftthai=63638;e.theharabic=1579;e.thehfinalarabic=65178;e.thehinitialarabic=65179;e.thehmedialarabic=65180;e.thereexists=8707;e.therefore=8756;e.theta=952;e.theta1=977;e.thetasymbolgreek=977;e.thieuthacirclekorean=12921;e.thieuthaparenkorean=12825;e.thieuthcircl\ -ekorean=12907;e.thieuthkorean=12620;e.thieuthparenkorean=12811;e.thirteencircle=9324;e.thirteenparen=9344;e.thirteenperiod=9364;e.thonangmonthothai=3601;e.thook=429;e.thophuthaothai=3602;e.thorn=254;e.thothahanthai=3607;e.thothanthai=3600;e.thothongthai=3608;e.thothungthai=3606;e.thousandcyrillic=1154;e.thousandsseparatorarabic=1644;e.thousandsseparatorpersian=1644;e.three=51;e.threearabic=1635;e.threebengali=2537;e.threecircle=9314;e.threecircleinversesansserif=10124;e.threedeva=2409;e.threeeig\ -hths=8540;e.threegujarati=2793;e.threegurmukhi=2665;e.threehackarabic=1635;e.threehangzhou=12323;e.threeideographicparen=12834;e.threeinferior=8323;e.threemonospace=65299;e.threenumeratorbengali=2550;e.threeoldstyle=63283;e.threeparen=9334;e.threeperiod=9354;e.threepersian=1779;e.threequarters=190;e.threequartersemdash=63198;e.threeroman=8562;e.threesuperior=179;e.threethai=3667;e.thzsquare=13204;e.tihiragana=12385;e.tikatakana=12481;e.tikatakanahalfwidth=65409;e.tikeutacirclekorean=12912;e.tike\ -utaparenkorean=12816;e.tikeutcirclekorean=12898;e.tikeutkorean=12599;e.tikeutparenkorean=12802;e.tilde=732;e.tildebelowcmb=816;e.tildecmb=771;e.tildecomb=771;e.tildedoublecmb=864;e.tildeoperator=8764;e.tildeoverlaycmb=820;e.tildeverticalcmb=830;e.timescircle=8855;e.tipehahebrew=1430;e.tipehalefthebrew=1430;e.tippigurmukhi=2672;e.titlocyrilliccmb=1155;e.tiwnarmenian=1407;e.tlinebelow=7791;e.tmonospace=65364;e.toarmenian=1385;e.tohiragana=12392;e.tokatakana=12488;e.tokatakanahalfwidth=65412;e.tone\ -barextrahighmod=741;e.tonebarextralowmod=745;e.tonebarhighmod=742;e.tonebarlowmod=744;e.tonebarmidmod=743;e.tonefive=445;e.tonesix=389;e.tonetwo=424;e.tonos=900;e.tonsquare=13095;e.topatakthai=3599;e.tortoiseshellbracketleft=12308;e.tortoiseshellbracketleftsmall=65117;e.tortoiseshellbracketleftvertical=65081;e.tortoiseshellbracketright=12309;e.tortoiseshellbracketrightsmall=65118;e.tortoiseshellbracketrightvertical=65082;e.totaothai=3605;e.tpalatalhook=427;e.tparen=9391;e.trademark=8482;e.tradem\ -arksans=63722;e.trademarkserif=63195;e.tretroflexhook=648;e.triagdn=9660;e.triaglf=9668;e.triagrt=9658;e.triagup=9650;e.ts=678;e.tsadi=1510;e.tsadidagesh=64326;e.tsadidageshhebrew=64326;e.tsadihebrew=1510;e.tsecyrillic=1094;e.tsere=1461;e.tsere12=1461;e.tsere1e=1461;e.tsere2b=1461;e.tserehebrew=1461;e.tserenarrowhebrew=1461;e.tserequarterhebrew=1461;e.tserewidehebrew=1461;e.tshecyrillic=1115;e.tsuperior=63219;e.ttabengali=2463;e.ttadeva=2335;e.ttagujarati=2719;e.ttagurmukhi=2591;e.tteharabic=165\ -7;e.ttehfinalarabic=64359;e.ttehinitialarabic=64360;e.ttehmedialarabic=64361;e.tthabengali=2464;e.tthadeva=2336;e.tthagujarati=2720;e.tthagurmukhi=2592;e.tturned=647;e.tuhiragana=12388;e.tukatakana=12484;e.tukatakanahalfwidth=65410;e.tusmallhiragana=12387;e.tusmallkatakana=12483;e.tusmallkatakanahalfwidth=65391;e.twelvecircle=9323;e.twelveparen=9343;e.twelveperiod=9363;e.twelveroman=8571;e.twentycircle=9331;e.twentyhangzhou=21316;e.twentyparen=9351;e.twentyperiod=9371;e.two=50;e.twoarabic=1634;e\ -.twobengali=2536;e.twocircle=9313;e.twocircleinversesansserif=10123;e.twodeva=2408;e.twodotenleader=8229;e.twodotleader=8229;e.twodotleadervertical=65072;e.twogujarati=2792;e.twogurmukhi=2664;e.twohackarabic=1634;e.twohangzhou=12322;e.twoideographicparen=12833;e.twoinferior=8322;e.twomonospace=65298;e.twonumeratorbengali=2549;e.twooldstyle=63282;e.twoparen=9333;e.twoperiod=9353;e.twopersian=1778;e.tworoman=8561;e.twostroke=443;e.twosuperior=178;e.twothai=3666;e.twothirds=8532;e.u=117;e.uacute=25\ -0;e.ubar=649;e.ubengali=2441;e.ubopomofo=12584;e.ubreve=365;e.ucaron=468;e.ucircle=9444;e.ucircumflex=251;e.ucircumflexbelow=7799;e.ucyrillic=1091;e.udattadeva=2385;e.udblacute=369;e.udblgrave=533;e.udeva=2313;e.udieresis=252;e.udieresisacute=472;e.udieresisbelow=7795;e.udieresiscaron=474;e.udieresiscyrillic=1265;e.udieresisgrave=476;e.udieresismacron=470;e.udotbelow=7909;e.ugrave=249;e.ugujarati=2697;e.ugurmukhi=2569;e.uhiragana=12358;e.uhookabove=7911;e.uhorn=432;e.uhornacute=7913;e.uhorndotbe\ -low=7921;e.uhorngrave=7915;e.uhornhookabove=7917;e.uhorntilde=7919;e.uhungarumlaut=369;e.uhungarumlautcyrillic=1267;e.uinvertedbreve=535;e.ukatakana=12454;e.ukatakanahalfwidth=65395;e.ukcyrillic=1145;e.ukorean=12636;e.umacron=363;e.umacroncyrillic=1263;e.umacrondieresis=7803;e.umatragurmukhi=2625;e.umonospace=65365;e.underscore=95;e.underscoredbl=8215;e.underscoremonospace=65343;e.underscorevertical=65075;e.underscorewavy=65103;e.union=8746;e.universal=8704;e.uogonek=371;e.uparen=9392;e.upblock=\ -9600;e.upperdothebrew=1476;e.upsilon=965;e.upsilondieresis=971;e.upsilondieresistonos=944;e.upsilonlatin=650;e.upsilontonos=973;e.uptackbelowcmb=797;e.uptackmod=724;e.uragurmukhi=2675;e.uring=367;e.ushortcyrillic=1118;e.usmallhiragana=12357;e.usmallkatakana=12453;e.usmallkatakanahalfwidth=65385;e.ustraightcyrillic=1199;e.ustraightstrokecyrillic=1201;e.utilde=361;e.utildeacute=7801;e.utildebelow=7797;e.uubengali=2442;e.uudeva=2314;e.uugujarati=2698;e.uugurmukhi=2570;e.uumatragurmukhi=2626;e.uuvow\ -elsignbengali=2498;e.uuvowelsigndeva=2370;e.uuvowelsigngujarati=2754;e.uvowelsignbengali=2497;e.uvowelsigndeva=2369;e.uvowelsigngujarati=2753;e.v=118;e.vadeva=2357;e.vagujarati=2741;e.vagurmukhi=2613;e.vakatakana=12535;e.vav=1493;e.vavdagesh=64309;e.vavdagesh65=64309;e.vavdageshhebrew=64309;e.vavhebrew=1493;e.vavholam=64331;e.vavholamhebrew=64331;e.vavvavhebrew=1520;e.vavyodhebrew=1521;e.vcircle=9445;e.vdotbelow=7807;e.vecyrillic=1074;e.veharabic=1700;e.vehfinalarabic=64363;e.vehinitialarabic=64\ -364;e.vehmedialarabic=64365;e.vekatakana=12537;e.venus=9792;e.verticalbar=124;e.verticallineabovecmb=781;e.verticallinebelowcmb=809;e.verticallinelowmod=716;e.verticallinemod=712;e.vewarmenian=1406;e.vhook=651;e.vikatakana=12536;e.viramabengali=2509;e.viramadeva=2381;e.viramagujarati=2765;e.visargabengali=2435;e.visargadeva=2307;e.visargagujarati=2691;e.vmonospace=65366;e.voarmenian=1400;e.voicediterationhiragana=12446;e.voicediterationkatakana=12542;e.voicedmarkkana=12443;e.voicedmarkkanahalfwi\ -dth=65438;e.vokatakana=12538;e.vparen=9393;e.vtilde=7805;e.vturned=652;e.vuhiragana=12436;e.vukatakana=12532;e.w=119;e.wacute=7811;e.waekorean=12633;e.wahiragana=12431;e.wakatakana=12527;e.wakatakanahalfwidth=65436;e.wakorean=12632;e.wasmallhiragana=12430;e.wasmallkatakana=12526;e.wattosquare=13143;e.wavedash=12316;e.wavyunderscorevertical=65076;e.wawarabic=1608;e.wawfinalarabic=65262;e.wawhamzaabovearabic=1572;e.wawhamzaabovefinalarabic=65158;e.wbsquare=13277;e.wcircle=9446;e.wcircumflex=373;e.\ -wdieresis=7813;e.wdotaccent=7815;e.wdotbelow=7817;e.wehiragana=12433;e.weierstrass=8472;e.wekatakana=12529;e.wekorean=12638;e.weokorean=12637;e.wgrave=7809;e.whitebullet=9702;e.whitecircle=9675;e.whitecircleinverse=9689;e.whitecornerbracketleft=12302;e.whitecornerbracketleftvertical=65091;e.whitecornerbracketright=12303;e.whitecornerbracketrightvertical=65092;e.whitediamond=9671;e.whitediamondcontainingblacksmalldiamond=9672;e.whitedownpointingsmalltriangle=9663;e.whitedownpointingtriangle=9661;\ -e.whiteleftpointingsmalltriangle=9667;e.whiteleftpointingtriangle=9665;e.whitelenticularbracketleft=12310;e.whitelenticularbracketright=12311;e.whiterightpointingsmalltriangle=9657;e.whiterightpointingtriangle=9655;e.whitesmallsquare=9643;e.whitesmilingface=9786;e.whitesquare=9633;e.whitestar=9734;e.whitetelephone=9743;e.whitetortoiseshellbracketleft=12312;e.whitetortoiseshellbracketright=12313;e.whiteuppointingsmalltriangle=9653;e.whiteuppointingtriangle=9651;e.wihiragana=12432;e.wikatakana=125\ -28;e.wikorean=12639;e.wmonospace=65367;e.wohiragana=12434;e.wokatakana=12530;e.wokatakanahalfwidth=65382;e.won=8361;e.wonmonospace=65510;e.wowaenthai=3623;e.wparen=9394;e.wring=7832;e.wsuperior=695;e.wturned=653;e.wynn=447;e.x=120;e.xabovecmb=829;e.xbopomofo=12562;e.xcircle=9447;e.xdieresis=7821;e.xdotaccent=7819;e.xeharmenian=1389;e.xi=958;e.xmonospace=65368;e.xparen=9395;e.xsuperior=739;e.y=121;e.yaadosquare=13134;e.yabengali=2479;e.yacute=253;e.yadeva=2351;e.yaekorean=12626;e.yagujarati=2735;\ -e.yagurmukhi=2607;e.yahiragana=12420;e.yakatakana=12516;e.yakatakanahalfwidth=65428;e.yakorean=12625;e.yamakkanthai=3662;e.yasmallhiragana=12419;e.yasmallkatakana=12515;e.yasmallkatakanahalfwidth=65388;e.yatcyrillic=1123;e.ycircle=9448;e.ycircumflex=375;e.ydieresis=255;e.ydotaccent=7823;e.ydotbelow=7925;e.yeharabic=1610;e.yehbarreearabic=1746;e.yehbarreefinalarabic=64431;e.yehfinalarabic=65266;e.yehhamzaabovearabic=1574;e.yehhamzaabovefinalarabic=65162;e.yehhamzaaboveinitialarabic=65163;e.yehham\ -zaabovemedialarabic=65164;e.yehinitialarabic=65267;e.yehmedialarabic=65268;e.yehmeeminitialarabic=64733;e.yehmeemisolatedarabic=64600;e.yehnoonfinalarabic=64660;e.yehthreedotsbelowarabic=1745;e.yekorean=12630;e.yen=165;e.yenmonospace=65509;e.yeokorean=12629;e.yeorinhieuhkorean=12678;e.yerahbenyomohebrew=1450;e.yerahbenyomolefthebrew=1450;e.yericyrillic=1099;e.yerudieresiscyrillic=1273;e.yesieungkorean=12673;e.yesieungpansioskorean=12675;e.yesieungsioskorean=12674;e.yetivhebrew=1434;e.ygrave=7923\ -;e.yhook=436;e.yhookabove=7927;e.yiarmenian=1397;e.yicyrillic=1111;e.yikorean=12642;e.yinyang=9775;e.yiwnarmenian=1410;e.ymonospace=65369;e.yod=1497;e.yoddagesh=64313;e.yoddageshhebrew=64313;e.yodhebrew=1497;e.yodyodhebrew=1522;e.yodyodpatahhebrew=64287;e.yohiragana=12424;e.yoikorean=12681;e.yokatakana=12520;e.yokatakanahalfwidth=65430;e.yokorean=12635;e.yosmallhiragana=12423;e.yosmallkatakana=12519;e.yosmallkatakanahalfwidth=65390;e.yotgreek=1011;e.yoyaekorean=12680;e.yoyakorean=12679;e.yoyakth\ -ai=3618;e.yoyingthai=3597;e.yparen=9396;e.ypogegrammeni=890;e.ypogegrammenigreekcmb=837;e.yr=422;e.yring=7833;e.ysuperior=696;e.ytilde=7929;e.yturned=654;e.yuhiragana=12422;e.yuikorean=12684;e.yukatakana=12518;e.yukatakanahalfwidth=65429;e.yukorean=12640;e.yusbigcyrillic=1131;e.yusbigiotifiedcyrillic=1133;e.yuslittlecyrillic=1127;e.yuslittleiotifiedcyrillic=1129;e.yusmallhiragana=12421;e.yusmallkatakana=12517;e.yusmallkatakanahalfwidth=65389;e.yuyekorean=12683;e.yuyeokorean=12682;e.yyabengali=25\ -27;e.yyadeva=2399;e.z=122;e.zaarmenian=1382;e.zacute=378;e.zadeva=2395;e.zagurmukhi=2651;e.zaharabic=1592;e.zahfinalarabic=65222;e.zahinitialarabic=65223;e.zahiragana=12374;e.zahmedialarabic=65224;e.zainarabic=1586;e.zainfinalarabic=65200;e.zakatakana=12470;e.zaqefgadolhebrew=1429;e.zaqefqatanhebrew=1428;e.zarqahebrew=1432;e.zayin=1494;e.zayindagesh=64310;e.zayindageshhebrew=64310;e.zayinhebrew=1494;e.zbopomofo=12567;e.zcaron=382;e.zcircle=9449;e.zcircumflex=7825;e.zcurl=657;e.zdot=380;e.zdotacc\ -ent=380;e.zdotbelow=7827;e.zecyrillic=1079;e.zedescendercyrillic=1177;e.zedieresiscyrillic=1247;e.zehiragana=12380;e.zekatakana=12476;e.zero=48;e.zeroarabic=1632;e.zerobengali=2534;e.zerodeva=2406;e.zerogujarati=2790;e.zerogurmukhi=2662;e.zerohackarabic=1632;e.zeroinferior=8320;e.zeromonospace=65296;e.zerooldstyle=63280;e.zeropersian=1776;e.zerosuperior=8304;e.zerothai=3664;e.zerowidthjoiner=65279;e.zerowidthnonjoiner=8204;e.zerowidthspace=8203;e.zeta=950;e.zhbopomofo=12563;e.zhearmenian=1386;e.\ -zhebrevecyrillic=1218;e.zhecyrillic=1078;e.zhedescendercyrillic=1175;e.zhedieresiscyrillic=1245;e.zihiragana=12376;e.zikatakana=12472;e.zinorhebrew=1454;e.zlinebelow=7829;e.zmonospace=65370;e.zohiragana=12382;e.zokatakana=12478;e.zparen=9397;e.zretroflexhook=656;e.zstroke=438;e.zuhiragana=12378;e.zukatakana=12474;e[".notdef"]=0;e.angbracketleftbig=9001;e.angbracketleftBig=9001;e.angbracketleftbigg=9001;e.angbracketleftBigg=9001;e.angbracketrightBig=9002;e.angbracketrightbig=9002;e.angbracketrigh\ -tBigg=9002;e.angbracketrightbigg=9002;e.arrowhookleft=8618;e.arrowhookright=8617;e.arrowlefttophalf=8636;e.arrowleftbothalf=8637;e.arrownortheast=8599;e.arrownorthwest=8598;e.arrowrighttophalf=8640;e.arrowrightbothalf=8641;e.arrowsoutheast=8600;e.arrowsouthwest=8601;e.backslashbig=8726;e.backslashBig=8726;e.backslashBigg=8726;e.backslashbigg=8726;e.bardbl=8214;e.bracehtipdownleft=65079;e.bracehtipdownright=65079;e.bracehtipupleft=65080;e.bracehtipupright=65080;e.braceleftBig=123;e.braceleftbig=1\ -23;e.braceleftbigg=123;e.braceleftBigg=123;e.bracerightBig=125;e.bracerightbig=125;e.bracerightbigg=125;e.bracerightBigg=125;e.bracketleftbig=91;e.bracketleftBig=91;e.bracketleftbigg=91;e.bracketleftBigg=91;e.bracketrightBig=93;e.bracketrightbig=93;e.bracketrightbigg=93;e.bracketrightBigg=93;e.ceilingleftbig=8968;e.ceilingleftBig=8968;e.ceilingleftBigg=8968;e.ceilingleftbigg=8968;e.ceilingrightbig=8969;e.ceilingrightBig=8969;e.ceilingrightbigg=8969;e.ceilingrightBigg=8969;e.circledotdisplay=8857\ -;e.circledottext=8857;e.circlemultiplydisplay=8855;e.circlemultiplytext=8855;e.circleplusdisplay=8853;e.circleplustext=8853;e.contintegraldisplay=8750;e.contintegraltext=8750;e.coproductdisplay=8720;e.coproducttext=8720;e.floorleftBig=8970;e.floorleftbig=8970;e.floorleftbigg=8970;e.floorleftBigg=8970;e.floorrightbig=8971;e.floorrightBig=8971;e.floorrightBigg=8971;e.floorrightbigg=8971;e.hatwide=770;e.hatwider=770;e.hatwidest=770;e.intercal=7488;e.integraldisplay=8747;e.integraltext=8747;e.inters\ -ectiondisplay=8898;e.intersectiontext=8898;e.logicalanddisplay=8743;e.logicalandtext=8743;e.logicalordisplay=8744;e.logicalortext=8744;e.parenleftBig=40;e.parenleftbig=40;e.parenleftBigg=40;e.parenleftbigg=40;e.parenrightBig=41;e.parenrightbig=41;e.parenrightBigg=41;e.parenrightbigg=41;e.prime=8242;e.productdisplay=8719;e.producttext=8719;e.radicalbig=8730;e.radicalBig=8730;e.radicalBigg=8730;e.radicalbigg=8730;e.radicalbt=8730;e.radicaltp=8730;e.radicalvertex=8730;e.slashbig=47;e.slashBig=47;e.\ -slashBigg=47;e.slashbigg=47;e.summationdisplay=8721;e.summationtext=8721;e.tildewide=732;e.tildewider=732;e.tildewidest=732;e.uniondisplay=8899;e.unionmultidisplay=8846;e.unionmultitext=8846;e.unionsqdisplay=8852;e.unionsqtext=8852;e.uniontext=8899;e.vextenddouble=8741;e.vextendsingle=8739})),Ir=getLookupTableFactory((function(e){e.space=32;e.a1=9985;e.a2=9986;e.a202=9987;e.a3=9988;e.a4=9742;e.a5=9990;e.a119=9991;e.a118=9992;e.a117=9993;e.a11=9755;e.a12=9758;e.a13=9996;e.a14=9997;e.a15=9998;e.a1\ -6=9999;e.a105=1e4;e.a17=10001;e.a18=10002;e.a19=10003;e.a20=10004;e.a21=10005;e.a22=10006;e.a23=10007;e.a24=10008;e.a25=10009;e.a26=10010;e.a27=10011;e.a28=10012;e.a6=10013;e.a7=10014;e.a8=10015;e.a9=10016;e.a10=10017;e.a29=10018;e.a30=10019;e.a31=10020;e.a32=10021;e.a33=10022;e.a34=10023;e.a35=9733;e.a36=10025;e.a37=10026;e.a38=10027;e.a39=10028;e.a40=10029;e.a41=10030;e.a42=10031;e.a43=10032;e.a44=10033;e.a45=10034;e.a46=10035;e.a47=10036;e.a48=10037;e.a49=10038;e.a50=10039;e.a51=10040;e.a52=1\ -0041;e.a53=10042;e.a54=10043;e.a55=10044;e.a56=10045;e.a57=10046;e.a58=10047;e.a59=10048;e.a60=10049;e.a61=10050;e.a62=10051;e.a63=10052;e.a64=10053;e.a65=10054;e.a66=10055;e.a67=10056;e.a68=10057;e.a69=10058;e.a70=10059;e.a71=9679;e.a72=10061;e.a73=9632;e.a74=10063;e.a203=10064;e.a75=10065;e.a204=10066;e.a76=9650;e.a77=9660;e.a78=9670;e.a79=10070;e.a81=9687;e.a82=10072;e.a83=10073;e.a84=10074;e.a97=10075;e.a98=10076;e.a99=10077;e.a100=10078;e.a101=10081;e.a102=10082;e.a103=10083;e.a104=10084;e.\ -a106=10085;e.a107=10086;e.a108=10087;e.a112=9827;e.a111=9830;e.a110=9829;e.a109=9824;e.a120=9312;e.a121=9313;e.a122=9314;e.a123=9315;e.a124=9316;e.a125=9317;e.a126=9318;e.a127=9319;e.a128=9320;e.a129=9321;e.a130=10102;e.a131=10103;e.a132=10104;e.a133=10105;e.a134=10106;e.a135=10107;e.a136=10108;e.a137=10109;e.a138=10110;e.a139=10111;e.a140=10112;e.a141=10113;e.a142=10114;e.a143=10115;e.a144=10116;e.a145=10117;e.a146=10118;e.a147=10119;e.a148=10120;e.a149=10121;e.a150=10122;e.a151=10123;e.a152=10\ -124;e.a153=10125;e.a154=10126;e.a155=10127;e.a156=10128;e.a157=10129;e.a158=10130;e.a159=10131;e.a160=10132;e.a161=8594;e.a163=8596;e.a164=8597;e.a196=10136;e.a165=10137;e.a192=10138;e.a166=10139;e.a167=10140;e.a168=10141;e.a169=10142;e.a170=10143;e.a171=10144;e.a172=10145;e.a173=10146;e.a162=10147;e.a174=10148;e.a175=10149;e.a176=10150;e.a177=10151;e.a178=10152;e.a179=10153;e.a193=10154;e.a180=10155;e.a199=10156;e.a181=10157;e.a200=10158;e.a182=10159;e.a201=10161;e.a183=10162;e.a184=10163;e.a19\ -7=10164;e.a185=10165;e.a194=10166;e.a198=10167;e.a186=10168;e.a195=10169;e.a187=10170;e.a188=10171;e.a189=10172;e.a190=10173;e.a191=10174;e.a89=10088;e.a90=10089;e.a93=10090;e.a94=10091;e.a91=10092;e.a92=10093;e.a205=10094;e.a85=10095;e.a206=10096;e.a86=10097;e.a87=10098;e.a88=10099;e.a95=10100;e.a96=10101;e[".notdef"]=0})),Tr=getLookupTableFactory((function(e){e[63721]=169;e[63193]=169;e[63720]=174;e[63194]=174;e[63722]=8482;e[63195]=8482;e[63729]=9127;e[63730]=9128;e[63731]=9129;e[63740]=9131;\ -e[63741]=9132;e[63742]=9133;e[63726]=9121;e[63727]=9122;e[63728]=9123;e[63737]=9124;e[63738]=9125;e[63739]=9126;e[63723]=9115;e[63724]=9116;e[63725]=9117;e[63734]=9118;e[63735]=9119;e[63736]=9120}));function getUnicodeForGlyph(e,t){let a=t[e];if(void 0!==a)return a;if(!e)return-1;if("u"===e[0]){const t=e.length;let r;if(7===t&&"n"===e[1]&&"i"===e[2])r=e.substring(3);else{if(!(t>=5&&t<=7))return-1;r=e.substring(1)}if(r===r.toUpperCase()){a=parseInt(r,16);if(a>=0)return a}}return-1}const Or=[[0,12\ -7],[128,255],[256,383],[384,591],[592,687,7424,7551,7552,7615],[688,767,42752,42783],[768,879,7616,7679],[880,1023],[11392,11519],[1024,1279,1280,1327,11744,11775,42560,42655],[1328,1423],[1424,1535],[42240,42559],[1536,1791,1872,1919],[1984,2047],[2304,2431],[2432,2559],[2560,2687],[2688,2815],[2816,2943],[2944,3071],[3072,3199],[3200,3327],[3328,3455],[3584,3711],[3712,3839],[4256,4351,11520,11567],[6912,7039],[4352,4607],[7680,7935,11360,11391,42784,43007],[7936,8191],[8192,8303,11776,11903],\ -[8304,8351],[8352,8399],[8400,8447],[8448,8527],[8528,8591],[8592,8703,10224,10239,10496,10623,11008,11263],[8704,8959,10752,11007,10176,10223,10624,10751],[8960,9215],[9216,9279],[9280,9311],[9312,9471],[9472,9599],[9600,9631],[9632,9727],[9728,9983],[9984,10175],[12288,12351],[12352,12447],[12448,12543,12784,12799],[12544,12591,12704,12735],[12592,12687],[43072,43135],[12800,13055],[13056,13311],[44032,55215],[55296,57343],[67840,67871],[19968,40959,11904,12031,12032,12255,12272,12287,13312,19\ -903,131072,173791,12688,12703],[57344,63743],[12736,12783,63744,64255,194560,195103],[64256,64335],[64336,65023],[65056,65071],[65040,65055],[65104,65135],[65136,65279],[65280,65519],[65520,65535],[3840,4095],[1792,1871],[1920,1983],[3456,3583],[4096,4255],[4608,4991,4992,5023,11648,11743],[5024,5119],[5120,5759],[5760,5791],[5792,5887],[6016,6143],[6144,6319],[10240,10495],[40960,42127],[5888,5919,5920,5951,5952,5983,5984,6015],[66304,66351],[66352,66383],[66560,66639],[118784,119039,119040,119\ -295,119296,119375],[119808,120831],[1044480,1048573],[65024,65039,917760,917999],[917504,917631],[6400,6479],[6480,6527],[6528,6623],[6656,6687],[11264,11359],[11568,11647],[19904,19967],[43008,43055],[65536,65663,65664,65791,65792,65855],[65856,65935],[66432,66463],[66464,66527],[66640,66687],[66688,66735],[67584,67647],[68096,68191],[119552,119647],[73728,74751,74752,74879],[119648,119679],[7040,7103],[7168,7247],[7248,7295],[43136,43231],[43264,43311],[43312,43359],[43520,43615],[65936,65999]\ -,[66e3,66047],[66208,66271,66176,66207,67872,67903],[127024,127135,126976,127023]];function getUnicodeRangeFor(e,t=-1){if(-1!==t){const a=Or[t];for(let r=0,i=a.length;r=a[r]&&e<=a[r+1])return t}for(let t=0,a=Or.length;t=a[r]&&e<=a[r+1])return t}return-1}const Mr=new RegExp("^(\\\\s)|(\\\\p{Mn})|(\\\\p{Cf})$","u"),Dr=new Map;const Rr=!0,Nr=1,Er=2,Pr=4,Lr=32,jr=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbe\ -rsign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",\ -"p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germa\ -ndbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quot\ -eright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron\ -","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function recoverGlyphName(e,t){if(void 0!==t[e])return e;const a=getUnicodeForGlyph(e,t);if(-1!==a)for(const e in t)if(t[e]===a)return e;info("Unable to recover a standard glyph name for: "+e);return e}fu\ -nction type1FontGlyphMapping(e,t,a){const r=Object.create(null);let i,n,s;const o=!!(e.flags&Pr);if(e.isInternalFont){s=t;for(n=0;n=0?i:0}}else if(e.baseEncodingName){s=getEncoding(e.baseEncodingName);for(n=0;n=0?i:0}}else if(o)for(n in t)r[n]=t[n];else{s=Ar;for(n=0;n=0?i:0}}const c=e.differences;let l;if(c)for(n in c){const e=c[n];i=a.indexOf(e);if(-1===i){l||(l=Fr());const t=\ -recoverGlyphName(e,l);t!==e&&(i=a.indexOf(t))}r[n]=i>=0?i:0}return r}function normalizeFontName(e){return e.replaceAll(/[,_]/g,"-").replaceAll(/\\s/g,"")}const _r=getLookupTableFactory((e=>{e[8211]=65074;e[8212]=65073;e[8229]=65072;e[8230]=65049;e[12289]=65041;e[12290]=65042;e[12296]=65087;e[12297]=65088;e[12298]=65085;e[12299]=65086;e[12300]=65089;e[12301]=65090;e[12302]=65091;e[12303]=65092;e[12304]=65083;e[12305]=65084;e[12308]=65081;e[12309]=65082;e[12310]=65047;e[12311]=65048;e[65103]=65076;\ -e[65281]=65045;e[65288]=65077;e[65289]=65078;e[65292]=65040;e[65306]=65043;e[65307]=65044;e[65311]=65046;e[65339]=65095;e[65341]=65096;e[65343]=65075;e[65371]=65079;e[65373]=65080}));const Ur=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A",\ -"B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endas\ -h","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequart\ -er","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atil\ -de","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron"],Xr=[".notdef","space","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","one\ -oldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","As\ -mall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall",\ -"onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferio\ -r","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],qr=[".notdef","s\ -pace","dollaroldstyle","dollarsuperior","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior",\ -"rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","hyphensuperior","colonmonetary","onefitted","rupiah","centoldstyle","figuredash","hypheninferior","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior",\ -"twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior"],Hr=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B\ -","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash"\ -,"dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter\ -","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde\ -","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveol\ -dstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksma\ -ll","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twot\ -hirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieres\ -issmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],Wr=391,zr=[null,{id:"hstem",min:2,stackClearing:!0,stem:!0},null,{id:"vstem",min:2,\ -stackClearing:!0,stem:!0},{id:"vmoveto",min:1,stackClearing:!0},{id:"rlineto",min:2,resetStack:!0},{id:"hlineto",min:1,resetStack:!0},{id:"vlineto",min:1,resetStack:!0},{id:"rrcurveto",min:6,resetStack:!0},null,{id:"callsubr",min:1,undefStack:!0},{id:"return",min:0,undefStack:!0},null,null,{id:"endchar",min:0,stackClearing:!0},null,null,null,{id:"hstemhm",min:2,stackClearing:!0,stem:!0},{id:"hintmask",min:0,stackClearing:!0},{id:"cntrmask",min:0,stackClearing:!0},{id:"rmoveto",min:2,stackClearin\ -g:!0},{id:"hmoveto",min:1,stackClearing:!0},{id:"vstemhm",min:2,stackClearing:!0,stem:!0},{id:"rcurveline",min:8,resetStack:!0},{id:"rlinecurve",min:8,resetStack:!0},{id:"vvcurveto",min:4,resetStack:!0},{id:"hhcurveto",min:4,resetStack:!0},null,{id:"callgsubr",min:1,undefStack:!0},{id:"vhcurveto",min:4,resetStack:!0},{id:"hvcurveto",min:4,resetStack:!0}],$r=[null,null,null,{id:"and",min:2,stackDelta:-1},{id:"or",min:2,stackDelta:-1},{id:"not",min:1,stackDelta:0},null,null,null,{id:"abs",min:1,st\ -ackDelta:0},{id:"add",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]+e[t-1]}},{id:"sub",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]-e[t-1]}},{id:"div",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]/e[t-1]}},null,{id:"neg",min:1,stackDelta:0,stackFn(e,t){e[t-1]=-e[t-1]}},{id:"eq",min:2,stackDelta:-1},null,null,{id:"drop",min:1,stackDelta:-1},null,{id:"put",min:2,stackDelta:-2},{id:"get",min:1,stackDelta:0},{id:"ifelse",min:4,stackDelta:-3},{id:"random",min:0,stackDelta:1},{id:"mul",min:2,stack\ -Delta:-1,stackFn(e,t){e[t-2]=e[t-2]*e[t-1]}},null,{id:"sqrt",min:1,stackDelta:0},{id:"dup",min:1,stackDelta:1},{id:"exch",min:2,stackDelta:0},{id:"index",min:2,stackDelta:0},{id:"roll",min:3,stackDelta:-2},null,null,null,{id:"hflex",min:7,resetStack:!0},{id:"flex",min:13,resetStack:!0},{id:"hflex1",min:9,resetStack:!0},{id:"flex1",min:11,resetStack:!0}];class CFFParser{constructor(e,t,a){this.bytes=e.getBytes();this.properties=t;this.seacAnalysisEnabled=!!a}parse(){const e=this.properties,t=new \ -CFF;this.cff=t;const a=this.parseHeader(),r=this.parseIndex(a.endPos),i=this.parseIndex(r.endPos),n=this.parseIndex(i.endPos),s=this.parseIndex(n.endPos),o=this.parseDict(i.obj.get(0)),c=this.createDict(CFFTopDict,o,t.strings);t.header=a.obj;t.names=this.parseNameIndex(r.obj);t.strings=this.parseStringIndex(n.obj);t.topDict=c;t.globalSubrIndex=s.obj;this.parsePrivateDict(t.topDict);t.isCIDFont=c.hasName("ROS");const l=c.getByName("CharStrings"),h=this.parseIndex(l).obj,u=c.getByName("FontMatrix"\ -);u&&(e.fontMatrix=u);const d=c.getByName("FontBBox");if(d){e.ascent=Math.max(d[3],d[1]);e.descent=Math.min(d[1],d[3]);e.ascentScaled=!0}let f,g;if(t.isCIDFont){const e=this.parseIndex(c.getByName("FDArray")).obj;for(let a=0,r=e.count;a=t)throw \ -new FormatError("Invalid CFF header");if(0!==a){info("cff data is shifted");e=e.subarray(a);this.bytes=e}const r=e[0],i=e[1],n=e[2],s=e[3];return{obj:new CFFHeader(r,i,n,s),endPos:n}}parseDict(e){let t=0;function parseOperand(){let a=e[t++];if(30===a)return function parseFloatOperand(){let a="";const r=15,i=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"],n=e.length;for(;t>4,o=15&n;if(s===r)break;a+=i[s];if(o===r)break;a+=i[o]}return parseFloat(a)}();if(28=\ -==a){a=readInt16(e,t);t+=2;return a}if(29===a){a=e[t++];a=a<<8|e[t++];a=a<<8|e[t++];a=a<<8|e[t++];return a}if(a>=32&&a<=246)return a-139;if(a>=247&&a<=250)return 256*(a-247)+e[t++]+108;if(a>=251&&a<=254)return-256*(a-251)-e[t++]-108;warn(\'CFFParser_parseDict: "\'+a+\'" is a reserved command.\');return NaN}let a=[];const r=[];t=0;const i=e.length;for(;t10)return!1;let i=e.stackSize;const n=e.stack;let s=t.length;for(let o=0;o=4){i-=4;if(this.seacAnalysisEnabled){e.seac=n.slice(i,i+4);return!1}}l=zr[c]}else if(c>=32&&c<=246){n[i]=c-139;i++}else if(c>=2\ -47&&c<=254){n[i]=c<251?(c-247<<8)+t[o]+108:-(c-251<<8)-t[o]-108;o++;i++}else if(255===c){n[i]=(t[o]<<24|t[o+1]<<16|t[o+2]<<8|t[o+3])/65536;o+=4;i++}else if(19===c||20===c){e.hints+=i>>1;if(0===e.hints){t.copyWithin(o-1,o,-1);o-=1;s-=1;continue}o+=e.hints+7>>3;i%=2;l=zr[c]}else{if(10===c||29===c){const t=10===c?a:r;if(!t){l=zr[c];warn("Missing subrsIndex for "+l.id);return!1}let s=32768;t.count<1240?s=107:t.count<33900&&(s=1131);const o=n[--i]+s;if(o<0||o>=t.count||isNaN(o)){l=zr[c];warn("Out of \ -bounds subrIndex for "+l.id);return!1}e.stackSize=i;e.callDepth++;if(!this.parseCharString(e,t.get(o),a,r))return!1;e.callDepth--;i=e.stackSize;continue}if(11===c){e.stackSize=i;return!0}if(0===c&&o===t.length){t[o-1]=14;l=zr[14]}else{if(9===c){t.copyWithin(o-1,o,-1);o-=1;s-=1;continue}l=zr[c]}}if(l){if(l.stem){e.hints+=i>>1;if(3===c||23===c)e.hasVStems=!0;else if(e.hasVStems&&(1===c||18===c)){warn("CFF stem hints are in wrong order");t[o-1]=1===c?3:23}}if("min"in l&&!e.undefStack&&i=2&&l.stem?i%=2:i>1&&warn("Found too many parameters for stack-clearing command");i>0&&(e.width=n[i-1])}if("stackDelta"in l){"stackFn"in l&&l.stackFn(n,i);i+=l.stackDelta}else if(l.stackClearing)i=0;else if(l.resetStack){i=0;e.undefStack=!1}else if(l.undefStack){i=0;e.undefStack=!0;e.firstStackClearing=!1}}}s=i.length){warn("Invalid fd index for glyph index."\ -);u=!1}if(u){f=i[e].privateDict;d=f.subrsIndex}}else t&&(d=t);u&&(u=this.parseCharString(h,c,d,a));if(null!==h.width){const e=f.getByName("nominalWidthX");o[l]=e+h.width}else{const e=f.getByName("defaultWidthX");o[l]=e}null!==h.seac&&(s[l]=h.seac);u||e.set(l,new Uint8Array([14]))}return{charStrings:e,seacs:s,widths:o}}emptyPrivateDictionary(e){const t=this.createDict(CFFPrivateDict,[],e.strings);e.setByKey(18,[0,0]);e.privateDict=t}parsePrivateDict(e){if(!e.hasName("Private")){this.emptyPrivateD\ -ictionary(e);return}const t=e.getByName("Private");if(!Array.isArray(t)||2!==t.length){e.removeByName("Private");return}const a=t[0],r=t[1];if(0===a||r>=this.bytes.length){this.emptyPrivateDictionary(e);return}const i=r+a,n=this.bytes.subarray(r,i),s=this.parseDict(n),o=this.createDict(CFFPrivateDict,s,e.strings);e.privateDict=o;0===o.getByName("ExpansionFactor")&&o.setByName("ExpansionFactor",.06);if(!o.getByName("Subrs"))return;const c=o.getByName("Subrs"),l=r+c;if(0===c||l>=this.bytes.length)\ -{this.emptyPrivateDictionary(e);return}const h=this.parseIndex(l);o.subrsIndex=h.obj}parseCharsets(e,t,a,r){if(0===e)return new CFFCharset(!0,Kr.ISO_ADOBE,Ur);if(1===e)return new CFFCharset(!0,Kr.EXPERT,Xr);if(2===e)return new CFFCharset(!0,Kr.EXPERT_SUBSET,qr);const i=this.bytes,n=e,s=i[e++],o=[r?0:".notdef"];let c,l,h;t-=1;switch(s){case 0:for(h=0;h=65535){warn("Not enough space in charstrings to duplicate first glyph.");return}const e=this.charStrings.get(0);this.charStrings.add(e);this.isCIDFont&&this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0])}hasGlyphId(e){if(e<0||e>=th\ -is.charStrings.count)return!1;return this.charStrings.get(e).length>0}}class CFFHeader{constructor(e,t,a,r){this.major=e;this.minor=t;this.hdrSize=a;this.offSize=r}}class CFFStrings{constructor(){this.strings=[]}get(e){return e>=0&&e<=390?Hr[e]:e-Wr<=this.strings.length?this.strings[e-Wr]:Hr[0]}getSID(e){let t=Hr.indexOf(e);if(-1!==t)return t;t=this.strings.indexOf(e);return-1!==t?t+Wr:-1}add(e){this.strings.push(e)}get count(){return this.strings.length}}class CFFIndex{constructor(){this.object\ -s=[];this.length=0}add(e){this.length+=e.length;this.objects.push(e)}set(e,t){this.length+=t.length-this.objects[e].length;this.objects[e]=t}get(e){return this.objects[e]}get count(){return this.objects.length}}class CFFDict{constructor(e,t){this.keyToNameMap=e.keyToNameMap;this.nameToKeyMap=e.nameToKeyMap;this.defaults=e.defaults;this.types=e.types;this.opcodes=e.opcodes;this.order=e.order;this.strings=t;this.values=Object.create(null)}setByKey(e,t){if(!(e in this.keyToNameMap))return!1;if(0===\ -t.length)return!0;for(const a of t)if(isNaN(a)){warn(`Invalid CFFDict value: "${t}" for key "${e}".`);return!0}const a=this.types[e];"num"!==a&&"sid"!==a&&"offset"!==a||(t=t[0]);this.values[e]=t;return!0}setByName(e,t){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name "${e}"`);this.values[this.nameToKeyMap[e]]=t}hasName(e){return this.nameToKeyMap[e]in this.values}getByName(e){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name ${e}"`);const t=th\ -is.nameToKeyMap[e];return t in this.values?this.values[t]:this.defaults[t]}removeByName(e){delete this.values[this.nameToKeyMap[e]]}static createTables(e){const t={keyToNameMap:{},nameToKeyMap:{},defaults:{},types:{},opcodes:{},order:[]};for(const a of e){const e=Array.isArray(a[0])?(a[0][0]<<8)+a[0][1]:a[0];t.keyToNameMap[e]=a[1];t.nameToKeyMap[a[1]]=e;t.types[e]=a[2];t.defaults[e]=a[3];t.opcodes[e]=Array.isArray(a[0])?a[0]:[a[0]];t.order.push(e)}return t}}const Gr=[[[12,30],"ROS",["sid","sid",\ -"num"],null],[[12,20],"SyntheticBase","num",null],[0,"version","sid",null],[1,"Notice","sid",null],[[12,0],"Copyright","sid",null],[2,"FullName","sid",null],[3,"FamilyName","sid",null],[4,"Weight","sid",null],[[12,1],"isFixedPitch","num",0],[[12,2],"ItalicAngle","num",0],[[12,3],"UnderlinePosition","num",-100],[[12,4],"UnderlineThickness","num",50],[[12,5],"PaintType","num",0],[[12,6],"CharstringType","num",2],[[12,7],"FontMatrix",["num","num","num","num","num","num"],[.001,0,0,.001,0,0]],[13,"U\ -niqueID","num",null],[5,"FontBBox",["num","num","num","num"],[0,0,0,0]],[[12,8],"StrokeWidth","num",0],[14,"XUID","array",null],[15,"charset","offset",0],[16,"Encoding","offset",0],[17,"CharStrings","offset",0],[18,"Private",["offset","offset"],null],[[12,21],"PostScript","sid",null],[[12,22],"BaseFontName","sid",null],[[12,23],"BaseFontBlend","delta",null],[[12,31],"CIDFontVersion","num",0],[[12,32],"CIDFontRevision","num",0],[[12,33],"CIDFontType","num",0],[[12,34],"CIDCount","num",8720],[[12,\ -35],"UIDBase","num",null],[[12,37],"FDSelect","offset",null],[[12,36],"FDArray","offset",null],[[12,38],"FontName","sid",null]];class CFFTopDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Gr))}constructor(e){super(CFFTopDict.tables,e);this.privateDict=null}}const Vr=[[6,"BlueValues","delta",null],[7,"OtherBlues","delta",null],[8,"FamilyBlues","delta",null],[9,"FamilyOtherBlues","delta",null],[[12,9],"BlueScale","num",.039625],[[12,10],"BlueShift","num",7],[\ -[12,11],"BlueFuzz","num",1],[10,"StdHW","num",null],[11,"StdVW","num",null],[[12,12],"StemSnapH","delta",null],[[12,13],"StemSnapV","delta",null],[[12,14],"ForceBold","num",0],[[12,17],"LanguageGroup","num",0],[[12,18],"ExpansionFactor","num",.06],[[12,19],"initialRandomSeed","num",0],[20,"defaultWidthX","num",0],[21,"nominalWidthX","num",0],[19,"Subrs","offset",null]];class CFFPrivateDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(Vr))}constructor(e){super\ -(CFFPrivateDict.tables,e);this.subrsIndex=null}}const Kr={ISO_ADOBE:0,EXPERT:1,EXPERT_SUBSET:2};class CFFCharset{constructor(e,t,a,r){this.predefined=e;this.format=t;this.charset=a;this.raw=r}}class CFFEncoding{constructor(e,t,a,r){this.predefined=e;this.format=t;this.encoding=a;this.raw=r}}class CFFFDSelect{constructor(e,t){this.format=e;this.fdSelect=t}getFDIndex(e){return e<0||e>=this.fdSelect.length?-1:this.fdSelect[e]}}class CFFOffsetTracker{constructor(){this.offsets=Object.create(null)}is\ -Tracking(e){return e in this.offsets}track(e,t){if(e in this.offsets)throw new FormatError(`Already tracking location of ${e}`);this.offsets[e]=t}offset(e){for(const t in this.offsets)this.offsets[t]+=e}setEntryLocation(e,t,a){if(!(e in this.offsets))throw new FormatError(`Not tracking location of ${e}`);const r=a.data,i=this.offsets[e];for(let e=0,a=t.length;e>24&255;r[s]=l>>16&255;r[o]=l>>8&255;r[c]=255&l}}}class CFFCompiler{constructor(e){this.cff=e}compile(){const e=this.cff,t={data:[],length:0,add(e){try{this.data.push(...e)}catch{this.data=this.data.concat(e)}this.length=this.data.length}},a=this.compileHeader(e.header);t.add(a);const r=this.compileNameIndex(e.names);t.add(r);if(e.isCIDFont&&e.topDict.hasName("FontMatrix")){const t=e.topDict.getByName("FontMatrix");e.topDict.removeByName("Fon\ -tMatrix");for(const a of e.fdArray){let e=t.slice(0);a.hasName("FontMatrix")&&(e=Util.transform(e,a.getByName("FontMatrix")));a.setByName("FontMatrix",e)}}const i=e.topDict.getByName("XUID");i?.length>16&&e.topDict.removeByName("XUID");e.topDict.setByName("charset",0);let n=this.compileTopDicts([e.topDict],t.length,e.isCIDFont);t.add(n.output);const s=n.trackers[0],o=this.compileStringIndex(e.strings.strings);t.add(o);const c=this.compileIndex(e.globalSubrIndex);t.add(c);if(e.encoding&&e.topDict\ -.hasName("Encoding"))if(e.encoding.predefined)s.setEntryLocation("Encoding",[e.encoding.format],t);else{const a=this.compileEncoding(e.encoding);s.setEntryLocation("Encoding",[t.length],t);t.add(a)}const l=this.compileCharset(e.charset,e.charStrings.count,e.strings,e.isCIDFont);s.setEntryLocation("charset",[t.length],t);t.add(l);const h=this.compileCharStrings(e.charStrings);s.setEntryLocation("CharStrings",[t.length],t);t.add(h);if(e.isCIDFont){s.setEntryLocation("FDSelect",[t.length],t);const \ -a=this.compileFDSelect(e.fdSelect);t.add(a);n=this.compileTopDicts(e.fdArray,t.length,!0);s.setEntryLocation("FDArray",[t.length],t);t.add(n.output);const r=n.trackers;this.compilePrivateDicts(e.fdArray,r,t)}this.compilePrivateDicts([e.topDict],[s],t);t.add([0]);return t.data}encodeNumber(e){return Number.isInteger(e)?this.encodeInteger(e):this.encodeFloat(e)}static get EncodeFloatRegExp(){return shadow(this,"EncodeFloatRegExp",/\\.(\\d*?)(?:9{5,20}|0{5,20})\\d{0,2}(?:e(.+)|$)/)}encodeFloat(e){let \ -t=e.toString();const a=CFFCompiler.EncodeFloatRegExp.exec(t);if(a){const r=parseFloat("1e"+((a[2]?+a[2]:0)+a[1].length));t=(Math.round(e*r)/r).toString()}let r,i,n="";for(r=0,i=t.length;r=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-\ -108)>>8),255&e]:e>=-32768&&e<=32767?[28,e>>8&255,255&e]:[29,e>>24&255,e>>16&255,e>>8&255,255&e];return t}compileHeader(e){return[e.major,e.minor,4,e.offSize]}compileNameIndex(e){const t=new CFFIndex;for(const a of e){const e=Math.min(a.length,127);let r=new Array(e);for(let t=0;t"~"||"["===e||"]"===e||"("===e||")"===e||"{"===e||"}"===e||"<"===e||">"===e||"/"===e||"%"===e)&&(e="_");r[t]=e}r=r.join("");""===r&&(r="Bad_Font_Name");t.add(stringToBytes(r))}return this.com\ -pileIndex(t)}compileTopDicts(e,t,a){const r=[];let i=new CFFIndex;for(const n of e){if(a){n.removeByName("CIDFontVersion");n.removeByName("CIDFontRevision");n.removeByName("CIDFontType");n.removeByName("CIDCount");n.removeByName("UIDBase")}const e=new CFFOffsetTracker,s=this.compileDict(n,e);r.push(e);i.add(s);e.offset(t)}i=this.compileIndex(i,r);return{trackers:r,output:i}}compilePrivateDicts(e,t,a){for(let r=0,i=e.length;r>8&255,255&e])}else{i=new Uint8Array(1+2*n);i[0]=0;let t=0;const r=e.charset.length;let s=!1;for(let n=1;n>8&255;i[n+1]=255&o}}return this.compileTypedArray(i)}compileEncoding(e){return this.compileTypedArray(e.raw)}compileFDSelect(e){const t=e.format;let a,r;switch(t){case 0:a=new Uint8Array(1+e.fdSelect.length);a[0]=t;for(r=0;r>8&255,255&i,n];for(r=1;r\ ->8&255,255&r,t);n=t}}const o=(s.length-3)/3;s[1]=o>>8&255;s[2]=255&o;s.push(r>>8&255,255&r);a=new Uint8Array(s)}return this.compileTypedArray(a)}compileTypedArray(e){return Array.from(e)}compileIndex(e,t=[]){const a=e.objects,r=a.length;if(0===r)return[0,0];const i=[r>>8&255,255&r];let n,s,o=1;for(n=0;n>8&255,255&c):3===s?i.push(c>>16&255,c>>8&255,255&c):i.push(c>>>24&\ -255,c>>16&255,c>>8&255,255&c);a[n]&&(c+=a[n].length)}for(n=0;n=this.firstChar&&e<=this.lastChar?e:-1}amend(e){unreachable("Should not call amend()")}}class CFFFont{constructor(e,t){this.properties=t;const a=new CFFParser(e,t,Rr);this.cff=a.parse();this.cff.duplicateFirstGlyph();const r=new CFFCompiler(this.cff);this.seacs=this.cff.seacs;try{this\ -.data=r.compile()}catch{warn("Failed to compile font "+t.loadedName);this.data=e}this._createBuiltInEncoding()}get numGlyphs(){return this.cff.charStrings.count}getCharset(){return this.cff.charset.charset}getGlyphMapping(){const e=this.cff,t=this.properties,{cidToGidMap:a,cMap:r}=t,i=e.charset.charset;let n,s;if(t.composite){let t,o;if(a?.length>0){t=Object.create(null);for(let e=0,r=a.length;e=0){const r=a[t];r&&(i[e]=r)}}i.length>0&&(\ -this.properties.builtInEncoding=i)}}function getFloat214(e,t){return readInt16(e,t)/16384}function getSubroutineBias(e){const t=e.length;let a=32768;t<1240?a=107:t<33900&&(a=1131);return a}function parseCmap(e,t,a){const r=1===readUint16(e,t+2)?readUint32(e,t+8):readUint32(e,t+16),i=readUint16(e,t+r);let n,s,o;if(4===i){readUint16(e,t+r+2);const a=readUint16(e,t+r+6)>>1;s=t+r+14;n=[];for(o=0;o>1;a0;)h.push({flags:n})}for(a=0;a>1;y=!0;break;case 4:s+=i.pop();moveTo(n,s);y=!0;break;case 5:for(;i.length>0;){n+=i.shift();s+=i.shift();lineTo(n,s)}break;case 6:for(;i.length>0;){n+=i.shift();lineTo(n,s);if(0===i.length)break;s+=i.shift();lineTo(n,s)}break;case 7:for(;i.length>0;){s+=i.shift();lineTo(n,s);if(0===i.length)break;n+=i.shift();lineTo(n,s)}break;case 8:for(;i.length>0;){l=n+i.shift();u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d+i.shift();bezierCurveTo(l,u,h,d,\ -n,s)}break;case 10:m=i.pop();b=null;if(a.isCFFCIDFont){const e=a.fdSelect.getFDIndex(r);if(e>=0&&eMath.abs(s-t)?n+=i.shift():s+=i.shift();bezierCurveTo(l,u,h,d,n,s);break;default:throw new FormatError(`unknown operator: 12 ${w}`)}break;case 14:if(i.length>=4){const e=i.pop(),r=i.pop();s=i.pop();n=i.pop();t.save();t.translate(n,s);let o=lookupCmap(a.cmap,String.fromCharCode(a.glyphNameMap[Ar[e]]));\ -compileCharString(a.glyphs[o.glyphId],t,a,o.glyphId);t.restore();o=lookupCmap(a.cmap,String.fromCharCode(a.glyphNameMap[Ar[r]]));compileCharString(a.glyphs[o.glyphId],t,a,o.glyphId)}return;case 19:case 20:o+=i.length>>1;c+=o+7>>3;y=!0;break;case 21:s+=i.pop();n+=i.pop();moveTo(n,s);y=!0;break;case 22:n+=i.pop();moveTo(n,s);y=!0;break;case 24:for(;i.length>2;){l=n+i.shift();u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d+i.shift();bezierCurveTo(l,u,h,d,n,s)}n+=i.shift();s+=i.shift();l\ -ineTo(n,s);break;case 25:for(;i.length>6;){n+=i.shift();s+=i.shift();lineTo(n,s)}l=n+i.shift();u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d+i.shift();bezierCurveTo(l,u,h,d,n,s);break;case 26:i.length%2&&(n+=i.shift());for(;i.length>0;){l=n;u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h;s=d+i.shift();bezierCurveTo(l,u,h,d,n,s)}break;case 27:i.length%2&&(s+=i.shift());for(;i.length>0;){l=n+i.shift();u=s;h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d;bezierCurveTo(l,u,h,d,n,s)}break;ca\ -se 28:i.push(readInt16(e,c));c+=2;break;case 29:m=i.pop()+a.gsubrsBias;b=a.gsubrs[m];b&&parse(b);break;case 30:for(;i.length>0;){l=n;u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d+(1===i.length?i.shift():0);bezierCurveTo(l,u,h,d,n,s);if(0===i.length)break;l=n+i.shift();u=s;h=l+i.shift();d=u+i.shift();s=d+i.shift();n=h+(1===i.length?i.shift():0);bezierCurveTo(l,u,h,d,n,s)}break;case 31:for(;i.length>0;){l=n+i.shift();u=s;h=l+i.shift();d=u+i.shift();s=d+i.shift();n=h+(1===i.length?i.s\ -hift():0);bezierCurveTo(l,u,h,d,n,s);if(0===i.length)break;l=n;u=s+i.shift();h=l+i.shift();d=u+i.shift();n=h+i.shift();s=d+(1===i.length?i.shift():0);bezierCurveTo(l,u,h,d,n,s)}break;default:if(w<32)throw new FormatError(`unknown operator: ${w}`);if(w<247)i.push(w-139);else if(w<251)i.push(256*(w-247)+e[c++]+108);else if(w<255)i.push(256*-(w-251)-e[c++]-108);else{i.push((e[c]<<24|e[c+1]<<16|e[c+2]<<8|e[c+3])/65536);c+=4}}y&&(i.length=0)}}(e)}class Commands{cmds=[];transformStack=[];currentTransf\ -orm=[1,0,0,1,0,0];add(e,t){if(t){const{currentTransform:a}=this;for(let e=0,r=t.length;e=0&&e2*readUint16(e,t)}const n=[];let s=i(t,0);for(let a=r;ae.getSize()+3&-4)))}write(){const e=this.getSize(),t=new DataView(new ArrayBuffer(e)),a=e>131070,r=a?4:2,i=new DataView(new ArrayBuffer((this.glyphs.length+1)*r));a?i.setUint32(0,0):i.setUint16(0,0);let n=0,s=0;for(const e of this.glyphs){n+=e.write(n,t);n=n+3&-4;s+=r;a?i.setUint32(s,n):i.setUint16(s,n>>1)}return{isLocationLong:a,loca:new Uint8Array(i.buffer),gly\ -f:new Uint8Array(t.buffer)}}scale(e){for(let t=0,a=this.glyphs.length;te.getSize())));return this.header.getSize()+e}write(e,t){if(!this.header)return 0;const a=e;e+=this.header.write(e,t);if(this.simple)e+=this.simple.write(e,t);else for(const a of this.composites)e+=a.write(e,t);return e-a}scale(e){if(!this.header)return;const t=(this.header.xMin+this.header.xMax)/2;this.header.scale(t,e);if(this.simple)this.simple.sc\ -ale(t,e);else for(const a of this.composites)a.scale(t,e)}}class GlyphHeader{constructor({numberOfContours:e,xMin:t,yMin:a,xMax:r,yMax:i}){this.numberOfContours=e;this.xMin=t;this.yMin=a;this.xMax=r;this.yMax=i}static parse(e,t){return[10,new GlyphHeader({numberOfContours:t.getInt16(e),xMin:t.getInt16(e+2),yMin:t.getInt16(e+4),xMax:t.getInt16(e+6),yMax:t.getInt16(e+8)})]}getSize(){return 10}write(e,t){t.setInt16(e,this.numberOfContours);t.setInt16(e+2,this.xMin);t.setInt16(e+4,this.yMin);t.setIn\ -t16(e+6,this.xMax);t.setInt16(e+8,this.yMax);return 10}scale(e,t){this.xMin=Math.round(e+(this.xMin-e)*t);this.xMax=Math.round(e+(this.xMax-e)*t)}}class Contour{constructor({flags:e,xCoordinates:t,yCoordinates:a}){this.xCoordinates=t;this.yCoordinates=a;this.flags=e}}class SimpleGlyph{constructor({contours:e,instructions:t}){this.contours=e;this.instructions=t}static parse(e,t,a){const r=[];for(let i=0;i255?e+=2:o>0&&(e+=1);t=n;o=Math.a\ -bs(s-a);o>255?e+=2:o>0&&(e+=1);a=s}}return e}write(e,t){const a=e,r=[],i=[],n=[];let s=0,o=0;for(const a of this.contours){for(let e=0,t=a.xCoordinates.length;e=0?18:2;r.push(e)}else r.push(l)}s=c;const h=a.yCoordinates[e];l=h-o;if(0===l){t|=32;i.push(0)}else{const e=Math.abs(l);if(e<=255){t|=l>=0?36:4;i.push(e)}else i.push(l)}o=h;n.push(t)}t.setUint16(e,r.length-1);e+=\ -2}t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}for(const a of n)t.setUint8(e++,a);for(let a=0,i=r.length;a=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(e+=2):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(e+=2);return e}write(e,t){const a=e;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(this.flags|=1):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(\ -this.flags|=1);t.setUint16(e,this.flags);t.setUint16(e+2,this.glyphIndex);e+=4;if(1&this.flags){if(2&this.flags){t.setInt16(e,this.argument1);t.setInt16(e+2,this.argument2)}else{t.setUint16(e,this.argument1);t.setUint16(e+2,this.argument2)}e+=4}else{t.setUint8(e,this.argument1);t.setUint8(e+1,this.argument2);e+=2}if(256&this.flags){t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions\ -.length}}return e-a}scale(e,t){}}function writeInt16(e,t,a){e[t]=a>>8&255;e[t+1]=255&a}function writeInt32(e,t,a){e[t]=a>>24&255;e[t+1]=a>>16&255;e[t+2]=a>>8&255;e[t+3]=255&a}function writeData(e,t,a){if(a instanceof Uint8Array)e.set(a,t);else if("string"==typeof a)for(let r=0,i=a.length;ra;){a\ -<<=1;r++}const i=a*t;return{range:i,entry:r,rangeShift:t*e-i}}toArray(){let e=this.sfnt;const t=this.tables,a=Object.keys(t);a.sort();const r=a.length;let i,n,s,o,c,l=12+16*r;const h=[l];for(i=0;i>>0;h.push(l)}const u=new Uint8Array(l);for(i=0;i>>0}writeInt32(u,l+4,e);writeInt32(u,l+8,h[i]);writeInt32(u,l+12,t[c].length);l+=16}return u}addTable(e,t){if(e in this.tables)throw new Error("Table "+e+" already exists");this.tables[e]=t}}const si=[4],oi=[5\ -],ci=[6],li=[7],hi=[8],ui=[12,35],di=[14],fi=[21],gi=[22],pi=[30],mi=[31];class Type1CharString{constructor(){this.width=0;this.lsb=0;this.flexing=!1;this.output=[];this.stack=[]}convert(e,t,a){const r=e.length;let i,n,s,o=!1;for(let c=0;cr)return!0;const i=r-e;for(let e=i;e>8&255,255&t);else{t=65536*t|0;this.output.push(255,t>>24&255,t>>16&255,t>>8&255,255&t)}}this.output.push(...t);a?this.stack.splice(i,e):this.stack.length=0;return!1}}function isHexDigit(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function decrypt(e,t,a){if(a>=e.length)return new Uint8Array(0);let r,i,n=0|t;for(r=0;r>8;n=52845*(t+n)+22719&65535}\ -return o}function isSpecial(e){return 47===e||91===e||93===e||123===e||125===e||40===e||41===e}class Type1Parser{constructor(e,t,a){if(t){const t=e.getBytes(),a=!((isHexDigit(t[0])||isWhiteSpace(t[0]))&&isHexDigit(t[1])&&isHexDigit(t[2])&&isHexDigit(t[3])&&isHexDigit(t[4])&&isHexDigit(t[5])&&isHexDigit(t[6])&&isHexDigit(t[7]));e=new Stream(a?decrypt(t,55665,4):function decryptAscii(e,t,a){let r=0|t;const i=e.length,n=new Uint8Array(i>>>1);let s,o;for(s=0,o=0;s>8;r=52845*(e+r)+22719&65535}}return n.slice(a,o)}(t,55665,4))}this.seacAnalysisEnabled=!!a;this.stream=e;this.nextChar()}readNumberArray(){this.getToken();const e=[];for(;;){const t=this.getToken();if(null===t||"]"===t||"}"===t)break;e.push(parseFloat(t||0))}return e}readNumber(){const e=this.getToken();return parseFloat(e||0)}readInt(){const e=this.getToken();return 0|parse\ -Int(e||0,10)}readBoolean(){return"true"===this.getToken()?1:0}nextChar(){return this.currentChar=this.stream.getByte()}prevChar(){this.stream.skip(-2);return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(-1===t)return null;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}if(isSpecial(t)){this.nextChar();return String.fromCharCode(t)}let a="";do{a+=String.fromCharCode(t);t=this.nextChar()}while(t>=0&&!isWhi\ -teSpace(t)&&!isSpecial(t));return a}readCharStrings(e,t){return-1===t?e:decrypt(e,4330,t)}extractFontProgram(e){const t=this.stream,a=[],r=[],i=Object.create(null);i.lenIV=4;const n={subrs:[],charstrings:[],properties:{privateData:i}};let s,o,c,l;for(;null!==(s=this.getToken());)if("/"===s){s=this.getToken();switch(s){case"CharStrings":this.getToken();this.getToken();this.getToken();this.getToken();for(;;){s=this.getToken();if(null===s||"end"===s)break;if("/"!==s)continue;const e=this.getToken()\ -;o=this.readInt();this.getToken();c=o>0?t.getBytes(o):new Uint8Array(0);l=n.properties.privateData.lenIV;const a=this.readCharStrings(c,l);this.nextChar();s=this.getToken();"noaccess"===s?this.getToken():"/"===s&&this.prevChar();r.push({glyph:e,encoded:a})}break;case"Subrs":this.readInt();this.getToken();for(;"dup"===this.getToken();){const e=this.readInt();o=this.readInt();this.getToken();c=o>0?t.getBytes(o):new Uint8Array(0);l=n.properties.privateData.lenIV;const r=this.readCharStrings(c,l);th\ -is.nextChar();s=this.getToken();"noaccess"===s&&this.getToken();a[e]=r}break;case"BlueValues":case"OtherBlues":case"FamilyBlues":case"FamilyOtherBlues":const e=this.readNumberArray();e.length>0&&e.length,0;break;case"StemSnapH":case"StemSnapV":n.properties.privateData[s]=this.readNumberArray();break;case"StdHW":case"StdVW":n.properties.privateData[s]=this.readNumberArray()[0];break;case"BlueShift":case"lenIV":case"BlueFuzz":case"BlueScale":case"LanguageGroup":n.properties.privateData[s]=this.rea\ -dNumber();break;case"ExpansionFactor":n.properties.privateData[s]=this.readNumber()||.06;break;case"ForceBold":n.properties.privateData[s]=this.readBoolean()}}for(const{encoded:t,glyph:i}of r){const r=new Type1CharString,s=r.convert(t,a,this.seacAnalysisEnabled);let o=r.output;s&&(o=[14]);const c={glyphName:i,charstring:o,width:r.width,lsb:r.lsb,seac:r.seac};".notdef"===i?n.charstrings.unshift(c):n.charstrings.push(c);if(e.builtInEncoding){const t=e.builtInEncoding.indexOf(i);t>-1&&void 0===e.wi\ -dths[t]&&t>=e.firstChar&&t<=e.lastChar&&(e.widths[t]=r.width)}}return n}extractFontHeader(e){let t;for(;null!==(t=this.getToken());)if("/"===t){t=this.getToken();switch(t){case"FontMatrix":const a=this.readNumberArray();e.fontMatrix=a;break;case"Encoding":const r=this.getToken();let i;if(/^\\d+$/.test(r)){i=[];const e=0|parseInt(r,10);this.getToken();for(let a=0;a=i){s+=a;for(;s=0&&(r[e]=i)}}return type1FontGlyphMapping(e,r,a)}hasGlyphId(e){if(e<0||e>=this.numGlyphs)return!1;if(0===e)return!0;return this.charstrings[e-1].charstring.length>0}getSeacs(e){const t=[];for(let a=0,r=e.length;a0;e--)t[e]-=t[e-1];f.setB\ -yName(e,t)}n.topDict.privateDict=f;const p=new CFFIndex;for(h=0,u=r.length;h0&&e.toUnicode.amend(t)}class fonts_Glyph{constructor(e,t,a,r,i,n,s,o,c){this.originalCharCode=e;this.fontChar=t;this.unicode=a;this.accent=r;this.width=i;this.vmetric=n;this.operatorListId=s;this.isSpace=o;this.isInFont=c}get category(){return shadow(this,"category",function getCharUnicodeCategory(e){const t=Dr.get(e);if(t)return t;const a=e.match(Mr),r={isWhitespace:!!a?.[1],isZeroWidthDiacritic:!!a?.[2],isInvisibl\ -eFormatMark:!!a?.[3]};Dr.set(e,r);return r}(this.unicode),!0)}}function int16(e,t){return(e<<8)+t}function writeSignedInt16(e,t,a){e[t+1]=a;e[t]=a>>>8}function signedInt16(e,t){const a=(e<<8)+t;return 32768&a?a-65536:a}function string16(e){return String.fromCharCode(e>>8&255,255&e)}function safeString16(e){e>32767?e=32767:e<-32768&&(e=-32768);return String.fromCharCode(e>>8&255,255&e)}function isTrueTypeCollectionFile(e){return"ttcf"===bytesToString(e.peekBytes(4))}function getFontFileType(e,{ty\ -pe:t,subtype:a,composite:r}){let i,n;if(function isTrueTypeFile(e){const t=e.peekBytes(4);return 65536===readUint32(t,0)||"true"===bytesToString(t)}(e)||isTrueTypeCollectionFile(e))i=r?"CIDFontType2":"TrueType";else if(function isOpenTypeFile(e){return"OTTO"===bytesToString(e.peekBytes(4))}(e))i=r?"CIDFontType2":"OpenType";else if(function isType1File(e){const t=e.peekBytes(2);return 37===t[0]&&33===t[1]||128===t[0]&&1===t[1]}(e))i=r?"CIDFontType0":"MMType1"===t?"MMType1":"Type1";else if(functio\ -n isCFFFile(e){const t=e.peekBytes(4);return t[0]>=1&&t[3]>=1&&t[3]<=4}(e))if(r){i="CIDFontType0";n="CIDFontType0C"}else{i="MMType1"===t?"MMType1":"Type1";n="Type1C"}else{warn("getFontFileType: Unable to detect correct font file Type/Subtype.");i=t;n=a}return[i,n]}function applyStandardFontGlyphMap(e,t){for(const a in t)e[+a]=t[a]}function buildToFontChar(e,t,a){const r=[];let i;for(let a=0,n=e.length;ah){c++;if(c>=bi.length){warn("Ran out of space in font private use area.");break}l=bi[c][0];h=bi[c][1]}const p=l++;0===g&&(g=a);let m=r.get(f);if("string"==typeof m)if(1===m.length)m=m.codePointAt(0);else{if(!u){u=new Map;for(let e=64256;e<=64335;e++){const t=String.fromCharCode(e).normalize("NFKD");t.length>1&&u.set(t,e)}}m=u.get(m)||m.codePointAt(0)}if(m&&!(d=m,bi[0][0]<=d&&d\ -<=bi[0][1]||bi[1][0]<=d&&d<=bi[1][1])&&!o.has(g)){n.set(m,g);o.add(g)}i[p]=g;s[f]=p}var d;return{toFontChar:s,charCodeToGlyphId:i,toUnicodeExtraMap:n,nextAvailableFontCharCode:l}}function createCmapTable(e,t,a){const r=function getRanges(e,t,a){const r=[];for(const t in e)e[t]>=a||r.push({fontCharCode:0|t,glyphId:e[t]});if(t)for(const[e,i]of t)i>=a||r.push({fontCharCode:e,glyphId:i});0===r.length&&r.push({fontCharCode:0,glyphId:0});r.sort(((e,t)=>e.fontCharCode-t.fontCharCode));const i=[],n=r.le\ -ngth;for(let e=0;e65535?2:1;let n,s,o,c,l="\\0\\0"+string16(i)+"\\0\\0"+string32(4+8*i);for(n=r.length-1;n>=0&&!(r[n][0]<=65535);--n);const h=n+1;r[n][0]<65535&&65535===r[n][1]&&(r[n][1]=65534);const u=r[n][1]<65535?1:0,d=h+u,f=OpenTypeFileBuilder.getSearchParams(d,2);let g,p,m,b,y="",w="",x="",S="",k="",C=0;f\ -or(n=0,s=h;n0){w+="ÿÿ";y+="ÿÿ";x+="\\0";S+="\\0\\0"}const v="\\0\\0"+string16(2*d)+string16(f.range)+string16(f.entry)+string16(f.rangeShift)+w+"\\0\\0"+y+x+S+k;let F="",T="";if(i>1){l+="\\0\\0\\n"+string32(4+8*i+\ -4+v.length);F="";for(n=0,s=r.length;ne||!o)&&(o=e\ -);c 123 are reserved for internal usage");s|=1<65535&&(c=65535)}else{o=0;c=255}const h=e.bbox||[0,0,0,0],u=a.unitsPerEm||(e.fontMatrix?1/Math.max(...e.fontMatrix.slice(0,4).map(Math.abs)):1e3),d=e.ascentScaled?1:u/yi,f=a.ascent||Math.round(d*(e.ascent||h[3]));let g=a.descent||Math.round(d*(e.descent||h[1]));g>0&&e.descent>0&&h[1\ -]<0&&(g=-g);const p=a.yMax||f,m=-a.yMin||-g;return"\\0$ô\\0\\0\\0Š»\\0\\0\\0ŒŠ»\\0\\0ß\\x001\\0\\0\\0\\0"+String.fromCharCode(e.fixedPitch?9:0)+"\\0\\0\\0\\0\\0\\0"+string32(r)+string32(i)+string32(n)+string32(s)+"*21*"+string16(e.italicAngle?1:0)+string16(o||e.firstChar)+string16(c||e.lastChar)+string16(f)+string16(g)+"\\0d"+string16(p)+string16(m)+"\\0\\0\\0\\0\\0\\0\\0\\0"+string16(e.xHeight)+string16(e.capHeight)+string16(0)+string16(o||e.firstChar)+"\\0"}function createPostTable(e){return"\\0\\0\\0"+string32(\ -Math.floor(65536*e.italicAngle))+"\\0\\0\\0\\0"+string32(e.fixedPitch?1:0)+"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"}function createPostscriptName(e){return e.replaceAll(/[^\\x21-\\x7E]|[[\\](){}<>/%]/g,"").slice(0,63)}function createNameTable(e,t){t||(t=[[],[]]);const a=[t[0][0]||"Original licence",t[0][1]||e,t[0][2]||"Unknown",t[0][3]||"uniqueID",t[0][4]||e,t[0][5]||"Version 0.11",t[0][6]||createPostscriptName(e),t[0][7]||"Unknown",t[0][8]||"Unknown",t[0][9]||"Unknown"],r=[];let i,n,s,o,c;for(i=0,n=a.length\ -;i0;if((s||o)&&"CIDFontType2"===a&&this.cidEncoding.startsWith("Identity-")){const a=e.cidToGidMap,r=[];applyStandar\ -dFontGlyphMap(r,ti());/Arial-?Black/i.test(t)?applyStandardFontGlyphMap(r,ai()):/Calibri/i.test(t)&&applyStandardFontGlyphMap(r,ri());if(a){for(const e in r){const t=r[e];void 0!==a[t]&&(r[+e]=a[t])}a.length!==this.toUnicode.length&&e.hasIncludedToUnicodeMap&&this.toUnicode instanceof IdentityToUnicodeMap&&this.toUnicode.forEach((function(e,t){const i=r[e];void 0===a[i]&&(r[+e]=t)}))}this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(e,t){r[+e]=t}));this.toFontChar=\ -r;this.toUnicode=new ToUnicodeMap(r)}else if(/Symbol/i.test(r))this.toFontChar=buildToFontChar(Cr,Fr(),this.differences);else if(/Dingbats/i.test(r))this.toFontChar=buildToFontChar(vr,Ir(),this.differences);else if(s||o){const e=buildToFontChar(this.defaultEncoding,Fr(),this.differences);"CIDFontType2"!==a||this.cidEncoding.startsWith("Identity-")||this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(t,a){e[+t]=a}));this.toFontChar=e}else{const e=Fr(),a=[];this.toUnic\ -ode.forEach(((t,r)=>{if(!this.composite){const a=getUnicodeForGlyph(this.differences[t]||this.defaultEncoding[t],e);-1!==a&&(r=a)}a[+t]=r}));this.composite&&this.toUnicode instanceof IdentityToUnicodeMap&&/Tahoma|Verdana/i.test(t)&&applyStandardFontGlyphMap(a,ti());this.toFontChar=a}amendFallbackToUnicode(e);this.loadedName=r.split("-",1)[0]}checkAndRepair(e,t,a){const r=["OS/2","cmap","head","hhea","hmtx","maxp","name","post","loca","glyf","fpgm","prep","cvt ","CFF "];function readTables(e,t){c\ -onst a=Object.create(null);a["OS/2"]=null;a.cmap=null;a.head=null;a.hhea=null;a.hmtx=null;a.maxp=null;a.name=null;a.post=null;for(let i=0;i>>0,r=e.getInt32()>>>0,i=e.getInt32()>>>0,n=e.pos;e.pos=e.start||0;e.skip(r);const s=e.getBytes(i);e.pos=n;if("head"===t){s[8]=s[9]=s[10]=s[11]=0;s[17]|=32}return{tag:t,checksum:a,length:i,offset:r,data:s}\ -}function readOpenTypeHeader(e){return{version:e.getString(4),numTables:e.getUint16(),searchRange:e.getUint16(),entrySelector:e.getUint16(),rangeShift:e.getUint16()}}function sanitizeGlyph(e,t,a,r,i,n){const s={length:0,sizeOfInstructions:0};if(t<0||t>=e.length||a>e.length||a-t<=12)return s;const o=e.subarray(t,a),c=signedInt16(o[2],o[3]),l=signedInt16(o[4],o[5]),h=signedInt16(o[6],o[7]),u=signedInt16(o[8],o[9]);if(c>h){writeSignedInt16(o,2,h);writeSignedInt16(o,6,c)}if(l>u){writeSignedInt16(o,4\ -,u);writeSignedInt16(o,8,l)}const d=signedInt16(o[0],o[1]);if(d<0){if(d<-1)return s;r.set(o,i);s.length=o.length;return s}let f,g=10,p=0;for(f=0;fo.length)return s;if(!n&&b>0){r\ -.set(o.subarray(0,m),i);r.set([0,0],i+m);r.set(o.subarray(y,x),i+m+2);x-=b;o.length-x>3&&(x=x+3&-4);s.length=x;return s}if(o.length-x>3){x=x+3&-4;r.set(o.subarray(0,x),i);s.length=x;return s}r.set(o,i);s.length=o.length;return s}function readNameTable(e){const a=(t.start||0)+e.offset;t.pos=a;const r=[[],[]],i=[],n=e.length,s=a+n;if(0!==t.getUint16()||n<6)return[r,i];const o=t.getUint16(),c=t.getUint16();let l,h;for(l=0;ls)continue;t.pos=n;const o=e.name;if(e.encoding){let a="";for(let r=0,i=e.length;r0&&(l+=e-1)}}else{if(m||y){warn("TT: nested FDEFs not allowed");p=!0}m=!0;u=l;s=d.pop();t.functionsDefined[s]={data:c,i:l}}else if(!m&&!y){s=d.at(-1);if(isNaN(s))info("TT: CALL empty stack (or invalid entry).");else{t.functionsUsed[s]=!0;if(s in t.funct\ -ionsStackDeltas){const e=d.length+t.functionsStackDeltas[s];if(e<0){warn("TT: CALL invalid functions stack delta.");t.hintsValid=!1;return}d.length=e}else if(s in t.functionsDefined&&!g.includes(s)){f.push({data:c,i:l,stackTop:d.length-1});g.push(s);o=t.functionsDefined[s];if(!o){warn("TT: CALL non-existent function");t.hintsValid=!1;return}c=o.data;l=o.i}}}if(!m&&!y){let t=0;e<=142?t=i[e]:e>=192&&e<=223?t=-1:e>=224&&(t=-2);if(e>=113&&e<=117){r=d.pop();isNaN(r)||(t=2*-r)}for(;t<0&&d.length>0;){d\ -.pop();t++}for(;t>0;){d.push(NaN);t--}}}t.tooComplexToFollowFunctions=p;const w=[c];l>c.length&&w.push(new Uint8Array(l-c.length));if(u>h){warn("TT: complementing a missing function tail");w.push(new Uint8Array([34,45]))}!function foldTTTable(e,t){if(t.length>1){let a,r,i=0;for(a=0,r=t.length;a>>0,n=[];for(let t=0;t>>0);const s={ttcTag:t,majorVersion:a,minorVersion:r,numFonts:i,offsetTable:n};switch(a){case 1:return s;case 2:s.dsigTag=e.getInt32()>>>0;s.dsigLength=e.getInt32()>>>\ -0;s.dsigOffset=e.getInt32()>>>0;return s}throw new FormatError(`Invalid TrueType Collection majorVersion: ${a}.`)}(e),i=t.split("+");let n;for(let s=0;s0||!(a.cMap instanceof IdentityCMap));if("OTTO"===n.version&&!t||!s.head||!s.hhea||!s.maxp||!s.post){c=new Stream(s["CFF "].data);o=new CFFFont(c,a);return this.convert(e,o,a)}delete s.glyf;delete s.loca;delete s.fpgm;delete s.prep;delete s["cvt "];this.isOpenType=!0}if(!s.maxp)throw new FormatError(\'Required "maxp" ta\ -ble is not found\');t.pos=(t.start||0)+s.maxp.offset;let h=t.getInt32();const u=t.getUint16();if(65536!==h&&20480!==h){if(6===s.maxp.length)h=20480;else{if(!(s.maxp.length>=32))throw new FormatError(\'"maxp" table has a wrong version number\');h=65536}!function writeUint32(e,t,a){e[t+3]=255&a;e[t+2]=a>>>8;e[t+1]=a>>>16;e[t]=a>>>24}(s.maxp.data,0,h)}if(a.scaleFactors?.length===u&&l){const{scaleFactors:e}=a,t=int16(s.head.data[50],s.head.data[51]),r=new GlyfTable({glyfTable:s.glyf.data,isGlyphLocatio\ -nsLong:t,locaTable:s.loca.data,numGlyphs:u});r.scale(e);const{glyf:i,loca:n,isLocationLong:o}=r.write();s.glyf.data=i;s.loca.data=n;if(o!==!!t){s.head.data[50]=0;s.head.data[51]=o?1:0}const c=s.hmtx.data;for(let t=0;t>8&255;c[a+1]=255&r;writeSignedInt16(c,a+2,Math.round(e[t]*signedInt16(c[a+2],c[a+3])))}}let d=u+1,f=!0;if(d>65535){f=!1;d=u;warn("Not enough space in glyfs to duplicate first glyph.")}let g=0,p=0;if(h>=65536&&s.maxp.l\ -ength>=32){t.pos+=8;if(t.getUint16()>2){s.maxp.data[14]=0;s.maxp.data[15]=2}t.pos+=4;g=t.getUint16();t.pos+=4;p=t.getUint16()}s.maxp.data[4]=d>>8;s.maxp.data[5]=255&d;const m=function sanitizeTTPrograms(e,t,a,r){const i={functionsDefined:[],functionsUsed:[],functionsStackDeltas:[],tooComplexToFollowFunctions:!1,hintsValid:!0};e&&sanitizeTTProgram(e,i);t&&sanitizeTTProgram(t,i);e&&function checkInvalidFunctions(e,t){if(!e.tooComplexToFollowFunctions)if(e.functionsDefined.length>t){warn("TT: more \ -functions defined than expected");e.hintsValid=!1}else for(let a=0,r=e.functionsUsed.length;at){warn("TT: invalid function id: "+a);e.hintsValid=!1;return}if(e.functionsUsed[a]&&!e.functionsDefined[a]){warn("TT: undefined function: "+a);e.hintsValid=!1;return}}}(i,r);if(a&&1&a.length){const e=new Uint8Array(a.length+1);e.set(a.data);a.data=e}return i.hintsValid}(s.fpgm,s.prep,s["cvt "],g);if(!m){delete s.fpgm;delete s.prep;delete s["cvt "]}!function sanitizeMetrics(e,t,a,r,i,n){if(!\ -t){a&&(a.data=null);return}e.pos=(e.start||0)+t.offset;e.pos+=4;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;const s=e.getUint16();e.pos+=8;e.pos+=2;let o=e.getUint16();if(0!==s){if(!(2&int16(r.data[44],r.data[45]))){t.data[22]=0;t.data[23]=0}}if(o>i){info(`The numOfMetrics (${o}) should not be greater than the numGlyphs (${i}).`);o=i;t.data[34]=(65280&o)>>8;t.data[35]=255&o}const c=i-o-(a.length-4*o>>1);if(c>0){const e=new Uint8Array(a.length+2*c);e.set(a.dat\ -a);if(n){e[a.length]=a.data[2];e[a.length+1]=a.data[3]}a.data=e}}(t,s.hhea,s.hmtx,s.head,d,f);if(!s.head)throw new FormatError(\'Required "head" table is not found\');!function sanitizeHead(e,t,a){const r=e.data,i=function int32(e,t,a,r){return(e<<24)+(t<<16)+(a<<8)+r}(r[0],r[1],r[2],r[3]);if(i>>16!=1){info("Attempting to fix invalid version in head table: "+i);r[0]=0;r[1]=1;r[2]=0;r[3]=0}const n=int16(r[50],r[51]);if(n<0||n>1){info("Attempting to fix invalid indexToLocFormat in head table: "+n);c\ -onst e=t+1;if(a===e<<1){r[50]=0;r[51]=0}else{if(a!==e<<2)throw new FormatError("Could not fix indexToLocFormat: "+n);r[50]=0;r[51]=1}}}(s.head,u,l?s.loca.length:0);let b=Object.create(null);if(l){const e=int16(s.head.data[50],s.head.data[51]),t=function sanitizeGlyphLocations(e,t,a,r,i,n,s){let o,c,l;if(r){o=4;c=function fontItemDecodeLong(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};l=function fontItemEncodeLong(e,t,a){e[t]=a>>>24&255;e[t+1]=a>>16&255;e[t+2]=a>>8&255;e[t+3]=255&a}}else{o=2\ -;c=function fontItemDecode(e,t){return e[t]<<9|e[t+1]<<1};l=function fontItemEncode(e,t,a){e[t]=a>>9&255;e[t+1]=a>>1&255}}const h=n?a+1:a,u=o*(1+h),d=new Uint8Array(u);d.set(e.data.subarray(0,u));e.data=d;const f=t.data,g=f.length,p=new Uint8Array(g);let m,b;const y=[];for(m=0,b=0;mg&&(e=g);y.push({index:m,offset:e,endOffset:0})}y.sort(((e,t)=>e.offset-t.offset));for(m=0;me.index-t.index));for(m=0;ms&&(s=e.sizeOfInstructions);S+=t;l(d,b,S)}if(0===S){const e=new Uint8Array([0,1,0,0,0,0,0,0,0,0,0,0,0,0,49,0]);for(m=0,b=o;ma+S)t.data=p.subarray(0,a+S);else{t.data=new Uint8Array(a+S);t.data.set(p.subarray(0,S))}t.data.set(p.subarray(0,a),S);l(e.data,d.length-o,S+a)}else t.data=p.subarray(0,S);return{missingGlyphs:x,maxSizeOfInstructions:s}}(s.loca,s.glyf,u,e,m,f,p);b=t.missingGlyphs;if(h>=65536&&s.maxp.length>=32){s.maxp.data[26]=t.maxSizeOfInstructions>>8;s.maxp.data[27]=255&t.maxSizeOfInstructions}}if(!s.hhea)throw new FormatError(\'Required "hhea" table is not found\');if\ -(0===s.hhea.data[10]&&0===s.hhea.data[11]){s.hhea.data[10]=255;s.hhea.data[11]=255}const y={unitsPerEm:int16(s.head.data[18],s.head.data[19]),yMax:signedInt16(s.head.data[42],s.head.data[43]),yMin:signedInt16(s.head.data[38],s.head.data[39]),ascent:signedInt16(s.hhea.data[4],s.hhea.data[5]),descent:signedInt16(s.hhea.data[6],s.hhea.data[7]),lineGap:signedInt16(s.hhea.data[8],s.hhea.data[9])};this.ascent=y.ascent/y.unitsPerEm;this.descent=y.descent/y.unitsPerEm;this.lineGap=y.lineGap/y.unitsPerEm\ -;if(this.cssFontInfo?.lineHeight){this.lineHeight=this.cssFontInfo.metrics.lineHeight;this.lineGap=this.cssFontInfo.metrics.lineGap}else this.lineHeight=this.ascent-this.descent+this.lineGap;s.post&&function readPostScriptTable(e,a,r){const i=(t.start||0)+e.offset;t.pos=i;const n=i+e.length,s=t.getInt32();t.skip(28);let o,c,l=!0;switch(s){case 65536:o=jr;break;case 131072:const e=t.getUint16();if(e!==r){l=!1;break}const i=[];for(c=0;c=32768){l=!1;break}i.push(e\ -)}if(!l)break;const h=[],u=[];for(;t.pos65535)throw new FormatError("Max size of CID is 65,535");let i=-1;t?i=r:void 0!==e[r]&&(i=e[r]);i>=0&&i>>0;let h=!1;if(o?.platformId!==i||o?.encodingId!==n){if(0!==i||0!==n&&1!==n&&3!==n)if(1===i&&0===n)h=!0;else if(3!==i||1!==n||!r&&o){if(a&&3===i&&0===n){h=!0;let a=!0;if(e>3;e.push(r);a=Math.max(r,a)}const r=[];for(let e=0;e<=a;e++)r.push({firstCode:t.getUint16(),entryCount:t.getUint16(),idDelta:signedInt16(t.getByte(),t.getByte()),idRangePos:t.pos+t.getUint16()});for(let a=0;a<2\ -56;a++)if(0===e[a]){t.pos=r[0].idRangePos+2*a;f=t.getUint16();u.push({charCode:a,glyphId:f})}else{const i=r[e[a]];for(d=0;d>1;t.skip(6);const a=[];let r;for(r=0;r>1)-(e-r);i.offsetIndex=s;o=Math.max(o,s+i.end-i.start+1)}else i.offsetIndex=-1}const c=[];for(d=0;d>>0;for(d=0;d>>0,a=t.getInt32()>>>0;let r=t.getInt32()>>>0;for(let t=e;t<=a;t++)u.push({charCode:t,glyphId:r++})}}}u.sort(((e,t)=>e.charCode-t.charCode));const g=[],p=new Set;for(const e of u){const{charCode:t}=e;if(!p.has(t)){p.add(t);g.push(e)}}return{platformId:o.platformId,encodingId:o.encodingId,mappings:g,hasShortC\ -map:h}}(s.cmap,t,this.isSymbolicFont,a.hasEncoding),r=e.platformId,i=e.encodingId,n=e.mappings;let o=[],c=!1;!a.hasEncoding||"MacRomanEncoding"!==a.baseEncodingName&&"WinAnsiEncoding"!==a.baseEncodingName||(o=getEncoding(a.baseEncodingName));if(a.hasEncoding&&!this.isSymbolicFont&&(3===r&&1===i||1===r&&0===i)){const e=Fr();for(let t=0;t<256;t++){let s;s=void 0!==this.differences[t]?this.differences[t]:o.length&&""!==o[t]?o[t]:Ar[t];if(!s)continue;const c=recoverGlyphName(s,e);let l;3===r&&1===i?\ -l=e[c]:1===r&&0===i&&(l=Sr.indexOf(c));if(void 0===l){if(!a.glyphNames&&a.hasIncludedToUnicodeMap&&!(this.toUnicode instanceof IdentityToUnicodeMap)){const e=this.toUnicode.get(t);e&&(l=e.codePointAt(0))}if(void 0===l)continue}for(const e of n)if(e.charCode===l){w[t]=e.glyphId;break}}}else if(0===r){for(const e of n)w[e.charCode]=e.glyphId;c=!0}else if(3===r&&0===i)for(const e of n){let t=e.charCode;t>=61440&&t<=61695&&(t&=255);w[t]=e.glyphId}else for(const e of n)w[e.charCode]=e.glyphId;if(a.gl\ -yphNames&&(o.length||this.differences.length))for(let e=0;e<256;++e){if(!c&&void 0!==w[e])continue;const t=this.differences[e]||o[e];if(!t)continue;const r=a.glyphNames.indexOf(t);r>0&&hasGlyph(r)&&(w[e]=r)}}0===w.length&&(w[0]=0);let x=d-1;f||(x=0);if(!a.cssFontInfo){const e=adjustMapping(w,hasGlyph,x,this.toUnicode);this.toFontChar=e.toFontChar;s.cmap={tag:"cmap",data:createCmapTable(e.charCodeToGlyphId,e.toUnicodeExtraMap,d)};s["OS/2"]&&function validateOS2Table(e,t){t.pos=(t.start||0)+e.offs\ -et;const a=t.getUint16();t.skip(60);const r=t.getUint16();if(a<4&&768&r)return!1;if(t.getUint16()>t.getUint16())return!1;t.skip(6);if(0===t.getUint16())return!1;e.data[8]=e.data[9]=0;return!0}(s["OS/2"],t)||(s["OS/2"]={tag:"OS/2",data:createOS2Table(a,e.charCodeToGlyphId,y)})}if(!l)try{c=new Stream(s["CFF "].data);o=new CFFParser(c,a,Rr).parse();o.duplicateFirstGlyph();const e=new CFFCompiler(o);s["CFF "].data=e.compile()}catch{warn("Failed to compile font "+a.loadedName)}if(s.name){const[t,r]=r\ -eadNameTable(s.name);s.name.data=createNameTable(e,t);this.psName=t[0][6]||null;a.composite||function adjustTrueTypeToUnicode(e,t,a){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(e.hasEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;if(!t)return;if(0===a.length)return;if(e.defaultEncoding===kr)return;for(const e of a)if(!isWinNameRecord(e))return;const r=kr,i=[],n=Fr();for(const e in r){const t=r[e];if(""===t)continue;const a=n[t];void 0!==a&&(i[e]=String\ -.fromCharCode(a))}i.length>0&&e.toUnicode.amend(i)}(a,this.isSymbolicFont,r)}else s.name={tag:"name",data:createNameTable(this.name)};const S=new OpenTypeFileBuilder(n.version);for(const e in s)S.addTable(e,s[e].data);return S.toArray()}convert(e,a,r){r.fixedPitch=!1;r.builtInEncoding&&function adjustType1ToUnicode(e,t){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(t===e.defaultEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const a=[],r=Fr();for(const i\ - in t){if(e.hasEncoding&&(e.baseEncodingName||void 0!==e.differences[i]))continue;const n=getUnicodeForGlyph(t[i],r);-1!==n&&(a[i]=String.fromCharCode(n))}a.length>0&&e.toUnicode.amend(a)}(r,r.builtInEncoding);let i=1;a instanceof CFFFont&&(i=a.numGlyphs-1);const n=a.getGlyphMapping(r);let s=null,o=n,c=null;if(!r.cssFontInfo){s=adjustMapping(n,a.hasGlyphId.bind(a),i,this.toUnicode);this.toFontChar=s.toFontChar;o=s.charCodeToGlyphId;c=s.toUnicodeExtraMap}const l=a.numGlyphs;function getCharCodes(\ -e,t){let a=null;for(const r in e)t===e[r]&&(a||=[]).push(0|r);return a}function createCharCode(e,t){for(const a in e)if(t===e[a])return 0|a;s.charCodeToGlyphId[s.nextAvailableFontCharCode]=t;return s.nextAvailableFontCharCode++}const h=a.seacs;if(s&&h?.length){const e=r.fontMatrix||t,i=a.getCharset(),o=Object.create(null);for(let t in h){t|=0;const a=h[t],r=Ar[a[2]],c=Ar[a[3]],l=i.indexOf(r),u=i.indexOf(c);if(l<0||u<0)continue;const d={x:a[0]*e[0]+a[1]*e[2]+e[4],y:a[0]*e[1]+a[1]*e[3]+e[5]},f=get\ -CharCodes(n,t);if(f)for(const e of f){const t=s.charCodeToGlyphId,a=createCharCode(t,l),r=createCharCode(t,u);o[e]={baseFontCharCode:a,accentFontCharCode:r,accentOffset:d}}}r.seacMap=o}const u=r.fontMatrix?1/Math.max(...r.fontMatrix.slice(0,4).map(Math.abs)):1e3,d=new OpenTypeFileBuilder("OTTO");d.addTable("CFF ",a.data);d.addTable("OS/2",createOS2Table(r,o));d.addTable("cmap",createCmapTable(o,c,l));d.addTable("head","\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0_<õ\\0\\0"+safeString16(u)+"\\0\\0\\0\\0ž\\v~\'\\0\\0\\0\\0ž\\v~\'\\0\ -\\0"+safeString16(r.descent)+"ÿ"+safeString16(r.ascent)+string16(r.italicAngle?2:0)+"\\0\\0\\0\\0\\0\\0\\0");d.addTable("hhea","\\0\\0\\0"+safeString16(r.ascent)+safeString16(r.descent)+"\\0\\0ÿÿ\\0\\0\\0\\0\\0\\0"+safeString16(r.capHeight)+safeString16(Math.tan(r.italicAngle)*r.xHeight)+"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"+string16(l));d.addTable("hmtx",function fontFieldsHmtx(){const e=a.charstrings,t=a.cff?a.cff.widths:null;let r="\\0\\0\\0\\0";for(let a=1,i=l;a=65520&&e<=65535?0:e>=62976&&e<=63743?Tr()[e]||e:173===e?45:e}(a)}this.isType3Font&&(i=a);let h=null;if(this.seacMap?.[e]){l=!0;const t=this.seacMap[e];a=t.baseFontCharCode;h={fontChar:String.fromCodePoint(t.accentFontCharCode),offset:t.accentOffs\ -et}}let u="";"number"==typeof a&&(a<=1114111?u=String.fromCodePoint(a):warn(`charToGlyph - invalid fontCharCode: ${a}`));if(this.missingFile&&this.vertical&&1===u.length){const e=_r()[u.charCodeAt(0)];e&&(u=c=String.fromCharCode(e))}n=new fonts_Glyph(e,u,c,h,r,o,i,t,l);return this._glyphCache[e]=n}charsToGlyphs(e){let t=this._charsCache[e];if(t)return t;t=[];if(this.cMap){const a=Object.create(null),r=e.length;let i=0;for(;it.length%2==1,r=this.toUnicode instanceof IdentityToUnicodeMap?e=>this.toUnicode.charCodeOf(e):e=>this.toUnicode.charCodeOf(String.fromCodePoint(e));for(let i=0,n=e.length;i55295&&(n<57344||n>65533)&&i++;if(this.toUnicode){const e=r(n);if(-1!==e){if(hasCurrentBufErrors()){t.push(a.join(""));a.length=0}for(let t=(this.cMap?this.cMap.getCharCodeLength(e):1)-1;t>=0;t--)a.push(String.fromCharCode(e>>8*t&255));continue}}if\ -(!hasCurrentBufErrors()){t.push(a.join(""));a.length=0}a.push(String.fromCodePoint(n))}t.push(a.join(""));return t}}class ErrorFont{constructor(e){this.error=e;this.loadedName="g_font_error";this.missingFile=!0}charsToGlyphs(){return[]}encodeString(e){return[e]}exportData(){return{error:this.error}}}const Si=2,Ai=3,ki=4,Ci=5,vi=6,Fi=7;class Pattern{constructor(){unreachable("Cannot initialize Pattern.")}static parseShading(e,t,a,r,i,n){const s=e instanceof BaseStream?e.dict:e,o=s.get("ShadingTyp\ -e");try{switch(o){case Si:case Ai:return new RadialAxialShading(s,t,a,r,i,n);case ki:case Ci:case vi:case Fi:return new MeshShading(e,t,a,r,i,n);default:throw new FormatError("Unsupported ShadingType: "+o)}}catch(e){if(e instanceof MissingDataException)throw e;warn(e);return new DummyShading}}}class BaseShading{static SMALL_NUMBER=1e-6;getIR(){unreachable("Abstract method `getIR` called.")}}class RadialAxialShading extends BaseShading{constructor(e,t,a,r,i,n){super();this.shadingType=e.get("Shad\ -ingType");let s=0;this.shadingType===Si?s=4:this.shadingType===Ai&&(s=6);this.coordsArr=e.getArray("Coords");if(!isNumberArray(this.coordsArr,s))throw new FormatError("RadialAxialShading: Invalid /Coords array.");const o=ColorSpaceUtils.parse({cs:e.getRaw("CS")||e.getRaw("ColorSpace"),xref:t,resources:a,pdfFunctionFactory:r,globalColorSpaceCache:i,localColorSpaceCache:n});this.bbox=lookupNormalRect(e.getArray("BBox"),null);let c=0,l=1;const h=e.getArray("Domain");isNumberArray(h,2)&&([c,l]=h);le\ -t u=!1,d=!1;const f=e.getArray("Extend");(function isBooleanArray(e,t){return Array.isArray(e)&&(null===t||e.length===t)&&e.every((e=>"boolean"==typeof e))})(f,2)&&([u,d]=f);if(!(this.shadingType!==Ai||u&&d)){const[e,t,a,r,i,n]=this.coordsArr,s=Math.hypot(e-r,t-i);a<=n+s&&n<=a+s&&warn("Unsupported radial gradient.")}this.extendStart=u;this.extendEnd=d;const g=e.getRaw("Function"),p=r.create(g,!0),m=(l-c)/840,b=this.colorStops=[];if(c>=l||m<=0){info("Bad shading domain.");return}const y=new Float\ -32Array(o.numComps),w=new Float32Array(1);let x=0;w[0]=c;p(w,0,y,0);const S=new Uint8ClampedArray(3);o.getRgb(y,0,S);let[k,C,v]=S;b.push([0,Util.makeHexColor(k,C,v)]);let F=1;w[0]=c+m;p(w,0,y,0);o.getRgb(y,0,S);let[T,O,M]=S,D=T-k+1,R=O-C+1,N=M-v+1,E=T-k-1,L=O-C-1,j=M-v-1;for(let e=2;e<840;e++){w[0]=c+e*m;p(w,0,y,0);o.getRgb(y,0,S);const[t,a,r]=S,i=e-x;D=Math.min(D,(t-k+1)/i);R=Math.min(R,(a-C+1)/i);N=Math.min(N,(r-v+1)/i);E=Math.max(E,(t-k-1)/i);L=Math.max(L,(a-C-1)/i);j=Math.max(j,(r-v-1)/i);if\ -(!(E<=D&&L<=R&&j<=N)){const e=Util.makeHexColor(T,O,M);b.push([F/840,e]);D=t-T+1;R=a-O+1;N=r-M+1;E=t-T-1;L=a-O-1;j=r-M-1;x=F;k=T;C=O;v=M}F=e;T=t;O=a;M=r}b.push([1,Util.makeHexColor(T,O,M)]);let _="transparent";e.has("Background")&&(_=o.getRgbHex(e.get("Background"),0));if(!u){b.unshift([0,_]);b[1][0]+=BaseShading.SMALL_NUMBER}if(!d){b.at(-1)[0]-=BaseShading.SMALL_NUMBER;b.push([1,_])}this.colorStops=b}getIR(){const{coordsArr:e,shadingType:t}=this;let a,r,i,n,s;if(t===Si){r=[e[0],e[1]];i=[e[2],e[\ -3]];n=null;s=null;a="axial"}else if(t===Ai){r=[e[0],e[1]];i=[e[3],e[4]];n=e[2];s=e[5];a="radial"}else unreachable(`getPattern type unknown: ${t}`);return["RadialAxial",a,this.bbox,this.colorStops,r,i,n,s]}}class MeshStreamReader{constructor(e,t){this.stream=e;this.context=t;this.buffer=0;this.bufferLength=0;const a=t.numComps;this.tmpCompsBuf=new Float32Array(a);const r=t.colorSpace.numComps;this.tmpCsCompsBuf=t.colorFn?new Float32Array(r):this.tmpCompsBuf}get hasData(){if(this.stream.end)return\ - this.stream.pos0)return!0;const e=this.stream.getByte();if(e<0)return!1;this.buffer=e;this.bufferLength=8;return!0}readBits(e){const{stream:t}=this;let{buffer:a,bufferLength:r}=this;if(32===e){if(0===r)return t.getInt32()>>>0;a=a<<24|t.getByte()<<16|t.getByte()<<8|t.getByte();const e=t.getByte();this.buffer=e&(1<>r)>>>0}if(8===e&&0===r)return t.getByte();for(;r>r}align(){this.buffer=0;this.bufferLength=0}readFlag(){return this.readBits(this.context.bitsPerFlag)}readCoordinate(){const{bitsPerCoordinate:e,decode:t}=this.context,a=this.readBits(e),r=this.readBits(e),i=e<32?1/((1<n?n:e;t=t>s?s:t;a=ae*i[t])):a;let s,o=-2;const c=[\ -];for(const[e,t]of r.map(((e,t)=>[e,t])).sort((([e],[t])=>e-t)))if(-1!==e)if(e===o+1){s.push(n[t]);o+=1}else{o=e;s=[n[t]];c.push(e,s)}return c}(e),a=new Dict(null);a.set("BaseFont",Name.get(e));a.set("Type",Name.get("Font"));a.set("Subtype",Name.get("CIDFontType2"));a.set("Encoding",Name.get("Identity-H"));a.set("CIDToGIDMap",Name.get("Identity"));a.set("W",t);a.set("FirstChar",t[0]);a.set("LastChar",t.at(-2)+t.at(-1).length-1);const r=new Dict(null);a.set("FontDescriptor",r);const i=new Dict(nu\ -ll);i.set("Ordering","Identity");i.set("Registry","Adobe");i.set("Supplement",0);a.set("CIDSystemInfo",i);return a}class PostScriptParser{constructor(e){this.lexer=e;this.operators=[];this.token=null;this.prev=null}nextToken(){this.prev=this.token;this.token=this.lexer.getToken()}accept(e){if(this.token.type===e){this.nextToken();return!0}return!1}expect(e){if(this.accept(e))return!0;throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${e}.`)}parse(){this.nextToken();this\ -.expect(yn.LBRACE);this.parseBlock();this.expect(yn.RBRACE);return this.operators}parseBlock(){for(;;)if(this.accept(yn.NUMBER))this.operators.push(this.prev.value);else if(this.accept(yn.OPERATOR))this.operators.push(this.prev.value);else{if(!this.accept(yn.LBRACE))return;this.parseCondition()}}parseCondition(){const e=this.operators.length;this.operators.push(null,null);this.parseBlock();this.expect(yn.RBRACE);if(this.accept(yn.IF)){this.operators[e]=this.operators.length;this.operators[e+1]="\ -jz"}else{if(!this.accept(yn.LBRACE))throw new FormatError("PS Function: error parsing conditional.");{const t=this.operators.length;this.operators.push(null,null);const a=this.operators.length;this.parseBlock();this.expect(yn.RBRACE);this.expect(yn.IFELSE);this.operators[t]=this.operators.length;this.operators[t+1]="j";this.operators[e]=a;this.operators[e+1]="jz"}}}}const yn={LBRACE:0,RBRACE:1,NUMBER:2,OPERATOR:3,IF:4,IFELSE:5};class PostScriptToken{static get opCache(){return shadow(this,"opCac\ -he",Object.create(null))}constructor(e,t){this.type=e;this.value=t}static getOperator(e){return PostScriptToken.opCache[e]||=new PostScriptToken(yn.OPERATOR,e)}static get LBRACE(){return shadow(this,"LBRACE",new PostScriptToken(yn.LBRACE,"{"))}static get RBRACE(){return shadow(this,"RBRACE",new PostScriptToken(yn.RBRACE,"}"))}static get IF(){return shadow(this,"IF",new PostScriptToken(yn.IF,"IF"))}static get IFELSE(){return shadow(this,"IFELSE",new PostScriptToken(yn.IFELSE,"IFELSE"))}}class Pos\ -tScriptLexer{constructor(e){this.stream=e;this.nextChar();this.strBuf=[]}nextChar(){return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(t<0)return wa;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return new PostScriptToken(yn.NUMBER,this.getNumber());case 123:this.nextChar();return PostSc\ -riptToken.LBRACE;case 125:this.nextChar();return PostScriptToken.RBRACE}const a=this.strBuf;a.length=0;a[0]=String.fromCharCode(t);for(;(t=this.nextChar())>=0&&(t>=65&&t<=90||t>=97&&t<=122);)a.push(String.fromCharCode(t));const r=a.join("");switch(r.toLowerCase()){case"if":return PostScriptToken.IF;case"ifelse":return PostScriptToken.IFELSE;default:return PostScriptToken.getOperator(r)}}getNumber(){let e=this.currentChar;const t=this.strBuf;t.length=0;t[0]=String.fromCharCode(e);for(;(e=this.nex\ -tChar())>=0&&(e>=48&&e<=57||45===e||46===e);)t.push(String.fromCharCode(e));const a=parseFloat(t.join(""));if(isNaN(a))throw new FormatError(`Invalid floating point number: ${a}`);return a}}class BaseLocalCache{constructor(e){this._onlyRefs=!0===e?.onlyRefs;if(!this._onlyRefs){this._nameRefMap=new Map;this._imageMap=new Map}this._imageCache=new RefSetCache}getByName(e){this._onlyRefs&&unreachable("Should not call `getByName` method.");const t=this._nameRefMap.get(e);return t?this.getByRef(t):thi\ -s._imageMap.get(e)||null}getByRef(e){return this._imageCache.get(e)||null}set(e,t,a){unreachable("Abstract method `set` called.")}}class LocalImageCache extends BaseLocalCache{set(e,t=null,a){if("string"!=typeof e)throw new Error(\'LocalImageCache.set - expected "name" argument.\');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalColorSpaceCache extends BaseLocalCache{set(e=null,t=null,a){if(\ -"string"!=typeof e&&!t)throw new Error(\'LocalColorSpaceCache.set - expected "name" and/or "ref" argument.\');if(t){if(this._imageCache.has(t))return;null!==e&&this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalFunctionCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error(\'LocalFunctionCache.set - expected "ref" argument.\');this._imageCache.has(t)||this._imageCache.put(t,a)}}class Loca\ -lGStateCache extends BaseLocalCache{set(e,t=null,a){if("string"!=typeof e)throw new Error(\'LocalGStateCache.set - expected "name" argument.\');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,a)}else this._imageMap.has(e)||this._imageMap.set(e,a)}}class LocalTilingPatternCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error(\'LocalTilingPatternCache.set - expected "ref" argument.\');this._imageCache.has(t)||thi\ -s._imageCache.put(t,a)}}class RegionalImageCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error(\'RegionalImageCache.set - expected "ref" argument.\');this._imageCache.has(t)||this._imageCache.put(t,a)}}class GlobalColorSpaceCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,a){if(!t)throw new Error(\'GlobalColorSpaceCache.set - expected "ref" argument.\');this._imageCache.has(t)||this._imageCache.put(t,a)}clear(){this._ima\ -geCache.clear()}}class GlobalImageCache{static NUM_PAGES_THRESHOLD=2;static MIN_IMAGES_TO_CACHE=10;static MAX_BYTE_SIZE=5e7;#H=new RefSet;constructor(){this._refCache=new RefSetCache;this._imageCache=new RefSetCache}get#W(){let e=0;for(const t of this._imageCache)e+=t.byteSize;return e}get#z(){return!(this._imageCache.size+e)):null}class\ - PDFFunction{static getSampleArray(e,t,a,r){let i,n,s=1;for(i=0,n=e.length;i>c)*h;l&=(1<0&&(d=n[u-1]);let f=a[1];u>1,c=r.length>>1,l=new Po\ -stScriptEvaluator(s),h=Object.create(null);let u=8192;const d=new Float32Array(c);return function constructPostScriptFn(e,t,a,r){let n,s,f="";const g=d;for(n=0;ne&&(s=e)}m[n]=s}if(u>0){u--;h[f]=m}a.set(m,r)}}}function isPDFFunction(e){let t;if(e instanceof Dict)t=e;else{if(!(e instanceof BaseStre\ -am))return!1;t=e.dict}return t.has("FunctionType")}class PostScriptStack{static MAX_STACK_SIZE=100;constructor(e){this.stack=e?Array.from(e):[]}push(e){if(this.stack.length>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");this.stack.push(e)}pop(){if(this.stack.length<=0)throw new Error("PostScript function stack underflow.");return this.stack.pop()}copy(e){if(this.stack.length+e>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overf\ -low.");const t=this.stack;for(let a=t.length-e,r=e-1;r>=0;r--,a++)t.push(t[a])}index(e){this.push(this.stack[this.stack.length-e-1])}roll(e,t){const a=this.stack,r=a.length-e,i=a.length-1,n=r+(t-Math.floor(t/e)*e);for(let e=r,t=i;e0?t.p\ -ush(s<>o);break;case"ceiling":s=t.pop();t.push(Math.ceil(s));break;case"copy":s=t.pop();t.copy(s);break;case"cos":s=t.pop();t.push(Math.cos(s%360/180*Math.PI));break;case"cvi":s=0|t.pop();t.push(s);break;case"cvr":break;case"div":o=t.pop();s=t.pop();t.push(s/o);break;case"dup":t.copy(1);break;case"eq":o=t.pop();s=t.pop();t.push(s===o);break;case"exch":t.roll(2,1);break;case"exp":o=t.pop();s=t.pop();t.push(s**o);break;case"false":t.push(!1);break;case"floor":s=t.pop();t.push(Math.flo\ -or(s));break;case"ge":o=t.pop();s=t.pop();t.push(s>=o);break;case"gt":o=t.pop();s=t.pop();t.push(s>o);break;case"idiv":o=t.pop();s=t.pop();t.push(s/o|0);break;case"index":s=t.pop();t.index(s);break;case"le":o=t.pop();s=t.pop();t.push(s<=o);break;case"ln":s=t.pop();t.push(Math.log(s));break;case"log":s=t.pop();t.push(Math.log10(s));break;case"lt":o=t.pop();s=t.pop();t.push(s=t?new AstLiteral(t):e.max<=t?e:new AstMin(e,t)}class PostScript\ -Compiler{compile(e,t,a){const r=[],i=[],n=t.length>>1,s=a.length>>1;let o,c,l,h,u,d,f,g,p=0;for(let e=0;et.min){o.unshift("Math.max(",n,", ");o.pu\ -sh(")")}if(s4){r=!0;t=0}else{r=!1;t=1}const c=[];for(n=0;n=0&&"ET"===An[e];--e)An[e]="EN";for(let e=n+1;e0&&(t=An[n-1]);let a=u;e+1g&&isOdd(g)&&(m=g)}for(g=p;g>=m;--g){let e=-1;for(n=0,s=c.length;n=0){reverseValues(Sn,e,n);e=-1}}else e<0&&(e=n);e>=0&&reverseValues(Sn,e,c.length)}for(n=0,s=Sn.length;n"!==e||(Sn[n]="")}return createBidiText(Sn.join(""),r)}const kn={style:"normal",weight:"n\ -ormal"},Cn={style:"normal",weight:"bold"},vn={style:"italic",weight:"normal"},Fn={style:"italic",weight:"bold"},In=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:kn,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:Cn,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roma\ -n",style:vn,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:Fn,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:kn,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSa\ -ns-Bold.ttf",style:Cn,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:vn,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:Fn,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:kn,ultimate:"monospace"}],["Courier-Bold"\ -,{alias:"Courier",style:Cn,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:vn,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:Fn,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["\ -ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:kn,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:Cn,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:vn,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:Fn,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:kn,fallback:"Helvetica"}],["Calibri-Bold",{al\ -ias:"Calibri",style:Cn,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:vn,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:Fn,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:kn}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}]]),Tn=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","ligh\ -t","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter((e=>!t.has(e.toLowerCase()))).join(" ")}function generateFont({alias:e,local:t,path:a,fallback:r,style:i,ultimate:n},s,o,c=!0,l=!0,h=""){const u={style:null,ultim\ -ate:null};if(t){const e=h?` ${h}`:"";for(const a of t)s.push(`local(${a}${e})`)}if(e){const t=In.get(e),n=h||function getStyleToAppend(e){switch(e){case Cn:return"Bold";case vn:return"Italic";case Fn:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(i);Object.assign(u,generateFont(t,s,o,c&&!r,l&&!a,n))}i&&(u.style=i);n&&(u.ultimate=n);if(c&&r){const e=In.get(r),{ultimate:t}=generateFont(e,s,o,c,l&&!a,h);u.ultimate||=t}l&&a&&o&&s.push(`\ -url(${o}${a})`);return u}function getFontSubstitution(e,t,a,r,i,n){if(r.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==n&&"Type1"!==n||!/^[A-Z]{6}\\+/.test(r)||(r=r.slice(7));const s=r=normalizeFontName(r);let o=e.get(s);if(o)return o;let c=In.get(r);if(!c)for(const[e,t]of Tn)if(r.startsWith(e)){r=`${t}${r.substring(e.length)}`;c=In.get(r);break}let l=!1;if(!c){c=In.get(i);l=!0}const h=`${t.getDocId()}_s${t.createFontId()}`;if(!c){if(!validateFontName(r)){warn(`Cannot substitute the fon\ -t because of its name: ${r}`);e.set(s,null);return null}const t=/bold/gi.test(r),a=/oblique|italic/gi.test(r),i=t&&a&&Fn||t&&Cn||a&&vn||kn;o={css:`"${getFamilyName(r)}",${h}`,guessFallback:!0,loadedName:h,baseFontName:r,src:`local(${r})`,style:i};e.set(s,o);return o}const u=[];l&&validateFontName(r)&&u.push(`local(${r})`);const{style:d,ultimate:f}=generateFont(c,u,a),g=null===f,p=g?"":`,${f}`;o={css:`"${getFamilyName(r)}",${h}${p}`,guessFallback:g,loadedName:h,baseFontName:r,src:u.join(","),styl\ -e:d};e.set(s,o);return o}const On=3285377520,Mn=4294901760,Dn=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:On;this.h2=e?4294967295&e:On}update(e){let t,a;if("string"==typeof e){t=new Uint8Array(2*e.length);a=0;for(let r=0,i=e.length;r>>8;t[a++]=255&i}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();a=t.byteLength}const r=a>>2,i=a-4*r,n=new Uint3\ -2Array(t.buffer,0,r);let s=0,o=0,c=this.h1,l=this.h2;const h=3432918353,u=461845907,d=11601,f=13715;for(let e=0;e>>17;s=s*u&Mn|s*f&Dn;c^=s;c=c<<13|c>>>19;c=5*c+3864292196}else{o=n[e];o=o*h&Mn|o*d&Dn;o=o<<15|o>>>17;o=o*u&Mn|o*f&Dn;l^=o;l=l<<13|l>>>19;l=5*l+3864292196}s=0;switch(i){case 3:s^=t[4*r+2]<<16;case 2:s^=t[4*r+1]<<8;case 1:s^=t[4*r];s=s*h&Mn|s*d&Dn;s=s<<15|s>>>17;s=s*u&Mn|s*f&Dn;1&r?c^=s:l^=s}this.h1=c;this.h2=l}hexdigest(){let e=this.h1,t=\ -this.h2;e^=t>>>1;e=3981806797*e&Mn|36045*e&Dn;t=4283543511*t&Mn|(2950163797*(t<<16|e>>>16)&Mn)>>>16;e^=t>>>1;e=444984403*e&Mn|60499*e&Dn;t=3301882366*t&Mn|(3120437893*(t<<16|e>>>16)&Mn)>>>16;e^=t>>>1;return(e>>>0).toString(16).padStart(8,"0")+(t>>>0).toString(16).padStart(8,"0")}}function resizeImageMask(e,t,a,r,i,n){const s=i*n;let o;o=t<=8?new Uint8Array(s):t<=16?new Uint16Array(s):new Uint32Array(s);const c=a/i,l=r/n;let h,u,d,f,g=0;const p=new Uint16Array(i),m=a;for(h=0;h0&&Number.isInteger(a.height)&&a.height>0&&(a.width!==f||a.height!==g)){warn("PDFImage - using the Width/Height of the image data, rather than the image dictionary.");f=a.width;g=a.height}else{const e="number"==typeof f&&f>0,t="number"==typeof g&&g>0;if(!e||!t){if(!a.fallbackDims)throw new FormatError(`Invalid image width: ${f} or height: ${g}`);warn("PDFImage - using the Width/Height of the parent image, for SMask/Mask data.");e||(f=a.fallbackDims.width);t||(g=a.fallbackDims.heigh\ -t)}}this.width=f;this.height=g;this.interpolate=h.get("I","Interpolate");this.imageMask=h.get("IM","ImageMask")||!1;this.matte=h.get("Matte")||!1;let p=a.bitsPerComponent;if(!p){p=h.get("BPC","BitsPerComponent");if(!p){if(!this.imageMask)throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);p=1}}this.bpc=p;if(!this.imageMask){let i=h.getRaw("CS")||h.getRaw("ColorSpace");const n=!!i;if(n)this.jpxDecoderOptions?.smaskInData&&(i=Name.get("DeviceRGBA"));else if(this.jpxDeco\ -derOptions)i=Name.get("DeviceRGBA");else switch(a.numComps){case 1:i=Name.get("DeviceGray");break;case 3:i=Name.get("DeviceRGB");break;case 4:i=Name.get("DeviceCMYK");break;default:throw new Error(`Images with ${a.numComps} color components not supported.`)}this.colorSpace=ColorSpaceUtils.parse({cs:i,xref:e,resources:r?t:null,pdfFunctionFactory:o,globalColorSpaceCache:c,localColorSpaceCache:l});this.numComps=this.colorSpace.numComps;if(this.jpxDecoderOptions){this.jpxDecoderOptions.numComponents\ -=n?this.numComps:0;this.jpxDecoderOptions.isIndexedColormap="Indexed"===this.colorSpace.name}}this.decode=h.getArray("D","Decode");this.needsDecode=!1;if(this.decode&&(this.colorSpace&&!this.colorSpace.isDefaultDecode(this.decode,p)||s&&!ColorSpace.isDefaultDecode(this.decode,1))){this.needsDecode=!0;const e=(1<0,c=(r+7>>3)*i,l=e.getBytes(c),h=1===r&&1===i&&o===(0===l.length||!!(128&l[0]));if(h)return{isSingleOpaquePixel:h};if(t){if(ImageResizer.needsToBeResized(r,i)){const e=new Uint8ClampedArray(r*i*4);convertBlackAndWhiteToRGBA({src:l,dest:e,width:r,height:i,nonB\ -lackColor:0,inverseDecode:o});return ImageResizer.createImage({kind:v,data:e,width:r,height:i,interpolate:n})}const e=new OffscreenCanvas(r,i),t=e.getContext("2d"),a=t.createImageData(r,i);convertBlackAndWhiteToRGBA({src:l,dest:a.data,width:r,height:i,nonBlackColor:0,inverseDecode:o});t.putImageData(a,0,0);return{data:null,width:r,height:i,interpolate:n,bitmap:e.transferToImageBitmap()}}const u=l.byteLength;let d;if(e instanceof DecodeStream&&(!o||c===u))d=l;else if(o){d=new Uint8Array(c);d.set(\ -l);d.fill(255,u)}else d=new Uint8Array(l);if(o)for(let e=0;e>7&1;s[d+1]=u>>6&1;s[d+2]=u>>5&1;s[d+3]=u>>4&1;s[d+4]=u>>3&1;s[d+5]=u>>2&1;s[d+6]=u>>1&1;s[d+7]=1&u;d+=8}if(d>=1}}}}else{let a=0;u=0;for(d=0,h=n;d>r;i<0?i=0:i>l&&(i=l);s[d]=i;u&=(1<s[r+1]){t=255;break}}o[h]=t}}}if(o)for(h=0,d=3,u=t*r;h>3,h=t&&ImageResizer.needsToBeResiz\ -ed(a,r);if(!this.smask&&!this.mask&&"DeviceRGBA"===this.colorSpace.name){i.kind=v;const e=i.data=await this.getImageBytes(o*s*4,{});return t?h?ImageResizer.createImage(i,!1):this.createBitmap(v,a,r,e):i}if(!e){let e;"DeviceGray"===this.colorSpace.name&&1===c?e=k:"DeviceRGB"!==this.colorSpace.name||8!==c||this.needsDecode||(e=C);if(e&&!this.smask&&!this.mask&&a===s&&r===o){const n=await this.#$(s,o);if(n)return n;const c=await this.getImageBytes(o*l,{});if(t)return h?ImageResizer.createImage({dat\ -a:c,kind:e,width:a,height:r,interpolate:this.interpolate},this.needsDecode):this.createBitmap(e,s,o,c);i.kind=e;i.data=c;if(this.needsDecode){assert(e===k,"PDFImage.createImageData: The image must be grayscale.");const t=i.data;for(let e=0,a=t.length;e>3,s=await this.getImageBytes(r*n,{internal:!0}),o=this.getComponents(s);let c,l;if(1===i){l=a*r;if(this.needsDecode)for(c=0;c0&&r[0].count++}class TimeSlotManager{static TIME_SLOT_DURATION_MS=20;\ -static CHECK_TIME_EVERY=100;constructor(){this.reset()}check(){if(++this.checkedo){const e="Image exceeded maximum allowed size and was removed.";if(!c)throw new Error(e);warn(e);return}let g;h.has("OC")&&(g=await this.parseMarkedContentProps(h.get("OC"),e));let p,m,b;if(h.get("IM","ImageMask")||!1){p=await PDFImage.createMask({image:t,isOffscreenCanvasSupported:l&&!this.parsingType3Font});if(p.isSingleOpaquePixel){m=ta;b=[];r.addImageOps(m,b,g);if(i){const e={fn:m,args:b,optionalContent:g};n.set(i,u,e);u&&this\ -._regionalImageCache.set(null,u,e)}return}if(this.parsingType3Font){b=function compileType3Glyph({data:e,width:t,height:a}){if(t>1e3||a>1e3)return null;const r=new Uint8Array([0,2,4,0,1,0,5,4,8,10,0,8,0,2,1,0]),i=t+1,n=new Uint8Array(i*(a+1));let s,o,c;const l=t+7&-8,h=new Uint8Array(l*a);let u=0;for(const t of e){let e=128;for(;e>0;){h[u++]=t&e?0:255;e>>=1}}let d=0;u=0;if(0!==h[u]){n[0]=1;++d}for(o=1;o>2)+(h[u+1]?4:0)+(h[u-l+1]?8:0);if(r[e]){n[c+o]=r[e];++d}u++}if(h[u-l]!==h[u]){n[c+o]=h[u]?2:4;++d}if(d>1e3)return null}u=l*(a-1);c=s*i;if(0!==h[u]){n[c]=8;++d}for(o=1;o1e3)return null;const f=new Int32Array([0,i,-1,0,-i,0,0,0,1]),g=[],{a:p,b:m,c:b,d:y,e:w,f:x}=(new DOMMatrix).scaleSelf(1/t,-1/a).translateSelf(0,-a);for(s=\ -0;d&&s<=a;s++){let e=s*i;const a=e+t;for(;e>4;n[e]&=l>>2|l<<2}r=e%i;o=e/i|0;g.push(oa,p*r+b*o+w,m*r+y*o+x);n[e]||--d}while(c!==e);--s}return[na,[new Float32Array(g)],new Float32Array([0,0,t,a])]}(p);if(b){r.addImageOps(aa,b,g);return}warn("Cannot compile Type3 glyph.");r.addImageOps(Vt,[p],g);return}const e\ -=`mask_${this.idFactory.createObjId()}`;r.addDependency(e);p.dataLen=p.bitmap?p.width*p.height*4:p.data.length;this._sendImgData(e,p);m=Vt;b=[{data:e,width:p.width,height:p.height,interpolate:p.interpolate,count:1}];r.addImageOps(m,b,g);if(i){const t={objId:e,fn:m,args:b,optionalContent:g};n.set(i,u,t);u&&this._regionalImageCache.set(null,u,t)}return}const y=h.has("SMask")||h.has("Mask");if(a&&d+f<200&&!y){try{const i=new PDFImage({xref:this.xref,res:e,image:t,isInline:a,pdfFunctionFactory:this.\ -_pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:s});p=await i.createImageData(!0,!1);r.addImageOps(Yt,[p],g)}catch(e){const t=`Unable to decode inline image: "${e}".`;if(!c)throw new Error(t);warn(t)}return}let w=`img_${this.idFactory.createObjId()}`,x=!1,S=null;if(this.parsingType3Font)w=`${this.idFactory.getDocId()}_type3_${w}`;else if(i&&u){x=this.globalImageCache.shouldCache(u,this.pageIndex);if(x){assert(!a,"Cannot cache an inline image globally.");\ -w=`${this.idFactory.getDocId()}_${w}`}}r.addDependency(w);m=Jt;b=[w,d,f];r.addImageOps(m,b,g,y);if(x){S={objId:w,fn:m,args:b,optionalContent:g,hasMask:y,byteSize:0};if(this.globalImageCache.hasDecodeFailed(u)){this.globalImageCache.setData(u,S);this._sendImgData(w,null,x);return}if(d*f>25e4||y){const e=await this.handler.sendWithPromise("commonobj",[w,"CopyLocalImage",{imageRef:u}]);if(e){this.globalImageCache.setData(u,S);this.globalImageCache.addByteSize(u,e);return}}}PDFImage.buildImage({xref\ -:this.xref,res:e,image:t,isInline:a,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:s}).then((async e=>{p=await e.createImageData(!1,l);p.dataLen=p.bitmap?p.width*p.height*4:p.data.length;p.ref=u;x&&this.globalImageCache.addByteSize(u,p.dataLen);return this._sendImgData(w,p,x)})).catch((e=>{warn(`Unable to decode image "${w}": "${e}".`);u&&this.globalImageCache.addDecodeFailed(u);return this._sendImgData(w,null,x)}));if(i){const e\ -={objId:w,fn:m,args:b,optionalContent:g,hasMask:y};n.set(i,u,e);if(u){this._regionalImageCache.set(null,u,e);if(x){assert(S,"The global cache-data must be available.");this.globalImageCache.setData(u,S)}}}}handleSMask(e,t,a,r,i,n,s){const o=e.get("G"),c={subtype:e.get("S").name,backdrop:e.get("BC")},l=e.get("TR");if(isPDFFunction(l)){const e=this._pdfFunctionFactory.create(l),t=new Uint8Array(256),a=new Float32Array(1);for(let r=0;r<256;r++){a[0]=r/255;e(a,0,a,0);t[r]=255*a[0]|0}c.transferMap=t}\ -return this.buildFormXObject(t,o,c,a,r,i.state.clone({newPath:!0}),n,s)}handleTransferFunction(e){let t;if(Array.isArray(e))t=e;else{if(!isPDFFunction(e))return null;t=[e]}const a=[];let r=0,i=0;for(const e of t){const t=this.xref.fetchIfRef(e);r++;if(isName(t,"Identity")){a.push(null);continue}if(!isPDFFunction(t))return null;const n=this._pdfFunctionFactory.create(t),s=new Uint8Array(256),o=new Float32Array(1);for(let e=0;e<256;e++){o[0]=e/255;n(o,0,o,0);s[e]=255*o[0]|0}a.push(s);i++}return 1!\ -==r&&4!==r||0===i?null:a}handleTilingType(e,t,a,r,i,n,s,o){const c=new OperatorList,l=Dict.merge({xref:this.xref,dictArray:[i.get("Resources"),a]});return this.getOperatorList({stream:r,task:s,resources:l,operatorList:c}).then((function(){const a=c.getIR(),r=getTilingPatternIR(a,i,t);n.addDependencies(c.dependencies);n.addOp(e,r);i.objId&&o.set(null,i.objId,{operatorListIR:a,dict:i})})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`handleTilingType - i\ -gnoring pattern: "${e}".`)}}))}async handleSetFont(e,t,a,r,i,n,s=null,o=null){const c=t?.[0]instanceof Name?t[0].name:null,l=await this.loadFont(c,a,e,i,s,o);l.font.isType3Font&&r.addDependencies(l.type3Dependencies);n.font=l.font;l.send(this.handler);return l.loadedName}handleText(e,t){const a=t.font,r=a.charsToGlyphs(e);if(a.data){(!!(t.textRenderingMode&S)||"Pattern"===t.fillColorSpace.name||a.disableFontFace)&&PartialEvaluator.buildFontPaths(a,r,this.handler,this.options)}return r}ensureStat\ -eFont(e){if(e.font)return;const t=new FormatError("Missing setFont (Tf) operator before text rendering operator.");if(!this.options.ignoreErrors)throw t;warn(`ensureStateFont: "${t}".`)}async setGState({resources:e,gState:t,operatorList:a,cacheKey:r,task:i,stateManager:n,localGStateCache:s,localColorSpaceCache:o,seenRefs:c}){const l=t.objId;let h=!0;const u=[];let d=Promise.resolve();for(const[r,s]of t)switch(r){case"Type":break;case"LW":if("number"!=typeof s){warn(`Invalid LW (line width): ${s}\ -`);break}u.push([r,Math.abs(s)]);break;case"LC":case"LJ":case"ML":case"D":case"RI":case"FL":case"CA":case"ca":u.push([r,s]);break;case"Font":h=!1;d=d.then((()=>this.handleSetFont(e,null,s[0],a,i,n.state).then((function(e){a.addDependency(e);u.push([r,[e,s[1]]])}))));break;case"BM":u.push([r,normalizeBlendMode(s)]);break;case"SMask":if(isName(s,"None")){u.push([r,!1]);break}if(s instanceof Dict){h=!1;d=d.then((()=>this.handleSMask(s,e,a,i,n,o,c)));u.push([r,!0])}else warn("Unsupported SMask type"\ -);break;case"TR":const t=this.handleTransferFunction(s);u.push([r,t]);break;case"OP":case"op":case"OPM":case"BG":case"BG2":case"UCR":case"UCR2":case"TR2":case"HT":case"SM":case"SA":case"AIS":case"TK":info("graphic state operator "+r);break;default:info("Unknown graphic state operator "+r)}await d;u.length>0&&a.addOp(De,[u]);h&&s.set(r,l,u)}loadFont(e,t,a,r,i=null,n=null){const errorFont=async()=>new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Font "${e}" is not available.`),dic\ -t:t});let s;if(t)t instanceof Ref&&(s=t);else{const t=a.get("Font");t&&(s=t.getRaw(e))}if(s){if(this.type3FontRefs?.has(s))return errorFont();if(this.fontCache.has(s))return this.fontCache.get(s);try{t=this.xref.fetchIfRef(s)}catch(e){warn(`loadFont - lookup failed: "${e}".`)}}if(!(t instanceof Dict)){if(!this.options.ignoreErrors&&!this.parsingType3Font){warn(`Font "${e}" is not available.`);return errorFont()}warn(`Font "${e}" is not available -- attempting to fallback to a default font.`);t=i\ -||PartialEvaluator.fallbackFontDict}if(t.cacheKey&&this.fontCache.has(t.cacheKey))return this.fontCache.get(t.cacheKey);const{promise:o,resolve:c}=Promise.withResolvers();let l;try{l=this.preEvaluateFont(t);l.cssFontInfo=n}catch(e){warn(`loadFont - preEvaluateFont failed: "${e}".`);return errorFont()}const{descriptor:h,hash:u}=l,d=s instanceof Ref;let f;if(u&&h instanceof Dict){const e=h.fontAliases||=Object.create(null);if(e[u]){const t=e[u].aliasRef;if(d&&t&&this.fontCache.has(t)){this.fontCac\ -he.putAlias(s,t);return this.fontCache.get(s)}}else e[u]={fontID:this.idFactory.createFontId()};d&&(e[u].aliasRef=s);f=e[u].fontID}else f=this.idFactory.createFontId();assert(f?.startsWith("f"),\'The "fontID" must be (correctly) defined.\');if(d)this.fontCache.put(s,o);else{t.cacheKey=`cacheKey_${f}`;this.fontCache.put(t.cacheKey,o)}t.loadedName=`${this.idFactory.getDocId()}_${f}`;this.translateFont(l).then((async e=>{const i=new TranslatedFont({loadedName:t.loadedName,font:e,dict:t});if(e.isType3\ -Font)try{await i.loadType3Data(this,a,r)}catch(e){throw new Error(`Type3 font load error: ${e}`)}c(i)})).catch((e=>{warn(`loadFont - translateFont failed: "${e}".`);c(new TranslatedFont({loadedName:t.loadedName,font:new ErrorFont(e?.message),dict:t}))}));return o}buildPath(e,t,a){const{pathMinMax:r,pathBuffer:i}=a;switch(0|e){case Xe:{const e=a.currentPointX=t[0],n=a.currentPointY=t[1],s=t[2],o=t[3],c=e+s,l=n+o;0===s||0===o?i.push(sa,e,n,oa,c,l,la):i.push(sa,e,n,oa,c,n,oa,c,l,oa,e,l,la);Util.rec\ -tBoundingBox(e,n,c,l,r);break}case Ee:{const e=a.currentPointX=t[0],n=a.currentPointY=t[1];i.push(sa,e,n);Util.pointBoundingBox(e,n,r);break}case Pe:{const e=a.currentPointX=t[0],n=a.currentPointY=t[1];i.push(oa,e,n);Util.pointBoundingBox(e,n,r);break}case Le:{const e=a.currentPointX,n=a.currentPointY,[s,o,c,l,h,u]=t;a.currentPointX=h;a.currentPointY=u;i.push(ca,s,o,c,l,h,u);Util.bezierBoundingBox(e,n,s,o,c,l,h,u,r);break}case je:{const e=a.currentPointX,n=a.currentPointY,[s,o,c,l]=t;a.currentPo\ -intX=c;a.currentPointY=l;i.push(ca,e,n,s,o,c,l);Util.bezierBoundingBox(e,n,e,n,s,o,c,l,r);break}case _e:{const e=a.currentPointX,n=a.currentPointY,[s,o,c,l]=t;a.currentPointX=c;a.currentPointY=l;i.push(ca,s,o,c,l,c,l);Util.bezierBoundingBox(e,n,s,o,c,l,c,l,r);break}case Ue:i.push(la)}}_getColorSpace(e,t,a){return ColorSpaceUtils.parse({cs:e,xref:this.xref,resources:t,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCache,localColorSpaceCache:a,asyncIfNotCach\ -ed:!0})}async _handleColorSpace(e){try{return await e}catch(e){if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`_handleColorSpace - ignoring ColorSpace: "${e}".`);return null}throw e}}parseShading({shading:e,resources:t,localColorSpaceCache:a,localShadingPatternCache:r}){let i,n=r.get(e);if(n)return n;try{i=Pattern.parseShading(e,this.xref,t,this._pdfFunctionFactory,this.globalColorSpaceCache,a).getIR()}catch(t){if(t instanceof AbortException)return null;if(this.opt\ -ions.ignoreErrors){warn(`parseShading - ignoring shading: "${t}".`);r.set(e,null);return null}throw t}n=`pattern_${this.idFactory.createObjId()}`;this.parsingType3Font&&(n=`${this.idFactory.getDocId()}_type3_${n}`);r.set(e,n);this.parsingType3Font?this.handler.send("commonobj",[n,"Pattern",i]):this.handler.send("obj",[n,this.pageIndex,"Pattern",i]);return n}handleColorN(e,t,a,r,i,n,s,o,c,l){const h=a.pop();if(h instanceof Name){const u=i.getRaw(h.name),d=u instanceof Ref&&c.getByRef(u);if(d)try{\ -const i=r.base?r.base.getRgbHex(a,0):null,n=getTilingPatternIR(d.operatorListIR,d.dict,i);e.addOp(t,n);return}catch{}const f=this.xref.fetchIfRef(u);if(f){const i=f instanceof BaseStream?f.dict:f,h=i.get("PatternType");if(h===Rn){const o=r.base?r.base.getRgbHex(a,0):null;return this.handleTilingType(t,o,n,f,i,e,s,c)}if(h===Nn){const a=i.get("Shading"),r=this.parseShading({shading:a,resources:n,localColorSpaceCache:o,localShadingPatternCache:l});if(r){const a=lookupMatrix(i.getArray("Matrix"),nul\ -l);e.addOp(t,["Shading",r,a])}return}throw new FormatError(`Unknown PatternType: ${h}`)}}throw new FormatError(`Unknown PatternName: ${h}`)}_parseVisibilityExpression(e,t,a){if(++t>10){warn("Visibility expression is too deeply nested");return}const r=e.length,i=this.xref.fetchIfRef(e[0]);if(!(r<2)&&i instanceof Name){switch(i.name){case"And":case"Or":case"Not":a.push(i.name);break;default:warn(`Invalid operator ${i.name} in visibility expression`);return}for(let i=1;i0)return{type:"OCMD",expression:t}}const t=a.get("OCGs");if(Array.isArray(t)||t instanceof Dict){const e=[];if(Array.isArray(t))for(const a of t)e.push(a.toString());else e.push(t.objId);return{type:r,ids:e,policy:a.get("P")instanceof Name?a.get("P").name:null,expression:null}}if(t instanceof Ref)return{type:r,id:t.toString()}}return null}getOperatorList({stream:e,task:t,resources:a,operatorList:r,initialState:i=null,fallbackFont\ -Dict:n=null,prevRefs:s=null}){const o=e.dict?.objId,c=new RefSet(s);if(o){if(s?.has(o))throw new Error(`getOperatorList - ignoring circular reference: ${o}`);c.put(o)}a||=Dict.empty;i||=new EvalState;if(!r)throw new Error(\'getOperatorList: missing "operatorList" parameter\');const l=this,h=this.xref,u=new LocalImageCache,d=new LocalColorSpaceCache,f=new LocalGStateCache,g=new LocalTilingPatternCache,p=new Map,m=a.get("XObject")||Dict.empty,b=a.get("Pattern")||Dict.empty,y=new StateManager(i),w=ne\ -w EvaluatorPreprocessor(e,h,y),x=new TimeSlotManager;function closePendingRestoreOPS(e){for(let e=0,t=w.savedStatesDepth;e{y.state.fillColorSpace=e||ColorSpaceUtils.gray})));return}case yt:{const t=l._getColorSpace(e[0],a,d);if(t instanceof ColorSpace){y.state.strokeColorSpace=t;continue}next(l._handleColorSpace(t).then((e=>{y.state.strokeColorSpace=e||ColorSpa\ -ceUtils.gray})));return}case At:C=y.state.fillColorSpace;e=[C.getRgbHex(e,0)];i=It;break;case xt:C=y.state.strokeColorSpace;e=[C.getRgbHex(e,0)];i=Ft;break;case vt:y.state.fillColorSpace=ColorSpaceUtils.gray;e=[ColorSpaceUtils.gray.getRgbHex(e,0)];i=It;break;case Ct:y.state.strokeColorSpace=ColorSpaceUtils.gray;e=[ColorSpaceUtils.gray.getRgbHex(e,0)];i=Ft;break;case Ot:y.state.fillColorSpace=ColorSpaceUtils.cmyk;e=[ColorSpaceUtils.cmyk.getRgbHex(e,0)];i=It;break;case Tt:y.state.strokeColorSpace=\ -ColorSpaceUtils.cmyk;e=[ColorSpaceUtils.cmyk.getRgbHex(e,0)];i=Ft;break;case It:y.state.fillColorSpace=ColorSpaceUtils.rgb;e=[ColorSpaceUtils.rgb.getRgbHex(e,0)];break;case Ft:y.state.strokeColorSpace=ColorSpaceUtils.rgb;e=[ColorSpaceUtils.rgb.getRgbHex(e,0)];break;case kt:C=y.state.patternFillColorSpace;if(!C){if(isNumberArray(e,null)){e=[ColorSpaceUtils.gray.getRgbHex(e,0)];i=It;break}e=[];i=ia;break}if("Pattern"===C.name){next(l.handleColorN(r,kt,e,C,b,a,t,d,g,p));return}e=[C.getRgbHex(e,0)];\ -i=It;break;case St:C=y.state.patternStrokeColorSpace;if(!C){if(isNumberArray(e,null)){e=[ColorSpaceUtils.gray.getRgbHex(e,0)];i=Ft;break}e=[];i=ra;break}if("Pattern"===C.name){next(l.handleColorN(r,St,e,C,b,a,t,d,g,p));return}e=[C.getRgbHex(e,0)];i=Ft;break;case Mt:let T;try{const t=a.get("Shading");if(!t)throw new FormatError("No shading resource found");T=t.get(e[0].name);if(!T)throw new FormatError("No shading object found")}catch(e){if(e instanceof AbortException)continue;if(l.options.ignore\ -Errors){warn(`getOperatorList - ignoring Shading: "${e}".`);continue}throw e}const O=l.parseShading({shading:T,resources:a,localColorSpaceCache:d,localShadingPatternCache:p});if(!O)continue;e=[O];i=Mt;break;case De:F=e[0]instanceof Name;v=e[0].name;if(F){const t=f.getByName(v);if(t){t.length>0&&r.addOp(De,[t]);e=null;continue}}next(new Promise((function(e,i){if(!F)throw new FormatError("GState must be referred to by name.");const n=a.get("ExtGState");if(!(n instanceof Dict))throw new FormatError\ -("ExtGState should be a dictionary.");const s=n.get(v);if(!(s instanceof Dict))throw new FormatError("GState should be a dictionary.");l.setGState({resources:a,gState:s,operatorList:r,cacheKey:v,task:t,stateManager:y,localGStateCache:f,localColorSpaceCache:d,seenRefs:c}).then(e,i)})).catch((function(e){if(!(e instanceof AbortException)){if(!l.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring ExtGState: "${e}".`)}})));return;case Ce:{const[t]=e;if("number"!=typeof t){warn(`Invalid set\ -LineWidth: ${t}`);continue}e[0]=Math.abs(t);break}case Ee:case Pe:case Le:case je:case _e:case Ue:case Xe:l.buildPath(i,e,y.state);continue;case qe:case He:case We:case ze:case $e:case Ge:case Ve:case Ke:case Je:{const{state:{pathBuffer:e,pathMinMax:t}}=y;i!==He&&i!==Ve&&i!==Ke||e.push(la);if(0===e.length)r.addOp(aa,[i,[null],null]);else{r.addOp(aa,[i,[new Float32Array(e)],t.slice()]);e.length=0;t.set([1/0,1/0,-1/0,-1/0],0)}continue}case ht:r.addOp(i,[new Float32Array(e)]);continue;case Et:case \ -Pt:case Ut:case Xt:continue;case jt:if(!(e[0]instanceof Name)){warn(`Expected name for beginMarkedContentProps arg0=${e[0]}`);r.addOp(jt,["OC",null]);continue}if("OC"===e[0].name){next(l.parseMarkedContentProps(e[1],a).then((e=>{r.addOp(jt,["OC",e])})).catch((e=>{if(!(e instanceof AbortException)){if(!l.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring beginMarkedContentProps: "${e}".`);r.addOp(jt,["OC",null])}})));return}e=[e[0].name,e[1]instanceof Dict?e[1].get("MCID"):null];break;\ -default:if(null!==e){for(S=0,k=e.length;S{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring errors during "${t.name}" task: "${e}".`);closePendingRestoreOPS()}}))}getTextContent({stream:e,task:a,resources:r,stateManager:i=null,includeMarkedContent:n=!1,sink:s,seenStyles\ -:o=new Set,viewBox:c,lang:l=null,markedContentData:h=null,disableNormalization:u=!1,keepWhiteSpace:d=!1,prevRefs:f=null,intersector:g=null}){const p=e.dict?.objId,m=new RefSet(f);if(p){if(f?.has(p))throw new Error(`getTextContent - ignoring circular reference: ${p}`);m.put(p)}r||=Dict.empty;i||=new StateManager(new TextState);n&&(h||={level:0});const b={items:[],styles:Object.create(null),lang:l},y={initialized:!1,str:[],totalWidth:0,totalHeight:0,width:0,height:0,vertical:!1,prevTransform:null,\ -textAdvanceScale:0,spaceInFlowMin:0,spaceInFlowMax:0,trackingSpaceMin:1/0,negativeSpaceMax:-1/0,notASpace:-1/0,transform:null,fontName:null,hasEOL:!1},w=[" "," "];let x=0;function saveLastChar(e){const t=(x+1)%2,a=" "!==w[x]&&" "===w[t];w[x]=e;x=t;return!d&&a}function shouldAddWhitepsace(){return!d&&" "!==w[x]&&" "===w[(x+1)%2]}function resetLastChars(){w[0]=w[1]=" ";x=0}const S=this,k=this.xref,C=[];let v=null;const F=new LocalImageCache,T=new LocalGStateCache,O=new EvaluatorPreprocessor(e,k,i)\ -;let M;function pushWhitespace({width:e=0,height:t=0,transform:a=y.prevTransform,fontName:r=y.fontName}){g?.addExtraChar(" ");b.items.push({str:" ",dir:"ltr",width:e,height:t,transform:a,fontName:r,hasEOL:!1})}function getCurrentTextTransform(){const e=M.font,a=[M.fontSize*M.textHScale,0,0,M.fontSize,0,M.textRise];if(e.isType3Font&&(M.fontSize<=1||e.isCharBBox)&&!isArrayEqual(M.fontMatrix,t)){const t=e.bbox[3]-e.bbox[1];t>0&&(a[3]*=t*M.fontMatrix[3])}return Util.transform(M.ctm,Util.transform(M.\ -textMatrix,a))}function ensureTextContentItem(){if(y.initialized)return y;const{font:e,loadedName:t}=M;if(!o.has(t)){o.add(t);b.styles[t]={fontFamily:e.fallbackName,ascent:e.ascent,descent:e.descent,vertical:e.vertical};if(S.options.fontExtraProperties&&e.systemFontInfo){const a=b.styles[t];a.fontSubstitution=e.systemFontInfo.css;a.fontSubstitutionLoadedName=e.systemFontInfo.loadedName}}y.fontName=t;const a=y.transform=getCurrentTextTransform();if(e.vertical){y.width=y.totalWidth=Math.hypot(a[0]\ -,a[1]);y.height=y.totalHeight=0;y.vertical=!0}else{y.width=y.totalWidth=0;y.height=y.totalHeight=Math.hypot(a[2],a[3]);y.vertical=!1}const r=Math.hypot(M.textLineMatrix[0],M.textLineMatrix[1]),i=Math.hypot(M.ctm[0],M.ctm[1]);y.textAdvanceScale=i*r;const{fontSize:n}=M;y.trackingSpaceMin=.102*n;y.notASpace=.03*n;y.negativeSpaceMax=-.2*n;y.spaceInFlowMin=.102*n;y.spaceInFlowMax=.6*n;y.hasEOL=!1;y.initialized=!0;return y}function updateAdvanceScale(){if(!y.initialized)return;const e=Math.hypot(M.tex\ -tLineMatrix[0],M.textLineMatrix[1]),t=Math.hypot(M.ctm[0],M.ctm[1])*e;if(t!==y.textAdvanceScale){if(y.vertical){y.totalHeight+=y.height*y.textAdvanceScale;y.height=0}else{y.totalWidth+=y.width*y.textAdvanceScale;y.width=0}y.textAdvanceScale=t}}function runBidiTransform(e){let t=e.str.join("");u||(t=function normalizeUnicode(e){if(!ma){ma=/([\\u00a0\\u00b5\\u037e\\u0eb3\\u2000-\\u200a\\u202f\\u2126\\ufb00-\\ufb04\\ufb06\\ufb20-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40-\\ufb41\\ufb43-\\ufb44\\ufb46-\\ufba1\\ufba4-\\ufba9\\ufba\ -e-\\ufbb1\\ufbd3-\\ufbdc\\ufbde-\\ufbe7\\ufbea-\\ufbf8\\ufbfc-\\ufbfd\\ufc00-\\ufc5d\\ufc64-\\ufcf1\\ufcf5-\\ufd3d\\ufd88\\ufdf4\\ufdfa-\\ufdfb\\ufe71\\ufe77\\ufe79\\ufe7b\\ufe7d]+)|(\\ufb05+)/gu;ba=new Map([["ſt","ſt"]])}return e.replaceAll(ma,((e,t,a)=>t?t.normalize("NFKC"):ba.get(a)))}(t));const a=bidi(t,-1,e.vertical);return{str:a.str,dir:a.dir,width:Math.abs(e.totalWidth),height:Math.abs(e.totalHeight),transform:e.transform,fontName:e.fontName,hasEOL:e.hasEOL}}async function handleSetFont(e,i){const n=await S.loadFo\ -nt(e,i,r,a);M.loadedName=n.loadedName;M.font=n.font;M.fontMatrix=n.font.fontMatrix||t}function applyInverseRotation(e,t,a){const r=Math.hypot(a[0],a[1]);return[(a[0]*e+a[1]*t)/r,(a[2]*e+a[3]*t)/r]}function compareWithLastPosition(e){const t=getCurrentTextTransform();let a=t[4],r=t[5];if(M.font?.vertical){if(ac[2]||r+ec[3])return!1}else if(a+ec[2]||rc[3])return!1;if(!M.font||!y.prevTransform)return!0;let i=y.prevTransform[4],n=y.prevTransform[5];if(i===a&&n===r\ -)return!0;let s=-1;t[0]&&0===t[1]&&0===t[2]?s=t[0]>0?0:180:t[1]&&0===t[0]&&0===t[3]&&(s=t[1]>0?90:270);switch(s){case 0:break;case 90:[a,r]=[r,a];[i,n]=[n,i];break;case 180:[a,r,i,n]=[-a,-r,-i,-n];break;case 270:[a,r]=[-r,-a];[i,n]=[-n,-i];break;default:[a,r]=applyInverseRotation(a,r,t);[i,n]=applyInverseRotation(i,n,y.prevTransform)}if(M.font.vertical){const e=(n-r)/y.textAdvanceScale,t=a-i,s=Math.sign(y.height);if(e.5*y.width){appendEOL();return!0}resetLas\ -tChars();flushTextContentItem();return!0}if(Math.abs(t)>y.width){appendEOL();return!0}e<=s*y.notASpace&&resetLastChars();if(e<=s*y.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else y.height+=e;else if(!addFakeSpaces(e,y.prevTransform,s))if(0===y.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else y.height+=e;Math.abs(t)>.25*y.width&&flushTextContentItem();return!0}const o=(a-i)/y.textAdvanceScale,l=r-n,\ -h=Math.sign(y.width);if(o.5*y.height){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(l)>y.height){appendEOL();return!0}o<=h*y.notASpace&&resetLastChars();if(o<=h*y.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({width:Math.abs(o)})}else y.width+=o;else if(!addFakeSpaces(o,y.prevTransform,h))if(0===y.str.length){resetLastChars();pushWhitespace({width:Math.abs(o)})}else y.wid\ -th+=o;Math.abs(l)>.25*y.height&&flushTextContentItem();return!0}function buildTextContentItem({chars:e,extraSpacing:t}){const a=M.font;if(!e){const e=M.charSpacing+t;e&&(a.vertical?M.translateTextMatrix(0,-e):M.translateTextMatrix(e*M.textHScale,0));d&&compareWithLastPosition(0);return}const r=a.charsToGlyphs(e),i=M.fontMatrix[0]*M.fontSize;for(let e=0,n=r.length;e0){const e=C.join("");C.length=0;buildTextContentItem({chars:e,extraSpacing:0})}break;case dt:if(!i.state.font){S.ensureStateFont(i.state);continue}buildTextContentItem({chars:w[0],extraSpacing:0});break;case gt:if(!i.state.font){\ -S.ensureStateFont(i.state);continue}M.carriageReturn();buildTextContentItem({chars:w[0],extraSpacing:0});break;case pt:if(!i.state.font){S.ensureStateFont(i.state);continue}M.wordSpacing=w[0];M.charSpacing=w[1];M.carriageReturn();buildTextContentItem({chars:w[2],extraSpacing:0});break;case Nt:flushTextContentItem();v??=r.get("XObject")||Dict.empty;y=w[0]instanceof Name;p=w[0].name;if(y&&F.getByName(p))break;next(new Promise((function(e,t){if(!y)throw new FormatError("XObject must be referred to \ -by name.");let f=v.getRaw(p);if(f instanceof Ref){if(F.getByRef(f)){e();return}if(S.globalImageCache.getData(f,S.pageIndex)){e();return}f=k.fetch(f)}if(!(f instanceof BaseStream))throw new FormatError("XObject should be a stream");const{dict:g}=f,b=g.get("Subtype");if(!(b instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==b.name){F.set(p,g.objId,!0);e();return}const w=i.state.clone(),x=new StateManager(w),C=lookupMatrix(g.getArray("Matrix"),null);C&&x.transf\ -orm(C);const T=g.get("Resources");enqueueChunk();const O={enqueueInvoked:!1,enqueue(e,t){this.enqueueInvoked=!0;s.enqueue(e,t)},get desiredSize(){return s.desiredSize??0},get ready(){return s.ready}};S.getTextContent({stream:f,task:a,resources:T instanceof Dict?T:r,stateManager:x,includeMarkedContent:n,sink:s&&O,seenStyles:o,viewBox:c,lang:l,markedContentData:h,disableNormalization:u,keepWhiteSpace:d,prevRefs:m}).then((function(){O.enqueueInvoked||F.set(p,g.objId,!0);e()}),t)})).catch((function(\ -e){if(!(e instanceof AbortException)){if(!S.options.ignoreErrors)throw e;warn(`getTextContent - ignoring XObject: "${e}".`)}})));return;case De:y=w[0]instanceof Name;p=w[0].name;if(y&&T.getByName(p))break;next(new Promise((function(e,t){if(!y)throw new FormatError("GState must be referred to by name.");const a=r.get("ExtGState");if(!(a instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const i=a.get(p);if(!(i instanceof Dict))throw new FormatError("GState should be a dic\ -tionary.");const n=i.get("Font");if(n){flushTextContentItem();M.fontName=null;M.fontSize=n[1];handleSetFont(null,n[0]).then(e,t)}else{T.set(p,i.objId,!0);e()}})).catch((function(e){if(!(e instanceof AbortException)){if(!S.options.ignoreErrors)throw e;warn(`getTextContent - ignoring ExtGState: "${e}".`)}})));return;case Lt:flushTextContentItem();if(n){h.level++;b.items.push({type:"beginMarkedContent",tag:w[0]instanceof Name?w[0].name:null})}break;case jt:flushTextContentItem();if(n){h.level++;let\ - e=null;w[1]instanceof Dict&&(e=w[1].get("MCID"));b.items.push({type:"beginMarkedContentProps",id:Number.isInteger(e)?`${S.idFactory.getPageObjId()}_mc${e}`:null,tag:w[0]instanceof Name?w[0].name:null})}break;case _t:flushTextContentItem();if(n){if(0===h.level)break;h.level--;b.items.push({type:"endMarkedContent"})}break;case Re:!e||e.font===M.font&&e.fontSize===M.fontSize&&e.fontName===M.fontName||flushTextContentItem()}if(b.items.length>=(s?.desiredSize??1)){g=!0;break}}if(g)next(En);else{flus\ -hTextContentItem();enqueueChunk();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getTextContent - ignoring errors during "${a.name}" task: "${e}".`);flushTextContentItem();enqueueChunk()}}))}async extractDataStructures(e,t){const a=this.xref;let r;const i=this.readToUnicode(t.toUnicode);if(t.composite){const a=e.get("CIDSystemInfo");a instanceof Dict&&(t.cidSystemInfo={registry:stringToPDFString(a.get("Registry")),ordering:stringToPDFString(a.ge\ -t("Ordering")),supplement:a.get("Supplement")});try{const t=e.get("CIDToGIDMap");t instanceof BaseStream&&(r=t.getBytes())}catch(e){if(!this.options.ignoreErrors)throw e;warn(`extractDataStructures - ignoring CIDToGIDMap data: "${e}".`)}}const n=[];let s,o=null;if(e.has("Encoding")){s=e.get("Encoding");if(s instanceof Dict){o=s.get("BaseEncoding");o=o instanceof Name?o.name:null;if(s.has("Differences")){const e=s.get("Differences");let t=0;for(const r of e){const e=a.fetchIfRef(r);if("number"==t\ -ypeof e)t=e;else{if(!(e instanceof Name))throw new FormatError(`Invalid entry in \'Differences\' array: ${e}`);n[t++]=e.name}}}}else if(s instanceof Name)o=s.name;else{const e="Encoding is not a Name nor a Dict";if(!this.options.ignoreErrors)throw new FormatError(e);warn(e)}"MacRomanEncoding"!==o&&"MacExpertEncoding"!==o&&"WinAnsiEncoding"!==o&&(o=null)}const c=!t.file||t.isInternalFont,l=ei()[t.name];o&&c&&l&&(o=null);if(o)t.defaultEncoding=getEncoding(o);else{const e=!!(t.flags&Pr),a=!!(t.flags&\ -Lr);s=Ar;"TrueType"!==t.type||a||(s=kr);if(e||l){s=Sr;c&&(/Symbol/i.test(t.name)?s=Cr:/Dingbats/i.test(t.name)?s=vr:/Wingdings/i.test(t.name)&&(s=kr))}t.defaultEncoding=s}t.differences=n;t.baseEncodingName=o;t.hasEncoding=!!o||n.length>0;t.dict=e;t.toUnicode=await i;const h=await this.buildToUnicode(t);t.toUnicode=h;r&&(t.cidToGidMap=this.readCidToGidMap(r,h));return t}_simpleFontToUnicode(e,t=!1){assert(!e.composite,"Must be a simple font.");const a=[],r=e.defaultEncoding.slice(),i=e.baseEncodi\ -ngName,n=e.differences;for(const e in n){const t=n[e];".notdef"!==t&&(r[e]=t)}const s=Fr();for(const n in r){let o=r[n];if(""===o)continue;let c=s[o];if(void 0!==c){a[n]=String.fromCharCode(c);continue}let l=0;switch(o[0]){case"G":3===o.length&&(l=parseInt(o.substring(1),16));break;case"g":5===o.length&&(l=parseInt(o.substring(1),16));break;case"C":case"c":if(o.length>=3&&o.length<=4){const a=o.substring(1);if(t){l=parseInt(a,16);break}l=+a;if(Number.isNaN(l)&&Number.isInteger(parseInt(a,16)))re\ -turn this._simpleFontToUnicode(e,!0)}break;case"u":c=getUnicodeForGlyph(o,s);-1!==c&&(l=c);break;default:switch(o){case"f_h":case"f_t":case"T_h":a[n]=o.replaceAll("_","");continue}}if(l>0&&l<=1114111&&Number.isInteger(l)){if(i&&l===+n){const e=getEncoding(i);if(e&&(o=e[n])){a[n]=String.fromCharCode(s[o]);continue}}a[n]=String.fromCodePoint(l)}}return a}async buildToUnicode(e){e.hasIncludedToUnicodeMap=e.toUnicode?.length>0;if(e.hasIncludedToUnicodeMap){!e.composite&&e.hasEncoding&&(e.fallbackToU\ -nicode=this._simpleFontToUnicode(e));return e.toUnicode}if(!e.composite)return new ToUnicodeMap(this._simpleFontToUnicode(e));if(e.composite&&(e.cMap.builtInCMap&&!(e.cMap instanceof IdentityCMap)||"Adobe"===e.cidSystemInfo?.registry&&("GB1"===e.cidSystemInfo.ordering||"CNS1"===e.cidSystemInfo.ordering||"Japan1"===e.cidSystemInfo.ordering||"Korea1"===e.cidSystemInfo.ordering))){const{registry:t,ordering:a}=e.cidSystemInfo,r=Name.get(`${t}-${a}-UCS2`),i=await CMapFactory.create({encoding:r,fetchB\ -uiltInCMap:this._fetchBuiltInCMapBound,useCMap:null}),n=[],s=[];e.cMap.forEach((function(e,t){if(t>65535)throw new FormatError("Max size of CID is 65,535");const a=i.lookup(t);if(a){s.length=0;for(let e=0,t=a.length;e>1;(0!==i||t.has(n))&&(a[n]=i)}return a}extractWidths(e,t,a){const r=this.xref;let i=[],n=0;const s=[];let o;if(a.composite){const t=e.get("DW");n="number"==typeof t?Math.ceil(t):1e3;const c=e.get("W");if(Array.isArray(c))for(let e=0,t=c.length;e{const t=c.get(e),r=new OperatorList;return n.getOperatorList({stream:t,task:a,resources:l,operatorList:r}).then((()=>{switch(r.fnArray[0]){case bt:this.#K(r,b);break;case mt:b||this.#J(r)}h[e]=r.getIR();for(const e of r.dependencies)i.add(e)})).catch((function(t){warn(`Type3 font resource "${e}" is not available.`);const a=new OperatorList;h[e]=a.getIR()}))}));this.#V=o.then((()=>{r.charProcOperatorList=h;if(this\ -._bbox){r.isCharBBox=!0;r.bbox=this._bbox}}));return this.#V}#K(e,t=NaN){const a=Util.normalizeRect(e.argsArray[0].slice(2)),r=a[2]-a[0],i=a[3]-a[1],n=Math.hypot(r,i);if(0===r||0===i){e.fnArray.splice(0,1);e.argsArray.splice(0,1)}else if(0===t||Math.round(n/t)>=10){this._bbox??=[1/0,1/0,-1/0,-1/0];Util.rectBoundingBox(...a,this._bbox)}let s=0,o=e.length;for(;s=Ee&&n<=Je;if(i.variableArgs)o>s&&info(`Command ${r}: expected [0, ${s}] args, but received ${o} args.`);else{\ -if(o!==s){const e=this.nonProcessedArgs;for(;o>s;){e.push(t.shift());o--}for(;oEvaluatorPreprocessor.MAX_INVALID_PATH_OPS)throw new FormatError(`Invalid ${e}`);warn(`Skipping ${e}`);null!==t&&(t.length=0);continue}}this.preprocessCommand(n,t);e.fn=n;e.args=t;return!0}if(a===wa)return!1;if(null!==a){null===t&&(t=[]);t.\ -push(a);if(t.length>33)throw new FormatError("Too many arguments")}}}preprocessCommand(e,t){switch(0|e){case Be:this.stateManager.save();break;case Re:this.stateManager.restore();break;case Ne:this.stateManager.transform(t)}}}class DefaultAppearanceEvaluator extends EvaluatorPreprocessor{constructor(e){super(new StringStream(e))}parse(){const e={fn:0,args:[]},t={fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3)};try{for(;;){e.args.length=0;if(!this.read(e))break;if(0!==this.savedStatesDe\ -pth)continue;const{fn:a,args:r}=e;switch(0|a){case nt:const[e,a]=r;e instanceof Name&&(t.fontName=e.name);"number"==typeof a&&a>0&&(t.fontSize=a);break;case It:ColorSpaceUtils.rgb.getRgbItem(r,0,t.fontColor,0);break;case vt:ColorSpaceUtils.gray.getRgbItem(r,0,t.fontColor,0);break;case Ot:ColorSpaceUtils.cmyk.getRgbItem(r,0,t.fontColor,0)}}}catch(e){warn(`parseDefaultAppearance - ignoring errors: "${e}".`)}return t}}function parseDefaultAppearance(e){return new DefaultAppearanceEvaluator(e).parse\ -()}class AppearanceStreamEvaluator extends EvaluatorPreprocessor{constructor(e,t,a,r){super(e);this.stream=e;this.evaluatorOptions=t;this.xref=a;this.globalColorSpaceCache=r;this.resources=e.dict?.get("Resources")}parse(){const e={fn:0,args:[]};let t={scaleFactor:1,fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3),fillColorSpace:ColorSpaceUtils.gray},a=!1;const r=[];try{for(;;){e.args.length=0;if(a||!this.read(e))break;const{fn:i,args:n}=e;switch(0|i){case Be:r.push({scaleFactor:t.scaleF\ -actor,fontSize:t.fontSize,fontName:t.fontName,fontColor:t.fontColor.slice(),fillColorSpace:t.fillColorSpace});break;case Re:t=r.pop()||t;break;case ht:t.scaleFactor*=Math.hypot(n[0],n[1]);break;case nt:const[e,i]=n;e instanceof Name&&(t.fontName=e.name);"number"==typeof i&&i>0&&(t.fontSize=i*t.scaleFactor);break;case wt:t.fillColorSpace=ColorSpaceUtils.parse({cs:n[0],xref:this.xref,resources:this.resources,pdfFunctionFactory:this._pdfFunctionFactory,globalColorSpaceCache:this.globalColorSpaceCac\ -he,localColorSpaceCache:this._localColorSpaceCache});break;case At:t.fillColorSpace.getRgbItem(n,0,t.fontColor,0);break;case It:ColorSpaceUtils.rgb.getRgbItem(n,0,t.fontColor,0);break;case vt:ColorSpaceUtils.gray.getRgbItem(n,0,t.fontColor,0);break;case Ot:ColorSpaceUtils.cmyk.getRgbItem(n,0,t.fontColor,0);break;case dt:case ft:case gt:case pt:a=!0}}}catch(e){warn(`parseAppearanceStream - ignoring errors: "${e}".`)}this.stream.reset();delete t.scaleFactor;delete t.fillColorSpace;return t}get _lo\ -calColorSpaceCache(){return shadow(this,"_localColorSpaceCache",new LocalColorSpaceCache)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.evaluatorOptions.isEvalSupported}))}}function getPdfColor(e,t){if(e[0]===e[1]&&e[1]===e[2]){return`${numberToString(e[0]/255)} ${t?"g":"G"}`}return Array.from(e,(e=>numberToString(e/255))).join(" ")+" "+(t?"rg":"RG")}class FakeUnicodeFont{constructor(e,t){this.xref=e;this.widths=nul\ -l;this.firstChar=1/0;this.lastChar=-1/0;this.fontFamily=t;const a=new OffscreenCanvas(1,1);this.ctxMeasure=a.getContext("2d",{willReadFrequently:!0});FakeUnicodeFont._fontNameId||(FakeUnicodeFont._fontNameId=1);this.fontName=Name.get(`InvalidPDFjsFont_${t}_${FakeUnicodeFont._fontNameId++}`)}get fontDescriptorRef(){if(!FakeUnicodeFont._fontDescriptorRef){const e=new Dict(this.xref);e.setIfName("Type","FontDescriptor");e.set("FontName",this.fontName);e.set("FontFamily","MyriadPro Regular");e.set("\ -FontBBox",[0,0,0,0]);e.setIfName("FontStretch","Normal");e.set("FontWeight",400);e.set("ItalicAngle",0);FakeUnicodeFont._fontDescriptorRef=this.xref.getNewPersistentRef(e)}return FakeUnicodeFont._fontDescriptorRef}get descendantFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.setIfName("Type","Font");e.setIfName("Subtype","CIDFontType0");e.setIfName("CIDToGIDMap","Identity");e.set("FirstChar",this.firstChar);e.set("LastChar",this.lastChar);e.set("FontDescriptor",this.fontD\ -escriptorRef);e.set("DW",1e3);const t=[],a=[...this.widths.entries()].sort();let r=null,i=null;for(const[e,n]of a)if(r)if(e===r+i.length)i.push(n);else{t.push(r,i);r=e;i=[n]}else{r=e;i=[n]}r&&t.push(r,i);e.set("W",t);const n=new Dict(this.xref);n.set("Ordering","Identity");n.set("Registry","Adobe");n.set("Supplement",0);e.set("CIDSystemInfo",n);return this.xref.getNewPersistentRef(e)}get baseFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.setIfName("Type","Font");e.setIfN\ -ame("Subtype","Type0");e.setIfName("Encoding","Identity-H");e.set("DescendantFonts",[this.descendantFontRef]);e.setIfName("ToUnicode","Identity-H");return this.xref.getNewPersistentRef(e)}get resources(){const e=new Dict(this.xref),t=new Dict(this.xref);t.set(this.fontName.name,this.baseFontRef);e.set("Font",t);return e}_createContext(){this.widths=new Map;this.ctxMeasure.font=`1000px ${this.fontFamily}`;return this.ctxMeasure}createFontResources(e){const t=this._createContext();for(const a of e\ -.split(/\\r\\n?|\\n/))for(const e of a.split("")){const a=e.charCodeAt(0);if(this.widths.has(a))continue;const r=t.measureText(e),i=Math.ceil(r.width);this.widths.set(a,i);this.firstChar=Math.min(a,this.firstChar);this.lastChar=Math.max(a,this.lastChar)}return this.resources}static getFirstPositionInfo(e,t,i){const[n,s,o,c]=e;let l=o-n,h=c-s;t%180!=0&&([l,h]=[h,l]);const u=a*i;return{coords:[0,h+r*i-u],bbox:[0,0,l,h],matrix:0!==t?getRotationMatrix(t,h,u):void 0}}createAppearance(e,t,i,n,s,o){const \ -c=this._createContext(),l=[];let h=-1/0;for(const t of e.split(/\\r\\n?|\\n/)){l.push(t);const e=c.measureText(t).width;h=Math.max(h,e);for(const e of codePointIter(t)){const t=String.fromCodePoint(e);let a=this.widths.get(e);if(void 0===a){const r=c.measureText(t);a=Math.ceil(r.width);this.widths.set(e,a);this.firstChar=Math.min(e,this.firstChar);this.lastChar=Math.max(e,this.lastChar)}}}h*=n/1e3;const[u,d,f,g]=t;let p=f-u,m=g-d;i%180!=0&&([p,m]=[m,p]);let b=1;h>p&&(b=p/h);let y=1;const w=a*n,x=r*\ -n,S=w*l.length;S>m&&(y=m/S);const k=n*Math.min(b,y),C=["q",`0 0 ${numberToString(p)} ${numberToString(m)} re W n`,"BT",`1 0 0 1 0 ${numberToString(m+x)} Tm 0 Tc ${getPdfColor(s,!0)}`,`/${this.fontName.name} ${numberToString(k)} Tf`],{resources:v}=this;if(1!==(o="number"==typeof o&&o>=0&&o<=1?o:1)){C.push("/R0 gs");const e=new Dict(this.xref),t=new Dict(this.xref);t.set("ca",o);t.set("CA",o);t.setIfName("Type","ExtGState");e.set("R0",t);v.set("ExtGState",e)}const F=numberToString(w);for(const e o\ -f l)C.push(`0 -${F} Td <${stringToUTF16HexString(e)}> Tj`);C.push("ET","Q");const T=C.join("\\n"),O=new Dict(this.xref);O.setIfName("Subtype","Form");O.setIfName("Type","XObject");O.set("BBox",[0,0,p,m]);O.set("Length",T.length);O.set("Resources",v);if(i){const e=getRotationMatrix(i,p,m);O.set("Matrix",e)}const M=new StringStream(T);M.dict=O;return M}}const Pn=["m/d","m/d/yy","mm/dd/yy","mm/yy","d-mmm","d-mmm-yy","dd-mmm-yy","yy-mm-dd","mmm-yy","mmmm-yy","mmm d, yyyy","mmmm d, yyyy","m/d/yy h:MM \ -tt","m/d/yy HH:MM"],Ln=["HH:MM","h:MM tt","HH:MM:ss","h:MM:ss tt"];class NameOrNumberTree{constructor(e,t,a){this.root=e;this.xref=t;this._type=a}getAll(){const e=new Map;if(!this.root)return e;const t=this.xref,a=new RefSet;a.put(this.root);const r=[this.root];for(;r.length>0;){const i=t.fetchIfRef(r.shift());if(!(i instanceof Dict))continue;if(i.has("Kids")){const e=i.get("Kids");if(!Array.isArray(e))continue;for(const t of e){if(a.has(t))throw new FormatError(`Duplicate entry in "${this._type\ -}" tree.`);r.push(t);a.put(t)}continue}const n=i.get(this._type);if(Array.isArray(n))for(let a=0,r=n.length;a10){warn(`Search depth limit reached for "${this._type}" tree.`);return null}const i=a.get("Kids");if(!Array.isArray(i))return null;let n=0,s=i.length-1;for(;n<=s;){const r=n+s>>1,o=t.fetchIfRef(i[r]),c=o.get("L\ -imits");if(et.fetchIfRef(c[1]))){a=o;break}n=r+1}}if(n>s)return null}const i=a.get(this._type);if(Array.isArray(i)){let a=0,r=i.length-2;for(;a<=r;){const n=a+r>>1,s=n+(1&n),o=t.fetchIfRef(i[s]);if(eo))return i[s+1];a=s+2}}}return null}get(e){return this.xref.fetchIfRef(this.getRaw(e))}}class NameTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Names")}}class NumberTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Nums")\ -}}function clearGlobalCaches(){!function clearPatternCaches(){Ii=Object.create(null)}();!function clearPrimitiveCaches(){xa=Object.create(null);Sa=Object.create(null);Aa=Object.create(null)}();!function clearUnicodeCaches(){Dr.clear()}();JpxImage.cleanup()}function pickPlatformItem(e){return e instanceof Dict?e.has("UF")?e.get("UF"):e.has("F")?e.get("F"):e.has("Unix")?e.get("Unix"):e.has("Mac")?e.get("Mac"):e.has("DOS")?e.get("DOS"):null:null}class FileSpec{#Y=!1;constructor(e,t,a=!1){if(e insta\ -nceof Dict){this.xref=t;this.root=e;e.has("FS")&&(this.fs=e.get("FS"));e.has("RF")&&warn("Related file specifications are not supported");a||(e.has("EF")?this.#Y=!0:warn("Non-embedded file specifications are not supported"))}}get filename(){let e="";const t=pickPlatformItem(this.root);t&&"string"==typeof t&&(e=stringToPDFString(t,!0).replaceAll("\\\\\\\\","\\\\").replaceAll("\\\\/","/").replaceAll("\\\\","/"));return shadow(this,"filename",e||"unnamed")}get content(){if(!this.#Y)return null;this._contentR\ -ef||=pickPlatformItem(this.root?.get("EF"));let e=null;if(this._contentRef){const t=this.xref.fetchIfRef(this._contentRef);t instanceof BaseStream?e=t.getBytes():warn("Embedded file specification points to non-existing/invalid content")}else warn("Embedded file specification does not have any content");return e}get description(){let e="";const t=this.root?.get("Desc");t&&"string"==typeof t&&(e=stringToPDFString(t));return shadow(this,"description",e)}get serializable(){return{rawFilename:this.fi\ -lename,filename:(e=this.filename,e.substring(e.lastIndexOf("/")+1)),content:this.content,description:this.description};var e}}const jn=0,_n=-2,Un=-3,Xn=-4,qn=-5,Hn=-6,Wn=-9;function isWhitespace(e,t){const a=e[t];return" "===a||"\\n"===a||"\\r"===a||"\\t"===a}class XMLParserBase{_resolveEntities(e){return e.replaceAll(/&([^;]+);/g,((e,t)=>{if("#x"===t.substring(0,2))return String.fromCodePoint(parseInt(t.substring(2),16));if("#"===t.substring(0,1))return String.fromCodePoint(parseInt(t.substring(1)\ -,10));switch(t){case"lt":return"<";case"gt":return">";case"amp":return"&";case"quot":return\'"\';case"apos":return"\'"}return this.onResolveEntity(t)}))}_parseContent(e,t){const a=[];let r=t;function skipWs(){for(;r"!==e[r]&&"/"!==e[r];)++r;const i=e.substring(t,r);skipWs();for(;r"!==e[r]&&"/"!==e[r]&&"?"!==e[r];){skipWs();let t="",i="";for(;r"!==e[a]&&"?"!==e[a]&&"/"!==e[a];)++a;const r=e.substring(t,a);!function skipWs(){for(;a"!==e[a+1])\ -;)++a;return{name:r,value:e.substring(i,a),parsed:a-t}}parseXml(e){let t=0;for(;t",a);if(t<0){this.onError(Wn);return}this.onEndElement(e.substring(a,t));a=t+1;break;case"?":++a;const r=this._parseProcessingInstruction(e,a);if("?>"!==e.substring(a+r.parsed,a+r.parsed+2)){this.onError(Un);return}this.onPi(r.name,r.value);a+=r.parsed+2;break;case"!":if("--"===e.substring(a+1,a+3)){t=e.indexOf("--\\x3e",a+3);if(t<0){\ -this.onError(qn);return}this.onComment(e.substring(a+3,t));a=t+3}else if("[CDATA["===e.substring(a+1,a+8)){t=e.indexOf("]]>",a+8);if(t<0){this.onError(_n);return}this.onCdata(e.substring(a+8,t));a=t+3}else{if("DOCTYPE"!==e.substring(a+1,a+8)){this.onError(Hn);return}{const r=e.indexOf("[",a+8);let i=!1;t=e.indexOf(">",a+8);if(t<0){this.onError(Xn);return}if(r>0&&t>r){t=e.indexOf("]>",a+8);if(t<0){this.onError(Xn);return}i=!0}const n=e.substring(a+8,t+(i?1:0));this.onDoctype(n);a=t+(i?2:1)}}break\ -;default:const i=this._parseContent(e,a);if(null===i){this.onError(Hn);return}let n=!1;if("/>"===e.substring(a+i.parsed,a+i.parsed+2))n=!0;else if(">"!==e.substring(a+i.parsed,a+i.parsed+1)){this.onError(Wn);return}this.onBeginElement(i.name,i.attributes,n);a+=i.parsed+(n?2:1)}}else{for(;ae.textContent)).join(""):this.nodeValue||""}get children(){return this.childNodes||[]}hasChildNodes(){r\ -eturn this.childNodes?.length>0}searchNode(e,t){if(t>=e.length)return this;const a=e[t];if(a.name.startsWith("#")&&t0){r.push([i,0]);i=i.\ -childNodes[0]}else{if(0===r.length)return null;for(;0!==r.length;){const[e,t]=r.pop(),a=t+1;if(a");for(const t of this.childNodes)t.dump(e);e.push(``)}else this.nodeValue?e.push(`>${encodeT\ -oXmlString(this.nodeValue)}`):e.push("/>")}else e.push(encodeToXmlString(this.nodeValue))}}class SimpleXMLParser extends XMLParserBase{constructor({hasAttributes:e=!1,lowerCaseName:t=!1}){super();this._currentFragment=null;this._stack=null;this._errorCode=jn;this._hasAttributes=e;this._lowerCaseName=t}parseFromString(e){this._currentFragment=[];this._stack=[];this._errorCode=jn;this.parseXml(e);if(this._errorCode!==jn)return;const[t]=this._currentFragment;return t?{documentEle\ -ment:t}:void 0}onText(e){if(function isWhitespaceString(e){for(let t=0,a=e.length;t\\\\376\\\\377([^<]+)/g,(funct\ -ion(e,t){const a=t.replaceAll(/\\\\([0-3])([0-7])([0-7])/g,(function(e,t,a,r){return String.fromCharCode(64*t+8*a+1*r)})).replaceAll(/&(amp|apos|gt|lt|quot);/g,(function(e,t){switch(t){case"amp":return"&";case"apos":return"\'";case"gt":return">";case"lt":return"<";case"quot":return\'"\'}throw new Error(`_repair: ${t} isn\'t defined.`)})),r=[">"];for(let e=0,t=a.length;e=32&&t<127&&60!==t&&62!==t&&38!==t?r.push(String.fromCharCode(t)):r.push("&#x\ -"+(65536+t).toString(16).substring(1)+";")}return r.join("")}))}_getSequence(e){const t=e.nodeName;return"rdf:bag"!==t&&"rdf:seq"!==t&&"rdf:alt"!==t?null:e.childNodes.filter((e=>"rdf:li"===e.nodeName))}_parseArray(e){if(!e.hasChildNodes())return;const[t]=e.childNodes,a=this._getSequence(t)||[];this._metadataMap.set(e.nodeName,a.map((e=>e.textContent.trim())))}_parse(e){let t=e.documentElement;if("rdf:rdf"!==t.nodeName){t=t.firstChild;for(;t&&"rdf:rdf"!==t.nodeName;)t=t.nextSibling}if(t&&"rdf:rdf\ -"===t.nodeName&&t.hasChildNodes())for(const e of t.childNodes)if("rdf:description"===e.nodeName)for(const t of e.childNodes){const e=t.nodeName;switch(e){case"#text":continue;case"dc:creator":case"dc:subject":this._parseArray(t);continue}this._metadataMap.set(e,t.textContent.trim())}}get serializable(){return{parsedData:this._metadataMap,rawData:this._data}}}const zn=1,$n=2,Gn=3,Vn=4,Kn=5;class StructTreeRoot{constructor(e,t,a){this.xref=e;this.dict=t;this.ref=a instanceof Ref?a:null;this.roleMa\ -p=new Map;this.structParentIds=null}init(){this.readRoleMap()}#Z(e,t,a){if(!(e instanceof Ref)||t<0)return;this.structParentIds||=new RefSetCache;let r=this.structParentIds.get(e);if(!r){r=[];this.structParentIds.put(e,r)}r.push([t,a])}addAnnotationIdToPage(e,t){this.#Z(e,t,Vn)}readRoleMap(){const e=this.dict.get("RoleMap");if(e instanceof Dict)for(const[t,a]of e)a instanceof Name&&this.roleMap.set(t,a.name)}static async canCreateStructureTree({catalogRef:e,pdfManager:t,newAnnotationsByPage:a}){\ -if(!(e instanceof Ref)){warn("Cannot save the struct tree: no catalog reference.");return!1}let r=0,i=!0;for(const[e,n]of a){const{ref:a}=await t.getPage(e);if(!(a instanceof Ref)){warn(`Cannot save the struct tree: page ${e} has no ref.`);i=!0;break}for(const e of n)if(e.accessibilityData?.type){e.parentTreeId=r++;i=!1}}if(i){for(const e of a.values())for(const t of e)delete t.parentTreeId;return!1}return!0}static async createStructureTree({newAnnotationsByPage:e,xref:t,catalogRef:a,pdfManager:\ -r,changes:i}){const n=await r.ensureCatalog("cloneDict"),s=new RefSetCache;s.put(a,n);const o=t.getNewTemporaryRef();n.set("StructTreeRoot",o);const c=new Dict(t);c.set("Type",Name.get("StructTreeRoot"));const l=t.getNewTemporaryRef();c.set("ParentTree",l);const h=[];c.set("K",h);s.put(o,c);const u=new Dict(t),d=[];u.set("Nums",d);const f=await this.#Q({newAnnotationsByPage:e,structTreeRootRef:o,structTreeRoot:null,kids:h,nums:d,xref:t,pdfManager:r,changes:i,cache:s});c.set("ParentTreeNextKey",f\ -);s.put(l,u);for(const[e,t]of s.items())i.put(e,{data:t})}async canUpdateStructTree({pdfManager:e,newAnnotationsByPage:t}){if(!this.ref){warn("Cannot update the struct tree: no root reference.");return!1}let a=this.dict.get("ParentTreeNextKey");if(!Number.isInteger(a)||a<0){warn("Cannot update the struct tree: invalid next key.");return!1}const r=this.dict.get("ParentTree");if(!(r instanceof Dict)){warn("Cannot update the struct tree: ParentTree isn\'t a dict.");return!1}const i=r.get("Nums");if(\ -!Array.isArray(i)){warn("Cannot update the struct tree: nums isn\'t an array.");return!1}const n=new NumberTree(r,this.xref);for(const a of t.keys()){const{pageDict:t}=await e.getPage(a);if(!t.has("StructParents"))continue;const r=t.get("StructParents");if(!Number.isInteger(r)||!Array.isArray(n.get(r))){warn(`Cannot save the struct tree: page ${a} has a wrong id.`);return!1}}let s=!0;for(const[r,i]of t){const{pageDict:t}=await e.getPage(r);StructTreeRoot.#ee({elements:i,xref:this.xref,pageDict:t,\ -numberTree:n});for(const e of i)if(e.accessibilityData?.type){e.accessibilityData.structParent>=0||(e.parentTreeId=a++);s=!1}}if(s){for(const e of t.values())for(const t of e){delete t.parentTreeId;delete t.structTreeParent}return!1}return!0}async updateStructureTree({newAnnotationsByPage:e,pdfManager:t,changes:a}){const{ref:r,xref:i}=this,n=this.dict.clone(),s=new RefSetCache;s.put(r,n);let o,c=n.getRaw("ParentTree");if(c instanceof Ref)o=i.fetch(c);else{o=c;c=i.getNewTemporaryRef();n.set("Pare\ -ntTree",c)}o=o.clone();s.put(c,o);let l=o.getRaw("Nums"),h=null;if(l instanceof Ref){h=l;l=i.fetch(h)}l=l.slice();h||o.set("Nums",l);const u=await StructTreeRoot.#Q({newAnnotationsByPage:e,structTreeRootRef:r,structTreeRoot:this,kids:null,nums:l,xref:i,pdfManager:t,changes:a,cache:s});if(-1!==u){n.set("ParentTreeNextKey",u);h&&s.put(h,l);for(const[e,t]of s.items())a.put(e,{data:t})}}static async#Q({newAnnotationsByPage:e,structTreeRootRef:t,structTreeRoot:a,kids:r,nums:i,xref:n,pdfManager:s,chan\ -ges:o,cache:c}){const l=Name.get("OBJR");let h,u=-1;for(const[d,f]of e){const e=await s.getPage(d),{ref:g}=e,p=g instanceof Ref;for(const{accessibilityData:s,ref:m,parentTreeId:b,structTreeParent:y}of f){if(!s?.type)continue;const{structParent:f}=s;if(a&&Number.isInteger(f)&&f>=0){let t=(h||=new Map).get(d);if(void 0===t){t=new StructTreePage(a,e.pageDict).collectObjects(g);h.set(d,t)}const r=t?.get(f);if(r){const e=n.fetch(r).clone();StructTreeRoot.#te(e,s);o.put(r,{data:e});continue}}u=Math.ma\ -x(u,b);const w=n.getNewTemporaryRef(),x=new Dict(n);StructTreeRoot.#te(x,s);await this.#ae({structTreeParent:y,tagDict:x,newTagRef:w,structTreeRootRef:t,fallbackKids:r,xref:n,cache:c});const S=new Dict(n);x.set("K",S);S.set("Type",l);p&&S.set("Pg",g);S.set("Obj",m);c.put(w,x);i.push(b,w)}}return u+1}static#te(e,{type:t,title:a,lang:r,alt:i,expanded:n,actualText:s}){e.set("S",Name.get(t));a&&e.set("T",stringToAsciiOrUTF16BE(a));r&&e.set("Lang",stringToAsciiOrUTF16BE(r));i&&e.set("Alt",stringToAsc\ -iiOrUTF16BE(i));n&&e.set("E",stringToAsciiOrUTF16BE(n));s&&e.set("ActualText",stringToAsciiOrUTF16BE(s))}static#ee({elements:e,xref:t,pageDict:a,numberTree:r}){const i=new Map;for(const t of e)if(t.structTreeParentId){const e=parseInt(t.structTreeParentId.split("_mc")[1],10);let a=i.get(e);if(!a){a=[];i.set(e,a)}a.push(t)}const n=a.get("StructParents");if(!Number.isInteger(n))return;const s=r.get(n),updateElement=(e,a,r)=>{const n=i.get(e);if(n){const e=a.getRaw("P"),i=t.fetchIfRef(e);if(e insta\ -nceof Ref&&i instanceof Dict){const e={ref:r,dict:a};for(const t of n)t.structTreeParent=e}return!0}return!1};for(const e of s){if(!(e instanceof Ref))continue;const a=t.fetch(e),r=a.get("K");if(Number.isInteger(r))updateElement(r,a,e);else if(Array.isArray(r))for(let i of r){i=t.fetchIfRef(i);if(Number.isInteger(i)&&updateElement(i,a,e))break;if(!(i instanceof Dict))continue;if(!isName(i.get("Type"),"MCR"))break;const r=i.get("MCID");if(Number.isInteger(r)&&updateElement(r,a,e))break}}}static a\ -sync#ae({structTreeParent:e,tagDict:t,newTagRef:a,structTreeRootRef:r,fallbackKids:i,xref:n,cache:s}){let o,c=null;if(e){({ref:c}=e);o=e.dict.getRaw("P")||r}else o=r;t.set("P",o);const l=n.fetchIfRef(o);if(!l){i.push(a);return}let h=s.get(o);if(!h){h=l.clone();s.put(o,h)}const u=h.getRaw("K");let d=u instanceof Ref?s.get(u):null;if(!d){d=n.fetchIfRef(u);d=Array.isArray(d)?d.slice():[u];const e=n.getNewTemporaryRef();h.set("K",e);s.put(e,d)}const f=d.indexOf(c);d.splice(f>=0?f+1:d.length,0,a)}}cl\ -ass StructElementNode{constructor(e,t){this.tree=e;this.xref=e.xref;this.dict=t;this.kids=[];this.parseKids()}get role(){const e=this.dict.get("S"),t=e instanceof Name?e.name:"",{root:a}=this.tree;return a.roleMap.get(t)??t}parseKids(){let e=null;const t=this.dict.getRaw("Pg");t instanceof Ref&&(e=t.toString());const a=this.dict.get("K");if(Array.isArray(a))for(const t of a){const a=this.parseKid(e,this.xref.fetchIfRef(t));a&&this.kids.push(a)}else{const t=this.parseKid(e,a);t&&this.kids.push(t)\ -}}parseKid(e,t){if(Number.isInteger(t))return this.tree.pageDict.objId!==e?null:new StructElement({type:zn,mcid:t,pageObjId:e});if(!(t instanceof Dict))return null;const a=t.getRaw("Pg");a instanceof Ref&&(e=a.toString());const r=t.get("Type")instanceof Name?t.get("Type").name:null;if("MCR"===r){if(this.tree.pageDict.objId!==e)return null;const a=t.getRaw("Stm");return new StructElement({type:$n,refObjId:a instanceof Ref?a.toString():null,pageObjId:e,mcid:t.get("MCID")})}if("OBJR"===r){if(this.t\ -ree.pageDict.objId!==e)return null;const a=t.getRaw("Obj");return new StructElement({type:Gn,refObjId:a instanceof Ref?a.toString():null,pageObjId:e})}return new StructElement({type:Kn,dict:t})}}class StructElement{constructor({type:e,dict:t=null,mcid:a=null,pageObjId:r=null,refObjId:i=null}){this.type=e;this.dict=t;this.mcid=a;this.pageObjId=r;this.refObjId=i;this.parentNode=null}}class StructTreePage{constructor(e,t){this.root=e;this.xref=e?.xref??null;this.rootDict=e?.dict??null;this.pageDict\ -=t;this.nodes=[]}collectObjects(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return null;const t=this.rootDict.get("ParentTree");if(!t)return null;const a=this.root.structParentIds?.get(e);if(!a)return null;const r=new Map,i=new NumberTree(t,this.xref);for(const[e]of a){const t=i.getRaw(e);t instanceof Ref&&r.set(e,t)}return r}parse(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return;const t=this.rootDict.get("ParentTree");if(!t)return;const a=this.pageDict.get("StructParents"),r=t\ -his.root.structParentIds?.get(e);if(!Number.isInteger(a)&&!r)return;const i=new Map,n=new NumberTree(t,this.xref);if(Number.isInteger(a)){const e=n.get(a);if(Array.isArray(e))for(const t of e)t instanceof Ref&&this.addNode(this.xref.fetch(t),i)}if(r)for(const[e,t]of r){const a=n.get(e);if(a){const e=this.addNode(this.xref.fetchIfRef(a),i);1===e?.kids?.length&&e.kids[0].type===Gn&&(e.kids[0].type=t)}}}addNode(e,t,a=0){if(a>40){warn("StructTree MAX_DEPTH reached.");return null}if(!(e instanceof Di\ -ct))return null;if(t.has(e))return t.get(e);const r=new StructElementNode(this,e);t.set(e,r);const i=e.get("P");if(!(i instanceof Dict)||isName(i.get("Type"),"StructTreeRoot")){this.addTopLevelNode(e,r)||t.delete(e);return r}const n=this.addNode(i,t,a+1);if(!n)return r;let s=!1;for(const t of n.kids)if(t.type===Kn&&t.dict===e){t.parentNode=r;s=!0}s||t.delete(e);return r}addTopLevelNode(e,t){const a=this.rootDict.get("K");if(!a)return!1;if(a instanceof Dict){if(a.objId!==e.objId)return!1;this.nod\ -es[0]=t;return!0}if(!Array.isArray(a))return!0;let r=!1;for(let i=0;i40){warn("StructTree too deep to be fully serialized.");return}const r=Object.create(null);r.role=e.role;r.children=[];t.children.push(r);let i=e.dict.get("Alt");"string"!=typeof i&&(i=e.dict.get("ActualText"));"string"==typeof i&&(r.alt=stringToPDFString(i));const n=e.dict.get("A")\ -;if(n instanceof Dict){const e=lookupNormalRect(n.getArray("BBox"),null);if(e)r.bbox=e;else{const e=n.get("Width"),t=n.get("Height");"number"==typeof e&&e>0&&"number"==typeof t&&t>0&&(r.bbox=[0,0,e,t])}}const s=e.dict.get("Lang");"string"==typeof s&&(r.lang=stringToPDFString(s));for(const t of e.kids){const e=t.type===Kn?t.parentNode:null;e?nodeToSerializable(e,r,a+1):t.type===zn||t.type===$n?r.children.push({type:"content",id:`p${t.pageObjId}_mc${t.mcid}`}):t.type===Gn?r.children.push({type:"ob\ -ject",id:t.refObjId}):t.type===Vn&&r.children.push({type:"annotation",id:`pdfjs_internal_id_${t.refObjId}`})}}const e=Object.create(null);e.children=[];e.role="Root";for(const t of this.nodes)t&&nodeToSerializable(t,e);return e}}const Jn=function _isValidExplicitDest(e,t,a){if(!Array.isArray(a)||a.length<2)return!1;const[r,i,...n]=a;if(!e(r)&&!Number.isInteger(r))return!1;if(!t(i))return!1;const s=n.length;let o=!0;switch(i.name){case"XYZ":if(s<2||s>3)return!1;break;case"Fit":case"FitB":return 0\ -===s;case"FitH":case"FitBH":case"FitV":case"FitBV":if(s>1)return!1;break;case"FitR":if(4!==s)return!1;o=!1;break;default:return!1}for(const e of n)if(!("number"==typeof e||o&&null===e))return!1;return!0}.bind(null,(e=>e instanceof Ref),isName);function fetchDest(e){e instanceof Dict&&(e=e.get("D"));return Jn(e)?e:null}function fetchRemoteDest(e){let t=e.get("D");if(t){t instanceof Name&&(t=t.name);if("string"==typeof t)return stringToPDFString(t,!0);if(Jn(t))return JSON.stringify(t)}return null}\ -class Catalog{#re=null;#ie=null;builtInCMapCache=new Map;fontCache=new RefSetCache;globalColorSpaceCache=new GlobalColorSpaceCache;globalImageCache=new GlobalImageCache;nonBlendModesSet=new RefSet;pageDictCache=new RefSetCache;pageIndexCache=new RefSetCache;pageKidsCountCache=new RefSetCache;standardFontDataCache=new Map;systemFontCache=new Map;constructor(e,t){this.pdfManager=e;this.xref=t;this.#ie=t.getCatalogObj();if(!(this.#ie instanceof Dict))throw new FormatError("Catalog object is not a d\ -ictionary.");this.toplevelPagesDict}cloneDict(){return this.#ie.clone()}get version(){const e=this.#ie.get("Version");if(e instanceof Name){if(Ca.test(e.name))return shadow(this,"version",e.name);warn(`Invalid PDF catalog version: ${e.name}`)}return shadow(this,"version",null)}get lang(){const e=this.#ie.get("Lang");return shadow(this,"lang",e&&"string"==typeof e?stringToPDFString(e):null)}get needsRendering(){const e=this.#ie.get("NeedsRendering");return shadow(this,"needsRendering","boolean"==\ -typeof e&&e)}get collection(){let e=null;try{const t=this.#ie.get("Collection");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch Collection entry; assuming no collection is present.")}return shadow(this,"collection",e)}get acroForm(){let e=null;try{const t=this.#ie.get("AcroForm");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch AcroForm entry; assuming no forms are present.")}re\ -turn shadow(this,"acroForm",e)}get acroFormRef(){const e=this.#ie.getRaw("AcroForm");return shadow(this,"acroFormRef",e instanceof Ref?e:null)}get metadata(){const e=this.#ie.getRaw("Metadata");if(!(e instanceof Ref))return shadow(this,"metadata",null);let t=null;try{const a=this.xref.fetch(e,!this.xref.encrypt?.encryptMetadata);if(a instanceof BaseStream&&a.dict instanceof Dict){const e=a.dict.get("Type"),r=a.dict.get("Subtype");if(isName(e,"Metadata")&&isName(r,"XML")){const e=stringToUTF8Stri\ -ng(a.getString());e&&(t=new MetadataParser(e).serializable)}}}catch(e){if(e instanceof MissingDataException)throw e;info(`Skipping invalid Metadata: "${e}".`)}return shadow(this,"metadata",t)}get markInfo(){let e=null;try{e=this.#ne()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read mark info.")}return shadow(this,"markInfo",e)}#ne(){const e=this.#ie.get("MarkInfo");if(!(e instanceof Dict))return null;const t={Marked:!1,UserProperties:!1,Suspects:!1};for(const a in t){c\ -onst r=e.get(a);"boolean"==typeof r&&(t[a]=r)}return t}get structTreeRoot(){let e=null;try{e=this.#se()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable read to structTreeRoot info.")}return shadow(this,"structTreeRoot",e)}#se(){const e=this.#ie.getRaw("StructTreeRoot"),t=this.xref.fetchIfRef(e);if(!(t instanceof Dict))return null;const a=new StructTreeRoot(this.xref,t,e);a.init();return a}get toplevelPagesDict(){const e=this.#ie.get("Pages");if(!(e instanceof Dict))throw new F\ -ormatError("Invalid top-level pages dictionary.");return shadow(this,"toplevelPagesDict",e)}get documentOutline(){let e=null;try{e=this.#oe()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutline",e)}#oe(){let e=this.#ie.get("Outlines");if(!(e instanceof Dict))return null;e=e.getRaw("First");if(!(e instanceof Ref))return null;const t={items:[]},a=[{obj:e,parent:t}],r=new RefSet;r.put(e);const i=this.xref,n=new Uint8Clam\ -pedArray(3);for(;a.length>0;){const t=a.shift(),s=i.fetchIfRef(t.obj);if(null===s)continue;s.has("Title")||warn("Invalid outline item encountered.");const o={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:s,resultObj:o,docBaseUrl:this.baseUrl,docAttachments:this.attachments});const c=s.get("Title"),l=s.get("F")||0,h=s.getArray("C"),u=s.get("Count");let d=n;!isNumberArray(h,3)||0===h[0]&&0===h[1]&&0===h[2]||(d=ColorSpaceUtils.rgb.getRgb(h,0));const f={action:o.action,attach\ -ment:o.attachment,dest:o.dest,url:o.url,unsafeUrl:o.unsafeUrl,newWindow:o.newWindow,setOCGState:o.setOCGState,title:"string"==typeof c?stringToPDFString(c):"",color:d,count:Number.isInteger(u)?u:void 0,bold:!!(2&l),italic:!!(1&l),items:[]};t.parent.items.push(f);e=s.getRaw("First");if(e instanceof Ref&&!r.has(e)){a.push({obj:e,parent:f});r.put(e)}e=s.getRaw("Next");if(e instanceof Ref&&!r.has(e)){a.push({obj:e,parent:t.parent});r.put(e)}}return t.items.length>0?t.items:null}get permissions(){let\ - e=null;try{e=this.#ce()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read permissions.")}return shadow(this,"permissions",e)}#ce(){const e=this.xref.trailer.get("Encrypt");if(!(e instanceof Dict))return null;let t=e.get("P");if("number"!=typeof t)return null;t+=2**32;const a=[];for(const e in w){const r=w[e];t&r&&a.push(r)}return a}get optionalContentConfig(){let e=null;try{const t=this.#ie.get("OCProperties");if(!t)return shadow(this,"optionalContentConfig",null);const\ - a=t.get("D");if(!a)return shadow(this,"optionalContentConfig",null);const r=t.get("OCGs");if(!Array.isArray(r))return shadow(this,"optionalContentConfig",null);const i=new RefSetCache;for(const e of r)e instanceof Ref&&!i.has(e)&&i.put(e,this.#le(e));e=this.#he(a,i)}catch(e){if(e instanceof MissingDataException)throw e;warn(`Unable to read optional content config: ${e}`)}return shadow(this,"optionalContentConfig",e)}#le(e){const t=this.xref.fetch(e),a={id:e.toString(),name:null,intent:null,usag\ -e:{print:null,view:null},rbGroups:[]},r=t.get("Name");"string"==typeof r&&(a.name=stringToPDFString(r));let i=t.getArray("Intent");Array.isArray(i)||(i=[i]);i.every((e=>e instanceof Name))&&(a.intent=i.map((e=>e.name)));const n=t.get("Usage");if(!(n instanceof Dict))return a;const s=a.usage,o=n.get("Print");if(o instanceof Dict){const e=o.get("PrintState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":s.print={printState:e.name}}}const c=n.get("View");if(c instanceof Dict){const e=c.get\ -("ViewState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":s.view={viewState:e.name}}}return a}#he(e,t){function parseOnOff(e){const a=[];if(Array.isArray(e))for(const r of e)r instanceof Ref&&t.has(r)&&a.push(r.toString());return a}function parseOrder(e,a=0){if(!Array.isArray(e))return null;const i=[];for(const n of e){if(n instanceof Ref&&t.has(n)){r.put(n);i.push(n.toString());continue}const e=parseNestedOrder(n,a);e&&i.push(e)}if(a>0)return i;const n=[];for(const[e]of t.items())r.h\ -as(e)||n.push(e.toString());n.length&&i.push({name:null,order:n});return i}function parseNestedOrder(e,t){if(++t>i){warn("parseNestedOrder - reached MAX_NESTED_LEVELS.");return null}const r=a.fetchIfRef(e);if(!Array.isArray(r))return null;const n=a.fetchIfRef(r[0]);if("string"!=typeof n)return null;const s=parseOrder(r.slice(1),t);return s?.length?{name:stringToPDFString(n),order:s}:null}const a=this.xref,r=new RefSet,i=10;!function parseRBGroups(e){if(Array.isArray(e))for(const r of e){const e=\ -a.fetchIfRef(r);if(!Array.isArray(e)||!e.length)continue;const i=new Set;for(const a of e)if(a instanceof Ref&&t.has(a)&&!i.has(a.toString())){i.add(a.toString());t.get(a).rbGroups.push(i)}}}(e.get("RBGroups"));return{name:"string"==typeof e.get("Name")?stringToPDFString(e.get("Name")):null,creator:"string"==typeof e.get("Creator")?stringToPDFString(e.get("Creator")):null,baseState:e.get("BaseState")instanceof Name?e.get("BaseState").name:null,on:parseOnOff(e.get("ON")),off:parseOnOff(e.get("OFF\ -")),order:parseOrder(e.get("Order")),groups:[...t]}}setActualNumPages(e=null){this.#re=e}get hasActualNumPages(){return null!==this.#re}get _pagesCount(){const e=this.toplevelPagesDict.get("Count");if(!Number.isInteger(e))throw new FormatError("Page count in top-level pages dictionary is not an integer.");return shadow(this,"_pagesCount",e)}get numPages(){return this.#re??this._pagesCount}get destinations(){const e=this.#ue(),t=Object.create(null);for(const a of e)if(a instanceof NameTree)for(co\ -nst[e,r]of a.getAll()){const a=fetchDest(r);a&&(t[stringToPDFString(e,!0)]=a)}else if(a instanceof Dict)for(const[e,r]of a){const a=fetchDest(r);a&&(t[stringToPDFString(e,!0)]||=a)}return shadow(this,"destinations",t)}getDestination(e){if(this.hasOwnProperty("destinations"))return this.destinations[e]??null;const t=this.#ue();for(const a of t)if(a instanceof NameTree||a instanceof Dict){const t=fetchDest(a.get(e));if(t)return t}if(t.length){const t=this.destinations[e];if(t)return t}return null}\ -#ue(){const e=this.#ie.get("Names"),t=[];e?.has("Dests")&&t.push(new NameTree(e.getRaw("Dests"),this.xref));this.#ie.has("Dests")&&t.push(this.#ie.get("Dests"));return t}get pageLabels(){let e=null;try{e=this.#de()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read page labels.")}return shadow(this,"pageLabels",e)}#de(){const e=this.#ie.getRaw("PageLabels");if(!e)return null;const t=new Array(this.numPages);let a=null,r="";const i=new NumberTree(e,this.xref).getAll();let \ -n="",s=1;for(let e=0,o=this.numPages;e=1))throw new FormatError("Invalid start in PageLabel dictionary.");s=e}else s=1}switch(a){case"D":n=s;break;case"R":case"r":n=toRomanNumerals(s,"r"===a);break;case"A":case"a":const e=26,t="a"===a?97:65,r=s-1;n=String.fromCharCode(t+r%e).repeat(Math.floor(r/e)+1);break;default:if(a)throw new FormatError(`Invalid style "${a}" in PageLabel dictionary.`);n=""}t[e]=r\ -+n;s++}return t}get pageLayout(){const e=this.#ie.get("PageLayout");let t="";if(e instanceof Name)switch(e.name){case"SinglePage":case"OneColumn":case"TwoColumnLeft":case"TwoColumnRight":case"TwoPageLeft":case"TwoPageRight":t=e.name}return shadow(this,"pageLayout",t)}get pageMode(){const e=this.#ie.get("PageMode");let t="UseNone";if(e instanceof Name)switch(e.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"FullScreen":case"UseOC":case"UseAttachments":t=e.name}return shadow(this,"pageM\ -ode",t)}get viewerPreferences(){const e=this.#ie.get("ViewerPreferences");if(!(e instanceof Dict))return shadow(this,"viewerPreferences",null);let t=null;for(const[a,r]of e){let e;switch(a){case"HideToolbar":case"HideMenubar":case"HideWindowUI":case"FitWindow":case"CenterWindow":case"DisplayDocTitle":case"PickTrayByPDFSize":"boolean"==typeof r&&(e=r);break;case"NonFullScreenPageMode":if(r instanceof Name)switch(r.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"UseOC":e=r.name;break;de\ -fault:e="UseNone"}break;case"Direction":if(r instanceof Name)switch(r.name){case"L2R":case"R2L":e=r.name;break;default:e="L2R"}break;case"ViewArea":case"ViewClip":case"PrintArea":case"PrintClip":if(r instanceof Name)switch(r.name){case"MediaBox":case"CropBox":case"BleedBox":case"TrimBox":case"ArtBox":e=r.name;break;default:e="CropBox"}break;case"PrintScaling":if(r instanceof Name)switch(r.name){case"None":case"AppDefault":e=r.name;break;default:e="AppDefault"}break;case"Duplex":if(r instanceof N\ -ame)switch(r.name){case"Simplex":case"DuplexFlipShortEdge":case"DuplexFlipLongEdge":e=r.name;break;default:e="None"}break;case"PrintPageRange":if(Array.isArray(r)&&r.length%2==0){r.every(((e,t,a)=>Number.isInteger(e)&&e>0&&(0===t||e>=a[t-1])&&e<=this.numPages))&&(e=r)}break;case"NumCopies":Number.isInteger(r)&&r>0&&(e=r);break;default:warn(`Ignoring non-standard key in ViewerPreferences: ${a}.`);continue}if(void 0!==e){t??=Object.create(null);t[a]=e}else warn(`Bad value, for key "${a}", in Viewe\ -rPreferences: ${r}.`)}return shadow(this,"viewerPreferences",t)}get openAction(){const e=this.#ie.get("OpenAction"),t=Object.create(null);if(e instanceof Dict){const a=new Dict(this.xref);a.set("A",e);const r={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:a,resultObj:r});Array.isArray(r.dest)?t.dest=r.dest:r.action&&(t.action=r.action)}else Jn(e)&&(t.dest=e);return shadow(this,"openAction",objectSize(t)>0?t:null)}get attachments(){const e=this.#ie.get("Names");let t=null;\ -if(e instanceof Dict&&e.has("EmbeddedFiles")){const a=new NameTree(e.getRaw("EmbeddedFiles"),this.xref);for(const[e,r]of a.getAll()){const a=new FileSpec(r,this.xref);t??=Object.create(null);t[stringToPDFString(e,!0)]=a.serializable}}return shadow(this,"attachments",t)}get xfaImages(){const e=this.#ie.get("Names");let t=null;if(e instanceof Dict&&e.has("XFAImages")){const a=new NameTree(e.getRaw("XFAImages"),this.xref);for(const[e,r]of a.getAll())if(r instanceof BaseStream){t??=new Map;t.set(str\ -ingToPDFString(e,!0),r.getBytes())}}return shadow(this,"xfaImages",t)}#fe(){const e=this.#ie.get("Names");let t=null;function appendIfJavaScriptDict(e,a){if(!(a instanceof Dict))return;if(!isName(a.get("S"),"JavaScript"))return;let r=a.get("JS");if(r instanceof BaseStream)r=r.getString();else if("string"!=typeof r)return;r=stringToPDFString(r,!0).replaceAll("\\0","");r&&(t||=new Map).set(e,r)}if(e instanceof Dict&&e.has("JavaScript")){const t=new NameTree(e.getRaw("JavaScript"),this.xref);for(con\ -st[e,a]of t.getAll())appendIfJavaScriptDict(stringToPDFString(e,!0),a)}const a=this.#ie.get("OpenAction");a&&appendIfJavaScriptDict("OpenAction",a);return t}get jsActions(){const e=this.#fe();let t=collectActions(this.xref,this.#ie,we);if(e){t||=Object.create(null);for(const[a,r]of e)a in t?t[a].push(r):t[a]=[r]}return shadow(this,"jsActions",t)}async cleanup(e=!1){clearGlobalCaches();this.globalColorSpaceCache.clear();this.globalImageCache.clear(e);this.pageKidsCountCache.clear();this.pageIndex\ -Cache.clear();this.pageDictCache.clear();this.nonBlendModesSet.clear();for(const{dict:e}of await Promise.all(this.fontCache))delete e.cacheKey;this.fontCache.clear();this.builtInCMapCache.clear();this.standardFontDataCache.clear();this.systemFontCache.clear()}async getPageDict(e){const t=[this.toplevelPagesDict],a=new RefSet,r=this.#ie.getRaw("Pages");r instanceof Ref&&a.put(r);const i=this.xref,n=this.pageKidsCountCache,s=this.pageIndexCache,o=this.pageDictCache;let c=0;for(;t.length;){const r=\ -t.pop();if(r instanceof Ref){const l=n.get(r);if(l>=0&&c+l<=e){c+=l;continue}if(a.has(r))throw new FormatError("Pages tree contains circular reference.");a.put(r);const h=await(o.get(r)||i.fetchAsync(r));if(h instanceof Dict){let t=h.getRaw("Type");t instanceof Ref&&(t=await i.fetchAsync(t));if(isName(t,"Page")||!h.has("Kids")){n.has(r)||n.put(r,1);s.has(r)||s.put(r,c);if(c===e)return[h,r];c++;continue}}t.push(h);continue}if(!(r instanceof Dict))throw new FormatError("Page dictionary kid referen\ -ce points to wrong type of object.");const{objId:l}=r;let h=r.getRaw("Count");h instanceof Ref&&(h=await i.fetchAsync(h));if(Number.isInteger(h)&&h>=0){l&&!n.has(l)&&n.put(l,h);if(c+h<=e){c+=h;continue}}let u=r.getRaw("Kids");u instanceof Ref&&(u=await i.fetchAsync(u));if(!Array.isArray(u)){let t=r.getRaw("Type");t instanceof Ref&&(t=await i.fetchAsync(t));if(isName(t,"Page")||!r.has("Kids")){if(c===e)return[r,null];c++;continue}throw new FormatError("Page dictionary kids object is not an array.\ -")}for(let e=u.length-1;e>=0;e--){const a=u[e];t.push(a);r===this.toplevelPagesDict&&a instanceof Ref&&!o.has(a)&&o.put(a,i.fetchAsync(a))}}throw new Error(`Page index ${e} not found.`)}async getAllPageDicts(e=!1){const{ignoreErrors:t}=this.pdfManager.evaluatorOptions,a=[{currentNode:this.toplevelPagesDict,posInKids:0}],r=new RefSet,i=this.#ie.getRaw("Pages");i instanceof Ref&&r.put(i);const n=new Map,s=this.xref,o=this.pageIndexCache;let c=0;function addPageDict(e,t){t&&!o.has(t)&&o.put(t,c);n.\ -set(c++,[e,t])}function addPageError(a){if(a instanceof XRefEntryException&&!e)throw a;if(e&&t&&0===c){warn(`getAllPageDicts - Skipping invalid first page: "${a}".`);a=Dict.empty}n.set(c++,[a,null])}for(;a.length>0;){const e=a.at(-1),{currentNode:t,posInKids:i}=e;let n=t.getRaw("Kids");if(n instanceof Ref)try{n=await s.fetchAsync(n)}catch(e){addPageError(e);break}if(!Array.isArray(n)){addPageError(new FormatError("Page dictionary kids object is not an array."));break}if(i>=n.length){a.pop();cont\ -inue}const o=n[i];let c;if(o instanceof Ref){if(r.has(o)){addPageError(new FormatError("Pages tree contains circular reference."));break}r.put(o);try{c=await s.fetchAsync(o)}catch(e){addPageError(e);break}}else c=o;if(!(c instanceof Dict)){addPageError(new FormatError("Page dictionary kid reference points to wrong type of object."));break}let l=c.getRaw("Type");if(l instanceof Ref)try{l=await s.fetchAsync(l)}catch(e){addPageError(e);break}isName(l,"Page")||!c.has("Kids")?addPageDict(c,o instance\ -of Ref?o:null):a.push({currentNode:c,posInKids:0});e.posInKids++}return n}getPageIndex(e){const t=this.pageIndexCache.get(e);if(void 0!==t)return Promise.resolve(t);const a=this.xref;let r=0;const next=t=>function pagesBeforeRef(t){let r,i=0;return a.fetchAsync(t).then((function(a){if(isRefsEqual(t,e)&&!isDict(a,"Page")&&!(a instanceof Dict&&!a.has("Type")&&a.has("Contents")))throw new FormatError("The reference does not point to a /Page dictionary.");if(!a)return null;if(!(a instanceof Dict))th\ -row new FormatError("Node must be a dictionary.");r=a.getRaw("Parent");return a.getAsync("Parent")})).then((function(e){if(!e)return null;if(!(e instanceof Dict))throw new FormatError("Parent must be a dictionary.");return e.getAsync("Kids")})).then((function(e){if(!e)return null;const n=[];let s=!1;for(const r of e){if(!(r instanceof Ref))throw new FormatError("Kid must be a reference.");if(isRefsEqual(r,t)){s=!0;break}n.push(a.fetchAsync(r).then((function(e){if(!(e instanceof Dict))throw new F\ -ormatError("Kid node must be a dictionary.");e.has("Count")?i+=e.get("Count"):i++})))}if(!s)throw new FormatError("Kid reference not found in parent\'s kids.");return Promise.all(n).then((()=>[i,r]))}))}(t).then((t=>{if(!t){this.pageIndexCache.put(e,r);return r}const[a,i]=t;r+=a;return next(i)}));return next(e)}get baseUrl(){const e=this.#ie.get("URI");if(e instanceof Dict){const t=e.get("Base");if("string"==typeof t){const e=createValidAbsoluteUrl(t,null,{tryConvertEncoding:!0});if(e)return shad\ -ow(this,"baseUrl",e.href)}}return shadow(this,"baseUrl",this.pdfManager.docBaseUrl)}static parseDestDictionary({destDict:e,resultObj:t,docBaseUrl:a=null,docAttachments:r=null}){if(!(e instanceof Dict)){warn("parseDestDictionary: `destDict` must be a dictionary.");return}let i,n,s=e.get("A");if(!(s instanceof Dict))if(e.has("Dest"))s=e.get("Dest");else{s=e.get("AA");s instanceof Dict&&(s.has("D")?s=s.get("D"):s.has("U")&&(s=s.get("U")))}if(s instanceof Dict){const e=s.get("S");if(!(e instanceof N\ -ame)){warn("parseDestDictionary: Invalid type in Action dictionary.");return}const a=e.name;switch(a){case"ResetForm":const e=s.get("Flags"),o=!(1&("number"==typeof e?e:0)),c=[],l=[];for(const e of s.get("Fields")||[])e instanceof Ref?l.push(e.toString()):"string"==typeof e&&c.push(stringToPDFString(e));t.resetForm={fields:c,refs:l,include:o};break;case"URI":i=s.get("URI");i instanceof Name&&(i="/"+i.name);break;case"GoTo":n=s.get("D");break;case"Launch":case"GoToR":const h=s.get("F");if(h insta\ -nceof Dict){const e=new FileSpec(h,null,!0),{rawFilename:t}=e.serializable;i=t}else"string"==typeof h&&(i=h);const u=fetchRemoteDest(s);u&&"string"==typeof i&&(i=i.split("#",1)[0]+"#"+u);const d=s.get("NewWindow");"boolean"==typeof d&&(t.newWindow=d);break;case"GoToE":const f=s.get("T");let g;if(r&&f instanceof Dict){const e=f.get("R"),t=f.get("N");isName(e,"C")&&"string"==typeof t&&(g=r[stringToPDFString(t,!0)])}if(g){t.attachment=g;const e=fetchRemoteDest(s);e&&(t.attachmentDest=e)}else warn(\'\ -parseDestDictionary - unimplemented "GoToE" action.\');break;case"Named":const p=s.get("N");p instanceof Name&&(t.action=p.name);break;case"SetOCGState":const m=s.get("State"),b=s.get("PreserveRB");if(!Array.isArray(m)||0===m.length)break;const y=[];for(const e of m)if(e instanceof Name)switch(e.name){case"ON":case"OFF":case"Toggle":y.push(e.name)}else e instanceof Ref&&y.push(e.toString());if(y.length!==m.length)break;t.setOCGState={state:y,preserveRB:"boolean"!=typeof b||b};break;case"JavaScrip\ -t":const w=s.get("JS");let x;w instanceof BaseStream?x=w.getString():"string"==typeof w&&(x=w);const S=x&&recoverJsURL(stringToPDFString(x,!0));if(S){i=S.url;t.newWindow=S.newWindow;break}default:if("JavaScript"===a||"SubmitForm"===a)break;warn(`parseDestDictionary - unsupported action: "${a}".`)}}else e.has("Dest")&&(n=e.get("Dest"));if("string"==typeof i){const e=createValidAbsoluteUrl(i,a,{addDefaultProtocol:!0,tryConvertEncoding:!0});e&&(t.url=e.href);t.unsafeUrl=i}if(n){n instanceof Name&&(\ -n=n.name);"string"==typeof n?t.dest=stringToPDFString(n,!0):Jn(n)&&(t.dest=n)}}}function addChildren(e,t){if(e instanceof Dict)e=e.getRawValues();else if(e instanceof BaseStream)e=e.dict.getRawValues();else if(!Array.isArray(e))return;for(const r of e)((a=r)instanceof Ref||a instanceof Dict||a instanceof BaseStream||Array.isArray(a))&&t.push(r);var a}class ObjectLoader{refSet=new RefSet;constructor(e,t,a){this.dict=e;this.keys=t;this.xref=a}async load(){const{keys:e,dict:t}=this,a=[];for(const r\ - of e){const e=t.getRaw(r);void 0!==e&&a.push(e)}await this.#ge(a);this.refSet=null}async#ge(e){const t=[],a=[];for(;e.length;){let r=e.pop();if(r instanceof Ref){if(this.refSet.has(r))continue;try{this.refSet.put(r);r=this.xref.fetch(r)}catch(e){if(!(e instanceof MissingDataException)){warn(`ObjectLoader.#walk - requesting all data: "${e}".`);await this.xref.stream.manager.requestAllChunks();return}t.push(r);a.push({begin:e.begin,end:e.end})}}if(r instanceof BaseStream){const e=r.getBaseStreams\ -();if(e){let i=!1;for(const t of e)if(!t.isDataLoaded){i=!0;a.push({begin:t.start,end:t.end})}i&&t.push(r)}}addChildren(r,e)}if(a.length){await this.xref.stream.manager.requestRanges(a);for(const e of t)e instanceof Ref&&this.refSet.remove(e);await this.#ge(t)}}static async load(e,t,a){if(a.stream.isDataLoaded)return;const r=new ObjectLoader(e,t,a);await r.load()}}const Yn=Symbol(),Zn=Symbol(),Qn=Symbol(),es=Symbol(),ts=Symbol(),as=Symbol(),rs=Symbol(),is=Symbol(),ns=Symbol(),ss=Symbol("content"\ -),os=Symbol("data"),cs=Symbol(),ls=Symbol("extra"),hs=Symbol(),us=Symbol(),ds=Symbol(),fs=Symbol(),gs=Symbol(),ps=Symbol(),ms=Symbol(),bs=Symbol(),ys=Symbol(),ws=Symbol(),xs=Symbol(),Ss=Symbol(),As=Symbol(),ks=Symbol(),Cs=Symbol(),vs=Symbol(),Fs=Symbol(),Is=Symbol(),Ts=Symbol(),Os=Symbol(),Ms=Symbol(),Ds=Symbol(),Bs=Symbol(),Rs=Symbol(),Ns=Symbol(),Es=Symbol(),Ls=Symbol(),js=Symbol(),_s=Symbol(),Us=Symbol(),Xs=Symbol(),qs=Symbol(),Hs=Symbol("namespaceId"),Ws=Symbol("nodeName"),zs=Symbol(),$s=Sym\ -bol(),Gs=Symbol(),Vs=Symbol(),Ks=Symbol(),Js=Symbol(),Ys=Symbol(),Zs=Symbol(),Qs=Symbol("root"),eo=Symbol(),to=Symbol(),ao=Symbol(),ro=Symbol(),io=Symbol(),no=Symbol(),so=Symbol(),oo=Symbol(),co=Symbol(),lo=Symbol(),ho=Symbol(),uo=Symbol("uid"),fo=Symbol(),go={config:{id:0,check:e=>e.startsWith("http://www.xfa.org/schema/xci/")},connectionSet:{id:1,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-connection-set/")},datasets:{id:2,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-data/")},\ -form:{id:3,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-form/")},localeSet:{id:4,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-locale-set/")},pdf:{id:5,check:e=>"http://ns.adobe.com/xdp/pdf/"===e},signature:{id:6,check:e=>"http://www.w3.org/2000/09/xmldsig#"===e},sourceSet:{id:7,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-source-set/")},stylesheet:{id:8,check:e=>"http://www.w3.org/1999/XSL/Transform"===e},template:{id:9,check:e=>e.startsWith("http://www.xfa.org/schema/xfa\ --template/")},xdc:{id:10,check:e=>e.startsWith("http://www.xfa.org/schema/xdc/")},xdp:{id:11,check:e=>"http://ns.adobe.com/xdp/"===e},xfdf:{id:12,check:e=>"http://ns.adobe.com/xfdf/"===e},xhtml:{id:13,check:e=>"http://www.w3.org/1999/xhtml"===e},xmpmeta:{id:14,check:e=>"http://ns.adobe.com/xmpmeta/"===e}},po={pt:e=>e,cm:e=>e/2.54*72,mm:e=>e/25.4*72,in:e=>72*e,px:e=>e},mo=/([+-]?\\d+\\.?\\d*)(.*)/;function stripQuotes(e){return e.startsWith("\'")||e.startsWith(\'"\')?e.slice(1,-1):e}function getInteger\ -({data:e,defaultValue:t,validate:a}){if(!e)return t;e=e.trim();const r=parseInt(e,10);return!isNaN(r)&&a(r)?r:t}function getFloat({data:e,defaultValue:t,validate:a}){if(!e)return t;e=e.trim();const r=parseFloat(e);return!isNaN(r)&&a(r)?r:t}function getKeyword({data:e,defaultValue:t,validate:a}){return e&&a(e=e.trim())?e:t}function getStringOption(e,t){return getKeyword({data:e,defaultValue:t[0],validate:e=>t.includes(e)})}function getMeasurement(e,t="0"){t||="0";if(!e)return getMeasurement(t);co\ -nst a=e.trim().match(mo);if(!a)return getMeasurement(t);const[,r,i]=a,n=parseFloat(r);if(isNaN(n))return getMeasurement(t);if(0===n)return 0;const s=po[i];return s?s(n):n}function getRatio(e){if(!e)return{num:1,den:1};const t=e.split(":",2).map((e=>parseFloat(e.trim()))).filter((e=>!isNaN(e)));1===t.length&&t.push(1);if(0===t.length)return{num:1,den:1};const[a,r]=t;return{num:a,den:r}}function getRelevant(e){return e?e.trim().split(/\\s+/).map((e=>({excluded:"-"===e[0],viewname:e.substring(1)})))\ -:[]}class HTMLResult{static get FAILURE(){return shadow(this,"FAILURE",new HTMLResult(!1,null,null,null))}static get EMPTY(){return shadow(this,"EMPTY",new HTMLResult(!0,null,null,null))}constructor(e,t,a,r){this.success=e;this.html=t;this.bbox=a;this.breakNode=r}isBreak(){return!!this.breakNode}static breakNode(e){return new HTMLResult(!1,null,null,e)}static success(e,t=null){return new HTMLResult(!0,e,t,null)}}class FontFinder{constructor(e){this.fonts=new Map;this.cache=new Map;this.warned=ne\ -w Set;this.defaultFont=null;this.add(e)}add(e,t=null){for(const t of e)this.addPdfFont(t);for(const e of this.fonts.values())e.regular||(e.regular=e.italic||e.bold||e.bolditalic);if(!t||0===t.size)return;const a=this.fonts.get("PdfJS-Fallback-PdfJS-XFA");for(const e of t)this.fonts.set(e,a)}addPdfFont(e){const t=e.cssFontInfo,a=t.fontFamily;let r=this.fonts.get(a);if(!r){r=Object.create(null);this.fonts.set(a,r);this.defaultFont||(this.defaultFont=r)}let i="";const n=parseFloat(t.fontWeight);0!=\ -=parseFloat(t.italicAngle)?i=n>=700?"bolditalic":"italic":n>=700&&(i="bold");if(!i){(e.name.includes("Bold")||e.psName?.includes("Bold"))&&(i="bold");(e.name.includes("Italic")||e.name.endsWith("It")||e.psName?.includes("Italic")||e.psName?.endsWith("It"))&&(i+="italic")}i||(i="regular");r[i]=e}getDefault(){return this.defaultFont}find(e,t=!0){let a=this.fonts.get(e)||this.cache.get(e);if(a)return a;const r=/,|-|_| |bolditalic|bold|italic|regular|it/gi;let i=e.replaceAll(r,"");a=this.fonts.get(i\ -);if(a){this.cache.set(e,a);return a}i=i.toLowerCase();const n=[];for(const[e,t]of this.fonts.entries())e.replaceAll(r,"").toLowerCase().startsWith(i)&&n.push(t);if(0===n.length)for(const[,e]of this.fonts.entries())e.regular.name?.replaceAll(r,"").toLowerCase().startsWith(i)&&n.push(e);if(0===n.length){i=i.replaceAll(/psmt|mt/gi,"");for(const[e,t]of this.fonts.entries())e.replaceAll(r,"").toLowerCase().startsWith(i)&&n.push(t)}if(0===n.length)for(const e of this.fonts.values())e.regular.name?.re\ -placeAll(r,"").toLowerCase().startsWith(i)&&n.push(e);if(n.length>=1){1!==n.length&&t&&warn(`XFA - Too many choices to guess the correct font: ${e}`);this.cache.set(e,n[0]);return n[0]}if(t&&!this.warned.has(e)){this.warned.add(e);warn(`XFA - Cannot find the font: ${e}`)}return null}}function selectFont(e,t){return"italic"===e.posture?"bold"===e.weight?t.bolditalic:t.italic:"bold"===e.weight?t.bold:t.regular}class FontInfo{constructor(e,t,a,r){this.lineHeight=a;this.paraMargin=t||{top:0,bottom:0\ -,left:0,right:0};if(!e){[this.pdfFont,this.xfaFont]=this.defaultFont(r);return}this.xfaFont={typeface:e.typeface,posture:e.posture,weight:e.weight,size:e.size,letterSpacing:e.letterSpacing};const i=r.find(e.typeface);if(i){this.pdfFont=selectFont(e,i);this.pdfFont||([this.pdfFont,this.xfaFont]=this.defaultFont(r))}else[this.pdfFont,this.xfaFont]=this.defaultFont(r)}defaultFont(e){const t=e.find("Helvetica",!1)||e.find("Myriad Pro",!1)||e.find("Arial",!1)||e.getDefault();if(t?.regular){const e=t.\ -regular;return[e,{typeface:e.cssFontInfo.fontFamily,posture:"normal",weight:"normal",size:10,letterSpacing:0}]}return[null,{typeface:"Courier",posture:"normal",weight:"normal",size:10,letterSpacing:0}]}}class FontSelector{constructor(e,t,a,r){this.fontFinder=r;this.stack=[new FontInfo(e,t,a,r)]}pushData(e,t,a){const r=this.stack.at(-1);for(const t of["typeface","posture","weight","size","letterSpacing"])e[t]||(e[t]=r.xfaFont[t]);for(const e of["top","bottom","left","right"])isNaN(t[e])&&(t[e]=r.\ -paraMargin[e]);const i=new FontInfo(e,t,a||r.lineHeight,this.fontFinder);i.pdfFont||(i.pdfFont=r.pdfFont);this.stack.push(i)}popFont(){this.stack.pop()}topFont(){return this.stack.at(-1)}}class TextMeasure{constructor(e,t,a,r){this.glyphs=[];this.fontSelector=new FontSelector(e,t,a,r);this.extraHeight=0}pushData(e,t,a){this.fontSelector.pushData(e,t,a)}popFont(e){return this.fontSelector.popFont()}addPara(){const e=this.fontSelector.topFont();this.extraHeight+=e.paraMargin.top+e.paraMargin.botto\ -m}addString(e){if(!e)return;const t=this.fontSelector.topFont(),a=t.xfaFont.size;if(t.pdfFont){const r=t.xfaFont.letterSpacing,i=t.pdfFont,n=i.lineHeight||1.2,s=t.lineHeight||Math.max(1.2,n)*a,o=n-(void 0===i.lineGap?.2:i.lineGap),c=Math.max(1,o)*a,l=a/1e3,h=i.defaultWidth||i.charsToGlyphs(" ")[0].width;for(const t of e.split(/[\\u2029\\n]/)){const e=i.encodeString(t).join(""),a=i.charsToGlyphs(e);for(const e of a){const t=e.width||h;this.glyphs.push([t*l+r,s,c,e.unicode,!1])}this.glyphs.push([0,0\ -,0,"\\n",!0])}this.glyphs.pop()}else{for(const t of e.split(/[\\u2029\\n]/)){for(const e of t.split(""))this.glyphs.push([a,1.2*a,a,e,!1]);this.glyphs.push([0,0,0,"\\n",!0])}this.glyphs.pop()}}compute(e){let t=-1,a=0,r=0,i=0,n=0,s=0,o=!1,c=!0;for(let l=0,h=this.glyphs.length;le){r=Math.max(r,n);n=0;i+=s;s=m;t=-1;a=0;o=!0;c=!1}else{s=Math.max(m,s);a=n;n+=h;t=l}else if(n+h>e){i+=\ -s;s=m;if(-1!==t){l=t;r=Math.max(r,a);n=0;t=-1;a=0}else{r=Math.max(r,n);n=h}o=!0;c=!1}else{n+=h;s=Math.max(m,s)}}r=Math.max(r,n);i+=s+this.extraHeight;return{width:1.02*r,height:i,isBroken:o}}}const bo=/^[^.[]+/,yo=/^[^\\]]+/,wo=0,xo=1,So=2,Ao=3,ko=4,Co=new Map([["$data",(e,t)=>e.datasets?e.datasets.data:e],["$record",(e,t)=>(e.datasets?e.datasets.data:e)[Ss]()[0]],["$template",(e,t)=>e.template],["$connectionSet",(e,t)=>e.connectionSet],["$form",(e,t)=>e.form],["$layout",(e,t)=>e.layout],["$host"\ -,(e,t)=>e.host],["$dataWindow",(e,t)=>e.dataWindow],["$event",(e,t)=>e.event],["!",(e,t)=>e.datasets],["$xfa",(e,t)=>e],["xfa",(e,t)=>e],["$",(e,t)=>t]]),vo=new WeakMap;function parseExpression(e,t,a=!0){let r=e.match(bo);if(!r)return null;let[i]=r;const n=[{name:i,cacheName:"."+i,index:0,js:null,formCalc:null,operator:wo}];let s=i.length;for(;s0&&h.push(e)}if(0!==h.length||o||0!==c)e=isFinite(l)?h.filter((e=>le[l])):h.flat();else{const a=t[vs]();if(!(t=a))return null;c=-1;e=[t]}}return 0===e.length?null:e}function createDataNode(e,t,a){const r=parseExpression(a);if(!r)return null;if(r.some((e=>e.operator===xo)))return null;const i=Co.get(r[0].name);let n=0;if(i){e=i(e,t);n=1}else e=t||e;for(let t=r.length;ne[so]())).join("")}get[Oo](){const e=Object.getPrototypeOf(this);if(!e._attributes){const t=e._attributes=new Set;for(const e of Object.getOwnPropertyNames(this)){if(null===this[e]||this[e]instanceof XFAObject||this[e]instanceof X\ -FAObjectArray)break;t.add(e)}}return shadow(this,Oo,e._attributes)}[Es](e){let t=this;for(;t;){if(t===e)return!0;t=t[vs]()}return!1}[vs](){return this[Uo]}[Cs](){return this[vs]()}[Ss](e=null){return e?this[e]:this[Mo]}[cs](){const e=Object.create(null);this[ss]&&(e.$content=this[ss]);for(const t of Object.getOwnPropertyNames(this)){const a=this[t];null!==a&&(a instanceof XFAObject?e[t]=a[cs]():a instanceof XFAObjectArray?a.isEmpty()||(e[t]=a.dump()):e[t]=a)}return e}[ho](){return null}[co](){re\ -turn HTMLResult.EMPTY}*[As](){for(const e of this[Ss]())yield e}*[No](e,t){for(const a of this[As]())if(!e||t===e.has(a[Ws])){const e=this[gs](),t=a[co](e);t.success||(this[ls].failingNode=a);yield t}}[us](){return null}[Zn](e,t){this[ls].children.push(e)}[gs](){}[es]({filter:e=null,include:t=!0}){if(this[ls].generator){const e=this[gs](),t=this[ls].failingNode[co](e);if(!t.success)return t;t.html&&this[Zn](t.html,t.bbox);delete this[ls].failingNode}else this[ls].generator=this[No](e,t);for(;;){\ -const e=this[ls].generator.next();if(e.done)break;const t=e.value;if(!t.success)return t;t.html&&this[Zn](t.html,t.bbox)}this[ls].generator=null;return HTMLResult.EMPTY}[ro](e){this[qo]=new Set(Object.keys(e))}[Po](e){const t=this[Oo],a=this[qo];return[...e].filter((e=>t.has(e)&&!a.has(e)))}[eo](e,t=new Set){for(const a of this[Mo])a[Xo](e,t)}[Xo](e,t){const a=this[Eo](e,t);a?this[Fo](a,e,t):this[eo](e,t)}[Eo](e,t){const{use:a,usehref:r}=this;if(!a&&!r)return null;let i=null,n=null,s=null,o=a;if\ -(r){o=r;r.startsWith("#som(")&&r.endsWith(")")?n=r.slice(5,-1):r.startsWith(".#som(")&&r.endsWith(")")?n=r.slice(6,-1):r.startsWith("#")?s=r.slice(1):r.startsWith(".#")&&(s=r.slice(2))}else a.startsWith("#")?s=a.slice(1):n=a;this.use=this.usehref="";if(s)i=e.get(s);else{i=searchNode(e.get(Qs),this,n,!0,!1);i&&(i=i[0])}if(!i){warn(`XFA - Invalid prototype reference: ${o}.`);return null}if(i[Ws]!==this[Ws]){warn(`XFA - Incompatible prototype: ${i[Ws]} !== ${this[Ws]}.`);return null}if(t.has(i)){wa\ -rn("XFA - Cycle detected in prototypes use.");return null}t.add(i);const c=i[Eo](e,t);c&&i[Fo](c,e,t);i[eo](e,t);t.delete(i);return i}[Fo](e,t,a){if(a.has(e)){warn("XFA - Cycle detected in prototypes use.");return}!this[ss]&&e[ss]&&(this[ss]=e[ss]);new Set(a).add(e);for(const t of this[Po](e[qo])){this[t]=e[t];this[qo]&&this[qo].add(t)}for(const r of Object.getOwnPropertyNames(this)){if(this[Oo].has(r))continue;const i=this[r],n=e[r];if(i instanceof XFAObjectArray){for(const e of i[Mo])e[Xo](t,a\ -);for(let r=i[Mo].length,s=n[Mo].length;rXFAObject[Do](e))):"object"==typeof e&&null!==e?Object.assign({},e):e}[is](){const e=Object.create(Object.getPrototypeOf(this));for(const t of Object.getOwnPropertySymbols(this))try{e[t]=th\ -is[t]}catch{shadow(e,t,this[t])}e[uo]=`${e[Ws]}${Wo++}`;e[Mo]=[];for(const t of Object.getOwnPropertyNames(this)){if(this[Oo].has(t)){e[t]=XFAObject[Do](this[t]);continue}const a=this[t];e[t]=a instanceof XFAObjectArray?new XFAObjectArray(a[jo]):null}for(const t of this[Mo]){const a=t[Ws],r=t[is]();e[Mo].push(r);r[Uo]=e;null===e[a]?e[a]=r:e[a][Mo].push(r)}return e}[Ss](e=null){return e?this[Mo].filter((t=>t[Ws]===e)):this[Mo]}[ps](e){return this[e]}[ms](e,t,a=!0){return Array.from(this[bs](e,t,a\ -))}*[bs](e,t,a=!0){if("parent"!==e){for(const a of this[Mo]){a[Ws]===e&&(yield a);a.name===e&&(yield a);(t||a[Us]())&&(yield*a[bs](e,t,!1))}a&&this[Oo].has(e)&&(yield new XFAAttribute(this,e,this[e]))}else yield this[Uo]}}class XFAObjectArray{constructor(e=1/0){this[jo]=e;this[Mo]=[]}get isXFAObject(){return!1}get isXFAObjectArray(){return!0}push(e){if(this[Mo].length<=this[jo]){this[Mo].push(e);return!0}warn(`XFA - node "${e[Ws]}" accepts no more than ${this[jo]} children`);return!1}isEmpty(){r\ -eturn 0===this[Mo].length}dump(){return 1===this[Mo].length?this[Mo][0][cs]():this[Mo].map((e=>e[cs]()))}[is](){const e=new XFAObjectArray(this[jo]);e[Mo]=this[Mo].map((e=>e[is]()));return e}get children(){return this[Mo]}clear(){this[Mo].length=0}}class XFAAttribute{constructor(e,t,a){this[Uo]=e;this[Ws]=t;this[ss]=a;this[ns]=!1;this[uo]="attribute"+Wo++}[vs](){return this[Uo]}[Ns](){return!0}[ys](){return this[ss].trim()}[io](e){e=e.value||"";this[ss]=e.toString()}[so](){return this[ss]}[Es](e\ -){return this[Uo]===e||this[Uo][Es](e)}}class XmlObject extends XFAObject{constructor(e,t,a={}){super(e,t);this[ss]="";this[Bo]=null;if("#text"!==t){const e=new Map;this[Io]=e;for(const[t,r]of Object.entries(a))e.set(t,new XFAAttribute(this,t,r));if(a.hasOwnProperty(zs)){const e=a[zs].xfa.dataNode;void 0!==e&&("dataGroup"===e?this[Bo]=!1:"dataValue"===e&&(this[Bo]=!0))}}this[ns]=!1}[lo](e){const t=this[Ws];if("#text"===t){e.push(encodeToXmlString(this[ss]));return}const a=utf8StringToString(t),r\ -=this[Hs]===zo?"xfa:":"";e.push(`<${r}${a}`);for(const[t,a]of this[Io].entries()){const r=utf8StringToString(t);e.push(` ${r}="${encodeToXmlString(a[ss])}"`)}null!==this[Bo]&&(this[Bo]?e.push(\' xfa:dataNode="dataValue"\'):e.push(\' xfa:dataNode="dataGroup"\'));if(this[ss]||0!==this[Mo].length){e.push(">");if(this[ss])"string"==typeof this[ss]?e.push(encodeToXmlString(this[ss])):this[ss][lo](e);else for(const t of this[Mo])t[lo](e);e.push(``)}else e.push("/>")}[$s](e){if(this[ss]){const e\ -=new XmlObject(this[Hs],"#text");this[Qn](e);e[ss]=this[ss];this[ss]=""}this[Qn](e);return!0}[Vs](e){this[ss]+=e}[hs](){if(this[ss]&&this[Mo].length>0){const e=new XmlObject(this[Hs],"#text");this[Qn](e);e[ss]=this[ss];delete this[ss]}}[co](){return"#text"===this[Ws]?HTMLResult.success({name:"#text",value:this[ss]}):HTMLResult.EMPTY}[Ss](e=null){return e?this[Mo].filter((t=>t[Ws]===e)):this[Mo]}[fs](){return this[Io]}[ps](e){const t=this[Io].get(e);return void 0!==t?t:this[Ss](e)}*[bs](e,t){cons\ -t a=this[Io].get(e);a&&(yield a);for(const a of this[Mo]){a[Ws]===e&&(yield a);t&&(yield*a[bs](e,t))}}*[ds](e,t){const a=this[Io].get(e);!a||t&&a[ns]||(yield a);for(const a of this[Mo])yield*a[ds](e,t)}*[xs](e,t,a){for(const r of this[Mo]){r[Ws]!==e||a&&r[ns]||(yield r);t&&(yield*r[xs](e,t,a))}}[Ns](){return null===this[Bo]?0===this[Mo].length||this[Mo][0][Hs]===go.xhtml.id:this[Bo]}[ys](){return null===this[Bo]?0===this[Mo].length?this[ss].trim():this[Mo][0][Hs]===go.xhtml.id?this[Mo][0][so]().\ -trim():null:this[ss].trim()}[io](e){e=e.value||"";this[ss]=e.toString()}[cs](e=!1){const t=Object.create(null);e&&(t.$ns=this[Hs]);this[ss]&&(t.$content=this[ss]);t.$name=this[Ws];t.children=[];for(const a of this[Mo])t.children.push(a[cs](e));t.attributes=Object.create(null);for(const[e,a]of this[Io])t.attributes[e]=a[ss];return t}}class ContentObject extends XFAObject{constructor(e,t){super(e,t);this[ss]=""}[Vs](e){this[ss]+=e}[hs](){}}class OptionObject extends ContentObject{constructor(e,t,a\ -){super(e,t);this[_o]=a}[hs](){this[ss]=getKeyword({data:this[ss],defaultValue:this[_o][0],validate:e=>this[_o].includes(e)})}[ts](e){super[ts](e);delete this[_o]}}class StringObject extends ContentObject{[hs](){this[ss]=this[ss].trim()}}class IntegerObject extends ContentObject{constructor(e,t,a,r){super(e,t);this[Ro]=a;this[Ho]=r}[hs](){this[ss]=getInteger({data:this[ss],defaultValue:this[Ro],validate:this[Ho]})}[ts](e){super[ts](e);delete this[Ro];delete this[Ho]}}class Option01 extends Integ\ -erObject{constructor(e,t){super(e,t,0,(e=>1===e))}}class Option10 extends IntegerObject{constructor(e,t){super(e,t,1,(e=>0===e))}}function measureToString(e){return"string"==typeof e?"0px":Number.isInteger(e)?`${e}px`:`${e.toFixed(2)}px`}const $o={anchorType(e,t){const a=e[Cs]();if(a&&(!a.layout||"position"===a.layout)){"transform"in t||(t.transform="");switch(e.anchorType){case"bottomCenter":t.transform+="translate(-50%, -100%)";break;case"bottomLeft":t.transform+="translate(0,-100%)";break;cas\ -e"bottomRight":t.transform+="translate(-100%,-100%)";break;case"middleCenter":t.transform+="translate(-50%,-50%)";break;case"middleLeft":t.transform+="translate(0,-50%)";break;case"middleRight":t.transform+="translate(-100%,-50%)";break;case"topCenter":t.transform+="translate(-50%,0)";break;case"topRight":t.transform+="translate(-100%,0)"}}},dimensions(e,t){const a=e[Cs]();let r=e.w;const i=e.h;if(a.layout?.includes("row")){const t=a[ls],i=e.colSpan;let n;if(-1===i){n=Math.sumPrecise(t.columnWid\ -ths.slice(t.currentColumn));t.currentColumn=0}else{n=Math.sumPrecise(t.columnWidths.slice(t.currentColumn,t.currentColumn+i));t.currentColumn=(t.currentColumn+e.colSpan)%t.columnWidths.length}isNaN(n)||(r=e.w=n)}t.width=""!==r?measureToString(r):"auto";t.height=""!==i?measureToString(i):"auto"},position(e,t){const a=e[Cs]();if(!a?.layout||"position"===a.layout){t.position="absolute";t.left=measureToString(e.x);t.top=measureToString(e.y)}},rotate(e,t){if(e.rotate){"transform"in t||(t.transform=""\ -);t.transform+=`rotate(-${e.rotate}deg)`;t.transformOrigin="top left"}},presence(e,t){switch(e.presence){case"invisible":t.visibility="hidden";break;case"hidden":case"inactive":t.display="none"}},hAlign(e,t){if("para"===e[Ws])switch(e.hAlign){case"justifyAll":t.textAlign="justify-all";break;case"radix":t.textAlign="left";break;default:t.textAlign=e.hAlign}else switch(e.hAlign){case"left":t.alignSelf="start";break;case"center":t.alignSelf="center";break;case"right":t.alignSelf="end"}},margin(e,t)\ -{e.margin&&(t.margin=e.margin[ho]().margin)}};function setMinMaxDimensions(e,t){if("position"===e[Cs]().layout){e.minW>0&&(t.minWidth=measureToString(e.minW));e.maxW>0&&(t.maxWidth=measureToString(e.maxW));e.minH>0&&(t.minHeight=measureToString(e.minH));e.maxH>0&&(t.maxHeight=measureToString(e.maxH))}}function layoutText(e,t,a,r,i,n){const s=new TextMeasure(t,a,r,i);"string"==typeof e?s.addString(e):e[Ks](s);return s.compute(n)}function layoutNode(e,t){let a=null,r=null,i=!1;if((!e.w||!e.h)&&e.v\ -alue){let n=0,s=0;if(e.margin){n=e.margin.leftInset+e.margin.rightInset;s=e.margin.topInset+e.margin.bottomInset}let o=null,c=null;if(e.para){c=Object.create(null);o=""===e.para.lineHeight?null:e.para.lineHeight;c.top=""===e.para.spaceAbove?0:e.para.spaceAbove;c.bottom=""===e.para.spaceBelow?0:e.para.spaceBelow;c.left=""===e.para.marginLeft?0:e.para.marginLeft;c.right=""===e.para.marginRight?0:e.para.marginRight}let l=e.font;if(!l){const t=e[Fs]();let a=e[vs]();for(;a&&a!==t;){if(a.font){l=a.fon\ -t;break}a=a[vs]()}}const h=(e.w||t.width)-n,u=e[Is].fontFinder;if(e.value.exData&&e.value.exData[ss]&&"text/html"===e.value.exData.contentType){const t=layoutText(e.value.exData[ss],l,c,o,u,h);r=t.width;a=t.height;i=t.isBroken}else{const t=e.value[so]();if(t){const e=layoutText(t,l,c,o,u,h);r=e.width;a=e.height;i=e.isBroken}}null===r||e.w||(r+=n);null===a||e.h||(a+=s)}return{w:r,h:a,isBroken:i}}function computeBbox(e,t,a){let r;if(""!==e.w&&""!==e.h)r=[e.x,e.y,e.w,e.h];else{if(!a)return null;let\ - i=e.w;if(""===i){if(0===e.maxW){const t=e[Cs]();i="position"===t.layout&&""!==t.w?0:e.minW}else i=Math.min(e.maxW,a.width);t.attributes.style.width=measureToString(i)}let n=e.h;if(""===n){if(0===e.maxH){const t=e[Cs]();n="position"===t.layout&&""!==t.h?0:e.minH}else n=Math.min(e.maxH,a.height);t.attributes.style.height=measureToString(n)}r=[e.x,e.y,i,n]}return r}function fixDimensions(e){const t=e[Cs]();if(t.layout?.includes("row")){const a=t[ls],r=e.colSpan;let i;i=-1===r?Math.sumPrecise(a.col\ -umnWidths.slice(a.currentColumn)):Math.sumPrecise(a.columnWidths.slice(a.currentColumn,a.currentColumn+r));isNaN(i)||(e.w=i)}t.layout&&"position"!==t.layout&&(e.x=e.y=0);"table"===e.layout&&""===e.w&&Array.isArray(e.columnWidths)&&(e.w=Math.sumPrecise(e.columnWidths))}function layoutClass(e){switch(e.layout){case"position":default:return"xfaPosition";case"lr-tb":return"xfaLrTb";case"rl-row":return"xfaRlRow";case"rl-tb":return"xfaRlTb";case"row":return"xfaRow";case"table":return"xfaTable";case"tb\ -":return"xfaTb"}}function toStyle(e,...t){const a=Object.create(null);for(const r of t){const t=e[r];if(null!==t)if($o.hasOwnProperty(r))$o[r](e,a);else if(t instanceof XFAObject){const e=t[ho]();e?Object.assign(a,e):warn(`(DEBUG) - XFA - style for ${r} not implemented yet`)}}return a}function createWrapper(e,t){const{attributes:a}=t,{style:r}=a,i={name:"div",attributes:{class:["xfaWrapper"],style:Object.create(null)},children:[]};a.class.push("xfaWrapped");if(e.border){const{widths:a,insets:n}=\ -e.border[ls];let s,o,c=n[0],l=n[3];const h=n[0]+n[2],u=n[1]+n[3];switch(e.border.hand){case"even":c-=a[0]/2;l-=a[3]/2;s=`calc(100% + ${(a[1]+a[3])/2-u}px)`;o=`calc(100% + ${(a[0]+a[2])/2-h}px)`;break;case"left":c-=a[0];l-=a[3];s=`calc(100% + ${a[1]+a[3]-u}px)`;o=`calc(100% + ${a[0]+a[2]-h}px)`;break;case"right":s=u?`calc(100% - ${u}px)`:"100%";o=h?`calc(100% - ${h}px)`:"100%"}const d=["xfaBorder"];isPrintOnly(e.border)&&d.push("xfaPrintOnly");const f={name:"div",attributes:{class:d,style:{top:`$\ -{c}px`,left:`${l}px`,width:s,height:o}},children:[]};for(const e of["border","borderWidth","borderColor","borderRadius","borderStyle"])if(void 0!==r[e]){f.attributes.style[e]=r[e];delete r[e]}i.children.push(f,t)}else i.children.push(t);for(const e of["background","backgroundClip","top","left","width","height","minWidth","minHeight","maxWidth","maxHeight","transform","transformOrigin","visibility"])if(void 0!==r[e]){i.attributes.style[e]=r[e];delete r[e]}i.attributes.style.position="absolute"===\ -r.position?"absolute":"relative";delete r.position;if(r.alignSelf){i.attributes.style.alignSelf=r.alignSelf;delete r.alignSelf}return i}function fixTextIndent(e){const t=getMeasurement(e.textIndent,"0px");if(t>=0)return;const a="padding"+("left"===("right"===e.textAlign?"right":"left")?"Left":"Right"),r=getMeasurement(e[a],"0px");e[a]=r-t+"px"}function setAccess(e,t){switch(e.access){case"nonInteractive":t.push("xfaNonInteractive");break;case"readOnly":t.push("xfaReadOnly");break;case"protected"\ -:t.push("xfaDisabled")}}function isPrintOnly(e){return e.relevant.length>0&&!e.relevant[0].excluded&&"print"===e.relevant[0].viewname}function getCurrentPara(e){const t=e[Fs]()[ls].paraStack;return t.length?t.at(-1):null}function setPara(e,t,a){if(a.attributes.class?.includes("xfaRich")){if(t){""===e.h&&(t.height="auto");""===e.w&&(t.width="auto")}const r=getCurrentPara(e);if(r){const e=a.attributes.style;e.display="flex";e.flexDirection="column";switch(r.vAlign){case"top":e.justifyContent="star\ -t";break;case"bottom":e.justifyContent="end";break;case"middle":e.justifyContent="center"}const t=r[ho]();for(const[a,r]of Object.entries(t))a in e||(e[a]=r)}}}function setFontFamily(e,t,a,r){if(!a){delete r.fontFamily;return}const i=stripQuotes(e.typeface);r.fontFamily=`"${i}"`;const n=a.find(i);if(n){const{fontFamily:a}=n.regular.cssFontInfo;a!==i&&(r.fontFamily=`"${a}"`);const s=getCurrentPara(t);if(s&&""!==s.lineHeight)return;if(r.lineHeight)return;const o=selectFont(e,n);o&&(r.lineHeight=Ma\ -th.max(1.2,o.lineHeight))}}function fixURL(e){const t=createValidAbsoluteUrl(e,null,{addDefaultProtocol:!0,tryConvertEncoding:!0});return t?t.href:null}function createLine(e,t){return{name:"div",attributes:{class:["lr-tb"===e.layout?"xfaLr":"xfaRl"]},children:t}}function flushHTML(e){if(!e[ls])return null;const t={name:"div",attributes:e[ls].attributes,children:e[ls].children};if(e[ls].failingNode){const a=e[ls].failingNode[us]();a&&(e.layout.endsWith("-tb")?t.children.push(createLine(e,[a])):t.\ -children.push(a))}return 0===t.children.length?null:t}function addHTML(e,t,a){const r=e[ls],i=r.availableSpace,[n,s,o,c]=a;switch(e.layout){case"position":r.width=Math.max(r.width,n+o);r.height=Math.max(r.height,s+c);r.children.push(t);break;case"lr-tb":case"rl-tb":if(!r.line||1===r.attempt){r.line=createLine(e,[]);r.children.push(r.line);r.numberInLine=0}r.numberInLine+=1;r.line.children.push(t);if(0===r.attempt){r.currentWidth+=o;r.height=Math.max(r.height,r.prevHeight+c)}else{r.currentWidth=o\ -;r.prevHeight=r.height;r.height+=c;r.attempt=0}r.width=Math.max(r.width,r.currentWidth);break;case"rl-row":case"row":{r.children.push(t);r.width+=o;r.height=Math.max(r.height,c);const e=measureToString(r.height);for(const t of r.children)t.attributes.style.height=e;break}case"table":case"tb":r.width=MathClamp(o,r.width,i.width);r.height+=c;r.children.push(t)}}function getAvailableSpace(e){const t=e[ls].availableSpace,a=e.margin?e.margin.topInset+e.margin.bottomInset:0,r=e.margin?e.margin.leftIns\ -et+e.margin.rightInset:0;switch(e.layout){case"lr-tb":case"rl-tb":return 0===e[ls].attempt?{width:t.width-r-e[ls].currentWidth,height:t.height-a-e[ls].prevHeight}:{width:t.width-r,height:t.height-a-e[ls].height};case"rl-row":case"row":return{width:Math.sumPrecise(e[ls].columnWidths.slice(e[ls].currentColumn)),height:t.height-r};case"table":case"tb":return{width:t.width-r,height:t.height-a-e[ls].height};default:return t}}function checkDimensions(e,t){if(null===e[Fs]()[ls].firstUnsplittable)return\ -!0;if(0===e.w||0===e.h)return!0;const a=e[Cs](),r=a[ls]?.attempt||0,[,i,n,s]=function getTransformedBBox(e){let t,a,r=""===e.w?NaN:e.w,i=""===e.h?NaN:e.h,[n,s]=[0,0];switch(e.anchorType||""){case"bottomCenter":[n,s]=[r/2,i];break;case"bottomLeft":[n,s]=[0,i];break;case"bottomRight":[n,s]=[r,i];break;case"middleCenter":[n,s]=[r/2,i/2];break;case"middleLeft":[n,s]=[0,i/2];break;case"middleRight":[n,s]=[r,i/2];break;case"topCenter":[n,s]=[r/2,0];break;case"topRight":[n,s]=[r,0]}switch(e.rotate||0){\ -case 0:[t,a]=[-n,-s];break;case 90:[t,a]=[-s,n];[r,i]=[i,-r];break;case 180:[t,a]=[n,s];[r,i]=[-r,-i];break;case 270:[t,a]=[s,-n];[r,i]=[-i,r]}return[e.x+t+Math.min(0,r),e.y+a+Math.min(0,i),Math.abs(r),Math.abs(i)]}(e);switch(a.layout){case"lr-tb":case"rl-tb":return 0===r?e[Fs]()[ls].noLayoutFailure?""!==e.w?Math.round(n-t.width)<=2:t.width>2:!(""!==e.h&&Math.round(s-t.height)>2)&&(""!==e.w?Math.round(n-t.width)<=2||0===a[ls].numberInLine&&t.height>2:t.width>2):!!e[Fs]()[ls].noLayoutFailure||!("\ -"!==e.h&&Math.round(s-t.height)>2)&&((""===e.w||Math.round(n-t.width)<=2||!a[_s]())&&t.height>2);case"table":case"tb":return!!e[Fs]()[ls].noLayoutFailure||(""===e.h||e[js]()?(""===e.w||Math.round(n-t.width)<=2||!a[_s]())&&t.height>2:Math.round(s-t.height)<=2);case"position":if(e[Fs]()[ls].noLayoutFailure)return!0;if(""===e.h||Math.round(s+i-t.height)<=2)return!0;return s+i>e[Fs]()[ls].currentContentArea.h;case"rl-row":case"row":return!!e[Fs]()[ls].noLayoutFailure||(""===e.h||Math.round(s-t.heigh\ -t)<=2);default:return!0}}const Go=go.template.id,Vo="http://www.w3.org/2000/svg",Ko=/^H(\\d+)$/,Jo=new Set(["image/gif","image/jpeg","image/jpg","image/pjpeg","image/png","image/apng","image/x-png","image/bmp","image/x-ms-bmp","image/tiff","image/tif","application/octet-stream"]),Yo=[[[66,77],"image/bmp"],[[255,216,255],"image/jpeg"],[[73,73,42,0],"image/tiff"],[[77,77,0,42],"image/tiff"],[[71,73,70,56,57,97],"image/gif"],[[137,80,78,71,13,10,26,10],"image/png"]];function getBorderDims(e){if(!e||\ -!e.border)return{w:0,h:0};const t=e.border[ws]();return t?{w:t.widths[0]+t.widths[2]+t.insets[0]+t.insets[2],h:t.widths[1]+t.widths[3]+t.insets[1]+t.insets[3]}:{w:0,h:0}}function hasMargin(e){return e.margin&&(e.margin.topInset||e.margin.rightInset||e.margin.bottomInset||e.margin.leftInset)}function _setValue(e,t){if(!e.value){const t=new Value({});e[Qn](t);e.value=t}e.value[io](t)}function*getContainedChildren(e){for(const t of e[Ss]())t instanceof SubformSet?yield*t[As]():yield t}function isRe\ -quired(e){return"error"===e.validate?.nullTest}function setTabIndex(e){for(;e;){if(!e.traversal){e[no]=e[vs]()[no];return}if(e[no])return;let t=null;for(const a of e.traversal[Ss]())if("next"===a.operation){t=a;break}if(!t||!t.ref){e[no]=e[vs]()[no];return}const a=e[Fs]();e[no]=++a[no];const r=a[to](t.ref,e);if(!r)return;e=r[0]}}function applyAssist(e,t){const a=e.assist;if(a){const e=a[co]();e&&(t.title=e);const r=a.role.match(Ko);if(r){const e="heading",a=r[1];t.role=e;t["aria-level"]=a}}if("t\ -able"===e.layout)t.role="table";else if("row"===e.layout)t.role="row";else{const a=e[vs]();"row"===a.layout&&(t.role="TH"===a.assist?.role?"columnheader":"cell")}}function ariaLabel(e){if(!e.assist)return null;const t=e.assist;return t.speak&&""!==t.speak[ss]?t.speak[ss]:t.toolTip?t.toolTip[ss]:null}function valueToHtml(e){return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:Object.create(null)},children:[{name:"span",attributes:{style:Object.create(null)},value:e}]})}functi\ -on setFirstUnsplittable(e){const t=e[Fs]();if(null===t[ls].firstUnsplittable){t[ls].firstUnsplittable=e;t[ls].noLayoutFailure=!0}}function unsetFirstUnsplittable(e){const t=e[Fs]();t[ls].firstUnsplittable===e&&(t[ls].noLayoutFailure=!1)}function handleBreak(e){if(e[ls])return!1;e[ls]=Object.create(null);if("auto"===e.targetType)return!1;const t=e[Fs]();let a=null;if(e.target){a=t[to](e.target,e[vs]());if(!a)return!1;a=a[0]}const{currentPageArea:r,currentContentArea:i}=t[ls];if("pageArea"===e.tar\ -getType){a instanceof PageArea||(a=null);if(e.startNew){e[ls].target=a||r;return!0}if(a&&a!==r){e[ls].target=a;return!0}return!1}a instanceof ContentArea||(a=null);const n=a&&a[vs]();let s,o=n;if(e.startNew)if(a){const e=n.contentArea.children,t=e.indexOf(i),r=e.indexOf(a);-1!==t&&te;r[ls].noLayoutFailure=!0;const s=t[co](a);e[Zn](s.html,s.bbox);r[ls].noLayoutFailure=i;t[Cs]=n}class AppearanceFilter extends StringObject{constructor(e){super(Go,"appearanceFilter");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Arc extends XFAObject{constructor(e){super(Go,"arc",!0);this.circular=getInteger({data:e.circular,defaultValue:0,validate:e=>1===e});this\ -.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.startAngle=getFloat({data:e.startAngle,defaultValue:0,validate:e=>!0});this.sweepAngle=getFloat({data:e.sweepAngle,defaultValue:360,validate:e=>!0});this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null;this.fill=null}[co](){const e=this.edge||new Edge({}),t=e[ho](),a=Object.create(null);"visible"===this.fill?.presence?Object.assign(a,this.fill[ho]()):a.fill="transparent";a.strokeWidth=measureToString("visible"===\ -e.presence?e.thickness:0);a.stroke=t.color;let r;const i={xmlns:Vo,style:{width:"100%",height:"100%",overflow:"visible"}};if(360===this.sweepAngle)r={name:"ellipse",attributes:{xmlns:Vo,cx:"50%",cy:"50%",rx:"50%",ry:"50%",style:a}};else{const e=this.startAngle*Math.PI/180,t=this.sweepAngle*Math.PI/180,n=this.sweepAngle>180?1:0,[s,o,c,l]=[50*(1+Math.cos(e)),50*(1-Math.sin(e)),50*(1+Math.cos(e+t)),50*(1-Math.sin(e+t))];r={name:"path",attributes:{xmlns:Vo,d:`M ${s} ${o} A 50 50 0 ${n} 0 ${c} ${l}`,\ -vectorEffect:"non-scaling-stroke",style:a}};Object.assign(i,{viewBox:"0 0 100 100",preserveAspectRatio:"none"})}const n={name:"svg",children:[r],attributes:i};if(hasMargin(this[vs]()[vs]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[n]});n.attributes.style.position="absolute";return HTMLResult.success(n)}}class Area extends XFAObject{constructor(e){super(Go,"area",!0);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,vali\ -date:e=>e>=1||-1===e});this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null;this.area=new XFAObjectArray;this.draw=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[As](){yield*getContainedChildren(\ -this)}[Us](){return!0}[Rs](){return!0}[Zn](e,t){const[a,r,i,n]=t;this[ls].width=Math.max(this[ls].width,a+i);this[ls].height=Math.max(this[ls].height,r+n);this[ls].children.push(e)}[gs](){return this[ls].availableSpace}[co](e){const t=toStyle(this,"position"),a={style:t,id:this[uo],class:["xfaArea"]};isPrintOnly(this)&&a.class.push("xfaPrintOnly");this.name&&(a.xfaName=this.name);const r=[];this[ls]={children:r,width:0,height:0,availableSpace:e};const i=this[es]({filter:new Set(["area","draw","f\ -ield","exclGroup","subform","subformSet"]),include:!0});if(!i.success){if(i.isBreak())return i;delete this[ls];return HTMLResult.FAILURE}t.width=measureToString(this[ls].width);t.height=measureToString(this[ls].height);const n={name:"div",attributes:a,children:r},s=[this.x,this.y,this[ls].width,this[ls].height];delete this[ls];return HTMLResult.success(n,s)}}class Assist extends XFAObject{constructor(e){super(Go,"assist",!0);this.id=e.id||"";this.role=e.role||"";this.use=e.use||"";this.usehref=e\ -.usehref||"";this.speak=null;this.toolTip=null}[co](){return this.toolTip?.[ss]||null}}class Barcode extends XFAObject{constructor(e){super(Go,"barcode",!0);this.charEncoding=getKeyword({data:e.charEncoding?e.charEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\\d{2}/)});this.checksum=getStringOption(e.checksum,["none","1mod10","1mod10_1mod11","2mod10"\ -,"auto"]);this.dataColumnCount=getInteger({data:e.dataColumnCount,defaultValue:-1,validate:e=>e>=0});this.dataLength=getInteger({data:e.dataLength,defaultValue:-1,validate:e=>e>=0});this.dataPrep=getStringOption(e.dataPrep,["none","flateCompress"]);this.dataRowCount=getInteger({data:e.dataRowCount,defaultValue:-1,validate:e=>e>=0});this.endChar=e.endChar||"";this.errorCorrectionLevel=getInteger({data:e.errorCorrectionLevel,defaultValue:-1,validate:e=>e>=0&&e<=8});this.id=e.id||"";this.moduleHeig\ -ht=getMeasurement(e.moduleHeight,"5mm");this.moduleWidth=getMeasurement(e.moduleWidth,"0.25mm");this.printCheckDigit=getInteger({data:e.printCheckDigit,defaultValue:0,validate:e=>1===e});this.rowColumnRatio=getRatio(e.rowColumnRatio);this.startChar=e.startChar||"";this.textLocation=getStringOption(e.textLocation,["below","above","aboveEmbedded","belowEmbedded","none"]);this.truncate=getInteger({data:e.truncate,defaultValue:0,validate:e=>1===e});this.type=getStringOption(e.type?e.type.toLowerCase\ -():"",["aztec","codabar","code2of5industrial","code2of5interleaved","code2of5matrix","code2of5standard","code3of9","code3of9extended","code11","code49","code93","code128","code128a","code128b","code128c","code128sscc","datamatrix","ean8","ean8add2","ean8add5","ean13","ean13add2","ean13add5","ean13pwcd","fim","logmars","maxicode","msi","pdf417","pdf417macro","plessey","postauscust2","postauscust3","postausreplypaid","postausstandard","postukrm4scc","postusdpbc","postusimb","postusstandard","postu\ -s5zip","qrcode","rfid","rss14","rss14expanded","rss14limited","rss14stacked","rss14stackedomni","rss14truncated","telepen","ucc128","ucc128random","ucc128sscc","upca","upcaadd2","upcaadd5","upcapwcd","upce","upceadd2","upceadd5","upcean2","upcean5","upsmaxicode"]);this.upsMode=getStringOption(e.upsMode,["usCarrier","internationalCarrier","secureSymbol","standardSymbol"]);this.use=e.use||"";this.usehref=e.usehref||"";this.wideNarrowRatio=getRatio(e.wideNarrowRatio);this.encrypt=null;this.extras=n\ -ull}}class Bind extends XFAObject{constructor(e){super(Go,"bind",!0);this.match=getStringOption(e.match,["once","dataRef","global","none"]);this.ref=e.ref||"";this.picture=null}}class BindItems extends XFAObject{constructor(e){super(Go,"bindItems");this.connection=e.connection||"";this.labelRef=e.labelRef||"";this.ref=e.ref||"";this.valueRef=e.valueRef||""}}class Bookend extends XFAObject{constructor(e){super(Go,"bookend");this.id=e.id||"";this.leader=e.leader||"";this.trailer=e.trailer||"";this\ -.use=e.use||"";this.usehref=e.usehref||""}}class BooleanElement extends Option01{constructor(e){super(Go,"boolean");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[co](e){return valueToHtml(1===this[ss]?"1":"0")}}class Border extends XFAObject{constructor(e){super(Go,"border",!0);this.break=getStringOption(e.break,["close","open"]);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","h\ -idden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.extras=null;this.fill=null;this.margin=null}[ws](){if(!this[ls]){const e=this.edge.children.slice();if(e.length<4){const t=e.at(-1)||new Edge({});for(let a=e.length;a<4;a++)e.push(t)}const t=e.map((e=>e.thickness)),a=[0,0,0,0];if(this.margin){a[0]=this.margin.topInset;a[1]=this.margin.rightInset;a[2]=this.margin\ -.bottomInset;a[3]=this.margin.leftInset}this[ls]={widths:t,insets:a,edges:e}}return this[ls]}[ho](){const{edges:e}=this[ws](),t=e.map((e=>{const t=e[ho]();t.color||="#000000";return t})),a=Object.create(null);this.margin&&Object.assign(a,this.margin[ho]());"visible"===this.fill?.presence&&Object.assign(a,this.fill[ho]());if(this.corner.children.some((e=>0!==e.radius))){const e=this.corner.children.map((e=>e[ho]()));if(2===e.length||3===e.length){const t=e.at(-1);for(let a=e.length;a<4;a++)e.push\ -(t)}a.borderRadius=e.map((e=>e.radius)).join(" ")}switch(this.presence){case"invisible":case"hidden":a.borderStyle="";break;case"inactive":a.borderStyle="none";break;default:a.borderStyle=t.map((e=>e.style)).join(" ")}a.borderWidth=t.map((e=>e.width)).join(" ");a.borderColor=t.map((e=>e.color)).join(" ");return a}}class Break extends XFAObject{constructor(e){super(Go,"break",!0);this.after=getStringOption(e.after,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.afterTarget=e.afterTar\ -get||"";this.before=getStringOption(e.before,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.beforeTarget=e.beforeTarget||"";this.bookendLeader=e.bookendLeader||"";this.bookendTrailer=e.bookendTrailer||"";this.id=e.id||"";this.overflowLeader=e.overflowLeader||"";this.overflowTarget=e.overflowTarget||"";this.overflowTrailer=e.overflowTrailer||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}\ -}class BreakAfter extends XFAObject{constructor(e){super(Go,"breakAfter",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}}class BreakBefore extends XFAObject{constructor(e){super(Go,"breakBefore",!0);this.id=e.id||"";this.leader=e\ -.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}[co](e){this[ls]={};return HTMLResult.FAILURE}}class Button extends XFAObject{constructor(e){super(Go,"button",!0);this.highlight=getStringOption(e.highlight,["inverted","none","outline","push"]);this.id=e.id||"";thi\ -s.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[co](e){const t=this[vs]()[vs](),a={name:"button",attributes:{id:this[uo],class:["xfaButton"],style:{}},children:[]};for(const e of t.event.children){if("click"!==e.activity||!e.script)continue;const t=recoverJsURL(e.script[ss]);if(!t)continue;const r=fixURL(t.url);r&&a.children.push({name:"a",attributes:{id:"link"+this[uo],href:r,newWindow:t.newWindow,class:["xfaLink"],style:{}},children:[]})}return HTMLResult.success(a)}}class Calcula\ -te extends XFAObject{constructor(e){super(Go,"calculate",!0);this.id=e.id||"";this.override=getStringOption(e.override,["disabled","error","ignore","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.script=null}}class Caption extends XFAObject{constructor(e){super(Go,"caption",!0);this.id=e.id||"";this.placement=getStringOption(e.placement,["left","bottom","inline","right","top"]);this.presence=getStringOption(e.presence,["visible","hidden","inactiv\ -e","invisible"]);this.reserve=Math.ceil(getMeasurement(e.reserve));this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.font=null;this.margin=null;this.para=null;this.value=null}[io](e){_setValue(this,e)}[ws](e){if(!this[ls]){let{width:t,height:a}=e;switch(this.placement){case"left":case"right":case"inline":t=this.reserve<=0?t:this.reserve;break;case"top":case"bottom":a=this.reserve<=0?a:this.reserve}this[ls]=layoutNode(this,{width:t,height:a})}return this[ls]}[co](e){if(!this.val\ -ue)return HTMLResult.EMPTY;this[Ys]();const t=this.value[co](e).html;if(!t){this[Js]();return HTMLResult.EMPTY}const a=this.reserve;if(this.reserve<=0){const{w:t,h:a}=this[ws](e);switch(this.placement){case"left":case"right":case"inline":this.reserve=t;break;case"top":case"bottom":this.reserve=a}}const r=[];"string"==typeof t?r.push({name:"#text",value:t}):r.push(t);const i=toStyle(this,"font","margin","visibility");switch(this.placement){case"left":case"right":this.reserve>0&&(i.width=measureTo\ -String(this.reserve));break;case"top":case"bottom":this.reserve>0&&(i.height=measureToString(this.reserve))}setPara(this,null,t);this[Js]();this.reserve=a;return HTMLResult.success({name:"div",attributes:{style:i,class:["xfaCaption"]},children:r})}}class Certificate extends StringObject{constructor(e){super(Go,"certificate");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Certificates extends XFAObject{constructor(e){super(Go,"certificates",!0);this.cre\ -dentialServerPolicy=getStringOption(e.credentialServerPolicy,["optional","required"]);this.id=e.id||"";this.url=e.url||"";this.urlPolicy=e.urlPolicy||"";this.use=e.use||"";this.usehref=e.usehref||"";this.encryption=null;this.issuers=null;this.keyUsage=null;this.oids=null;this.signing=null;this.subjectDNs=null}}class CheckButton extends XFAObject{constructor(e){super(Go,"checkButton",!0);this.id=e.id||"";this.mark=getStringOption(e.mark,["default","check","circle","cross","diamond","square","star\ -"]);this.shape=getStringOption(e.shape,["square","round"]);this.size=getMeasurement(e.size,"10pt");this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[co](e){const t=toStyle(this,"margin"),a=measureToString(this.size);t.width=t.height=a;let r,i,n;const s=this[vs]()[vs](),o=s.items.children.length&&s.items.children[0][co]().html||[],c={on:(void 0!==o[0]?o[0]:"on").toString(),off:(void 0!==o[1]?o[1]:"off").toString()},l=(s.value?.[so]()||"off")===c.on|\ -|void 0,h=s[Cs](),u=s[uo];let d;if(h instanceof ExclGroup){n=h[uo];r="radio";i="xfaRadio";d=h[os]?.[uo]||h[uo]}else{r="checkbox";i="xfaCheckbox";d=s[os]?.[uo]||s[uo]}const f={name:"input",attributes:{class:[i],style:t,fieldId:u,dataId:d,type:r,checked:l,xfaOn:c.on,xfaOff:c.off,"aria-label":ariaLabel(s),"aria-required":!1}};n&&(f.attributes.name=n);if(isRequired(s)){f.attributes["aria-required"]=!0;f.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},ch\ -ildren:[f]})}}class ChoiceList extends XFAObject{constructor(e){super(Go,"choiceList",!0);this.commitOn=getStringOption(e.commitOn,["select","exit"]);this.id=e.id||"";this.open=getStringOption(e.open,["userControl","always","multiSelect","onEntry"]);this.textEntry=getInteger({data:e.textEntry,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[co](e){const t=toStyle(this,"border","margin"),a=this[vs]()[vs](),r={font\ -Size:`calc(${a.font?.size||10}px * var(--total-scale-factor))`},i=[];if(a.items.children.length>0){const e=a.items;let t=0,n=0;if(2===e.children.length){t=e.children[0].save;n=1-t}const s=e.children[t][co]().html,o=e.children[n][co]().html;let c=!1;const l=a.value?.[so]()||"";for(let e=0,t=s.length;eMathClamp(parseInt(e.trim(),10),0,255))).map((e=>isNaN(e)?0:e));if(n.length<3)return{r:a,g:r,b:i};[a,r,i]=n;return{r:a,g:r,b:i}}(e.value):"";this.extras=null}[Ts](){return!1}[ho](){return this.value?Util.makeHexColor(this.value.r,this.value.g,this.value.b):null}}class Comb extends XFAObject{constructor(e){super(Go,"comb");this.id=e.id||"";this.num\ -berOfCells=getInteger({data:e.numberOfCells,defaultValue:0,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||""}}class Connect extends XFAObject{constructor(e){super(Go,"connect",!0);this.connection=e.connection||"";this.id=e.id||"";this.ref=e.ref||"";this.usage=getStringOption(e.usage,["exportAndImport","exportOnly","importOnly"]);this.use=e.use||"";this.usehref=e.usehref||"";this.picture=null}}class ContentArea extends XFAObject{constructor(e){super(Go,"contentArea",!0);this.h=getM\ -easurement(e.h);this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=getMeasurement(e.w);this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null}[co](e){const t={left:measureToString(this.x),top:measureToString(this.y),width:measureToString(this.w),height:measureToString(this.h)},a=["xfaContentarea"];isPrintOnly(this)&&a.push("xfaPrintOnly");return HTMLResult.success({name:"div",\ -children:[],attributes:{style:t,class:a,id:this[uo]}})}}class Corner extends XFAObject{constructor(e){super(Go,"corner",!0);this.id=e.id||"";this.inverted=getInteger({data:e.inverted,defaultValue:0,validate:e=>1===e});this.join=getStringOption(e.join,["square","round"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.radius=getMeasurement(e.radius);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched"\ -,"lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](){const e=toStyle(this,"visibility");e.radius=measureToString("square"===this.join?0:this.radius);return e}}class DateElement extends ContentObject{constructor(e){super(Go,"date");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=this[ss].trim();this[ss]=e?new Date(e):null}[co](e){return value\ -ToHtml(this[ss]?this[ss].toString():"")}}class DateTime extends ContentObject{constructor(e){super(Go,"dateTime");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=this[ss].trim();this[ss]=e?new Date(e):null}[co](e){return valueToHtml(this[ss]?this[ss].toString():"")}}class DateTimeEdit extends XFAObject{constructor(e){super(Go,"dateTimeEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.picker=g\ -etStringOption(e.picker,["host","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[co](e){const t=toStyle(this,"border","font","margin"),a=this[vs]()[vs](),r={name:"input",attributes:{type:"text",fieldId:a[uo],dataId:a[os]?.[uo]||a[uo],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){r.attributes["aria-required"]=!0;r.attributes.required=!0}return HTMLResult.success({name:"label"\ -,attributes:{class:["xfaLabel"]},children:[r]})}}class Decimal extends ContentObject{constructor(e){super(Go,"decimal");this.fracDigits=getInteger({data:e.fracDigits,defaultValue:2,validate:e=>!0});this.id=e.id||"";this.leadDigits=getInteger({data:e.leadDigits,defaultValue:-1,validate:e=>!0});this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=parseFloat(this[ss].trim());this[ss]=isNaN(e)?null:e}[co](e){return valueToHtml(null!==this[ss]?this[ss].toString():"")}}cla\ -ss DefaultUi extends XFAObject{constructor(e){super(Go,"defaultUi",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class Desc extends XFAObject{constructor(e){super(Go,"desc",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this\ -.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class DigestMethod extends OptionObject{constructor(e){super(Go,"digestMethod",["","SHA1","SHA256","SHA512","RIPEMD160"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class DigestMethods extends XFAObject{constructor(e){super(Go,"digestMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.digestMethod=new XF\ -AObjectArray}}class Draw extends XFAObject{constructor(e){super(Go,"draw",!0);this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this\ -.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasu\ -rement(e.y,"0pt");this.assist=null;this.border=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.value=null;this.setProperty=new XFAObjectArray}[io](e){_setValue(this,e)}[co](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;fixDimensions(this);this[Ys]();const t=this.w,a=this.h,{w:r,h:i,isBroken:n}=layoutNode(this,e);if(r&&""===this.w){if(n&\ -&this[Cs]()[_s]()){this[Js]();return HTMLResult.FAILURE}this.w=r}i&&""===this.h&&(this.h=i);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=t;this.h=a;this[Js]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const s=toStyle(this,"font","hAlign","dimensions","position","presence","rotate","anchorType","border","margin");setMinMaxDimensions(this,s);if(s.margin){s.padding=s.margin;delete s.margin}const o=["xfaDraw"];this.font&&o.push("xfaFont");isPrintOnly(this)&&o.push("xfa\ -PrintOnly");const c={style:s,id:this[uo],class:o};this.name&&(c.xfaName=this.name);const l={name:"div",attributes:c,children:[]};applyAssist(this,c);const h=computeBbox(this,l,e),u=this.value?this.value[co](e).html:null;if(null===u){this.w=t;this.h=a;this[Js]();return HTMLResult.success(createWrapper(this,l),h)}l.children.push(u);setPara(this,s,u);this.w=t;this.h=a;this[Js]();return HTMLResult.success(createWrapper(this,l),h)}}class Edge extends XFAObject{constructor(e){super(Go,"edge",!0);this.\ -cap=getStringOption(e.cap,["square","butt","round"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](){const e=toStyle(this,"visibility");Object.assign(e,{linecap:this.cap,width:meas\ -ureToString(this.thickness),color:this.color?this.color[ho]():"#000000",style:""});if("visible"!==this.presence)e.style="none";else switch(this.stroke){case"solid":e.style="solid";break;case"dashDot":case"dashDotDot":case"dashed":e.style="dashed";break;case"dotted":e.style="dotted";break;case"embossed":e.style="ridge";break;case"etched":e.style="groove";break;case"lowered":e.style="inset";break;case"raised":e.style="outset"}return e}}class Encoding extends OptionObject{constructor(e){super(Go,"e\ -ncoding",["adbe.x509.rsa_sha1","adbe.pkcs7.detached","adbe.pkcs7.sha1"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Encodings extends XFAObject{constructor(e){super(Go,"encodings",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encoding=new XFAObjectArray}}class Encrypt extends XFAObject{constructor(e){super(Go,"encrypt",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";t\ -his.certificate=null}}class EncryptData extends XFAObject{constructor(e){super(Go,"encryptData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["encrypt","decrypt"]);this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Encryption extends XFAObject{constructor(e){super(Go,"encryption",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certifi\ -cate=new XFAObjectArray}}class EncryptionMethod extends OptionObject{constructor(e){super(Go,"encryptionMethod",["","AES256-CBC","TRIPLEDES-CBC","AES128-CBC","AES192-CBC"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EncryptionMethods extends XFAObject{constructor(e){super(Go,"encryptionMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encryptionMethod=new XFAObjectArray}}class Even\ -t extends XFAObject{constructor(e){super(Go,"event",!0);this.activity=getStringOption(e.activity,["click","change","docClose","docReady","enter","exit","full","indexChange","initialize","mouseDown","mouseEnter","mouseExit","mouseUp","postExecute","postOpen","postPrint","postSave","postSign","postSubmit","preExecute","preOpen","prePrint","preSave","preSign","preSubmit","ready","validationState"]);this.id=e.id||"";this.listen=getStringOption(e.listen,["refOnly","refAndDescendents"]);this.name=e.na\ -me||"";this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.encryptData=null;this.execute=null;this.script=null;this.signData=null;this.submit=null}}class ExData extends ContentObject{constructor(e){super(Go,"exData");this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.maxLength=getInteger({data:e.maxLength,defaultValue:-1,validate:e=>e>=-1});this.name=e.name||"";this.rid=e.rid||"";this.transferEncoding=getStringOption(e.transferEncodin\ -g,["none","base64","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[Bs](){return"text/html"===this.contentType}[$s](e){if("text/html"===this.contentType&&e[Hs]===go.xhtml.id){this[ss]=e;return!0}if("text/xml"===this.contentType){this[ss]=e;return!0}return!1}[co](e){return"text/html"===this.contentType&&this[ss]?this[ss][co](e):HTMLResult.EMPTY}}class ExObject extends XFAObject{constructor(e){super(Go,"exObject",!0);this.archive=e.archive||"";this.classId=e.classId||"";this.codeBase=e.c\ -odeBase||"";this.codeType=e.codeType||"";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class ExclGroup e\ -xtends XFAObject{constructor(e){super(Go,"exclGroup",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left"\ -,"center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.us\ -ehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.margin=null;this.para=null;this.traversal=null;this.validate=null;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.field=new XFAObjectArray;this.setProperty=new XFAObjectArray}[Rs](){return!0}[Ts](){return!0}[io](e){for(const t of this.field.childr\ -en){if(!t.value){const e=new Value({});t[Qn](e);t.value=e}t.value[io](e)}}[_s](){return this.layout.endsWith("-tb")&&0===this[ls].attempt&&this[ls].numberInLine>0||this[vs]()[_s]()}[js](){const e=this[Cs]();if(!e[js]())return!1;if(void 0!==this[ls]._isSplittable)return this[ls]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[ls]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[ls].numberInLine)return!1;this[ls]._isSplittable=!0;return!0}[us](){return flu\ -shHTML(this)}[Zn](e,t){addHTML(this,e,t)}[gs](){return getAvailableSpace(this)}[co](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;fixDimensions(this);const t=[],a={id:this[uo],class:[]};setAccess(this,a.class);this[ls]||=Object.create(null);Object.assign(this[ls],{children:t,attributes:a,attempt:0,line:null,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:\ -0,height:0,prevHeight:0,currentWidth:0});const r=this[js]();r||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const i=new Set(["field"]);if(this.layout.includes("row")){const e=this[Cs]().columnWidths;if(Array.isArray(e)&&e.length>0){this[ls].columnWidths=e;this[ls].currentColumn=0}}const n=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),s=["xfaExclgroup"],o=layoutClass(this);o&&s.push(o);isPrintOnly(this)&&s.push("xfaPri\ -ntOnly");a.style=n;a.class=s;this.name&&(a.xfaName=this.name);this[Ys]();const c="lr-tb"===this.layout||"rl-tb"===this.layout,l=c?2:1;for(;this[ls].attempte>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0p\ -t");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this\ -.desc=null;this.extras=null;this.font=null;this.format=null;this.items=new XFAObjectArray(2);this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.validate=null;this.value=null;this.bindItems=new XFAObjectArray;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.setProperty=new XFAObjectArray}[Rs](){return!0}[io](e){_setValue(this,e)}[co](e){setTabIndex(this);if(!this.ui){this.ui=new Ui({});this.ui[Is]=this[Is];this[Qn](this.ui);let e;switch(this.ite\ -ms.children.length){case 0:e=new TextEdit({});this.ui.textEdit=e;break;case 1:e=new CheckButton({});this.ui.checkButton=e;break;case 2:e=new ChoiceList({});this.ui.choiceList=e}this.ui[Qn](e)}if(!this.ui||"hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;this.caption&&delete this.caption[ls];this[Ys]();const t=this.caption?this.caption[co](e).html:null,a=this.w,r=this.h;let i=0,n=0;if(this.margin){i=this.margin.leftInset+this.margin.rightInset;n\ -=this.margin.topInset+this.margin.bottomInset}let s=null;if(""===this.w||""===this.h){let t=null,a=null,r=0,o=0;if(this.ui.checkButton)r=o=this.ui.checkButton.size;else{const{w:t,h:a}=layoutNode(this,e);if(null!==t){r=t;o=a}else o=function fonts_getMetrics(e,t=!1){let a=null;if(e){const t=stripQuotes(e.typeface),r=e[Is].fontFinder.find(t);a=selectFont(e,r)}if(!a)return{lineHeight:12,lineGap:2,lineNoGap:10};const r=e.size||10,i=a.lineHeight?Math.max(t?0:1.2,a.lineHeight):1.2,n=void 0===a.lineGap?\ -.2:a.lineGap;return{lineHeight:i*r,lineGap:n*r,lineNoGap:Math.max(1,i-n)*r}}(this.font,!0).lineNoGap}s=getBorderDims(this.ui[ws]());r+=s.w;o+=s.h;if(this.caption){const{w:i,h:n,isBroken:s}=this.caption[ws](e);if(s&&this[Cs]()[_s]()){this[Js]();return HTMLResult.FAILURE}t=i;a=n;switch(this.caption.placement){case"left":case"right":case"inline":t+=r;break;case"top":case"bottom":a+=o}}else{t=r;a=o}if(t&&""===this.w){t+=i;this.w=Math.min(this.maxW<=0?1/0:this.maxW,this.minW+1e>=1&&e<=5});this.appearanceFi\ -lter=null;this.certificates=null;this.digestMethods=null;this.encodings=null;this.encryptionMethods=null;this.handler=null;this.lockDocument=null;this.mdp=null;this.reasons=null;this.timeStamp=null}}class Float extends ContentObject{constructor(e){super(Go,"float");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=parseFloat(this[ss].trim());this[ss]=isNaN(e)?null:e}[co](e){return valueToHtml(null!==this[ss]?this[ss].toString():"")}}class template\ -_Font extends XFAObject{constructor(e){super(Go,"font",!0);this.baselineShift=getMeasurement(e.baselineShift);this.fontHorizontalScale=getFloat({data:e.fontHorizontalScale,defaultValue:100,validate:e=>e>=0});this.fontVerticalScale=getFloat({data:e.fontVerticalScale,defaultValue:100,validate:e=>e>=0});this.id=e.id||"";this.kerningMode=getStringOption(e.kerningMode,["none","pair"]);this.letterSpacing=getMeasurement(e.letterSpacing,"0");this.lineThrough=getInteger({data:e.lineThrough,defaultValue:0\ -,validate:e=>1===e||2===e});this.lineThroughPeriod=getStringOption(e.lineThroughPeriod,["all","word"]);this.overline=getInteger({data:e.overline,defaultValue:0,validate:e=>1===e||2===e});this.overlinePeriod=getStringOption(e.overlinePeriod,["all","word"]);this.posture=getStringOption(e.posture,["normal","italic"]);this.size=getMeasurement(e.size,"10pt");this.typeface=e.typeface||"Courier";this.underline=getInteger({data:e.underline,defaultValue:0,validate:e=>1===e||2===e});this.underlinePeriod=g\ -etStringOption(e.underlinePeriod,["all","word"]);this.use=e.use||"";this.usehref=e.usehref||"";this.weight=getStringOption(e.weight,["normal","bold"]);this.extras=null;this.fill=null}[ts](e){super[ts](e);this[Is].usedTypefaces.add(this.typeface)}[ho](){const e=toStyle(this,"fill"),t=e.color;if(t)if("#000000"===t)delete e.color;else if(!t.startsWith("#")){e.background=t;e.backgroundClip="text";e.color="transparent"}this.baselineShift&&(e.verticalAlign=measureToString(this.baselineShift));e.fontKe\ -rning="none"===this.kerningMode?"none":"normal";e.letterSpacing=measureToString(this.letterSpacing);if(0!==this.lineThrough){e.textDecoration="line-through";2===this.lineThrough&&(e.textDecorationStyle="double")}if(0!==this.overline){e.textDecoration="overline";2===this.overline&&(e.textDecorationStyle="double")}e.fontStyle=this.posture;e.fontSize=measureToString(.99*this.size);setFontFamily(this,this,this[Is].fontFinder,e);if(0!==this.underline){e.textDecoration="underline";2===this.underline&&\ -(e.textDecorationStyle="double")}e.fontWeight=this.weight;return e}}class Format extends XFAObject{constructor(e){super(Go,"format",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null}}class Handler extends StringObject{constructor(e){super(Go,"handler");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Hyphenation extends XFAObject{constructor(e){super(Go,"hyphenation"\ -);this.excludeAllCaps=getInteger({data:e.excludeAllCaps,defaultValue:0,validate:e=>1===e});this.excludeInitialCap=getInteger({data:e.excludeInitialCap,defaultValue:0,validate:e=>1===e});this.hyphenate=getInteger({data:e.hyphenate,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.pushCharacterCount=getInteger({data:e.pushCharacterCount,defaultValue:3,validate:e=>e>=0});this.remainCharacterCount=getInteger({data:e.remainCharacterCount,defaultValue:3,validate:e=>e>=0});this.use=e.use||"";thi\ -s.usehref=e.usehref||"";this.wordCharacterCount=getInteger({data:e.wordCharacterCount,defaultValue:7,validate:e=>e>=0})}}class Image extends StringObject{constructor(e){super(Go,"image");this.aspect=getStringOption(e.aspect,["fit","actual","height","none","width"]);this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.name=e.name||"";this.transferEncoding=getStringOption(e.transferEncoding,["base64","none","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[co](){i\ -f(this.contentType&&!Jo.has(this.contentType.toLowerCase()))return HTMLResult.EMPTY;let e=this[Is].images?.get(this.href);if(!e&&(this.href||!this[ss]))return HTMLResult.EMPTY;e||"base64"!==this.transferEncoding||(e=function fromBase64Util(e){return Uint8Array.fromBase64?Uint8Array.fromBase64(e):stringToBytes(atob(e))}(this[ss]));if(!e)return HTMLResult.EMPTY;if(!this.contentType){for(const[t,a]of Yo)if(e.length>t.length&&t.every(((t,a)=>t===e[a]))){this.contentType=a;break}if(!this.contentType)\ -return HTMLResult.EMPTY}const t=new Blob([e],{type:this.contentType});let a;switch(this.aspect){case"fit":case"actual":break;case"height":a={height:"100%",objectFit:"fill"};break;case"none":a={width:"100%",height:"100%",objectFit:"fill"};break;case"width":a={width:"100%",objectFit:"fill"}}const r=this[vs]();return HTMLResult.success({name:"img",attributes:{class:["xfaImage"],style:a,src:URL.createObjectURL(t),alt:r?ariaLabel(r[vs]()):null}})}}class ImageEdit extends XFAObject{constructor(e){supe\ -r(Go,"imageEdit",!0);this.data=getStringOption(e.data,["link","embed"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[co](e){return"embed"===this.data?HTMLResult.success({name:"div",children:[],attributes:{}}):HTMLResult.EMPTY}}class Integer extends ContentObject{constructor(e){super(Go,"integer");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=parseInt(this[ss].trim(),10);this\ -[ss]=isNaN(e)?null:e}[co](e){return valueToHtml(null!==this[ss]?this[ss].toString():"")}}class Issuers extends XFAObject{constructor(e){super(Go,"issuers",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Items extends XFAObject{constructor(e){super(Go,"items",!0);this.id=e.id||"";this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisi\ -ble"]);this.ref=e.ref||"";this.save=getInteger({data:e.save,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[co](){const e=[];for(const t of this[Ss]())e.push(t\ -[so]());return HTMLResult.success(e)}}class Keep extends XFAObject{constructor(e){super(Go,"keep",!0);this.id=e.id||"";const t=["none","contentArea","pageArea"];this.intact=getStringOption(e.intact,t);this.next=getStringOption(e.next,t);this.previous=getStringOption(e.previous,t);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class KeyUsage extends XFAObject{constructor(e){super(Go,"keyUsage");const t=["","yes","no"];this.crlSign=getStringOption(e.crlSign,t);this.dataEnciphermen\ -t=getStringOption(e.dataEncipherment,t);this.decipherOnly=getStringOption(e.decipherOnly,t);this.digitalSignature=getStringOption(e.digitalSignature,t);this.encipherOnly=getStringOption(e.encipherOnly,t);this.id=e.id||"";this.keyAgreement=getStringOption(e.keyAgreement,t);this.keyCertSign=getStringOption(e.keyCertSign,t);this.keyEncipherment=getStringOption(e.keyEncipherment,t);this.nonRepudiation=getStringOption(e.nonRepudiation,t);this.type=getStringOption(e.type,["optional","required"]);this.\ -use=e.use||"";this.usehref=e.usehref||""}}class Line extends XFAObject{constructor(e){super(Go,"line",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.slope=getStringOption(e.slope,["\\\\","/"]);this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null}[co](){const e=this[vs]()[vs](),t=this.edge||new Edge({}),a=t[ho](),r=Object.create(null),i="visible"===t.presence?t.thickness:0;r.strokeWidth=measureToString(i);r.stroke=a.color;let n,s,o,c,l="100%",h="100%";if\ -(e.w<=i){[n,s,o,c]=["50%",0,"50%","100%"];l=r.strokeWidth}else if(e.h<=i){[n,s,o,c]=[0,"50%","100%","50%"];h=r.strokeWidth}else"\\\\"===this.slope?[n,s,o,c]=[0,0,"100%","100%"]:[n,s,o,c]=[0,"100%","100%",0];const u={name:"svg",children:[{name:"line",attributes:{xmlns:Vo,x1:n,y1:s,x2:o,y2:c,style:r}}],attributes:{xmlns:Vo,width:l,height:h,style:{overflow:"visible"}}};if(hasMargin(e))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[u]})\ -;u.attributes.style.position="absolute";return HTMLResult.success(u)}}class Linear extends XFAObject{constructor(e){super(Go,"linear",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toRight","toBottom","toLeft","toTop"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](e){e=e?e[ho]():"#FFFFFF";return`linear-gradient(${this.type.replace(/([RBLT])/," $1").toLowerCase()}, ${e}, ${this.color?this.color[ho]():"#000000"})`}}class LockDocument extends ContentO\ -bject{constructor(e){super(Go,"lockDocument");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}[hs](){this[ss]=getStringOption(this[ss],["auto","0","1"])}}class Manifest extends XFAObject{constructor(e){super(Go,"manifest",!0);this.action=getStringOption(e.action,["include","all","exclude"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.ref=new XFAObjectArray}}class\ - Margin extends XFAObject{constructor(e){super(Go,"margin",!0);this.bottomInset=getMeasurement(e.bottomInset,"0");this.id=e.id||"";this.leftInset=getMeasurement(e.leftInset,"0");this.rightInset=getMeasurement(e.rightInset,"0");this.topInset=getMeasurement(e.topInset,"0");this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[ho](){return{margin:measureToString(this.topInset)+" "+measureToString(this.rightInset)+" "+measureToString(this.bottomInset)+" "+measureToString(this.leftInset)}}}\ -class Mdp extends XFAObject{constructor(e){super(Go,"mdp");this.id=e.id||"";this.permissions=getInteger({data:e.permissions,defaultValue:2,validate:e=>1===e||3===e});this.signatureType=getStringOption(e.signatureType,["filler","author"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Medium extends XFAObject{constructor(e){super(Go,"medium");this.id=e.id||"";this.imagingBBox=function getBBox(e){const t=-1;if(!e)return{x:t,y:t,width:t,height:t};const a=e.split(",",4).map((e=>getMeasurement(\ -e.trim(),"-1")));if(a.length<4||a[2]<0||a[3]<0)return{x:t,y:t,width:t,height:t};const[r,i,n,s]=a;return{x:r,y:i,width:n,height:s}}(e.imagingBBox);this.long=getMeasurement(e.long);this.orientation=getStringOption(e.orientation,["portrait","landscape"]);this.short=getMeasurement(e.short);this.stock=e.stock||"";this.trayIn=getStringOption(e.trayIn,["auto","delegate","pageFront"]);this.trayOut=getStringOption(e.trayOut,["auto","delegate"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Message\ - extends XFAObject{constructor(e){super(Go,"message",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.text=new XFAObjectArray}}class NumericEdit extends XFAObject{constructor(e){super(Go,"numericEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[co](e){const t=toStyle(this,"border","font","margin"),a=this[vs]()[vs]\ -(),r={name:"input",attributes:{type:"text",fieldId:a[uo],dataId:a[os]?.[uo]||a[uo],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){r.attributes["aria-required"]=!0;r.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[r]})}}class Occur extends XFAObject{constructor(e){super(Go,"occur",!0);this.id=e.id||"";this.initial=""!==e.initial?getInteger({data:e.initial,defaultValue:"",validate:e=>!0}):"";t\ -his.max=""!==e.max?getInteger({data:e.max,defaultValue:1,validate:e=>!0}):"";this.min=""!==e.min?getInteger({data:e.min,defaultValue:1,validate:e=>!0}):"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[ts](){const e=this[vs](),t=this.min;""===this.min&&(this.min=e instanceof PageArea||e instanceof PageSet?0:1);""===this.max&&(this.max=""===t?e instanceof PageArea||e instanceof PageSet?-1:1:this.min);-1!==this.max&&this.max!0});this.name=e.name||"";this.numbered=getInteger({data:e.numbered,defaultValue:1,validate:e=>!0});this.oddOrEven=getStringOption(e.oddOrEven,["any","even","odd"]);this.pagePosition=getStringOption(e.pagePosition,["any","first","last","only","rest"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.desc=null;this.extras=null;this.medium=null;this.occur=nu\ -ll;this.area=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.draw=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray}[Xs](){if(!this[ls]){this[ls]={numberOfUse:0};return!0}return!this.occur||-1===this.occur.max||this[ls].numberOfUsee.oddOrEven===t&&e.pagePosition===a));if(r)return r;r=this.pageArea.children.find((e=>"any"===e.oddOrEven&&e.pagePosition===a));if(r)return r;r=this.pageArea.children.find((e=>"any"===e.oddOrEven&&"any"===e.pagePosition));return r||this.pageArea.children[0]}}class Para extends XFAObject{constructor(e){super(Go,"para",!0);this.hAlign=getStrin\ -gOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.lineHeight=e.lineHeight?getMeasurement(e.lineHeight,"0pt"):"";this.marginLeft=e.marginLeft?getMeasurement(e.marginLeft,"0pt"):"";this.marginRight=e.marginRight?getMeasurement(e.marginRight,"0pt"):"";this.orphans=getInteger({data:e.orphans,defaultValue:0,validate:e=>e>=0});this.preserve=e.preserve||"";this.radixOffset=e.radixOffset?getMeasurement(e.radixOffset,"0pt"):"";this.spaceAbove=e.spaceAbove?ge\ -tMeasurement(e.spaceAbove,"0pt"):"";this.spaceBelow=e.spaceBelow?getMeasurement(e.spaceBelow,"0pt"):"";this.tabDefault=e.tabDefault?getMeasurement(this.tabDefault):"";this.tabStops=(e.tabStops||"").trim().split(/\\s+/).map(((e,t)=>t%2==1?getMeasurement(e):e));this.textIndent=e.textIndent?getMeasurement(e.textIndent,"0pt"):"";this.use=e.use||"";this.usehref=e.usehref||"";this.vAlign=getStringOption(e.vAlign,["top","bottom","middle"]);this.widows=getInteger({data:e.widows,defaultValue:0,validate:e=\ ->e>=0});this.hyphenation=null}[ho](){const e=toStyle(this,"hAlign");""!==this.marginLeft&&(e.paddingLeft=measureToString(this.marginLeft));""!==this.marginRight&&(e.paddingRight=measureToString(this.marginRight));""!==this.spaceAbove&&(e.paddingTop=measureToString(this.spaceAbove));""!==this.spaceBelow&&(e.paddingBottom=measureToString(this.spaceBelow));if(""!==this.textIndent){e.textIndent=measureToString(this.textIndent);fixTextIndent(e)}this.lineHeight>0&&(e.lineHeight=measureToString(this.li\ -neHeight));""!==this.tabDefault&&(e.tabSize=measureToString(this.tabDefault));this.tabStops.length;this.hyphenatation&&Object.assign(e,this.hyphenatation[ho]());return e}}class PasswordEdit extends XFAObject{constructor(e){super(Go,"passwordEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.passwordChar=e.passwordChar||"*";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}}class template_Pattern e\ -xtends XFAObject{constructor(e){super(Go,"pattern",!0);this.id=e.id||"";this.type=getStringOption(e.type,["crossHatch","crossDiagonal","diagonalLeft","diagonalRight","horizontal","vertical"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](e){e=e?e[ho]():"#FFFFFF";const t=this.color?this.color[ho]():"#000000",a="repeating-linear-gradient",r=`${e},${e} 5px,${t} 5px,${t} 10px`;switch(this.type){case"crossHatch":return`${a}(to top,${r}) ${a}(to right,${r})`;case"\ -crossDiagonal":return`${a}(45deg,${r}) ${a}(-45deg,${r})`;case"diagonalLeft":return`${a}(45deg,${r})`;case"diagonalRight":return`${a}(-45deg,${r})`;case"horizontal":return`${a}(to top,${r})`;case"vertical":return`${a}(to right,${r})`}return""}}class Picture extends StringObject{constructor(e){super(Go,"picture");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Proto extends XFAObject{constructor(e){super(Go,"proto",!0);this.appearanceFilter=new XFAObjectArray;this.arc=new XF\ -AObjectArray;this.area=new XFAObjectArray;this.assist=new XFAObjectArray;this.barcode=new XFAObjectArray;this.bindItems=new XFAObjectArray;this.bookend=new XFAObjectArray;this.boolean=new XFAObjectArray;this.border=new XFAObjectArray;this.break=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.button=new XFAObjectArray;this.calculate=new XFAObjectArray;this.caption=new XFAObjectArray;this.certificate=new XFAObjectArray;this.certificates=new XFAObjectA\ -rray;this.checkButton=new XFAObjectArray;this.choiceList=new XFAObjectArray;this.color=new XFAObjectArray;this.comb=new XFAObjectArray;this.connect=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.corner=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.dateTimeEdit=new XFAObjectArray;this.decimal=new XFAObjectArray;this.defaultUi=new XFAObjectArray;this.desc=new XFAObjectArray;this.digestMethod=new XFAObjectArray;this.digestMethods=new XFAObjectArr\ -ay;this.draw=new XFAObjectArray;this.edge=new XFAObjectArray;this.encoding=new XFAObjectArray;this.encodings=new XFAObjectArray;this.encrypt=new XFAObjectArray;this.encryptData=new XFAObjectArray;this.encryption=new XFAObjectArray;this.encryptionMethod=new XFAObjectArray;this.encryptionMethods=new XFAObjectArray;this.event=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.execute=new XFAObjectArray;this.extras=new XFAObjectA\ -rray;this.field=new XFAObjectArray;this.fill=new XFAObjectArray;this.filter=new XFAObjectArray;this.float=new XFAObjectArray;this.font=new XFAObjectArray;this.format=new XFAObjectArray;this.handler=new XFAObjectArray;this.hyphenation=new XFAObjectArray;this.image=new XFAObjectArray;this.imageEdit=new XFAObjectArray;this.integer=new XFAObjectArray;this.issuers=new XFAObjectArray;this.items=new XFAObjectArray;this.keep=new XFAObjectArray;this.keyUsage=new XFAObjectArray;this.line=new XFAObjectArra\ -y;this.linear=new XFAObjectArray;this.lockDocument=new XFAObjectArray;this.manifest=new XFAObjectArray;this.margin=new XFAObjectArray;this.mdp=new XFAObjectArray;this.medium=new XFAObjectArray;this.message=new XFAObjectArray;this.numericEdit=new XFAObjectArray;this.occur=new XFAObjectArray;this.oid=new XFAObjectArray;this.oids=new XFAObjectArray;this.overflow=new XFAObjectArray;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray;this.para=new XFAObjectArray;this.passwordEdit=new XFA\ -ObjectArray;this.pattern=new XFAObjectArray;this.picture=new XFAObjectArray;this.radial=new XFAObjectArray;this.reason=new XFAObjectArray;this.reasons=new XFAObjectArray;this.rectangle=new XFAObjectArray;this.ref=new XFAObjectArray;this.script=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.signData=new XFAObjectArray;this.signature=new XFAObjectArray;this.signing=new XFAObjectArray;this.solid=new XFAObjectArray;this.speak=new XFAObjectArray;this.stipple=new XFAObjectArray;this.subfo\ -rm=new XFAObjectArray;this.subformSet=new XFAObjectArray;this.subjectDN=new XFAObjectArray;this.subjectDNs=new XFAObjectArray;this.submit=new XFAObjectArray;this.text=new XFAObjectArray;this.textEdit=new XFAObjectArray;this.time=new XFAObjectArray;this.timeStamp=new XFAObjectArray;this.toolTip=new XFAObjectArray;this.traversal=new XFAObjectArray;this.traverse=new XFAObjectArray;this.ui=new XFAObjectArray;this.validate=new XFAObjectArray;this.value=new XFAObjectArray;this.variables=new XFAObjectA\ -rray}}class Radial extends XFAObject{constructor(e){super(Go,"radial",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toEdge","toCenter"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](e){e=e?e[ho]():"#FFFFFF";const t=this.color?this.color[ho]():"#000000";return`radial-gradient(circle at center, ${"toEdge"===this.type?`${e},${t}`:`${t},${e}`})`}}class Reason extends StringObject{constructor(e){super(Go,"reason");this.id=e.id||"";this.name=e.name||"";\ -this.use=e.use||"";this.usehref=e.usehref||""}}class Reasons extends XFAObject{constructor(e){super(Go,"reasons",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.reason=new XFAObjectArray}}class Rectangle extends XFAObject{constructor(e){super(Go,"rectangle",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);t\ -his.edge=new XFAObjectArray(4);this.fill=null}[co](){const e=this.edge.children.length?this.edge.children[0]:new Edge({}),t=e[ho](),a=Object.create(null);"visible"===this.fill?.presence?Object.assign(a,this.fill[ho]()):a.fill="transparent";a.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);a.stroke=t.color;const r=(this.corner.children.length?this.corner.children[0]:new Corner({}))[ho](),i={name:"svg",children:[{name:"rect",attributes:{xmlns:Vo,width:"100%",height:"100%",x:0,y:0\ -,rx:r.radius,ry:r.radius,style:a}}],attributes:{xmlns:Vo,style:{overflow:"visible"},width:"100%",height:"100%"}};if(hasMargin(this[vs]()[vs]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[i]});i.attributes.style.position="absolute";return HTMLResult.success(i)}}class RefElement extends StringObject{constructor(e){super(Go,"ref");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Script extends StringObject{co\ -nstructor(e){super(Go,"script");this.binding=e.binding||"";this.contentType=e.contentType||"";this.id=e.id||"";this.name=e.name||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class SetProperty extends XFAObject{constructor(e){super(Go,"setProperty");this.connection=e.connection||"";this.ref=e.ref||"";this.target=e.target||""}}class SignData extends XFAObject{constructor(e){super(Go,"signData",!0);this.id=e.id||"";this.operation=g\ -etStringOption(e.operation,["sign","clear","verify"]);this.ref=e.ref||"";this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Signature extends XFAObject{constructor(e){super(Go,"signature",!0);this.id=e.id||"";this.type=getStringOption(e.type,["PDF1.3","PDF1.6"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.filter=null;this.manifest=null;this.margin=null}}class Signing extends XFAObject{construc\ -tor(e){super(Go,"signing",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Solid extends XFAObject{constructor(e){super(Go,"solid",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[ho](e){return e?e[ho]():"#FFFFFF"}}class Speak extends StringObject{constructor(e){super(Go,"speak");this.disable=getInteger({data:e.disable,defaultValue:0,validate:\ -e=>1===e});this.id=e.id||"";this.priority=getStringOption(e.priority,["custom","caption","name","toolTip"]);this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Stipple extends XFAObject{constructor(e){super(Go,"stipple",!0);this.id=e.id||"";this.rate=getInteger({data:e.rate,defaultValue:50,validate:e=>e>=0&&e<=100});this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[ho](e){const t=this.rate/100;return Util.makeHexColor(Math.round(e.value.r*(1-t)+t\ -his.value.r*t),Math.round(e.value.g*(1-t)+this.value.g*t),Math.round(e.value.b*(1-t)+this.value.b*t))}}class Subform extends XFAObject{constructor(e){super(Go,"subform",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.allowMacro=getInteger({data:e.allowMacro,defaultValue:0,validate:e=>1===e});this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRig\ -ht"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.columnWidths=(e.columnWidths||"").trim().split(/\\s+/).map((e=>"-1"===e?-1:getMeasurement(e)));this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.max\ -W=getMeasurement(e.maxW,"0pt");this.mergeMode=getStringOption(e.mergeMode,["consumeData","matchTemplate"]);this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.restoreState=getStringOption(e.restoreState,["manual","auto"]);this.scope=getStringOption(e.scope,["name","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this\ -.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.bookend=null;this.border=null;this.break=null;this.calculate=null;this.desc=null;this.extras=null;this.keep=null;this.margin=null;this.occur=null;this.overflow=null;this.pageSet=null;this.para=null;this.traversal=null;this.validate=null;this.variables=null;this.area=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.conne\ -ct=new XFAObjectArray;this.draw=new XFAObjectArray;this.event=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.proto=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}[Cs](){const e=this[vs]();return e instanceof SubformSet?e[Cs]():e}[Rs](){return!0}[_s](){return this.layout.endsWith("-tb")&&0===this[ls].attempt&&this[ls].numberInLine>0||this[vs]()[_s]()\ -}*[As](){yield*getContainedChildren(this)}[us](){return flushHTML(this)}[Zn](e,t){addHTML(this,e,t)}[gs](){return getAvailableSpace(this)}[js](){const e=this[Cs]();if(!e[js]())return!1;if(void 0!==this[ls]._isSplittable)return this[ls]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[ls]._isSplittable=!1;return!1}if(this.keep&&"none"!==this.keep.intact){this[ls]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[ls].numberInLine)return!1;this[ls]._isSplitta\ -ble=!0;return!0}[co](e){setTabIndex(this);if(this.break){if("auto"!==this.break.after||""!==this.break.afterTarget){const e=new BreakAfter({targetType:this.break.after,target:this.break.afterTarget,startNew:this.break.startNew.toString()});e[Is]=this[Is];this[Qn](e);this.breakAfter.push(e)}if("auto"!==this.break.before||""!==this.break.beforeTarget){const e=new BreakBefore({targetType:this.break.before,target:this.break.beforeTarget,startNew:this.break.startNew.toString()});e[Is]=this[Is];this[Q\ -n](e);this.breakBefore.push(e)}if(""!==this.break.overflowTarget){const e=new Overflow({target:this.break.overflowTarget,leader:this.break.overflowLeader,trailer:this.break.overflowTrailer});e[Is]=this[Is];this[Qn](e);this.overflow.push(e)}this[Zs](this.break);this.break=null}if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;(this.breakBefore.children.length>1||this.breakAfter.children.length>1)&&warn("XFA - Several breakBefore or breakAfter in subforms: please file\ - a bug.");if(this.breakBefore.children.length>=1){const e=this.breakBefore.children[0];if(handleBreak(e))return HTMLResult.breakNode(e)}if(this[ls]?.afterBreakAfter)return HTMLResult.EMPTY;fixDimensions(this);const t=[],a={id:this[uo],class:[]};setAccess(this,a.class);this[ls]||=Object.create(null);Object.assign(this[ls],{children:t,line:null,attributes:a,attempt:0,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeigh\ -t:0,currentWidth:0});const r=this[Fs](),i=r[ls].noLayoutFailure,n=this[js]();n||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const s=new Set(["area","draw","exclGroup","field","subform","subformSet"]);if(this.layout.includes("row")){const e=this[Cs]().columnWidths;if(Array.isArray(e)&&e.length>0){this[ls].columnWidths=e;this[ls].currentColumn=0}}const o=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),c=["xfaSubform"],l=\ -layoutClass(this);l&&c.push(l);a.style=o;a.class=c;this.name&&(a.xfaName=this.name);if(this.overflow){const t=this.overflow[ws]();if(t.addLeader){t.addLeader=!1;handleOverflow(this,t.leader,e)}}this[Ys]();const h="lr-tb"===this.layout||"rl-tb"===this.layout,u=h?2:1;for(;this[ls].attempt=1){const e=this.breakAfter.children[0];if(handleBreak(e)){this[ls].afterBreakAfter=y;r\ -eturn HTMLResult.breakNode(e)}}delete this[ls];return y}}class SubformSet extends XFAObject{constructor(e){super(Go,"subformSet",!0);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["ordered","choice","unordered"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.bookend=null;this.break=null;this.desc=null;this.extras=null;this.occur=null;this.overflow=null;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArra\ -y;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[As](){yield*getContainedChildren(this)}[Cs](){let e=this[vs]();for(;!(e instanceof Subform);)e=e[vs]();return e}[Rs](){return!0}}class SubjectDN extends ContentObject{constructor(e){super(Go,"subjectDN");this.delimiter=e.delimiter||",";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){this[ss]=new Map(this[ss].split(this.delimiter).map((e=>{(e=e.split("=",2))[0]=e[0].trim();return e}))\ -)}}class SubjectDNs extends XFAObject{constructor(e){super(Go,"subjectDNs",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.subjectDN=new XFAObjectArray}}class Submit extends XFAObject{constructor(e){super(Go,"submit",!0);this.embedPDF=getInteger({data:e.embedPDF,defaultValue:0,validate:e=>1===e});this.format=getStringOption(e.format,["xdp","formdata","pdf","urlencoded","xfd","xml"]);this.id=e.id||"";this.target=e.t\ -arget||"";this.textEncoding=getKeyword({data:e.textEncoding?e.textEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\\d{2}/)});this.use=e.use||"";this.usehref=e.usehref||"";this.xdpContent=e.xdpContent||"";this.encrypt=null;this.encryptData=new XFAObjectArray;this.signData=new XFAObjectArray}}class Template extends XFAObject{constructor(e){super(Go,"temp\ -late",!0);this.baseProfile=getStringOption(e.baseProfile,["full","interactiveForms"]);this.extras=null;this.subform=new XFAObjectArray}[hs](){0===this.subform.children.length&&warn("XFA - No subforms in template node.");this.subform.children.length>=2&&warn("XFA - Several subforms in template node: please file a bug.");this[no]=5e3}[js](){return!0}[to](e,t){return e.startsWith("#")?[this[Os].get(e.slice(1))]:searchNode(this,t,e,!0,!0)}*[oo](){if(!this.subform.children.length)return HTMLResult.su\ -ccess({name:"div",children:[]});this[ls]={overflowNode:null,firstUnsplittable:null,currentContentArea:null,currentPageArea:null,noLayoutFailure:!1,pageNumber:1,pagePosition:"first",oddOrEven:"odd",blankOrNotBlank:"nonBlank",paraStack:[]};const e=this.subform.children[0];e.pageSet[as]();const t=e.pageSet.pageArea.children,a={name:"div",children:[]};let r=null,i=null,n=null;if(e.breakBefore.children.length>=1){i=e.breakBefore.children[0];n=i.target}else if(e.subform.children.length>=1&&e.subform.c\ -hildren[0].breakBefore.children.length>=1){i=e.subform.children[0].breakBefore.children[0];n=i.target}else if(e.break?.beforeTarget){i=e.break;n=i.beforeTarget}else if(e.subform.children.length>=1&&e.subform.children[0].break?.beforeTarget){i=e.subform.children[0].break;n=i.beforeTarget}if(i){const e=this[to](n,i[vs]());if(e instanceof PageArea){r=e;i[ls]={}}}r||=t[0];r[ls]={numberOfUse:1};const s=r[vs]();s[ls]={numberOfUse:1,pageIndex:s.pageArea.children.indexOf(r),pageSetIndex:0};let o,c=null,\ -l=null,h=!0,u=0,d=0;for(;;){if(h)u=0;else{a.children.pop();if(3==++u){warn("XFA - Something goes wrong: please file a bug.");return a}}o=null;this[ls].currentPageArea=r;const t=r[co]().html;a.children.push(t);if(c){this[ls].noLayoutFailure=!0;t.children.push(c[co](r[ls].space).html);c=null}if(l){this[ls].noLayoutFailure=!0;t.children.push(l[co](r[ls].space).html);l=null}const i=r.contentArea.children,n=t.children.filter((e=>e.attributes.class.includes("xfaContentarea")));h=!1;this[ls].firstUnspl\ -ittable=null;this[ls].noLayoutFailure=!1;const flush=t=>{const a=e[us]();if(a){h||=a.children?.length>0;n[t].children.push(a)}};for(let t=d,r=i.length;t0;n[t].children.push(u.html)}else!h&&a.children.length>1&&a.children.pop();return a}if(u.isBreak()){const \ -e=u.breakNode;flush(t);if("auto"===e.targetType)continue;if(e.leader){c=this[to](e.leader,e[vs]());c=c?c[0]:null}if(e.trailer){l=this[to](e.trailer,e[vs]());l=l?l[0]:null}if("pageArea"===e.targetType){o=e[ls].target;t=1/0}else if(e[ls].target){o=e[ls].target;d=e[ls].index+1;t=1/0}else t=e[ls].index}else if(this[ls].overflowNode){const e=this[ls].overflowNode;this[ls].overflowNode=null;const a=e[ws](),r=a.target;a.addLeader=null!==a.leader;a.addTrailer=null!==a.trailer;flush(t);const n=t;t=1/0;if\ -(r instanceof PageArea)o=r;else if(r instanceof ContentArea){const e=i.indexOf(r);if(-1!==e)e>n?t=e-1:d=e;else{o=r[vs]();d=o.contentArea.children.indexOf(r)}}}else flush(t)}this[ls].pageNumber+=1;o&&(o[Xs]()?o[ls].numberOfUse+=1:o=null);r=o||r[ks]();yield null}}}class Text extends ContentObject{constructor(e){super(Go,"text");this.id=e.id||"";this.maxChars=getInteger({data:e.maxChars,defaultValue:0,validate:e=>e>=0});this.name=e.name||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehr\ -ef||""}[Yn](){return!0}[$s](e){if(e[Hs]===go.xhtml.id){this[ss]=e;return!0}warn(`XFA - Invalid content in Text: ${e[Ws]}.`);return!1}[Vs](e){this[ss]instanceof XFAObject||super[Vs](e)}[hs](){"string"==typeof this[ss]&&(this[ss]=this[ss].replaceAll("\\r\\n","\\n"))}[ws](){return"string"==typeof this[ss]?this[ss].split(/[\\u2029\\u2028\\n]/).filter((e=>!!e)).join("\\n"):this[ss][so]()}[co](e){if("string"==typeof this[ss]){const e=valueToHtml(this[ss]).html;if(this[ss].includes("\\u2029")){e.name="div";e.c\ -hildren=[];this[ss].split("\\u2029").map((e=>e.split(/[\\u2028\\n]/).flatMap((e=>[{name:"span",value:e},{name:"br"}])))).forEach((t=>{e.children.push({name:"p",children:t})}))}else if(/[\\u2028\\n]/.test(this[ss])){e.name="div";e.children=[];this[ss].split(/[\\u2028\\n]/).forEach((t=>{e.children.push({name:"span",value:t},{name:"br"})}))}return HTMLResult.success(e)}return this[ss][co](e)}}class TextEdit extends XFAObject{constructor(e){super(Go,"textEdit",!0);this.allowRichText=getInteger({data:e.allo\ -wRichText,defaultValue:0,validate:e=>1===e});this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.multiLine=getInteger({data:e.multiLine,defaultValue:"",validate:e=>0===e||1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.vScrollPolicy=getStringOption(e.vScrollPolicy,["auto","off","on"]);this.border=null;this.comb=null;this.extras=null;this.margin=null}[co](e){const t=toStyle(this,"border","font","margin");let a;const r=this[vs]()[vs]();""===this\ -.multiLine&&(this.multiLine=r instanceof Draw?1:0);a=1===this.multiLine?{name:"textarea",attributes:{dataId:r[os]?.[uo]||r[uo],fieldId:r[uo],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(r),"aria-required":!1}}:{name:"input",attributes:{type:"text",dataId:r[os]?.[uo]||r[uo],fieldId:r[uo],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(r),"aria-required":!1}};if(isRequired(r)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attribute\ -s:{class:["xfaLabel"]},children:[a]})}}class Time extends StringObject{constructor(e){super(Go,"time");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[hs](){const e=this[ss].trim();this[ss]=e?new Date(e):null}[co](e){return valueToHtml(this[ss]?this[ss].toString():"")}}class TimeStamp extends XFAObject{constructor(e){super(Go,"timeStamp");this.id=e.id||"";this.server=e.server||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";thi\ -s.usehref=e.usehref||""}}class ToolTip extends StringObject{constructor(e){super(Go,"toolTip");this.id=e.id||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Traversal extends XFAObject{constructor(e){super(Go,"traversal",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.traverse=new XFAObjectArray}}class Traverse extends XFAObject{constructor(e){super(Go,"traverse",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["\ -next","back","down","first","left","right","up"]);this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.script=null}get name(){return this.operation}[Us](){return!1}}class Ui extends XFAObject{constructor(e){super(Go,"ui",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null;this.barcode=null;this.button=null;this.checkButton=null;this.choiceList=null;this.dateTimeEdit=null;this.defaultUi=null;this.imageEdit=null;th\ -is.numericEdit=null;this.passwordEdit=null;this.signature=null;this.textEdit=null}[ws](){if(void 0===this[ls]){for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"picture"===e)continue;const t=this[e];if(t instanceof XFAObject){this[ls]=t;return t}}this[ls]=null}return this[ls]}[co](e){const t=this[ws]();return t?t[co](e):HTMLResult.EMPTY}}class Validate extends XFAObject{constructor(e){super(Go,"validate",!0);this.formatTest=getStringOption(e.formatTest,["warning","disabled","err\ -or"]);this.id=e.id||"";this.nullTest=getStringOption(e.nullTest,["disabled","error","warning"]);this.scriptTest=getStringOption(e.scriptTest,["error","disabled","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.picture=null;this.script=null}}class Value extends XFAObject{constructor(e){super(Go,"value",!0);this.id=e.id||"";this.override=getInteger({data:e.override,defaultValue:0,validate:e=>1===e});this.relevant=getRelevant(e.relevant);this.use=e.u\ -se||"";this.usehref=e.usehref||"";this.arc=null;this.boolean=null;this.date=null;this.dateTime=null;this.decimal=null;this.exData=null;this.float=null;this.image=null;this.integer=null;this.line=null;this.rectangle=null;this.text=null;this.time=null}[io](e){const t=this[vs]();if(t instanceof Field&&t.ui?.imageEdit){if(!this.image){this.image=new Image({});this[Qn](this.image)}this.image[ss]=e[ss];return}const a=e[Ws];if(null===this[a]){for(const e of Object.getOwnPropertyNames(this)){const t=thi\ -s[e];if(t instanceof XFAObject){this[e]=null;this[Zs](t)}}this[e[Ws]]=e;this[Qn](e)}else this[a][ss]=e[ss]}[so](){if(this.exData)return"string"==typeof this.exData[ss]?this.exData[ss].trim():this.exData[ss][so]().trim();for(const e of Object.getOwnPropertyNames(this)){if("image"===e)continue;const t=this[e];if(t instanceof XFAObject)return(t[ss]||"").toString().trim()}return null}[co](e){for(const t of Object.getOwnPropertyNames(this)){const a=this[t];if(a instanceof XFAObject)return a[co](e)}re\ -turn HTMLResult.EMPTY}}class Variables extends XFAObject{constructor(e){super(Go,"variables",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.manifest=new XFAObjectArray;this.script=new XFAObjectArray;this.text=new XFAObjectArra\ -y;this.time=new XFAObjectArray}[Us](){return!0}}class TemplateNamespace{static[fo](e,t){if(TemplateNamespace.hasOwnProperty(e)){const a=TemplateNamespace[e](t);a[ro](t);return a}}static appearanceFilter(e){return new AppearanceFilter(e)}static arc(e){return new Arc(e)}static area(e){return new Area(e)}static assist(e){return new Assist(e)}static barcode(e){return new Barcode(e)}static bind(e){return new Bind(e)}static bindItems(e){return new BindItems(e)}static bookend(e){return new Bookend(e)}s\ -tatic boolean(e){return new BooleanElement(e)}static border(e){return new Border(e)}static break(e){return new Break(e)}static breakAfter(e){return new BreakAfter(e)}static breakBefore(e){return new BreakBefore(e)}static button(e){return new Button(e)}static calculate(e){return new Calculate(e)}static caption(e){return new Caption(e)}static certificate(e){return new Certificate(e)}static certificates(e){return new Certificates(e)}static checkButton(e){return new CheckButton(e)}static choiceList(\ -e){return new ChoiceList(e)}static color(e){return new Color(e)}static comb(e){return new Comb(e)}static connect(e){return new Connect(e)}static contentArea(e){return new ContentArea(e)}static corner(e){return new Corner(e)}static date(e){return new DateElement(e)}static dateTime(e){return new DateTime(e)}static dateTimeEdit(e){return new DateTimeEdit(e)}static decimal(e){return new Decimal(e)}static defaultUi(e){return new DefaultUi(e)}static desc(e){return new Desc(e)}static digestMethod(e){re\ -turn new DigestMethod(e)}static digestMethods(e){return new DigestMethods(e)}static draw(e){return new Draw(e)}static edge(e){return new Edge(e)}static encoding(e){return new Encoding(e)}static encodings(e){return new Encodings(e)}static encrypt(e){return new Encrypt(e)}static encryptData(e){return new EncryptData(e)}static encryption(e){return new Encryption(e)}static encryptionMethod(e){return new EncryptionMethod(e)}static encryptionMethods(e){return new EncryptionMethods(e)}static event(e){r\ -eturn new Event(e)}static exData(e){return new ExData(e)}static exObject(e){return new ExObject(e)}static exclGroup(e){return new ExclGroup(e)}static execute(e){return new Execute(e)}static extras(e){return new Extras(e)}static field(e){return new Field(e)}static fill(e){return new Fill(e)}static filter(e){return new Filter(e)}static float(e){return new Float(e)}static font(e){return new template_Font(e)}static format(e){return new Format(e)}static handler(e){return new Handler(e)}static hyphena\ -tion(e){return new Hyphenation(e)}static image(e){return new Image(e)}static imageEdit(e){return new ImageEdit(e)}static integer(e){return new Integer(e)}static issuers(e){return new Issuers(e)}static items(e){return new Items(e)}static keep(e){return new Keep(e)}static keyUsage(e){return new KeyUsage(e)}static line(e){return new Line(e)}static linear(e){return new Linear(e)}static lockDocument(e){return new LockDocument(e)}static manifest(e){return new Manifest(e)}static margin(e){return new Ma\ -rgin(e)}static mdp(e){return new Mdp(e)}static medium(e){return new Medium(e)}static message(e){return new Message(e)}static numericEdit(e){return new NumericEdit(e)}static occur(e){return new Occur(e)}static oid(e){return new Oid(e)}static oids(e){return new Oids(e)}static overflow(e){return new Overflow(e)}static pageArea(e){return new PageArea(e)}static pageSet(e){return new PageSet(e)}static para(e){return new Para(e)}static passwordEdit(e){return new PasswordEdit(e)}static pattern(e){return\ - new template_Pattern(e)}static picture(e){return new Picture(e)}static proto(e){return new Proto(e)}static radial(e){return new Radial(e)}static reason(e){return new Reason(e)}static reasons(e){return new Reasons(e)}static rectangle(e){return new Rectangle(e)}static ref(e){return new RefElement(e)}static script(e){return new Script(e)}static setProperty(e){return new SetProperty(e)}static signData(e){return new SignData(e)}static signature(e){return new Signature(e)}static signing(e){return new\ - Signing(e)}static solid(e){return new Solid(e)}static speak(e){return new Speak(e)}static stipple(e){return new Stipple(e)}static subform(e){return new Subform(e)}static subformSet(e){return new SubformSet(e)}static subjectDN(e){return new SubjectDN(e)}static subjectDNs(e){return new SubjectDNs(e)}static submit(e){return new Submit(e)}static template(e){return new Template(e)}static text(e){return new Text(e)}static textEdit(e){return new TextEdit(e)}static time(e){return new Time(e)}static tim\ -eStamp(e){return new TimeStamp(e)}static toolTip(e){return new ToolTip(e)}static traversal(e){return new Traversal(e)}static traverse(e){return new Traverse(e)}static ui(e){return new Ui(e)}static validate(e){return new Validate(e)}static value(e){return new Value(e)}static variables(e){return new Variables(e)}}const Zo=go.datasets.id;function createText(e){const t=new Text({});t[ss]=e;return t}class Binder{constructor(e){this.root=e;this.datasets=e.datasets;this.data=e.datasets?.data||new XmlOb\ -ject(go.datasets.id,"data");this.emptyMerge=0===this.data[Ss]().length;this.root.form=this.form=e.template[is]()}_isConsumeData(){return!this.emptyMerge&&this._mergeMode}_isMatchTemplate(){return!this._isConsumeData()}bind(){this._bindElement(this.form,this.data);return this.form}getData(){return this.data}_bindValue(e,t,a){e[os]=t;if(e[Ts]())if(t[Ns]()){const a=t[ys]();e[io](createText(a))}else if(e instanceof Field&&"multiSelect"===e.ui?.choiceList?.open){const a=t[Ss]().map((e=>e[ss].trim()))\ -.join("\\n");e[io](createText(a))}else this._isConsumeData()&&warn("XFA - Nodes haven\'t the same type.");else!t[Ns]()||this._isMatchTemplate()?this._bindElement(e,t):warn("XFA - Nodes haven\'t the same type.")}_findDataByNameToConsume(e,t,a,r){if(!e)return null;let i,n;for(let r=0;r<3;r++){i=a[xs](e,!1,!0);for(;;){n=i.next().value;if(!n)break;if(t===n[Ns]())return n}if(a[Hs]===go.datasets.id&&"data"===a[Ws])break;a=a[vs]()}if(!r)return null;i=this.data[xs](e,!0,!1);n=i.next().value;if(n)return n;i\ -=this.data[ds](e,!0);n=i.next().value;return n?.[Ns]()?n:null}_setProperties(e,t){if(e.hasOwnProperty("setProperty"))for(const{ref:a,target:r,connection:i}of e.setProperty.children){if(i)continue;if(!a)continue;const n=searchNode(this.root,t,a,!1,!1);if(!n){warn(`XFA - Invalid reference: ${a}.`);continue}const[s]=n;if(!s[Es](this.data)){warn("XFA - Invalid node: must be a data node.");continue}const o=searchNode(this.root,e,r,!1,!1);if(!o){warn(`XFA - Invalid target: ${r}.`);continue}const[c]=o;\ -if(!c[Es](e)){warn("XFA - Invalid target: must be a property or subproperty.");continue}const l=c[vs]();if(c instanceof SetProperty||l instanceof SetProperty){warn("XFA - Invalid target: cannot be a setProperty or one of its properties.");continue}if(c instanceof BindItems||l instanceof BindItems){warn("XFA - Invalid target: cannot be a bindItems or one of its properties.");continue}const h=s[so](),u=c[Ws];if(c instanceof XFAAttribute){const e=Object.create(null);e[u]=h;const t=Reflect.construct\ -(Object.getPrototypeOf(l).constructor,[e]);l[u]=t[u]}else if(c.hasOwnProperty(ss)){c[os]=s;c[ss]=h;c[hs]()}else warn("XFA - Invalid node to use in setProperty")}}_bindItems(e,t){if(!e.hasOwnProperty("items")||!e.hasOwnProperty("bindItems")||e.bindItems.isEmpty())return;for(const t of e.items.children)e[Zs](t);e.items.clear();const a=new Items({}),r=new Items({});e[Qn](a);e.items.push(a);e[Qn](r);e.items.push(r);for(const{ref:i,labelRef:n,valueRef:s,connection:o}of e.bindItems.children){if(o)cont\ -inue;if(!i)continue;const e=searchNode(this.root,t,i,!1,!1);if(e)for(const t of e){if(!t[Es](this.datasets)){warn(`XFA - Invalid ref (${i}): must be a datasets child.`);continue}const e=searchNode(this.root,t,n,!0,!1);if(!e){warn(`XFA - Invalid label: ${n}.`);continue}const[o]=e;if(!o[Es](this.datasets)){warn("XFA - Invalid label: must be a datasets child.");continue}const c=searchNode(this.root,t,s,!0,!1);if(!c){warn(`XFA - Invalid value: ${s}.`);continue}const[l]=c;if(!l[Es](this.datasets)){wa\ -rn("XFA - Invalid value: must be a datasets child.");continue}const h=createText(o[so]()),u=createText(l[so]());a[Qn](h);a.text.push(h);r[Qn](u);r.text.push(u)}else warn(`XFA - Invalid reference: ${i}.`)}}_bindOccurrences(e,t,a){let r;if(t.length>1){r=e[is]();r[Zs](r.occur);r.occur=null}this._bindValue(e,t[0],a);this._setProperties(e,t[0]);this._bindItems(e,t[0]);if(1===t.length)return;const i=e[vs](),n=e[Ws],s=i[Ms](e);for(let e=1,o=t.length;et.name===e.name)).length:a[r].children.length;const n=a[Ms](e)+1,s=t.initial-i;if(s){const t=e[is]();t[Zs](t.occur);t.occur=null;a[r].push(t);a[Ds](n,t);for(let e=1;e0)this._bindOccurrences(r,[e[0]],null);else if(this.emptyMerge){const e=t[Hs]===Zo?-1:t[Hs],a=r\ -[os]=new XmlObject(e,r.name||"root");t[Qn](a);this._bindElement(r,a)}continue}if(!r[Rs]())continue;let e=!1,i=null,n=null,s=null;if(r.bind){switch(r.bind.match){case"none":this._setAndBind(r,t);continue;case"global":e=!0;break;case"dataRef":if(!r.bind.ref){warn(`XFA - ref is empty in node ${r[Ws]}.`);this._setAndBind(r,t);continue}n=r.bind.ref}r.bind.picture&&(i=r.bind.picture[ss])}const[o,c]=this._getOccurInfo(r);if(n){s=searchNode(this.root,t,n,!0,!1);if(null===s){s=createDataNode(this.data,t,\ -n);if(!s)continue;this._isConsumeData()&&(s[ns]=!0);this._setAndBind(r,s);continue}this._isConsumeData()&&(s=s.filter((e=>!e[ns])));s.length>c?s=s.slice(0,c):0===s.length&&(s=null);s&&this._isConsumeData()&&s.forEach((e=>{e[ns]=!0}))}else{if(!r.name){this._setAndBind(r,t);continue}if(this._isConsumeData()){const a=[];for(;a.length0?a:null}else{s=t[xs](r.name,!1,this.emptyMerge).next().value;i\ -f(!s){if(0===o){a.push(r);continue}const e=t[Hs]===Zo?-1:t[Hs];s=r[os]=new XmlObject(e,r.name);this.emptyMerge&&(s[ns]=!0);t[Qn](s);this._setAndBind(r,s);continue}this.emptyMerge&&(s[ns]=!0);s=[s]}}s?this._bindOccurrences(r,s,i):o>0?this._setAndBind(r,t):a.push(r)}a.forEach((e=>e[vs]()[Zs](e)))}}class DataHandler{constructor(e,t){this.data=t;this.dataset=e.datasets||null}serialize(e){const t=[[-1,this.data[Ss]()]];for(;t.length>0;){const a=t.at(-1),[r,i]=a;if(r+1===i.length){t.pop();continue}con\ -st n=i[++a[0]],s=e.get(n[uo]);if(s)n[io](s);else{const t=n[fs]();for(const a of t.values()){const t=e.get(a[uo]);if(t){a[io](t);break}}}const o=n[Ss]();o.length>0&&t.push([-1,o])}const a=[\'\'];if(this.dataset)for(const e of this.dataset[Ss]())"data"!==e[Ws]&&e[lo](a);this.data[lo](a);a.push("");return a.join("")}}const Qo=go.config.id;class Acrobat extends XFAObject{constructor(e){super(Qo,"acrobat",!0);this.acrobat7\ -=null;this.autoSave=null;this.common=null;this.validate=null;this.validateApprovalSignatures=null;this.submitUrl=new XFAObjectArray}}class Acrobat7 extends XFAObject{constructor(e){super(Qo,"acrobat7",!0);this.dynamicRender=null}}class ADBE_JSConsole extends OptionObject{constructor(e){super(Qo,"ADBE_JSConsole",["delegate","Enable","Disable"])}}class ADBE_JSDebugger extends OptionObject{constructor(e){super(Qo,"ADBE_JSDebugger",["delegate","Enable","Disable"])}}class AddSilentPrint extends Optio\ -n01{constructor(e){super(Qo,"addSilentPrint")}}class AddViewerPreferences extends Option01{constructor(e){super(Qo,"addViewerPreferences")}}class AdjustData extends Option10{constructor(e){super(Qo,"adjustData")}}class AdobeExtensionLevel extends IntegerObject{constructor(e){super(Qo,"adobeExtensionLevel",0,(e=>e>=1&&e<=8))}}class Agent extends XFAObject{constructor(e){super(Qo,"agent",!0);this.name=e.name?e.name.trim():"";this.common=new XFAObjectArray}}class AlwaysEmbed extends ContentObject{c\ -onstructor(e){super(Qo,"alwaysEmbed")}}class Amd extends StringObject{constructor(e){super(Qo,"amd")}}class config_Area extends XFAObject{constructor(e){super(Qo,"area");this.level=getInteger({data:e.level,defaultValue:0,validate:e=>e>=1&&e<=3});this.name=getStringOption(e.name,["","barcode","coreinit","deviceDriver","font","general","layout","merge","script","signature","sourceSet","templateCache"])}}class Attributes extends OptionObject{constructor(e){super(Qo,"attributes",["preserve","delegat\ -e","ignore"])}}class AutoSave extends OptionObject{constructor(e){super(Qo,"autoSave",["disabled","enabled"])}}class Base extends StringObject{constructor(e){super(Qo,"base")}}class BatchOutput extends XFAObject{constructor(e){super(Qo,"batchOutput");this.format=getStringOption(e.format,["none","concat","zip","zipCompress"])}}class BehaviorOverride extends ContentObject{constructor(e){super(Qo,"behaviorOverride")}[hs](){this[ss]=new Map(this[ss].trim().split(/\\s+/).filter((e=>e.includes(":"))).m\ -ap((e=>e.split(":",2))))}}class Cache extends XFAObject{constructor(e){super(Qo,"cache",!0);this.templateCache=null}}class Change extends Option01{constructor(e){super(Qo,"change")}}class Common extends XFAObject{constructor(e){super(Qo,"common",!0);this.data=null;this.locale=null;this.localeSet=null;this.messaging=null;this.suppressBanner=null;this.template=null;this.validationMessaging=null;this.versionControl=null;this.log=new XFAObjectArray}}class Compress extends XFAObject{constructor(e){su\ -per(Qo,"compress");this.scope=getStringOption(e.scope,["imageOnly","document"])}}class CompressLogicalStructure extends Option01{constructor(e){super(Qo,"compressLogicalStructure")}}class CompressObjectStream extends Option10{constructor(e){super(Qo,"compressObjectStream")}}class Compression extends XFAObject{constructor(e){super(Qo,"compression",!0);this.compressLogicalStructure=null;this.compressObjectStream=null;this.level=null;this.type=null}}class Config extends XFAObject{constructor(e){sup\ -er(Qo,"config",!0);this.acrobat=null;this.present=null;this.trace=null;this.agent=new XFAObjectArray}}class Conformance extends OptionObject{constructor(e){super(Qo,"conformance",["A","B"])}}class ContentCopy extends Option01{constructor(e){super(Qo,"contentCopy")}}class Copies extends IntegerObject{constructor(e){super(Qo,"copies",1,(e=>e>=1))}}class Creator extends StringObject{constructor(e){super(Qo,"creator")}}class CurrentPage extends IntegerObject{constructor(e){super(Qo,"currentPage",0,(\ -e=>e>=0))}}class Data extends XFAObject{constructor(e){super(Qo,"data",!0);this.adjustData=null;this.attributes=null;this.incrementalLoad=null;this.outputXSL=null;this.range=null;this.record=null;this.startNode=null;this.uri=null;this.window=null;this.xsl=null;this.excludeNS=new XFAObjectArray;this.transform=new XFAObjectArray}}class Debug extends XFAObject{constructor(e){super(Qo,"debug",!0);this.uri=null}}class DefaultTypeface extends ContentObject{constructor(e){super(Qo,"defaultTypeface");th\ -is.writingScript=getStringOption(e.writingScript,["*","Arabic","Cyrillic","EastEuropeanRoman","Greek","Hebrew","Japanese","Korean","Roman","SimplifiedChinese","Thai","TraditionalChinese","Vietnamese"])}}class Destination extends OptionObject{constructor(e){super(Qo,"destination",["pdf","pcl","ps","webClient","zpl"])}}class DocumentAssembly extends Option01{constructor(e){super(Qo,"documentAssembly")}}class Driver extends XFAObject{constructor(e){super(Qo,"driver",!0);this.name=e.name?e.name.trim\ -():"";this.fontInfo=null;this.xdc=null}}class DuplexOption extends OptionObject{constructor(e){super(Qo,"duplexOption",["simplex","duplexFlipLongEdge","duplexFlipShortEdge"])}}class DynamicRender extends OptionObject{constructor(e){super(Qo,"dynamicRender",["forbidden","required"])}}class Embed extends Option01{constructor(e){super(Qo,"embed")}}class config_Encrypt extends Option01{constructor(e){super(Qo,"encrypt")}}class config_Encryption extends XFAObject{constructor(e){super(Qo,"encryption",\ -!0);this.encrypt=null;this.encryptionLevel=null;this.permissions=null}}class EncryptionLevel extends OptionObject{constructor(e){super(Qo,"encryptionLevel",["40bit","128bit"])}}class Enforce extends StringObject{constructor(e){super(Qo,"enforce")}}class Equate extends XFAObject{constructor(e){super(Qo,"equate");this.force=getInteger({data:e.force,defaultValue:1,validate:e=>0===e});this.from=e.from||"";this.to=e.to||""}}class EquateRange extends XFAObject{constructor(e){super(Qo,"equateRange");th\ -is.from=e.from||"";this.to=e.to||"";this._unicodeRange=e.unicodeRange||""}get unicodeRange(){const e=[],t=/U\\+([0-9a-fA-F]+)/,a=this._unicodeRange;for(let r of a.split(",").map((e=>e.trim())).filter((e=>!!e))){r=r.split("-",2).map((e=>{const a=e.match(t);return a?parseInt(a[1],16):0}));1===r.length&&r.push(r[0]);e.push(r)}return shadow(this,"unicodeRange",e)}}class Exclude extends ContentObject{constructor(e){super(Qo,"exclude")}[hs](){this[ss]=this[ss].trim().split(/\\s+/).filter((e=>e&&["calcul\ -ate","close","enter","exit","initialize","ready","validate"].includes(e)))}}class ExcludeNS extends StringObject{constructor(e){super(Qo,"excludeNS")}}class FlipLabel extends OptionObject{constructor(e){super(Qo,"flipLabel",["usePrinterSetting","on","off"])}}class config_FontInfo extends XFAObject{constructor(e){super(Qo,"fontInfo",!0);this.embed=null;this.map=null;this.subsetBelow=null;this.alwaysEmbed=new XFAObjectArray;this.defaultTypeface=new XFAObjectArray;this.neverEmbed=new XFAObjectArray\ -}}class FormFieldFilling extends Option01{constructor(e){super(Qo,"formFieldFilling")}}class GroupParent extends StringObject{constructor(e){super(Qo,"groupParent")}}class IfEmpty extends OptionObject{constructor(e){super(Qo,"ifEmpty",["dataValue","dataGroup","ignore","remove"])}}class IncludeXDPContent extends StringObject{constructor(e){super(Qo,"includeXDPContent")}}class IncrementalLoad extends OptionObject{constructor(e){super(Qo,"incrementalLoad",["none","forwardOnly"])}}class IncrementalM\ -erge extends Option01{constructor(e){super(Qo,"incrementalMerge")}}class Interactive extends Option01{constructor(e){super(Qo,"interactive")}}class Jog extends OptionObject{constructor(e){super(Qo,"jog",["usePrinterSetting","none","pageSet"])}}class LabelPrinter extends XFAObject{constructor(e){super(Qo,"labelPrinter",!0);this.name=getStringOption(e.name,["zpl","dpl","ipl","tcpl"]);this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class Layout extends OptionObject{const\ -ructor(e){super(Qo,"layout",["paginate","panel"])}}class Level extends IntegerObject{constructor(e){super(Qo,"level",0,(e=>e>0))}}class Linearized extends Option01{constructor(e){super(Qo,"linearized")}}class Locale extends StringObject{constructor(e){super(Qo,"locale")}}class LocaleSet extends StringObject{constructor(e){super(Qo,"localeSet")}}class Log extends XFAObject{constructor(e){super(Qo,"log",!0);this.mode=null;this.threshold=null;this.to=null;this.uri=null}}class MapElement extends XFA\ -Object{constructor(e){super(Qo,"map",!0);this.equate=new XFAObjectArray;this.equateRange=new XFAObjectArray}}class MediumInfo extends XFAObject{constructor(e){super(Qo,"mediumInfo",!0);this.map=null}}class config_Message extends XFAObject{constructor(e){super(Qo,"message",!0);this.msgId=null;this.severity=null}}class Messaging extends XFAObject{constructor(e){super(Qo,"messaging",!0);this.message=new XFAObjectArray}}class Mode extends OptionObject{constructor(e){super(Qo,"mode",["append","overwr\ -ite"])}}class ModifyAnnots extends Option01{constructor(e){super(Qo,"modifyAnnots")}}class MsgId extends IntegerObject{constructor(e){super(Qo,"msgId",1,(e=>e>=1))}}class NameAttr extends StringObject{constructor(e){super(Qo,"nameAttr")}}class NeverEmbed extends ContentObject{constructor(e){super(Qo,"neverEmbed")}}class NumberOfCopies extends IntegerObject{constructor(e){super(Qo,"numberOfCopies",null,(e=>e>=2&&e<=5))}}class OpenAction extends XFAObject{constructor(e){super(Qo,"openAction",!0);t\ -his.destination=null}}class Output extends XFAObject{constructor(e){super(Qo,"output",!0);this.to=null;this.type=null;this.uri=null}}class OutputBin extends StringObject{constructor(e){super(Qo,"outputBin")}}class OutputXSL extends XFAObject{constructor(e){super(Qo,"outputXSL",!0);this.uri=null}}class Overprint extends OptionObject{constructor(e){super(Qo,"overprint",["none","both","draw","field"])}}class Packets extends StringObject{constructor(e){super(Qo,"packets")}[hs](){"*"!==this[ss]&&(thi\ -s[ss]=this[ss].trim().split(/\\s+/).filter((e=>["config","datasets","template","xfdf","xslt"].includes(e))))}}class PageOffset extends XFAObject{constructor(e){super(Qo,"pageOffset");this.x=getInteger({data:e.x,defaultValue:"useXDCSetting",validate:e=>!0});this.y=getInteger({data:e.y,defaultValue:"useXDCSetting",validate:e=>!0})}}class PageRange extends StringObject{constructor(e){super(Qo,"pageRange")}[hs](){const e=this[ss].trim().split(/\\s+/).map((e=>parseInt(e,10))),t=[];for(let a=0,r=e.lengt\ -h;a!1))}}class Pcl extends XFAObject{constructor(e){super(Qo,"pcl",!0);this.name=e.name\ -||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.pageOffset=null;this.staple=null;this.xdc=null}}class Pdf extends XFAObject{constructor(e){super(Qo,"pdf",!0);this.name=e.name||"";this.adobeExtensionLevel=null;this.batchOutput=null;this.compression=null;this.creator=null;this.encryption=null;this.fontInfo=null;this.interactive=null;this.linearized=null;this.openAction=null;this.pdfa=null;this.producer=null;this.renderPolicy=null;this.scrip\ -tModel=null;this.silentPrint=null;this.submitFormat=null;this.tagged=null;this.version=null;this.viewerPreferences=null;this.xdc=null}}class Pdfa extends XFAObject{constructor(e){super(Qo,"pdfa",!0);this.amd=null;this.conformance=null;this.includeXDPContent=null;this.part=null}}class Permissions extends XFAObject{constructor(e){super(Qo,"permissions",!0);this.accessibleContent=null;this.change=null;this.contentCopy=null;this.documentAssembly=null;this.formFieldFilling=null;this.modifyAnnots=null\ -;this.plaintextMetadata=null;this.print=null;this.printHighQuality=null}}class PickTrayByPDFSize extends Option01{constructor(e){super(Qo,"pickTrayByPDFSize")}}class config_Picture extends StringObject{constructor(e){super(Qo,"picture")}}class PlaintextMetadata extends Option01{constructor(e){super(Qo,"plaintextMetadata")}}class Presence extends OptionObject{constructor(e){super(Qo,"presence",["preserve","dissolve","dissolveStructure","ignore","remove"])}}class Present extends XFAObject{construc\ -tor(e){super(Qo,"present",!0);this.behaviorOverride=null;this.cache=null;this.common=null;this.copies=null;this.destination=null;this.incrementalMerge=null;this.layout=null;this.output=null;this.overprint=null;this.pagination=null;this.paginationOverride=null;this.script=null;this.validate=null;this.xdp=null;this.driver=new XFAObjectArray;this.labelPrinter=new XFAObjectArray;this.pcl=new XFAObjectArray;this.pdf=new XFAObjectArray;this.ps=new XFAObjectArray;this.submitUrl=new XFAObjectArray;this.\ -webClient=new XFAObjectArray;this.zpl=new XFAObjectArray}}class Print extends Option01{constructor(e){super(Qo,"print")}}class PrintHighQuality extends Option01{constructor(e){super(Qo,"printHighQuality")}}class PrintScaling extends OptionObject{constructor(e){super(Qo,"printScaling",["appdefault","noScaling"])}}class PrinterName extends StringObject{constructor(e){super(Qo,"printerName")}}class Producer extends StringObject{constructor(e){super(Qo,"producer")}}class Ps extends XFAObject{constru\ -ctor(e){super(Qo,"ps",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.staple=null;this.xdc=null}}class Range extends ContentObject{constructor(e){super(Qo,"range")}[hs](){this[ss]=this[ss].split(",",2).map((e=>e.split("-").map((e=>parseInt(e.trim(),10))))).filter((e=>e.every((e=>!isNaN(e))))).map((e=>{1===e.length&&e.push(e[0]);return e}))}}class Record extends ContentObject{constructor(e){super(Qo,"record")}[hs](){thi\ -s[ss]=this[ss].trim();const e=parseInt(this[ss],10);!isNaN(e)&&e>=0&&(this[ss]=e)}}class Relevant extends ContentObject{constructor(e){super(Qo,"relevant")}[hs](){this[ss]=this[ss].trim().split(/\\s+/)}}class Rename extends ContentObject{constructor(e){super(Qo,"rename")}[hs](){this[ss]=this[ss].trim();(this[ss].toLowerCase().startsWith("xml")||new RegExp("[\\\\p{L}_][\\\\p{L}\\\\d._\\\\p{M}-]*","u").test(this[ss]))&&warn("XFA - Rename: invalid XFA name")}}class RenderPolicy extends OptionObject{construc\ -tor(e){super(Qo,"renderPolicy",["server","client"])}}class RunScripts extends OptionObject{constructor(e){super(Qo,"runScripts",["both","client","none","server"])}}class config_Script extends XFAObject{constructor(e){super(Qo,"script",!0);this.currentPage=null;this.exclude=null;this.runScripts=null}}class ScriptModel extends OptionObject{constructor(e){super(Qo,"scriptModel",["XFA","none"])}}class Severity extends OptionObject{constructor(e){super(Qo,"severity",["ignore","error","information","t\ -race","warning"])}}class SilentPrint extends XFAObject{constructor(e){super(Qo,"silentPrint",!0);this.addSilentPrint=null;this.printerName=null}}class Staple extends XFAObject{constructor(e){super(Qo,"staple");this.mode=getStringOption(e.mode,["usePrinterSetting","on","off"])}}class StartNode extends StringObject{constructor(e){super(Qo,"startNode")}}class StartPage extends IntegerObject{constructor(e){super(Qo,"startPage",0,(e=>!0))}}class SubmitFormat extends OptionObject{constructor(e){super(\ -Qo,"submitFormat",["html","delegate","fdf","xml","pdf"])}}class SubmitUrl extends StringObject{constructor(e){super(Qo,"submitUrl")}}class SubsetBelow extends IntegerObject{constructor(e){super(Qo,"subsetBelow",100,(e=>e>=0&&e<=100))}}class SuppressBanner extends Option01{constructor(e){super(Qo,"suppressBanner")}}class Tagged extends Option01{constructor(e){super(Qo,"tagged")}}class config_Template extends XFAObject{constructor(e){super(Qo,"template",!0);this.base=null;this.relevant=null;this.s\ -tartPage=null;this.uri=null;this.xsl=null}}class Threshold extends OptionObject{constructor(e){super(Qo,"threshold",["trace","error","information","warning"])}}class To extends OptionObject{constructor(e){super(Qo,"to",["null","memory","stderr","stdout","system","uri"])}}class TemplateCache extends XFAObject{constructor(e){super(Qo,"templateCache");this.maxEntries=getInteger({data:e.maxEntries,defaultValue:5,validate:e=>e>=0})}}class Trace extends XFAObject{constructor(e){super(Qo,"trace",!0);th\ -is.area=new XFAObjectArray}}class Transform extends XFAObject{constructor(e){super(Qo,"transform",!0);this.groupParent=null;this.ifEmpty=null;this.nameAttr=null;this.picture=null;this.presence=null;this.rename=null;this.whitespace=null}}class Type extends OptionObject{constructor(e){super(Qo,"type",["none","ascii85","asciiHex","ccittfax","flate","lzw","runLength","native","xdp","mergedXDP"])}}class Uri extends StringObject{constructor(e){super(Qo,"uri")}}class config_Validate extends OptionObjec\ -t{constructor(e){super(Qo,"validate",["preSubmit","prePrint","preExecute","preSave"])}}class ValidateApprovalSignatures extends ContentObject{constructor(e){super(Qo,"validateApprovalSignatures")}[hs](){this[ss]=this[ss].trim().split(/\\s+/).filter((e=>["docReady","postSign"].includes(e)))}}class ValidationMessaging extends OptionObject{constructor(e){super(Qo,"validationMessaging",["allMessagesIndividually","allMessagesTogether","firstMessageOnly","noMessages"])}}class Version extends OptionObje\ -ct{constructor(e){super(Qo,"version",["1.7","1.6","1.5","1.4","1.3","1.2"])}}class VersionControl extends XFAObject{constructor(e){super(Qo,"VersionControl");this.outputBelow=getStringOption(e.outputBelow,["warn","error","update"]);this.sourceAbove=getStringOption(e.sourceAbove,["warn","error"]);this.sourceBelow=getStringOption(e.sourceBelow,["update","maintain"])}}class ViewerPreferences extends XFAObject{constructor(e){super(Qo,"viewerPreferences",!0);this.ADBE_JSConsole=null;this.ADBE_JSDebug\ -ger=null;this.addViewerPreferences=null;this.duplexOption=null;this.enforce=null;this.numberOfCopies=null;this.pageRange=null;this.pickTrayByPDFSize=null;this.printScaling=null}}class WebClient extends XFAObject{constructor(e){super(Qo,"webClient",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class Whitespace extends OptionObject{constructor(e){super(Qo,"whitespace",["preserve","ltrim","normalize","rtrim","trim"])}}class Window extends ContentObject{constructor(e){super\ -(Qo,"window")}[hs](){const e=this[ss].split(",",2).map((e=>parseInt(e.trim(),10)));if(e.some((e=>isNaN(e))))this[ss]=[0,0];else{1===e.length&&e.push(e[0]);this[ss]=e}}}class Xdc extends XFAObject{constructor(e){super(Qo,"xdc",!0);this.uri=new XFAObjectArray;this.xsl=new XFAObjectArray}}class Xdp extends XFAObject{constructor(e){super(Qo,"xdp",!0);this.packets=null}}class Xsl extends XFAObject{constructor(e){super(Qo,"xsl",!0);this.debug=null;this.uri=null}}class Zpl extends XFAObject{constructor\ -(e){super(Qo,"zpl",!0);this.name=e.name?e.name.trim():"";this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class ConfigNamespace{static[fo](e,t){if(ConfigNamespace.hasOwnProperty(e))return ConfigNamespace[e](t)}static acrobat(e){return new Acrobat(e)}static acrobat7(e){return new Acrobat7(e)}static ADBE_JSConsole(e){return new ADBE_JSConsole(e)}static ADBE_JSDebugger(e){return new ADBE_JSDebugger(e)}static addSilentPrint(e){return new AddSilentPrint(e)}static addViewerP\ -references(e){return new AddViewerPreferences(e)}static adjustData(e){return new AdjustData(e)}static adobeExtensionLevel(e){return new AdobeExtensionLevel(e)}static agent(e){return new Agent(e)}static alwaysEmbed(e){return new AlwaysEmbed(e)}static amd(e){return new Amd(e)}static area(e){return new config_Area(e)}static attributes(e){return new Attributes(e)}static autoSave(e){return new AutoSave(e)}static base(e){return new Base(e)}static batchOutput(e){return new BatchOutput(e)}static behavio\ -rOverride(e){return new BehaviorOverride(e)}static cache(e){return new Cache(e)}static change(e){return new Change(e)}static common(e){return new Common(e)}static compress(e){return new Compress(e)}static compressLogicalStructure(e){return new CompressLogicalStructure(e)}static compressObjectStream(e){return new CompressObjectStream(e)}static compression(e){return new Compression(e)}static config(e){return new Config(e)}static conformance(e){return new Conformance(e)}static contentCopy(e){return\ - new ContentCopy(e)}static copies(e){return new Copies(e)}static creator(e){return new Creator(e)}static currentPage(e){return new CurrentPage(e)}static data(e){return new Data(e)}static debug(e){return new Debug(e)}static defaultTypeface(e){return new DefaultTypeface(e)}static destination(e){return new Destination(e)}static documentAssembly(e){return new DocumentAssembly(e)}static driver(e){return new Driver(e)}static duplexOption(e){return new DuplexOption(e)}static dynamicRender(e){return new\ - DynamicRender(e)}static embed(e){return new Embed(e)}static encrypt(e){return new config_Encrypt(e)}static encryption(e){return new config_Encryption(e)}static encryptionLevel(e){return new EncryptionLevel(e)}static enforce(e){return new Enforce(e)}static equate(e){return new Equate(e)}static equateRange(e){return new EquateRange(e)}static exclude(e){return new Exclude(e)}static excludeNS(e){return new ExcludeNS(e)}static flipLabel(e){return new FlipLabel(e)}static fontInfo(e){return new config\ -_FontInfo(e)}static formFieldFilling(e){return new FormFieldFilling(e)}static groupParent(e){return new GroupParent(e)}static ifEmpty(e){return new IfEmpty(e)}static includeXDPContent(e){return new IncludeXDPContent(e)}static incrementalLoad(e){return new IncrementalLoad(e)}static incrementalMerge(e){return new IncrementalMerge(e)}static interactive(e){return new Interactive(e)}static jog(e){return new Jog(e)}static labelPrinter(e){return new LabelPrinter(e)}static layout(e){return new Layout(e)\ -}static level(e){return new Level(e)}static linearized(e){return new Linearized(e)}static locale(e){return new Locale(e)}static localeSet(e){return new LocaleSet(e)}static log(e){return new Log(e)}static map(e){return new MapElement(e)}static mediumInfo(e){return new MediumInfo(e)}static message(e){return new config_Message(e)}static messaging(e){return new Messaging(e)}static mode(e){return new Mode(e)}static modifyAnnots(e){return new ModifyAnnots(e)}static msgId(e){return new MsgId(e)}static \ -nameAttr(e){return new NameAttr(e)}static neverEmbed(e){return new NeverEmbed(e)}static numberOfCopies(e){return new NumberOfCopies(e)}static openAction(e){return new OpenAction(e)}static output(e){return new Output(e)}static outputBin(e){return new OutputBin(e)}static outputXSL(e){return new OutputXSL(e)}static overprint(e){return new Overprint(e)}static packets(e){return new Packets(e)}static pageOffset(e){return new PageOffset(e)}static pageRange(e){return new PageRange(e)}static pagination(e\ -){return new Pagination(e)}static paginationOverride(e){return new PaginationOverride(e)}static part(e){return new Part(e)}static pcl(e){return new Pcl(e)}static pdf(e){return new Pdf(e)}static pdfa(e){return new Pdfa(e)}static permissions(e){return new Permissions(e)}static pickTrayByPDFSize(e){return new PickTrayByPDFSize(e)}static picture(e){return new config_Picture(e)}static plaintextMetadata(e){return new PlaintextMetadata(e)}static presence(e){return new Presence(e)}static present(e){retu\ -rn new Present(e)}static print(e){return new Print(e)}static printHighQuality(e){return new PrintHighQuality(e)}static printScaling(e){return new PrintScaling(e)}static printerName(e){return new PrinterName(e)}static producer(e){return new Producer(e)}static ps(e){return new Ps(e)}static range(e){return new Range(e)}static record(e){return new Record(e)}static relevant(e){return new Relevant(e)}static rename(e){return new Rename(e)}static renderPolicy(e){return new RenderPolicy(e)}static runScri\ -pts(e){return new RunScripts(e)}static script(e){return new config_Script(e)}static scriptModel(e){return new ScriptModel(e)}static severity(e){return new Severity(e)}static silentPrint(e){return new SilentPrint(e)}static staple(e){return new Staple(e)}static startNode(e){return new StartNode(e)}static startPage(e){return new StartPage(e)}static submitFormat(e){return new SubmitFormat(e)}static submitUrl(e){return new SubmitUrl(e)}static subsetBelow(e){return new SubsetBelow(e)}static suppressBa\ -nner(e){return new SuppressBanner(e)}static tagged(e){return new Tagged(e)}static template(e){return new config_Template(e)}static templateCache(e){return new TemplateCache(e)}static threshold(e){return new Threshold(e)}static to(e){return new To(e)}static trace(e){return new Trace(e)}static transform(e){return new Transform(e)}static type(e){return new Type(e)}static uri(e){return new Uri(e)}static validate(e){return new config_Validate(e)}static validateApprovalSignatures(e){return new Validat\ -eApprovalSignatures(e)}static validationMessaging(e){return new ValidationMessaging(e)}static version(e){return new Version(e)}static versionControl(e){return new VersionControl(e)}static viewerPreferences(e){return new ViewerPreferences(e)}static webClient(e){return new WebClient(e)}static whitespace(e){return new Whitespace(e)}static window(e){return new Window(e)}static xdc(e){return new Xdc(e)}static xdp(e){return new Xdp(e)}static xsl(e){return new Xsl(e)}static zpl(e){return new Zpl(e)}}co\ -nst ec=go.connectionSet.id;class ConnectionSet extends XFAObject{constructor(e){super(ec,"connectionSet",!0);this.wsdlConnection=new XFAObjectArray;this.xmlConnection=new XFAObjectArray;this.xsdConnection=new XFAObjectArray}}class EffectiveInputPolicy extends XFAObject{constructor(e){super(ec,"effectiveInputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EffectiveOutputPolicy extends XFAObject{constructor(e){super(ec,"effectiveOutputPolicy");th\ -is.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Operation extends StringObject{constructor(e){super(ec,"operation");this.id=e.id||"";this.input=e.input||"";this.name=e.name||"";this.output=e.output||"";this.use=e.use||"";this.usehref=e.usehref||""}}class RootElement extends StringObject{constructor(e){super(ec,"rootElement");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAction extends StringObject{construct\ -or(e){super(ec,"soapAction");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAddress extends StringObject{constructor(e){super(ec,"soapAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class connection_set_Uri extends StringObject{constructor(e){super(ec,"uri");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlAddress extends StringObject{constructor(e){super(e\ -c,"wsdlAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlConnection extends XFAObject{constructor(e){super(ec,"wsdlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.effectiveInputPolicy=null;this.effectiveOutputPolicy=null;this.operation=null;this.soapAction=null;this.soapAddress=null;this.wsdlAddress=null}}class XmlConnection extends XFAObject{constructor(e){super(ec,"xmlConnection",!0);this.dataDescriptio\ -n=e.dataDescription||"";this.name=e.name||"";this.uri=null}}class XsdConnection extends XFAObject{constructor(e){super(ec,"xsdConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.rootElement=null;this.uri=null}}class ConnectionSetNamespace{static[fo](e,t){if(ConnectionSetNamespace.hasOwnProperty(e))return ConnectionSetNamespace[e](t)}static connectionSet(e){return new ConnectionSet(e)}static effectiveInputPolicy(e){return new EffectiveInputPolicy(e)}static effecti\ -veOutputPolicy(e){return new EffectiveOutputPolicy(e)}static operation(e){return new Operation(e)}static rootElement(e){return new RootElement(e)}static soapAction(e){return new SoapAction(e)}static soapAddress(e){return new SoapAddress(e)}static uri(e){return new connection_set_Uri(e)}static wsdlAddress(e){return new WsdlAddress(e)}static wsdlConnection(e){return new WsdlConnection(e)}static xmlConnection(e){return new XmlConnection(e)}static xsdConnection(e){return new XsdConnection(e)}}const \ -tc=go.datasets.id;class datasets_Data extends XmlObject{constructor(e){super(tc,"data",e)}[Ls](){return!0}}class Datasets extends XFAObject{constructor(e){super(tc,"datasets",!0);this.data=null;this.Signature=null}[$s](e){const t=e[Ws];("data"===t&&e[Hs]===tc||"Signature"===t&&e[Hs]===go.signature.id)&&(this[t]=e);this[Qn](e)}}class DatasetsNamespace{static[fo](e,t){if(DatasetsNamespace.hasOwnProperty(e))return DatasetsNamespace[e](t)}static datasets(e){return new Datasets(e)}static data(e){retu\ -rn new datasets_Data(e)}}const ac=go.localeSet.id;class CalendarSymbols extends XFAObject{constructor(e){super(ac,"calendarSymbols",!0);this.name="gregorian";this.dayNames=new XFAObjectArray(2);this.eraNames=null;this.meridiemNames=null;this.monthNames=new XFAObjectArray(2)}}class CurrencySymbol extends StringObject{constructor(e){super(ac,"currencySymbol");this.name=getStringOption(e.name,["symbol","isoname","decimal"])}}class CurrencySymbols extends XFAObject{constructor(e){super(ac,"currencyS\ -ymbols",!0);this.currencySymbol=new XFAObjectArray(3)}}class DatePattern extends StringObject{constructor(e){super(ac,"datePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class DatePatterns extends XFAObject{constructor(e){super(ac,"datePatterns",!0);this.datePattern=new XFAObjectArray(4)}}class DateTimeSymbols extends ContentObject{constructor(e){super(ac,"dateTimeSymbols")}}class Day extends StringObject{constructor(e){super(ac,"day")}}class DayNames extends XFAObjec\ -t{constructor(e){super(ac,"dayNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.day=new XFAObjectArray(7)}}class Era extends StringObject{constructor(e){super(ac,"era")}}class EraNames extends XFAObject{constructor(e){super(ac,"eraNames",!0);this.era=new XFAObjectArray(2)}}class locale_set_Locale extends XFAObject{constructor(e){super(ac,"locale",!0);this.desc=e.desc||"";this.name="isoname";this.calendarSymbols=null;this.currencySymbols=null;this.datePatterns=n\ -ull;this.dateTimeSymbols=null;this.numberPatterns=null;this.numberSymbols=null;this.timePatterns=null;this.typeFaces=null}}class locale_set_LocaleSet extends XFAObject{constructor(e){super(ac,"localeSet",!0);this.locale=new XFAObjectArray}}class Meridiem extends StringObject{constructor(e){super(ac,"meridiem")}}class MeridiemNames extends XFAObject{constructor(e){super(ac,"meridiemNames",!0);this.meridiem=new XFAObjectArray(2)}}class Month extends StringObject{constructor(e){super(ac,"month")}}c\ -lass MonthNames extends XFAObject{constructor(e){super(ac,"monthNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.month=new XFAObjectArray(12)}}class NumberPattern extends StringObject{constructor(e){super(ac,"numberPattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class NumberPatterns extends XFAObject{constructor(e){super(ac,"numberPatterns",!0);this.numberPattern=new XFAObjectArray(4)}}class NumberSymbol extends StringObject{construc\ -tor(e){super(ac,"numberSymbol");this.name=getStringOption(e.name,["decimal","grouping","percent","minus","zero"])}}class NumberSymbols extends XFAObject{constructor(e){super(ac,"numberSymbols",!0);this.numberSymbol=new XFAObjectArray(5)}}class TimePattern extends StringObject{constructor(e){super(ac,"timePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class TimePatterns extends XFAObject{constructor(e){super(ac,"timePatterns",!0);this.timePattern=new XFAObjectArray(4)}\ -}class TypeFace extends XFAObject{constructor(e){super(ac,"typeFace",!0);this.name=""|e.name}}class TypeFaces extends XFAObject{constructor(e){super(ac,"typeFaces",!0);this.typeFace=new XFAObjectArray}}class LocaleSetNamespace{static[fo](e,t){if(LocaleSetNamespace.hasOwnProperty(e))return LocaleSetNamespace[e](t)}static calendarSymbols(e){return new CalendarSymbols(e)}static currencySymbol(e){return new CurrencySymbol(e)}static currencySymbols(e){return new CurrencySymbols(e)}static datePattern(\ -e){return new DatePattern(e)}static datePatterns(e){return new DatePatterns(e)}static dateTimeSymbols(e){return new DateTimeSymbols(e)}static day(e){return new Day(e)}static dayNames(e){return new DayNames(e)}static era(e){return new Era(e)}static eraNames(e){return new EraNames(e)}static locale(e){return new locale_set_Locale(e)}static localeSet(e){return new locale_set_LocaleSet(e)}static meridiem(e){return new Meridiem(e)}static meridiemNames(e){return new MeridiemNames(e)}static month(e){ret\ -urn new Month(e)}static monthNames(e){return new MonthNames(e)}static numberPattern(e){return new NumberPattern(e)}static numberPatterns(e){return new NumberPatterns(e)}static numberSymbol(e){return new NumberSymbol(e)}static numberSymbols(e){return new NumberSymbols(e)}static timePattern(e){return new TimePattern(e)}static timePatterns(e){return new TimePatterns(e)}static typeFace(e){return new TypeFace(e)}static typeFaces(e){return new TypeFaces(e)}}const rc=go.signature.id;class signature_Sig\ -nature extends XFAObject{constructor(e){super(rc,"signature",!0)}}class SignatureNamespace{static[fo](e,t){if(SignatureNamespace.hasOwnProperty(e))return SignatureNamespace[e](t)}static signature(e){return new signature_Signature(e)}}const ic=go.stylesheet.id;class Stylesheet extends XFAObject{constructor(e){super(ic,"stylesheet",!0)}}class StylesheetNamespace{static[fo](e,t){if(StylesheetNamespace.hasOwnProperty(e))return StylesheetNamespace[e](t)}static stylesheet(e){return new Stylesheet(e)}}\ -const nc=go.xdp.id;class xdp_Xdp extends XFAObject{constructor(e){super(nc,"xdp",!0);this.uuid=e.uuid||"";this.timeStamp=e.timeStamp||"";this.config=null;this.connectionSet=null;this.datasets=null;this.localeSet=null;this.stylesheet=new XFAObjectArray;this.template=null}[Gs](e){const t=go[e[Ws]];return t&&e[Hs]===t.id}}class XdpNamespace{static[fo](e,t){if(XdpNamespace.hasOwnProperty(e))return XdpNamespace[e](t)}static xdp(e){return new xdp_Xdp(e)}}const sc=go.xhtml.id,oc=Symbol(),cc=new Set(["c\ -olor","font","font-family","font-size","font-stretch","font-style","font-weight","margin","margin-bottom","margin-left","margin-right","margin-top","letter-spacing","line-height","orphans","page-break-after","page-break-before","page-break-inside","tab-interval","tab-stop","text-align","text-decoration","text-indent","vertical-align","widows","kerning-mode","xfa-font-horizontal-scale","xfa-font-vertical-scale","xfa-spacerun","xfa-tab-stops"]),lc=new Map([["page-break-after","breakAfter"],["page-\ -break-before","breakBefore"],["page-break-inside","breakInside"],["kerning-mode",e=>"none"===e?"none":"normal"],["xfa-font-horizontal-scale",e=>`scaleX(${Math.max(0,parseInt(e)/100).toFixed(2)})`],["xfa-font-vertical-scale",e=>`scaleY(${Math.max(0,parseInt(e)/100).toFixed(2)})`],["xfa-spacerun",""],["xfa-tab-stops",""],["font-size",(e,t)=>measureToString(.99*(e=t.fontSize=Math.abs(getMeasurement(e))))],["letter-spacing",e=>measureToString(getMeasurement(e))],["line-height",e=>measureToString(get\ -Measurement(e))],["margin",e=>measureToString(getMeasurement(e))],["margin-bottom",e=>measureToString(getMeasurement(e))],["margin-left",e=>measureToString(getMeasurement(e))],["margin-right",e=>measureToString(getMeasurement(e))],["margin-top",e=>measureToString(getMeasurement(e))],["text-indent",e=>measureToString(getMeasurement(e))],["font-family",e=>e],["vertical-align",e=>measureToString(getMeasurement(e))]]),hc=/\\s+/g,uc=/[\\r\\n]+/g,dc=/\\r\\n?/g;function mapStyle(e,t,a){const r=Object.create\ -(null);if(!e)return r;const i=Object.create(null);for(const[t,a]of e.split(";").map((e=>e.split(":",2)))){const e=lc.get(t);if(""===e)continue;let n=a;e&&(n="string"==typeof e?e:e(a,i));t.endsWith("scale")?r.transform=r.transform?`${r[t]} ${n}`:n:r[t.replaceAll(/-([a-zA-Z])/g,((e,t)=>t.toUpperCase()))]=n}r.fontFamily&&setFontFamily({typeface:r.fontFamily,weight:r.fontWeight||"normal",posture:r.fontStyle||"normal",size:i.fontSize||0},t,t[Is].fontFinder,r);if(a&&r.verticalAlign&&"0px"!==r.vertical\ -Align&&r.fontSize){const e=.583,t=.333,a=getMeasurement(r.fontSize);r.fontSize=measureToString(a*e);r.verticalAlign=measureToString(Math.sign(getMeasurement(r.verticalAlign))*a*t)}a&&r.fontSize&&(r.fontSize=`calc(${r.fontSize} * var(--total-scale-factor))`);fixTextIndent(r);return r}const fc=new Set(["body","html"]);class XhtmlObject extends XmlObject{constructor(e,t){super(sc,t);this[oc]=!1;this.style=e.style||""}[ts](e){super[ts](e);this.style=function checkStyle(e){return e.style?e.style.spli\ -t(";").filter((e=>!!e.trim())).map((e=>e.split(":",2).map((e=>e.trim())))).filter((([t,a])=>{"font-family"===t&&e[Is].usedTypefaces.add(a);return cc.has(t)})).map((e=>e.join(":"))).join(";"):""}(this)}[Yn](){return!fc.has(this[Ws])}[Vs](e,t=!1){if(t)this[oc]=!0;else{e=e.replaceAll(uc,"");this.style.includes("xfa-spacerun:yes")||(e=e.replaceAll(hc," "))}e&&(this[ss]+=e)}[Ks](e,t=!0){const a=Object.create(null),r={top:NaN,bottom:NaN,left:NaN,right:NaN};let i=null;for(const[e,t]of this.style.split(\ -";").map((e=>e.split(":",2))))switch(e){case"font-family":a.typeface=stripQuotes(t);break;case"font-size":a.size=getMeasurement(t);break;case"font-weight":a.weight=t;break;case"font-style":a.posture=t;break;case"letter-spacing":a.letterSpacing=getMeasurement(t);break;case"margin":const e=t.split(/ \\t/).map((e=>getMeasurement(e)));switch(e.length){case 1:r.top=r.bottom=r.left=r.right=e[0];break;case 2:r.top=r.bottom=e[0];r.left=r.right=e[1];break;case 3:r.top=e[0];r.bottom=e[2];r.left=r.right=e[1\ -];break;case 4:r.top=e[0];r.left=e[1];r.bottom=e[2];r.right=e[3]}break;case"margin-top":r.top=getMeasurement(t);break;case"margin-bottom":r.bottom=getMeasurement(t);break;case"margin-left":r.left=getMeasurement(t);break;case"margin-right":r.right=getMeasurement(t);break;case"line-height":i=getMeasurement(t)}e.pushData(a,r,i);if(this[ss])e.addString(this[ss]);else for(const t of this[Ss]())"#text"!==t[Ws]?t[Ks](e):e.addString(t[ss]);t&&e.popFont()}[co](e){const t=[];this[ls]={children:t};this[es]\ -({});if(0===t.length&&!this[ss])return HTMLResult.EMPTY;let a;a=this[oc]?this[ss]?this[ss].replaceAll(dc,"\\n"):void 0:this[ss]||void 0;return HTMLResult.success({name:this[Ws],attributes:{href:this.href,style:mapStyle(this.style,this,this[oc])},children:t,value:a})}}class A extends XhtmlObject{constructor(e){super(e,"a");this.href=fixURL(e.href)||""}}class B extends XhtmlObject{constructor(e){super(e,"b")}[Ks](e){e.pushFont({weight:"bold"});super[Ks](e);e.popFont()}}class Body extends XhtmlObjec\ -t{constructor(e){super(e,"body")}[co](e){const t=super[co](e),{html:a}=t;if(!a)return HTMLResult.EMPTY;a.name="div";a.attributes.class=["xfaRich"];return t}}class Br extends XhtmlObject{constructor(e){super(e,"br")}[so](){return"\\n"}[Ks](e){e.addString("\\n")}[co](e){return HTMLResult.success({name:"br"})}}class Html extends XhtmlObject{constructor(e){super(e,"html")}[co](e){const t=[];this[ls]={children:t};this[es]({});if(0===t.length)return HTMLResult.success({name:"div",attributes:{class:["xfa\ -Rich"],style:{}},value:this[ss]||""});if(1===t.length){const e=t[0];if(e.attributes?.class.includes("xfaRich"))return HTMLResult.success(e)}return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},children:t})}}class I extends XhtmlObject{constructor(e){super(e,"i")}[Ks](e){e.pushFont({posture:"italic"});super[Ks](e);e.popFont()}}class Li extends XhtmlObject{constructor(e){super(e,"li")}}class Ol extends XhtmlObject{constructor(e){super(e,"ol")}}class P extends XhtmlObject{c\ -onstructor(e){super(e,"p")}[Ks](e){super[Ks](e,!1);e.addString("\\n");e.addPara();e.popFont()}[so](){return this[vs]()[Ss]().at(-1)===this?super[so]():super[so]()+"\\n"}}class Span extends XhtmlObject{constructor(e){super(e,"span")}}class Sub extends XhtmlObject{constructor(e){super(e,"sub")}}class Sup extends XhtmlObject{constructor(e){super(e,"sup")}}class Ul extends XhtmlObject{constructor(e){super(e,"ul")}}class XhtmlNamespace{static[fo](e,t){if(XhtmlNamespace.hasOwnProperty(e))return XhtmlNam\ -espace[e](t)}static a(e){return new A(e)}static b(e){return new B(e)}static body(e){return new Body(e)}static br(e){return new Br(e)}static html(e){return new Html(e)}static i(e){return new I(e)}static li(e){return new Li(e)}static ol(e){return new Ol(e)}static p(e){return new P(e)}static span(e){return new Span(e)}static sub(e){return new Sub(e)}static sup(e){return new Sup(e)}static ul(e){return new Ul(e)}}const gc={config:ConfigNamespace,connection:ConnectionSetNamespace,datasets:DatasetsName\ -space,localeSet:LocaleSetNamespace,signature:SignatureNamespace,stylesheet:StylesheetNamespace,template:TemplateNamespace,xdp:XdpNamespace,xhtml:XhtmlNamespace};class UnknownNamespace{constructor(e){this.namespaceId=e}[fo](e,t){return new XmlObject(this.namespaceId,e,t)}}class Root extends XFAObject{constructor(e){super(-1,"root",Object.create(null));this.element=null;this[Os]=e}[$s](e){this.element=e;return!0}[hs](){super[hs]();if(this.element.template instanceof Template){this[Os].set(Qs,this.\ -element);this.element.template[eo](this[Os]);this.element.template[Os]=this[Os]}}}class Empty extends XFAObject{constructor(){super(-1,"",Object.create(null))}[$s](e){return!1}}class Builder{constructor(e=null){this._namespaceStack=[];this._nsAgnosticLevel=0;this._namespacePrefixes=new Map;this._namespaces=new Map;this._nextNsId=Math.max(...Object.values(go).map((({id:e})=>e)));this._currentNamespace=e||new UnknownNamespace(++this._nextNsId)}buildRoot(e){return new Root(e)}build({nsPrefix:e,name\ -:t,attributes:a,namespace:r,prefixes:i}){const n=null!==r;if(n){this._namespaceStack.push(this._currentNamespace);this._currentNamespace=this._searchNamespace(r)}i&&this._addNamespacePrefix(i);if(a.hasOwnProperty(zs)){const e=gc.datasets,t=a[zs];let r=null;for(const[a,i]of Object.entries(t)){if(this._getNamespaceToUse(a)===e){r={xfa:i};break}}r?a[zs]=r:delete a[zs]}const s=this._getNamespaceToUse(e),o=s?.[fo](t,a)||new Empty;o[Ls]()&&this._nsAgnosticLevel++;(n||i||o[Ls]())&&(o[rs]={hasNamespace:\ -n,prefixes:i,nsAgnostic:o[Ls]()});return o}isNsAgnostic(){return this._nsAgnosticLevel>0}_searchNamespace(e){let t=this._namespaces.get(e);if(t)return t;for(const[a,{check:r}]of Object.entries(go))if(r(e)){t=gc[a];if(t){this._namespaces.set(e,t);return t}break}t=new UnknownNamespace(++this._nextNsId);this._namespaces.set(e,t);return t}_addNamespacePrefix(e){for(const{prefix:t,value:a}of e){const e=this._searchNamespace(a);let r=this._namespacePrefixes.get(t);if(!r){r=[];this._namespacePrefixes.s\ -et(t,r)}r.push(e)}}_getNamespaceToUse(e){if(!e)return this._currentNamespace;const t=this._namespacePrefixes.get(e);if(t?.length>0)return t.at(-1);warn(`Unknown namespace prefix: ${e}.`);return null}clean(e){const{hasNamespace:t,prefixes:a,nsAgnostic:r}=e;t&&(this._currentNamespace=this._namespaceStack.pop());a&&a.forEach((({prefix:e})=>{this._namespacePrefixes.get(e).pop()}));r&&this._nsAgnosticLevel--}}class XFAParser extends XMLParserBase{constructor(e=null,t=!1){super();this._builder=new Bui\ -lder(e);this._stack=[];this._globalData={usedTypefaces:new Set};this._ids=new Map;this._current=this._builder.buildRoot(this._ids);this._errorCode=jn;this._whiteRegex=/^\\s+$/;this._nbsps=/\\xa0+/g;this._richText=t}parse(e){this.parseXml(e);if(this._errorCode===jn){this._current[hs]();return this._current.element}}onText(e){e=e.replace(this._nbsps,(e=>e.slice(1)+" "));this._richText||this._current[Yn]()?this._current[Vs](e,this._richText):this._whiteRegex.test(e)||this._current[Vs](e.trim())}onCda\ -ta(e){this._current[Vs](e)}_mkAttributes(e,t){let a=null,r=null;const i=Object.create({});for(const{name:n,value:s}of e)if("xmlns"===n)a?warn(`XFA - multiple namespace definition in <${t}>`):a=s;else if(n.startsWith("xmlns:")){const e=n.substring(6);r??=[];r.push({prefix:e,value:s})}else{const e=n.indexOf(":");if(-1===e)i[n]=s;else{const t=i[zs]??=Object.create(null),[a,r]=[n.slice(0,e),n.slice(e+1)];(t[a]||=Object.create(null))[r]=s}}return[a,r,i]}_getNameAndPrefix(e,t){const a=e.indexOf(":");r\ -eturn-1===a?[e,null]:[e.substring(a+1),t?"":e.substring(0,a)]}onBeginElement(e,t,a){const[r,i,n]=this._mkAttributes(t,e),[s,o]=this._getNameAndPrefix(e,this._builder.isNsAgnostic()),c=this._builder.build({nsPrefix:o,name:s,attributes:n,namespace:r,prefixes:i});c[Is]=this._globalData;if(a){c[hs]();this._current[$s](c)&&c[ao](this._ids);c[ts](this._builder)}else{this._stack.push(this._current);this._current=c}}onEndElement(e){const t=this._current;if(t[Bs]()&&"string"==typeof t[ss]){const e=new XF\ -AParser;e._globalData=this._globalData;const a=e.parse(t[ss]);t[ss]=null;t[$s](a)}t[hs]();this._current=this._stack.pop();this._current[$s](t)&&t[ao](this._ids);t[ts](this._builder)}onError(e){this._errorCode=e}}class XFAFactory{constructor(e){try{this.root=(new XFAParser).parse(XFAFactory._createDocument(e));const t=new Binder(this.root);this.form=t.bind();this.dataHandler=new DataHandler(this.root,t.getData());this.form[Is].template=this.form}catch(e){warn(`XFA - an error occurred during parsi\ -ng and binding: ${e}`)}}isValid(){return!(!this.root||!this.form)}_createPagesHelper(){const e=this.form[oo]();return new Promise(((t,a)=>{const nextIteration=()=>{try{const a=e.next();a.done?t(a.value):setTimeout(nextIteration,0)}catch(e){a(e)}};setTimeout(nextIteration,0)}))}async _createPages(){try{this.pages=await this._createPagesHelper();this.dims=this.pages.children.map((e=>{const{width:t,height:a}=e.attributes.style;return[0,0,parseInt(t),parseInt(a)]}))}catch(e){warn(`XFA - an error occ\ -urred during layout: ${e}`)}}getBoundingBox(e){return this.dims[e]}async getNumPages(){this.pages||await this._createPages();return this.dims.length}setImages(e){this.form[Is].images=e}setFonts(e){this.form[Is].fontFinder=new FontFinder(e);const t=[];for(let e of this.form[Is].usedTypefaces){e=stripQuotes(e);this.form[Is].fontFinder.find(e)||t.push(e)}return t.length>0?t:null}appendFonts(e,t){this.form[Is].fontFinder.add(e,t)}async getPages(){this.pages||await this._createPages();const e=this.pa\ -ges;this.pages=null;return e}serializeData(e){return this.dataHandler.serialize(e)}static _createDocument(e){return e["/xdp:xdp"]?Object.values(e).join(""):e["xdp:xdp"]}static getRichTextAsHtml(e){if(!e||"string"!=typeof e)return null;try{let t=new XFAParser(XhtmlNamespace,!0).parse(e);if(!["body","xhtml"].includes(t[Ws])){const e=XhtmlNamespace.body({});e[Qn](t);t=e}const a=t[co]();if(!a.success)return null;const{html:r}=a,{attributes:i}=r;if(i){i.class&&(i.class=i.class.filter((e=>!e.startsWit\ -h("xfa"))));i.dir="auto"}return{html:r,str:t[so]()}}catch(e){warn(`XFA - an error occurred during parsing of rich text: ${e}`)}return null}}class AnnotationFactory{static createGlobals(e){return Promise.all([e.ensureCatalog("acroForm"),e.ensureDoc("xfaDatasets"),e.ensureCatalog("structTreeRoot"),e.ensureCatalog("baseUrl"),e.ensureCatalog("attachments"),e.ensureCatalog("globalColorSpaceCache")]).then((([t,a,r,i,n,s])=>({pdfManager:e,acroForm:t instanceof Dict?t:Dict.empty,xfaDatasets:a,structTree\ -Root:r,baseUrl:i,attachments:n,globalColorSpaceCache:s})),(e=>{warn(`createGlobals: "${e}".`);return null}))}static async create(e,t,a,r,i,n,s){const o=i?await this._getPageIndex(e,t,a.pdfManager):null;return a.pdfManager.ensure(this,"_create",[e,t,a,r,i,n,o,s])}static _create(e,t,a,r,i=!1,n=null,s=null,o=null){const c=e.fetchIfRef(t);if(!(c instanceof Dict))return;const{acroForm:l,pdfManager:h}=a,u=t instanceof Ref?t.toString():`annot_${r.createObjId()}`;let d=c.get("Subtype");d=d instanceof Na\ -me?d.name:null;const f={xref:e,ref:t,dict:c,subtype:d,id:u,annotationGlobals:a,collectFields:i,orphanFields:n,needAppearances:!i&&!0===l.get("NeedAppearances"),pageIndex:s,evaluatorOptions:h.evaluatorOptions,pageRef:o};switch(d){case"Link":return new LinkAnnotation(f);case"Text":return new TextAnnotation(f);case"Widget":let e=getInheritableProperty({dict:c,key:"FT"});e=e instanceof Name?e.name:null;switch(e){case"Tx":return new TextWidgetAnnotation(f);case"Btn":return new ButtonWidgetAnnotation(\ -f);case"Ch":return new ChoiceWidgetAnnotation(f);case"Sig":return new SignatureWidgetAnnotation(f)}warn(`Unimplemented widget field type "${e}", falling back to base field type.`);return new WidgetAnnotation(f);case"Popup":return new PopupAnnotation(f);case"FreeText":return new FreeTextAnnotation(f);case"Line":return new LineAnnotation(f);case"Square":return new SquareAnnotation(f);case"Circle":return new CircleAnnotation(f);case"PolyLine":return new PolylineAnnotation(f);case"Polygon":return ne\ -w PolygonAnnotation(f);case"Caret":return new CaretAnnotation(f);case"Ink":return new InkAnnotation(f);case"Highlight":return new HighlightAnnotation(f);case"Underline":return new UnderlineAnnotation(f);case"Squiggly":return new SquigglyAnnotation(f);case"StrikeOut":return new StrikeOutAnnotation(f);case"Stamp":return new StampAnnotation(f);case"FileAttachment":return new FileAttachmentAnnotation(f);default:i||warn(d?`Unimplemented annotation type "${d}", falling back to base annotation.`:"Annot\ -ation is missing the required /Subtype.");return new Annotation(f)}}static async _getPageIndex(e,t,a){try{const r=await e.fetchIfRefAsync(t);if(!(r instanceof Dict))return-1;const i=r.getRaw("P");if(i instanceof Ref)try{return await a.ensureCatalog("getPageIndex",[i])}catch(e){info(`_getPageIndex -- not a valid page reference: "${e}".`)}if(r.has("Kids"))return-1;const n=await a.ensureDoc("numPages");for(let e=0;ee/255))||t}function getQuadPoints(e,t){const a=e.getArray("QuadPoints");if(!isNumberArray(a,null)||0===a.length||a.length%8>0)return null;const r=new \ -Float32Array(a.length);for(let e=0,i=a.length;et[2]||gt[3]))return null;r.set([d,p,f,p,d,g,f,g],e)}return r}function getTransformMatrix(e,t,a){const r=new Float32Array([1/0,1/0,-1/0,-1/0]);Util.axialAlignedBoundingBox(t,a,r);const[i,n,s,o]=r;if(i===s||n===o)return[1,0,0,1,e[0],e[1]];const c=(e[2]-e[0])/(s-i),l=(e[3]-e[1])/(o-n);return[c,0\ -,0,l,e[0]-i*c,e[1]-n*l]}class Annotation{constructor(e){const{dict:t,xref:a,annotationGlobals:r,ref:i,orphanFields:n}=e,s=n?.get(i);s&&t.set("Parent",s);this.setTitle(t.get("T"));this.setContents(t.get("Contents"));this.setModificationDate(t.get("M"));this.setFlags(t.get("F"));this.setRectangle(t.getArray("Rect"));this.setColor(t.getArray("C"));this.setBorderStyle(t);this.setAppearance(t);this.setOptionalContent(t);const o=t.get("MK");this.setBorderAndBackgroundColors(o);this.setRotation(o,t);th\ -is.ref=e.ref instanceof Ref?e.ref:null;this._streams=[];this.appearance&&this._streams.push(this.appearance);const c=!!(this.flags&ee),l=!!(this.flags&te);this.data={annotationFlags:this.flags,borderStyle:this.borderStyle,color:this.color,backgroundColor:this.backgroundColor,borderColor:this.borderColor,rotation:this.rotation,contentsObj:this._contents,hasAppearance:!!this.appearance,id:e.id,modificationDate:this.modificationDate,rect:this.rectangle,subtype:e.subtype,hasOwnCanvas:!1,noRotate:!!(\ -this.flags&Z),noHTML:c&&l,isEditable:!1,structParent:-1};if(r.structTreeRoot){let a=t.get("StructParent");this.data.structParent=a=Number.isInteger(a)&&a>=0?a:-1;r.structTreeRoot.addAnnotationIdToPage(e.pageRef,a)}if(e.collectFields){const r=t.get("Kids");if(Array.isArray(r)){const e=[];for(const t of r)t instanceof Ref&&e.push(t.toString());0!==e.length&&(this.data.kidIds=e)}this.data.actions=collectActions(a,t,ye);this.data.fieldName=this._constructFieldName(t);this.data.pageIndex=e.pageIndex}\ -const h=t.get("IT");h instanceof Name&&(this.data.it=h.name);this._isOffscreenCanvasSupported=e.evaluatorOptions.isOffscreenCanvasSupported;this._fallbackFontDict=null;this._needAppearances=!1}_hasFlag(e,t){return!!(e&t)}_buildFlags(e,t){let{flags:a}=this;if(void 0===e){if(void 0===t)return;return t?a&~Y:a&~J|Y}if(e){a|=Y;return t?a&~Q|J:a&~J|Q}a&=~(J|Q);return t?a&~Y:a|Y}_isViewable(e){return!this._hasFlag(e,K)&&!this._hasFlag(e,Q)}_isPrintable(e){return this._hasFlag(e,Y)&&!this._hasFlag(e,J)&\ -&!this._hasFlag(e,K)}mustBeViewed(e,t){const a=e?.get(this.data.id)?.noView;return void 0!==a?!a:this.viewable&&!this._hasFlag(this.flags,J)}mustBePrinted(e){const t=e?.get(this.data.id)?.noPrint;return void 0!==t?!t:this.printable}mustBeViewedWhenEditing(e,t=null){return e?!this.data.isEditable:!t?.has(this.data.id)}get viewable(){return null!==this.data.quadPoints&&(0===this.flags||this._isViewable(this.flags))}get printable(){return null!==this.data.quadPoints&&(0!==this.flags&&this._isPrinta\ -ble(this.flags))}_parseStringHelper(e){const t="string"==typeof e?stringToPDFString(e):"";return{str:t,dir:t&&"rtl"===bidi(t).dir?"rtl":"ltr"}}setDefaultAppearance(e){const{dict:t,annotationGlobals:a}=e,r=getInheritableProperty({dict:t,key:"DA"})||a.acroForm.get("DA");this._defaultAppearance="string"==typeof r?r:"";this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance)}setTitle(e){this._title=this._parseStringHelper(e)}setContents(e){this._contents=this._parseStringHelpe\ -r(e)}setModificationDate(e){this.modificationDate="string"==typeof e?e:null}setFlags(e){this.flags=Number.isInteger(e)&&e>0?e:0;this.flags&K&&"Annotation"!==this.constructor.name&&(this.flags^=K)}hasFlag(e){return this._hasFlag(this.flags,e)}setRectangle(e){this.rectangle=lookupNormalRect(e,[0,0,0,0])}setColor(e){this.color=getRgbColor(e)}setLineEndings(e){this.lineEndings=["None","None"];if(Array.isArray(e)&&2===e.length)for(let t=0;t<2;t++){const a=e[t];if(a instanceof Name)switch(a.name){case\ -"None":continue;case"Square":case"Circle":case"Diamond":case"OpenArrow":case"ClosedArrow":case"Butt":case"ROpenArrow":case"RClosedArrow":case"Slash":this.lineEndings[t]=a.name;continue}warn(`Ignoring invalid lineEnding: ${a}`)}}setRotation(e,t){this.rotation=0;let a=e instanceof Dict?e.get("R")||0:t.get("Rotate")||0;if(Number.isInteger(a)&&0!==a){a%=360;a<0&&(a+=360);a%90==0&&(this.rotation=a)}}setBorderAndBackgroundColors(e){if(e instanceof Dict){this.borderColor=getRgbColor(e.getArray("BC"),nu\ -ll);this.backgroundColor=getRgbColor(e.getArray("BG"),null)}else this.borderColor=this.backgroundColor=null}setBorderStyle(e){this.borderStyle=new AnnotationBorderStyle;if(e instanceof Dict)if(e.has("BS")){const t=e.get("BS");if(t instanceof Dict){const e=t.get("Type");if(!e||isName(e,"Border")){this.borderStyle.setWidth(t.get("W"),this.rectangle);this.borderStyle.setStyle(t.get("S"));this.borderStyle.setDashArray(t.getArray("D"))}}}else if(e.has("Border")){const t=e.getArray("Border");if(Array.\ -isArray(t)&&t.length>=3){this.borderStyle.setHorizontalCornerRadius(t[0]);this.borderStyle.setVerticalCornerRadius(t[1]);this.borderStyle.setWidth(t[2],this.rectangle);4===t.length&&this.borderStyle.setDashArray(t[3],!0)}}else this.borderStyle.setWidth(0)}setAppearance(e){this.appearance=null;const t=e.get("AP");if(!(t instanceof Dict))return;const a=t.get("N");if(a instanceof BaseStream){this.appearance=a;return}if(!(a instanceof Dict))return;const r=e.get("AS");if(!(r instanceof Name&&a.has(r.\ -name)))return;const i=a.get(r.name);i instanceof BaseStream&&(this.appearance=i)}setOptionalContent(e){this.oc=null;const t=e.get("OC");t instanceof Name?warn("setOptionalContent: Support for /Name-entry is not implemented."):t instanceof Dict&&(this.oc=t)}async loadResources(e,t){const a=await t.dict.getAsync("Resources");a&&await ObjectLoader.load(a,e,a.xref);return a}async getOperatorList(e,t,a,r){const{hasOwnCanvas:i,id:n,rect:o}=this.data;let c=this.appearance;const l=!!(i&&a&s);if(l&&(0===\ -this.width||0===this.height)){this.data.hasOwnCanvas=!1;return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}if(!c){if(!l)return{opList:new OperatorList,separateForm:!1,separateCanvas:!1};c=new StringStream("");c.dict=new Dict}const h=c.dict,u=await this.loadResources(Ia,c),d=lookupRect(h.getArray("BBox"),[0,0,1,1]),f=lookupMatrix(h.getArray("Matrix"),Fa),g=getTransformMatrix(o,d,f),p=new OperatorList;let m;this.oc&&(m=await e.parseMarkedContentProps(this.oc,null));void 0!==m&&p.add\ -Op(jt,["OC",m]);p.addOp($t,[n,o,g,f,l]);await e.getOperatorList({stream:c,task:t,resources:u,operatorList:p,fallbackFontDict:this._fallbackFontDict});p.addOp(Gt,[]);void 0!==m&&p.addOp(_t,[]);this.reset();return{opList:p,separateForm:!1,separateCanvas:l}}async save(e,t,a,r){return null}get overlaysTextContent(){return!1}get hasTextContent(){return!1}async extractTextContent(e,t,a){if(!this.appearance)return;const r=await this.loadResources(Ta,this.appearance),i=[],n=[];let s=null;const o={desire\ -dSize:Math.Infinity,ready:!0,enqueue(e,t){for(const t of e.items)if(void 0!==t.str){s||=t.transform.slice(-2);n.push(t.str);if(t.hasEOL){i.push(n.join("").trimEnd());n.length=0}}}};await e.getTextContent({stream:this.appearance,task:t,resources:r,includeMarkedContent:!0,keepWhiteSpace:!0,sink:o,viewBox:a});this.reset();n.length&&i.push(n.join("").trimEnd());if(i.length>1||i[0]){const e=this.appearance.dict,t=lookupRect(e.getArray("BBox"),null),a=lookupMatrix(e.getArray("Matrix"),null);this.data.\ -textPosition=this._transformPoint(s,t,a);this.data.textContent=i}}_transformPoint(e,t,a){const{rect:r}=this.data;t||=[0,0,1,1];a||=[1,0,0,1,0,0];const i=getTransformMatrix(r,t,a);i[4]-=r[0];i[5]-=r[1];const n=e.slice();Util.applyTransform(n,i);Util.applyTransform(n,a);return n}getFieldObject(){return this.data.kidIds?{id:this.data.id,actions:this.data.actions,name:this.data.fieldName,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,type:"",kidIds:this.data.kidIds,page:this.d\ -ata.pageIndex,rotation:this.rotation}:null}reset(){for(const e of this._streams)e.reset()}_constructFieldName(e){if(!e.has("T")&&!e.has("Parent")){warn("Unknown field name, falling back to empty field name.");return""}if(!e.has("Parent"))return stringToPDFString(e.get("T"));const t=[];e.has("T")&&t.unshift(stringToPDFString(e.get("T")));let a=e;const r=new RefSet;e.objId&&r.put(e.objId);for(;a.has("Parent");){a=a.get("Parent");if(!(a instanceof Dict)||a.objId&&r.has(a.objId))break;a.objId&&r.put\ -(a.objId);a.has("T")&&t.unshift(stringToPDFString(a.get("T")))}return t.join(".")}get width(){return this.data.rect[2]-this.data.rect[0]}get height(){return this.data.rect[3]-this.data.rect[1]}}class AnnotationBorderStyle{constructor(){this.width=1;this.rawWidth=1;this.style=fe;this.dashArray=[3];this.horizontalCornerRadius=0;this.verticalCornerRadius=0}setWidth(e,t=[0,0,0,0]){if(e instanceof Name)this.width=0;else if("number"==typeof e){if(e>0){this.rawWidth=e;const a=(t[2]-t[0])/2,r=(t[3]-t[1]\ -)/2;if(a>0&&r>0&&(e>a||e>r)){warn(`AnnotationBorderStyle.setWidth - ignoring width: ${e}`);e=1}}this.width=e}}setStyle(e){if(e instanceof Name)switch(e.name){case"S":this.style=fe;break;case"D":this.style=ge;break;case"B":this.style=pe;break;case"I":this.style=me;break;case"U":this.style=be}}setDashArray(e,t=!1){if(Array.isArray(e)){let a=!0,r=!0;for(const t of e){if(!(+t>=0)){a=!1;break}t>0&&(r=!1)}if(0===e.length||a&&!r){this.dashArray=e;t&&this.setStyle(Name.get("D"))}else this.width=0}else e\ -&&(this.width=0)}setHorizontalCornerRadius(e){Number.isInteger(e)&&(this.horizontalCornerRadius=e)}setVerticalCornerRadius(e){Number.isInteger(e)&&(this.verticalCornerRadius=e)}}class MarkupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;if(t.has("IRT")){const e=t.getRaw("IRT");this.data.inReplyTo=e instanceof Ref?e.toString():null;const a=t.get("RT");this.data.replyType=a instanceof Name?a.name:V}let a=null;if(this.data.replyType===G){const e=t.get("IRT");this.setTitle(e.g\ -et("T"));this.data.titleObj=this._title;this.setContents(e.get("Contents"));this.data.contentsObj=this._contents;if(e.has("CreationDate")){this.setCreationDate(e.get("CreationDate"));this.data.creationDate=this.creationDate}else this.data.creationDate=null;if(e.has("M")){this.setModificationDate(e.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;a=e.getRaw("Popup");if(e.has("C")){this.setColor(e.getArray("C"));this.data.color=this.color}else this.da\ -ta.color=null}else{this.data.titleObj=this._title;this.setCreationDate(t.get("CreationDate"));this.data.creationDate=this.creationDate;a=t.getRaw("Popup");t.has("C")||(this.data.color=null)}this.data.popupRef=a instanceof Ref?a.toString():null;t.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(t.get("RC")))}setCreationDate(e){this.creationDate="string"==typeof e?e:null}_setDefaultAppearance({xref:e,extra:t,strokeColor:a,fillColor:r,blendMode:i,strokeAlpha:n,fillAlpha:s,pointsCallback:\ -o}){const c=this.data.rect=[1/0,1/0,-1/0,-1/0],l=["q"];t&&l.push(t);a&&l.push(`${a[0]} ${a[1]} ${a[2]} RG`);r&&l.push(`${r[0]} ${r[1]} ${r[2]} rg`);const h=this.data.quadPoints||Float32Array.from([this.rectangle[0],this.rectangle[3],this.rectangle[2],this.rectangle[3],this.rectangle[0],this.rectangle[1],this.rectangle[2],this.rectangle[1]]);for(let e=0,t=h.length;e"string"==\ -typeof e)).map((e=>stringToPDFString(e))):e instanceof Name?stringToPDFString(e.name):"string"==typeof e?stringToPDFString(e):null}hasFieldFlag(e){return!!(this.data.fieldFlags&e)}_isViewable(e){return!0}mustBeViewed(e,t){return t?this.viewable:super.mustBeViewed(e,t)&&!this._hasFlag(this.flags,Q)}getRotationMatrix(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);return 0===t?Fa:getRotationMatrix(t,this.width,this.height)}getBorderAndBackgroundAppearances(e){let t=e?.get(thi\ -s.data.id)?.rotation;void 0===t&&(t=this.rotation);if(!this.backgroundColor&&!this.borderColor)return"";const a=0===t||180===t?`0 0 ${this.width} ${this.height} re`:`0 0 ${this.height} ${this.width} re`;let r="";this.backgroundColor&&(r=`${getPdfColor(this.backgroundColor,!0)} ${a} f `);if(this.borderColor){r+=`${this.borderStyle.width||1} w ${getPdfColor(this.borderColor,!1)} ${a} S `}return r}async getOperatorList(e,t,a,r){if(a&l&&!(this instanceof SignatureWidgetAnnotation)&&!this.data.noHTML\ -&&!this.data.hasOwnCanvas)return{opList:new OperatorList,separateForm:!0,separateCanvas:!1};if(!this._hasText)return super.getOperatorList(e,t,a,r);const i=await this._getAppearance(e,t,a,r);if(this.appearance&&null===i)return super.getOperatorList(e,t,a,r);const n=new OperatorList;if(!this._defaultAppearance||null===i)return{opList:n,separateForm:!1,separateCanvas:!1};const o=!!(this.data.hasOwnCanvas&&a&s),c=[0,0,this.width,this.height],h=getTransformMatrix(this.data.rect,c,[1,0,0,1,0,0]);let \ -u;this.oc&&(u=await e.parseMarkedContentProps(this.oc,null));void 0!==u&&n.addOp(jt,["OC",u]);n.addOp($t,[this.data.id,this.data.rect,h,this.getRotationMatrix(r),o]);const d=new StringStream(i);await e.getOperatorList({stream:d,task:t,resources:this._fieldResources.mergedResources,operatorList:n});n.addOp(Gt,[]);void 0!==u&&n.addOp(_t,[]);return{opList:n,separateForm:!1,separateCanvas:o}}_getMKDict(e){const t=new Dict(null);e&&t.set("R",e);t.setIfArray("BC",getPdfColorArray(this.borderColor));t.\ -setIfArray("BG",getPdfColorArray(this.backgroundColor));return t.size>0?t:null}amendSavedDict(e,t){}setValue(e,t,a,r){const{dict:i,ref:n}=function getParentToUpdate(e,t,a){const r=new RefSet,i=e,n={dict:null,ref:null};for(;e instanceof Dict&&!r.has(t);){r.put(t);if(e.has("T"))break;if(!((t=e.getRaw("Parent"))instanceof Ref))return n;e=a.fetch(t)}if(e instanceof Dict&&e!==i){n.dict=e;n.ref=t}return n}(e,this.ref,a);if(i){if(!r.has(n)){const e=i.clone();e.set("V",t);r.put(n,{data:e});return e}}els\ -e e.set("V",t);return null}async save(e,t,a,r){const i=a?.get(this.data.id),n=this._buildFlags(i?.noView,i?.noPrint);let s=i?.value,o=i?.rotation;if(s===this.data.fieldValue||void 0===s){if(!this._hasValueFromXFA&&void 0===o&&void 0===n)return;s||=this.data.fieldValue}if(void 0===o&&!this._hasValueFromXFA&&Array.isArray(s)&&Array.isArray(this.data.fieldValue)&&isArrayEqual(s,this.data.fieldValue)&&void 0===n)return;void 0===o&&(o=this.rotation);let l=null;if(!this._needAppearances){l=await this.\ -_getAppearance(e,t,c,a);if(null===l&&void 0===n)return}let h=!1;if(l?.needAppearances){h=!0;l=null}const{xref:u}=e,d=u.fetchIfRef(this.ref);if(!(d instanceof Dict))return;const f=new Dict(u);for(const e of d.getKeys())"AP"!==e&&f.set(e,d.getRaw(e));if(void 0!==n){f.set("F",n);if(null===l&&!h){const e=d.getRaw("AP");e&&f.set("AP",e)}}const g={path:this.data.fieldName,value:s},p=this.setValue(f,Array.isArray(s)?s.map(stringToAsciiOrUTF16BE):stringToAsciiOrUTF16BE(s),u,r);this.amendSavedDict(a,p||f\ -);const m=this._getMKDict(o);m&&f.set("MK",m);r.put(this.ref,{data:f,xfa:g,needAppearances:h});if(null!==l){const e=u.getNewTemporaryRef(),t=new Dict(u);f.set("AP",t);t.set("N",e);const i=this._getSaveFieldResources(u),n=new StringStream(l),s=n.dict=new Dict(u);s.setIfName("Subtype","Form");s.set("Resources",i);const c=o%180==0?[0,0,this.width,this.height]:[0,0,this.height,this.width];s.set("BBox",c);const h=this.getRotationMatrix(a);h!==Fa&&s.set("Matrix",h);r.put(e,{data:n,xfa:null,needAppeara\ -nces:!1})}f.set("M",`D:${getModificationDate()}`)}async _getAppearance(e,t,a,r){if(this.data.password)return null;const n=r?.get(this.data.id);let s,o;if(n){s=n.formattedValue||n.value;o=n.rotation}if(void 0===o&&void 0===s&&!this._needAppearances&&(!this._hasValueFromXFA||this.appearance))return null;const l=this.getBorderAndBackgroundAppearances(r);if(void 0===s){s=this.data.fieldValue;if(!s)return`/Tx BMC q ${l}Q EMC`}Array.isArray(s)&&1===s.length&&(s=s[0]);assert("string"==typeof s,"Expecte\ -d `value` to be a string.");s=s.trimEnd();if(this.data.combo){const e=this.data.options.find((({exportValue:e})=>s===e));s=e?.displayValue||s}if(""===s)return`/Tx BMC q ${l}Q EMC`;void 0===o&&(o=this.rotation);let h,u=-1;if(this.data.multiLine){h=s.split(/\\r\\n?|\\n/).map((e=>e.normalize("NFC")));u=h.length}else h=[s.replace(/\\r\\n?|\\n/,"").normalize("NFC")];let{width:d,height:f}=this;90!==o&&270!==o||([d,f]=[f,d]);this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(thi\ -s._defaultAppearance="/Helvetica 0 Tf 0 g"));let g,p,m,b=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);const y=[];let w=!1;for(const e of h){const t=b.encodeString(e);t.length>1&&(w=!0);y.push(t.join(""))}if(w&&a&c)return{needAppearances:!0};if(w&&this._isOffscreenCanvasSupported){const a=this.data.comb?"monospace":"sans-serif",r=new FakeUnicodeFont(e.xref,a),i=r.createFontResources(h.join("")),n=i.getRaw("Font");if(this._fieldResou\ -rces.mergedResources.has("Font")){const e=this._fieldResources.mergedResources.get("Font");for(const t of n.getKeys())e.set(t,n.getRaw(t))}else this._fieldResources.mergedResources.set("Font",n);const o=r.fontName.name;b=await WidgetAnnotation._getFontData(e,t,{fontName:o,fontSize:0},i);for(let e=0,t=y.length;e2)return`/Tx BMC q ${l}BT `+g+` 1 0 0 1 ${numberToString(2)} ${numberToString(C)} Tm (${escapeString(y[0])}) Tj ET Q EMC`;return`/Tx BMC q ${l}BT `+g+` 1 0 0 1 0 0 Tm ${this._renderText(y[0],b,p,d,k,{shift:0},2,C)} ET Q EMC`}static async _getFontData(e,t,a,r){const i=new OperatorList,n={font:null,clone(){return this}},{fontName:s,fontSize:o}=a;await e.handleSetFont(r,[s&&Name.get(s),o],null,i,t,n,null);return n.font}_ge\ -tTextWidth(e,t){return Math.sumPrecise(t.charsToGlyphs(e).map((e=>e.width)))/1e3}_computeFontSize(e,t,r,i,n){let{fontSize:s}=this.data.defaultAppearanceData,o=(s||12)*a,c=Math.round(e/o);if(!s){const roundWithTwoDigits=e=>Math.floor(100*e)/100;if(-1===n){const n=this._getTextWidth(r,i);s=roundWithTwoDigits(Math.min(e/a,t/n));c=1}else{const l=r.split(/\\r\\n?|\\n/),h=[];for(const e of l){const t=i.encodeString(e).join(""),a=i.charsToGlyphs(t),r=i.getCharPositions(t);h.push({line:t,glyphs:a,positions\ -:r})}const isTooBig=a=>{let r=0;for(const n of h){r+=this._splitLine(null,i,a,t,n).length*a;if(r>e)return!0}return!1};c=Math.max(c,n);for(;;){o=e/c;s=roundWithTwoDigits(o/a);if(!isTooBig(s))break;c++}}const{fontName:l,fontColor:h}=this.data.defaultAppearanceData;this._defaultAppearance=function createDefaultAppearance({fontSize:e,fontName:t,fontColor:a}){return`/${escapePDFName(t)} ${e} Tf ${getPdfColor(a,!0)}`}({fontSize:s,fontName:l,fontColor:h})}return[this._defaultAppearance,s,e/c]}_renderTe\ -xt(e,t,a,r,i,n,s,o){let c;if(1===i){c=(r-this._getTextWidth(e,t)*a)/2}else if(2===i){c=r-this._getTextWidth(e,t)*a-s}else c=s;const l=numberToString(c-n.shift);n.shift=c;return`${l} ${o=numberToString(o)} Td (${escapeString(e)}) Tj`}_getSaveFieldResources(e){const{localResources:t,appearanceResources:a,acroFormResources:r}=this._fieldResources,i=this.data.defaultAppearanceData?.fontName;if(!i)return t||Dict.empty;for(const e of[t,a])if(e instanceof Dict){const t=e.get("Font");if(t instanceof Dic\ -t&&t.has(i))return e}if(r instanceof Dict){const a=r.get("Font");if(a instanceof Dict&&a.has(i)){const r=new Dict(e);r.set(i,a.getRaw(i));const n=new Dict(e);n.set("Font",r);return Dict.merge({xref:e,dictArray:[n,t],mergeSubDicts:!0})}}return t||Dict.empty}getFieldObject(){return null}}class TextWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t}=e;if(t.has("PMD")){this.flags|=J;this.data.hidden=!0;warn("Barcodes are not supported")}this.data.hasOwnCanvas=this.data.re\ -adOnly&&!this.data.noHTML;this._hasText=!0;"string"!=typeof this.data.fieldValue&&(this.data.fieldValue="");let a=getInheritableProperty({dict:t,key:"Q"});(!Number.isInteger(a)||a<0||a>2)&&(a=null);this.data.textAlignment=a;let r=getInheritableProperty({dict:t,key:"MaxLen"});(!Number.isInteger(r)||r<0)&&(r=0);this.data.maxLen=r;this.data.multiLine=this.hasFieldFlag(ie);this.data.comb=this.hasFieldFlag(de)&&!this.data.multiLine&&!this.data.password&&!this.hasFieldFlag(le)&&0!==this.data.maxLen;th\ -is.data.doNotScroll=this.hasFieldFlag(ue);const{data:{actions:i}}=this;if(!i)return;const n=/^AF(Date|Time)_(?:Keystroke|Format)(?:Ex)?\\([\'"]?([^\'"]+)[\'"]?\\);$/;let s=!1;(1===i.Format?.length&&1===i.Keystroke?.length&&n.test(i.Format[0])&&n.test(i.Keystroke[0])||0===i.Format?.length&&1===i.Keystroke?.length&&n.test(i.Keystroke[0])||0===i.Keystroke?.length&&1===i.Format?.length&&n.test(i.Format[0]))&&(s=!0);const o=[];i.Format&&o.push(...i.Format);i.Keystroke&&o.push(...i.Keystroke);if(s){delete \ -i.Keystroke;i.Format=o}for(const e of o){const t=e.match(n);if(!t)continue;const a="Date"===t[1];let r=t[2];const i=parseInt(r,10);isNaN(i)||Math.floor(Math.log10(i))+1!==t[2].length||(r=(a?Pn:Ln)[i]??r);this.data.datetimeFormat=r;if(!s)break;if(a){if(/HH|MM|ss|h/.test(r)){this.data.datetimeType="datetime-local";this.data.timeStep=/ss/.test(r)?1:60}else this.data.datetimeType="date";break}this.data.datetimeType="time";this.data.timeStep=/ss/.test(r)?1:60;break}}get hasTextContent(){return!!this.\ -appearance&&!this._needAppearances}_getCombAppearance(e,t,a,r,i,n,s,o,c,l,h){const u=i/this.data.maxLen,d=this.getBorderAndBackgroundAppearances(h),f=[],g=t.getCharPositions(a);for(const[e,t]of g)f.push(`(${escapeString(a.substring(e,t))}) Tj`);const p=f.join(` ${numberToString(u)} 0 Td `);return`/Tx BMC q ${d}BT `+e+` 1 0 0 1 ${numberToString(s)} ${numberToString(o+c)} Tm ${p} ET Q EMC`}_getMultilineAppearance(e,t,a,r,i,n,s,o,c,l,h,u){const d=[],f=i-2*o,g={shift:0};for(let e=0,n=t.length;er){c.push(e.substring(d,a));d=a;f=p;l=-1;u=-1}else{f+=p;l=a;h=i;u=t}else if(f+p>r)if(-1!==l){c.push(e.substring(d,h));d=h;t=u+1;l=-1;f=0}else{c.push(e.substring(d,a));d=a;f=p}else f+=p}dt?`\\\\${t}`:"\\\\s+"));new RegExp(`^\\\\s*${n}\\\\s*$`).test(this.data.fieldValue)&&(this.data.textContent=this.data.fieldValue.split("\\n"))}getFieldObject(){return{id:this.data.id,value:this.data.fieldValue,defaultValue:this.data.defaultFieldValue||"",multiline:this.data.multiLine,password:this.data.password,charLimit:this.data.maxLen,comb:this.data.comb,editable:!this.data.readOnly,hidden:this.data.hidden,name:this.data.fieldName,rect:this.data.rect,actions:\ -this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,datetimeFormat:this.data.datetimeFormat,hasDatetimeHTML:!!this.data.datetimeType,type:"text"}}}class ButtonWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.checkedAppearance=null;this.uncheckedAppearance=null;const t=this.hasFieldFlag(se),a=this.hasFieldFlag(oe);this.data.checkBox=!t&&!a;this.data.radioButton=t&&!a;this.data.pushButton=a;th\ -is.data.isTooltipOnly=!1;if(this.data.checkBox)this._processCheckBox(e);else if(this.data.radioButton)this._processRadioButton(e);else if(this.data.pushButton){this.data.hasOwnCanvas=!0;this.data.noHTML=!1;this._processPushButton(e)}else warn("Invalid field flags for button widget annotation")}async getOperatorList(e,t,a,r){if(this.data.pushButton)return super.getOperatorList(e,t,a,!1,r);let i=null,n=null;if(r){const e=r.get(this.data.id);i=e?e.value:null;n=e?e.rotation:null}if(null===i&&this.ap\ -pearance)return super.getOperatorList(e,t,a,r);null==i&&(i=this.data.checkBox?this.data.fieldValue===this.data.exportValue:this.data.fieldValue===this.data.buttonValue);const s=i?this.checkedAppearance:this.uncheckedAppearance;if(s){const i=this.appearance,o=lookupMatrix(s.dict.getArray("Matrix"),Fa);n&&s.dict.set("Matrix",this.getRotationMatrix(r));this.appearance=s;const c=super.getOperatorList(e,t,a,r);this.appearance=i;s.dict.set("Matrix",o);return c}return{opList:new OperatorList,separateFo\ -rm:!1,separateCanvas:!1}}async save(e,t,a,r){this.data.checkBox?this._saveCheckbox(e,t,a,r):this.data.radioButton&&this._saveRadioButton(e,t,a,r)}async _saveCheckbox(e,t,a,r){if(!a)return;const i=a.get(this.data.id),n=this._buildFlags(i?.noView,i?.noPrint);let s=i?.rotation,o=i?.value;if(void 0===s&&void 0===n){if(void 0===o)return;if(this.data.fieldValue===this.data.exportValue===o)return}let c=e.xref.fetchIfRef(this.ref);if(!(c instanceof Dict))return;c=c.clone();void 0===s&&(s=this.rotation);\ -void 0===o&&(o=this.data.fieldValue===this.data.exportValue);const l={path:this.data.fieldName,value:o?this.data.exportValue:""},h=Name.get(o?this.data.exportValue:"Off");this.setValue(c,h,e.xref,r);c.set("AS",h);c.set("M",`D:${getModificationDate()}`);void 0!==n&&c.set("F",n);const u=this._getMKDict(s);u&&c.set("MK",u);r.put(this.ref,{data:c,xfa:l,needAppearances:!1})}async _saveRadioButton(e,t,a,r){if(!a)return;const i=a.get(this.data.id),n=this._buildFlags(i?.noView,i?.noPrint);let s=i?.rotat\ -ion,o=i?.value;if(void 0===s&&void 0===n){if(void 0===o)return;if(this.data.fieldValue===this.data.buttonValue===o)return}let c=e.xref.fetchIfRef(this.ref);if(!(c instanceof Dict))return;c=c.clone();void 0===o&&(o=this.data.fieldValue===this.data.buttonValue);void 0===s&&(s=this.rotation);const l={path:this.data.fieldName,value:o?this.data.buttonValue:""},h=Name.get(o?this.data.buttonValue:"Off");o&&this.setValue(c,h,e.xref,r);c.set("AS",h);c.set("M",`D:${getModificationDate()}`);void 0!==n&&c.s\ -et("F",n);const u=this._getMKDict(s);u&&c.set("MK",u);r.put(this.ref,{data:c,xfa:l,needAppearances:!1})}_getDefaultCheckedAppearance(e,t){const{width:a,height:r}=this,i=[0,0,a,r],n=.8*Math.min(a,r);let s,o;if("check"===t){s={width:.755*n,height:.705*n};o="3"}else if("disc"===t){s={width:.791*n,height:.705*n};o="l"}else unreachable(`_getDefaultCheckedAppearance - unsupported type: ${t}`);const c=`q BT /PdfJsZaDb ${n} Tf 0 g ${numberToString((a-s.width)/2)} ${numberToString((r-s.height)/2)} Td (${\ -o}) Tj ET Q`,l=new Dict(e.xref);l.set("FormType",1);l.setIfName("Subtype","Form");l.setIfName("Type","XObject");l.set("BBox",i);l.set("Matrix",[1,0,0,1,0,0]);l.set("Length",c.length);const h=new Dict(e.xref),u=new Dict(e.xref);u.set("PdfJsZaDb",this.fallbackFontDict);h.set("Font",u);l.set("Resources",h);this.checkedAppearance=new StringStream(c);this.checkedAppearance.dict=l;this._streams.push(this.checkedAppearance)}_processCheckBox(e){const t=e.dict.get("AP");if(!(t instanceof Dict))return;con\ -st a=t.get("N");if(!(a instanceof Dict))return;const r=this._decodeFormValue(e.dict.get("AS"));"string"==typeof r&&(this.data.fieldValue=r);const i=null!==this.data.fieldValue&&"Off"!==this.data.fieldValue?this.data.fieldValue:"Yes",n=this._decodeFormValue(a.getKeys());if(0===n.length)n.push("Off",i);else if(1===n.length)"Off"===n[0]?n.push(i):n.unshift("Off");else if(n.includes(i)){n.length=0;n.push("Off",i)}else{const e=n.find((e=>"Off"!==e));n.length=0;n.push("Off",e)}n.includes(this.data.fie\ -ldValue)||(this.data.fieldValue="Off");this.data.exportValue=n[1];const s=a.get(this.data.exportValue);this.checkedAppearance=s instanceof BaseStream?s:null;const o=a.get("Off");this.uncheckedAppearance=o instanceof BaseStream?o:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"check");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(th\ -is.data.defaultFieldValue="Off")}_processRadioButton(e){this.data.buttonValue=null;const t=e.dict.get("Parent");if(t instanceof Dict){this.parent=e.dict.getRaw("Parent");const a=t.get("V");a instanceof Name&&(this.data.fieldValue=this._decodeFormValue(a))}const a=e.dict.get("AP");if(!(a instanceof Dict))return;const r=a.get("N");if(!(r instanceof Dict))return;for(const e of r.getKeys())if("Off"!==e){this.data.buttonValue=this._decodeFormValue(e);break}const i=r.get(this.data.buttonValue);this.ch\ -eckedAppearance=i instanceof BaseStream?i:null;const n=r.get("Off");this.uncheckedAppearance=n instanceof BaseStream?n:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"disc");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processPushButton(e){const{dict:t,annotationGlobals:a}=e;if(t.has("A")||t.has\ -("AA")||this.data.alternativeText){this.data.isTooltipOnly=!t.has("A")&&!t.has("AA");Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:a.baseUrl,docAttachments:a.attachments})}else warn("Push buttons without action dictionaries are not supported")}getFieldObject(){let e,t="button";if(this.data.checkBox){t="checkbox";e=this.data.exportValue}else if(this.data.radioButton){t="radiobutton";e=this.data.buttonValue}return{id:this.data.id,value:this.data.fieldValue||"Off",defaultVa\ -lue:this.data.defaultFieldValue,exportValues:e,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,hidden:this.data.hidden,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:t}}get fallbackFontDict(){const e=new Dict;e.setIfName("BaseFont","ZapfDingbats");e.setIfName("Type","FallbackType");e.setIfName("Subtype","FallbackType");e.setIfName("Encoding","ZapfDingbatsEncoding");retu\ -rn shadow(this,"fallbackFontDict",e)}}class ChoiceWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.indices=t.getArray("I");this.hasIndices=Array.isArray(this.indices)&&this.indices.length>0;this.data.options=[];const r=getInheritableProperty({dict:t,key:"Opt"});if(Array.isArray(r))for(let e=0,t=r.length;e=0&&t0&&(this.data.options=this.data.fieldValue.map((e=>({exportValue:e,displayValue:e}))));t\ -his.data.combo=this.hasFieldFlag(ce);this.data.multiSelect=this.hasFieldFlag(he);this._hasText=!0}getFieldObject(){const e=this.data.combo?"combobox":"listbox",t=this.data.fieldValue.length>0?this.data.fieldValue[0]:null;return{id:this.data.id,value:t,defaultValue:this.data.defaultFieldValue,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,numItems:this.data.fieldValue.length,multipleSelection:this.data.multiSelect,hidden:this.data.hidden,actions:this.data.actions,items:\ -this.data.options,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:e}}amendSavedDict(e,t){if(!this.hasIndices)return;let a=e?.get(this.data.id)?.value;Array.isArray(a)||(a=[a]);const r=[],{options:i}=this.data;for(let e=0,t=0,n=i.length;ea){a=r;t=e}}[f,g]=this._computeFontSize(e,c-4,t,d,-1)}const p=g*a,m=(p-g)/2,b=Math.floor(l/p);let y=0;if(u.length>0){const e=Math.min(...u),t=Math.max(...u);y=Math.max(0,t-b+1);y>e&&(y=e)}const w=Math.min(y+b+1,h),x=["/Tx \ -BMC q",`1 1 ${c} ${l} re W n`];if(u.length){x.push("0.600006 0.756866 0.854904 rg");for(const e of u)y<=e&&ee.trimEnd()));const{coords:e,bbox:t,matrix:r}=FakeUnicodeFont.getFirstPositionInfo(this.rectangle,this.rotation,a);this.data.textPosition=this._transformPoint(e,t,r)}if(this._isOffscreenCanvasSupported){const i=e.dict.get("CA"),n=new FakeUnicodeFont(r,"sans-serif");this.appearance=n.cre\ -ateAppearance(this._contents.str,this.rectangle,this.rotation,a,t,i);this._streams.push(this.appearance)}else warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.")}}get hasTextContent(){return this._hasAppearance}static createNewDict(e,t,{apRef:a,ap:r}){const{color:i,date:n,fontSize:s,oldAnnotation:o,rect:c,rotation:l,user:h,value:u}=e,d=o||new Dict(t);d.setIfNotExists("Type",Name.get("Annot"));d.setIfNotExists("Subtype",Name.get("FreeText"));d.set(o?\ -"M":"CreationDate",`D:${getModificationDate(n)}`);o&&d.delete("RC");d.setIfArray("Rect",c);const f=`/Helv ${s} Tf ${getPdfColor(i,!0)}`;d.set("DA",f);d.setIfDefined("Contents",stringToAsciiOrUTF16BE(u));d.setIfNotExists("F",4);d.setIfNotExists("Border",[0,0,0]);d.setIfNumber("Rotate",l);d.setIfDefined("T",stringToAsciiOrUTF16BE(h));if(a||r){const e=new Dict(t);d.set("AP",e);e.set("N",a||r)}return d}static async createNewAppearanceStream(e,t,r){const{baseFontRef:i,evaluator:n,task:s}=r,{color:o,f\ -ontSize:c,rect:l,rotation:h,value:u}=e;if(!o)return null;const d=new Dict(t),f=new Dict(t);if(i)f.set("Helv",i);else{const e=new Dict(t);e.setIfName("BaseFont","Helvetica");e.setIfName("Type","Font");e.setIfName("Subtype","Type1");e.setIfName("Encoding","WinAnsiEncoding");f.set("Helv",e)}d.set("Font",f);const g=await WidgetAnnotation._getFontData(n,s,{fontName:"Helv",fontSize:c},d),[p,m,b,y]=l;let w=b-p,x=y-m;h%180!=0&&([w,x]=[x,w]);const S=u.split("\\n"),k=c/1e3;let C=-1/0;const v=[];for(let e o\ -f S){const t=g.encodeString(e);if(t.length>1)return null;e=t.join("");v.push(e);let a=0;const r=g.charsToGlyphs(e);for(const e of r)a+=e.width*k;C=Math.max(C,a)}let F=1;C>w&&(F=w/C);let T=1;const O=a*c,M=1*c,D=O*S.length;D>x&&(T=x/D);const R=c*Math.min(F,T);let N,E,L;switch(h){case 0:L=[1,0,0,1];E=[l[0],l[1],w,x];N=[l[0],l[3]-M];break;case 90:L=[0,1,-1,0];E=[l[1],-l[2],w,x];N=[l[1],-l[0]-M];break;case 180:L=[-1,0,0,-1];E=[-l[2],-l[3],w,x];N=[-l[2],-l[1]-M];break;case 270:L=[0,-1,1,0];E=[-l[3],l[\ -0],w,x];N=[-l[3],l[2]-M]}const j=["q",`${L.join(" ")} 0 0 cm`,`${E.join(" ")} re W n`,"BT",`${getPdfColor(o,!0)}`,`0 Tc /Helv ${numberToString(R)} Tf`];j.push(`${N.join(" ")} Td (${escapeString(v[0])}) Tj`);const _=numberToString(O);for(let e=1,t=v.length;e{e.push(`${r[0]} ${r[1]} m`,`${r[2]} ${r[3]} l`,"S");return[t[0]-o,t[7]-o,t[2]+o,t[3]\ -+o]}})}}}class SquareAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.data.annotationType=D;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),r=t.get("CA"),i=getPdfColorArray(getRgbColor(t.getArray("IC"),null)),n=i?r:null;if(0===this.borderStyle.width&&!i)return;this._setDefaultAppearance({xref:a,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:i,strokeAlpha:r,fillAlpha\ -:n,pointsCallback:(e,t)=>{const a=t[4]+this.borderStyle.width/2,r=t[5]+this.borderStyle.width/2,n=t[6]-t[4]-this.borderStyle.width,s=t[3]-t[7]-this.borderStyle.width;e.push(`${a} ${r} ${n} ${s} re`);i?e.push("B"):e.push("S");return[t[0],t[7],t[2],t[3]]}})}}}class CircleAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.data.annotationType=R;if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),r=t.get("CA"),i=getPdfColorArray(getRgbColor(t.getArra\ -y("IC"),null)),n=i?r:null;if(0===this.borderStyle.width&&!i)return;const s=4/3*Math.tan(Math.PI/8);this._setDefaultAppearance({xref:a,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:i,strokeAlpha:r,fillAlpha:n,pointsCallback:(e,t)=>{const a=t[0]+this.borderStyle.width/2,r=t[1]-this.borderStyle.width/2,n=t[6]-this.borderStyle.width/2,o=t[7]+this.borderStyle.width/2,c=a+(n-a)/2,l=r+(o-r)/2,h=(n-a)/2*s,u=(o-r)/2*s;e.push(`${c} ${o} m`,`${c+h} ${o} ${n} ${l+u} ${n} ${l} c`,`${n} ${l-u} $\ -{c+h} ${r} ${c} ${r} c`,`${c-h} ${r} ${a} ${l-u} ${a} ${l} c`,`${a} ${l+u} ${c-h} ${o} ${c} ${o} c`,"h");i?e.push("B"):e.push("S");return[t[0],t[7],t[2],t[3]]}})}}}class PolylineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:a}=e;this.data.annotationType=E;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.vertices=null;if(!(this instanceof PolygonAnnotation)){this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings}const r=\ -t.getArray("Vertices");if(!isNumberArray(r,null))return;const i=this.data.vertices=Float32Array.from(r);if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),r=t.get("CA");let n,s=getRgbColor(t.getArray("IC"),null);s&&(s=getPdfColorArray(s));n=s?this.color?s.every(((t,a)=>t===e[a]))?"f":"B":"f":"S";const o=this.borderStyle.width||1,c=2*o,l=[1/0,1/0,-1/0,-1/0];for(let e=0,t=i.length;e{for(let t=0,a=i.length;t{for(const t of this.data.inkLists){for(let a=0,r=t.length;a0){const e=new Dict(t);g.set("BS",e);e.set("W",d)}g.setIfArray("C",getPdfColorArray(n));g.setIfNumber("CA",o);if(r||a){const e=new Dict(t);g.set("AP",e);e.set("N",a||r)}return g}static async createNewAppearanceStream(e,t,a){if(e.outlines)return this.createNewAppearanceStreamForHighlight(e,t,a);const{color:r,rect:i,pat\ -hs:n,thickness:s,opacity:o}=e;if(!r)return null;const c=[`${s} w 1 J 1 j`,`${getPdfColor(r,!1)}`];1!==o&&c.push("/R0 gs");for(const e of n.lines){c.push(`${numberToString(e[4])} ${numberToString(e[5])} m`);for(let t=6,a=e.length;t{e.push(`${t[0]} ${t[1]} m`,`${t[2]} ${t[3]} l`,`${t[6]} ${t[7]} l`,`${t[4]} ${t[5]} l`,"f");return[t[0],t[7],t[2],t[3]]}})}}else this.data.popupRef=null}get overlaysTextContent(){return!0\ -}static createNewDict(e,t,{apRef:a,ap:r}){const{color:i,date:n,oldAnnotation:s,opacity:o,rect:c,rotation:l,user:h,quadPoints:u}=e,d=s||new Dict(t);d.setIfNotExists("Type",Name.get("Annot"));d.setIfNotExists("Subtype",Name.get("Highlight"));d.set(s?"M":"CreationDate",`D:${getModificationDate(n)}`);d.setIfArray("Rect",c);d.setIfNotExists("F",4);d.setIfNotExists("Border",[0,0,0]);d.setIfNumber("Rotate",l);d.setIfArray("QuadPoints",u);d.setIfArray("C",getPdfColorArray(i));d.setIfNumber("CA",o);d.set\ -IfDefined("T",stringToAsciiOrUTF16BE(h));if(a||r){const e=new Dict(t);d.set("AP",e);e.set("N",a||r)}return d}static async createNewAppearanceStream(e,t,a){const{color:r,rect:i,outlines:n,opacity:s}=e;if(!r)return null;const o=[`${getPdfColor(r,!0)}`,"/R0 gs"],c=[];for(const e of n){c.length=0;c.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);for(let t=2,a=e.length;t{e.push(`${t[4]} ${t[5]+1.3} m`,`${t[6]} ${t[7]+1.3} l`,"S");return[t[0],t[7],t[2],t[3]]}})}}else this.data.popupRef=null}get overlaysTextContent(){return!0}}class SquigglyAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xre\ -f:a}=e;this.data.annotationType=_;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=getPdfColorArray(this.color,[0,0,0]),r=t.get("CA");this._setDefaultAppearance({xref:a,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:r,pointsCallback:(e,t)=>{const a=(t[1]-t[5])/6;let r=a,i=t[4];const n=t[5],s=t[6];e.push(`${i} ${n+r} m`);do{i+=2;r=0===r?a:0;e.push(`${i} ${n+r} l`)}while(i{e.push((t[0]+t[4])/2+" "+(t[1]+t[5])/2+" m",(t[2]+t[6])/2+" "+(t[3]+t[7])/2+" l","S");return[t[0],t[7],t[2],t[3]]}})}}else this.data.\ -popupRef=null}get overlaysTextContent(){return!0}}class StampAnnotation extends MarkupAnnotation{#pe=null;constructor(e){super(e);this.data.annotationType=X;this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1}mustBeViewedWhenEditing(e,t=null){if(e){if(!this.data.isEditable)return!0;this.#pe??=this.data.hasOwnCanvas;this.data.hasOwnCanvas=!0;return!0}if(null!==this.#pe){this.data.hasOwnCanvas=this.#pe;this.#pe=null}return!t?.has(this.data.id)}stati\ -c async createImage(e,t){const{width:a,height:r}=e,i=new OffscreenCanvas(a,r),n=i.getContext("2d",{alpha:!0});n.drawImage(e,0,0);const s=n.getImageData(0,0,a,r).data,o=new Uint32Array(s.buffer),c=o.some(FeatureTest.isLittleEndian?e=>e>>>24!=255:e=>!!(255&~e));if(c){n.fillStyle="white";n.fillRect(0,0,a,r);n.drawImage(e,0,0)}const l=i.convertToBlob({type:"image/jpeg",quality:1}).then((e=>e.arrayBuffer())),h=Name.get("XObject"),u=Name.get("Image"),d=new Dict(t);d.set("Type",h);d.set("Subtype",u);d.\ -set("BitsPerComponent",8);d.setIfName("ColorSpace","DeviceRGB");d.setIfName("Filter","DCTDecode");d.set("BBox",[0,0,a,r]);d.set("Width",a);d.set("Height",r);let f=null;if(c){const e=new Uint8Array(o.length);if(FeatureTest.isLittleEndian)for(let t=0,a=o.length;t>>24;else for(let t=0,a=o.length;t=0&&n<=1?n:null}}const pc={get r(){return shadow(this,"r",new Uint8Array([7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21]))},get k(){return shadow(this\ -,"k",new Int32Array([-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-6\ -40364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]))}};function calculateMD5(e,t,a){let r=1732584193,i=-271733879,n=-1732584194,s=271733878;const o=a+72&-64,c=new Uint8Array(o);let l,h;for(l=0;l>5&255;c[l++]=a>>13&255;c[l++]=a>>21&255;c[l++]=a>>>29&255;l+\ -=3;const d=new Int32Array(16),{k:f,r:g}=pc;for(l=0;l>>32-n)|0;a=r}r=r+a|0;i=i+o|0;n=n+u|0;s=s+p|0}return new Uint8Array([255&r,r>>8&255,r>>16&255,r>>>24&255,255&i,i>>8&255,i>>16&255,i>>>24&255,255&n,n>>8&255,n>>16&25\ -5,n>>>24&255,255&s,s>>8&255,s>>16&255,s>>>24&255])}function decodeString(e){try{return stringToUTF8String(e)}catch(t){warn(`UTF-8 decoding failed: "${t}".`);return e}}class DatasetXMLParser extends SimpleXMLParser{constructor(e){super(e);this.node=null}onEndElement(e){const t=super.onEndElement(e);if(t&&"xfa:datasets"===e){this.node=t;throw new Error("Aborting DatasetXMLParser.")}}}class DatasetReader{constructor(e){if(e.datasets)this.node=new SimpleXMLParser({hasAttributes:!0}).parseFromString(\ -e.datasets).documentElement;else{const t=new DatasetXMLParser({hasAttributes:!0});try{t.parseFromString(e["xdp:xdp"])}catch{}this.node=t.node}}getValue(e){if(!this.node||!e)return"";const t=this.node.searchNode(parseXFAPath(e),0);if(!t)return"";const a=t.firstChild;return"value"===a?.nodeName?t.children.map((e=>decodeString(e.textContent))):decodeString(t.textContent)}}class SingleIntersector{#be;#ye=1/0;#we=1/0;#xe=-1/0;#Se=-1/0;#Ae=null;#ke=[];#Ce=[];#ve=-1;#Fe=!1;constructor(e){this.#be=e;con\ -st t=e.data.quadPoints;if(t){for(let e=0,a=t.length;e8&&(this.#Ae=t)}else[this.#ye,this.#we,this.#xe,this.#Se]=e.data.rect}overlaps(e){return!(this.#ye>=e.#xe||this.#xe<=e.#ye||this.#we>=e.#Se||this.#Se<=e.#we)}#Ie(e,t){if(this.#ye>=e||this.#xe<=e||this.#we>=t||this.#Se<=t)return!1;const a=this.#Ae;if(!a)return!0;if(this.#ve>=0){const r=this.\ -#ve;if(!(a[r]>=e||a[r+2]<=e||a[r+5]>=t||a[r+1]<=t))return!0;this.#ve=-1}for(let r=0,i=a.length;r=e||a[r+2]<=e||a[r+5]>=t||a[r+1]<=t)){this.#ve=r;return!0}return!1}addGlyph(e,t,a){if(!this.#Ie(e,t)){this.disableExtraChars();return!1}if(this.#Ce.length>0){this.#ke.push(this.#Ce.join(""));this.#Ce.length=0}this.#ke.push(a);this.#Fe=!0;return!0}addExtraChar(e){this.#Fe&&this.#Ce.push(e)}disableExtraChars(){if(this.#Fe){this.#Fe=!1;this.#Ce.length=0}}setText(){this.#be.data.overlaid\ -Text=this.#ke.join("")}}class Intersector{#Te=new Map;constructor(e){for(const t of e){if(!t.data.quadPoints&&!t.data.rect)continue;const e=new SingleIntersector(t);for(const[t,a]of this.#Te)t.overlaps(e)&&(a?a.add(e):this.#Te.set(t,new Set([e])));this.#Te.set(e,null)}}addGlyph(e,t,a,r){const i=e[4]+t/2,n=e[5]+a/2;let s;for(const[e,t]of this.#Te)s?s.has(e)?e.addGlyph(i,n,r):e.disableExtraChars():e.addGlyph(i,n,r)&&(s=t)}addExtraChar(e){for(const t of this.#Te.keys())t.addExtraChar(e)}setText(){f\ -or(const e of this.#Te.keys())e.setText()}}class Word64{constructor(e,t){this.high=0|e;this.low=0|t}and(e){this.high&=e.high;this.low&=e.low}xor(e){this.high^=e.high;this.low^=e.low}shiftRight(e){if(e>=32){this.low=this.high>>>e-32|0;this.high=0}else{this.low=this.low>>>e|this.high<<32-e;this.high=this.high>>>e|0}}rotateRight(e){let t,a;if(32&e){a=this.low;t=this.high}else{t=this.low;a=this.high}e&=31;this.low=t>>>e|a<<32-e;this.high=a>>>e|t<<32-e}not(){this.high=~this.high;this.low=~this.low}ad\ -d(e){const t=(this.low>>>0)+(e.low>>>0);let a=(this.high>>>0)+(e.high>>>0);t>4294967295&&(a+=1);this.low=0|t;this.high=0|a}copyTo(e,t){e[t]=this.high>>>24&255;e[t+1]=this.high>>16&255;e[t+2]=this.high>>8&255;e[t+3]=255&this.high;e[t+4]=this.low>>>24&255;e[t+5]=this.low>>16&255;e[t+6]=this.low>>8&255;e[t+7]=255&this.low}assign(e){this.high=e.high;this.low=e.low}}const mc={get k(){return shadow(this,"k",[new Word64(1116352408,3609767458),new Word64(1899447441,602891725),new Word64(3049323471,39644\ -84399),new Word64(3921009573,2173295548),new Word64(961987163,4081628472),new Word64(1508970993,3053834265),new Word64(2453635748,2937671579),new Word64(2870763221,3664609560),new Word64(3624381080,2734883394),new Word64(310598401,1164996542),new Word64(607225278,1323610764),new Word64(1426881987,3590304994),new Word64(1925078388,4068182383),new Word64(2162078206,991336113),new Word64(2614888103,633803317),new Word64(3248222580,3479774868),new Word64(3835390401,2666613458),new Word64(4022224774,\ -944711139),new Word64(264347078,2341262773),new Word64(604807628,2007800933),new Word64(770255983,1495990901),new Word64(1249150122,1856431235),new Word64(1555081692,3175218132),new Word64(1996064986,2198950837),new Word64(2554220882,3999719339),new Word64(2821834349,766784016),new Word64(2952996808,2566594879),new Word64(3210313671,3203337956),new Word64(3336571891,1034457026),new Word64(3584528711,2466948901),new Word64(113926993,3758326383),new Word64(338241895,168717936),new Word64(666307205\ -,1188179964),new Word64(773529912,1546045734),new Word64(1294757372,1522805485),new Word64(1396182291,2643833823),new Word64(1695183700,2343527390),new Word64(1986661051,1014477480),new Word64(2177026350,1206759142),new Word64(2456956037,344077627),new Word64(2730485921,1290863460),new Word64(2820302411,3158454273),new Word64(3259730800,3505952657),new Word64(3345764771,106217008),new Word64(3516065817,3606008344),new Word64(3600352804,1432725776),new Word64(4094571909,1467031594),new Word64(275\ -423344,851169720),new Word64(430227734,3100823752),new Word64(506948616,1363258195),new Word64(659060556,3750685593),new Word64(883997877,3785050280),new Word64(958139571,3318307427),new Word64(1322822218,3812723403),new Word64(1537002063,2003034995),new Word64(1747873779,3602036899),new Word64(1955562222,1575990012),new Word64(2024104815,1125592928),new Word64(2227730452,2716904306),new Word64(2361852424,442776044),new Word64(2428436474,593698344),new Word64(2756734187,3733110249),new Word64(32\ -04031479,2999351573),new Word64(3329325298,3815920427),new Word64(3391569614,3928383900),new Word64(3515267271,566280711),new Word64(3940187606,3454069534),new Word64(4118630271,4000239992),new Word64(116418474,1914138554),new Word64(174292421,2731055270),new Word64(289380356,3203993006),new Word64(460393269,320620315),new Word64(685471733,587496836),new Word64(852142971,1086792851),new Word64(1017036298,365543100),new Word64(1126000580,2618297676),new Word64(1288033470,3409855158),new Word64(15\ -01505948,4234509866),new Word64(1607167915,987167468),new Word64(1816402316,1246189591)])}};function ch(e,t,a,r,i){e.assign(t);e.and(a);i.assign(t);i.not();i.and(r);e.xor(i)}function maj(e,t,a,r,i){e.assign(t);e.and(a);i.assign(t);i.and(r);e.xor(i);i.assign(a);i.and(r);e.xor(i)}function sigma(e,t,a){e.assign(t);e.rotateRight(28);a.assign(t);a.rotateRight(34);e.xor(a);a.assign(t);a.rotateRight(39);e.xor(a)}function sigmaPrime(e,t,a){e.assign(t);e.rotateRight(14);a.assign(t);a.rotateRight(18);e.xo\ -r(a);a.assign(t);a.rotateRight(41);e.xor(a)}function littleSigma(e,t,a){e.assign(t);e.rotateRight(1);a.assign(t);a.rotateRight(8);e.xor(a);a.assign(t);a.shiftRight(7);e.xor(a)}function littleSigmaPrime(e,t,a){e.assign(t);e.rotateRight(19);a.assign(t);a.rotateRight(61);e.xor(a);a.assign(t);a.shiftRight(6);e.xor(a)}function calculateSHA512(e,t,a,r=!1){let i,n,s,o,c,l,h,u;if(r){i=new Word64(3418070365,3238371032);n=new Word64(1654270250,914150663);s=new Word64(2438529370,812702999);o=new Word64(355\ -462360,4144912697);c=new Word64(1731405415,4290775857);l=new Word64(2394180231,1750603025);h=new Word64(3675008525,1694076839);u=new Word64(1203062813,3204075428)}else{i=new Word64(1779033703,4089235720);n=new Word64(3144134277,2227873595);s=new Word64(1013904242,4271175723);o=new Word64(2773480762,1595750129);c=new Word64(1359893119,2917565137);l=new Word64(2600822924,725511199);h=new Word64(528734635,4215389547);u=new Word64(1541459225,327033209)}const d=128*Math.ceil((a+17)/128),f=new Uint8Ar\ -ray(d);let g,p;for(g=0;g>>29&255;f[g++]=a>>21&255;f[g++]=a>>13&255;f[g++]=a>>5&255;f[g++]=a<<3&255;const b=new Array(80);for(g=0;g<80;g++)b[g]=new Word64(0,0);const{k:y}=mc;let w=new Word64(0,0),x=new Word64(0,0),S=new Word64(0,0),k=new Word64(0,0),C=new Word64(0,0),v=new Word64(0,0),F=new Word64(0,0),T=new Word64(0,0);const O=new Word64(0,0),M=new Word64(0,0),D=new Word64(0,0),R=new Word64(0,0);let N,E;for(g=0;g>>t|e<<32-t}function calculate_sha256_ch(e,t,a){return e&t^~e&a}function calculate_sha256_maj(e,t,a){return e&t^e&a^t&a}function calculate_sha256_sigma(e){return rotr(e,2)^rotr(e,13)^rotr(e,22)}function calculate_sha256_sigmaPrime(e){return rotr(e,6)^rotr(e,11)^rotr(e,25)}function calculate_sha256_littleSigma(e){return rotr(e,7)^rotr(e,18)^e>>>3}function calculateSHA\ -256(e,t,a){let r=1779033703,i=3144134277,n=1013904242,s=2773480762,o=1359893119,c=2600822924,l=528734635,h=1541459225;const u=64*Math.ceil((a+9)/64),d=new Uint8Array(u);let f,g;for(f=0;f>>29&255;d[f++]=a>>21&255;d[f++]=a>>13&255;d[f++]=a>>5&255;d[f++]=a<<3&255;const m=new Uint32Array(64),{k:b}=bc;for(f=0;f>\ ->10)+m[g-7]+calculate_sha256_littleSigma(m[g-15])+m[g-16]|0;let e,t,a=r,u=i,p=n,w=s,x=o,S=c,k=l,C=h;for(g=0;g<64;++g){e=C+calculate_sha256_sigmaPrime(x)+calculate_sha256_ch(x,S,k)+b[g]+m[g];t=calculate_sha256_sigma(a)+calculate_sha256_maj(a,u,p);C=k;k=S;S=x;x=w+e|0;w=p;p=u;u=a;a=e+t|0}r=r+a|0;i=i+u|0;n=n+p|0;s=s+w|0;o=o+x|0;c=c+S|0;l=l+k|0;h=h+C|0}var y;return new Uint8Array([r>>24&255,r>>16&255,r>>8&255,255&r,i>>24&255,i>>16&255,i>>8&255,255&i,n>>24&255,n>>16&255,n>>8&255,255&n,s>>24&255,s>>16&\ -255,s>>8&255,255&s,o>>24&255,o>>16&255,o>>8&255,255&o,c>>24&255,c>>16&255,c>>8&255,255&c,l>>24&255,l>>16&255,l>>8&255,255&l,h>>24&255,h>>16&255,h>>8&255,255&h])}class DecryptStream extends DecodeStream{constructor(e,t,a){super(t);this.str=e;this.dict=e.dict;this.decrypt=a;this.nextChunk=null;this.initialized=!1}readBlock(){let e;if(this.initialized)e=this.nextChunk;else{e=this.str.getBytes(512);this.initialized=!0}if(!e?.length){this.eof=!0;return}this.nextChunk=this.str.getBytes(512);const t=th\ -is.nextChunk?.length>0;e=(0,this.decrypt)(e,!t);const a=this.bufferLength,r=a+e.length;this.ensureBuffer(r).set(e,a);this.bufferLength=r}}class ARCFourCipher{constructor(e){this.a=0;this.b=0;const t=new Uint8Array(256),a=e.length;for(let e=0;e<256;++e)t[e]=e;for(let r=0,i=0;r<256;++r){const n=t[r];i=i+n+e[r%a]&255;t[r]=t[i];t[i]=n}this.s=t}encryptBlock(e){let t=this.a,a=this.b;const r=this.s,i=e.length,n=new Uint8Array(i);for(let s=0;st<128?t<<1:t<<1^27));constructor(){this.buffer=new Uint8Array(16);this.bufferPosition=0}_expandK\ -ey(e){unreachable("Cannot call `_expandKey` on the base class")}_decrypt(e,t){let a,r,i;const n=new Uint8Array(16);n.set(e);for(let e=0,a=this._keySize;e<16;++e,++a)n[e]^=t[a];for(let e=this._cyclesOfRepetition-1;e>=1;--e){a=n[13];n[13]=n[9];n[9]=n[5];n[5]=n[1];n[1]=a;a=n[14];r=n[10];n[14]=n[6];n[10]=n[2];n[6]=a;n[2]=r;a=n[15];r=n[11];i=n[7];n[15]=n[3];n[11]=a;n[7]=r;n[3]=i;for(let e=0;e<16;++e)n[e]=this._inv_s[n[e]];for(let a=0,r=16*e;a<16;++a,++r)n[a]^=t[r];for(let e=0;e<16;e+=4){const t=this.\ -_mix[n[e]],r=this._mix[n[e+1]],i=this._mix[n[e+2]],s=this._mix[n[e+3]];a=t^r>>>8^r<<24^i>>>16^i<<16^s>>>24^s<<8;n[e]=a>>>24&255;n[e+1]=a>>16&255;n[e+2]=a>>8&255;n[e+3]=255&a}}a=n[13];n[13]=n[9];n[9]=n[5];n[5]=n[1];n[1]=a;a=n[14];r=n[10];n[14]=n[6];n[10]=n[2];n[6]=a;n[2]=r;a=n[15];r=n[11];i=n[7];n[15]=n[3];n[11]=a;n[7]=r;n[3]=i;for(let e=0;e<16;++e){n[e]=this._inv_s[n[e]];n[e]^=t[e]}return n}_encrypt(e,t){const a=this._s;let r,i,n;const s=new Uint8Array(16);s.set(e);for(let e=0;e<16;++e)s[e]^=t[e\ -];for(let e=1;e=r;--a)if(e[a]!==t){t=0;break}o-=t;n[n.length-1]=e.subarray(0,16-t)}}const c=new Uint8Array(o);for(let e=0,t=0,a=n.length;e=256&&(o=255&(27^o))}for(let t=0;t<4;++t){a[e]=r^=a[e-32];e++;a[e]=i^=a[e-32];e++;a[e]=n^=a[e-32];e++;a[e]=s^=a[e-32];e++}}return a}}class PDFBase{_hash(e,t,a){unreachable("Abst\ -ract method `_hash` called")}checkOwnerPassword(e,t,a,r){const i=new Uint8Array(e.length+56);i.set(e,0);i.set(t,e.length);i.set(a,e.length+t.length);return isArrayEqual(this._hash(e,i,a),r)}checkUserPassword(e,t,a){const r=new Uint8Array(e.length+8);r.set(e,0);r.set(t,e.length);return isArrayEqual(this._hash(e,r,[]),a)}getOwnerKey(e,t,a,r){const i=new Uint8Array(e.length+56);i.set(e,0);i.set(t,e.length);i.set(a,e.length+t.length);const n=this._hash(e,i,a);return new AES256Cipher(n).decryptBlock(\ -r,!1,new Uint8Array(16))}getUserKey(e,t,a){const r=new Uint8Array(e.length+8);r.set(e,0);r.set(t,e.length);const i=this._hash(e,r,[]);return new AES256Cipher(i).decryptBlock(a,!1,new Uint8Array(16))}}class PDF17 extends PDFBase{_hash(e,t,a){return calculateSHA256(t,0,t.length)}}class PDF20 extends PDFBase{_hash(e,t,a){let r=calculateSHA256(t,0,t.length).subarray(0,32),i=[0],n=0;for(;n<64||i.at(-1)>n-32;){const t=e.length+r.length+a.length,l=new Uint8Array(t);let h=0;l.set(e,h);h+=e.length;l.set(\ -r,h);h+=r.length;l.set(a,h);const u=new Uint8Array(64*t);for(let e=0,a=0;e<64;e++,a+=t)u.set(l,a);i=new AES128Cipher(r.subarray(0,16)).encrypt(u,r.subarray(16,32));const d=Math.sumPrecise(i.slice(0,16))%3;0===d?r=calculateSHA256(i,0,i.length):1===d?r=(s=i,o=0,c=i.length,calculateSHA512(s,o,c,!0)):2===d&&(r=calculateSHA512(i,0,i.length));n++}var s,o,c;return r.subarray(0,32)}}class CipherTransform{constructor(e,t){this.StringCipherConstructor=e;this.StreamCipherConstructor=t}createStream(e,t){con\ -st a=new this.StreamCipherConstructor;return new DecryptStream(e,t,(function cipherTransformDecryptStream(e,t){return a.decryptBlock(e,t)}))}decryptString(e){const t=new this.StringCipherConstructor;let a=stringToBytes(e);a=t.decryptBlock(a,!0);return bytesToString(a)}encryptString(e){const t=new this.StringCipherConstructor;if(t instanceof AESBaseCipher){const a=16-e.length%16;e+=String.fromCharCode(a).repeat(a);const r=new Uint8Array(16);crypto.getRandomValues(r);let i=stringToBytes(e);i=t.enc\ -rypt(i,r);const n=new Uint8Array(16+i.length);n.set(r);n.set(i,16);return bytesToString(n)}let a=stringToBytes(e);a=t.encrypt(a);return bytesToString(a)}}class CipherTransformFactory{static get _defaultPasswordBytes(){return shadow(this,"_defaultPasswordBytes",new Uint8Array([40,191,78,94,78,117,138,65,100,0,78,86,255,250,1,8,46,46,0,182,208,104,62,128,47,12,169,254,100,83,105,122]))}#Oe(e,t,a,r,i,n,s,o,c,l,h,u){if(t){const e=Math.min(127,t.length);t=t.subarray(0,e)}else t=[];const d=6===e?new P\ -DF20:new PDF17;return d.checkUserPassword(t,o,s)?d.getUserKey(t,c,h):t.length&&d.checkOwnerPassword(t,r,n,a)?d.getOwnerKey(t,i,n,l):null}#Me(e,t,a,r,i,n,s,o){const c=40+a.length+e.length,l=new Uint8Array(c);let h,u,d=0;if(t){u=Math.min(32,t.length);for(;d>8&255;l[d++]=i>>16&255;l[d++]=i>>>24&255;l.set(e,d);d+=e.length;if(n>=4&&!o){l.fill(255,d,d+4);d+=4}let f=calc\ -ulateMD5(l,0,d);const g=s>>3;if(n>=3)for(h=0;h<50;++h)f=calculateMD5(f,0,g);const p=f.subarray(0,g);let m,b;if(n>=3){d=0;l.set(CipherTransformFactory._defaultPasswordBytes,d);d+=32;l.set(e,d);d+=e.length;m=new ARCFourCipher(p);b=m.encryptBlock(calculateMD5(l,0,d));u=p.length;const t=new Uint8Array(u);for(h=1;h<=19;++h){for(let e=0;er[t]===e))?p:null}#De(e,t,a,r){const i=new Uint8Array(32);let n=0;const s=Math.min(32,e.length);for(;n>3;if(a>=3)for(o=0;o<50;++o)c=calculateMD5(c,0,c.length);let h,u;if(a>=3){u=t;const e=new Uint8Array(l);for(o=19;o>=0;o--){for(let t=0;t>8&255;n[s++]=e>>16&255;n[s++]=255&t;n[s++]=t>>8&255;if(r){n[s++]=115;n[s++]=65;n[s++]=108;n[s++]=84}return calculateMD5(n,0,s).subarray(0,Math.min(i+5,16))}#Re(e,t,a,r,i){if(!(t instanceof Name))throw new FormatError("Invalid crypt filter name.");const n=this,s=e.get(t.name),o=s?.get("CFM");if(!o||"None"===o.name)return function(){return new NullCipher};if("V2"===o.name)return function(){re\ -turn new ARCFourCipher(n.#Be(a,r,i,!1))};if("AESV2"===o.name)return function(){return new AES128Cipher(n.#Be(a,r,i,!0))};if("AESV3"===o.name)return function(){return new AES256Cipher(i)};throw new FormatError("Unknown crypto method")}constructor(e,t,a){const r=e.get("Filter");if(!isName(r,"Standard"))throw new FormatError("unknown encryption method");this.filterName=r.name;this.dict=e;const i=e.get("V");if(!Number.isInteger(i)||1!==i&&2!==i&&4!==i&&5!==i)throw new FormatError("unsupported encryp\ -tion algorithm");this.algorithm=i;let n=e.get("Length");if(!n)if(i<=3)n=40;else{const t=e.get("CF"),a=e.get("StmF");if(t instanceof Dict&&a instanceof Name){t.suppressEncryption=!0;const e=t.get(a.name);n=e?.get("Length")||128;n<40&&(n<<=3)}}if(!Number.isInteger(n)||n<40||n%8!=0)throw new FormatError("invalid key length");const s=stringToBytes(e.get("O")),o=stringToBytes(e.get("U")),c=s.subarray(0,32),l=o.subarray(0,32),h=e.get("P"),u=e.get("R"),d=(4===i||5===i)&&!1!==e.get("EncryptMetadata");th\ -is.encryptMetadata=d;const f=stringToBytes(t);let g,p;if(a){if(6===u)try{a=utf8StringToString(a)}catch{warn("CipherTransformFactory: Unable to convert UTF8 encoded password.")}g=stringToBytes(a)}if(5!==i)p=this.#Me(f,g,c,l,h,u,n,d);else{const t=s.subarray(32,40),a=s.subarray(40,48),r=o.subarray(0,48),i=o.subarray(32,40),n=o.subarray(40,48),h=stringToBytes(e.get("OE")),d=stringToBytes(e.get("UE")),f=stringToBytes(e.get("Perms"));p=this.#Oe(u,g,c,t,a,r,l,i,n,h,d,f)}if(!p){if(!a)throw new PasswordE\ -xception("No password given",ha);const e=this.#De(g,c,u,n);p=this.#Me(f,e,c,l,h,u,n,d)}if(!p)throw new PasswordException("Incorrect Password",ua);if(4===i&&p.length<16){this.encryptionKey=new Uint8Array(16);this.encryptionKey.set(p)}else this.encryptionKey=p;if(i>=4){const t=e.get("CF");t instanceof Dict&&(t.suppressEncryption=!0);this.cf=t;this.stmf=e.get("StmF")||Name.get("Identity");this.strf=e.get("StrF")||Name.get("Identity");this.eff=e.get("EFF")||this.stmf}}createCipherTransform(e,t){if(4\ -===this.algorithm||5===this.algorithm)return new CipherTransform(this.#Re(this.cf,this.strf,e,t,this.encryptionKey),this.#Re(this.cf,this.stmf,e,t,this.encryptionKey));const a=this.#Be(e,t,this.encryptionKey,!1),cipherConstructor=function(){return new ARCFourCipher(a)};return new CipherTransform(cipherConstructor,cipherConstructor)}}class XRef{#Ne=null;constructor(e,t){this.stream=e;this.pdfManager=t;this.entries=[];this._xrefStms=new Set;this._cacheMap=new Map;this._pendingRefs=new RefSet;this.\ -_newPersistentRefNum=null;this._newTemporaryRefNum=null;this._persistentRefsCache=null}getNewPersistentRef(e){null===this._newPersistentRefNum&&(this._newPersistentRefNum=this.entries.length||1);const t=this._newPersistentRefNum++;this._cacheMap.set(t,e);return Ref.get(t,0)}getNewTemporaryRef(){if(null===this._newTemporaryRefNum){this._newTemporaryRefNum=this.entries.length||1;if(this._newPersistentRefNum){this._persistentRefsCache=new Map;for(let e=this._newTemporaryRefNum;e0;){const[s,o]=n;if(!Number.isInteger(s)||!Number.isInteger(o))throw new FormatError(`Invalid XRef range fields: ${s}, ${o}`);if(!Number.isInteger(a)||!Number.isInteger(r)||!Number.isInteger(i))throw new FormatError(`Invalid XRef entry fields length: ${s}, ${o}`);for(let n=t.entryNum;n=e.length);){a+=String.fromCharCode(r);r=e[t]}return a}function skipUntil(e,t,a){const r=a.length,i=e.length;let n=0;for(;t=r)break;t++;n++}return n}const e=/\\b(endobj|\\d+\\s+\\d+\\s+obj|xref|trailer\\s*<<)\\b/g,t=/\\b(startxref|\\d+\\s+\\d+\\s+obj)\\b\ -/g,a=/^(\\d+)\\s+(\\d+)\\s+obj\\b/,r=new Uint8Array([116,114,97,105,108,101,114]),i=new Uint8Array([115,116,97,114,116,120,114,101,102]),n=new Uint8Array([47,88,82,101,102]);this.entries.length=0;this._cacheMap.clear();const s=this.stream;s.pos=0;const o=s.getBytes(),c=bytesToString(o),l=o.length;let h=s.start;const u=[],d=[];for(;h=l)break;f=o[h]}while(10!==f&&13!==f);continue}const g=readToken(o,h);let p;if(g.star\ -tsWith("xref")&&(4===g.length||/\\s/.test(g[4]))){h+=skipUntil(o,h,r);u.push(h);h+=skipUntil(o,h,i)}else if(p=a.exec(g)){const t=0|p[1],a=0|p[2],r=h+g.length;let i,u=!1;if(this.entries[t]){if(this.entries[t].gen===a)try{new Parser({lexer:new Lexer(s.makeSubStream(r))}).getObj();u=!0}catch(e){e instanceof ParserEOFException?warn(`indexObjects -- checking object (${g}): "${e}".`):u=!0}}else u=!0;u&&(this.entries[t]={offset:h-s.start,gen:a,uncompressed:!0});e.lastIndex=r;const f=e.exec(c);if(f){i=e.\ -lastIndex+1-h;if("endobj"!==f[1]){warn(`indexObjects: Found "${f[1]}" inside of another "obj", caused by missing "endobj" -- trying to recover.`);i-=f[1].length+1}}else i=l-h;const m=o.subarray(h,h+i),b=skipUntil(m,0,n);if(b0?Math.max(...this._xrefStms):null)}getEntry(e){const t=this.entries[e];return t&&!t.free&&t.offset?t:null}fetchI\ -fRef(e,t=!1){return e instanceof Ref?this.fetch(e,t):e}fetch(e,t=!1){if(!(e instanceof Ref))throw new Error("ref object is not a reference");const a=e.num,r=this._cacheMap.get(a);if(void 0!==r){r instanceof Dict&&!r.objId&&(r.objId=e.toString());return r}let i=this.getEntry(a);if(null===i)return i;if(this._pendingRefs.has(e)){this._pendingRefs.remove(e);warn(`Ignoring circular reference: ${e}.`);return ya}this._pendingRefs.put(e);try{i=i.uncompressed?this.fetchUncompressed(e,i,t):this.fetchCompr\ -essed(e,i,t);this._pendingRefs.remove(e)}catch(t){this._pendingRefs.remove(e);throw t}i instanceof Dict?i.objId=e.toString():i instanceof BaseStream&&(i.dict.objId=e.toString());return i}fetchUncompressed(e,t,a=!1){const r=e.gen;let i=e.num;if(t.gen!==r){const n=`Inconsistent generation in XRef: ${e}`;if(this._generationFallback&&t.gen0&&t[3]-t[1]>0)return t;warn(`Empty, or invalid, /${e} entry.`)}return null}get mediaBox(){return shadow(this,"mediaBox",this.#je("MediaBox")||yc)}get cropBox(){return shadow(this,"cropBox",this.#je("CropBox")||this.mediaBox)}get userUnit(){const e=this.pageDict.get("UserUnit");return shadow(this,"userUnit","number"==typeof e&&e>0?e:1)}get view(){const{cropBo\ -x:e,mediaBox:t}=this;if(e!==t&&!isArrayEqual(e,t)){const a=Util.intersect(e,t);if(a&&a[2]-a[0]>0&&a[3]-a[1]>0)return shadow(this,"view",a);warn("Empty /CropBox and /MediaBox intersection.")}return shadow(this,"view",t)}get rotate(){let e=this.#Le("Rotate")||0;e%90!=0?e=0:e>=360?e%=360:e<0&&(e=(e%360+360)%360);return shadow(this,"rotate",e)}#_e(e,t){if(!this.evaluatorOptions.ignoreErrors)throw e;warn(`getContentStream - ignoring sub-stream (${t}): "${e}".`)}async getContentStream(){const e=await \ -this.pdfManager.ensure(this,"content");return e instanceof BaseStream?e:Array.isArray(e)?new StreamsSequenceStream(e,this.#_e.bind(this)):new NullStream}get xfaData(){return shadow(this,"xfaData",this.xfaFactory?{bbox:this.xfaFactory.getBoundingBox(this.pageIndex)}:null)}async#Ue(e,t,a){const r=[];for(const i of e)if(i.id){const e=Ref.fromString(i.id);if(!e){warn(`A non-linked annotation cannot be modified: ${i.id}`);continue}if(i.deleted){t.put(e,e);if(i.popupRef){const e=Ref.fromString(i.popup\ -Ref);e&&t.put(e,e)}continue}if(i.popup?.deleted){const e=Ref.fromString(i.popupRef);e&&t.put(e,e)}a?.put(e);i.ref=e;r.push(this.xref.fetchAsync(e).then((e=>{e instanceof Dict&&(i.oldAnnotation=e.clone())}),(()=>{warn(`Cannot fetch \\`oldAnnotation\\` for: ${e}.`)})));delete i.id}await Promise.all(r)}async saveNewAnnotations(e,t,a,r,i){if(this.xfaFactory)throw new Error("XFA: Cannot save new annotations.");const n=this.#Pe(e),s=new RefSetCache,o=new RefSet;await this.#Ue(a,s,o);const c=this.pageDic\ -t,l=this.annotations.filter((e=>!(e instanceof Ref&&s.has(e)))),h=await AnnotationFactory.saveNewAnnotations(n,t,a,r,i);for(const{ref:e}of h.annotations)e instanceof Ref&&!o.has(e)&&l.push(e);const u=c.clone();u.set("Annots",l);i.put(this.ref,{data:u});for(const e of s)i.put(e,{data:null})}async save(e,t,a,r){const i=this.#Pe(e),n=await this._parsedAnnotations,s=[];for(const e of n)s.push(e.save(i,t,a,r).catch((function(e){warn(`save - ignoring annotation data during "${t.name}" task: "${e}".`);\ -return null})));return Promise.all(s)}async loadResources(e){await(this.#Ee??=this.pdfManager.ensure(this,"resources"));await ObjectLoader.load(this.resources,e,this.xref)}async#Xe(e,t){const a=e?.get("Resources");if(!(a instanceof Dict&&a.size))return this.resources;await ObjectLoader.load(a,t,this.xref);return Dict.merge({xref:this.xref,dictArray:[a,this.resources],mergeSubDicts:!0})}async getOperatorList({handler:e,sink:t,task:a,intent:r,cacheKey:i,annotationStorage:c=null,modifiedIds:d=null}\ -){const g=this.getContentStream(),p=this.loadResources(Ia),m=this.#Pe(e),b=this.xfaFactory?null:getNewAnnotationsMap(c),y=b?.get(this.pageIndex);let w=Promise.resolve(null),x=null;if(y){const e=this.pdfManager.ensureDoc("annotationGlobals");let t;const r=new Set;for(const{bitmapId:e,bitmap:t}of y)!e||t||r.has(e)||r.add(e);const{isOffscreenCanvasSupported:i}=this.evaluatorOptions;if(r.size>0){const e=y.slice();for(const[t,a]of c)t.startsWith(f)&&a.bitmap&&r.has(a.bitmapId)&&e.push(a);t=Annotation\ -Factory.generateImages(e,this.xref,i)}else t=AnnotationFactory.generateImages(y,this.xref,i);x=new RefSet;w=Promise.all([e,this.#Ue(y,x,null)]).then((([e])=>e?AnnotationFactory.printNewAnnotations(e,m,a,y,t):null))}const S=Promise.all([g,p]).then((async([n])=>{const s=await this.#Xe(n.dict,Ia),o=new OperatorList(r,t);e.send("StartRenderPage",{transparency:m.hasBlendModes(s,this.nonBlendModesSet),pageIndex:this.pageIndex,cacheKey:i});await m.getOperatorList({stream:n,task:a,resources:s,operatorLi\ -st:o});return o}));let[k,C,v]=await Promise.all([S,this._parsedAnnotations,w]);if(v){C=C.filter((e=>!(e.ref&&x.has(e.ref))));for(let e=0,t=v.length;ee.ref&&isRefsEqual(e.ref,a.refToReplace)));if(r>=0){C.splice(r,1,a);v.splice(e--,1);t--}}}C=C.concat(v)}if(0===C.length||r&h){k.flush(!0);return{length:k.totalLength}}const F=!!(r&l),T=!!(r&u),O=!!(r&n),M=!!(r&s),D=!!(r&o),R=[];for(const e of C)(O||M&&e.mustBeViewed(c,F)&&e.mustBeViewed\ -WhenEditing(T,d)||D&&e.mustBePrinted(c))&&R.push(e.getOperatorList(m,a,r,c).catch((function(e){warn(`getOperatorList - ignoring annotation data during "${a.name}" task: "${e}".`);return{opList:null,separateForm:!1,separateCanvas:!1}})));const N=await Promise.all(R);let E=!1,L=!1;for(const{opList:e,separateForm:t,separateCanvas:a}of N){k.addOpList(e);E||=t;L||=a}k.flush(!0,{form:E,canvas:L});return{length:k.totalLength}}async extractTextContent({handler:e,task:t,includeMarkedContent:a,disableNorm\ -alization:r,sink:i,intersector:n=null}){const s=this.getContentStream(),o=this.loadResources(Ta),c=this.pdfManager.ensureCatalog("lang"),[l,,h]=await Promise.all([s,o,c]),u=await this.#Xe(l.dict,Ta);return this.#Pe(e).getTextContent({stream:l,task:t,resources:u,includeMarkedContent:a,disableNormalization:r,sink:i,viewBox:this.view,lang:h,intersector:n})}async getStructTree(){const e=await this.pdfManager.ensureCatalog("structTreeRoot");if(!e)return null;await this._parsedAnnotations;try{const t=\ -await this.pdfManager.ensure(this,"_parseStructTree",[e]);return await this.pdfManager.ensure(t,"serializable")}catch(e){warn(`getStructTree: "${e}".`);return null}}_parseStructTree(e){const t=new StructTreePage(e,this.pageDict);t.parse(this.ref);return t}async getAnnotationsData(e,t,a){const r=await this._parsedAnnotations;if(0===r.length)return r;const i=[],c=[];let l;const h=!!(a&n),u=!!(a&s),d=!!(a&o),f=[];for(const a of r){const r=h||u&&a.viewable;(r||d&&a.printable)&&i.push(a.data);if(a.ha\ -sTextContent&&r){l??=this.#Pe(e);c.push(a.extractTextContent(l,t,[-1/0,-1/0,1/0,1/0]).catch((function(e){warn(`getAnnotationsData - ignoring textContent during "${t.name}" task: "${e}".`)})))}else a.overlaysTextContent&&r&&f.push(a)}if(f.length>0){const a=new Intersector(f);c.push(this.extractTextContent({handler:e,task:t,includeMarkedContent:!1,disableNormalization:!1,sink:null,viewBox:this.view,lang:null,intersector:a}).then((()=>{a.setText()})))}await Promise.all(c);return i}get annotations()\ -{const e=this.#Le("Annots");return shadow(this,"annotations",Array.isArray(e)?e:[])}get _parsedAnnotations(){return shadow(this,"_parsedAnnotations",this.pdfManager.ensure(this,"annotations").then((async e=>{if(0===e.length)return e;const[t,a]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureDoc("fieldObjects")]);if(!t)return[];const r=a?.orphanFields,i=[];for(const a of e)i.push(AnnotationFactory.create(this.xref,a,t,this._localIdFactory,!1,r,this.ref).cat\ -ch((function(e){warn(`_parsedAnnotations: "${e}".`);return null})));const n=[];let s,o;for(const e of await Promise.all(i))e&&(e instanceof WidgetAnnotation?(o||=[]).push(e):e instanceof PopupAnnotation?(s||=[]).push(e):n.push(e));o&&n.push(...o);s&&n.push(...s);return n})))}get jsActions(){return shadow(this,"jsActions",collectActions(this.xref,this.pageDict,xe))}}const wc=new Uint8Array([37,80,68,70,45]),xc=new Uint8Array([115,116,97,114,116,120,114,101,102]),Sc=new Uint8Array([101,110,100,111\ -,98,106]);function find(e,t,a=1024,r=!1){const i=t.length,n=e.peekBytes(a),s=n.length-i;if(s<=0)return!1;if(r){const a=i-1;let r=n.length-1;for(;r>=a;){let s=0;for(;s=i){e.pos+=r-a;return!0}r--}}else{let a=0;for(;a<=s;){let r=0;for(;r=i){e.pos+=a;return!0}a++}}return!1}class PDFDocument{#qe=new Map;#He=null;constructor(e,t){if(t.length<=0)throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");this.pdfManager=\ -e;this.stream=t;this.xref=new XRef(t,e);const a={font:0};this._globalIdFactory=class{static getDocId(){return`g_${e.docId}`}static createFontId(){return"f"+ ++a.font}static createObjId(){unreachable("Abstract method `createObjId` called.")}static getPageObjId(){unreachable("Abstract method `getPageObjId` called.")}}}parse(e){this.xref.parse(e);this.catalog=new Catalog(this.pdfManager,this.xref)}get linearization(){let e=null;try{e=Linearization.create(this.stream)}catch(e){if(e instanceof Missin\ -gDataException)throw e;info(e)}return shadow(this,"linearization",e)}get startXRef(){const e=this.stream;let t=0;if(this.linearization){e.reset();if(find(e,Sc)){e.skip(6);let a=e.peekByte();for(;isWhiteSpace(a);){e.pos++;a=e.peekByte()}t=e.pos-e.start}}else{const a=1024,r=xc.length;let i=!1,n=e.end;for(;!i&&n>0;){n-=a-r;n<0&&(n=0);e.pos=n;i=find(e,xc,a,!0)}if(i){e.skip(9);let a;do{a=e.getByte()}while(isWhiteSpace(a));let r="";for(;a>=32&&a<=57;){r+=String.fromCharCode(a);a=e.getByte()}t=parseInt\ -(r,10);isNaN(t)&&(t=0)}}return shadow(this,"startXRef",t)}checkHeader(){const e=this.stream;e.reset();if(!find(e,wc))return;e.moveStart();e.skip(wc.length);let t,a="";for(;(t=e.getByte())>32&&a.length<7;)a+=String.fromCharCode(t);Ca.test(a)?this.#He=a:warn(`Invalid PDF header version: ${a}`)}parseStartXRef(){this.xref.setStartXRef(this.startXRef)}get numPages(){let e=0;e=this.catalog.hasActualNumPages?this.catalog.numPages:this.xfaFactory?this.xfaFactory.getNumPages():this.linearization?this.lin\ -earization.numPages:this.catalog.numPages;return shadow(this,"numPages",e)}#We(e,t=0){return!!Array.isArray(e)&&e.every((e=>{if(!((e=this.xref.fetchIfRef(e))instanceof Dict))return!1;if(e.has("Kids")){if(++t>10){warn("#hasOnlyDocumentSignatures: maximum recursion depth reached");return!1}return this.#We(e.get("Kids"),t)}const a=isName(e.get("FT"),"Sig"),r=e.get("Rect"),i=Array.isArray(r)&&r.every((e=>0===e));return a&&i}))}#ze(e,t,a=new RefSet){if(Array.isArray(e))for(let r of e){if(r instanceof\ - Ref){if(a.has(r))continue;a.put(r)}r=this.xref.fetchIfRef(r);if(!(r instanceof Dict))continue;if(r.has("Kids")){this.#ze(r.get("Kids"),t,a);continue}if(!isName(r.get("FT"),"Sig"))continue;const e=r.get("V");if(!(e instanceof Dict))continue;const i=e.get("SubFilter");i instanceof Name&&t.add(i.name)}}get _xfaStreams(){const{acroForm:e}=this.catalog;if(!e)return null;const t=e.get("XFA"),a=new Map(["xdp:xdp","template","datasets","config","connectionSet","localeSet","stylesheet","/xdp:xdp"].map((\ -e=>[e,null])));if(t instanceof BaseStream&&!t.isEmpty){a.set("xdp:xdp",t);return a}if(!Array.isArray(t)||0===t.length)return null;for(let e=0,r=t.length;el.handleSetFont(r,[Name.get(e),1],null,h,t,d,a,i).catch((e=>{warn(`loadXfaFonts: "${e}".`);return null})),f=[];for(const[e,t]of i){const a=t.get("FontDescriptor");if(!(a instanceof Dict))continue;let r=a.get("FontFamily");r=r.replaceAll(/[ ]+(\\d)/g,"$1");const i={fontFamily:r,fontWeight:a.get("FontWeight"),italicAngle:-a.get("ItalicAngle")};validateCSSFont(i)&&f.push(parseFont(e,null,i))}await Promise.all(f);const g=this.xfaFactory.setFonts(u);if(!g)return;n.ignoreErrors=!0;f.l\ -ength=0;u.length=0;const p=new Set;for(const e of g)getXfaFontName(`${e}-Regular`)||p.add(e);p.size&&g.push("PdfJS-Fallback");for(const e of g)if(!p.has(e))for(const t of[{name:"Regular",fontWeight:400,italicAngle:0},{name:"Bold",fontWeight:700,italicAngle:0},{name:"Italic",fontWeight:400,italicAngle:12},{name:"BoldItalic",fontWeight:700,italicAngle:12}]){const a=`${e}-${t.name}`;f.push(parseFont(a,getXfaFontDict(a),{fontFamily:e,fontWeight:t.fontWeight,italicAngle:t.italicAngle}))}await Promise\ -.all(f);this.xfaFactory.appendFonts(u,p)}loadXfaResources(e,t){return Promise.all([this.#Ge(e,t).catch((()=>{})),this.#$e()])}serializeXfaData(e){return this.xfaFactory?this.xfaFactory.serializeData(e):null}get version(){return this.catalog.version||this.#He}get formInfo(){const e={hasFields:!1,hasAcroForm:!1,hasXfa:!1,hasSignatures:!1},{acroForm:t}=this.catalog;if(!t)return shadow(this,"formInfo",e);try{const a=t.get("Fields"),r=Array.isArray(a)&&a.length>0;e.hasFields=r;const i=t.get("XFA");e.\ -hasXfa=Array.isArray(i)&&i.length>0||i instanceof BaseStream&&!i.isEmpty;const n=!!(1&t.get("SigFlags")),s=n&&this.#We(a);e.hasAcroForm=r&&!s;e.hasSignatures=n}catch(e){if(e instanceof MissingDataException)throw e;warn(`Cannot fetch form information: "${e}".`)}return shadow(this,"formInfo",e)}get documentInfo(){const{catalog:e,formInfo:t,xref:a}=this,r={PDFFormatVersion:this.version,Language:e.lang,EncryptFilterName:a.encrypt?.filterName??null,IsLinearized:!!this.linearization,IsAcroFormPresent:\ -t.hasAcroForm,IsXFAPresent:t.hasXfa,IsCollectionPresent:!!e.collection,IsSignaturesPresent:t.hasSignatures};let i;try{i=a.trailer.get("Info")}catch(e){if(e instanceof MissingDataException)throw e;info("The document information dictionary is invalid.")}if(!(i instanceof Dict))return shadow(this,"documentInfo",r);for(const[e,t]of i){switch(e){case"Title":case"Author":case"Subject":case"Keywords":case"Creator":case"Producer":case"CreationDate":case"ModDate":if("string"==typeof t){r[e]=stringToPDFSt\ -ring(t);continue}break;case"Trapped":if(t instanceof Name){r[e]=t;continue}break;default:let a;switch(typeof t){case"string":a=stringToPDFString(t);break;case"number":case"boolean":a=t;break;default:t instanceof Name&&(a=t)}if(void 0===a){warn(`Bad value, for custom key "${e}", in Info: ${t}.`);continue}r.Custom??=Object.create(null);r.Custom[e]=a;continue}warn(`Bad value, for key "${e}", in Info: ${t}.`)}return shadow(this,"documentInfo",r)}get fingerprints(){const e="\\0".repeat(16);function va\ -lidate(t){return"string"==typeof t&&16===t.length&&t!==e}const t=this.xref.trailer.get("ID");let a,r;if(Array.isArray(t)&&validate(t[0])){a=stringToBytes(t[0]);t[1]!==t[0]&&validate(t[1])&&(r=stringToBytes(t[1]))}else a=calculateMD5(this.stream.getByteRange(0,1024),0,1024);return shadow(this,"fingerprints",[toHexUtil(a),r?toHexUtil(r):null])}async#Ve(e){const{catalog:t,linearization:a,xref:r}=this,i=Ref.get(a.objectNumberFirst,0);try{const e=await r.fetchAsync(i);if(e instanceof Dict){let a=e.ge\ -tRaw("Type");a instanceof Ref&&(a=await r.fetchAsync(a));if(isName(a,"Page")||!e.has("Type")&&!e.has("Kids")&&e.has("Contents")){t.pageKidsCountCache.has(i)||t.pageKidsCountCache.put(i,1);t.pageIndexCache.has(i)||t.pageIndexCache.put(i,0);return[e,i]}}throw new FormatError("The Linearization dictionary doesn\'t point to a valid Page dictionary.")}catch(a){warn(`_getLinearizationPage: "${a.message}".`);return t.getPageDict(e)}}getPage(e){const t=this.#qe.get(e);if(t)return t;const{catalog:a,linear\ -ization:r,xfaFactory:i}=this;let n;n=i?Promise.resolve([Dict.empty,null]):r?.pageFirst===e?this.#Ve(e):a.getPageDict(e);n=n.then((([t,r])=>new Page({pdfManager:this.pdfManager,xref:this.xref,pageIndex:e,pageDict:t,ref:r,globalIdFactory:this._globalIdFactory,fontCache:a.fontCache,builtInCMapCache:a.builtInCMapCache,standardFontDataCache:a.standardFontDataCache,globalColorSpaceCache:a.globalColorSpaceCache,globalImageCache:a.globalImageCache,systemFontCache:a.systemFontCache,nonBlendModesSet:a.non\ -BlendModesSet,xfaFactory:i})));this.#qe.set(e,n);return n}async checkFirstPage(e=!1){if(!e)try{await this.getPage(0)}catch(e){if(e instanceof XRefEntryException){this.#qe.delete(0);await this.cleanup();throw new XRefParseException}}}async checkLastPage(e=!1){const{catalog:t,pdfManager:a}=this;t.setActualNumPages();let r;try{await Promise.all([a.ensureDoc("xfaFactory"),a.ensureDoc("linearization"),a.ensureCatalog("numPages")]);if(this.xfaFactory)return;r=this.linearization?this.linearization.numP\ -ages:t.numPages;if(!Number.isInteger(r))throw new FormatError("Page count is not an integer.");if(r<=1)return;await this.getPage(r-1)}catch(i){this.#qe.delete(r-1);await this.cleanup();if(i instanceof XRefEntryException&&!e)throw new XRefParseException;warn(`checkLastPage - invalid /Pages tree /Count: ${r}.`);let n;try{n=await t.getAllPageDicts(e)}catch(a){if(a instanceof XRefEntryException&&!e)throw new XRefParseException;t.setActualNumPages(1);return}for(const[e,[r,i]]of n){let n;if(r instance\ -of Error){n=Promise.reject(r);n.catch((()=>{}))}else n=Promise.resolve(new Page({pdfManager:a,xref:this.xref,pageIndex:e,pageDict:r,ref:i,globalIdFactory:this._globalIdFactory,fontCache:t.fontCache,builtInCMapCache:t.builtInCMapCache,standardFontDataCache:t.standardFontDataCache,globalColorSpaceCache:this.globalColorSpaceCache,globalImageCache:t.globalImageCache,systemFontCache:t.systemFontCache,nonBlendModesSet:t.nonBlendModesSet,xfaFactory:null}));this.#qe.set(e,n)}t.setActualNumPages(n.size)}\ -}async fontFallback(e,t){const{catalog:a,pdfManager:r}=this;for(const i of await Promise.all(a.fontCache))if(i.loadedName===e){i.fallback(t,r.evaluatorOptions);return}}async cleanup(e=!1){return this.catalog?this.catalog.cleanup(e):clearGlobalCaches()}async#Ke(e,t,a,r,i,n,s){const{xref:o}=this;if(!(a instanceof Ref)||n.has(a))return;n.put(a);const c=await o.fetchAsync(a);if(!(c instanceof Dict))return;let l=await c.getAsync("Subtype");l=l instanceof Name?l.name:null;if("Link"===l)return;if(c.has\ -("T")){const t=stringToPDFString(await c.getAsync("T"));e=""===e?t:`${e}.${t}`}else{let a=c;for(;;){a=a.getRaw("Parent")||t;if(a instanceof Ref){if(n.has(a))break;a=await o.fetchAsync(a)}if(!(a instanceof Dict))break;if(a.has("T")){const t=stringToPDFString(await a.getAsync("T"));e=""===e?t:`${e}.${t}`;break}}}t&&!c.has("Parent")&&isName(c.get("Subtype"),"Widget")&&s.put(a,t);r.has(e)||r.set(e,[]);r.get(e).push(AnnotationFactory.create(o,a,i,null,!0,s,null).then((e=>e?.getFieldObject())).catch((\ -function(e){warn(`#collectFieldObjects: "${e}".`);return null})));if(!c.has("Kids"))return;const h=await c.getAsync("Kids");if(Array.isArray(h))for(const t of h)await this.#Ke(e,a,t,r,i,n,s)}get fieldObjects(){return shadow(this,"fieldObjects",this.pdfManager.ensureDoc("formInfo").then((async e=>{if(!e.hasFields)return null;const t=await this.annotationGlobals;if(!t)return null;const{acroForm:a}=t,r=new RefSet,i=Object.create(null),n=new Map,s=new RefSetCache;for(const e of a.get("Fields"))await\ - this.#Ke("",null,e,n,t,r,s);const o=[];for(const[e,t]of n)o.push(Promise.all(t).then((t=>{(t=t.filter((e=>!!e))).length>0&&(i[e]=t)})));await Promise.all(o);return{allFields:objectSize(i)>0?i:null,orphanFields:s}})))}get hasJSActions(){return shadow(this,"hasJSActions",this.pdfManager.ensureDoc("_parseHasJSActions"))}async _parseHasJSActions(){const[e,t]=await Promise.all([this.pdfManager.ensureCatalog("jsActions"),this.pdfManager.ensureDoc("fieldObjects")]);return!!e||!!t?.allFields&&Object.va\ -lues(t.allFields).some((e=>e.some((e=>null!==e.actions))))}get calculationOrderIds(){const e=this.catalog.acroForm?.get("CO");if(!Array.isArray(e)||0===e.length)return shadow(this,"calculationOrderIds",null);const t=[];for(const a of e)a instanceof Ref&&t.push(a.toString());return shadow(this,"calculationOrderIds",t.length?t:null)}get annotationGlobals(){return shadow(this,"annotationGlobals",AnnotationFactory.createGlobals(this.pdfManager))}}class BasePdfManager{constructor({docBaseUrl:e,docId:\ -t,enableXfa:a,evaluatorOptions:r,handler:i,password:n}){this._docBaseUrl=function parseDocBaseUrl(e){if(e){const t=createValidAbsoluteUrl(e);if(t)return t.href;warn(`Invalid absolute docBaseUrl: "${e}".`)}return null}(e);this._docId=t;this._password=n;this.enableXfa=a;r.isOffscreenCanvasSupported&&=FeatureTest.isOffscreenCanvasSupported;r.isImageDecoderSupported&&=FeatureTest.isImageDecoderSupported;this.evaluatorOptions=Object.freeze(r);ImageResizer.setOptions(r);JpegStream.setOptions(r);Operat\ -orList.setOptions(r);const s={...r,handler:i};JpxImage.setOptions(s);IccColorSpace.setOptions(s);CmykICCBasedCS.setOptions(s)}get docId(){return this._docId}get password(){return this._password}get docBaseUrl(){return this._docBaseUrl}ensureDoc(e,t){return this.ensure(this.pdfDocument,e,t)}ensureXRef(e,t){return this.ensure(this.pdfDocument.xref,e,t)}ensureCatalog(e,t){return this.ensure(this.pdfDocument.catalog,e,t)}getPage(e){return this.pdfDocument.getPage(e)}fontFallback(e,t){return this.pdf\ -Document.fontFallback(e,t)}cleanup(e=!1){return this.pdfDocument.cleanup(e)}async ensure(e,t,a){unreachable("Abstract method `ensure` called")}requestRange(e,t){unreachable("Abstract method `requestRange` called")}requestLoadedStream(e=!1){unreachable("Abstract method `requestLoadedStream` called")}sendProgressiveData(e){unreachable("Abstract method `sendProgressiveData` called")}updatePassword(e){this._password=e}terminate(e){unreachable("Abstract method `terminate` called")}}class LocalPdfMana\ -ger extends BasePdfManager{constructor(e){super(e);const t=new Stream(e.source);this.pdfDocument=new PDFDocument(this,t);this._loadedStreamPromise=Promise.resolve(t)}async ensure(e,t,a){const r=e[t];return"function"==typeof r?r.apply(e,a):r}requestRange(e,t){return Promise.resolve()}requestLoadedStream(e=!1){return this._loadedStreamPromise}terminate(e){}}class NetworkPdfManager extends BasePdfManager{constructor(e){super(e);this.streamManager=new ChunkedStreamManager(e.source,{msgHandler:e.hand\ -ler,length:e.length,disableAutoFetch:e.disableAutoFetch,rangeChunkSize:e.rangeChunkSize});this.pdfDocument=new PDFDocument(this,this.streamManager.getStream())}async ensure(e,t,a){try{const r=e[t];return"function"==typeof r?r.apply(e,a):r}catch(r){if(!(r instanceof MissingDataException))throw r;await this.requestRange(r.begin,r.end);return this.ensure(e,t,a)}}requestRange(e,t){return this.streamManager.requestRange(e,t)}requestLoadedStream(e=!1){return this.streamManager.requestAllChunks(e)}send\ -ProgressiveData(e){this.streamManager.onReceiveData({chunk:e})}terminate(e){this.streamManager.abort(e)}}const Ac=1,kc=2,Cc=1,vc=2,Fc=3,Ic=4,Tc=5,Oc=6,Mc=7,Dc=8;function onFn(){}function wrapReason(e){if(e instanceof AbortException||e instanceof InvalidPDFException||e instanceof PasswordException||e instanceof ResponseException||e instanceof UnknownErrorException)return e;e instanceof Error||"object"==typeof e&&null!==e||unreachable(\'wrapReason: Expected "reason" to be a (possibly cloned) Error.\ -\');switch(e.name){case"AbortException":return new AbortException(e.message);case"InvalidPDFException":return new InvalidPDFException(e.message);case"PasswordException":return new PasswordException(e.message,e.code);case"ResponseException":return new ResponseException(e.message,e.status,e.missing);case"UnknownErrorException":return new UnknownErrorException(e.message,e.details)}return new UnknownErrorException(e.message,e.toString())}class MessageHandler{#Je=new AbortController;constructor(e,t,a)\ -{this.sourceName=e;this.targetName=t;this.comObj=a;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);a.addEventListener("message",this.#Ye.bind(this),{signal:this.#Je.signal})}#Ye({data:e}){if(e.targetName!==this.sourceName)return;if(e.stream){this.#Ze(e);return}if(e.callback){const t=e.callbackId,a=this.callbackCapabilities[t];if(!a)throw new Error\ -(`Cannot resolve callback ${t}`);delete this.callbackCapabilities[t];if(e.callback===Ac)a.resolve(e.data);else{if(e.callback!==kc)throw new Error("Unexpected callback case");a.reject(wrapReason(e.reason))}return}const t=this.actionHandler[e.action];if(!t)throw new Error(`Unknown action from worker: ${e.action}`);if(e.callbackId){const a=this.sourceName,r=e.sourceName,i=this.comObj;Promise.try(t,e.data).then((function(t){i.postMessage({sourceName:a,targetName:r,callback:Ac,callbackId:e.callbackId\ -,data:t})}),(function(t){i.postMessage({sourceName:a,targetName:r,callback:kc,callbackId:e.callbackId,reason:wrapReason(t)})}))}else e.streamId?this.#Qe(e):t(e.data)}on(e,t){const a=this.actionHandler;if(a[e])throw new Error(`There is already an actionName called "${e}"`);a[e]=t}send(e,t,a){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,data:t},a)}sendWithPromise(e,t,a){const r=this.callbackId++,i=Promise.withResolvers();this.callbackCapabilities[r]=i;try\ -{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,callbackId:r,data:t},a)}catch(e){i.reject(e)}return i.promise}sendWithStream(e,t,a,r){const i=this.streamId++,n=this.sourceName,s=this.targetName,o=this.comObj;return new ReadableStream({start:a=>{const c=Promise.withResolvers();this.streamControllers[i]={controller:a,startCall:c,pullCall:null,cancelCall:null,isClosed:!1};o.postMessage({sourceName:n,targetName:s,action:e,streamId:i,data:t,desiredSize:a.desir\ -edSize},r);return c.promise},pull:e=>{const t=Promise.withResolvers();this.streamControllers[i].pullCall=t;o.postMessage({sourceName:n,targetName:s,stream:Oc,streamId:i,desiredSize:e.desiredSize});return t.promise},cancel:e=>{assert(e instanceof Error,"cancel must have a valid reason");const t=Promise.withResolvers();this.streamControllers[i].cancelCall=t;this.streamControllers[i].isClosed=!0;o.postMessage({sourceName:n,targetName:s,stream:Cc,streamId:i,reason:wrapReason(e)});return t.promise}},\ -a)}#Qe(e){const t=e.streamId,a=this.sourceName,r=e.sourceName,i=this.comObj,n=this,s=this.actionHandler[e.action],o={enqueue(e,n=1,s){if(this.isCancelled)return;const o=this.desiredSize;this.desiredSize-=n;if(o>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}i.postMessage({sourceName:a,targetName:r,stream:Ic,streamId:t,chunk:e},s)},close(){if(!this.isCancelled){this.isCancelled=!0;i.postMessage({sourceName:a,targetName:r,stream:Fc,stream\ -Id:t});delete n.streamSinks[t]}},error(e){assert(e instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;i.postMessage({sourceName:a,targetName:r,stream:Tc,streamId:t,reason:wrapReason(e)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:e.desiredSize,ready:null};o.sinkCapability.resolve();o.ready=o.sinkCapability.promise;this.streamSinks[t]=o;Promise.try(s,e.data,o).then((function(){i.postMessage({sourceName:a,\ -targetName:r,stream:Dc,streamId:t,success:!0})}),(function(e){i.postMessage({sourceName:a,targetName:r,stream:Dc,streamId:t,reason:wrapReason(e)})}))}#Ze(e){const t=e.streamId,a=this.sourceName,r=e.sourceName,i=this.comObj,n=this.streamControllers[t],s=this.streamSinks[t];switch(e.stream){case Dc:e.success?n.startCall.resolve():n.startCall.reject(wrapReason(e.reason));break;case Mc:e.success?n.pullCall.resolve():n.pullCall.reject(wrapReason(e.reason));break;case Oc:if(!s){i.postMessage({sourceNa\ -me:a,targetName:r,stream:Mc,streamId:t,success:!0});break}s.desiredSize<=0&&e.desiredSize>0&&s.sinkCapability.resolve();s.desiredSize=e.desiredSize;Promise.try(s.onPull||onFn).then((function(){i.postMessage({sourceName:a,targetName:r,stream:Mc,streamId:t,success:!0})}),(function(e){i.postMessage({sourceName:a,targetName:r,stream:Mc,streamId:t,reason:wrapReason(e)})}));break;case Ic:assert(n,"enqueue should have stream controller");if(n.isClosed)break;n.controller.enqueue(e.chunk);break;case Fc:a\ -ssert(n,"close should have stream controller");if(n.isClosed)break;n.isClosed=!0;n.controller.close();this.#et(n,t);break;case Tc:assert(n,"error should have stream controller");n.controller.error(wrapReason(e.reason));this.#et(n,t);break;case vc:e.success?n.cancelCall.resolve():n.cancelCall.reject(wrapReason(e.reason));this.#et(n,t);break;case Cc:if(!s)break;const o=wrapReason(e.reason);Promise.try(s.onCancel||onFn,o).then((function(){i.postMessage({sourceName:a,targetName:r,stream:vc,streamId:\ -t,success:!0})}),(function(e){i.postMessage({sourceName:a,targetName:r,stream:vc,streamId:t,reason:wrapReason(e)})}));s.sinkCapability.reject(o);s.isCancelled=!0;delete this.streamSinks[t];break;default:throw new Error("Unexpected stream case")}}async#et(e,t){await Promise.allSettled([e.startCall?.promise,e.pullCall?.promise,e.cancelCall?.promise]);delete this.streamControllers[t]}destroy(){this.#Je?.abort();this.#Je=null}}async function writeObject(e,t,a,{encrypt:r=null}){const i=r?.createCiphe\ -rTransform(e.num,e.gen);a.push(`${e.num} ${e.gen} obj\\n`);t instanceof Dict?await writeDict(t,a,i):t instanceof BaseStream?await writeStream(t,a,i):(Array.isArray(t)||ArrayBuffer.isView(t))&&await writeArray(t,a,i);a.push("\\nendobj\\n")}async function writeDict(e,t,a){t.push("<<");for(const r of e.getKeys()){t.push(` /${escapePDFName(r)} `);await writeValue(e.getRaw(r),t,a)}t.push(">>")}async function writeStream(e,t,a){let r=e.getBytes();const{dict:i}=e,[n,s]=await Promise.all([i.getAsync("Filte\ -r"),i.getAsync("DecodeParms")]),o=isName(Array.isArray(n)?await i.xref.fetchIfRefAsync(n[0]):n,"FlateDecode");if(r.length>=256||o)try{const e=new CompressionStream("deflate"),t=e.writable.getWriter();await t.ready;t.write(r).then((async()=>{await t.ready;await t.close()})).catch((()=>{}));const a=await new Response(e.readable).arrayBuffer();r=new Uint8Array(a);let c,l;if(n){if(!o){c=Array.isArray(n)?[Name.get("FlateDecode"),...n]:[Name.get("FlateDecode"),n];s&&(l=Array.isArray(s)?[null,...s]:[nu\ -ll,s])}}else c=Name.get("FlateDecode");c&&i.set("Filter",c);l&&i.set("DecodeParms",l)}catch(e){info(`writeStream - cannot compress data: "${e}".`)}let c=bytesToString(r);a&&(c=a.encryptString(c));i.set("Length",c.length);await writeDict(i,t,a);t.push(" stream\\n",c,"\\nendstream")}async function writeArray(e,t,a){t.push("[");let r=!0;for(const i of e){r?r=!1:t.push(" ");await writeValue(i,t,a)}t.push("]")}async function writeValue(e,t,a){if(e instanceof Name)t.push(`/${escapePDFName(e.name)}`);els\ -e if(e instanceof Ref)t.push(`${e.num} ${e.gen} R`);else if(Array.isArray(e)||ArrayBuffer.isView(e))await writeArray(e,t,a);else if("string"==typeof e){a&&(e=a.encryptString(e));t.push(`(${escapeString(e)})`)}else"number"==typeof e?t.push(numberToString(e)):"boolean"==typeof e?t.push(e.toString()):e instanceof Dict?await writeDict(e,t,a):e instanceof BaseStream?await writeStream(e,t,a):null===e?t.push("null"):warn(`Unhandled value in writer: ${typeof e}, please file a bug.`)}function writeInt(e,\ -t,a,r){for(let i=t+a-1;i>a-1;i--){r[i]=255&e;e>>=8}return a+t}function writeString(e,t,a){const r=e.length;for(let i=0;i1&&(n=a.documentElement.searchNode([i.at(-1)],0));n?n.childNodes=Array.isArray(r)?r.map((e=>new SimpleDOMNode("value",e))):[new SimpleDOMNode("#text",r)]:warn(`Node not found for path: ${t}`)}const r=[];a.documentElement.dump(r);return r.join("")}(r.fetchIfRef(t).getString(),a)}const i=new StringStream(e);i.dict=new Dict(r);i.dict.setIfName("Type","EmbeddedFile");a.put(t,{data:i})}function getIndexes(e){const t=[];for(const{ref:a}of e)a.num===t.at(-2)+t.at(-1)?t[t.length-1]+=1:t.push(a.num,1\ -);return t}function computeIDs(e,t,a){if(Array.isArray(t.fileIds)&&t.fileIds.length>0){const r=function computeMD5(e,t){const a=Math.floor(Date.now()/1e3),r=t.filename||"",i=[a.toString(),r,e.toString(),...t.infoMap.values()],n=Math.sumPrecise(i.map((e=>e.length))),s=new Uint8Array(n);let o=0;for(const e of i)o=writeString(e,o,s);return bytesToString(calculateMD5(s,0,s.length))}(e,t);a.set("ID",[t.fileIds[0],r])}}async function incrementalUpdate({originalData:e,xrefInfo:t,changes:a,xref:r=null,h\ -asXfa:i=!1,xfaDatasetsRef:n=null,hasXfaDatasetsEntry:s=!1,needAppearances:o,acroFormRef:c=null,acroForm:l=null,xfaData:h=null,useXrefStream:u=!1}){await async function updateAcroform({xref:e,acroForm:t,acroFormRef:a,hasXfa:r,hasXfaDatasetsEntry:i,xfaDatasetsRef:n,needAppearances:s,changes:o}){!r||i||n||warn("XFA - Cannot save it");if(!s&&(!r||!n||i))return;const c=t.clone();if(r&&!i){const e=t.get("XFA").slice();e.splice(2,0,"datasets");e.splice(3,0,n);c.set("XFA",e)}s&&c.set("NeedAppearances",!\ -0);o.put(a,{data:c})}({xref:r,acroForm:l,acroFormRef:c,hasXfa:i,hasXfaDatasetsEntry:s,xfaDatasetsRef:n,needAppearances:o,changes:a});i&&updateXFA({xfaData:h,xfaDatasetsRef:n,changes:a,xref:r});const d=function getTrailerDict(e,t,a){const r=new Dict(null);r.set("Prev",e.startXRef);const i=e.newRef;if(a){t.put(i,{data:""});r.set("Size",i.num+1);r.setIfName("Type","XRef")}else r.set("Size",i.num);null!==e.rootRef&&r.set("Root",e.rootRef);null!==e.infoRef&&r.set("Info",e.infoRef);null!==e.encryptRef\ -&&r.set("Encrypt",e.encryptRef);return r}(t,a,u),f=[],g=await async function writeChanges(e,t,a=[]){const r=[];for(const[i,{data:n}]of e.items())if(null!==n&&"string"!=typeof n){await writeObject(i,n,a,t);r.push({ref:i,data:a.join("")});a.length=0}else r.push({ref:i,data:n});return r.sort(((e,t)=>e.ref.num-t.ref.num))}(a,r,f);let p=e.length;const m=e.at(-1);if(10!==m&&13!==m){f.push("\\n");p+=1}for(const{data:e}of g)null!==e&&f.push(e);await(u?async function getXRefStreamTable(e,t,a,r,i){const n=\ -[];let s=0,o=0;for(const{ref:e,data:r}of a){let a;s=Math.max(s,t);if(null!==r){a=Math.min(e.gen,65535);n.push([1,t,a]);t+=r.length}else{a=Math.min(e.gen+1,65535);n.push([0,0,a])}o=Math.max(o,a)}r.set("Index",getIndexes(a));const c=[1,getSizeInBytes(s),getSizeInBytes(o)];r.set("W",c);computeIDs(t,e,r);const l=Math.sumPrecise(c),h=new Uint8Array(l*n.length),u=new Stream(h);u.dict=r;let d=0;for(const[e,t,a]of n){d=writeInt(e,c[0],d,h);d=writeInt(t,c[1],d,h);d=writeInt(a,c[2],d,h)}await writeObject(\ -e.newRef,u,i,{});i.push("startxref\\n",t.toString(),"\\n%%EOF\\n")}(t,p,g,d,f):async function getXRefTable(e,t,a,r,i){i.push("xref\\n");const n=getIndexes(a);let s=0;for(const{ref:e,data:r}of a){if(e.num===n[s]){i.push(`${n[s]} ${n[s+1]}\\n`);s+=2}if(null!==r){i.push(`${t.toString().padStart(10,"0")} ${Math.min(e.gen,65535).toString().padStart(5,"0")} n\\r\\n`);t+=r.length}else i.push(`0000000000 ${Math.min(e.gen+1,65535).toString().padStart(5,"0")} f\\r\\n`)}computeIDs(t,e,r);i.push("trailer\\n");await w\ -riteDict(r,i);i.push("\\nstartxref\\n",t.toString(),"\\n%%EOF\\n")}(t,p,g,d,f));const b=e.length+Math.sumPrecise(f.map((e=>e.length))),y=new Uint8Array(b);y.set(e);let w=e.length;for(const e of f)w=writeString(e,w,y);return y}class PDFWorkerStream{constructor(e){this._msgHandler=e;this._contentLength=null;this._fullRequestReader=null;this._rangeRequestReaders=[]}getFullReader(){assert(!this._fullRequestReader,"PDFWorkerStream.getFullReader can only be called once.");this._fullRequestReader=new PDFWo\ -rkerStreamReader(this._msgHandler);return this._fullRequestReader}getRangeReader(e,t){const a=new PDFWorkerStreamRangeReader(e,t,this._msgHandler);this._rangeRequestReaders.push(a);return a}cancelAllRequests(e){this._fullRequestReader?.cancel(e);for(const t of this._rangeRequestReaders.slice(0))t.cancel(e)}}class PDFWorkerStreamReader{constructor(e){this._msgHandler=e;this.onProgress=null;this._contentLength=null;this._isRangeSupported=!1;this._isStreamingSupported=!1;const t=this._msgHandler.se\ -ndWithStream("GetReader");this._reader=t.getReader();this._headersReady=this._msgHandler.sendWithPromise("ReaderHeadersReady").then((e=>{this._isStreamingSupported=e.isStreamingSupported;this._isRangeSupported=e.isRangeSupported;this._contentLength=e.contentLength}))}get headersReady(){return this._headersReady}get contentLength(){return this._contentLength}get isStreamingSupported(){return this._isStreamingSupported}get isRangeSupported(){return this._isRangeSupported}async read(){const{value:e\ -,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class PDFWorkerStreamRangeReader{constructor(e,t,a){this._msgHandler=a;this.onProgress=null;const r=this._msgHandler.sendWithStream("GetRangeReader",{begin:e,end:t});this._reader=r.getReader()}get isStreamingSupported(){return!1}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.canc\ -el(e)}}class WorkerTask{constructor(e){this.name=e;this.terminated=!1;this._capability=Promise.withResolvers()}get finished(){return this._capability.promise}finish(){this._capability.resolve()}terminate(){this.terminated=!0}ensureNotTerminated(){if(this.terminated)throw new Error("Worker task was terminated")}}class WorkerMessageHandler{static{"undefined"==typeof window&&!e&&"undefined"!=typeof self&&"function"==typeof self.postMessage&&"onmessage"in self&&this.initializeFromPort(self)}static s\ -etup(e,t){let a=!1;e.on("test",(t=>{if(!a){a=!0;e.send("test",t instanceof Uint8Array)}}));e.on("configure",(e=>{!function setVerbosityLevel(e){Number.isInteger(e)&&(da=e)}(e.verbosity)}));e.on("GetDocRequest",(e=>this.createDocumentHandler(e,t)))}static createDocumentHandler(e,t){let a,r=!1,i=null;const n=new Set,s=getVerbosityLevel(),{docId:o,apiVersion:c}=e,l="5.4.54";if(c!==l)throw new Error(`The API version "${c}" does not match the Worker version "${l}".`);const buildMsg=(e,t)=>`The \\`${e}\ -.prototype\\` contains unexpected enumerable property "${t}", thus breaking e.g. \\`for...in\\` iteration of ${e}s.`;for(const e in{})throw new Error(buildMsg("Object",e));for(const e in[])throw new Error(buildMsg("Array",e));const h=o+"_worker";let u=new MessageHandler(h,o,t);function ensureNotTerminated(){if(r)throw new Error("Worker was terminated")}function startWorkerTask(e){n.add(e)}function finishWorkerTask(e){e.finish();n.delete(e)}async function loadDocument(e){await a.ensureDoc("checkHead\ -er");await a.ensureDoc("parseStartXRef");await a.ensureDoc("parse",[e]);await a.ensureDoc("checkFirstPage",[e]);await a.ensureDoc("checkLastPage",[e]);const t=await a.ensureDoc("isPureXfa");if(t){const e=new WorkerTask("loadXfaResources");startWorkerTask(e);await a.ensureDoc("loadXfaResources",[u,e]);finishWorkerTask(e)}const[r,i]=await Promise.all([a.ensureDoc("numPages"),a.ensureDoc("fingerprints")]);return{numPages:r,fingerprints:i,htmlForXfa:t?await a.ensureDoc("htmlForXfa"):null}}function s\ -etupDoc(e){function onSuccess(e){ensureNotTerminated();u.send("GetDoc",{pdfInfo:e})}function onFailure(e){ensureNotTerminated();if(e instanceof PasswordException){const t=new WorkerTask(`PasswordException: response ${e.code}`);startWorkerTask(t);u.sendWithPromise("PasswordRequest",e).then((function({password:e}){finishWorkerTask(t);a.updatePassword(e);pdfManagerReady()})).catch((function(){finishWorkerTask(t);u.send("DocException",e)}))}else u.send("DocException",wrapReason(e))}function pdfManag\ -erReady(){ensureNotTerminated();loadDocument(!1).then(onSuccess,(function(e){ensureNotTerminated();e instanceof XRefParseException?a.requestLoadedStream().then((function(){ensureNotTerminated();loadDocument(!0).then(onSuccess,onFailure)})):onFailure(e)}))}ensureNotTerminated();(async function getPdfManager({data:e,password:t,disableAutoFetch:a,rangeChunkSize:r,length:n,docBaseUrl:s,enableXfa:c,evaluatorOptions:l}){const h={source:null,disableAutoFetch:a,docBaseUrl:s,docId:o,enableXfa:c,evaluator\ -Options:l,handler:u,length:n,password:t,rangeChunkSize:r};if(e){h.source=e;return new LocalPdfManager(h)}const d=new PDFWorkerStream(u),f=d.getFullReader(),g=Promise.withResolvers();let p,m=[],b=0;f.headersReady.then((function(){if(f.isRangeSupported){h.source=d;h.length=f.contentLength;h.disableAutoFetch||=f.isStreamingSupported;p=new NetworkPdfManager(h);for(const e of m)p.sendProgressiveData(e);m=[];g.resolve(p);i=null}})).catch((function(e){g.reject(e);i=null}));new Promise((function(e,t){co\ -nst readChunk=function({value:e,done:a}){try{ensureNotTerminated();if(a){if(!p){const e=arrayBuffersToBytes(m);m=[];n&&e.length!==n&&warn("reported HTTP length is different from actual");h.source=e;p=new LocalPdfManager(h);g.resolve(p)}i=null;return}b+=e.byteLength;f.isStreamingSupported||u.send("DocProgress",{loaded:b,total:Math.max(b,f.contentLength||0)});p?p.sendProgressiveData(e):m.push(e);f.read().then(readChunk,t)}catch(e){t(e)}};f.read().then(readChunk,t)})).catch((function(e){g.reject(e)\ -;i=null}));i=e=>{d.cancelAllRequests(e)};return g.promise})(e).then((function(e){if(r){e.terminate(new AbortException("Worker was terminated."));throw new Error("Worker was terminated")}a=e;a.requestLoadedStream(!0).then((e=>{u.send("DataLoaded",{length:e.bytes.byteLength})}))})).then(pdfManagerReady,onFailure)}u.on("GetPage",(function(e){return a.getPage(e.pageIndex).then((function(e){return Promise.all([a.ensure(e,"rotate"),a.ensure(e,"ref"),a.ensure(e,"userUnit"),a.ensure(e,"view")]).then((fu\ -nction([e,t,a,r]){return{rotate:e,ref:t,refStr:t?.toString()??null,userUnit:a,view:r}}))}))}));u.on("GetPageIndex",(function(e){const t=Ref.get(e.num,e.gen);return a.ensureCatalog("getPageIndex",[t])}));u.on("GetDestinations",(function(e){return a.ensureCatalog("destinations")}));u.on("GetDestination",(function(e){return a.ensureCatalog("getDestination",[e.id])}));u.on("GetPageLabels",(function(e){return a.ensureCatalog("pageLabels")}));u.on("GetPageLayout",(function(e){return a.ensureCatalog("p\ -ageLayout")}));u.on("GetPageMode",(function(e){return a.ensureCatalog("pageMode")}));u.on("GetViewerPreferences",(function(e){return a.ensureCatalog("viewerPreferences")}));u.on("GetOpenAction",(function(e){return a.ensureCatalog("openAction")}));u.on("GetAttachments",(function(e){return a.ensureCatalog("attachments")}));u.on("GetDocJSActions",(function(e){return a.ensureCatalog("jsActions")}));u.on("GetPageJSActions",(function({pageIndex:e}){return a.getPage(e).then((e=>a.ensure(e,"jsActions"))\ -)}));u.on("GetOutline",(function(e){return a.ensureCatalog("documentOutline")}));u.on("GetOptionalContentConfig",(function(e){return a.ensureCatalog("optionalContentConfig")}));u.on("GetPermissions",(function(e){return a.ensureCatalog("permissions")}));u.on("GetMetadata",(function(e){return Promise.all([a.ensureDoc("documentInfo"),a.ensureCatalog("metadata")])}));u.on("GetMarkInfo",(function(e){return a.ensureCatalog("markInfo")}));u.on("GetData",(function(e){return a.requestLoadedStream().then(\ -(e=>e.bytes))}));u.on("GetAnnotations",(function({pageIndex:e,intent:t}){return a.getPage(e).then((function(a){const r=new WorkerTask(`GetAnnotations: page ${e}`);startWorkerTask(r);return a.getAnnotationsData(u,r,t).then((e=>{finishWorkerTask(r);return e}),(e=>{finishWorkerTask(r);throw e}))}))}));u.on("GetFieldObjects",(function(e){return a.ensureDoc("fieldObjects").then((e=>e?.allFields||null))}));u.on("HasJSActions",(function(e){return a.ensureDoc("hasJSActions")}));u.on("GetCalculationOrder\ -Ids",(function(e){return a.ensureDoc("calculationOrderIds")}));u.on("SaveDocument",(async function({isPureXfa:e,numPages:t,annotationStorage:r,filename:i}){const n=[a.requestLoadedStream(),a.ensureCatalog("acroForm"),a.ensureCatalog("acroFormRef"),a.ensureDoc("startXRef"),a.ensureDoc("xref"),a.ensureDoc("linearization"),a.ensureCatalog("structTreeRoot")],s=new RefSetCache,o=[],c=e?null:getNewAnnotationsMap(r),[l,h,d,f,g,p,m]=await Promise.all(n),b=g.trailer.getRaw("Root")||null;let y;if(c){m?awa\ -it m.canUpdateStructTree({pdfManager:a,newAnnotationsByPage:c})&&(y=m):await StructTreeRoot.canCreateStructureTree({catalogRef:b,pdfManager:a,newAnnotationsByPage:c})&&(y=null);const e=AnnotationFactory.generateImages(r.values(),g,a.evaluatorOptions.isOffscreenCanvasSupported),t=void 0===y?o:[];for(const[r,i]of c)t.push(a.getPage(r).then((t=>{const a=new WorkerTask(`Save (editor): page ${r}`);startWorkerTask(a);return t.saveNewAnnotations(u,a,i,e,s).finally((function(){finishWorkerTask(a)}))})))\ -;null===y?o.push(Promise.all(t).then((async()=>{await StructTreeRoot.createStructureTree({newAnnotationsByPage:c,xref:g,catalogRef:b,pdfManager:a,changes:s})}))):y&&o.push(Promise.all(t).then((async()=>{await y.updateStructureTree({newAnnotationsByPage:c,pdfManager:a,changes:s})})))}if(e)o.push(a.ensureDoc("serializeXfaData",[r]));else for(let e=0;ee.needAppearances)),k=h instanceof Dict&&h.get("XFA")||null;let C=null,v=!1;if(Array.isArray(k)){for(let e=0,t=k.length;e{g.resetNewTemporaryRef()}))}));u.on("GetOperatorList",(function(e,t){const r=e.pageIndex;a.getPage(r).then((function(a){const i=new WorkerTask(`GetOperatorList: page ${r}`);startWorkerTask(i);const n=s>=Ae?Date.now():0;a.getOperatorList({handler:u,sink:t,task:i,intent:e.intent,cacheKey:e.cacheKey,annotationStorage:e.annotationStorage,modifiedIds:e.modifiedIds}).then((function(e){finishWorkerTask(i);n&&info(`page=${r+1} - getOperatorList: time\ -=${Date.now()-n}ms, len=${e.length}`);t.close()}),(function(e){finishWorkerTask(i);i.terminated||t.error(e)}))}))}));u.on("GetTextContent",(function(e,t){const{pageIndex:r,includeMarkedContent:i,disableNormalization:n}=e;a.getPage(r).then((function(e){const a=new WorkerTask("GetTextContent: page "+r);startWorkerTask(a);const o=s>=Ae?Date.now():0;e.extractTextContent({handler:u,task:a,sink:t,includeMarkedContent:i,disableNormalization:n}).then((function(){finishWorkerTask(a);o&&info(`page=${r+1} \ -- getTextContent: time=`+(Date.now()-o)+"ms");t.close()}),(function(e){finishWorkerTask(a);a.terminated||t.error(e)}))}))}));u.on("GetStructTree",(function(e){return a.getPage(e.pageIndex).then((e=>a.ensure(e,"getStructTree")))}));u.on("FontFallback",(function(e){return a.fontFallback(e.id,u)}));u.on("Cleanup",(function(e){return a.cleanup(!0)}));u.on("Terminate",(function(e){r=!0;const t=[];if(a){a.terminate(new AbortException("Worker was terminated."));const e=a.cleanup();t.push(e);a=null}else\ - clearGlobalCaches();i?.(new AbortException("Worker was terminated."));for(const e of n){t.push(e.finished);e.terminate()}return Promise.all(t).then((function(){u.destroy();u=null}))}));u.on("Ready",(function(t){setupDoc(e);e=null}));return h}static initializeFromPort(e){const t=new MessageHandler("worker","main",e);this.setup(t,e);t.send("ready",null)}}globalThis.pdfjsWorker={WorkerMessageHandler};export{WorkerMessageHandler};',pdf_worker_min$1=Object.freeze(Object.defineProperty({__proto__:null, -default:pdf_worker_min},Symbol.toStringTag,{value:"Module"}));export{app,start}; diff --git a/tools/server/public/index.html b/tools/server/public/index.html deleted file mode 100644 index 4c8c8d50b62..00000000000 --- a/tools/server/public/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - -
    - -
    - - diff --git a/tools/server/public/loading.html b/tools/server/public/loading.html deleted file mode 100644 index c3fd19a0f5a..00000000000 --- a/tools/server/public/loading.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - -
    - The model is loading. Please wait.
    - The user interface will appear soon. -
    - - diff --git a/tools/server/server-http.cpp b/tools/server/server-http.cpp index dd5a6f9e756..af4536fdd77 100644 --- a/tools/server/server-http.cpp +++ b/tools/server/server-http.cpp @@ -238,30 +238,30 @@ bool server_http_context::init(const common_params & params) { }; auto middleware_server_state = [this](const httplib::Request & req, httplib::Response & res) { + (void)req; // suppress unused parameter warning when LLAMA_BUILD_WEBUI is not defined bool ready = is_ready.load(); if (!ready) { #ifdef LLAMA_BUILD_WEBUI auto tmp = string_split(req.path, '.'); - if (req.path == "/" || tmp.back() == "html") { + if (req.path == "/" || (tmp.size() > 0 && tmp.back() == "html")) { res.status = 503; res.set_content(reinterpret_cast(loading_html), loading_html_len, "text/html; charset=utf-8"); - } else -#endif - { - // no endpoints is allowed to be accessed when the server is not ready - // this is to prevent any data races or inconsistent states - res.status = 503; - res.set_content( - safe_json_to_str(json { - {"error", { - {"message", "Loading model"}, - {"type", "unavailable_error"}, - {"code", 503} - }} - }), - "application/json; charset=utf-8" - ); + return false; } +#endif + // no endpoints are allowed to be accessed when the server is not ready + // this is to prevent any data races or inconsistent states + res.status = 503; + res.set_content( + safe_json_to_str(json { + {"error", { + {"message", "Loading model"}, + {"type", "unavailable_error"}, + {"code", 503} + }} + }), + "application/json; charset=utf-8" + ); return false; } return true; diff --git a/tools/server/webui/README.md b/tools/server/webui/README.md index 4532bae3efa..40742b00e1a 100644 --- a/tools/server/webui/README.md +++ b/tools/server/webui/README.md @@ -1,6 +1,6 @@ -# llama.cpp Web UI +# llama-ui -A modern, feature-rich web interface for llama.cpp built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities. +A modern, feature-rich web interface for llama-server built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities. The WebUI supports two server operation modes: diff --git a/tools/server/webui/package-lock.json b/tools/server/webui/package-lock.json index 4449d8852b2..bf23307b82c 100644 --- a/tools/server/webui/package-lock.json +++ b/tools/server/webui/package-lock.json @@ -1,11 +1,11 @@ { - "name": "llama-server-webui", + "name": "llama-ui", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "llama-server-webui", + "name": "llama-ui", "version": "1.0.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.25.1", diff --git a/tools/server/webui/package.json b/tools/server/webui/package.json index cb33b4845be..2338c38402a 100644 --- a/tools/server/webui/package.json +++ b/tools/server/webui/package.json @@ -1,5 +1,5 @@ { - "name": "llama-server-webui", + "name": "llama-ui", "private": true, "version": "1.0.0", "type": "module", diff --git a/tools/server/webui/scripts/dev.sh b/tools/server/webui/scripts/dev.sh index b7539c205e2..97c14b87328 100644 --- a/tools/server/webui/scripts/dev.sh +++ b/tools/server/webui/scripts/dev.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Development script for llama.cpp webui -# +# Development script for llama-ui +# # This script starts the webui development servers (Storybook and Vite). # Note: You need to start llama-server separately. # @@ -14,12 +14,12 @@ cd ../../../ # Check and install git hooks if missing check_and_install_hooks() { local hooks_missing=false - + # Check for required hooks if [ ! -f ".git/hooks/pre-commit" ] || [ ! -f ".git/hooks/pre-push" ] || [ ! -f ".git/hooks/post-push" ]; then hooks_missing=true fi - + if [ "$hooks_missing" = true ]; then echo "🔧 Git hooks missing, installing them..." cd tools/server/webui diff --git a/tools/server/webui/scripts/install-git-hooks.sh b/tools/server/webui/scripts/install-git-hooks.sh index efdea025c16..8aa1014ba36 100755 --- a/tools/server/webui/scripts/install-git-hooks.sh +++ b/tools/server/webui/scripts/install-git-hooks.sh @@ -1,7 +1,7 @@ #!/bin/bash # Script to install pre-commit hook for webui -# Pre-commit: formats, checks, builds, and stages build output +# Pre-commit: formats, checks, and builds webui REPO_ROOT=$(git rev-parse --show-toplevel) PRE_COMMIT_HOOK="$REPO_ROOT/.git/hooks/pre-commit" @@ -56,11 +56,7 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then exit 1 fi - # Stage the build output alongside the source changes - cd "$REPO_ROOT" - git add tools/server/public/ - - echo "✅ Webui built and build output staged" + echo "✅ Webui built successfully" fi exit 0 @@ -75,7 +71,7 @@ if [ $? -eq 0 ]; then echo "" echo "The hook will automatically:" echo " • Format, lint and check webui code before commits" - echo " • Build webui and stage tools/server/public/ into the same commit" + echo " • Build webui" else echo "❌ Failed to make hook executable" exit 1 diff --git a/tools/server/webui/src/lib/components/app/server/ServerLoadingSplash.svelte b/tools/server/webui/src/lib/components/app/server/ServerLoadingSplash.svelte index 505325d656d..95fa61e9369 100644 --- a/tools/server/webui/src/lib/components/app/server/ServerLoadingSplash.svelte +++ b/tools/server/webui/src/lib/components/app/server/ServerLoadingSplash.svelte @@ -8,8 +8,7 @@ message?: string; } - let { class: className = '', message = 'Initializing connection to llama.cpp server...' }: Props = - $props(); + let { class: className = '', message = 'Initializing connection to server...' }: Props = $props();
    diff --git a/tools/server/webui/src/lib/constants/ui.ts b/tools/server/webui/src/lib/constants/ui.ts index 5f3c532dbd1..f6e7f7d8a9b 100644 --- a/tools/server/webui/src/lib/constants/ui.ts +++ b/tools/server/webui/src/lib/constants/ui.ts @@ -5,7 +5,7 @@ import { ROUTES } from './routes'; export const FORK_TREE_DEPTH_PADDING = 8; export const SYSTEM_MESSAGE_PLACEHOLDER = 'System message'; -export const APP_NAME = import.meta.env.VITE_PUBLIC_APP_NAME || 'llama.cpp'; +export const APP_NAME = import.meta.env.VITE_PUBLIC_APP_NAME || 'llama-ui'; export const ICON_STRIP_TRANSITION_DURATION = 150; export const ICON_STRIP_TRANSITION_DELAY_MULTIPLIER = 50; diff --git a/tools/server/webui/src/lib/services/chat.service.ts b/tools/server/webui/src/lib/services/chat.service.ts index 587b48b6f19..2cd521da0bd 100644 --- a/tools/server/webui/src/lib/services/chat.service.ts +++ b/tools/server/webui/src/lib/services/chat.service.ts @@ -76,7 +76,7 @@ export class ChatService { */ /** - * Sends a chat completion request to the llama.cpp server. + * Sends a chat completion request to the llama-server. * Supports both streaming and non-streaming responses with comprehensive parameter configuration. * Automatically converts database messages with attachments to the appropriate API format. * diff --git a/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte b/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte index 8d0981a4e16..e31d4443ef3 100644 --- a/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte +++ b/tools/server/webui/src/routes/(chat)/chat/[id]/+page.svelte @@ -3,7 +3,7 @@ import { page } from '$app/state'; import { afterNavigate } from '$app/navigation'; import { DialogModelNotAvailable } from '$lib/components/app'; - import { ROUTES } from '$lib/constants/routes'; + import { APP_NAME, ROUTES } from '$lib/constants'; import { chatStore, isLoading } from '$lib/stores/chat.svelte'; import { conversationsStore, activeConversation } from '$lib/stores/conversations.svelte'; import { modelsStore, modelOptions } from '$lib/stores/models.svelte'; @@ -125,7 +125,7 @@ - {activeConversation()?.name || 'Chat'} - llama.cpp + {activeConversation()?.name || 'Chat'} - {APP_NAME} Date: Thu, 14 May 2026 23:55:24 +0800 Subject: [PATCH 105/158] contributing: new contributors should not submit trivial fixes (#23045) --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8000b471867..99504f14f31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,9 @@ Before submitting your PR: - provide KL divergence data calculated vs. the FP16/BF16 (whichever is the native precision) version for both the new type as well as types of similar size - provide [performance data](https://github.com/ggml-org/llama.cpp/tree/master/tools/llama-bench) for the new type in comparison to types of similar size on pure CPU - Consider allowing write access to your branch for faster reviews, as reviewers can push commits directly -- If you are a new contributor, limit your open PRs to 1. +- If you are a new contributor + - Limit your open PRs to 1 + - Do not submit trivial fixes (e.g. typos, formatting changes) After submitting your PR: - Expect requests for modifications to ensure the code meets llama.cpp's standards for quality and long-term maintainability From 0c3e4fccca8aea028df37d39510e9df11d90c1b3 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Thu, 14 May 2026 17:57:20 +0200 Subject: [PATCH 106/158] fix: Propagate version tag to WebUI asset download in self-hosted CI (#23051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Propagate version tag to WebUI asset download in self-hosted CI * refactor: Apply suggestions from @CISC Co-authored-by: Sigbjørn Skjæret * fix: Skip npm build when Node.js is not installed Avoid 'no such file or directory' errors on CI runners that lack Node.js. Check if npm is available via find_program before attempting npm install + npm run build. Falls back to HF Bucket download. * fix: Use + separator for ASSETS list to fix Windows build Replace fragile \; escaping with a + separator when passing the WebUI asset list via -DASSETS to the download script. On Windows, the \; escaping was not reliably preserved through the CMake build system, causing all asset filenames to be concatenated into one (e.g., 'index.html;bundle.js;bundle.css;loading.html' as a single file), which broke the HF Bucket download and subsequent xxd.cmake step. + is safe because it is not special in cmd.exe (unlike | which is a pipe operator), not special in CMake's -D argument parser, and not a valid Windows filename character. CMakeLists.txt joins assets with + and webui-download.cmake splits them back via regex. * fix: Validate HF_WEBUI_VERSION environment variable with regex Add input validation for the HF_WEBUI_VERSION env var to prevent CMake list separator or path-traversal issues in stamp filenames and download URLs. Rejects non-conforming characters early. * fix: Remove 'latest' fallback for HF_WEBUI_VERSION When needs.determine-tag.outputs.tag_name is empty, let CMake's default resolution handle it (empty -> git-based version lookup) instead of falling back to 'latest'. This ensures the sentinel stamp file is consistent with CMake's resolution logic. * fix: Demote checksum verification failure to warning instead of hard gate * fix: End line character --------- Co-authored-by: Sigbjørn Skjæret --- .github/workflows/build-self-hosted.yml | 40 ++++++++++++ scripts/webui-download.cmake | 85 ++++++++++++++----------- tools/server/CMakeLists.txt | 21 ++++-- 3 files changed, 102 insertions(+), 44 deletions(-) diff --git a/.github/workflows/build-self-hosted.yml b/.github/workflows/build-self-hosted.yml index e9148dd7399..9121e7a9eae 100644 --- a/.github/workflows/build-self-hosted.yml +++ b/.github/workflows/build-self-hosted.yml @@ -55,7 +55,22 @@ env: LLAMA_LOG_TIMESTAMPS: 1 jobs: + determine-tag: + name: Determine tag name + runs-on: ubuntu-slim + outputs: + tag_name: ${{ steps.tag.outputs.name }} + steps: + - name: Clone + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Determine tag name + id: tag + uses: ./.github/actions/get-tag-name + ggml-ci-nvidia-cuda: + needs: determine-tag runs-on: [self-hosted, Linux, NVIDIA] steps: @@ -65,11 +80,14 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | nvidia-smi GG_BUILD_CUDA=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp ggml-ci-nvidia-vulkan-cm: + needs: determine-tag runs-on: [self-hosted, Linux, NVIDIA] steps: @@ -79,11 +97,14 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 GGML_VK_DISABLE_COOPMAT2=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp ggml-ci-nvidia-vulkan-cm2: + needs: determine-tag runs-on: [self-hosted, Linux, NVIDIA, COOPMAT2] steps: @@ -93,6 +114,8 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp @@ -172,6 +195,7 @@ jobs: # GG_BUILD_ROCM=1 GG_BUILD_AMDGPU_TARGETS="gfx1101" bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp ggml-ci-mac-metal: + needs: determine-tag runs-on: [self-hosted, macOS, ARM64] steps: @@ -181,10 +205,13 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | GG_BUILD_METAL=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp ggml-ci-mac-webgpu: + needs: determine-tag runs-on: [self-hosted, macOS, ARM64] steps: @@ -207,11 +234,14 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | GG_BUILD_WEBGPU=1 GG_BUILD_WEBGPU_DAWN_PREFIX="$GITHUB_WORKSPACE/dawn" \ bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp ggml-ci-mac-vulkan: + needs: determine-tag runs-on: [self-hosted, macOS, ARM64] steps: @@ -221,11 +251,14 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp ggml-ci-linux-intel-vulkan: + needs: determine-tag runs-on: [self-hosted, Linux, Intel] steps: @@ -237,11 +270,14 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp ggml-ci-win-intel-vulkan: + needs: determine-tag runs-on: [self-hosted, Windows, X64, Intel] steps: @@ -256,6 +292,7 @@ jobs: MSYSTEM: UCRT64 CHERE_INVOKING: 1 PATH: C:\msys64\ucrt64\bin;C:\msys64\usr\bin;C:\Windows\System32;${{ env.PATH }} + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary # Skip python related tests with GG_BUILD_LOW_PERF=1 since Windows MSYS2 UCRT64 currently fails to create @@ -263,6 +300,7 @@ jobs: LLAMA_FATAL_WARNINGS=OFF GG_BUILD_NINJA=1 GG_BUILD_VULKAN=1 GG_BUILD_LOW_PERF=1 ./ci/run.sh ./results/llama.cpp ./mnt/llama.cpp ggml-ci-intel-openvino-gpu-low-perf: + needs: determine-tag runs-on: [self-hosted, Linux, Intel, OpenVINO] concurrency: @@ -294,6 +332,8 @@ jobs: - name: Test id: ggml-ci + env: + HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | source ./openvino_toolkit/setupvars.sh GG_BUILD_OPENVINO=1 GGML_OPENVINO_DEVICE=GPU GG_BUILD_LOW_PERF=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt diff --git a/scripts/webui-download.cmake b/scripts/webui-download.cmake index 67b938fc3f7..344ba5ecf53 100644 --- a/scripts/webui-download.cmake +++ b/scripts/webui-download.cmake @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.16) set(PUBLIC_DIR "" CACHE STRING "Directory to store/download assets") set(HF_BUCKET "" CACHE STRING "Hugging Face bucket name") set(HF_VERSION "" CACHE STRING "Version to download (empty = resolve from git)") -set(ASSETS "" CACHE STRING "Semicolon-separated list of asset filenames") +set(ASSETS "" CACHE STRING "Plus-separated list of asset filenames (+)") set(STAMP_FILE "" CACHE STRING "Stamp file to create on success (optional)") set(SOURCE_DIR "" CACHE STRING "Project source root (to resolve version from git)") set(NPM_DIR "" CACHE STRING "WebUI source directory (to run npm build)") @@ -31,6 +31,10 @@ if("${RESOLVED_VERSION}" STREQUAL "" AND NOT "${SOURCE_DIR}" STREQUAL "") endif() endif() +# Convert + back to CMake list (+ is used as separator instead of ; to +# avoid platform-specific escaping issues when passing via -D arguments) +string(REGEX REPLACE "\\+" ";" ASSETS "${ASSETS}") + # --------------------------------------------------------------------------- # 2. Check stamp freshness — re-download if resolved version changed # --------------------------------------------------------------------------- @@ -69,52 +73,58 @@ set(PROVISION_SUCCESS FALSE) if(NOT PROVISION_SUCCESS AND NOT "${NPM_DIR}" STREQUAL "") if(EXISTS "${NPM_DIR}/package.json") - message(STATUS "WebUI: building from source in ${NPM_DIR}") + # Check if npm is available before attempting npm build + find_program(NPM_EXECUTABLE npm) + if(NPM_EXECUTABLE) + message(STATUS "WebUI: building from source in ${NPM_DIR}") + + # Run npm install if node_modules is missing + if(NOT EXISTS "${NPM_DIR}/node_modules") + message(STATUS "WebUI: running npm install (first time)") + execute_process( + COMMAND npm install + WORKING_DIRECTORY "${NPM_DIR}" + RESULT_VARIABLE NPM_INSTALL_RESULT + OUTPUT_VARIABLE NPM_OUT + ERROR_VARIABLE NPM_ERR + ) + if(NOT NPM_INSTALL_RESULT EQUAL 0) + message(STATUS "WebUI: npm install failed (${NPM_INSTALL_RESULT}), falling back to download") + message(STATUS " stderr: ${NPM_ERR}") + endif() + endif() - # Run npm install if node_modules is missing - if(NOT EXISTS "${NPM_DIR}/node_modules") - message(STATUS "WebUI: running npm install (first time)") + # Run the build execute_process( - COMMAND npm install + COMMAND npm run build WORKING_DIRECTORY "${NPM_DIR}" - RESULT_VARIABLE NPM_INSTALL_RESULT + RESULT_VARIABLE NPM_BUILD_RESULT OUTPUT_VARIABLE NPM_OUT ERROR_VARIABLE NPM_ERR ) - if(NOT NPM_INSTALL_RESULT EQUAL 0) - message(STATUS "WebUI: npm install failed (${NPM_INSTALL_RESULT}), falling back to download") - message(STATUS " stderr: ${NPM_ERR}") - endif() - endif() - # Run the build - execute_process( - COMMAND npm run build - WORKING_DIRECTORY "${NPM_DIR}" - RESULT_VARIABLE NPM_BUILD_RESULT - OUTPUT_VARIABLE NPM_OUT - ERROR_VARIABLE NPM_ERR - ) - - if(NPM_BUILD_RESULT EQUAL 0) - # Verify that the expected assets were produced - set(ALL_BUILT TRUE) - foreach(asset ${ASSETS}) - if(NOT EXISTS "${PUBLIC_DIR}/${asset}") - set(ALL_BUILT FALSE) - break() + if(NPM_BUILD_RESULT EQUAL 0) + # Verify that the expected assets were produced + set(ALL_BUILT TRUE) + foreach(asset ${ASSETS}) + if(NOT EXISTS "${PUBLIC_DIR}/${asset}") + set(ALL_BUILT FALSE) + break() + endif() + endforeach() + + if(ALL_BUILT) + message(STATUS "WebUI: local npm build succeeded") + set(PROVISION_SUCCESS TRUE) + else() + message(STATUS "WebUI: npm build completed but assets missing from ${PUBLIC_DIR}, falling back to download") endif() - endforeach() - - if(ALL_BUILT) - message(STATUS "WebUI: local npm build succeeded") - set(PROVISION_SUCCESS TRUE) else() - message(STATUS "WebUI: npm build completed but assets missing from ${PUBLIC_DIR}, falling back to download") + message(STATUS "WebUI: npm build failed (${NPM_BUILD_RESULT}), falling back to download") + message(STATUS " stderr: ${NPM_ERR}") endif() else() - message(STATUS "WebUI: npm build failed (${NPM_BUILD_RESULT}), falling back to download") - message(STATUS " stderr: ${NPM_ERR}") + message(STATUS "WebUI: npm not found, skipping npm build and trying HF Bucket download") endif() else() message(STATUS "WebUI: NPM_DIR (${NPM_DIR}) has no package.json, skipping npm build") @@ -178,8 +188,7 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) string(REGEX MATCH "${EXPECTED_HASH_UPPER}[ \\t]+${asset}" CHECKSUM_LINE "${CHECKSUMS_CONTENT}") if(NOT CHECKSUM_LINE) message(WARNING "WebUI: checksum verification failed for ${asset}") - set(ALL_OK FALSE) - break() + message(WARNING " downloaded file may not match expected checksum, but will be used") endif() endforeach() if(ALL_OK) diff --git a/tools/server/CMakeLists.txt b/tools/server/CMakeLists.txt index 2d628704b7b..20e4eb37697 100644 --- a/tools/server/CMakeLists.txt +++ b/tools/server/CMakeLists.txt @@ -76,12 +76,21 @@ if (LLAMA_BUILD_WEBUI) # Priority 2: Build-time asset provisioning (npm build → HF Bucket fallback) if(NOT WEBUI_SOURCE_DIR) - if(DEFINED LLAMA_BUILD_NUMBER) - set(HF_WEBUI_VERSION "${LLAMA_BUILD_NUMBER}") + # Environment variable takes precedence (e.g., from CI workflows) + if(DEFINED ENV{HF_WEBUI_VERSION}) + set(HF_WEBUI_VERSION "$ENV{HF_WEBUI_VERSION}") + # Validate against allowed characters to prevent CMake list separator + # or path-traversal issues in stamp filenames and download URLs + if(NOT HF_WEBUI_VERSION MATCHES "^[A-Za-z0-9._-]+$") + message(FATAL_ERROR "WebUI: invalid HF_WEBUI_VERSION='${HF_WEBUI_VERSION}' - must match ^[A-Za-z0-9._-]+$") + endif() + message(STATUS "WebUI: using HF_WEBUI_VERSION from environment=${HF_WEBUI_VERSION}") + elseif(DEFINED LLAMA_BUILD_NUMBER) + set(HF_WEBUI_VERSION "b${LLAMA_BUILD_NUMBER}") message(STATUS "WebUI: using LLAMA_BUILD_NUMBER=${HF_WEBUI_VERSION}") else() set(HF_WEBUI_VERSION "") - message(STATUS "WebUI: LLAMA_BUILD_NUMBER not defined") + message(STATUS "WebUI: version not specified (will use HF 'latest')") endif() # Stamp file embeds the version tag so a changed build number triggers @@ -93,8 +102,8 @@ if (LLAMA_BUILD_WEBUI) endif() set(WEBUI_STAMP "${CMAKE_CURRENT_BINARY_DIR}/.webui-${WEBUI_VERSION_TAG}.stamp") - # Escape semicolons so the CMake list is passed as a single -D argument - string(REPLACE ";" "\\;" PUBLIC_ASSETS_ESC "${PUBLIC_ASSETS}") + # Join assets with + separator (safe across all platforms, unlike ; and |) + string(REPLACE ";" "+" PUBLIC_ASSETS_JOINED "${PUBLIC_ASSETS}") add_custom_command( OUTPUT ${WEBUI_STAMP} @@ -104,7 +113,7 @@ if (LLAMA_BUILD_WEBUI) "-DHF_BUCKET=${LLAMA_WEBUI_HF_BUCKET}" "-DHF_VERSION=${HF_WEBUI_VERSION}" "-DHF_ENABLED=${LLAMA_USE_PREBUILT_WEBUI}" - "-DASSETS=${PUBLIC_ASSETS_ESC}" + "-DASSETS=${PUBLIC_ASSETS_JOINED}" "-DSTAMP_FILE=${WEBUI_STAMP}" "-DNPM_DIR=${CMAKE_CURRENT_SOURCE_DIR}/webui" -P ${PROJECT_SOURCE_DIR}/scripts/webui-download.cmake From 5ec717d1256e34558a44dc09adf1e6e16f2e2682 Mon Sep 17 00:00:00 2001 From: Zheyuan Chen Date: Thu, 14 May 2026 09:31:36 -0700 Subject: [PATCH 107/158] ggml-webgpu: makes the flash attn vec path subgroup-aware (#23040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ggml-webgpu: makes the flash attn vec path compile and size its split/reduce work from the device’s reported subgroup range instead of assuming 32 subgroup size. * ggml-webgpu: remove the extra max_wg_size >= max_subgroup_size guard. Remove hardcoded 32 when determine the value of reduce_wg_size and vec_nwg_cap --- ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp | 13 +++++++++---- ggml/src/ggml-webgpu/ggml-webgpu.cpp | 12 +++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp b/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp index 62a523365b9..4c4eda1cbe5 100644 --- a/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp +++ b/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp @@ -770,9 +770,14 @@ inline ggml_webgpu_flash_attn_decisions ggml_webgpu_flash_attn_get_decisions( (v_offset_elems % GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH == 0u); const bool kv_vec_type_supported = K->type == GGML_TYPE_F16 || K->type == GGML_TYPE_Q4_0 || K->type == GGML_TYPE_Q8_0; - const bool use_vec = context.supports_subgroups && (context.src0->ne[1] < 20) && (context.src0->ne[0] % 32 == 0) && - (context.src2->ne[0] % GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH == 0) && - kv_vec_type_supported && (K->type != GGML_TYPE_F16 || f16_vec4_aligned) && + const uint32_t kv_vec_head_align = K->type == GGML_TYPE_F16 ? GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH : + (uint32_t) ggml_blck_size(K->type); + const bool kv_vec_head_dims_aligned = context.src0->ne[0] % kv_vec_head_align == 0 && + context.src2->ne[0] % kv_vec_head_align == 0; + // Compile with enough invocations to cover the largest reported subgroup. + const bool use_vec = context.supports_subgroups && (context.src0->ne[1] < 20) && + kv_vec_head_dims_aligned && kv_vec_type_supported && + (K->type != GGML_TYPE_F16 || f16_vec4_aligned) && (context.src2->type == K->type); const bool tile_can_dispatch_all_q_rows = context.max_subgroup_size > 0 && @@ -808,7 +813,7 @@ inline ggml_webgpu_flash_attn_decisions ggml_webgpu_flash_attn_get_decisions( decisions.q_tile = 1u; decisions.kv_tile = std::max(8u, std::min(32u, max_kv_tile)); decisions.kv_tile = (decisions.kv_tile / 8u) * 8u; - decisions.wg_size = std::max(1u, std::min(32u, context.max_subgroup_size)); + decisions.wg_size = context.max_subgroup_size; if (decisions.kv_direct) { decisions.kv_tile = std::min(decisions.kv_tile, GGML_WEBGPU_KV_SEQ_PAD); while (GGML_WEBGPU_KV_SEQ_PAD % decisions.kv_tile != 0) { diff --git a/ggml/src/ggml-webgpu/ggml-webgpu.cpp b/ggml/src/ggml-webgpu/ggml-webgpu.cpp index 401c75c1230..78cb02be06d 100644 --- a/ggml/src/ggml-webgpu/ggml-webgpu.cpp +++ b/ggml/src/ggml-webgpu/ggml-webgpu.cpp @@ -1832,7 +1832,7 @@ static webgpu_encoded_op ggml_webgpu_flash_attn(webgpu_context & ctx, uint32_t blk_nblk1 = 0; uint32_t blk_batch_count = 0; - const uint32_t vec_nwg_cap = std::max(1u, std::min(32u, ctx->global_ctx->capabilities.max_subgroup_size)); + const uint32_t vec_nwg_cap = ctx->global_ctx->capabilities.min_subgroup_size; uint32_t nwg = 1u; const uint64_t kv_span = (uint64_t) std::max(1u, decisions->kv_tile); while ((2u * nwg * kv_span) < (uint64_t) K->ne[1] && nwg < vec_nwg_cap) { @@ -1953,8 +1953,11 @@ static webgpu_encoded_op ggml_webgpu_flash_attn(webgpu_context & ctx, std::vector reduce_params; std::vector reduce_entries; if (use_vec_reduce) { - const uint32_t reduce_wg_size = std::max( - 32u, std::min(nwg * 32u, ctx->global_ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup)); + const uint32_t reduce_sg_size = ctx->global_ctx->capabilities.max_subgroup_size; + const uint32_t reduce_wg_size = + std::max(reduce_sg_size, (uint32_t) std::min( + (uint64_t) nwg * reduce_sg_size, + ctx->global_ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup)); ggml_webgpu_shader_lib_context reduce_shader_ctx = shader_lib_ctx; reduce_shader_ctx.max_wg_size = reduce_wg_size; reduce_pipeline = ctx->shader_lib->get_flash_attn_vec_reduce_pipeline(reduce_shader_ctx); @@ -3542,8 +3545,7 @@ static size_t ggml_backend_webgpu_buffer_type_get_alloc_size(ggml_backend_buffer if (decisions.path == GGML_WEBGPU_FLASH_ATTN_PATH_VEC) { const uint32_t kv_tile = decisions.kv_tile; - const uint32_t vec_nwg_cap = std::max( - 1u, std::min(32u, ctx->webgpu_global_ctx->capabilities.max_subgroup_size)); + const uint32_t vec_nwg_cap = ctx->webgpu_global_ctx->capabilities.min_subgroup_size; uint32_t nwg = 1u; const uint64_t kv_span = (uint64_t) std::max(1u, kv_tile); while ((2u * nwg * kv_span) < (uint64_t) K->ne[1] && nwg < vec_nwg_cap) { From 834a243664114487f99520370a7a7b00fc7a486f Mon Sep 17 00:00:00 2001 From: Reese Levine Date: Thu, 14 May 2026 09:41:32 -0700 Subject: [PATCH 108/158] ggml-webgpu: Enable NVIDIA self-hosted CI (#22976) * Enabel nvidia ci for webgpu * Address precision issues * fix placement * Relax more set_rows and div * Try relaxing all f16 * formatting and naming * Add comment explaining max_nmse_err logic Added comment referencing pull request for clarification. --- .github/workflows/build-self-hosted.yml | 51 ++++++++++++------------- tests/test-backend-ops.cpp | 31 ++++++++++++++- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-self-hosted.yml b/.github/workflows/build-self-hosted.yml index 9121e7a9eae..ee8fd41d7c7 100644 --- a/.github/workflows/build-self-hosted.yml +++ b/.github/workflows/build-self-hosted.yml @@ -120,35 +120,34 @@ jobs: vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp - # TODO: investigate slight precision issues in some operations for test-backend-ops on the WebGPU backend. - #ggml-ci-nvidia-webgpu: - # runs-on: [self-hosted, Linux, NVIDIA] + ggml-ci-nvidia-webgpu: + runs-on: [self-hosted, Linux, NVIDIA] - # steps: - # - name: Clone - # id: checkout - # uses: actions/checkout@v6 + steps: + - name: Clone + id: checkout + uses: actions/checkout@v6 - # - name: Dawn Dependency - # id: dawn-depends - # run: | - # DAWN_VERSION="v20260317.182325" - # DAWN_OWNER="google" - # DAWN_REPO="dawn" - # DAWN_ASSET_NAME="Dawn-18eb229ef5f707c1464cc581252e7603c73a3ef0-ubuntu-latest-Release" - # echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz" - # curl -L -o artifact.tar.gz \ - # "https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz" - # mkdir dawn - # tar -xvf artifact.tar.gz -C dawn --strip-components=1 + - name: Dawn Dependency + id: dawn-depends + run: | + DAWN_VERSION="v20260317.182325" + DAWN_OWNER="google" + DAWN_REPO="dawn" + DAWN_ASSET_NAME="Dawn-18eb229ef5f707c1464cc581252e7603c73a3ef0-ubuntu-latest-Release" + echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz" + curl -L -o artifact.tar.gz \ + "https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz" + mkdir dawn + tar -xvf artifact.tar.gz -C dawn --strip-components=1 - # - name: Test - # id: ggml-ci - # run: | - # GG_BUILD_WEBGPU=1 \ - # GG_BUILD_WEBGPU_DAWN_PREFIX="$GITHUB_WORKSPACE/dawn" \ - # GG_BUILD_WEBGPU_DAWN_DIR="$GITHUB_WORKSPACE/dawn/lib64/cmake/Dawn" \ - # bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp + - name: Test + id: ggml-ci + run: | + GG_BUILD_WEBGPU=1 \ + GG_BUILD_WEBGPU_DAWN_PREFIX="$GITHUB_WORKSPACE/dawn" \ + GG_BUILD_WEBGPU_DAWN_DIR="$GITHUB_WORKSPACE/dawn/lib64/cmake/Dawn" \ + bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp # TODO: provision AMX-compatible machine #ggml-ci-cpu-amx: diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp index 33311948669..c60f6d2c2b0 100644 --- a/tests/test-backend-ops.cpp +++ b/tests/test-backend-ops.cpp @@ -1128,7 +1128,11 @@ struct test_case { } virtual double max_nmse_err(ggml_backend_t backend) { - GGML_UNUSED(backend); + ggml_backend_reg_t reg = ggml_backend_dev_backend_reg(ggml_backend_get_device(backend)); + // See https://github.com/ggml-org/llama.cpp/pull/22976 for explanation. + if (contains_f16 && strcmp(ggml_backend_reg_name(reg), "WebGPU") == 0) { + return std::max(max_nmse_err(), 1e-6); + } return max_nmse_err(); } @@ -1205,6 +1209,18 @@ struct test_case { std::vector sentinels; std::string current_op_name; + bool contains_f16 = false; + + // Used by the WebGPU backend to relax error thresholds on ops on f16 tensors + void check_for_f16_tensor(ggml_context * ctx) { + contains_f16 = false; + for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != nullptr; t = ggml_get_next_tensor(ctx, t)) { + if (t->type == GGML_TYPE_F16) { + contains_f16 = true; + break; + } + } + } void add_sentinel(ggml_context * ctx) { if (mode == MODE_PERF || mode == MODE_GRAD || mode == MODE_SUPPORT) { @@ -1298,6 +1314,7 @@ struct test_case { ggml_tensor * out = build_graph(ctx); current_op_name = op_desc(out); + check_for_f16_tensor(ctx); if (!matches_filter(out, op_names_filter)) { //printf(" %s: skipping\n", op_desc(out).c_str()); @@ -1973,9 +1990,19 @@ struct test_unary : public test_case { } void initialize_tensors(ggml_context * ctx) override { + float min = -150.f; + float max = 150.f; + + // Keep FP16 exp/expm1 inputs in-range so all backends stay finite instead of + // disagreeing on whether overflow saturates to max-F16 or produces +inf. + if (type == GGML_TYPE_F16 && (op == GGML_UNARY_OP_EXP || op == GGML_UNARY_OP_EXPM1)) { + min = -10.f; + max = 10.f; + } + for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) { // test extended range of values to check for NaNs in GELU - init_tensor_uniform(t, -150.f, 150.f); + init_tensor_uniform(t, min, max); } } From d81e63dcfd104cbfd7409a2bc6c09706d4b313be Mon Sep 17 00:00:00 2001 From: Zack Li <39573601+zhiyuan8@users.noreply.github.com> Date: Thu, 14 May 2026 13:58:34 -0700 Subject: [PATCH 109/158] CI : support IOT device (IQ9) (#22987) * update test scripts * align CI behavior between linux and android * remove automatically cancel in 15min * enable cancel-in-progress * fix ty check issue * update and fix pylint issue * update runner such that we are not restricted by the 15min limit rule * fix flake8 lint issue * update runner according to review feedback * code update according to review feedback * switch from llama-cli to llama-completion binary with -no-cnv flag --- .../workflows/build-and-test-snapdragon.yml | 46 +- scripts/snapdragon/qdc/run_qdc_jobs.py | 455 ++++++++++++++---- .../snapdragon/qdc/tests/linux/run_linux.sh | 232 +++++++++ .../qdc/tests/run_backend_ops_posix.py | 28 +- .../qdc/tests/run_bench_tests_posix.py | 89 ++-- scripts/snapdragon/qdc/tests/utils.py | 112 +++-- ty.toml | 2 +- 7 files changed, 795 insertions(+), 169 deletions(-) create mode 100644 scripts/snapdragon/qdc/tests/linux/run_linux.sh diff --git a/.github/workflows/build-and-test-snapdragon.yml b/.github/workflows/build-and-test-snapdragon.yml index deed8e808b7..ef3fe502fa7 100644 --- a/.github/workflows/build-and-test-snapdragon.yml +++ b/.github/workflows/build-and-test-snapdragon.yml @@ -58,14 +58,45 @@ jobs: name: llama-cpp-android-arm64-snapdragon path: pkg-snapdragon/llama.cpp + linux-iot-snapdragon: + runs-on: ubuntu-latest + container: + image: 'ghcr.io/snapdragon-toolchain/arm64-linux:v0.1' + defaults: + run: + shell: bash + + steps: + - name: Clone + uses: actions/checkout@v6 + with: + fetch-depth: 0 + lfs: false + + - name: Build Llama.CPP for Snapdragon Linux IoT + id: build_llama_cpp_snapdragon_linux + run: | + cp docs/backend/snapdragon/CMakeUserPresets.json . + cmake --preset arm64-linux-snapdragon-release -B build-snapdragon -DGGML_OPENCL=ON + cmake --build build-snapdragon -j $(nproc) + cmake --install build-snapdragon --prefix pkg-snapdragon/llama.cpp + + - name: Upload Llama.CPP Snapdragon Linux IoT Build Artifact + if: ${{ always() && steps.build_llama_cpp_snapdragon_linux.outcome == 'success' }} + uses: actions/upload-artifact@v6 + with: + name: llama-cpp-linux-arm64-snapdragon + path: pkg-snapdragon/llama.cpp + test-snapdragon-qdc: - name: Test on QDC Android Device (${{ matrix.device }}) - needs: [android-ndk-snapdragon] - runs-on: ubuntu-slim + name: Test on QDC Device (${{ matrix.device }}) + needs: [android-ndk-snapdragon, linux-iot-snapdragon] + runs-on: ubuntu-24.04-arm + timeout-minutes: 90 strategy: fail-fast: false matrix: - device: [SM8750, SM8650, SM8850] + device: [SM8750, SM8850, QCS9075M] steps: - name: Checkout @@ -74,11 +105,11 @@ jobs: - name: Download build artifact uses: actions/download-artifact@v7 with: - name: llama-cpp-android-arm64-snapdragon + name: ${{ startsWith(matrix.device, 'QCS') && 'llama-cpp-linux-arm64-snapdragon' || 'llama-cpp-android-arm64-snapdragon' }} path: pkg-snapdragon/llama.cpp - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' cache: pip @@ -107,7 +138,8 @@ jobs: --test all \ --pkg-dir pkg-snapdragon/llama.cpp \ --model-url "https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf" \ - --device ${{ matrix.device }} + --device ${{ matrix.device }} \ + ${{ startsWith(matrix.device, 'QCS') && '--retries 2 --retry-delay 300' || '' }} env: QDC_API_KEY: ${{ secrets.QDC_API_KEY }} diff --git a/scripts/snapdragon/qdc/run_qdc_jobs.py b/scripts/snapdragon/qdc/run_qdc_jobs.py index b4eede3d019..f1b0453eec4 100644 --- a/scripts/snapdragon/qdc/run_qdc_jobs.py +++ b/scripts/snapdragon/qdc/run_qdc_jobs.py @@ -1,4 +1,4 @@ -"""Run llama.cpp Hexagon Android tests in a single QDC Appium job. +"""Run llama.cpp Hexagon tests in a single QDC job. Bundles test scripts into one artifact and submits a single QDC job: @@ -10,6 +10,10 @@ Prerequisites: pip install /path/to/qualcomm_device_cloud_sdk*.whl +Platform is inferred from --device: + android Appium + pytest (Android phones: SM8750 / SM8650 / SM8850) + linux BASH (Linux IoT: QCS9075M) + Required environment variables: QDC_API_KEY API key from QDC UI -> Users -> Settings -> API Keys @@ -23,6 +27,7 @@ from __future__ import annotations import argparse +import enum import logging import os import re @@ -30,15 +35,35 @@ import sys import tempfile import time +import urllib.request import xml.etree.ElementTree as ET from dataclasses import dataclass, field from pathlib import Path +from typing import Callable + +from qualcomm_device_cloud_sdk.api import qdc_api +from qualcomm_device_cloud_sdk.logging import configure_logging +from qualcomm_device_cloud_sdk.models import ( + ArtifactType, + JobMode, + JobState, + JobSubmissionParameter, + JobType, + TestFramework, +) -from qualcomm_device_cloud_sdk.api import qdc_api # ty: ignore[unresolved-import] -from qualcomm_device_cloud_sdk.logging import configure_logging # ty: ignore[unresolved-import] -from qualcomm_device_cloud_sdk.models import ArtifactType, JobMode, JobState, JobSubmissionParameter, JobType, TestFramework # ty: ignore[unresolved-import] - +# configure_logging only sets up the SDK logger; basicConfig is needed for +# our own log.info to reach stdout. +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(name)s %(levelname)s - %(message)s", + handlers=[logging.StreamHandler()], +) configure_logging(level=logging.INFO, handlers=[logging.StreamHandler()]) +# Silence per-poll GET/status spam from the SDK and its HTTP client. +logging.getLogger("qualcomm_device_cloud").setLevel(logging.WARNING) +logging.getLogger("httpx").setLevel(logging.WARNING) +logging.getLogger("httpcore").setLevel(logging.WARNING) log = logging.getLogger(__name__) POLL_INTERVAL = 30 @@ -47,23 +72,56 @@ CAPACITY_TIMEOUT = 1800 CAPACITY_POLL = 60 MAX_CONCURRENT_JOBS = 5 +DEFAULT_RETRIES = 0 +RETRY_DELAY = 300 TERMINAL_STATES = {JobState.COMPLETED, JobState.CANCELED} NON_TERMINAL_STATES = {JobState.DISPATCHED, JobState.RUNNING, JobState.SETUP, JobState.SUBMITTED} -_SCRIPTS_DIR = Path(__file__).parent -_TESTS_DIR = _SCRIPTS_DIR / "tests" -_RUN_BENCH = _TESTS_DIR / "run_bench_tests_posix.py" -_RUN_BACKEND_OPS = _TESTS_DIR / "run_backend_ops_posix.py" -_UTILS = _TESTS_DIR / "utils.py" -_CONFTEST = _TESTS_DIR / "conftest.py" -_REQUIREMENTS = _SCRIPTS_DIR / "requirements.txt" +class DeviceUnavailableError(Exception): + """Raised when the QDC device resource is not available (retryable).""" + + +_SCRIPTS_DIR = Path(__file__).parent +_TESTS_DIR = _SCRIPTS_DIR / "tests" + +# --- Shared test assets ------------------------------------------------------- +_UTILS = _TESTS_DIR / "utils.py" +_CONFTEST = _TESTS_DIR / "conftest.py" _PYTEST_LINE_RE = re.compile( r"(?:[\w/]+\.py::)?(?:\w+::)?([\w\[\].-]+)\s+(PASSED|FAILED|ERROR|SKIPPED)" ) -_EXCLUDED_LOGS = {"qdc_android_whole_host-000.log", "qdc_kernel_host-000.log"} +_EXCLUDED_LOGS = { + "qdc_android_whole_host-000.log", + "qdc_kernel_host-000.log", + "qdc_LE_whole_host-000.log", + "qdc_LE_kernel_host-000.log", + "script.log", +} _NON_TERMINAL_STATE_VALUES = {s.value for s in NON_TERMINAL_STATES} +# --- Android (Appium + pytest) assets ---------------------------------------- +_RUN_BENCH = _TESTS_DIR / "run_bench_tests_posix.py" +_RUN_BACKEND_OPS = _TESTS_DIR / "run_backend_ops_posix.py" +_REQUIREMENTS = _SCRIPTS_DIR / "requirements.txt" +_UPSTREAM_ADB_SCRIPTS = ( + "https://raw.githubusercontent.com/ggml-org/llama.cpp/master/scripts/snapdragon/adb" +) +_ADB_SCRIPT_NAMES = [ + "run-bench.sh", + "run-cli.sh", + "run-completion.sh", + "run-tool.sh", +] + +# --- Linux (BASH) assets ------------------------------------------------------ +_RUN_LINUX_TEMPLATE = _TESTS_DIR / "linux" / "run_linux.sh" +_LINUX_ENTRY_SCRIPT = "/bin/bash /data/local/tmp/TestContent/run_linux.sh" + +# ============================================================================= +# Artifact builders (per platform) +# ============================================================================= + @dataclass class JobResult: @@ -73,35 +131,58 @@ class JobResult: failure_details: dict[str, str] = field(default_factory=dict) -def build_artifact_zip( +def _write_lf(path: Path, content: str) -> None: + """Write text with LF line endings (required by /bin/bash on Linux).""" + with open(path, "w", encoding="utf-8", newline="\n") as f: + f.write(content) + + +def _build_android_artifact( pkg_dir: Path, stage_dir: Path, - *, - test_mode: str = "bench", - model_url: str | None = None, + test_mode: str, + model_url: str | None, ) -> Path: - """Bundle everything into a single QDC artifact zip. + """Android zip (Appium/pytest). Extracted by QDC under /qdc/appium/. - Zip structure (extracted by QDC to /qdc/appium/ on the runner): + Zip structure: llama_cpp_bundle/ installed package (adb pushed to /data/local/tmp/) + run-{bench,cli,completion,tool}.sh upstream adb wrappers (patched) tests/ - utils.py shared helpers (paths, run_adb_command, …) - conftest.py shared pytest fixtures (driver) - test_bench_posix.py bench + cli tests (<> substituted) - AND/OR - test_backend_ops_posix.py test-backend-ops -b HTP0 + utils.py shared adb helpers + conftest.py Appium pytest fixtures + test_bench_posix.py bench + cli tests (for --test bench or all) + test_backend_ops_posix.py test-backend-ops on HTP0 requirements.txt + pytest.ini addopts = --junitxml=results.xml """ - shutil.copytree(pkg_dir, stage_dir / "llama_cpp_bundle") + bundle_dir = stage_dir / "llama_cpp_bundle" + shutil.copytree(pkg_dir, bundle_dir) + + # Download upstream adb scripts so they land at /qdc/appium/ on the QDC + # runner. They wrap `adb shell` internally. Patch in `chmod +x bin/* lib/*` + # right after `cd $basedir` so device binaries are executable. + for name in _ADB_SCRIPT_NAMES: + url = f"{_UPSTREAM_ADB_SCRIPTS}/{name}" + dest = stage_dir / name + log.info("Downloading %s", url) + urllib.request.urlretrieve(url, str(dest)) + content = dest.read_text() + content = content.replace( + "cd $basedir;", + "cd $basedir; chmod +x bin/* lib/* 2>/dev/null;", + ) + dest.write_text(content) + dest.chmod(0o755) tests_dir = stage_dir / "tests" tests_dir.mkdir() - shutil.copy(_UTILS, tests_dir / "utils.py") + shutil.copy(_UTILS, tests_dir / "utils.py") shutil.copy(_CONFTEST, tests_dir / "conftest.py") if test_mode in ("bench", "all"): - assert model_url is not None, "--model-url is required for bench/all test modes" + assert model_url is not None (tests_dir / "test_bench_posix.py").write_text( _RUN_BENCH.read_text().replace("<>", model_url) ) @@ -109,33 +190,140 @@ def build_artifact_zip( shutil.copy(_RUN_BACKEND_OPS, tests_dir / "test_backend_ops_posix.py") shutil.copy(_REQUIREMENTS, stage_dir / "requirements.txt") - (stage_dir / "pytest.ini").write_text("[pytest]\naddopts = --junitxml=results.xml\n") + (stage_dir / "pytest.ini").write_text( + "[pytest]\naddopts = --junitxml=results.xml\n" + ) zip_base = str(stage_dir / "artifact") shutil.make_archive(zip_base, "zip", stage_dir) return Path(f"{zip_base}.zip") +def _build_linux_artifact( + pkg_dir: Path, + stage_dir: Path, + test_mode: str, + model_url: str | None, +) -> Path: + """Linux IoT zip (BASH framework). Extracted by QDC to /data/local/tmp/TestContent/. + + Zip structure: + run_linux.sh entry script (placeholder-substituted, LF line endings) + llama_cpp_bundle/ installed package + """ + bundle_dir = stage_dir / "llama_cpp_bundle" + shutil.copytree(pkg_dir, bundle_dir) + + template = _RUN_LINUX_TEMPLATE.read_text(encoding="utf-8") + rendered = template.replace("{MODEL_URL}", model_url or "").replace( + "{TEST_MODE}", test_mode + ) + script_path = stage_dir / "run_linux.sh" + _write_lf(script_path, rendered) + script_path.chmod(0o755) + + zip_base = str(stage_dir / "artifact") + shutil.make_archive(zip_base, "zip", stage_dir) + return Path(f"{zip_base}.zip") + + +# ============================================================================= +# Platform enum + strategy table +# ============================================================================= + + +class Platform(enum.Enum): + ANDROID = "android" + LINUX = "linux" + + +@dataclass(frozen=True) +class PlatformSpec: + test_framework: TestFramework + entry_script: str | None + build_artifact: Callable[[Path, Path, str, str | None], Path] + job_name_fmt: str + + +PLATFORM_SPECS: dict[Platform, PlatformSpec] = { + Platform.ANDROID: PlatformSpec( + test_framework=TestFramework.APPIUM, + entry_script=None, + build_artifact=_build_android_artifact, + job_name_fmt="{base}", + ), + Platform.LINUX: PlatformSpec( + test_framework=TestFramework.BASH, + entry_script=_LINUX_ENTRY_SCRIPT, + build_artifact=_build_linux_artifact, + job_name_fmt="{base} (Linux)", + ), +} + +DEVICE_PLATFORM: dict[str, Platform] = { + "SM8750": Platform.ANDROID, + "SM8650": Platform.ANDROID, + "SM8850": Platform.ANDROID, + "QCS9075M": Platform.LINUX, +} + + +# ============================================================================= +# Shared QDC job plumbing +# ============================================================================= + + def wait_for_job(client, job_id: str, timeout: int) -> str: elapsed = 0 + last_state = None + consecutive_errors = 0 + max_consecutive_errors = 5 while elapsed < timeout: - raw = qdc_api.get_job_status(client, job_id) + try: + raw = qdc_api.get_job_status(client, job_id) + consecutive_errors = 0 + except Exception as e: + consecutive_errors += 1 + log.warning( + "Transient error polling job %s (%d/%d): %s", + job_id, + consecutive_errors, + max_consecutive_errors, + e, + ) + if consecutive_errors >= max_consecutive_errors: + raise + time.sleep(POLL_INTERVAL) + elapsed += POLL_INTERVAL + continue try: status = JobState(raw) except ValueError: status = raw if status in TERMINAL_STATES: return raw.lower() - log.info("Job %s: %s", job_id, raw) + if raw != last_state: + log.info("Job %s: %s", job_id, raw) + last_state = raw time.sleep(POLL_INTERVAL) elapsed += POLL_INTERVAL + # Abort to free the QDC concurrency slot instead of leaking it. + try: + qdc_api.abort_job(client, job_id) + log.warning("Aborted job %s after timeout to free concurrency slot", job_id) + except Exception as e: + log.warning("Failed to abort job %s: %s", job_id, e) raise TimeoutError(f"Job {job_id} did not finish within {timeout}s") def wait_for_log_upload(client, job_id: str) -> None: elapsed = 0 while elapsed <= LOG_UPLOAD_TIMEOUT: - status = (qdc_api.get_job_log_upload_status(client, job_id) or "").lower() + try: + status = (qdc_api.get_job_log_upload_status(client, job_id) or "").lower() + except Exception as e: + log.warning("get_job_log_upload_status failed: %s — will retry", e) + status = "" if status in {"completed", "failed"}: return log.info("Waiting for log upload (status=%s) ...", status) @@ -150,17 +338,33 @@ def wait_for_capacity(client, max_jobs: int = MAX_CONCURRENT_JOBS) -> None: while elapsed < CAPACITY_TIMEOUT: jobs_page = qdc_api.get_jobs_list(client, page_number=0, page_size=50) if jobs_page is None: - log.warning("Could not retrieve job list; proceeding without capacity check") + log.warning( + "Could not retrieve job list; proceeding without capacity check" + ) return items = getattr(jobs_page, "data", []) or [] - active = sum(1 for j in items if getattr(j, "state", None) in _NON_TERMINAL_STATE_VALUES) + active = sum( + 1 for j in items if getattr(j, "state", None) in _NON_TERMINAL_STATE_VALUES + ) if active < max_jobs: log.info("Active QDC jobs: %d / %d — proceeding", active, max_jobs) return - log.info("Active QDC jobs: %d / %d — waiting %ds ...", active, max_jobs, CAPACITY_POLL) + log.info( + "Active QDC jobs: %d / %d — waiting %ds ...", + active, + max_jobs, + CAPACITY_POLL, + ) time.sleep(CAPACITY_POLL) elapsed += CAPACITY_POLL - log.warning("Capacity wait timed out after %ds; proceeding anyway", CAPACITY_TIMEOUT) + raise TimeoutError( + f"Capacity wait timed out after {CAPACITY_TIMEOUT}s" + ) + + +# --------------------------------------------------------------------------- +# Log parsing helpers +# --------------------------------------------------------------------------- def _parse_junit_xml(content: str) -> tuple[dict[str, bool], dict[str, str]]: @@ -192,10 +396,26 @@ def _parse_pytest_output(content: str) -> dict[str, bool]: def fetch_logs_and_parse_tests( - client, job_id: str + client, job_id: str, max_retries: int = 5, retry_delay: int = 30 ) -> tuple[dict[str, bool], dict[str, str], dict[str, str]]: """Returns (test_results, raw_logs, failure_details).""" - log_files = qdc_api.get_job_log_files(client, job_id) + log_files = None + for attempt in range(1, max_retries + 1): + try: + log_files = qdc_api.get_job_log_files(client, job_id) + break + except Exception as e: + if attempt < max_retries: + log.warning( + "get_job_log_files failed (attempt %d/%d): %s — retrying in %ds", + attempt, max_retries, e, retry_delay, + ) + time.sleep(retry_delay) + else: + log.error( + "get_job_log_files failed after %d attempts: %s", max_retries, e + ) + return {}, {}, {} if not log_files: log.warning("No log files returned for job %s", job_id) return {}, {}, {} @@ -207,8 +427,8 @@ def fetch_logs_and_parse_tests( with tempfile.TemporaryDirectory() as tmpdir: for lf in log_files: - log.info("Downloading log file: %s", lf.filename) zip_path = os.path.join(tmpdir, "log.zip") + log.info("Downloading log file: %s", lf.filename) qdc_api.download_job_log_files(client, lf.filename, zip_path) try: shutil.unpack_archive(zip_path, tmpdir, "zip") @@ -226,12 +446,15 @@ def fetch_logs_and_parse_tests( elif fname.endswith(".log"): if fname in _EXCLUDED_LOGS: continue - log.info("--- %s ---", fname) - log.info("%s", content) + log.info("--- %s ---\n%s", fname, content) raw_logs[fname] = content pytest_fallback.update(_parse_pytest_output(content)) - return (test_results if test_results else pytest_fallback), raw_logs, failure_details + return ( + (test_results if test_results else pytest_fallback), + raw_logs, + failure_details, + ) def write_summary(result: JobResult, title: str = "QDC Test Results") -> None: @@ -289,30 +512,106 @@ def write_summary(result: JobResult, title: str = "QDC Test Results") -> None: f.write("\n".join(lines) + "\n") +# ============================================================================= +# CLI + main +# ============================================================================= + def parse_args() -> argparse.Namespace: p = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) - p.add_argument("--pkg-dir", required=True, type=Path, + p.add_argument("--pkg-dir", required=True, type=Path, help="Installed llama.cpp package directory (contains bin/ and lib/)") p.add_argument("--model-url", help="Direct URL to the GGUF model file (required for --test bench)") - p.add_argument("--device", required=True, + p.add_argument("--device", required=True, help="QDC chipset name, e.g. SM8750") p.add_argument("--test", choices=["bench", "backend-ops", "all"], default="bench", help="Test suite to run (default: bench)") p.add_argument("--job-timeout", type=int, default=JOB_TIMEOUT, metavar="SECONDS", help=f"Max seconds to wait for job completion (default: {JOB_TIMEOUT})") + p.add_argument("--retries", type=int, default=DEFAULT_RETRIES, metavar="N", + help="Number of retries when device is unavailable (default: 0)") + p.add_argument("--retry-delay", type=int, default=RETRY_DELAY, metavar="SECONDS", + help=f"Seconds to wait between retries (default: {RETRY_DELAY})") args = p.parse_args() if args.test in ("bench", "all") and not args.model_url: p.error("--model-url is required when --test bench or --test all") return args +def _submit_and_run_job(client, args, spec, target_id, artifact_id) -> JobResult: + """Submit a QDC job and wait for results. + + Raises DeviceUnavailableError for transient device/resource issues that + are worth retrying. Returns JobResult for definitive outcomes (pass or + test failure). + """ + try: + wait_for_capacity(client) + except TimeoutError: + raise DeviceUnavailableError("Capacity wait timed out — device busy") + + job_name = spec.job_name_fmt.format(base="llama.cpp Hexagon tests") + + job_id = qdc_api.submit_job( + public_api_client=client, + target_id=target_id, + job_name=job_name, + external_job_id=None, + job_type=JobType.AUTOMATED, + job_mode=JobMode.APPLICATION, + timeout=max(1, args.job_timeout // 60), + test_framework=spec.test_framework, + entry_script=spec.entry_script, + job_artifacts=[artifact_id], + monkey_events=None, + monkey_session_timeout=None, + job_parameters=[JobSubmissionParameter.WIFIENABLED], + ) + if job_id is None: + raise DeviceUnavailableError("Job submission failed — device may be unavailable") + log.info("Job submitted: %s (device=%s)", job_id, args.device) + + try: + job_status = wait_for_job(client, job_id, timeout=args.job_timeout) + except TimeoutError as e: + raise DeviceUnavailableError(str(e)) + log.info("Job %s finished: %s", job_id, job_status) + + wait_for_log_upload(client, job_id) + tests, raw_logs, failure_details = fetch_logs_and_parse_tests(client, job_id) + + job_ok = job_status == JobState.COMPLETED.value.lower() + + if not job_ok and not tests: + raise DeviceUnavailableError( + f"Job did not complete (status={job_status}) and produced no test results" + ) + + passed = job_ok and all(tests.values()) if tests else job_ok + if spec.test_framework == TestFramework.BASH and not tests: + log.error("No test results recovered (state=%s). Script likely never ran.", job_status) + passed = False + if not passed: + log.error("Job did not complete successfully or tests failed (status=%s)", job_status) + + return JobResult(passed=passed, tests=tests, raw_logs=raw_logs, failure_details=failure_details) + + def main() -> int: args = parse_args() + platform = DEVICE_PLATFORM.get(args.device) + if platform is None: + log.error( + "Unknown device %r. Known: %s", + args.device, ", ".join(sorted(DEVICE_PLATFORM.keys())), + ) + return 1 + spec = PLATFORM_SPECS[platform] + api_key = os.environ.get("QDC_API_KEY") if not api_key: log.error("QDC_API_KEY environment variable must be set") @@ -334,10 +633,9 @@ def main() -> int: return 1 with tempfile.TemporaryDirectory() as tmpdir: - log.info("Building artifact ...") - zip_path = build_artifact_zip( - args.pkg_dir, Path(tmpdir), - test_mode=args.test, model_url=args.model_url, + log.info("Building %s artifact (test=%s) ...", platform.value, args.test) + zip_path = spec.build_artifact( + args.pkg_dir, Path(tmpdir), args.test, args.model_url ) log.info("Uploading artifact (%d MB) ...", zip_path.stat().st_size // 1_000_000) artifact_id = qdc_api.upload_file(client, str(zip_path), ArtifactType.TESTSCRIPT) @@ -346,46 +644,31 @@ def main() -> int: log.error("Artifact upload failed") return 1 - wait_for_capacity(client) - - job_id = qdc_api.submit_job( - public_api_client=client, - target_id=target_id, - job_name="llama.cpp Hexagon tests", - external_job_id=None, - job_type=JobType.AUTOMATED, - job_mode=JobMode.APPLICATION, - timeout=max(1, args.job_timeout // 60), - test_framework=TestFramework.APPIUM, - entry_script=None, - job_artifacts=[artifact_id], - monkey_events=None, - monkey_session_timeout=None, - job_parameters=[JobSubmissionParameter.WIFIENABLED], - ) - if job_id is None: - log.error("Job submission failed") - return 1 - log.info("Job submitted: %s (device=%s)", job_id, args.device) - - try: - job_status = wait_for_job(client, job_id, timeout=args.job_timeout) - except TimeoutError as e: - log.error("%s", e) - write_summary(JobResult(passed=False, tests={}), title=f"QDC Job Timed Out ({args.device})") + max_attempts = 1 + args.retries + for attempt in range(1, max_attempts + 1): + try: + result = _submit_and_run_job(client, args, spec, target_id, artifact_id) + break + except DeviceUnavailableError as e: + if attempt < max_attempts: + log.warning( + "Attempt %d/%d failed (device unavailable): %s — retrying in %ds", + attempt, max_attempts, e, args.retry_delay, + ) + time.sleep(args.retry_delay) + else: + log.error( + "Attempt %d/%d failed (device unavailable): %s — no retries left", + attempt, max_attempts, e, + ) + write_summary( + JobResult(passed=False, tests={}), + title=f"QDC Device Unavailable ({args.device})", + ) + return 1 + else: return 1 - log.info("Job %s finished: %s", job_id, job_status) - - wait_for_log_upload(client, job_id) - tests, raw_logs, failure_details = fetch_logs_and_parse_tests(client, job_id) - - passed = job_status == JobState.COMPLETED.value.lower() - if tests: - passed = passed and all(tests.values()) - if not passed: - log.error("Job did not complete successfully or tests failed (status=%s)", job_status) - result = JobResult(passed=passed, tests=tests, raw_logs=raw_logs, failure_details=failure_details) if args.test == "backend-ops": title = f"Backend Ops — HTP0 ({args.device})" elif args.test == "all": @@ -394,7 +677,7 @@ def main() -> int: title = f"QDC Test Results ({args.device})" write_summary(result, title=title) - return 0 if passed else 1 + return 0 if result.passed else 1 if __name__ == "__main__": diff --git a/scripts/snapdragon/qdc/tests/linux/run_linux.sh b/scripts/snapdragon/qdc/tests/linux/run_linux.sh new file mode 100644 index 00000000000..a6abf8ec301 --- /dev/null +++ b/scripts/snapdragon/qdc/tests/linux/run_linux.sh @@ -0,0 +1,232 @@ +#!/bin/bash +# llama.cpp Hexagon test entry script for QDC Linux IoT (BASH framework). +# +# Placeholders substituted by run_qdc_jobs.py (--platform linux) before upload: +# {MODEL_URL} direct URL to a .gguf model file +# {TEST_MODE} bench | backend-ops | all +# +# QDC extracts the artifact zip to /data/local/tmp/TestContent/ and invokes +# this script via: /bin/bash /data/local/tmp/TestContent/run_linux.sh +# Any files written under /data/local/tmp/QDC_logs/ are auto-uploaded. + +set +e +umask 022 + +LOG_DIR=/data/local/tmp/QDC_logs +BUNDLE_DIR=/data/local/tmp/TestContent/llama_cpp_bundle +MODEL_DIR=/data/local/tmp/gguf +MODEL_PATH="$MODEL_DIR/model.gguf" +RESULTS_XML="$LOG_DIR/results.xml" + +mkdir -p "$LOG_DIR" "$MODEL_DIR" +# Redirect all parent-shell output to script.log so QDC auto-uploads it; +# per-case runs still capture their own stdout/stderr into dedicated logs. +exec > "$LOG_DIR/script.log" 2>&1 + +echo "=== env ===" +date -u +uname -a +pwd + +mount -o rw,remount / 2>/dev/null || true + +cd "$BUNDLE_DIR" || { echo "FATAL: bundle missing at $BUNDLE_DIR"; exit 1; } +chmod +x bin/* 2>/dev/null +export LD_LIBRARY_PATH="$BUNDLE_DIR/lib:$LD_LIBRARY_PATH" +export ADSP_LIBRARY_PATH="$BUNDLE_DIR/lib" +export GGML_HEXAGON_EXPERIMENTAL=1 + +echo "=== download model ===" +MODEL_URL="{MODEL_URL}" +if [ -z "$MODEL_URL" ]; then + echo "No model URL provided, skipping download" +elif [ ! -f "$MODEL_PATH" ]; then + curl -L -fS --retry 3 --retry-delay 5 -o "$MODEL_PATH" "$MODEL_URL" + curl_rc=$? + if [ $curl_rc -ne 0 ]; then + echo "FATAL: model download failed (rc=$curl_rc)" + exit 1 + fi + ls -la "$MODEL_PATH" +fi + +# --------------------------------------------------------------------------- +# JUnit XML helpers +# --------------------------------------------------------------------------- + +xml_open() { + printf '%s\n' \ + '' \ + "" \ + "" \ + > "$RESULTS_XML" +} + +xml_close() { + printf '%s\n' '' '' >> "$RESULTS_XML" +} + +xml_case_pass() { + local classname=$1 name=$2 + printf '\n' "$classname" "$name" >> "$RESULTS_XML" +} + +xml_case_fail() { + local classname=$1 name=$2 rc=$3 logfile=$4 + { + printf '\n' "$classname" "$name" + printf '/dev/null | sed 's/]]>/]] >/g' + printf '\n]]>\n\n' + } >> "$RESULTS_XML" +} + +# Map backend name -> "NDEV --device" pair. "none" means no offload (CPU). +backend_env() { + case "$1" in + cpu) echo "0 none" ;; + gpu) echo "0 GPUOpenCL" ;; + npu) echo "1 HTP0" ;; + esac +} + +backend_log_name() { + case "$1" in + cpu) echo "cpu" ;; + gpu) echo "gpu" ;; + npu) echo "htp" ;; + esac +} + + +backend_device_name() { + case "$1" in + cpu) echo "none" ;; + gpu) echo "GPUOpenCL" ;; + npu) echo "HTP0" ;; + esac +} + +# Append a diagnostic block when a per-case `timeout N` fires (rc=124). The +# naked log file at that point usually just ends mid-OpenCL-init with no +# stderr, which is hard to read in CI summaries. +note_timeout_if_triggered() { + local rc=$1 budget=$2 log=$3 + [ "$rc" -eq 124 ] || return 0 + { + printf '\n' + printf '=== TIMEOUT after %ss ===\n' "$budget" + printf 'uptime: '; uptime 2>/dev/null + printf 'free -m:\n'; free -m 2>/dev/null + printf 'loadavg: '; cat /proc/loadavg 2>/dev/null + } >> "$log" +} + +completion_extra_args() { + case "$1" in + cpu) echo "--device none --ctx-size 128 -no-cnv -n 32 --seed 42 --batch-size 128" ;; + gpu) echo "--device GPUOpenCL --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 512" ;; + npu) echo "--device HTP0 --ctx-size 128 -no-cnv -n 32 --seed 42 --ubatch-size 1024" ;; + esac +} + +run_completion_case() { + local name=$1 + local parts=($(backend_env "$name")) + local ndev=${parts[0]} device=${parts[1]} + local device_log_name=$(backend_device_name "$name") + local log="$LOG_DIR/llama_completion_${device_log_name}.log" + local prompt="$LOG_DIR/bench_prompt.txt" + echo 'What is the capital of France?' > "$prompt" + local extra + extra=$(completion_extra_args "$name") + echo "=== [completion:$name] llama-completion --device $device (NDEV=$ndev) ===" + timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-completion \ + -m "$MODEL_PATH" \ + -f "$prompt" \ + $extra \ + > "$log" 2>&1 < /dev/null + local rc=$? + note_timeout_if_triggered "$rc" 600 "$log" + if [ $rc -eq 0 ]; then + xml_case_pass "tests.test_bench_posix" "test_llama_completion[$name]" + else + xml_case_fail "tests.test_bench_posix" "test_llama_completion[$name]" "$rc" "$log" + fi +} + +run_bench_case() { + local name=$1 + local parts=($(backend_env "$name")) + local ndev=${parts[0]} device=${parts[1]} + local log_suffix=$(backend_log_name "$name") + local log="$LOG_DIR/llama_bench_${log_suffix}.log" + echo "=== [bench:$name] llama-bench --device $device (NDEV=$ndev) ===" + timeout 600 env GGML_HEXAGON_NDEV=$ndev ./bin/llama-bench \ + -m "$MODEL_PATH" \ + --device "$device" \ + -ngl 99 \ + --batch-size 128 \ + -t 4 \ + -p 128 \ + -n 32 \ + > "$log" 2>&1 + local rc=$? + note_timeout_if_triggered "$rc" 600 "$log" + if [ $rc -eq 0 ]; then + xml_case_pass "tests.test_bench_posix" "test_llama_bench[$name]" + else + xml_case_fail "tests.test_bench_posix" "test_llama_bench[$name]" "$rc" "$log" + fi +} + +run_backend_ops_case() { + local dtype=$1 + local log="$LOG_DIR/backend_ops_${dtype}.log" + local pattern + case "$dtype" in + q4_0) + # Matches Android: exclude a known-broken shape on NPU. + pattern='^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$' + ;; + *) + pattern="type_a=${dtype}" + ;; + esac + echo "=== [backend-ops:$dtype] test-backend-ops -b HTP0 -o MUL_MAT ===" + timeout 600 env GGML_HEXAGON_NDEV=1 GGML_HEXAGON_HOSTBUF=0 ./bin/test-backend-ops \ + -b HTP0 -o MUL_MAT -p "$pattern" \ + > "$log" 2>&1 + local rc=$? + note_timeout_if_triggered "$rc" 600 "$log" + if [ $rc -eq 0 ]; then + xml_case_pass "tests.test_backend_ops_posix" "test_backend_ops_htp0[$dtype]" + else + xml_case_fail "tests.test_backend_ops_posix" "test_backend_ops_htp0[$dtype]" "$rc" "$log" + fi +} + +xml_open + +case "{TEST_MODE}" in + bench) + for b in cpu gpu npu; do run_completion_case "$b"; done + for b in cpu gpu npu; do run_bench_case "$b"; done + ;; + backend-ops) + for d in mxfp4 fp16 q4_0; do run_backend_ops_case "$d"; done + ;; + all) + for b in cpu gpu npu; do run_completion_case "$b"; done + for b in cpu gpu npu; do run_bench_case "$b"; done + for d in mxfp4 fp16 q4_0; do run_backend_ops_case "$d"; done + ;; + *) + echo "FATAL: unsupported TEST_MODE={TEST_MODE}" + ;; +esac + +xml_close +echo "=== done ===" +# Host parses results.xml to decide pass/fail. +exit 0 diff --git a/scripts/snapdragon/qdc/tests/run_backend_ops_posix.py b/scripts/snapdragon/qdc/tests/run_backend_ops_posix.py index 958fc074762..355bf6c6a5b 100644 --- a/scripts/snapdragon/qdc/tests/run_backend_ops_posix.py +++ b/scripts/snapdragon/qdc/tests/run_backend_ops_posix.py @@ -1,8 +1,9 @@ """ On-device test-backend-ops runner for llama.cpp (HTP0 backend). -Executed by QDC's Appium test framework on the QDC runner. +On Android: executed by QDC's Appium test framework on the QDC runner. The runner has ADB access to the allocated device. +On Linux: runs test-backend-ops directly via run_linux.sh (BASH framework). """ import os @@ -10,7 +11,12 @@ import pytest -from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_adb_command, write_qdc_log +from utils import ( + BIN_PATH, + push_bundle_if_needed, + run_script, + write_qdc_log, +) @pytest.fixture(scope="session", autouse=True) @@ -20,17 +26,21 @@ def install(driver): @pytest.mark.parametrize("type_a", ["mxfp4", "fp16", "q4_0"]) def test_backend_ops_htp0(type_a): - cmd = f"{CMD_PREFIX} GGML_HEXAGON_HOSTBUF=0 GGML_HEXAGON_EXPERIMENTAL=1 {BIN_PATH}/test-backend-ops -b HTP0 -o MUL_MAT" if type_a == "q4_0": - cmd += r' -p "^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$"' + pattern = r'^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$' else: - cmd += f" -p type_a={type_a}" - result = run_adb_command( - cmd, - check=False, + pattern = f"type_a={type_a}" + + quoted_pattern = f'"{pattern}"' if type_a == "q4_0" else pattern + result = run_script( + "run-tool.sh", + extra_env={"HB": "0"}, + extra_args=["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", quoted_pattern], ) write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "") - assert result.returncode == 0, f"test-backend-ops type_a={type_a} failed (exit {result.returncode})" + assert result.returncode == 0, ( + f"test-backend-ops type_a={type_a} failed (exit {result.returncode})" + ) if __name__ == "__main__": diff --git a/scripts/snapdragon/qdc/tests/run_bench_tests_posix.py b/scripts/snapdragon/qdc/tests/run_bench_tests_posix.py index 44802c3136a..f42227c9f6e 100644 --- a/scripts/snapdragon/qdc/tests/run_bench_tests_posix.py +++ b/scripts/snapdragon/qdc/tests/run_bench_tests_posix.py @@ -1,11 +1,13 @@ """ On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends). -Executed by QDC's Appium test framework on the QDC runner. -The runner has ADB access to the allocated device. +On Android: calls upstream run-*.sh scripts from llama.cpp/scripts/snapdragon/adb/ +on the QDC runner host (scripts wrap commands in ``adb shell`` internally). + +On Linux: runs llama-bench directly via run_linux.sh (BASH framework). Placeholders replaced at artifact creation time by run_qdc_jobs.py: - <> Direct URL to the GGUF model file (downloaded on-device via curl) + <> Direct URL to the GGUF model file (downloaded on-device) """ import os @@ -14,58 +16,75 @@ import pytest -from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_adb_command, write_qdc_log +from utils import ( + BIN_PATH, + MODEL_DEVICE_PATH, + MODEL_NAME, + PROMPT_DIR, + push_bundle_if_needed, + run_adb_command, + run_script, + write_qdc_log, +) -MODEL_PATH = "/data/local/tmp/model.gguf" -PROMPT = "What is the capital of France?" -CLI_OPTS = "--batch-size 128 -n 128 -no-cnv --seed 42" +MODEL_URL = "<>" @pytest.fixture(scope="session", autouse=True) def install(driver): push_bundle_if_needed(f"{BIN_PATH}/llama-cli") - - # Skip model download if already present + run_adb_command(f"mkdir -p /data/local/tmp/gguf {PROMPT_DIR}") + run_adb_command(f"echo 'What is the capital of France?' > {PROMPT_DIR}/bench_prompt.txt") check = subprocess.run( - ["adb", "shell", f"ls {MODEL_PATH}"], + ["adb", "shell", f"ls {MODEL_DEVICE_PATH}"], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) if check.returncode != 0: - run_adb_command(f'curl -L -J --output {MODEL_PATH} "<>"') - - -@pytest.mark.parametrize("device,extra_flags", [ - pytest.param("none", "-ctk q8_0 -ctv q8_0", id="cpu"), - pytest.param("GPUOpenCL", "", id="gpu"), - pytest.param("HTP0", "-ctk q8_0 -ctv q8_0", id="npu"), -]) -def test_llama_completion(device, extra_flags): - result = run_adb_command( - f'{CMD_PREFIX} {BIN_PATH}/llama-completion' - f' -m {MODEL_PATH} --device {device} -ngl 99 -t 4 {CLI_OPTS} {extra_flags} -fa on' - f' -p "{PROMPT}"', - check=False, + run_adb_command(f'curl -L -J --output {MODEL_DEVICE_PATH} "{MODEL_URL}"') + + +@pytest.mark.parametrize( + "device", + [ + pytest.param("none", id="cpu"), + pytest.param("GPUOpenCL", id="gpu"), + pytest.param("HTP0", id="npu"), + ], +) +def test_llama_completion(device): + result = run_script( + "run-completion.sh", + extra_env={"D": device, "M": MODEL_NAME}, + extra_args=["--batch-size", "128", "-n", "128", "--seed", "42", + "-f", f"{PROMPT_DIR}/bench_prompt.txt"], ) write_qdc_log(f"llama_completion_{device}.log", result.stdout or "") - assert result.returncode == 0, f"llama-completion {device} failed (exit {result.returncode})" + assert result.returncode == 0, ( + f"llama-completion {device} failed (exit {result.returncode})" + ) _DEVICE_LOG_NAME = {"none": "cpu", "GPUOpenCL": "gpu", "HTP0": "htp"} -@pytest.mark.parametrize("device", [ - pytest.param("none", id="cpu"), - pytest.param("GPUOpenCL", id="gpu"), - pytest.param("HTP0", id="npu"), -]) +@pytest.mark.parametrize( + "device", + [ + pytest.param("none", id="cpu"), + pytest.param("GPUOpenCL", id="gpu"), + pytest.param("HTP0", id="npu"), + ], +) def test_llama_bench(device): - result = run_adb_command( - f"{CMD_PREFIX} {BIN_PATH}/llama-bench" - f" -m {MODEL_PATH} --device {device} -ngl 99 --batch-size 128 -t 4 -p 128 -n 32", - check=False, + result = run_script( + "run-bench.sh", + extra_env={"D": device, "M": MODEL_NAME}, + extra_args=["--batch-size", "128", "-p", "128", "-n", "32"], ) write_qdc_log(f"llama_bench_{_DEVICE_LOG_NAME[device]}.log", result.stdout or "") - assert result.returncode == 0, f"llama-bench {device} failed (exit {result.returncode})" + assert result.returncode == 0, ( + f"llama-bench {device} failed (exit {result.returncode})" + ) if __name__ == "__main__": diff --git a/scripts/snapdragon/qdc/tests/utils.py b/scripts/snapdragon/qdc/tests/utils.py index 00f0f1b2f91..fad6a923295 100644 --- a/scripts/snapdragon/qdc/tests/utils.py +++ b/scripts/snapdragon/qdc/tests/utils.py @@ -1,5 +1,7 @@ """Shared helpers for QDC on-device test runners.""" +from __future__ import annotations + import logging import os import subprocess @@ -13,16 +15,14 @@ # On-device paths # --------------------------------------------------------------------------- -BUNDLE_PATH = "/data/local/tmp/llama_cpp_bundle" +BUNDLE_PATH = "/data/local/tmp/llama.cpp" +BIN_PATH = f"{BUNDLE_PATH}/bin" +LIB_PATH = f"{BUNDLE_PATH}/lib" QDC_LOGS_PATH = "/data/local/tmp/QDC_logs" -LIB_PATH = f"{BUNDLE_PATH}/lib" -BIN_PATH = f"{BUNDLE_PATH}/bin" -ENV_PREFIX = ( - f"export LD_LIBRARY_PATH={LIB_PATH} && " - f"export ADSP_LIBRARY_PATH={LIB_PATH} && " - f"chmod +x {BIN_PATH}/* &&" -) -CMD_PREFIX = f"cd {BUNDLE_PATH} && {ENV_PREFIX}" +SCRIPTS_DIR = "/qdc/appium" +MODEL_NAME = "model.gguf" +MODEL_DEVICE_PATH = "/data/local/tmp/gguf/model.gguf" +PROMPT_DIR = "/data/local/tmp/scorecard_prompts" # --------------------------------------------------------------------------- # Appium session options @@ -34,16 +34,47 @@ options.set_capability("deviceName", os.getenv("ANDROID_DEVICE_VERSION")) # --------------------------------------------------------------------------- -# ADB helpers +# Shell / process helpers +# --------------------------------------------------------------------------- + + +def write_qdc_log(filename: str, content: str) -> None: + """Write content as a log file for QDC log collection.""" + subprocess.run( + ["adb", "shell", f"mkdir -p {QDC_LOGS_PATH}"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + with tempfile.NamedTemporaryFile(mode="w", suffix=".log", delete=False) as f: + f.write(content) + tmp_path = f.name + try: + subprocess.run( + ["adb", "push", tmp_path, f"{QDC_LOGS_PATH}/{filename}"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + finally: + os.unlink(tmp_path) + + +def ensure_bundle(check_binary: str | None = None) -> None: + """Ensure the llama_cpp_bundle is available on the target device.""" + push_bundle_if_needed(check_binary or f"{BIN_PATH}/llama-cli") + + +# --------------------------------------------------------------------------- +# Android / Linux host helpers # --------------------------------------------------------------------------- def run_adb_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: - # Append exit-code sentinel because `adb shell` doesn't reliably propagate - # the on-device exit code (older ADB versions always return 0). + """Run a command on-device via ``adb shell`` with exit-code sentinel.""" raw = subprocess.run( ["adb", "shell", f"{cmd}; echo __RC__:$?"], - text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, ) stdout = raw.stdout returncode = raw.returncode @@ -55,39 +86,58 @@ def run_adb_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProc stdout = "\n".join(lines[:-1]) + "\n" except ValueError: pass - log.info("%s", stdout) + log.info(stdout) result = subprocess.CompletedProcess(raw.args, returncode, stdout=stdout) if check: assert returncode == 0, f"Command failed (exit {returncode})" return result -def write_qdc_log(filename: str, content: str) -> None: - """Push content as a log file to QDC_LOGS_PATH on the device for QDC log collection.""" +def run_script( + script: str, + extra_env: dict[str, str] | None = None, + extra_args: list[str] | None = None, +) -> subprocess.CompletedProcess: + """Run an upstream shell script from /qdc/appium/ on the QDC runner host.""" + env = os.environ.copy() + env["GGML_HEXAGON_EXPERIMENTAL"] = "1" + if extra_env: + env.update(extra_env) + cmd = [f"{SCRIPTS_DIR}/{script}"] + (extra_args or []) + result = subprocess.run( + cmd, env=env, + text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ) + log.info(result.stdout) + return result + + +def adb_shell(cmd: str) -> None: + """Run a command via adb shell (fire-and-forget, no error check).""" subprocess.run( - ["adb", "shell", f"mkdir -p {QDC_LOGS_PATH}"], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ["adb", "shell", "sh", "-c", cmd], + capture_output=True, encoding="utf-8", errors="replace", check=False, ) - with tempfile.NamedTemporaryFile(mode="w", suffix=".log", delete=False) as f: - f.write(content) - tmp_path = f.name - try: - subprocess.run( - ["adb", "push", tmp_path, f"{QDC_LOGS_PATH}/{filename}"], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - ) - finally: - os.unlink(tmp_path) def push_bundle_if_needed(check_binary: str) -> None: """Push llama_cpp_bundle to the device if check_binary is not already present.""" result = subprocess.run( ["adb", "shell", f"ls {check_binary}"], - text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, ) if result.returncode != 0: subprocess.run( - ["adb", "push", "/qdc/appium/llama_cpp_bundle/", "/data/local/tmp"], - text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ["adb", "push", "/qdc/appium/llama_cpp_bundle/", BUNDLE_PATH], + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + subprocess.run( + ["adb", "shell", f"find {BUNDLE_PATH}/bin -type f -exec chmod 755 {{}} +"], + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, ) diff --git a/ty.toml b/ty.toml index a07d7485d43..ad88ac7bdad 100644 --- a/ty.toml +++ b/ty.toml @@ -13,7 +13,7 @@ exclude = [ [[overrides]] include = [ "./tools/server/tests/**", - "./scripts/snapdragon/qdc/tests/**", + "./scripts/snapdragon/qdc/**", ] [overrides.rules] From 3e037f313c2c4cfce897d9be8f43954283a61de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=A4=C3=9Fler?= Date: Thu, 14 May 2026 22:58:58 +0200 Subject: [PATCH 110/158] HIP: RDNA3 mma FA, faster AMD transpose, tune AMD (#22880) Adds RDNA3 support to the CUDA mma FA kernel. To make the RDNA3 tensor cores work with the FP16 accumulation for VKQ the tiles they need to be 32 logical units long in direction of the attention head; for head sizes 80 and 112 that are not exactly divided by 32 the regular length of 16 with FP32 accumulation is used instead. The longer tiles also enable more efficient transposition for a warp size of 32 which is why it's also used for RDNA4. However, this scrambles the data layout of the accumulators along the attention head dimension. To prevent accidental misuse I added another entry to ggml_cuda_mma::data_layout. I also tuned the kernel parameters for RDNA3, RDNA4, and CDNA1 in general, during which I discovered that the kernel can be made to work for head sizes up to 256 for CDNA. For RDNA3/4 I was not able to get better performance that the tile kernel for head sizes > 128. --- ggml/src/ggml-cuda/fattn-mma-f16.cuh | 319 ++++++++++++++++++++------- ggml/src/ggml-cuda/fattn.cu | 57 ++--- ggml/src/ggml-cuda/mma.cuh | 149 +++++++++++-- 3 files changed, 398 insertions(+), 127 deletions(-) diff --git a/ggml/src/ggml-cuda/fattn-mma-f16.cuh b/ggml/src/ggml-cuda/fattn-mma-f16.cuh index 43e22c5e5ee..a25e912c4d2 100644 --- a/ggml/src/ggml-cuda/fattn-mma-f16.cuh +++ b/ggml/src/ggml-cuda/fattn-mma-f16.cuh @@ -125,61 +125,107 @@ static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_co } static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_config_rdna(const int DKQ, const int DV, const int ncols) { - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 128, 2, 64, 128, 128, 128, 2, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 128, 2, 64, 128, 128, 64, 2, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 128, 2, 64, 128, 128, 64, 2, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 8, 128, 2, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 16, 128, 2, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 32, 128, 2, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 64, 128, 2, 64, 32, 32, 32, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 32, 128, 2, 64, 160, 128, 64, 2, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 64, 128, 2, 64, 160, 128, 64, 2, false); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 8, 64, 2, 32, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 16, 64, 2, 32, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 32, 128, 2, 64, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 64, 128, 2, 64, 40, 40, 40, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 16, 64, 4, 32, 128, 128, 128, 1, false); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 32, 128, 2, 32, 128, 128, 128, 1, false); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 64, 256, 1, 32, 128, 128, 128, 1, false); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 8, 64, 2, 32, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 16, 64, 2, 32, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 32, 128, 2, 64, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 64, 128, 2, 64, 48, 48, 48, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 16, 64, 4, 32, 96, 64, 128, 1, false); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 32, 128, 2, 32, 160, 128, 128, 1, false); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 64, 256, 1, 32, 160, 128, 128, 1, false); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 8, 64, 2, 32, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 16, 64, 2, 32, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 32, 128, 2, 64, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 64, 128, 2, 64, 56, 56, 56, 1, true); - // TODO tune specifically for RDNA - return ggml_cuda_fattn_mma_get_config_ampere(DKQ, DV, ncols); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 8, 64, 2, 32, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 16, 64, 2, 32, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 32, 128, 2, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 64, 128, 2, 64, 64, 64, 64, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 8, 64, 2, 32, 96, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 16, 64, 2, 32, 96, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 32, 128, 2, 64, 96, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 64, 128, 2, 64, 96, 64, 64, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 64, 2, 32, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 64, 2, 32, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 128, 2, 64, 128, 128, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 128, 2, 64, 128, 128, 64, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 32, 128, 2, 32, 160, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 64, 128, 2, 32, 160, 128, 128, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 8, 128, 3, 64, 96, 64, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 16, 128, 3, 64, 96, 64, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 32, 128, 2, 32, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 64, 128, 2, 32, 128, 128, 128, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 8, 128, 3, 64, 96, 64, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 16, 128, 3, 64, 96, 64, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 32, 128, 2, 32, 160, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 64, 128, 2, 32, 160, 128, 128, 1, true); + + return fattn_mma_config(32, 1, 0, 0, 0, 0, 0, false); } static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_config_cdna(const int DKQ, const int DV, const int ncols) { - // Conservative configs for CDNA (MI100+): 64KB LDS, wavefront64, nstages=1 (no cp.async). - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 8, 128, 2, 128, 32, 32, 32, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 16, 128, 2, 64, 32, 32, 32, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 32, 128, 2, 64, 32, 32, 32, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 64, 256, 2, 64, 32, 32, 32, 1, true); - - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 8, 128, 2, 128, 40, 40, 40, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 16, 128, 2, 64, 40, 40, 40, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 32, 128, 2, 64, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 8, 128, 1, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 16, 256, 2, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 32, 256, 2, 64, 32, 32, 32, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 64, 256, 4, 64, 32, 32, 32, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 8, 256, 2, 64, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 16, 256, 2, 64, 40, 40, 40, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 32, 256, 2, 64, 40, 40, 40, 1, true); GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 64, 256, 2, 64, 40, 40, 40, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 8, 128, 2, 128, 48, 48, 48, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 16, 128, 2, 64, 48, 48, 48, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 32, 128, 2, 64, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 8, 256, 2, 64, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 16, 256, 2, 64, 48, 48, 48, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 32, 256, 2, 64, 48, 48, 48, 1, true); GGML_CUDA_FATTN_MMA_CONFIG_CASE( 96, 96, 64, 256, 2, 64, 48, 48, 48, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 8, 128, 2, 128, 56, 56, 56, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 16, 128, 2, 64, 56, 56, 56, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 32, 128, 2, 64, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 8, 256, 2, 64, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 16, 256, 2, 64, 56, 56, 56, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 32, 256, 2, 64, 56, 56, 56, 1, true); GGML_CUDA_FATTN_MMA_CONFIG_CASE(112, 112, 64, 256, 2, 64, 56, 56, 56, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 8, 128, 2, 128, 64, 64, 64, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 16, 128, 2, 64, 64, 64, 64, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 32, 128, 2, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 8, 256, 2, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 16, 256, 2, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 32, 256, 2, 64, 64, 64, 64, 1, true); GGML_CUDA_FATTN_MMA_CONFIG_CASE(128, 128, 64, 256, 2, 64, 64, 64, 64, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 64, 4, 64, 128, 128, 128, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 64, 4, 32, 128, 128, 128, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 128, 2, 32, 128, 128, 128, 1, true); - GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 256, 2, 32, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 8, 256, 1, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 16, 256, 1, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 32, 256, 1, 64, 64, 64, 64, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 64, 512, 1, 64, 64, 64, 64, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 512, 1, 64, 128, 128, 64, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 32, 256, 1, 64, 160, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 64, 256, 1, 64, 160, 128, 128, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 8, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 16, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 32, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 64, 256, 1, 64, 128, 128, 128, 1, true); + + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 8, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 16, 256, 1, 64, 128, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 32, 256, 1, 64, 160, 128, 128, 1, true); + GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 64, 256, 1, 64, 160, 128, 128, 1, true); - // Fallback for unsupported DKQ values (e.g. 576). Must return non-zero values to satisfy - // compile-time static_asserts even though the kernel guard prevents runtime execution. - // nthreads=256 gives nwarps=4 (warp_size=64) or 8 (warp_size=32), nbatch_fa=128 satisfies np*16 divisibility. - return fattn_mma_config(256, 1, 128, 4, 4, 4, 1, false); + return fattn_mma_config(32, 1, 0, 0, 0, 0, 0, false); } static __host__ fattn_mma_config ggml_cuda_fattn_mma_get_config(const int DKQ, const int DV, const int ncols, const int cc) { @@ -510,7 +556,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( const int jt, const int kb0, const int k_VKQ_sup) { -#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE) +#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) constexpr int warp_size = ggml_cuda_get_physical_warp_size(); constexpr int ncols = ncols1 * ncols2; constexpr int cols_per_warp = T_B_KQ::I; @@ -712,6 +758,18 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( #pragma unroll for (int i00 = 0; i00 < nbatch_fa; i00 += np*T_C_KQ::J) { const int i0 = i00 + (threadIdx.y % np)*T_C_KQ::J; + + // The mask is stored as 16 bit half values, loading them as 32 bit half2 values is preferred in terms of speed. + // However, this is not possible for RDNA3 where 2 consecutive l indices are not consecutive in the mask memory layout. +#ifdef RDNA3 +#pragma unroll + for (int l = 0; l < T_C_KQ::ne; ++l) { + const int i = i0 + T_C_KQ::get_j(l); + const int j = ((threadIdx.y / np)*cols_per_warp + T_C_KQ::get_i(l)) / ncols2; + + KQ_C[i00/(np*T_C_KQ::J)].x[l] += __half2float(tile_mask[j*(nbatch_fa + 8) + i]); + } +#else #pragma unroll for (int l0 = 0; l0 < T_C_KQ::ne; l0 += 2) { const int i = (i0 + T_C_KQ::get_j(l0)) / 2; @@ -721,6 +779,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( KQ_C[i00/(np*T_C_KQ::J)].x[l0 + 0] += slope*tmp.x; KQ_C[i00/(np*T_C_KQ::J)].x[l0 + 1] += slope*tmp.y; } +#endif // RDNA3 } } @@ -827,13 +886,23 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( } } #elif defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) - const half2 KQ_max_scale_h2 = make_half2( - KQ_max_scale[0], KQ_max_scale[0]); + if constexpr (std::is_same_v) { + const half2 KQ_max_scale_h2 = make_half2(KQ_max_scale[0], KQ_max_scale[0]); #pragma unroll - for (int i = 0; i < (DV/2)/T_C_VKQ::J; ++i) { + for (int i = 0; i < (DV/2)/T_C_VKQ::J; ++i) { #pragma unroll - for (int l = 0; l < T_C_VKQ::ne; ++l) { - VKQ_C[i].x[l] *= KQ_max_scale_h2; + for (int l = 0; l < T_C_VKQ::ne; ++l) { + VKQ_C[i].x[l] *= KQ_max_scale_h2; + } + } + } else { + static_assert(std::is_same_v, "bad VKQ type"); +#pragma unroll + for (int i = 0; i < DV/T_C_VKQ::J; ++i) { +#pragma unroll + for (int l = 0; l < T_C_VKQ::ne; ++l) { + VKQ_C[i].x[l] *= KQ_max_scale[0]; + } } } #else // Volta @@ -901,9 +970,8 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( const half2 * tile_V_i = !V_is_K_view || i0_stop > 2*nbatch_K2 ? tile_V : tile_V + i0_start/2; #if defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) - constexpr int i0_stride = cols_per_warp == 8 ? T_C_VKQ::I : 2*T_C_VKQ::J; #pragma unroll - for (int i_VKQ_0 = i0_start; i_VKQ_0 < i0_stop; i_VKQ_0 += i0_stride) { + for (int i_VKQ_0 = i0_start; i_VKQ_0 < i0_stop; i_VKQ_0 += T_A_VKQ::I) { static_assert((nbatch_fa/2) % (np*T_A_VKQ::J) == 0, "bad loop size"); #pragma unroll for (int k00 = 0; k00 < nbatch_fa/2; k00 += np*T_A_VKQ::J) { @@ -912,15 +980,15 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( T_A_VKQ A; // Transposed in SRAM but not in registers, gets transposed on load. load_ldmatrix_trans(A, tile_V_i + 2*k0*stride_tile_V + (i_VKQ_0 - i0_start)/2, stride_tile_V); if constexpr (T_B_KQ::I == 8) { - mma(VKQ_C[i_VKQ_0/i0_stride], A, B[k00/(np*T_A_VKQ::J)]); + mma(VKQ_C[i_VKQ_0/T_A_VKQ::I], A, B[k00/(np*T_A_VKQ::J)]); } else { // Wide version of VKQ_C is column-major. #if defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) // AMD matrix C is column-major. - mma(VKQ_C[i_VKQ_0/i0_stride], A, B[k00/(np*T_A_VKQ::J)]); + mma(VKQ_C[i_VKQ_0/T_A_VKQ::I], A, B[k00/(np*T_A_VKQ::J)]); #else // swap A and B for CUDA. - mma(VKQ_C[i_VKQ_0/i0_stride], B[k00/(np*T_A_VKQ::J)], A); + mma(VKQ_C[i_VKQ_0/T_A_VKQ::I], B[k00/(np*T_A_VKQ::J)], A); #endif // defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) } } @@ -953,11 +1021,11 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter( tile_Q, tile_K, tile_V, tile_mask, Q_B, VKQ_C, KQ_max, KQ_rowsum, kb0); NO_DEVICE_CODE; -#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE) +#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) } #if defined(TURING_MMA_AVAILABLE) -template struct mma_tile_sizes { +template struct mma_tile_sizes { using T_A_KQ = tile<16, 8, half2>; // row-major using T_B_KQ = tile<16, 8, half2>; // column-major using T_C_KQ = tile<16, 16, float>; // column-major @@ -965,7 +1033,7 @@ template struct mma_tile_sizes { using T_B_VKQ = tile<16, 8, half2>; // column-major using T_C_VKQ = tile<16, 8, half2>; // column-major }; -template<> struct mma_tile_sizes<8> { +template struct mma_tile_sizes { using T_A_KQ = tile<16, 8, half2>; // row-major using T_B_KQ = tile< 8, 8, half2>; // column-major using T_C_KQ = tile<16, 8, float>; // row-major @@ -973,8 +1041,60 @@ template<> struct mma_tile_sizes<8> { using T_B_VKQ = tile< 8, 8, half2>; // column-major using T_C_VKQ = tile<16, 4, half2>; // row-major }; -#elif defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) -template struct mma_tile_sizes { +#elif defined(AMD_WMMA_AVAILABLE) +#ifdef RDNA3 +template struct mma_tile_sizes { + using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major + using T_A_VKQ = tile<32, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_VKQ = tile<16, 16, half2, DATA_LAYOUT_I_MAJOR>; // column-major +}; +template struct mma_tile_sizes<80, ncols> { + using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major + using T_A_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_VKQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major +}; +template struct mma_tile_sizes<112, ncols> { + using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major + using T_A_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major + using T_B_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major + using T_C_VKQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major +}; +#else +template struct mma_tile_sizes { + using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR>; // row-major + using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR>; // column-major + using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major + using T_A_VKQ = tile<32, 8, half2, DATA_LAYOUT_I_MAJOR>; // row-major + using T_B_VKQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR>; // column-major + using T_C_VKQ = tile<16, 16, half2, DATA_LAYOUT_I_MAJOR_SCRAMBLED>; // column-major +}; +template struct mma_tile_sizes<80, ncols> { + using T_A_KQ = tile<16, 8, half2>; // row-major + using T_B_KQ = tile<16, 8, half2>; // column-major + using T_C_KQ = tile<16, 16, float>; // column-major + using T_A_VKQ = tile<16, 8, half2>; // row-major + using T_B_VKQ = tile<16, 8, half2>; // column-major + using T_C_VKQ = tile<16, 8, half2>; // column-major +}; +template struct mma_tile_sizes<112, ncols> { + using T_A_KQ = tile<16, 8, half2>; // row-major + using T_B_KQ = tile<16, 8, half2>; // column-major + using T_C_KQ = tile<16, 16, float>; // column-major + using T_A_VKQ = tile<16, 8, half2>; // row-major + using T_B_VKQ = tile<16, 8, half2>; // column-major + using T_C_VKQ = tile<16, 8, half2>; // column-major +}; +#endif // RDNA3 +#elif defined(AMD_MFMA_AVAILABLE) +template struct mma_tile_sizes { using T_A_KQ = tile<16, 8, half2>; // row-major using T_B_KQ = tile<16, 8, half2>; // column-major using T_C_KQ = tile<16, 16, float>; // column-major @@ -983,7 +1103,7 @@ template struct mma_tile_sizes { using T_C_VKQ = tile<16, 8, half2>; // column-major }; #else // Volta -template struct mma_tile_sizes { +template struct mma_tile_sizes { using T_A_KQ = tile< 8, 4, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major using T_B_KQ = tile<32, 4, half2, DATA_LAYOUT_I_MAJOR>; // column-major using T_C_KQ = tile<32, 8, float, DATA_LAYOUT_I_MAJOR>; // column-major @@ -1018,17 +1138,17 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( const int zt_gqa, const int kb0_start, const int kb0_stop) { -#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE) +#if defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) //In this kernel Q, K, V are matrices while i, j, k are matrix indices. constexpr int warp_size = ggml_cuda_get_physical_warp_size(); constexpr int ncols = ncols1 * ncols2; - using T_A_KQ = typename mma_tile_sizes::T_A_KQ; - using T_B_KQ = typename mma_tile_sizes::T_B_KQ; - using T_C_KQ = typename mma_tile_sizes::T_C_KQ; - using T_A_VKQ = typename mma_tile_sizes::T_A_VKQ; - using T_B_VKQ = typename mma_tile_sizes::T_B_VKQ; - using T_C_VKQ = typename mma_tile_sizes::T_C_VKQ; + using T_A_KQ = typename mma_tile_sizes::T_A_KQ; + using T_B_KQ = typename mma_tile_sizes::T_B_KQ; + using T_C_KQ = typename mma_tile_sizes::T_C_KQ; + using T_A_VKQ = typename mma_tile_sizes::T_A_VKQ; + using T_B_VKQ = typename mma_tile_sizes::T_B_VKQ; + using T_C_VKQ = typename mma_tile_sizes::T_C_VKQ; constexpr int cols_per_warp = T_B_KQ::I; constexpr int cols_per_thread = get_cols_per_thread(); @@ -1061,6 +1181,8 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( T_B_KQ Q_B[(Q_in_reg ? DKQ/(2*T_B_KQ::J) : 1)]; #if defined(TURING_MMA_AVAILABLE) T_C_VKQ VKQ_C[cols_per_warp == 8 ? DV/T_C_VKQ::I : DV/(2*T_C_VKQ::J)]; +#elif defined(AMD_WMMA_AVAILABLE) && defined(RDNA3) + T_C_VKQ VKQ_C[DV % 32 != 0 ? DV/T_C_VKQ::J : DV/(2*T_C_VKQ::J)]; #elif defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) T_C_VKQ VKQ_C[ DV/(2*T_C_VKQ::J)]; #else // Volta @@ -1269,12 +1391,23 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( } } #elif defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) - const half2 KQ_max_scale_h2 = make_half2(KQ_max_scale[0], KQ_max_scale[0]); + if constexpr (std::is_same_v) { + const half2 KQ_max_scale_h2 = make_half2(KQ_max_scale[0], KQ_max_scale[0]); #pragma unroll - for (int i = 0; i < (DV/2)/T_C_VKQ::J; ++i) { + for (int i = 0; i < (DV/2)/T_C_VKQ::J; ++i) { #pragma unroll - for (int l = 0; l < T_C_VKQ::ne; ++l) { - VKQ_C[i].x[l] *= KQ_max_scale_h2; + for (int l = 0; l < T_C_VKQ::ne; ++l) { + VKQ_C[i].x[l] *= KQ_max_scale_h2; + } + } + } else { + static_assert(std::is_same_v, "bad VKQ type"); +#pragma unroll + for (int i = 0; i < DV/T_C_VKQ::J; ++i) { +#pragma unroll + for (int l = 0; l < T_C_VKQ::ne; ++l) { + VKQ_C[i].x[l] *= KQ_max_scale[0]; + } } } #else // Volta @@ -1433,6 +1566,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( #pragma unroll for (int k00 = 0; k00 < DV/2; k00 += nbatch_combine) { if constexpr (cols_per_warp == 8) { + static_assert(std::is_same_v, "bad VKQ type"); const int jc_cwd = threadIdx.y*T_B_KQ::I + T_B_KQ::get_i(-1); // jc combine write data #pragma unroll for (int k1 = 0; k1 < nbatch_combine; k1 += T_B_KQ::J) { @@ -1447,14 +1581,45 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( } } else { const int j0 = threadIdx.y*cols_per_warp; + if constexpr (std::is_same_v) { + if constexpr (T_C_VKQ::dl == DATA_LAYOUT_I_MAJOR) { #pragma unroll - for (int k1 = 0; k1 < nbatch_combine; k1 += T_C_VKQ::J) { + for (int k1 = 0; k1 < nbatch_combine; k1 += T_C_VKQ::J) { #pragma unroll - for (int l = 0; l < T_C_VKQ::ne; ++l) { - const int j = j0 + T_C_VKQ::get_i(l); - const int k = k1 + T_C_VKQ::get_j(l); + for (int l = 0; l < T_C_VKQ::ne; ++l) { + const int j = j0 + T_C_VKQ::get_i(l); + const int k = k1 + T_C_VKQ::get_j(l); - tile_Q[j*tile_stride + k] = VKQ_C[(k00 + k1)/T_C_VKQ::J].x[l]; + tile_Q[j*tile_stride + k] = VKQ_C[(k00 + k1)/T_C_VKQ::J].x[l]; + } + } + } else { + static_assert(T_C_VKQ::dl == DATA_LAYOUT_I_MAJOR_SCRAMBLED, "bad T_C_VKQ data layout"); + using T_C_VKQ_us = tile; // us == unscrambled +#pragma unroll + for (int k1 = 0; k1 < nbatch_combine; k1 += T_C_VKQ::J) { + const T_C_VKQ_us VKQ_C_us = unscramble(VKQ_C[(k00 + k1)/T_C_VKQ::J]); +#pragma unroll + for (int l = 0; l < T_C_VKQ_us::ne; ++l) { + const int j = j0 + T_C_VKQ_us::get_i(l); + const int k = k1 + T_C_VKQ_us::get_j(l); + + tile_Q[j*tile_stride + k] = VKQ_C_us.x[l]; + } + } + } + } else { + static_assert(std::is_same_v, "bad VKQ type"); + half * tile_Q_h = (half *) tile_Q; +#pragma unroll + for (int k1 = 0; k1 < nbatch_combine; k1 += T_C_VKQ::J/2) { +#pragma unroll + for (int l = 0; l < T_C_VKQ::ne; ++l) { + const int j = j0 + T_C_VKQ::get_i(l); + const int k = 2*k1 + T_C_VKQ::get_j(l); + + tile_Q_h[j*(2*tile_stride) + k] = VKQ_C[(k00 + k1)/(T_C_VKQ::J/2)].x[l]; + } } } } @@ -1532,7 +1697,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( stride_Q1, stride_Q2, stride_K, stride_V, stride_mask, jt, kb0_start, kb0_stop); NO_DEVICE_CODE; -#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE) +#endif // defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) } template @@ -1559,7 +1724,7 @@ static __global__ void flash_attn_ext_f16( const int32_t nb21, const int32_t nb22, const int64_t nb23, const int32_t ne31, const int32_t ne32, const int32_t ne33, const int32_t nb31, const int32_t nb32, const int64_t nb33) { -#if defined(FLASH_ATTN_AVAILABLE) && (defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE)) +#if defined(FLASH_ATTN_AVAILABLE) && (defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)) // Skip unused kernel variants for faster compilation: if (use_logit_softcap && !(DKQ == 128 || DKQ == 256 || DKQ == 512)) { @@ -1585,14 +1750,14 @@ static __global__ void flash_attn_ext_f16( #endif // __CUDA_ARCH__ == GGML_CUDA_CC_TURING #if defined(AMD_WMMA_AVAILABLE) - if (ncols1*ncols2 > 32 || ncols1*ncols2 < 16 || DKQ > 128 || ncols2 == 1) { + if (ncols1*ncols2 < 16 || ncols2 == 1 || DKQ > 128) { NO_DEVICE_CODE; return; } #endif // defined(AMD_WMMA_AVAILABLE) #if defined(AMD_MFMA_AVAILABLE) - if (DKQ != 64 && DKQ != 80 && DKQ != 96 && DKQ != 112 && DKQ != 128) { + if (ncols1*ncols2 < 16 || DKQ > 256) { NO_DEVICE_CODE; return; } @@ -1715,7 +1880,7 @@ static __global__ void flash_attn_ext_f16( ne31, ne32, ne33, nb31, nb32, nb33); NO_DEVICE_CODE; -#endif // defined(FLASH_ATTN_AVAILABLE) && (defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) || defined(AMD_MFMA_AVAILABLE)) +#endif // defined(FLASH_ATTN_AVAILABLE) && (defined(VOLTA_MMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)) } template diff --git a/ggml/src/ggml-cuda/fattn.cu b/ggml/src/ggml-cuda/fattn.cu index e045b04f727..1c7777e8a71 100644 --- a/ggml/src/ggml-cuda/fattn.cu +++ b/ggml/src/ggml-cuda/fattn.cu @@ -19,13 +19,14 @@ static void ggml_cuda_flash_attn_ext_mma_f16_switch_ncols1(ggml_backend_cuda_con } if constexpr (ncols2 <= 16) { - if ((turing_mma_available(cc) || amd_wmma_available(cc)) && Q->ne[1] <= 16/ncols2) { + if (Q->ne[1] <= 16/ncols2) { ggml_cuda_flash_attn_ext_mma_f16_case(ctx, dst); return; } } - if (ggml_cuda_highest_compiled_arch(cc) == GGML_CUDA_CC_TURING || amd_wmma_available(cc) || Q->ne[1] <= 32/ncols2) { + if (Q->ne[1] <= 32/ncols2 || (GGML_CUDA_CC_IS_NVIDIA(cc) && ggml_cuda_highest_compiled_arch(cc) == GGML_CUDA_CC_TURING) || + (GGML_CUDA_CC_IS_AMD(cc) && DKQ > 256)) { ggml_cuda_flash_attn_ext_mma_f16_case(ctx, dst); return; } @@ -477,12 +478,13 @@ static best_fattn_kernel ggml_cuda_get_best_fattn_kernel(const int device, const return BEST_FATTN_KERNEL_MMA_F16; } + const int ncols2_max = Q->ne[0] == 320 ? 32 : ((Q->ne[0] == 576 || Q->ne[0] == 192) ? 16 : 8); + int gqa_ratio_eff = 1; + while (gqa_ratio % (2*gqa_ratio_eff) == 0 && gqa_ratio_eff < ncols2_max) { + gqa_ratio_eff *= 2; + } + if (volta_mma_available(cc) && Q->ne[0] != 40 && Q->ne[0] != 72) { - int gqa_ratio_eff = 1; - const int ncols2_max = (Q->ne[0] == 576 || Q->ne[0] == 192) ? 16 : 8; - while (gqa_ratio % (2*gqa_ratio_eff) == 0 && gqa_ratio_eff < ncols2_max) { - gqa_ratio_eff *= 2; - } if (can_use_vector_kernel && Q->ne[1] * gqa_ratio_eff <= 2) { return BEST_FATTN_KERNEL_VEC; } @@ -500,41 +502,22 @@ static best_fattn_kernel ggml_cuda_get_best_fattn_kernel(const int device, const return BEST_FATTN_KERNEL_WMMA_F16; } - if (amd_wmma_available(cc) && GGML_CUDA_CC_IS_RDNA4(cc) && gqa_opt_applies && Q->ne[0] <= 128 && Q->ne[0] != 40 && Q->ne[0] != 72) { - if (can_use_vector_kernel) { - if (!ggml_is_quantized(K->type) && !ggml_is_quantized(V->type)) { - if (Q->ne[1] == 1) { - if (!gqa_opt_applies) { - return BEST_FATTN_KERNEL_VEC; - } - } - } else { - if (Q->ne[1] <= 2) { - return BEST_FATTN_KERNEL_VEC; - } - } + // AMD MFMA needs a certain minimum batch size to outscale the tile kernel for large head sizes. + if ((amd_mfma_available(cc) && Q->ne[0] <= 256) && Q->ne[0] != 40 && Q->ne[0] != 72) { + if ((Q->ne[0] <= 64 && Q->ne[1] * gqa_ratio_eff > 8)) { + return BEST_FATTN_KERNEL_MMA_F16; } - int gqa_ratio_eff = 1; - const int ncols2_max = Q->ne[0] == 576 ? 16 : 8; - while (gqa_ratio % (2*gqa_ratio_eff) == 0 && gqa_ratio_eff < ncols2_max) { - gqa_ratio_eff *= 2; + if ((Q->ne[0] <= 128 && Q->ne[1] * gqa_ratio_eff > 16)) { + return BEST_FATTN_KERNEL_MMA_F16; } - if (Q->ne[1] * gqa_ratio_eff <= 8) { - return BEST_FATTN_KERNEL_TILE; // AMD WMMA is only faster if the full tile width of 16 can be utilized. + if ((Q->ne[0] <= 256 && Q->ne[1] * gqa_ratio_eff > 64)) { + return BEST_FATTN_KERNEL_MMA_F16; } - return BEST_FATTN_KERNEL_MMA_F16; } - // Use MFMA flash attention for CDNA (MI100+): - if (amd_mfma_available(cc) && Q->ne[0] != 40 && Q->ne[0] != 72 && Q->ne[0] != 192 && Q->ne[0] != 256 && Q->ne[0] != 512 && Q->ne[0] != 576) { - const int64_t eff_nq = Q->ne[1] * (gqa_opt_applies ? gqa_ratio : 1); - // MMA vs tile crossover benchmarked on MI300X @ d32768: - // hsk=64 (gqa=4): MMA wins at eff >= 128 (+11%) - // hsk=128 (gqa=4): MMA wins at eff >= 128 (+4%) - if (eff_nq >= (GGML_CUDA_CC_IS_CDNA1(cc) && Q->ne[0] == 64 ? 64 : 128)) { - return BEST_FATTN_KERNEL_MMA_F16; - } - // Fall through to tile kernel for small effective batch sizes. + // AMD WMMA is always faster than the tile kernel if the full tile width of 16 can be utilized. + if ((amd_wmma_available(cc) && gqa_opt_applies && Q->ne[0] <= 128) && Q->ne[0] != 40 && Q->ne[0] != 72 && Q->ne[1] * gqa_ratio_eff > 8) { + return BEST_FATTN_KERNEL_MMA_F16; } // If there are no tensor cores available, use the generic tile kernel: diff --git a/ggml/src/ggml-cuda/mma.cuh b/ggml/src/ggml-cuda/mma.cuh index 79bb2934c5f..8d7c69dc3e8 100644 --- a/ggml/src/ggml-cuda/mma.cuh +++ b/ggml/src/ggml-cuda/mma.cuh @@ -80,6 +80,7 @@ namespace ggml_cuda_mma { DATA_LAYOUT_J_MAJOR = 10, // Matrix C for CDNA and RDNA4, int and float matrix C for RDNA3. DATA_LAYOUT_I_MAJOR_MIRRORED = 20, // Volta, matrix A&B for RDNA3. DATA_LAYOUT_J_MAJOR_MIRRORED = 30, + DATA_LAYOUT_I_MAJOR_SCRAMBLED = 40, // Scrambled matrix C for faster transposition (RDNA4/CDNA), convert to float to unscramble. }; // Implemented mma combinations are: // - (I_MAJOR, I_MAJOR) -> I_MAJOR @@ -312,13 +313,19 @@ namespace ggml_cuda_mma { half2 x[ne] = {{0.0f, 0.0f}}; static constexpr __device__ bool supported() { - if (I == 16 && J == 8) return true; + if (I == 16 && J == 8) return true; + if (I == 16 && J == 16) return true; + if (I == 32 && J == 8) return true; return false; } static __device__ __forceinline__ int get_i(const int l) { if constexpr (I == 16 && J == 8) { return threadIdx.x % 16; + } else if constexpr (I == 16 && J == 16) { + return threadIdx.x % 16; + } else if constexpr (I == 32 && J == 8) { + return (threadIdx.x % 16) * 2 + l / (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -327,7 +334,15 @@ namespace ggml_cuda_mma { static __device__ __forceinline__ int get_j(const int l) { if constexpr (I == 16 && J == 8) { - return ne * (threadIdx.x / 16) + l; + return (threadIdx.x / 16) * ne + l; + } else if constexpr (I == 16 && J == 16) { +#ifdef RDNA3 + return l*2 + (threadIdx.x / 16); +#else + return (threadIdx.x / 16) * ne + l; +#endif // RDNA3 + } else if constexpr (I == 32 && J == 8) { + return (threadIdx.x / 16) * (ne/2) + l % (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -338,13 +353,19 @@ namespace ggml_cuda_mma { half2 x[ne] = {{0.0f, 0.0f}}; static constexpr __device__ bool supported() { - if (I == 16 && J == 8) return true; + if (I == 16 && J == 8) return true; + if (I == 16 && J == 16) return true; + if (I == 32 && J == 8) return true; return false; } static __device__ __forceinline__ int get_i(const int l) { if constexpr (I == 16 && J == 8) { return threadIdx.x % 16; + } else if constexpr (I == 16 && J == 16) { + return threadIdx.x % 16; + } else if constexpr (I == 32 && J == 8) { + return (threadIdx.x % 16) * 2 + l / (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -353,7 +374,11 @@ namespace ggml_cuda_mma { static __device__ __forceinline__ int get_j(const int l) { if constexpr (I == 16 && J == 8) { - return ne * (threadIdx.x / 16) + l; + return (threadIdx.x / 16) * ne + l; + } else if constexpr (I == 16 && J == 16) { + return (threadIdx.x / 16) * ne + l; + } else if constexpr (I == 32 && J == 8) { + return (threadIdx.x / 16) * (ne/2) + l % (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -516,12 +541,15 @@ namespace ggml_cuda_mma { if (I == 16 && J == 16) return true; if (I == 16 && J == 8) return true; if (I == 16 && J == 4) return true; + if (I == 32 && J == 8) return true; return false; } - static __device__ __forceinline__ int get_i(const int /*l*/) { - if constexpr (supported()) { + static __device__ __forceinline__ int get_i(const int l) { + if constexpr (I == 16) { return threadIdx.x % 16; + } else if constexpr (I == 32) { + return (threadIdx.x % 16) * 2 + l / (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -529,8 +557,10 @@ namespace ggml_cuda_mma { } static __device__ __forceinline__ int get_j(const int l) { - if constexpr (supported()) { + if constexpr (I == 16) { return l; + } else if constexpr (I == 32) { + return l % (ne/2); } else { NO_DEVICE_CODE; return -1; @@ -644,6 +674,40 @@ namespace ggml_cuda_mma { } }; + template + struct tile { + static constexpr int I = I_; + static constexpr int J = J_; + static constexpr data_layout dl = DATA_LAYOUT_I_MAJOR_SCRAMBLED; + + static constexpr int ne = I * J / ggml_cuda_get_physical_warp_size(); + half2 x[ne] = {{0.0f, 0.0f}}; + + static constexpr __device__ bool supported() { + if (I == 16 && J == 16) return true; + return false; + } + + static __device__ __forceinline__ int get_i(const int l) { + return tile::get_i(l); + } + }; + + static __device__ __forceinline__ tile<16, 16, half2, DATA_LAYOUT_I_MAJOR> unscramble(const tile<16, 16, half2, DATA_LAYOUT_I_MAJOR_SCRAMBLED> & t) { +#if defined(AMD_MFMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) + tile<16, 16, half2, DATA_LAYOUT_I_MAJOR> ret; +#pragma unroll + for (int l0 = 0; l0 < t.ne/2; ++l0) { + ret.x[2*l0 + 0] = __lows2half2(t.x[l0], t.x[l0 + t.ne/2]); + ret.x[2*l0 + 1] = __highs2half2(t.x[l0], t.x[l0 + t.ne/2]); + } + return ret; +#else + NO_DEVICE_CODE; + GGML_UNUSED(t); +#endif // defined(AMD_MFMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) + } + #if defined(TURING_MMA_AVAILABLE) template static __device__ __forceinline__ tile get_half2(const tile & tile_float) { @@ -660,6 +724,21 @@ namespace ggml_cuda_mma { ret.x[0] = ggml_cuda_movmatrix(t.x[0]); ret.x[1] = ggml_cuda_movmatrix(t.x[1]); + return ret; + } +#elif defined(AMD_WMMA_AVAILABLE) && defined(RDNA3) + static __device__ __forceinline__ tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED> get_half2( + const tile<16, 16, float, DATA_LAYOUT_I_MAJOR> & tile_float) { + tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED> ret; +#pragma unroll + for (int l = 0; l < tile_float.ne; ++l) { + float tmp[2]; + int i = threadIdx.x / 16; + tmp[i] = tile_float.x[l]; + i ^= 1; + tmp[i] = __shfl_xor_sync(0xFFFFFFFF, tile_float.x[l], 16, WARP_SIZE); + ret.x[l] = make_half2(tmp[0], tmp[1]); + } return ret; } #elif defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE) @@ -802,21 +881,35 @@ namespace ggml_cuda_mma { #endif // defined(VOLTA_MMA_AVAILABLE) } - template + template static __device__ __forceinline__ void load_ldmatrix_trans( - tile<16, 8, T> & t, const T * __restrict__ xs0, const int stride) { + tile & t, const T * __restrict__ xs0, const int stride) { #ifdef TURING_MMA_AVAILABLE + static_assert(I == 16, "bad tile width"); + static_assert(dl == DATA_LAYOUT_I_MAJOR, "bad data layout"); int * xi = (int *) t.x; const int * xs = (const int *) xs0 + (threadIdx.x % t.I) * stride + (threadIdx.x / t.I) * (t.J / 2); asm volatile("ldmatrix.sync.aligned.m8n8.x4.trans.b16 {%0, %1, %2, %3}, [%4];" : "=r"(xi[0]), "=r"(xi[2]), "=r"(xi[1]), "=r"(xi[3]) : "l"(xs)); #elif defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) - half * xh = (half *) t.x; + static_assert(dl == DATA_LAYOUT_I_MAJOR || dl == DATA_LAYOUT_I_MAJOR_MIRRORED, "bad data layout"); + if constexpr (I == 32) { #pragma unroll - for (int l = 0; l < t.ne; ++l) { - xh[2*l + 0] = ((const half *) xs0)[(2*t.get_j(l) + 0)*(2*stride) + t.get_i(l)]; - xh[2*l + 1] = ((const half *) xs0)[(2*t.get_j(l) + 1)*(2*stride) + t.get_i(l)]; + for (int l0 = 0; l0 < t.ne/2; ++l0) { + const half2 tmp0 = xs0[(2*t.get_j(l0) + 0)*stride + t.get_i(l0)/2]; + const half2 tmp1 = xs0[(2*t.get_j(l0) + 1)*stride + t.get_i(l0)/2]; + + t.x[l0] = __lows2half2(tmp0, tmp1); + t.x[l0 + t.ne/2] = __highs2half2(tmp0, tmp1); + } + } else { + half * xh = (half *) t.x; +#pragma unroll + for (int l = 0; l < t.ne; ++l) { + xh[2*l + 0] = ((const half *) xs0)[(2*t.get_j(l) + 0)*(2*stride) + t.get_i(l)]; + xh[2*l + 1] = ((const half *) xs0)[(2*t.get_j(l) + 1)*(2*stride) + t.get_i(l)]; + } } #else GGML_UNUSED_VARS(t, xs0, stride); @@ -972,6 +1065,20 @@ namespace ggml_cuda_mma { #endif // TURING_MMA_AVAILABLE } + static __device__ __forceinline__ void mma( + tile<16, 16, half2, DATA_LAYOUT_I_MAJOR_SCRAMBLED> & D, const tile<32, 8, half2, DATA_LAYOUT_I_MAJOR> & A, + const tile<16, 8, half2, DATA_LAYOUT_I_MAJOR> & B) { +#if defined(AMD_MFMA_AVAILABLE) || (defined(AMD_WMMA_AVAILABLE) && defined(RDNA4)) + tile<16, 8, half2> * D16 = (tile<16, 8, half2> *) &D; + const tile<16, 8, half2> * A16 = (const tile<16, 8, half2> *) &A; + mma(D16[0], A16[0], B); + mma(D16[1], A16[1], B); +#else + GGML_UNUSED_VARS(D, A, B); + NO_DEVICE_CODE; +#endif // defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) && defined(RDNA4) + } + template static __device__ __forceinline__ void mma( tile<16, 8, float, dl_d> & D, const tile<16, 8, float, dl_ab> & A, const tile<8, 8, float, dl_ab> & B) { @@ -1296,6 +1403,22 @@ namespace ggml_cuda_mma { #endif // defined(VOLTA_MMA_AVAILABLE) } + static __device__ __forceinline__ void mma( + tile<16, 16, half2, DATA_LAYOUT_I_MAJOR> & D, const tile<32, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED> & A, + const tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED> & B) { +#if defined(AMD_WMMA_AVAILABLE) && defined(RDNA3) + using halfx16_t = __attribute__((ext_vector_type(16))) _Float16; + halfx16_t * xD = (halfx16_t *) D.x; + const halfx16_t * xA = (const halfx16_t *) A.x; + const halfx16_t * xB = (const halfx16_t *) B.x; + xD[0] = __builtin_amdgcn_wmma_f16_16x16x16_f16_w32(xA[0], xB[0], xD[0], /*opsel =*/ 0); + xD[0] = __builtin_amdgcn_wmma_f16_16x16x16_f16_w32(xA[1], xB[0], xD[0], /*opsel =*/ 1); +#else + GGML_UNUSED_VARS(D, A, B); + NO_DEVICE_CODE; +#endif // TURING_MMA_AVAILABLE + } + template static __device__ __forceinline__ void mma( tile<16, 16, int, dl_d> & D, const tile<16, 4, int, dl_ab> & A, const tile<16, 4, int, dl_ab> & B) { From 5c0e9468378eba6bf3cc1989ff5d62fbbe4d9e3a Mon Sep 17 00:00:00 2001 From: Pranav Dhinakar Date: Thu, 14 May 2026 16:55:54 -0700 Subject: [PATCH 111/158] ggml-hexagon: cpy: add contiguous fast-path in reshape copy (#23076) --- ggml/src/ggml-hexagon/htp/cpy-ops.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ggml/src/ggml-hexagon/htp/cpy-ops.c b/ggml/src/ggml-hexagon/htp/cpy-ops.c index e5b9d350fd7..5c040a32224 100644 --- a/ggml/src/ggml-hexagon/htp/cpy-ops.c +++ b/ggml/src/ggml-hexagon/htp/cpy-ops.c @@ -88,6 +88,29 @@ static void cpy_thread_sametype_reshape(struct htp_copy_context * ct, struct htp const uint32_t ir0 = dr * ith; const uint32_t ir1 = (ir0 + dr) < nr ? (ir0 + dr) : nr; + // Fast path: when both src0 and dst are contiguous in memory + // Replace the element-by-element loop with a single bulk HVX copy per (i03, i02) slice. + const bool src0_contig = (nb00 == ct->src0_type_size) && + (nb01 == ne00 * nb00) && + (nb02 == ne01 * nb01) && + (nb03 == ne02 * nb02); + const bool dst_contig = (nb0 == ct->dst_type_size) && + (nb1 == ne0 * nb0) && + (nb2 == ne1 * nb1) && + (nb3 == ne2 * nb2); + + if (src0_contig && dst_contig) { + for (int64_t i03 = 0; i03 < ne03; i03++) { + for (int64_t i02 = 0; i02 < ne02; i02++) { + uint8_t * src_ptr = (uint8_t *) src0->data + i03*nb03 + i02*nb02 + ir0*nb01; + uint32_t flat = ((i03*ne02 + i02)*ne01 + ir0) * ne00; + uint8_t * dst_ptr = (uint8_t *) dst->data + flat * ct->src0_type_size; + hvx_copy_uu(dst_ptr, src_ptr, (ir1 - ir0) * ne00, ct->src0_type_size); + } + } + return; + } + // dst counters int64_t k10 = 0; int64_t i11 = 0; From 7155a49771279063a8b9b647d33347811abc64c3 Mon Sep 17 00:00:00 2001 From: KITAITI Makoto Date: Fri, 15 May 2026 14:41:24 +0900 Subject: [PATCH 112/158] readme : update bindings (#23063) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index de93f17eb77..a0c14b9d7f0 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ Instructions for adding support for new models: [HOWTO-add-model.md](docs/develo - JavaScript/Wasm (works in browser): [tangledgroup/llama-cpp-wasm](https://github.com/tangledgroup/llama-cpp-wasm) - Typescript/Wasm (nicer API, available on npm): [ngxson/wllama](https://github.com/ngxson/wllama) - Ruby: [yoshoku/llama_cpp.rb](https://github.com/yoshoku/llama_cpp.rb) +- Ruby: [docusealco/rllama](https://github.com/docusealco/rllama) - Rust (more features): [edgenai/llama_cpp-rs](https://github.com/edgenai/llama_cpp-rs) - Rust (nicer API): [mdrokz/rust-llama.cpp](https://github.com/mdrokz/rust-llama.cpp) - Rust (more direct bindings): [utilityai/llama-cpp-rs](https://github.com/utilityai/llama-cpp-rs) From 91e84fed64329cd96202d68220724a1d92f5ec1f Mon Sep 17 00:00:00 2001 From: Sid Shaytay <2595088+SidShaytay@users.noreply.github.com> Date: Fri, 15 May 2026 00:03:24 -0700 Subject: [PATCH 113/158] Support for Codex CLI by skipping unsupported Responses tools (#23041) * Support for Codex CLI by skipping unsupported Responses tools * Warn on skipped Responses tools and preserve gpt-oss apply_patch rejection * Revert gpt-oss apply_patch special handling --- tests/test-chat.cpp | 77 ++++++++++++++++++++++++++++++++++++ tools/server/server-chat.cpp | 11 ++++-- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp index ea9d87ebed2..05c60d29743 100644 --- a/tests/test-chat.cpp +++ b/tests/test-chat.cpp @@ -1664,6 +1664,83 @@ static void test_convert_responses_to_chatcmpl() { assert_equals(false, result.contains("max_output_tokens")); assert_equals(100, result.at("max_tokens").get()); } + + // Test mixed Responses tools: convert only function tools + { + json input = json::parse(R"({ + "input": "Hello", + "model": "test-model", + "tools": [ + { + "type": "web_search" + }, + { + "type": "function", + "name": "get_weather", + "description": "Get weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string" + } + }, + "required": ["location"] + } + }, + { + "type": "image_generation" + }, + { + "type": "mcp", + "server_label": "test-server" + }, + { + "type": "namespace", + "name": "browser" + } + ] + })"); + + json result = server_chat_convert_responses_to_chatcmpl(input); + + assert_equals(true, result.contains("tools")); + assert_equals(true, result.at("tools").is_array()); + assert_equals((size_t)1, result.at("tools").size()); + + const auto & tool = result.at("tools")[0]; + assert_equals(std::string("function"), tool.at("type").get()); + assert_equals(std::string("get_weather"), tool.at("function").at("name").get()); + assert_equals(true, tool.at("function").at("strict").get()); + } + + // Test non-function Responses tools are ignored + { + json input = json::parse(R"({ + "input": "Hello", + "model": "test-model", + "tools": [ + { + "type": "web_search" + }, + { + "type": "image_generation" + }, + { + "type": "mcp", + "server_label": "test-server" + }, + { + "type": "namespace", + "name": "browser" + } + ] + })"); + + json result = server_chat_convert_responses_to_chatcmpl(input); + + assert_equals(false, result.contains("tools")); + } } static void test_template_output_peg_parsers(bool detailed_debug) { diff --git a/tools/server/server-chat.cpp b/tools/server/server-chat.cpp index f276f8da58f..02858a2a028 100644 --- a/tools/server/server-chat.cpp +++ b/tools/server/server-chat.cpp @@ -257,8 +257,11 @@ json server_chat_convert_responses_to_chatcmpl(const json & response_body) { for (json resp_tool : response_body.at("tools")) { json chatcmpl_tool; - if (json_value(resp_tool, "type", std::string()) != "function") { - throw std::invalid_argument("'type' of tool must be 'function'"); + const std::string type = json_value(resp_tool, "type", std::string()); + if (type != "function") { + // Non-function Responses tools have no Chat Completions equivalent. + SRV_WRN("unsupported Responses tool type '%s' skipped\n", type.c_str()); + continue; } resp_tool.erase("type"); chatcmpl_tool["type"] = "function"; @@ -270,7 +273,9 @@ json server_chat_convert_responses_to_chatcmpl(const json & response_body) { chatcmpl_tools.push_back(chatcmpl_tool); } chatcmpl_body.erase("tools"); - chatcmpl_body["tools"] = chatcmpl_tools; + if (!chatcmpl_tools.empty()) { + chatcmpl_body["tools"] = chatcmpl_tools; + } } if (response_body.contains("max_output_tokens")) { From d5284445802b5af6bff3240329f2558f9c35b5f5 Mon Sep 17 00:00:00 2001 From: Pascal Date: Fri, 15 May 2026 11:18:11 +0200 Subject: [PATCH 114/158] webui: preserve partial response on streaming error (#23090) --- .../server/webui/src/lib/constants/agentic.ts | 3 --- .../webui/src/lib/stores/agentic.svelte.ts | 12 +++------- .../webui/src/lib/stores/chat.svelte.ts | 23 +++++++++++-------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/tools/server/webui/src/lib/constants/agentic.ts b/tools/server/webui/src/lib/constants/agentic.ts index 4fe6da61c38..c0575163efc 100644 --- a/tools/server/webui/src/lib/constants/agentic.ts +++ b/tools/server/webui/src/lib/constants/agentic.ts @@ -4,9 +4,6 @@ export const ATTACHMENT_SAVED_REGEX = /\[Attachment saved: ([^\]]+)\]/; export const NEWLINE_SEPARATOR = '\n'; -export const LLM_ERROR_BLOCK_START = '\n\n```\nUpstream LLM error:\n'; -export const LLM_ERROR_BLOCK_END = '\n```\n'; - export const DEFAULT_AGENTIC_CONFIG: AgenticConfig = { enabled: true, maxTurns: 100, diff --git a/tools/server/webui/src/lib/stores/agentic.svelte.ts b/tools/server/webui/src/lib/stores/agentic.svelte.ts index 3334f6c111a..1f1f05c4539 100644 --- a/tools/server/webui/src/lib/stores/agentic.svelte.ts +++ b/tools/server/webui/src/lib/stores/agentic.svelte.ts @@ -30,12 +30,7 @@ import { ToolSource, ToolPermissionDecision } from '$lib/enums'; import { SvelteMap } from 'svelte/reactivity'; import { ToolsService } from '$lib/services/tools.service'; import { isAbortError } from '$lib/utils'; -import { - DEFAULT_AGENTIC_CONFIG, - NEWLINE_SEPARATOR, - LLM_ERROR_BLOCK_START, - LLM_ERROR_BLOCK_END -} from '$lib/constants'; +import { DEFAULT_AGENTIC_CONFIG, NEWLINE_SEPARATOR } from '$lib/constants'; import { IMAGE_MIME_TO_EXTENSION, DATA_URI_BASE64_REGEX, @@ -640,10 +635,9 @@ class AgenticStore { return; } const normalizedError = error instanceof Error ? error : new Error('LLM stream error'); - // Save error as content in the current turn - onChunk?.(`${LLM_ERROR_BLOCK_START}${normalizedError.message}${LLM_ERROR_BLOCK_END}`); + // preserve partial output as is, the outer error dialog informs the user separately await onAssistantTurnComplete?.( - turnContent + `${LLM_ERROR_BLOCK_START}${normalizedError.message}${LLM_ERROR_BLOCK_END}`, + turnContent, turnReasoningContent || undefined, this.buildFinalTimings(capturedTimings, agenticTimings), undefined diff --git a/tools/server/webui/src/lib/stores/chat.svelte.ts b/tools/server/webui/src/lib/stores/chat.svelte.ts index 7c34579ca56..04a735eec91 100644 --- a/tools/server/webui/src/lib/stores/chat.svelte.ts +++ b/tools/server/webui/src/lib/stores/chat.svelte.ts @@ -814,7 +814,7 @@ class ChatStore { ); } }, - onError: (error: Error) => { + onError: async (error: Error) => { this.setStreamingActive(false); if (isAbortError(error)) { cleanupStreamingState(); @@ -826,13 +826,10 @@ class ChatStore { return; } console.error('Streaming error:', error); + // keep whatever was streamed so far, the message stays in memory and in DB + await this.savePartialResponseIfNeeded(convId); cleanupStreamingState(); this.clearPendingMessage(convId); - const idx = conversationsStore.findMessageIndex(assistantMessage.id); - if (idx !== -1) { - const failedMessage = conversationsStore.removeMessageAtIndex(idx); - if (failedMessage) DatabaseService.deleteMessage(failedMessage.id).catch(console.error); - } const contextInfo = ( error as Error & { contextInfo?: { n_prompt_tokens: number; n_ctx: number } } ).contextInfo; @@ -1389,9 +1386,17 @@ class ChatStore { } console.error('Continue generation error:', error); - conversationsStore.updateMessageAtIndex(idx, { content: originalContent }); - - await DatabaseService.updateMessage(msg.id, { content: originalContent }); + // keep whatever was appended so far, the message stays in memory and in DB + await DatabaseService.updateMessage(msg.id, { + content: originalContent + appendedContent, + reasoningContent: originalReasoning + appendedReasoning || undefined, + timestamp: Date.now() + }); + conversationsStore.updateMessageAtIndex(idx, { + content: originalContent + appendedContent, + reasoningContent: originalReasoning + appendedReasoning || undefined, + timestamp: Date.now() + }); this.setChatLoading(msg.convId, false); this.clearChatStreaming(msg.convId); From ac33f032ac41748ffa9aaefca07cfc16e732f73f Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Fri, 15 May 2026 17:59:07 +0800 Subject: [PATCH 115/158] reasoning-budget: clone should do a deep-copy (#23095) --- common/reasoning-budget.cpp | 21 +++++------ tests/test-reasoning-budget.cpp | 65 ++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/common/reasoning-budget.cpp b/common/reasoning-budget.cpp index c6e1f86c91e..958c9cacf51 100644 --- a/common/reasoning-budget.cpp +++ b/common/reasoning-budget.cpp @@ -171,22 +171,12 @@ static void common_reasoning_budget_reset(struct llama_sampler * smpl) { ctx->force_pos = 0; } -// forward declaration for use in clone static struct llama_sampler * common_reasoning_budget_init_state( const struct llama_vocab * vocab, const std::vector & start_tokens, const std::vector & end_tokens, const std::vector & forced_tokens, int32_t budget, common_reasoning_budget_state initial_state); -static struct llama_sampler * common_reasoning_budget_clone(const struct llama_sampler * smpl) { - const auto * ctx = (const common_reasoning_budget_ctx *) smpl->ctx; - return common_reasoning_budget_init_state( - ctx->vocab, - ctx->start_matcher.tokens, - ctx->end_matcher.tokens, - ctx->forced_tokens, - ctx->budget, - ctx->state); -} +static struct llama_sampler * common_reasoning_budget_clone(const struct llama_sampler * smpl); static void common_reasoning_budget_free(struct llama_sampler * smpl) { delete (common_reasoning_budget_ctx *) smpl->ctx; @@ -205,6 +195,15 @@ static struct llama_sampler_i common_reasoning_budget_i = { /* .backend_set_input = */ nullptr, }; +static struct llama_sampler * common_reasoning_budget_clone(const struct llama_sampler * smpl) { + const auto * ctx = (const common_reasoning_budget_ctx *) smpl->ctx; + + return llama_sampler_init( + /* .iface = */ &common_reasoning_budget_i, + /* .ctx = */ new common_reasoning_budget_ctx(*ctx) + ); +} + static struct llama_sampler * common_reasoning_budget_init_state( const struct llama_vocab * vocab, const std::vector & start_tokens, diff --git a/tests/test-reasoning-budget.cpp b/tests/test-reasoning-budget.cpp index f7a60178996..10d0ac21da8 100644 --- a/tests/test-reasoning-budget.cpp +++ b/tests/test-reasoning-budget.cpp @@ -124,6 +124,66 @@ static void test_reasoning_budget( (void)sequence; } +static llama_token get_forced_token(struct llama_sampler * sampler, llama_token max_token) { + std::vector cur; + const size_t n_vocab = (size_t) max_token + 1; + for (size_t i = 0; i < n_vocab; i++) { + cur.emplace_back(llama_token_data{(llama_token) i, logf((float) (i + 1)), 0.0f}); + } + + llama_token_data_array cur_p = { cur.data(), cur.size(), -1, false }; + llama_sampler_apply(sampler, &cur_p); + + size_t finite_count = 0; + llama_token finite_token = LLAMA_TOKEN_NULL; + for (size_t i = 0; i < cur.size(); i++) { + if (std::isfinite(cur[i].logit)) { + finite_count++; + finite_token = cur[i].id; + } + } + + GGML_ASSERT(finite_count == 1 && "sampler is not forcing exactly one token"); + return finite_token; +} + +static void test_reasoning_budget_clone_mid_counting() { + const std::vector start = {100}; + const std::vector end = {101}; + const std::vector forced = {102, 101}; + + auto * sampler = common_reasoning_budget_init(nullptr, start, end, forced, 2, REASONING_BUDGET_IDLE); + + llama_sampler_accept(sampler, 100); // COUNTING, remaining=2 + llama_sampler_accept(sampler, 50); // COUNTING, remaining=1 + + auto * clone = llama_sampler_clone(sampler); + llama_sampler_accept(clone, 51); // should exhaust the cloned remaining budget + + GGML_ASSERT(get_forced_token(clone, 102) == 102 && "cloned counting state lost remaining budget"); + + llama_sampler_free(clone); + llama_sampler_free(sampler); +} + +static void test_reasoning_budget_clone_mid_forcing() { + const std::vector start = {100}; + const std::vector end = {101}; + const std::vector forced = {102, 101}; + + auto * sampler = common_reasoning_budget_init(nullptr, start, end, forced, 0, REASONING_BUDGET_FORCING); + + GGML_ASSERT(get_forced_token(sampler, 102) == 102); + llama_sampler_accept(sampler, 102); // advance to the second forced token + + auto * clone = llama_sampler_clone(sampler); + + GGML_ASSERT(get_forced_token(clone, 102) == 101 && "cloned forcing state lost force position"); + + llama_sampler_free(clone); + llama_sampler_free(sampler); +} + // UTF-8 boundary detection unit test // Tests common_utf8_is_complete() from reasoning-budget.h static void test_utf8_boundary_detection() { @@ -250,7 +310,10 @@ int main(void) { 7); // forcing continues through i=7 } - printf("OK (6 tests passed)\n"); + test_reasoning_budget_clone_mid_counting(); + test_reasoning_budget_clone_mid_forcing(); + + printf("OK (8 tests passed)\n"); printf("Testing UTF-8 boundary detection... "); test_utf8_boundary_detection(); From d5dc2e0a0275bf51a43074920f03b08c0fa906de Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Fri, 15 May 2026 13:58:30 +0300 Subject: [PATCH 116/158] llama-eval : add AIME 2026 dataset support (#23058) Add Aime2026Dataset class loading from MathArena/aime_2026 on HuggingFace. 30 problems (two sets of 15), single config/split. Usage: --dataset aime2026 Assisted-by: llama.cpp:local pi --- examples/llama-eval/llama-eval.py | 57 ++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/examples/llama-eval/llama-eval.py b/examples/llama-eval/llama-eval.py index a2948273512..e833070eee9 100755 --- a/examples/llama-eval/llama-eval.py +++ b/examples/llama-eval/llama-eval.py @@ -44,6 +44,7 @@ def wilson_interval(correct: int, total: int, z: float = 1.96) -> Tuple[float, f GRADER_PATTERNS = { "aime": r'\boxed{(\d+)}|\b(\d+)\b', "aime2025": r'\boxed{(\d+)}|\b(\d+)\b', + "aime2026": r'\boxed{(\d+)}|\b(\d+)\b', "gsm8k": r'\b(\d+)\b', } @@ -58,6 +59,11 @@ def wilson_interval(correct: int, total: int, z: float = 1.96) -> Tuple[float, f "-123", "999" ], + "aime2026": [ + "42", + "-123", + "999" + ], "gsm8k": [ "42", "-123", @@ -81,6 +87,12 @@ def wilson_interval(correct: int, total: int, z: float = 1.96) -> Tuple[float, f {question} +Remember to put your answer inside \\boxed{{}}. +""", + "aime2026": """Solve the following math problem step by step. Put your answer inside \\boxed{{}}. + +{question} + Remember to put your answer inside \\boxed{{}}. """, "gsm8k": """{question} @@ -166,6 +178,8 @@ def load_dataset(self, seed: int = 1234): self.dataset = AimeDataset() elif self.dataset_type == "aime2025": self.dataset = Aime2025Dataset() + elif self.dataset_type == "aime2026": + self.dataset = Aime2026Dataset() elif self.dataset_type == "gsm8k": self.dataset = Gsm8kDataset() elif self.dataset_type == "gpqa": @@ -679,6 +693,47 @@ def get_prompt(self, question: Dict) -> str: question=self.get_question_text(question), ) +class Aime2026Dataset(BaseDataset): + def __init__(self): + self.questions = [] + self._load_dataset() + + def _load_dataset(self): + print(f"Loading AIME2026 dataset...") + from datasets import load_dataset + + cache_path = cache_dir / "MathArena___aime_2026" / "default" / "0.0.0" + if cache_path.exists(): + print(f"Using cached dataset from {cache_path}") + ds = load_dataset("MathArena/aime_2026", "default", split="train", cache_dir=str(cache_path)) + else: + ds = load_dataset("MathArena/aime_2026", "default", split="train") + + self.questions = [] + for row in ds: + question = dict(row) + question["dataset_type"] = "aime2026" + self.questions.append(question) + + print(f"AIME2026 dataset loaded: {len(self.questions)} questions") + + def get_question(self, index: int) -> Dict: + """Get question by index""" + return self.questions[index] + + def get_question_text(self, question: Dict) -> str: + """Get question string""" + return question["problem"] + + def get_answer(self, question: Dict) -> str: + return str(question["answer"]) + + def get_prompt(self, question: Dict) -> str: + """Get formatted prompt for the question""" + return TEMPLATE_REGISTRY["aime2026"].format( + question=self.get_question_text(question), + ) + class Gsm8kDataset(BaseDataset): def __init__(self, split: str = "test"): self.split = split @@ -1188,7 +1243,7 @@ def main(): "--dataset", type=str, default="aime", - choices=["aime", "aime2025", "gsm8k", "gpqa"], + choices=["aime", "aime2025", "aime2026", "gsm8k", "gpqa"], help="Dataset type (default: aime)" ) parser.add_argument( From 769cc93a43b51bf6013986180c73ee60cf24cede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigbj=C3=B8rn=20Skj=C3=A6ret?= Date: Fri, 15 May 2026 13:13:16 +0200 Subject: [PATCH 117/158] ci : fix transform of top . entry in release archive (#23080) * fix transform of top . entry in release archive * simplify --- .github/workflows/release.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b01798ae78..218d24296be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,7 +104,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-macos-${{ matrix.build }}.tar.gz -s ",./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-macos-${{ matrix.build }}.tar.gz -s ",^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -182,7 +182,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-${{ matrix.build }}.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -259,7 +259,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-${{ matrix.build }}.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -337,7 +337,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-android-arm64.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-android-arm64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -426,7 +426,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/ReleaseOV/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-${{ env.OPENVINO_VERSION_MAJOR }}-x64.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/ReleaseOV/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-${{ env.OPENVINO_VERSION_MAJOR }}-x64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/ReleaseOV/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -867,7 +867,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -979,7 +979,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -1240,7 +1240,7 @@ jobs: - name: Pack artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-${{ matrix.chip_type }}-openEuler-${{ matrix.arch }}${{ matrix.use_acl_graph == 'on' && '-aclgraph' || '' }}.tar.gz --transform "s,./,llama-${{ steps.tag.outputs.name }}/," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-${{ matrix.chip_type }}-openEuler-${{ matrix.arch }}${{ matrix.use_acl_graph == 'on' && '-aclgraph' || '' }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 From cc7200bf12eac4f5c9ec5377c16ae75b332f8e0c Mon Sep 17 00:00:00 2001 From: "Piotr Wilkin (ilintar)" Date: Fri, 15 May 2026 15:18:12 +0200 Subject: [PATCH 118/158] Refactor: convert_hf_to_gguf.py (#17114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move conversion code to a dedicated conversion directory and split the files akin to the src/models architecture --------- Co-authored-by: Sigbjørn Skjæret --- conversion/__init__.py | 333 + conversion/afmoe.py | 79 + conversion/arctic.py | 162 + conversion/baichuan.py | 59 + conversion/bailingmoe.py | 216 + conversion/base.py | 2468 ++++++ conversion/bert.py | 616 ++ conversion/bitnet.py | 49 + conversion/bloom.py | 67 + conversion/chameleon.py | 58 + conversion/chatglm.py | 167 + conversion/codeshell.py | 21 + conversion/cogvlm.py | 33 + conversion/command_r.py | 57 + conversion/dbrx.py | 75 + conversion/deci.py | 184 + conversion/deepseek.py | 388 + conversion/dots1.py | 32 + conversion/dotsocr.py | 48 + conversion/dream.py | 72 + conversion/ernie.py | 200 + conversion/exaone.py | 210 + conversion/falcon.py | 58 + conversion/falcon_h1.py | 118 + conversion/gemma.py | 840 ++ conversion/glm.py | 259 + conversion/gpt2.py | 78 + conversion/gpt_oss.py | 130 + conversion/gptneox.py | 63 + conversion/granite.py | 328 + conversion/grok.py | 116 + conversion/grovemoe.py | 108 + conversion/hunyuan.py | 407 + conversion/internlm.py | 232 + conversion/internvl.py | 98 + conversion/jais.py | 104 + conversion/jamba.py | 119 + conversion/januspro.py | 116 + conversion/kimi_linear.py | 223 + conversion/kimivl.py | 154 + conversion/lfm2.py | 256 + conversion/lighton_ocr.py | 29 + conversion/llada.py | 172 + conversion/llama.py | 312 + conversion/llama4.py | 38 + conversion/llava.py | 129 + conversion/maincoder.py | 14 + conversion/mamba.py | 199 + conversion/mimo.py | 295 + conversion/minicpm.py | 184 + conversion/minimax.py | 54 + conversion/mistral.py | 201 + conversion/mistral3.py | 67 + conversion/mpt.py | 49 + conversion/nemotron.py | 384 + conversion/olmo.py | 120 + conversion/openelm.py | 83 + conversion/orion.py | 37 + conversion/pangu.py | 46 + conversion/phi.py | 390 + conversion/pixtral.py | 41 + conversion/plamo.py | 195 + conversion/plm.py | 23 + conversion/qwen.py | 544 ++ conversion/qwen3vl.py | 357 + conversion/qwenvl.py | 200 + conversion/refact.py | 68 + conversion/rwkv.py | 302 + conversion/sarashina2.py | 32 + conversion/smallthinker.py | 82 + conversion/smolvlm.py | 47 + conversion/stablelm.py | 98 + conversion/starcoder.py | 23 + conversion/step3.py | 231 + conversion/t5.py | 286 + conversion/ultravox.py | 203 + conversion/wavtokenizer.py | 45 + conversion/xverse.py | 90 + conversion/youtuvl.py | 64 + convert_hf_to_gguf.py | 14054 +-------------------------------- convert_hf_to_gguf_update.py | 4 +- convert_lora_to_gguf.py | 9 +- 82 files changed, 15179 insertions(+), 14023 deletions(-) create mode 100644 conversion/__init__.py create mode 100644 conversion/afmoe.py create mode 100644 conversion/arctic.py create mode 100644 conversion/baichuan.py create mode 100644 conversion/bailingmoe.py create mode 100644 conversion/base.py create mode 100644 conversion/bert.py create mode 100644 conversion/bitnet.py create mode 100644 conversion/bloom.py create mode 100644 conversion/chameleon.py create mode 100644 conversion/chatglm.py create mode 100644 conversion/codeshell.py create mode 100644 conversion/cogvlm.py create mode 100644 conversion/command_r.py create mode 100644 conversion/dbrx.py create mode 100644 conversion/deci.py create mode 100644 conversion/deepseek.py create mode 100644 conversion/dots1.py create mode 100644 conversion/dotsocr.py create mode 100644 conversion/dream.py create mode 100644 conversion/ernie.py create mode 100644 conversion/exaone.py create mode 100644 conversion/falcon.py create mode 100644 conversion/falcon_h1.py create mode 100644 conversion/gemma.py create mode 100644 conversion/glm.py create mode 100644 conversion/gpt2.py create mode 100644 conversion/gpt_oss.py create mode 100644 conversion/gptneox.py create mode 100644 conversion/granite.py create mode 100644 conversion/grok.py create mode 100644 conversion/grovemoe.py create mode 100644 conversion/hunyuan.py create mode 100644 conversion/internlm.py create mode 100644 conversion/internvl.py create mode 100644 conversion/jais.py create mode 100644 conversion/jamba.py create mode 100644 conversion/januspro.py create mode 100644 conversion/kimi_linear.py create mode 100644 conversion/kimivl.py create mode 100644 conversion/lfm2.py create mode 100644 conversion/lighton_ocr.py create mode 100644 conversion/llada.py create mode 100644 conversion/llama.py create mode 100644 conversion/llama4.py create mode 100644 conversion/llava.py create mode 100644 conversion/maincoder.py create mode 100644 conversion/mamba.py create mode 100644 conversion/mimo.py create mode 100644 conversion/minicpm.py create mode 100644 conversion/minimax.py create mode 100644 conversion/mistral.py create mode 100644 conversion/mistral3.py create mode 100644 conversion/mpt.py create mode 100644 conversion/nemotron.py create mode 100644 conversion/olmo.py create mode 100644 conversion/openelm.py create mode 100644 conversion/orion.py create mode 100644 conversion/pangu.py create mode 100644 conversion/phi.py create mode 100644 conversion/pixtral.py create mode 100644 conversion/plamo.py create mode 100644 conversion/plm.py create mode 100644 conversion/qwen.py create mode 100644 conversion/qwen3vl.py create mode 100644 conversion/qwenvl.py create mode 100644 conversion/refact.py create mode 100644 conversion/rwkv.py create mode 100644 conversion/sarashina2.py create mode 100644 conversion/smallthinker.py create mode 100644 conversion/smolvlm.py create mode 100644 conversion/stablelm.py create mode 100644 conversion/starcoder.py create mode 100644 conversion/step3.py create mode 100644 conversion/t5.py create mode 100644 conversion/ultravox.py create mode 100644 conversion/wavtokenizer.py create mode 100644 conversion/xverse.py create mode 100644 conversion/youtuvl.py diff --git a/conversion/__init__.py b/conversion/__init__.py new file mode 100644 index 00000000000..2c38123dff8 --- /dev/null +++ b/conversion/__init__.py @@ -0,0 +1,333 @@ +from __future__ import annotations + +from .base import ( + ModelBase, TextModel, MmprojModel, ModelType, SentencePieceTokenTypes, + logger, _mistral_common_installed, _mistral_import_error_msg, + get_model_architecture, LazyTorchTensor, +) +from typing import Type + + +__all__ = [ + "ModelBase", "TextModel", "MmprojModel", "ModelType", "SentencePieceTokenTypes", + "get_model_architecture", "LazyTorchTensor", "logger", + "_mistral_common_installed", "_mistral_import_error_msg", + "get_model_class", "print_registered_models", "load_all_models", +] + + +TEXT_MODEL_MAP: dict[str, str] = { + "AfmoeForCausalLM": "afmoe", + "ApertusForCausalLM": "llama", + "ArceeForCausalLM": "llama", + "ArcticForCausalLM": "arctic", + "AudioFlamingo3ForConditionalGeneration": "qwen", + "BaiChuanForCausalLM": "baichuan", + "BaichuanForCausalLM": "baichuan", + "BailingMoeForCausalLM": "bailingmoe", + "BailingMoeV2ForCausalLM": "bailingmoe", + "BambaForCausalLM": "granite", + "BertForMaskedLM": "bert", + "BertForSequenceClassification": "bert", + "BertModel": "bert", + "BitnetForCausalLM": "bitnet", + "BloomForCausalLM": "bloom", + "BloomModel": "bloom", + "CamembertModel": "bert", + "ChameleonForCausalLM": "chameleon", + "ChameleonForConditionalGeneration": "chameleon", + "ChatGLMForConditionalGeneration": "chatglm", + "ChatGLMModel": "chatglm", + "CodeShellForCausalLM": "codeshell", + "CogVLMForCausalLM": "cogvlm", + "Cohere2ForCausalLM": "command_r", + "CohereForCausalLM": "command_r", + "DbrxForCausalLM": "dbrx", + "DeciLMForCausalLM": "deci", + "DeepseekForCausalLM": "deepseek", + "DeepseekV2ForCausalLM": "deepseek", + "DeepseekV3ForCausalLM": "deepseek", + "DistilBertForMaskedLM": "bert", + "DistilBertForSequenceClassification": "bert", + "DistilBertModel": "bert", + "Dots1ForCausalLM": "dots1", + "DotsOCRForCausalLM": "qwen", + "DreamModel": "dream", + "Ernie4_5ForCausalLM": "ernie", + "Ernie4_5_ForCausalLM": "ernie", + "Ernie4_5_MoeForCausalLM": "ernie", + "EuroBertModel": "bert", + "Exaone4ForCausalLM": "exaone", + "ExaoneForCausalLM": "exaone", + "ExaoneMoEForCausalLM": "exaone", + "FalconForCausalLM": "falcon", + "FalconH1ForCausalLM": "falcon_h1", + "FalconMambaForCausalLM": "mamba", + "GPT2LMHeadModel": "gpt2", + "GPTBigCodeForCausalLM": "starcoder", + "GPTNeoXForCausalLM": "gptneox", + "GPTRefactForCausalLM": "refact", + "Gemma2ForCausalLM": "gemma", + "Gemma3ForCausalLM": "gemma", + "Gemma3ForConditionalGeneration": "gemma", + "Gemma3TextModel": "gemma", + "Gemma3nForCausalLM": "gemma", + "Gemma3nForConditionalGeneration": "gemma", + "Gemma4ForConditionalGeneration": "gemma", + "GemmaForCausalLM": "gemma", + "Glm4ForCausalLM": "glm", + "Glm4MoeForCausalLM": "glm", + "Glm4MoeLiteForCausalLM": "glm", + "Glm4vForConditionalGeneration": "glm", + "Glm4vMoeForConditionalGeneration": "glm", + "GlmForCausalLM": "chatglm", + "GlmMoeDsaForCausalLM": "glm", + "GlmOcrForConditionalGeneration": "glm", + "GptOssForCausalLM": "gpt_oss", + "GraniteForCausalLM": "granite", + "GraniteMoeForCausalLM": "granite", + "GraniteMoeHybridForCausalLM": "granite", + "GraniteMoeSharedForCausalLM": "granite", + "GraniteSpeechForConditionalGeneration": "granite", + "Grok1ForCausalLM": "grok", + "GrokForCausalLM": "grok", + "GroveMoeForCausalLM": "grovemoe", + "HunYuanDenseV1ForCausalLM": "hunyuan", + "HunYuanMoEV1ForCausalLM": "hunyuan", + "HunYuanVLForConditionalGeneration": "hunyuan", + "IQuestCoderForCausalLM": "llama", + "InternLM2ForCausalLM": "internlm", + "InternLM3ForCausalLM": "internlm", + "JAISLMHeadModel": "jais", + "Jais2ForCausalLM": "jais", + "JambaForCausalLM": "jamba", + "JanusForConditionalGeneration": "januspro", + "JinaBertForMaskedLM": "bert", + "JinaBertModel": "bert", + "JinaEmbeddingsV5Model": "bert", + "KORMoForCausalLM": "qwen", + "KimiK25ForConditionalGeneration": "deepseek", + "KimiLinearForCausalLM": "kimi_linear", + "KimiLinearModel": "kimi_linear", + "KimiVLForConditionalGeneration": "deepseek", + "LFM2ForCausalLM": "lfm2", + "LLaDAMoEModel": "llada", + "LLaDAMoEModelLM": "llada", + "LLaDAModelLM": "llada", + "LLaMAForCausalLM": "llama", + "Lfm25AudioTokenizer": "lfm2", + "Lfm2ForCausalLM": "lfm2", + "Lfm2Model": "lfm2", + "Lfm2MoeForCausalLM": "lfm2", + "Llama4ForCausalLM": "llama", + "Llama4ForConditionalGeneration": "llama", + "LlamaBidirectionalModel": "llama", + "LlamaForCausalLM": "llama", + "LlamaModel": "llama", + "LlavaForConditionalGeneration": "llama", + "LlavaStableLMEpochForCausalLM": "stablelm", + "MPTForCausalLM": "mpt", + "MT5ForConditionalGeneration": "t5", + "MaincoderForCausalLM": "maincoder", + "Mamba2ForCausalLM": "mamba", + "MambaForCausalLM": "mamba", + "MambaLMHeadModel": "mamba", + "MiMoV2FlashForCausalLM": "mimo", + "MiMoV2ForCausalLM": "mimo", + "MiniCPM3ForCausalLM": "minicpm", + "MiniCPMForCausalLM": "minicpm", + "MiniCPMV4_6ForConditionalGeneration": "minicpm", + "MiniMaxM2ForCausalLM": "minimax", + "Ministral3ForCausalLM": "mistral3", + "Mistral3ForConditionalGeneration": "mistral3", + "MistralForCausalLM": "llama", + "MixtralForCausalLM": "llama", + "ModernBertForMaskedLM": "bert", + "ModernBertForSequenceClassification": "bert", + "ModernBertModel": "bert", + "NemotronForCausalLM": "nemotron", + "NemotronHForCausalLM": "nemotron", + "NeoBERT": "bert", + "NeoBERTForSequenceClassification": "bert", + "NeoBERTLMHead": "bert", + "NomicBertModel": "bert", + "OLMoForCausalLM": "olmo", + "Olmo2ForCausalLM": "olmo", + "Olmo3ForCausalLM": "olmo", + "OlmoForCausalLM": "olmo", + "OlmoeForCausalLM": "olmo", + "OpenELMForCausalLM": "openelm", + "OrionForCausalLM": "orion", + "PLMForCausalLM": "plm", + "PLaMo2ForCausalLM": "plamo", + "PLaMo3ForCausalLM": "plamo", + "PaddleOCRVLForConditionalGeneration": "ernie", + "PanguEmbeddedForCausalLM": "pangu", + "Phi3ForCausalLM": "phi", + "Phi4ForCausalLMV": "phi", + "PhiForCausalLM": "phi", + "PhiMoEForCausalLM": "phi", + "Plamo2ForCausalLM": "plamo", + "Plamo3ForCausalLM": "plamo", + "PlamoForCausalLM": "plamo", + "QWenLMHeadModel": "qwen", + "Qwen2AudioForConditionalGeneration": "qwen", + "Qwen2ForCausalLM": "qwen", + "Qwen2Model": "qwen", + "Qwen2MoeForCausalLM": "qwen", + "Qwen2VLForConditionalGeneration": "qwenvl", + "Qwen2VLModel": "qwenvl", + "Qwen2_5OmniModel": "qwenvl", + "Qwen2_5_VLForConditionalGeneration": "qwenvl", + "Qwen3ASRForConditionalGeneration": "qwen3vl", + "Qwen3ForCausalLM": "qwen", + "Qwen3Model": "qwen", + "Qwen3MoeForCausalLM": "qwen", + "Qwen3NextForCausalLM": "qwen", + "Qwen3OmniMoeForConditionalGeneration": "qwen3vl", + "Qwen3VLForConditionalGeneration": "qwen3vl", + "Qwen3VLMoeForConditionalGeneration": "qwen3vl", + "Qwen3_5ForCausalLM": "qwen", + "Qwen3_5ForConditionalGeneration": "qwen", + "Qwen3_5MoeForCausalLM": "qwen", + "Qwen3_5MoeForConditionalGeneration": "qwen", + "RND1": "qwen", + "RWForCausalLM": "falcon", + "RWKV6Qwen2ForCausalLM": "rwkv", + "RWKV7ForCausalLM": "rwkv", + "RobertaForSequenceClassification": "bert", + "RobertaModel": "bert", + "RuGPT3XLForCausalLM": "gpt2", + "Rwkv6ForCausalLM": "rwkv", + "Rwkv7ForCausalLM": "rwkv", + "RwkvHybridForCausalLM": "rwkv", + "Sarashina2VisionForCausalLM": "sarashina2", + "SarvamMoEForCausalLM": "bailingmoe", + "SeedOssForCausalLM": "olmo", + "SmallThinkerForCausalLM": "smallthinker", + "SmolLM3ForCausalLM": "llama", + "SolarOpenForCausalLM": "glm", + "StableLMEpochForCausalLM": "stablelm", + "StableLmForCausalLM": "stablelm", + "Starcoder2ForCausalLM": "starcoder", + "Step3p5ForCausalLM": "step3", + "StepVLForConditionalGeneration": "step3", + "T5EncoderModel": "t5", + "T5ForConditionalGeneration": "t5", + "T5WithLMHeadModel": "t5", + "UMT5ForConditionalGeneration": "t5", + "UMT5Model": "t5", + "UltravoxModel": "ultravox", + "VLlama3ForCausalLM": "llama", + "VoxtralForConditionalGeneration": "llama", + "WavTokenizerDec": "wavtokenizer", + "XLMRobertaForSequenceClassification": "bert", + "XLMRobertaModel": "bert", + "XverseForCausalLM": "xverse", + "YoutuForCausalLM": "deepseek", + "YoutuVLForConditionalGeneration": "deepseek", + "modeling_grove_moe.GroveMoeForCausalLM": "grovemoe", + "modeling_sarvam_moe.SarvamMoEForCausalLM": "bailingmoe", +} + + +MMPROJ_MODEL_MAP: dict[str, str] = { + "AudioFlamingo3ForConditionalGeneration": "ultravox", + "CogVLMForCausalLM": "cogvlm", + "DeepseekOCRForCausalLM": "deepseek", + "DotsOCRForCausalLM": "dotsocr", + "Gemma3ForConditionalGeneration": "gemma", + "Gemma3nForConditionalGeneration": "gemma", + "Gemma4ForConditionalGeneration": "gemma", + "Glm4vForConditionalGeneration": "qwen3vl", + "Glm4vMoeForConditionalGeneration": "qwen3vl", + "GlmOcrForConditionalGeneration": "qwen3vl", + "GlmasrModel": "ultravox", + "GraniteSpeechForConditionalGeneration": "granite", + "HunYuanVLForConditionalGeneration": "hunyuan", + "Idefics3ForConditionalGeneration": "smolvlm", + "InternVisionModel": "internvl", + "JanusForConditionalGeneration": "januspro", + "KimiK25ForConditionalGeneration": "kimivl", + "KimiVLForConditionalGeneration": "kimivl", + "Lfm2AudioForConditionalGeneration": "lfm2", + "Lfm2VlForConditionalGeneration": "lfm2", + "LightOnOCRForConditionalGeneration": "lighton_ocr", + "Llama4ForConditionalGeneration": "llama4", + "LlavaForConditionalGeneration": "llava", + "MERaLiON2ForConditionalGeneration": "ultravox", + "MiMoV2ForCausalLM": "mimo", + "MiniCPMV4_6ForConditionalGeneration": "minicpm", + "Mistral3ForConditionalGeneration": "llava", + "NemotronH_Nano_VL_V2": "nemotron", + "PaddleOCRVisionModel": "ernie", + "Phi4ForCausalLMV": "phi", + "Qwen2AudioForConditionalGeneration": "ultravox", + "Qwen2VLForConditionalGeneration": "qwenvl", + "Qwen2VLModel": "qwenvl", + "Qwen2_5OmniModel": "qwenvl", + "Qwen2_5_VLForConditionalGeneration": "qwenvl", + "Qwen3ASRForConditionalGeneration": "qwen3vl", + "Qwen3OmniMoeForConditionalGeneration": "qwen3vl", + "Qwen3VLForConditionalGeneration": "qwen3vl", + "Qwen3VLMoeForConditionalGeneration": "qwen3vl", + "Qwen3_5ForConditionalGeneration": "qwen3vl", + "Qwen3_5MoeForConditionalGeneration": "qwen3vl", + "RADIOModel": "nemotron", + "Sarashina2VisionForCausalLM": "sarashina2", + "SmolVLMForConditionalGeneration": "smolvlm", + "StepVLForConditionalGeneration": "step3", + "UltravoxModel": "ultravox", + "VoxtralForConditionalGeneration": "ultravox", + "YoutuVLForConditionalGeneration": "youtuvl", +} + + +_TEXT_MODEL_MODULES = sorted(set(TEXT_MODEL_MAP.values())) +_MMPROJ_MODEL_MODULES = sorted(set(MMPROJ_MODEL_MAP.values())) + + +_loaded_text_modules: set[str] = set() +_loaded_mmproj_modules: set[str] = set() + + +def load_all_models() -> None: + """Import all model modules to trigger @ModelBase.register() decorators.""" + if len(_loaded_text_modules) != len(_TEXT_MODEL_MODULES): + for module_name in _TEXT_MODEL_MODULES: + if module_name not in _loaded_text_modules: + try: + __import__(f"conversion.{module_name}") + _loaded_text_modules.add(module_name) + except Exception as e: + logger.warning(f"Failed to load model module {module_name}: {e}") + + if len(_loaded_mmproj_modules) != len(_MMPROJ_MODEL_MODULES): + for module_name in _MMPROJ_MODEL_MODULES: + if module_name not in _loaded_mmproj_modules: + try: + __import__(f"conversion.{module_name}") + _loaded_mmproj_modules.add(module_name) + except Exception as e: + logger.warning(f"Failed to load model module {module_name}: {e}") + + +def get_model_class(name: str, mmproj: bool = False) -> Type[ModelBase]: + """Dynamically import and return a model class by its HuggingFace architecture name.""" + relevant_map = MMPROJ_MODEL_MAP if mmproj else TEXT_MODEL_MAP + if name not in relevant_map: + raise NotImplementedError(f"Architecture {name!r} not supported!") + module_name = relevant_map[name] + __import__(f"conversion.{module_name}") + model_type = ModelType.MMPROJ if mmproj else ModelType.TEXT + return ModelBase._model_classes[model_type][name] + + +def print_registered_models() -> None: + load_all_models() + logger.error("TEXT models:") + for name in sorted(TEXT_MODEL_MAP.keys()): + logger.error(f" - {name}") + logger.error("MMPROJ models:") + for name in sorted(MMPROJ_MODEL_MAP.keys()): + logger.error(f" - {name}") diff --git a/conversion/afmoe.py b/conversion/afmoe.py new file mode 100644 index 00000000000..5e66a51da61 --- /dev/null +++ b/conversion/afmoe.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, gguf + +from .llama import LlamaModel + + +@ModelBase.register("AfmoeForCausalLM") +class AfmoeModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.AFMOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # MoE parameters + if (n_shared_experts := self.hparams.get("num_shared_experts")) is not None: + self.gguf_writer.add_expert_shared_count(n_shared_experts) + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + if (n_dense_layers := self.hparams.get("num_dense_layers")) is not None: + self.gguf_writer.add_leading_dense_block_count(n_dense_layers) + + # Route normalization and scaling + if (route_norm := self.hparams.get("route_norm")) is not None: + self.gguf_writer.add_expert_weights_norm(route_norm) + if (route_scale := self.hparams.get("route_scale")) is not None: + self.gguf_writer.add_expert_weights_scale(route_scale) + + # Sliding window attention + if (sliding_window := self.hparams.get("sliding_window")) is not None: + self.gguf_writer.add_sliding_window(sliding_window) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith(".expert_bias"): + name = name.replace(".expert_bias", ".expert_bias.bias") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Handle expert weights - they're already merged in the HF format + # process the experts separately + if name.find("mlp.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["gate_proj", "up_proj", "down_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename_to_retrieve]) + del self._experts[bid][ename_to_retrieve] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + yield from ModelBase.modify_tensors(self, data_torch, merged_name, bid) + + return + else: + return + + yield from ModelBase.modify_tensors(self, data_torch, name, bid) diff --git a/conversion/arctic.py b/conversion/arctic.py new file mode 100644 index 00000000000..775cacaab9f --- /dev/null +++ b/conversion/arctic.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +import json +import sys + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, SentencePieceTokenTypes, TextModel, gguf, logger + +from .llama import LlamaModel + + +@ModelBase.register("ArcticForCausalLM") +class ArcticModel(TextModel): + model_arch = gguf.MODEL_ARCH.ARCTIC + + def set_vocab(self): + # The reason for using a custom implementation here is that the + # snowflake-arctic-instruct model redefined tokens 31998 and 31999 from + # tokenizer.model and used them as BOS and EOS instead of adding new tokens. + from sentencepiece import SentencePieceProcessor + + tokenizer_path = self.dir_model / 'tokenizer.model' + + if not tokenizer_path.is_file(): + logger.error(f'Error: Missing {tokenizer_path}') + sys.exit(1) + + # Read the whole vocabulary from the tokenizer.model file + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + for token_id in range(tokenizer.vocab_size()): + + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + # Use the added_tokens_decoder field from tokeniser_config.json as the source + # of information about added/redefined tokens and modify them accordingly. + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + + if "added_tokens_decoder" in tokenizer_config_json: + added_tokens_decoder = tokenizer_config_json["added_tokens_decoder"] + for token_id, token_json in added_tokens_decoder.items(): + token_id = int(token_id) + if token_id >= vocab_size: + logger.debug(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + + token_content = token_json["content"] + token_type = SentencePieceTokenTypes.USER_DEFINED + token_score = -10000.0 + + # Map unk_token to UNKNOWN, other special tokens to CONTROL + # Set the score to 0.0 as in the original tokenizer.model + if ("special" in token_json) and token_json["special"]: + if token_content == tokenizer_config_json["unk_token"]: + token_type = SentencePieceTokenTypes.UNKNOWN + else: + token_type = SentencePieceTokenTypes.CONTROL + token_score = 0.0 + + logger.info(f"Setting added token {token_id} to '{token_content}' (type: {token_type}, score: {token_score:.2f})") + tokens[token_id] = token_content.encode("utf-8") + toktypes[token_id] = token_type + scores[token_id] = token_score + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + self.gguf_writer.add_rope_dimension_count(hparams["hidden_size"] // hparams["num_attention_heads"]) + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + + if name.endswith("q_proj.weight"): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith("k_proj.weight"): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + + # process the experts separately + if name.find("block_sparse_moe.experts") != -1: + n_experts = self.hparams["num_local_experts"] + + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for wid in ["w1", "w2", "w3"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"layers.{bid}.feed_forward.experts.{wid}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/baichuan.py b/conversion/baichuan.py new file mode 100644 index 00000000000..4cf34057cd9 --- /dev/null +++ b/conversion/baichuan.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("BaichuanForCausalLM", "BaiChuanForCausalLM") +class BaichuanModel(TextModel): + model_arch = gguf.MODEL_ARCH.BAICHUAN + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_tensor_data_layout("Meta AI original pth") + self.gguf_writer.add_rope_dimension_count(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + head_count = self.hparams["num_attention_heads"] + head_count_kv = self.hparams.get("num_key_value_heads", head_count) + + if bid is not None and name == f"model.layers.{bid}.self_attn.W_pack.weight": + logger.info(f"Unpacking and permuting layer {bid}") + yield from [ + (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), + self._reverse_hf_permute_part(data_torch, 0, head_count, head_count)), + (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), + self._reverse_hf_permute_part(data_torch, 1, head_count, head_count_kv)), + (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), + self._reverse_hf_part(data_torch, 2)), + ] + else: + yield from self.modify_tensors(data_torch, self.map_tensor_name(name), bid) + + def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: + if n_kv_head is not None and n_head != n_kv_head: + n_head //= n_kv_head + + return ( + weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape) + ) + + def _reverse_hf_permute_part( + self, weights: Tensor, n_part: int, n_head: int, n_head_kv: int | None = None, + ) -> Tensor: + r = weights.shape[0] // 3 + return self._reverse_hf_permute(weights[r * n_part:r * n_part + r, ...], n_head, n_head_kv) + + def _reverse_hf_part(self, weights: Tensor, n_part: int) -> Tensor: + r = weights.shape[0] // 3 + return weights[r * n_part:r * n_part + r, ...] diff --git a/conversion/bailingmoe.py b/conversion/bailingmoe.py new file mode 100644 index 00000000000..319ff6dabee --- /dev/null +++ b/conversion/bailingmoe.py @@ -0,0 +1,216 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("BailingMoeForCausalLM") +class BailingMoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.BAILINGMOE + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + + self.gguf_writer.add_rope_dimension_count(rope_dim) + self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) + self.gguf_writer.add_expert_weights_scale(1.0) + self.gguf_writer.add_expert_shared_count(hparams["num_shared_experts"]) + self.gguf_writer.add_expert_weights_norm(hparams["norm_topk_prob"]) + + _experts: list[dict[str, Tensor]] | None = None + + @staticmethod + def permute(weights: Tensor, n_head: int, n_head_kv: int | None): + if n_head_kv is not None and n_head != n_head_kv: + n_head = n_head_kv + return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + n_embd = self.hparams["hidden_size"] + if (head_dim := self.hparams.get("head_dim")) is None: + head_dim = n_embd // n_head + + output_name = self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT) + + if name.endswith("attention.dense.weight"): + yield from super().modify_tensors(data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_OUT, bid), bid) + return + elif name.endswith("query_key_value.weight"): + q, k, v = data_torch.split([n_head * head_dim, n_kv_head * head_dim, n_kv_head * head_dim], dim=-2) + + yield from super().modify_tensors(BailingMoeModel.permute(q, n_head, n_head), self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) + yield from super().modify_tensors(BailingMoeModel.permute(k, n_head, n_kv_head), self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) + yield from super().modify_tensors(v,self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) + return + elif name.find("mlp.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + new_name = self.map_tensor_name(merged_name) + + yield from super().modify_tensors(data_torch, new_name, bid) + + return + + new_name = self.map_tensor_name(name) + + if new_name == output_name and self.hparams.get("norm_head"): + data_torch = data_torch.float() + data_torch /= torch.norm(data_torch, p=2, dim=0, keepdim=True) + 1e-7 + + yield from super().modify_tensors(data_torch, new_name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("BailingMoeV2ForCausalLM") +class BailingMoeV2Model(TextModel): + model_arch = gguf.MODEL_ARCH.BAILINGMOE2 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if nextn_layers := self.hparams.get("num_nextn_predict_layers", 0): + self.block_count = self.hparams["num_hidden_layers"] + nextn_layers + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + + self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5))) + self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) + self.gguf_writer.add_expert_shared_feed_forward_length(hparams.get("moe_shared_expert_intermediate_size", hparams["moe_intermediate_size"] * hparams["num_shared_experts"])) + self.gguf_writer.add_expert_weights_scale(hparams["routed_scaling_factor"]) + self.gguf_writer.add_expert_shared_count(hparams["num_shared_experts"]) + self.gguf_writer.add_expert_weights_norm(hparams["norm_topk_prob"]) + + if (nextn_layers := self.hparams.get("num_nextn_predict_layers")) is not None: + self.gguf_writer.add_nextn_predict_layers(nextn_layers) + + _experts: list[dict[str, Tensor]] | None = None + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith(".expert_bias"): + name = name.replace(".expert_bias", ".expert_bias.bias") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "mlp.experts" in name: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("SarvamMoEForCausalLM", "modeling_sarvam_moe.SarvamMoEForCausalLM") +class SarvamMoEModel(BailingMoeV2Model): + model_arch = gguf.MODEL_ARCH.BAILINGMOE2 + # Sarvam-MoE shares the BailingMoeV2 architecture; only differences: + # - full rotary (no partial_rotary_factor) + # - expert bias is zero-mean normalized at load time + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + # Override the partial-rotary value written by BailingMoeV2 with the full rotary dim + self.gguf_writer.add_rope_dimension_count(rope_dim) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + if name.endswith(".expert_bias"): + # Sarvam normalizes expert bias to zero mean + inner = gen + + def gen(): + t = inner() + return t - t.mean() + return super().filter_tensors((name, gen)) diff --git a/conversion/base.py b/conversion/base.py new file mode 100644 index 00000000000..d89d32fe150 --- /dev/null +++ b/conversion/base.py @@ -0,0 +1,2468 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import annotations + +import ast +import logging +import contextlib +import json +import os +import re +import sys +from enum import IntEnum +from pathlib import Path +from hashlib import sha256 +from typing import TYPE_CHECKING, Any, Callable, ContextManager, Iterable, Iterator, Literal, Sequence, TypeVar, cast +from itertools import chain +from transformers import AutoConfig + +import numpy as np +import torch + +if TYPE_CHECKING: + from torch import Tensor + +if 'NO_LOCAL_GGUF' not in os.environ: + sys.path.insert(1, str(Path(__file__).parent.parent / 'gguf-py')) +import gguf +from gguf.vocab import MistralTokenizerType, MistralVocab + +try: + from mistral_common.tokens.tokenizers.base import TokenizerVersion # type: ignore[import-not-found, ty:unresolved-import] + from mistral_common.tokens.tokenizers.multimodal import DATASET_MEAN as _MISTRAL_COMMON_DATASET_MEAN, DATASET_STD as _MISTRAL_COMMON_DATASET_STD # type: ignore[import-not-found, ty:unresolved-import] + from mistral_common.tokens.tokenizers.tekken import Tekkenizer # type: ignore[import-not-found, ty:unresolved-import] + from mistral_common.tokens.tokenizers.sentencepiece import ( # type: ignore[import-not-found, ty:unresolved-import] + SentencePieceTokenizer, + ) + + _mistral_common_installed = True + _mistral_import_error_msg = "" +except ImportError: + _MISTRAL_COMMON_DATASET_MEAN = (0.48145466, 0.4578275, 0.40821073) + _MISTRAL_COMMON_DATASET_STD = (0.26862954, 0.26130258, 0.27577711) + + _mistral_common_installed = False + TokenizerVersion: Any = None + Tekkenizer: Any = None + SentencePieceTokenizer: Any = None + _mistral_import_error_msg = ( + "Mistral format requires `mistral-common` to be installed. Please run " + "`pip install mistral-common[image,audio]` to install it." + ) + + +logger = logging.getLogger("hf-to-gguf") + + +AnyModel = TypeVar("AnyModel", bound="type[ModelBase]") + + +class SentencePieceTokenTypes(IntEnum): + NORMAL = 1 + UNKNOWN = 2 + CONTROL = 3 + USER_DEFINED = 4 + UNUSED = 5 + BYTE = 6 + + +class ModelType(IntEnum): + TEXT = 1 + MMPROJ = 2 + + +class ModelBase: + _model_classes: dict[ModelType, dict[str, type[ModelBase]]] = { + ModelType.TEXT: {}, + ModelType.MMPROJ: {}, + } + + dir_model: Path + ftype: gguf.LlamaFileType + fname_out: Path + is_big_endian: bool + endianess: gguf.GGUFEndian + use_temp_file: bool + lazy: bool + dry_run: bool + hparams: dict[str, Any] + model_tensors: dict[str, Callable[[], Tensor]] + gguf_writer: gguf.GGUFWriter + model_name: str | None + metadata_override: Path | None + dir_model_card: Path + remote_hf_model_id: str | None + + # subclasses should define this! + model_arch: gguf.MODEL_ARCH + + # subclasses should initialize this! + block_count: int + tensor_map: gguf.TensorNameMap + + # Mistral format specifics + is_mistral_format: bool = False + disable_mistral_community_chat_template: bool = False + sentence_transformers_dense_modules: bool = False + + def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, *, is_big_endian: bool = False, + use_temp_file: bool = False, eager: bool = False, + metadata_override: Path | None = None, model_name: str | None = None, + split_max_tensors: int = 0, split_max_size: int = 0, dry_run: bool = False, + small_first_shard: bool = False, hparams: dict[str, Any] | None = None, remote_hf_model_id: str | None = None, + disable_mistral_community_chat_template: bool = False, + sentence_transformers_dense_modules: bool = False, + fuse_gate_up_exps: bool = False): + if type(self) is ModelBase or \ + type(self) is TextModel or \ + type(self) is MmprojModel: + raise TypeError(f"{type(self).__name__!r} should not be directly instantiated") + + if self.is_mistral_format and not _mistral_common_installed: + raise ImportError(_mistral_import_error_msg) + + self.dir_model = dir_model + self.ftype = ftype + self.fname_out = fname_out + self.is_big_endian = is_big_endian + self.endianess = gguf.GGUFEndian.BIG if is_big_endian else gguf.GGUFEndian.LITTLE + self.use_temp_file = use_temp_file + self.lazy = not eager or (remote_hf_model_id is not None) + self.dry_run = dry_run + self.remote_hf_model_id = remote_hf_model_id + self.sentence_transformers_dense_modules = sentence_transformers_dense_modules + self.fuse_gate_up_exps = fuse_gate_up_exps + self._gate_exp_buffer: dict[int, Tensor] = {} + self._up_exp_buffer: dict[int, Tensor] = {} + self.hparams = ModelBase.load_hparams(self.dir_model, self.is_mistral_format) if hparams is None else hparams + self.model_tensors = self.index_tensors(remote_hf_model_id=remote_hf_model_id) + self.metadata_override = metadata_override + self.model_name = model_name + self.dir_model_card = dir_model # overridden in convert_lora_to_gguf.py + self._is_nvfp4 = False + self._is_mxfp4 = False + + # Apply heuristics to figure out typical tensor encoding based on first tensor's dtype + # NOTE: can't use field "torch_dtype" in config.json, because some finetunes lie. + if self.ftype == gguf.LlamaFileType.GUESSED: + for _, tensor in self.get_tensors(): + if tensor.dim() < 2: + continue + + if tensor.dtype == torch.bfloat16: + self.ftype = gguf.LlamaFileType.MOSTLY_BF16 + logger.info("heuristics detected bfloat16 tensor dtype, setting --outtype bf16") + break + elif tensor.dtype == torch.float16: + self.ftype = gguf.LlamaFileType.MOSTLY_F16 + logger.info("heuristics detected float16 tensor dtype, setting --outtype f16") + break + else: + self.ftype = gguf.LlamaFileType.MOSTLY_F16 + logger.info("heuristics unable to detect tensor dtype, defaulting to --outtype f16") + + # Configure GGUF Writer + self.gguf_writer = gguf.GGUFWriter(path=None, arch=gguf.MODEL_ARCH_NAMES[self.model_arch], endianess=self.endianess, use_temp_file=self.use_temp_file, + split_max_tensors=split_max_tensors, split_max_size=split_max_size, dry_run=dry_run, small_first_shard=small_first_shard) + + # Mistral specific + self.disable_mistral_community_chat_template = disable_mistral_community_chat_template + + @classmethod + def add_prefix_to_filename(cls, path: Path, prefix: str) -> Path: + stem, suffix = path.stem, path.suffix + new_name = f"{prefix}{stem}{suffix}" + return path.with_name(new_name) + + def find_hparam(self, keys: Iterable[str], optional: bool = False) -> Any: + key = next((k for k in keys if k in self.hparams), None) + if key is not None: + return self.hparams[key] + if optional: + return None + raise KeyError(f"could not find any of: {keys}") + + def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]: + tensors: dict[str, Callable[[], Tensor]] = {} + + if remote_hf_model_id is not None: + is_safetensors = True + + logger.info(f"Using remote model with HuggingFace id: {remote_hf_model_id}") + remote_tensors = gguf.utility.SafetensorRemote.get_list_tensors_hf_model(remote_hf_model_id) + for name, remote_tensor in remote_tensors.items(): + data_gen = lambda r=remote_tensor: LazyTorchTensor.from_remote_tensor(r) # noqa: E731 + if titem := self.filter_tensors((name, data_gen)): + tname, tgen = titem + tensors[tname] = tgen + + return tensors + + prefix = "model" if not self.is_mistral_format else "consolidated" + part_names: list[str] = ModelBase.get_model_part_names(self.dir_model, prefix, ".safetensors") + is_safetensors: bool = len(part_names) > 0 + if not is_safetensors: + part_names = ModelBase.get_model_part_names(self.dir_model, "pytorch_model", ".bin") + + tensor_names_from_index: set[str] = set() + tensor_names_from_parts: set[str] = set() + + if not self.is_mistral_format: + index_name = "model.safetensors" if is_safetensors else "pytorch_model.bin" + index_name += ".index.json" + index_file = self.dir_model / index_name + + if index_file.is_file(): + logger.info(f"gguf: loading model weight map from '{index_name}'") + with open(index_file, "r", encoding="utf-8") as f: + index: dict[str, Any] = json.load(f) + weight_map = index.get("weight_map") + if weight_map is None or not isinstance(weight_map, dict): + raise ValueError(f"Can't load 'weight_map' from {index_name!r}") + tensor_names_from_index.update(weight_map.keys()) + part_dict: dict[str, None] = dict.fromkeys(weight_map.values(), None) # ty: ignore[invalid-assignment] + part_names = sorted(part_dict.keys()) + else: + weight_map = {} + else: + weight_map = {} + + for part_name in part_names: + logger.info(f"gguf: indexing model part '{part_name}'") + ctx: ContextManager[Any] + if is_safetensors: + ctx = cast(ContextManager[Any], gguf.utility.SafetensorsLocal(self.dir_model / part_name)) + else: + ctx = contextlib.nullcontext(torch.load(str(self.dir_model / part_name), map_location="cpu", mmap=True, weights_only=True)) + + with ctx as model_part: + assert model_part is not None + + for name in model_part.keys(): + tensor_names_from_parts.add(name) + if is_safetensors: + data: gguf.utility.LocalTensor = model_part[name] + if self.lazy: + data_gen = lambda data=data: LazyTorchTensor.from_local_tensor(data) # noqa: E731 + else: + dtype = LazyTorchTensor._dtype_str_map[data.dtype] + data_gen = lambda data=data, dtype=dtype: torch.from_numpy(data.mmap_bytes()).view(dtype).reshape(data.shape) # noqa: E731 + else: + data_torch: Tensor = model_part[name] + if self.lazy: + data_gen = lambda data=data_torch: LazyTorchTensor.from_eager(data) # noqa: E731 + else: + data_gen = lambda data=data_torch: data # noqa: E731 + if titem := self.filter_tensors((name, data_gen)): + tname, tgen = titem + tensors[tname] = tgen + + # verify tensor name presence and identify potentially missing files + if len(tensor_names_from_index) > 0: + if len(tensor_names_from_parts.symmetric_difference(tensor_names_from_index)) > 0: + missing = sorted(tensor_names_from_index.difference(tensor_names_from_parts)) + extra = sorted(tensor_names_from_parts.difference(tensor_names_from_index)) + missing_files = sorted(set(weight_map[n] for n in missing if n in weight_map)) + if len(extra) == 0 and len(missing_files) > 0: + raise ValueError(f"Missing or incomplete model files: {missing_files}\n" + f"Missing tensors: {missing}") + else: + raise ValueError("Mismatch between weight map and model parts for tensor names:\n" + f"Missing tensors: {missing}\n" + f"Extra tensors: {extra}") + + return tensors + + @staticmethod + def _scale_is_trivial(scale: Tensor) -> bool: + return scale.numel() <= 1 and abs(float(scale.float().sum()) - 1.0) < 1e-6 + + def _write_scale_tensor(self, scale_name: str, scale: Tensor): + if not self._scale_is_trivial(scale): + scale_f32 = scale.float().numpy().flatten() + logger.info(f" + {scale_name} (per-tensor scale, shape [{scale_f32.size}])") + self.gguf_writer.add_tensor(scale_name, scale_f32) + + def _write_scales_tensor(self, scale_name: str, scales: list[float]): + if not np.allclose(scales, 1.0, atol=1e-6): + scale_vals = np.array(scales, dtype=np.float32) + logger.info(f" + {scale_name} (per-expert scale, shape [{len(scales)}])") + self.gguf_writer.add_tensor(scale_name, scale_vals) + + def dequant_model(self): + # If all quantized tensors were already handled (e.g. pure NVFP4), skip + if self._is_nvfp4 and not any(k.endswith((".weight_scale", ".weight_scale_inv")) for k in self.model_tensors): + return + + tensors_to_remove: list[str] = [] + new_tensors: dict[str, Callable[[], Tensor]] = {} + + if (quant_config := self.hparams.get("quantization_config")) and isinstance(quant_config, dict): + quant_method = quant_config.get("quant_method") + + def dequant_bitnet(weight: Tensor, scale: Tensor) -> Tensor: + weight = weight.view(torch.uint8) + orig_shape = weight.shape + + shift = torch.tensor([0, 2, 4, 6], dtype=torch.uint8).reshape((4, *(1 for _ in range(len(orig_shape))))) + data = weight.unsqueeze(0).expand((4, *orig_shape)) >> shift + data = data & 3 + data = (data.float() - 1).reshape((orig_shape[0] * 4, *orig_shape[1:])) + + # The scale is inverted + return data / scale.float() + + def dequant_simple(weight: Tensor, scale: Tensor, block_size: Sequence[int] | None = None) -> Tensor: + scale = scale.float() + + if block_size is not None: + dim_offset = scale.ndim - len(block_size) + for i, size in enumerate(block_size): + scale = scale.repeat_interleave(size, dim_offset + i) + # unpad the scale (e.g. when the tensor size isn't a multiple of the block size) + scale = scale[tuple(slice(0, size) for size in weight.shape)] + + # align scale dims to weight for correct broadcasting (e.g. [128] -> [128, 1, 1]) + while scale.ndim < weight.ndim: + scale = scale.unsqueeze(-1) + + return weight.float() * scale + + # ref: https://github.com/ModelCloud/GPTQModel/blob/037c5c0f6c9e33c500d975b038d02e7ca437546d/gptqmodel/nn_modules/qlinear/__init__.py#L437-L476 + def dequant_gptq(g_idx: Tensor, qweight: Tensor, qzeros: Tensor, scales: Tensor) -> Tensor: + bits = quant_config["bits"] + assert bits in (2, 3, 4, 8) + assert qweight.dtype == qzeros.dtype + maxq = (2 ** bits) - 1 + weight = None + zeros = None + pack_dtype_bits = qweight.dtype.itemsize * 8 + + if bits in [2, 4, 8]: + pack_factor = pack_dtype_bits // bits + wf = torch.tensor(list(range(0, pack_dtype_bits, bits)), dtype=torch.int32).unsqueeze(0) + if self.lazy: + wf = LazyTorchTensor.from_eager(wf) + + zeros = torch.bitwise_right_shift( + qzeros.unsqueeze(2).expand(-1, -1, pack_factor), + wf.unsqueeze(0) + ).to(torch.int16 if bits == 8 else torch.int8) + zeros = torch.bitwise_and(zeros, maxq).reshape(scales.shape) + + weight = torch.bitwise_and( + torch.bitwise_right_shift( + qweight.unsqueeze(1).expand(-1, pack_factor, -1), + wf.unsqueeze(-1) + ).to(torch.int16 if bits == 8 else torch.int8), + maxq + ) + elif bits == 3: + raise NotImplementedError("3-bit gptq dequantization is not yet implemented") + + assert weight is not None + assert zeros is not None + + weight = weight.reshape(weight.shape[0] * weight.shape[1], weight.shape[2]) + + # gptq_v2 doesn't need to offset zeros + if quant_config.get("checkpoint_format", "gptq") == "gptq": + zeros += 1 + + return (scales[g_idx].float() * (weight - zeros[g_idx]).float()).T + + def dequant_packed(w: Tensor, scale: Tensor, shape_tensor: Tensor, zero_point: Tensor | None, num_bits: int, group_size: int): + assert w.dtype == torch.int32 + shape = tuple(shape_tensor.tolist()) + assert len(shape) == 2 + mask = (1 << num_bits) - 1 + + shifts = torch.arange(0, 32 - (num_bits - 1), num_bits, dtype=torch.int32) + if self.lazy: + shifts = LazyTorchTensor.from_eager(shifts) + + if zero_point is None: + offset = 1 << (num_bits - 1) + else: + assert len(zero_point.shape) == 2 + offset = (zero_point.unsqueeze(1) >> shifts.reshape(1, -1, 1)) & mask + offset = offset.reshape(-1, zero_point.shape[1]) + # trim padding, and prepare for broadcast + # NOTE: the zero-point is packed along dim 0 + offset = offset[:shape[0], :].unsqueeze(-1) + + # extract values + # NOTE: the weights are packed along dim 1 + unpacked = (w.unsqueeze(-1) >> shifts.reshape(1, 1, -1)) & mask + unpacked = unpacked.reshape(shape[0], -1) + + # trim padding + unpacked = unpacked[:, :shape[1]] + + # prepare for broadcast of the scale + unpacked = unpacked.reshape(shape[0], (unpacked.shape[-1] + group_size - 1) // group_size, group_size) + unpacked = unpacked - offset + + return (unpacked * scale.unsqueeze(-1).float()).reshape(shape) + + if quant_method == "bitnet": + for name in self.model_tensors.keys(): + if name.endswith(".weight_scale"): + weight_name = name.removesuffix("_scale") + w = self.model_tensors[weight_name] + s = self.model_tensors[name] + self.model_tensors[weight_name] = lambda w=w, s=s: dequant_bitnet(w(), s()) + tensors_to_remove.append(name) + elif quant_method == "fp8": + block_size = quant_config.get("weight_block_size") + for name in self.model_tensors.keys(): + if name.endswith("_scale_inv"): + weight_name = name.removesuffix("_scale_inv") + w = self.model_tensors[weight_name] + s = self.model_tensors[name] + self.model_tensors[weight_name] = lambda w=w, s=s, bs=block_size: dequant_simple(w(), s(), bs) + tensors_to_remove.append(name) + if name.endswith(".activation_scale"): # unused + tensors_to_remove.append(name) + if name.endswith("_activation_scale"): # Mistral-Small-4-119B-2602, unused + tensors_to_remove.append(name) + # mistral format + if name.endswith(".qscale_weight"): + weight_name = name.removesuffix("qscale_weight") + "weight" + w = self.model_tensors[weight_name] + s = self.model_tensors[name] + self.model_tensors[weight_name] = lambda w=w, s=s, bs=block_size: dequant_simple(w(), s(), bs) + tensors_to_remove.append(name) + if name.endswith(".qscale_act"): + tensors_to_remove.append(name) + elif quant_method == "gptq": + for name in self.model_tensors.keys(): + if name.endswith(".qweight"): + base_name = name.removesuffix(".qweight") + g_idx = self.model_tensors[base_name + ".g_idx"] + qweight = self.model_tensors[base_name + ".qweight"] + qzeros = self.model_tensors[base_name + ".qzeros"] + scales = self.model_tensors[base_name + ".scales"] + new_tensors[base_name + ".weight"] = ( + lambda g=g_idx, z=qzeros, w=qweight, s=scales: dequant_gptq( + g(), w(), z(), s() + ) + ) + tensors_to_remove += [ + base_name + n + for n in ( + ".g_idx", + ".qzeros", + ".qweight", + ".scales", + ) + ] + elif quant_method == "compressed-tensors": + quant_format = quant_config["format"] + groups = quant_config["config_groups"] + if len(groups) > 1: + raise NotImplementedError("Can't handle multiple config groups for compressed-tensors yet") + weight_config = tuple(groups.values())[0]["weights"] + + if quant_format == "float-quantized" or quant_format == "int-quantized" or quant_format == "naive-quantized": + block_size = weight_config.get("block_structure", None) + strategy = weight_config.get("strategy") + assert strategy == "channel" or strategy == "block" + assert weight_config.get("group_size") is None # didn't find a model using this yet + for name in self.model_tensors.keys(): + if name.endswith(".weight_scale"): + weight_name = name.removesuffix("_scale") + w = self.model_tensors[weight_name] + s = self.model_tensors[name] + self.model_tensors[weight_name] = lambda w=w, s=s: dequant_simple(w(), s(), block_size) + tensors_to_remove.append(name) + elif quant_format == "pack-quantized": + assert weight_config.get("strategy") == "group" + assert weight_config.get("type", "int") == "int" + num_bits = weight_config.get("num_bits") + group_size = weight_config.get("group_size") + assert isinstance(num_bits, int) + assert isinstance(group_size, int) + for name in self.model_tensors.keys(): + if name.endswith(".weight_packed"): + base_name = name.removesuffix("_packed") + w = self.model_tensors[name] + scale = self.model_tensors[base_name + "_scale"] + shape = self.model_tensors[base_name + "_shape"] + zero_point = self.model_tensors.get(base_name + "_zero_point", lambda: None) + new_tensors[base_name] = ( + lambda w=w, scale=scale, shape=shape, zero_point=zero_point: dequant_packed( + w(), scale(), shape(), zero_point(), num_bits, group_size, + ) + ) + tensors_to_remove += [base_name + n for n in ("_packed", "_shape", "_scale")] + if (base_name + "_zero_point") in self.model_tensors: + tensors_to_remove.append(base_name + "_zero_point") + else: + raise NotImplementedError(f"Quant format {quant_format!r} for method {quant_method!r} is not yet supported") + elif quant_method == "modelopt": + # Mixed-precision ModelOpt models: NVFP4 tensors are handled by + # _generate_nvfp4_tensors; FP8 tensors have 1D weight_scale and + # are dequantized here. k/v scale tensors are unused. + for name in self.model_tensors.keys(): + if name.endswith(".weight_scale"): + weight_name = name.removesuffix("_scale") + w = self.model_tensors[weight_name] + s = self.model_tensors[name] + self.model_tensors[weight_name] = lambda w=w, s=s: dequant_simple(w(), s(), None) + tensors_to_remove.append(name) + if name.endswith((".input_scale", ".k_scale", ".v_scale")): + tensors_to_remove.append(name) + elif quant_method is not None: + raise NotImplementedError(f"Quant method is not yet supported: {quant_method!r}") + + for name in tensors_to_remove: + if name in self.model_tensors: + del self.model_tensors[name] + + for name, value in new_tensors.items(): + self.model_tensors[name] = value + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith("e_score_correction_bias"): + name = name.replace("e_score_correction_bias", "e_score_correction.bias") + + if "language_model." in name: + name = name.replace("language_model.", "") + + return name, gen + + def get_tensors(self) -> Iterator[tuple[str, Tensor]]: + for name, gen in self.model_tensors.items(): + yield name, gen() + + def format_tensor_name(self, key: gguf.MODEL_TENSOR, bid: int | None = None, suffix: str = ".weight") -> str: + if key not in gguf.MODEL_TENSORS[self.model_arch]: + raise ValueError(f"Missing {key!r} for MODEL_TENSORS of {self.model_arch!r}") + name: str = gguf.TENSOR_NAMES[key] + if "{bid}" in name: + assert bid is not None + name = name.format(bid=bid) + return name + suffix + + def match_model_tensor_name(self, name: str, key: gguf.MODEL_TENSOR, bid: int | None, suffix: str = ".weight") -> bool: + if key not in gguf.MODEL_TENSORS[self.model_arch]: + return False + key_name: str = gguf.TENSOR_NAMES[key] + if "{bid}" in key_name: + if bid is None: + return False + key_name = key_name.format(bid=bid) + else: + if bid is not None: + return False + return name == (key_name + suffix) + + def map_tensor_name(self, name: str, try_suffixes: Sequence[str] = (".weight", ".bias")) -> str: + new_name = self.tensor_map.get_name(key=name, try_suffixes=try_suffixes) + if new_name is None: + raise ValueError(f"Can not map tensor {name!r}") + return new_name + + def set_gguf_parameters(self): + raise NotImplementedError("set_gguf_parameters() must be implemented in subclasses") + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + new_name = self.map_tensor_name(name) + + # Handle gate/up expert tensor fusion if enabled + if self.fuse_gate_up_exps and bid is not None: + if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_GATE_EXP, bid): + self._gate_exp_buffer[bid] = data_torch + elif self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_UP_EXP, bid): + self._up_exp_buffer[bid] = data_torch + + # Check if both gate and up are buffered for this layer + if bid in self._gate_exp_buffer and bid in self._up_exp_buffer: + gate_data = self._gate_exp_buffer.pop(bid) + up_data = self._up_exp_buffer.pop(bid) + # gate/up shape: (n_expert, n_ff, n_embd), concatenate to (n_expert, n_ff*2, n_embd) + fused_data = torch.cat([gate_data, up_data], dim=1) + fused_name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_UP_EXP, bid) + logger.info(f"Fused gate_exps and up_exps for layer {bid}") + return [(fused_name, fused_data)] + + # If we buffered a gate/up tensor, wait for the other + if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_GATE_EXP, bid) or \ + self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_UP_EXP, bid): + return [] + + return [(new_name, data_torch)] + + def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool: + del name, new_name, bid, n_dims # unused + + return False + + # some models need extra generated tensors (like rope_freqs) + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + return () + + @staticmethod + def _nvfp4_pack(weight: Tensor, scale: Tensor) -> tuple[np.ndarray, list[int]]: + """Repack NVFP4 ModelOpt tensors into ggml super-block layout. + Preserves original E4M3 scale bits as UE4M3 (strip sign bit). + The per-tensor scale2 factor is stored as a separate tensor and applied at inference time via ggml_mul(). + Returns (raw_data, logical_shape).""" + + out_features = weight.shape[0] + n_blocks = scale.shape[1] + + # Unpack ModelOpt nibble-packed weights + w = weight.reshape(out_features, n_blocks, 8) + vals = torch.stack([w & 0x0F, w >> 4], dim=-1).reshape(out_features, n_blocks, 16) + + # Preserve original E4M3 scale bits as UE4M3 (strip sign bit) + d_ue = scale.view(torch.uint8).numpy().reshape(out_features, n_blocks) & 0x7F + qs = (vals[:, :, :8] | (vals[:, :, 8:] << 4)).to(torch.uint8).numpy() + + # Pack into super-blocks: [4 UE4M3 scales, 32 qs bytes] = 36 bytes per 64 elements + n_super = n_blocks // 4 + d_grouped = d_ue.reshape(out_features, n_super, 4) + qs_grouped = qs.reshape(out_features, n_super, 4, 8).reshape(out_features, n_super, 32) + raw = np.concatenate([d_grouped, qs_grouped], axis=-1).reshape(out_features, n_super * 36) + return raw, [out_features, n_super * 64] + + def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): + new_name = self.map_tensor_name(name) + + raw, shape = self._nvfp4_pack(weight, scale) + logger.info(f"Repacked {new_name} with shape {shape} and quantization NVFP4") + self.gguf_writer.add_tensor(new_name, raw, raw_dtype=gguf.GGMLQuantizationType.NVFP4) + + self._write_scale_tensor(new_name.replace(".weight", ".scale"), scale2) + self._write_scale_tensor(new_name.replace(".weight", ".input_scale"), input_scale) + + def _generate_nvfp4_tensors(self): + # Per-layer expert merging to avoid holding all experts in memory + expert_blocks: dict[tuple[int, str], list[tuple[int, np.ndarray]]] = {} + expert_scales: dict[tuple[int, str], list[tuple[int, float]]] = {} + expert_input_scales: dict[tuple[int, str], list[tuple[int, float]]] = {} + expert_shapes: dict[tuple[int, str], list[int]] = {} + n_experts = self.find_hparam(["num_local_experts", "num_experts"], optional=True) or 0 + consumed: list[str] = [] + + for name in self.model_tensors.keys(): + if not name.endswith(".weight"): + continue + scale_name = name.replace(".weight", ".weight_scale") + scale2_name = name.replace(".weight", ".weight_scale_2") + input_scale_name = name.replace(".weight", ".input_scale") + if scale_name not in self.model_tensors: + continue + # Force eager materialization of lazy tensors + weight = LazyTorchTensor.to_eager(self.model_tensors[name]()) + scale = LazyTorchTensor.to_eager(self.model_tensors[scale_name]()) + + # Skip non-NVFP4 tensors (e.g. FP8 with per-channel 1D scales) + if scale.ndim < 2: + continue + + scale2 = LazyTorchTensor.to_eager(self.model_tensors.get(scale2_name, lambda: torch.tensor(1.0))()) + input_scale = LazyTorchTensor.to_eager(self.model_tensors.get(input_scale_name, lambda: torch.tensor(1.0))()) + + # Mark tensors for removal from model_tensors (already written to gguf) + consumed.extend([name, scale_name]) + if scale2_name in self.model_tensors: + consumed.append(scale2_name) + if input_scale_name in self.model_tensors: + consumed.append(input_scale_name) + + # Check if this is a per-expert tensor + m = re.search(r'\.experts\.(\d+)\.(gate_proj|up_proj|down_proj)\.weight$', name) + if m: + expert_id = int(m.group(1)) + proj_type = m.group(2) + bid_m = re.search(r'\.layers\.(\d+)\.', name) + bid = int(bid_m.group(1)) if bid_m else 0 + key = (bid, proj_type) + + raw, shape = self._nvfp4_pack(weight, scale) + + if key not in expert_blocks: + expert_blocks[key] = [] + expert_scales[key] = [] + expert_input_scales[key] = [] + expert_shapes[key] = shape + expert_blocks[key].append((expert_id, raw.copy())) + # Collect per-expert scale2 (scalar per expert) + expert_scales[key].append((expert_id, float(scale2.float().sum()))) + # Collect per-expert input_scale (scalar per expert) + expert_input_scales[key].append((expert_id, float(input_scale.float().sum()))) + + # Flush when all experts for this (layer, proj) are collected + if n_experts > 0 and len(expert_blocks[key]) >= n_experts: + self._flush_nvfp4_experts(key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type) + else: + self._repack_nvfp4(name, weight, scale, scale2, input_scale) + + # Flush any remaining experts (fallback if n_experts was unknown) + for bid, proj_type in list(expert_blocks.keys()): + self._flush_nvfp4_experts((bid, proj_type), expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type) + + # Remove consumed tensors so get_tensors/modify_tensors won't see them + for name in consumed: + self.model_tensors.pop(name, None) + + # Remove any remaining unused auxiliary tensors + for name in list(self.model_tensors.keys()): + if name.endswith((".k_scale", ".v_scale")): + del self.model_tensors[name] + + def _flush_nvfp4_experts(self, key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type): + experts = expert_blocks.pop(key) + scales = expert_scales.pop(key) + input_scales = expert_input_scales.pop(key) + shape = expert_shapes.pop(key) + + experts.sort(key=lambda x: x[0]) + merged = np.stack([e[1] for e in experts], axis=0) + merged_name = f"model.layers.{bid}.mlp.experts.{proj_type}.weight" + new_name = self.map_tensor_name(merged_name) + logger.info(f"Repacked {new_name} with shape [{len(experts)}, {shape[0]}, {shape[1]}] and quantization NVFP4") + self.gguf_writer.add_tensor(new_name, merged, raw_dtype=gguf.GGMLQuantizationType.NVFP4) + + scales.sort(key=lambda x: x[0]) + self._write_scales_tensor(new_name.replace(".weight", ".scale"), [s[1] for s in scales]) + + input_scales.sort(key=lambda x: x[0]) + self._write_scales_tensor(new_name.replace(".weight", ".input_scale"), [s[1] for s in input_scales]) + + del experts, merged + + def prepare_tensors(self): + # detect NVFP4 quantization (ModelOpt format) + quant_algo = (self.hparams.get("quantization_config") or {}).get("quant_algo") + quant_method = (self.hparams.get("quantization_config") or {}).get("quant_method") + quant_layers = (self.hparams.get("quantization_config") or {}).get("quantized_layers") or {} + quant_config_file = self.dir_model / "hf_quant_config.json" + + if (not quant_algo or not quant_layers) and quant_config_file.is_file(): + with open(quant_config_file, "r", encoding="utf-8") as f: + hf_quant_config = json.load(f) + quant_config = hf_quant_config.get("quantization") or {} + producer = hf_quant_config.get("producer") or {} + producer_name = (producer.get("name") or "").lower() + if quant_method is None: + self.hparams.setdefault("quantization_config", {})["quant_method"] = producer_name + quant_algo = quant_config.get("quant_algo", quant_algo) + quant_layers = quant_config.get("quantized_layers", quant_layers) or {} + + # Some models use per-tensor quant_algo (e.g. "MIXED_PRECISION" with + # per-layer NVFP4/FP8) instead of a single global "NVFP4" value. + if quant_algo != "NVFP4": + if any(v.get("quant_algo") == "NVFP4" for v in quant_layers.values() if isinstance(v, dict)): + quant_algo = "NVFP4" + + self._is_nvfp4 = quant_algo == "NVFP4" + self._is_mxfp4 = quant_method == "mxfp4" + + # NVFP4 weights are repacked and written directly to gguf_writer. + # This must run before dequant_model so NVFP4 tensors are removed + # from model_tensors, leaving only non-NVFP4 (e.g. FP8) for dequant. + if self._is_nvfp4: + self._generate_nvfp4_tensors() + + self.dequant_model() + + # Handle empty tensor_map for models with block_count=0 (like MobileNetV5) + if self.tensor_map.mapping: + max_name_len = max(len(s) for _, s in self.tensor_map.mapping.values()) + len(".weight,") + else: + max_name_len = len("vision_encoder.weight,") # Default reasonable length + + for name, data_torch in chain(self.generate_extra_tensors(), self.get_tensors()): + # we don't need these + if name.endswith((".attention.masked_bias", ".attention.bias", ".rotary_emb.inv_freq")): + continue + + old_dtype = data_torch.dtype + + # convert any unsupported data types to float32 + if data_torch.dtype not in (torch.float16, torch.float32): + data_torch = data_torch.to(torch.float32) + + # use the first number-like part of the tensor name as the block id + bid = None + for part in name.split("."): + if part.isdecimal(): + bid = int(part) + break + + for new_name, data_torch in (self.modify_tensors(data_torch, name, bid)): + # TODO: why do we squeeze here? + # data = data_torch.squeeze().numpy() + data = data_torch.numpy() + + n_dims = len(data.shape) + data_qtype: gguf.GGMLQuantizationType | bool = self.tensor_force_quant(name, new_name, bid, n_dims) + + # Most of the codebase that takes in 1D tensors or norms only handles F32 tensors + if n_dims <= 1 or new_name.endswith("_norm.weight"): + data_qtype = gguf.GGMLQuantizationType.F32 + + # Conditions should closely match those in llama_model_quantize_internal in llama.cpp + # Some tensor types are always in float32 + if data_qtype is False and ( + any( + self.match_model_tensor_name(new_name, key, bid) + for key in ( + gguf.MODEL_TENSOR.FFN_GATE_INP, + gguf.MODEL_TENSOR.FFN_GATE_INP_SHEXP, + gguf.MODEL_TENSOR.POS_EMBD, + gguf.MODEL_TENSOR.TOKEN_TYPES, + gguf.MODEL_TENSOR.SSM_CONV1D, + gguf.MODEL_TENSOR.SHORTCONV_CONV, + gguf.MODEL_TENSOR.TIME_MIX_FIRST, + gguf.MODEL_TENSOR.TIME_MIX_W1, + gguf.MODEL_TENSOR.TIME_MIX_W2, + gguf.MODEL_TENSOR.TIME_MIX_DECAY_W1, + gguf.MODEL_TENSOR.TIME_MIX_DECAY_W2, + gguf.MODEL_TENSOR.TIME_MIX_LERP_FUSED, + gguf.MODEL_TENSOR.POSNET_NORM1, + gguf.MODEL_TENSOR.POSNET_NORM2, + gguf.MODEL_TENSOR.V_ENC_EMBD_POS, + gguf.MODEL_TENSOR.A_ENC_EMBD_POS, + gguf.MODEL_TENSOR.ALTUP_CORRECT_COEF, + gguf.MODEL_TENSOR.ALTUP_PREDICT_COEF, + # Kimi KDA conv weights should be F32 + gguf.MODEL_TENSOR.SSM_CONV1D_Q, + gguf.MODEL_TENSOR.SSM_CONV1D_K, + gguf.MODEL_TENSOR.SSM_CONV1D_V, + ) + ) + or new_name[-7:] not in (".weight", ".lora_a", ".lora_b") + ): + data_qtype = gguf.GGMLQuantizationType.F32 + + if data_qtype is False and any( + self.match_model_tensor_name(new_name, key, bid) + for key in ( + gguf.MODEL_TENSOR.TOKEN_EMBD, + gguf.MODEL_TENSOR.PER_LAYER_TOKEN_EMBD, + gguf.MODEL_TENSOR.OUTPUT, + gguf.MODEL_TENSOR.ALTUP_ROUTER, + gguf.MODEL_TENSOR.LAUREL_L, + gguf.MODEL_TENSOR.LAUREL_R, + ) + ): + if self.ftype in ( + gguf.LlamaFileType.MOSTLY_TQ1_0, + gguf.LlamaFileType.MOSTLY_TQ2_0, + ): + # TODO: use Q4_K and Q6_K + data_qtype = gguf.GGMLQuantizationType.F16 + + # No override (data_qtype is False), or wants to be quantized (data_qtype is True) + if isinstance(data_qtype, bool): + if self.ftype == gguf.LlamaFileType.ALL_F32: + data_qtype = gguf.GGMLQuantizationType.F32 + elif self.ftype == gguf.LlamaFileType.MOSTLY_F16: + data_qtype = gguf.GGMLQuantizationType.F16 + elif self.ftype == gguf.LlamaFileType.MOSTLY_BF16: + data_qtype = gguf.GGMLQuantizationType.BF16 + elif self.ftype == gguf.LlamaFileType.MOSTLY_Q8_0: + data_qtype = gguf.GGMLQuantizationType.Q8_0 + elif self.ftype == gguf.LlamaFileType.MOSTLY_TQ1_0: + data_qtype = gguf.GGMLQuantizationType.TQ1_0 + elif self.ftype == gguf.LlamaFileType.MOSTLY_TQ2_0: + data_qtype = gguf.GGMLQuantizationType.TQ2_0 + else: + raise ValueError(f"Unknown file type: {self.ftype.name}") + + try: + data = gguf.quants.quantize(data, data_qtype) + except gguf.QuantError as e: + logger.warning("%s, %s", e, "falling back to F16") + data_qtype = gguf.GGMLQuantizationType.F16 + data = gguf.quants.quantize(data, data_qtype) + + shape = gguf.quant_shape_from_byte_shape(data.shape, data_qtype) if data.dtype == np.uint8 else data.shape + + # reverse shape to make it similar to the internal ggml dimension order + shape_str = f"{{{', '.join(str(n) for n in reversed(shape))}}}" + + # n_dims is implicit in the shape + logger.info(f"{f'%-{max_name_len}s' % f'{new_name},'} {old_dtype} --> {data_qtype.name}, shape = {shape_str}") + + self.gguf_writer.add_tensor(new_name, data, raw_dtype=data_qtype) + + def set_type(self): + self.gguf_writer.add_type(gguf.GGUFType.MODEL) + + def prepare_metadata(self, vocab_only: bool): + + total_params, shared_params, expert_params, expert_count = self.gguf_writer.get_total_parameter_count() + + self.metadata = gguf.Metadata.load(self.metadata_override, self.dir_model_card, self.model_name, total_params) + + # If we are using HF model id, set the metadata name to the model id + if self.remote_hf_model_id: + self.metadata.name = self.remote_hf_model_id + + # Fallback to model directory name if metadata name is still missing + if self.metadata.name is None: + self.metadata.name = self.dir_model.name + + if self.ftype in (gguf.LlamaFileType.ALL_F32, gguf.LlamaFileType.MOSTLY_F16, gguf.LlamaFileType.MOSTLY_BF16): + if self._is_nvfp4: + self.ftype = gguf.LlamaFileType.MOSTLY_NVFP4 + elif self._is_mxfp4: + self.ftype = gguf.LlamaFileType.MOSTLY_MXFP4_MOE + + # Generate parameter weight class (useful for leader boards) if not yet determined + if self.metadata.size_label is None and total_params > 0: + self.metadata.size_label = gguf.size_label(total_params, shared_params, expert_params, expert_count) + + self.set_type() + + logger.info("Set meta model") + self.metadata.set_gguf_meta_model(self.gguf_writer) + + logger.info("Set model parameters") + self.set_gguf_parameters() + + logger.info("Set model quantization version") + self.gguf_writer.add_quantization_version(gguf.GGML_QUANT_VERSION) + + def write_vocab(self): + raise NotImplementedError("write_vocab() must be implemented in subclasses") + + def write(self): + self.prepare_tensors() + self.prepare_metadata(vocab_only=False) + self.gguf_writer.write_header_to_file(path=self.fname_out) + self.gguf_writer.write_kv_data_to_file() + self.gguf_writer.write_tensors_to_file(progress=True) + self.gguf_writer.close() + + @staticmethod + def get_model_part_names(dir_model: Path, prefix: str, suffix: str) -> list[str]: + part_names: list[str] = [] + for filename in os.listdir(dir_model): + if filename.startswith(prefix) and filename.endswith(suffix): + part_names.append(filename) + + part_names.sort() + + return part_names + + @staticmethod + def load_hparams(dir_model: Path, is_mistral_format: bool): + if is_mistral_format: + with open(dir_model / "params.json", "r", encoding="utf-8") as f: + config = json.load(f) + return config + + try: + # for security reason, we don't allow loading remote code by default + # if a model need remote code, we will fallback to config.json + config = AutoConfig.from_pretrained(dir_model, trust_remote_code=False).to_dict() + except Exception as e: + logger.warning(f"Failed to load model config from {dir_model}: {e}") + logger.warning("Trying to load config.json instead") + with open(dir_model / "config.json", "r", encoding="utf-8") as f: + config = json.load(f) + if "llm_config" in config: + # rename for InternVL + config["text_config"] = config["llm_config"] + if "lm_config" in config: + # rename for GlmASR + config["text_config"] = config["lm_config"] + if "thinker_config" in config: + # rename for Qwen2.5-Omni + config["text_config"] = config["thinker_config"]["text_config"] + if "language_config" in config: + # rename for DeepSeekOCR + config["text_config"] = config["language_config"] + if "lfm" in config: + # rename for LFM2-Audio + config["text_config"] = config["lfm"] + return config + + @classmethod + def register(cls, *names: str) -> Callable[[AnyModel], AnyModel]: + assert names + + def func(modelcls: AnyModel) -> AnyModel: + model_type = ModelType.MMPROJ if modelcls.model_arch == gguf.MODEL_ARCH.MMPROJ else ModelType.TEXT + for name in names: + cls._model_classes[model_type][name] = modelcls + return modelcls + return func + + @classmethod + def print_registered_models(cls): + for model_type, model_classes in cls._model_classes.items(): + logger.error(f"{model_type.name} models:") + for name in sorted(model_classes.keys()): + logger.error(f" - {name}") + + @classmethod + def from_model_architecture(cls, arch: str, model_type = ModelType.TEXT) -> type[ModelBase]: + try: + return cls._model_classes[model_type][arch] + except KeyError: + raise NotImplementedError(f'Architecture {arch!r} not supported!') from None + + +class TextModel(ModelBase): + model_type = ModelType.TEXT + hf_arch: str + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.is_mistral_format: + self.hf_arch = get_model_architecture(self.hparams, self.model_type) + else: + self.hf_arch = "" + + if "text_config" in self.hparams: + # move the text_config to the root level + self.hparams = {**self.hparams, **self.hparams["text_config"]} + + self.block_count = self.find_hparam(["n_layers", "num_hidden_layers", "n_layer", "num_layers"]) + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + self.rope_parameters = self.hparams.get("rope_parameters", self.hparams.get("rope_scaling")) or {} + + rope_theta = self.find_hparam(["global_rope_theta", "rope_global_theta", "rope_theta_global", "rope_theta", "rotary_emb_base"], optional=True) + local_rope_theta = self.find_hparam(["local_rope_theta", "rope_local_theta", "rope_theta_local", "swa_rope_theta", "rope_local_base_freq"], optional=True) + + # Ensure "rope_theta" and "rope_type" is mirrored in rope_parameters + if "full_attention" not in self.rope_parameters and "sliding_attention" not in self.rope_parameters: + if local_rope_theta is not None: + self.rope_parameters["sliding_attention"] = {"rope_theta": local_rope_theta} + if "rope_theta" not in self.rope_parameters and rope_theta is not None: + self.rope_parameters["rope_theta"] = rope_theta + if "rope_type" not in self.rope_parameters and (rope_type := self.rope_parameters.get("type")) is not None: + self.rope_parameters["rope_type"] = rope_type + + @classmethod + def __init_subclass__(cls): + # can't use an abstract property, because overriding it without type errors + # would require using decorated functions instead of simply defining the property + if "model_arch" not in cls.__dict__: + raise TypeError(f"Missing property 'model_arch' for {cls.__name__!r}") + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip multimodal tensors + if name.startswith(("mlp", "vit.", "vpm.", "siglip2.", "conformer.", "merger.", "resampler.", "sound_encoder.", "sound_projection.", "speech_embeddings.")) \ + or "visual." in name or "vision." in name or "audio." in name or "talker." in name \ + or "vision_" in name or "audio_" in name or "sam_model" in name \ + or "token2wav." in name or "code2wav." in name \ + or "projector." in name or "pre_mm_projector_norm" in name \ + or "image_newline" in name or "view_seperator" in name \ + or "patch_embed" in name or "patch_embedding" in name \ + or "patch_merger." in name or "model.connector." in name: + return None + + return super().filter_tensors(item) + + def set_vocab(self): + self._set_vocab_gpt2() + + def prepare_metadata(self, vocab_only: bool): + super().prepare_metadata(vocab_only=vocab_only) + + total_params = self.gguf_writer.get_total_parameter_count()[0] + # Extract the encoding scheme from the file type name. e.g. 'gguf.LlamaFileType.MOSTLY_Q8_0' --> 'Q8_0' + output_type: str = self.ftype.name.partition("_")[2] + + # Filename Output + if self.fname_out.is_dir(): + # Generate default filename based on model specification and available metadata + if not vocab_only: + fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, self.metadata.size_label, output_type, model_type="LoRA" if total_params < 0 else None) + else: + fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, size_label=None, output_type=None, model_type="vocab") + + # Use the default filename + self.fname_out = self.fname_out / f"{fname_default}.gguf" + else: + # Output path is a custom defined templated filename + # Note: `not is_dir()` is used because `.is_file()` will not detect + # file template strings as it doesn't actually exist as a file + + # Process templated file name with the output ftype, useful with the "auto" ftype + self.fname_out = self.fname_out.parent / gguf.fill_templated_filename(self.fname_out.name, output_type) + + logger.info("Set model tokenizer") + self.set_vocab() + + def set_gguf_parameters(self): + self.gguf_writer.add_block_count(self.block_count) + + if (n_ctx := self.find_hparam(["max_position_embeddings", "n_ctx", "n_positions", "max_length", "max_sequence_length", "model_max_length"], optional=True)) is not None: + self.gguf_writer.add_context_length(n_ctx) + logger.info(f"gguf: context length = {n_ctx}") + + if (n_embd := self.find_hparam(["hidden_size", "n_embd", "dim"], optional=True)) is not None: + self.gguf_writer.add_embedding_length(n_embd) + logger.info(f"gguf: embedding length = {n_embd}") + + if (n_ff := self.find_hparam(["intermediate_size", "n_inner", "hidden_dim"], optional=True)) is not None: + self.gguf_writer.add_feed_forward_length(n_ff) + logger.info(f"gguf: feed forward length = {n_ff}") + + if (n_head := self.find_hparam(["num_attention_heads", "n_head", "n_heads"], optional=True)) is not None: + self.gguf_writer.add_head_count(n_head) + logger.info(f"gguf: head count = {n_head}") + + if (n_head_kv := self.find_hparam(["num_key_value_heads", "n_kv_heads"], optional=True)) is not None: + self.gguf_writer.add_head_count_kv(n_head_kv) + logger.info(f"gguf: key-value head count = {n_head_kv}") + + if self.hparams.get("is_causal") is False: + self.gguf_writer.add_causal_attention(False) + logger.info("gguf: causal attention = False") + + # TODO: Handle "sliding_attention" similarly when models start implementing it + rope_params = self.rope_parameters.get("full_attention", self.rope_parameters) + if (rope_type := rope_params.get("rope_type")) is not None: + rope_factor = rope_params.get("factor") + rope_gguf_type = gguf.RopeScalingType.NONE + if rope_type == "linear" and rope_factor is not None: + rope_gguf_type = gguf.RopeScalingType.LINEAR + self.gguf_writer.add_rope_scaling_type(rope_gguf_type) + self.gguf_writer.add_rope_scaling_factor(rope_factor) + elif rope_type == "yarn" and rope_factor is not None: + rope_gguf_type = gguf.RopeScalingType.YARN + self.gguf_writer.add_rope_scaling_type(rope_gguf_type) + self.gguf_writer.add_rope_scaling_factor(rope_factor) + self.gguf_writer.add_rope_scaling_orig_ctx_len(rope_params["original_max_position_embeddings"]) + if (yarn_ext_factor := rope_params.get("extrapolation_factor")) is not None: + self.gguf_writer.add_rope_scaling_yarn_ext_factor(yarn_ext_factor) + if (yarn_attn_factor := rope_params.get("attention_factor", rope_params.get("attn_factor"))) is not None: + self.gguf_writer.add_rope_scaling_yarn_attn_factor(yarn_attn_factor) + if (yarn_beta_fast := rope_params.get("beta_fast")) is not None: + self.gguf_writer.add_rope_scaling_yarn_beta_fast(yarn_beta_fast) + if (yarn_beta_slow := rope_params.get("beta_slow")) is not None: + self.gguf_writer.add_rope_scaling_yarn_beta_slow(yarn_beta_slow) + # self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"]) + elif rope_type == "su" or rope_type == "longrope": + rope_gguf_type = gguf.RopeScalingType.LONGROPE + self.gguf_writer.add_rope_scaling_type(rope_gguf_type) + elif rope_type == "dynamic": + # HunYuan, handled in model class + pass + elif rope_type.lower() == "llama3": + # Handled in generate_extra_tensors + pass + else: + logger.warning(f"Unknown RoPE type: {rope_type}") + logger.info(f"gguf: rope scaling type = {rope_gguf_type.name}") + + if "mrope_section" in self.rope_parameters: + mrope_section = self.rope_parameters["mrope_section"] + # Pad to 4 dimensions [time, height, width, extra] + while len(mrope_section) < 4: + mrope_section.append(0) + self.gguf_writer.add_rope_dimension_sections(mrope_section[:4]) + logger.info(f"gguf: mrope sections: {mrope_section[:4]}") + + if (rope_theta := rope_params.get("rope_theta")) is not None: + self.gguf_writer.add_rope_freq_base(rope_theta) + logger.info(f"gguf: rope theta = {rope_theta}") + if (local_rope_theta := self.rope_parameters.get("sliding_attention", {}).get("rope_theta")) is not None: + self.gguf_writer.add_rope_freq_base_swa(local_rope_theta) + logger.info(f"gguf: rope theta swa = {local_rope_theta}") + if (f_rms_eps := self.find_hparam(["rms_norm_eps", "norm_eps"], optional=True)) is not None: + self.gguf_writer.add_layer_norm_rms_eps(f_rms_eps) + logger.info(f"gguf: rms norm epsilon = {f_rms_eps}") + if (f_norm_eps := self.find_hparam(["layer_norm_eps", "layer_norm_epsilon", "norm_epsilon"], optional=True)) is not None: + self.gguf_writer.add_layer_norm_eps(f_norm_eps) + logger.info(f"gguf: layer norm epsilon = {f_norm_eps}") + if (n_experts := self.find_hparam(["num_local_experts", "num_experts"], optional=True)) is not None: + self.gguf_writer.add_expert_count(n_experts) + logger.info(f"gguf: expert count = {n_experts}") + if (n_experts_used := self.find_hparam(["num_experts_per_tok", "num_experts_per_token", "top_k_experts"], optional=True)) is not None: + self.gguf_writer.add_expert_used_count(n_experts_used) + logger.info(f"gguf: experts used count = {n_experts_used}") + if (n_expert_groups := self.hparams.get("n_group")) is not None: + self.gguf_writer.add_expert_group_count(n_expert_groups) + logger.info(f"gguf: expert groups count = {n_expert_groups}") + if (n_group_used := self.hparams.get("topk_group")) is not None: + self.gguf_writer.add_expert_group_used_count(n_group_used) + logger.info(f"gguf: expert groups used count = {n_group_used}") + + if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func"], optional=True)) is not None: + if score_func == "sigmoid": + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) + elif score_func == "softmax": + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SOFTMAX) + else: + raise ValueError(f"Unsupported expert score gating function value: {score_func}") + logger.info(f"gguf: expert score gating function = {score_func}") + + if (head_dim := self.hparams.get("head_dim")) is not None: + self.gguf_writer.add_key_length(head_dim) + self.gguf_writer.add_value_length(head_dim) + + self.gguf_writer.add_file_type(self.ftype) + logger.info(f"gguf: file type = {self.ftype}") + + def write_vocab(self): + if len(self.gguf_writer.tensors) != 1: + raise ValueError('Splitting the vocabulary is not supported') + + self.prepare_metadata(vocab_only=True) + self.gguf_writer.write_header_to_file(path=self.fname_out) + self.gguf_writer.write_kv_data_to_file() + self.gguf_writer.close() + + def does_token_look_special(self, token: str | bytes) -> bool: + if isinstance(token, (bytes, bytearray)): + token_text = token.decode(encoding="utf-8") + elif isinstance(token, memoryview): + token_text = token.tobytes().decode(encoding="utf-8") + else: + token_text = token + + # Some models mark some added tokens which ought to be control tokens as not special. + # (e.g. command-r, command-r-plus, deepseek-coder, gemma{,-2}) + seems_special = token_text in ( + "", # deepseek-coder + "", "<2mass>", "[@BOS@]", # gemma{,-2} + ) + + seems_special = seems_special or (token_text.startswith("<|") and token_text.endswith("|>")) + seems_special = seems_special or (token_text.startswith("<|") and token_text.endswith("|>")) # deepseek-coder + + # TODO: should these be marked as UNUSED instead? (maybe not) + seems_special = seems_special or (token_text.startswith("")) # gemma{,-2} + + return seems_special + + # used for GPT-2 BPE and WordPiece vocabs + def get_vocab_base(self) -> tuple[list[str], list[int], str]: + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + vocab_size = self.hparams.get("vocab_size", len(tokenizer.vocab)) # ty: ignore[unresolved-attribute] + assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute] + + tokpre = self.get_vocab_base_pre(tokenizer) + + reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token: str = reverse_vocab[i] + if token in added_vocab: + # The tokenizer in llama.cpp assumes the CONTROL and USER_DEFINED tokens are pre-normalized. + # To avoid unexpected issues - we make sure to normalize non-normalized tokens + if not added_tokens_decoder[i].normalized: + previous_token = token + token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] + if previous_token != token: + logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") + + if added_tokens_decoder[i].special or self.does_token_look_special(token): + toktypes.append(gguf.TokenType.CONTROL) + else: + # NOTE: this was added for Gemma. + # Encoding and decoding the tokens above isn't sufficient for this case. + token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces + toktypes.append(gguf.TokenType.USER_DEFINED) + else: + toktypes.append(gguf.TokenType.NORMAL) + tokens.append(token) + + return tokens, toktypes, tokpre + + # NOTE: this function is generated by convert_hf_to_gguf_update.py + # do not modify it manually! + # ref: https://github.com/ggml-org/llama.cpp/pull/6920 + # Marker: Start get_vocab_base_pre + def get_vocab_base_pre(self, tokenizer) -> str: + # encoding this string and hashing the resulting tokens would (hopefully) give us a unique identifier that + # is specific for the BPE pre-tokenizer used by the model + # we will use this unique identifier to write a "tokenizer.ggml.pre" entry in the GGUF file which we can + # use in llama.cpp to implement the same pre-tokenizer + + chktxt = '\n \n\n \n\n\n \t \t\t \t\n \n \n \n \n🚀 (normal) 😶\u200d🌫️ (multiple emojis concatenated) ✅ 🦙🦙 3 33 333 3333 33333 333333 3333333 33333333 3.3 3..3 3...3 កាន់តែពិសេសអាច😁 ?我想在apple工作1314151天~ ------======= нещо на Български \'\'\'\'\'\'```````""""......!!!!!!?????? I\'ve been \'told he\'s there, \'RE you sure? \'M not sure I\'ll make it, \'D you like some tea? We\'Ve a\'lL' + + chktok = tokenizer.encode(chktxt) + chkhsh = sha256(str(chktok).encode()).hexdigest() + + logger.debug(f"chktok: {chktok}") + logger.debug(f"chkhsh: {chkhsh}") + + res = None + + # NOTE: if you get an error here, you need to update the convert_hf_to_gguf_update.py script + # or pull the latest version of the model from Huggingface + # don't edit the hashes manually! + if chkhsh == "b6e8e1518dc4305be2fe39c313ed643381c4da5db34a98f6a04c093f8afbe99b": + # ref: https://huggingface.co/THUDM/glm-4-9b-chat + res = "chatglm-bpe" + if chkhsh == "81d72c7348a9f0ebe86f23298d37debe0a5e71149e29bd283904c02262b27516": + # ref: https://huggingface.co/THUDM/glm-4-9b-chat + res = "chatglm-bpe" + if chkhsh == "a1336059768a55c99a734006ffb02203cd450fed003e9a71886c88acf24fdbc2": + # ref: https://huggingface.co/THUDM/glm-4-9b-hf + res = "glm4" + if chkhsh == "9ca2dd618e8afaf09731a7cf6e2105b373ba6a1821559f258b272fe83e6eb902": + # ref: https://huggingface.co/zai-org/GLM-4.5-Air + res = "glm4" + if chkhsh == "cdf5f35325780597efd76153d4d1c16778f766173908894c04afc20108536267": + # ref: https://huggingface.co/zai-org/GLM-4.7-Flash + res = "glm4" + if chkhsh == "1431a23e583c97432bc230bff598d103ddb5a1f89960c8f1d1051aaa944d0b35": + # ref: https://huggingface.co/sapienzanlp/Minerva-7B-base-v1.0 + res = "minerva-7b" + if chkhsh == "7e57df22b1fe23a7b1e1c7f3dc4e3f96d43a4eb0836d0c6bdc3436d7b2f1c664": + # ref: https://huggingface.co/tencent/Hunyuan-A13B-Instruct + res = "hunyuan" + if chkhsh == "bba3b3366b646dbdded5dbc42d59598b849371afc42f7beafa914afaa5b70aa6": + # ref: https://huggingface.co/tencent/Hunyuan-4B-Instruct + res = "hunyuan-dense" + if chkhsh == "a6b57017d60e6edb4d88ecc2845188e0eb333a70357e45dcc9b53964a73bbae6": + # ref: https://huggingface.co/tiiuae/Falcon-H1-0.5B-Base + res = "falcon-h1" + if chkhsh == "60476e1243776c4fb1b993dbd7a5f15ac22f83c80afdf425fa5ae01c8d44ef86": + # ref: https://huggingface.co/tiiuae/Falcon-H1-1B-Base + res = "falcon-h1" + if chkhsh == "3eda48b4c4dc7de733d1a8b3e3b4a85243dbbf704da2ee9d42c6beced8897896": + # ref: https://huggingface.co/tiiuae/Falcon-H1-7B-Base + res = "falcon-h1" + if chkhsh == "48f8e02c0359c0bbdd82f26909171fac1c18a457bb47573ed1fe3bbb2c1cfd4b": + # ref: https://huggingface.co/tiiuae/Falcon-H1-34B-Base + res = "falcon-h1" + if chkhsh == "81212dc7cdb7e0c1074ca62c5aeab0d43c9f52b8a737be7b12a777c953027890": + # ref: https://huggingface.co/moonshotai/Kimi-K2-Base + res = "kimi-k2" + if chkhsh == "d4540891389ea895b53b399da6ac824becc30f2fba0e9ddbb98f92e55ca0e97c": + # ref: https://huggingface.co/Qwen/Qwen3-Embedding-0.6B + res = "qwen2" + if chkhsh == "1444df51289cfa8063b96f0e62b1125440111bc79a52003ea14b6eac7016fd5f": + # ref: https://huggingface.co/openbmb/MiniCPM-V-4_6 + res = "qwen35" + if chkhsh == "66b8d4e19ab16c3bfd89bce5d785fb7e0155e8648708a1f42077cb9fe002c273": + # ref: https://huggingface.co/alvarobartt/grok-2-tokenizer + res = "grok-2" + if chkhsh == "b3d1dd861f1d4c5c0d2569ce36baf3f90fe8a102db3de50dd71ff860d91be3df": + # ref: https://huggingface.co/aari1995/German_Semantic_V3 + res = "jina-v2-de" + if chkhsh == "0fe1cf6eda062318a1af7270f3331a85c539a01778ff948e24388e949c5282f4": + # ref: https://huggingface.co/evilfreelancer/ruGPT3XL + res = "gpt-2" + if chkhsh == "0ef9807a4087ebef797fc749390439009c3b9eda9ad1a097abbe738f486c01e5": + # ref: https://huggingface.co/meta-llama/Meta-Llama-3-8B + res = "llama-bpe" + if chkhsh == "049ecf7629871e3041641907f3de7c733e4dbfdc736f57d882ba0b0845599754": + # ref: https://huggingface.co/deepseek-ai/deepseek-llm-7b-base + res = "deepseek-llm" + if chkhsh == "347715f544604f9118bb75ed199f68779f423cabb20db6de6f31b908d04d7821": + # ref: https://huggingface.co/deepseek-ai/deepseek-coder-6.7b-base + res = "deepseek-coder" + if chkhsh == "8aeee3860c56296a157a1fe2fad249ec40aa59b1bb5709f4ade11c4e6fe652ed": + # ref: https://huggingface.co/tiiuae/falcon-7b + res = "falcon" + if chkhsh == "0876d13b50744004aa9aeae05e7b0647eac9d801b5ba4668afc01e709c15e19f": + # ref: https://huggingface.co/BAAI/bge-small-en-v1.5 + res = "bert-bge" + if chkhsh == "9d032fcbd5501f4a38150912590928bfb36091efb5df11b8e2124b0390e3fb1e": + # ref: https://huggingface.co/tiiuae/Falcon3-7B-Base + res = "falcon3" + if chkhsh == "8e62295832751ca1e8f92f2226f403dea30dc5165e448b5bfa05af5340c64ec7": + # ref: https://huggingface.co/BAAI/bge-large-zh-v1.5 + res = "bert-bge-large" + if chkhsh == "b6dc8df998e1cfbdc4eac8243701a65afe638679230920b50d6f17d81c098166": + # ref: https://huggingface.co/mosaicml/mpt-7b + res = "mpt" + if chkhsh == "35d91631860c815f952d711435f48d356ebac988362536bed955d43bfa436e34": + # ref: https://huggingface.co/bigcode/starcoder2-3b + res = "starcoder" + if chkhsh == "3ce83efda5659b07b1ad37ca97ca5797ea4285d9b9ab0dc679e4a720c9da7454": + # ref: https://huggingface.co/openai-community/gpt2 + res = "gpt-2" + if chkhsh == "32d85c31273f8019248f2559fed492d929ea28b17e51d81d3bb36fff23ca72b3": + # ref: https://huggingface.co/stabilityai/stablelm-2-zephyr-1_6b + res = "stablelm2" + if chkhsh == "6221ad2852e85ce96f791f476e0b390cf9b474c9e3d1362f53a24a06dc8220ff": + # ref: https://huggingface.co/smallcloudai/Refact-1_6-base + res = "refact" + if chkhsh == "9c2227e4dd922002fb81bde4fc02b0483ca4f12911410dee2255e4987644e3f8": + # ref: https://huggingface.co/CohereForAI/c4ai-command-r-v01 + res = "command-r" + if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1": + # ref: https://huggingface.co/CohereLabs/tiny-aya-base + res = "tiny_aya" + if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea": + # ref: https://huggingface.co/Qwen/Qwen1.5-7B + res = "qwen2" + if chkhsh == "b6dc8df998e1cfbdc4eac8243701a65afe638679230920b50d6f17d81c098166": + # ref: https://huggingface.co/allenai/OLMo-1.7-7B-hf + res = "olmo" + if chkhsh == "a8594e3edff7c29c003940395316294b2c623e09894deebbc65f33f1515df79e": + # ref: https://huggingface.co/databricks/dbrx-base + res = "dbrx" + if chkhsh == "c7699093ba4255a91e702aa38a596aa81669f3525dae06c2953267dde580f448": + # ref: https://huggingface.co/jinaai/jina-reranker-v1-tiny-en + res = "jina-v1-en" + if chkhsh == "0876d13b50744004aa9aeae05e7b0647eac9d801b5ba4668afc01e709c15e19f": + # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-en + res = "jina-v2-en" + if chkhsh == "171aeeedd6fb548d418a7461d053f11b6f1f1fc9b387bd66640d28a4b9f5c643": + # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-es + res = "jina-v2-es" + if chkhsh == "27949a2493fc4a9f53f5b9b029c82689cfbe5d3a1929bb25e043089e28466de6": + # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-de + res = "jina-v2-de" + if chkhsh == "a023e9fdc5a11f034d3ef515b92350e56fb2af1f66c6b6811a4444ea9bf8763d": + # ref: https://huggingface.co/jinaai/jina-embeddings-v5-text-nano + res = "jina-v5-nano" + if chkhsh == "c136ed14d01c2745d4f60a9596ae66800e2b61fa45643e72436041855ad4089d": + # ref: https://huggingface.co/abacusai/Smaug-Llama-3-70B-Instruct + res = "smaug-bpe" + if chkhsh == "c7ea5862a53e4272c035c8238367063e2b270d51faa48c0f09e9d5b54746c360": + # ref: https://huggingface.co/LumiOpen/Poro-34B-chat + res = "poro-chat" + if chkhsh == "7967bfa498ade6b757b064f31e964dddbb80f8f9a4d68d4ba7998fcf281c531a": + # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-code + res = "jina-v2-code" + if chkhsh == "7fc505bd3104ca1083b150b17d088b59534ede9bde81f0dd2090967d7fe52cee": + # ref: https://huggingface.co/LumiOpen/Viking-7B + res = "viking" + if chkhsh == "b53802fb28e26d645c3a310b34bfe07da813026ec7c7716883404d5e0f8b1901": + # ref: https://huggingface.co/core42/jais-13b + res = "jais" + if chkhsh == "bc5108ee1eb6a3d600cadd065f63190fbd0554dbc9e4bbd6a0d977970afc8d2a": + # ref: https://huggingface.co/inceptionai/Jais-2-8B-Chat + res = "jais-2" + if chkhsh == "7b3e7548e4308f52a76e8229e4e6cc831195d0d1df43aed21ac6c93da05fec5f": + # ref: https://huggingface.co/WisdomShell/CodeShell-7B + res = "codeshell" + if chkhsh == "63b97e4253352e6f357cc59ea5b583e3a680eaeaf2632188c2b952de2588485e": + # ref: https://huggingface.co/mistralai/Mistral-Nemo-Base-2407 + res = "tekken" + if chkhsh == "855059429035d75a914d1eda9f10a876752e281a054a7a3d421ef0533e5b6249": + # ref: https://huggingface.co/HuggingFaceTB/SmolLM-135M + res = "smollm" + if chkhsh == "3c30d3ad1d6b64202cd222813e7736c2db6e1bd6d67197090fc1211fbc612ae7": + # ref: https://huggingface.co/bigscience/bloom + res = "bloom" + if chkhsh == "bc01ce58980e1db43859146dc51b1758b3b88729b217a74792e9f8d43e479d21": + # ref: https://huggingface.co/TurkuNLP/gpt3-finnish-small + res = "gpt3-finnish" + if chkhsh == "4e2b24cc4770243d65a2c9ec19770a72f08cffc161adbb73fcbb6b7dd45a0aae": + # ref: https://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct + res = "exaone" + if chkhsh == "fcace8b9cac38ce847670c970cd5892031a753a1ef381abd1d9af00f713da085": + # ref: https://huggingface.co/microsoft/phi-2 + res = "phi-2" + if chkhsh == "60824e3c0d9401f89943cbb2fff727f0e2d4c545ba4df2d6e4f09a6db0f5b450": + # ref: https://huggingface.co/facebook/chameleon-7b + res = "chameleon" + if chkhsh == "8b5a93ed704057481f240da0be7e7dca721d7f8f4755263b6807227a2cbeae65": + # ref: https://huggingface.co/sentence-transformers/stsb-roberta-base + res = "roberta-bpe" + if chkhsh == "ad851be1dba641f2e3711822f816db2c265f788b37c63b4e1aeacb9ee92de8eb": + # ref: https://huggingface.co/ai-sage/GigaChat-20B-A3B-instruct + res = "gigachat" + if chkhsh == "d4c8f286ea6b520b3d495c4455483cfa2302c0cfcd4be05d781b6a8a0a7cdaf1": + # ref: https://huggingface.co/Infinigence/Megrez-3B-Instruct + res = "megrez" + if chkhsh == "877081d19cf6996e2c4ff0e1236341e9b7bde288f5311a56a937f0afbbb3aeb5": + # ref: https://huggingface.co/deepseek-ai/DeepSeek-V3 + res = "deepseek-v3" + if chkhsh == "b3f499bb4255f8ca19fccd664443283318f2fd2414d5e0b040fbdd0cc195d6c5": + # ref: https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B + res = "deepseek-r1-qwen" + if chkhsh == "ccc2ef013c104be7bae2965776d611e1d7a8a2a9c547dd93a682c9a9fc80352e": + # ref: https://huggingface.co/Xenova/gpt-4o + res = "gpt-4o" + if chkhsh == "7dec86086fcc38b66b7bc1575a160ae21cf705be7718b9d5598190d7c12db76f": + # ref: https://huggingface.co/UW/OLMo2-8B-SuperBPE-t180k + res = "superbpe" + if chkhsh == "1994ffd01900cfb37395608534236ecd63f2bd5995d6cb1004dda1af50240f15": + # ref: https://huggingface.co/trillionlabs/Trillion-7B-preview + res = "trillion" + if chkhsh == "96a5f08be6259352137b512d4157e333e21df7edd3fcd152990608735a65b224": + # ref: https://huggingface.co/inclusionAI/Ling-lite + res = "bailingmoe" + if chkhsh == "d353350c764d8c3b39c763113960e4fb4919bea5fbf208a0e3b22e8469dc7406": + # ref: https://huggingface.co/meta-llama/Llama-4-Scout-17B-16E-Instruct + res = "llama4" + if chkhsh == "0e9433cbbb161f89e264eb32e8e64bfe69e834973ffca5d41d3948a604a3e2a3": + # ref: https://huggingface.co/mistral-community/pixtral-12b + res = "pixtral" + if chkhsh == "d5f1dd6f980fec569fb218a81a7658ac45fc56b38c5a0adeb1c232fbe04ef5ec": + # ref: https://huggingface.co/ByteDance-Seed/Seed-Coder-8B-Base + res = "seed-coder" + if chkhsh == "b0a6b1c0bd5998ebd9df08611efde34a4ff03faed45ae09c43e6b31ebd4b94cf": + # ref: https://huggingface.co/skt/A.X-4.0 + res = "a.x-4.0" + if chkhsh == "f6791d196f87ce6b56a7d234be618e0d58f8cda3549416635b2bebcd22cd95c4": + # ref: https://huggingface.co/K-intelligence/Midm-2.0-Base-Instruct + res = "midm-2.0" + if chkhsh == "169bf0296a13c4d9b7672313f749eb36501d931022de052aad6e36f2bf34dd51": + # ref: https://huggingface.co/LiquidAI/LFM2-Tokenizer + res = "lfm2" + if chkhsh == "2085e1638f6c377a0aa4ead21b27bb4cb941bf800df86ed391011769c1758dfb": + # ref: https://huggingface.co/LGAI-EXAONE/EXAONE-4.0-32B + res = "exaone4" + if chkhsh == "a1e163ecab2e718a4c829d1148b6e86824ec36163bb71941c3dca9cd5ac25756": + # ref: https://huggingface.co/JetBrains/Mellum-4b-base + res = "mellum" + if chkhsh == "a0b64b4385f123663873756336c085744376d015ff328bb1d901598f63c44152": + # ref: https://huggingface.co/answerdotai/ModernBERT-base + res = "modern-bert" + if chkhsh == "49fc0303c9e0d2c2c565c510f64b2d9b271276acdcdadff733249eda9f7d59df": + # ref: https://huggingface.co/arcee-ai/Trinity-Tokenizer + res = "afmoe" + if chkhsh == "9b1be57e70d20d9501b2b3186e792d81181ae36ada3903c26f9fea418cf87206": + # ref: https://huggingface.co/inclusionAI/Ling-mini-base-2.0 + res = "bailingmoe2" + if chkhsh == "53e325976a6e142379c19b09afcae354f2f496f147afa8f9e189a33fe4e3024e": + # ref: https://huggingface.co/ibm-granite/granite-docling-258M + res = "granite-docling" + if chkhsh == "f4f37b6c8eb9ea29b3eac6bb8c8487c5ab7885f8d8022e67edc1c68ce8403e95": + # ref: https://huggingface.co/MiniMaxAI/MiniMax-M2 + res = "minimax-m2" + if chkhsh == "4a2e2abae11ca2b86d570fc5b44be4d5eb5e72cc8f22dd136a94b37da83ab665": + # ref: https://huggingface.co/KORMo-Team/KORMo-tokenizer + res = "kormo" + if chkhsh == "9d70134b369a70e5735009b6de918f7581b5211f7c074d1f89f753aea8248af1": + # ref: https://huggingface.co/tencent/Youtu-LLM-2B + res = "youtu" + if chkhsh == "16389f0a1f51ee53e562ffd51c371dc508639ab0e4261502071836e50e223e91": + # ref: https://huggingface.co/upstage/Solar-Open-100B + res = "solar-open" + if chkhsh == "6c81ce329e0802883b22eabab0d3fa48357337ef1ecb45443828bf1f6254833f": + # ref: https://huggingface.co/LGAI-EXAONE/K-EXAONE-236B-A23B + res = "exaone-moe" + if chkhsh == "d30d75d9059f1aa2c19359de71047b3ae408c70875e8a3ccf8c5fba56c9d8af4": + # ref: https://huggingface.co/Qwen/Qwen3.5-9B-Instruct + res = "qwen35" + if chkhsh == "b4b8ca1f9769494fbd956ebc4c249de6131fb277a4a3345a7a92c7dd7a55808d": + # ref: https://huggingface.co/jdopensource/JoyAI-LLM-Flash + res = "joyai-llm" + if chkhsh == "e4d54df1ebc1f2b91acd986c5b51aa50837d5faf7c7398e73c1f9e9ee5d19869": + # ref: https://huggingface.co/kakaocorp/kanana-2-30b-a3b-instruct-2601 + res = "kanana2" + if chkhsh == "862f827721df956049dff5ca81a57f29e575280bc622e290d3bf4e35eca29015": + # ref: https://huggingface.co/codefuse-ai/F2LLM-v2-4B + res = "f2llmv2" + if chkhsh == "62f6fb0a6fd5098caeabb19b07a5c1099cafc8b9c40eab6ea89ece4ec02fbc57": + # ref: https://huggingface.co/sarvamai/sarvam-30b + res = "sarvam-moe" + + if res is None: + logger.warning("\n") + logger.warning("**************************************************************************************") + logger.warning("** WARNING: The BPE pre-tokenizer was not recognized!") + logger.warning("** There are 2 possible reasons for this:") + logger.warning("** - the model has not been added to convert_hf_to_gguf_update.py yet") + logger.warning("** - the pre-tokenization config has changed upstream") + logger.warning("** Check your model files and convert_hf_to_gguf_update.py and update them accordingly.") + logger.warning("** ref: https://github.com/ggml-org/llama.cpp/pull/6920") + logger.warning("**") + logger.warning(f"** chkhsh: {chkhsh}") + logger.warning("**************************************************************************************") + logger.warning("\n") + raise NotImplementedError("BPE pre-tokenizer was not recognized - update get_vocab_base_pre()") + + logger.debug(f"tokenizer.ggml.pre: {repr(res)}") + logger.debug(f"chkhsh: {chkhsh}") + + return res + # Marker: End get_vocab_base_pre + + def _set_vocab_none(self) -> None: + self.gguf_writer.add_tokenizer_model("none") + + def _set_vocab_gpt2(self) -> None: + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_qwen(self): + from .qwen import QwenModel + + dir_model = self.dir_model + hparams = self.hparams + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) + vocab_size = hparams["vocab_size"] + assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] + + tokpre = self.get_vocab_base_pre(tokenizer) + + merges = [] + vocab = {} + mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] + for token, rank in mergeable_ranks.items(): + vocab[QwenModel.token_bytes_to_string(token)] = rank + if len(token) == 1: + continue + merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) + assert len(merged) == 2 + merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) + + # for this kind of tokenizer, added_vocab is not a subset of vocab, so they need to be combined + added_vocab = tokenizer.special_tokens # ty: ignore[unresolved-attribute] + reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **added_vocab}.items()} + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + elif reverse_vocab[i] in added_vocab: + tokens.append(reverse_vocab[i]) + toktypes.append(gguf.TokenType.CONTROL) + else: + tokens.append(reverse_vocab[i]) + toktypes.append(gguf.TokenType.NORMAL) + + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(dir_model, load_merges=False) + special_vocab.merges = merges + # only add special tokens when they were not already loaded from config.json + if len(special_vocab.special_token_ids) == 0: + special_vocab._set_special_token("bos", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("eos", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + # this one is usually not in config.json anyway + special_vocab._set_special_token("unk", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_sentencepiece(self, add_to_gguf=True): + tokens, scores, toktypes = self._create_vocab_sentencepiece() + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def _create_vocab_sentencepiece(self): + from sentencepiece import SentencePieceProcessor + + tokenizer_path = self.dir_model / 'tokenizer.model' + + if not tokenizer_path.is_file(): + raise FileNotFoundError(f"File not found: {tokenizer_path}") + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.find_hparam([ + "vocab_size_per_layer_input", # gemma3n + "vocab_size", + ], optional=True) or tokenizer.vocab_size() + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + for token_id in range(tokenizer.vocab_size()): + if token_id >= vocab_size: + logger.warning(f'ignore tokens from {token_id}: id is out of range, max={vocab_size - 1}') + break + + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + added_tokens_file = self.dir_model / 'added_tokens.json' + if added_tokens_file.is_file(): + with open(added_tokens_file, "r", encoding="utf-8") as f: + added_tokens_json = json.load(f) + for key in added_tokens_json: + token_id = added_tokens_json[key] + if token_id >= vocab_size: + logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + + tokens[token_id] = key.encode("utf-8") + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) + for token_id, token_data in added_tokens_decoder.items(): + token_id = int(token_id) + token: str = token_data["content"] + if token_id >= vocab_size: + logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: + if tokens[token_id] != token.encode("utf-8"): + logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token!r}') + if token_data.get("special") or self.does_token_look_special(token): + toktypes[token_id] = SentencePieceTokenTypes.CONTROL + else: + token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + + scores[token_id] = -1000.0 + tokens[token_id] = token.encode("utf-8") + + if vocab_size > len(tokens): + pad_count = vocab_size - len(tokens) + logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") + for i in range(1, pad_count + 1): + tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) + scores.append(-1000.0) + toktypes.append(SentencePieceTokenTypes.UNUSED) + + return tokens, scores, toktypes + + def _set_vocab_llama_hf(self): + vocab = gguf.LlamaHfVocab(self.dir_model) + tokens = [] + scores = [] + toktypes = [] + + for text, score, toktype in vocab.all_tokens(): + tokens.append(text) + scores.append(score) + toktypes.append(toktype) + + assert len(tokens) == vocab.vocab_size + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_rwkv_world(self): + assert (self.dir_model / "rwkv_vocab_v20230424.txt").is_file() + vocab_size = self.hparams.get("vocab_size", 65536) + + tokens: list[bytes] = [''.encode("utf-8")] + toktypes: list[int] = [gguf.TokenType.CONTROL] + + with open(self.dir_model / "rwkv_vocab_v20230424.txt", "r", encoding="utf-8") as f: + lines = f.readlines() + for line in lines: + parts = line.split(' ') + assert len(parts) >= 3 + token, token_len = ast.literal_eval(' '.join(parts[1:-1])), int(parts[-1]) + token = token.encode("utf-8") if isinstance(token, str) else token + assert isinstance(token, bytes) + assert len(token) == token_len + token_text: str = repr(token)[2:-1] # "b'\xff'" -> "\xff" + tokens.append(token_text.encode("utf-8")) + toktypes.append(gguf.TokenType.NORMAL) + remainder = vocab_size - len(tokens) + assert remainder >= 0 + for i in range(len(tokens), vocab_size): + tokens.append(f"[PAD{i}]".encode("utf-8")) + toktypes.append(gguf.TokenType.UNUSED) + + self.gguf_writer.add_tokenizer_model("rwkv") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) + if special_vocab.chat_template is None: + template_path = Path(__file__).parent.parent / "models" / "templates" / "llama-cpp-rwkv-world.jinja" + if template_path.is_file(): + with open(template_path, "r", encoding="utf-8") as f: + template = f.read() + else: + template = "rwkv-world" + special_vocab.chat_template = template + # hack: Add '\n\n' as the EOT token to make it chat normally + special_vocab._set_special_token("eot", 261) + # hack: Override these as they have already been set (incorrectly) + special_vocab.special_token_ids["bos"] = 0 + special_vocab.special_token_ids["eos"] = 0 + + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_builtin(self, model_name: Literal["gpt-neox", "llama-spm"], vocab_size: int): + tokenizer_path = Path(sys.path[0]) / "models" / f"ggml-vocab-{model_name}.gguf" + logger.warning(f"Using tokenizer from '{os.path.relpath(tokenizer_path, os.getcwd())}'") + vocab_reader = gguf.GGUFReader(tokenizer_path, "r") + + default_pre = "mpt" if model_name == "gpt-neox" else "default" + + field = vocab_reader.get_field(gguf.Keys.Tokenizer.MODEL) + assert field # tokenizer model + self.gguf_writer.add_tokenizer_model(bytes(field.parts[-1]).decode("utf-8")) + + field = vocab_reader.get_field(gguf.Keys.Tokenizer.PRE) + self.gguf_writer.add_tokenizer_pre(bytes(field.parts[-1]).decode("utf-8") if field else default_pre) + + field = vocab_reader.get_field(gguf.Keys.Tokenizer.LIST) + assert field # token list + self.gguf_writer.add_token_list([bytes(field.parts[i]) for i in field.data][:vocab_size]) + + if model_name == "llama-spm": + field = vocab_reader.get_field(gguf.Keys.Tokenizer.SCORES) + assert field # token scores + self.gguf_writer.add_token_scores([field.parts[i].tolist()[0] for i in field.data][:vocab_size]) + + field = vocab_reader.get_field(gguf.Keys.Tokenizer.TOKEN_TYPE) + assert field # token types + self.gguf_writer.add_token_types([field.parts[i].tolist()[0] for i in field.data][:vocab_size]) + + if model_name != "llama-spm": + field = vocab_reader.get_field(gguf.Keys.Tokenizer.MERGES) + assert field # token merges + self.gguf_writer.add_token_merges([bytes(field.parts[i]) for i in field.data]) + + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.BOS_ID)) is not None: + self.gguf_writer.add_bos_token_id(field.parts[-1].tolist()[0]) + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.EOS_ID)) is not None: + self.gguf_writer.add_eos_token_id(field.parts[-1].tolist()[0]) + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.UNK_ID)) is not None: + self.gguf_writer.add_unk_token_id(field.parts[-1].tolist()[0]) + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.PAD_ID)) is not None: + self.gguf_writer.add_pad_token_id(field.parts[-1].tolist()[0]) + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.ADD_BOS)) is not None: + self.gguf_writer.add_add_bos_token(field.parts[-1].tolist()[0]) + if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.ADD_EOS)) is not None: + self.gguf_writer.add_add_eos_token(field.parts[-1].tolist()[0]) + + def _try_set_pooling_type(self) -> None: + # get pooling path + pooling_path = None + module_path = self.dir_model / "modules.json" + if module_path.is_file(): + with open(module_path, encoding="utf-8") as f: + modules = json.load(f) + for mod in modules: + if mod["type"].endswith("Pooling"): + pooling_path = mod["path"] + break + + mode_mapping = { + "mean": gguf.PoolingType.MEAN, + "cls": gguf.PoolingType.CLS, + "lasttoken": gguf.PoolingType.LAST, + } + + # get pooling type + if pooling_path is not None: + with open(self.dir_model / pooling_path / "config.json", encoding="utf-8") as f: + pooling = json.load(f) + if pooling.get("pooling_mode_mean_tokens"): + pooling_type = gguf.PoolingType.MEAN + elif pooling.get("pooling_mode_cls_token"): + pooling_type = gguf.PoolingType.CLS + elif pooling.get("pooling_mode_lasttoken"): + pooling_type = gguf.PoolingType.LAST + elif (pooling_mode := pooling.get("pooling_mode")) in mode_mapping: + pooling_type = mode_mapping[pooling_mode] + else: + raise NotImplementedError("Only MEAN, CLS, and LAST pooling types supported") + self.gguf_writer.add_pooling_type(pooling_type) + + def _set_vocab_glmedge(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_glm(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + # Special tokens + # Note: Using <|endoftext|> (151329) for eot causes endless generation + special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["[gMASK]"]) # ty: ignore[unresolved-attribute] # 151331 + special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] # 151336 + special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] # 151329 + special_vocab._set_special_token("eom", tokenizer.get_added_vocab()["<|observation|>"]) # ty: ignore[unresolved-attribute] # 151338 + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_interns1(self): + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + vocab = getattr(tokenizer, 'vocab', tokenizer.get_vocab()) # ty: ignore[unresolved-attribute] + vocab_size = self.hparams.get("vocab_size", len(vocab)) + assert max(vocab.values()) < vocab_size + + tokpre = self.get_vocab_base_pre(tokenizer) + + reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab.items()} + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token: str = reverse_vocab[i] + if token in added_vocab: + # The tokenizer in llama.cpp assumes the CONTROL and USER_DEFINED tokens are pre-normalized. + # To avoid unexpected issues - we make sure to normalize non-normalized tokens + if not added_tokens_decoder[i].normalized: + previous_token = token + token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] + if previous_token != token: + logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") + + if added_tokens_decoder[i].special or self.does_token_look_special(token): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.USER_DEFINED) + else: + toktypes.append(gguf.TokenType.NORMAL) + tokens.append(token) + + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab._set_special_token("bos", 151643) + special_vocab.add_to_gguf(self.gguf_writer) + + def _set_vocab_mistral(self): + from .mistral import MistralModel + + if not _mistral_common_installed: + raise ImportError(_mistral_import_error_msg) + + vocab = MistralVocab(self.dir_model) + logger.info( + f"Converting tokenizer {vocab.tokenizer_type} of size {vocab.vocab_size}." + ) + + self.gguf_writer.add_tokenizer_model(vocab.gguf_tokenizer_model) + + tokens = [] + scores = [] + toktypes = [] + + for text, score, toktype in vocab.all_tokens(): + tokens.append(text) + scores.append(score) + toktypes.append(toktype) + + assert len(tokens) == vocab.vocab_size, ( + f"token count ({len(tokens)}) != vocab size ({vocab.vocab_size})" + ) + + if vocab.tokenizer_type == MistralTokenizerType.tekken: + self.gguf_writer.add_tokenizer_pre("tekken") + self.gguf_writer.add_token_merges( + vocab.extract_vocab_merges_from_model() + ) + + logger.info( + f"Setting bos, eos, unk and pad token IDs to {vocab.bos_id}, {vocab.eos_id}, {vocab.unk_id}, {vocab.pad_id}." + ) + + self.gguf_writer.add_bos_token_id(vocab.bos_id) + self.gguf_writer.add_eos_token_id(vocab.eos_id) + self.gguf_writer.add_unk_token_id(vocab.unk_id) + self.gguf_writer.add_pad_token_id(vocab.pad_id) + + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_vocab_size(vocab.vocab_size) + + self.gguf_writer.add_add_bos_token(True) + self.gguf_writer.add_add_eos_token(False) + + local_template_file_path = self.dir_model / "chat_template.jinja" + + if self.is_mistral_format and local_template_file_path.is_file(): + # Ministral-3 and other new Mistral models come with chat templates. + # ref: https://huggingface.co/mistralai/Ministral-3-14B-Instruct-2512/tree/main + logger.info("Using an existing Mistral local chat template.") + + with open(local_template_file_path, "r", encoding="utf-8") as f: + template = f.read() + elif not self.is_mistral_format or not self.disable_mistral_community_chat_template: + template_dir = Path(__file__).parent.parent / "models/templates/" + + # Log only for Mistral format that the official tokenization and detokenization is via `mistral-common`. + if self.is_mistral_format: + logger.info( + "Using a Mistral community chat template. These templates can be subject to errors in early days or weeks after a release. " + "Mistral recommends to use `mistral-common` to perform tokenization and detokenization." + ) + template = MistralModel.get_community_chat_template(vocab, template_dir, self.is_mistral_format) + else: + logger.info("Not using a Mistral local or community chat template. Ensure to perform the tokenization and detokenization via `mistral-common`.") + template = None + + if template is not None: + self.gguf_writer.add_chat_template(template) + + def _set_vocab_plamo(self): + # PLaMo models use a custom tokenizer with a .jsonl file + tokenizer_jsonl_path = self.dir_model / "tokenizer.jsonl" + tokenizer_config_path = self.dir_model / "tokenizer_config.json" + + if not tokenizer_jsonl_path.is_file(): + raise FileNotFoundError(f"PLaMo tokenizer file not found: {tokenizer_jsonl_path}") + + # Load tokenizer config + with open(tokenizer_config_path, "r", encoding="utf-8") as f: + tokenizer_config = json.load(f) + + # Load tokens from JSONL file (actually a list format) + tokens = [] + scores = [] + toktypes = [] + + with open(tokenizer_jsonl_path, "r", encoding="utf-8") as f: + for line_num, line in enumerate(f): + if line.strip(): + token_data = json.loads(line) + # Format: [token, score, type, ?, ?, ?, ?] + token = token_data[0].encode("utf-8") + score = float(token_data[1]) + token_type_str = token_data[2] if len(token_data) > 2 else "NORMAL" + + tokens.append(token) + scores.append(score) + + if token_type_str == "UNKNOWN": + toktypes.append(gguf.TokenType.UNKNOWN) + elif token_type_str == "CONTROL": + toktypes.append(gguf.TokenType.CONTROL) + elif token_type_str == "BYTE": + toktypes.append(gguf.TokenType.BYTE) + else: + token_str = token_data[0] + if token_str.startswith("<|plamo:") and token_str.endswith("|>"): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.NORMAL) + + vocab_size = self.hparams["vocab_size"] + if vocab_size > len(tokens): + pad_count = vocab_size - len(tokens) + logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") + for i in range(1, pad_count + 1): + tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) + scores.append(-1000.0) + toktypes.append(gguf.TokenType.UNUSED) + + self.gguf_writer.add_tokenizer_model("plamo2") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + if "bos_token" in tokenizer_config and tokenizer_config["bos_token"] is not None: + token_id = tokens.index(tokenizer_config["bos_token"].encode("utf-8")) + self.gguf_writer.add_bos_token_id(token_id) + if "eos_token" in tokenizer_config and tokenizer_config["eos_token"] is not None: + token_id = tokens.index(tokenizer_config["eos_token"].encode("utf-8")) + self.gguf_writer.add_eos_token_id(token_id) + if "pad_token" in tokenizer_config and tokenizer_config["pad_token"] is not None: + token_id = tokens.index(tokenizer_config["pad_token"].encode("utf-8")) + self.gguf_writer.add_pad_token_id(token_id) + if "sep_token" in tokenizer_config and tokenizer_config["sep_token"] is not None: + token_id = tokens.index(tokenizer_config["sep_token"].encode("utf-8")) + self.gguf_writer.add_sep_token_id(token_id) + if "unk_token" in tokenizer_config and tokenizer_config["unk_token"] is not None: + token_id = tokens.index(tokenizer_config["unk_token"].encode("utf-8")) + self.gguf_writer.add_unk_token_id(token_id) + + # Add <|plamo:op|> as EOT to ensure appropriate end of generation + self.gguf_writer.add_eot_token_id(4) + + self.gguf_writer.add_add_space_prefix(False) + + +class MmprojModel(ModelBase): + model_type = ModelType.MMPROJ + model_arch = gguf.MODEL_ARCH.MMPROJ + preprocessor_config: dict[str, Any] + global_config: dict[str, Any] + + n_block_keys = ["n_layers", "num_hidden_layers", "n_layer", "num_layers", "depth", "layers", "encoder_layers", "vt_num_hidden_layers"] + + has_vision_encoder: bool = True # by default + has_audio_encoder: bool = False + + # for models having multiple encoders, we need to separate their hparams + hparams_vision: dict[str, Any] | None = None + hparams_audio: dict[str, Any] | None = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if self.model_arch != gguf.MODEL_ARCH.MMPROJ: + raise TypeError("MmprojModel must be subclassed with model_arch = gguf.MODEL_ARCH.MMPROJ") + + # get n_embd of the text model + if not self.is_mistral_format: + if "text_config" not in self.hparams: + self.hparams["text_config"] = {} + if "audio_config" not in self.hparams: + self.hparams["audio_config"] = {} + text_config = {**self.hparams, **self.hparams["text_config"]} + self.n_embd_text = text_config.get("hidden_size", text_config.get("n_embd", 0)) + else: + text_config = { + k: v for k, v in self.hparams.items() if k not in ["vision_encoder", "audio_encoder"] + } + # mistral native params.json: "dim" is the text hidden size ("hidden_dim" is the FFN intermediate size) + self.n_embd_text = text_config.get("dim", 0) + + assert self.n_embd_text > 0, "n_embd not found in hparams" + + # move vision config to the top level, while preserving the original hparams in global_config + import copy + self.global_config = copy.deepcopy(self.hparams) + self.hparams_vision = self.get_vision_config() + self.hparams_audio = self.get_audio_config() + + if self.hparams_vision is None and self.hparams_audio is None: + raise ValueError("vision_config / audio_config not found in hparams") + + # for compat with vision-only models + self.hparams = self.hparams_vision or self.hparams_audio or self.hparams + + # TODO @ngxson : this is a hack to support both vision and audio encoders + have_multiple_encoders = self.has_audio_encoder and self.has_vision_encoder + self.block_count = 128 if have_multiple_encoders else self.find_hparam(self.n_block_keys, True) + self.tensor_map = gguf.get_tensor_name_map(gguf.MODEL_ARCH.MMPROJ, self.block_count) + + # load preprocessor config + self.preprocessor_config = {} + + # prefer preprocessor_config.json if possible + preprocessor_config_path = self.dir_model / "preprocessor_config.json" + if preprocessor_config_path.is_file(): + with open(preprocessor_config_path, "r", encoding="utf-8") as f: + cfg = json.load(f) + # move media_proc_cfg to root level for compat + if "media_proc_cfg" in cfg: + cfg = { + **cfg, + **cfg["media_proc_cfg"], + } + # merge configs + self.preprocessor_config = {**self.preprocessor_config, **cfg} + + # prefer processor_config.json if possible + processor_config_path = self.dir_model / "processor_config.json" + if processor_config_path.is_file(): + with open(processor_config_path, "r", encoding="utf-8") as f: + cfg = json.load(f) + # move image_processor to root level for compat + if "image_processor" in cfg: + cfg = { + **cfg, + **cfg["image_processor"], + } + # merge configs + self.preprocessor_config = {**self.preprocessor_config, **cfg} + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip non-multimodal tensors + if "language_model." in name: + return None + + return super().filter_tensors(item) + + def get_vision_config(self) -> dict[str, Any] | None: + config_name = "vision_config" if not self.is_mistral_format else "vision_encoder" + return self.global_config.get(config_name) + + def get_audio_config(self) -> dict[str, Any] | None: + mm_config_key = "whisper_config" if "whisper_config" in self.hparams else "audio_config" + return self.global_config.get(mm_config_key) + + def set_type(self): + self.gguf_writer.add_type(gguf.GGUFType.MMPROJ) + + def prepare_metadata(self, vocab_only: bool): + super().prepare_metadata(vocab_only=vocab_only) + + output_type: str = self.ftype.name.partition("_")[2] + + if self.fname_out.is_dir(): + fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, size_label=None, output_type=output_type, model_type=None) + self.fname_out = self.fname_out / f"mmproj-{fname_default}.gguf" + else: + self.fname_out = self.fname_out.parent / gguf.fill_templated_filename(self.fname_out.name, output_type) + + def set_gguf_parameters(self): + self.gguf_writer.add_file_type(self.ftype) + + if self.has_vision_encoder: + self.gguf_writer.add_clip_has_vision_encoder(True) + self.gguf_writer.add_vision_projection_dim(self.n_embd_text) + + # vision config + self.image_size = self.find_vparam(["image_size"]) + self.gguf_writer.add_vision_image_size(self.image_size) + self.gguf_writer.add_vision_patch_size(self.find_vparam(["patch_size"])) + self.gguf_writer.add_vision_embedding_length(self.find_vparam(["hidden_size", "width", "vt_hidden_size"])) + self.gguf_writer.add_vision_feed_forward_length(self.find_vparam(["intermediate_size", "vt_intermediate_size"])) + self.gguf_writer.add_vision_block_count(self.find_vparam(self.n_block_keys)) + self.gguf_writer.add_vision_head_count(self.find_vparam(["num_attention_heads", "num_heads", "heads", "vt_num_attention_heads"])) + + # preprocessor config + image_mean = _MISTRAL_COMMON_DATASET_MEAN if self.is_mistral_format else self.preprocessor_config["image_mean"] + image_std = _MISTRAL_COMMON_DATASET_STD if self.is_mistral_format else self.preprocessor_config["image_std"] + + self.gguf_writer.add_vision_image_mean(image_mean) + self.gguf_writer.add_vision_image_std(image_std) + + if self.has_audio_encoder: + self.gguf_writer.add_clip_has_audio_encoder(True) + self.gguf_writer.add_audio_projection_dim(self.n_embd_text) + + # audio config + self.gguf_writer.add_audio_embedding_length(self.find_aparam(["hidden_size"])) + self.gguf_writer.add_audio_feed_forward_length(self.find_aparam(["intermediate_size"])) + self.gguf_writer.add_audio_block_count(self.find_aparam(self.n_block_keys)) + self.gguf_writer.add_audio_head_count(self.find_aparam(["num_attention_heads"])) + + if not self.has_vision_encoder and not self.has_audio_encoder: + raise ValueError("MmprojModel must have either vision or audio encoder") + + def write_vocab(self): + raise ValueError("MmprojModel does not support vocab writing") + + def find_vparam(self, keys: Iterable[str], optional: bool = False) -> Any: + assert self.hparams_vision is not None + return self._find_param(self.hparams_vision, keys, optional) + + def find_aparam(self, keys: Iterable[str], optional: bool = False) -> Any: + assert self.hparams_audio is not None + return self._find_param(self.hparams_audio, keys, optional) + + def _find_param(self, obj: dict[str, Any], keys: Iterable[str], optional: bool = False) -> Any: + key = next((k for k in keys if k in obj), None) + if key is not None: + return obj[key] + if optional: + return None + raise KeyError(f"could not find any of: {keys}") + + def tensor_force_quant(self, name, new_name, bid, n_dims): + del bid, name, n_dims # unused + if ".patch_embd.weight" in new_name or ".patch_merger.weight" in new_name: + return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 + return False + + +class LazyTorchTensor(gguf.LazyBase): + _tensor_type = torch.Tensor + # to keep the type-checker happy + dtype: torch.dtype + shape: torch.Size + + # only used when converting a torch.Tensor to a np.ndarray + _dtype_map: dict[torch.dtype, type] = { + torch.float16: np.float16, + torch.float32: np.float32, + torch.uint8: np.uint8, + } + + # only used when byteswapping data. Only correct size is needed + # TODO: uncomment uint64, uint32, and uint16, ref: https://github.com/pytorch/pytorch/issues/58734 + _dtype_byteswap_map: dict[torch.dtype, type] = { + torch.float64: np.float64, + torch.float32: np.float32, + torch.bfloat16: np.float16, + torch.float16: np.float16, + torch.int64: np.int64, + # torch.uint64: np.uint64, + torch.int32: np.int32, + # torch.uint32: np.uint32, + torch.int16: np.int16, + # torch.uint16: np.uint16, + torch.int8: np.int8, + torch.uint8: np.uint8, + torch.bool: np.uint8, + torch.float8_e4m3fn: np.uint8, + torch.float8_e5m2: np.uint8, + } + + # used for safetensors slices + # ref: https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/src/lib.rs#L1046 + # TODO: uncomment U64, U32, and U16, ref: https://github.com/pytorch/pytorch/issues/58734 + _dtype_str_map: dict[str, torch.dtype] = { + "F64": torch.float64, + "F32": torch.float32, + "BF16": torch.bfloat16, + "F16": torch.float16, + # "U64": torch.uint64, + "I64": torch.int64, + # "U32": torch.uint32, + "I32": torch.int32, + # "U16": torch.uint16, + "I16": torch.int16, + "U8": torch.uint8, + "I8": torch.int8, + "BOOL": torch.bool, + "F8_E4M3": torch.float8_e4m3fn, + "F8_E5M2": torch.float8_e5m2, + } + + def numpy(self) -> gguf.LazyNumpyTensor: + dtype = self._dtype_map[self.dtype] + return gguf.LazyNumpyTensor( + meta=gguf.LazyNumpyTensor.meta_with_dtype_and_shape(dtype, self.shape), + args=(self,), + func=(lambda s: s.numpy()) + ) + + @classmethod + def meta_with_dtype_and_shape(cls, dtype: torch.dtype, shape: tuple[int, ...]) -> Tensor: + return torch.empty(size=shape, dtype=dtype, device="meta") + + @classmethod + def from_safetensors_slice(cls, st_slice: Any) -> Tensor: + dtype = cls._dtype_str_map[st_slice.get_dtype()] + shape: tuple[int, ...] = tuple(st_slice.get_shape()) + lazy = cls(meta=cls.meta_with_dtype_and_shape(dtype, shape), args=(st_slice,), func=lambda s: s[...] if len(s.get_shape()) == 0 else s[:]) + return cast(torch.Tensor, lazy) + + @classmethod + def from_local_tensor(cls, t: gguf.utility.LocalTensor) -> Tensor: + def load_tensor(tensor: gguf.utility.LocalTensor) -> Tensor: + def byteswap_tensor(tensor: np.ndarray, dtype: type) -> np.ndarray: + if sys.byteorder == 'big': + # switch data back to big endian + tensor = tensor.view(dtype).byteswap(inplace=False) + return tensor + dtype = cls._dtype_str_map[tensor.dtype] + numpy_dtype = cls._dtype_byteswap_map[dtype] + return torch.from_numpy(byteswap_tensor(tensor.mmap_bytes(), numpy_dtype)).view(dtype).reshape(tensor.shape) + dtype = cls._dtype_str_map[t.dtype] + shape = t.shape + lazy = cls(meta=cls.meta_with_dtype_and_shape(dtype, shape), args=(t,), func=lambda r: load_tensor(r)) + return cast(torch.Tensor, lazy) + + @classmethod + def from_remote_tensor(cls, remote_tensor: gguf.utility.RemoteTensor): + def byteswap_tensor(tensor: np.ndarray, dtype: type) -> np.ndarray: + if sys.byteorder == 'big': + # switch data back to big endian + tensor = tensor.view(dtype).byteswap(inplace=False) + return tensor + dtype = cls._dtype_str_map[remote_tensor.dtype] + numpy_dtype = cls._dtype_byteswap_map[dtype] + shape = remote_tensor.shape + meta = cls.meta_with_dtype_and_shape(dtype, shape) + lazy = cls(meta=meta, args=(remote_tensor,), func=lambda r: torch.from_numpy(byteswap_tensor(np.frombuffer(r.data(), dtype=numpy_dtype), numpy_dtype)).view(dtype).reshape(shape)) + return cast(torch.Tensor, lazy) + + @classmethod + def __torch_function__(cls, func, types, args=(), kwargs=None): + del types # unused + + if kwargs is None: + kwargs = {} + + if func is torch.Tensor.numpy: + assert len(args) + return args[0].numpy() + + return cls._wrap_fn(func)(*args, **kwargs) + + +def get_model_architecture(hparams: dict[str, Any], model_type: ModelType) -> str: + # TODO @ngxson : this won't work correctly if the model has both audio & vision encoders + # maybe we should fallback to text model's arch in that case, since not many models have both + text_config = hparams.get("text_config", {}) + vision_config = hparams.get("vision_config", {}) + arch = None + if (arches := hparams.get("architectures")) is not None and len(arches) > 0: + arch = arches[0] + elif "ssm_cfg" in hparams: + # For non-hf Mamba and Mamba2 models + arch = hparams["ssm_cfg"].get("layer", "Mamba") + "ForCausalLM" + + # Step3-VL keeps text config under text_config but uses a custom top-level architecture. + # For text conversion we route to a dedicated text-only class. + # TODO: refactor this later to avoid adding exception here + if model_type == ModelType.TEXT and arch in ("StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM"): + return arch + + # if "architectures" is found in the sub-config, use that instead + if model_type == ModelType.TEXT and text_config.get("architectures") is not None: + arch = text_config["architectures"][0] + elif model_type == ModelType.MMPROJ and vision_config.get("architectures") is not None: + arch = vision_config["architectures"][0] + if arch is None: + raise ValueError("Failed to detect model architecture") + return arch diff --git a/conversion/bert.py b/conversion/bert.py new file mode 100644 index 00000000000..8af6c534d07 --- /dev/null +++ b/conversion/bert.py @@ -0,0 +1,616 @@ +from __future__ import annotations + +import json +import os + +from pathlib import Path +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, SentencePieceTokenTypes, TextModel, gguf, logger + + +@ModelBase.register("BertModel", "BertForMaskedLM", "CamembertModel", "BertForSequenceClassification") +class BertModel(TextModel): + model_arch = gguf.MODEL_ARCH.BERT + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.vocab_size = None + + if cls_out_labels := self.hparams.get("id2label"): + if len(cls_out_labels) == 2 and cls_out_labels[0] == "LABEL_0": + # Remove dummy labels added by AutoConfig + cls_out_labels = None + self.cls_out_labels = cls_out_labels + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_causal_attention(False) + self._try_set_pooling_type() + + if self.cls_out_labels: + self.gguf_writer.add_classifier_output_labels([v for k, v in sorted(self.cls_out_labels.items())]) + + def set_vocab(self): + tokens, toktypes, tokpre = self.get_vocab_base() + self.vocab_size = len(tokens) + + # we need this to validate the size of the token_type embeddings + # though currently we are passing all zeros to the token_type embeddings + # "Sequence A" or "Sequence B" + self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) + + # convert to phantom space vocab + def phantom(tok, toktype): + if toktype == gguf.TokenType.CONTROL: + return tok + if tok.startswith("##"): + return tok[2:] + return "\u2581" + tok + assert len(tokens) == len(toktypes) + tokens = list(map(phantom, tokens, toktypes)) + + # add vocab to gguf + self.gguf_writer.add_tokenizer_model("bert") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + # handle special tokens + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("bert."): + name = name[5:] + + if name.endswith(".gamma"): + name = name[:-6] + ".weight" + + if name.endswith(".beta"): + name = name[:-5] + ".bias" + + # we are only using BERT for embeddings so we don't need the pooling layer + if name in ("embeddings.position_ids", "pooler.dense.weight", "pooler.dense.bias"): + return None + + if name.startswith("cls.predictions"): + return None + + if name.startswith("cls.seq_relationship"): + return None + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.cls_out_labels: + # For BertForSequenceClassification (direct projection layer) + if name == "classifier.weight": + name = "classifier.out_proj.weight" + + if name == "classifier.bias": + name = "classifier.out_proj.bias" + + yield from super().modify_tensors(data_torch, name, bid) + + def _xlmroberta_tokenizer_init(self) -> None: + # we need the pad_token_id to know how to chop down position_embd matrix + if (pad_token_id := self.hparams.get("pad_token_id")) is not None: + self._position_offset = 1 + pad_token_id + if "max_position_embeddings" in self.hparams: + self.hparams["max_position_embeddings"] -= self._position_offset + else: + self._position_offset = None + + def _xlmroberta_set_vocab(self) -> None: + # to avoid TypeError: Descriptors cannot be created directly + # exception when importing sentencepiece_model_pb2 + os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" + from sentencepiece import SentencePieceProcessor + from sentencepiece import sentencepiece_model_pb2 as model + + tokenizer_path = self.dir_model / 'sentencepiece.bpe.model' + + tokenizer_json = {} + tokenizer_config_json = {} + if not tokenizer_path.is_file(): + tokenizer_path = self.dir_model / 'tokenizer.json' + tokenizer_config_path = self.dir_model / 'tokenizer_config.json' + + if not tokenizer_path.is_file(): + raise FileNotFoundError(f"File not found: {tokenizer_path}") + + from base64 import b64decode + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + + with open(tokenizer_path, "r", encoding="utf-8") as fp: + tokenizer_json = json.load(fp) + + if tokenizer_config_path.is_file(): + with open(tokenizer_config_path, "r", encoding="utf-8") as fp: + tokenizer_config_json = json.load(fp) + + add_prefix = tokenizer.add_prefix_space # ty: ignore[unresolved-attribute] + remove_whitespaces = tokenizer.clean_up_tokenization_spaces # ty: ignore[unresolved-attribute] + precompiled_charsmap = b64decode(tokenizer_json["normalizer"]["precompiled_charsmap"]) + + vocab_size = max(self.hparams.get("vocab_size", 0), tokenizer.vocab_size) # ty: ignore[unresolved-attribute] + else: + sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] + sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) + assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM + + add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix + remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces + precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = max(self.hparams.get("vocab_size", 0), tokenizer.vocab_size()) + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + if isinstance(tokenizer, SentencePieceProcessor): + for token_id in range(tokenizer.vocab_size()): + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + else: + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + unk_token = tokenizer_config_json.get("unk_token") + unk_token_id = added_vocab.get(unk_token, tokenizer_json["model"].get("unk_id", 3)) # ty: ignore[no-matching-overload] + + for token_id in range(tokenizer.vocab_size): # ty: ignore[unresolved-attribute] + piece = tokenizer._convert_id_to_token(token_id) # ty: ignore[unresolved-attribute] + if (piece := tokenizer._convert_id_to_token(token_id)) is not None: # ty: ignore[unresolved-attribute] + text = piece.encode("utf-8") + score = tokenizer_json["model"]["vocab"][token_id][1] + + toktype = SentencePieceTokenTypes.NORMAL + if token_id == unk_token_id: + toktype = SentencePieceTokenTypes.UNKNOWN + elif token_id in tokenizer.all_special_ids: # ty: ignore[unresolved-attribute] + toktype = SentencePieceTokenTypes.CONTROL + elif token_id in added_vocab.values(): + toktype = SentencePieceTokenTypes.USER_DEFINED + # No reliable way to detect this, but jina doesn't have any + # elif tokenizer.IsByte(token_id): + # toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + if isinstance(tokenizer, SentencePieceProcessor): + # realign tokens (see HF tokenizer code) + tokens = [b'', b'', b'', b''] + tokens[3:-1] + scores = [0.0, 0.0, 0.0, 0.0] + scores[3:-1] + toktypes = [ + SentencePieceTokenTypes.CONTROL, + SentencePieceTokenTypes.CONTROL, + SentencePieceTokenTypes.CONTROL, + SentencePieceTokenTypes.UNKNOWN, + ] + toktypes[3:-1] + + if self.model_arch == gguf.MODEL_ARCH.NOMIC_BERT_MOE: + # Add mask token missing from sentencepiece.bpe.model + tokens[250001] = b'' + scores[250001] = 0.0 + toktypes[250001] = SentencePieceTokenTypes.CONTROL + + self.gguf_writer.add_tokenizer_model("t5") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_add_space_prefix(add_prefix) + self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) + self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) + if precompiled_charsmap: + self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + +@ModelBase.register("DistilBertModel", "DistilBertForMaskedLM", "DistilBertForSequenceClassification") +class DistilBertModel(BertModel): + model_arch = gguf.MODEL_ARCH.BERT + + def set_gguf_parameters(self): + self.gguf_writer.add_layer_norm_eps(1e-12) + logger.info("gguf: layer norm epsilon = 1e-12") + super().set_gguf_parameters() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("distilbert."): + name = name[11:] + + # These layers act as MLM head, so we don't need them + if name.startswith("vocab_"): + return None + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("RobertaModel", "RobertaForSequenceClassification") +class RobertaModel(BertModel): + model_arch = gguf.MODEL_ARCH.BERT + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # we need the pad_token_id to know how to chop down position_embd matrix + if (pad_token_id := self.hparams.get("pad_token_id")) is not None: + self._position_offset = 1 + pad_token_id + if "max_position_embeddings" in self.hparams: + self.hparams["max_position_embeddings"] -= self._position_offset + else: + self._position_offset = None + + def set_vocab(self): + """Support BPE tokenizers for roberta models""" + bpe_tok_path = self.dir_model / "tokenizer.json" + if bpe_tok_path.exists(): + self._set_vocab_gpt2() + + # we need this to validate the size of the token_type embeddings + # though currently we are passing all zeros to the token_type embeddings + # "Sequence A" or "Sequence B" + self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) + + else: + return super().set_vocab() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # if name starts with "roberta.", remove the prefix + # e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main + if name.startswith("roberta."): + name = name[8:] + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # position embeddings start at pad_token_id + 1, so just chop down the weight tensor + if name == "embeddings.position_embeddings.weight": + if self._position_offset is not None: + data_torch = data_torch[self._position_offset:,:] + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("NomicBertModel") +class NomicBertModel(BertModel): + model_arch = gguf.MODEL_ARCH.BERT + + def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, **kwargs: Any): + hparams = kwargs.pop("hparams", None) + if hparams is None: + hparams = ModelBase.load_hparams(dir_model, False) + + self.is_moe = bool(hparams.get("moe_every_n_layers")) + self.model_arch = gguf.MODEL_ARCH.NOMIC_BERT_MOE if self.is_moe else gguf.MODEL_ARCH.NOMIC_BERT + + super().__init__(dir_model, ftype, fname_out, hparams=hparams, **kwargs) + + self._tokenizer_is_xlmroberta = self._is_tokenizer_xlmroberta() + if self._tokenizer_is_xlmroberta: + self._xlmroberta_tokenizer_init() + + npos, mtp = self.hparams["n_positions"], self.hparams.get("max_trained_positions", 2048) + if npos == 8192 and mtp == 2048: + self.hparams["n_positions"] = 2048 # nomic-embed-text v1 and v1.5 are trained for 2048 tokens. + elif npos == 2048 and mtp == 2048: + self.hparams["n_positions"] = 512 # nomic-embed-text-v2-moe is trained for 512 tokens. + else: + raise ValueError(f"unrecognized parameters: n_positions={npos}, max_trained_positions={mtp}") + + assert self.hparams["activation_function"] == "gelu" if self.is_moe else "swiglu" + + # this doesn't do anything in the HF version + assert self.hparams["causal"] is False + # no bias tensors unless MoE + assert self.hparams["qkv_proj_bias"] == self.is_moe + assert self.hparams["mlp_fc1_bias"] == self.is_moe + assert self.hparams["mlp_fc2_bias"] == self.is_moe + + # norm at end of layer + assert self.hparams["prenorm"] is False + # standard RoPE + assert self.hparams["rotary_emb_fraction"] == 1.0 + assert self.hparams["rotary_emb_interleaved"] is False + assert self.hparams["rotary_emb_scale_base"] is None + + def set_vocab(self) -> None: + if self._tokenizer_is_xlmroberta: + return self._xlmroberta_set_vocab() + return super().set_vocab() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # If the tensor is an experts bias tensor, skip it. + if "mlp.experts.bias" in name: + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: torch.Tensor, name: str, bid: int | None) -> Iterable[tuple[str, torch.Tensor]]: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + if "mlp.experts.mlp.w1" in name: + data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"]) + name += ".weight" + + if "mlp.experts.mlp.w2" in name: + data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"]) + data_torch = data_torch.transpose(1, 2) + name += ".weight" + + yield from super().modify_tensors(data_torch, name, bid) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if self.is_moe: + self.gguf_writer.add_moe_every_n_layers(self.hparams["moe_every_n_layers"]) + self.gguf_writer.add_expert_used_count(self.hparams["moe_top_k"]) + + def _is_tokenizer_xlmroberta(self) -> bool: + with open(self.dir_model / "tokenizer.json") as f: + tokenizer_json = json.load(f) + toktyp = tokenizer_json["model"]["type"] + if toktyp == "Unigram": + return True + if toktyp == "WordPiece": + return False + raise ValueError(f"unknown tokenizer: {toktyp}") + + +@ModelBase.register("NeoBERT", "NeoBERTLMHead", "NeoBERTForSequenceClassification") +class NeoBert(BertModel): + model_arch = gguf.MODEL_ARCH.NEO_BERT + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # NeoBERT uses 2/3 of the intermediate size as feed forward length + self.gguf_writer.add_feed_forward_length(int(2 * self.hparams["intermediate_size"] / 3)) + self.gguf_writer.add_rope_freq_base(10000.0) # default value for NeoBERT + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + + f_rms_eps = self.hparams.get("norm_eps", 1e-6) # default value for NeoBERT + self.gguf_writer.add_layer_norm_rms_eps(f_rms_eps) + logger.info(f"gguf: rms norm epsilon = {f_rms_eps}") + + self.gguf_writer.add_pooling_type(gguf.PoolingType.CLS) # https://huggingface.co/chandar-lab/NeoBERT#how-to-use + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("decoder."): + return None + + if name.startswith("model."): + name = name[6:] + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("EuroBertModel", "JinaEmbeddingsV5Model") +class EuroBertModel(TextModel): + model_arch = gguf.MODEL_ARCH.EUROBERT + + def set_vocab(self): + self.gguf_writer.add_add_bos_token(False) + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # EuroBert is bidirectional (encoder) + self.gguf_writer.add_causal_attention(False) + + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + + self._try_set_pooling_type() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("model."): + name = name[6:] + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("XLMRobertaModel", "XLMRobertaForSequenceClassification") +class XLMRobertaModel(BertModel): + model_arch = gguf.MODEL_ARCH.BERT + _lora_files = {} + _lora_names = [] + + def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, **kwargs: Any): + hparams = kwargs.pop("hparams", None) + if hparams is None: + hparams = ModelBase.load_hparams(dir_model, False) + + if lora_names := hparams.get("lora_adaptations"): + self._lora_names = lora_names + self.model_arch = gguf.MODEL_ARCH.JINA_BERT_V3 + + super().__init__(dir_model, ftype, fname_out, hparams=hparams, **kwargs) + self._xlmroberta_tokenizer_init() + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + if self._lora_names: + for name in self._lora_names: + fname = self.add_prefix_to_filename(self.fname_out, f"lora-{name}-") + self._lora_files[name] = gguf.GGUFWriter(fname, arch=gguf.MODEL_ARCH_NAMES[self.model_arch], endianess=self.endianess, use_temp_file=self.use_temp_file, dry_run=self.dry_run) + + return super().generate_extra_tensors() + + def set_type(self): + for lora_writer in self._lora_files.values(): + lora_writer.add_type(gguf.GGUFType.ADAPTER) + lora_writer.add_string(gguf.Keys.Adapter.TYPE, "lora") + super().set_type() + + def set_vocab(self): + self._xlmroberta_set_vocab() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # if name starts with "roberta.", remove the prefix + # e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main + if name.startswith("roberta."): + name = name[8:] + + # jina-embeddings-v3 + if ".parametrizations." in name: + name = name.replace(".parametrizations.", ".") + if name.endswith(".original"): + name = name[:-9] + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # position embeddings start at pad_token_id + 1, so just chop down the weight tensor + if name == "embeddings.position_embeddings.weight": + if self._position_offset is not None: + data_torch = data_torch[self._position_offset:,:] + + if name.endswith(".0.lora_A") or name.endswith(".0.lora_B"): + if name.startswith("pooler.dense"): + return + + num_loras = data_torch.size(0) + assert num_loras == len(self._lora_names) + + # Split out each LoRA in their own GGUF + for i, lora_writer in enumerate(self._lora_files.values()): + new_name = self.map_tensor_name(name[:-9]) + name[-7:].lower() + data = data_torch[i, :, :] + # Transpose/flip token_embd/types into correct shape + if new_name == "token_embd.weight.lora_b": + data = data.T + elif new_name.startswith("token_types.weight."): + new_name = new_name[:-1] + ("a" if new_name[-1:] == "b" else "b") + lora_writer.add_tensor(new_name, data.float().numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) + + return + + yield from super().modify_tensors(data_torch, name, bid) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # jina-embeddings-v3 + lora_alpha = self.hparams.get("lora_alpha") + if lora_prompt_prefixes := self.hparams.get("task_instructions"): + assert self._lora_files and all(lora_name in lora_prompt_prefixes for lora_name in self._lora_files.keys()) + for lora_name, lora_writer in self._lora_files.items(): + lora_writer.add_float32(gguf.Keys.Adapter.LORA_ALPHA, lora_alpha if lora_alpha is not None else 1.0) + lora_writer.add_string(gguf.Keys.Adapter.LORA_TASK_NAME, lora_name) + if lora_prompt_prefixes: + lora_writer.add_string(gguf.Keys.Adapter.LORA_PROMPT_PREFIX, lora_prompt_prefixes[lora_name]) + + def write(self): + super().write() + for lora_writer in self._lora_files.values(): + lora_writer.write_header_to_file() + lora_writer.write_kv_data_to_file() + lora_writer.write_tensors_to_file(progress=True) + lora_writer.close() + + +@ModelBase.register("JinaBertModel", "JinaBertForMaskedLM") +class JinaBertV2Model(BertModel): + model_arch = gguf.MODEL_ARCH.JINA_BERT_V2 + + def set_vocab(self): + tokenizer_class = 'BertTokenizer' + with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: + tokenizer_class = json.load(f)['tokenizer_class'] + + if tokenizer_class == 'BertTokenizer': + super().set_vocab() + elif tokenizer_class == 'RobertaTokenizer': + self._set_vocab_gpt2() + self.gguf_writer.add_token_type_count(2) + else: + raise NotImplementedError(f'Tokenizer {tokenizer_class} is not supported for JinaBertModel') + + +@ModelBase.register("ModernBertModel", "ModernBertForMaskedLM", "ModernBertForSequenceClassification") +class ModernBertModel(BertModel): + model_arch = gguf.MODEL_ARCH.MODERN_BERT + + def set_vocab(self): + self.gguf_writer.add_add_bos_token(True) + self.gguf_writer.add_add_eos_token(True) + self.gguf_writer.add_add_sep_token(True) + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_sliding_window(self.hparams["local_attention"]) + if (sliding_window_pattern := self.hparams.get("global_attn_every_n_layers")) is not None: + self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("model."): + name = name[6:] + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.cls_out_labels: + # For BertForSequenceClassification (direct projection layer) + if name == "classifier.weight": + name = "classifier.out_proj.weight" + + if name == "classifier.bias": + name = "classifier.out_proj.bias" + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/bitnet.py b/conversion/bitnet.py new file mode 100644 index 00000000000..a66446abee2 --- /dev/null +++ b/conversion/bitnet.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("BitnetForCausalLM") +class BitnetModel(TextModel): + model_arch = gguf.MODEL_ARCH.BITNET + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) + self.gguf_writer.add_rope_scaling_factor(1.0) + + def weight_quant(self, weight: Tensor) -> Tensor: + dtype = weight.dtype + weight = weight.float() + scale = weight.abs().mean().clamp(min=1e-5) + iscale = 1 / scale + # TODO: multiply by the scale directly instead of inverting it twice + # (this is also unnecessarily doubly inverted upstream) + # ref: https://huggingface.co/1bitLLM/bitnet_b1_58-3B/blob/af89e318d78a70802061246bf037199d2fb97020/utils_quant.py#L10 + result = (weight * iscale).round().clamp(-1, 1) / iscale + return result.type(dtype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + new_name = self.map_tensor_name(name) + + if any(self.match_model_tensor_name(new_name, key, bid) for key in [ + gguf.MODEL_TENSOR.ATTN_Q, + gguf.MODEL_TENSOR.ATTN_K, + gguf.MODEL_TENSOR.ATTN_V, + gguf.MODEL_TENSOR.ATTN_OUT, + gguf.MODEL_TENSOR.FFN_UP, + gguf.MODEL_TENSOR.FFN_DOWN, + gguf.MODEL_TENSOR.FFN_GATE, + ]): + # transform weight into 1/0/-1 (in fp32) + data_torch = self.weight_quant(data_torch) + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/bloom.py b/conversion/bloom.py new file mode 100644 index 00000000000..d98edf6d500 --- /dev/null +++ b/conversion/bloom.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import re + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("BloomForCausalLM", "BloomModel") +class BloomModel(TextModel): + model_arch = gguf.MODEL_ARCH.BLOOM + + def set_gguf_parameters(self): + n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) + n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) + assert n_head is not None + assert n_embed is not None + self.gguf_writer.add_context_length(self.hparams.get("seq_length", n_embed)) + self.gguf_writer.add_embedding_length(n_embed) + self.gguf_writer.add_feed_forward_length(4 * n_embed) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(n_head) + self.gguf_writer.add_head_count_kv(n_head) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) + n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) + assert n_head is not None + assert n_embed is not None + + name = re.sub(r'transformer\.', '', name) + + if re.match(r"h\.\d+\.self_attention\.query_key_value\.weight", name): + # Map bloom-style qkv_linear to gpt-style qkv_linear + # bloom: https://github.com/huggingface/transformers/blob/main/src/transformers/models/bloom/modeling_bloom.py#L238-L252 # noqa + # gpt-2: https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py#L312 # noqa + qkv_weights = data_torch.reshape((n_head, 3, n_embed // n_head, n_embed)) + data_torch = torch.cat( + ( + qkv_weights[:, 0, :, :].reshape((-1, n_embed)), + qkv_weights[:, 1, :, :].reshape((-1, n_embed)), + qkv_weights[:, 2, :, :].reshape((-1, n_embed)), + ), + dim=0, + ) + logger.info("re-format attention.linear_qkv.weight") + elif re.match(r"h\.\d+\.self_attention\.query_key_value\.bias", name): + qkv_bias = data_torch.reshape((n_head, 3, n_embed // n_head)) + data_torch = torch.cat( + ( + qkv_bias[:, 0, :].reshape((n_embed,)), + qkv_bias[:, 1, :].reshape((n_embed,)), + qkv_bias[:, 2, :].reshape((n_embed,)), + ), + dim=0, + ) + logger.info("re-format attention.linear_qkv.bias") + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/chameleon.py b/conversion/chameleon.py new file mode 100644 index 00000000000..a996bfa53cf --- /dev/null +++ b/conversion/chameleon.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + +from .llama import LlamaModel + + +@ModelBase.register("ChameleonForConditionalGeneration") +@ModelBase.register("ChameleonForCausalLM") # obsolete +class ChameleonModel(TextModel): + model_arch = gguf.MODEL_ARCH.CHAMELEON + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_swin_norm(self.hparams.get("swin_norm", False)) + + def set_vocab(self): + self._set_vocab_gpt2() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # ignore image tokenizer for now + # TODO: image support for Chameleon + if name.startswith("model.vqmodel"): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + hidden_dim = self.hparams.get("hidden_size") + + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + if name.endswith(("q_norm.weight", "q_norm.bias")): + data_torch = ChameleonModel._reverse_hf_permute(data_torch, n_head, hidden_dim) + if name.endswith(("k_norm.weight", "k_norm.bias")): + data_torch = ChameleonModel._reverse_hf_permute(data_torch, n_kv_head, hidden_dim) + + yield from super().modify_tensors(data_torch, name, bid) + + # see: https://github.com/huggingface/transformers/blob/72fb02c47dbbe1999ae105319f24631cad6e2e00/src/transformers/models/chameleon/convert_chameleon_weights_to_hf.py#L176-L203 + @staticmethod + def _reverse_hf_permute(data_torch, n_heads, hidden_dim): + head_dim = hidden_dim // n_heads + data_torch = data_torch[0].view(2, head_dim // 2).t().reshape(1, -1) + data_torch = data_torch.repeat_interleave(n_heads, 0) + return data_torch diff --git a/conversion/chatglm.py b/conversion/chatglm.py new file mode 100644 index 00000000000..7e323b89004 --- /dev/null +++ b/conversion/chatglm.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, SentencePieceTokenTypes, TextModel, gguf + + +@ModelBase.register("GlmForCausalLM", "ChatGLMModel", "ChatGLMForConditionalGeneration") +class ChatGLMModel(TextModel): + model_arch = gguf.MODEL_ARCH.CHATGLM + + def set_vocab_chatglm3(self): + dir_model = self.dir_model + hparams = self.hparams + tokens: list[bytes] = [] + toktypes: list[int] = [] + scores: list[float] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) + vocab_size = hparams.get("padded_vocab_size", len(tokenizer.get_vocab())) # ty: ignore[unresolved-attribute] + assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] + role_special_tokens = ["<|system|>", "<|user|>", "<|assistant|>", "<|observation|>"] + special_tokens = ["[MASK]", "[gMASK]", "[sMASK]", "sop", "eop"] + role_special_tokens + for token_id in range(vocab_size): + piece = tokenizer._convert_id_to_token(token_id) # ty: ignore[unresolved-attribute] + if token_id == 0: + piece = "" + elif token_id == 1: + piece = "" + elif token_id == 2: + piece = "" + + text = piece.encode("utf-8") # ty: ignore[unresolved-attribute] + score = 0.0 + # Referencing the tokenizer Python implementation(https://huggingface.co/THUDM/chatglm3-6b/blob/main/tokenization_chatglm.py), + # it is only valid if it is less than tokenizer.tokenizer.sp_model.vocab_size() + if len(piece) != 0 and token_id < tokenizer.tokenizer.sp_model.vocab_size(): # ty: ignore[unresolved-attribute, invalid-argument-type] + score = tokenizer.tokenizer.sp_model.get_score(token_id) # ty: ignore[unresolved-attribute] + + if token_id >= tokenizer.tokenizer.sp_model.vocab_size(): # ty: ignore[unresolved-attribute] + if piece in special_tokens: + toktype = SentencePieceTokenTypes.CONTROL + elif len(piece) == 0: # ty: ignore[invalid-argument-type] + text = f"[PAD{token_id}]".encode("utf-8") + toktype = SentencePieceTokenTypes.UNUSED + else: + toktype = SentencePieceTokenTypes.USER_DEFINED + tokens.append(text) + scores.append(score) + toktypes.append(toktype) + continue + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.tokenizer.sp_model.is_unknown(token_id): # ty: ignore[unresolved-attribute] + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.tokenizer.sp_model.is_control(token_id): # ty: ignore[unresolved-attribute] + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.tokenizer.sp_model.is_unused(token_id): # ty: ignore[unresolved-attribute] + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.tokenizer.sp_model.is_byte(token_id): # ty: ignore[unresolved-attribute] + toktype = SentencePieceTokenTypes.BYTE + + tokens.append(text) + scores.append(score) + toktypes.append(toktype) + + self.gguf_writer.add_tokenizer_model("llama") + # glm3 needs prefix and suffix formatted as: + # prompt = "[gMASK]sop<|user|>\n" + prompt + "<|assistant|>" + self.gguf_writer.add_tokenizer_pre("chatglm-spm") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + @staticmethod + def token_bytes_to_string(b): + from transformers.models.gpt2.tokenization_gpt2 import bytes_to_unicode # ty: ignore[unresolved-import] + byte_encoder = bytes_to_unicode() + return ''.join([byte_encoder[ord(char)] for char in b.decode('latin-1')]) + + @staticmethod + def bpe(mergeable_ranks: dict[bytes, int], token: bytes, max_rank: int | None = None) -> list[bytes]: + parts = [bytes([b]) for b in token] + while True: + min_idx = None + min_rank = None + for i, pair in enumerate(zip(parts[:-1], parts[1:])): + rank = mergeable_ranks.get(pair[0] + pair[1]) + if rank is not None and (min_rank is None or rank < min_rank): + min_idx = i + min_rank = rank + if min_rank is None or (max_rank is not None and min_rank >= max_rank): + break + assert min_idx is not None + parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2:] + return parts + + def set_vocab(self): + if "THUDM/chatglm3-6b" in self.hparams.get("_name_or_path", ""): + self.set_vocab_chatglm3() + return + + dir_model = self.dir_model + hparams = self.hparams + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) + vocab_size = hparams.get("padded_vocab_size",hparams["vocab_size"]) + assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] + + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + # only add special tokens when they were not already loaded from config.json + special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] + # this one is usually not in config.json anyway + special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) + assert n_embed is not None + n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) + assert n_head is not None + n_head_kv = self.hparams.get("multi_query_group_num", self.hparams.get("num_key_value_heads", n_head)) + self.gguf_writer.add_context_length(self.hparams.get("seq_length", n_embed)) + self.gguf_writer.add_embedding_length(n_embed) + self.gguf_writer.add_feed_forward_length(self.hparams.get("ffn_hidden_size", self.hparams.get("intermediate_size", 4 * n_embed))) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(n_head) + self.gguf_writer.add_head_count_kv(n_head_kv) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("layernorm_epsilon",1e-5)) + self.gguf_writer.add_file_type(self.ftype) + if "attention_dim" in self.hparams: + rope_dim = self.hparams["attention_dim"] + else: + rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5))) + self.gguf_writer.add_add_bos_token(False) + rope_freq = 10000 + if "rope_ratio" in self.hparams: + rope_freq = rope_freq * self.hparams["rope_ratio"] + self.gguf_writer.add_rope_freq_base(rope_freq) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith(".rotary_pos_emb.inv_freq"): + return None + + name = name.removeprefix("transformer.") + + return super().filter_tensors((name, gen)) diff --git a/conversion/codeshell.py b/conversion/codeshell.py new file mode 100644 index 00000000000..8bfc3178d46 --- /dev/null +++ b/conversion/codeshell.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("CodeShellForCausalLM") +class CodeShellModel(TextModel): + model_arch = gguf.MODEL_ARCH.CODESHELL + + def set_gguf_parameters(self): + self.gguf_writer.add_context_length(self.hparams["n_positions"]) + self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) + self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(self.hparams["n_head"]) + self.gguf_writer.add_head_count_kv(self.hparams["num_query_groups"]) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_rope_freq_base(10000.0) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) + self.gguf_writer.add_rope_scaling_factor(1.0) diff --git a/conversion/cogvlm.py b/conversion/cogvlm.py new file mode 100644 index 00000000000..d92df55d46b --- /dev/null +++ b/conversion/cogvlm.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + +from .llama import LlamaModel + + +@ModelBase.register("CogVLMForCausalLM") +class CogVLMVisionModel(MmprojModel): + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.COGVLM) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if not name.startswith("model.vision."): + return None + + return super().filter_tensors(item) + + +@ModelBase.register("CogVLMForCausalLM") +class CogVLMModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.COGVLM diff --git a/conversion/command_r.py b/conversion/command_r.py new file mode 100644 index 00000000000..603288d165c --- /dev/null +++ b/conversion/command_r.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("CohereForCausalLM") +class CommandR2Model(TextModel): + model_arch = gguf.MODEL_ARCH.COMMAND_R + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # max_position_embeddings = 8192 in config.json but model was actually + # trained on 128k context length + # aya-23 models don't have model_max_length specified + self.hparams["max_position_embeddings"] = self.find_hparam(["model_max_length", "max_position_embeddings"]) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_logit_scale(self.hparams["logit_scale"]) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + + +@ModelBase.register("Cohere2ForCausalLM") +class Cohere2Model(TextModel): + model_arch = gguf.MODEL_ARCH.COHERE2 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_logit_scale(self.hparams["logit_scale"]) + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + + rotary_pct = self.hparams["rotary_pct"] + hidden_size = self.hparams["hidden_size"] + num_attention_heads = self.hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(int(rotary_pct * (hidden_size // num_attention_heads))) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Cohere2 runtime in llama.cpp expects no bias tensors; + # the actual weight only contains 0-value tensors as bias, we can skip them + if name.endswith(".bias"): + if torch.any(data_torch != 0): + raise ValueError(f"Bias tensor {name!r} is not zero.") + logger.debug(f"Skipping bias tensor {name!r} for Cohere2 conversion.") + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/dbrx.py b/conversion/dbrx.py new file mode 100644 index 00000000000..207ebcb8931 --- /dev/null +++ b/conversion/dbrx.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("DbrxForCausalLM") +class DbrxModel(TextModel): + model_arch = gguf.MODEL_ARCH.DBRX + + def set_gguf_parameters(self): + ffn_config = self.hparams["ffn_config"] + attn_config = self.hparams["attn_config"] + self.gguf_writer.add_block_count(self.block_count) + + self.gguf_writer.add_context_length(self.hparams["max_seq_len"]) + self.gguf_writer.add_embedding_length(self.hparams["d_model"]) + self.gguf_writer.add_feed_forward_length(ffn_config["ffn_hidden_size"]) + + self.gguf_writer.add_head_count(self.hparams["n_heads"]) + self.gguf_writer.add_head_count_kv(attn_config["kv_n_heads"]) + + self.gguf_writer.add_rope_freq_base(attn_config["rope_theta"]) + + self.gguf_writer.add_clamp_kqv(attn_config["clip_qkv"]) + + self.gguf_writer.add_expert_count(ffn_config["moe_num_experts"]) + self.gguf_writer.add_expert_used_count(ffn_config["moe_top_k"]) + + self.gguf_writer.add_layer_norm_eps(1e-5) + + self.gguf_writer.add_file_type(self.ftype) + logger.info(f"gguf: file type = {self.ftype}") + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_expert = self.hparams["ffn_config"]["moe_num_experts"] + n_ff = self.hparams["ffn_config"]["ffn_hidden_size"] + n_embd = self.hparams["d_model"] + + # Specific behavior for experts tensors: suffix .weight, view as 3D and transpose + # original implementation expects (n_expert, n_ff, n_embd) for all experts weights + # But llama.cpp moe graph works differently + # AND the dimensions in ggml are typically in the reverse order of the pytorch dimensions + # so (n_expert, n_ff, n_embd) in pytorch is {n_embd, n_ff, n_expert} in ggml_tensor + exp_tensor_names = {"ffn.experts.mlp.w1": None, # LLM_TENSOR_FFN_GATE_EXPS ggml_tensor->ne{n_embd, n_ff, n_expert} + "ffn.experts.mlp.w2": (0, 2, 1), # LLM_TENSOR_FFN_DOWN_EXPS ggml_tensor->ne{n_ff, n_embd, n_expert} + "ffn.experts.mlp.v1": None} # LLM_TENSOR_FFN_UP_EXPS ggml_tensor->ne{n_embd, n_ff, n_expert} + experts = False + + for exp_tensor_name in exp_tensor_names.keys(): + if name.find(exp_tensor_name) != -1 and name.find(".weight") == -1: + experts = True + data_torch = data_torch.view(n_expert, n_ff, n_embd) + if (permute_tensor := exp_tensor_names[exp_tensor_name]) is not None: + data_torch = data_torch.permute(*permute_tensor) + break + + # map tensor names + # In MoE models the ffn tensors are typically most of the model weights, + # and need to be quantizable. Quantize expects tensor names to be suffixed by .weight. + # Every other model has the weight names ending in .weight, + # let's assume that is the convention which is not the case for dbrx: + # https://huggingface.co/databricks/dbrx-instruct/blob/main/model.safetensors.index.json#L15 + new_name = self.map_tensor_name(name if not experts else name + ".weight", try_suffixes=(".weight",)) + + yield from super().modify_tensors(data_torch, new_name, bid) + + def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool: + del name, new_name, bid # unused + + return n_dims > 1 diff --git a/conversion/deci.py b/conversion/deci.py new file mode 100644 index 00000000000..46d8568c5a4 --- /dev/null +++ b/conversion/deci.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +import math + +from typing import Any, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("DeciLMForCausalLM") +class DeciModel(TextModel): + model_arch = gguf.MODEL_ARCH.DECI + + @staticmethod + def _ffn_mult_to_intermediate_size(ffn_mult: float, n_embd: int) -> int: + # DeciLM-specific code + intermediate_size = int(2 * ffn_mult * n_embd / 3) + return DeciModel._find_multiple(intermediate_size, 256) + + @staticmethod + def _find_multiple(n: int, k: int) -> int: + # DeciLM-specific code + if n % k == 0: + return n + return n + k - (n % k) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if "block_configs" in self.hparams: # Llama-3_1-Nemotron-51B + _block_configs: list[dict[str,Any]] = self.hparams["block_configs"] + assert self.block_count == len(_block_configs) + self._num_kv_heads = list() + self._num_heads = list() + _ffn_multipliers = list() + # ***linear attention layer*** + # if n_heads_in_group is None and replace_with_linear is True + # then _num_kv_heads[il] is 0 and _num_heads[il] is num_attention_heads + # ***attention-free layer*** + # if n_heads_in_group is None and replace_with_linear is False + # then _num_kv_heads[il] is 0 and _num_heads[il] is 0 + # ***normal attention-layer*** + # if n_heads_in_group is not None, then + # _num_kv_heads[il] is num_attention_head // n_heads_in_group and + # _num_heads[il] is num_attention_head + # ***dummy layer*** for nemotron 253B + # if n_heads_in_group is None and ffn_mult is None + # then _num_kv_heads[il] is 0 and _num_heads[il] is 0 and _ffn_dims is 0 + for il in range(len(_block_configs)): + if _block_configs[il]["attention"]["n_heads_in_group"] is None: + if _block_configs[il]["attention"]["replace_with_linear"] is True: + self._num_kv_heads.append(0) + self._num_heads.append(self.hparams["num_attention_heads"]) + else: + self._num_kv_heads.append(0) + self._num_heads.append(0) + else: + self._num_kv_heads.append(self.hparams["num_attention_heads"] // _block_configs[il]["attention"]["n_heads_in_group"]) + self._num_heads.append(self.hparams["num_attention_heads"]) + if _block_configs[il]["ffn"]["ffn_mult"] is None: # dummy layer + _ffn_multipliers.append(0.0) + else: + _ffn_multipliers.append(_block_configs[il]["ffn"]["ffn_mult"]) + assert self.block_count == len(self._num_kv_heads) + assert self.block_count == len(self._num_heads) + assert self.block_count == len(_ffn_multipliers) + assert isinstance(self._num_kv_heads, list) and isinstance(self._num_kv_heads[0], int) + assert isinstance(self._num_heads, list) and isinstance(self._num_heads[0], int) + assert isinstance(_ffn_multipliers, list) and isinstance(_ffn_multipliers[0], float) + self._ffn_dims: list[int] = [ + DeciModel._ffn_mult_to_intermediate_size(multiplier, self.hparams["hidden_size"]) + for multiplier in _ffn_multipliers + ] + + def set_vocab(self): + # Please change tokenizer_config.json of Llama-3_1-Nemotron-51B's + # eos_token from '|eot_id|' to '|end_of_text|' + if self.hparams.get("vocab_size", 128256) == 128256: + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab.add_to_gguf(self.gguf_writer) + else: + # DeciLM-7B + self._set_vocab_llama_hf() + + def set_gguf_parameters(self): + if "block_configs" in self.hparams: # Llama-3_1-Nemotron-51B + assert self.block_count == len(self._num_kv_heads) + assert self.block_count == len(self._num_heads) + assert self.block_count == len(self._ffn_dims) + if (rope_theta := self.rope_parameters.get("rope_theta")) is not None: + self.gguf_writer.add_rope_freq_base(rope_theta) + self.gguf_writer.add_head_count_kv(self._num_kv_heads) + self.gguf_writer.add_head_count(self._num_heads) + self.gguf_writer.add_feed_forward_length(self._ffn_dims) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) + self.gguf_writer.add_key_length(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) + self.gguf_writer.add_value_length(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) + self.gguf_writer.add_file_type(self.ftype) + else: # DeciLM-7B + super().set_gguf_parameters() + if "num_key_value_heads_per_layer" in self.hparams: # DeciLM-7B + self._num_kv_heads: list[int] = self.hparams["num_key_value_heads_per_layer"] + assert self.block_count == len(self._num_kv_heads) + self.gguf_writer.add_head_count_kv(self._num_kv_heads) + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(rope_dim) + + @staticmethod + def permute(weights: Tensor, n_head: int, n_head_kv: int | None): + if n_head_kv is not None and n_head != n_head_kv: + n_head = n_head_kv + return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + if bid is not None: + if "num_key_value_heads_per_layer" in self.hparams: + n_kv_head = self.hparams["num_key_value_heads_per_layer"][bid] + elif "block_configs" in self.hparams: + n_kv_head = self._num_kv_heads[bid] + n_head = self._num_heads[bid] + else: + n_kv_head = self.hparams.get("num_key_value_heads") + else: + n_kv_head = self.hparams.get("num_key_value_heads") + + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = DeciModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = DeciModel.permute(data_torch, n_head, n_kv_head) + yield from super().modify_tensors(data_torch, name, bid) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): + if rope_params.get("rope_type", '').lower() == "llama3": + base = rope_params.get("rope_theta", 10000.0) + if (dim := self.hparams.get("head_dim")) is None: + dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) + + factor = rope_params.get("factor", 8.0) + low_freq_factor = rope_params.get("low_freq_factor", 1.0) + high_freq_factor = rope_params.get("high_freq_factor", 4.0) + old_context_len = self.hparams.get("original_max_position_embeddings", 8192) + + low_freq_wavelen = old_context_len / low_freq_factor + high_freq_wavelen = old_context_len / high_freq_factor + assert low_freq_wavelen != high_freq_wavelen + + rope_factors = [] + for freq in freqs: + wavelen = 2 * math.pi / freq + if wavelen < high_freq_wavelen: + rope_factors.append(1) + elif wavelen > low_freq_wavelen: + rope_factors.append(factor) + else: + smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) + rope_factors.append(1 / ((1 - smooth) / factor + smooth)) + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) + + def prepare_tensors(self): + super().prepare_tensors() diff --git a/conversion/deepseek.py b/conversion/deepseek.py new file mode 100644 index 00000000000..e149fcbf752 --- /dev/null +++ b/conversion/deepseek.py @@ -0,0 +1,388 @@ +from __future__ import annotations + +import re + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf, logger + +from .qwen import QwenModel + + +@ModelBase.register("DeepseekOCRForCausalLM") +class DeepseekOCRVisionModel(MmprojModel): + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.DEEPSEEKOCR) + # default values below are taken from HF tranformers code + self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("layer_norm_eps", 1e-6)) + self.gguf_writer.add_vision_use_gelu(True) + # calculate proj_scale_factor (used by tinygemma3 test model) + image_seq_length = self.preprocessor_config.get("image_seq_length", 256) + n_per_side = int(image_seq_length ** 0.5) + image_size = self.hparams["image_size"] + patch_size = self.hparams["patch_size"] + proj_scale_factor = (image_size // patch_size) // n_per_side + if proj_scale_factor > 0 and proj_scale_factor != 4: + # we only need to write this if it's not the default value + # in this case, we are converting a test model + self.gguf_writer.add_vision_projector_scale_factor(proj_scale_factor) + # @bluebread: there's no window_size in config but just add it here anyway + self.gguf_writer.add_vision_window_size(self.hparams.get("window_size", 14)) + + # SAM configuration + sam_hparams = hparams['sam'] + self.gguf_writer.add_vision_sam_layers_count(sam_hparams['layers']) + self.gguf_writer.add_vision_sam_embedding_length(sam_hparams['width']) + self.gguf_writer.add_vision_sam_head_count(sam_hparams['heads']) + + def get_vision_config(self) -> dict[str, Any]: + vision_config: dict[str, Any] | None = self.global_config.get("vision_config") + + if not vision_config: + raise ValueError("DeepseekOCR model requires 'vision_config' in the model configuration, but it was not found") + + vision_config['sam'] = vision_config['width']['sam_vit_b'] + vision_config.update(vision_config['width']['clip-l-14-224']) + vision_config['hidden_size'] = vision_config['width'] + vision_config['num_heads'] = vision_config['heads'] + vision_config['intermediate_size'] = vision_config['heads'] * 4 + + return vision_config + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".embeddings." in name or 'pos_embed' in name: + return gguf.GGMLQuantizationType.F32 + if ".rel_pos_h" in name or '.rel_pos_w' in name: + return gguf.GGMLQuantizationType.F32 + if ".neck." in name or ".net_" in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Only process vision-related tensors, skip language model tensors + # Vision components: sam_model, vision_model, projector, image_newline, view_seperator + # Language model components to skip: lm_head, embed_tokens, layers, norm + if name.startswith(("lm_head.", "model.embed_tokens.", "model.layers.", "model.norm.")): + return None + + if name.endswith("pos_embed") or name.endswith("rel_pos_h") or name.endswith("rel_pos_w"): + name += ".weight" + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("DeepseekForCausalLM") +class DeepseekModel(TextModel): + model_arch = gguf.MODEL_ARCH.DEEPSEEK + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + + self.gguf_writer.add_rope_dimension_count(rope_dim) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) + self.gguf_writer.add_expert_weights_scale(1.0) + self.gguf_writer.add_expert_count(hparams["n_routed_experts"]) + self.gguf_writer.add_expert_shared_count(hparams["n_shared_experts"]) + + _experts: list[dict[str, Tensor]] | None = None + + @staticmethod + def permute(weights: Tensor, n_head: int, n_head_kv: int | None): + if n_head_kv is not None and n_head != n_head_kv: + n_head = n_head_kv + return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = DeepseekModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = DeepseekModel.permute(data_torch, n_head, n_kv_head) + + # process the experts separately + if name.find("mlp.experts") != -1: + n_experts = self.hparams["n_routed_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register( + "DeepseekV2ForCausalLM", + "DeepseekV3ForCausalLM", + "KimiVLForConditionalGeneration", + "KimiK25ForConditionalGeneration", + "YoutuForCausalLM", + "YoutuVLForConditionalGeneration", +) +class DeepseekV2Model(TextModel): + model_arch = gguf.MODEL_ARCH.DEEPSEEK2 + + # TODO @ngxson : remove this when we support MTP for deepseek models + skip_mtp = True + + merge_expert = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + hparams: dict = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) + self.origin_hf_arch = hparams.get('architectures', [None])[0] + + # special handling for Deepseek OCR + if self.origin_hf_arch == "DeepseekOCRForCausalLM": + self.model_arch = gguf.MODEL_ARCH.DEEPSEEK2OCR + self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[self.model_arch] + self.gguf_writer.add_architecture() + # default jinja template + self.gguf_writer.add_chat_template("{% for m in messages %}{{m['content']}}{% endfor %}") + + def set_vocab(self): + try: + self._set_vocab_gpt2() + return + except Exception: + pass + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + tokpre = self.get_vocab_base_pre(tokenizer) + + if tokpre == "kimi-k2": + # Build merges list using the approach similar to HunYuanMoE + merges = [] + vocab = {} + mergeable_ranks = tokenizer.model._mergeable_ranks # ty: ignore[unresolved-attribute] + for token, rank in mergeable_ranks.items(): + vocab[QwenModel.token_bytes_to_string(token)] = rank + if len(token) == 1: + continue + merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) + if len(merged) == 2: + merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) + + # Build token list + vocab_size = self.hparams["vocab_size"] + special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] + reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} + tokens: list[str] = [] + toktypes: list[int] = [] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token = reverse_vocab[i] + tokens.append(token) + if i in special_tokens.values(): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.NORMAL) + + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_token_merges(merges) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) + special_vocab.add_to_gguf(self.gguf_writer) + else: + raise NotImplementedError(f"Deepseek pre-tokenizer {tokpre!r} is not supported yet!") + + def set_gguf_parameters(self): + is_ocr = (self.model_arch == gguf.MODEL_ARCH.DEEPSEEK2OCR) + + if is_ocr: + self.hparams['rope_theta'] = self.hparams.get('rope_theta', 10000.0) + else: + # note: deepseek2 using MLA converts into MQA (ie: GQA with 1 group) + self.hparams["num_key_value_heads"] = 1 + + self.hparams['rms_norm_eps'] = self.hparams.get('rms_norm_eps', 1e-6) + + super().set_gguf_parameters() + hparams = self.hparams + + # first_k_dense_replace: number of leading layers using dense FFN instead of MoE + # For non-MoE models (like Youtu), set to n_layer to use dense FFN for all layers + # For MoE models (like DeepSeek-V2), this is the number of leading non-MoE layers + has_moe = hparams.get("n_routed_experts") is not None + first_k_dense_replace = hparams.get("first_k_dense_replace") + if first_k_dense_replace is None: + # Default: if no MoE, all layers are dense; if MoE, none are dense + first_k_dense_replace = hparams["num_hidden_layers"] if not has_moe else 0 + self.gguf_writer.add_leading_dense_block_count(first_k_dense_replace) + kv_lora_rank = hparams.get("kv_lora_rank", 512) + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + if "q_lora_rank" in hparams and hparams["q_lora_rank"] is not None: + self.gguf_writer.add_q_lora_rank(hparams["q_lora_rank"]) + + # note: deepseek2 using MLA converts into MQA with larger heads, then decompresses to MHA + if not is_ocr: + self.gguf_writer.add_kv_lora_rank(kv_lora_rank) + self.gguf_writer.add_key_length(kv_lora_rank + hparams["qk_rope_head_dim"]) + self.gguf_writer.add_value_length(kv_lora_rank) + self.gguf_writer.add_key_length_mla(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) + self.gguf_writer.add_value_length_mla(hparams["v_head_dim"]) + + # MoE parameters (required by C++ code for DEEPSEEK2 arch) + # For non-MoE models like Youtu, use intermediate_size as expert_feed_forward_length + moe_intermediate_size = self.find_hparam(["moe_intermediate_size", "intermediate_size"], optional=False) + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + + if (n_routed_experts := hparams.get("n_routed_experts")) is not None: + self.gguf_writer.add_expert_count(n_routed_experts) + + # expert_shared_count is required by C++ code, default to 0 for non-MoE models + n_shared_experts = hparams.get("n_shared_experts", 0) + self.gguf_writer.add_expert_shared_count(n_shared_experts) + + # When not set, C++ code will use scale_w = false to skip the no-op scaling + if (routed_scaling_factor := hparams.get("routed_scaling_factor")) is not None: + self.gguf_writer.add_expert_weights_scale(routed_scaling_factor) + + if (norm_topk_prob := hparams.get("norm_topk_prob")) is not None and norm_topk_prob: + self.gguf_writer.add_expert_weights_norm(norm_topk_prob) + + self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) + + if (rope_mscale_all := self.rope_parameters.get("mscale_all_dim")) is not None: + # [TAG_DEEPSEEK2_YARN_LOG_MUL_FIX] + # note: for legacy reasons, this is not consistent with the other usages of self.gguf_writer.add_rope_scaling_yarn_log_mul + # ref https://github.com/ggml-org/llama.cpp/pull/17945 + self.gguf_writer.add_rope_scaling_yarn_log_mul(0.1 * rope_mscale_all) + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # skip lm_head.weight if tie_word_embeddings is True + if self.hparams.get("tie_word_embeddings", False): + if name == "lm_head.weight" or name == "model.lm_head.weight": + logger.info("Skipping tied output layer 'lm_head.weight' (will use token_embd.weight)") + return + + # skip Multi-Token Prediction (MTP) layers + if self.skip_mtp: + block_count = self.hparams["num_hidden_layers"] + match = re.match(r"model.layers.(\d+)", name) + if match and int(match.group(1)) >= block_count: + return + + # process the experts separately + if self.merge_expert and name.find("mlp.experts") != -1: + n_experts = self.hparams["n_routed_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + # note: MLA with the absorption optimization, needs these two split and k_b_proj transposed + if name.endswith("kv_b_proj.weight"): + name_kb = name.replace("kv_b_proj", "k_b_proj") + name_vb = name.replace("kv_b_proj", "v_b_proj") + + n_head_kv = self.hparams["num_key_value_heads"] + v_head_dim = self.hparams["v_head_dim"] + qk_nope_head_dim = self.hparams["qk_nope_head_dim"] + + assert data_torch.shape[0] == n_head_kv * (v_head_dim + qk_nope_head_dim) + + kv_b = data_torch.view(n_head_kv, v_head_dim + qk_nope_head_dim, data_torch.shape[-1]) + k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1) + k_b = k_b.transpose(1, 2) + + yield from super().modify_tensors(k_b, name_kb, bid) + yield from super().modify_tensors(v_b, name_vb, bid) + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/dots1.py b/conversion/dots1.py new file mode 100644 index 00000000000..7ac299a6e65 --- /dev/null +++ b/conversion/dots1.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, gguf + +from .qwen import Qwen2MoeModel + + +@ModelBase.register("Dots1ForCausalLM") +class Dots1Model(Qwen2MoeModel): + model_arch = gguf.MODEL_ARCH.DOTS1 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.hparams["num_experts"] = self.hparams["n_routed_experts"] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_leading_dense_block_count(self.hparams["first_k_dense_replace"]) + self.gguf_writer.add_expert_shared_count(self.hparams["n_shared_experts"]) + self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) + self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): + if "shared_experts" in name: + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + else: + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/dotsocr.py b/conversion/dotsocr.py new file mode 100644 index 00000000000..f87f62abde9 --- /dev/null +++ b/conversion/dotsocr.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + + +@ModelBase.register("DotsOCRForCausalLM") +class DotsOCRVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["image_size"] = 0 # dynamic resolution + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.DOTSOCR) + self.gguf_writer.add_vision_min_pixels(self.preprocessor_config["min_pixels"]) + self.gguf_writer.add_vision_max_pixels(self.preprocessor_config["max_pixels"]) + self.gguf_writer.add_vision_attention_layernorm_eps(self.find_vparam(["rms_norm_eps"])) + self.gguf_writer.add_vision_projector_scale_factor(self.find_vparam(["spatial_merge_size"])) + self.gguf_writer.add_vision_use_silu(True) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if not name.startswith("vision_tower."): + return None + + if "vision_tower.blocks." in name and ".mlp." in name: + # note: to avoid naming conflicts in tensor_mapping.py, we need to handle FFN renaming here + # x = F.silu(self.fc1(x)) * self.fc3(x) + # x = self.fc2(x) + # fc1 -> gate, fc2 -> down, fc3 -> up + # mapping original names to Qwen2.5 naming scheme + name = name.replace("vision_tower.blocks.", "visual.blocks.") + name = name.replace(".fc1", ".gate_proj") + name = name.replace(".fc2", ".down_proj") + name = name.replace(".fc3", ".up_proj") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/dream.py b/conversion/dream.py new file mode 100644 index 00000000000..459e8d46afb --- /dev/null +++ b/conversion/dream.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("DreamModel") +class DreamModel(TextModel): + model_arch = gguf.MODEL_ARCH.DREAM + + def get_vocab_base(self) -> tuple[list[str], list[int], str]: + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + + vocab_dict = tokenizer.get_vocab() # ty: ignore[unresolved-attribute] + vocab_size = self.hparams.get("vocab_size", len(vocab_dict)) + assert max(vocab_dict.values()) < vocab_size + + tokpre = self.get_vocab_base_pre(tokenizer) + + reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab_dict.items()} + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + elif reverse_vocab[i] in added_vocab: + tokens.append(reverse_vocab[i]) + # Check if it's a special token - treat special tokens as CONTROL tokens + if hasattr(tokenizer, 'added_tokens_decoder') and i in tokenizer.added_tokens_decoder: + if tokenizer.added_tokens_decoder[i].special: + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.USER_DEFINED) + else: + # Fallback: treat all added vocab as control tokens for special tokens like <|im_start|> + toktypes.append(gguf.TokenType.CONTROL) + else: + tokens.append(reverse_vocab[i]) + toktypes.append(gguf.TokenType.NORMAL) + + return tokens, toktypes, tokpre + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self._try_set_pooling_type() + + # Dream models use non-causal attention for diffusion + self.gguf_writer.add_causal_attention(False) + + # Add Dream-specific parameters + mask_token_id = self.hparams.get("mask_token_id") + if mask_token_id is not None: + self.gguf_writer.add_mask_token_id(mask_token_id) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Dream model tensors should be mapped directly since it's the base model + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/ernie.py b/conversion/ernie.py new file mode 100644 index 00000000000..aa8a3bc8ee5 --- /dev/null +++ b/conversion/ernie.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +import json +import math +import re + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf + + +@ModelBase.register("Ernie4_5_ForCausalLM", "Ernie4_5ForCausalLM") +class Ernie4_5Model(TextModel): + model_arch = gguf.MODEL_ARCH.ERNIE4_5 + + def set_vocab(self): + self._set_vocab_sentencepiece() + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + if "add_prefix_space" in tokenizer_config_json: + self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "ernie." in name: + name = name.replace("ernie.", "model.") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + num_heads = self.hparams["num_attention_heads"] + num_kv_heads = self.hparams["num_key_value_heads"] + if (head_dim := self.hparams.get("head_dim")) is None: + head_dim = self.hparams["hidden_size"] // num_heads + + # split the qkv weights + # qkv_proj shape: [(num_heads + 2 * num_kv_heads) * head_dim, hidden_size] + if "qkv_proj" in name: + name_q = name.replace("qkv_proj.weight", "q_proj.weight") + name_k = name.replace("qkv_proj.weight", "k_proj.weight") + name_v = name.replace("qkv_proj.weight", "v_proj.weight") + total_q_dim = num_heads * head_dim + total_k_dim = num_kv_heads * head_dim + total_v_dim = num_kv_heads * head_dim + q_proj_weight, k_proj_weight, v_proj_weight = data_torch.split([total_q_dim, total_k_dim, total_v_dim], dim=0) + yield from super().modify_tensors(q_proj_weight, name_q, bid) + yield from super().modify_tensors(k_proj_weight, name_k, bid) + yield from super().modify_tensors(v_proj_weight, name_v, bid) + # split the up_gate_proj into gate and up + # up_gate_proj shape: [2 * intermediate_size, hidden_size] + elif "up_gate_proj" in name: + name_up = name.replace("up_gate_proj.weight", "up_proj.weight") + name_gate = name.replace("up_gate_proj.weight", "gate_proj.weight") + dim_half = data_torch.shape[0] // 2 + gate_proj_weight, up_proj_weight = data_torch.split(dim_half, dim=0) + yield from super().modify_tensors(gate_proj_weight, name_gate, bid) + yield from super().modify_tensors(up_proj_weight, name_up, bid) + else: + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Ernie4_5_MoeForCausalLM") +class Ernie4_5MoeModel(Ernie4_5Model): + model_arch = gguf.MODEL_ARCH.ERNIE4_5_MOE + _experts: list[dict[str, Tensor]] | None = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._experts = [{} for _ in range(self.block_count)] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_expert_count(self.hparams["moe_num_experts"]) + self.gguf_writer.add_expert_used_count(self.hparams["moe_k"]) + self.gguf_writer.add_interleave_moe_layer_step(self.hparams["moe_layer_interval"]) + self.gguf_writer.add_leading_dense_block_count(self.hparams["moe_layer_start_index"]) + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + if (shared_expert_count := self.hparams.get('moe_num_shared_experts')) is not None: + self.gguf_writer.add_expert_shared_count(shared_expert_count) + if shared_expert_count > 0 and (shared_expert_intermediate_size := self.hparams.get('intermediate_size')) is not None and (num_key_value_heads := self.hparams.get('num_key_value_heads')) is not None: + self.gguf_writer.add_expert_shared_feed_forward_length(shared_expert_intermediate_size // num_key_value_heads) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # skip Multi-Token Prediction (MTP) layers (again, same as DeepseekV2) + match = re.match(r"model.mtp_block.(\d+)", name) + if match: + return None + + # skip all other MTP tensors for now + match = re.match(r"model.mtp_emb_norm.(\d+)", name) + if match: + return None + + match = re.match(r"model.mtp_hidden_norm.(\d+)", name) + if match: + return None + + match = re.match(r"model.mtp_linear_proj.(\d+)", name) + if match: + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # process the experts separately + if name.find("mlp.experts") != -1: + n_experts = self.hparams["moe_num_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["gate_proj", "up_proj", "down_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename_to_retrieve]) + del self._experts[bid][ename_to_retrieve] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + yield from super().modify_tensors(data_torch, merged_name, bid) + else: + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("PaddleOCRVLForConditionalGeneration") +class PaddleOCRModel(Ernie4_5Model): + model_arch = gguf.MODEL_ARCH.PADDLEOCR + + +@ModelBase.register("PaddleOCRVisionModel") +class PaddleOCRVisionModel(MmprojModel): + # PaddleOCR-VL uses a modified version of Siglip + min_pixels: int = 0 + max_pixels: int = 0 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.min_pixels = self.preprocessor_config["min_pixels"] + self.max_pixels = self.preprocessor_config["max_pixels"] + self.hparams_vision["image_size"] = int(math.sqrt(self.max_pixels)) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + hparams = self.hparams_vision + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PADDLEOCR) + self.gguf_writer.add_vision_max_pixels(self.max_pixels) + self.gguf_writer.add_vision_min_pixels(self.min_pixels) + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("rms_norm_eps", 1e-6)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "vision_model" not in name and "mlp_AR" not in name: + return None + name = name.replace("visual.", "model.") + if "packing_position_embedding" in name: + # unused + return None + if "vision_model.head" in name: + # we don't yet support image embeddings for this model + return None + + return super().filter_tensors((name, gen)) diff --git a/conversion/exaone.py b/conversion/exaone.py new file mode 100644 index 00000000000..aa1313e2f80 --- /dev/null +++ b/conversion/exaone.py @@ -0,0 +1,210 @@ +from __future__ import annotations + +import math + +from pathlib import Path +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("ExaoneForCausalLM") +class ExaoneModel(TextModel): + model_arch = gguf.MODEL_ARCH.EXAONE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + + assert (hparams["activation_function"] == "silu") + + rotary_factor = self.find_hparam(["partial_rotary_factor", "rope_pct"], optional=True) + rotary_factor = rotary_factor if rotary_factor is not None else 1.0 + self.gguf_writer.add_rope_dimension_count(int(rotary_factor * (hparams["hidden_size"] // hparams["num_attention_heads"]))) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): + if rope_params.get("rope_type", '').lower() == "llama3": + base = self.rope_parameters.get("rope_theta", 10000.0) + if (dim := self.hparams.get("head_dim")) is None: + dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) + + factor = rope_params.get("factor", 8.0) + low_freq_factor = rope_params.get("low_freq_factor", 1.0) + high_freq_factor = rope_params.get("high_freq_factor", 4.0) + old_context_len = self.hparams.get("original_max_position_embeddings", 8192) + + low_freq_wavelen = old_context_len / low_freq_factor + high_freq_wavelen = old_context_len / high_freq_factor + assert low_freq_wavelen != high_freq_wavelen + + rope_factors = [] + for freq in freqs: + wavelen = 2 * math.pi / freq + if wavelen < high_freq_wavelen: + rope_factors.append(1) + elif wavelen > low_freq_wavelen: + rope_factors.append(factor) + else: + smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) + rope_factors.append(1 / ((1 - smooth) / factor + smooth)) + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) + + +@ModelBase.register("Exaone4ForCausalLM") +class Exaone4Model(TextModel): + model_arch = gguf.MODEL_ARCH.EXAONE4 + + def set_vocab(self): + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + if hparams.get("sliding_window") is not None: + self.gguf_writer.add_sliding_window(hparams["sliding_window"]) + if "layer_types" in hparams: + self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]]) + elif "sliding_window_pattern" in hparams: + sliding_window_pattern = [] + if isinstance(hparams["sliding_window_pattern"], str): # e.g. LLLG + for i in range(hparams["num_hidden_layers"]): + sliding_window_pattern.append(hparams["sliding_window_pattern"][i % len(hparams["sliding_window_pattern"])] == "L") + if isinstance(hparams["sliding_window_pattern"], int): # e.g. 4 + for i in range(hparams["num_hidden_layers"]): + sliding_window_pattern.append((i + 1) % hparams["sliding_window_pattern"] != 0) + if len(sliding_window_pattern) == hparams["num_hidden_layers"]: + self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): + if rope_params.get("rope_type", '').lower() == "llama3": + base = rope_params.get("rope_theta", 10_000.0) + if (dim := self.hparams.get("head_dim")) is None: + dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) + + factor = rope_params.get("factor", 16.0) + low_freq_factor = rope_params.get("low_freq_factor", 1.0) + high_freq_factor = rope_params.get("high_freq_factor", 4.0) + old_context_len = self.hparams.get("original_max_position_embeddings", 8192) + + low_freq_wavelen = old_context_len / low_freq_factor + high_freq_wavelen = old_context_len / high_freq_factor + + rope_factors = [] + for freq in freqs: + wavelen = 2 * math.pi / freq + if wavelen < high_freq_wavelen: + rope_factors.append(1) + elif wavelen > low_freq_wavelen: + rope_factors.append(factor) + else: + smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) + rope_factors.append(1 / ((1 - smooth) / factor + smooth)) + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) + + +@ModelBase.register("ExaoneMoEForCausalLM") +class ExaoneMoEModel(Exaone4Model): + model_arch = gguf.MODEL_ARCH.EXAONE_MOE + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + moe_intermediate_size = self.hparams["moe_intermediate_size"] + num_shared_experts = self.hparams["num_shared_experts"] + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + self.gguf_writer.add_expert_shared_count(num_shared_experts) + self.gguf_writer.add_expert_shared_feed_forward_length(moe_intermediate_size * num_shared_experts) + self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) + self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) + n_dense_layer = self.hparams.get("first_k_dense_replace", self.hparams.get("first_last_k_dense_replace", 0)) + self.gguf_writer.add_leading_dense_block_count(n_dense_layer) + self.gguf_writer.add_nextn_predict_layers(self.hparams.get("num_nextn_predict_layers", 0)) + + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.startswith("mtp."): + if name.find("layers.") != -1: + # `mtp.layers.0.[module_name]` format + name = name.replace(f"mtp.layers.{bid}", f"model.layers.{bid + self.hparams['num_hidden_layers']}") + else: + # mtp fc/norm weights + remapper = { + "mtp.fc": "model.layers.{bid}.eh_proj", + "mtp.pre_fc_norm_embedding": "model.layers.{bid}.enorm", + "mtp.pre_fc_norm_hidden": "model.layers.{bid}.hnorm", + "mtp.norm": "model.layers.{bid}.shared_head.norm", + } + _n = Path(name) + new_name = remapper[_n.stem] + _n.suffix + + # set shared weights for all NextN/MTP layers + for bid in range(self.hparams['num_hidden_layers'], self.block_count): + yield from super().modify_tensors(data_torch, new_name.format(bid=bid), bid) + return + + if name.find("mlp.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + new_name = self.map_tensor_name(merged_name) + + yield from super().modify_tensors(data_torch, new_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/falcon.py b/conversion/falcon.py new file mode 100644 index 00000000000..085fd4cd33f --- /dev/null +++ b/conversion/falcon.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("FalconForCausalLM", "RWForCausalLM") +class FalconModel(TextModel): + model_arch = gguf.MODEL_ARCH.FALCON + + def set_gguf_parameters(self): + n_head = self.hparams.get("num_attention_heads") + if n_head is None: + n_head = self.hparams["n_head"] # old name + + n_head_kv = self.hparams.get("num_kv_heads") + if n_head_kv is None: + n_head_kv = self.hparams.get("n_head_kv", 1) # old name + + self.gguf_writer.add_context_length(2048) # not in config.json + self.gguf_writer.add_tensor_data_layout("jploski") # qkv tensor transform + self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) + self.gguf_writer.add_feed_forward_length(4 * self.hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(n_head) + self.gguf_writer.add_head_count_kv(n_head_kv) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # QKV tensor transform + # The original query_key_value tensor contains n_head_kv "kv groups", + # each consisting of n_head/n_head_kv query weights followed by one key + # and one value weight (shared by all query heads in the kv group). + # This layout makes it a big pain to work with in GGML. + # So we rearrange them here,, so that we have n_head query weights + # followed by n_head_kv key weights followed by n_head_kv value weights, + # in contiguous fashion. + # ref: https://github.com/jploski/ggml/blob/falcon40b/examples/falcon/convert-hf-to-ggml.py + + if "query_key_value" in name: + n_head = self.find_hparam(["num_attention_heads", "n_head"]) + n_head_kv = self.find_hparam(["num_kv_heads", "n_head_kv"], optional=True) or 1 + head_dim = self.hparams["hidden_size"] // n_head + + qkv = data_torch.view(n_head_kv, n_head // n_head_kv + 2, head_dim, head_dim * n_head) + q = qkv[:, :-2].reshape(n_head * head_dim, head_dim * n_head) + k = qkv[:, [-2]].reshape(n_head_kv * head_dim, head_dim * n_head) + v = qkv[:, [-1]].reshape(n_head_kv * head_dim, head_dim * n_head) + data_torch = torch.cat((q, k, v)).reshape_as(data_torch) + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/falcon_h1.py b/conversion/falcon_h1.py new file mode 100644 index 00000000000..a8bc880b2c4 --- /dev/null +++ b/conversion/falcon_h1.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from typing import Any, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, gguf + +from .llama import LlamaModel +from .mamba import Mamba2Model + + +@ModelBase.register("FalconH1ForCausalLM") +class FalconH1Model(Mamba2Model): + model_arch = gguf.MODEL_ARCH.FALCON_H1 + + def __init__(self, *args, **kwargs): + # Set the hparam prefixes for Falcon Mamba2 + self.hparam_prefixes = ["mamba"] + + # Initialize the base Mamba2Model + super().__init__(*args, **kwargs) + + # Use Llama conversion for attention + self._transformer_model_class = LlamaModel + + # n_group and d_inner are used during reshape_tensors for mamba2 + self.n_group = self.find_hparam(["n_groups"]) + self.d_inner = self.find_hparam(["mamba_d_ssm"]) + self.d_head = self.find_hparam(["d_head"]) + + # Initialize any Falcon Mamba2 specific attributes + self.has_attention = True # Falcon Mamba2 has attention components + + # Load Falcon-H1 multipliers from hyperparameters + self.attention_in_multiplier = self.find_hparam(["attention_in_multiplier"], optional=True) + self.attention_out_multiplier = self.find_hparam(["attention_out_multiplier"], optional=True) + self.ssm_in_multiplier = self.find_hparam(["ssm_in_multiplier"], optional=True) + self.ssm_out_multiplier = self.find_hparam(["ssm_out_multiplier"], optional=True) + self.mlp_multipliers = self.find_hparam(["mlp_multipliers"], optional=True) + self.ssm_multipliers = self.find_hparam(["ssm_multipliers"], optional=True) + self.intermediate_size = self.find_hparam(["intermediate_size"]) + self.key_multiplier = self.find_hparam(["key_multiplier"], optional=True) + + def find_hparam(self, keys: Iterable[str], *args, **kwargs) -> Any: + prefixed = [] + for pfx in self.hparam_prefixes: + prefixed.extend( + "_".join([pfx, k]) + for k in keys + ) + keys = list(keys) + prefixed + return super().find_hparam(keys, *args, **kwargs) + + def set_vocab(self): + self._set_vocab_gpt2() + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + tensors = list(super().modify_tensors(data_torch, name, bid)) + tensor = tensors[0][1] + + if "down_proj" in name: + tensor = tensor * self.mlp_multipliers[1] + elif "gate_proj" in name: + tensor = tensor * self.mlp_multipliers[0] + elif "k_proj" in name: + tensor = tensor * self.key_multiplier * self.attention_in_multiplier + elif "q_proj" in name: + tensor = tensor * self.attention_in_multiplier + elif "v_proj" in name: + tensor = tensor * self.attention_in_multiplier + elif "o_proj" in name: + tensor = tensor * self.attention_out_multiplier + elif "out_proj" in name: + tensor = tensor * self.ssm_out_multiplier + elif "in_proj" in name: + tensor = tensor * self.ssm_in_multiplier + zxbcdt_multipliers = self.hparams["ssm_multipliers"] + intermediate_size = self.hparams["mamba_d_ssm"] + groups_time_state_size = self.hparams["mamba_n_groups"] * self.hparams["mamba_d_state"] + tensor[:intermediate_size, :] *= zxbcdt_multipliers[0] + tensor[intermediate_size:2 * intermediate_size, :] *= zxbcdt_multipliers[1] + tensor[2 * intermediate_size:2 * intermediate_size + groups_time_state_size, :] *= zxbcdt_multipliers[2] + tensor[2 * intermediate_size + groups_time_state_size:2 * intermediate_size + 2 * groups_time_state_size, :] *= zxbcdt_multipliers[3] + tensor[2 * intermediate_size + 2 * groups_time_state_size:, :] *= zxbcdt_multipliers[4] + elif "lm_head" in name: + tensor = tensor * self.hparams["lm_head_multiplier"] + elif "embed_tokens" in name: + tensor = tensor * self.hparams["embedding_multiplier"] + elif "mamba.norm" in name: + tensor = tensor.reshape(self.n_group, self.d_inner // self.n_group) + + tensors = [(tensors[0][0], tensor)] + return tensors + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + ## General Params ## + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + # Override some Mamba2 defaults + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.hparams.get("max_position_embeddings", 0)) + self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) + + ## Attention params ## + self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) # Override value 0 from Mamba2 + self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"]) + self.gguf_writer.add_key_length(self.hparams["head_dim"]) + self.gguf_writer.add_value_length(self.hparams["head_dim"]) + + ## Validation ## + assert self.hparams.get("hidden_act") in [None, "silu"], "Only SILU activation supported" + assert self.d_inner % self.d_head == 0, f"SSM inner size {self.d_inner} not a multiple of head dim {self.d_head}" + + # Add any other Falcon Mamba2 specific configuration + self.gguf_writer.add_rope_freq_base(self.rope_parameters["rope_theta"]) diff --git a/conversion/gemma.py b/conversion/gemma.py new file mode 100644 index 00000000000..a6e14fbcb98 --- /dev/null +++ b/conversion/gemma.py @@ -0,0 +1,840 @@ +from __future__ import annotations + +import json +import re + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GemmaForCausalLM") +class GemmaModel(TextModel): + model_arch = gguf.MODEL_ARCH.GEMMA + + def set_vocab(self): + self._set_vocab_sentencepiece() + + # TODO: these special tokens should be exported only for the CodeGemma family + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False, + special_token_types = ['prefix', 'suffix', 'middle', 'fsep', 'eot']) + special_vocab._set_special_token("prefix", 67) + special_vocab._set_special_token("suffix", 69) + special_vocab._set_special_token("middle", 68) + special_vocab._set_special_token("fsep", 70) + special_vocab._set_special_token("eot", 107) + special_vocab.chat_template = None # do not add it twice + special_vocab.add_to_gguf(self.gguf_writer) + + self.gguf_writer.add_add_space_prefix(False) + + def set_gguf_parameters(self): + hparams = self.hparams + + self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) + self.gguf_writer.add_head_count(hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"] if "num_key_value_heads" in hparams else hparams["num_attention_heads"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) + self.gguf_writer.add_key_length(hparams["head_dim"]) + self.gguf_writer.add_value_length(hparams["head_dim"]) + self.gguf_writer.add_file_type(self.ftype) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # lm_head is not used in llama.cpp, while autoawq will include this tensor in model + # To prevent errors, skip loading lm_head.weight. + if name == "lm_head.weight": + logger.debug(f"Skipping get tensor {name!r} in safetensors so that convert can end normally.") + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # ref: https://github.com/huggingface/transformers/blob/fc37f38915372c15992b540dfcbbe00a916d4fc6/src/transformers/models/gemma/modeling_gemma.py#L89 + if name.endswith("norm.weight"): + data_torch = data_torch + 1 + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Gemma2ForCausalLM") +class Gemma2Model(TextModel): + model_arch = gguf.MODEL_ARCH.GEMMA2 + + def set_vocab(self): + self._set_vocab_sentencepiece() + + self.gguf_writer.add_add_space_prefix(False) + + def set_gguf_parameters(self): + hparams = self.hparams + + self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) + self.gguf_writer.add_head_count(hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"] if "num_key_value_heads" in hparams else hparams["num_attention_heads"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) + self.gguf_writer.add_key_length(hparams["head_dim"]) + self.gguf_writer.add_value_length(hparams["head_dim"]) + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_attn_logit_softcapping( + self.hparams["attn_logit_softcapping"] + ) + self.gguf_writer.add_final_logit_softcapping( + self.hparams["final_logit_softcapping"] + ) + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # lm_head is not used in llama.cpp, while autoawq will include this tensor in model + # To prevent errors, skip loading lm_head.weight. + if name == "lm_head.weight": + logger.debug(f"Skipping get tensor {name!r} in safetensors so that convert can end normally.") + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # ref: https://github.com/huggingface/transformers/blob/fc37f38915372c15992b540dfcbbe00a916d4fc6/src/transformers/models/gemma/modeling_gemma.py#L89 + if name.endswith("norm.weight"): + data_torch = data_torch + 1 + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Gemma3ForCausalLM", "Gemma3ForConditionalGeneration") +class Gemma3Model(TextModel): + model_arch = gguf.MODEL_ARCH.GEMMA3 + + def norm_shift(self, name: str) -> float: + return 1.0 if name.endswith("norm.weight") else 0.0 # Gemma3RMSNorm adds 1.0 to the norm value + + def set_vocab(self): + if (self.dir_model / "tokenizer.model").is_file(): + self._set_vocab_sentencepiece() + self.gguf_writer.add_add_space_prefix(False) + else: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + + # some default values are not specified in the hparams + self.gguf_writer.add_context_length(hparams.get("max_position_embeddings", 131072)) + self.gguf_writer.add_head_count(hparams.get("num_attention_heads", 8)) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("rms_norm_eps", 1e-6)) + self.gguf_writer.add_key_length(hparams.get("head_dim", 256)) + self.gguf_writer.add_value_length(hparams.get("head_dim", 256)) + self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("full_attention", self.rope_parameters).get("rope_theta", 1_000_000.0)) # for global layers + # attn_logit_softcapping is removed in Gemma3 + assert hparams.get("attn_logit_softcapping") is None + if (final_logit_softcap := hparams.get("final_logit_softcapping")): + self.gguf_writer.add_final_logit_softcapping(final_logit_softcap) + if hparams.get("sliding_window_pattern") != 1: + self.gguf_writer.add_sliding_window(hparams["sliding_window"]) + self.gguf_writer.add_head_count_kv(hparams.get("num_key_value_heads", 4)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # remove OOV (out-of-vocabulary) rows in token_embd + if "embed_tokens.weight" in name: + n_vocab_real = -1 + if (self.dir_model / "tokenizer.model").is_file(): + tokens = self._create_vocab_sentencepiece()[0] + n_vocab_real = len(tokens) + else: + with open(self.dir_model / "tokenizer.json", "r", encoding="utf-8") as f: + tokenizer_json = json.load(f) + n_vocab_real = len(tokenizer_json["model"]["vocab"]) + len(tokenizer_json["added_tokens"]) + data_torch = data_torch[:n_vocab_real] + + # ref code in Gemma3RMSNorm + # output = output * (1.0 + self.weight.float()) + # note: this is not the case on gemma3n + f_shift = self.norm_shift(name) + if f_shift != 0.0: + data_torch = data_torch + f_shift + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Gemma3TextModel") +class EmbeddingGemma(Gemma3Model): + model_arch = gguf.MODEL_ARCH.GEMMA_EMBEDDING + module_paths = [] + dense_features_dims = {} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.sentence_transformers_dense_modules: + # read modules.json to determine if model has Dense layers + modules_file = self.dir_model / "modules.json" + if modules_file.is_file(): + with open(modules_file, encoding="utf-8") as modules_json_file: + mods = json.load(modules_json_file) + for mod in mods: + if mod["type"].endswith("Dense"): + mod_path = mod["path"] + # check if model.safetensors file for Dense layer exists + model_tensors_file = self.dir_model / mod_path / "model.safetensors" + if model_tensors_file.is_file(): + self.module_paths.append(mod_path) + # read config.json of the Dense layer to get in/out features + mod_conf_file = self.dir_model / mod_path / "config.json" + if mod_conf_file.is_file(): + with open(mod_conf_file, encoding="utf-8") as mod_conf_json_file: + mod_conf = json.load(mod_conf_json_file) + # hparams dense_2_feat_out and dense_3_feat_in are required when loading model's dense weights + prefix = self._get_dense_prefix(mod_path) + if mod_conf["in_features"] is not None and mod_conf["out_features"] is not None: + self.dense_features_dims[prefix] = (mod_conf["in_features"], mod_conf["out_features"]) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + from safetensors.torch import load_file + module_paths = list(self.module_paths) + for i, module_path in enumerate(module_paths): + tensors_file = self.dir_model / module_path / "model.safetensors" + local_tensors = load_file(tensors_file) + tensor_name = self._get_dense_prefix(module_path) + for name, local_tensor in local_tensors.items(): + if not name.endswith(".weight"): + continue + orig_name = name.replace("linear", tensor_name) + name = self.map_tensor_name(orig_name) + yield name, local_tensor.clone() + + @staticmethod + def _get_dense_prefix(module_path) -> str: + """Get the tensor name prefix for the Dense layer from module path.""" + tensor_name = "dense_2" if module_path == "2_Dense" else "dense_3" + return tensor_name + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # Override the sliding window size as it gets adjusted by the Gemma3TextConfig + # constructor. We want to use the value from the original model's config.json. + # ref: https://github.com/huggingface/transformers/pull/40700 + with open(self.dir_model / "config.json", "r", encoding="utf-8") as f: + config = json.load(f) + orig_sliding_window = config.get("sliding_window") + if orig_sliding_window is None: + raise ValueError("sliding_window not found in model config - this is required for the model") + + logger.info(f"Using original sliding_window from config: {orig_sliding_window} " + f"instead of {self.hparams['sliding_window']}") + self.gguf_writer.add_sliding_window(orig_sliding_window) + if self.sentence_transformers_dense_modules: + for dense, dims in self.dense_features_dims.items(): + logger.info(f"Setting dense layer {dense} in/out features to {dims}") + self.gguf_writer.add_dense_features_dims(dense, dims[0], dims[1]) + + self._try_set_pooling_type() + + +@ModelBase.register("Gemma3ForConditionalGeneration") +class Gemma3VisionModel(MmprojModel): + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GEMMA3) + # default values below are taken from HF transformers code + self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("layer_norm_eps", 1e-6)) + self.gguf_writer.add_vision_use_gelu(True) + # calculate proj_scale_factor (used by tinygemma3 test model) + image_seq_length = self.preprocessor_config.get("image_seq_length", 256) + n_per_side = int(image_seq_length ** 0.5) + image_size = self.hparams["image_size"] + patch_size = self.hparams["patch_size"] + proj_scale_factor = (image_size // patch_size) // n_per_side + if proj_scale_factor > 0 and proj_scale_factor != 4: + # we only need to write this if it's not the default value + # in this case, we are converting a test model + self.gguf_writer.add_vision_projector_scale_factor(proj_scale_factor) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + # related to https://github.com/ggml-org/llama.cpp/issues/13025 + if "input_projection" in name: + return gguf.GGMLQuantizationType.F16 + if ".embeddings." in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "vision_model.head." in name: + # skip redundant tensors for tinygemma3 + return None + + if not name.startswith(("multi_modal_projector.", "vision_tower.", "multimodal_projector.", "vision_model.")): + return None + + name = name.replace("_weight", ".weight") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # correct norm value ; only this "soft_emb_norm" need to be corrected as it's part of Gemma projector + # the other norm values are part of SigLIP model, and they are already correct + # ref code: Gemma3RMSNorm + if "soft_emb_norm.weight" in name: + logger.info(f"Correcting norm value for '{name}'") + data_torch = data_torch + 1 + + yield from super().modify_tensors(data_torch, name, bid) + + +class ConformerAudioModel(MmprojModel): + _batch_norm_tensors: list[dict[str, Tensor]] | None = None + + @staticmethod + def is_audio_tensor(name: str): + return any(p in name for p in ["audio", "codebook", "conformer", "depth_embedding", "depthformer", "depth_linear"]) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ConformerAudioModel.is_audio_tensor(name): + if ".conv" in name or "_conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # fold running_mean, running_var and eps into weight and bias for batch_norm + if "batch_norm" in name: + if self._batch_norm_tensors is None: + self._batch_norm_tensors = [{} for _ in range(self.block_count)] + assert bid is not None + self._batch_norm_tensors[bid][name] = data_torch + + if len(self._batch_norm_tensors[bid]) < 5: + return + + weight = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.weight"] + bias = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.bias"] + running_mean = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.running_mean"] + running_var = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.running_var"] + eps = 1e-5 # default value + + a = weight / torch.sqrt(running_var + eps) + b = bias - running_mean * a + yield from super().modify_tensors(a, f"conformer.layers.{bid}.conv.batch_norm.weight", bid) + yield from super().modify_tensors(b, f"conformer.layers.{bid}.conv.batch_norm.bias", bid) + return + + # reshape conv weights + if name.startswith("conformer.pre_encode.conv.") and name.endswith(".bias"): + data_torch = data_torch[:, None, None] + if "conv.depthwise_conv" in name and name.endswith(".weight"): + assert data_torch.shape[1] == 1 + data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[2]) + if "conv.pointwise_conv" in name and name.endswith(".weight"): + assert data_torch.shape[2] == 1 + data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[1]) + + mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) + yield (mapped_name, data_torch) + + +@ModelBase.register("Gemma3nForConditionalGeneration") +class Gemma3nVisionAudioModel(ConformerAudioModel): + has_audio_encoder = True + has_vision_encoder = True + + # Double indexed mapping for MobileNetV5 blocks (not supported by tensor_mapping.py) + # This is the only known model having this, so we prefer implementing it outside of tensor_mapping.py + block_tensor_mapping = { + "model.vision_tower.timm_model.blocks.{bid}.{sid}.conv_exp.weight": "v.blk.{bid}.{sid}.conv_exp.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.bn1.weight": "v.blk.{bid}.{sid}.bn1.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.conv_pwl.weight": "v.blk.{bid}.{sid}.conv_pwl.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.bn2.weight": "v.blk.{bid}.{sid}.bn2.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_start.conv.weight": "v.blk.{bid}.{sid}.dw_start.conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_start.bn.weight": "v.blk.{bid}.{sid}.dw_start.bn.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_mid.conv.weight": "v.blk.{bid}.{sid}.dw_mid.conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_mid.bn.weight": "v.blk.{bid}.{sid}.dw_mid.bn.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_exp.conv.weight": "v.blk.{bid}.{sid}.pw_exp.conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_exp.bn.weight": "v.blk.{bid}.{sid}.pw_exp.bn.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_proj.conv.weight": "v.blk.{bid}.{sid}.pw_proj.conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_proj.bn.weight": "v.blk.{bid}.{sid}.pw_proj.bn.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.layer_scale.gamma": "v.blk.{bid}.{sid}.layer_scale.gamma", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.query.proj.weight": "v.blk.{bid}.{sid}.attn.query.proj.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.proj.weight": "v.blk.{bid}.{sid}.attn.key.proj.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.proj.weight": "v.blk.{bid}.{sid}.attn.value.proj.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.output.proj.weight": "v.blk.{bid}.{sid}.attn.output.proj.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.down_conv.weight": "v.blk.{bid}.{sid}.attn.key.down_conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.norm.weight": "v.blk.{bid}.{sid}.attn.key.norm.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.down_conv.weight": "v.blk.{bid}.{sid}.attn.value.down_conv.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.norm.weight": "v.blk.{bid}.{sid}.attn.value.norm.weight", + "model.vision_tower.timm_model.blocks.{bid}.{sid}.norm.weight": "v.blk.{bid}.{sid}.norm.weight", + } + + def __init__(self, *args, **kwargs): + # Parent init will call find_hparam which now returns 0 for empty keys + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["n_layers"] = 128 # fake value for audio encoder, vision encoder doesn't use it + self.hparams_vision["intermediate_size"] = self.hparams_vision.get("intermediate_size", 2048) * 4 + self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_attention_heads", 8) + + # MobileNetV5 does not use image_mean/std + self.preprocessor_config["image_mean"] = [0.0 ,0.0 , 0.0] + self.preprocessor_config["image_std"] = [1.0 ,1.0 ,1.0] + self.hparams_vision["image_size"] = self.preprocessor_config.get( + "size", {"height": 768, "width": 768} + )["height"] + + # Image sequence length (256 tokens = 16x16 for Gemma3n) + image_seq_length = self.preprocessor_config.get("image_seq_length", 256) + image_size = self.hparams_vision["image_size"] + self.hparams_vision["patch_size"] = image_size // image_seq_length + + # remap audio hparams + assert self.hparams_audio is not None + self.hparams_audio["n_layers"] = self.hparams_audio["conf_num_hidden_layers"] + self.hparams_audio["num_attention_heads"] = self.hparams_audio["conf_num_attention_heads"] + self.hparams_audio["feat_in"] = self.hparams_audio["input_feat_size"] + self.hparams_audio["intermediate_size"] = self.hparams_audio.get("intermediate_size", 6144) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # vision params + self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA3NV) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) + + # audio params + assert self.hparams_audio is not None + self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.GEMMA3NA) + self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) + self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + # Force quantization settings for specific tensor types + if "input_projection" in name or "input_proj" in name: + return gguf.GGMLQuantizationType.F16 + if ".embeddings." in name or "stem" in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + def custom_map(self, name: str) -> str: + """Parses names like model.vision_tower.timm_model.blocks.1.2.suffix and applies template mapping.""" + parts = name.split(".") + # MobileNet blocks have at least 7 parts: model, vision_tower, timm_model, blocks, bid, sid, and suffix + if len(parts) >= 7: + bid, sid = parts[4], parts[5] + suffix = ".".join(parts[6:]) + template = f"model.vision_tower.timm_model.blocks.{{bid}}.{{sid}}.{suffix}" + if template in self.block_tensor_mapping: + return self.block_tensor_mapping[template].format(bid=bid, sid=sid) + + raise ValueError(f"Unknown name: {name}") + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if (ConformerAudioModel.is_audio_tensor(name)): + name = name.replace("model.audio_tower.conformer.", "conformer.layers.") + yield from super().modify_tensors(data_torch, name, bid) + + # Gemma3n uses + # - model.embed_vision.* for projection layers + # - model.vision_tower.* for vision encoder + # Skip non-vision tensors + if not (name.startswith("model.embed_vision.") or name.startswith("model.vision_tower.")): + return + + if name.startswith("model.vision_tower.timm_model.blocks."): + # Double-indexed block tensors through custom logic + yield (self.custom_map(name), data_torch) + return + else: + # Route non-repeating (conv_stem, msfa, embedding, etc.) and un-catched through tensor_mapping.py + new_name = self.map_tensor_name(name) + + if new_name.endswith("conv_stem.conv.bias") or new_name.endswith("layer_scale.gamma"): + data_torch = data_torch.unsqueeze(0).unsqueeze(-1).unsqueeze(-1) # [1, C, 1, 1] + + yield from ModelBase.modify_tensors(self, data_torch, new_name, bid) + + +@ModelBase.register("Gemma3nForCausalLM", "Gemma3nForConditionalGeneration") +class Gemma3NModel(Gemma3Model): + model_arch = gguf.MODEL_ARCH.GEMMA3N + + _altup_proj: list[Tensor] = [] + _altup_unembd: list[Tensor] = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams["altup_num_inputs"] == 4, "Current conversion only supports 4 altup inputs" + self._altup_proj = [ + torch.Tensor(), # to be replaced + torch.Tensor(), # to be replaced + torch.Tensor(), # to be replaced + ] + self._altup_unembd = [ + torch.Tensor(), # to be replaced + torch.Tensor(), # to be replaced + torch.Tensor(), # to be replaced + ] + + def norm_shift(self, name: str) -> float: + del name + return 0.0 # same value with Gemma3p5RMSNorm scale_shift on python code + + def set_vocab(self): + # For Gemma3n multimodal models, we need the FULL vocab_size (262400) + # which includes special tokens from 262144-262399 for vision/audio. + # The vocab_size_per_layer_input (262144) is only the embedding size per layer. + # Temporarily override the hparams lookup order to prioritize vocab_size. + + # Store original vocab_size_per_layer_input if it exists + vocab_size_per_layer_input = self.hparams.get("vocab_size_per_layer_input") + + # Temporarily remove vocab_size_per_layer_input to force using vocab_size + if vocab_size_per_layer_input is not None: + del self.hparams["vocab_size_per_layer_input"] + + # Call parent set_vocab which will now use vocab_size (262400) + super().set_vocab() + + # Restore vocab_size_per_layer_input for later use + if vocab_size_per_layer_input is not None: + self.hparams["vocab_size_per_layer_input"] = vocab_size_per_layer_input + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_altup_active_idx(self.hparams["altup_active_idx"]) + self.gguf_writer.add_altup_num_inputs(self.hparams["altup_num_inputs"]) + self.gguf_writer.add_embedding_length_per_layer_input(self.hparams["hidden_size_per_layer_input"]) + self.gguf_writer.add_shared_kv_layers(self.hparams["num_kv_shared_layers"]) + + activation_sparsity_scale = [] + for s in self.hparams["activation_sparsity_pattern"]: + normal_dist = torch.distributions.normal.Normal(0, 1) + std_multiplier = normal_dist.icdf(torch.tensor(s, dtype=torch.float32)) + activation_sparsity_scale.append(std_multiplier.item()) + self.gguf_writer.add_activation_sparsity_scale(activation_sparsity_scale) + + sliding_window_pattern = [] + for t in self.hparams["layer_types"]: + sliding_window_pattern.append(t == "sliding_attention") + self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) + + def _stack_matrices(self, matrices: list[Tensor]) -> Tensor | None: + has_all = all(m.numel() > 0 for m in matrices) + if not has_all: + return None + else: + return torch.stack(matrices, dim=0) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith("_scale"): + name = name + ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # TODO: implement self.prediction_coefs.weight.clamp_(...) + + # Pad token embeddings for vision/audio special tokens (262144-262399) + if "embed_tokens.weight" in name or "embed_tokens_per_layer" in name: + # Move to CPU to avoid meta device issues during padding + data_torch = data_torch.to(device="cpu") + + vocab_size = self.hparams.get("vocab_size", 262400) + current_size = data_torch.shape[0] # First dimension is vocab_size + + if current_size < vocab_size: + # Pad with zeros for vision/audio tokens (they get embeddings from vision tower) + padding_size = vocab_size - current_size + tensor_type = "per-layer embeddings" if "per_layer" in name else "token embeddings" + logger.info(f"Padding {tensor_type} shape {list(data_torch.shape)} from {current_size} to {vocab_size} (adding {padding_size} vision/audio token slots)") + + # Create padding with zeros (vision tokens won't use these embeddings) + padding = torch.zeros((padding_size, data_torch.shape[1]), dtype=data_torch.dtype, device=data_torch.device) + data_torch = torch.cat([data_torch, padding], dim=0) + + # Continue with normal processing + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + return + + if "altup_unembed_projections" in name: + data_torch = data_torch.to(device="cpu") + # altup_unembed matrices are [hidden_size, hidden_size], NOT vocab-based + # They should NOT be padded + if ".0." in name: + self._altup_unembd[0] = data_torch + elif ".1." in name: + self._altup_unembd[1] = data_torch + elif ".2." in name: + self._altup_unembd[2] = data_torch + else: + raise ValueError(f"Unknown name: {name}") + out = self._stack_matrices(self._altup_unembd) + if out is not None: + yield from ModelBase.modify_tensors(self, out, "model.altup_unembed_projections.weight", bid) + return + else: + return + + if "altup_projections" in name: + data_torch = data_torch.to(device="cpu") + if ".0." in name: + self._altup_proj[0] = data_torch + elif ".1." in name: + self._altup_proj[1] = data_torch + elif ".2." in name: + self._altup_proj[2] = data_torch + else: + raise ValueError(f"Unknown name: {name}") + out = self._stack_matrices(self._altup_proj) + if out is not None: + yield from ModelBase.modify_tensors(self, out, "model.altup_projections.weight", bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Gemma4ForConditionalGeneration") +class Gemma4Model(Gemma3Model): + model_arch = gguf.MODEL_ARCH.GEMMA4 + + def norm_shift(self, name: str) -> float: + del name # unused + return 0.0 + + def set_vocab(self): + vocab = gguf.LlamaHfVocab(self.dir_model) + tokens = [] + scores = [] + toktypes = [] + visible_tokens = {"<|channel>", "", "<|tool_call>", "", "<|tool_response>", "", "<|\"|>"} + + for text, score, toktype in vocab.all_tokens(): + tokens.append(text) + scores.append(score) + text_str = text.decode() + if text_str in visible_tokens: + # always render these tokens, so that the chat parser can read them + toktypes.append(gguf.TokenType.USER_DEFINED) + logger.info(f"Token '{text_str}' is set to USER_DEFINED") + else: + toktypes.append(toktype) + + assert len(tokens) == vocab.vocab_size + + self.gguf_writer.add_tokenizer_model("gemma4") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab.add_to_gguf(self.gguf_writer) + self.gguf_writer.add_add_space_prefix(False) + self.gguf_writer.add_add_bos_token(True) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + num_kv_shared_layers = self.hparams["num_kv_shared_layers"] + self.gguf_writer.add_shared_kv_layers(num_kv_shared_layers) + + # per-layer embedding is optional + n_pl_embd = self.hparams.get("hidden_size_per_layer_input") or 0 + self.gguf_writer.add_embedding_length_per_layer_input(n_pl_embd) + + swa_layers = [t == "sliding_attention" for t in self.hparams["layer_types"]] + self.gguf_writer.add_sliding_window_pattern(swa_layers) + + head_dim_full = self.hparams["global_head_dim"] + head_dim_swa = self.hparams["head_dim"] + # correct the head dim for global/swa layers + self.gguf_writer.add_key_length(head_dim_full) + self.gguf_writer.add_value_length(head_dim_full) + self.gguf_writer.add_key_length_swa(head_dim_swa) + self.gguf_writer.add_value_length_swa(head_dim_swa) + + expert_intermediate_size = self.find_hparam(["expert_intermediate_size", "moe_intermediate_size"]) + if expert_intermediate_size is not None: + self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size) + + # if use_double_wide_mlp is set, we need to adjust the value for kv shared layers + use_double_wide_mlp = self.hparams.get("use_double_wide_mlp", False) + first_kv_shared_layer_idx = self.block_count - num_kv_shared_layers + if use_double_wide_mlp: + n_ff = self.hparams["intermediate_size"] + n_ff_arr = [n_ff if il < first_kv_shared_layer_idx else n_ff * 2 for il in range(self.block_count)] + self.gguf_writer.add_feed_forward_length(n_ff_arr) + + # handle num_global_key_value_heads + num_key_value_heads_full = self.hparams.get("num_global_key_value_heads") + num_key_value_heads_swa = self.hparams.get("num_key_value_heads") + if num_key_value_heads_full is not None and num_key_value_heads_swa is not None: + value_arr = [num_key_value_heads_swa if is_swa else num_key_value_heads_full for is_swa in swa_layers] + self.gguf_writer.add_head_count_kv(value_arr) + + # handle n_rot differently for global vs swa layers + partial_rotary_factor_swa = self.hparams.get("partial_rotary_factor", 1.0) + n_rot_full = int(head_dim_full) # "proportional" is used, see generate_extra_tensors + n_rot_swa = int(head_dim_swa * partial_rotary_factor_swa) + self.gguf_writer.add_rope_dimension_count(n_rot_full) + self.gguf_writer.add_rope_dimension_count_swa(n_rot_swa) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + # full layer uses "proportional" rope with partial_rotary_factor=0.25 + # the expected ordering is cc000000ss000000 (c = cos, s = sin, 0 = unrotated), + # but ggml neox only supports ccss000000000000, and we cannot rearrange the head because that will break use_alternative_attention + # solution is to set specific freq_factors for the unrotated dims + + # IMPORTANT: this ROPE_FREQS tensor is ONLY used by the full_attention layers + rope_params_full = self.hparams["rope_parameters"]["full_attention"] + assert rope_params_full["rope_type"] == "proportional" + head_dim_full = (self.hparams["global_head_dim"]) + partial_rotary_factor_full = rope_params_full["partial_rotary_factor"] + n_rot_full = int(head_dim_full * partial_rotary_factor_full / 2) + n_unrot_full = int(head_dim_full / 2) - n_rot_full + values = [1.0] * n_rot_full + [1e30] * n_unrot_full + rope_freqs_full = torch.tensor(values, dtype=torch.float32) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), rope_freqs_full) + + def _generate_nvfp4_tensors(self): + # Gemma-4 stores a per-layer router.per_expert_scale ([n_expert]) that scales + # each expert's contribution. It's mathematically equivalent to a per-expert + # scalar on the down_proj output, which is exactly where ffn_down_exps_s is + # applied at inference. Fold it into each expert's NVFP4 weight_scale_2 so the + # existing NVFP4 path produces the right scales. + n_experts = self.find_hparam(["num_local_experts", "num_experts"], optional=True) or 0 + for name in [n for n in self.model_tensors if n.endswith(".router.per_expert_scale")]: + bid_match = re.search(r"\.layers\.(\d+)\.", name) + if bid_match is None: + continue + bid = bid_match.group(1) + prefix = name[: name.index(f".layers.{bid}.") + len(f".layers.{bid}.")] + w2_targets = [f"{prefix}experts.{e}.down_proj.weight_scale_2" for e in range(n_experts)] + present = [w2 in self.model_tensors for w2 in w2_targets] + if not any(present): + continue + assert all(present), f"layer {bid}: partial NVFP4 quantization across experts" + r = self.model_tensors.pop(name) + for e, w2 in enumerate(w2_targets): + s = self.model_tensors[w2] + self.model_tensors[w2] = lambda s=s, r=r, i=e: s() * r()[i] + super()._generate_nvfp4_tensors() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith("per_dim_scale") or name.endswith("layer_scalar"): + name = name + ".weight" + if ".experts." in name and not name.endswith((".weight", ".weight_scale", ".weight_scale_2", ".input_scale")): + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.endswith("router.scale"): + name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_INP, bid, ".scale") + yield (name, data_torch) + return + if ".per_expert_scale" in name: + # convert per-expert scale to FFN down scale + name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN_EXP, bid, ".scale") + yield (name, data_torch) + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Gemma4ForConditionalGeneration") +class Gemma4VisionAudioModel(MmprojModel): + has_audio_encoder = True + has_vision_encoder = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["image_size"] = 224 # unused, but set to avoid error + + # remap audio hparams + if self.hparams_audio: + self.hparams_audio["feat_in"] = self.hparams_audio.get("input_feat_size", 128) + self.hparams_audio["intermediate_size"] = self.hparams_audio["hidden_size"] * 4 + else: + self.has_audio_encoder = False + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # vision params + self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA4V) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) + + # audio params + if self.hparams_audio: + self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.GEMMA4A) + self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) + self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) + + def is_audio_tensor(self, name: str) -> bool: + return "audio_tower" in name or "embed_audio" in name + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if self.is_audio_tensor(name): + if ".conv" in name or "_conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F32 + if "position_embedding_table" in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + del bid # unused + + if len(data_torch.shape) == 0: + # convert scalar tensors (input/output_mix/max) to 1D tensors + data_torch = data_torch.unsqueeze(0) + + if self.is_audio_tensor(name): + assert self.hparams_audio is not None + name = name.replace("model.audio_tower.", "conformer.") + name = name.replace(".linear.", ".") + if name.endswith("per_dim_key_scale") or name.endswith("per_dim_scale"): + name = name + ".weight" + data_torch = torch.nn.functional.softplus(data_torch) + if "lconv1d.depthwise_conv1d" in name and name.endswith(".weight"): + assert data_torch.shape[1] == 1 + data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[2]) + mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) + yield (mapped_name, data_torch) + + else: + name = name.replace("model.vision_tower.encoder.", "vision_model.model.") + name = name.replace(".linear.weight", ".weight") + if name.endswith("layer_scalar") or name.endswith("position_embedding_table"): + name = name + ".weight" + if name.endswith("patch_embedder.input_proj.weight"): + n_embd, ksize_sq_c = data_torch.shape + patch_size = int((ksize_sq_c // 3) ** 0.5) + data_torch = data_torch.reshape(n_embd, patch_size, patch_size, 3) + data_torch = data_torch.permute(0, 3, 1, 2).contiguous() + mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) + yield (mapped_name, data_torch) diff --git a/conversion/glm.py b/conversion/glm.py new file mode 100644 index 00000000000..641937720d6 --- /dev/null +++ b/conversion/glm.py @@ -0,0 +1,259 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + +from .deepseek import DeepseekV2Model + + +@ModelBase.register("Glm4ForCausalLM", "Glm4vForConditionalGeneration") +class Glm4Model(TextModel): + model_arch = gguf.MODEL_ARCH.GLM4 + use_mrope = False + partial_rotary_factor = 0.5 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.partial_rotary_factor = self.rope_parameters.get("partial_rotary_factor", 0.5) + if "mrope_section" in self.rope_parameters: + self.use_mrope = True + logger.info("Q/K weight will need to be permuted for M-RoPE") + + def set_vocab(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (rope_dim := self.hparams.get("head_dim")) is None: + rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.partial_rotary_factor)) + + @staticmethod + def normal_to_neox(weights: Tensor, n_head: int, n_head_kv: int, head_dim: int, partial_rotary_factor: float) -> Tensor: + orig_shape = weights.shape + if len(orig_shape) == 1: + weights = weights.unsqueeze(1) # [out_dim, 1] + if len(weights.shape) != 2: + raise ValueError("Only 1D and 2D tensors are supported.") + n_effective_heads = weights.shape[0] // head_dim + if n_head_kv is not None and n_effective_heads != n_head: + if n_effective_heads != n_head_kv: + raise AssertionError(f"Mismatch in effective heads: computed {n_effective_heads}, expected {n_head} or {n_head_kv}") + rotary_dim = int(head_dim * partial_rotary_factor) + if rotary_dim % 2 != 0: + raise ValueError("rotary_dim must be even.") + reshaped = weights.reshape(n_effective_heads, head_dim, -1) + rot_part = reshaped[:, :rotary_dim, :] + non_rot_part = reshaped[:, rotary_dim:, :] + permuted_rot = torch.cat((rot_part[:, ::2, :], rot_part[:, 1::2, :]), dim=1) + combined = torch.cat((permuted_rot, non_rot_part), dim=1) + result = combined.reshape(weights.shape) + return result if len(orig_shape) != 1 else result.squeeze(1) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.use_mrope: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams["num_key_value_heads"] + n_embd = self.hparams["hidden_size"] + head_dim = self.hparams.get("head_dim", n_embd // n_head) + # because llama.cpp M-RoPE kernel only supports Neox ordering, we have to permute the weights here + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = Glm4Model.normal_to_neox(data_torch, n_head, n_head, head_dim, self.partial_rotary_factor) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = Glm4Model.normal_to_neox(data_torch, n_head, n_kv_head, head_dim, self.partial_rotary_factor) + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("GlmOcrForConditionalGeneration") +class GlmOCRModel(Glm4Model): + model_arch = gguf.MODEL_ARCH.GLM4 + use_mrope = False + partial_rotary_factor = 0.5 + + # Note: GLM-OCR is the same as GLM4, but with an extra NextN/MTP prediction layer + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # GLM-OCR has num_hidden_layers + 1 actual layers (including NextN layer) + self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + # NextN/MTP prediction layers + if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: + self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) + + +@ModelBase.register("Glm4MoeForCausalLM", "Glm4vMoeForConditionalGeneration") +class Glm4MoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.GLM4_MOE + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # GLM4_MOE has num_hidden_layers + 1 actual layers (including NextN layer) + self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def set_vocab(self): + return self._set_vocab_glm() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (rope_dim := self.hparams.get("head_dim")) is None: + rope_dim = ( + self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + ) + self.gguf_writer.add_rope_dimension_count( + int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5)) + ) + + # MoE parameters - Use only routed expert count (shared experts handled separately) + if (n_routed_experts := self.hparams.get("n_routed_experts")) is not None: + self.gguf_writer.add_expert_count(n_routed_experts) + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + if (n_shared_experts := self.hparams.get("n_shared_experts")) is not None: + self.gguf_writer.add_expert_shared_count(n_shared_experts) + if (first_k_dense_replace := self.hparams.get("first_k_dense_replace")) is not None: + self.gguf_writer.add_leading_dense_block_count(first_k_dense_replace) + + # Expert gating function (sigmoid for GLM4_MOE) + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) + + # Routed scaling factor + if (routed_scaling_factor := self.hparams.get("routed_scaling_factor")) is not None: + self.gguf_writer.add_expert_weights_scale(routed_scaling_factor) + + # Normalise topk probabilities + if (norm_topk_prob := self.hparams.get("norm_topk_prob")) is not None: + self.gguf_writer.add_expert_weights_norm(norm_topk_prob) + + # NextN/MTP prediction layers + if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: + self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) + + _experts: list[dict[str, Tensor]] | None = None + + # note: unlike GLM4V non-MoE, we don't need to permute Q/K here since GLM4V_MOE uses Neox ordering already + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Handle main token embedding (but not layer-specific NextN embeddings) + if name == "model.embed_tokens.weight" and ".layers." not in name: + yield from super().modify_tensors(data_torch, "token_embd.weight", bid) + return + + # Handle routed experts + if name.find("mlp.experts") != -1: + n_experts = self.hparams["n_routed_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("Glm4MoeLiteForCausalLM") +class Glm4MoeLiteModel(DeepseekV2Model): + model_arch = gguf.MODEL_ARCH.DEEPSEEK2 + + def set_vocab(self): + return self._set_vocab_glm() + + +@ModelBase.register("GlmMoeDsaForCausalLM") +class GlmMoeDsaModel(DeepseekV2Model): + model_arch = gguf.MODEL_ARCH.GLM_DSA + skip_mtp = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def set_vocab(self): + return self._set_vocab_glm() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + rope_dim = self.hparams["qk_rope_head_dim"] + partial_rotary_factor = self.hparams.get("partial_rotary_factor", 1.0) + self.gguf_writer.add_rope_dimension_count(int(rope_dim * partial_rotary_factor)) + + # NextN/MTP prediction layers + if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: + self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) + + # DSA indexer parameters + self.gguf_writer.add_indexer_head_count(self.hparams["index_n_heads"]) + self.gguf_writer.add_indexer_key_length(self.hparams["index_head_dim"]) + self.gguf_writer.add_indexer_top_k(self.hparams["index_topk"]) + + +@ModelBase.register("SolarOpenForCausalLM") +class SolarOpenModel(Glm4MoeModel): + model_arch = gguf.MODEL_ARCH.GLM4_MOE + + def set_vocab(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("unk", tokenizer.get_added_vocab()[""]) # ty: ignore[unresolved-attribute] + special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|startoftext|>"]) # ty: ignore[unresolved-attribute] + special_vocab.add_to_gguf(self.gguf_writer) diff --git a/conversion/gpt2.py b/conversion/gpt2.py new file mode 100644 index 00000000000..1cf06ae8b50 --- /dev/null +++ b/conversion/gpt2.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GPT2LMHeadModel") +class GPT2Model(TextModel): + model_arch = gguf.MODEL_ARCH.GPT2 + + def set_gguf_parameters(self): + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.hparams["n_ctx"]) + self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) + self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) + self.gguf_writer.add_head_count(self.hparams["n_head"]) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # we don't need these + if name.endswith((".attn.bias", ".attn.masked_bias")): + yield from super().modify_tensors(data_torch, name, bid) + return + + if name.endswith((".c_attn.weight", ".c_proj.weight", ".c_fc.weight", ".c_proj.weight")): + data_torch = data_torch.transpose(1, 0) + + new_name = self.map_tensor_name(name) + + yield from super().modify_tensors(data_torch, new_name, bid) + + +@ModelBase.register("RuGPT3XLForCausalLM") +class RuGPT3XLModel(TextModel): + model_arch = gguf.MODEL_ARCH.GPT2 + + _qkv_parts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Fuse separate Q, K, V projections into a single QKV tensor + if ".self_attn.q_proj." in name or ".self_attn.k_proj." in name or ".self_attn.v_proj." in name: + suffix = "weight" if name.endswith(".weight") else "bias" + part = "q" if ".q_proj." in name else ("k" if ".k_proj." in name else "v") + key = f"{part}.{suffix}" + + assert bid is not None + if self._qkv_parts is None: + self._qkv_parts = [{} for _ in range(self.block_count)] + self._qkv_parts[bid][key] = data_torch + + q_key, k_key, v_key = f"q.{suffix}", f"k.{suffix}", f"v.{suffix}" + if all(k in self._qkv_parts[bid] for k in [q_key, k_key, v_key]): + q = self._qkv_parts[bid].pop(q_key) + k = self._qkv_parts[bid].pop(k_key) + v = self._qkv_parts[bid].pop(v_key) + data_torch = torch.cat([q, k, v], dim=0) + name = self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_QKV, bid, f".{suffix}") + logger.debug(f"Fused Q/K/V {suffix} for layer {bid} -> {name}") + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._qkv_parts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + parts = [f"({i}){k}" for i, d in enumerate(self._qkv_parts) for k in d.keys()] + if len(parts) > 0: + raise ValueError(f"Unprocessed Q/K/V parts: {parts}") diff --git a/conversion/gpt_oss.py b/conversion/gpt_oss.py new file mode 100644 index 00000000000..d2c70c0bba5 --- /dev/null +++ b/conversion/gpt_oss.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GptOssForCausalLM") +class GptOssModel(TextModel): + model_arch = gguf.MODEL_ARCH.GPT_OSS + + # TODO: remove once MXFP4 is supported more generally + def dequant_model(self): + if self._is_mxfp4: + return + return super().dequant_model() + + def transform_nibble_layout(self, tensor): + assert tensor.dtype == torch.uint8 + assert tensor.shape[-1] == 16 + # swap nibbles + t_lo = tensor & 0x0F + t_hi = tensor & 0xF0 + t_swapped = (t_lo << 4) | (t_hi >> 4) + tensor = t_swapped + # transform aaaa...bbbb... to abababab... + blk_a, blk_b = tensor.chunk(2, dim=-1) + # get a_ + blk_a0 = (blk_a & 0xF0).view(-1, 1) + blk_a1 = (blk_a << 4).view(-1, 1) + blk_a = torch.stack((blk_a0, blk_a1), dim=2).view(tensor.shape) + # get _b + blk_b0 = (blk_b >> 4).view(-1, 1) + blk_b1 = (blk_b & 0x0F).view(-1, 1) + blk_b = torch.stack((blk_b0, blk_b1), dim=2).view(tensor.shape) + # swap once more + out = blk_a | blk_b + out_h = out & 0xF0 + out_l = out & 0x0F + out = (out_h >> 4) | (out_l << 4) + return out + + def repack_mxfp4(self, new_name: str, blocks: Tensor, scales: Tensor): + assert blocks.dtype == torch.uint8 + assert scales.dtype == torch.uint8 + scales = scales.unsqueeze(-1) + assert len(blocks.shape) == 4 + assert len(scales.shape) == 4 + blocks = self.transform_nibble_layout(blocks) + new_data = torch.concat((scales, blocks), dim=-1) + new_shape = [new_data.shape[0], new_data.shape[1], new_data.shape[2] * 32] + logger.info(f"Repacked {new_name} with shape {new_shape} and quantization MXFP4") + # flatten last dim + new_data = new_data.view(new_data.shape[0], new_data.shape[1], new_data.shape[2] * new_data.shape[3]) + new_data = new_data.numpy() + self.gguf_writer.add_tensor(new_name, new_data, raw_dtype=gguf.GGMLQuantizationType.MXFP4) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + blocks0: Tensor = torch.zeros(1) + blocks1: Tensor = torch.zeros(1) + # we assume that tensors are loaded in the correct order + for name, data_torch in self.get_tensors(): + if "mlp.experts.down_proj_blocks" in name: + blocks0 = data_torch + elif "mlp.experts.down_proj_scales" in name: + new_name = self.map_tensor_name(name.replace("_scales", ".weight")) + self.repack_mxfp4(new_name, blocks0, data_torch) + elif "mlp.experts.gate_up_proj_blocks" in name: + blocks0, blocks1 = data_torch[:, ::2, :, :], data_torch[:, 1::2, :, :] + elif "mlp.experts.gate_up_proj_scales" in name: + scales0, scales1 = data_torch[:, ::2, :], data_torch[:, 1::2, :] + new_name_gate = self.map_tensor_name(name.replace("gate_up_proj_scales", "gate_proj.weight")) + new_name_up = self.map_tensor_name(name.replace("gate_up_proj_scales", "up_proj.weight")) + self.repack_mxfp4(new_name_gate, blocks0, scales0) + self.repack_mxfp4(new_name_up, blocks1, scales1) + return [] + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "sinks" in name: + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # correct naming for down_proj + if "down_proj" in name: + if name.endswith("_bias"): + name = name.replace("down_proj_bias", "down_proj.bias") + elif "_blocks" not in name and "_scales" not in name: + logger.warning(f"{name} is not in MXFP4, performance may be degraded") + name = name.replace("down_proj", "down_proj.weight") + data_torch = data_torch.transpose(-1, -2) + else: + # otherwise, it should already be repacked to ggml MXFP4 format + return + + # split the gate_up into gate and up + if "gate_up_proj" in name: + if name.endswith("_bias"): + name_up = name.replace("gate_up_proj_bias", "up_proj.bias") + name_gate = name.replace("gate_up_proj_bias", "gate_proj.bias") + gate_proj_bias, up_proj_bias = data_torch[..., ::2], data_torch[..., 1::2] + yield from super().modify_tensors(gate_proj_bias, name_gate, bid) + yield from super().modify_tensors(up_proj_bias, name_up, bid) + elif "_blocks" not in name and "_scales" not in name: + logger.warning(f"{name} is not in MXFP4, performance may be degraded") + name_up = name.replace("gate_up_proj", "up_proj.weight") + name_gate = name.replace("gate_up_proj", "gate_proj.weight") + data_torch = data_torch.transpose(-1, -2) + gate_proj_weight, up_proj_weight = data_torch[:, ::2, :], data_torch[:, 1::2, :] + yield from super().modify_tensors(gate_proj_weight, name_gate, bid) + yield from super().modify_tensors(up_proj_weight, name_up, bid) + else: + yield from super().modify_tensors(data_torch, name, bid) + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["intermediate_size"]) diff --git a/conversion/gptneox.py b/conversion/gptneox.py new file mode 100644 index 00000000000..6a42b12b15a --- /dev/null +++ b/conversion/gptneox.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import re + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GPTNeoXForCausalLM") +class GPTNeoXModel(TextModel): + model_arch = gguf.MODEL_ARCH.GPTNEOX + + def set_gguf_parameters(self): + self.gguf_writer.add_context_length(self.hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) + self.gguf_writer.add_rope_dimension_count( + int(self.hparams["rotary_pct"] * (self.hparams["hidden_size"] // self.hparams["num_attention_heads"])), + ) + self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) + self.gguf_writer.add_parallel_residual(self.hparams.get("use_parallel_residual", True)) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_eps"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) + n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) + assert n_head is not None + assert n_embed is not None + + if re.match(r"gpt_neox\.layers\.\d+\.attention\.query_key_value\.weight", name): + # Map bloom-style qkv_linear to gpt-style qkv_linear + # bloom: https://github.com/huggingface/transformers/blob/main/src/transformers/models/bloom/modeling_bloom.py#L238-L252 # noqa + # gpt-2: https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py#L312 # noqa + qkv_weights = data_torch.reshape((n_head, 3, n_embed // n_head, n_embed)) + data_torch = torch.cat( + ( + qkv_weights[:, 0, :, :].reshape((-1, n_embed)), + qkv_weights[:, 1, :, :].reshape((-1, n_embed)), + qkv_weights[:, 2, :, :].reshape((-1, n_embed)), + ), + dim=0, + ) + logger.info("re-format attention.linear_qkv.weight") + elif re.match(r"gpt_neox\.layers\.\d+\.attention\.query_key_value\.bias", name): + qkv_bias = data_torch.reshape((n_head, 3, n_embed // n_head)) + data_torch = torch.cat( + ( + qkv_bias[:, 0, :].reshape((n_embed,)), + qkv_bias[:, 1, :].reshape((n_embed,)), + qkv_bias[:, 2, :].reshape((n_embed,)), + ), + dim=0, + ) + logger.info("re-format attention.linear_qkv.bias") + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/granite.py b/conversion/granite.py new file mode 100644 index 00000000000..647269ba740 --- /dev/null +++ b/conversion/granite.py @@ -0,0 +1,328 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf, logger + +from .llama import LlamaModel +from .mamba import Mamba2Model + + +@ModelBase.register("GraniteForCausalLM", "GraniteSpeechForConditionalGeneration") +class GraniteModel(LlamaModel): + """Conversion for IBM's GraniteForCausalLM""" + model_arch = gguf.MODEL_ARCH.GRANITE + + def set_gguf_parameters(self): + """Granite uses standard llama parameters with the following differences: + + - No head_dim support + - New multiplier params: + - attention_scale + - embedding_scale + - residual_scale + - logits_scaling + """ + if head_dim := self.hparams.pop("head_dim", None): + logger.warning("Ignoring head_dim (%s) from config for Granite", head_dim) + super().set_gguf_parameters() + # NOTE: Convert _multiplier params to _scale params for naming + # consistency + if attention_scale := self.hparams.get("attention_multiplier"): + self.gguf_writer.add_attention_scale(attention_scale) + logger.info("gguf: (granite) attention_scale = %s", attention_scale) + if embedding_scale := self.hparams.get("embedding_multiplier"): + self.gguf_writer.add_embedding_scale(embedding_scale) + logger.info("gguf: (granite) embedding_scale = %s", embedding_scale) + if residual_scale := self.hparams.get("residual_multiplier"): + self.gguf_writer.add_residual_scale(residual_scale) + logger.info("gguf: (granite) residual_scale = %s", residual_scale) + if logits_scale := self.hparams.get("logits_scaling"): + self.gguf_writer.add_logit_scale(logits_scale) + logger.info("gguf: (granite) logits_scale = %s", logits_scale) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + if name.startswith("encoder."): + return None + return super().filter_tensors(item) + + +@ModelBase.register("GraniteMoeForCausalLM", "GraniteMoeSharedForCausalLM") +class GraniteMoeModel(GraniteModel): + """Conversion for IBM's GraniteMoeForCausalLM""" + model_arch = gguf.MODEL_ARCH.GRANITE_MOE + + def set_gguf_parameters(self): + """GraniteMoeShared uses GraniteMoe parameters plus the following: + - shared_intermediate_size + """ + super().set_gguf_parameters() + if shared_feed_forward_length := self.hparams.get("shared_intermediate_size"): + self.gguf_writer.add_expert_shared_feed_forward_length(shared_feed_forward_length) + logger.info("gguf: (granitemoeshared) shared_feed_forward_length = %s", shared_feed_forward_length) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + """In modeling_granitemoe, the JetMoe implementation of parallel experts + is used. This essentially merges w1 and w3 into a single tensor with 2x + the hidden size that is then split during forward. To keep compatibility + with existing mixtral support, we pull them apart here. + """ + + if name.endswith("block_sparse_moe.input_linear.weight"): + ffn_dim = self.hparams["intermediate_size"] + assert data_torch.shape[-2] == 2 * ffn_dim, "Merged FFN tensor size must be 2 * intermediate_size" + gate, up = data_torch.split(ffn_dim, dim=-2) + yield from ModelBase.modify_tensors(self, gate, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_EXP, bid), bid) + yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_EXP, bid), bid) + return + + has_experts = bool(self.hparams.get('num_local_experts')) + + if name.endswith("shared_mlp.input_linear.weight"): + ffn_dim = self.hparams["shared_intermediate_size"] + assert data_torch.shape[-2] == 2 * ffn_dim, "Merged FFN tensor size must be 2 * shared_intermediate_size" + gate, up = data_torch.split(ffn_dim, dim=-2) + if has_experts: + yield from ModelBase.modify_tensors(self, gate,self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_SHEXP, bid), bid) + yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_SHEXP, bid), bid) + return + yield from ModelBase.modify_tensors(self, gate, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), bid) + yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), bid) + return + + if not has_experts and name.endswith("shared_mlp.output_linear.weight"): + yield from ModelBase.modify_tensors(self, data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN, bid), bid) + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("GraniteMoeHybridForCausalLM", "BambaForCausalLM") +class GraniteHybridModel(Mamba2Model, GraniteMoeModel): + """GraniteHybrid is a hybrid SSM + Attention model that uses Mamba2 SSM + layers and optionally uses MoE w/ a shared expert""" + model_arch = gguf.MODEL_ARCH.GRANITE_HYBRID + undo_permute = True + + def __init__(self, *args, **kwargs): + + # Hybrid mamba models use a prefix for the mamba-specific params. + # TODO: Extend this if the prefix(es) need to be configurable + self.hparam_prefixes = ["mamba"] + + super().__init__(*args, **kwargs) + + # Lists of which layers use ssm vs attention + self._attn_layers = self.get_attn_layers() + self._ssm_layers = [ + i for i in range(self.block_count) + if i not in self._attn_layers + ] + + # There are some models in this family that are non-hybrid, but keep the + # same parent class by setting all layers to "attention." If this is the + # case, the model architecture needs to be updated to a standard + # "granite" or "granitemoe" model + if not self._ssm_layers: + has_experts = self.find_hparam(["num_experts_per_tok", "num_experts_per_token"], optional=True) + new_arch = ( + gguf.MODEL_ARCH.GRANITE_MOE + if has_experts else + gguf.MODEL_ARCH.GRANITE + ) + self.model_arch = new_arch + self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[new_arch] + self.gguf_writer.add_architecture() + + # n_group and d_inner are used during reshape_tensors for mamba2 + # NOTE: Explicitly include hparam prefix prefix for d_model to + # disambiguate with top-level head_dim + # NOTE 2: If needed for future models, this can be isolated in a method + # to separate the prefix setting and the keys used + self.d_model = self.find_hparam([f"{self.hparam_prefixes[0]}_head_dim", "hidden_size", "d_model"]) + self.n_group = self.find_hparam(["n_groups", "num_groups"]) + self.d_inner = self.find_hparam(["expand", "num_heads"]) * self.d_model + + def get_attn_layers(self): + # Explicit list of layer type names + if layer_types := self.hparams.get("layer_types"): + return [ + i for i, typ in enumerate(layer_types) + if typ == "attention" + ] + + # Layer types indicated by index or period + attn_layers = self.hparams.get("attn_layer_indices", []) + if not attn_layers: + attn_period = self.hparams.get("attn_layer_period") + assert attn_period, "Didn't find attn_layer_indices or attn_layer_period" + attn_offset = self.hparams.get("attn_layer_offset") + assert attn_offset is not None, "No attention layer offset set with attn_layer_period" + attn_layers = [ + i for i in range(self.block_count) + if i % attn_period == attn_offset + ] + return attn_layers + + def find_hparam(self, keys: Iterable[str], *args, **kwargs) -> Any: + prefixed = [] + for pfx in self.hparam_prefixes: + prefixed.extend( + "_".join([pfx, k]) + for k in keys + ) + keys = list(keys) + prefixed + return Mamba2Model.find_hparam(self, keys, *args, **kwargs) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if ( + name.endswith("block_sparse_moe.input_linear.weight") + or "shared_mlp" in name + ): + yield from GraniteMoeModel.modify_tensors(self, data_torch, name, bid) + return + + # Determine whether this is a mamba layer or an attention layer + if bid in self._ssm_layers: + yield from Mamba2Model.modify_tensors(self, data_torch, name, bid) + return + elif bid in self._attn_layers: + yield from GraniteMoeModel.modify_tensors(self, data_torch, name, bid) + return + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + + def set_gguf_parameters(self): + """This method merges params from both parents and some that are + specific to this model. The result is some duplication of how the params + get set. The following warnings are expected during conversion: + + WARNING:Duplicated key name 'granitehybrid.attention.head_count_kv' + WARNING:Duplicated key name 'granitehybrid.context_length' + """ + GraniteMoeModel.set_gguf_parameters(self) + + ## Mamba mixer params ## + self.gguf_writer.add_ssm_conv_kernel(self.find_hparam(["conv_kernel", "d_conv"])) + self.gguf_writer.add_ssm_state_size(self.find_hparam(["state_size", "d_state", "state_dim", "ssm_state_size"])) + self.gguf_writer.add_ssm_group_count(self.n_group) + self.gguf_writer.add_ssm_inner_size(self.d_inner) + # NOTE: The mamba_dt_rank is _not_ the right field for how this is used + # in llama.cpp + self.gguf_writer.add_ssm_time_step_rank(self.find_hparam(["n_heads", "num_heads"])) + + ## Attention params ## + head_count_kv = self.find_hparam(["num_key_value_heads", "n_head_kv"]) + head_count_kv_vec = [ + head_count_kv if i in self._attn_layers else 0 for i in range(self.block_count) + ] + if rope_dim := self.hparams.get("attn_rotary_emb"): + self.gguf_writer.add_rope_dimension_count(rope_dim) + self.gguf_writer.add_head_count_kv(head_count_kv_vec) + + ## If Bamba or non-hybrid, use rope, otherwise don't + use_rope = ( + "BambaForCausalLM" in self.hparams["architectures"] + or not self._ssm_layers + ) + self.gguf_writer.add_rope_scaling_finetuned(use_rope) + if not use_rope: + self.gguf_writer.add_context_length(2**20) + + ## Validation ## + d_head = self.find_hparam(["d_head"], optional=True) or 64 + assert self.hparams.get("hidden_act") in [None, "silu"], "Only SILU activation supported" + assert self.d_inner % d_head == 0, f"SSM inner size {self.d_inner} not a multiple of head dim {d_head}" + + def set_vocab(self): + self.hparams["pad_vocab_size_multiple"] = 8 + Mamba2Model.set_vocab(self) + + +@ModelBase.register("GraniteSpeechForConditionalGeneration") +class GraniteSpeechMmprojModel(MmprojModel): + has_vision_encoder = False + has_audio_encoder = True + + _batch_norm_tensors: list[dict[str, Tensor]] | None = None + + def get_audio_config(self) -> dict[str, Any] | None: + return self.global_config.get("encoder_config") + + def set_gguf_parameters(self): + assert self.hparams_audio is not None + a = self.hparams_audio + a["hidden_size"] = a["hidden_dim"] + a["intermediate_size"] = a["hidden_dim"] * a["feedforward_mult"] + a["num_attention_heads"] = a["num_heads"] + a["num_hidden_layers"] = a["num_layers"] + + super().set_gguf_parameters() + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GRANITE_SPEECH) + self.gguf_writer.add_audio_num_mel_bins(a["input_dim"]) + self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) + self.gguf_writer.add_audio_chunk_size(a["context_size"]) + self.gguf_writer.add_audio_conv_kernel_size(a["conv_kernel_size"]) + self.gguf_writer.add_audio_max_pos_emb(a["max_pos_emb"]) + + p = self.global_config + self.gguf_writer.add_audio_projector_window_size(p["window_size"]) + self.gguf_writer.add_audio_projector_downsample_rate(p["downsample_rate"]) + self.gguf_writer.add_audio_projector_head_count(p["projector_config"]["num_attention_heads"]) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if "encoder" in name or "projector" in name: + if ".conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + if "attention_dists" in name or "num_batches_tracked" in name: + return None + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # fold running_mean, running_var and eps into weight and bias for batch_norm + if "batch_norm" in name and "encoder.layers." in name: + if self._batch_norm_tensors is None: + self._batch_norm_tensors = [{} for _ in range(self.block_count)] + assert bid is not None + self._batch_norm_tensors[bid][name] = data_torch + if len(self._batch_norm_tensors[bid]) < 4: + return + prefix = f"encoder.layers.{bid}.conv.batch_norm" + weight = self._batch_norm_tensors[bid][f"{prefix}.weight"] + bias = self._batch_norm_tensors[bid][f"{prefix}.bias"] + running_mean = self._batch_norm_tensors[bid][f"{prefix}.running_mean"] + running_var = self._batch_norm_tensors[bid][f"{prefix}.running_var"] + eps = 1e-5 + a = weight / torch.sqrt(running_var + eps) + b = bias - running_mean * a + yield from super().modify_tensors(a, f"encoder.layers.{bid}.conv.batch_norm.weight", bid) + yield from super().modify_tensors(b, f"encoder.layers.{bid}.conv.batch_norm.bias", bid) + return + + if ".attn.to_kv.weight" in name: + k_weight, v_weight = data_torch.chunk(2, dim=0) + yield from super().modify_tensors(k_weight, name.replace("to_kv", "to_k"), bid) + yield from super().modify_tensors(v_weight, name.replace("to_kv", "to_v"), bid) + return + + if ("up_conv" in name or "down_conv" in name) and name.endswith(".weight"): + if data_torch.ndim == 3 and data_torch.shape[2] == 1: + data_torch = data_torch.squeeze(2) + + if "depth_conv" in name and name.endswith(".weight"): + if data_torch.ndim == 3 and data_torch.shape[1] == 1: + data_torch = data_torch.squeeze(1) + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/grok.py b/conversion/grok.py new file mode 100644 index 00000000000..9098e514a3a --- /dev/null +++ b/conversion/grok.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +import sys + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GrokForCausalLM", "Grok1ForCausalLM") +class GrokModel(TextModel): + model_arch = gguf.MODEL_ARCH.GROK + + def set_vocab(self): + if (self.dir_model / 'tokenizer.model').is_file(): + self._set_vocab_sentencepiece() + return + + if not (self.dir_model / 'tokenizer.json').is_file() or not (self.dir_model / 'chat_template.jinja').is_file(): + logger.error('Error: Missing vocab and chat template, download files from https://huggingface.co/alvarobartt/grok-2-tokenizer') + sys.exit(1) + + self._set_vocab_gpt2() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_attn_logit_softcapping(self.hparams.get("attn_logit_softcapping", 30.0)) + self.gguf_writer.add_router_logit_softcapping(self.hparams.get("router_logit_softcapping", 30.0)) + if (final_logit_softcap := self.hparams.get("final_logit_softcapping")): + self.gguf_writer.add_final_logit_softcapping(final_logit_softcap) + + if (rope_dim := self.hparams.get("head_dim")) is None: + rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + + # Treat "original" as "yarn", seems to have been a mistake + if self.hparams.get("rope_type") in ("yarn", "original"): + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.YARN) + self.gguf_writer.add_rope_scaling_factor(self.hparams["scaling_factor"]) + self.gguf_writer.add_rope_scaling_orig_ctx_len(self.hparams["original_max_position_embeddings"]) + self.gguf_writer.add_rope_scaling_yarn_ext_factor(self.hparams["extrapolation_factor"]) + self.gguf_writer.add_rope_scaling_yarn_attn_factor(self.hparams["attn_factor"]) + self.gguf_writer.add_rope_scaling_yarn_beta_fast(self.hparams["beta_fast"]) + self.gguf_writer.add_rope_scaling_yarn_beta_slow(self.hparams["beta_slow"]) + + if temp_len := self.hparams.get("attn_temperature_len"): + self.gguf_writer.add_attn_temperature_length(temp_len) + + self.gguf_writer.add_attn_output_scale(self.hparams.get("attn_output_multiplier", rope_dim**-0.5)) + self.gguf_writer.add_embedding_scale(self.hparams["embedding_multiplier_scale"]) + self.gguf_writer.add_logit_scale(self.hparams["output_multiplier_scale"]) + + _experts: list[dict[str, list[Tensor]]] | None = None + _cur_expert = "" + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + deferred: list[tuple[Tensor, str, int | None]] = [] + is_expert = ".moe." in name or ".block_sparse_moe.experts." in name + + if not is_expert: + deferred.append((data_torch, name, bid)) + + # process the experts separately + if is_expert or self._cur_expert: + n_experts = self.hparams["num_local_experts"] + + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + # concatenate split tensors + if name in self._experts[bid]: + self._cur_expert = name + self._experts[bid][name].append(data_torch) + return + elif is_expert: + self._cur_expert = name + self._experts[bid][name] = [data_torch] + return + else: + self._cur_expert = "" + + for bid in range(self.block_count): + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for wid in [("linear", "w1", 0), ("linear_1", "w2", 1), ("linear_v", "w3", 0)]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"transformer.decoder_layer.{bid}.moe.{xid}.{wid[0]}.weight" + if ename not in self._experts[bid]: + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid[1]}.weight" + tensor_list = self._experts[bid][ename] + datas.append(torch.cat(tensor_list, dim=wid[2]) if len(tensor_list) > 1 else tensor_list[0]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"transformer.decoder_layer.{bid}.moe.{wid[0]}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + + for t in deferred: + yield from super().modify_tensors(*t) diff --git a/conversion/grovemoe.py b/conversion/grovemoe.py new file mode 100644 index 00000000000..a8be931cb90 --- /dev/null +++ b/conversion/grovemoe.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("GroveMoeForCausalLM", "modeling_grove_moe.GroveMoeForCausalLM") +class GroveMoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.GROVEMOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") + # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L299 + self.gguf_writer.add_expert_chunk_feed_forward_length(self.hparams.get("head_dim") or 128) + # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L298 + self.gguf_writer.add_experts_per_group(2) + # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L376 + self.gguf_writer.add_expert_group_scale(0.05) + + _experts: list[dict[str, Tensor]] | None = None + _chunk_experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.endswith(".expert_bias"): + # FIXME?: Unused https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L303 + return + + # process the experts separately + if name.find("chunk_experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) // 2 # see add_experts_per_group + assert bid is not None + + if self._chunk_experts is None: + self._chunk_experts = [{} for _ in range(self.block_count)] + + self._chunk_experts[bid][name] = data_torch + + if len(self._chunk_experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.chunk_experts.{xid}.{w_name}.weight" + datas.append(self._chunk_experts[bid][ename]) + del self._chunk_experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.chunk_experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + elif name.find("experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._chunk_experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + chunk_experts = [k for d in self._chunk_experts for k in d.keys()] + if len(chunk_experts) > 0: + raise ValueError(f"Unprocessed adjugate experts: {chunk_experts}") + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/hunyuan.py b/conversion/hunyuan.py new file mode 100644 index 00000000000..be54f5810b0 --- /dev/null +++ b/conversion/hunyuan.py @@ -0,0 +1,407 @@ +from __future__ import annotations + +import json + +from pathlib import Path +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf, logger + +from .qwen import QwenModel + + +@ModelBase.register("HunYuanMoEV1ForCausalLM") +class HunYuanMoEModel(TextModel): + model_arch = gguf.MODEL_ARCH.HUNYUAN_MOE + + def set_vocab(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + + # 1. Get the pre-tokenizer identifier hash + tokpre = self.get_vocab_base_pre(tokenizer) + + # 2. Reverse-engineer the merges list from mergeable_ranks + merges = [] + vocab = {} + mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] + for token, rank in mergeable_ranks.items(): + vocab[QwenModel.token_bytes_to_string(token)] = rank + if len(token) == 1: + continue + merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) + if len(merged) == 2: # todo this is an assert in Qwen, why? + merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) + + # 3. Generate the tokens and toktypes lists + vocab_size = self.hparams["vocab_size"] + assert tokenizer.vocab_size == vocab_size # ty: ignore[unresolved-attribute] + special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] + reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} + tokens: list[str] = [] + toktypes: list[int] = [] + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token = reverse_vocab[i] + tokens.append(token) + if i in special_tokens.values(): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.NORMAL) + + # 4. Write all vocab-related fields to the GGUF writer + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_token_merges(merges) + + # 5. Add special tokens and chat templates + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) + special_vocab.add_to_gguf(self.gguf_writer) + # FIX for BOS token: Overwrite incorrect id read from config.json + self.gguf_writer.add_bos_token_id(127959) # <|bos|> + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + + self.gguf_writer.add_expert_shared_feed_forward_length(hparams["intermediate_size"]) + + moe_intermediate_size = hparams["moe_intermediate_size"] + assert all(n == moe_intermediate_size[0] for n in moe_intermediate_size) + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size[0]) + + moe_topk = hparams["moe_topk"] + assert all(topk == moe_topk[0] for topk in moe_topk) + self.gguf_writer.add_expert_used_count(moe_topk[0]) + + moe_shared_expert = hparams["num_shared_expert"] + assert all(n == moe_shared_expert[0] for n in moe_shared_expert) + self.gguf_writer.add_expert_shared_count(moe_shared_expert[0]) + + # Rope + if self.rope_parameters.get("rope_type") == "dynamic": + # HunYuan uses NTK Aware Alpha based scaling. Original implementation: https://www.reddit.com/r/LocalLLaMA/comments/14lz7j5/ntkaware_scaled_rope_allows_llama_models_to_have/ + # 1000 corresponds to a usable context length of 256k (https://github.com/Tencent-Hunyuan/Hunyuan-A13B/blob/main/report/Hunyuan_A13B_Technical_Report.pdf) + alpha = self.rope_parameters.get("alpha", 1000) + base = self.rope_parameters.get("rope_theta", 10000.0) + dim = (hparams["hidden_size"] // hparams["num_attention_heads"]) # 128 + scaled_base = base * (alpha ** (dim / (dim - 2))) # 10000 * (1000 ** (128 / 126)) = 11158839.9251 + self.gguf_writer.add_rope_freq_base(scaled_base) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + self.gguf_writer.add_rope_scaling_factor(1) + # There is no consistent way to calculate ctx from alpha, and the config is incorrectly set to 32k + self.gguf_writer.add_rope_scaling_orig_ctx_len(256 * 1024) # 256k context length + self.gguf_writer.add_context_length(256 * 1024) # 256k context length + + # if any of our assumptions about the values are wrong, something has changed and this may need to be updated + assert alpha == 1000 and base == 10000.0 and dim == 128 and self.hparams["max_position_embeddings"] in [32 * 1024, 256 * 1024] , \ + "HunYuan dynamic RoPE scaling assumptions changed, please update the logic or context length manually" + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name == "lm_head.weight": + if self.hparams.get("tie_word_embeddings", False): + logger.info("Skipping tied output layer 'lm_head.weight'") + return + + if name.find("mlp.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + if self._experts is not None: + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("HunYuanDenseV1ForCausalLM") +class HunYuanModel(TextModel): + model_arch = gguf.MODEL_ARCH.HUNYUAN_DENSE + + def _get_eod_token_id(self) -> int | None: + """Get the actual end-of-generation token from config (eod_token_id).""" + return self.hparams.get("eod_token_id") + + def _get_eot_token_id(self) -> int | None: + """Get the end-of-turn token from generation_config.json. + This is the first entry in eos_token_id when it's a list.""" + gen_cfg_path = self.dir_model / "generation_config.json" + if gen_cfg_path.is_file(): + with open(gen_cfg_path, encoding="utf-8") as f: + gen_cfg = json.load(f) + eos = gen_cfg.get("eos_token_id") + if isinstance(eos, list) and len(eos) >= 2: + return eos[0] + return None + + def _fix_special_tokens(self): + """Fix EOS/EOT tokens that are incorrect in upstream configs.""" + eod_id = self._get_eod_token_id() + if eod_id is not None: + self.gguf_writer.add_eos_token_id(eod_id) + eot_id = self._get_eot_token_id() + if eot_id is not None: + self.gguf_writer.add_eot_token_id(eot_id) + + def set_vocab(self): + if (self.dir_model / "tokenizer.json").is_file(): + tokens, toktypes, tokpre = self.get_vocab_base() + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + # HunyuanOCR has pad_token_id=-1 in config.json; exclude pad from SpecialVocab + token_types = None + if (self.hparams.get("pad_token_id") or 0) < 0: + token_types = ('bos', 'eos', 'unk', 'sep', 'cls', 'mask') + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True, special_token_types=token_types) + special_vocab.add_to_gguf(self.gguf_writer) + self._fix_special_tokens() + else: + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + + # 1. Get the pre-tokenizer identifier hash + tokpre = self.get_vocab_base_pre(tokenizer) + + # 2. Reverse-engineer the merges list from mergeable_ranks + merges = [] + vocab = {} + mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] + for token, rank in mergeable_ranks.items(): + vocab[QwenModel.token_bytes_to_string(token)] = rank + if len(token) == 1: + continue + merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) + if len(merged) == 2: + merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) + + # 3. Generate the tokens and toktypes lists + vocab_size = self.hparams["vocab_size"] + assert tokenizer.vocab_size == vocab_size # ty: ignore[unresolved-attribute] + special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] + reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} + tokens: list[str] = [] + toktypes: list[int] = [] + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token = reverse_vocab[i] + tokens.append(token) + if i in special_tokens.values(): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.NORMAL) + + # 4. Write all vocab-related fields to the GGUF writer + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_token_merges(merges) + + # 5. Add special tokens and chat templates + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) + special_vocab.add_to_gguf(self.gguf_writer) + # FIX for BOS token: Overwrite incorrect id read from config.json + if self.hparams['hidden_size'] == 4096: + self.gguf_writer.add_bos_token_id(127958) # only for 7b dense, fix <|bos|> token + self._fix_special_tokens() + + def set_gguf_parameters(self): + # HunyuanOCR has num_experts=1 which is not MoE, prevent parent from writing it + saved_num_experts = self.hparams.pop("num_experts", None) + super().set_gguf_parameters() + if saved_num_experts is not None and saved_num_experts > 1: + self.hparams["num_experts"] = saved_num_experts + hparams = self.hparams + + # Rope + if self.rope_parameters.get("rope_type") in ("dynamic", "xdrope"): + # HunYuan uses NTK Aware Alpha based scaling. Original implementation: https://www.reddit.com/r/LocalLLaMA/comments/14lz7j5/ntkaware_scaled_rope_allows_llama_models_to_have/ + # 1000 corresponds to a usable context length of 256k (https://github.com/Tencent-Hunyuan/Hunyuan-A13B/blob/main/report/Hunyuan_A13B_Technical_Report.pdf) + alpha = self.rope_parameters.get("alpha", 50) + base = self.rope_parameters.get("rope_theta", 10000.0) + dim = hparams["head_dim"] + scaled_base = base * (alpha ** (dim / (dim - 2))) + self.gguf_writer.add_rope_freq_base(scaled_base) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + self.gguf_writer.add_rope_scaling_factor(1) + if self.rope_parameters.get("rope_type") == "dynamic": + # There is no consistent way to calculate ctx from alpha, and the config is incorrectly set to 32k + self.gguf_writer.add_rope_scaling_orig_ctx_len(256 * 1024) # 256k context length + self.gguf_writer.add_context_length(256 * 1024) # 256k context length + + # if any of our assumptions about the values are wrong, something has changed and this may need to be updated + assert base == 10000.0 and self.hparams["max_position_embeddings"] in [32 * 1024, 256 * 1024] , \ + "HunYuan dynamic RoPE scaling assumptions changed, please update the logic or context length manually" + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name == "lm_head.weight": + if self.hparams.get("tie_word_embeddings", False): + logger.info("Skipping tied output layer 'lm_head.weight'") + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("HunYuanVLForConditionalGeneration") +class HunyuanVLVisionModel(MmprojModel): + # Handles both HunyuanOCR and HunyuanVL, which share the HF architecture name + # "HunYuanVLForConditionalGeneration" and the `vit.perceive.*` vision layout. + # Each variant maps to a different projector type in clip.cpp so image + # preprocessing follows the correct code path. + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + # HunyuanOCR / HunyuanVL uses max_image_size instead of image_size + if "image_size" not in self.hparams_vision: + self.hparams_vision["image_size"] = self.hparams_vision.get("max_image_size", 2048) + + @staticmethod + def is_ocr_variant(hparams: dict) -> bool: + """Return True for HunyuanOCR, False for HunyuanVL. + + The projector's output dim must equal the text model's hidden_size by + construction (that's what "projector" means). HunyuanOCR pairs a 1B text + backbone (hidden=1024); HunyuanVL pairs a 4B one (hidden=3072). So the + ViT -> LLM projection dim is a hard architectural signature, not a + magic number. + """ + vision_out = int((hparams.get("vision_config") or {}).get("out_hidden_size", 0)) + return vision_out == 1024 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + vcfg = self.hparams_vision + + if self.is_ocr_variant(self.global_config): + # --- HunyuanOCR --- + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.HUNYUANOCR) + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_attention_layernorm_eps(vcfg.get("rms_norm_eps", 1e-5)) + self.gguf_writer.add_vision_spatial_merge_size(vcfg.get("spatial_merge_size", 2)) + self.gguf_writer.add_vision_min_pixels(self.preprocessor_config["min_pixels"]) + self.gguf_writer.add_vision_max_pixels(self.preprocessor_config["max_pixels"]) + return + + # --- HunyuanVL --- + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.HUNYUANVL) + self.gguf_writer.add_vision_use_gelu(str(vcfg["hidden_act"]).lower() == "gelu") + self.gguf_writer.add_vision_attention_layernorm_eps(float(vcfg["rms_norm_eps"])) + self.gguf_writer.add_vision_spatial_merge_size(int(vcfg["spatial_merge_size"])) + self.gguf_writer.add_vision_min_pixels(int(self.preprocessor_config["min_pixels"])) + self.gguf_writer.add_vision_max_pixels(int(self.preprocessor_config["max_pixels"])) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if not name.startswith("vit."): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # strip CLS token (row 0) from position embeddings so resize_position_embeddings works + if "position_embedding" in name: + data_torch = data_torch[1:] # [n_patches+1, n_embd] -> [n_patches, n_embd] + yield from super().modify_tensors(data_torch, name, bid) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + # force conv weights to F32 or F16 to avoid BF16 IM2COL issues on Metal + # Both HunyuanOCR and HunyuanVL emit the ViT -> LLM projection as mm.0/mm.2. + if ("mm.0." in new_name or "mm.2." in new_name) and new_name.endswith(".weight"): + return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + +@ModelBase.register("HunYuanVLForConditionalGeneration") +class HunyuanVLTextModel(HunYuanModel): + # The "HunYuanVLForConditionalGeneration" HF architecture covers both HunyuanOCR + # and HunyuanVL. HunyuanOCR reuses the HunYuan-Dense text backbone (standard RoPE), + # while HunyuanVL introduces a new LLM arch with XD-RoPE. Detect the variant from + # the config and pick the matching GGUF architecture. + model_arch = gguf.MODEL_ARCH.HUNYUAN_VL + + @staticmethod + def _is_ocr_config(hparams: dict) -> bool: + # OCR pairs a 1B text backbone (hidden=1024) with a ViT projector that + # outputs 1024-d; HunyuanVL uses 3072-d. Keep in sync with + # HunyuanVLVisionModel.is_ocr_variant. + return int((hparams.get("vision_config") or {}).get("out_hidden_size", 0)) == 1024 + + def __init__(self, dir_model: Path, *args, **kwargs): + raw_hparams = kwargs.get("hparams") or ModelBase.load_hparams(dir_model, is_mistral_format=False) + if self._is_ocr_config(raw_hparams): + self.model_arch = gguf.MODEL_ARCH.HUNYUAN_DENSE + else: + self.model_arch = gguf.MODEL_ARCH.HUNYUAN_VL + super().__init__(dir_model, *args, **kwargs) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # Only emit XD-RoPE metadata for the HunyuanVL backbone; HunyuanOCR uses + # the HunYuan-Dense arch which already handles standard rope in super(). + if self.model_arch != gguf.MODEL_ARCH.HUNYUAN_VL: + return + + if self.rope_parameters.get("rope_type") != "xdrope": + return + + # defaults for HunyuanVL. The C++ side later computes: + # freq_base = rope_theta * alpha ** (head_dim / (head_dim - 2)) + self.gguf_writer.add_rope_freq_base(float(self.rope_parameters["rope_theta"])) + self.gguf_writer.add_rope_scaling_alpha(float(self.rope_parameters["alpha"])) + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + self.gguf_writer.add_rope_scaling_factor(float(self.rope_parameters.get("factor", 1))) + + ctx_len = int(self.hparams["max_position_embeddings"]) + self.gguf_writer.add_rope_scaling_orig_ctx_len(ctx_len) + self.gguf_writer.add_context_length(ctx_len) + + self.gguf_writer.add_rope_dimension_sections(list(self.rope_parameters["xdrope_section"])) diff --git a/conversion/internlm.py b/conversion/internlm.py new file mode 100644 index 00000000000..7e11aca3ce0 --- /dev/null +++ b/conversion/internlm.py @@ -0,0 +1,232 @@ +from __future__ import annotations + +import json +import sys + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, SentencePieceTokenTypes, TextModel, gguf, logger + +from .llama import LlamaModel + + +@ModelBase.register("InternLM2ForCausalLM") +class InternLM2Model(TextModel): + model_arch = gguf.MODEL_ARCH.INTERNLM2 + + def set_vocab(self): + # (TODO): Is there a better way? + # Copy from _set_vocab_sentencepiece, The only difference is that we will treat the character + # \x00 specially and convert it into an emoji character to prevent it from being mistakenly + # recognized as an empty string in C++. + from sentencepiece import SentencePieceProcessor + from sentencepiece import sentencepiece_model_pb2 as model + + tokenizer_path = self.dir_model / 'tokenizer.model' + + tokens: list[bytes] = [] + scores: list[float] = [] + toktypes: list[int] = [] + + if not tokenizer_path.is_file(): + logger.error(f'Error: Missing {tokenizer_path}') + sys.exit(1) + + sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] + sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) + add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) + + for token_id in range(vocab_size): + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + if text == b"\x00": + # (TODO): fixme + # Hack here and replace the \x00 characters. + logger.warning(f"InternLM2 convert token '{text}' to '🐉'!") + text = "🐉".encode("utf-8") + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + # take care of ununsed raw token + if piece.startswith('[UNUSED'): + toktype = SentencePieceTokenTypes.UNUSED + + tokens.append(text) + scores.append(score) + toktypes.append(toktype) + + added_tokens_file = self.dir_model / 'added_tokens.json' + if added_tokens_file.is_file(): + with open(added_tokens_file, "r", encoding="utf-8") as f: + added_tokens_json = json.load(f) + + for key in added_tokens_json: + tokens.append(key.encode("utf-8")) + scores.append(-1000.0) + toktypes.append(SentencePieceTokenTypes.USER_DEFINED) + + chat_eos_token = '<|im_end|>' + chat_eos_token_id = None + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) + for token_id, foken_data in added_tokens_decoder.items(): + token_id = int(token_id) + token = foken_data["content"] + if token == chat_eos_token: + chat_eos_token_id = token_id + token = token.encode("utf-8") + if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: + if tokens[token_id] != token: + logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') + tokens[token_id] = token + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + if foken_data.get("special"): + toktypes[token_id] = SentencePieceTokenTypes.CONTROL + + tokenizer_file = self.dir_model / 'tokenizer.json' + if tokenizer_file.is_file(): + with open(tokenizer_file, "r", encoding="utf-8") as f: + tokenizer_json = json.load(f) + added_tokens = tokenizer_json.get("added_tokens", []) + for foken_data in added_tokens: + token_id = int(foken_data["id"]) + token = foken_data["content"] + if token == chat_eos_token: + chat_eos_token_id = token_id + token = token.encode("utf-8") + if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: + if tokens[token_id] != token: + logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') + tokens[token_id] = token + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + if foken_data.get("special"): + toktypes[token_id] = SentencePieceTokenTypes.CONTROL + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_add_space_prefix(add_prefix) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + old_eos = special_vocab.special_token_ids["eos"] + if chat_eos_token_id is not None: + # For the chat model, we replace the eos with '<|im_end|>'. + # TODO: this is a hack, should be fixed + # https://github.com/ggml-org/llama.cpp/pull/6745#issuecomment-2067687048 + special_vocab.special_token_ids["eos"] = chat_eos_token_id + logger.warning(f"Replace eos:{old_eos} with a special token:{chat_eos_token_id}" + " in chat mode so that the conversation can end normally.") + + special_vocab.add_to_gguf(self.gguf_writer) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + num_heads = self.hparams["num_attention_heads"] + num_kv_heads = self.hparams["num_key_value_heads"] + n_embd = self.hparams["hidden_size"] + q_per_kv = num_heads // num_kv_heads + head_dim = n_embd // num_heads + num_groups = num_heads // q_per_kv + + if bid is not None and f"model.layers.{bid}.attention.wqkv" in name: + qkv = data_torch + + qkv = qkv.reshape((num_groups, q_per_kv + 2, head_dim, n_embd)) + q, k, v = qkv[:, : q_per_kv], qkv[:, -2], qkv[:, -1] + + # The model weights of q and k equire additional reshape. + q = LlamaModel.permute(q.reshape((-1, q.shape[-1])), num_heads, num_heads) + k = LlamaModel.permute(k.reshape((-1, k.shape[-1])), num_heads, num_kv_heads) + v = v.reshape((-1, v.shape[-1])) + + yield from super().modify_tensors(q, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) + yield from super().modify_tensors(k, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) + yield from super().modify_tensors(v, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) + else: + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("InternLM3ForCausalLM") +class InternLM3Model(TextModel): + model_arch = gguf.MODEL_ARCH.LLAMA + + def set_vocab(self): + tokens, scores, toktypes = self._create_vocab_sentencepiece() + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + if "add_prefix_space" in tokenizer_config_json: + self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) + + if "added_tokens_decoder" in tokenizer_config_json: + for token_id, token_data in tokenizer_config_json["added_tokens_decoder"].items(): + if token_data.get("special"): + token_id = int(token_id) + token = token_data["content"] + special_vocab._set_special_token(token, token_id) + # update eos token + if token == '<|im_end|>' and "eos" in special_vocab.special_token_ids: + special_vocab.special_token_ids["eos"] = token_id + + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(rope_dim) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith(("mlp", "vision_model")): + # skip visual tensors + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/internvl.py b/conversion/internvl.py new file mode 100644 index 00000000000..9a2a1e43df7 --- /dev/null +++ b/conversion/internvl.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + + +@ModelBase.register("InternVisionModel") +class InternVisionModel(MmprojModel): + + min_dynamic_tiles: int = 0 + max_dynamic_tiles: int = 0 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.min_dynamic_tiles = self.global_config.get("min_dynamic_patch", 0) + self.max_dynamic_tiles = self.global_config.get("max_dynamic_patch", 0) + + def set_gguf_parameters(self): + assert self.hparams_vision is not None + if isinstance(self.hparams_vision['image_size'], list): + self.hparams_vision['image_size'] = self.hparams_vision['image_size'][0] + if isinstance(self.hparams_vision['patch_size'], list): + self.hparams_vision['patch_size'] = self.hparams_vision['patch_size'][0] + super().set_gguf_parameters() + + hparams = self.hparams + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.INTERNVL) + self.gguf_writer.add_vision_attention_layernorm_eps(hparams["layer_norm_eps"]) + # hidden_act + if hparams["hidden_act"] == "silu": + self.gguf_writer.add_vision_use_silu(True) + elif hparams["hidden_act"] == "gelu": + self.gguf_writer.add_vision_use_gelu(True) + else: + raise ValueError(f"Unsupported hidden_act: {hparams['hidden_act']}") + # downsample_ratio + downsample_ratio = self.global_config.get("downsample_ratio") + assert downsample_ratio is not None + self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / downsample_ratio)) + # older models may not have min/max_dynamic_patch in config + if self.min_dynamic_tiles > 0: + self.gguf_writer.add_vision_preproc_min_tiles(self.min_dynamic_tiles) + if self.max_dynamic_tiles > 0: + self.gguf_writer.add_vision_preproc_max_tiles(self.max_dynamic_tiles) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".position_embd." in new_name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + vision_prefix = ['vision_model', 'mlp', 'model.vision_tower', 'model.multi_modal_projector'] + if not any([name.startswith(prefix) for prefix in vision_prefix]): + return None + # deal with intern-s1 special case + names_map = { + "model.multi_modal_projector.layer_norm.bias": "mlp1.0.bias", + "model.multi_modal_projector.layer_norm.weight": "mlp1.0.weight", + "model.multi_modal_projector.linear_1.bias": "mlp1.1.bias", + "model.multi_modal_projector.linear_1.weight": "mlp1.1.weight", + "model.multi_modal_projector.linear_2.bias": "mlp1.3.bias", + "model.multi_modal_projector.linear_2.weight": "mlp1.3.weight", + } + if name in names_map: + name = names_map[name] + # correct name + if name.startswith("vision_model"): + name = "vision_tower." + name + if (".ls" in name or ".lambda_" in name or "position_embedding" in name) and not name.endswith(".weight"): + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # split QKV tensors if needed + if ".qkv." in name: + if data_torch.ndim == 2: # weight + c3, _ = data_torch.shape + else: # bias + c3 = data_torch.shape[0] + assert c3 % 3 == 0 + c = c3 // 3 + wq = data_torch[:c] + wk = data_torch[c: c * 2] + wv = data_torch[c * 2:] + yield from super().modify_tensors(wq, name.replace("attn.qkv", "self_attn.q_proj"), bid) + yield from super().modify_tensors(wk, name.replace("attn.qkv", "self_attn.k_proj"), bid) + yield from super().modify_tensors(wv, name.replace("attn.qkv", "self_attn.v_proj"), bid) + else: + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/jais.py b/conversion/jais.py new file mode 100644 index 00000000000..00add4c77fc --- /dev/null +++ b/conversion/jais.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import math + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("Jais2ForCausalLM") +class Jais2Model(TextModel): + model_arch = gguf.MODEL_ARCH.JAIS2 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + head_dim = hparams.get("head_dim", hparams["hidden_size"] // hparams["num_attention_heads"]) + self.gguf_writer.add_rope_dimension_count(head_dim) + + +@ModelBase.register("JAISLMHeadModel") +class JaisModel(TextModel): + model_arch = gguf.MODEL_ARCH.JAIS + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # SwigLU activation + assert self.hparams["activation_function"] == "swiglu" + # ALiBi position embedding + assert self.hparams["position_embedding_type"] == "alibi" + + # Embeddings scale + self.embeddings_scale = 1.0 + if 'mup_embeddings_scale' in self.hparams: + self.embeddings_scale = self.hparams['mup_embeddings_scale'] + elif 'embeddings_scale' in self.hparams: + self.embeddings_scale = self.hparams['embeddings_scale'] + else: + assert False + + self.width_scale = 1.0 + if 'mup_output_alpha' in self.hparams: + assert 'mup_width_scale' in self.hparams + self.width_scale = self.hparams['mup_output_alpha'] * self.hparams['mup_width_scale'] + elif 'width_scale' in self.hparams: + self.width_scale = self.hparams['width_scale'] + else: + assert False + + self.max_alibi_bias = 8.0 + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.hparams["n_positions"]) + self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) + self.gguf_writer.add_feed_forward_length(self.hparams["n_inner"]) + self.gguf_writer.add_head_count(self.hparams["n_head"]) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # we don't need these + if name.endswith((".attn.bias")): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.endswith(("relative_pe.slopes")): + # Calculate max ALiBi bias (this is the inverse of the ALiBi calculation) + # Some other models has max_alibi_bias spelled out explicitly in the hyperparams, + # but Jais's PyTorch model simply precalculates the slope values and places them + # in relative_pes.slopes + n_head_closest_log2 = 2 ** math.floor(math.log2(self.hparams["n_head"])) + first_val = float(data_torch[0].item()) + self.max_alibi_bias = -round(math.log2(first_val) * n_head_closest_log2) + + return + + if name.endswith((".c_attn.weight", ".c_proj.weight", ".c_fc.weight", ".c_fc2.weight")): + data_torch = data_torch.transpose(1, 0) + + new_name = self.map_tensor_name(name) + + if new_name == self.format_tensor_name(gguf.MODEL_TENSOR.TOKEN_EMBD): + yield from super().modify_tensors(data_torch * self.embeddings_scale, new_name, bid) + elif new_name == self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT): + yield from super().modify_tensors(data_torch * self.width_scale, new_name, bid) + else: + yield from super().modify_tensors(data_torch, new_name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + self.gguf_writer.add_max_alibi_bias(self.max_alibi_bias) diff --git a/conversion/jamba.py b/conversion/jamba.py new file mode 100644 index 00000000000..da712ba5014 --- /dev/null +++ b/conversion/jamba.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("JambaForCausalLM") +class JambaModel(TextModel): + model_arch = gguf.MODEL_ARCH.JAMBA + + def set_vocab(self): + if (self.dir_model / "tokenizer.model").is_file(): + self._set_vocab_sentencepiece() + else: + self._set_vocab_llama_hf() + self.gguf_writer.add_add_space_prefix(False) + + def set_gguf_parameters(self): + d_model = self.find_hparam(["hidden_size", "mamba_d_model"]) + d_conv = self.find_hparam(["mamba_d_conv"], optional=True) or 4 + d_inner = self.hparams["mamba_expand"] * d_model + d_state = self.find_hparam(["mamba_d_state"], optional=True) or 16 + # ceiling division + # ref: https://stackoverflow.com/a/17511341/22827863 + # ref: https://github.com/state-spaces/mamba/blob/ce59daea3a090d011d6476c6e5b97f6d58ddad8b/mamba_ssm/modules/mamba_simple.py#L58 + dt_rank = self.find_hparam(["mamba_dt_rank"], optional=True) or -(d_model // -16) + rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-6 + n_kv_head = self.hparams["num_key_value_heads"] + attn_offset = self.hparams["attn_layer_offset"] + attn_period = self.hparams["attn_layer_period"] + n_kv_vec = [0 for _ in range(attn_offset)] + [ + n_kv_head if (i - attn_offset) % attn_period == 0 else 0 for i in range(attn_offset, self.block_count) + ] + + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.find_hparam(["max_position_embeddings", "n_ctx"])) + self.gguf_writer.add_embedding_length(d_model) + self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) + self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(n_kv_vec) + self.gguf_writer.add_ssm_conv_kernel(d_conv) + self.gguf_writer.add_ssm_inner_size(d_inner) + self.gguf_writer.add_ssm_state_size(d_state) + self.gguf_writer.add_ssm_time_step_rank(dt_rank) + self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) + self.gguf_writer.add_expert_count(self.find_hparam(["num_local_experts", "num_experts"])) + self.gguf_writer.add_expert_used_count(self.find_hparam(["num_experts_per_tok", "num_experts_per_token"])) + self.gguf_writer.add_file_type(self.ftype) + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + + # Mini-Jamba + name = name.replace(".moe.", ".feed_forward.") + if bid is not None: + moe_offset = self.hparams["expert_layer_offset"] + moe_period = self.hparams["expert_layer_period"] + + if not (bid >= moe_offset and (bid - moe_offset) % moe_period == 0): + name = name.replace(".experts.0.", ".") + + # process the experts separately + if ".feed_forward.experts." in name: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + + # merge the experts into a single 3d tensor + for wid in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.feed_forward.experts.{xid}.{wid}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + # using the same merged name as qwen2moe + merged_name = f"model.layers.{bid}.mlp.experts.{wid}.weight" + + new_name = self.map_tensor_name(merged_name) + + yield new_name, data_torch + return + + new_name = self.map_tensor_name(name) + + if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): + data_torch = data_torch.squeeze() + + if name.endswith(".A_log"): + logger.debug("A_log --> A ==> " + new_name) + data_torch = -torch.exp(data_torch) + + yield (new_name, data_torch) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/januspro.py b/conversion/januspro.py new file mode 100644 index 00000000000..b49691205cc --- /dev/null +++ b/conversion/januspro.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + +from .llama import LlamaModel + + +@ModelBase.register("JanusForConditionalGeneration") +class JanusProModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.LLAMA # reuse Llama arch + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip vision, aligner, and generation tensors + skip_prefixes = ( + 'model.vision_model.', + 'model.aligner.', + 'model.vqmodel.', + 'model.generation_embeddings.', + 'model.generation_aligner.', + 'model.generation_head.', + ) + if name.startswith(skip_prefixes): + return None + + return super().filter_tensors(item) + + +@ModelBase.register("JanusForConditionalGeneration") +class JanusProVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + if "intermediate_size" not in self.hparams_vision: + mlp_ratio = self.hparams_vision.get("mlp_ratio") + hidden_size = self.hparams_vision.get("hidden_size") + if mlp_ratio is not None and hidden_size is not None: + self.hparams_vision["intermediate_size"] = int(round(hidden_size * mlp_ratio)) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.JANUS_PRO) + + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6)) + + hidden_act = str(self.hparams_vision.get("hidden_act", "")).lower() + if hidden_act == "gelu": + self.gguf_writer.add_vision_use_gelu(True) + elif hidden_act == "silu": + self.gguf_writer.add_vision_use_silu(True) + + def _map_aligner_tensor(self, data_torch: Tensor, name: str) -> Iterable[tuple[str, Tensor]]: + """Map aligner tensors to projector format""" + suffix = ".bias" if name.endswith(".bias") else ".weight" + + if name.startswith("model.aligner."): + local_name = name[len("model.aligner."):] + elif name.startswith("aligner."): + local_name = name[len("aligner."):] + else: + raise ValueError(f"Unsupported Janus aligner prefix: {name}") + + if local_name.startswith("fc1."): + mm_index = 0 + elif local_name.startswith("hidden_layers."): + parts = local_name.split(".", 2) + if len(parts) < 3: + raise ValueError(f"Unexpected Janus aligner tensor name: {name}") + mm_index = int(parts[1]) + 1 + else: + raise ValueError(f"Unsupported Janus aligner tensor: {name}") + + tensor_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, mm_index, suffix=suffix) + return [(tensor_name, data_torch)] + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip generation-related components + skip_generation_prefixes = ( + 'model.vqmodel.', + 'vqmodel.', + 'model.generation_embeddings.', + 'generation_embeddings.', + 'model.generation_aligner.', + 'generation_aligner.', + 'model.generation_head.', + 'generation_head.', + ) + if name.startswith(skip_generation_prefixes): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Handle aligner tensors + if name.startswith(('model.aligner.', 'aligner.')): + yield from self._map_aligner_tensor(data_torch, name) + return + + # Handle vision tensors + if name.startswith(('model.vision_model.', 'vision_model.')): + yield from super().modify_tensors(data_torch, name, bid) + return + + return diff --git a/conversion/kimi_linear.py b/conversion/kimi_linear.py new file mode 100644 index 00000000000..f2e6cda83c1 --- /dev/null +++ b/conversion/kimi_linear.py @@ -0,0 +1,223 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + +from .qwen import QwenModel + + +@ModelBase.register("KimiLinearModel", "KimiLinearForCausalLM") +class KimiLinearModel(TextModel): + """Kimi-Linear model with hybrid MLA+KDA architecture""" + model_arch = gguf.MODEL_ARCH.KIMI_LINEAR + + _experts: list[dict[str, Tensor]] | None = None + + def set_vocab(self): + try: + self._set_vocab_gpt2() + return + except Exception: + pass + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + tokpre = self.get_vocab_base_pre(tokenizer) + + if tokpre == "kimi-k2": + # Build merges list using the approach similar to HunYuanMoE + merges = [] + vocab = {} + mergeable_ranks = tokenizer.model._mergeable_ranks # ty: ignore[unresolved-attribute] + for token, rank in mergeable_ranks.items(): + vocab[QwenModel.token_bytes_to_string(token)] = rank + if len(token) == 1: + continue + merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) + if len(merged) == 2: + merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) + # Build token list + vocab_size = self.hparams["vocab_size"] + special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] + reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} + tokens: list[str] = [] + toktypes: list[int] = [] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token = reverse_vocab[i] + tokens.append(token) + if i in special_tokens.values(): + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.NORMAL) + + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_token_merges(merges) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) + special_vocab.add_to_gguf(self.gguf_writer) + # override eos id in config.json with tiktoken eos id + self.gguf_writer.add_eos_token_id(tokenizer.eos_id) # ty: ignore[unresolved-attribute] + else: + raise NotImplementedError(f"Deepseek pre-tokenizer {tokpre!r} is not supported yet!") + + def set_gguf_parameters(self): + # note: To enable MLA KV cache, attention needs to be converted into MQA (ie: GQA with 1 group) + self.hparams["num_key_value_heads"] = 1 + + super().set_gguf_parameters() + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + + # KDA & MLA params + # Get ssm_d_conv from linear_attn_config.short_conv_kernel_size or ssm_d_conv + linear_attn_config = self.hparams["linear_attn_config"] + # n_head == 0 for KDA layers, n_head > 0 for MLA layers + # full_attention_layers list will be used to distinguish layer type + _num_kv_heads = list() + _full_attn_layers = linear_attn_config["full_attn_layers"] + for il in range(self.hparams["num_hidden_layers"]): + if il + 1 in _full_attn_layers: + _num_kv_heads.append(self.hparams["num_key_value_heads"]) + else: + _num_kv_heads.append(0) + assert len(_num_kv_heads) == self.hparams["num_hidden_layers"] + self.gguf_writer.add_head_count_kv(_num_kv_heads) + + if (ssm_d_conv := linear_attn_config.get("short_conv_kernel_size")) is not None: + self.gguf_writer.add_ssm_conv_kernel(ssm_d_conv) + if (kda_head_dim := linear_attn_config.get("head_dim")) is not None: + self.gguf_writer.add_kda_head_dim(kda_head_dim) + + # MLA params - use add_* methods that handle arch substitution + # Support both HuggingFace naming (q_lora_rank, kv_lora_rank) and internal naming (n_lora_q, n_lora_kv) + if (q_lora_rank := self.find_hparam(["q_lora_rank", "n_lora_q"], optional=True)) is not None: + self.gguf_writer.add_q_lora_rank(q_lora_rank) + # To enable MLA KV cache, MLA needs to be converted into MQA with larger heads, then decompresses to MHA + kv_lora_rank = self.find_hparam(["kv_lora_rank", "n_lora_kv"], optional=False) + self.gguf_writer.add_kv_lora_rank(kv_lora_rank) + + # MLA head dimensions + # Support HuggingFace naming: qk_nope_head_dim, qk_rope_head_dim, v_head_dim + qk_nope_head_dim = self.hparams.get("qk_nope_head_dim") + # Rotation - use qk_rope_head_dim for Kimi + qk_rope_head_dim = self.find_hparam(["qk_rope_head_dim", "n_rot"], optional=False) + self.gguf_writer.add_rope_dimension_count(qk_rope_head_dim) + self.gguf_writer.add_key_length(kv_lora_rank + qk_rope_head_dim) + v_head_dim = self.hparams.get("v_head_dim") + + # Calculate n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim + if (n_embd_head_k_mla := self.find_hparam(["n_embd_head_k_mla"], optional=True)) is not None: + self.gguf_writer.add_key_length_mla(n_embd_head_k_mla) + elif qk_nope_head_dim is not None: + n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim + self.gguf_writer.add_key_length_mla(n_embd_head_k_mla) + + # n_embd_head_v_mla = v_head_dim + if (n_embd_head_v_mla := self.hparams.get("n_embd_head_v_mla")) is not None: + self.gguf_writer.add_value_length_mla(n_embd_head_v_mla) + elif v_head_dim is not None: + self.gguf_writer.add_value_length_mla(v_head_dim) + + # moe_intermediate_size (1024 for Kimi) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) + # num_shared_experts (1 for Kimi) + self.gguf_writer.add_expert_shared_count(self.hparams["num_shared_experts"]) + # first_k_dense_replace (1 for Kimi - first layer uses dense MLP) + self.gguf_writer.add_leading_dense_block_count(self.hparams["first_k_dense_replace"]) + # Routed scaling factor (expert_weights_scale = 2.446 for Kimi) + self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) + + def prepare_tensors(self): + super().prepare_tensors() + if self._experts is not None: + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + logger.info(f"Processing {name}: shape before = {tuple(data_torch.shape)}") + + # Handle KDA conv1d weights + # HuggingFace/vLLM stores as [d_inner, d_conv] (2D), memory layout: conv_step changes fastest + # llama.cpp expects ggml ne = [d_conv, 1, d_inner, 1], memory layout: ne[0]=d_conv changes fastest + # GGUF reverses numpy shape when writing, so numpy (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1] + # Memory layouts match: both have conv_step (d_conv) changing fastest + if name.endswith((".q_conv1d.weight", ".k_conv1d.weight", ".v_conv1d.weight")): + # HF shape: [d_inner, d_conv] e.g. [4096, 4] + # Target numpy shape: (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1] + if data_torch.ndim == 2: + d_inner, d_conv = data_torch.shape + # Reshape to (1, d_inner, 1, d_conv) - memory layout preserved (d_conv fastest) + data_torch = data_torch.reshape(1, d_inner, 1, d_conv) + logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]") + elif data_torch.ndim == 3: + # Already 3D [d_inner, 1, d_conv] from unsqueeze + d_inner, _, d_conv = data_torch.shape + data_torch = data_torch.reshape(1, d_inner, 1, d_conv) + logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, 1, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]") + + # Handle A_log: iHF stores as [1, 1, num_heads, 1] + # llama.cpp expects ggml ne = [1, num_heads, 1, 1] + # GGUF reverses numpy shape: numpy (1, 1, num_heads, 1) -> ggml ne = [1, num_heads, 1, 1] + if name.endswith(".A_log"): + data_torch = -torch.exp(data_torch) + if name.endswith(".dt_bias"): + name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" + logger.info("Changed dt_bias to dt_proj.bias") + + # process the experts separately + if name.find("block_sparse_moe.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + # w1: gate, w2: down, w3: up + for wid, tname in [("w1", gguf.MODEL_TENSOR.FFN_GATE_EXP), + ("w2", gguf.MODEL_TENSOR.FFN_DOWN_EXP), + ("w3", gguf.MODEL_TENSOR.FFN_UP_EXP)]: + datas: list[Tensor] = [] + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + data_torch = torch.stack(datas, dim=0) + new_name = self.format_tensor_name(tname, bid) + yield from super().modify_tensors(data_torch, new_name, bid) + return + + # note: MLA with the absorption optimization, needs these two split and k_b_proj transposed + if name.endswith("kv_b_proj.weight"): + name_kb = name.replace("kv_b_proj", "k_b_proj") + name_vb = name.replace("kv_b_proj", "v_b_proj") + n_head_kv = self.hparams["num_key_value_heads"] + v_head_dim = self.find_hparam(["n_embd_head_v_mla", "v_head_dim"], optional=False) + qk_nope_head_dim = self.hparams["qk_nope_head_dim"] + logger.info("Split kv_b n_head_kv %d\n" % n_head_kv) + assert data_torch.shape[0] == n_head_kv * (v_head_dim + qk_nope_head_dim) + kv_b = data_torch.view(n_head_kv, v_head_dim + qk_nope_head_dim, data_torch.shape[-1]) + k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1) + k_b = k_b.transpose(1, 2) + yield from super().modify_tensors(k_b, name_kb, bid) + yield from super().modify_tensors(v_b, name_vb, bid) + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/kimivl.py b/conversion/kimivl.py new file mode 100644 index 00000000000..63b8a079b72 --- /dev/null +++ b/conversion/kimivl.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + + +@ModelBase.register("KimiVLForConditionalGeneration") +class KimiVLModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["image_size"] = 64 * 14 # for compatibility + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.KIMIVL) + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_projector_scale_factor(2) + # eps is the same as pytorch's default value + assert self.hparams_vision is not None + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-5)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + is_vision_tensor = "vision_tower" in name or "multi_modal_projector" in name + + if not is_vision_tensor: + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "pos_emb.weight" in name: + data_torch = data_torch.view(data_torch.shape[0] * data_torch.shape[1], data_torch.shape[2]) + + if "wqkv" in name: + split_dim = 0 if "weight" in name else -1 + wq, wk, wv = data_torch.chunk(3, dim=split_dim) + yield from super().modify_tensors(wq, name.replace("wqkv", "wq"), bid) + yield from super().modify_tensors(wk, name.replace("wqkv", "wk"), bid) + yield from super().modify_tensors(wv, name.replace("wqkv", "wv"), bid) + else: + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("KimiK25ForConditionalGeneration") +class KimiK25Model(MmprojModel): + """Kimi-K2.5 with MoonViT3d vision encoder""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + assert self.hparams_vision is not None, "Kimi-K2.5 requires vision_config in model config" + + self.merge_kernel_size = tuple(self.hparams_vision.get("merge_kernel_size", [2, 2])) + self.patch_size = self.hparams_vision.get("patch_size", 14) + + # Set image_size for compatibility with base class + # Use position embedding dimensions as image_size reference + pos_emb_h = self.hparams_vision.get("init_pos_emb_height", 64) + self.hparams_vision["image_size"] = pos_emb_h * self.patch_size + + def set_gguf_parameters(self): + # Base class MmprojModel.set_gguf_parameters() already writes: + # - vision_block_count, vision_head_count, vision_embedding_length + # - vision_feed_forward_length, vision_patch_size, image_mean, image_std + # via find_vparam() which handles the vt_* prefixed keys in Kimi-K2.5's config + super().set_gguf_parameters() + assert self.hparams_vision is not None + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.KIMIK25) + + # Position embedding parameters (for interpolation) + self.gguf_writer.add_uint32("vision.pos_emb_height", self.hparams_vision.get("init_pos_emb_height", 64)) + self.gguf_writer.add_uint32("vision.pos_emb_width", self.hparams_vision.get("init_pos_emb_width", 64)) + self.gguf_writer.add_uint32("vision.pos_emb_time", self.hparams_vision.get("init_pos_emb_time", 4)) + + # Projector parameters + self.gguf_writer.add_vision_use_gelu(self.hparams_vision.get("projector_hidden_act", "gelu") == "gelu") + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("projector_ln_eps", 1e-5)) + self.gguf_writer.add_vision_projector_scale_factor(self.merge_kernel_size[0]) + + # Image size limits + # Note: in_patch_limit is for images, in_patch_limit_each_frame is for video (not supported yet) + in_patch_limit = self.preprocessor_config.get("in_patch_limit", 16384) + min_patches = 8 # reasonable minimum + pixels_per_patch = self.patch_size ** 2 + self.gguf_writer.add_vision_min_pixels(min_patches * pixels_per_patch) + self.gguf_writer.add_vision_max_pixels(in_patch_limit * pixels_per_patch) + + @staticmethod + def permute(weights: Tensor, n_head: int) -> Tensor: + out_dim, in_dim = weights.shape + head_dim = out_dim // n_head + w = weights.reshape(n_head, head_dim // 4, 2, 2, in_dim) + w = w.permute(0, 2, 1, 3, 4) + return w.reshape(out_dim, in_dim) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Only process vision and projector tensors + is_vision = any(x in name for x in ["vision_tower", "mm_projector"]) + + if not is_vision: + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + assert self.hparams_vision is not None + n_head = self.hparams_vision.get("num_attention_heads", 16) + + # Permute Q/K weights/biases from interleaved to split RoPE format + # This allows using build_rope_2d at runtime without post-permutation. + if "wqkv" in name: + out_dim = data_torch.shape[0] + qkv_dim = out_dim // 3 + head_dim = qkv_dim // n_head + + if "weight" in name: + wq, wk, wv = data_torch[:qkv_dim, :], data_torch[qkv_dim:2 * qkv_dim, :], data_torch[2 * qkv_dim:, :] + wq = self.permute(wq, n_head) + wk = self.permute(wk, n_head) + data_torch = torch.cat([wq, wk, wv], dim=0) + elif "bias" in name: + bq, bk, bv = data_torch[:qkv_dim], data_torch[qkv_dim:2 * qkv_dim], data_torch[2 * qkv_dim:] + bq = bq.reshape(n_head, head_dim // 4, 2, 2).permute(0, 2, 1, 3).reshape(-1) + bk = bk.reshape(n_head, head_dim // 4, 2, 2).permute(0, 2, 1, 3).reshape(-1) + data_torch = torch.cat([bq, bk, bv], dim=0) + + # Temporal embeddings: (T, 1, C) → (T, C) + if "pos_emb.time_weight" in name: + T, _, C = data_torch.shape + data_torch = data_torch.reshape(T, C) + + # PatchMergerMLP tensor name mapping + # proj.0.weight → proj.linear_1.weight + # proj.2.weight → proj.linear_2.weight + if "mm_projector.proj.0." in name: + name = name.replace(".proj.0.", ".proj.linear_1.") + elif "mm_projector.proj.2." in name: + name = name.replace(".proj.2.", ".proj.linear_2.") + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/lfm2.py b/conversion/lfm2.py new file mode 100644 index 00000000000..f28fccf10f2 --- /dev/null +++ b/conversion/lfm2.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf + +from .gemma import ConformerAudioModel + + +@ModelBase.register("Lfm2ForCausalLM", "LFM2ForCausalLM") +class LFM2Model(TextModel): + model_arch = gguf.MODEL_ARCH.LFM2 + + def _add_feed_forward_length(self): + ff_dim = self.find_hparam(["block_ff_dim", "intermediate_size"]) + auto_adjust_ff_dim = self.hparams["block_auto_adjust_ff_dim"] + ffn_dim_multiplier = self.hparams["block_ffn_dim_multiplier"] + multiple_of = self.hparams["block_multiple_of"] + + if auto_adjust_ff_dim: + ff_dim = int(2 * ff_dim / 3) + # custom dim factor multiplier + if ffn_dim_multiplier is not None: + ff_dim = int(ffn_dim_multiplier * ff_dim) + ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of) + + self.gguf_writer.add_feed_forward_length(ff_dim) + + def set_gguf_parameters(self): + # set num_key_value_heads only for attention layers + self.hparams["num_key_value_heads"] = [ + self.hparams["num_key_value_heads"] if layer_type != "conv" else 0 + for layer_type in self.hparams["layer_types"] + ] + + super().set_gguf_parameters() + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["norm_eps"]) + self._add_feed_forward_length() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if ConformerAudioModel.is_audio_tensor(name): + # skip multimodal tensors + return None + + name = name.replace("lfm.", "model.") # audio + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # conv op requires 2d tensor + if 'conv.conv' in name: + data_torch = data_torch.squeeze(1) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Lfm2Model") +class LFM2ColBertModel(LFM2Model): + model_arch = gguf.MODEL_ARCH.LFM2 + dense_tensor_name = "dense_2" + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if not name.startswith(self.dense_tensor_name): + name = "model." + name + + yield from super().modify_tensors(data_torch, name, bid) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + # dense tensor is stored in a separate safetensors file + from safetensors.torch import load_file + tensors_file = self.dir_model / "1_Dense" / "model.safetensors" + assert tensors_file.is_file() + tensor = load_file(tensors_file)["linear.weight"] + self.gguf_writer.add_embedding_length_out(tensor.shape[0]) + yield f"{self.dense_tensor_name}.weight", tensor.clone() + + +@ModelBase.register("Lfm2MoeForCausalLM") +class LFM2MoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.LFM2MOE + + def set_gguf_parameters(self): + # set num_key_value_heads only for attention layers + self.hparams["num_key_value_heads"] = [ + self.hparams["num_key_value_heads"] if layer_type == "full_attention" else 0 + for layer_type in self.hparams["layer_types"] + ] + + super().set_gguf_parameters() + + self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) + self.gguf_writer.add_leading_dense_block_count(self.hparams["num_dense_layers"]) + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) + + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"]) + + # cache for experts weights for merging + _experts_cache: dict[int, dict[str, Tensor]] = {} + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.endswith(".expert_bias"): + name = name.replace(".expert_bias", ".expert_bias.bias") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # conv op requires 2d tensor + if 'conv.conv' in name: + data_torch = data_torch.squeeze(1) + + # merge expert weights + if 'experts' in name: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + expert_cache = self._experts_cache.setdefault(bid, {}) + expert_cache[name] = data_torch + expert_weights = ["w1", "w2", "w3"] + + # not enough expert weights to merge + if len(expert_cache) < n_experts * len(expert_weights): + return + + for w_name in expert_weights: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.feed_forward.experts.{xid}.{w_name}.weight" + datas.append(expert_cache[ename]) + del expert_cache[ename] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"layers.{bid}.feed_forward.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + + del self._experts_cache[bid] + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + assert not self._experts_cache + + +@ModelBase.register("Lfm2VlForConditionalGeneration") +class LFM2VLModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + # TODO(tarek): for dynamic resolution image_size is not specified, setting here for compatibility + self.hparams_vision["image_size"] = 256 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LFM2) + self.gguf_writer.add_vision_attention_layernorm_eps(self.find_vparam(["layer_norm_eps"])) + self.gguf_writer.add_vision_projector_scale_factor(self.global_config.get("downsample_factor", 2)) + self.gguf_writer.add_vision_use_gelu(True) + # python notation, e.g. for vision_feature_layer == -1, we pick last layer -> vision_feature_layers_to_drop = 0 + vision_feature_layers_to_drop = -(self.global_config.get("vision_feature_layer", -1) + 1) + self.gguf_writer.add_vision_block_count(self.find_vparam(self.n_block_keys) - vision_feature_layers_to_drop) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + name = name.replace("model.vision_tower.", "vision_tower.") + name = name.replace("model.multi_modal_projector.", "multi_modal_projector.") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "patch_embedding.weight" in name: + data_torch = data_torch.view(data_torch.shape[0], 16, 16, 3).permute(0, 3, 1, 2) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Lfm2AudioForConditionalGeneration") +class LFM2AudioModel(ConformerAudioModel): + has_vision_encoder = False + has_audio_encoder = True + model_name = "Lfm2AudioEncoder" + + def get_audio_config(self) -> dict[str, Any] | None: + return self.global_config.get("encoder") + + def set_gguf_parameters(self): + assert self.hparams_audio is not None + self.hparams_audio["hidden_size"] = self.hparams_audio["d_model"] + self.hparams_audio["intermediate_size"] = self.hparams_audio["d_model"] + self.hparams_audio["num_attention_heads"] = self.hparams_audio["n_heads"] + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LFM2A) + self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) + self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # skip language model tensors + if name.startswith("lfm."): + return None + + # for training only + if any(p in name for p in ["audio_loss_weight"]): + return None + + # for audio output + if any(p in name for p in ["codebook_offsets", "depth_embeddings", "depth_linear", "depthformer"]): + return None + + return super().filter_tensors(item) + + +@ModelBase.register("Lfm25AudioTokenizer") +class LFM25AudioTokenizer(LFM2Model): + model_arch = gguf.MODEL_ARCH.LFM2 + + def set_vocab(self): + self._set_vocab_none() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + self.gguf_writer.add_embedding_length_out(self.hparams["output_size"]) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # skip language model tensors + if name == "istft.window" or name.startswith("emb.emb"): + return None + + if name.startswith("lin"): + name = name.replace("lin", "dense_2_out") + + return super().filter_tensors((name, gen)) diff --git a/conversion/lighton_ocr.py b/conversion/lighton_ocr.py new file mode 100644 index 00000000000..ead3200ac18 --- /dev/null +++ b/conversion/lighton_ocr.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, gguf + +from .llava import LlavaVisionModel + + +@ModelBase.register("LightOnOCRForConditionalGeneration") +class LightOnOCRVisionModel(LlavaVisionModel): + is_mistral_format = False + use_break_tok = False + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LIGHTONOCR) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + name = name.replace("model.vision_encoder.", "vision_tower.") + name = name.replace("model.vision_projection.", "multi_modal_projector.") + + return super().filter_tensors((name, gen)) diff --git a/conversion/llada.py b/conversion/llada.py new file mode 100644 index 00000000000..98dc9de95b3 --- /dev/null +++ b/conversion/llada.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("LLaDAModelLM") +class LLaDAModel(TextModel): + model_arch = gguf.MODEL_ARCH.LLADA + undo_permute = True + + def get_vocab_base(self) -> tuple[list[str], list[int], str]: + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + + vocab_dict = tokenizer.get_vocab() # ty: ignore[unresolved-attribute] + vocab_size = self.hparams.get("vocab_size", len(vocab_dict)) + assert max(vocab_dict.values()) < vocab_size + + tokpre = self.get_vocab_base_pre(tokenizer) + + reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab_dict.items()} + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + elif reverse_vocab[i] in added_vocab: + tokens.append(reverse_vocab[i]) + # Check if it's a special token - treat special tokens as CONTROL tokens + if hasattr(tokenizer, 'added_tokens_decoder') and i in tokenizer.added_tokens_decoder: + if tokenizer.added_tokens_decoder[i].special: + toktypes.append(gguf.TokenType.CONTROL) + else: + toktypes.append(gguf.TokenType.USER_DEFINED) + else: + # Fallback: treat all added vocab as control tokens for special tokens like <|im_start|> + toktypes.append(gguf.TokenType.CONTROL) + else: + tokens.append(reverse_vocab[i]) + toktypes.append(gguf.TokenType.NORMAL) + + return tokens, toktypes, tokpre + + def set_vocab(self): + self._set_vocab_gpt2() + + # LLaDA specific parameters + self.gguf_writer.add_add_bos_token(True) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self._try_set_pooling_type() + + # Add parameters similar to LlamaModel + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + if (rope_dim := hparams.get("head_dim")) is None: + n_heads = hparams.get("num_attention_heads", hparams.get("n_heads")) + assert n_heads is not None + rope_dim = hparams.get("hidden_size", hparams.get("d_model")) // n_heads + self.gguf_writer.add_rope_dimension_count(rope_dim) + + # Set context length for LLaDA + context_length = self.hparams.get("max_sequence_length", 4096) + self.gguf_writer.add_context_length(context_length) + + # Set embedding length (dimension size) + embedding_length = self.hparams.get("d_model", 4096) + self.gguf_writer.add_embedding_length(embedding_length) + + # Set feed forward length (MLP hidden size) + feed_forward_length = self.hparams.get("mlp_hidden_size", 12288) + self.gguf_writer.add_feed_forward_length(feed_forward_length) + + # LLaDA models use non-causal attention for diffusion, similar to Dream + self.gguf_writer.add_causal_attention(False) + + # LLaDA models don't shift their logits + self.gguf_writer.add_diffusion_shift_logits(False) + + @staticmethod + def permute(weights: Tensor, n_head: int, n_head_kv: int | None): + if n_head_kv is not None and n_head != n_head_kv: + n_head = n_head_kv + return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams.get("num_attention_heads", self.hparams.get("n_heads")) + assert n_head is not None + n_kv_head = self.hparams.get("num_key_value_heads", self.hparams.get("n_kv_heads")) + + if self.undo_permute: + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = LLaDAModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = LLaDAModel.permute(data_torch, n_head, n_kv_head) + + # LLaDA model tensors should be mapped directly since it's the base model + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("LLaDAMoEModel", "LLaDAMoEModelLM") +class LLaDAMoEModel(TextModel): + model_arch = gguf.MODEL_ARCH.LLADA_MOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (expert_intermediate_size := self.hparams.get("expert_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size) + + self.gguf_writer.add_mask_token_id(156895) + self.gguf_writer.add_causal_attention(False) + self.gguf_writer.add_diffusion_shift_logits(False) + + _experts: list[dict[str, Tensor]] | None = None + + # Copied from: Qwen2MoeModel + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # process the experts separately + if name.find("experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + # Copied from: Qwen2MoeModel + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/llama.py b/conversion/llama.py new file mode 100644 index 00000000000..41fde5143f8 --- /dev/null +++ b/conversion/llama.py @@ -0,0 +1,312 @@ +from __future__ import annotations + +import json +import math + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register( + "LLaMAForCausalLM", + "LlamaForCausalLM", + "MistralForCausalLM", + "MixtralForCausalLM", + "VLlama3ForCausalLM", + "LlavaForConditionalGeneration", + "VoxtralForConditionalGeneration", + "IQuestCoderForCausalLM", + "LlamaModel") +class LlamaModel(TextModel): + model_arch = gguf.MODEL_ARCH.LLAMA + undo_permute = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # fix for SmolVLM2, missing `num_attention_heads` in config.json + if self.hf_arch == "VLlama3ForCausalLM": + self.hparams["num_attention_heads"] = self.hparams.get("num_attention_heads", 32) + # Mistral consolidated format has no config.json; origin_hf_arch is HF-only. + if self.is_mistral_format: + self.origin_hf_arch = None + else: + hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) + self.origin_hf_arch = hparams.get('architectures', [None])[0] + + def set_vocab(self): + if self.origin_hf_arch == "GlmasrModel": + return self._set_vocab_glmedge() + + if self.is_mistral_format: + return self._set_vocab_mistral() + + path_tekken_json = self.dir_model / "tekken.json" + path_tokenizer_json = self.dir_model / "tokenizer.json" + if path_tekken_json.is_file() and not path_tokenizer_json.is_file(): + self._set_vocab_mistral() + + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + try: + self._set_vocab_llama_hf() + except (FileNotFoundError, TypeError): + # Llama 3 + self._set_vocab_gpt2() + + # Apply to CodeLlama only (and ignore for Llama 3 with a vocab size of 128256) + if self.hparams.get("vocab_size", 32000) == 32016: + special_vocab = gguf.SpecialVocab( + self.dir_model, load_merges=False, + special_token_types = ['prefix', 'suffix', 'middle', 'eot'] + ) + special_vocab._set_special_token("prefix", 32007) + special_vocab._set_special_token("suffix", 32008) + special_vocab._set_special_token("middle", 32009) + special_vocab._set_special_token("eot", 32010) + special_vocab.add_to_gguf(self.gguf_writer) + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + if "add_prefix_space" in tokenizer_config_json: + self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) + + # Apply to granite small models only + if self.hparams.get("vocab_size", 32000) == 49152: + self.gguf_writer.add_add_bos_token(False) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + + if not self.is_mistral_format: + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(rope_dim) + + @staticmethod + def permute(weights: Tensor, n_head: int, n_head_kv: int | None): + if n_head_kv is not None and n_head != n_head_kv: + n_head = n_head_kv + return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape)) + + def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): + # Mirror the BF16 Q/K RoPE permutation site in modify_tensors; the NVFP4 path bypasses it. + if self.undo_permute: + n_head = self.find_hparam(["n_heads", "num_attention_heads"], optional=True) + n_kv_head = self.find_hparam(["n_kv_heads", "num_key_value_heads"], optional=True) + if n_head is not None: + if name.endswith("q_proj.weight"): + weight = LlamaModel.permute(weight, n_head, n_head) + scale = LlamaModel.permute(scale, n_head, n_head) + elif name.endswith("k_proj.weight"): + weight = LlamaModel.permute(weight, n_head, n_kv_head) + scale = LlamaModel.permute(scale, n_head, n_kv_head) + super()._repack_nvfp4(name, weight, scale, scale2, input_scale) + + _experts: list[dict[str, Tensor]] | None = None + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "text_model." in name: + name = name.replace("text_model.", "") # for SmolVLM + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.find_hparam(["n_heads", "num_attention_heads"]) + n_kv_head = self.find_hparam(["n_kv_heads", "num_key_value_heads"]) + + if self.hf_arch == "LlamaModel": + name = "model." + name + + if self.undo_permute: + if name.endswith(("q_proj.weight", "q_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + + # process the experts separately + if name.find("block_sparse_moe.experts") != -1: + n_experts = self.hparams["num_local_experts"] + + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for wid in ["w1", "w2", "w3"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"layers.{bid}.feed_forward.experts.{wid}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): + if rope_params.get("rope_type", '').lower() == "llama3": + base = rope_params.get("rope_theta", 10000.0) + if (dim := self.hparams.get("head_dim")) is None: + dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) + + factor = rope_params.get("factor", 8.0) + low_freq_factor = rope_params.get("low_freq_factor", 1.0) + high_freq_factor = rope_params.get("high_freq_factor", 4.0) + old_context_len = self.hparams.get("original_max_position_embeddings", 8192) + + low_freq_wavelen = old_context_len / low_freq_factor + high_freq_wavelen = old_context_len / high_freq_factor + # assert low_freq_wavelen != high_freq_wavelen # Errors for Llama4 + + rope_factors = [] + for freq in freqs: + wavelen = 2 * math.pi / freq + if wavelen < high_freq_wavelen: + rope_factors.append(1) + elif wavelen > low_freq_wavelen: + rope_factors.append(factor) + else: + smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) + rope_factors.append(1 / ((1 - smooth) / factor + smooth)) + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("ArceeForCausalLM") +class ArceeModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.ARCEE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self._try_set_pooling_type() + + +@ModelBase.register( + "Llama4ForConditionalGeneration", + "Llama4ForCausalLM", +) +class Llama4Model(LlamaModel): + model_arch = gguf.MODEL_ARCH.LLAMA4 + undo_permute = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # IMPORTANT: the normal "intermediate_size" is renamed to "intermediate_size_mlp", we need to undo this + self.hparams["intermediate_size_moe"] = self.hparams["intermediate_size"] + self.hparams["intermediate_size"] = self.hparams["intermediate_size_mlp"] + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_interleave_moe_layer_step(self.hparams["interleave_moe_layer_step"]) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["intermediate_size_moe"]) + if "layer_types" in self.hparams: + if all(lt == "full_attention" for lt in self.hparams["layer_types"]): + # all layers are full attention (for MobileLLM), disable swa + self.gguf_writer.add_sliding_window(0) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): + # split the gate_up into gate and up + if "gate_up_proj" in name: + name_up = name.replace("gate_up_proj", "up_proj.weight") + name_gate = name.replace("gate_up_proj", "gate_proj.weight") + dim_half = data_torch.shape[-1] // 2 + gate_proj_weight, up_proj_weight = data_torch.transpose(-1, -2).split(dim_half, dim=-2) + yield from super().modify_tensors(gate_proj_weight, name_gate, bid) + yield from super().modify_tensors(up_proj_weight, name_up, bid) + return + + if name.endswith("down_proj"): + name += ".weight" + data_torch = data_torch.transpose(-1, -2) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("LlamaBidirectionalModel") +class LlamaEmbedNemotronModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.LLAMA_EMBED + + +@ModelBase.register("SmolLM3ForCausalLM") +class SmolLM3Model(LlamaModel): + model_arch = gguf.MODEL_ARCH.SMOLLM3 + + +@ModelBase.register("ApertusForCausalLM") +class ApertusModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.APERTUS + undo_permute = False + + _alpha_n = {} + _alpha_p = {} + _beta = {} + _eps = {} + + def modify_tensors(self, data_torch, name, bid): + # Handle xIELU activation parameters + n_layers = self.hparams["num_hidden_layers"] + if name.endswith(".act_fn.alpha_n"): + self._alpha_n[bid] = data_torch.to("cpu").float().item() + if (len(self._alpha_n) == n_layers): + self.gguf_writer.add_xielu_alpha_n([self._alpha_n[k] for k in sorted(self._alpha_n)]) + return + if name.endswith(".act_fn.alpha_p"): + self._alpha_p[bid] = data_torch.to("cpu").float().item() + if (len(self._alpha_p) == n_layers): + self.gguf_writer.add_xielu_alpha_p([self._alpha_p[k] for k in sorted(self._alpha_p)]) + return + if name.endswith(".act_fn.beta"): + self._beta[bid] = data_torch.to("cpu").float().item() + if (len(self._beta) == n_layers): + self.gguf_writer.add_xielu_beta([self._beta[k] for k in sorted(self._beta)]) + return + if name.endswith(".act_fn.eps"): + self._eps[bid] = data_torch.to("cpu").float().item() + if (len(self._eps) == n_layers): + self.gguf_writer.add_xielu_eps([self._eps[k] for k in sorted(self._eps)]) + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/llama4.py b/conversion/llama4.py new file mode 100644 index 00000000000..f84c7629619 --- /dev/null +++ b/conversion/llama4.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + + +@ModelBase.register("Llama4ForConditionalGeneration") +class Llama4VisionModel(MmprojModel): + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LLAMA4) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams["norm_eps"]) + self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / self.hparams["pixel_shuffle_ratio"])) + assert self.hparams["hidden_act"] == "gelu" + self.gguf_writer.add_vision_use_gelu(True) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "multi_modal_projector" not in name and "vision_model" not in name: + return None + + if "positional_embedding_vlm" in name and ".weight" not in name: + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "multi_modal_projector.linear_1" in name: + # despite the name with number postfix, this is a single fully connected layer + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_MMPROJ_FC] + '.weight', data_torch) + else: + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/llava.py b/conversion/llava.py new file mode 100644 index 00000000000..31d6e2ad80e --- /dev/null +++ b/conversion/llava.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import json + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf, logger + +from .llama import LlamaModel + + +@ModelBase.register( + "LlavaForConditionalGeneration", # pixtral + "Mistral3ForConditionalGeneration", # mistral small 3.1 +) +class LlavaVisionModel(MmprojModel): + img_break_tok_id = -1 + use_break_tok = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams.get("model_type") == "pixtral": + # layer_norm_eps is not in config.json, it is hard-coded in modeling_pixtral.py + self.hparams["layer_norm_eps"] = self.hparams.get("layer_norm_eps", 1e-5) + if self.use_break_tok: + self.img_break_tok_id = self.get_token_id("[IMG_BREAK]") + elif self.is_mistral_format: + # hparams is already vision config here so norm_eps is only defined in global_config. + self.hparams["norm_eps"] = self.global_config.get("norm_eps", None) + assert self.hparams["norm_eps"] is not None, "norm_eps not found in params.json" + if self.use_break_tok: + self.img_break_tok_id = self.find_vparam(["image_break_token_id"]) + + # params.json may ship -1 placeholders (Mistral Medium 3.5) + # resolve the real id from the bundled tokenizer in that case + if self.img_break_tok_id < 0: + self.img_break_tok_id = self.get_mistral_token_id("[IMG_BREAK]") + else: + raise ValueError(f"Unsupported model type: {self.hparams['model_type']}") + logger.info(f"Image break token id: {self.img_break_tok_id}") + + def get_token_id(self, token: str) -> int: + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + added_tokens_decoder = json.load(f).get('added_tokens_decoder') or {} + for id_, token_data in added_tokens_decoder.items(): + if token_data.get("content") == token: + return int(id_) + # fallthrough to tokenizer.json + with open(self.dir_model / "tokenizer.json", "r", encoding="utf-8") as f: + tokenizer_json = json.load(f) + for token_data in tokenizer_json["added_tokens"]: + if token_data["content"] == token: + return int(token_data["id"]) + raise ValueError(f"Token '{token}' not found in tokenizer config.") + + def get_mistral_token_id(self, token: str) -> int: + # mistral native format ships tekken.json or a versioned spm tokenizer + tekken_file = self.dir_model / "tekken.json" + if tekken_file.is_file(): + with open(tekken_file, "r", encoding="utf-8") as f: + data = json.load(f) + for entry in data.get("special_tokens", []): + if entry.get("token_str") == token: + return int(entry["rank"]) + tokenizer_json_file = self.dir_model / "tokenizer.json" + if tokenizer_json_file.is_file(): + with open(tokenizer_json_file, "r", encoding="utf-8") as f: + data = json.load(f) + for entry in data.get("added_tokens", []): + if entry.get("content") == token: + return int(entry["id"]) + raise ValueError(f"Token '{token}' not found in mistral tokenizer files.") + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + if hparams.get("model_type") == "pixtral": + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PIXTRAL) + self.gguf_writer.add_vision_attention_layernorm_eps(hparams["layer_norm_eps"]) + + # hidden_act + if hparams["hidden_act"] == "silu": + self.gguf_writer.add_vision_use_silu(True) + elif hparams["hidden_act"] == "gelu": + self.gguf_writer.add_vision_use_gelu(True) + else: + raise ValueError(f"Unsupported hidden_act: {hparams['hidden_act']}") + + # spatial_merge_size + if "spatial_merge_size" in self.global_config: + self.gguf_writer.add_vision_spatial_merge_size(self.global_config["spatial_merge_size"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = ( + self.hparams["num_attention_heads"] if not self.is_mistral_format else self.find_vparam(["num_attention_heads"]) + ) + n_kv_head = n_head + + valid_prefixes = ( + "multi_modal_projector.", + "vision_tower.", + "vision_encoder.", + "vision_language_adapter.", + "patch_merger.", + "pre_mm_projector_norm", + ) + + if any(name.startswith(prefix) for prefix in valid_prefixes): + # process vision tensors + if name.endswith(("q_proj.weight", "q_proj.bias")) and not self.is_mistral_format: + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight", "k_proj.bias")) and not self.is_mistral_format: + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + yield from super().modify_tensors(data_torch, name, bid) + return + + embed_key = "embed_tokens.weight" if not self.is_mistral_format else "tok_embeddings.weight" + if self.img_break_tok_id > 0 and embed_key in name: + logger.info(f"Extracting [IMG_BREAK] token embedding from {name}") + # for pixtral model, we need to extract the [IMG_BREAK] token embedding + img_break_embd = data_torch[self.img_break_tok_id] + name = gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_TOK_EMBD_IMG_BREAK] + yield from super().modify_tensors(img_break_embd, name, bid) + + return # skip other tensors diff --git a/conversion/maincoder.py b/conversion/maincoder.py new file mode 100644 index 00000000000..18b625b08fb --- /dev/null +++ b/conversion/maincoder.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("MaincoderForCausalLM") +class MaincoderModel(TextModel): + model_arch = gguf.MODEL_ARCH.MAINCODER + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + if (head_dim := self.hparams.get("head_dim")) is not None: + self.gguf_writer.add_rope_dimension_count(head_dim) diff --git a/conversion/mamba.py b/conversion/mamba.py new file mode 100644 index 00000000000..be0e36a29ba --- /dev/null +++ b/conversion/mamba.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +import json + +from pathlib import Path +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("MambaForCausalLM", "MambaLMHeadModel", "FalconMambaForCausalLM") +class MambaModel(TextModel): + model_arch = gguf.MODEL_ARCH.MAMBA + + def __init__(self, dir_model: Path, *args, **kwargs): + # Avoid using AutoConfig for hparams + hparams = kwargs.pop("hparams", None) + if hparams is None: + with open(dir_model / "config.json", "r", encoding="utf-8") as f: + hparams = json.load(f) + super().__init__(dir_model, *args, hparams=hparams, **kwargs) + + def set_vocab(self): + vocab_size = self.hparams["vocab_size"] + # Round vocab size to next multiple of 8 + pad_vocab = self.hparams.get("pad_vocab_size_multiple", 8) + # pad using ceiling division + # ref: https://stackoverflow.com/a/17511341/22827863 + vocab_size = -(vocab_size // -pad_vocab) * pad_vocab + self.hparams["vocab_size"] = vocab_size + + if (self.dir_model / "tokenizer.json").is_file(): + self._set_vocab_gpt2() + elif (self.dir_model / "tokenizer.model").is_file(): + self._set_vocab_sentencepiece() + else: + # Use the GPT-NeoX tokenizer when no tokenizer files are present + self._set_vocab_builtin("gpt-neox", vocab_size) + + def set_gguf_parameters(self): + d_model = self.find_hparam(["hidden_size", "d_model"]) + d_conv = self.find_hparam(["conv_kernel", "d_conv"], optional=True) or 4 + d_inner = self.find_hparam(["intermediate_size", "d_inner"], optional=True) or 2 * d_model + d_state = self.find_hparam(["state_size", "d_state"], optional=True) or 16 + # ceiling division + # ref: https://stackoverflow.com/a/17511341/22827863 + # ref: https://github.com/state-spaces/mamba/blob/ce59daea3a090d011d6476c6e5b97f6d58ddad8b/mamba_ssm/modules/mamba_simple.py#L58 + dt_rank = self.find_hparam(["time_step_rank", "dt_rank"], optional=True) or -(d_model // -16) + rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-5 + use_dt_b_c_norm = False + # For falconmamba we do apply RMS norm on B / DT and C layers + if self.find_hparam(["model_type"], optional=True) in ("falcon_mamba",): + use_dt_b_c_norm = True + # Fail early for models which don't have a block expansion factor of 2 + assert d_inner == 2 * d_model + + self.gguf_writer.add_context_length(2**20) # arbitrary value; for those who use the default + self.gguf_writer.add_embedding_length(d_model) + self.gguf_writer.add_feed_forward_length(0) # unused, but seemingly required when loading + self.gguf_writer.add_head_count(0) # unused, but seemingly required when loading + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_ssm_conv_kernel(d_conv) + self.gguf_writer.add_ssm_inner_size(d_inner) + self.gguf_writer.add_ssm_state_size(d_state) + self.gguf_writer.add_ssm_time_step_rank(dt_rank) + self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) + self.gguf_writer.add_ssm_dt_b_c_rms(use_dt_b_c_norm) # For classic Mamba we don't apply rms norm on B / DT layers + self.gguf_writer.add_file_type(self.ftype) + + _tok_embd = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + output_name = self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT) + tok_embd_name = self.format_tensor_name(gguf.MODEL_TENSOR.TOKEN_EMBD) + + new_name = self.map_tensor_name(name) + + if name.endswith(".A_log"): + logger.debug("A_log --> A ==> " + new_name) + data_torch = -torch.exp(data_torch) + + # [4 1 8192 1] -> [4 8192 1 1] + if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): + data_torch = data_torch.squeeze() + + # assuming token_embd.weight is seen before output.weight + if self._tok_embd is not None and new_name == output_name: + if torch.equal(self._tok_embd, data_torch): + logger.debug(f"{output_name} is equivalent to {tok_embd_name}, omitting") + return + elif new_name == tok_embd_name: + self._tok_embd = data_torch + + yield from super().modify_tensors(data_torch, new_name, bid) + + +@ModelBase.register("Mamba2ForCausalLM") +class Mamba2Model(TextModel): + model_arch = gguf.MODEL_ARCH.MAMBA2 + + def __init__(self, dir_model: Path, *args, **kwargs): + # Avoid using AutoConfig for hparams + # It wrongly assumes all Mamba2 models are Mamba-Codestral-7B-v0.1 + hparams = kwargs.pop("hparams", None) + if hparams is None: + with open(dir_model / "config.json", "r", encoding="utf-8") as f: + hparams = json.load(f) + if "llm_config" in hparams: + hparams["text_config"] = hparams["llm_config"] + super().__init__(dir_model, *args, hparams=hparams, **kwargs) + self.d_model = self.find_hparam(["hidden_size", "d_model", "dim"]) + self.d_inner = self.find_hparam(["mamba_d_ssm", "intermediate_size", "d_inner"], optional=True) or 2 * self.d_model + self.n_group = self.find_hparam(["n_groups"], optional=True) or 1 + + def set_vocab(self): + vocab_size = self.hparams["vocab_size"] + # Round vocab size to next multiple of 16 + pad_vocab = self.hparams.get("pad_vocab_size_multiple", 16) + # pad using ceiling division + # ref: https://stackoverflow.com/a/17511341/22827863 + vocab_size = -(vocab_size // -pad_vocab) * pad_vocab + self.hparams["vocab_size"] = vocab_size + + if (self.dir_model / "tokenizer.model").is_file(): + self._set_vocab_sentencepiece() + elif (self.dir_model / "tokenizer.model.v3").is_file(): + # mamba-codestral + raise NotImplementedError(f"Please rename {self.dir_model / 'tokenizer.model.v3'} to {self.dir_model / 'tokenizer.model'}") + elif (self.dir_model / "tokenizer.json").is_file(): + self._set_vocab_gpt2() + else: + # Use the GPT-NeoX tokenizer when no tokenizer files are present + self._set_vocab_builtin("gpt-neox", vocab_size) + + def set_gguf_parameters(self): + d_conv = self.find_hparam(["conv_kernel", "d_conv"], optional=True) or 4 + d_state = self.find_hparam(["state_size", "d_state"], optional=True) or 128 + head_dim = self.find_hparam(["mamba_d_head", "head_dim"], optional=True) or 64 + + rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-5 + + # Fail early for models which don't have a block expansion factor of 2 + # TODO: does this really matter? + # skip the assertion for FalconH1 Model + if self.model_arch != gguf.MODEL_ARCH.FALCON_H1: + assert self.d_inner == 2 * self.d_model + assert self.d_inner % head_dim == 0 + + self.gguf_writer.add_context_length(2**20) # arbitrary value; for those who use the default + self.gguf_writer.add_embedding_length(self.d_model) + self.gguf_writer.add_feed_forward_length(0) # unused, but seemingly required when loading + self.gguf_writer.add_head_count(0) # unused, but seemingly required when loading + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_ssm_conv_kernel(d_conv) + self.gguf_writer.add_ssm_inner_size(self.d_inner) + self.gguf_writer.add_ssm_state_size(d_state) + self.gguf_writer.add_ssm_time_step_rank(self.d_inner // head_dim) + self.gguf_writer.add_ssm_group_count(self.n_group) + self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) + self.gguf_writer.add_file_type(self.ftype) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith(("model.backbone", "model.lm_head")): + # map Mamba-Codestral-7B-v0.1 tensor names to the names used by Mamba-2 + name = name.removeprefix("model.") + + if name.endswith(".dt_bias"): + name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + new_name = self.map_tensor_name(name) + + if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): + data_torch = data_torch.squeeze() + elif any(self.match_model_tensor_name(new_name, t, bid, suffix="") for t in [ + gguf.MODEL_TENSOR.SSM_A, + gguf.MODEL_TENSOR.SSM_D, + ]): + # unsqueeze A to use similar shape semantics as Mamba-1 + # (D is also unsqueezed, but for more straightforward broadcast internally) + data_torch = data_torch.reshape((*data_torch.shape, 1)) + elif self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_NORM, bid): + data_torch = data_torch.reshape((self.n_group, self.d_inner // self.n_group)) + + if name.endswith(".A_log"): + logger.debug("A_log --> A ==> " + new_name) + data_torch = -torch.exp(data_torch) + + yield (new_name, data_torch) diff --git a/conversion/mimo.py b/conversion/mimo.py new file mode 100644 index 00000000000..d4067aab4b6 --- /dev/null +++ b/conversion/mimo.py @@ -0,0 +1,295 @@ +from __future__ import annotations + +import re + +from typing import Callable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf + + +@ModelBase.register("MiMoV2FlashForCausalLM", "MiMoV2ForCausalLM") +class MimoV2Model(TextModel): + model_arch = gguf.MODEL_ARCH.MIMO2 + + # MiMo V2-Flash, V2.5 and V2.5-Pro all ship 3 trained MTP layers under model.mtp.layers.{0,1,2}. + # The HF config does not expose the count, so it's hardcoded to match the count found in the safetensors. + _n_nextn = 3 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.block_count = self.hparams["num_hidden_layers"] + self._n_nextn + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + @staticmethod + def _tp_aware_qkv_dequant(weight: Tensor, scale_inv: Tensor, + n_q: int, n_kv: int, hd: int, vhd: int, + bs: int = 128) -> Tensor: + # MiMo-V2.5 (TP=4) and V2.5-Pro (TP=8) ship qkv_proj sharded across TP + # ranks; per rank, rows are stacked as [Q_per | K_per | V_per]. + # weight_scale_inv has ceil(rows_per_rank/bs) block-rows per rank (last + # may extend past rows_per_rank with phantom rows not in the weight). + # Naive repeat_interleave aligns rank 0 only and mis-applies scales to + # later ranks once rows_per_rank isn't a multiple of bs. + # Re-group the per-rank [Q_per|K_per|V_per] rows into a single fused + # [Q | K | V] tensor matching the un-sharded original layout. + q_size = n_q * hd + k_size = n_kv * hd + v_size = n_kv * vhd + total_rows = q_size + k_size + v_size + if weight.shape[0] != total_rows: + raise ValueError(f"qkv_proj weight rows {weight.shape[0]} != q+k+v {total_rows}") + + # detect TP from scale_inv block count, descending order so larger matches first + tp = None + for cand in (8, 4): + if total_rows % cand != 0: + continue + rpr = total_rows // cand + bpr = (rpr + bs - 1) // bs + if scale_inv.shape[0] == cand * bpr: + tp = cand + break + if tp is None: + raise ValueError( + f"qkv_proj: cannot detect TP - scale_inv rows {scale_inv.shape[0]}, " + f"q+k+v {total_rows}") + + q_per = q_size // tp + k_per = k_size // tp + v_per = v_size // tp + rows_per_rank = q_per + k_per + v_per + blocks_per_rank = (rows_per_rank + bs - 1) // bs + + scale_inv = scale_inv.float() + # per-row scale-row index: rank * blocks_per_rank + (rr_in_rank // bs) + row_idx = torch.arange(total_rows) + rr = row_idx % rows_per_rank + rank = row_idx // rows_per_rank + scale_row_idx = rank * blocks_per_rank + (rr // bs) + # gather: (total_rows, n_col_blocks) + scale_per_row_block = scale_inv[scale_row_idx] + # expand col-blocks -> cols: each block-col covers `bs` weight cols + scale_full = scale_per_row_block.repeat_interleave(bs, dim=1) + # crop to weight col count (in case last col-block isn't full) + scale_full = scale_full[:, : weight.shape[1]] + dequant = weight.float() * scale_full + + if tp == 1: + return dequant + + # Re-group per-rank [Q_per|K_per|V_per] rows into unified [Q | K | V] + qs, ks, vs = [], [], [] + for r in range(tp): + base = r * rows_per_rank + qs.append(dequant[base : base + q_per]) + ks.append(dequant[base + q_per : base + q_per + k_per]) + vs.append(dequant[base + q_per + k_per : base + rows_per_rank]) + return torch.cat(qs + ks + vs, dim=0) + + def dequant_model(self): + # Capture raw FP8 (weight, scale_inv) lambdas for qkv_proj BEFORE super + # rewrites them with the existing dequant. Replace super's lambda after + # it runs so scale_inv removal still happens via the standard path. + qkv_overrides: dict[str, tuple[Callable, Callable, int]] = {} + qc = self.hparams.get("quantization_config") + if isinstance(qc, dict) and qc.get("quant_method") == "fp8": + pat = re.compile(r"^model\.layers\.(\d+)\.self_attn\.qkv_proj\.weight_scale_inv$") + for name in list(self.model_tensors.keys()): + m = pat.match(name) + if not m: + continue + weight_name = name.removesuffix("_scale_inv") + if weight_name not in self.model_tensors: + continue + qkv_overrides[weight_name] = ( + self.model_tensors[weight_name], + self.model_tensors[name], + int(m.group(1)), + ) + + super().dequant_model() + + if not qkv_overrides: + return + + n_q = self.hparams["num_attention_heads"] + hd = self.hparams["head_dim"] + vhd = self.hparams["v_head_dim"] + hybrid = self.hparams["hybrid_layer_pattern"] + n_layer_text = self.hparams["num_hidden_layers"] + for weight_name, (w_fn, s_fn, bid) in qkv_overrides.items(): + # MTP layers (bid >= n_layer_text) use SWA-style attention dims + is_swa = True if bid >= n_layer_text else hybrid[bid] == 1 + n_kv = self.hparams["swa_num_key_value_heads" if is_swa else "num_key_value_heads"] + self.model_tensors[weight_name] = ( + lambda w_fn=w_fn, s_fn=s_fn, n_q=n_q, n_kv=n_kv, hd=hd, vhd=vhd: + MimoV2Model._tp_aware_qkv_dequant(w_fn(), s_fn(), n_q, n_kv, hd, vhd) + ) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + assert self.hparams["swa_head_dim"] == self.hparams["head_dim"] + assert self.hparams["swa_num_attention_heads"] == self.hparams["num_attention_heads"] + assert self.hparams["swa_v_head_dim"] == self.hparams["v_head_dim"] + assert self.hparams["topk_method"] == "noaux_tc" + + n_head_kv = self.hparams["num_key_value_heads"] + n_head_kv_swa = self.hparams["swa_num_key_value_heads"] + # Extend the per-layer pattern with SWA entries for the MTP blocks so the + # runtime arrays (sized to extended block_count) are fully populated. + hybrid = list(self.hparams["hybrid_layer_pattern"]) + [1] * self._n_nextn + n_head_kv_arr = [n_head_kv_swa if use_swa == 1 else n_head_kv for use_swa in hybrid] + self.gguf_writer.add_head_count_kv(n_head_kv_arr) + + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + self.gguf_writer.add_sliding_window_pattern(hybrid) + self.gguf_writer.add_value_length(self.hparams["v_head_dim"]) + self.gguf_writer.add_expert_count(self.hparams["n_routed_experts"]) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) + + rope_dim = int(self.hparams["head_dim"] * self.hparams["partial_rotary_factor"]) + self.gguf_writer.add_rope_dimension_count(rope_dim) + + self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("layernorm_epsilon", 1e-5)) + + v_scale = self.hparams.get("attention_value_scale") + if v_scale is not None: + self.gguf_writer.add_attn_value_scale(float(v_scale)) + + self.gguf_writer.add_nextn_predict_layers(self._n_nextn) + + _experts: list[dict[str, Tensor]] | None = None + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "attention_sink" in name and not name.endswith(".weight"): + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch, name, bid): + # Remap MTP/NextN tensors to additional layer slots so the standard tensor map handles them. + # HF: model.mtp.layers.{i}.foo -> model.layers.{n_layer_text + i}.foo + m = re.match(r"^model\.mtp\.layers\.(\d+)\.(.*)$", name) + if m is not None: + mtp_idx = int(m.group(1)) + assert mtp_idx < self._n_nextn, f"MTP layer index {mtp_idx} >= _n_nextn ({self._n_nextn})" + rest = m.group(2) + n_layer_text = self.hparams["num_hidden_layers"] + new_bid = n_layer_text + mtp_idx + name = f"model.layers.{new_bid}.{rest}" + bid = new_bid + + # process the experts separately + if name.find("mlp.experts") != -1: + n_experts = self.hparams["n_routed_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["gate_proj", "up_proj", "down_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename_to_retrieve]) + del self._experts[bid][ename_to_retrieve] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("MiMoV2ForCausalLM") +class MiMoV2VisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + hp = self.hparams_vision + + hp["image_size"] = hp.get("image_size", 560) + hp["num_attention_heads"] = hp.get("num_heads", 32) + hp["num_hidden_layers"] = hp.get("depth", 28) + + self.n_q_heads = int(hp["num_heads"]) + self.num_kv_heads = int(hp.get("num_key_value_heads", 8)) + self.head_dim = int(hp.get("qk_channels", 64)) + self.spatial_merge_size = int(hp["spatial_merge_size"]) + # MiMoV2 vision RMSNorm: HF uses getattr(config, "rms_norm_eps", 1e-6) and the + # field is absent from MiMo-V2.5's vision_config + self.rms_norm_eps = float(hp.get("rms_norm_eps", 1e-6)) + + # fullatt_block_indexes are also reflected in vit_window_attn_types as -1 + self.fullatt_block_indexes = list(hp.get("fullatt_block_indexes") or []) + self.vit_window_attn_types = list(hp.get("vit_window_attn_types") or []) + self.visual_token_window_size = int(hp.get("visual_token_window_size", -1)) + self.use_sink = bool(hp.get("use_sink", False)) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MIMOVL) + self.gguf_writer.add_vision_use_silu(True) + self.gguf_writer.add_vision_head_count_kv(self.num_kv_heads) + self.gguf_writer.add_vision_spatial_merge_size(self.spatial_merge_size) + self.gguf_writer.add_uint32(gguf.Keys.ClipVision.WINDOW_SIZE, self.visual_token_window_size) + self.gguf_writer.add_vision_wa_pattern_mode(self.vit_window_attn_types) + self.gguf_writer.add_vision_attention_layernorm_eps(self.rms_norm_eps) + self.gguf_writer.add_vision_min_pixels(int(self.preprocessor_config["min_pixels"])) + self.gguf_writer.add_vision_max_pixels(int(self.preprocessor_config["max_pixels"])) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + # Sinks must be F32: any sink-style softmax/mask add in ggml requires + # F32, and we fold sinks into a host-built F32 mask at encode time. + if new_name.endswith(".attn_sinks"): + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, _ = item + if not name.startswith("visual."): + return None + return super().filter_tensors(item) + + def modify_tensors(self, data_torch, name, bid): + # Conv3D patch embed: split along the temporal axis (kt=2) into two Conv2D + # weights that the existing qwen2vl-style two-Conv2D path consumes. + if name == "visual.patch_embed.proj.weight": + _, _, kt, _, _ = data_torch.shape + if kt != 2: + raise ValueError(f"unexpected temporal_patch_size: {kt}") + embd_name = gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + yield (embd_name + ".weight", data_torch[:, :, 0, ...]) + yield (embd_name + ".weight.1", data_torch[:, :, 1, ...]) + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/minicpm.py b/conversion/minicpm.py new file mode 100644 index 00000000000..e9a4c4a74dc --- /dev/null +++ b/conversion/minicpm.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf, logger + +from .llama import LlamaModel +from .qwen import Qwen3_5TextModel + + +@ModelBase.register("MiniCPMForCausalLM") +class MiniCPMModel(TextModel): + model_arch = gguf.MODEL_ARCH.MINICPM + + def set_gguf_parameters(self): + super().set_gguf_parameters() + embedding_scale = float(self.hparams["scale_emb"]) + self.gguf_writer.add_embedding_scale(embedding_scale) + logger.info(f"gguf: (minicpm) embedding_scale = {embedding_scale}") + residual_scale = self.hparams["scale_depth"] / self.hparams["num_hidden_layers"] ** 0.5 + self.gguf_writer.add_residual_scale(residual_scale) + logger.info(f"gguf: (minicpm) residual_scale = {residual_scale}") + logit_scale = self.hparams["hidden_size"] / self.hparams["dim_model_base"] + self.gguf_writer.add_logit_scale(logit_scale) + logger.info(f"gguf: (minicpm) logit_scale = {logit_scale}") + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + rope_dims = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + + rope_scaling = self.find_hparam(['rope_scaling'], True) + if rope_scaling is not None: + long_factors = rope_scaling.get('long_factor', None) + short_factors = rope_scaling.get('short_factor', None) + + if long_factors is None or short_factors is None: + raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') + + if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: + raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}') + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + + # HF models permute some of the tensors, so we need to undo that + if name.endswith(("q_proj.weight")): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith(("k_proj.weight")): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("MiniCPM3ForCausalLM") +class MiniCPM3Model(TextModel): + model_arch = gguf.MODEL_ARCH.MINICPM3 + + def set_gguf_parameters(self): + hparams = self.hparams + + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) + self.gguf_writer.add_head_count(hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(hparams["num_key_value_heads"]) + self.gguf_writer.add_layer_norm_rms_eps(hparams["rms_norm_eps"]) + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + if "q_lora_rank" in hparams and hparams["q_lora_rank"] is not None: + self.gguf_writer.add_q_lora_rank(hparams["q_lora_rank"]) + self.gguf_writer.add_kv_lora_rank(hparams["kv_lora_rank"]) + self.gguf_writer.add_key_length(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) + self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + rope_scaling = self.find_hparam(['rope_scaling'], True) + if rope_scaling is not None: + rope_dims = self.hparams["qk_rope_head_dim"] + + long_factors = rope_scaling.get('long_factor', None) + short_factors = rope_scaling.get('short_factor', None) + + if long_factors is None or short_factors is None: + raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') + + if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: + raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}') + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: + if n_kv_head is not None and n_head != n_kv_head: + n_head //= n_kv_head + + return ( + weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape) + ) + + +# MiniCPM-V 4.6: text tower is Qwen3.5 (linear+full hybrid attention) wrapped under +# `model.language_model.*`; vision tower is SigLIP + a window-attention ViT merger +# + a final DownsampleMLP merger. The same HF arch is registered twice below: once as +# the LM (text mode) and once as the mmproj (vision mode), mirroring the Qwen3-VL setup. + +@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") +class MiniCPMV4_6TextModel(Qwen3_5TextModel): + model_arch = gguf.MODEL_ARCH.QWEN35 + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("model.merger."): + return None + # MTP tensors are not used at inference yet; align with Qwen3Next behaviour + if name.startswith("mtp"): + return None + + return super().filter_tensors(item) + + +@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") +class MiniCPMV4_6VisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams_vision is not None: + # In MiniCPM-V 4.6 `vision_config.image_size` (980) describes the SigLIP + # positional embedding bucket grid (70 x 70), while the per-slice processing + # resolution is the preprocessor's `scale_resolution` (typically 448). + # The CLIP loader in tools/mtmd/clip.cpp consumes `clip.vision.image_size` + # as the slice size and warmup resolution, so report `scale_resolution` there + # to match the upstream MiniCPMV4_6ImageProcessorPil slicing rules. + scale_resolution = self.preprocessor_config.get("scale_resolution") + if scale_resolution is not None: + self.hparams_vision["image_size"] = int(scale_resolution) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + + # projector type string is consumed by clip_projector_type_from_string() in clip.cpp + # (mapped to PROJECTOR_TYPE_MINICPMV4_6). + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MINICPMV4_6) + + # ViT merger 2x2 + final merger 2x2 = 4x spatial merge per dimension; used for slice alignment + self.gguf_writer.add_vision_projector_scale_factor(4) + + # borrow wa_layer_indexes for vit_merger insertion point + insert_layer_id = int(self.global_config.get( + "insert_layer_id", self.hparams_vision.get("insert_layer_id", 6))) + self.gguf_writer.add_vision_wa_layer_indexes([insert_layer_id]) + + # SigLIP vision body uses gelu_pytorch_tanh, which matches ggml_gelu (tanh approx). + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_attention_layernorm_eps( + self.hparams_vision.get("layer_norm_eps", 1e-6)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # lm_head / MTP -> belong to the LM file + if name.startswith(("lm_head.", "mtp")): + return None + + return super().filter_tensors(item) diff --git a/conversion/minimax.py b/conversion/minimax.py new file mode 100644 index 00000000000..4857775cbfb --- /dev/null +++ b/conversion/minimax.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("MiniMaxM2ForCausalLM") +class MiniMaxM2Model(TextModel): + model_arch = gguf.MODEL_ARCH.MINIMAXM2 + _experts_cache: dict[int, dict[str, Tensor]] = {} + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_expert_feed_forward_length(self.find_hparam(["intermediate_size"])) + self.gguf_writer.add_rope_dimension_count(self.find_hparam(["rotary_dim"])) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): + # merge expert weights + if 'experts' in name: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + expert_cache = self._experts_cache.setdefault(bid, {}) + expert_cache[name] = data_torch + expert_weights = ["w1", "w2", "w3"] + + # not enough expert weights to merge + if len(expert_cache) < n_experts * len(expert_weights): + return + + for w_name in expert_weights: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" + datas.append(expert_cache[ename]) + del expert_cache[ename] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" + new_name = self.map_tensor_name(merged_name) + yield from super().modify_tensors(data_torch, new_name, bid) + + del self._experts_cache[bid] + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/mistral.py b/conversion/mistral.py new file mode 100644 index 00000000000..7a7d6e03933 --- /dev/null +++ b/conversion/mistral.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MistralTokenizerType, MistralVocab, _mistral_common_installed, _mistral_import_error_msg, gguf, logger + +from .deepseek import DeepseekV2Model +from .llama import LlamaModel + +if _mistral_common_installed: + from mistral_common.tokens.tokenizers.base import TokenizerVersion # type: ignore[import-not-found, ty:unresolved-import] + from mistral_common.tokens.tokenizers.tekken import Tekkenizer # type: ignore[import-not-found, ty:unresolved-import] + from mistral_common.tokens.tokenizers.sentencepiece import SentencePieceTokenizer # type: ignore[import-not-found, ty:unresolved-import] +else: + TokenizerVersion = None # type: ignore[assignment] + Tekkenizer = None # type: ignore[assignment] + SentencePieceTokenizer = None # type: ignore[assignment] + + +class MistralModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.MISTRAL3 + model_name = "Mistral" + hf_arch = "" + is_mistral_format = True + undo_permute = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # for compatibility, we use LLAMA arch for older models + # TODO: remove this once everyone migrates to newer version of llama.cpp + if "llama_4_scaling" not in self.hparams: + self.model_arch = gguf.MODEL_ARCH.LLAMA + self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[self.model_arch] + self.gguf_writer.add_architecture() + self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) + + def dequant_model(self): + # transform quantization config into HF format + quant_config = self.hparams.get("quantization") + if quant_config is not None: + assert quant_config["qformat_weight"] == "fp8_e4m3" + self.hparams["quantization_config"] = { + "activation_scheme": "static", + "quant_method": "fp8", + "weight_block_size": None, + } + return super().dequant_model() + + @staticmethod + def get_community_chat_template(vocab: MistralVocab, templates_dir: Path, is_mistral_format: bool): + assert TokenizerVersion is not None and Tekkenizer is not None and SentencePieceTokenizer is not None, _mistral_import_error_msg + assert isinstance(vocab.tokenizer, (Tekkenizer, SentencePieceTokenizer)), ( + f"Expected Tekkenizer or SentencePieceTokenizer, got {type(vocab.tokenizer)}" + ) + + if vocab.tokenizer.version == TokenizerVersion.v1: + return "mistral-v1" + elif vocab.tokenizer.version == TokenizerVersion.v3 and vocab.tokenizer_type == MistralTokenizerType.spm: + return "mistral-v3" + elif vocab.tokenizer.version == TokenizerVersion.v3 and vocab.tokenizer_type == MistralTokenizerType.tekken: + return "mistral-v3-tekken" + elif vocab.tokenizer.version == TokenizerVersion.v7 and vocab.tokenizer_type == MistralTokenizerType.spm: + return "mistral-v7" + elif vocab.tokenizer.version == TokenizerVersion.v7 and vocab.tokenizer_type == MistralTokenizerType.tekken: + return "mistral-v7-tekken" + elif vocab.tokenizer.version == TokenizerVersion.v11: + template_file = "Mistral-Small-3.2-24B-Instruct-2506.jinja" + elif vocab.tokenizer.version == TokenizerVersion.v13: + template_file = "unsloth-mistral-Devstral-Small-2507.jinja" + else: + err_message = f"Unknown tokenizer type: {vocab.tokenizer_type} and version {vocab.tokenizer.version}" + if is_mistral_format: + err_message += ( + " . Please pass --disable-mistral-community-chat-template argument to the CLI " + "if you want to skip this error and use the Mistral official `mistral-common` pre-processing library." + ) + raise ValueError(err_message) + + template_path = templates_dir / template_file + if not template_path.exists(): + raise FileNotFoundError(f"Template file not found: {template_path}") + + with open(template_path, "r", encoding="utf-8") as f: + template = f.read() + + return template + + def set_gguf_parameters(self): + super().set_gguf_parameters() + MistralModel.set_mistral_config(self.gguf_writer, self.hparams) + + @staticmethod + def set_mistral_config(gguf_writer: gguf.GGUFWriter, hparams: dict): + if "yarn" in hparams: + yarn_params = hparams["yarn"] + mscale_all_dim = 1.0 if not yarn_params["apply_scale"] else 0.0 + gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.YARN) + gguf_writer.add_rope_scaling_factor(yarn_params["factor"]) + gguf_writer.add_rope_scaling_yarn_beta_fast(yarn_params["beta"]) + gguf_writer.add_rope_scaling_yarn_beta_slow(yarn_params["alpha"]) + gguf_writer.add_rope_scaling_yarn_log_mul(mscale_all_dim) + gguf_writer.add_rope_scaling_orig_ctx_len(yarn_params["original_max_position_embeddings"]) + + if "llama_4_scaling" in hparams: + gguf_writer.add_attn_temperature_scale(hparams["llama_4_scaling"]["beta"]) + + +class MistralMoeModel(DeepseekV2Model): + model_arch = gguf.MODEL_ARCH.DEEPSEEK2 + model_name = "Mistral" + hf_arch = "" + is_mistral_format = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + logger.info("Using MistralMoeModel") + # remap hparams from Mistral MoE format to DeepseekV2 format + # we do this way to be able to reuse DeepseekV2Model set_gguf_parameters logic + # ref: https://github.com/vllm-project/vllm/blob/b294e28db2c5dee61bc25157664edcada8b90b31/vllm/transformers_utils/configs/mistral.py + config = self.hparams + # Mistral key -> HF key + config_mapping = { + "dim": "hidden_size", + "norm_eps": "rms_norm_eps", + "n_kv_heads": "num_key_value_heads", + "n_layers": "num_hidden_layers", + "n_heads": "num_attention_heads", + "hidden_dim": "intermediate_size", + } + # HF key -> (Mistral key, default value) + top_level_mapping_with_default = { + "model_type": ("model_type", "transformer"), + "hidden_act": ("activation", "silu"), + "tie_word_embeddings": ("tied_embeddings", False), + "max_seq_len": ("max_seq_len", config.get("max_position_embeddings", 128_000)), + "max_position_embeddings": ("max_position_embeddings", 128_000), + } + # mapping top-level keys + for key, new_key in config_mapping.items(): + if key in config: + config[new_key] = config[key] + for new_key, (key, default_value) in top_level_mapping_with_default.items(): + config[new_key] = config.get(key, default_value) + # mapping MoE-specific keys + moe_config_map = { + "route_every_n": "moe_layer_freq", + "first_k_dense_replace": "first_k_dense_replace", + "num_experts_per_tok": "num_experts_per_tok", + "num_experts": "n_routed_experts", + "expert_hidden_dim": "moe_intermediate_size", + "routed_scale": "routed_scaling_factor", + "num_shared_experts": "n_shared_experts", + "num_expert_groups": "n_group", + "num_expert_groups_per_tok": "topk_group", + } + moe = config["moe"] + for key, new_key in moe_config_map.items(): + if key in moe: + config[new_key] = moe[key] + # provide missing values + config["topk_method"] = None + config["norm_topk_prob"] = True + config["scoring_func"] = "softmax" + + def set_vocab(self): + self._set_vocab_mistral() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + MistralModel.set_mistral_config(self.gguf_writer, self.hparams) + yarn_params = self.hparams["yarn"] + self.gguf_writer.add_attn_temperature_length(yarn_params["original_max_position_embeddings"]) + + # [TAG_DEEPSEEK2_YARN_LOG_MUL_FIX] + # note: for legacy reasons, this is not consistent with the other usages of self.gguf_writer.add_rope_scaling_yarn_log_mul + # ref https://github.com/ggml-org/llama.cpp/pull/17945 + self.gguf_writer.add_rope_scaling_yarn_log_mul(0.1) # mscale_all_dim * 0.1 + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # rename certain tensors so that we can reuse DeepseekV2Model modify_tensors logic + if name.endswith(".qscale_act"): + name = name.replace(".qscale_act", ".input_scale") + if name.endswith(".qscale_weight"): + name = name.replace(".qscale_weight", ".weight_scale") + if ".wkv_b." in name: + name = name.replace(".wkv_b.", ".kv_b_proj.") + if ".experts." in name: + name = name.replace(".experts.", ".mlp.experts.") + name = name.replace(".w1.", ".gate_proj.") + name = name.replace(".w2.", ".down_proj.") + name = name.replace(".w3.", ".up_proj.") + name = "model." + name + + return super().filter_tensors((name, gen)) diff --git a/conversion/mistral3.py b/conversion/mistral3.py new file mode 100644 index 00000000000..af9438ae705 --- /dev/null +++ b/conversion/mistral3.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + +from .deepseek import DeepseekV2Model +from .llama import LlamaModel + + +@ModelBase.register( + "Mistral3ForConditionalGeneration", + "Ministral3ForCausalLM", +) +class Mistral3Model(TextModel): + class Ministral3Model(LlamaModel): + model_arch = gguf.MODEL_ARCH.MISTRAL3 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + rope_params = self.rope_parameters + if self.hparams.get("model_type") == "ministral3": + assert rope_params, "ministral3 must have 'rope_parameters' config" + assert rope_params["rope_type"] == "yarn", "ministral3 rope_type must be 'yarn'" + self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"]) + self.gguf_writer.add_attn_temperature_scale(rope_params["llama_4_scaling_beta"]) + + class Mistral4Model(DeepseekV2Model): + model_arch = gguf.MODEL_ARCH.MISTRAL4 + skip_mtp = False # model contains no MTP layers, so no need to skip + merge_expert = False # experts are already stacked as 3D + + def modify_tensors(self, data_torch, name, bid): + if name.endswith(".down_proj") or name.endswith(".gate_up_proj"): + name = name + ".weight" + yield from super().modify_tensors(data_torch, name, bid) + + model_arch = gguf.MODEL_ARCH.MISTRAL3 # unused + impl: TextModel + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams.get("model_type") == "mistral4": + self.impl = Mistral3Model.Mistral4Model(*args, **kwargs) + else: + self.impl = Mistral3Model.Ministral3Model(*args, **kwargs) + + def set_vocab(self): + self.impl.set_vocab() + + def set_gguf_parameters(self): + self.impl.set_gguf_parameters() + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): + yield from self.impl.modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + self.impl.prepare_tensors() + + def write_vocab(self): + self.impl.write_vocab() + + def write(self): + self.impl.write() diff --git a/conversion/mpt.py b/conversion/mpt.py new file mode 100644 index 00000000000..9557ab7fa64 --- /dev/null +++ b/conversion/mpt.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("MPTForCausalLM") +class MPTModel(TextModel): + model_arch = gguf.MODEL_ARCH.MPT + + def set_vocab(self): + try: + self._set_vocab_gpt2() + except Exception: + # Fallback for SEA-LION model + self._set_vocab_sentencepiece() + self.gguf_writer.add_add_bos_token(False) + self.gguf_writer.add_pad_token_id(3) + self.gguf_writer.add_eos_token_id(1) + self.gguf_writer.add_unk_token_id(0) + + def set_gguf_parameters(self): + self.gguf_writer.add_context_length(self.hparams["max_seq_len"]) + self.gguf_writer.add_embedding_length(self.hparams["d_model"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(4 * self.hparams["d_model"]) + self.gguf_writer.add_head_count(self.hparams["n_heads"]) + if kv_n_heads := self.hparams["attn_config"].get("kv_n_heads"): + self.gguf_writer.add_head_count_kv(kv_n_heads) + self.gguf_writer.add_layer_norm_eps(1e-5) + if self.hparams["attn_config"]["clip_qkv"] is not None: + self.gguf_writer.add_clamp_kqv(self.hparams["attn_config"]["clip_qkv"]) + if self.hparams["attn_config"]["alibi"]: + self.gguf_writer.add_max_alibi_bias(self.hparams["attn_config"]["alibi_bias_max"]) + else: + self.gguf_writer.add_max_alibi_bias(0.0) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "scales" in name: + new_name = self.map_tensor_name(name, try_suffixes=(".weight", ".bias", ".scales")) + new_name = new_name.replace("scales", "act.scales") + else: + new_name = self.map_tensor_name(name, try_suffixes=(".weight", ".bias")) + + yield from super().modify_tensors(data_torch, new_name, bid) diff --git a/conversion/nemotron.py b/conversion/nemotron.py new file mode 100644 index 00000000000..dfeeb978582 --- /dev/null +++ b/conversion/nemotron.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf, logger + +from .granite import GraniteHybridModel + + +@ModelBase.register( + "NemotronH_Nano_VL_V2", + "RADIOModel", +) +class NemotronNanoV2VLModel(MmprojModel): + # ViT-Huge architecture parameters for RADIO v2.5-h + _vit_hidden_size = 1280 + _vit_intermediate_size = 5120 + _vit_num_layers = 32 + _vit_num_heads = 16 + + def get_vision_config(self) -> dict[str, Any] | None: + # RADIO config doesn't have standard ViT parameters, so they need to be constructed manually + vision_config = self.global_config.get("vision_config") + if vision_config is None: + return None + # Add ViT-H parameters + vision_config = { + **vision_config, + "hidden_size": self._vit_hidden_size, + "intermediate_size": self._vit_intermediate_size, + "num_hidden_layers": self._vit_num_layers, + "num_attention_heads": self._vit_num_heads, + "image_size": self.global_config.get("force_image_size", 512), + } + return vision_config + + def set_gguf_parameters(self): + if "image_mean" not in self.preprocessor_config: + self.preprocessor_config["image_mean"] = [0.485, 0.456, 0.406] + if "image_std" not in self.preprocessor_config: + self.preprocessor_config["image_std"] = [0.229, 0.224, 0.225] + + super().set_gguf_parameters() + hparams = self.global_config + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.NEMOTRON_V2_VL) + self.gguf_writer.add_vision_attention_layernorm_eps(1e-6) + self.gguf_writer.add_vision_use_gelu(True) + downsample_ratio = hparams.get("downsample_ratio", 0.5) + self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / downsample_ratio)) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".position_embd." in new_name or "pos_embed" in new_name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if "input_conditioner" in name: + return None + + # mtmd does not support video yet so skip tensors related to video. + if "radio_model.model.patch_generator.video_embedder" in name: + return None + + if not name.startswith("vision_model.radio_model.model.") and not name.startswith("mlp1."): + return None + + if "patch_generator.pos_embed" in name: + if not name.endswith(".weight"): + name += ".weight" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # RADIO's pos_embed doesn't have .weight suffix, but clip.cpp expects it + if "patch_generator.pos_embed" in name: + # Downsample position embeddings for fixed 512x512 image size + import torch.nn.functional as F + n_embd = self.hparams["hidden_size"] + image_size = self.global_config.get("force_image_size", 512) + patch_size = self.hparams["patch_size"] + target_patches_per_side = image_size // patch_size # 32 + max_patches_per_side = int((data_torch.shape[1]) ** 0.5) # 128 + if target_patches_per_side != max_patches_per_side: + # Reshape to grid, interpolate, flatten back + data_torch = data_torch.reshape(1, max_patches_per_side, max_patches_per_side, n_embd) + data_torch = data_torch.permute(0, 3, 1, 2).float() # [1, n_embd, 128, 128] + data_torch = F.interpolate(data_torch, size=(target_patches_per_side, target_patches_per_side), + mode='bilinear', align_corners=True) + data_torch = data_torch.permute(0, 2, 3, 1) # [1, 32, 32, n_embd] + data_torch = data_torch.reshape(1, target_patches_per_side * target_patches_per_side, n_embd) + + # Reshape linear patch embedding to conv2d format for ggml_conv_2d + # From [n_embd, patch_size*patch_size*3] to [n_embd, 3, patch_size, patch_size] + if "patch_generator.embedder" in name: + patch_size = self.hparams["patch_size"] + n_embd = self.hparams["hidden_size"] + data_torch = data_torch.reshape(n_embd, 3, patch_size, patch_size) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("NemotronForCausalLM") +class NemotronModel(TextModel): + model_arch = gguf.MODEL_ARCH.NEMOTRON + + def set_vocab(self): + self._set_vocab_sentencepiece() + self.gguf_writer.add_pad_token_id(0) + self.gguf_writer.add_unk_token_id(1) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + f_norm_eps = self.find_hparam(["layer_norm_eps", "layer_norm_epsilon", "norm_epsilon", "norm_eps"]) + self.gguf_writer.add_layer_norm_eps(f_norm_eps) + + # * Partial RoPE + rot_pct = self.find_hparam(["partial_rotary_factor", "rope_pct", "rope_percent"]) + n_embd = self.find_hparam(["hidden_size", "n_embd"]) + n_head = self.find_hparam(["num_attention_heads", "n_head"]) + self.gguf_writer.add_rope_dimension_count(int(rot_pct * n_embd) // n_head) + + # * RopeScaling for Nemotron + if "rope_scaling" not in self.hparams or self.hparams["rope_scaling"] is None: + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) + else: + self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) + self.gguf_writer.add_rope_scaling_factor(self.hparams["factor"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # * Adding +1 to LayerNorm's weights here to implement layernorm1p w/o changing anything on the GGML engine side + # model.layers.{l}.input_layernorm.weight + # model.layers.{l}.post_attention_layernorm.weight + # model.norm.weight + if name.endswith("norm.weight"): + data_torch = data_torch + 1 + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("NemotronHForCausalLM") +class NemotronHModel(GraniteHybridModel): + """Hybrid mamba2/attention model from NVIDIA""" + model_arch = gguf.MODEL_ARCH.NEMOTRON_H + is_moe: bool = False + + def __init__(self, *args, **kwargs): + # We have to determine the correct model architecture (MoE vs non-MoE) before + # calling the parent __init__. This is because the parent constructor + # uses self.model_arch to build the tensor name map, and all MoE-specific + # mappings would be missed if it were called with the default non-MoE arch. + hparams = ModelBase.load_hparams(args[0], self.is_mistral_format) + has_moe_params = ( + "num_experts_per_tok" in hparams + or (isinstance(hparams.get("llm_config"), dict) and "num_experts_per_tok" in hparams["llm_config"]) + ) + if has_moe_params: + self.model_arch = gguf.MODEL_ARCH.NEMOTRON_H_MOE + self.is_moe = True + + super().__init__(*args, **kwargs) + + # Save the top-level head_dim for later + self.head_dim = self.hparams.get("head_dim", self.hparams.get("attention_head_dim")) + assert self.head_dim is not None, "Could not find the attention head dim in config" + + # Don't use expand to calculate d_inner + self.d_inner = self.find_hparam(["num_heads"]) * self.d_model + + # Update the ssm / attn / mlp layers + # M: Mamba2, *: Attention, -: MLP + # MoE: + # M: Mamba2, *: Attention, E: Expert + pattern = self.hparams.get("hybrid_override_pattern") or self.hparams.get("layers_block_type") + if pattern is None: + self._ssm_layers = [] + self._mlp_layers = [] + elif isinstance(pattern, str): + self._ssm_layers = [i for i, val in enumerate(pattern) if val == "M"] + self._mlp_layers = [i for i, val in enumerate(pattern) if val == ("E" if self.is_moe else "-")] + else: + self._ssm_layers = [i for i, val in enumerate(pattern) if val == "mamba"] + self._mlp_layers = [i for i, val in enumerate(pattern) if val == "moe"] + + def get_attn_layers(self): + pattern = self.hparams.get("hybrid_override_pattern") or self.hparams.get("layers_block_type") + if pattern is None: + return [] + assert len(pattern) == self.block_count, f"Mismatch between pattern ({len(pattern)}) and block_count ({self.block_count})!" + if isinstance(pattern, str): + return [i for i, val in enumerate(pattern) if val == "*"] + + return [i for i, val in enumerate(pattern) if val == "attention"] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + head_dim = self.head_dim + if head_dim is None: + raise ValueError("Could not find the attention head dim in config") + self.gguf_writer.add_key_length(head_dim) + self.gguf_writer.add_value_length(head_dim) + + # Set feed_forward_length + # NOTE: This will trigger an override warning. This is preferable to + # duplicating all the parent logic + if not self.is_moe: + n_ff = self.find_hparam(["intermediate_size", "n_inner", "hidden_dim"]) + self.gguf_writer.add_feed_forward_length([ + n_ff if i in self._mlp_layers else 0 for i in range(self.block_count) + ]) + else: + moe_intermediate_size = self.hparams["moe_intermediate_size"] + self.gguf_writer.add_feed_forward_length([ + moe_intermediate_size if i in self._mlp_layers else 0 for i in range(self.block_count) + ]) + self.gguf_writer.add_expert_used_count(self.hparams["num_experts_per_tok"]) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) + self.gguf_writer.add_expert_shared_feed_forward_length(self.hparams["moe_shared_expert_intermediate_size"]) + self.gguf_writer.add_expert_count(self.hparams["n_routed_experts"]) + self.gguf_writer.add_expert_shared_count(self.hparams["n_shared_experts"]) + self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) + self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) + self.gguf_writer.add_expert_group_count(self.hparams["n_group"]) + + # number of experts used per token (top-k) + if (n_experts_used := self.hparams.get("num_experts_per_tok")) is not None: + self.gguf_writer.add_expert_used_count(n_experts_used) + + if (latent_size := self.hparams.get("moe_latent_size")) is not None: + self.gguf_writer.add_moe_latent_size(latent_size) + + def set_vocab(self): + # The NemotronH config uses pattern characters (e.g. '-') that may not + # be supported by the installed transformers version. AutoTokenizer + # internally calls AutoConfig which triggers this parsing failure. + # Using trust_remote_code=True to load the model's own config class. + tokens: list[str] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) + + # Pad vocab size (from Mamba2Model/GraniteHybridModel) + self.hparams["pad_vocab_size_multiple"] = 8 # Setting this here since GraniteHybridModel.set_vocab() isn't being invoked now. + # From Mamba2Model.set_vocab(): + vocab_size = self.hparams["vocab_size"] + pad_vocab = self.hparams.get("pad_vocab_size_multiple", 16) + # ref: https://stackoverflow.com/a/17511341/22827863 + vocab_size = -(vocab_size // -pad_vocab) * pad_vocab + self.hparams["vocab_size"] = vocab_size + + assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute] + + tokpre = self.get_vocab_base_pre(tokenizer) + + reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] + + for i in range(vocab_size): + if i not in reverse_vocab: + tokens.append(f"[PAD{i}]") + toktypes.append(gguf.TokenType.UNUSED) + else: + token: str = reverse_vocab[i] + if token in added_vocab: + if not added_tokens_decoder[i].normalized: + previous_token = token + token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] + if previous_token != token: + logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") + + if added_tokens_decoder[i].special or self.does_token_look_special(token): + toktypes.append(gguf.TokenType.CONTROL) + else: + token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces + toktypes.append(gguf.TokenType.USER_DEFINED) + else: + toktypes.append(gguf.TokenType.NORMAL) + tokens.append(token) + + # From TextModel.set_vocab_gpt2(): + self.gguf_writer.add_tokenizer_model("gpt2") + self.gguf_writer.add_tokenizer_pre(tokpre) + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) + special_vocab.add_to_gguf(self.gguf_writer) + + # The tokenizer _does_ add a BOS token (via post_processor type + # TemplateProcessing) but does not set add_bos_token to true in the + # config, so we need to explicitly override it here. + if not self.is_moe: + self.gguf_writer.add_add_bos_token(True) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.is_moe and bid is not None: + # Skip Multi-Token Prediction (MTP) tensors. These are used for + # for speculative decoding but we don't include them in this model + # conversion. See https://github.com/ggml-org/llama.cpp/pull/18886 + if name.startswith("mtp."): + logger.info(f"gguf: Skipping MTP (Speculative) layer: {name}") + return + + if name.endswith("mixer.gate.e_score_correction.bias"): + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + return + + if name.endswith("mixer.dt_bias"): + new_name = name.replace("dt_bias", "dt.bias") + yield from ModelBase.modify_tensors(self, data_torch, new_name, bid) + return + + if name.endswith("mixer.conv1d.weight"): + squeezed_data = data_torch.squeeze() + yield from ModelBase.modify_tensors(self, squeezed_data, name, bid) + return + + if name.endswith("mixer.A_log"): + transformed_data = -torch.exp(data_torch) + reshaped_data = transformed_data.squeeze().reshape(-1, 1) + yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) + return + + if name.endswith("mixer.D"): + reshaped_data = data_torch.squeeze().reshape(-1, 1) + yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) + return + + if name.endswith("mixer.norm.weight"): + reshaped_data = data_torch.reshape(self.n_group, -1) + yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) + return + + if name.find("mixer.experts") != -1: + n_experts = self.hparams["n_routed_experts"] + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 2: + # merge the experts into a single tensor + for w_name in ["down_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"backbone.layers.{bid}.mixer.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from ModelBase.modify_tensors(self, data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/olmo.py b/conversion/olmo.py new file mode 100644 index 00000000000..1664c30e402 --- /dev/null +++ b/conversion/olmo.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + +from .llama import LlamaModel + + +@ModelBase.register("OlmoForCausalLM") +@ModelBase.register("OLMoForCausalLM") +class OlmoModel(TextModel): + model_arch = gguf.MODEL_ARCH.OLMO + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_layer_norm_eps(1e-5) + clip_qkv = self.hparams.get("clip_qkv") + if clip_qkv is not None: + self.gguf_writer.add_clamp_kqv(clip_qkv) + + # Same as super class, but permuting q_proj, k_proj + # Copied from: LlamaModel + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams.get("num_key_value_heads") + + if name.endswith("q_proj.weight"): + data_torch = LlamaModel.permute(data_torch, n_head, n_head) + if name.endswith("k_proj.weight"): + data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("SeedOssForCausalLM") +class SeedOssModel(TextModel): + model_arch = gguf.MODEL_ARCH.SEED_OSS + + +@ModelBase.register("Olmo2ForCausalLM") +@ModelBase.register("Olmo3ForCausalLM") +class Olmo2Model(TextModel): + model_arch = gguf.MODEL_ARCH.OLMO2 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + if "sliding_window" in self.hparams: + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + + sliding_window_pattern = [] + if "layer_types" in self.hparams: + sliding_window_pattern = [t == "sliding_attention" for t in self.hparams["layer_types"]] + else: + # Olmo2 does not use sliding window attention. + # Olmo3 defaults to using sliding window for all layers except every 4th. + for i in range(self.hparams["num_hidden_layers"]): + sliding_window_pattern.append((i + 1) % 4 != 0) + + self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) + + +@ModelBase.register("OlmoeForCausalLM") +class OlmoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.OLMOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_layer_norm_rms_eps(1e-5) + + _experts: list[dict[str, Tensor]] | None = None + + # Copied from: Qwen2MoeModel + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # process the experts separately + if name.find("experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + # Copied from: Qwen2MoeModel + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/openelm.py b/conversion/openelm.py new file mode 100644 index 00000000000..ecc746dc348 --- /dev/null +++ b/conversion/openelm.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +from typing import Any, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("OpenELMForCausalLM") +class OpenELMModel(TextModel): + model_arch = gguf.MODEL_ARCH.OPENELM + + @staticmethod + def _make_divisible(v: float | int, divisor: int) -> int: + # ref: https://huggingface.co/apple/OpenELM-270M-Instruct/blob/eb111ff2e6724348e5b905984063d4064d4bc579/configuration_openelm.py#L34-L38 + new_v = max(divisor, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + ffn_multipliers: list[float] = self.hparams["ffn_multipliers"] + ffn_dim_divisor: int = self.hparams["ffn_dim_divisor"] + self._n_embd: int = self.hparams["model_dim"] + self._num_kv_heads: list[int] = self.hparams["num_kv_heads"] + self._num_query_heads: list[int] = self.hparams["num_query_heads"] + self._ffn_dims: list[int] = [ + OpenELMModel._make_divisible(multiplier * self._n_embd, ffn_dim_divisor) + for multiplier in ffn_multipliers + ] + assert isinstance(self._num_kv_heads, list) and isinstance(self._num_kv_heads[0], int) + assert isinstance(self._num_query_heads, list) and isinstance(self._num_query_heads[0], int) + + # Uses the tokenizer from meta-llama/Llama-2-7b-hf + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_builtin("llama-spm", self.hparams["vocab_size"]) + + def set_gguf_parameters(self): + n_embd = self._n_embd + head_dim = self.hparams["head_dim"] + rot_pct = 1.0 + assert self.block_count == len(self._num_kv_heads) + assert self.block_count == len(self._num_query_heads) + assert self.block_count == len(self._ffn_dims) + + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_context_length(self.hparams["max_context_length"]) + self.gguf_writer.add_embedding_length(n_embd) + self.gguf_writer.add_feed_forward_length(self._ffn_dims) + self.gguf_writer.add_head_count(self._num_query_heads) + self.gguf_writer.add_head_count_kv(self._num_kv_heads) + self.gguf_writer.add_rope_freq_base(self.hparams["rope_freq_constant"]) + # https://huggingface.co/apple/OpenELM-270M-Instruct/blob/c401df2/modeling_openelm.py#L30 + self.gguf_writer.add_layer_norm_rms_eps(1e-6) + self.gguf_writer.add_rope_dimension_count(int(rot_pct * head_dim)) + self.gguf_writer.add_key_length(head_dim) + self.gguf_writer.add_value_length(head_dim) + self.gguf_writer.add_file_type(self.ftype) + + def find_hparam(self, keys: Iterable[str], optional: bool = False) -> Any: + if "n_layers" in keys: + return self.hparams["num_transformer_layers"] + + return super().find_hparam(keys, optional) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + + # split ff + if bid is not None and name == f"transformer.layers.{bid}.ffn.proj_1.weight": + ff_dim = self._ffn_dims[bid] + yield (self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), data_torch[:ff_dim]) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), data_torch[ff_dim:]) + return + + yield (self.map_tensor_name(name), data_torch) diff --git a/conversion/orion.py b/conversion/orion.py new file mode 100644 index 00000000000..8dfceeed1f7 --- /dev/null +++ b/conversion/orion.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("OrionForCausalLM") +class OrionModel(TextModel): + model_arch = gguf.MODEL_ARCH.ORION + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def set_gguf_parameters(self): + head_count = self.hparams["num_attention_heads"] + head_count_kv = self.hparams.get("num_key_value_heads", head_count) + + ctx_length = 0 + if "max_sequence_length" in self.hparams: + ctx_length = self.hparams["max_sequence_length"] + elif "max_position_embeddings" in self.hparams: + ctx_length = self.hparams["max_position_embeddings"] + elif "model_max_length" in self.hparams: + ctx_length = self.hparams["model_max_length"] + else: + raise ValueError("gguf: can not find ctx length parameter.") + + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_tensor_data_layout("Meta AI original pth") + self.gguf_writer.add_context_length(ctx_length) + self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) + self.gguf_writer.add_head_count(head_count) + self.gguf_writer.add_head_count_kv(head_count_kv) + # note: config provides rms norm but it is actually layer norm + # ref: https://huggingface.co/OrionStarAI/Orion-14B-Chat/blob/276a17221ce42beb45f66fac657a41540e71f4f5/modeling_orion.py#L570-L571 + self.gguf_writer.add_layer_norm_eps(self.hparams["rms_norm_eps"]) diff --git a/conversion/pangu.py b/conversion/pangu.py new file mode 100644 index 00000000000..42016ba0286 --- /dev/null +++ b/conversion/pangu.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import json + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("PanguEmbeddedForCausalLM") +class PanguEmbeddedModel(TextModel): + model_arch = gguf.MODEL_ARCH.PANGU_EMBED + + def set_vocab(self): + self._set_vocab_sentencepiece() + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + if "add_prefix_space" in tokenizer_config_json: + self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + + # PanguEmbedded's hparam loaded from config.json without head_dim + if (rope_dim := hparams.get("head_dim")) is None: + rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(rope_dim) + + if hparams.get("head_dim") is None: + self.gguf_writer.add_key_length(rope_dim) + self.gguf_writer.add_value_length(rope_dim) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name == "lm_head.weight": + if self.hparams.get("tie_word_embeddings", False): + logger.info("Skipping tied output layer 'lm_head.weight'") + return + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/phi.py b/conversion/phi.py new file mode 100644 index 00000000000..5e0d72847aa --- /dev/null +++ b/conversion/phi.py @@ -0,0 +1,390 @@ +from __future__ import annotations + +import json +import math + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, SentencePieceTokenTypes, TextModel, gguf, logger + + +@ModelBase.register("PhiForCausalLM") +class Phi2Model(TextModel): + model_arch = gguf.MODEL_ARCH.PHI2 + + def set_gguf_parameters(self): + rot_pct = self.find_hparam(["partial_rotary_factor"]) + n_embd = self.find_hparam(["hidden_size", "n_embd"]) + n_head = self.find_hparam(["num_attention_heads", "n_head"]) + + self.gguf_writer.add_context_length(self.find_hparam(["n_positions", "max_position_embeddings"])) + + self.gguf_writer.add_embedding_length(n_embd) + self.gguf_writer.add_feed_forward_length(4 * n_embd) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(n_head) + self.gguf_writer.add_head_count_kv(n_head) + self.gguf_writer.add_layer_norm_eps(self.find_hparam(["layer_norm_epsilon", "layer_norm_eps"])) + self.gguf_writer.add_rope_dimension_count(int(rot_pct * n_embd) // n_head) + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_add_bos_token(False) + + +@ModelBase.register("Phi3ForCausalLM", "Phi4ForCausalLMV") +class Phi3MiniModel(TextModel): + model_arch = gguf.MODEL_ARCH.PHI3 + + def set_vocab(self): + # Phi-4 model uses GPT2Tokenizer + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + tokenizer_class = tokenizer_config_json['tokenizer_class'] + if tokenizer_class == 'GPT2Tokenizer': + return self._set_vocab_gpt2() + + from sentencepiece import SentencePieceProcessor + + tokenizer_path = self.dir_model / 'tokenizer.model' + + if not tokenizer_path.is_file(): + raise ValueError(f'Error: Missing {tokenizer_path}') + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + for token_id in range(tokenizer.vocab_size()): + + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + added_tokens_file = self.dir_model / 'added_tokens.json' + if added_tokens_file.is_file(): + with open(added_tokens_file, "r", encoding="utf-8") as f: + added_tokens_json = json.load(f) + + for key in added_tokens_json: + token_id = added_tokens_json[key] + if token_id >= vocab_size: + logger.debug(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + + tokens[token_id] = key.encode("utf-8") + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + + tokenizer_config_file = self.dir_model / 'tokenizer_config.json' + if tokenizer_config_file.is_file(): + with open(tokenizer_config_file, "r", encoding="utf-8") as f: + tokenizer_config_json = json.load(f) + added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) + for token_id, foken_data in added_tokens_decoder.items(): + token_id = int(token_id) + token = foken_data["content"].encode("utf-8") + if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: + if tokens[token_id] != token: + logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') + tokens[token_id] = token + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + if foken_data.get("special"): + toktypes[token_id] = SentencePieceTokenTypes.CONTROL + + tokenizer_file = self.dir_model / 'tokenizer.json' + if tokenizer_file.is_file(): + with open(tokenizer_file, "r", encoding="utf-8") as f: + tokenizer_json = json.load(f) + added_tokens = tokenizer_json.get("added_tokens", []) + for foken_data in added_tokens: + token_id = int(foken_data["id"]) + token = foken_data["content"].encode("utf-8") + if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: + if tokens[token_id] != token: + logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') + tokens[token_id] = token + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + if foken_data.get("special"): + toktypes[token_id] = SentencePieceTokenTypes.CONTROL + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + n_embd = self.find_hparam(["hidden_size", "n_embd"]) + n_head = self.find_hparam(["num_attention_heads", "n_head"]) + n_head_kv = self.find_hparam(["num_key_value_heads", "n_head_kv"]) + rms_eps = self.find_hparam(["rms_norm_eps"]) + max_pos_embds = self.find_hparam(["n_positions", "max_position_embeddings"]) + orig_max_pos_embds = self.find_hparam(["original_max_position_embeddings"]) + rot_pct = self.hparams.get("partial_rotary_factor", 1.0) + rope_dims = int(rot_pct * n_embd) // n_head + + self.gguf_writer.add_context_length(max_pos_embds) + self.gguf_writer.add_rope_scaling_orig_ctx_len(orig_max_pos_embds) + self.gguf_writer.add_embedding_length(n_embd) + self.gguf_writer.add_feed_forward_length(self.find_hparam(["intermediate_size"])) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(n_head) + self.gguf_writer.add_head_count_kv(n_head_kv) + self.gguf_writer.add_layer_norm_rms_eps(rms_eps) + self.gguf_writer.add_rope_dimension_count(rope_dims) + self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("full_attention", self.rope_parameters)["rope_theta"]) + self.gguf_writer.add_file_type(self.ftype) + sliding_window = self.hparams.get("sliding_window") + # use zero value of sliding_window to distinguish Phi-4 from other PHI3 models + if sliding_window is None: + sliding_window = 0 + self.gguf_writer.add_sliding_window(sliding_window) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + n_embd = self.find_hparam(["hidden_size", "n_embd"]) + n_head = self.find_hparam(["num_attention_heads", "n_head"]) + max_pos_embds = self.find_hparam(["n_positions", "max_position_embeddings"]) + orig_max_pos_embds = self.find_hparam(["original_max_position_embeddings"]) + rot_pct = self.hparams.get("partial_rotary_factor", 1.0) + rope_dims = int(rot_pct * n_embd) // n_head + + # write rope scaling for long context (128k) model + rope_scaling = self.find_hparam(['rope_scaling'], True) + if rope_scaling is None: + return + + scale = max_pos_embds / orig_max_pos_embds + + rope_scaling_type = rope_scaling.get('rope_type', rope_scaling.get('type', '')).lower() + if len(rope_scaling_type) == 0: + raise KeyError('Missing the required key rope_scaling.type') + + if rope_scaling_type == 'su' or rope_scaling_type == 'longrope': + attn_factor = math.sqrt(1 + math.log(scale) / math.log(orig_max_pos_embds)) if scale > 1.0 else 1.0 + elif rope_scaling_type == 'yarn': + attn_factor = 0.1 * math.log(scale) + 1.0 if scale > 1.0 else 1.0 + else: + raise NotImplementedError(f'The rope scaling type {rope_scaling_type} is not supported yet') + + self.gguf_writer.add_rope_scaling_attn_factors(attn_factor) + + long_factors = rope_scaling.get('long_factor', None) + short_factors = rope_scaling.get('short_factor', None) + + if long_factors is None or short_factors is None: + raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') + + if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: + raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}. long_factors = {len(long_factors)}, short_factors = {len(short_factors)}.') + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) + + +@ModelBase.register("Phi4ForCausalLMV") +class Phi4VisionMmprojModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + + self.vision_total_layers = int(self.find_vparam(self.n_block_keys)) + if self.vision_total_layers < 2: + raise ValueError( + f"Phi-4 vision mmproj conversion requires at least 2 vision layers, got {self.vision_total_layers}" + ) + + # Phi-4 uses SigLIP2 hidden_states[-2], so export one fewer encoder block and + # drop post-layernorm/head weights. This makes the GGUF runtime output match + # the feature map consumed by the patched siglip.cpp Phi-4 projector path. + self.vision_export_layers = self.vision_total_layers - 1 + self.vision_last_layer_idx = self.vision_total_layers - 1 + + for key in self.n_block_keys: + if key in self.hparams_vision: + self.hparams_vision[key] = self.vision_export_layers + break + + self.block_count = self.vision_export_layers + self.tensor_map = gguf.get_tensor_name_map(gguf.MODEL_ARCH.MMPROJ, self.block_count) + + patch_size = self.preprocessor_config.get("patch_size") + if patch_size is None: + raise KeyError("Phi-4 vision mmproj conversion requires patch_size in preprocessor_config.json") + + self.hparams_vision["patch_size"] = patch_size + + pos_emb_name = next( + ( + name for name in self.model_tensors + if name.endswith("vision_model.embeddings.position_embedding.weight") + ), + None, + ) + if pos_emb_name is None: + raise KeyError("Phi-4 vision mmproj conversion could not find position_embedding.weight") + + pos_emb_shape = self.model_tensors[pos_emb_name]().shape + base_grid_tokens = int(pos_emb_shape[0]) + grid_side = math.isqrt(base_grid_tokens) + if grid_side * grid_side != base_grid_tokens: + raise ValueError(f"Unexpected Phi-4 position embedding shape: {tuple(pos_emb_shape)}") + + self.hparams_vision["image_size"] = grid_side * patch_size + + min_num_patches = self.preprocessor_config.get("min_num_patches", self.global_config.get("min_num_patches")) + max_num_patches = self.preprocessor_config.get("max_num_patches", self.global_config.get("max_num_patches")) + if min_num_patches is None or max_num_patches is None: + raise KeyError("Phi-4 vision mmproj conversion requires min_num_patches and max_num_patches") + + self.min_pixels = int(min_num_patches) * patch_size * patch_size + self.max_pixels = int(max_num_patches) * patch_size * patch_size + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PHI4) + self.gguf_writer.add_vision_min_pixels(self.min_pixels) + self.gguf_writer.add_vision_max_pixels(self.max_pixels) + self.gguf_writer.add_vision_use_gelu(True) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + name = name.replace("model.vision_tower.vision_tower.", "vision_tower.") + + if not name.startswith(("vision_tower.", "model.mm_projector.", "mm_projector.")): + return None + + if ".vision_model.head." in name: + return None + + if ".vision_model.post_layernorm." in name: + return None + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.startswith("vision_tower."): + if bid is not None and bid == self.vision_last_layer_idx: + return + + if name.endswith("vision_model.embeddings.patch_embedding.weight"): + assert self.hparams_vision is not None + if data_torch.ndim != 2: + raise ValueError(f"Unexpected Phi-4 patch embedding shape: {tuple(data_torch.shape)}") + + patch_area = self.hparams_vision["patch_size"] ** 2 + in_features = data_torch.shape[1] + if in_features % patch_area != 0: + raise ValueError( + f"Phi-4 patch embedding input dim {in_features} is not divisible by patch area {patch_area}" + ) + + num_channels = in_features // patch_area + patch_size = self.hparams_vision["patch_size"] + data_torch = data_torch.view(data_torch.shape[0], patch_size, patch_size, num_channels) + data_torch = data_torch.permute(0, 3, 1, 2) + + yield from super().modify_tensors(data_torch, name, bid) + return + + if name.startswith(("model.mm_projector.", "mm_projector.")): + local_name = name + local_name = local_name.replace("model.mm_projector.", "") + local_name = local_name.replace("mm_projector.", "") + + if not (local_name.startswith("0.") or local_name.startswith("2.")): + return + + suffix = ".bias" if local_name.endswith(".bias") else ".weight" + mm_idx = int(local_name.split(".", maxsplit=1)[0]) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, mm_idx, suffix=suffix), data_torch) + return + + return + + +@ModelBase.register("PhiMoEForCausalLM") +class PhiMoeModel(Phi3MiniModel): + model_arch = gguf.MODEL_ARCH.PHIMOE + + _experts: list[dict[str, Tensor]] | None = None + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_expert_used_count(self.find_hparam(["num_experts_per_tok", "num_experts_per_token"])) + self.gguf_writer.add_expert_count(self.find_hparam(["num_local_experts", "num_experts"])) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # process the experts separately + if name.find("block_sparse_moe.experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["w1", "w2", "w3"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/pixtral.py b/conversion/pixtral.py new file mode 100644 index 00000000000..acd9ce1cf70 --- /dev/null +++ b/conversion/pixtral.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import Sequence + +from .base import gguf + +from .llava import LlavaVisionModel + + +class PixtralModel(LlavaVisionModel): + model_name = "Pixtral" + hf_arch = "" + is_mistral_format = True + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PIXTRAL) + + self.gguf_writer.add_vision_attention_layernorm_eps( + self.find_hparam(["norm_eps"]) + ) + self.gguf_writer.add_rope_freq_base(self.find_vparam(["rope_theta"])) + + self.gguf_writer.add_vision_use_silu(True) + + # spatial_merge_size + if self.find_vparam(["mm_projector_id"], optional=True) == "patch_merge": + self.gguf_writer.add_vision_spatial_merge_size( + self.find_vparam(["spatial_merge_size"]) + ) + + def map_tensor_name(self, name: str, try_suffixes: Sequence[str] = (".weight", ".bias")) -> str: + if name == "vision_language_adapter.w_in.weight": + return "mm.1.weight" + elif name == "vision_language_adapter.w_in.bias": + return "mm.1.bias" + elif name == "vision_language_adapter.w_out.weight": + return "mm.2.weight" + elif name == "vision_language_adapter.w_out.bias": + return "mm.2.bias" + return super().map_tensor_name(name, try_suffixes) diff --git a/conversion/plamo.py b/conversion/plamo.py new file mode 100644 index 00000000000..c4bcbdf06bc --- /dev/null +++ b/conversion/plamo.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import json + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("PlamoForCausalLM") +class PlamoModel(TextModel): + model_arch = gguf.MODEL_ARCH.PLAMO + + def set_vocab(self): + self._set_vocab_sentencepiece() + + def set_gguf_parameters(self): + hparams = self.hparams + + self.gguf_writer.add_context_length(4096) # not in config.json + self.gguf_writer.add_embedding_length(hparams["hidden_size"]) + self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(5) # hparams["num_key_value_heads"]) is wrong + self.gguf_writer.add_layer_norm_rms_eps(hparams["rms_norm_eps"]) + self.gguf_writer.add_file_type(self.ftype) + + def shuffle_attn_q_weight(self, data_torch): + assert data_torch.size() == (5120, 5120) + data_torch = data_torch.reshape(8, 5, 128, 5120) + data_torch = torch.permute(data_torch, (1, 0, 2, 3)) + data_torch = torch.reshape(data_torch, (5120, 5120)) + return data_torch + + def shuffle_attn_output_weight(self, data_torch): + assert data_torch.size() == (5120, 5120) + data_torch = data_torch.reshape(5120, 8, 5, 128) + data_torch = torch.permute(data_torch, (0, 2, 1, 3)) + data_torch = torch.reshape(data_torch, (5120, 5120)) + return data_torch + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + new_name = self.map_tensor_name(name) + + # shuffle for broadcasting of gqa in ggml_mul_mat + if new_name.endswith("attn_q.weight"): + data_torch = self.shuffle_attn_q_weight(data_torch) + elif new_name.endswith("attn_output.weight"): + data_torch = self.shuffle_attn_output_weight(data_torch) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Plamo2ForCausalLM", "PLaMo2ForCausalLM") +class Plamo2Model(TextModel): + model_arch = gguf.MODEL_ARCH.PLAMO2 + + def set_vocab(self): + self._set_vocab_plamo() + + def set_gguf_parameters(self): + hparams = self.hparams + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + + # Which layers are Mamba layers + # PLaMo 2 uses mamba_step to indicate the pattern (e.g., 2 means every other layer) + # This logic matches modeling_plamo.py's is_mamba function + mamba_step = hparams.get("mamba_step", 2) + mamba_enabled = hparams.get("mamba_enabled", True) + num_key_value_heads = [] + num_attention_heads = [] + + if mamba_enabled: + for i in range(self.block_count): + if self.block_count <= (mamba_step // 2): + # use attention in last layer + is_mamba = (i != self.block_count - 1) + else: + is_mamba = (i % mamba_step) != (mamba_step // 2) + if is_mamba: + num_key_value_heads.append(0) + num_attention_heads.append(0) + else: + num_key_value_heads.append(hparams.get("num_key_value_heads", 4)) + num_attention_heads.append(hparams.get("num_attention_heads", 32)) + + if num_key_value_heads and num_attention_heads: + self.gguf_writer.add_head_count_kv(num_key_value_heads) + self.gguf_writer.add_head_count(num_attention_heads) + + self.gguf_writer.add_context_length(hparams.get("max_position_embeddings", 2048)) + self.gguf_writer.add_embedding_length(hparams.get("hidden_size", 4096)) + self.gguf_writer.add_key_length(hparams.get("hidden_size_per_head", 128)) + self.gguf_writer.add_value_length(hparams.get("hidden_size_per_head", 128)) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_layer_norm_rms_eps(hparams.get("rms_norm_eps", 1e-06)) + self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("rope_theta", 10000)) + + # Mamba parameters + self.gguf_writer.add_ssm_state_size(hparams.get("mamba_d_state", 64)) + self.gguf_writer.add_ssm_conv_kernel(hparams.get("mamba_d_conv", 4)) + self.gguf_writer.add_ssm_time_step_rank(hparams.get("mamba_num_heads", 64)) + intermediate_size = hparams.get("mamba_num_heads", 64) * hparams.get("hidden_size_per_head", 128) + self.gguf_writer.add_ssm_inner_size(intermediate_size) + self.gguf_writer.add_ssm_group_count(0) + + # MLP feed forward parameters (for attention layers) + self.gguf_writer.add_feed_forward_length(hparams.get("intermediate_size", 13312)) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.endswith(".A_log"): + data_torch = -torch.exp(data_torch) + elif name.endswith(".dt_bias"): + name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" + elif name.endswith(".dt_norm_weight"): + name = name.rpartition(".dt_norm_weight")[0] + ".dt_norm.weight" + elif name.endswith(".B_norm_weight"): + name = name.rpartition(".B_norm_weight")[0] + ".B_norm.weight" + elif name.endswith(".C_norm_weight"): + name = name.rpartition(".C_norm_weight")[0] + ".C_norm.weight" + elif name.endswith(".k_weight"): + name = name.rpartition(".k_weight")[0] + ".k.weight" + elif name.endswith(".q_weight"): + name = name.rpartition(".q_weight")[0] + ".q.weight" + elif name.endswith(".conv1d.weight"): + data_torch = torch.squeeze(data_torch) # remove (, 1, ) + assert data_torch.ndim == 2 + elif name.endswith(".pre_mixer_norm.weight"): + data_torch += 1.0 + elif name.endswith(".post_mixer_norm.weight"): + data_torch += 1.0 / 5 + elif name.endswith(".pre_mlp_norm.weight"): + data_torch += 1.0 + elif name.endswith(".post_mlp_norm.weight"): + data_torch += 1.0 / (5**1.5) + elif name.endswith(".norm.weight"): + data_torch += 1.0 + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Plamo3ForCausalLM", "PLaMo3ForCausalLM") +class Plamo3Model(TextModel): + model_arch = gguf.MODEL_ARCH.PLAMO3 + + def set_vocab(self): + self._set_vocab_plamo() + + tokenizer_config_path = self.dir_model / "tokenizer_config.json" + tokenizer_config = {} + + if tokenizer_config_path.is_file(): + with open(tokenizer_config_path, encoding="utf-8") as f: + tokenizer_config = json.load(f) + + chat_template = tokenizer_config.get("chat_template") + chat_template_jinja = self.dir_model / "chat_template.jinja" + + if chat_template_jinja.is_file(): + with open(chat_template_jinja, encoding="utf-8") as f: + chat_template = f.read() + + if chat_template: + self.gguf_writer.add_chat_template(chat_template) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) + if (sliding_window := self.find_hparam(["window_size", "sliding_window"], optional=True)) is not None: + self.gguf_writer.add_sliding_window(sliding_window) + self.gguf_writer.add_sliding_window_pattern(self.hparams["sliding_window_pattern"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + + if name.endswith(".pre_mixer_norm.weight"): + data_torch = data_torch + 1.0 + elif name.endswith(".post_mixer_norm.weight"): + data_torch = data_torch + 1.0 / 5 + elif name.endswith(".pre_mlp_norm.weight"): + data_torch = data_torch + 1.0 + elif name.endswith(".post_mlp_norm.weight"): + data_torch = data_torch + 1.0 / (5**1.5) + elif name.endswith((".mixer.q_norm.weight", ".mixer.k_norm.weight")): + data_torch = data_torch + 1.0 + elif name.endswith(".norm.weight"): + data_torch = data_torch + 1.0 + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/plm.py b/conversion/plm.py new file mode 100644 index 00000000000..3fde487085b --- /dev/null +++ b/conversion/plm.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("PLMForCausalLM") +class PLMModel(TextModel): + model_arch = gguf.MODEL_ARCH.PLM + + def set_vocab(self): + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + hparams = self.hparams + self.gguf_writer.add_vocab_size(hparams["vocab_size"]) + self.gguf_writer.add_kv_lora_rank(hparams["kv_lora_rank"]) + self.gguf_writer.add_key_length(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) + self.gguf_writer.add_value_length(hparams["v_head_dim"]) + self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) + + def prepare_tensors(self): + super().prepare_tensors() diff --git a/conversion/qwen.py b/conversion/qwen.py new file mode 100644 index 00000000000..919ecddcb91 --- /dev/null +++ b/conversion/qwen.py @@ -0,0 +1,544 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("QWenLMHeadModel") +class QwenModel(TextModel): + model_arch = gguf.MODEL_ARCH.QWEN + + @staticmethod + def token_bytes_to_string(b): + from transformers.models.gpt2.tokenization_gpt2 import bytes_to_unicode # ty: ignore[unresolved-import] + byte_encoder = bytes_to_unicode() + return ''.join([byte_encoder[ord(char)] for char in b.decode('latin-1')]) + + @staticmethod + def bpe(mergeable_ranks: dict[bytes, int], token: bytes, max_rank: int | None = None) -> list[bytes]: + parts = [bytes([b]) for b in token] + while True: + min_idx = None + min_rank = None + for i, pair in enumerate(zip(parts[:-1], parts[1:])): + rank = mergeable_ranks.get(pair[0] + pair[1]) + if rank is not None and (min_rank is None or rank < min_rank): + min_idx = i + min_rank = rank + if min_rank is None or (max_rank is not None and min_rank >= max_rank): + break + assert min_idx is not None + parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2:] + return parts + + def set_vocab(self): + self._set_vocab_qwen() + + +@ModelBase.register( + "Qwen2Model", + "Qwen2ForCausalLM", + "Qwen2AudioForConditionalGeneration", + "KORMoForCausalLM", + "AudioFlamingo3ForConditionalGeneration", + "DotsOCRForCausalLM", +) +class Qwen2Model(TextModel): + model_arch = gguf.MODEL_ARCH.QWEN2 + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self._try_set_pooling_type() + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.hf_arch == "Qwen2Model": + name = f"model.{name}" # map to Qwen2ForCausalLM tensors + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Qwen2MoeForCausalLM") +class Qwen2MoeModel(TextModel): + model_arch = gguf.MODEL_ARCH.QWEN2MOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") + if (shared_expert_intermediate_size := self.hparams.get('shared_expert_intermediate_size')) is not None: + self.gguf_writer.add_expert_shared_feed_forward_length(shared_expert_intermediate_size) + logger.info(f"gguf: expert shared feed forward length = {shared_expert_intermediate_size}") + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # handle aggregated expert tensors + # GGUF stores dimensions reversed from PyTorch, so: + # PyTorch (A,B,C) -> GGUF writes [C,B,A] -> GGML reads ne={C,B,A} + # Input shapes from HF: (n_expert, n_ff_exp, n_embd) or (n_expert, n_embd, n_ff_exp) + # Expected GGML ne: {n_embd, n_ff_exp, n_expert} for gate/up, {n_ff_exp, n_embd, n_expert} for down + if name.endswith("mlp.experts.down_proj") or name.endswith("mlp.experts.down_proj.weight"): + mapped = f"{name}.weight" if not name.endswith(".weight") else name + # HF: [n_expert, n_embd, n_ff] -> GGML: {n_ff, n_embd, n_expert} + yield from super().modify_tensors(data_torch, mapped, bid) + return + + if name.endswith("mlp.experts.gate_up_proj") or name.endswith("mlp.experts.gate_up_proj.weight"): + if data_torch.ndim < 3 or data_torch.shape[-2] % 2 != 0: + raise ValueError(f"Unexpected gate_up_proj shape for {name}: {tuple(data_torch.shape)}") + # HF: [n_expert, 2*n_ff, n_embd] -> split on dim=-2 + n_ff = data_torch.shape[-2] // 2 + gate = data_torch[..., :n_ff, :].contiguous() + up = data_torch[..., n_ff:, :].contiguous() + # gate/up: [n_expert, n_ff, n_embd] -> GGML: {n_embd, n_ff, n_expert} + base_name = name.removesuffix(".weight").removesuffix(".gate_up_proj") + mapped_gate = f"{base_name}.gate_proj.weight" + mapped_up = f"{base_name}.up_proj.weight" + yield from super().modify_tensors(gate, mapped_gate, bid) + yield from super().modify_tensors(up, mapped_up, bid) + return + + if name.find("experts") != -1: + n_experts = self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down_proj", "gate_proj", "up_proj"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") + + +@ModelBase.register("Qwen3ForCausalLM", "Qwen3Model") +class Qwen3Model(Qwen2Model): + model_arch = gguf.MODEL_ARCH.QWEN3 + + # extra logic for rerank models + is_rerank: bool = False + is_tied_embeddings: bool = False + token_false_id: int | None = None + token_true_id: int | None = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # track for intern-s1-mini + hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) + self.origin_hf_arch = hparams.get('architectures', [None])[0] + + if self._is_qwen3_reranker(): + self._find_rerank_config() + + def _is_qwen3_reranker(self) -> bool: + readme_path = self.dir_model / "README.md" + readme_text = "" + if readme_path.exists(): + with readme_path.open("r", encoding="utf-8") as f: + readme_text = f.read() + + name_hints = [ + str(self.dir_model.name), + str(self.hparams.get("_name_or_path", "")), + str(self.hparams.get("model_type", "")), + str(self.origin_hf_arch or ""), + ] + name_hints = [hint.lower() for hint in name_hints if hint] + + if "# qwen3-reranker" in readme_text.lower() or "# qwen3-vl-reranker" in readme_text.lower(): + return True + + if any("qwen3-reranker" in hint or "qwen3-vl-reranker" in hint for hint in name_hints): + return True + + return "sequenceclassification" in (self.origin_hf_arch or "").lower() + + def set_vocab(self): + # deal with intern-s1-mini + if self.origin_hf_arch == 'InternS1ForConditionalGeneration': + self._set_vocab_interns1() + return + + super().set_vocab() + + def _find_rerank_config(self): + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(self.dir_model) + + self.is_rerank = True + self.is_tied_embeddings = self.hparams.get("tie_word_embeddings", False) + self.token_false_id = tokenizer.convert_tokens_to_ids("no") # ty: ignore[unresolved-attribute, invalid-assignment] + self.token_true_id = tokenizer.convert_tokens_to_ids("yes") # ty: ignore[unresolved-attribute, invalid-assignment] + self.sep_token_id = tokenizer.convert_tokens_to_ids("|") # ty: ignore[unresolved-attribute] + + assert self.token_false_id is not None and self.token_true_id is not None + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if self.is_rerank: + self.gguf_writer.add_pooling_type(gguf.PoolingType.RANK) + self.gguf_writer.add_classifier_output_labels(["yes", "no"]) + self.gguf_writer.add_chat_template([{ + "name": "rerank", + "template": "<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be \"yes\" or \"no\".<|im_end|>\n" + "<|im_start|>user\n: Given a web search query, retrieve relevant passages that answer the query\n: {query}\n: {document}<|im_end|>\n" + "<|im_start|>assistant\n\n\n\n\n" + }]) + + def _get_cls_out_tensor(self, data_torch: Tensor) -> Tensor: + # extract "yes" and "no" tokens from the output lm_head tensor + false_row = data_torch[self.token_false_id] + true_row = data_torch[self.token_true_id] + return torch.stack([true_row, false_row], dim=0) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if self.is_rerank: + is_tied_head = self.is_tied_embeddings and "embed_tokens" in name + is_real_head = not self.is_tied_embeddings and "lm_head" in name + if is_tied_head or is_real_head: + cls_out_head = ( + gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.CLS_OUT] + ".weight", + self._get_cls_out_tensor(data_torch), + ) + yield cls_out_head + if is_tied_head: + yield from super().modify_tensors(data_torch, name, bid) + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Qwen3MoeForCausalLM") +class Qwen3MoeModel(Qwen2MoeModel): + model_arch = gguf.MODEL_ARCH.QWEN3MOE + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + hparams = ModelBase.load_hparams(self.dir_model, False) + self.origin_hf_arch = hparams.get('architectures', [None])[0] + + def set_vocab(self): + # deal with intern-s1 + if self.origin_hf_arch == 'InternS1ForConditionalGeneration': + self._set_vocab_interns1() + return + + super().set_vocab() + + +@ModelBase.register("Qwen3NextForCausalLM") +class Qwen3NextModel(Qwen2MoeModel): + model_arch = gguf.MODEL_ARCH.QWEN3NEXT + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_ssm_conv_kernel(self.hparams["linear_conv_kernel_dim"]) + self.gguf_writer.add_ssm_state_size(self.hparams["linear_key_head_dim"]) + self.gguf_writer.add_ssm_group_count(self.hparams["linear_num_key_heads"]) + self.gguf_writer.add_ssm_time_step_rank(self.hparams["linear_num_value_heads"]) + self.gguf_writer.add_ssm_inner_size(self.hparams["linear_value_head_dim"] * self.hparams["linear_num_value_heads"]) + self.gguf_writer.add_full_attention_interval(self.hparams.get("full_attention_interval", 4)) + if (rope_dim := self.hparams.get("head_dim")) is None: + rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.25))) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("mtp"): + # ignore MTP layers for now + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.endswith(".A_log"): + data_torch = -torch.exp(data_torch) + elif name.endswith(".dt_bias"): + name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" + elif "conv1d" in name: + data_torch = data_torch.squeeze() + elif name.endswith("norm.weight") and not name.endswith("linear_attn.norm.weight"): + data_torch = data_torch + 1 + + if "in_proj_qkvz.weight" in name: + # original order: [q, k, v, z] * head_count + # corrected order: [q * head_count, k * head_count, v * head_count, z * head_count] + head_k_dim = self.hparams["linear_key_head_dim"] + head_v_dim = self.hparams["linear_value_head_dim"] + num_v_heads = self.hparams["linear_num_value_heads"] + num_k_heads = self.hparams["linear_num_key_heads"] + hidden_size = self.hparams["hidden_size"] + split_arg_list_qkvz = [ + head_k_dim, # q partition + head_k_dim, # k partition + (num_v_heads // num_k_heads * head_v_dim), # v partition + (num_v_heads // num_k_heads * head_v_dim), # z partition + ] + # view as (n_embd, head_count, [q+k+v+z]) + data_torch = data_torch.permute(1, 0).contiguous() + data_torch = data_torch.view(-1, num_k_heads, sum(split_arg_list_qkvz)) + # split into q, k, v, z + q, k, v, z = torch.split(data_torch, split_arg_list_qkvz, dim=-1) + # flatten dim + head_count + q = q.contiguous().view(hidden_size, -1) + k = k.contiguous().view(hidden_size, -1) + v = v.contiguous().view(hidden_size, -1) + z = z.contiguous().view(hidden_size, -1) + # stack back + qkv = torch.cat([q, k, v], dim=-1).permute(1, 0).contiguous() + z = z.permute(1, 0).contiguous() + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_QKV, bid, ".weight"), qkv) + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_GATE, bid, ".weight"), z) + else: + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("RND1") +class RND1Model(Qwen2MoeModel): + model_arch = gguf.MODEL_ARCH.RND1 + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + # RND1 specific parameters + # RND1 uses bidirectional attention + self.gguf_writer.add_causal_attention(False) + + if (mask_token_id := self.hparams.get("mask_token_id")) is not None: + self.gguf_writer.add_mask_token_id(mask_token_id) + + +class _LinearAttentionVReorderBase(Qwen3NextModel): + model_arch = gguf.MODEL_ARCH.QWEN3NEXT # overridden by subclasses + """reorders V heads from grouped to tiled order for ggml broadcast + + see https://github.com/ggml-org/llama.cpp/pull/19468#discussion_r2786394306 + + Linear attention may has num_k_heads < num_v_heads. The HF weights store + V heads grouped by K head: [G0_v0..v{r-1}, G1_v0..v{r-1}, ...]. + ggml binary ops use tiled broadcast: [K0, K1, ..., K0, K1, ...]. + We reorder V heads to tiled order so ggml_repeat can replace the expensive + interleaved repeat: [G0_v0, G1_v0, ..., G0_v1, G1_v1, ...]. + """ + + @staticmethod + def _reorder_v_heads(tensor: Tensor, dim: int, num_k_heads: int, num_v_per_k: int, head_dim: int) -> Tensor: + """Reorder V heads from grouped (by K head) to tiled order along the given dimension.""" + shape = list(tensor.shape) + if dim < 0: + dim += len(shape) + new_shape = shape[:dim] + [num_k_heads, num_v_per_k, head_dim] + shape[dim + 1:] + tensor = tensor.reshape(*new_shape) + perm = list(range(len(new_shape))) + perm[dim], perm[dim + 1] = perm[dim + 1], perm[dim] + return tensor.permute(*perm).contiguous().reshape(*shape) + + def _transform_nvfp4_weight(self, name: str, weight: Tensor, scale: Tensor) -> tuple[Tensor, Tensor]: + if not name.endswith(( + ".linear_attn.in_proj_qkv.weight", + ".linear_attn.in_proj_z.weight", + ".linear_attn.in_proj_a.weight", + ".linear_attn.in_proj_b.weight", + ".linear_attn.out_proj.weight", + )): + return weight, scale + + num_k_heads = self.hparams["linear_num_key_heads"] + num_v_heads = self.hparams["linear_num_value_heads"] + head_k_dim = self.hparams["linear_key_head_dim"] + head_v_dim = self.hparams["linear_value_head_dim"] + num_v_per_k = num_v_heads // num_k_heads + + def unpack_nibbles(qs: Tensor) -> Tensor: + lo = torch.bitwise_and(qs, 0x0F) + hi = torch.bitwise_right_shift(qs, 4) + return torch.stack((lo, hi), dim=-1).reshape(*qs.shape[:-1], qs.shape[-1] * 2) + + def pack_nibbles(codes: Tensor) -> Tensor: + codes = codes.reshape(*codes.shape[:-1], codes.shape[-1] // 2, 2) + lo = torch.bitwise_and(codes[..., 0], 0x0F) + hi = torch.bitwise_left_shift(torch.bitwise_and(codes[..., 1], 0x0F), 4) + return torch.bitwise_or(lo, hi).contiguous() + + def apply_col_perm(qs: Tensor, scales: Tensor, col_perm: Tensor) -> tuple[Tensor, Tensor]: + assert qs.ndim >= 2 + assert scales.ndim >= 2 + + k = qs.shape[-1] * 2 + assert col_perm.numel() == k + assert k % 16 == 0 + + group_cols = col_perm.reshape(-1, 16) + group_starts = group_cols[:, 0] + expected = group_starts.unsqueeze(1) + torch.arange(16, dtype=col_perm.dtype) + assert torch.equal(group_cols, expected) + assert torch.all(group_starts % 16 == 0) + + group_perm = (group_starts // 16).to(dtype=torch.long) + expected_groups = torch.arange(scales.shape[-1], dtype=torch.long) + assert group_perm.numel() == scales.shape[-1] + assert torch.equal(torch.sort(group_perm).values, expected_groups) + + codes = unpack_nibbles(qs) + codes = codes.index_select(-1, col_perm.to(device=qs.device, dtype=torch.long)) + qs = pack_nibbles(codes) + scales = scales.index_select(-1, group_perm.to(device=scales.device)) + return qs, scales + + def reorder_rows(qs: Tensor, scales: Tensor, head_dim: int) -> tuple[Tensor, Tensor]: + row_perm = self._reorder_v_heads( + torch.arange(num_v_heads * head_dim, dtype=torch.long).unsqueeze(-1), + 0, num_k_heads, num_v_per_k, head_dim, + ).squeeze(-1) + return ( + qs.index_select(0, row_perm.to(device=qs.device)), + scales.index_select(0, row_perm.to(device=scales.device)), + ) + + if name.endswith(".linear_attn.in_proj_qkv.weight"): + q_dim = head_k_dim * num_k_heads + k_dim = head_k_dim * num_k_heads + q = weight[:q_dim] + k = weight[q_dim:q_dim + k_dim] + v = weight[q_dim + k_dim:] + q_scale = scale[:q_dim] + k_scale = scale[q_dim:q_dim + k_dim] + v_scale = scale[q_dim + k_dim:] + v, v_scale = reorder_rows(v, v_scale, head_v_dim) + return torch.cat([q, k, v], dim=0), torch.cat([q_scale, k_scale, v_scale], dim=0) + + if name.endswith(".linear_attn.in_proj_z.weight"): + weight, scale = reorder_rows(weight, scale, head_v_dim) + elif name.endswith((".linear_attn.in_proj_a.weight", ".linear_attn.in_proj_b.weight")): + weight, scale = reorder_rows(weight, scale, 1) + elif name.endswith(".linear_attn.out_proj.weight"): + col_perm = self._reorder_v_heads( + torch.arange(num_v_heads * head_v_dim, dtype=torch.long).unsqueeze(0), + 1, num_k_heads, num_v_per_k, head_v_dim, + ).squeeze(0) + weight, scale = apply_col_perm(weight, scale, col_perm) + + return weight, scale + + def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): + weight, scale = self._transform_nvfp4_weight(name, weight, scale) + super()._repack_nvfp4(name, weight, scale, scale2, input_scale) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + num_k_heads = self.hparams.get("linear_num_key_heads", 0) + num_v_heads = self.hparams.get("linear_num_value_heads", 0) + + if num_k_heads > 0 and num_v_heads > 0 and num_k_heads != num_v_heads and "linear_attn." in name: + head_k_dim = self.hparams["linear_key_head_dim"] + head_v_dim = self.hparams["linear_value_head_dim"] + num_v_per_k = num_v_heads // num_k_heads + + if ".in_proj_qkv." in name: + # QKV weight: reorder only the V rows + q_dim = head_k_dim * num_k_heads + k_dim = head_k_dim * num_k_heads + q = data_torch[:q_dim] + k = data_torch[q_dim:q_dim + k_dim] + v = data_torch[q_dim + k_dim:] + v = self._reorder_v_heads(v, 0, num_k_heads, num_v_per_k, head_v_dim) + data_torch = torch.cat([q, k, v], dim=0) + + elif ".in_proj_z." in name: + # Z gate weight: reorder rows (num_v_heads * head_v_dim) + data_torch = self._reorder_v_heads(data_torch, 0, num_k_heads, num_v_per_k, head_v_dim) + + elif ".in_proj_b." in name or ".in_proj_a." in name: + # Beta/Alpha weight: reorder rows (num_v_heads, head_dim=1) + data_torch = self._reorder_v_heads(data_torch, 0, num_k_heads, num_v_per_k, 1) + + elif ".A_log" in name or ".dt_bias" in name or ".dt_proj" in name: + # A_log / dt_bias: 1D parameters with num_v_heads elements + if data_torch.ndim == 1: + data_torch = self._reorder_v_heads( + data_torch.unsqueeze(-1), 0, num_k_heads, num_v_per_k, 1 + ).squeeze(-1) + else: + data_torch = self._reorder_v_heads(data_torch, -1, num_k_heads, num_v_per_k, 1) + + elif ".conv1d" in name: + # Conv1d kernel: reorder only the V channel portion + data = data_torch.squeeze() + qk_channels = head_k_dim * num_k_heads * 2 + qk_part = data[:qk_channels] + v_part = data[qk_channels:] + v_part = self._reorder_v_heads(v_part, 0, num_k_heads, num_v_per_k, head_v_dim) + data_torch = torch.cat([qk_part, v_part], dim=0) + + elif ".out_proj." in name: + # Out projection weight: reorder columns (input dimension) + data_torch = self._reorder_v_heads(data_torch, 1, num_k_heads, num_v_per_k, head_v_dim) + + yield from super().modify_tensors(data_torch, name, bid) + + +class _Qwen35MRopeMixin: + # Qwen3.5 always applies interleaved MRoPE (see Qwen3_5RotaryEmbedding in transformers); + # the upstream default mrope_section is [11, 11, 10] and llama.cpp's QWEN35 / QWEN35MOE + # loaders treat qwen35.rope.dimension_sections as required, so make sure it is always + # written even when a particular checkpoint omits the field in `rope_parameters`. + _QWEN35_DEFAULT_MROPE_SECTION = [11, 11, 10, 0] + + gguf_writer: gguf.GGUFWriter + rope_parameters: dict + + def set_gguf_parameters(self): + super().set_gguf_parameters() # ty: ignore[unresolved-attribute] + if "mrope_section" not in self.rope_parameters: + self.gguf_writer.add_rope_dimension_sections(self._QWEN35_DEFAULT_MROPE_SECTION) + + +@ModelBase.register("Qwen3_5ForConditionalGeneration", "Qwen3_5ForCausalLM") +class Qwen3_5TextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): + model_arch = gguf.MODEL_ARCH.QWEN35 + + +@ModelBase.register("Qwen3_5MoeForConditionalGeneration", "Qwen3_5MoeForCausalLM") +class Qwen3_5MoeTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): + model_arch = gguf.MODEL_ARCH.QWEN35MOE diff --git a/conversion/qwen3vl.py b/conversion/qwen3vl.py new file mode 100644 index 00000000000..2d20b30dbde --- /dev/null +++ b/conversion/qwen3vl.py @@ -0,0 +1,357 @@ +from __future__ import annotations + +import json + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf, logger + +from .qwen import Qwen3Model, Qwen3MoeModel +from .qwenvl import Qwen25AudioModel + + +@ModelBase.register("Qwen3VLForConditionalGeneration", "Qwen3VLMoeForConditionalGeneration", "Qwen3_5ForConditionalGeneration", "Qwen3_5MoeForConditionalGeneration") +class Qwen3VLVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams_vision is None: + logger.info("No vision config found, skipping vision tensor processing") + return + + # Compute image_size if not present + if "image_size" not in self.hparams_vision: + # For Qwen3VL/Qwen3VLMoe, compute from num_position_embeddings + num_pos = self.hparams_vision.get("num_position_embeddings", 2304) + patch_size = self.hparams_vision.get("patch_size", 16) + # num_position_embeddings = (image_size / patch_size) ** 2 + # So image_size = sqrt(num_position_embeddings) * patch_size + image_size = int(num_pos**0.5 * patch_size) + self.hparams_vision["image_size"] = image_size + + # Rename config values for compatibility + self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_heads") + self.hparams_vision["num_hidden_layers"] = self.hparams_vision.get("depth") + + self.is_deepstack_layers = [False] * int(self.hparams_vision["num_hidden_layers"] or 0) + for idx in self.hparams_vision.get("deepstack_visual_indexes", []): + self.is_deepstack_layers[idx] = True + + def set_gguf_parameters(self): + super().set_gguf_parameters() + # in case mixed modalities, the arch will be handled by subclass + if not self.has_audio_encoder: + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN3VL) + self.gguf_writer.add_vision_use_gelu(True) + + if self.hparams_vision is not None: + merge_size = self.hparams_vision.get("spatial_merge_size") + if merge_size is not None: + self.gguf_writer.add_vision_spatial_merge_size(int(merge_size)) + + # Use text config's rms_norm_eps for vision attention layernorm eps + rms_norm_eps = self.global_config.get("text_config", {}).get("rms_norm_eps", 1e-6) + self.gguf_writer.add_vision_attention_layernorm_eps(rms_norm_eps) + + if self.is_deepstack_layers: + self.gguf_writer.add_vision_is_deepstack_layers(self.is_deepstack_layers) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip text model tensors + if name.startswith("lm_head."): + return None + + # Skip MTP tensors + if name.startswith("mtp."): + return None + + if name.startswith("model.visual."): + name = name.replace("model.visual.", "visual.", 1) + + if not name.startswith("visual."): + return None + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + assert self.hparams_vision is not None + + if name.startswith("visual.deepstack_merger_list."): + prefix, rest = name.split(".", maxsplit=3)[2:] + # prefix is the layer index, convert to absolute clip layer index! + idx = self.hparams_vision.get("deepstack_visual_indexes", [])[int(prefix)] + target = rest + + tensor_type: gguf.MODEL_TENSOR + if target.startswith("norm."): + tensor_type = gguf.MODEL_TENSOR.V_DS_NORM + suffix = target.split(".", 1)[1] + elif target.startswith("linear_fc1."): + tensor_type = gguf.MODEL_TENSOR.V_DS_FC1 + suffix = target.split(".", 1)[1] + elif target.startswith("linear_fc2."): + tensor_type = gguf.MODEL_TENSOR.V_DS_FC2 + suffix = target.split(".", 1)[1] + else: + raise ValueError(f"Unexpected deepstack tensor: {name}") + + new_name = self.format_tensor_name(tensor_type, idx, suffix=f".{suffix}") + yield from super().modify_tensors(data_torch, new_name, bid) + return + + if name.startswith("visual.merger."): + suffix = name.split(".", 2)[2] + if suffix.startswith("linear_fc"): + fc_idx_str, tail = suffix.split(".", 1) + fc_num = int(fc_idx_str.replace("linear_fc", "")) + # Qwen3VL has linear_fc1 and linear_fc2 + # Map to indices 0 and 2 (matching Qwen2VL which uses indices 0 and 2) + if fc_num == 1: + fc_idx = 0 + elif fc_num == 2: + fc_idx = 2 + else: + raise ValueError(f"unexpected fc index {fc_num} in {name}") + new_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, fc_idx, suffix=f".{tail}") + elif suffix.startswith("norm."): + new_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_POST_NORM, suffix=f".{suffix.split('.', 1)[1]}") + else: + raise ValueError(f"Unexpected merger tensor: {name}") + yield (new_name, data_torch) + return + + if name == "visual.patch_embed.proj.weight": + # split Conv3D into Conv2Ds along temporal dimension + c1, c2, kt, _, _ = data_torch.shape + del c1, c2 + if kt != 2: + raise ValueError("Current implementation only supports temporal_patch_size of 2") + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight", data_torch[:, :, 0, ...]) + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight.1", data_torch[:, :, 1, ...]) + return + + if name == "visual.patch_embed.proj.bias": + # Include the bias - it's used by the C++ code + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".bias", data_torch) + return + + yield from MmprojModel.modify_tensors(self, data_torch, name, bid) + + +@ModelBase.register("Qwen3OmniMoeForConditionalGeneration") +class Qwen3OmniMmprojModel(Qwen3VLVisionModel, Qwen25AudioModel): + has_audio_encoder = True + has_vision_encoder = True + + def get_vision_config(self) -> dict[str, Any] | None: + if self.has_vision_encoder: + return self.global_config["thinker_config"].get("vision_config") + else: + return None + + def get_audio_config(self) -> dict[str, Any] | None: + if self.has_audio_encoder: + return self.global_config["thinker_config"].get("audio_config") + else: + return None + + def set_gguf_parameters(self): + if self.has_vision_encoder: + Qwen3VLVisionModel.set_gguf_parameters(self) + self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.QWEN3VL) + if self.has_audio_encoder: + Qwen25AudioModel.set_gguf_parameters(self) + self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.QWEN3A) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip text model tensors + if name.startswith("lm_head."): + return None + + # Skip MTP tensors + if name.startswith("mtp."): + return None + + if name.startswith("model.visual."): + name = name.replace("model.visual.", "visual.", 1) + + if "visual." not in name and "audio_tower." not in name: + return None + + return MmprojModel.filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "visual." in name: + if not self.has_vision_encoder: + raise ValueError(f"Model does not have vision encoder, but found tensor {name}") + # need to transform vision tensor naming, so that modify_tensors() logic can be used correctly + name = name.replace("thinker.visual.", "model.visual.") + if ".merger_list." in name: + name = name.replace(".merger_list.", ".deepstack_merger_list.") + name = name.replace(".ln_q", ".norm") + name = name.replace(".mlp.0", ".linear_fc1") + name = name.replace(".mlp.2", ".linear_fc2") + elif ".merger." in name: + name = name.replace(".ln_q", ".norm") + name = name.replace(".mlp.0", ".linear_fc1") + name = name.replace(".mlp.2", ".linear_fc2") + yield from Qwen3VLVisionModel.modify_tensors(self, data_torch, name, bid) + elif "audio_tower." in name: + if not self.has_audio_encoder: + raise ValueError(f"Model does not have audio encoder, but found tensor {name}") + if "conv2d" in name and name.endswith(".bias"): + # transform conv2d bias [n_embd] --> [1, 1, n_embd] + data_torch = data_torch.unsqueeze(-1).unsqueeze(-1) + yield from Qwen25AudioModel.modify_tensors(self, data_torch, name, bid) + + +@ModelBase.register("Qwen3ASRForConditionalGeneration") +class Qwen3ASRMmprojModel(Qwen3OmniMmprojModel): + has_audio_encoder = True + has_vision_encoder = False + + +@ModelBase.register("Glm4vForConditionalGeneration", "Glm4vMoeForConditionalGeneration", "GlmOcrForConditionalGeneration") +class Glm4VVisionModel(Qwen3VLVisionModel): + def set_gguf_parameters(self): + MmprojModel.set_gguf_parameters(self) # skip Qwen3VLVisionModel parameters + assert self.hparams_vision is not None + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GLM4V) + + hidden_act = str(self.hparams_vision.get("hidden_act", "")).lower() + if hidden_act == "gelu": + self.gguf_writer.add_vision_use_gelu(True) + elif hidden_act == "silu": + self.gguf_writer.add_vision_use_silu(True) + + rms_norm_eps = self.hparams_vision.get("rms_norm_eps", 1e-5) + self.gguf_writer.add_vision_attention_layernorm_eps(rms_norm_eps) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.startswith("visual.merger."): + yield from ModelBase.modify_tensors(self, data_torch, name, bid) + return + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Qwen3VLForConditionalGeneration") +class Qwen3VLTextModel(Qwen3Model): + model_arch = gguf.MODEL_ARCH.QWEN3VL + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if "thinker_config" in self.hparams: + vision_config = self.hparams["thinker_config"].get("vision_config", {}) + else: + vision_config = self.hparams.get("vision_config", {}) + deepstack_layer_num = len(vision_config.get("deepstack_visual_indexes", [])) + self.gguf_writer.add_num_deepstack_layers(deepstack_layer_num) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + name = name.replace("thinker.", "") + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("Qwen3VLMoeForConditionalGeneration") +class Qwen3VLMoeTextModel(Qwen3MoeModel): + model_arch = gguf.MODEL_ARCH.QWEN3VLMOE + + def set_gguf_parameters(self): + super().set_gguf_parameters() + vision_config = self.hparams.get("vision_config", {}) + deepstack_layer_num = len(vision_config.get("deepstack_visual_indexes", [])) + self.gguf_writer.add_num_deepstack_layers(deepstack_layer_num) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + name = name.replace("thinker.", "") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Qwen3VL has transposed packed tensors, so we treat it differently from general Qwen2MoE packed tensors + if name.endswith("mlp.experts.down_proj") or name.endswith("mlp.experts.down_proj.weight"): + mapped = f"{name}.weight" if not name.endswith(".weight") else name + permuted = data_torch.permute(0, 2, 1).contiguous() + yield from ModelBase.modify_tensors(self, permuted, mapped, bid) + return + + if name.endswith("mlp.experts.gate_up_proj") or name.endswith("mlp.experts.gate_up_proj.weight"): + if data_torch.ndim < 3 or data_torch.shape[-1] % 2 != 0: + raise ValueError(f"Unexpected gate_up_proj shape for {name}: {tuple(data_torch.shape)}") + split_dim = data_torch.shape[-1] // 2 + gate = data_torch[..., :split_dim].contiguous() + up = data_torch[..., split_dim:].contiguous() + # Input gate/up: (n_expert=128, n_embd=2048, n_ff_exp=768) + # Want GGML ne: {n_embd, n_ff_exp, n_expert} = {2048, 768, 128} + # Need PyTorch: (128, 768, 2048) [reversed of GGML] + # So: permute(0, 2, 1): (128, 2048, 768) -> (128, 768, 2048) + base_name = name.removesuffix(".weight") + base = base_name.rsplit('.', 1)[0] + mapped_gate = f"{base}.gate_proj.weight" + mapped_up = f"{base}.up_proj.weight" + perm_gate = gate.permute(0, 2, 1).contiguous() + perm_up = up.permute(0, 2, 1).contiguous() + yield from ModelBase.modify_tensors(self, perm_gate, mapped_gate, bid) + yield from ModelBase.modify_tensors(self, perm_up, mapped_up, bid) + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Qwen3OmniMoeForConditionalGeneration") +class Qwen3OmniMoeTextModel(Qwen3VLMoeTextModel): + model_arch = gguf.MODEL_ARCH.QWEN3VLMOE + + def set_vocab(self): + super().set_vocab() + # correct BOS/EOS tokens + with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: + tokenizer_config = json.load(f) + added_tokens = tokenizer_config.get("added_tokens_decoder", {}) + for token_id, data in added_tokens.items(): + if data.get("content") == "<|im_end|>": + self.gguf_writer.add_bos_token_id(int(token_id)) + self.gguf_writer.add_eos_token_id(int(token_id)) + break + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_num_deepstack_layers(0) + + +@ModelBase.register("Qwen3ASRForConditionalGeneration") +class Qwen3ASRTextModel(Qwen3VLTextModel): + model_arch = gguf.MODEL_ARCH.QWEN3VL + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_num_deepstack_layers(0) + + def set_vocab(self): + super().set_vocab() + # fix chat template, use correct chatml format + self.gguf_writer.add_chat_template("{% for message in messages %}{{'<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\\n' }}{% endif %}") + # correct BOS/EOS tokens + with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: + tokenizer_config = json.load(f) + added_tokens = tokenizer_config.get("added_tokens_decoder", {}) + for token_id, data in added_tokens.items(): + if data.get("content") == "<|im_end|>": + self.gguf_writer.add_bos_token_id(int(token_id)) + self.gguf_writer.add_eos_token_id(int(token_id)) + break diff --git a/conversion/qwenvl.py b/conversion/qwenvl.py new file mode 100644 index 00000000000..7befd0c8d81 --- /dev/null +++ b/conversion/qwenvl.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +import numpy as np +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf + + +@ModelBase.register( + "Qwen2VLModel", + "Qwen2VLForConditionalGeneration", + "Qwen2_5_VLForConditionalGeneration", + "Qwen2_5OmniModel", +) +class Qwen2VLModel(TextModel): + model_arch = gguf.MODEL_ARCH.QWEN2VL + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("thinker."): + name = name.replace("thinker.", "") + + return super().filter_tensors((name, gen)) + + +@ModelBase.register("Qwen2VLModel", "Qwen2VLForConditionalGeneration", "Qwen2_5_VLForConditionalGeneration") +class Qwen2VLVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["image_size"] = self.hparams_vision.get("image_size", 560) + # rename config.json values + self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_heads") + self.hparams_vision["num_hidden_layers"] = self.hparams_vision.get("depth") + if "embed_dim" in self.hparams_vision: # qwen2vl + self.hparams_vision["intermediate_size"] = self.hparams_vision.get("hidden_size") + self.hparams_vision["hidden_size"] = self.hparams_vision.get("embed_dim") + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + hparams = self.hparams_vision + model_type = self.global_config['model_type'] + if model_type == 'qwen2_vl': + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN2VL) + elif model_type == 'qwen2_5_vl' or model_type == 'qwen2_5_omni': + if model_type == 'qwen2_5_omni': + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25O) + else: + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25VL) + self.gguf_writer.add_vision_use_silu(True) + # find n_wa_pattern (window attention pattern) + fullatt_block_indexes = hparams.get("fullatt_block_indexes") + assert fullatt_block_indexes is not None, "fullatt_block_indexes is required for qwen2_5_vl" + n_wa_pattern = fullatt_block_indexes[0] + 1 + # validate n_wa_pattern + for i in range(1, len(fullatt_block_indexes)): + if fullatt_block_indexes[i] - fullatt_block_indexes[i - 1] != n_wa_pattern: + raise ValueError(f"Invalid fullatt_block_indexes: {fullatt_block_indexes}") + self.gguf_writer.add_vision_n_wa_pattern(n_wa_pattern) + else: + raise ValueError(f"Unknown QwenVL model type: {self.global_config['model_type']}") + # default values below are taken from HF tranformers code + self.gguf_writer.add_vision_attention_layernorm_eps(self.global_config.get("rms_norm_eps", 1e-6)) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".position_embd." in new_name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if not name.startswith("visual."): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # split QKV tensors if needed + if ".qkv." in name: + if data_torch.ndim == 2: # weight + c3, _ = data_torch.shape + else: # bias + c3 = data_torch.shape[0] + assert c3 % 3 == 0 + c = c3 // 3 + wq = data_torch[:c] + wk = data_torch[c: c * 2] + wv = data_torch[c * 2:] + yield from super().modify_tensors(wq, name.replace("qkv", "q"), bid) + yield from super().modify_tensors(wk, name.replace("qkv", "k"), bid) + yield from super().modify_tensors(wv, name.replace("qkv", "v"), bid) + elif 'patch_embed.proj.weight' in name: + # split Conv3D into Conv2Ds + c1, c2, kt, kh, kw = data_torch.shape + del c1, c2, kh, kw # unused + assert kt == 2, "Current implementation only support temporal_patch_size of 2" + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight" , data_torch[:, :, 0, ...]) + yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight.1", data_torch[:, :, 1, ...]) + else: + yield from super().modify_tensors(data_torch, name, bid) + + +class Qwen25AudioModel(MmprojModel): + has_audio_encoder = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_audio is not None + self.hparams_audio["hidden_size"] = self.hparams_audio["d_model"] + self.hparams_audio["intermediate_size"] = self.hparams_audio["encoder_ffn_dim"] + self.hparams_audio["num_attention_heads"] = self.hparams_audio["encoder_attention_heads"] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_audio is not None + self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["num_mel_bins"]) + self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams_audio.get("layer_norm_eps", 1e-5)) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + # SinusoidsPositionEmbedding + assert self.hparams_audio is not None + max_timescale = 10000 + length = 1500 + channels = self.hparams_audio["hidden_size"] + log_timescale_increment = np.log(max_timescale) / (channels // 2 - 1) + inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2).float()) + scaled_time = torch.arange(length)[:, np.newaxis] * inv_timescales[np.newaxis, :] + pos_embd = torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1).to(dtype=torch.float32) + yield ("audio_tower.embed_positions.weight", pos_embd) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F16 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "conv1.bias" in name or "conv2.bias" in name: + # transpose conv1 and conv2 bias + data_torch = data_torch.unsqueeze(-1) + + yield from MmprojModel.modify_tensors(self, data_torch, name, bid) + + +@ModelBase.register("Qwen2_5OmniModel") +class Qwen25OmniModel(Qwen2VLVisionModel, Qwen25AudioModel): + has_audio_encoder = True + has_vision_encoder = True + + def get_vision_config(self) -> dict[str, Any] | None: + return self.global_config["thinker_config"].get("vision_config") + + def get_audio_config(self) -> dict[str, Any] | None: + return self.global_config["thinker_config"].get("audio_config") + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25O) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if not name.startswith("visual.") and not name.startswith("audio_tower."): + return None + + if name.startswith("thinker."): + name = name.replace("thinker.", "") + + if "audio_bos_eos_token" in name: + # this tensor is left unused in transformers code + # https://github.com/huggingface/transformers/blob/6e3063422c4b1c014aa60c32b9254fd2902f0f28/src/transformers/models/qwen2_5_omni/modular_qwen2_5_omni.py#L1809 + return None + + return MmprojModel.filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "visual." in name: + yield from Qwen2VLVisionModel.modify_tensors(self, data_torch, name, bid) + elif "audio_tower." in name: + yield from Qwen25AudioModel.modify_tensors(self, data_torch, name, bid) + return # skip other tensors diff --git a/conversion/refact.py b/conversion/refact.py new file mode 100644 index 00000000000..1170cddeb2c --- /dev/null +++ b/conversion/refact.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("GPTRefactForCausalLM") +class RefactModel(TextModel): + model_arch = gguf.MODEL_ARCH.REFACT + + def set_vocab(self): + super().set_vocab() + + # TODO: how to determine special FIM tokens automatically? + special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False, + special_token_types = ['prefix', 'suffix', 'middle', 'eot']) + special_vocab._set_special_token("prefix", 1) + special_vocab._set_special_token("suffix", 3) + special_vocab._set_special_token("middle", 2) + special_vocab.chat_template = None # do not add it twice + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + hidden_dim = self.hparams["n_embd"] + inner_dim = 4 * hidden_dim + hidden_dim = int(2 * inner_dim / 3) + multiple_of = 256 + ff_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) + + # refact uses Alibi. So this is from config.json which might be used by training. + self.gguf_writer.add_context_length(self.hparams["n_positions"]) + self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) + + self.gguf_writer.add_feed_forward_length(ff_dim) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(self.hparams["n_head"]) + self.gguf_writer.add_head_count_kv(1) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + hidden_dim = self.hparams["n_embd"] + inner_dim = 4 * hidden_dim + hidden_dim = int(2 * inner_dim / 3) + multiple_of = 256 + ff_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) + n_head = self.hparams["n_head"] + n_head_kv = 1 + head_dim = self.hparams["n_embd"] // n_head + + if bid is not None: + if name == f"transformer.h.{bid}.attn.kv.weight": + yield from super().modify_tensors(data_torch[:n_head_kv * head_dim], self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) + yield from super().modify_tensors(data_torch[n_head_kv * head_dim:], self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) + return + if name == f"transformer.h.{bid}.attn.q.weight": + yield from super().modify_tensors(data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) + return + if name == f"transformer.h.{bid}.mlp.gate_up_proj.weight": + yield from super().modify_tensors(data_torch[:ff_dim], self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), bid) + yield from super().modify_tensors(data_torch[ff_dim:], self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), bid) + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/rwkv.py b/conversion/rwkv.py new file mode 100644 index 00000000000..2de0aa5346e --- /dev/null +++ b/conversion/rwkv.py @@ -0,0 +1,302 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("Rwkv6ForCausalLM") +class Rwkv6Model(TextModel): + model_arch = gguf.MODEL_ARCH.RWKV6 + + def set_vocab(self): + self._set_vocab_rwkv_world() + + def set_gguf_parameters(self): + head_size = self.hparams["head_size"] + hidden_size = self.hparams["hidden_size"] + layer_norm_eps = self.hparams["layer_norm_epsilon"] + rescale_every_n_layers = self.hparams["rescale_every"] + intermediate_size = self.hparams["intermediate_size"] if self.hparams["intermediate_size"] is not None else int((hidden_size * 3.5) // 32 * 32) + time_mix_extra_dim = 64 if hidden_size == 4096 else 32 + time_decay_extra_dim = 128 if hidden_size == 4096 else 64 + + # RWKV isn't context limited + self.gguf_writer.add_context_length(1048576) + self.gguf_writer.add_embedding_length(hidden_size) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_layer_norm_eps(layer_norm_eps) + self.gguf_writer.add_rescale_every_n_layers(rescale_every_n_layers) + self.gguf_writer.add_wkv_head_size(head_size) + self.gguf_writer.add_time_mix_extra_dim(time_mix_extra_dim) + self.gguf_writer.add_time_decay_extra_dim(time_decay_extra_dim) + self.gguf_writer.add_feed_forward_length(intermediate_size) + self.gguf_writer.add_file_type(self.ftype) + + # required by llama.cpp, unused + self.gguf_writer.add_head_count(0) + + lerp_weights: dict[int, dict[str, Tensor]] = {} + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + new_name = self.map_tensor_name(name) + + if not (new_name.endswith(".weight") or new_name.endswith(".bias")): + new_name += ".weight" + + if new_name.endswith("time_mix_w1.weight") or new_name.endswith("time_mix_decay_w1.weight") or new_name.endswith("time_mix_decay_w2.weight"): + data_torch = data_torch.transpose(0, 1) + + if new_name.endswith("time_mix_w2.weight"): + data_torch = data_torch.permute(0, 2, 1) + + if new_name.endswith("time_mix_decay.weight") or "lerp" in new_name: + data_torch = data_torch.squeeze() + + try: + rescale_every_n_layers = self.hparams["rescale_every"] + if rescale_every_n_layers > 0: + if new_name.endswith("time_mix_output.weight") or new_name.endswith("channel_mix_value.weight"): + data_torch = data_torch.div_(2 ** int(bid // rescale_every_n_layers)) + except KeyError: + pass + + # concat time_mix_lerp weights to reduce some cpu overhead + # also reduces the number of tensors in the model + if bid is not None and "time_mix_lerp" in new_name and "time_mix_lerp_x" not in new_name: + try: + self.lerp_weights[bid][new_name] = data_torch + except KeyError: + self.lerp_weights[bid] = {new_name: data_torch} + if all(f"blk.{bid}.time_mix_lerp_{i}.weight" in self.lerp_weights[bid].keys() for i in ["w", "k", "v", "r", "g"]): + new_name = f"blk.{bid}.time_mix_lerp_fused.weight" + data = torch.stack([self.lerp_weights[bid][f"blk.{bid}.time_mix_lerp_{i}.weight"].unsqueeze(0) for i in ["w", "k", "v", "r", "g"]], dim=0).unsqueeze(1) + yield (new_name, data) + return + + yield (new_name, data_torch) + + +@ModelBase.register("RWKV6Qwen2ForCausalLM") +class RWKV6Qwen2Model(Rwkv6Model): + model_arch = gguf.MODEL_ARCH.RWKV6QWEN2 + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + num_attention_heads = self.hparams["num_attention_heads"] + num_key_value_heads = self.hparams["num_key_value_heads"] + hidden_size = self.hparams["hidden_size"] + head_size = hidden_size // num_attention_heads + rms_norm_eps = self.hparams["rms_norm_eps"] + intermediate_size = self.hparams["intermediate_size"] + time_mix_extra_dim = self.hparams.get("lora_rank_tokenshift", 64 if hidden_size >= 4096 else 32) + time_decay_extra_dim = self.hparams.get("lora_rank_decay", 128 if hidden_size >= 4096 else 64) + + # RWKV isn't context limited + self.gguf_writer.add_context_length(1048576) + self.gguf_writer.add_embedding_length(hidden_size) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_wkv_head_size(head_size) + self.gguf_writer.add_time_mix_extra_dim(time_mix_extra_dim) + self.gguf_writer.add_time_decay_extra_dim(time_decay_extra_dim) + self.gguf_writer.add_feed_forward_length(intermediate_size) + self.gguf_writer.add_file_type(self.ftype) + + # special parameters for time_mixing in RWKV6QWEN2 + self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) + self.gguf_writer.add_token_shift_count(1) + # RWKV6QWEN2 use grouped key/value like GQA + self.gguf_writer.add_head_count_kv(num_key_value_heads) + + # required by llama.cpp, unused + self.gguf_writer.add_head_count(0) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + for new_name, data in super().modify_tensors(data_torch, name, bid): + if "time_mix_w1" in new_name or "time_mix_w2" in new_name: + data = data.view(5, -1, data.shape[-1]) + # rwkv6qwen2 has a different order of rkvwg instead of the original wkvrg + # permute them here to avoid code changes + data = torch.stack([data[3], data[1], data[2], data[0], data[4]], dim=0).view(-1, data.shape[-1]) + if "w2" in new_name: + data = data.view(5, -1, data.shape[-1]) + yield (new_name, data) + continue + yield (new_name, data) + + +@ModelBase.register("Rwkv7ForCausalLM", "RWKV7ForCausalLM") +class Rwkv7Model(TextModel): + model_arch = gguf.MODEL_ARCH.RWKV7 + + def set_vocab(self): + self._set_vocab_rwkv_world() + + def calc_lora_rank(self, hidden_size, exponent, multiplier): + return max(1, round(hidden_size ** exponent * multiplier / 32)) * 32 + + def set_gguf_parameters(self): + try: + head_size = self.hparams["head_size"] + layer_norm_eps = self.hparams["layer_norm_epsilon"] + except KeyError: + head_size = self.hparams["head_dim"] + layer_norm_eps = self.hparams["norm_eps"] + hidden_size = self.hparams["hidden_size"] + intermediate_size = self.hparams["intermediate_size"] if self.hparams["intermediate_size"] is not None else (hidden_size * 4) + + # ICLR: In-Context-Learning-Rate + try: + lora_rank_decay = self.hparams["lora_rank_decay"] if self.hparams["lora_rank_decay"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) + lora_rank_iclr = self.hparams["lora_rank_iclr"] if self.hparams["lora_rank_iclr"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) + lora_rank_value_residual_mix = self.hparams["lora_rank_value_residual_mix"] if self.hparams["lora_rank_value_residual_mix"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.3) + lora_rank_gate = self.hparams["lora_rank_gate"] if self.hparams["lora_rank_gate"] is not None else self.calc_lora_rank(hidden_size, 0.8, 0.6) + except KeyError: + lora_rank_decay = self.hparams["decay_low_rank_dim"] if self.hparams["decay_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) + lora_rank_iclr = self.hparams["a_low_rank_dim"] if self.hparams["a_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) + lora_rank_value_residual_mix = self.hparams["v_low_rank_dim"] if self.hparams["v_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.3) + lora_rank_gate = self.hparams["gate_low_rank_dim"] if self.hparams["gate_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.8, 0.6) + + # RWKV isn't context limited + self.gguf_writer.add_context_length(1048576) + self.gguf_writer.add_embedding_length(hidden_size) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_layer_norm_eps(layer_norm_eps) + self.gguf_writer.add_wkv_head_size(head_size) + self.gguf_writer.add_decay_lora_rank(lora_rank_decay) + self.gguf_writer.add_iclr_lora_rank(lora_rank_iclr) + self.gguf_writer.add_value_residual_mix_lora_rank(lora_rank_value_residual_mix) + self.gguf_writer.add_gate_lora_rank(lora_rank_gate) + self.gguf_writer.add_feed_forward_length(intermediate_size) + self.gguf_writer.add_file_type(self.ftype) + + # required by llama.cpp, unused + self.gguf_writer.add_head_count(0) + + lerp_weights: dict[int, dict[str, Tensor]] = {} + lora_needs_transpose: bool = True + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # unify tensor names here to make life easier + name = name.replace("blocks", "layers").replace("ffn", "feed_forward") + name = name.replace("self_attn", "attention").replace("attn", "attention") + name = name.replace("time_mixer.", "") + + name = name.replace("feed_forward_norm", "ln2") + name = name.replace("g_norm", "ln_x") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # lora layer names in fla-hub's impl + if "_lora.lora" in name: + self.lora_needs_transpose = False + name = name.replace("_lora.lora.0.weight", "1.weight") + name = name.replace("_lora.lora.2.weight", "2.weight") + name = name.replace("_lora.lora.2.bias", "0.weight") + + if "attention.v" in name and "value" not in self.map_tensor_name(name) and bid == 0: + # some models have dummy v0/v1/v2 on first layer while others don't + # ignore them all since they are not used + return + + wkv_has_gate = self.hparams.get("wkv_has_gate", True) + lerp_list = ["r", "w", "k", "v", "a", "g"] if wkv_has_gate else ["r", "w", "k", "v", "a"] + + if bid is not None and "attention.x_" in name: + if "attention.x_x" in name: + # already concatenated + new_name = f"blk.{bid}.time_mix_lerp_fused.weight" + data = data_torch.reshape(len(lerp_list), 1, 1, -1) + yield (new_name, data) + else: + try: + self.lerp_weights[bid][name] = data_torch + except KeyError: + self.lerp_weights[bid] = {name: data_torch} + if all(f"model.layers.{bid}.attention.x_{i}" in self.lerp_weights[bid].keys() for i in lerp_list): + new_name = f"blk.{bid}.time_mix_lerp_fused.weight" + data = torch.stack([self.lerp_weights[bid][f"model.layers.{bid}.attention.x_{i}"] for i in lerp_list], dim=0) + yield (new_name, data) + return + else: + data_torch = data_torch.squeeze() + new_name = self.map_tensor_name(name) + + if not (new_name.endswith(".weight") or new_name.endswith(".bias")): + new_name += ".weight" + + if self.lora_needs_transpose and any( + new_name.endswith(t) for t in [ + "time_mix_w1.weight", "time_mix_w2.weight", + "time_mix_a1.weight", "time_mix_a2.weight", + "time_mix_v1.weight", "time_mix_v2.weight", + "time_mix_g1.weight", "time_mix_g2.weight", + ] + ): + data_torch = data_torch.transpose(0, 1) + + if 'r_k' in new_name: + data_torch = data_torch.flatten() + + if bid == 0 and "time_mix_a" in new_name: + # dummy v0/v1/v2 on first layer + # easiest way to make llama happy + yield (new_name.replace("time_mix_a", "time_mix_v"), data_torch) + + yield (new_name, data_torch) + + +@ModelBase.register("RwkvHybridForCausalLM") +class ARwkv7Model(Rwkv7Model): + model_arch = gguf.MODEL_ARCH.ARWKV7 + + def set_vocab(self): + try: + self._set_vocab_sentencepiece() + except FileNotFoundError: + self._set_vocab_gpt2() + + def set_gguf_parameters(self): + hidden_size = self.hparams["hidden_size"] + head_size = self.hparams["head_size"] + rms_norm_eps = self.hparams["rms_norm_eps"] + intermediate_size = self.hparams["intermediate_size"] + wkv_has_gate = self.hparams["wkv_has_gate"] + assert self.hparams["wkv_version"] == 7 + + # ICLR: In-Context-Learning-Rate + lora_rank_decay = 64 + lora_rank_iclr = 64 + lora_rank_value_residual_mix = 32 + lora_rank_gate = 128 if wkv_has_gate else 0 + + # RWKV isn't context limited + self.gguf_writer.add_context_length(1048576) + self.gguf_writer.add_embedding_length(hidden_size) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) + self.gguf_writer.add_wkv_head_size(head_size) + self.gguf_writer.add_decay_lora_rank(lora_rank_decay) + self.gguf_writer.add_iclr_lora_rank(lora_rank_iclr) + self.gguf_writer.add_value_residual_mix_lora_rank(lora_rank_value_residual_mix) + self.gguf_writer.add_gate_lora_rank(lora_rank_gate) + self.gguf_writer.add_feed_forward_length(intermediate_size) + self.gguf_writer.add_file_type(self.ftype) + self.gguf_writer.add_token_shift_count(1) + + # required by llama.cpp, unused + self.gguf_writer.add_head_count(0) diff --git a/conversion/sarashina2.py b/conversion/sarashina2.py new file mode 100644 index 00000000000..05448db812e --- /dev/null +++ b/conversion/sarashina2.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, gguf + +from .llama import LlamaModel +from .qwenvl import Qwen2VLVisionModel + + +@ModelBase.register("Sarashina2VisionForCausalLM") +class Sarashina2VLTextModel(LlamaModel): + model_arch = gguf.MODEL_ARCH.LLAMA + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + if name.startswith("llm."): + name = name.replace("llm.", "", 1) + elif name.startswith("norm."): + return None + return super().filter_tensors((name, gen)) + + +@ModelBase.register("Sarashina2VisionForCausalLM") +class Sarashina2VLVisionModel(Qwen2VLVisionModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.global_config['model_type'] = "qwen2_vl" diff --git a/conversion/smallthinker.py b/conversion/smallthinker.py new file mode 100644 index 00000000000..1b0f79aa3ea --- /dev/null +++ b/conversion/smallthinker.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("SmallThinkerForCausalLM") +class SmallThinkerModel(TextModel): + model_arch = gguf.MODEL_ARCH.SMALLTHINKER + + def set_gguf_parameters(self): + super().set_gguf_parameters() + if (n_experts := self.hparams.get("moe_num_primary_experts")) is not None: + self.gguf_writer.add_expert_count(n_experts) + if (n_experts_used := self.hparams.get("moe_num_active_primary_experts")) is not None: + self.gguf_writer.add_expert_used_count(n_experts_used) + if (moe_intermediate_size := self.hparams.get("moe_ffn_hidden_size")) is not None: + self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) + self.gguf_writer.add_feed_forward_length(moe_intermediate_size) + logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") + if (self.hparams.get('moe_primary_router_apply_softmax')): + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SOFTMAX) + else: + self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) + + sliding_window_layout = self.hparams.get("sliding_window_layout") + if sliding_window_layout: + for i in sliding_window_layout: + if i != 0: + sliding_window = self.hparams.get("sliding_window_size") + if sliding_window: + self.gguf_writer.add_sliding_window(sliding_window) + break + + _experts: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # process the experts separately + if name.find("experts") != -1: + n_experts = self.hparams.get("moe_num_primary_experts") or self.find_hparam(["num_local_experts", "num_experts"]) + assert bid is not None + + if self._experts is None: + self._experts = [{} for _ in range(self.block_count)] + + self._experts[bid][name] = data_torch + + if len(self._experts[bid]) >= n_experts * 3: + # merge the experts into a single 3d tensor + for w_name in ["down", "gate", "up"]: + datas: list[Tensor] = [] + + for xid in range(n_experts): + ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" + datas.append(self._experts[bid][ename]) + del self._experts[bid][ename] + + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + return + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._experts is not None: + # flatten `list[dict[str, Tensor]]` into `list[str]` + experts = [k for d in self._experts for k in d.keys()] + if len(experts) > 0: + raise ValueError(f"Unprocessed experts: {experts}") diff --git a/conversion/smolvlm.py b/conversion/smolvlm.py new file mode 100644 index 00000000000..30e9dca329b --- /dev/null +++ b/conversion/smolvlm.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf + + +@ModelBase.register("Idefics3ForConditionalGeneration", "SmolVLMForConditionalGeneration") +class SmolVLMModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.hparams["model_type"] == "smolvlm_vision": + # fix for SmolVLM2, missing some keys in config.json + # default values are taken from transformers code + self.hparams["hidden_size"] = self.hparams.get("hidden_size", 1152) + self.hparams["num_attention_heads"] = self.hparams.get("num_attention_heads", 16) + self.hparams["intermediate_size"] = self.hparams.get("intermediate_size", 3072) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.IDEFICS3) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) + self.gguf_writer.add_vision_projector_scale_factor(self.global_config.get("scale_factor", 2)) + self.gguf_writer.add_vision_use_gelu(True) + + # Add the preprocessor longest edge size + preproc_image_size = self.preprocessor_config.get("size", {}).get("longest_edge", self.image_size) + self.gguf_writer.add_vision_preproc_image_size(preproc_image_size) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".embeddings." in name: + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + is_vision_tensor = "vision_tower" in name or "vision_model" in name or "model.connector" in name + + if not is_vision_tensor: + return None + + return super().filter_tensors(item) diff --git a/conversion/stablelm.py b/conversion/stablelm.py new file mode 100644 index 00000000000..ba5e9aa6ca9 --- /dev/null +++ b/conversion/stablelm.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("StableLmForCausalLM", "StableLMEpochForCausalLM", "LlavaStableLMEpochForCausalLM") +class StableLMModel(TextModel): + model_arch = gguf.MODEL_ARCH.STABLELM + + def set_vocab(self): + if (self.dir_model / "tokenizer.json").is_file(): + self._set_vocab_gpt2() + else: + # StableLM 2 1.6B used to have a vocab in a similar format to Qwen's vocab + self._set_vocab_qwen() + + def set_gguf_parameters(self): + hparams = self.hparams + + self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) + self.gguf_writer.add_embedding_length(hparams["hidden_size"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) + rotary_factor = self.find_hparam(["partial_rotary_factor", "rope_pct"]) + self.gguf_writer.add_rope_dimension_count(int(rotary_factor * (hparams["hidden_size"] // hparams["num_attention_heads"]))) + self.gguf_writer.add_head_count(hparams["num_attention_heads"]) + self.gguf_writer.add_head_count_kv(hparams["num_key_value_heads"]) + self.gguf_writer.add_parallel_residual(hparams["use_parallel_residual"] if "use_parallel_residual" in hparams else True) + self.gguf_writer.add_layer_norm_eps(self.find_hparam(["layer_norm_eps", "norm_eps"])) + self.gguf_writer.add_file_type(self.ftype) + + _q_norms: list[dict[str, Tensor]] | None = None + _k_norms: list[dict[str, Tensor]] | None = None + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + n_head = self.hparams["num_attention_heads"] + n_kv_head = self.hparams["num_key_value_heads"] + + if name.find("q_layernorm.norms") != -1: + assert bid is not None + + if self._q_norms is None: + self._q_norms = [{} for _ in range(self.block_count)] + + self._q_norms[bid][name] = data_torch + + if len(self._q_norms[bid]) >= n_head: + return self._stack_qk_norm(bid, n_head, self._q_norms[bid], "q_layernorm") + else: + return + + if name.find("k_layernorm.norms") != -1: + assert bid is not None + + if self._k_norms is None: + self._k_norms = [{} for _ in range(self.block_count)] + + self._k_norms[bid][name] = data_torch + + if len(self._k_norms[bid]) >= n_kv_head: + return self._stack_qk_norm(bid, n_kv_head, self._k_norms[bid], "k_layernorm") + else: + return + + yield from super().modify_tensors(data_torch, name, bid) + + def _stack_qk_norm(self, bid: int, n_head: int, norms: dict[str, Tensor], layer_name: str = "q_layernorm"): + datas: list[Tensor] = [] + # extract the norms in order + for xid in range(n_head): + ename = f"model.layers.{bid}.self_attn.{layer_name}.norms.{xid}.weight" + datas.append(norms[ename]) + del norms[ename] + data_torch = torch.stack(datas, dim=0) + + merged_name = f"model.layers.{bid}.self_attn.{layer_name}.weight" + + yield from super().modify_tensors(data_torch, merged_name, bid) + + def prepare_tensors(self): + super().prepare_tensors() + + if self._q_norms is not None or self._k_norms is not None: + # flatten two `list[dict[str, Tensor]]` into a single `list[str]` + norms = ( + [k for d in self._q_norms for k in d.keys()] if self._q_norms is not None else [] + ) + ( + [k for d in self._k_norms for k in d.keys()] if self._k_norms is not None else [] + ) + if len(norms) > 0: + raise ValueError(f"Unprocessed norms: {norms}") diff --git a/conversion/starcoder.py b/conversion/starcoder.py new file mode 100644 index 00000000000..0b4ffd84702 --- /dev/null +++ b/conversion/starcoder.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("GPTBigCodeForCausalLM") +class StarCoderModel(TextModel): + model_arch = gguf.MODEL_ARCH.STARCODER + + def set_gguf_parameters(self): + self.gguf_writer.add_context_length(self.hparams["n_positions"]) + self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) + self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(self.hparams["n_head"]) + self.gguf_writer.add_head_count_kv(1) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + +@ModelBase.register("Starcoder2ForCausalLM") +class StarCoder2Model(TextModel): + model_arch = gguf.MODEL_ARCH.STARCODER2 diff --git a/conversion/step3.py b/conversion/step3.py new file mode 100644 index 00000000000..ba867fb831b --- /dev/null +++ b/conversion/step3.py @@ -0,0 +1,231 @@ +from __future__ import annotations + +import math +import re + +from typing import Callable, Iterable, TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, _MISTRAL_COMMON_DATASET_MEAN, _MISTRAL_COMMON_DATASET_STD, gguf + +from .qwen import Qwen3Model + + +@ModelBase.register("StepVLForConditionalGeneration") +class Step3VLVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + + if not self.hparams_vision.get("intermediate_size"): + hidden_size = self.hparams_vision.get("hidden_size") or self.hparams_vision.get("width") or 0 + assert hidden_size > 0 + mlp_ratio = float(self.hparams_vision.get("mlp_ratio", 8960 / 1536)) + self.hparams_vision["intermediate_size"] = int(round(hidden_size * mlp_ratio)) + + self.preprocessor_config.setdefault("image_mean", list(_MISTRAL_COMMON_DATASET_MEAN)) + self.preprocessor_config.setdefault("image_std", list(_MISTRAL_COMMON_DATASET_STD)) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + assert self.hparams_vision is not None + + projector_stride = int(self.global_config.get("understand_projector_stride", -1)) + hidden_size = int(self.hparams_vision.get("hidden_size", self.hparams_vision.get("width", -1))) + num_layers = int(self.hparams_vision.get("num_hidden_layers", self.hparams_vision.get("layers", -1))) + assert (projector_stride, int(self.hparams_vision.get("image_size", -1)), hidden_size, num_layers) == (2, 728, 1536, 47), ( + "current Step3-VL conversion path is only validated for Step3-VL-10B" + ) + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.STEP3VL) + self.gguf_writer.add_vision_attention_layernorm_eps(float(self.hparams_vision.get("layer_norm_eps", 1e-5))) + self.gguf_writer.add_vision_projector_scale_factor(projector_stride ** 2) + # 3024 max resize comes from step3-vl-10b processing_step3.py. + self.gguf_writer.add_vision_preproc_image_size(3024) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".position_embd." in new_name: + return gguf.GGMLQuantizationType.F32 + if ("mm.0." in new_name or "mm.1." in new_name) and new_name.endswith(".weight"): + return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith(("model.", "lm_head.")): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.startswith("vision_model.vit_downsampler"): + match = re.match(r"vision_model\.vit_downsampler(\d+)\.(weight|bias)", name) + if match is None: + raise ValueError(f"Unexpected Step3-VL projector tensor {name!r}") + + proj_id = int(match.group(1)) - 1 + suffix = f".{match.group(2)}" + yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, proj_id, suffix=suffix), data_torch) + return + + if name == "vit_large_projector.weight": + yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ_FC), data_torch) + return + + if name.startswith("vision_model."): + if name == "vision_model.positional_embedding": + name += ".weight" + elif name.endswith(".gamma") and ".ls_" in name: + name = name.removesuffix(".gamma") + ".weight" + + name = name.replace("attn.in_proj_weight", "attn.in_proj.weight") + name = name.replace("attn.in_proj_bias", "attn.in_proj.bias") + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("StepVLForConditionalGeneration") +class Step3VLTextModel(Qwen3Model): + model_arch = gguf.MODEL_ARCH.QWEN3 + + +@ModelBase.register("Step3p5ForCausalLM") +class Step35Model(TextModel): + model_arch = gguf.MODEL_ARCH.STEP35 + + def set_gguf_parameters(self): + rope_theta = self.hparams.get("rope_theta") + if isinstance(rope_theta, list): + self.hparams["rope_theta"] = float(rope_theta[0]) + self.hparams["local_rope_theta"] = float(rope_theta[1]) + self.rope_parameters["rope_theta"] = self.hparams["rope_theta"] + self.rope_parameters["sliding_attention"] = {"rope_theta": self.hparams["local_rope_theta"]} + + super().set_gguf_parameters() + + layer_types = self.hparams.get("layer_types") or [] + partial_rotary_factors = self.hparams.get("partial_rotary_factors") or [] + attn_other = self.hparams.get("attention_other_setting") or {} + + n_head_base = self.hparams["num_attention_heads"] + n_kv_base = self.hparams["num_attention_groups"] + + n_head_swa = attn_other.get("num_attention_heads", n_head_base) + n_kv_swa = attn_other.get("num_attention_groups", n_kv_base) + + layer_types = layer_types[: self.block_count] + partial_rotary_factors = partial_rotary_factors[: self.block_count] + assert [1.0 if lt == "sliding_attention" else 0.5 for lt in layer_types] == partial_rotary_factors + head_arr = [n_head_swa if lt == "sliding_attention" else n_head_base for lt in layer_types] + kv_arr = [n_kv_swa if lt == "sliding_attention" else n_kv_base for lt in layer_types] + swa_pat = [lt == "sliding_attention" for lt in layer_types] + + self.gguf_writer.add_head_count(head_arr) + self.gguf_writer.add_head_count_kv(kv_arr) + + self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) + self.gguf_writer.add_sliding_window_pattern(swa_pat) + + self.gguf_writer.add_value_length(self.hparams["head_dim"]) + + # MoE params + self.gguf_writer.add_expert_count(self.hparams["moe_num_experts"]) + self.gguf_writer.add_expert_used_count(self.hparams["moe_top_k"]) + self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) + self.gguf_writer.add_expert_shared_feed_forward_length(self.hparams["share_expert_dim"]) + + if (moe_router_scaling_factor := self.hparams.get("moe_router_scaling_factor")) is not None: + self.gguf_writer.add_expert_weights_scale(moe_router_scaling_factor) + if (norm_expert_weight := self.hparams.get("norm_expert_weight")) is not None: + self.gguf_writer.add_expert_weights_norm(norm_expert_weight) + + # leading dense blocks + leading_dense = 0 + moe_layers_enum = self.hparams.get("moe_layers_enum") + if isinstance(moe_layers_enum, str) and moe_layers_enum.strip(): + moe_layers = sorted(int(i) for i in moe_layers_enum.strip().split(",")) + if moe_layers: + leading_dense = max(0, moe_layers[0]) + self.gguf_writer.add_leading_dense_block_count(leading_dense) + self.gguf_writer.add_moe_every_n_layers(int(self.hparams.get("moe_every_n_layer", 1))) + + self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("rms_norm_eps", 1e-5)) + + # Optional per-layer SwiGLU clamps. + if (limits := self.hparams.get("swiglu_limits")) is not None: + limits_f = [0.0 if v is None else float(v) for v in limits[: self.block_count]] + self.gguf_writer.add_swiglu_clamp_exp(limits_f) + if (limits_shared := self.hparams.get("swiglu_limits_shared")) is not None: + limits_shared_f = [0.0 if v is None else float(v) for v in limits_shared[: self.block_count]] + self.gguf_writer.add_swiglu_clamp_shexp(limits_shared_f) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Map router bias (expert selection bias) to a GGUF bias tensor + if name.endswith(".moe.router_bias"): + name += ".bias" + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): + # remove mtp layers + if (m := re.match(r"model\.layers\.(\d+)\.", name)) is not None: + il = int(m.group(1)) + n_main = int(self.hparams.get("num_hidden_layers", self.block_count)) + if il >= n_main: + return + if name.endswith("norm.weight"): + data_torch += 1.0 + + if name.endswith((".self_attn.g_proj.weight", ".moe.gate.weight", ".moe.up_proj.weight", ".moe.gate_proj.weight", ".moe.down_proj.weight")): + data_torch = data_torch.squeeze().contiguous() + + yield from super().modify_tensors(data_torch, name, bid) + + def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: + # Step35 can optionally use Llama-3 style RoPE scaling (HF: rope_scaling.rope_type == "llama3"). + # llama.cpp represents this via a single extra tensor: "rope_freqs.weight" (aka MODEL_TENSOR.ROPE_FREQS). + rope_params = self.rope_parameters.get("full_attention", self.rope_parameters) + rope_type = rope_params.get("rope_type") or "" + if rope_type.lower() != "llama3": + return + + # Step35 configs can carry per-layer rope_theta as a list; for llama3 rope factors we use the base value. + rope_theta = self.hparams.get("rope_theta", 10000.0) + if isinstance(rope_theta, list): + rope_theta = rope_theta[0] + base = float(rope_theta) + if (dim := self.hparams.get("head_dim")) is None: + dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] + dim = int(dim) + + freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) + + factor = float(rope_params.get("factor", 8.0)) + low_freq_factor = float(rope_params.get("low_freq_factor", 1.0)) + high_freq_factor = float(rope_params.get("high_freq_factor", 4.0)) + old_context_len = int(rope_params.get("original_max_position_embeddings", self.hparams.get("original_max_position_embeddings", 8192))) + + low_freq_wavelen = old_context_len / low_freq_factor + high_freq_wavelen = old_context_len / high_freq_factor + + rope_factors: list[float] = [] + for freq in freqs: + wavelen = 2 * math.pi / float(freq) + if wavelen < high_freq_wavelen: + rope_factors.append(1.0) + elif wavelen > low_freq_wavelen: + rope_factors.append(factor) + else: + smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) + rope_factors.append(1.0 / ((1.0 - smooth) / factor + smooth)) + + yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) diff --git a/conversion/t5.py b/conversion/t5.py new file mode 100644 index 00000000000..73dcfd1a2ce --- /dev/null +++ b/conversion/t5.py @@ -0,0 +1,286 @@ +from __future__ import annotations + +import json +import os + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, SentencePieceTokenTypes, TextModel, gguf, logger + + +@ModelBase.register("T5WithLMHeadModel") +@ModelBase.register("T5ForConditionalGeneration") +@ModelBase.register("MT5ForConditionalGeneration") +@ModelBase.register("UMT5ForConditionalGeneration") +@ModelBase.register("UMT5Model") +class T5Model(TextModel): + model_arch = gguf.MODEL_ARCH.T5 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.shared_token_embeddings_found = False + + def set_vocab(self): + # to avoid TypeError: Descriptors cannot be created directly + # exception when importing sentencepiece_model_pb2 + os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" + from sentencepiece import SentencePieceProcessor + from sentencepiece import sentencepiece_model_pb2 as model + + tokenizer_path = self.dir_model / 'tokenizer.model' + + # many older models use spiece.model tokenizer model filename + if not tokenizer_path.is_file(): + tokenizer_path = self.dir_model / 'spiece.model' + + if not tokenizer_path.is_file(): + raise FileNotFoundError(f"File not found: {tokenizer_path}") + + sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] + sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) + + # some models like Pile-T5 family use BPE tokenizer instead of Unigram + if sentencepiece_model.trainer_spec.model_type == 2: # BPE + # assure the tokenizer model file name is correct + assert tokenizer_path.name == 'tokenizer.model' + return self._set_vocab_sentencepiece() + else: + assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM + + add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix + remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces + precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + for token_id in range(tokenizer.vocab_size()): + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + added_tokens_file = self.dir_model / 'added_tokens.json' + if added_tokens_file.is_file(): + with open(added_tokens_file, "r", encoding="utf-8") as f: + added_tokens_json = json.load(f) + for key in added_tokens_json: + token_id = added_tokens_json[key] + if token_id >= vocab_size: + logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + + tokens[token_id] = key.encode("utf-8") + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + + if vocab_size > len(tokens): + pad_count = vocab_size - len(tokens) + logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") + for i in range(1, pad_count + 1): + tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) + scores.append(-1000.0) + toktypes.append(SentencePieceTokenTypes.UNUSED) + + self.gguf_writer.add_tokenizer_model("t5") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_add_space_prefix(add_prefix) + self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) + if precompiled_charsmap: + self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + if (n_ctx := self.find_hparam(["n_positions"], optional=True)) is None: + logger.warning("Couldn't find context length in config.json, assuming default value of 512") + n_ctx = 512 + self.gguf_writer.add_context_length(n_ctx) + self.gguf_writer.add_embedding_length(self.hparams["d_model"]) + self.gguf_writer.add_feed_forward_length(self.hparams["d_ff"]) + self.gguf_writer.add_block_count(self.block_count) + if (dec_n_layer := self.hparams.get("num_decoder_layers")) is not None: + self.gguf_writer.add_decoder_block_count(dec_n_layer) + self.gguf_writer.add_head_count(self.hparams["num_heads"]) + self.gguf_writer.add_key_length(self.hparams["d_kv"]) + self.gguf_writer.add_value_length(self.hparams["d_kv"]) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_relative_attn_buckets_count(self.hparams["relative_attention_num_buckets"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_decoder_start_token_id(self.hparams["decoder_start_token_id"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", + # "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored + # in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder + # and decoder and ignore the remaining ones. + if name in ["decoder.embed_tokens.weight", "encoder.embed_tokens.weight", "shared.weight"]: + if not self.shared_token_embeddings_found: + name = "shared.weight" + self.shared_token_embeddings_found = True + else: + logger.debug(f"Skipping shared tensor {name!r} in safetensors so that convert can end normally.") + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("T5EncoderModel") +class T5EncoderModel(TextModel): + model_arch = gguf.MODEL_ARCH.T5ENCODER + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.shared_token_embeddings_found = False + + def set_vocab(self): + # to avoid TypeError: Descriptors cannot be created directly + # exception when importing sentencepiece_model_pb2 + os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" + from sentencepiece import SentencePieceProcessor + from sentencepiece import sentencepiece_model_pb2 as model + + tokenizer_path = self.dir_model / 'tokenizer.model' + + # many older models use spiece.model tokenizer model filename + if not tokenizer_path.is_file(): + tokenizer_path = self.dir_model / 'spiece.model' + + if not tokenizer_path.is_file(): + raise FileNotFoundError(f"File not found: {tokenizer_path}") + + sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] + sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) + + # some models like Pile-T5 family use BPE tokenizer instead of Unigram + if sentencepiece_model.trainer_spec.model_type == 2: # BPE + # assure the tokenizer model file name is correct + assert tokenizer_path.name == 'tokenizer.model' + return self._set_vocab_sentencepiece() + else: + assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM + + add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix + remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces + precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap + + tokenizer = SentencePieceProcessor() + tokenizer.LoadFromFile(str(tokenizer_path)) + + vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) + + tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] + scores: list[float] = [-10000.0] * vocab_size + toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size + + for token_id in range(tokenizer.vocab_size()): + piece = tokenizer.IdToPiece(token_id) + text = piece.encode("utf-8") + score = tokenizer.GetScore(token_id) + + toktype = SentencePieceTokenTypes.NORMAL + if tokenizer.IsUnknown(token_id): + toktype = SentencePieceTokenTypes.UNKNOWN + elif tokenizer.IsControl(token_id): + toktype = SentencePieceTokenTypes.CONTROL + elif tokenizer.IsUnused(token_id): + toktype = SentencePieceTokenTypes.UNUSED + elif tokenizer.IsByte(token_id): + toktype = SentencePieceTokenTypes.BYTE + + tokens[token_id] = text + scores[token_id] = score + toktypes[token_id] = toktype + + added_tokens_file = self.dir_model / 'added_tokens.json' + if added_tokens_file.is_file(): + with open(added_tokens_file, "r", encoding="utf-8") as f: + added_tokens_json = json.load(f) + for key in added_tokens_json: + token_id = added_tokens_json[key] + if token_id >= vocab_size: + logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') + continue + + tokens[token_id] = key.encode("utf-8") + scores[token_id] = -1000.0 + toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED + + if vocab_size > len(tokens): + pad_count = vocab_size - len(tokens) + logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") + for i in range(1, pad_count + 1): + tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) + scores.append(-1000.0) + toktypes.append(SentencePieceTokenTypes.UNUSED) + + self.gguf_writer.add_tokenizer_model("t5") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_scores(scores) + self.gguf_writer.add_token_types(toktypes) + self.gguf_writer.add_add_space_prefix(add_prefix) + self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) + if precompiled_charsmap: + self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) + + special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + if (n_ctx := self.find_hparam(["n_positions"], optional=True)) is None: + logger.warning("Couldn't find context length in config.json, assuming default value of 512") + n_ctx = 512 + self.gguf_writer.add_context_length(n_ctx) + self.gguf_writer.add_embedding_length(self.hparams["d_model"]) + self.gguf_writer.add_feed_forward_length(self.hparams["d_ff"]) + self.gguf_writer.add_block_count(self.block_count) + self.gguf_writer.add_head_count(self.hparams["num_heads"]) + self.gguf_writer.add_key_length(self.hparams["d_kv"]) + self.gguf_writer.add_value_length(self.hparams["d_kv"]) + self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_relative_attn_buckets_count(self.hparams["relative_attention_num_buckets"]) + self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) + self.gguf_writer.add_file_type(self.ftype) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", + # "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored + # in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder + # and decoder and ignore the remaining ones. + if name in ["decoder.embed_tokens.weight", "encoder.embed_tokens.weight", "shared.weight"]: + if not self.shared_token_embeddings_found: + name = "shared.weight" + self.shared_token_embeddings_found = True + else: + logger.debug(f"Skipping shared tensor {name!r} in safetensors so that convert can end normally.") + return + + yield from super().modify_tensors(data_torch, name, bid) diff --git a/conversion/ultravox.py b/conversion/ultravox.py new file mode 100644 index 00000000000..347188733a5 --- /dev/null +++ b/conversion/ultravox.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from typing import Any, Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, TextModel, gguf + + +@ModelBase.register("UltravoxModel") +class UltravoxModel(TextModel): + model_arch = gguf.MODEL_ARCH.LLAMA # dummy + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + raise NotImplementedError("Ultravox does not have text decoder. Instead, it uses Llama or other models for text. If you want to get the audio encoder, please use --mmproj argument") + + +@ModelBase.register("GlmasrModel") +class GlmASRWhisperEncoderModel(MmprojModel): + has_vision_encoder = False + has_audio_encoder = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if "hidden_size" not in self.hparams and "intermediate_size" not in self.hparams: + self.hparams["hidden_size"] = self.hparams["d_model"] + self.hparams["intermediate_size"] = self.hparams["encoder_ffn_dim"] + self.hparams["num_attention_heads"] = self.hparams["encoder_attention_heads"] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GLMA) + self.gguf_writer.add_audio_num_mel_bins(self.hparams["num_mel_bins"]) + self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) + self.gguf_writer.add_audio_stack_factor(self.global_config["merge_factor"]) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F16 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith(("model.", "lm_head.")): + # skip language model tensors + return None + + if name.startswith("audio_encoder.whisper."): + name = name.replace("audio_encoder.whisper.","audio_tower.") + if "audio_encoder.layer_norm." in name or "audio_encoder.proj." in name: + name = name.replace("audio_encoder.", "audio_encoder.adapting.") + if name.startswith("audio_encoder.adapting."): + name = name.replace("audio_encoder.adapting.","audio.multi_modal_projector.") + if ".layer_norm." in name: + name = name.replace(".layer_norm.", ".ln_pre.") + if ".0." in name: + name = name.replace(".0.", ".linear_1.") + if ".2." in name: + name = name.replace(".2.", ".linear_2.") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if name.startswith("audio_encoder.audio_bos_eos_token."): + yield from super().modify_tensors(data_torch[0], "model.vision.boi", bid) + yield from super().modify_tensors(data_torch[1], "model.vision.eoi", bid) + return + + if name.startswith("audio_encoder.adapting."): + if ".proj." in name: + return + + if "conv1.bias" in name or "conv2.bias" in name: + # transpose conv1 and conv2 bias + data_torch = data_torch.unsqueeze(-1) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("Qwen2AudioForConditionalGeneration") +class WhisperEncoderModel(MmprojModel): + has_vision_encoder = False # no vision encoder + has_audio_encoder = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if "hidden_size" not in self.hparams and "intermediate_size" not in self.hparams: + self.hparams["hidden_size"] = self.hparams["d_model"] + self.hparams["intermediate_size"] = self.hparams["encoder_ffn_dim"] + self.hparams["num_attention_heads"] = self.hparams["encoder_attention_heads"] + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN2A) + self.gguf_writer.add_audio_num_mel_bins(self.hparams["num_mel_bins"]) + self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".conv" in name and ".weight" in name: + return gguf.GGMLQuantizationType.F16 + return super().tensor_force_quant(name, new_name, bid, n_dims) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # prevent clash naming with vision tensors + if name.startswith("multi_modal_projector"): + name = "audio." + name + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + if "conv1.bias" in name or "conv2.bias" in name: + # transpose conv1 and conv2 bias + data_torch = data_torch.unsqueeze(-1) + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("UltravoxModel") +class UltravoxWhisperEncoderModel(WhisperEncoderModel): + has_vision_encoder = False # no vision encoder + has_audio_encoder = True + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.ULTRAVOX) + self.gguf_writer.add_audio_stack_factor(self.global_config["stack_factor"]) + + +@ModelBase.register("MERaLiON2ForConditionalGeneration") +class MERaLiONWhisperEncoderModel(WhisperEncoderModel): + has_vision_encoder = False + has_audio_encoder = True + + def get_audio_config(self) -> dict[str, Any] | None: + return self.global_config.get("speech_config") + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MERALION) + self.gguf_writer.add_audio_stack_factor(self.global_config.get("speech_mlp_scale_factor", 15)) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if name.startswith("text_decoder."): + return None + + if name.startswith("speech_encoder."): + name = name.replace("speech_encoder.", "audio_tower.") + + return super().filter_tensors((name, gen)) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + suffix = "." + name.rsplit(".", 1)[-1] + + if name.startswith("ln_speech."): + yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MM_NORM_PRE, suffix=suffix), data_torch) + return + + if name.startswith("speech_audio_adapter."): + if ".mlp_adapter.0." in name: + yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 0, suffix=suffix), data_torch) + elif ".gate_proj." in name: + yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 1, suffix=suffix), data_torch) + elif ".pool_proj." in name: + yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 2, suffix=suffix), data_torch) + elif ".out_proj." in name: + yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 3, suffix=suffix), data_torch) + return + + yield from super().modify_tensors(data_torch, name, bid) + + +@ModelBase.register("VoxtralForConditionalGeneration") +class VoxtralWhisperEncoderModel(WhisperEncoderModel): + has_vision_encoder = False # no vision encoder + has_audio_encoder = True + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.VOXTRAL) + self.gguf_writer.add_audio_stack_factor(4) # == intermediate_size // hidden_size + + +@ModelBase.register("AudioFlamingo3ForConditionalGeneration") +class AudioFlamingo3WhisperEncoderModel(WhisperEncoderModel): + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MUSIC_FLAMINGO) + + def tensor_force_quant(self, name, new_name, bid, n_dims): + if ".conv" in name and ".weight" in name: + # Was trained in BF16, being safe, avoiding quantizing to FP16 + return gguf.GGMLQuantizationType.F32 + return super().tensor_force_quant(name, new_name, bid, n_dims) diff --git a/conversion/wavtokenizer.py b/conversion/wavtokenizer.py new file mode 100644 index 00000000000..7d25447be88 --- /dev/null +++ b/conversion/wavtokenizer.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf, logger + + +@ModelBase.register("WavTokenizerDec") +class WavTokenizerDecModel(TextModel): + model_arch = gguf.MODEL_ARCH.WAVTOKENIZER_DEC + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + if \ + name.endswith("codebook.cluster_size") or \ + name.endswith("codebook.embed_avg") or \ + name.endswith("codebook.inited"): + logger.debug(f"Skipping {name!r}") + return None + + return super().filter_tensors(item) + + def set_vocab(self): + self._set_vocab_none() + + def set_gguf_parameters(self): + super().set_gguf_parameters() + self.gguf_writer.add_vocab_size (self.hparams["vocab_size"]) + self.gguf_writer.add_features_length (self.hparams["n_embd_features"]) + self.gguf_writer.add_feed_forward_length(self.hparams["n_ff"]) + self.gguf_writer.add_group_norm_eps (self.hparams["group_norm_epsilon"]) + self.gguf_writer.add_group_norm_groups (self.hparams["group_norm_groups"]) + + self.gguf_writer.add_posnet_embedding_length(self.hparams["posnet"]["n_embd"]) + self.gguf_writer.add_posnet_block_count (self.hparams["posnet"]["n_layer"]) + + self.gguf_writer.add_convnext_embedding_length(self.hparams["convnext"]["n_embd"]) + self.gguf_writer.add_convnext_block_count (self.hparams["convnext"]["n_layer"]) + + self.gguf_writer.add_causal_attention(False) diff --git a/conversion/xverse.py b/conversion/xverse.py new file mode 100644 index 00000000000..fa8a31a133f --- /dev/null +++ b/conversion/xverse.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +import re + +from typing import Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import ModelBase, TextModel, gguf + + +@ModelBase.register("XverseForCausalLM") +class XverseModel(TextModel): + model_arch = gguf.MODEL_ARCH.XVERSE + + def set_vocab(self): + assert (self.dir_model / "tokenizer.json").is_file() + dir_model = self.dir_model + hparams = self.hparams + + tokens: list[bytes] = [] + toktypes: list[int] = [] + + from transformers import AutoTokenizer + tokenizer = AutoTokenizer.from_pretrained(dir_model) + vocab_size = hparams.get("vocab_size", len(tokenizer.vocab)) # ty: ignore[unresolved-attribute] + # Since we are checking the maximum index, we need to ensure it's strictly less than vocab_size, + # because vocab_size is the count of items, and indexes start at 0. + max_vocab_index = max(tokenizer.get_vocab().values()) # ty: ignore[unresolved-attribute] + if max_vocab_index >= vocab_size: + raise ValueError("Vocabulary size exceeds expected maximum size.") + + reverse_vocab: dict[int, str] = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] + added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] + + for token_id in range(vocab_size): + token_text = reverse_vocab[token_id].encode('utf-8') + # replace "\x00" to string with length > 0 + if token_text == b"\x00": + toktype = gguf.TokenType.BYTE # special + token_text = f"<{token_text}>".encode('utf-8') + elif re.fullmatch(br"<0x[0-9A-Fa-f]{2}>", token_text): + toktype = gguf.TokenType.BYTE # special + elif reverse_vocab[token_id] in added_vocab: + if tokenizer.added_tokens_decoder[token_id].special: # ty: ignore[unresolved-attribute] + toktype = gguf.TokenType.CONTROL + else: + toktype = gguf.TokenType.USER_DEFINED + else: + toktype = gguf.TokenType.NORMAL + + tokens.append(token_text) + toktypes.append(toktype) + + self.gguf_writer.add_tokenizer_model("llama") + self.gguf_writer.add_tokenizer_pre("default") + self.gguf_writer.add_token_list(tokens) + self.gguf_writer.add_token_types(toktypes) + + special_vocab = gguf.SpecialVocab(dir_model, n_vocab=len(tokens)) + special_vocab.add_to_gguf(self.gguf_writer) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_tensor_data_layout("Meta AI original pth") + self.gguf_writer.add_rope_dimension_count(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + head_count = self.hparams["num_attention_heads"] + head_count_kv = self.hparams.get("num_key_value_heads", head_count) + + # HF models permute some of the tensors, so we need to undo that + if name.endswith("q_proj.weight"): + data_torch = self._reverse_hf_permute(data_torch, head_count, head_count) + if name.endswith("k_proj.weight"): + data_torch = self._reverse_hf_permute(data_torch, head_count, head_count_kv) + + yield from super().modify_tensors(data_torch, name, bid) + + def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: + if n_kv_head is not None and n_head != n_kv_head: + n_head //= n_kv_head + + return ( + weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) + .swapaxes(1, 2) + .reshape(weights.shape) + ) diff --git a/conversion/youtuvl.py b/conversion/youtuvl.py new file mode 100644 index 00000000000..cabc44445f3 --- /dev/null +++ b/conversion/youtuvl.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Callable, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: + from torch import Tensor + +from .base import MmprojModel, ModelBase, gguf, logger + + +@ModelBase.register("YoutuVLForConditionalGeneration") +class YoutuVLVisionModel(MmprojModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + assert self.hparams_vision is not None + self.hparams_vision["image_size"] = self.hparams_vision.get("image_size", 560) + + def set_gguf_parameters(self): + super().set_gguf_parameters() + + self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.YOUTUVL) + self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) + + # Handle activation function + hidden_act = str(self.hparams.get("hidden_act", "gelu_pytorch_tanh")).lower() + if hidden_act in ("gelu", "gelu_pytorch_tanh", "gelu_fast", "gelu_new", "gelu_accurate"): + self.gguf_writer.add_vision_use_gelu(True) + elif hidden_act == "silu": + self.gguf_writer.add_vision_use_silu(True) + else: + raise ValueError(f"Unsupported activation function for YOUTUVL: {hidden_act}") + + self.gguf_writer.add_vision_spatial_merge_size(self.hparams.get("spatial_merge_size", 2)) + + window_size = self.hparams.get("window_size") + if window_size is not None: + self.gguf_writer.add_vision_window_size(window_size) + # fullatt_block_indexes contains explicit layer indices that use full attention + # e.g., [2, 5, 8, 11] means layers 2, 5, 8, 11 use full attention + # All other layers use window attention + fullatt_block_indexes = self.hparams.get("fullatt_block_indexes") + assert fullatt_block_indexes is not None, "fullatt_block_indexes is required for youtuvl" + # Store the explicit layer indices for YoutuVL (irregular pattern approach) + self.gguf_writer.add_vision_wa_layer_indexes(layers=fullatt_block_indexes) + + @classmethod + def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: + name, gen = item + + # Skip language model tensors + skip_prefixes = ('lm_head.', 'model.layers.', 'model.embed_tokens.', 'model.norm.') + if name.startswith(skip_prefixes): + return None + + return super().filter_tensors(item) + + def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: + # Try to map the tensor using TensorNameMap (handles vision encoder and projector) + try: + yield from super().modify_tensors(data_torch, name, bid) + except ValueError: + # If mapping fails, log warning and skip + logger.warning(f"Cannot map tensor: {name}") + return diff --git a/convert_hf_to_gguf.py b/convert_hf_to_gguf.py index 5cff8684856..7173f616009 100755 --- a/convert_hf_to_gguf.py +++ b/convert_hf_to_gguf.py @@ -3,13981 +3,46 @@ from __future__ import annotations -import ast -import logging -import argparse -import contextlib -import json -import os -import re -import sys -from enum import IntEnum -from pathlib import Path -from hashlib import sha256 -from typing import TYPE_CHECKING, Any, Callable, ContextManager, Iterable, Iterator, Literal, Sequence, TypeVar, cast -from itertools import chain -from transformers import AutoConfig - -import math -import numpy as np -import torch - -if TYPE_CHECKING: - from torch import Tensor - -if 'NO_LOCAL_GGUF' not in os.environ: - sys.path.insert(1, str(Path(__file__).parent / 'gguf-py')) -import gguf -from gguf.vocab import MistralTokenizerType, MistralVocab - -try: - from mistral_common.tokens.tokenizers.base import TokenizerVersion # type: ignore[import-not-found, ty:unresolved-import] - from mistral_common.tokens.tokenizers.multimodal import DATASET_MEAN as _MISTRAL_COMMON_DATASET_MEAN, DATASET_STD as _MISTRAL_COMMON_DATASET_STD # type: ignore[import-not-found, ty:unresolved-import] - from mistral_common.tokens.tokenizers.tekken import Tekkenizer # type: ignore[import-not-found, ty:unresolved-import] - from mistral_common.tokens.tokenizers.sentencepiece import ( # type: ignore[import-not-found, ty:unresolved-import] - SentencePieceTokenizer, - ) - - _mistral_common_installed = True - _mistral_import_error_msg = "" -except ImportError: - _MISTRAL_COMMON_DATASET_MEAN = (0.48145466, 0.4578275, 0.40821073) - _MISTRAL_COMMON_DATASET_STD = (0.26862954, 0.26130258, 0.27577711) - - _mistral_common_installed = False - TokenizerVersion: Any = None - Tekkenizer: Any = None - SentencePieceTokenizer: Any = None - _mistral_import_error_msg = ( - "Mistral format requires `mistral-common` to be installed. Please run " - "`pip install mistral-common[image,audio]` to install it." - ) - - -logger = logging.getLogger("hf-to-gguf") - - -###### MODEL DEFINITIONS ###### - -class SentencePieceTokenTypes(IntEnum): - NORMAL = 1 - UNKNOWN = 2 - CONTROL = 3 - USER_DEFINED = 4 - UNUSED = 5 - BYTE = 6 - - -class ModelType(IntEnum): - TEXT = 1 - MMPROJ = 2 - - -AnyModel = TypeVar("AnyModel", bound="type[ModelBase]") - - -class ModelBase: - _model_classes: dict[ModelType, dict[str, type[ModelBase]]] = { - ModelType.TEXT: {}, - ModelType.MMPROJ: {}, - } - - dir_model: Path - ftype: gguf.LlamaFileType - fname_out: Path - is_big_endian: bool - endianess: gguf.GGUFEndian - use_temp_file: bool - lazy: bool - dry_run: bool - hparams: dict[str, Any] - model_tensors: dict[str, Callable[[], Tensor]] - gguf_writer: gguf.GGUFWriter - model_name: str | None - metadata_override: Path | None - dir_model_card: Path - remote_hf_model_id: str | None - - # subclasses should define this! - model_arch: gguf.MODEL_ARCH - - # subclasses should initialize this! - block_count: int - tensor_map: gguf.TensorNameMap - - # Mistral format specifics - is_mistral_format: bool = False - disable_mistral_community_chat_template: bool = False - sentence_transformers_dense_modules: bool = False - - def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, *, is_big_endian: bool = False, - use_temp_file: bool = False, eager: bool = False, - metadata_override: Path | None = None, model_name: str | None = None, - split_max_tensors: int = 0, split_max_size: int = 0, dry_run: bool = False, - small_first_shard: bool = False, hparams: dict[str, Any] | None = None, remote_hf_model_id: str | None = None, - disable_mistral_community_chat_template: bool = False, - sentence_transformers_dense_modules: bool = False, - fuse_gate_up_exps: bool = False): - if type(self) is ModelBase or \ - type(self) is TextModel or \ - type(self) is MmprojModel: - raise TypeError(f"{type(self).__name__!r} should not be directly instantiated") - - if self.is_mistral_format and not _mistral_common_installed: - raise ImportError(_mistral_import_error_msg) - - self.dir_model = dir_model - self.ftype = ftype - self.fname_out = fname_out - self.is_big_endian = is_big_endian - self.endianess = gguf.GGUFEndian.BIG if is_big_endian else gguf.GGUFEndian.LITTLE - self.use_temp_file = use_temp_file - self.lazy = not eager or (remote_hf_model_id is not None) - self.dry_run = dry_run - self.remote_hf_model_id = remote_hf_model_id - self.sentence_transformers_dense_modules = sentence_transformers_dense_modules - self.fuse_gate_up_exps = fuse_gate_up_exps - self._gate_exp_buffer: dict[int, Tensor] = {} - self._up_exp_buffer: dict[int, Tensor] = {} - self.hparams = ModelBase.load_hparams(self.dir_model, self.is_mistral_format) if hparams is None else hparams - self.model_tensors = self.index_tensors(remote_hf_model_id=remote_hf_model_id) - self.metadata_override = metadata_override - self.model_name = model_name - self.dir_model_card = dir_model # overridden in convert_lora_to_gguf.py - self._is_nvfp4 = False - self._is_mxfp4 = False - - # Apply heuristics to figure out typical tensor encoding based on first tensor's dtype - # NOTE: can't use field "torch_dtype" in config.json, because some finetunes lie. - if self.ftype == gguf.LlamaFileType.GUESSED: - for _, tensor in self.get_tensors(): - if tensor.dim() < 2: - continue - - if tensor.dtype == torch.bfloat16: - self.ftype = gguf.LlamaFileType.MOSTLY_BF16 - logger.info("heuristics detected bfloat16 tensor dtype, setting --outtype bf16") - break - elif tensor.dtype == torch.float16: - self.ftype = gguf.LlamaFileType.MOSTLY_F16 - logger.info("heuristics detected float16 tensor dtype, setting --outtype f16") - break - else: - self.ftype = gguf.LlamaFileType.MOSTLY_F16 - logger.info("heuristics unable to detect tensor dtype, defaulting to --outtype f16") - - # Configure GGUF Writer - self.gguf_writer = gguf.GGUFWriter(path=None, arch=gguf.MODEL_ARCH_NAMES[self.model_arch], endianess=self.endianess, use_temp_file=self.use_temp_file, - split_max_tensors=split_max_tensors, split_max_size=split_max_size, dry_run=dry_run, small_first_shard=small_first_shard) - - # Mistral specific - self.disable_mistral_community_chat_template = disable_mistral_community_chat_template - - @classmethod - def add_prefix_to_filename(cls, path: Path, prefix: str) -> Path: - stem, suffix = path.stem, path.suffix - new_name = f"{prefix}{stem}{suffix}" - return path.with_name(new_name) - - def find_hparam(self, keys: Iterable[str], optional: bool = False) -> Any: - key = next((k for k in keys if k in self.hparams), None) - if key is not None: - return self.hparams[key] - if optional: - return None - raise KeyError(f"could not find any of: {keys}") - - def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]: - tensors: dict[str, Callable[[], Tensor]] = {} - - if remote_hf_model_id is not None: - is_safetensors = True - - logger.info(f"Using remote model with HuggingFace id: {remote_hf_model_id}") - remote_tensors = gguf.utility.SafetensorRemote.get_list_tensors_hf_model(remote_hf_model_id) - for name, remote_tensor in remote_tensors.items(): - data_gen = lambda r=remote_tensor: LazyTorchTensor.from_remote_tensor(r) # noqa: E731 - if titem := self.filter_tensors((name, data_gen)): - tname, tgen = titem - tensors[tname] = tgen - - return tensors - - prefix = "model" if not self.is_mistral_format else "consolidated" - part_names: list[str] = ModelBase.get_model_part_names(self.dir_model, prefix, ".safetensors") - is_safetensors: bool = len(part_names) > 0 - if not is_safetensors: - part_names = ModelBase.get_model_part_names(self.dir_model, "pytorch_model", ".bin") - - tensor_names_from_index: set[str] = set() - tensor_names_from_parts: set[str] = set() - - if not self.is_mistral_format: - index_name = "model.safetensors" if is_safetensors else "pytorch_model.bin" - index_name += ".index.json" - index_file = self.dir_model / index_name - - if index_file.is_file(): - logger.info(f"gguf: loading model weight map from '{index_name}'") - with open(index_file, "r", encoding="utf-8") as f: - index: dict[str, Any] = json.load(f) - weight_map = index.get("weight_map") - if weight_map is None or not isinstance(weight_map, dict): - raise ValueError(f"Can't load 'weight_map' from {index_name!r}") - tensor_names_from_index.update(weight_map.keys()) - part_dict: dict[str, None] = dict.fromkeys(weight_map.values(), None) # ty: ignore[invalid-assignment] - part_names = sorted(part_dict.keys()) - else: - weight_map = {} - else: - weight_map = {} - - for part_name in part_names: - logger.info(f"gguf: indexing model part '{part_name}'") - ctx: ContextManager[Any] - if is_safetensors: - ctx = cast(ContextManager[Any], gguf.utility.SafetensorsLocal(self.dir_model / part_name)) - else: - ctx = contextlib.nullcontext(torch.load(str(self.dir_model / part_name), map_location="cpu", mmap=True, weights_only=True)) - - with ctx as model_part: - assert model_part is not None - - for name in model_part.keys(): - tensor_names_from_parts.add(name) - if is_safetensors: - data: gguf.utility.LocalTensor = model_part[name] - if self.lazy: - data_gen = lambda data=data: LazyTorchTensor.from_local_tensor(data) # noqa: E731 - else: - dtype = LazyTorchTensor._dtype_str_map[data.dtype] - data_gen = lambda data=data, dtype=dtype: torch.from_numpy(data.mmap_bytes()).view(dtype).reshape(data.shape) # noqa: E731 - else: - data_torch: Tensor = model_part[name] - if self.lazy: - data_gen = lambda data=data_torch: LazyTorchTensor.from_eager(data) # noqa: E731 - else: - data_gen = lambda data=data_torch: data # noqa: E731 - if titem := self.filter_tensors((name, data_gen)): - tname, tgen = titem - tensors[tname] = tgen - - # verify tensor name presence and identify potentially missing files - if len(tensor_names_from_index) > 0: - if len(tensor_names_from_parts.symmetric_difference(tensor_names_from_index)) > 0: - missing = sorted(tensor_names_from_index.difference(tensor_names_from_parts)) - extra = sorted(tensor_names_from_parts.difference(tensor_names_from_index)) - missing_files = sorted(set(weight_map[n] for n in missing if n in weight_map)) - if len(extra) == 0 and len(missing_files) > 0: - raise ValueError(f"Missing or incomplete model files: {missing_files}\n" - f"Missing tensors: {missing}") - else: - raise ValueError("Mismatch between weight map and model parts for tensor names:\n" - f"Missing tensors: {missing}\n" - f"Extra tensors: {extra}") - - return tensors - - @staticmethod - def _scale_is_trivial(scale: Tensor) -> bool: - return scale.numel() <= 1 and abs(float(scale.float().sum()) - 1.0) < 1e-6 - - def _write_scale_tensor(self, scale_name: str, scale: Tensor): - if not self._scale_is_trivial(scale): - scale_f32 = scale.float().numpy().flatten() - logger.info(f" + {scale_name} (per-tensor scale, shape [{scale_f32.size}])") - self.gguf_writer.add_tensor(scale_name, scale_f32) - - def _write_scales_tensor(self, scale_name: str, scales: list[float]): - if not np.allclose(scales, 1.0, atol=1e-6): - scale_vals = np.array(scales, dtype=np.float32) - logger.info(f" + {scale_name} (per-expert scale, shape [{len(scales)}])") - self.gguf_writer.add_tensor(scale_name, scale_vals) - - def dequant_model(self): - # If all quantized tensors were already handled (e.g. pure NVFP4), skip - if self._is_nvfp4 and not any(k.endswith((".weight_scale", ".weight_scale_inv")) for k in self.model_tensors): - return - - tensors_to_remove: list[str] = [] - new_tensors: dict[str, Callable[[], Tensor]] = {} - - if (quant_config := self.hparams.get("quantization_config")) and isinstance(quant_config, dict): - quant_method = quant_config.get("quant_method") - - def dequant_bitnet(weight: Tensor, scale: Tensor) -> Tensor: - weight = weight.view(torch.uint8) - orig_shape = weight.shape - - shift = torch.tensor([0, 2, 4, 6], dtype=torch.uint8).reshape((4, *(1 for _ in range(len(orig_shape))))) - data = weight.unsqueeze(0).expand((4, *orig_shape)) >> shift - data = data & 3 - data = (data.float() - 1).reshape((orig_shape[0] * 4, *orig_shape[1:])) - - # The scale is inverted - return data / scale.float() - - def dequant_simple(weight: Tensor, scale: Tensor, block_size: Sequence[int] | None = None) -> Tensor: - scale = scale.float() - - if block_size is not None: - dim_offset = scale.ndim - len(block_size) - for i, size in enumerate(block_size): - scale = scale.repeat_interleave(size, dim_offset + i) - # unpad the scale (e.g. when the tensor size isn't a multiple of the block size) - scale = scale[tuple(slice(0, size) for size in weight.shape)] - - # align scale dims to weight for correct broadcasting (e.g. [128] -> [128, 1, 1]) - while scale.ndim < weight.ndim: - scale = scale.unsqueeze(-1) - - return weight.float() * scale - - # ref: https://github.com/ModelCloud/GPTQModel/blob/037c5c0f6c9e33c500d975b038d02e7ca437546d/gptqmodel/nn_modules/qlinear/__init__.py#L437-L476 - def dequant_gptq(g_idx: Tensor, qweight: Tensor, qzeros: Tensor, scales: Tensor) -> Tensor: - bits = quant_config["bits"] - assert bits in (2, 3, 4, 8) - assert qweight.dtype == qzeros.dtype - maxq = (2 ** bits) - 1 - weight = None - zeros = None - pack_dtype_bits = qweight.dtype.itemsize * 8 - - if bits in [2, 4, 8]: - pack_factor = pack_dtype_bits // bits - wf = torch.tensor(list(range(0, pack_dtype_bits, bits)), dtype=torch.int32).unsqueeze(0) - if self.lazy: - wf = LazyTorchTensor.from_eager(wf) - - zeros = torch.bitwise_right_shift( - qzeros.unsqueeze(2).expand(-1, -1, pack_factor), - wf.unsqueeze(0) - ).to(torch.int16 if bits == 8 else torch.int8) - zeros = torch.bitwise_and(zeros, maxq).reshape(scales.shape) - - weight = torch.bitwise_and( - torch.bitwise_right_shift( - qweight.unsqueeze(1).expand(-1, pack_factor, -1), - wf.unsqueeze(-1) - ).to(torch.int16 if bits == 8 else torch.int8), - maxq - ) - elif bits == 3: - raise NotImplementedError("3-bit gptq dequantization is not yet implemented") - - assert weight is not None - assert zeros is not None - - weight = weight.reshape(weight.shape[0] * weight.shape[1], weight.shape[2]) - - # gptq_v2 doesn't need to offset zeros - if quant_config.get("checkpoint_format", "gptq") == "gptq": - zeros += 1 - - return (scales[g_idx].float() * (weight - zeros[g_idx]).float()).T - - def dequant_packed(w: Tensor, scale: Tensor, shape_tensor: Tensor, zero_point: Tensor | None, num_bits: int, group_size: int): - assert w.dtype == torch.int32 - shape = tuple(shape_tensor.tolist()) - assert len(shape) == 2 - mask = (1 << num_bits) - 1 - - shifts = torch.arange(0, 32 - (num_bits - 1), num_bits, dtype=torch.int32) - if self.lazy: - shifts = LazyTorchTensor.from_eager(shifts) - - if zero_point is None: - offset = 1 << (num_bits - 1) - else: - assert len(zero_point.shape) == 2 - offset = (zero_point.unsqueeze(1) >> shifts.reshape(1, -1, 1)) & mask - offset = offset.reshape(-1, zero_point.shape[1]) - # trim padding, and prepare for broadcast - # NOTE: the zero-point is packed along dim 0 - offset = offset[:shape[0], :].unsqueeze(-1) - - # extract values - # NOTE: the weights are packed along dim 1 - unpacked = (w.unsqueeze(-1) >> shifts.reshape(1, 1, -1)) & mask - unpacked = unpacked.reshape(shape[0], -1) - - # trim padding - unpacked = unpacked[:, :shape[1]] - - # prepare for broadcast of the scale - unpacked = unpacked.reshape(shape[0], (unpacked.shape[-1] + group_size - 1) // group_size, group_size) - unpacked = unpacked - offset - - return (unpacked * scale.unsqueeze(-1).float()).reshape(shape) - - if quant_method == "bitnet": - for name in self.model_tensors.keys(): - if name.endswith(".weight_scale"): - weight_name = name.removesuffix("_scale") - w = self.model_tensors[weight_name] - s = self.model_tensors[name] - self.model_tensors[weight_name] = lambda w=w, s=s: dequant_bitnet(w(), s()) - tensors_to_remove.append(name) - elif quant_method == "fp8": - block_size = quant_config.get("weight_block_size") - for name in self.model_tensors.keys(): - if name.endswith("_scale_inv"): - weight_name = name.removesuffix("_scale_inv") - w = self.model_tensors[weight_name] - s = self.model_tensors[name] - self.model_tensors[weight_name] = lambda w=w, s=s, bs=block_size: dequant_simple(w(), s(), bs) - tensors_to_remove.append(name) - if name.endswith(".activation_scale"): # unused - tensors_to_remove.append(name) - if name.endswith("_activation_scale"): # Mistral-Small-4-119B-2602, unused - tensors_to_remove.append(name) - # mistral format - if name.endswith(".qscale_weight"): - weight_name = name.removesuffix("qscale_weight") + "weight" - w = self.model_tensors[weight_name] - s = self.model_tensors[name] - self.model_tensors[weight_name] = lambda w=w, s=s, bs=block_size: dequant_simple(w(), s(), bs) - tensors_to_remove.append(name) - if name.endswith(".qscale_act"): - tensors_to_remove.append(name) - elif quant_method == "gptq": - for name in self.model_tensors.keys(): - if name.endswith(".qweight"): - base_name = name.removesuffix(".qweight") - g_idx = self.model_tensors[base_name + ".g_idx"] - qweight = self.model_tensors[base_name + ".qweight"] - qzeros = self.model_tensors[base_name + ".qzeros"] - scales = self.model_tensors[base_name + ".scales"] - new_tensors[base_name + ".weight"] = ( - lambda g=g_idx, z=qzeros, w=qweight, s=scales: dequant_gptq( - g(), w(), z(), s() - ) - ) - tensors_to_remove += [ - base_name + n - for n in ( - ".g_idx", - ".qzeros", - ".qweight", - ".scales", - ) - ] - elif quant_method == "compressed-tensors": - quant_format = quant_config["format"] - groups = quant_config["config_groups"] - if len(groups) > 1: - raise NotImplementedError("Can't handle multiple config groups for compressed-tensors yet") - weight_config = tuple(groups.values())[0]["weights"] - - if quant_format == "float-quantized" or quant_format == "int-quantized" or quant_format == "naive-quantized": - block_size = weight_config.get("block_structure", None) - strategy = weight_config.get("strategy") - assert strategy == "channel" or strategy == "block" - assert weight_config.get("group_size") is None # didn't find a model using this yet - for name in self.model_tensors.keys(): - if name.endswith(".weight_scale"): - weight_name = name.removesuffix("_scale") - w = self.model_tensors[weight_name] - s = self.model_tensors[name] - self.model_tensors[weight_name] = lambda w=w, s=s: dequant_simple(w(), s(), block_size) - tensors_to_remove.append(name) - elif quant_format == "pack-quantized": - assert weight_config.get("strategy") == "group" - assert weight_config.get("type", "int") == "int" - num_bits = weight_config.get("num_bits") - group_size = weight_config.get("group_size") - assert isinstance(num_bits, int) - assert isinstance(group_size, int) - for name in self.model_tensors.keys(): - if name.endswith(".weight_packed"): - base_name = name.removesuffix("_packed") - w = self.model_tensors[name] - scale = self.model_tensors[base_name + "_scale"] - shape = self.model_tensors[base_name + "_shape"] - zero_point = self.model_tensors.get(base_name + "_zero_point", lambda: None) - new_tensors[base_name] = ( - lambda w=w, scale=scale, shape=shape, zero_point=zero_point: dequant_packed( - w(), scale(), shape(), zero_point(), num_bits, group_size, - ) - ) - tensors_to_remove += [base_name + n for n in ("_packed", "_shape", "_scale")] - if (base_name + "_zero_point") in self.model_tensors: - tensors_to_remove.append(base_name + "_zero_point") - else: - raise NotImplementedError(f"Quant format {quant_format!r} for method {quant_method!r} is not yet supported") - elif quant_method == "modelopt": - # Mixed-precision ModelOpt models: NVFP4 tensors are handled by - # _generate_nvfp4_tensors; FP8 tensors have 1D weight_scale and - # are dequantized here. k/v scale tensors are unused. - for name in self.model_tensors.keys(): - if name.endswith(".weight_scale"): - weight_name = name.removesuffix("_scale") - w = self.model_tensors[weight_name] - s = self.model_tensors[name] - self.model_tensors[weight_name] = lambda w=w, s=s: dequant_simple(w(), s(), None) - tensors_to_remove.append(name) - if name.endswith((".input_scale", ".k_scale", ".v_scale")): - tensors_to_remove.append(name) - elif quant_method is not None: - raise NotImplementedError(f"Quant method is not yet supported: {quant_method!r}") - - for name in tensors_to_remove: - if name in self.model_tensors: - del self.model_tensors[name] - - for name, value in new_tensors.items(): - self.model_tensors[name] = value - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith("e_score_correction_bias"): - name = name.replace("e_score_correction_bias", "e_score_correction.bias") - - if "language_model." in name: - name = name.replace("language_model.", "") - - return name, gen - - def get_tensors(self) -> Iterator[tuple[str, Tensor]]: - for name, gen in self.model_tensors.items(): - yield name, gen() - - def format_tensor_name(self, key: gguf.MODEL_TENSOR, bid: int | None = None, suffix: str = ".weight") -> str: - if key not in gguf.MODEL_TENSORS[self.model_arch]: - raise ValueError(f"Missing {key!r} for MODEL_TENSORS of {self.model_arch!r}") - name: str = gguf.TENSOR_NAMES[key] - if "{bid}" in name: - assert bid is not None - name = name.format(bid=bid) - return name + suffix - - def match_model_tensor_name(self, name: str, key: gguf.MODEL_TENSOR, bid: int | None, suffix: str = ".weight") -> bool: - if key not in gguf.MODEL_TENSORS[self.model_arch]: - return False - key_name: str = gguf.TENSOR_NAMES[key] - if "{bid}" in key_name: - if bid is None: - return False - key_name = key_name.format(bid=bid) - else: - if bid is not None: - return False - return name == (key_name + suffix) - - def map_tensor_name(self, name: str, try_suffixes: Sequence[str] = (".weight", ".bias")) -> str: - new_name = self.tensor_map.get_name(key=name, try_suffixes=try_suffixes) - if new_name is None: - raise ValueError(f"Can not map tensor {name!r}") - return new_name - - def set_gguf_parameters(self): - raise NotImplementedError("set_gguf_parameters() must be implemented in subclasses") - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - new_name = self.map_tensor_name(name) - - # Handle gate/up expert tensor fusion if enabled - if self.fuse_gate_up_exps and bid is not None: - if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_GATE_EXP, bid): - self._gate_exp_buffer[bid] = data_torch - elif self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_UP_EXP, bid): - self._up_exp_buffer[bid] = data_torch - - # Check if both gate and up are buffered for this layer - if bid in self._gate_exp_buffer and bid in self._up_exp_buffer: - gate_data = self._gate_exp_buffer.pop(bid) - up_data = self._up_exp_buffer.pop(bid) - # gate/up shape: (n_expert, n_ff, n_embd), concatenate to (n_expert, n_ff*2, n_embd) - fused_data = torch.cat([gate_data, up_data], dim=1) - fused_name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_UP_EXP, bid) - logger.info(f"Fused gate_exps and up_exps for layer {bid}") - return [(fused_name, fused_data)] - - # If we buffered a gate/up tensor, wait for the other - if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_GATE_EXP, bid) or \ - self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.FFN_UP_EXP, bid): - return [] - - return [(new_name, data_torch)] - - def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool: - del name, new_name, bid, n_dims # unused - - return False - - # some models need extra generated tensors (like rope_freqs) - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - return () - - @staticmethod - def _nvfp4_pack(weight: Tensor, scale: Tensor) -> tuple[np.ndarray, list[int]]: - """Repack NVFP4 ModelOpt tensors into ggml super-block layout. - Preserves original E4M3 scale bits as UE4M3 (strip sign bit). - The per-tensor scale2 factor is stored as a separate tensor and applied at inference time via ggml_mul(). - Returns (raw_data, logical_shape).""" - - out_features = weight.shape[0] - n_blocks = scale.shape[1] - - # Unpack ModelOpt nibble-packed weights - w = weight.reshape(out_features, n_blocks, 8) - vals = torch.stack([w & 0x0F, w >> 4], dim=-1).reshape(out_features, n_blocks, 16) - - # Preserve original E4M3 scale bits as UE4M3 (strip sign bit) - d_ue = scale.view(torch.uint8).numpy().reshape(out_features, n_blocks) & 0x7F - qs = (vals[:, :, :8] | (vals[:, :, 8:] << 4)).to(torch.uint8).numpy() - - # Pack into super-blocks: [4 UE4M3 scales, 32 qs bytes] = 36 bytes per 64 elements - n_super = n_blocks // 4 - d_grouped = d_ue.reshape(out_features, n_super, 4) - qs_grouped = qs.reshape(out_features, n_super, 4, 8).reshape(out_features, n_super, 32) - raw = np.concatenate([d_grouped, qs_grouped], axis=-1).reshape(out_features, n_super * 36) - return raw, [out_features, n_super * 64] - - def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): - new_name = self.map_tensor_name(name) - - raw, shape = self._nvfp4_pack(weight, scale) - logger.info(f"Repacked {new_name} with shape {shape} and quantization NVFP4") - self.gguf_writer.add_tensor(new_name, raw, raw_dtype=gguf.GGMLQuantizationType.NVFP4) - - self._write_scale_tensor(new_name.replace(".weight", ".scale"), scale2) - self._write_scale_tensor(new_name.replace(".weight", ".input_scale"), input_scale) - - def _generate_nvfp4_tensors(self): - # Per-layer expert merging to avoid holding all experts in memory - expert_blocks: dict[tuple[int, str], list[tuple[int, np.ndarray]]] = {} - expert_scales: dict[tuple[int, str], list[tuple[int, float]]] = {} - expert_input_scales: dict[tuple[int, str], list[tuple[int, float]]] = {} - expert_shapes: dict[tuple[int, str], list[int]] = {} - n_experts = self.find_hparam(["num_local_experts", "num_experts"], optional=True) or 0 - consumed: list[str] = [] - - for name in self.model_tensors.keys(): - if not name.endswith(".weight"): - continue - scale_name = name.replace(".weight", ".weight_scale") - scale2_name = name.replace(".weight", ".weight_scale_2") - input_scale_name = name.replace(".weight", ".input_scale") - if scale_name not in self.model_tensors: - continue - # Force eager materialization of lazy tensors - weight = LazyTorchTensor.to_eager(self.model_tensors[name]()) - scale = LazyTorchTensor.to_eager(self.model_tensors[scale_name]()) - - # Skip non-NVFP4 tensors (e.g. FP8 with per-channel 1D scales) - if scale.ndim < 2: - continue - - scale2 = LazyTorchTensor.to_eager(self.model_tensors.get(scale2_name, lambda: torch.tensor(1.0))()) - input_scale = LazyTorchTensor.to_eager(self.model_tensors.get(input_scale_name, lambda: torch.tensor(1.0))()) - - # Mark tensors for removal from model_tensors (already written to gguf) - consumed.extend([name, scale_name]) - if scale2_name in self.model_tensors: - consumed.append(scale2_name) - if input_scale_name in self.model_tensors: - consumed.append(input_scale_name) - - # Check if this is a per-expert tensor - m = re.search(r'\.experts\.(\d+)\.(gate_proj|up_proj|down_proj)\.weight$', name) - if m: - expert_id = int(m.group(1)) - proj_type = m.group(2) - bid_m = re.search(r'\.layers\.(\d+)\.', name) - bid = int(bid_m.group(1)) if bid_m else 0 - key = (bid, proj_type) - - raw, shape = self._nvfp4_pack(weight, scale) - - if key not in expert_blocks: - expert_blocks[key] = [] - expert_scales[key] = [] - expert_input_scales[key] = [] - expert_shapes[key] = shape - expert_blocks[key].append((expert_id, raw.copy())) - # Collect per-expert scale2 (scalar per expert) - expert_scales[key].append((expert_id, float(scale2.float().sum()))) - # Collect per-expert input_scale (scalar per expert) - expert_input_scales[key].append((expert_id, float(input_scale.float().sum()))) - - # Flush when all experts for this (layer, proj) are collected - if n_experts > 0 and len(expert_blocks[key]) >= n_experts: - self._flush_nvfp4_experts(key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type) - else: - self._repack_nvfp4(name, weight, scale, scale2, input_scale) - - # Flush any remaining experts (fallback if n_experts was unknown) - for bid, proj_type in list(expert_blocks.keys()): - self._flush_nvfp4_experts((bid, proj_type), expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type) - - # Remove consumed tensors so get_tensors/modify_tensors won't see them - for name in consumed: - self.model_tensors.pop(name, None) - - # Remove any remaining unused auxiliary tensors - for name in list(self.model_tensors.keys()): - if name.endswith((".k_scale", ".v_scale")): - del self.model_tensors[name] - - def _flush_nvfp4_experts(self, key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type): - experts = expert_blocks.pop(key) - scales = expert_scales.pop(key) - input_scales = expert_input_scales.pop(key) - shape = expert_shapes.pop(key) - - experts.sort(key=lambda x: x[0]) - merged = np.stack([e[1] for e in experts], axis=0) - merged_name = f"model.layers.{bid}.mlp.experts.{proj_type}.weight" - new_name = self.map_tensor_name(merged_name) - logger.info(f"Repacked {new_name} with shape [{len(experts)}, {shape[0]}, {shape[1]}] and quantization NVFP4") - self.gguf_writer.add_tensor(new_name, merged, raw_dtype=gguf.GGMLQuantizationType.NVFP4) - - scales.sort(key=lambda x: x[0]) - self._write_scales_tensor(new_name.replace(".weight", ".scale"), [s[1] for s in scales]) - - input_scales.sort(key=lambda x: x[0]) - self._write_scales_tensor(new_name.replace(".weight", ".input_scale"), [s[1] for s in input_scales]) - - del experts, merged - - def prepare_tensors(self): - # detect NVFP4 quantization (ModelOpt format) - quant_algo = (self.hparams.get("quantization_config") or {}).get("quant_algo") - quant_method = (self.hparams.get("quantization_config") or {}).get("quant_method") - quant_layers = (self.hparams.get("quantization_config") or {}).get("quantized_layers") or {} - quant_config_file = self.dir_model / "hf_quant_config.json" - - if (not quant_algo or not quant_layers) and quant_config_file.is_file(): - with open(quant_config_file, "r", encoding="utf-8") as f: - hf_quant_config = json.load(f) - quant_config = hf_quant_config.get("quantization") or {} - producer = hf_quant_config.get("producer") or {} - producer_name = (producer.get("name") or "").lower() - if quant_method is None: - self.hparams.setdefault("quantization_config", {})["quant_method"] = producer_name - quant_algo = quant_config.get("quant_algo", quant_algo) - quant_layers = quant_config.get("quantized_layers", quant_layers) or {} - - # Some models use per-tensor quant_algo (e.g. "MIXED_PRECISION" with - # per-layer NVFP4/FP8) instead of a single global "NVFP4" value. - if quant_algo != "NVFP4": - if any(v.get("quant_algo") == "NVFP4" for v in quant_layers.values() if isinstance(v, dict)): - quant_algo = "NVFP4" - - self._is_nvfp4 = quant_algo == "NVFP4" - self._is_mxfp4 = quant_method == "mxfp4" - - # NVFP4 weights are repacked and written directly to gguf_writer. - # This must run before dequant_model so NVFP4 tensors are removed - # from model_tensors, leaving only non-NVFP4 (e.g. FP8) for dequant. - if self._is_nvfp4: - self._generate_nvfp4_tensors() - - self.dequant_model() - - # Handle empty tensor_map for models with block_count=0 (like MobileNetV5) - if self.tensor_map.mapping: - max_name_len = max(len(s) for _, s in self.tensor_map.mapping.values()) + len(".weight,") - else: - max_name_len = len("vision_encoder.weight,") # Default reasonable length - - for name, data_torch in chain(self.generate_extra_tensors(), self.get_tensors()): - # we don't need these - if name.endswith((".attention.masked_bias", ".attention.bias", ".rotary_emb.inv_freq")): - continue - - old_dtype = data_torch.dtype - - # convert any unsupported data types to float32 - if data_torch.dtype not in (torch.float16, torch.float32): - data_torch = data_torch.to(torch.float32) - - # use the first number-like part of the tensor name as the block id - bid = None - for part in name.split("."): - if part.isdecimal(): - bid = int(part) - break - - for new_name, data_torch in (self.modify_tensors(data_torch, name, bid)): - # TODO: why do we squeeze here? - # data = data_torch.squeeze().numpy() - data = data_torch.numpy() - - n_dims = len(data.shape) - data_qtype: gguf.GGMLQuantizationType | bool = self.tensor_force_quant(name, new_name, bid, n_dims) - - # Most of the codebase that takes in 1D tensors or norms only handles F32 tensors - if n_dims <= 1 or new_name.endswith("_norm.weight"): - data_qtype = gguf.GGMLQuantizationType.F32 - - # Conditions should closely match those in llama_model_quantize_internal in llama.cpp - # Some tensor types are always in float32 - if data_qtype is False and ( - any( - self.match_model_tensor_name(new_name, key, bid) - for key in ( - gguf.MODEL_TENSOR.FFN_GATE_INP, - gguf.MODEL_TENSOR.FFN_GATE_INP_SHEXP, - gguf.MODEL_TENSOR.POS_EMBD, - gguf.MODEL_TENSOR.TOKEN_TYPES, - gguf.MODEL_TENSOR.SSM_CONV1D, - gguf.MODEL_TENSOR.SHORTCONV_CONV, - gguf.MODEL_TENSOR.TIME_MIX_FIRST, - gguf.MODEL_TENSOR.TIME_MIX_W1, - gguf.MODEL_TENSOR.TIME_MIX_W2, - gguf.MODEL_TENSOR.TIME_MIX_DECAY_W1, - gguf.MODEL_TENSOR.TIME_MIX_DECAY_W2, - gguf.MODEL_TENSOR.TIME_MIX_LERP_FUSED, - gguf.MODEL_TENSOR.POSNET_NORM1, - gguf.MODEL_TENSOR.POSNET_NORM2, - gguf.MODEL_TENSOR.V_ENC_EMBD_POS, - gguf.MODEL_TENSOR.A_ENC_EMBD_POS, - gguf.MODEL_TENSOR.ALTUP_CORRECT_COEF, - gguf.MODEL_TENSOR.ALTUP_PREDICT_COEF, - # Kimi KDA conv weights should be F32 - gguf.MODEL_TENSOR.SSM_CONV1D_Q, - gguf.MODEL_TENSOR.SSM_CONV1D_K, - gguf.MODEL_TENSOR.SSM_CONV1D_V, - ) - ) - or new_name[-7:] not in (".weight", ".lora_a", ".lora_b") - ): - data_qtype = gguf.GGMLQuantizationType.F32 - - if data_qtype is False and any( - self.match_model_tensor_name(new_name, key, bid) - for key in ( - gguf.MODEL_TENSOR.TOKEN_EMBD, - gguf.MODEL_TENSOR.PER_LAYER_TOKEN_EMBD, - gguf.MODEL_TENSOR.OUTPUT, - gguf.MODEL_TENSOR.ALTUP_ROUTER, - gguf.MODEL_TENSOR.LAUREL_L, - gguf.MODEL_TENSOR.LAUREL_R, - ) - ): - if self.ftype in ( - gguf.LlamaFileType.MOSTLY_TQ1_0, - gguf.LlamaFileType.MOSTLY_TQ2_0, - ): - # TODO: use Q4_K and Q6_K - data_qtype = gguf.GGMLQuantizationType.F16 - - # No override (data_qtype is False), or wants to be quantized (data_qtype is True) - if isinstance(data_qtype, bool): - if self.ftype == gguf.LlamaFileType.ALL_F32: - data_qtype = gguf.GGMLQuantizationType.F32 - elif self.ftype == gguf.LlamaFileType.MOSTLY_F16: - data_qtype = gguf.GGMLQuantizationType.F16 - elif self.ftype == gguf.LlamaFileType.MOSTLY_BF16: - data_qtype = gguf.GGMLQuantizationType.BF16 - elif self.ftype == gguf.LlamaFileType.MOSTLY_Q8_0: - data_qtype = gguf.GGMLQuantizationType.Q8_0 - elif self.ftype == gguf.LlamaFileType.MOSTLY_TQ1_0: - data_qtype = gguf.GGMLQuantizationType.TQ1_0 - elif self.ftype == gguf.LlamaFileType.MOSTLY_TQ2_0: - data_qtype = gguf.GGMLQuantizationType.TQ2_0 - else: - raise ValueError(f"Unknown file type: {self.ftype.name}") - - try: - data = gguf.quants.quantize(data, data_qtype) - except gguf.QuantError as e: - logger.warning("%s, %s", e, "falling back to F16") - data_qtype = gguf.GGMLQuantizationType.F16 - data = gguf.quants.quantize(data, data_qtype) - - shape = gguf.quant_shape_from_byte_shape(data.shape, data_qtype) if data.dtype == np.uint8 else data.shape - - # reverse shape to make it similar to the internal ggml dimension order - shape_str = f"{{{', '.join(str(n) for n in reversed(shape))}}}" - - # n_dims is implicit in the shape - logger.info(f"{f'%-{max_name_len}s' % f'{new_name},'} {old_dtype} --> {data_qtype.name}, shape = {shape_str}") - - self.gguf_writer.add_tensor(new_name, data, raw_dtype=data_qtype) - - def set_type(self): - self.gguf_writer.add_type(gguf.GGUFType.MODEL) - - def prepare_metadata(self, vocab_only: bool): - - total_params, shared_params, expert_params, expert_count = self.gguf_writer.get_total_parameter_count() - - self.metadata = gguf.Metadata.load(self.metadata_override, self.dir_model_card, self.model_name, total_params) - - # If we are using HF model id, set the metadata name to the model id - if self.remote_hf_model_id: - self.metadata.name = self.remote_hf_model_id - - # Fallback to model directory name if metadata name is still missing - if self.metadata.name is None: - self.metadata.name = self.dir_model.name - - if self.ftype in (gguf.LlamaFileType.ALL_F32, gguf.LlamaFileType.MOSTLY_F16, gguf.LlamaFileType.MOSTLY_BF16): - if self._is_nvfp4: - self.ftype = gguf.LlamaFileType.MOSTLY_NVFP4 - elif self._is_mxfp4: - self.ftype = gguf.LlamaFileType.MOSTLY_MXFP4_MOE - - # Generate parameter weight class (useful for leader boards) if not yet determined - if self.metadata.size_label is None and total_params > 0: - self.metadata.size_label = gguf.size_label(total_params, shared_params, expert_params, expert_count) - - self.set_type() - - logger.info("Set meta model") - self.metadata.set_gguf_meta_model(self.gguf_writer) - - logger.info("Set model parameters") - self.set_gguf_parameters() - - logger.info("Set model quantization version") - self.gguf_writer.add_quantization_version(gguf.GGML_QUANT_VERSION) - - def write_vocab(self): - raise NotImplementedError("write_vocab() must be implemented in subclasses") - - def write(self): - self.prepare_tensors() - self.prepare_metadata(vocab_only=False) - self.gguf_writer.write_header_to_file(path=self.fname_out) - self.gguf_writer.write_kv_data_to_file() - self.gguf_writer.write_tensors_to_file(progress=True) - self.gguf_writer.close() - - @staticmethod - def get_model_part_names(dir_model: Path, prefix: str, suffix: str) -> list[str]: - part_names: list[str] = [] - for filename in os.listdir(dir_model): - if filename.startswith(prefix) and filename.endswith(suffix): - part_names.append(filename) - - part_names.sort() - - return part_names - - @staticmethod - def load_hparams(dir_model: Path, is_mistral_format: bool): - if is_mistral_format: - with open(dir_model / "params.json", "r", encoding="utf-8") as f: - config = json.load(f) - return config - - try: - # for security reason, we don't allow loading remote code by default - # if a model need remote code, we will fallback to config.json - config = AutoConfig.from_pretrained(dir_model, trust_remote_code=False).to_dict() - except Exception as e: - logger.warning(f"Failed to load model config from {dir_model}: {e}") - logger.warning("Trying to load config.json instead") - with open(dir_model / "config.json", "r", encoding="utf-8") as f: - config = json.load(f) - if "llm_config" in config: - # rename for InternVL - config["text_config"] = config["llm_config"] - if "lm_config" in config: - # rename for GlmASR - config["text_config"] = config["lm_config"] - if "thinker_config" in config: - # rename for Qwen2.5-Omni - config["text_config"] = config["thinker_config"]["text_config"] - if "language_config" in config: - # rename for DeepSeekOCR - config["text_config"] = config["language_config"] - if "lfm" in config: - # rename for LFM2-Audio - config["text_config"] = config["lfm"] - return config - - @classmethod - def register(cls, *names: str) -> Callable[[AnyModel], AnyModel]: - assert names - - def func(modelcls: AnyModel) -> AnyModel: - model_type = ModelType.MMPROJ if modelcls.model_arch == gguf.MODEL_ARCH.MMPROJ else ModelType.TEXT - for name in names: - cls._model_classes[model_type][name] = modelcls - return modelcls - return func - - @classmethod - def print_registered_models(cls): - for model_type, model_classes in cls._model_classes.items(): - logger.error(f"{model_type.name} models:") - for name in sorted(model_classes.keys()): - logger.error(f" - {name}") - - @classmethod - def from_model_architecture(cls, arch: str, model_type = ModelType.TEXT) -> type[ModelBase]: - try: - return cls._model_classes[model_type][arch] - except KeyError: - raise NotImplementedError(f'Architecture {arch!r} not supported!') from None - - -class TextModel(ModelBase): - model_type = ModelType.TEXT - hf_arch: str - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if not self.is_mistral_format: - self.hf_arch = get_model_architecture(self.hparams, self.model_type) - else: - self.hf_arch = "" - - if "text_config" in self.hparams: - # move the text_config to the root level - self.hparams = {**self.hparams, **self.hparams["text_config"]} - - self.block_count = self.find_hparam(["n_layers", "num_hidden_layers", "n_layer", "num_layers"]) - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - self.rope_parameters = self.hparams.get("rope_parameters", self.hparams.get("rope_scaling")) or {} - - rope_theta = self.find_hparam(["global_rope_theta", "rope_global_theta", "rope_theta_global", "rope_theta", "rotary_emb_base"], optional=True) - local_rope_theta = self.find_hparam(["local_rope_theta", "rope_local_theta", "rope_theta_local", "swa_rope_theta", "rope_local_base_freq"], optional=True) - - # Ensure "rope_theta" and "rope_type" is mirrored in rope_parameters - if "full_attention" not in self.rope_parameters and "sliding_attention" not in self.rope_parameters: - if local_rope_theta is not None: - self.rope_parameters["sliding_attention"] = {"rope_theta": local_rope_theta} - if "rope_theta" not in self.rope_parameters and rope_theta is not None: - self.rope_parameters["rope_theta"] = rope_theta - if "rope_type" not in self.rope_parameters and (rope_type := self.rope_parameters.get("type")) is not None: - self.rope_parameters["rope_type"] = rope_type - - @classmethod - def __init_subclass__(cls): - # can't use an abstract property, because overriding it without type errors - # would require using decorated functions instead of simply defining the property - if "model_arch" not in cls.__dict__: - raise TypeError(f"Missing property 'model_arch' for {cls.__name__!r}") - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip multimodal tensors - if name.startswith(("mlp", "vit.", "vpm.", "siglip2.", "conformer.", "merger.", "resampler.", "sound_encoder.", "sound_projection.", "speech_embeddings.")) \ - or "visual." in name or "vision." in name or "audio." in name or "talker." in name \ - or "vision_" in name or "audio_" in name or "sam_model" in name \ - or "token2wav." in name or "code2wav." in name \ - or "projector." in name or "pre_mm_projector_norm" in name \ - or "image_newline" in name or "view_seperator" in name \ - or "patch_embed" in name or "patch_embedding" in name \ - or "patch_merger." in name or "model.connector." in name: - return None - - return super().filter_tensors(item) - - def set_vocab(self): - self._set_vocab_gpt2() - - def prepare_metadata(self, vocab_only: bool): - super().prepare_metadata(vocab_only=vocab_only) - - total_params = self.gguf_writer.get_total_parameter_count()[0] - # Extract the encoding scheme from the file type name. e.g. 'gguf.LlamaFileType.MOSTLY_Q8_0' --> 'Q8_0' - output_type: str = self.ftype.name.partition("_")[2] - - # Filename Output - if self.fname_out.is_dir(): - # Generate default filename based on model specification and available metadata - if not vocab_only: - fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, self.metadata.size_label, output_type, model_type="LoRA" if total_params < 0 else None) - else: - fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, size_label=None, output_type=None, model_type="vocab") - - # Use the default filename - self.fname_out = self.fname_out / f"{fname_default}.gguf" - else: - # Output path is a custom defined templated filename - # Note: `not is_dir()` is used because `.is_file()` will not detect - # file template strings as it doesn't actually exist as a file - - # Process templated file name with the output ftype, useful with the "auto" ftype - self.fname_out = self.fname_out.parent / gguf.fill_templated_filename(self.fname_out.name, output_type) - - logger.info("Set model tokenizer") - self.set_vocab() - - def set_gguf_parameters(self): - self.gguf_writer.add_block_count(self.block_count) - - if (n_ctx := self.find_hparam(["max_position_embeddings", "n_ctx", "n_positions", "max_length", "max_sequence_length", "model_max_length"], optional=True)) is not None: - self.gguf_writer.add_context_length(n_ctx) - logger.info(f"gguf: context length = {n_ctx}") - - if (n_embd := self.find_hparam(["hidden_size", "n_embd", "dim"], optional=True)) is not None: - self.gguf_writer.add_embedding_length(n_embd) - logger.info(f"gguf: embedding length = {n_embd}") - - if (n_ff := self.find_hparam(["intermediate_size", "n_inner", "hidden_dim"], optional=True)) is not None: - self.gguf_writer.add_feed_forward_length(n_ff) - logger.info(f"gguf: feed forward length = {n_ff}") - - if (n_head := self.find_hparam(["num_attention_heads", "n_head", "n_heads"], optional=True)) is not None: - self.gguf_writer.add_head_count(n_head) - logger.info(f"gguf: head count = {n_head}") - - if (n_head_kv := self.find_hparam(["num_key_value_heads", "n_kv_heads"], optional=True)) is not None: - self.gguf_writer.add_head_count_kv(n_head_kv) - logger.info(f"gguf: key-value head count = {n_head_kv}") - - if self.hparams.get("is_causal") is False: - self.gguf_writer.add_causal_attention(False) - logger.info("gguf: causal attention = False") - - # TODO: Handle "sliding_attention" similarly when models start implementing it - rope_params = self.rope_parameters.get("full_attention", self.rope_parameters) - if (rope_type := rope_params.get("rope_type")) is not None: - rope_factor = rope_params.get("factor") - rope_gguf_type = gguf.RopeScalingType.NONE - if rope_type == "linear" and rope_factor is not None: - rope_gguf_type = gguf.RopeScalingType.LINEAR - self.gguf_writer.add_rope_scaling_type(rope_gguf_type) - self.gguf_writer.add_rope_scaling_factor(rope_factor) - elif rope_type == "yarn" and rope_factor is not None: - rope_gguf_type = gguf.RopeScalingType.YARN - self.gguf_writer.add_rope_scaling_type(rope_gguf_type) - self.gguf_writer.add_rope_scaling_factor(rope_factor) - self.gguf_writer.add_rope_scaling_orig_ctx_len(rope_params["original_max_position_embeddings"]) - if (yarn_ext_factor := rope_params.get("extrapolation_factor")) is not None: - self.gguf_writer.add_rope_scaling_yarn_ext_factor(yarn_ext_factor) - if (yarn_attn_factor := rope_params.get("attention_factor", rope_params.get("attn_factor"))) is not None: - self.gguf_writer.add_rope_scaling_yarn_attn_factor(yarn_attn_factor) - if (yarn_beta_fast := rope_params.get("beta_fast")) is not None: - self.gguf_writer.add_rope_scaling_yarn_beta_fast(yarn_beta_fast) - if (yarn_beta_slow := rope_params.get("beta_slow")) is not None: - self.gguf_writer.add_rope_scaling_yarn_beta_slow(yarn_beta_slow) - # self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"]) - elif rope_type == "su" or rope_type == "longrope": - rope_gguf_type = gguf.RopeScalingType.LONGROPE - self.gguf_writer.add_rope_scaling_type(rope_gguf_type) - elif rope_type == "dynamic": - # HunYuan, handled in model class - pass - elif rope_type.lower() == "llama3": - # Handled in generate_extra_tensors - pass - else: - logger.warning(f"Unknown RoPE type: {rope_type}") - logger.info(f"gguf: rope scaling type = {rope_gguf_type.name}") - - if "mrope_section" in self.rope_parameters: - mrope_section = self.rope_parameters["mrope_section"] - # Pad to 4 dimensions [time, height, width, extra] - while len(mrope_section) < 4: - mrope_section.append(0) - self.gguf_writer.add_rope_dimension_sections(mrope_section[:4]) - logger.info(f"gguf: mrope sections: {mrope_section[:4]}") - - if (rope_theta := rope_params.get("rope_theta")) is not None: - self.gguf_writer.add_rope_freq_base(rope_theta) - logger.info(f"gguf: rope theta = {rope_theta}") - if (local_rope_theta := self.rope_parameters.get("sliding_attention", {}).get("rope_theta")) is not None: - self.gguf_writer.add_rope_freq_base_swa(local_rope_theta) - logger.info(f"gguf: rope theta swa = {local_rope_theta}") - if (f_rms_eps := self.find_hparam(["rms_norm_eps", "norm_eps"], optional=True)) is not None: - self.gguf_writer.add_layer_norm_rms_eps(f_rms_eps) - logger.info(f"gguf: rms norm epsilon = {f_rms_eps}") - if (f_norm_eps := self.find_hparam(["layer_norm_eps", "layer_norm_epsilon", "norm_epsilon"], optional=True)) is not None: - self.gguf_writer.add_layer_norm_eps(f_norm_eps) - logger.info(f"gguf: layer norm epsilon = {f_norm_eps}") - if (n_experts := self.find_hparam(["num_local_experts", "num_experts"], optional=True)) is not None: - self.gguf_writer.add_expert_count(n_experts) - logger.info(f"gguf: expert count = {n_experts}") - if (n_experts_used := self.find_hparam(["num_experts_per_tok", "num_experts_per_token", "top_k_experts"], optional=True)) is not None: - self.gguf_writer.add_expert_used_count(n_experts_used) - logger.info(f"gguf: experts used count = {n_experts_used}") - if (n_expert_groups := self.hparams.get("n_group")) is not None: - self.gguf_writer.add_expert_group_count(n_expert_groups) - logger.info(f"gguf: expert groups count = {n_expert_groups}") - if (n_group_used := self.hparams.get("topk_group")) is not None: - self.gguf_writer.add_expert_group_used_count(n_group_used) - logger.info(f"gguf: expert groups used count = {n_group_used}") - - if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func"], optional=True)) is not None: - if score_func == "sigmoid": - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) - elif score_func == "softmax": - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SOFTMAX) - else: - raise ValueError(f"Unsupported expert score gating function value: {score_func}") - logger.info(f"gguf: expert score gating function = {score_func}") - - if (head_dim := self.hparams.get("head_dim")) is not None: - self.gguf_writer.add_key_length(head_dim) - self.gguf_writer.add_value_length(head_dim) - - self.gguf_writer.add_file_type(self.ftype) - logger.info(f"gguf: file type = {self.ftype}") - - def write_vocab(self): - if len(self.gguf_writer.tensors) != 1: - raise ValueError('Splitting the vocabulary is not supported') - - self.prepare_metadata(vocab_only=True) - self.gguf_writer.write_header_to_file(path=self.fname_out) - self.gguf_writer.write_kv_data_to_file() - self.gguf_writer.close() - - def does_token_look_special(self, token: str | bytes) -> bool: - if isinstance(token, (bytes, bytearray)): - token_text = token.decode(encoding="utf-8") - elif isinstance(token, memoryview): - token_text = token.tobytes().decode(encoding="utf-8") - else: - token_text = token - - # Some models mark some added tokens which ought to be control tokens as not special. - # (e.g. command-r, command-r-plus, deepseek-coder, gemma{,-2}) - seems_special = token_text in ( - "", # deepseek-coder - "", "<2mass>", "[@BOS@]", # gemma{,-2} - ) - - seems_special = seems_special or (token_text.startswith("<|") and token_text.endswith("|>")) - seems_special = seems_special or (token_text.startswith("<|") and token_text.endswith("|>")) # deepseek-coder - - # TODO: should these be marked as UNUSED instead? (maybe not) - seems_special = seems_special or (token_text.startswith("")) # gemma{,-2} - - return seems_special - - # used for GPT-2 BPE and WordPiece vocabs - def get_vocab_base(self) -> tuple[list[str], list[int], str]: - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - vocab_size = self.hparams.get("vocab_size", len(tokenizer.vocab)) # ty: ignore[unresolved-attribute] - assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute] - - tokpre = self.get_vocab_base_pre(tokenizer) - - reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token: str = reverse_vocab[i] - if token in added_vocab: - # The tokenizer in llama.cpp assumes the CONTROL and USER_DEFINED tokens are pre-normalized. - # To avoid unexpected issues - we make sure to normalize non-normalized tokens - if not added_tokens_decoder[i].normalized: - previous_token = token - token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] - if previous_token != token: - logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") - - if added_tokens_decoder[i].special or self.does_token_look_special(token): - toktypes.append(gguf.TokenType.CONTROL) - else: - # NOTE: this was added for Gemma. - # Encoding and decoding the tokens above isn't sufficient for this case. - token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces - toktypes.append(gguf.TokenType.USER_DEFINED) - else: - toktypes.append(gguf.TokenType.NORMAL) - tokens.append(token) - - return tokens, toktypes, tokpre - - # NOTE: this function is generated by convert_hf_to_gguf_update.py - # do not modify it manually! - # ref: https://github.com/ggml-org/llama.cpp/pull/6920 - # Marker: Start get_vocab_base_pre - def get_vocab_base_pre(self, tokenizer) -> str: - # encoding this string and hashing the resulting tokens would (hopefully) give us a unique identifier that - # is specific for the BPE pre-tokenizer used by the model - # we will use this unique identifier to write a "tokenizer.ggml.pre" entry in the GGUF file which we can - # use in llama.cpp to implement the same pre-tokenizer - - chktxt = '\n \n\n \n\n\n \t \t\t \t\n \n \n \n \n🚀 (normal) 😶\u200d🌫️ (multiple emojis concatenated) ✅ 🦙🦙 3 33 333 3333 33333 333333 3333333 33333333 3.3 3..3 3...3 កាន់តែពិសេសអាច😁 ?我想在apple工作1314151天~ ------======= нещо на Български \'\'\'\'\'\'```````""""......!!!!!!?????? I\'ve been \'told he\'s there, \'RE you sure? \'M not sure I\'ll make it, \'D you like some tea? We\'Ve a\'lL' - - chktok = tokenizer.encode(chktxt) - chkhsh = sha256(str(chktok).encode()).hexdigest() - - logger.debug(f"chktok: {chktok}") - logger.debug(f"chkhsh: {chkhsh}") - - res = None - - # NOTE: if you get an error here, you need to update the convert_hf_to_gguf_update.py script - # or pull the latest version of the model from Huggingface - # don't edit the hashes manually! - if chkhsh == "b6e8e1518dc4305be2fe39c313ed643381c4da5db34a98f6a04c093f8afbe99b": - # ref: https://huggingface.co/THUDM/glm-4-9b-chat - res = "chatglm-bpe" - if chkhsh == "81d72c7348a9f0ebe86f23298d37debe0a5e71149e29bd283904c02262b27516": - # ref: https://huggingface.co/THUDM/glm-4-9b-chat - res = "chatglm-bpe" - if chkhsh == "a1336059768a55c99a734006ffb02203cd450fed003e9a71886c88acf24fdbc2": - # ref: https://huggingface.co/THUDM/glm-4-9b-hf - res = "glm4" - if chkhsh == "9ca2dd618e8afaf09731a7cf6e2105b373ba6a1821559f258b272fe83e6eb902": - # ref: https://huggingface.co/zai-org/GLM-4.5-Air - res = "glm4" - if chkhsh == "cdf5f35325780597efd76153d4d1c16778f766173908894c04afc20108536267": - # ref: https://huggingface.co/zai-org/GLM-4.7-Flash - res = "glm4" - if chkhsh == "1431a23e583c97432bc230bff598d103ddb5a1f89960c8f1d1051aaa944d0b35": - # ref: https://huggingface.co/sapienzanlp/Minerva-7B-base-v1.0 - res = "minerva-7b" - if chkhsh == "7e57df22b1fe23a7b1e1c7f3dc4e3f96d43a4eb0836d0c6bdc3436d7b2f1c664": - # ref: https://huggingface.co/tencent/Hunyuan-A13B-Instruct - res = "hunyuan" - if chkhsh == "bba3b3366b646dbdded5dbc42d59598b849371afc42f7beafa914afaa5b70aa6": - # ref: https://huggingface.co/tencent/Hunyuan-4B-Instruct - res = "hunyuan-dense" - if chkhsh == "a6b57017d60e6edb4d88ecc2845188e0eb333a70357e45dcc9b53964a73bbae6": - # ref: https://huggingface.co/tiiuae/Falcon-H1-0.5B-Base - res = "falcon-h1" - if chkhsh == "60476e1243776c4fb1b993dbd7a5f15ac22f83c80afdf425fa5ae01c8d44ef86": - # ref: https://huggingface.co/tiiuae/Falcon-H1-1B-Base - res = "falcon-h1" - if chkhsh == "3eda48b4c4dc7de733d1a8b3e3b4a85243dbbf704da2ee9d42c6beced8897896": - # ref: https://huggingface.co/tiiuae/Falcon-H1-7B-Base - res = "falcon-h1" - if chkhsh == "48f8e02c0359c0bbdd82f26909171fac1c18a457bb47573ed1fe3bbb2c1cfd4b": - # ref: https://huggingface.co/tiiuae/Falcon-H1-34B-Base - res = "falcon-h1" - if chkhsh == "81212dc7cdb7e0c1074ca62c5aeab0d43c9f52b8a737be7b12a777c953027890": - # ref: https://huggingface.co/moonshotai/Kimi-K2-Base - res = "kimi-k2" - if chkhsh == "d4540891389ea895b53b399da6ac824becc30f2fba0e9ddbb98f92e55ca0e97c": - # ref: https://huggingface.co/Qwen/Qwen3-Embedding-0.6B - res = "qwen2" - if chkhsh == "1444df51289cfa8063b96f0e62b1125440111bc79a52003ea14b6eac7016fd5f": - # ref: https://huggingface.co/openbmb/MiniCPM-V-4_6 - res = "qwen35" - if chkhsh == "66b8d4e19ab16c3bfd89bce5d785fb7e0155e8648708a1f42077cb9fe002c273": - # ref: https://huggingface.co/alvarobartt/grok-2-tokenizer - res = "grok-2" - if chkhsh == "b3d1dd861f1d4c5c0d2569ce36baf3f90fe8a102db3de50dd71ff860d91be3df": - # ref: https://huggingface.co/aari1995/German_Semantic_V3 - res = "jina-v2-de" - if chkhsh == "0fe1cf6eda062318a1af7270f3331a85c539a01778ff948e24388e949c5282f4": - # ref: https://huggingface.co/evilfreelancer/ruGPT3XL - res = "gpt-2" - if chkhsh == "0ef9807a4087ebef797fc749390439009c3b9eda9ad1a097abbe738f486c01e5": - # ref: https://huggingface.co/meta-llama/Meta-Llama-3-8B - res = "llama-bpe" - if chkhsh == "049ecf7629871e3041641907f3de7c733e4dbfdc736f57d882ba0b0845599754": - # ref: https://huggingface.co/deepseek-ai/deepseek-llm-7b-base - res = "deepseek-llm" - if chkhsh == "347715f544604f9118bb75ed199f68779f423cabb20db6de6f31b908d04d7821": - # ref: https://huggingface.co/deepseek-ai/deepseek-coder-6.7b-base - res = "deepseek-coder" - if chkhsh == "8aeee3860c56296a157a1fe2fad249ec40aa59b1bb5709f4ade11c4e6fe652ed": - # ref: https://huggingface.co/tiiuae/falcon-7b - res = "falcon" - if chkhsh == "0876d13b50744004aa9aeae05e7b0647eac9d801b5ba4668afc01e709c15e19f": - # ref: https://huggingface.co/BAAI/bge-small-en-v1.5 - res = "bert-bge" - if chkhsh == "9d032fcbd5501f4a38150912590928bfb36091efb5df11b8e2124b0390e3fb1e": - # ref: https://huggingface.co/tiiuae/Falcon3-7B-Base - res = "falcon3" - if chkhsh == "8e62295832751ca1e8f92f2226f403dea30dc5165e448b5bfa05af5340c64ec7": - # ref: https://huggingface.co/BAAI/bge-large-zh-v1.5 - res = "bert-bge-large" - if chkhsh == "b6dc8df998e1cfbdc4eac8243701a65afe638679230920b50d6f17d81c098166": - # ref: https://huggingface.co/mosaicml/mpt-7b - res = "mpt" - if chkhsh == "35d91631860c815f952d711435f48d356ebac988362536bed955d43bfa436e34": - # ref: https://huggingface.co/bigcode/starcoder2-3b - res = "starcoder" - if chkhsh == "3ce83efda5659b07b1ad37ca97ca5797ea4285d9b9ab0dc679e4a720c9da7454": - # ref: https://huggingface.co/openai-community/gpt2 - res = "gpt-2" - if chkhsh == "32d85c31273f8019248f2559fed492d929ea28b17e51d81d3bb36fff23ca72b3": - # ref: https://huggingface.co/stabilityai/stablelm-2-zephyr-1_6b - res = "stablelm2" - if chkhsh == "6221ad2852e85ce96f791f476e0b390cf9b474c9e3d1362f53a24a06dc8220ff": - # ref: https://huggingface.co/smallcloudai/Refact-1_6-base - res = "refact" - if chkhsh == "9c2227e4dd922002fb81bde4fc02b0483ca4f12911410dee2255e4987644e3f8": - # ref: https://huggingface.co/CohereForAI/c4ai-command-r-v01 - res = "command-r" - if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1": - # ref: https://huggingface.co/CohereLabs/tiny-aya-base - res = "tiny_aya" - if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea": - # ref: https://huggingface.co/Qwen/Qwen1.5-7B - res = "qwen2" - if chkhsh == "b6dc8df998e1cfbdc4eac8243701a65afe638679230920b50d6f17d81c098166": - # ref: https://huggingface.co/allenai/OLMo-1.7-7B-hf - res = "olmo" - if chkhsh == "a8594e3edff7c29c003940395316294b2c623e09894deebbc65f33f1515df79e": - # ref: https://huggingface.co/databricks/dbrx-base - res = "dbrx" - if chkhsh == "c7699093ba4255a91e702aa38a596aa81669f3525dae06c2953267dde580f448": - # ref: https://huggingface.co/jinaai/jina-reranker-v1-tiny-en - res = "jina-v1-en" - if chkhsh == "0876d13b50744004aa9aeae05e7b0647eac9d801b5ba4668afc01e709c15e19f": - # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-en - res = "jina-v2-en" - if chkhsh == "171aeeedd6fb548d418a7461d053f11b6f1f1fc9b387bd66640d28a4b9f5c643": - # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-es - res = "jina-v2-es" - if chkhsh == "27949a2493fc4a9f53f5b9b029c82689cfbe5d3a1929bb25e043089e28466de6": - # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-de - res = "jina-v2-de" - if chkhsh == "a023e9fdc5a11f034d3ef515b92350e56fb2af1f66c6b6811a4444ea9bf8763d": - # ref: https://huggingface.co/jinaai/jina-embeddings-v5-text-nano - res = "jina-v5-nano" - if chkhsh == "c136ed14d01c2745d4f60a9596ae66800e2b61fa45643e72436041855ad4089d": - # ref: https://huggingface.co/abacusai/Smaug-Llama-3-70B-Instruct - res = "smaug-bpe" - if chkhsh == "c7ea5862a53e4272c035c8238367063e2b270d51faa48c0f09e9d5b54746c360": - # ref: https://huggingface.co/LumiOpen/Poro-34B-chat - res = "poro-chat" - if chkhsh == "7967bfa498ade6b757b064f31e964dddbb80f8f9a4d68d4ba7998fcf281c531a": - # ref: https://huggingface.co/jinaai/jina-embeddings-v2-base-code - res = "jina-v2-code" - if chkhsh == "7fc505bd3104ca1083b150b17d088b59534ede9bde81f0dd2090967d7fe52cee": - # ref: https://huggingface.co/LumiOpen/Viking-7B - res = "viking" - if chkhsh == "b53802fb28e26d645c3a310b34bfe07da813026ec7c7716883404d5e0f8b1901": - # ref: https://huggingface.co/core42/jais-13b - res = "jais" - if chkhsh == "bc5108ee1eb6a3d600cadd065f63190fbd0554dbc9e4bbd6a0d977970afc8d2a": - # ref: https://huggingface.co/inceptionai/Jais-2-8B-Chat - res = "jais-2" - if chkhsh == "7b3e7548e4308f52a76e8229e4e6cc831195d0d1df43aed21ac6c93da05fec5f": - # ref: https://huggingface.co/WisdomShell/CodeShell-7B - res = "codeshell" - if chkhsh == "63b97e4253352e6f357cc59ea5b583e3a680eaeaf2632188c2b952de2588485e": - # ref: https://huggingface.co/mistralai/Mistral-Nemo-Base-2407 - res = "tekken" - if chkhsh == "855059429035d75a914d1eda9f10a876752e281a054a7a3d421ef0533e5b6249": - # ref: https://huggingface.co/HuggingFaceTB/SmolLM-135M - res = "smollm" - if chkhsh == "3c30d3ad1d6b64202cd222813e7736c2db6e1bd6d67197090fc1211fbc612ae7": - # ref: https://huggingface.co/bigscience/bloom - res = "bloom" - if chkhsh == "bc01ce58980e1db43859146dc51b1758b3b88729b217a74792e9f8d43e479d21": - # ref: https://huggingface.co/TurkuNLP/gpt3-finnish-small - res = "gpt3-finnish" - if chkhsh == "4e2b24cc4770243d65a2c9ec19770a72f08cffc161adbb73fcbb6b7dd45a0aae": - # ref: https://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct - res = "exaone" - if chkhsh == "fcace8b9cac38ce847670c970cd5892031a753a1ef381abd1d9af00f713da085": - # ref: https://huggingface.co/microsoft/phi-2 - res = "phi-2" - if chkhsh == "60824e3c0d9401f89943cbb2fff727f0e2d4c545ba4df2d6e4f09a6db0f5b450": - # ref: https://huggingface.co/facebook/chameleon-7b - res = "chameleon" - if chkhsh == "8b5a93ed704057481f240da0be7e7dca721d7f8f4755263b6807227a2cbeae65": - # ref: https://huggingface.co/sentence-transformers/stsb-roberta-base - res = "roberta-bpe" - if chkhsh == "ad851be1dba641f2e3711822f816db2c265f788b37c63b4e1aeacb9ee92de8eb": - # ref: https://huggingface.co/ai-sage/GigaChat-20B-A3B-instruct - res = "gigachat" - if chkhsh == "d4c8f286ea6b520b3d495c4455483cfa2302c0cfcd4be05d781b6a8a0a7cdaf1": - # ref: https://huggingface.co/Infinigence/Megrez-3B-Instruct - res = "megrez" - if chkhsh == "877081d19cf6996e2c4ff0e1236341e9b7bde288f5311a56a937f0afbbb3aeb5": - # ref: https://huggingface.co/deepseek-ai/DeepSeek-V3 - res = "deepseek-v3" - if chkhsh == "b3f499bb4255f8ca19fccd664443283318f2fd2414d5e0b040fbdd0cc195d6c5": - # ref: https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B - res = "deepseek-r1-qwen" - if chkhsh == "ccc2ef013c104be7bae2965776d611e1d7a8a2a9c547dd93a682c9a9fc80352e": - # ref: https://huggingface.co/Xenova/gpt-4o - res = "gpt-4o" - if chkhsh == "7dec86086fcc38b66b7bc1575a160ae21cf705be7718b9d5598190d7c12db76f": - # ref: https://huggingface.co/UW/OLMo2-8B-SuperBPE-t180k - res = "superbpe" - if chkhsh == "1994ffd01900cfb37395608534236ecd63f2bd5995d6cb1004dda1af50240f15": - # ref: https://huggingface.co/trillionlabs/Trillion-7B-preview - res = "trillion" - if chkhsh == "96a5f08be6259352137b512d4157e333e21df7edd3fcd152990608735a65b224": - # ref: https://huggingface.co/inclusionAI/Ling-lite - res = "bailingmoe" - if chkhsh == "d353350c764d8c3b39c763113960e4fb4919bea5fbf208a0e3b22e8469dc7406": - # ref: https://huggingface.co/meta-llama/Llama-4-Scout-17B-16E-Instruct - res = "llama4" - if chkhsh == "0e9433cbbb161f89e264eb32e8e64bfe69e834973ffca5d41d3948a604a3e2a3": - # ref: https://huggingface.co/mistral-community/pixtral-12b - res = "pixtral" - if chkhsh == "d5f1dd6f980fec569fb218a81a7658ac45fc56b38c5a0adeb1c232fbe04ef5ec": - # ref: https://huggingface.co/ByteDance-Seed/Seed-Coder-8B-Base - res = "seed-coder" - if chkhsh == "b0a6b1c0bd5998ebd9df08611efde34a4ff03faed45ae09c43e6b31ebd4b94cf": - # ref: https://huggingface.co/skt/A.X-4.0 - res = "a.x-4.0" - if chkhsh == "f6791d196f87ce6b56a7d234be618e0d58f8cda3549416635b2bebcd22cd95c4": - # ref: https://huggingface.co/K-intelligence/Midm-2.0-Base-Instruct - res = "midm-2.0" - if chkhsh == "169bf0296a13c4d9b7672313f749eb36501d931022de052aad6e36f2bf34dd51": - # ref: https://huggingface.co/LiquidAI/LFM2-Tokenizer - res = "lfm2" - if chkhsh == "2085e1638f6c377a0aa4ead21b27bb4cb941bf800df86ed391011769c1758dfb": - # ref: https://huggingface.co/LGAI-EXAONE/EXAONE-4.0-32B - res = "exaone4" - if chkhsh == "a1e163ecab2e718a4c829d1148b6e86824ec36163bb71941c3dca9cd5ac25756": - # ref: https://huggingface.co/JetBrains/Mellum-4b-base - res = "mellum" - if chkhsh == "a0b64b4385f123663873756336c085744376d015ff328bb1d901598f63c44152": - # ref: https://huggingface.co/answerdotai/ModernBERT-base - res = "modern-bert" - if chkhsh == "49fc0303c9e0d2c2c565c510f64b2d9b271276acdcdadff733249eda9f7d59df": - # ref: https://huggingface.co/arcee-ai/Trinity-Tokenizer - res = "afmoe" - if chkhsh == "9b1be57e70d20d9501b2b3186e792d81181ae36ada3903c26f9fea418cf87206": - # ref: https://huggingface.co/inclusionAI/Ling-mini-base-2.0 - res = "bailingmoe2" - if chkhsh == "53e325976a6e142379c19b09afcae354f2f496f147afa8f9e189a33fe4e3024e": - # ref: https://huggingface.co/ibm-granite/granite-docling-258M - res = "granite-docling" - if chkhsh == "f4f37b6c8eb9ea29b3eac6bb8c8487c5ab7885f8d8022e67edc1c68ce8403e95": - # ref: https://huggingface.co/MiniMaxAI/MiniMax-M2 - res = "minimax-m2" - if chkhsh == "4a2e2abae11ca2b86d570fc5b44be4d5eb5e72cc8f22dd136a94b37da83ab665": - # ref: https://huggingface.co/KORMo-Team/KORMo-tokenizer - res = "kormo" - if chkhsh == "9d70134b369a70e5735009b6de918f7581b5211f7c074d1f89f753aea8248af1": - # ref: https://huggingface.co/tencent/Youtu-LLM-2B - res = "youtu" - if chkhsh == "16389f0a1f51ee53e562ffd51c371dc508639ab0e4261502071836e50e223e91": - # ref: https://huggingface.co/upstage/Solar-Open-100B - res = "solar-open" - if chkhsh == "6c81ce329e0802883b22eabab0d3fa48357337ef1ecb45443828bf1f6254833f": - # ref: https://huggingface.co/LGAI-EXAONE/K-EXAONE-236B-A23B - res = "exaone-moe" - if chkhsh == "d30d75d9059f1aa2c19359de71047b3ae408c70875e8a3ccf8c5fba56c9d8af4": - # ref: https://huggingface.co/Qwen/Qwen3.5-9B-Instruct - res = "qwen35" - if chkhsh == "b4b8ca1f9769494fbd956ebc4c249de6131fb277a4a3345a7a92c7dd7a55808d": - # ref: https://huggingface.co/jdopensource/JoyAI-LLM-Flash - res = "joyai-llm" - if chkhsh == "e4d54df1ebc1f2b91acd986c5b51aa50837d5faf7c7398e73c1f9e9ee5d19869": - # ref: https://huggingface.co/kakaocorp/kanana-2-30b-a3b-instruct-2601 - res = "kanana2" - if chkhsh == "862f827721df956049dff5ca81a57f29e575280bc622e290d3bf4e35eca29015": - # ref: https://huggingface.co/codefuse-ai/F2LLM-v2-4B - res = "f2llmv2" - if chkhsh == "62f6fb0a6fd5098caeabb19b07a5c1099cafc8b9c40eab6ea89ece4ec02fbc57": - # ref: https://huggingface.co/sarvamai/sarvam-30b - res = "sarvam-moe" - - if res is None: - logger.warning("\n") - logger.warning("**************************************************************************************") - logger.warning("** WARNING: The BPE pre-tokenizer was not recognized!") - logger.warning("** There are 2 possible reasons for this:") - logger.warning("** - the model has not been added to convert_hf_to_gguf_update.py yet") - logger.warning("** - the pre-tokenization config has changed upstream") - logger.warning("** Check your model files and convert_hf_to_gguf_update.py and update them accordingly.") - logger.warning("** ref: https://github.com/ggml-org/llama.cpp/pull/6920") - logger.warning("**") - logger.warning(f"** chkhsh: {chkhsh}") - logger.warning("**************************************************************************************") - logger.warning("\n") - raise NotImplementedError("BPE pre-tokenizer was not recognized - update get_vocab_base_pre()") - - logger.debug(f"tokenizer.ggml.pre: {repr(res)}") - logger.debug(f"chkhsh: {chkhsh}") - - return res - # Marker: End get_vocab_base_pre - - def _set_vocab_none(self) -> None: - self.gguf_writer.add_tokenizer_model("none") - - def _set_vocab_gpt2(self) -> None: - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_qwen(self): - dir_model = self.dir_model - hparams = self.hparams - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) - vocab_size = hparams["vocab_size"] - assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] - - tokpre = self.get_vocab_base_pre(tokenizer) - - merges = [] - vocab = {} - mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] - for token, rank in mergeable_ranks.items(): - vocab[QwenModel.token_bytes_to_string(token)] = rank - if len(token) == 1: - continue - merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) - assert len(merged) == 2 - merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) - - # for this kind of tokenizer, added_vocab is not a subset of vocab, so they need to be combined - added_vocab = tokenizer.special_tokens # ty: ignore[unresolved-attribute] - reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **added_vocab}.items()} - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - elif reverse_vocab[i] in added_vocab: - tokens.append(reverse_vocab[i]) - toktypes.append(gguf.TokenType.CONTROL) - else: - tokens.append(reverse_vocab[i]) - toktypes.append(gguf.TokenType.NORMAL) - - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(dir_model, load_merges=False) - special_vocab.merges = merges - # only add special tokens when they were not already loaded from config.json - if len(special_vocab.special_token_ids) == 0: - special_vocab._set_special_token("bos", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("eos", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - # this one is usually not in config.json anyway - special_vocab._set_special_token("unk", tokenizer.special_tokens["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_sentencepiece(self, add_to_gguf=True): - tokens, scores, toktypes = self._create_vocab_sentencepiece() - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def _create_vocab_sentencepiece(self): - from sentencepiece import SentencePieceProcessor - - tokenizer_path = self.dir_model / 'tokenizer.model' - - if not tokenizer_path.is_file(): - raise FileNotFoundError(f"File not found: {tokenizer_path}") - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.find_hparam([ - "vocab_size_per_layer_input", # gemma3n - "vocab_size", - ], optional=True) or tokenizer.vocab_size() - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - for token_id in range(tokenizer.vocab_size()): - if token_id >= vocab_size: - logger.warning(f'ignore tokens from {token_id}: id is out of range, max={vocab_size - 1}') - break - - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - added_tokens_file = self.dir_model / 'added_tokens.json' - if added_tokens_file.is_file(): - with open(added_tokens_file, "r", encoding="utf-8") as f: - added_tokens_json = json.load(f) - for key in added_tokens_json: - token_id = added_tokens_json[key] - if token_id >= vocab_size: - logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - - tokens[token_id] = key.encode("utf-8") - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) - for token_id, token_data in added_tokens_decoder.items(): - token_id = int(token_id) - token: str = token_data["content"] - if token_id >= vocab_size: - logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: - if tokens[token_id] != token.encode("utf-8"): - logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token!r}') - if token_data.get("special") or self.does_token_look_special(token): - toktypes[token_id] = SentencePieceTokenTypes.CONTROL - else: - token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - - scores[token_id] = -1000.0 - tokens[token_id] = token.encode("utf-8") - - if vocab_size > len(tokens): - pad_count = vocab_size - len(tokens) - logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") - for i in range(1, pad_count + 1): - tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) - scores.append(-1000.0) - toktypes.append(SentencePieceTokenTypes.UNUSED) - - return tokens, scores, toktypes - - def _set_vocab_llama_hf(self): - vocab = gguf.LlamaHfVocab(self.dir_model) - tokens = [] - scores = [] - toktypes = [] - - for text, score, toktype in vocab.all_tokens(): - tokens.append(text) - scores.append(score) - toktypes.append(toktype) - - assert len(tokens) == vocab.vocab_size - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_rwkv_world(self): - assert (self.dir_model / "rwkv_vocab_v20230424.txt").is_file() - vocab_size = self.hparams.get("vocab_size", 65536) - - tokens: list[bytes] = [''.encode("utf-8")] - toktypes: list[int] = [gguf.TokenType.CONTROL] - - with open(self.dir_model / "rwkv_vocab_v20230424.txt", "r", encoding="utf-8") as f: - lines = f.readlines() - for line in lines: - parts = line.split(' ') - assert len(parts) >= 3 - token, token_len = ast.literal_eval(' '.join(parts[1:-1])), int(parts[-1]) - token = token.encode("utf-8") if isinstance(token, str) else token - assert isinstance(token, bytes) - assert len(token) == token_len - token_text: str = repr(token)[2:-1] # "b'\xff'" -> "\xff" - tokens.append(token_text.encode("utf-8")) - toktypes.append(gguf.TokenType.NORMAL) - remainder = vocab_size - len(tokens) - assert remainder >= 0 - for i in range(len(tokens), vocab_size): - tokens.append(f"[PAD{i}]".encode("utf-8")) - toktypes.append(gguf.TokenType.UNUSED) - - self.gguf_writer.add_tokenizer_model("rwkv") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) - if special_vocab.chat_template is None: - template_path = Path(__file__).parent / "models" / "templates" / "llama-cpp-rwkv-world.jinja" - if template_path.is_file(): - with open(template_path, "r", encoding="utf-8") as f: - template = f.read() - else: - template = "rwkv-world" - special_vocab.chat_template = template - # hack: Add '\n\n' as the EOT token to make it chat normally - special_vocab._set_special_token("eot", 261) - # hack: Override these as they have already been set (incorrectly) - special_vocab.special_token_ids["bos"] = 0 - special_vocab.special_token_ids["eos"] = 0 - - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_builtin(self, model_name: Literal["gpt-neox", "llama-spm"], vocab_size: int): - tokenizer_path = Path(sys.path[0]) / "models" / f"ggml-vocab-{model_name}.gguf" - logger.warning(f"Using tokenizer from '{os.path.relpath(tokenizer_path, os.getcwd())}'") - vocab_reader = gguf.GGUFReader(tokenizer_path, "r") - - default_pre = "mpt" if model_name == "gpt-neox" else "default" - - field = vocab_reader.get_field(gguf.Keys.Tokenizer.MODEL) - assert field # tokenizer model - self.gguf_writer.add_tokenizer_model(bytes(field.parts[-1]).decode("utf-8")) - - field = vocab_reader.get_field(gguf.Keys.Tokenizer.PRE) - self.gguf_writer.add_tokenizer_pre(bytes(field.parts[-1]).decode("utf-8") if field else default_pre) - - field = vocab_reader.get_field(gguf.Keys.Tokenizer.LIST) - assert field # token list - self.gguf_writer.add_token_list([bytes(field.parts[i]) for i in field.data][:vocab_size]) - - if model_name == "llama-spm": - field = vocab_reader.get_field(gguf.Keys.Tokenizer.SCORES) - assert field # token scores - self.gguf_writer.add_token_scores([field.parts[i].tolist()[0] for i in field.data][:vocab_size]) - - field = vocab_reader.get_field(gguf.Keys.Tokenizer.TOKEN_TYPE) - assert field # token types - self.gguf_writer.add_token_types([field.parts[i].tolist()[0] for i in field.data][:vocab_size]) - - if model_name != "llama-spm": - field = vocab_reader.get_field(gguf.Keys.Tokenizer.MERGES) - assert field # token merges - self.gguf_writer.add_token_merges([bytes(field.parts[i]) for i in field.data]) - - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.BOS_ID)) is not None: - self.gguf_writer.add_bos_token_id(field.parts[-1].tolist()[0]) - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.EOS_ID)) is not None: - self.gguf_writer.add_eos_token_id(field.parts[-1].tolist()[0]) - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.UNK_ID)) is not None: - self.gguf_writer.add_unk_token_id(field.parts[-1].tolist()[0]) - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.PAD_ID)) is not None: - self.gguf_writer.add_pad_token_id(field.parts[-1].tolist()[0]) - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.ADD_BOS)) is not None: - self.gguf_writer.add_add_bos_token(field.parts[-1].tolist()[0]) - if (field := vocab_reader.get_field(gguf.Keys.Tokenizer.ADD_EOS)) is not None: - self.gguf_writer.add_add_eos_token(field.parts[-1].tolist()[0]) - - def _try_set_pooling_type(self) -> None: - # get pooling path - pooling_path = None - module_path = self.dir_model / "modules.json" - if module_path.is_file(): - with open(module_path, encoding="utf-8") as f: - modules = json.load(f) - for mod in modules: - if mod["type"].endswith("Pooling"): - pooling_path = mod["path"] - break - - mode_mapping = { - "mean": gguf.PoolingType.MEAN, - "cls": gguf.PoolingType.CLS, - "lasttoken": gguf.PoolingType.LAST, - } - - # get pooling type - if pooling_path is not None: - with open(self.dir_model / pooling_path / "config.json", encoding="utf-8") as f: - pooling = json.load(f) - if pooling.get("pooling_mode_mean_tokens"): - pooling_type = gguf.PoolingType.MEAN - elif pooling.get("pooling_mode_cls_token"): - pooling_type = gguf.PoolingType.CLS - elif pooling.get("pooling_mode_lasttoken"): - pooling_type = gguf.PoolingType.LAST - elif (pooling_mode := pooling.get("pooling_mode")) in mode_mapping: - pooling_type = mode_mapping[pooling_mode] - else: - raise NotImplementedError("Only MEAN, CLS, and LAST pooling types supported") - self.gguf_writer.add_pooling_type(pooling_type) - - def _set_vocab_glmedge(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_glm(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - # Special tokens - # Note: Using <|endoftext|> (151329) for eot causes endless generation - special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["[gMASK]"]) # ty: ignore[unresolved-attribute] # 151331 - special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] # 151336 - special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] # 151329 - special_vocab._set_special_token("eom", tokenizer.get_added_vocab()["<|observation|>"]) # ty: ignore[unresolved-attribute] # 151338 - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_interns1(self): - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - vocab = getattr(tokenizer, 'vocab', tokenizer.get_vocab()) # ty: ignore[unresolved-attribute] - vocab_size = self.hparams.get("vocab_size", len(vocab)) - assert max(vocab.values()) < vocab_size - - tokpre = self.get_vocab_base_pre(tokenizer) - - reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab.items()} - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token: str = reverse_vocab[i] - if token in added_vocab: - # The tokenizer in llama.cpp assumes the CONTROL and USER_DEFINED tokens are pre-normalized. - # To avoid unexpected issues - we make sure to normalize non-normalized tokens - if not added_tokens_decoder[i].normalized: - previous_token = token - token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] - if previous_token != token: - logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") - - if added_tokens_decoder[i].special or self.does_token_look_special(token): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.USER_DEFINED) - else: - toktypes.append(gguf.TokenType.NORMAL) - tokens.append(token) - - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab._set_special_token("bos", 151643) - special_vocab.add_to_gguf(self.gguf_writer) - - def _set_vocab_mistral(self): - if not _mistral_common_installed: - raise ImportError(_mistral_import_error_msg) - - vocab = MistralVocab(self.dir_model) - logger.info( - f"Converting tokenizer {vocab.tokenizer_type} of size {vocab.vocab_size}." - ) - - self.gguf_writer.add_tokenizer_model(vocab.gguf_tokenizer_model) - - tokens = [] - scores = [] - toktypes = [] - - for text, score, toktype in vocab.all_tokens(): - tokens.append(text) - scores.append(score) - toktypes.append(toktype) - - assert len(tokens) == vocab.vocab_size, ( - f"token count ({len(tokens)}) != vocab size ({vocab.vocab_size})" - ) - - if vocab.tokenizer_type == MistralTokenizerType.tekken: - self.gguf_writer.add_tokenizer_pre("tekken") - self.gguf_writer.add_token_merges( - vocab.extract_vocab_merges_from_model() - ) - - logger.info( - f"Setting bos, eos, unk and pad token IDs to {vocab.bos_id}, {vocab.eos_id}, {vocab.unk_id}, {vocab.pad_id}." - ) - - self.gguf_writer.add_bos_token_id(vocab.bos_id) - self.gguf_writer.add_eos_token_id(vocab.eos_id) - self.gguf_writer.add_unk_token_id(vocab.unk_id) - self.gguf_writer.add_pad_token_id(vocab.pad_id) - - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_vocab_size(vocab.vocab_size) - - self.gguf_writer.add_add_bos_token(True) - self.gguf_writer.add_add_eos_token(False) - - local_template_file_path = self.dir_model / "chat_template.jinja" - - if self.is_mistral_format and local_template_file_path.is_file(): - # Ministral-3 and other new Mistral models come with chat templates. - # ref: https://huggingface.co/mistralai/Ministral-3-14B-Instruct-2512/tree/main - logger.info("Using an existing Mistral local chat template.") - - with open(local_template_file_path, "r", encoding="utf-8") as f: - template = f.read() - elif not self.is_mistral_format or not self.disable_mistral_community_chat_template: - template_dir = Path(__file__).parent / "models/templates/" - - # Log only for Mistral format that the official tokenization and detokenization is via `mistral-common`. - if self.is_mistral_format: - logger.info( - "Using a Mistral community chat template. These templates can be subject to errors in early days or weeks after a release. " - "Mistral recommends to use `mistral-common` to perform tokenization and detokenization." - ) - template = MistralModel.get_community_chat_template(vocab, template_dir, self.is_mistral_format) - else: - logger.info("Not using a Mistral local or community chat template. Ensure to perform the tokenization and detokenization via `mistral-common`.") - template = None - - if template is not None: - self.gguf_writer.add_chat_template(template) - - def _set_vocab_plamo(self): - # PLaMo models use a custom tokenizer with a .jsonl file - tokenizer_jsonl_path = self.dir_model / "tokenizer.jsonl" - tokenizer_config_path = self.dir_model / "tokenizer_config.json" - - if not tokenizer_jsonl_path.is_file(): - raise FileNotFoundError(f"PLaMo tokenizer file not found: {tokenizer_jsonl_path}") - - # Load tokenizer config - with open(tokenizer_config_path, "r", encoding="utf-8") as f: - tokenizer_config = json.load(f) - - # Load tokens from JSONL file (actually a list format) - tokens = [] - scores = [] - toktypes = [] - - with open(tokenizer_jsonl_path, "r", encoding="utf-8") as f: - for line_num, line in enumerate(f): - if line.strip(): - token_data = json.loads(line) - # Format: [token, score, type, ?, ?, ?, ?] - token = token_data[0].encode("utf-8") - score = float(token_data[1]) - token_type_str = token_data[2] if len(token_data) > 2 else "NORMAL" - - tokens.append(token) - scores.append(score) - - if token_type_str == "UNKNOWN": - toktypes.append(gguf.TokenType.UNKNOWN) - elif token_type_str == "CONTROL": - toktypes.append(gguf.TokenType.CONTROL) - elif token_type_str == "BYTE": - toktypes.append(gguf.TokenType.BYTE) - else: - token_str = token_data[0] - if token_str.startswith("<|plamo:") and token_str.endswith("|>"): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.NORMAL) - - vocab_size = self.hparams["vocab_size"] - if vocab_size > len(tokens): - pad_count = vocab_size - len(tokens) - logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") - for i in range(1, pad_count + 1): - tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) - scores.append(-1000.0) - toktypes.append(gguf.TokenType.UNUSED) - - self.gguf_writer.add_tokenizer_model("plamo2") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - if "bos_token" in tokenizer_config and tokenizer_config["bos_token"] is not None: - token_id = tokens.index(tokenizer_config["bos_token"].encode("utf-8")) - self.gguf_writer.add_bos_token_id(token_id) - if "eos_token" in tokenizer_config and tokenizer_config["eos_token"] is not None: - token_id = tokens.index(tokenizer_config["eos_token"].encode("utf-8")) - self.gguf_writer.add_eos_token_id(token_id) - if "pad_token" in tokenizer_config and tokenizer_config["pad_token"] is not None: - token_id = tokens.index(tokenizer_config["pad_token"].encode("utf-8")) - self.gguf_writer.add_pad_token_id(token_id) - if "sep_token" in tokenizer_config and tokenizer_config["sep_token"] is not None: - token_id = tokens.index(tokenizer_config["sep_token"].encode("utf-8")) - self.gguf_writer.add_sep_token_id(token_id) - if "unk_token" in tokenizer_config and tokenizer_config["unk_token"] is not None: - token_id = tokens.index(tokenizer_config["unk_token"].encode("utf-8")) - self.gguf_writer.add_unk_token_id(token_id) - - # Add <|plamo:op|> as EOT to ensure appropriate end of generation - self.gguf_writer.add_eot_token_id(4) - - self.gguf_writer.add_add_space_prefix(False) - - -class MmprojModel(ModelBase): - model_type = ModelType.MMPROJ - model_arch = gguf.MODEL_ARCH.MMPROJ - preprocessor_config: dict[str, Any] - global_config: dict[str, Any] - - n_block_keys = ["n_layers", "num_hidden_layers", "n_layer", "num_layers", "depth", "layers", "encoder_layers", "vt_num_hidden_layers"] - - has_vision_encoder: bool = True # by default - has_audio_encoder: bool = False - - # for models having multiple encoders, we need to separate their hparams - hparams_vision: dict[str, Any] | None = None - hparams_audio: dict[str, Any] | None = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if self.model_arch != gguf.MODEL_ARCH.MMPROJ: - raise TypeError("MmprojModel must be subclassed with model_arch = gguf.MODEL_ARCH.MMPROJ") - - # get n_embd of the text model - if not self.is_mistral_format: - if "text_config" not in self.hparams: - self.hparams["text_config"] = {} - if "audio_config" not in self.hparams: - self.hparams["audio_config"] = {} - text_config = {**self.hparams, **self.hparams["text_config"]} - self.n_embd_text = text_config.get("hidden_size", text_config.get("n_embd", 0)) - else: - text_config = { - k: v for k, v in self.hparams.items() if k not in ["vision_encoder", "audio_encoder"] - } - # mistral native params.json: "dim" is the text hidden size ("hidden_dim" is the FFN intermediate size) - self.n_embd_text = text_config.get("dim", 0) - - assert self.n_embd_text > 0, "n_embd not found in hparams" - - # move vision config to the top level, while preserving the original hparams in global_config - import copy - self.global_config = copy.deepcopy(self.hparams) - self.hparams_vision = self.get_vision_config() - self.hparams_audio = self.get_audio_config() - - if self.hparams_vision is None and self.hparams_audio is None: - raise ValueError("vision_config / audio_config not found in hparams") - - # for compat with vision-only models - self.hparams = self.hparams_vision or self.hparams_audio or self.hparams - - # TODO @ngxson : this is a hack to support both vision and audio encoders - have_multiple_encoders = self.has_audio_encoder and self.has_vision_encoder - self.block_count = 128 if have_multiple_encoders else self.find_hparam(self.n_block_keys, True) - self.tensor_map = gguf.get_tensor_name_map(gguf.MODEL_ARCH.MMPROJ, self.block_count) - - # load preprocessor config - self.preprocessor_config = {} - - # prefer preprocessor_config.json if possible - preprocessor_config_path = self.dir_model / "preprocessor_config.json" - if preprocessor_config_path.is_file(): - with open(preprocessor_config_path, "r", encoding="utf-8") as f: - cfg = json.load(f) - # move media_proc_cfg to root level for compat - if "media_proc_cfg" in cfg: - cfg = { - **cfg, - **cfg["media_proc_cfg"], - } - # merge configs - self.preprocessor_config = {**self.preprocessor_config, **cfg} - - # prefer processor_config.json if possible - processor_config_path = self.dir_model / "processor_config.json" - if processor_config_path.is_file(): - with open(processor_config_path, "r", encoding="utf-8") as f: - cfg = json.load(f) - # move image_processor to root level for compat - if "image_processor" in cfg: - cfg = { - **cfg, - **cfg["image_processor"], - } - # merge configs - self.preprocessor_config = {**self.preprocessor_config, **cfg} - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip non-multimodal tensors - if "language_model." in name: - return None - - return super().filter_tensors(item) - - def get_vision_config(self) -> dict[str, Any] | None: - config_name = "vision_config" if not self.is_mistral_format else "vision_encoder" - return self.global_config.get(config_name) - - def get_audio_config(self) -> dict[str, Any] | None: - mm_config_key = "whisper_config" if "whisper_config" in self.hparams else "audio_config" - return self.global_config.get(mm_config_key) - - def set_type(self): - self.gguf_writer.add_type(gguf.GGUFType.MMPROJ) - - def prepare_metadata(self, vocab_only: bool): - super().prepare_metadata(vocab_only=vocab_only) - - output_type: str = self.ftype.name.partition("_")[2] - - if self.fname_out.is_dir(): - fname_default: str = gguf.naming_convention(self.metadata.name, self.metadata.basename, self.metadata.finetune, self.metadata.version, size_label=None, output_type=output_type, model_type=None) - self.fname_out = self.fname_out / f"mmproj-{fname_default}.gguf" - else: - self.fname_out = self.fname_out.parent / gguf.fill_templated_filename(self.fname_out.name, output_type) - - def set_gguf_parameters(self): - self.gguf_writer.add_file_type(self.ftype) - - if self.has_vision_encoder: - self.gguf_writer.add_clip_has_vision_encoder(True) - self.gguf_writer.add_vision_projection_dim(self.n_embd_text) - - # vision config - self.image_size = self.find_vparam(["image_size"]) - self.gguf_writer.add_vision_image_size(self.image_size) - self.gguf_writer.add_vision_patch_size(self.find_vparam(["patch_size"])) - self.gguf_writer.add_vision_embedding_length(self.find_vparam(["hidden_size", "width", "vt_hidden_size"])) - self.gguf_writer.add_vision_feed_forward_length(self.find_vparam(["intermediate_size", "vt_intermediate_size"])) - self.gguf_writer.add_vision_block_count(self.find_vparam(self.n_block_keys)) - self.gguf_writer.add_vision_head_count(self.find_vparam(["num_attention_heads", "num_heads", "heads", "vt_num_attention_heads"])) - - # preprocessor config - image_mean = _MISTRAL_COMMON_DATASET_MEAN if self.is_mistral_format else self.preprocessor_config["image_mean"] - image_std = _MISTRAL_COMMON_DATASET_STD if self.is_mistral_format else self.preprocessor_config["image_std"] - - self.gguf_writer.add_vision_image_mean(image_mean) - self.gguf_writer.add_vision_image_std(image_std) - - if self.has_audio_encoder: - self.gguf_writer.add_clip_has_audio_encoder(True) - self.gguf_writer.add_audio_projection_dim(self.n_embd_text) - - # audio config - self.gguf_writer.add_audio_embedding_length(self.find_aparam(["hidden_size"])) - self.gguf_writer.add_audio_feed_forward_length(self.find_aparam(["intermediate_size"])) - self.gguf_writer.add_audio_block_count(self.find_aparam(self.n_block_keys)) - self.gguf_writer.add_audio_head_count(self.find_aparam(["num_attention_heads"])) - - if not self.has_vision_encoder and not self.has_audio_encoder: - raise ValueError("MmprojModel must have either vision or audio encoder") - - def write_vocab(self): - raise ValueError("MmprojModel does not support vocab writing") - - def find_vparam(self, keys: Iterable[str], optional: bool = False) -> Any: - assert self.hparams_vision is not None - return self._find_param(self.hparams_vision, keys, optional) - - def find_aparam(self, keys: Iterable[str], optional: bool = False) -> Any: - assert self.hparams_audio is not None - return self._find_param(self.hparams_audio, keys, optional) - - def _find_param(self, obj: dict[str, Any], keys: Iterable[str], optional: bool = False) -> Any: - key = next((k for k in keys if k in obj), None) - if key is not None: - return obj[key] - if optional: - return None - raise KeyError(f"could not find any of: {keys}") - - def tensor_force_quant(self, name, new_name, bid, n_dims): - del bid, name, n_dims # unused - if ".patch_embd.weight" in new_name or ".patch_merger.weight" in new_name: - return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 - return False - - -@ModelBase.register("GPTNeoXForCausalLM") -class GPTNeoXModel(TextModel): - model_arch = gguf.MODEL_ARCH.GPTNEOX - - def set_gguf_parameters(self): - self.gguf_writer.add_context_length(self.hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) - self.gguf_writer.add_rope_dimension_count( - int(self.hparams["rotary_pct"] * (self.hparams["hidden_size"] // self.hparams["num_attention_heads"])), - ) - self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) - self.gguf_writer.add_parallel_residual(self.hparams.get("use_parallel_residual", True)) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_eps"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) - n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) - assert n_head is not None - assert n_embed is not None - - if re.match(r"gpt_neox\.layers\.\d+\.attention\.query_key_value\.weight", name): - # Map bloom-style qkv_linear to gpt-style qkv_linear - # bloom: https://github.com/huggingface/transformers/blob/main/src/transformers/models/bloom/modeling_bloom.py#L238-L252 # noqa - # gpt-2: https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py#L312 # noqa - qkv_weights = data_torch.reshape((n_head, 3, n_embed // n_head, n_embed)) - data_torch = torch.cat( - ( - qkv_weights[:, 0, :, :].reshape((-1, n_embed)), - qkv_weights[:, 1, :, :].reshape((-1, n_embed)), - qkv_weights[:, 2, :, :].reshape((-1, n_embed)), - ), - dim=0, - ) - logger.info("re-format attention.linear_qkv.weight") - elif re.match(r"gpt_neox\.layers\.\d+\.attention\.query_key_value\.bias", name): - qkv_bias = data_torch.reshape((n_head, 3, n_embed // n_head)) - data_torch = torch.cat( - ( - qkv_bias[:, 0, :].reshape((n_embed,)), - qkv_bias[:, 1, :].reshape((n_embed,)), - qkv_bias[:, 2, :].reshape((n_embed,)), - ), - dim=0, - ) - logger.info("re-format attention.linear_qkv.bias") - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("BloomForCausalLM", "BloomModel") -class BloomModel(TextModel): - model_arch = gguf.MODEL_ARCH.BLOOM - - def set_gguf_parameters(self): - n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) - n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) - assert n_head is not None - assert n_embed is not None - self.gguf_writer.add_context_length(self.hparams.get("seq_length", n_embed)) - self.gguf_writer.add_embedding_length(n_embed) - self.gguf_writer.add_feed_forward_length(4 * n_embed) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(n_head) - self.gguf_writer.add_head_count_kv(n_head) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) - n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) - assert n_head is not None - assert n_embed is not None - - name = re.sub(r'transformer\.', '', name) - - if re.match(r"h\.\d+\.self_attention\.query_key_value\.weight", name): - # Map bloom-style qkv_linear to gpt-style qkv_linear - # bloom: https://github.com/huggingface/transformers/blob/main/src/transformers/models/bloom/modeling_bloom.py#L238-L252 # noqa - # gpt-2: https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py#L312 # noqa - qkv_weights = data_torch.reshape((n_head, 3, n_embed // n_head, n_embed)) - data_torch = torch.cat( - ( - qkv_weights[:, 0, :, :].reshape((-1, n_embed)), - qkv_weights[:, 1, :, :].reshape((-1, n_embed)), - qkv_weights[:, 2, :, :].reshape((-1, n_embed)), - ), - dim=0, - ) - logger.info("re-format attention.linear_qkv.weight") - elif re.match(r"h\.\d+\.self_attention\.query_key_value\.bias", name): - qkv_bias = data_torch.reshape((n_head, 3, n_embed // n_head)) - data_torch = torch.cat( - ( - qkv_bias[:, 0, :].reshape((n_embed,)), - qkv_bias[:, 1, :].reshape((n_embed,)), - qkv_bias[:, 2, :].reshape((n_embed,)), - ), - dim=0, - ) - logger.info("re-format attention.linear_qkv.bias") - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("MPTForCausalLM") -class MPTModel(TextModel): - model_arch = gguf.MODEL_ARCH.MPT - - def set_vocab(self): - try: - self._set_vocab_gpt2() - except Exception: - # Fallback for SEA-LION model - self._set_vocab_sentencepiece() - self.gguf_writer.add_add_bos_token(False) - self.gguf_writer.add_pad_token_id(3) - self.gguf_writer.add_eos_token_id(1) - self.gguf_writer.add_unk_token_id(0) - - def set_gguf_parameters(self): - self.gguf_writer.add_context_length(self.hparams["max_seq_len"]) - self.gguf_writer.add_embedding_length(self.hparams["d_model"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(4 * self.hparams["d_model"]) - self.gguf_writer.add_head_count(self.hparams["n_heads"]) - if kv_n_heads := self.hparams["attn_config"].get("kv_n_heads"): - self.gguf_writer.add_head_count_kv(kv_n_heads) - self.gguf_writer.add_layer_norm_eps(1e-5) - if self.hparams["attn_config"]["clip_qkv"] is not None: - self.gguf_writer.add_clamp_kqv(self.hparams["attn_config"]["clip_qkv"]) - if self.hparams["attn_config"]["alibi"]: - self.gguf_writer.add_max_alibi_bias(self.hparams["attn_config"]["alibi_bias_max"]) - else: - self.gguf_writer.add_max_alibi_bias(0.0) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "scales" in name: - new_name = self.map_tensor_name(name, try_suffixes=(".weight", ".bias", ".scales")) - new_name = new_name.replace("scales", "act.scales") - else: - new_name = self.map_tensor_name(name, try_suffixes=(".weight", ".bias")) - - yield from super().modify_tensors(data_torch, new_name, bid) - - -@ModelBase.register("OrionForCausalLM") -class OrionModel(TextModel): - model_arch = gguf.MODEL_ARCH.ORION - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def set_gguf_parameters(self): - head_count = self.hparams["num_attention_heads"] - head_count_kv = self.hparams.get("num_key_value_heads", head_count) - - ctx_length = 0 - if "max_sequence_length" in self.hparams: - ctx_length = self.hparams["max_sequence_length"] - elif "max_position_embeddings" in self.hparams: - ctx_length = self.hparams["max_position_embeddings"] - elif "model_max_length" in self.hparams: - ctx_length = self.hparams["model_max_length"] - else: - raise ValueError("gguf: can not find ctx length parameter.") - - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_tensor_data_layout("Meta AI original pth") - self.gguf_writer.add_context_length(ctx_length) - self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) - self.gguf_writer.add_head_count(head_count) - self.gguf_writer.add_head_count_kv(head_count_kv) - # note: config provides rms norm but it is actually layer norm - # ref: https://huggingface.co/OrionStarAI/Orion-14B-Chat/blob/276a17221ce42beb45f66fac657a41540e71f4f5/modeling_orion.py#L570-L571 - self.gguf_writer.add_layer_norm_eps(self.hparams["rms_norm_eps"]) - - -@ModelBase.register("BaichuanForCausalLM", "BaiChuanForCausalLM") -class BaichuanModel(TextModel): - model_arch = gguf.MODEL_ARCH.BAICHUAN - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_tensor_data_layout("Meta AI original pth") - self.gguf_writer.add_rope_dimension_count(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - head_count = self.hparams["num_attention_heads"] - head_count_kv = self.hparams.get("num_key_value_heads", head_count) - - if bid is not None and name == f"model.layers.{bid}.self_attn.W_pack.weight": - logger.info(f"Unpacking and permuting layer {bid}") - yield from [ - (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), - self._reverse_hf_permute_part(data_torch, 0, head_count, head_count)), - (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), - self._reverse_hf_permute_part(data_torch, 1, head_count, head_count_kv)), - (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), - self._reverse_hf_part(data_torch, 2)), - ] - else: - yield from self.modify_tensors(data_torch, self.map_tensor_name(name), bid) - - def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: - if n_kv_head is not None and n_head != n_kv_head: - n_head //= n_kv_head - - return ( - weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape) - ) - - def _reverse_hf_permute_part( - self, weights: Tensor, n_part: int, n_head: int, n_head_kv: int | None = None, - ) -> Tensor: - r = weights.shape[0] // 3 - return self._reverse_hf_permute(weights[r * n_part:r * n_part + r, ...], n_head, n_head_kv) - - def _reverse_hf_part(self, weights: Tensor, n_part: int) -> Tensor: - r = weights.shape[0] // 3 - return weights[r * n_part:r * n_part + r, ...] - - -@ModelBase.register("XverseForCausalLM") -class XverseModel(TextModel): - model_arch = gguf.MODEL_ARCH.XVERSE - - def set_vocab(self): - assert (self.dir_model / "tokenizer.json").is_file() - dir_model = self.dir_model - hparams = self.hparams - - tokens: list[bytes] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(dir_model) - vocab_size = hparams.get("vocab_size", len(tokenizer.vocab)) # ty: ignore[unresolved-attribute] - # Since we are checking the maximum index, we need to ensure it's strictly less than vocab_size, - # because vocab_size is the count of items, and indexes start at 0. - max_vocab_index = max(tokenizer.get_vocab().values()) # ty: ignore[unresolved-attribute] - if max_vocab_index >= vocab_size: - raise ValueError("Vocabulary size exceeds expected maximum size.") - - reverse_vocab: dict[int, str] = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - for token_id in range(vocab_size): - token_text = reverse_vocab[token_id].encode('utf-8') - # replace "\x00" to string with length > 0 - if token_text == b"\x00": - toktype = gguf.TokenType.BYTE # special - token_text = f"<{token_text}>".encode('utf-8') - elif re.fullmatch(br"<0x[0-9A-Fa-f]{2}>", token_text): - toktype = gguf.TokenType.BYTE # special - elif reverse_vocab[token_id] in added_vocab: - if tokenizer.added_tokens_decoder[token_id].special: # ty: ignore[unresolved-attribute] - toktype = gguf.TokenType.CONTROL - else: - toktype = gguf.TokenType.USER_DEFINED - else: - toktype = gguf.TokenType.NORMAL - - tokens.append(token_text) - toktypes.append(toktype) - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_tensor_data_layout("Meta AI original pth") - self.gguf_writer.add_rope_dimension_count(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - head_count = self.hparams["num_attention_heads"] - head_count_kv = self.hparams.get("num_key_value_heads", head_count) - - # HF models permute some of the tensors, so we need to undo that - if name.endswith("q_proj.weight"): - data_torch = self._reverse_hf_permute(data_torch, head_count, head_count) - if name.endswith("k_proj.weight"): - data_torch = self._reverse_hf_permute(data_torch, head_count, head_count_kv) - - yield from super().modify_tensors(data_torch, name, bid) - - def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: - if n_kv_head is not None and n_head != n_kv_head: - n_head //= n_kv_head - - return ( - weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape) - ) - - -@ModelBase.register("FalconForCausalLM", "RWForCausalLM") -class FalconModel(TextModel): - model_arch = gguf.MODEL_ARCH.FALCON - - def set_gguf_parameters(self): - n_head = self.hparams.get("num_attention_heads") - if n_head is None: - n_head = self.hparams["n_head"] # old name - - n_head_kv = self.hparams.get("num_kv_heads") - if n_head_kv is None: - n_head_kv = self.hparams.get("n_head_kv", 1) # old name - - self.gguf_writer.add_context_length(2048) # not in config.json - self.gguf_writer.add_tensor_data_layout("jploski") # qkv tensor transform - self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) - self.gguf_writer.add_feed_forward_length(4 * self.hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(n_head) - self.gguf_writer.add_head_count_kv(n_head_kv) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # QKV tensor transform - # The original query_key_value tensor contains n_head_kv "kv groups", - # each consisting of n_head/n_head_kv query weights followed by one key - # and one value weight (shared by all query heads in the kv group). - # This layout makes it a big pain to work with in GGML. - # So we rearrange them here,, so that we have n_head query weights - # followed by n_head_kv key weights followed by n_head_kv value weights, - # in contiguous fashion. - # ref: https://github.com/jploski/ggml/blob/falcon40b/examples/falcon/convert-hf-to-ggml.py - - if "query_key_value" in name: - n_head = self.find_hparam(["num_attention_heads", "n_head"]) - n_head_kv = self.find_hparam(["num_kv_heads", "n_head_kv"], optional=True) or 1 - head_dim = self.hparams["hidden_size"] // n_head - - qkv = data_torch.view(n_head_kv, n_head // n_head_kv + 2, head_dim, head_dim * n_head) - q = qkv[:, :-2].reshape(n_head * head_dim, head_dim * n_head) - k = qkv[:, [-2]].reshape(n_head_kv * head_dim, head_dim * n_head) - v = qkv[:, [-1]].reshape(n_head_kv * head_dim, head_dim * n_head) - data_torch = torch.cat((q, k, v)).reshape_as(data_torch) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("GPTBigCodeForCausalLM") -class StarCoderModel(TextModel): - model_arch = gguf.MODEL_ARCH.STARCODER - - def set_gguf_parameters(self): - self.gguf_writer.add_context_length(self.hparams["n_positions"]) - self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) - self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(self.hparams["n_head"]) - self.gguf_writer.add_head_count_kv(1) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - -@ModelBase.register("GPTRefactForCausalLM") -class RefactModel(TextModel): - model_arch = gguf.MODEL_ARCH.REFACT - - def set_vocab(self): - super().set_vocab() - - # TODO: how to determine special FIM tokens automatically? - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False, - special_token_types = ['prefix', 'suffix', 'middle', 'eot']) - special_vocab._set_special_token("prefix", 1) - special_vocab._set_special_token("suffix", 3) - special_vocab._set_special_token("middle", 2) - special_vocab.chat_template = None # do not add it twice - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - hidden_dim = self.hparams["n_embd"] - inner_dim = 4 * hidden_dim - hidden_dim = int(2 * inner_dim / 3) - multiple_of = 256 - ff_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) - - # refact uses Alibi. So this is from config.json which might be used by training. - self.gguf_writer.add_context_length(self.hparams["n_positions"]) - self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) - - self.gguf_writer.add_feed_forward_length(ff_dim) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(self.hparams["n_head"]) - self.gguf_writer.add_head_count_kv(1) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - hidden_dim = self.hparams["n_embd"] - inner_dim = 4 * hidden_dim - hidden_dim = int(2 * inner_dim / 3) - multiple_of = 256 - ff_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) - n_head = self.hparams["n_head"] - n_head_kv = 1 - head_dim = self.hparams["n_embd"] // n_head - - if bid is not None: - if name == f"transformer.h.{bid}.attn.kv.weight": - yield from super().modify_tensors(data_torch[:n_head_kv * head_dim], self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) - yield from super().modify_tensors(data_torch[n_head_kv * head_dim:], self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) - return - if name == f"transformer.h.{bid}.attn.q.weight": - yield from super().modify_tensors(data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) - return - if name == f"transformer.h.{bid}.mlp.gate_up_proj.weight": - yield from super().modify_tensors(data_torch[:ff_dim], self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), bid) - yield from super().modify_tensors(data_torch[ff_dim:], self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("StableLmForCausalLM", "StableLMEpochForCausalLM", "LlavaStableLMEpochForCausalLM") -class StableLMModel(TextModel): - model_arch = gguf.MODEL_ARCH.STABLELM - - def set_vocab(self): - if (self.dir_model / "tokenizer.json").is_file(): - self._set_vocab_gpt2() - else: - # StableLM 2 1.6B used to have a vocab in a similar format to Qwen's vocab - self._set_vocab_qwen() - - def set_gguf_parameters(self): - hparams = self.hparams - - self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) - rotary_factor = self.find_hparam(["partial_rotary_factor", "rope_pct"]) - self.gguf_writer.add_rope_dimension_count(int(rotary_factor * (hparams["hidden_size"] // hparams["num_attention_heads"]))) - self.gguf_writer.add_head_count(hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(hparams["num_key_value_heads"]) - self.gguf_writer.add_parallel_residual(hparams["use_parallel_residual"] if "use_parallel_residual" in hparams else True) - self.gguf_writer.add_layer_norm_eps(self.find_hparam(["layer_norm_eps", "norm_eps"])) - self.gguf_writer.add_file_type(self.ftype) - - _q_norms: list[dict[str, Tensor]] | None = None - _k_norms: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams["num_key_value_heads"] - - if name.find("q_layernorm.norms") != -1: - assert bid is not None - - if self._q_norms is None: - self._q_norms = [{} for _ in range(self.block_count)] - - self._q_norms[bid][name] = data_torch - - if len(self._q_norms[bid]) >= n_head: - return self._stack_qk_norm(bid, n_head, self._q_norms[bid], "q_layernorm") - else: - return - - if name.find("k_layernorm.norms") != -1: - assert bid is not None - - if self._k_norms is None: - self._k_norms = [{} for _ in range(self.block_count)] - - self._k_norms[bid][name] = data_torch - - if len(self._k_norms[bid]) >= n_kv_head: - return self._stack_qk_norm(bid, n_kv_head, self._k_norms[bid], "k_layernorm") - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def _stack_qk_norm(self, bid: int, n_head: int, norms: dict[str, Tensor], layer_name: str = "q_layernorm"): - datas: list[Tensor] = [] - # extract the norms in order - for xid in range(n_head): - ename = f"model.layers.{bid}.self_attn.{layer_name}.norms.{xid}.weight" - datas.append(norms[ename]) - del norms[ename] - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.self_attn.{layer_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._q_norms is not None or self._k_norms is not None: - # flatten two `list[dict[str, Tensor]]` into a single `list[str]` - norms = ( - [k for d in self._q_norms for k in d.keys()] if self._q_norms is not None else [] - ) + ( - [k for d in self._k_norms for k in d.keys()] if self._k_norms is not None else [] - ) - if len(norms) > 0: - raise ValueError(f"Unprocessed norms: {norms}") - - -@ModelBase.register( - "LLaMAForCausalLM", - "LlamaForCausalLM", - "MistralForCausalLM", - "MixtralForCausalLM", - "VLlama3ForCausalLM", - "LlavaForConditionalGeneration", - "VoxtralForConditionalGeneration", - "IQuestCoderForCausalLM", - "LlamaModel") -class LlamaModel(TextModel): - model_arch = gguf.MODEL_ARCH.LLAMA - undo_permute = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # fix for SmolVLM2, missing `num_attention_heads` in config.json - if self.hf_arch == "VLlama3ForCausalLM": - self.hparams["num_attention_heads"] = self.hparams.get("num_attention_heads", 32) - # Mistral consolidated format has no config.json; origin_hf_arch is HF-only. - if self.is_mistral_format: - self.origin_hf_arch = None - else: - hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) - self.origin_hf_arch = hparams.get('architectures', [None])[0] - - def set_vocab(self): - if self.origin_hf_arch == "GlmasrModel": - return self._set_vocab_glmedge() - - if self.is_mistral_format: - return self._set_vocab_mistral() - - path_tekken_json = self.dir_model / "tekken.json" - path_tokenizer_json = self.dir_model / "tokenizer.json" - if path_tekken_json.is_file() and not path_tokenizer_json.is_file(): - self._set_vocab_mistral() - - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - try: - self._set_vocab_llama_hf() - except (FileNotFoundError, TypeError): - # Llama 3 - self._set_vocab_gpt2() - - # Apply to CodeLlama only (and ignore for Llama 3 with a vocab size of 128256) - if self.hparams.get("vocab_size", 32000) == 32016: - special_vocab = gguf.SpecialVocab( - self.dir_model, load_merges=False, - special_token_types = ['prefix', 'suffix', 'middle', 'eot'] - ) - special_vocab._set_special_token("prefix", 32007) - special_vocab._set_special_token("suffix", 32008) - special_vocab._set_special_token("middle", 32009) - special_vocab._set_special_token("eot", 32010) - special_vocab.add_to_gguf(self.gguf_writer) - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - if "add_prefix_space" in tokenizer_config_json: - self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) - - # Apply to granite small models only - if self.hparams.get("vocab_size", 32000) == 49152: - self.gguf_writer.add_add_bos_token(False) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - - if not self.is_mistral_format: - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(rope_dim) - - @staticmethod - def permute(weights: Tensor, n_head: int, n_head_kv: int | None): - if n_head_kv is not None and n_head != n_head_kv: - n_head = n_head_kv - return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape)) - - def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): - # Mirror the BF16 Q/K RoPE permutation site in modify_tensors; the NVFP4 path bypasses it. - if self.undo_permute: - n_head = self.find_hparam(["n_heads", "num_attention_heads"], optional=True) - n_kv_head = self.find_hparam(["n_kv_heads", "num_key_value_heads"], optional=True) - if n_head is not None: - if name.endswith("q_proj.weight"): - weight = LlamaModel.permute(weight, n_head, n_head) - scale = LlamaModel.permute(scale, n_head, n_head) - elif name.endswith("k_proj.weight"): - weight = LlamaModel.permute(weight, n_head, n_kv_head) - scale = LlamaModel.permute(scale, n_head, n_kv_head) - super()._repack_nvfp4(name, weight, scale, scale2, input_scale) - - _experts: list[dict[str, Tensor]] | None = None - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "text_model." in name: - name = name.replace("text_model.", "") # for SmolVLM - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.find_hparam(["n_heads", "num_attention_heads"]) - n_kv_head = self.find_hparam(["n_kv_heads", "num_key_value_heads"]) - - if self.hf_arch == "LlamaModel": - name = "model." + name - - if self.undo_permute: - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - - # process the experts separately - if name.find("block_sparse_moe.experts") != -1: - n_experts = self.hparams["num_local_experts"] - - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for wid in ["w1", "w2", "w3"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"layers.{bid}.feed_forward.experts.{wid}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): - if rope_params.get("rope_type", '').lower() == "llama3": - base = rope_params.get("rope_theta", 10000.0) - if (dim := self.hparams.get("head_dim")) is None: - dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) - - factor = rope_params.get("factor", 8.0) - low_freq_factor = rope_params.get("low_freq_factor", 1.0) - high_freq_factor = rope_params.get("high_freq_factor", 4.0) - old_context_len = self.hparams.get("original_max_position_embeddings", 8192) - - low_freq_wavelen = old_context_len / low_freq_factor - high_freq_wavelen = old_context_len / high_freq_factor - # assert low_freq_wavelen != high_freq_wavelen # Errors for Llama4 - - rope_factors = [] - for freq in freqs: - wavelen = 2 * math.pi / freq - if wavelen < high_freq_wavelen: - rope_factors.append(1) - elif wavelen > low_freq_wavelen: - rope_factors.append(factor) - else: - smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) - rope_factors.append(1 / ((1 - smooth) / factor + smooth)) - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("ArceeForCausalLM") -class ArceeModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.ARCEE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self._try_set_pooling_type() - - -@ModelBase.register("AfmoeForCausalLM") -class AfmoeModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.AFMOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # MoE parameters - if (n_shared_experts := self.hparams.get("num_shared_experts")) is not None: - self.gguf_writer.add_expert_shared_count(n_shared_experts) - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - if (n_dense_layers := self.hparams.get("num_dense_layers")) is not None: - self.gguf_writer.add_leading_dense_block_count(n_dense_layers) - - # Route normalization and scaling - if (route_norm := self.hparams.get("route_norm")) is not None: - self.gguf_writer.add_expert_weights_norm(route_norm) - if (route_scale := self.hparams.get("route_scale")) is not None: - self.gguf_writer.add_expert_weights_scale(route_scale) - - # Sliding window attention - if (sliding_window := self.hparams.get("sliding_window")) is not None: - self.gguf_writer.add_sliding_window(sliding_window) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith(".expert_bias"): - name = name.replace(".expert_bias", ".expert_bias.bias") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Handle expert weights - they're already merged in the HF format - # process the experts separately - if name.find("mlp.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["gate_proj", "up_proj", "down_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename_to_retrieve]) - del self._experts[bid][ename_to_retrieve] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - yield from ModelBase.modify_tensors(self, data_torch, merged_name, bid) - - return - else: - return - - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - - -@ModelBase.register( - "LlavaForConditionalGeneration", # pixtral - "Mistral3ForConditionalGeneration", # mistral small 3.1 -) -class LlavaVisionModel(MmprojModel): - img_break_tok_id = -1 - use_break_tok = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.hparams.get("model_type") == "pixtral": - # layer_norm_eps is not in config.json, it is hard-coded in modeling_pixtral.py - self.hparams["layer_norm_eps"] = self.hparams.get("layer_norm_eps", 1e-5) - if self.use_break_tok: - self.img_break_tok_id = self.get_token_id("[IMG_BREAK]") - elif self.is_mistral_format: - # hparams is already vision config here so norm_eps is only defined in global_config. - self.hparams["norm_eps"] = self.global_config.get("norm_eps", None) - assert self.hparams["norm_eps"] is not None, "norm_eps not found in params.json" - if self.use_break_tok: - self.img_break_tok_id = self.find_vparam(["image_break_token_id"]) - - # params.json may ship -1 placeholders (Mistral Medium 3.5) - # resolve the real id from the bundled tokenizer in that case - if self.img_break_tok_id < 0: - self.img_break_tok_id = self.get_mistral_token_id("[IMG_BREAK]") - else: - raise ValueError(f"Unsupported model type: {self.hparams['model_type']}") - logger.info(f"Image break token id: {self.img_break_tok_id}") - - def get_token_id(self, token: str) -> int: - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - added_tokens_decoder = json.load(f).get('added_tokens_decoder') or {} - for id_, token_data in added_tokens_decoder.items(): - if token_data.get("content") == token: - return int(id_) - # fallthrough to tokenizer.json - with open(self.dir_model / "tokenizer.json", "r", encoding="utf-8") as f: - tokenizer_json = json.load(f) - for token_data in tokenizer_json["added_tokens"]: - if token_data["content"] == token: - return int(token_data["id"]) - raise ValueError(f"Token '{token}' not found in tokenizer config.") - - def get_mistral_token_id(self, token: str) -> int: - # mistral native format ships tekken.json or a versioned spm tokenizer - tekken_file = self.dir_model / "tekken.json" - if tekken_file.is_file(): - with open(tekken_file, "r", encoding="utf-8") as f: - data = json.load(f) - for entry in data.get("special_tokens", []): - if entry.get("token_str") == token: - return int(entry["rank"]) - tokenizer_json_file = self.dir_model / "tokenizer.json" - if tokenizer_json_file.is_file(): - with open(tokenizer_json_file, "r", encoding="utf-8") as f: - data = json.load(f) - for entry in data.get("added_tokens", []): - if entry.get("content") == token: - return int(entry["id"]) - raise ValueError(f"Token '{token}' not found in mistral tokenizer files.") - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - if hparams.get("model_type") == "pixtral": - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PIXTRAL) - self.gguf_writer.add_vision_attention_layernorm_eps(hparams["layer_norm_eps"]) - - # hidden_act - if hparams["hidden_act"] == "silu": - self.gguf_writer.add_vision_use_silu(True) - elif hparams["hidden_act"] == "gelu": - self.gguf_writer.add_vision_use_gelu(True) - else: - raise ValueError(f"Unsupported hidden_act: {hparams['hidden_act']}") - - # spatial_merge_size - if "spatial_merge_size" in self.global_config: - self.gguf_writer.add_vision_spatial_merge_size(self.global_config["spatial_merge_size"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = ( - self.hparams["num_attention_heads"] if not self.is_mistral_format else self.find_vparam(["num_attention_heads"]) - ) - n_kv_head = n_head - - valid_prefixes = ( - "multi_modal_projector.", - "vision_tower.", - "vision_encoder.", - "vision_language_adapter.", - "patch_merger.", - "pre_mm_projector_norm", - ) - - if any(name.startswith(prefix) for prefix in valid_prefixes): - # process vision tensors - if name.endswith(("q_proj.weight", "q_proj.bias")) and not self.is_mistral_format: - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")) and not self.is_mistral_format: - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - yield from super().modify_tensors(data_torch, name, bid) - return - - embed_key = "embed_tokens.weight" if not self.is_mistral_format else "tok_embeddings.weight" - if self.img_break_tok_id > 0 and embed_key in name: - logger.info(f"Extracting [IMG_BREAK] token embedding from {name}") - # for pixtral model, we need to extract the [IMG_BREAK] token embedding - img_break_embd = data_torch[self.img_break_tok_id] - name = gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_TOK_EMBD_IMG_BREAK] - yield from super().modify_tensors(img_break_embd, name, bid) - - return # skip other tensors - - -@ModelBase.register("Idefics3ForConditionalGeneration", "SmolVLMForConditionalGeneration") -class SmolVLMModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.hparams["model_type"] == "smolvlm_vision": - # fix for SmolVLM2, missing some keys in config.json - # default values are taken from transformers code - self.hparams["hidden_size"] = self.hparams.get("hidden_size", 1152) - self.hparams["num_attention_heads"] = self.hparams.get("num_attention_heads", 16) - self.hparams["intermediate_size"] = self.hparams.get("intermediate_size", 3072) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.IDEFICS3) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) - self.gguf_writer.add_vision_projector_scale_factor(self.global_config.get("scale_factor", 2)) - self.gguf_writer.add_vision_use_gelu(True) - - # Add the preprocessor longest edge size - preproc_image_size = self.preprocessor_config.get("size", {}).get("longest_edge", self.image_size) - self.gguf_writer.add_vision_preproc_image_size(preproc_image_size) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".embeddings." in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - is_vision_tensor = "vision_tower" in name or "vision_model" in name or "model.connector" in name - - if not is_vision_tensor: - return None - - return super().filter_tensors(item) - - -@ModelBase.register( - "Llama4ForConditionalGeneration", - "Llama4ForCausalLM", -) -class Llama4Model(LlamaModel): - model_arch = gguf.MODEL_ARCH.LLAMA4 - undo_permute = False - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # IMPORTANT: the normal "intermediate_size" is renamed to "intermediate_size_mlp", we need to undo this - self.hparams["intermediate_size_moe"] = self.hparams["intermediate_size"] - self.hparams["intermediate_size"] = self.hparams["intermediate_size_mlp"] - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_interleave_moe_layer_step(self.hparams["interleave_moe_layer_step"]) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["intermediate_size_moe"]) - if "layer_types" in self.hparams: - if all(lt == "full_attention" for lt in self.hparams["layer_types"]): - # all layers are full attention (for MobileLLM), disable swa - self.gguf_writer.add_sliding_window(0) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): - # split the gate_up into gate and up - if "gate_up_proj" in name: - name_up = name.replace("gate_up_proj", "up_proj.weight") - name_gate = name.replace("gate_up_proj", "gate_proj.weight") - dim_half = data_torch.shape[-1] // 2 - gate_proj_weight, up_proj_weight = data_torch.transpose(-1, -2).split(dim_half, dim=-2) - yield from super().modify_tensors(gate_proj_weight, name_gate, bid) - yield from super().modify_tensors(up_proj_weight, name_up, bid) - return - - if name.endswith("down_proj"): - name += ".weight" - data_torch = data_torch.transpose(-1, -2) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Llama4ForConditionalGeneration") -class Llama4VisionModel(MmprojModel): - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LLAMA4) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams["norm_eps"]) - self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / self.hparams["pixel_shuffle_ratio"])) - assert self.hparams["hidden_act"] == "gelu" - self.gguf_writer.add_vision_use_gelu(True) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "multi_modal_projector" not in name and "vision_model" not in name: - return None - - if "positional_embedding_vlm" in name and ".weight" not in name: - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "multi_modal_projector.linear_1" in name: - # despite the name with number postfix, this is a single fully connected layer - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_MMPROJ_FC] + '.weight', data_torch) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("DeciLMForCausalLM") -class DeciModel(TextModel): - model_arch = gguf.MODEL_ARCH.DECI - - @staticmethod - def _ffn_mult_to_intermediate_size(ffn_mult: float, n_embd: int) -> int: - # DeciLM-specific code - intermediate_size = int(2 * ffn_mult * n_embd / 3) - return DeciModel._find_multiple(intermediate_size, 256) - - @staticmethod - def _find_multiple(n: int, k: int) -> int: - # DeciLM-specific code - if n % k == 0: - return n - return n + k - (n % k) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if "block_configs" in self.hparams: # Llama-3_1-Nemotron-51B - _block_configs: list[dict[str,Any]] = self.hparams["block_configs"] - assert self.block_count == len(_block_configs) - self._num_kv_heads = list() - self._num_heads = list() - _ffn_multipliers = list() - # ***linear attention layer*** - # if n_heads_in_group is None and replace_with_linear is True - # then _num_kv_heads[il] is 0 and _num_heads[il] is num_attention_heads - # ***attention-free layer*** - # if n_heads_in_group is None and replace_with_linear is False - # then _num_kv_heads[il] is 0 and _num_heads[il] is 0 - # ***normal attention-layer*** - # if n_heads_in_group is not None, then - # _num_kv_heads[il] is num_attention_head // n_heads_in_group and - # _num_heads[il] is num_attention_head - # ***dummy layer*** for nemotron 253B - # if n_heads_in_group is None and ffn_mult is None - # then _num_kv_heads[il] is 0 and _num_heads[il] is 0 and _ffn_dims is 0 - for il in range(len(_block_configs)): - if _block_configs[il]["attention"]["n_heads_in_group"] is None: - if _block_configs[il]["attention"]["replace_with_linear"] is True: - self._num_kv_heads.append(0) - self._num_heads.append(self.hparams["num_attention_heads"]) - else: - self._num_kv_heads.append(0) - self._num_heads.append(0) - else: - self._num_kv_heads.append(self.hparams["num_attention_heads"] // _block_configs[il]["attention"]["n_heads_in_group"]) - self._num_heads.append(self.hparams["num_attention_heads"]) - if _block_configs[il]["ffn"]["ffn_mult"] is None: # dummy layer - _ffn_multipliers.append(0.0) - else: - _ffn_multipliers.append(_block_configs[il]["ffn"]["ffn_mult"]) - assert self.block_count == len(self._num_kv_heads) - assert self.block_count == len(self._num_heads) - assert self.block_count == len(_ffn_multipliers) - assert isinstance(self._num_kv_heads, list) and isinstance(self._num_kv_heads[0], int) - assert isinstance(self._num_heads, list) and isinstance(self._num_heads[0], int) - assert isinstance(_ffn_multipliers, list) and isinstance(_ffn_multipliers[0], float) - self._ffn_dims: list[int] = [ - DeciModel._ffn_mult_to_intermediate_size(multiplier, self.hparams["hidden_size"]) - for multiplier in _ffn_multipliers - ] - - def set_vocab(self): - # Please change tokenizer_config.json of Llama-3_1-Nemotron-51B's - # eos_token from '|eot_id|' to '|end_of_text|' - if self.hparams.get("vocab_size", 128256) == 128256: - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab.add_to_gguf(self.gguf_writer) - else: - # DeciLM-7B - self._set_vocab_llama_hf() - - def set_gguf_parameters(self): - if "block_configs" in self.hparams: # Llama-3_1-Nemotron-51B - assert self.block_count == len(self._num_kv_heads) - assert self.block_count == len(self._num_heads) - assert self.block_count == len(self._ffn_dims) - if (rope_theta := self.rope_parameters.get("rope_theta")) is not None: - self.gguf_writer.add_rope_freq_base(rope_theta) - self.gguf_writer.add_head_count_kv(self._num_kv_heads) - self.gguf_writer.add_head_count(self._num_heads) - self.gguf_writer.add_feed_forward_length(self._ffn_dims) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(self.hparams["hidden_size"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) - self.gguf_writer.add_key_length(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) - self.gguf_writer.add_value_length(self.hparams["hidden_size"] // self.hparams["num_attention_heads"]) - self.gguf_writer.add_file_type(self.ftype) - else: # DeciLM-7B - super().set_gguf_parameters() - if "num_key_value_heads_per_layer" in self.hparams: # DeciLM-7B - self._num_kv_heads: list[int] = self.hparams["num_key_value_heads_per_layer"] - assert self.block_count == len(self._num_kv_heads) - self.gguf_writer.add_head_count_kv(self._num_kv_heads) - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(rope_dim) - - @staticmethod - def permute(weights: Tensor, n_head: int, n_head_kv: int | None): - if n_head_kv is not None and n_head != n_head_kv: - n_head = n_head_kv - return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - if bid is not None: - if "num_key_value_heads_per_layer" in self.hparams: - n_kv_head = self.hparams["num_key_value_heads_per_layer"][bid] - elif "block_configs" in self.hparams: - n_kv_head = self._num_kv_heads[bid] - n_head = self._num_heads[bid] - else: - n_kv_head = self.hparams.get("num_key_value_heads") - else: - n_kv_head = self.hparams.get("num_key_value_heads") - - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = DeciModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = DeciModel.permute(data_torch, n_head, n_kv_head) - yield from super().modify_tensors(data_torch, name, bid) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): - if rope_params.get("rope_type", '').lower() == "llama3": - base = rope_params.get("rope_theta", 10000.0) - if (dim := self.hparams.get("head_dim")) is None: - dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) - - factor = rope_params.get("factor", 8.0) - low_freq_factor = rope_params.get("low_freq_factor", 1.0) - high_freq_factor = rope_params.get("high_freq_factor", 4.0) - old_context_len = self.hparams.get("original_max_position_embeddings", 8192) - - low_freq_wavelen = old_context_len / low_freq_factor - high_freq_wavelen = old_context_len / high_freq_factor - assert low_freq_wavelen != high_freq_wavelen - - rope_factors = [] - for freq in freqs: - wavelen = 2 * math.pi / freq - if wavelen < high_freq_wavelen: - rope_factors.append(1) - elif wavelen > low_freq_wavelen: - rope_factors.append(factor) - else: - smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) - rope_factors.append(1 / ((1 - smooth) / factor + smooth)) - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) - - def prepare_tensors(self): - super().prepare_tensors() - - -@ModelBase.register("BitnetForCausalLM") -class BitnetModel(TextModel): - model_arch = gguf.MODEL_ARCH.BITNET - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) - self.gguf_writer.add_rope_scaling_factor(1.0) - - def weight_quant(self, weight: Tensor) -> Tensor: - dtype = weight.dtype - weight = weight.float() - scale = weight.abs().mean().clamp(min=1e-5) - iscale = 1 / scale - # TODO: multiply by the scale directly instead of inverting it twice - # (this is also unnecessarily doubly inverted upstream) - # ref: https://huggingface.co/1bitLLM/bitnet_b1_58-3B/blob/af89e318d78a70802061246bf037199d2fb97020/utils_quant.py#L10 - result = (weight * iscale).round().clamp(-1, 1) / iscale - return result.type(dtype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - new_name = self.map_tensor_name(name) - - if any(self.match_model_tensor_name(new_name, key, bid) for key in [ - gguf.MODEL_TENSOR.ATTN_Q, - gguf.MODEL_TENSOR.ATTN_K, - gguf.MODEL_TENSOR.ATTN_V, - gguf.MODEL_TENSOR.ATTN_OUT, - gguf.MODEL_TENSOR.FFN_UP, - gguf.MODEL_TENSOR.FFN_DOWN, - gguf.MODEL_TENSOR.FFN_GATE, - ]): - # transform weight into 1/0/-1 (in fp32) - data_torch = self.weight_quant(data_torch) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("GrokForCausalLM", "Grok1ForCausalLM") -class GrokModel(TextModel): - model_arch = gguf.MODEL_ARCH.GROK - - def set_vocab(self): - if (self.dir_model / 'tokenizer.model').is_file(): - self._set_vocab_sentencepiece() - return - - if not (self.dir_model / 'tokenizer.json').is_file() or not (self.dir_model / 'chat_template.jinja').is_file(): - logger.error('Error: Missing vocab and chat template, download files from https://huggingface.co/alvarobartt/grok-2-tokenizer') - sys.exit(1) - - self._set_vocab_gpt2() - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_attn_logit_softcapping(self.hparams.get("attn_logit_softcapping", 30.0)) - self.gguf_writer.add_router_logit_softcapping(self.hparams.get("router_logit_softcapping", 30.0)) - if (final_logit_softcap := self.hparams.get("final_logit_softcapping")): - self.gguf_writer.add_final_logit_softcapping(final_logit_softcap) - - if (rope_dim := self.hparams.get("head_dim")) is None: - rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - - # Treat "original" as "yarn", seems to have been a mistake - if self.hparams.get("rope_type") in ("yarn", "original"): - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.YARN) - self.gguf_writer.add_rope_scaling_factor(self.hparams["scaling_factor"]) - self.gguf_writer.add_rope_scaling_orig_ctx_len(self.hparams["original_max_position_embeddings"]) - self.gguf_writer.add_rope_scaling_yarn_ext_factor(self.hparams["extrapolation_factor"]) - self.gguf_writer.add_rope_scaling_yarn_attn_factor(self.hparams["attn_factor"]) - self.gguf_writer.add_rope_scaling_yarn_beta_fast(self.hparams["beta_fast"]) - self.gguf_writer.add_rope_scaling_yarn_beta_slow(self.hparams["beta_slow"]) - - if temp_len := self.hparams.get("attn_temperature_len"): - self.gguf_writer.add_attn_temperature_length(temp_len) - - self.gguf_writer.add_attn_output_scale(self.hparams.get("attn_output_multiplier", rope_dim**-0.5)) - self.gguf_writer.add_embedding_scale(self.hparams["embedding_multiplier_scale"]) - self.gguf_writer.add_logit_scale(self.hparams["output_multiplier_scale"]) - - _experts: list[dict[str, list[Tensor]]] | None = None - _cur_expert = "" - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - deferred: list[tuple[Tensor, str, int | None]] = [] - is_expert = ".moe." in name or ".block_sparse_moe.experts." in name - - if not is_expert: - deferred.append((data_torch, name, bid)) - - # process the experts separately - if is_expert or self._cur_expert: - n_experts = self.hparams["num_local_experts"] - - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - # concatenate split tensors - if name in self._experts[bid]: - self._cur_expert = name - self._experts[bid][name].append(data_torch) - return - elif is_expert: - self._cur_expert = name - self._experts[bid][name] = [data_torch] - return - else: - self._cur_expert = "" - - for bid in range(self.block_count): - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for wid in [("linear", "w1", 0), ("linear_1", "w2", 1), ("linear_v", "w3", 0)]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"transformer.decoder_layer.{bid}.moe.{xid}.{wid[0]}.weight" - if ename not in self._experts[bid]: - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid[1]}.weight" - tensor_list = self._experts[bid][ename] - datas.append(torch.cat(tensor_list, dim=wid[2]) if len(tensor_list) > 1 else tensor_list[0]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"transformer.decoder_layer.{bid}.moe.{wid[0]}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - - for t in deferred: - yield from super().modify_tensors(*t) - - -@ModelBase.register("DbrxForCausalLM") -class DbrxModel(TextModel): - model_arch = gguf.MODEL_ARCH.DBRX - - def set_gguf_parameters(self): - ffn_config = self.hparams["ffn_config"] - attn_config = self.hparams["attn_config"] - self.gguf_writer.add_block_count(self.block_count) - - self.gguf_writer.add_context_length(self.hparams["max_seq_len"]) - self.gguf_writer.add_embedding_length(self.hparams["d_model"]) - self.gguf_writer.add_feed_forward_length(ffn_config["ffn_hidden_size"]) - - self.gguf_writer.add_head_count(self.hparams["n_heads"]) - self.gguf_writer.add_head_count_kv(attn_config["kv_n_heads"]) - - self.gguf_writer.add_rope_freq_base(attn_config["rope_theta"]) - - self.gguf_writer.add_clamp_kqv(attn_config["clip_qkv"]) - - self.gguf_writer.add_expert_count(ffn_config["moe_num_experts"]) - self.gguf_writer.add_expert_used_count(ffn_config["moe_top_k"]) - - self.gguf_writer.add_layer_norm_eps(1e-5) - - self.gguf_writer.add_file_type(self.ftype) - logger.info(f"gguf: file type = {self.ftype}") - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_expert = self.hparams["ffn_config"]["moe_num_experts"] - n_ff = self.hparams["ffn_config"]["ffn_hidden_size"] - n_embd = self.hparams["d_model"] - - # Specific behavior for experts tensors: suffix .weight, view as 3D and transpose - # original implementation expects (n_expert, n_ff, n_embd) for all experts weights - # But llama.cpp moe graph works differently - # AND the dimensions in ggml are typically in the reverse order of the pytorch dimensions - # so (n_expert, n_ff, n_embd) in pytorch is {n_embd, n_ff, n_expert} in ggml_tensor - exp_tensor_names = {"ffn.experts.mlp.w1": None, # LLM_TENSOR_FFN_GATE_EXPS ggml_tensor->ne{n_embd, n_ff, n_expert} - "ffn.experts.mlp.w2": (0, 2, 1), # LLM_TENSOR_FFN_DOWN_EXPS ggml_tensor->ne{n_ff, n_embd, n_expert} - "ffn.experts.mlp.v1": None} # LLM_TENSOR_FFN_UP_EXPS ggml_tensor->ne{n_embd, n_ff, n_expert} - experts = False - - for exp_tensor_name in exp_tensor_names.keys(): - if name.find(exp_tensor_name) != -1 and name.find(".weight") == -1: - experts = True - data_torch = data_torch.view(n_expert, n_ff, n_embd) - if (permute_tensor := exp_tensor_names[exp_tensor_name]) is not None: - data_torch = data_torch.permute(*permute_tensor) - break - - # map tensor names - # In MoE models the ffn tensors are typically most of the model weights, - # and need to be quantizable. Quantize expects tensor names to be suffixed by .weight. - # Every other model has the weight names ending in .weight, - # let's assume that is the convention which is not the case for dbrx: - # https://huggingface.co/databricks/dbrx-instruct/blob/main/model.safetensors.index.json#L15 - new_name = self.map_tensor_name(name if not experts else name + ".weight", try_suffixes=(".weight",)) - - yield from super().modify_tensors(data_torch, new_name, bid) - - def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool: - del name, new_name, bid # unused - - return n_dims > 1 - - -@ModelBase.register("MiniCPMForCausalLM") -class MiniCPMModel(TextModel): - model_arch = gguf.MODEL_ARCH.MINICPM - - def set_gguf_parameters(self): - super().set_gguf_parameters() - embedding_scale = float(self.hparams["scale_emb"]) - self.gguf_writer.add_embedding_scale(embedding_scale) - logger.info(f"gguf: (minicpm) embedding_scale = {embedding_scale}") - residual_scale = self.hparams["scale_depth"] / self.hparams["num_hidden_layers"] ** 0.5 - self.gguf_writer.add_residual_scale(residual_scale) - logger.info(f"gguf: (minicpm) residual_scale = {residual_scale}") - logit_scale = self.hparams["hidden_size"] / self.hparams["dim_model_base"] - self.gguf_writer.add_logit_scale(logit_scale) - logger.info(f"gguf: (minicpm) logit_scale = {logit_scale}") - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - rope_dims = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - - rope_scaling = self.find_hparam(['rope_scaling'], True) - if rope_scaling is not None: - long_factors = rope_scaling.get('long_factor', None) - short_factors = rope_scaling.get('short_factor', None) - - if long_factors is None or short_factors is None: - raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') - - if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: - raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}') - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - - # HF models permute some of the tensors, so we need to undo that - if name.endswith(("q_proj.weight")): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight")): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("MiniCPM3ForCausalLM") -class MiniCPM3Model(TextModel): - model_arch = gguf.MODEL_ARCH.MINICPM3 - - def set_gguf_parameters(self): - hparams = self.hparams - - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) - self.gguf_writer.add_head_count(hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(hparams["num_key_value_heads"]) - self.gguf_writer.add_layer_norm_rms_eps(hparams["rms_norm_eps"]) - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - if "q_lora_rank" in hparams and hparams["q_lora_rank"] is not None: - self.gguf_writer.add_q_lora_rank(hparams["q_lora_rank"]) - self.gguf_writer.add_kv_lora_rank(hparams["kv_lora_rank"]) - self.gguf_writer.add_key_length(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) - self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - rope_scaling = self.find_hparam(['rope_scaling'], True) - if rope_scaling is not None: - rope_dims = self.hparams["qk_rope_head_dim"] - - long_factors = rope_scaling.get('long_factor', None) - short_factors = rope_scaling.get('short_factor', None) - - if long_factors is None or short_factors is None: - raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') - - if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: - raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}') - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def _reverse_hf_permute(self, weights: Tensor, n_head: int, n_kv_head: int | None = None) -> Tensor: - if n_kv_head is not None and n_head != n_kv_head: - n_head //= n_kv_head - - return ( - weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape) - ) - - -@ModelBase.register("QWenLMHeadModel") -class QwenModel(TextModel): - model_arch = gguf.MODEL_ARCH.QWEN - - @staticmethod - def token_bytes_to_string(b): - from transformers.models.gpt2.tokenization_gpt2 import bytes_to_unicode # ty: ignore[unresolved-import] - byte_encoder = bytes_to_unicode() - return ''.join([byte_encoder[ord(char)] for char in b.decode('latin-1')]) - - @staticmethod - def bpe(mergeable_ranks: dict[bytes, int], token: bytes, max_rank: int | None = None) -> list[bytes]: - parts = [bytes([b]) for b in token] - while True: - min_idx = None - min_rank = None - for i, pair in enumerate(zip(parts[:-1], parts[1:])): - rank = mergeable_ranks.get(pair[0] + pair[1]) - if rank is not None and (min_rank is None or rank < min_rank): - min_idx = i - min_rank = rank - if min_rank is None or (max_rank is not None and min_rank >= max_rank): - break - assert min_idx is not None - parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2:] - return parts - - def set_vocab(self): - self._set_vocab_qwen() - - -@ModelBase.register( - "Qwen2Model", - "Qwen2ForCausalLM", - "Qwen2AudioForConditionalGeneration", - "KORMoForCausalLM", - "AudioFlamingo3ForConditionalGeneration", - "DotsOCRForCausalLM", -) -class Qwen2Model(TextModel): - model_arch = gguf.MODEL_ARCH.QWEN2 - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self._try_set_pooling_type() - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.hf_arch == "Qwen2Model": - name = f"model.{name}" # map to Qwen2ForCausalLM tensors - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("DreamModel") -class DreamModel(TextModel): - model_arch = gguf.MODEL_ARCH.DREAM - - def get_vocab_base(self) -> tuple[list[str], list[int], str]: - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - - vocab_dict = tokenizer.get_vocab() # ty: ignore[unresolved-attribute] - vocab_size = self.hparams.get("vocab_size", len(vocab_dict)) - assert max(vocab_dict.values()) < vocab_size - - tokpre = self.get_vocab_base_pre(tokenizer) - - reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab_dict.items()} - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - elif reverse_vocab[i] in added_vocab: - tokens.append(reverse_vocab[i]) - # Check if it's a special token - treat special tokens as CONTROL tokens - if hasattr(tokenizer, 'added_tokens_decoder') and i in tokenizer.added_tokens_decoder: - if tokenizer.added_tokens_decoder[i].special: - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.USER_DEFINED) - else: - # Fallback: treat all added vocab as control tokens for special tokens like <|im_start|> - toktypes.append(gguf.TokenType.CONTROL) - else: - tokens.append(reverse_vocab[i]) - toktypes.append(gguf.TokenType.NORMAL) - - return tokens, toktypes, tokpre - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self._try_set_pooling_type() - - # Dream models use non-causal attention for diffusion - self.gguf_writer.add_causal_attention(False) - - # Add Dream-specific parameters - mask_token_id = self.hparams.get("mask_token_id") - if mask_token_id is not None: - self.gguf_writer.add_mask_token_id(mask_token_id) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Dream model tensors should be mapped directly since it's the base model - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("LLaDAModelLM") -class LLaDAModel(TextModel): - model_arch = gguf.MODEL_ARCH.LLADA - undo_permute = True - - def get_vocab_base(self) -> tuple[list[str], list[int], str]: - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - - vocab_dict = tokenizer.get_vocab() # ty: ignore[unresolved-attribute] - vocab_size = self.hparams.get("vocab_size", len(vocab_dict)) - assert max(vocab_dict.values()) < vocab_size - - tokpre = self.get_vocab_base_pre(tokenizer) - - reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab_dict.items()} - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - elif reverse_vocab[i] in added_vocab: - tokens.append(reverse_vocab[i]) - # Check if it's a special token - treat special tokens as CONTROL tokens - if hasattr(tokenizer, 'added_tokens_decoder') and i in tokenizer.added_tokens_decoder: - if tokenizer.added_tokens_decoder[i].special: - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.USER_DEFINED) - else: - # Fallback: treat all added vocab as control tokens for special tokens like <|im_start|> - toktypes.append(gguf.TokenType.CONTROL) - else: - tokens.append(reverse_vocab[i]) - toktypes.append(gguf.TokenType.NORMAL) - - return tokens, toktypes, tokpre - - def set_vocab(self): - self._set_vocab_gpt2() - - # LLaDA specific parameters - self.gguf_writer.add_add_bos_token(True) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self._try_set_pooling_type() - - # Add parameters similar to LlamaModel - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - if (rope_dim := hparams.get("head_dim")) is None: - n_heads = hparams.get("num_attention_heads", hparams.get("n_heads")) - assert n_heads is not None - rope_dim = hparams.get("hidden_size", hparams.get("d_model")) // n_heads - self.gguf_writer.add_rope_dimension_count(rope_dim) - - # Set context length for LLaDA - context_length = self.hparams.get("max_sequence_length", 4096) - self.gguf_writer.add_context_length(context_length) - - # Set embedding length (dimension size) - embedding_length = self.hparams.get("d_model", 4096) - self.gguf_writer.add_embedding_length(embedding_length) - - # Set feed forward length (MLP hidden size) - feed_forward_length = self.hparams.get("mlp_hidden_size", 12288) - self.gguf_writer.add_feed_forward_length(feed_forward_length) - - # LLaDA models use non-causal attention for diffusion, similar to Dream - self.gguf_writer.add_causal_attention(False) - - # LLaDA models don't shift their logits - self.gguf_writer.add_diffusion_shift_logits(False) - - @staticmethod - def permute(weights: Tensor, n_head: int, n_head_kv: int | None): - if n_head_kv is not None and n_head != n_head_kv: - n_head = n_head_kv - return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams.get("num_attention_heads", self.hparams.get("n_heads")) - assert n_head is not None - n_kv_head = self.hparams.get("num_key_value_heads", self.hparams.get("n_kv_heads")) - - if self.undo_permute: - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = LLaDAModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = LLaDAModel.permute(data_torch, n_head, n_kv_head) - - # LLaDA model tensors should be mapped directly since it's the base model - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Ernie4_5_ForCausalLM", "Ernie4_5ForCausalLM") -class Ernie4_5Model(TextModel): - model_arch = gguf.MODEL_ARCH.ERNIE4_5 - - def set_vocab(self): - self._set_vocab_sentencepiece() - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - if "add_prefix_space" in tokenizer_config_json: - self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "ernie." in name: - name = name.replace("ernie.", "model.") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - num_heads = self.hparams["num_attention_heads"] - num_kv_heads = self.hparams["num_key_value_heads"] - if (head_dim := self.hparams.get("head_dim")) is None: - head_dim = self.hparams["hidden_size"] // num_heads - - # split the qkv weights - # qkv_proj shape: [(num_heads + 2 * num_kv_heads) * head_dim, hidden_size] - if "qkv_proj" in name: - name_q = name.replace("qkv_proj.weight", "q_proj.weight") - name_k = name.replace("qkv_proj.weight", "k_proj.weight") - name_v = name.replace("qkv_proj.weight", "v_proj.weight") - total_q_dim = num_heads * head_dim - total_k_dim = num_kv_heads * head_dim - total_v_dim = num_kv_heads * head_dim - q_proj_weight, k_proj_weight, v_proj_weight = data_torch.split([total_q_dim, total_k_dim, total_v_dim], dim=0) - yield from super().modify_tensors(q_proj_weight, name_q, bid) - yield from super().modify_tensors(k_proj_weight, name_k, bid) - yield from super().modify_tensors(v_proj_weight, name_v, bid) - # split the up_gate_proj into gate and up - # up_gate_proj shape: [2 * intermediate_size, hidden_size] - elif "up_gate_proj" in name: - name_up = name.replace("up_gate_proj.weight", "up_proj.weight") - name_gate = name.replace("up_gate_proj.weight", "gate_proj.weight") - dim_half = data_torch.shape[0] // 2 - gate_proj_weight, up_proj_weight = data_torch.split(dim_half, dim=0) - yield from super().modify_tensors(gate_proj_weight, name_gate, bid) - yield from super().modify_tensors(up_proj_weight, name_up, bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Ernie4_5_MoeForCausalLM") -class Ernie4_5MoeModel(Ernie4_5Model): - model_arch = gguf.MODEL_ARCH.ERNIE4_5_MOE - _experts: list[dict[str, Tensor]] | None = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._experts = [{} for _ in range(self.block_count)] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_expert_count(self.hparams["moe_num_experts"]) - self.gguf_writer.add_expert_used_count(self.hparams["moe_k"]) - self.gguf_writer.add_interleave_moe_layer_step(self.hparams["moe_layer_interval"]) - self.gguf_writer.add_leading_dense_block_count(self.hparams["moe_layer_start_index"]) - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - if (shared_expert_count := self.hparams.get('moe_num_shared_experts')) is not None: - self.gguf_writer.add_expert_shared_count(shared_expert_count) - if shared_expert_count > 0 and (shared_expert_intermediate_size := self.hparams.get('intermediate_size')) is not None and (num_key_value_heads := self.hparams.get('num_key_value_heads')) is not None: - self.gguf_writer.add_expert_shared_feed_forward_length(shared_expert_intermediate_size // num_key_value_heads) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # skip Multi-Token Prediction (MTP) layers (again, same as DeepseekV2) - match = re.match(r"model.mtp_block.(\d+)", name) - if match: - return None - - # skip all other MTP tensors for now - match = re.match(r"model.mtp_emb_norm.(\d+)", name) - if match: - return None - - match = re.match(r"model.mtp_hidden_norm.(\d+)", name) - if match: - return None - - match = re.match(r"model.mtp_linear_proj.(\d+)", name) - if match: - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # process the experts separately - if name.find("mlp.experts") != -1: - n_experts = self.hparams["moe_num_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["gate_proj", "up_proj", "down_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename_to_retrieve]) - del self._experts[bid][ename_to_retrieve] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - yield from super().modify_tensors(data_torch, merged_name, bid) - else: - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("PaddleOCRVLForConditionalGeneration") -class PaddleOCRModel(Ernie4_5Model): - model_arch = gguf.MODEL_ARCH.PADDLEOCR - - -@ModelBase.register("PaddleOCRVisionModel") -class PaddleOCRVisionModel(MmprojModel): - # PaddleOCR-VL uses a modified version of Siglip - min_pixels: int = 0 - max_pixels: int = 0 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.min_pixels = self.preprocessor_config["min_pixels"] - self.max_pixels = self.preprocessor_config["max_pixels"] - self.hparams_vision["image_size"] = int(math.sqrt(self.max_pixels)) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - hparams = self.hparams_vision - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PADDLEOCR) - self.gguf_writer.add_vision_max_pixels(self.max_pixels) - self.gguf_writer.add_vision_min_pixels(self.min_pixels) - self.gguf_writer.add_vision_use_gelu(True) - self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("rms_norm_eps", 1e-6)) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "vision_model" not in name and "mlp_AR" not in name: - return None - name = name.replace("visual.", "model.") - if "packing_position_embedding" in name: - # unused - return None - if "vision_model.head" in name: - # we don't yet support image embeddings for this model - return None - - return super().filter_tensors((name, gen)) - - -@ModelBase.register( - "Qwen2VLModel", - "Qwen2VLForConditionalGeneration", - "Qwen2_5_VLForConditionalGeneration", - "Qwen2_5OmniModel", -) -class Qwen2VLModel(TextModel): - model_arch = gguf.MODEL_ARCH.QWEN2VL - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("thinker."): - name = name.replace("thinker.", "") - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("Qwen2VLModel", "Qwen2VLForConditionalGeneration", "Qwen2_5_VLForConditionalGeneration") -class Qwen2VLVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["image_size"] = self.hparams_vision.get("image_size", 560) - # rename config.json values - self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_heads") - self.hparams_vision["num_hidden_layers"] = self.hparams_vision.get("depth") - if "embed_dim" in self.hparams_vision: # qwen2vl - self.hparams_vision["intermediate_size"] = self.hparams_vision.get("hidden_size") - self.hparams_vision["hidden_size"] = self.hparams_vision.get("embed_dim") - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - hparams = self.hparams_vision - model_type = self.global_config['model_type'] - if model_type == 'qwen2_vl': - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN2VL) - elif model_type == 'qwen2_5_vl' or model_type == 'qwen2_5_omni': - if model_type == 'qwen2_5_omni': - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25O) - else: - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25VL) - self.gguf_writer.add_vision_use_silu(True) - # find n_wa_pattern (window attention pattern) - fullatt_block_indexes = hparams.get("fullatt_block_indexes") - assert fullatt_block_indexes is not None, "fullatt_block_indexes is required for qwen2_5_vl" - n_wa_pattern = fullatt_block_indexes[0] + 1 - # validate n_wa_pattern - for i in range(1, len(fullatt_block_indexes)): - if fullatt_block_indexes[i] - fullatt_block_indexes[i - 1] != n_wa_pattern: - raise ValueError(f"Invalid fullatt_block_indexes: {fullatt_block_indexes}") - self.gguf_writer.add_vision_n_wa_pattern(n_wa_pattern) - else: - raise ValueError(f"Unknown QwenVL model type: {self.global_config['model_type']}") - # default values below are taken from HF tranformers code - self.gguf_writer.add_vision_attention_layernorm_eps(self.global_config.get("rms_norm_eps", 1e-6)) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".position_embd." in new_name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if not name.startswith("visual."): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # split QKV tensors if needed - if ".qkv." in name: - if data_torch.ndim == 2: # weight - c3, _ = data_torch.shape - else: # bias - c3 = data_torch.shape[0] - assert c3 % 3 == 0 - c = c3 // 3 - wq = data_torch[:c] - wk = data_torch[c: c * 2] - wv = data_torch[c * 2:] - yield from super().modify_tensors(wq, name.replace("qkv", "q"), bid) - yield from super().modify_tensors(wk, name.replace("qkv", "k"), bid) - yield from super().modify_tensors(wv, name.replace("qkv", "v"), bid) - elif 'patch_embed.proj.weight' in name: - # split Conv3D into Conv2Ds - c1, c2, kt, kh, kw = data_torch.shape - del c1, c2, kh, kw # unused - assert kt == 2, "Current implementation only support temporal_patch_size of 2" - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight" , data_torch[:, :, 0, ...]) - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight.1", data_torch[:, :, 1, ...]) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -class Qwen25AudioModel(MmprojModel): - has_audio_encoder = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_audio is not None - self.hparams_audio["hidden_size"] = self.hparams_audio["d_model"] - self.hparams_audio["intermediate_size"] = self.hparams_audio["encoder_ffn_dim"] - self.hparams_audio["num_attention_heads"] = self.hparams_audio["encoder_attention_heads"] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_audio is not None - self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["num_mel_bins"]) - self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams_audio.get("layer_norm_eps", 1e-5)) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - # SinusoidsPositionEmbedding - assert self.hparams_audio is not None - max_timescale = 10000 - length = 1500 - channels = self.hparams_audio["hidden_size"] - log_timescale_increment = np.log(max_timescale) / (channels // 2 - 1) - inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2).float()) - scaled_time = torch.arange(length)[:, np.newaxis] * inv_timescales[np.newaxis, :] - pos_embd = torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1).to(dtype=torch.float32) - yield ("audio_tower.embed_positions.weight", pos_embd) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F16 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "conv1.bias" in name or "conv2.bias" in name: - # transpose conv1 and conv2 bias - data_torch = data_torch.unsqueeze(-1) - - yield from MmprojModel.modify_tensors(self, data_torch, name, bid) - - -@ModelBase.register("Qwen2_5OmniModel") -class Qwen25OmniModel(Qwen2VLVisionModel, Qwen25AudioModel): - has_audio_encoder = True - has_vision_encoder = True - - def get_vision_config(self) -> dict[str, Any] | None: - return self.global_config["thinker_config"].get("vision_config") - - def get_audio_config(self) -> dict[str, Any] | None: - return self.global_config["thinker_config"].get("audio_config") - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN25O) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if not name.startswith("visual.") and not name.startswith("audio_tower."): - return None - - if name.startswith("thinker."): - name = name.replace("thinker.", "") - - if "audio_bos_eos_token" in name: - # this tensor is left unused in transformers code - # https://github.com/huggingface/transformers/blob/6e3063422c4b1c014aa60c32b9254fd2902f0f28/src/transformers/models/qwen2_5_omni/modular_qwen2_5_omni.py#L1809 - return None - - return MmprojModel.filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "visual." in name: - yield from Qwen2VLVisionModel.modify_tensors(self, data_torch, name, bid) - elif "audio_tower." in name: - yield from Qwen25AudioModel.modify_tensors(self, data_torch, name, bid) - return # skip other tensors - - -@ModelBase.register("InternVisionModel") -class InternVisionModel(MmprojModel): - - min_dynamic_tiles: int = 0 - max_dynamic_tiles: int = 0 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.min_dynamic_tiles = self.global_config.get("min_dynamic_patch", 0) - self.max_dynamic_tiles = self.global_config.get("max_dynamic_patch", 0) - - def set_gguf_parameters(self): - assert self.hparams_vision is not None - if isinstance(self.hparams_vision['image_size'], list): - self.hparams_vision['image_size'] = self.hparams_vision['image_size'][0] - if isinstance(self.hparams_vision['patch_size'], list): - self.hparams_vision['patch_size'] = self.hparams_vision['patch_size'][0] - super().set_gguf_parameters() - - hparams = self.hparams - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.INTERNVL) - self.gguf_writer.add_vision_attention_layernorm_eps(hparams["layer_norm_eps"]) - # hidden_act - if hparams["hidden_act"] == "silu": - self.gguf_writer.add_vision_use_silu(True) - elif hparams["hidden_act"] == "gelu": - self.gguf_writer.add_vision_use_gelu(True) - else: - raise ValueError(f"Unsupported hidden_act: {hparams['hidden_act']}") - # downsample_ratio - downsample_ratio = self.global_config.get("downsample_ratio") - assert downsample_ratio is not None - self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / downsample_ratio)) - # older models may not have min/max_dynamic_patch in config - if self.min_dynamic_tiles > 0: - self.gguf_writer.add_vision_preproc_min_tiles(self.min_dynamic_tiles) - if self.max_dynamic_tiles > 0: - self.gguf_writer.add_vision_preproc_max_tiles(self.max_dynamic_tiles) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".position_embd." in new_name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - vision_prefix = ['vision_model', 'mlp', 'model.vision_tower', 'model.multi_modal_projector'] - if not any([name.startswith(prefix) for prefix in vision_prefix]): - return None - # deal with intern-s1 special case - names_map = { - "model.multi_modal_projector.layer_norm.bias": "mlp1.0.bias", - "model.multi_modal_projector.layer_norm.weight": "mlp1.0.weight", - "model.multi_modal_projector.linear_1.bias": "mlp1.1.bias", - "model.multi_modal_projector.linear_1.weight": "mlp1.1.weight", - "model.multi_modal_projector.linear_2.bias": "mlp1.3.bias", - "model.multi_modal_projector.linear_2.weight": "mlp1.3.weight", - } - if name in names_map: - name = names_map[name] - # correct name - if name.startswith("vision_model"): - name = "vision_tower." + name - if (".ls" in name or ".lambda_" in name or "position_embedding" in name) and not name.endswith(".weight"): - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # split QKV tensors if needed - if ".qkv." in name: - if data_torch.ndim == 2: # weight - c3, _ = data_torch.shape - else: # bias - c3 = data_torch.shape[0] - assert c3 % 3 == 0 - c = c3 // 3 - wq = data_torch[:c] - wk = data_torch[c: c * 2] - wv = data_torch[c * 2:] - yield from super().modify_tensors(wq, name.replace("attn.qkv", "self_attn.q_proj"), bid) - yield from super().modify_tensors(wk, name.replace("attn.qkv", "self_attn.k_proj"), bid) - yield from super().modify_tensors(wv, name.replace("attn.qkv", "self_attn.v_proj"), bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register( - "NemotronH_Nano_VL_V2", - "RADIOModel", -) -class NemotronNanoV2VLModel(MmprojModel): - # ViT-Huge architecture parameters for RADIO v2.5-h - _vit_hidden_size = 1280 - _vit_intermediate_size = 5120 - _vit_num_layers = 32 - _vit_num_heads = 16 - - def get_vision_config(self) -> dict[str, Any] | None: - # RADIO config doesn't have standard ViT parameters, so they need to be constructed manually - vision_config = self.global_config.get("vision_config") - if vision_config is None: - return None - # Add ViT-H parameters - vision_config = { - **vision_config, - "hidden_size": self._vit_hidden_size, - "intermediate_size": self._vit_intermediate_size, - "num_hidden_layers": self._vit_num_layers, - "num_attention_heads": self._vit_num_heads, - "image_size": self.global_config.get("force_image_size", 512), - } - return vision_config - - def set_gguf_parameters(self): - if "image_mean" not in self.preprocessor_config: - self.preprocessor_config["image_mean"] = [0.485, 0.456, 0.406] - if "image_std" not in self.preprocessor_config: - self.preprocessor_config["image_std"] = [0.229, 0.224, 0.225] - - super().set_gguf_parameters() - hparams = self.global_config - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.NEMOTRON_V2_VL) - self.gguf_writer.add_vision_attention_layernorm_eps(1e-6) - self.gguf_writer.add_vision_use_gelu(True) - downsample_ratio = hparams.get("downsample_ratio", 0.5) - self.gguf_writer.add_vision_projector_scale_factor(int(1.0 / downsample_ratio)) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".position_embd." in new_name or "pos_embed" in new_name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "input_conditioner" in name: - return None - - # mtmd does not support video yet so skip tensors related to video. - if "radio_model.model.patch_generator.video_embedder" in name: - return None - - if not name.startswith("vision_model.radio_model.model.") and not name.startswith("mlp1."): - return None - - if "patch_generator.pos_embed" in name: - if not name.endswith(".weight"): - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # RADIO's pos_embed doesn't have .weight suffix, but clip.cpp expects it - if "patch_generator.pos_embed" in name: - # Downsample position embeddings for fixed 512x512 image size - import torch.nn.functional as F - n_embd = self.hparams["hidden_size"] - image_size = self.global_config.get("force_image_size", 512) - patch_size = self.hparams["patch_size"] - target_patches_per_side = image_size // patch_size # 32 - max_patches_per_side = int((data_torch.shape[1]) ** 0.5) # 128 - if target_patches_per_side != max_patches_per_side: - # Reshape to grid, interpolate, flatten back - data_torch = data_torch.reshape(1, max_patches_per_side, max_patches_per_side, n_embd) - data_torch = data_torch.permute(0, 3, 1, 2).float() # [1, n_embd, 128, 128] - data_torch = F.interpolate(data_torch, size=(target_patches_per_side, target_patches_per_side), - mode='bilinear', align_corners=True) - data_torch = data_torch.permute(0, 2, 3, 1) # [1, 32, 32, n_embd] - data_torch = data_torch.reshape(1, target_patches_per_side * target_patches_per_side, n_embd) - - # Reshape linear patch embedding to conv2d format for ggml_conv_2d - # From [n_embd, patch_size*patch_size*3] to [n_embd, 3, patch_size, patch_size] - if "patch_generator.embedder" in name: - patch_size = self.hparams["patch_size"] - n_embd = self.hparams["hidden_size"] - data_torch = data_torch.reshape(n_embd, 3, patch_size, patch_size) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("WavTokenizerDec") -class WavTokenizerDecModel(TextModel): - model_arch = gguf.MODEL_ARCH.WAVTOKENIZER_DEC - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if \ - name.endswith("codebook.cluster_size") or \ - name.endswith("codebook.embed_avg") or \ - name.endswith("codebook.inited"): - logger.debug(f"Skipping {name!r}") - return None - - return super().filter_tensors(item) - - def set_vocab(self): - self._set_vocab_none() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_vocab_size (self.hparams["vocab_size"]) - self.gguf_writer.add_features_length (self.hparams["n_embd_features"]) - self.gguf_writer.add_feed_forward_length(self.hparams["n_ff"]) - self.gguf_writer.add_group_norm_eps (self.hparams["group_norm_epsilon"]) - self.gguf_writer.add_group_norm_groups (self.hparams["group_norm_groups"]) - - self.gguf_writer.add_posnet_embedding_length(self.hparams["posnet"]["n_embd"]) - self.gguf_writer.add_posnet_block_count (self.hparams["posnet"]["n_layer"]) - - self.gguf_writer.add_convnext_embedding_length(self.hparams["convnext"]["n_embd"]) - self.gguf_writer.add_convnext_block_count (self.hparams["convnext"]["n_layer"]) - - self.gguf_writer.add_causal_attention(False) - - -@ModelBase.register("Qwen2MoeForCausalLM") -class Qwen2MoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.QWEN2MOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") - if (shared_expert_intermediate_size := self.hparams.get('shared_expert_intermediate_size')) is not None: - self.gguf_writer.add_expert_shared_feed_forward_length(shared_expert_intermediate_size) - logger.info(f"gguf: expert shared feed forward length = {shared_expert_intermediate_size}") - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # handle aggregated expert tensors - # GGUF stores dimensions reversed from PyTorch, so: - # PyTorch (A,B,C) -> GGUF writes [C,B,A] -> GGML reads ne={C,B,A} - # Input shapes from HF: (n_expert, n_ff_exp, n_embd) or (n_expert, n_embd, n_ff_exp) - # Expected GGML ne: {n_embd, n_ff_exp, n_expert} for gate/up, {n_ff_exp, n_embd, n_expert} for down - if name.endswith("mlp.experts.down_proj") or name.endswith("mlp.experts.down_proj.weight"): - mapped = f"{name}.weight" if not name.endswith(".weight") else name - # HF: [n_expert, n_embd, n_ff] -> GGML: {n_ff, n_embd, n_expert} - yield from super().modify_tensors(data_torch, mapped, bid) - return - - if name.endswith("mlp.experts.gate_up_proj") or name.endswith("mlp.experts.gate_up_proj.weight"): - if data_torch.ndim < 3 or data_torch.shape[-2] % 2 != 0: - raise ValueError(f"Unexpected gate_up_proj shape for {name}: {tuple(data_torch.shape)}") - # HF: [n_expert, 2*n_ff, n_embd] -> split on dim=-2 - n_ff = data_torch.shape[-2] // 2 - gate = data_torch[..., :n_ff, :].contiguous() - up = data_torch[..., n_ff:, :].contiguous() - # gate/up: [n_expert, n_ff, n_embd] -> GGML: {n_embd, n_ff, n_expert} - base_name = name.removesuffix(".weight").removesuffix(".gate_up_proj") - mapped_gate = f"{base_name}.gate_proj.weight" - mapped_up = f"{base_name}.up_proj.weight" - yield from super().modify_tensors(gate, mapped_gate, bid) - yield from super().modify_tensors(up, mapped_up, bid) - return - - if name.find("experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("Qwen3ForCausalLM", "Qwen3Model") -class Qwen3Model(Qwen2Model): - model_arch = gguf.MODEL_ARCH.QWEN3 - - # extra logic for rerank models - is_rerank: bool = False - is_tied_embeddings: bool = False - token_false_id: int | None = None - token_true_id: int | None = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # track for intern-s1-mini - hparams = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) - self.origin_hf_arch = hparams.get('architectures', [None])[0] - - if self._is_qwen3_reranker(): - self._find_rerank_config() - - def _is_qwen3_reranker(self) -> bool: - readme_path = self.dir_model / "README.md" - readme_text = "" - if readme_path.exists(): - with readme_path.open("r", encoding="utf-8") as f: - readme_text = f.read() - - name_hints = [ - str(self.dir_model.name), - str(self.hparams.get("_name_or_path", "")), - str(self.hparams.get("model_type", "")), - str(self.origin_hf_arch or ""), - ] - name_hints = [hint.lower() for hint in name_hints if hint] - - if "# qwen3-reranker" in readme_text.lower() or "# qwen3-vl-reranker" in readme_text.lower(): - return True - - if any("qwen3-reranker" in hint or "qwen3-vl-reranker" in hint for hint in name_hints): - return True - - return "sequenceclassification" in (self.origin_hf_arch or "").lower() - - def set_vocab(self): - # deal with intern-s1-mini - if self.origin_hf_arch == 'InternS1ForConditionalGeneration': - self._set_vocab_interns1() - return - - super().set_vocab() - - def _find_rerank_config(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - - self.is_rerank = True - self.is_tied_embeddings = self.hparams.get("tie_word_embeddings", False) - self.token_false_id = tokenizer.convert_tokens_to_ids("no") # ty: ignore[unresolved-attribute, invalid-assignment] - self.token_true_id = tokenizer.convert_tokens_to_ids("yes") # ty: ignore[unresolved-attribute, invalid-assignment] - self.sep_token_id = tokenizer.convert_tokens_to_ids("|") # ty: ignore[unresolved-attribute] - - assert self.token_false_id is not None and self.token_true_id is not None - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if self.is_rerank: - self.gguf_writer.add_pooling_type(gguf.PoolingType.RANK) - self.gguf_writer.add_classifier_output_labels(["yes", "no"]) - self.gguf_writer.add_chat_template([{ - "name": "rerank", - "template": "<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be \"yes\" or \"no\".<|im_end|>\n" - "<|im_start|>user\n: Given a web search query, retrieve relevant passages that answer the query\n: {query}\n: {document}<|im_end|>\n" - "<|im_start|>assistant\n\n\n\n\n" - }]) - - def _get_cls_out_tensor(self, data_torch: Tensor) -> Tensor: - # extract "yes" and "no" tokens from the output lm_head tensor - false_row = data_torch[self.token_false_id] - true_row = data_torch[self.token_true_id] - return torch.stack([true_row, false_row], dim=0) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.is_rerank: - is_tied_head = self.is_tied_embeddings and "embed_tokens" in name - is_real_head = not self.is_tied_embeddings and "lm_head" in name - if is_tied_head or is_real_head: - cls_out_head = ( - gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.CLS_OUT] + ".weight", - self._get_cls_out_tensor(data_torch), - ) - yield cls_out_head - if is_tied_head: - yield from super().modify_tensors(data_torch, name, bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Qwen3MoeForCausalLM") -class Qwen3MoeModel(Qwen2MoeModel): - model_arch = gguf.MODEL_ARCH.QWEN3MOE - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - hparams = ModelBase.load_hparams(self.dir_model, False) - self.origin_hf_arch = hparams.get('architectures', [None])[0] - - def set_vocab(self): - # deal with intern-s1 - if self.origin_hf_arch == 'InternS1ForConditionalGeneration': - self._set_vocab_interns1() - return - - super().set_vocab() - - -@ModelBase.register("Qwen3NextForCausalLM") -class Qwen3NextModel(Qwen2MoeModel): - model_arch = gguf.MODEL_ARCH.QWEN3NEXT - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_ssm_conv_kernel(self.hparams["linear_conv_kernel_dim"]) - self.gguf_writer.add_ssm_state_size(self.hparams["linear_key_head_dim"]) - self.gguf_writer.add_ssm_group_count(self.hparams["linear_num_key_heads"]) - self.gguf_writer.add_ssm_time_step_rank(self.hparams["linear_num_value_heads"]) - self.gguf_writer.add_ssm_inner_size(self.hparams["linear_value_head_dim"] * self.hparams["linear_num_value_heads"]) - self.gguf_writer.add_full_attention_interval(self.hparams.get("full_attention_interval", 4)) - if (rope_dim := self.hparams.get("head_dim")) is None: - rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.25))) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("mtp"): - # ignore MTP layers for now - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.endswith(".A_log"): - data_torch = -torch.exp(data_torch) - elif name.endswith(".dt_bias"): - name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" - elif "conv1d" in name: - data_torch = data_torch.squeeze() - elif name.endswith("norm.weight") and not name.endswith("linear_attn.norm.weight"): - data_torch = data_torch + 1 - - if "in_proj_qkvz.weight" in name: - # original order: [q, k, v, z] * head_count - # corrected order: [q * head_count, k * head_count, v * head_count, z * head_count] - head_k_dim = self.hparams["linear_key_head_dim"] - head_v_dim = self.hparams["linear_value_head_dim"] - num_v_heads = self.hparams["linear_num_value_heads"] - num_k_heads = self.hparams["linear_num_key_heads"] - hidden_size = self.hparams["hidden_size"] - split_arg_list_qkvz = [ - head_k_dim, # q partition - head_k_dim, # k partition - (num_v_heads // num_k_heads * head_v_dim), # v partition - (num_v_heads // num_k_heads * head_v_dim), # z partition - ] - # view as (n_embd, head_count, [q+k+v+z]) - data_torch = data_torch.permute(1, 0).contiguous() - data_torch = data_torch.view(-1, num_k_heads, sum(split_arg_list_qkvz)) - # split into q, k, v, z - q, k, v, z = torch.split(data_torch, split_arg_list_qkvz, dim=-1) - # flatten dim + head_count - q = q.contiguous().view(hidden_size, -1) - k = k.contiguous().view(hidden_size, -1) - v = v.contiguous().view(hidden_size, -1) - z = z.contiguous().view(hidden_size, -1) - # stack back - qkv = torch.cat([q, k, v], dim=-1).permute(1, 0).contiguous() - z = z.permute(1, 0).contiguous() - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_QKV, bid, ".weight"), qkv) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_GATE, bid, ".weight"), z) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("RND1") -class RND1Model(Qwen2MoeModel): - model_arch = gguf.MODEL_ARCH.RND1 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # RND1 specific parameters - # RND1 uses bidirectional attention - self.gguf_writer.add_causal_attention(False) - - if (mask_token_id := self.hparams.get("mask_token_id")) is not None: - self.gguf_writer.add_mask_token_id(mask_token_id) - - -@ModelBase.register("Qwen3VLForConditionalGeneration", "Qwen3VLMoeForConditionalGeneration", "Qwen3_5ForConditionalGeneration", "Qwen3_5MoeForConditionalGeneration") -class Qwen3VLVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.hparams_vision is None: - logger.info("No vision config found, skipping vision tensor processing") - return - - # Compute image_size if not present - if "image_size" not in self.hparams_vision: - # For Qwen3VL/Qwen3VLMoe, compute from num_position_embeddings - num_pos = self.hparams_vision.get("num_position_embeddings", 2304) - patch_size = self.hparams_vision.get("patch_size", 16) - # num_position_embeddings = (image_size / patch_size) ** 2 - # So image_size = sqrt(num_position_embeddings) * patch_size - image_size = int(num_pos**0.5 * patch_size) - self.hparams_vision["image_size"] = image_size - - # Rename config values for compatibility - self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_heads") - self.hparams_vision["num_hidden_layers"] = self.hparams_vision.get("depth") - - self.is_deepstack_layers = [False] * int(self.hparams_vision["num_hidden_layers"] or 0) - for idx in self.hparams_vision.get("deepstack_visual_indexes", []): - self.is_deepstack_layers[idx] = True - - def set_gguf_parameters(self): - super().set_gguf_parameters() - # in case mixed modalities, the arch will be handled by subclass - if not self.has_audio_encoder: - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN3VL) - self.gguf_writer.add_vision_use_gelu(True) - - if self.hparams_vision is not None: - merge_size = self.hparams_vision.get("spatial_merge_size") - if merge_size is not None: - self.gguf_writer.add_vision_spatial_merge_size(int(merge_size)) - - # Use text config's rms_norm_eps for vision attention layernorm eps - rms_norm_eps = self.global_config.get("text_config", {}).get("rms_norm_eps", 1e-6) - self.gguf_writer.add_vision_attention_layernorm_eps(rms_norm_eps) - - if self.is_deepstack_layers: - self.gguf_writer.add_vision_is_deepstack_layers(self.is_deepstack_layers) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip text model tensors - if name.startswith("lm_head."): - return None - - # Skip MTP tensors - if name.startswith("mtp."): - return None - - if name.startswith("model.visual."): - name = name.replace("model.visual.", "visual.", 1) - - if not name.startswith("visual."): - return None - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - assert self.hparams_vision is not None - - if name.startswith("visual.deepstack_merger_list."): - prefix, rest = name.split(".", maxsplit=3)[2:] - # prefix is the layer index, convert to absolute clip layer index! - idx = self.hparams_vision.get("deepstack_visual_indexes", [])[int(prefix)] - target = rest - - tensor_type: gguf.MODEL_TENSOR - if target.startswith("norm."): - tensor_type = gguf.MODEL_TENSOR.V_DS_NORM - suffix = target.split(".", 1)[1] - elif target.startswith("linear_fc1."): - tensor_type = gguf.MODEL_TENSOR.V_DS_FC1 - suffix = target.split(".", 1)[1] - elif target.startswith("linear_fc2."): - tensor_type = gguf.MODEL_TENSOR.V_DS_FC2 - suffix = target.split(".", 1)[1] - else: - raise ValueError(f"Unexpected deepstack tensor: {name}") - - new_name = self.format_tensor_name(tensor_type, idx, suffix=f".{suffix}") - yield from super().modify_tensors(data_torch, new_name, bid) - return - - if name.startswith("visual.merger."): - suffix = name.split(".", 2)[2] - if suffix.startswith("linear_fc"): - fc_idx_str, tail = suffix.split(".", 1) - fc_num = int(fc_idx_str.replace("linear_fc", "")) - # Qwen3VL has linear_fc1 and linear_fc2 - # Map to indices 0 and 2 (matching Qwen2VL which uses indices 0 and 2) - if fc_num == 1: - fc_idx = 0 - elif fc_num == 2: - fc_idx = 2 - else: - raise ValueError(f"unexpected fc index {fc_num} in {name}") - new_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, fc_idx, suffix=f".{tail}") - elif suffix.startswith("norm."): - new_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_POST_NORM, suffix=f".{suffix.split('.', 1)[1]}") - else: - raise ValueError(f"Unexpected merger tensor: {name}") - yield (new_name, data_torch) - return - - if name == "visual.patch_embed.proj.weight": - # split Conv3D into Conv2Ds along temporal dimension - c1, c2, kt, _, _ = data_torch.shape - del c1, c2 - if kt != 2: - raise ValueError("Current implementation only supports temporal_patch_size of 2") - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight", data_torch[:, :, 0, ...]) - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight.1", data_torch[:, :, 1, ...]) - return - - if name == "visual.patch_embed.proj.bias": - # Include the bias - it's used by the C++ code - yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".bias", data_torch) - return - - yield from MmprojModel.modify_tensors(self, data_torch, name, bid) - - -@ModelBase.register("Qwen3OmniMoeForConditionalGeneration") -class Qwen3OmniMmprojModel(Qwen3VLVisionModel, Qwen25AudioModel): - has_audio_encoder = True - has_vision_encoder = True - - def get_vision_config(self) -> dict[str, Any] | None: - if self.has_vision_encoder: - return self.global_config["thinker_config"].get("vision_config") - else: - return None - - def get_audio_config(self) -> dict[str, Any] | None: - if self.has_audio_encoder: - return self.global_config["thinker_config"].get("audio_config") - else: - return None - - def set_gguf_parameters(self): - if self.has_vision_encoder: - Qwen3VLVisionModel.set_gguf_parameters(self) - self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.QWEN3VL) - if self.has_audio_encoder: - Qwen25AudioModel.set_gguf_parameters(self) - self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.QWEN3A) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip text model tensors - if name.startswith("lm_head."): - return None - - # Skip MTP tensors - if name.startswith("mtp."): - return None - - if name.startswith("model.visual."): - name = name.replace("model.visual.", "visual.", 1) - - if "visual." not in name and "audio_tower." not in name: - return None - - return MmprojModel.filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "visual." in name: - if not self.has_vision_encoder: - raise ValueError(f"Model does not have vision encoder, but found tensor {name}") - # need to transform vision tensor naming, so that modify_tensors() logic can be used correctly - name = name.replace("thinker.visual.", "model.visual.") - if ".merger_list." in name: - name = name.replace(".merger_list.", ".deepstack_merger_list.") - name = name.replace(".ln_q", ".norm") - name = name.replace(".mlp.0", ".linear_fc1") - name = name.replace(".mlp.2", ".linear_fc2") - elif ".merger." in name: - name = name.replace(".ln_q", ".norm") - name = name.replace(".mlp.0", ".linear_fc1") - name = name.replace(".mlp.2", ".linear_fc2") - yield from Qwen3VLVisionModel.modify_tensors(self, data_torch, name, bid) - elif "audio_tower." in name: - if not self.has_audio_encoder: - raise ValueError(f"Model does not have audio encoder, but found tensor {name}") - if "conv2d" in name and name.endswith(".bias"): - # transform conv2d bias [n_embd] --> [1, 1, n_embd] - data_torch = data_torch.unsqueeze(-1).unsqueeze(-1) - yield from Qwen25AudioModel.modify_tensors(self, data_torch, name, bid) - - -@ModelBase.register("Qwen3ASRForConditionalGeneration") -class Qwen3ASRMmprojModel(Qwen3OmniMmprojModel): - has_audio_encoder = True - has_vision_encoder = False - - -@ModelBase.register("Glm4vForConditionalGeneration", "Glm4vMoeForConditionalGeneration", "GlmOcrForConditionalGeneration") -class Glm4VVisionModel(Qwen3VLVisionModel): - def set_gguf_parameters(self): - MmprojModel.set_gguf_parameters(self) # skip Qwen3VLVisionModel parameters - assert self.hparams_vision is not None - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GLM4V) - - hidden_act = str(self.hparams_vision.get("hidden_act", "")).lower() - if hidden_act == "gelu": - self.gguf_writer.add_vision_use_gelu(True) - elif hidden_act == "silu": - self.gguf_writer.add_vision_use_silu(True) - - rms_norm_eps = self.hparams_vision.get("rms_norm_eps", 1e-5) - self.gguf_writer.add_vision_attention_layernorm_eps(rms_norm_eps) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.startswith("visual.merger."): - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - return - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("StepVLForConditionalGeneration") -class Step3VLVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - - if not self.hparams_vision.get("intermediate_size"): - hidden_size = self.hparams_vision.get("hidden_size") or self.hparams_vision.get("width") or 0 - assert hidden_size > 0 - mlp_ratio = float(self.hparams_vision.get("mlp_ratio", 8960 / 1536)) - self.hparams_vision["intermediate_size"] = int(round(hidden_size * mlp_ratio)) - - self.preprocessor_config.setdefault("image_mean", list(_MISTRAL_COMMON_DATASET_MEAN)) - self.preprocessor_config.setdefault("image_std", list(_MISTRAL_COMMON_DATASET_STD)) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - - projector_stride = int(self.global_config.get("understand_projector_stride", -1)) - hidden_size = int(self.hparams_vision.get("hidden_size", self.hparams_vision.get("width", -1))) - num_layers = int(self.hparams_vision.get("num_hidden_layers", self.hparams_vision.get("layers", -1))) - assert (projector_stride, int(self.hparams_vision.get("image_size", -1)), hidden_size, num_layers) == (2, 728, 1536, 47), ( - "current Step3-VL conversion path is only validated for Step3-VL-10B" - ) - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.STEP3VL) - self.gguf_writer.add_vision_attention_layernorm_eps(float(self.hparams_vision.get("layer_norm_eps", 1e-5))) - self.gguf_writer.add_vision_projector_scale_factor(projector_stride ** 2) - # 3024 max resize comes from step3-vl-10b processing_step3.py. - self.gguf_writer.add_vision_preproc_image_size(3024) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".position_embd." in new_name: - return gguf.GGMLQuantizationType.F32 - if ("mm.0." in new_name or "mm.1." in new_name) and new_name.endswith(".weight"): - return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith(("model.", "lm_head.")): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.startswith("vision_model.vit_downsampler"): - match = re.match(r"vision_model\.vit_downsampler(\d+)\.(weight|bias)", name) - if match is None: - raise ValueError(f"Unexpected Step3-VL projector tensor {name!r}") - - proj_id = int(match.group(1)) - 1 - suffix = f".{match.group(2)}" - yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, proj_id, suffix=suffix), data_torch) - return - - if name == "vit_large_projector.weight": - yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ_FC), data_torch) - return - - if name.startswith("vision_model."): - if name == "vision_model.positional_embedding": - name += ".weight" - elif name.endswith(".gamma") and ".ls_" in name: - name = name.removesuffix(".gamma") + ".weight" - - name = name.replace("attn.in_proj_weight", "attn.in_proj.weight") - name = name.replace("attn.in_proj_bias", "attn.in_proj.bias") - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Qwen3VLForConditionalGeneration") -class Qwen3VLTextModel(Qwen3Model): - model_arch = gguf.MODEL_ARCH.QWEN3VL - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if "thinker_config" in self.hparams: - vision_config = self.hparams["thinker_config"].get("vision_config", {}) - else: - vision_config = self.hparams.get("vision_config", {}) - deepstack_layer_num = len(vision_config.get("deepstack_visual_indexes", [])) - self.gguf_writer.add_num_deepstack_layers(deepstack_layer_num) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - name = name.replace("thinker.", "") - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("StepVLForConditionalGeneration") -class Step3VLTextModel(Qwen3Model): - model_arch = gguf.MODEL_ARCH.QWEN3 - - -@ModelBase.register("Qwen3VLMoeForConditionalGeneration") -class Qwen3VLMoeTextModel(Qwen3MoeModel): - model_arch = gguf.MODEL_ARCH.QWEN3VLMOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - vision_config = self.hparams.get("vision_config", {}) - deepstack_layer_num = len(vision_config.get("deepstack_visual_indexes", [])) - self.gguf_writer.add_num_deepstack_layers(deepstack_layer_num) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - name = name.replace("thinker.", "") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Qwen3VL has transposed packed tensors, so we treat it differently from general Qwen2MoE packed tensors - if name.endswith("mlp.experts.down_proj") or name.endswith("mlp.experts.down_proj.weight"): - mapped = f"{name}.weight" if not name.endswith(".weight") else name - permuted = data_torch.permute(0, 2, 1).contiguous() - yield from ModelBase.modify_tensors(self, permuted, mapped, bid) - return - - if name.endswith("mlp.experts.gate_up_proj") or name.endswith("mlp.experts.gate_up_proj.weight"): - if data_torch.ndim < 3 or data_torch.shape[-1] % 2 != 0: - raise ValueError(f"Unexpected gate_up_proj shape for {name}: {tuple(data_torch.shape)}") - split_dim = data_torch.shape[-1] // 2 - gate = data_torch[..., :split_dim].contiguous() - up = data_torch[..., split_dim:].contiguous() - # Input gate/up: (n_expert=128, n_embd=2048, n_ff_exp=768) - # Want GGML ne: {n_embd, n_ff_exp, n_expert} = {2048, 768, 128} - # Need PyTorch: (128, 768, 2048) [reversed of GGML] - # So: permute(0, 2, 1): (128, 2048, 768) -> (128, 768, 2048) - base_name = name.removesuffix(".weight") - base = base_name.rsplit('.', 1)[0] - mapped_gate = f"{base}.gate_proj.weight" - mapped_up = f"{base}.up_proj.weight" - perm_gate = gate.permute(0, 2, 1).contiguous() - perm_up = up.permute(0, 2, 1).contiguous() - yield from ModelBase.modify_tensors(self, perm_gate, mapped_gate, bid) - yield from ModelBase.modify_tensors(self, perm_up, mapped_up, bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Qwen3OmniMoeForConditionalGeneration") -class Qwen3OmniMoeTextModel(Qwen3VLMoeTextModel): - model_arch = gguf.MODEL_ARCH.QWEN3VLMOE - - def set_vocab(self): - super().set_vocab() - # correct BOS/EOS tokens - with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: - tokenizer_config = json.load(f) - added_tokens = tokenizer_config.get("added_tokens_decoder", {}) - for token_id, data in added_tokens.items(): - if data.get("content") == "<|im_end|>": - self.gguf_writer.add_bos_token_id(int(token_id)) - self.gguf_writer.add_eos_token_id(int(token_id)) - break - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_num_deepstack_layers(0) - - -@ModelBase.register("Qwen3ASRForConditionalGeneration") -class Qwen3ASRTextModel(Qwen3VLTextModel): - model_arch = gguf.MODEL_ARCH.QWEN3VL - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_num_deepstack_layers(0) - - def set_vocab(self): - super().set_vocab() - # fix chat template, use correct chatml format - self.gguf_writer.add_chat_template("{% for message in messages %}{{'<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\\n' }}{% endif %}") - # correct BOS/EOS tokens - with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: - tokenizer_config = json.load(f) - added_tokens = tokenizer_config.get("added_tokens_decoder", {}) - for token_id, data in added_tokens.items(): - if data.get("content") == "<|im_end|>": - self.gguf_writer.add_bos_token_id(int(token_id)) - self.gguf_writer.add_eos_token_id(int(token_id)) - break - - -class _LinearAttentionVReorderBase(Qwen3NextModel): - model_arch = gguf.MODEL_ARCH.QWEN3NEXT # overridden by subclasses - """reorders V heads from grouped to tiled order for ggml broadcast - - see https://github.com/ggml-org/llama.cpp/pull/19468#discussion_r2786394306 - - Linear attention may has num_k_heads < num_v_heads. The HF weights store - V heads grouped by K head: [G0_v0..v{r-1}, G1_v0..v{r-1}, ...]. - ggml binary ops use tiled broadcast: [K0, K1, ..., K0, K1, ...]. - We reorder V heads to tiled order so ggml_repeat can replace the expensive - interleaved repeat: [G0_v0, G1_v0, ..., G0_v1, G1_v1, ...]. - """ - - @staticmethod - def _reorder_v_heads(tensor: Tensor, dim: int, num_k_heads: int, num_v_per_k: int, head_dim: int) -> Tensor: - """Reorder V heads from grouped (by K head) to tiled order along the given dimension.""" - shape = list(tensor.shape) - if dim < 0: - dim += len(shape) - new_shape = shape[:dim] + [num_k_heads, num_v_per_k, head_dim] + shape[dim + 1:] - tensor = tensor.reshape(*new_shape) - perm = list(range(len(new_shape))) - perm[dim], perm[dim + 1] = perm[dim + 1], perm[dim] - return tensor.permute(*perm).contiguous().reshape(*shape) - - def _transform_nvfp4_weight(self, name: str, weight: Tensor, scale: Tensor) -> tuple[Tensor, Tensor]: - if not name.endswith(( - ".linear_attn.in_proj_qkv.weight", - ".linear_attn.in_proj_z.weight", - ".linear_attn.in_proj_a.weight", - ".linear_attn.in_proj_b.weight", - ".linear_attn.out_proj.weight", - )): - return weight, scale - - num_k_heads = self.hparams["linear_num_key_heads"] - num_v_heads = self.hparams["linear_num_value_heads"] - head_k_dim = self.hparams["linear_key_head_dim"] - head_v_dim = self.hparams["linear_value_head_dim"] - num_v_per_k = num_v_heads // num_k_heads - - def unpack_nibbles(qs: Tensor) -> Tensor: - lo = torch.bitwise_and(qs, 0x0F) - hi = torch.bitwise_right_shift(qs, 4) - return torch.stack((lo, hi), dim=-1).reshape(*qs.shape[:-1], qs.shape[-1] * 2) - - def pack_nibbles(codes: Tensor) -> Tensor: - codes = codes.reshape(*codes.shape[:-1], codes.shape[-1] // 2, 2) - lo = torch.bitwise_and(codes[..., 0], 0x0F) - hi = torch.bitwise_left_shift(torch.bitwise_and(codes[..., 1], 0x0F), 4) - return torch.bitwise_or(lo, hi).contiguous() - - def apply_col_perm(qs: Tensor, scales: Tensor, col_perm: Tensor) -> tuple[Tensor, Tensor]: - assert qs.ndim >= 2 - assert scales.ndim >= 2 - - k = qs.shape[-1] * 2 - assert col_perm.numel() == k - assert k % 16 == 0 - - group_cols = col_perm.reshape(-1, 16) - group_starts = group_cols[:, 0] - expected = group_starts.unsqueeze(1) + torch.arange(16, dtype=col_perm.dtype) - assert torch.equal(group_cols, expected) - assert torch.all(group_starts % 16 == 0) - - group_perm = (group_starts // 16).to(dtype=torch.long) - expected_groups = torch.arange(scales.shape[-1], dtype=torch.long) - assert group_perm.numel() == scales.shape[-1] - assert torch.equal(torch.sort(group_perm).values, expected_groups) - - codes = unpack_nibbles(qs) - codes = codes.index_select(-1, col_perm.to(device=qs.device, dtype=torch.long)) - qs = pack_nibbles(codes) - scales = scales.index_select(-1, group_perm.to(device=scales.device)) - return qs, scales - - def reorder_rows(qs: Tensor, scales: Tensor, head_dim: int) -> tuple[Tensor, Tensor]: - row_perm = self._reorder_v_heads( - torch.arange(num_v_heads * head_dim, dtype=torch.long).unsqueeze(-1), - 0, num_k_heads, num_v_per_k, head_dim, - ).squeeze(-1) - return ( - qs.index_select(0, row_perm.to(device=qs.device)), - scales.index_select(0, row_perm.to(device=scales.device)), - ) - - if name.endswith(".linear_attn.in_proj_qkv.weight"): - q_dim = head_k_dim * num_k_heads - k_dim = head_k_dim * num_k_heads - q = weight[:q_dim] - k = weight[q_dim:q_dim + k_dim] - v = weight[q_dim + k_dim:] - q_scale = scale[:q_dim] - k_scale = scale[q_dim:q_dim + k_dim] - v_scale = scale[q_dim + k_dim:] - v, v_scale = reorder_rows(v, v_scale, head_v_dim) - return torch.cat([q, k, v], dim=0), torch.cat([q_scale, k_scale, v_scale], dim=0) - - if name.endswith(".linear_attn.in_proj_z.weight"): - weight, scale = reorder_rows(weight, scale, head_v_dim) - elif name.endswith((".linear_attn.in_proj_a.weight", ".linear_attn.in_proj_b.weight")): - weight, scale = reorder_rows(weight, scale, 1) - elif name.endswith(".linear_attn.out_proj.weight"): - col_perm = self._reorder_v_heads( - torch.arange(num_v_heads * head_v_dim, dtype=torch.long).unsqueeze(0), - 1, num_k_heads, num_v_per_k, head_v_dim, - ).squeeze(0) - weight, scale = apply_col_perm(weight, scale, col_perm) - - return weight, scale - - def _repack_nvfp4(self, name: str, weight: Tensor, scale: Tensor, scale2: Tensor, input_scale: Tensor): - weight, scale = self._transform_nvfp4_weight(name, weight, scale) - super()._repack_nvfp4(name, weight, scale, scale2, input_scale) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - num_k_heads = self.hparams.get("linear_num_key_heads", 0) - num_v_heads = self.hparams.get("linear_num_value_heads", 0) - - if num_k_heads > 0 and num_v_heads > 0 and num_k_heads != num_v_heads and "linear_attn." in name: - head_k_dim = self.hparams["linear_key_head_dim"] - head_v_dim = self.hparams["linear_value_head_dim"] - num_v_per_k = num_v_heads // num_k_heads - - if ".in_proj_qkv." in name: - # QKV weight: reorder only the V rows - q_dim = head_k_dim * num_k_heads - k_dim = head_k_dim * num_k_heads - q = data_torch[:q_dim] - k = data_torch[q_dim:q_dim + k_dim] - v = data_torch[q_dim + k_dim:] - v = self._reorder_v_heads(v, 0, num_k_heads, num_v_per_k, head_v_dim) - data_torch = torch.cat([q, k, v], dim=0) - - elif ".in_proj_z." in name: - # Z gate weight: reorder rows (num_v_heads * head_v_dim) - data_torch = self._reorder_v_heads(data_torch, 0, num_k_heads, num_v_per_k, head_v_dim) - - elif ".in_proj_b." in name or ".in_proj_a." in name: - # Beta/Alpha weight: reorder rows (num_v_heads, head_dim=1) - data_torch = self._reorder_v_heads(data_torch, 0, num_k_heads, num_v_per_k, 1) - - elif ".A_log" in name or ".dt_bias" in name or ".dt_proj" in name: - # A_log / dt_bias: 1D parameters with num_v_heads elements - if data_torch.ndim == 1: - data_torch = self._reorder_v_heads( - data_torch.unsqueeze(-1), 0, num_k_heads, num_v_per_k, 1 - ).squeeze(-1) - else: - data_torch = self._reorder_v_heads(data_torch, -1, num_k_heads, num_v_per_k, 1) - - elif ".conv1d" in name: - # Conv1d kernel: reorder only the V channel portion - data = data_torch.squeeze() - qk_channels = head_k_dim * num_k_heads * 2 - qk_part = data[:qk_channels] - v_part = data[qk_channels:] - v_part = self._reorder_v_heads(v_part, 0, num_k_heads, num_v_per_k, head_v_dim) - data_torch = torch.cat([qk_part, v_part], dim=0) - - elif ".out_proj." in name: - # Out projection weight: reorder columns (input dimension) - data_torch = self._reorder_v_heads(data_torch, 1, num_k_heads, num_v_per_k, head_v_dim) - - yield from super().modify_tensors(data_torch, name, bid) - - -class _Qwen35MRopeMixin: - # Qwen3.5 always applies interleaved MRoPE (see Qwen3_5RotaryEmbedding in transformers); - # the upstream default mrope_section is [11, 11, 10] and llama.cpp's QWEN35 / QWEN35MOE - # loaders treat qwen35.rope.dimension_sections as required, so make sure it is always - # written even when a particular checkpoint omits the field in `rope_parameters`. - _QWEN35_DEFAULT_MROPE_SECTION = [11, 11, 10, 0] - - gguf_writer: gguf.GGUFWriter - rope_parameters: dict - - def set_gguf_parameters(self): - super().set_gguf_parameters() # ty: ignore[unresolved-attribute] - if "mrope_section" not in self.rope_parameters: - self.gguf_writer.add_rope_dimension_sections(self._QWEN35_DEFAULT_MROPE_SECTION) - - -@ModelBase.register("Qwen3_5ForConditionalGeneration", "Qwen3_5ForCausalLM") -class Qwen3_5TextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): - model_arch = gguf.MODEL_ARCH.QWEN35 - - -@ModelBase.register("Qwen3_5MoeForConditionalGeneration", "Qwen3_5MoeForCausalLM") -class Qwen3_5MoeTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase): - model_arch = gguf.MODEL_ARCH.QWEN35MOE - - -# MiniCPM-V 4.6: text tower is Qwen3.5 (linear+full hybrid attention) wrapped under -# `model.language_model.*`; vision tower is SigLIP + a window-attention ViT merger -# + a final DownsampleMLP merger. The same HF arch is registered twice below: once as -# the LM (text mode) and once as the mmproj (vision mode), mirroring the Qwen3-VL setup. - -@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") -class MiniCPMV4_6TextModel(Qwen3_5TextModel): - model_arch = gguf.MODEL_ARCH.QWEN35 - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("model.merger."): - return None - # MTP tensors are not used at inference yet; align with Qwen3Next behaviour - if name.startswith("mtp"): - return None - - return super().filter_tensors(item) - - -@ModelBase.register("MiniCPMV4_6ForConditionalGeneration") -class MiniCPMV4_6VisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.hparams_vision is not None: - # In MiniCPM-V 4.6 `vision_config.image_size` (980) describes the SigLIP - # positional embedding bucket grid (70 x 70), while the per-slice processing - # resolution is the preprocessor's `scale_resolution` (typically 448). - # The CLIP loader in tools/mtmd/clip.cpp consumes `clip.vision.image_size` - # as the slice size and warmup resolution, so report `scale_resolution` there - # to match the upstream MiniCPMV4_6ImageProcessorPil slicing rules. - scale_resolution = self.preprocessor_config.get("scale_resolution") - if scale_resolution is not None: - self.hparams_vision["image_size"] = int(scale_resolution) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - - # projector type string is consumed by clip_projector_type_from_string() in clip.cpp - # (mapped to PROJECTOR_TYPE_MINICPMV4_6). - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MINICPMV4_6) - - # ViT merger 2x2 + final merger 2x2 = 4x spatial merge per dimension; used for slice alignment - self.gguf_writer.add_vision_projector_scale_factor(4) - - # borrow wa_layer_indexes for vit_merger insertion point - insert_layer_id = int(self.global_config.get( - "insert_layer_id", self.hparams_vision.get("insert_layer_id", 6))) - self.gguf_writer.add_vision_wa_layer_indexes([insert_layer_id]) - - # SigLIP vision body uses gelu_pytorch_tanh, which matches ggml_gelu (tanh approx). - self.gguf_writer.add_vision_use_gelu(True) - self.gguf_writer.add_vision_attention_layernorm_eps( - self.hparams_vision.get("layer_norm_eps", 1e-6)) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # lm_head / MTP -> belong to the LM file - if name.startswith(("lm_head.", "mtp")): - return None - - return super().filter_tensors(item) - - -@ModelBase.register("GPT2LMHeadModel") -class GPT2Model(TextModel): - model_arch = gguf.MODEL_ARCH.GPT2 - - def set_gguf_parameters(self): - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.hparams["n_ctx"]) - self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) - self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) - self.gguf_writer.add_head_count(self.hparams["n_head"]) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # we don't need these - if name.endswith((".attn.bias", ".attn.masked_bias")): - yield from super().modify_tensors(data_torch, name, bid) - return - - if name.endswith((".c_attn.weight", ".c_proj.weight", ".c_fc.weight", ".c_proj.weight")): - data_torch = data_torch.transpose(1, 0) - - new_name = self.map_tensor_name(name) - - yield from super().modify_tensors(data_torch, new_name, bid) - - -@ModelBase.register("RuGPT3XLForCausalLM") -class RuGPT3XLModel(TextModel): - model_arch = gguf.MODEL_ARCH.GPT2 - - _qkv_parts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Fuse separate Q, K, V projections into a single QKV tensor - if ".self_attn.q_proj." in name or ".self_attn.k_proj." in name or ".self_attn.v_proj." in name: - suffix = "weight" if name.endswith(".weight") else "bias" - part = "q" if ".q_proj." in name else ("k" if ".k_proj." in name else "v") - key = f"{part}.{suffix}" - - assert bid is not None - if self._qkv_parts is None: - self._qkv_parts = [{} for _ in range(self.block_count)] - self._qkv_parts[bid][key] = data_torch - - q_key, k_key, v_key = f"q.{suffix}", f"k.{suffix}", f"v.{suffix}" - if all(k in self._qkv_parts[bid] for k in [q_key, k_key, v_key]): - q = self._qkv_parts[bid].pop(q_key) - k = self._qkv_parts[bid].pop(k_key) - v = self._qkv_parts[bid].pop(v_key) - data_torch = torch.cat([q, k, v], dim=0) - name = self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_QKV, bid, f".{suffix}") - logger.debug(f"Fused Q/K/V {suffix} for layer {bid} -> {name}") - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._qkv_parts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - parts = [f"({i}){k}" for i, d in enumerate(self._qkv_parts) for k in d.keys()] - if len(parts) > 0: - raise ValueError(f"Unprocessed Q/K/V parts: {parts}") - - -@ModelBase.register("PhiForCausalLM") -class Phi2Model(TextModel): - model_arch = gguf.MODEL_ARCH.PHI2 - - def set_gguf_parameters(self): - rot_pct = self.find_hparam(["partial_rotary_factor"]) - n_embd = self.find_hparam(["hidden_size", "n_embd"]) - n_head = self.find_hparam(["num_attention_heads", "n_head"]) - - self.gguf_writer.add_context_length(self.find_hparam(["n_positions", "max_position_embeddings"])) - - self.gguf_writer.add_embedding_length(n_embd) - self.gguf_writer.add_feed_forward_length(4 * n_embd) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(n_head) - self.gguf_writer.add_head_count_kv(n_head) - self.gguf_writer.add_layer_norm_eps(self.find_hparam(["layer_norm_epsilon", "layer_norm_eps"])) - self.gguf_writer.add_rope_dimension_count(int(rot_pct * n_embd) // n_head) - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_add_bos_token(False) - - -@ModelBase.register("Phi3ForCausalLM", "Phi4ForCausalLMV") -class Phi3MiniModel(TextModel): - model_arch = gguf.MODEL_ARCH.PHI3 - - def set_vocab(self): - # Phi-4 model uses GPT2Tokenizer - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - tokenizer_class = tokenizer_config_json['tokenizer_class'] - if tokenizer_class == 'GPT2Tokenizer': - return self._set_vocab_gpt2() - - from sentencepiece import SentencePieceProcessor - - tokenizer_path = self.dir_model / 'tokenizer.model' - - if not tokenizer_path.is_file(): - raise ValueError(f'Error: Missing {tokenizer_path}') - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - for token_id in range(tokenizer.vocab_size()): - - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - added_tokens_file = self.dir_model / 'added_tokens.json' - if added_tokens_file.is_file(): - with open(added_tokens_file, "r", encoding="utf-8") as f: - added_tokens_json = json.load(f) - - for key in added_tokens_json: - token_id = added_tokens_json[key] - if token_id >= vocab_size: - logger.debug(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - - tokens[token_id] = key.encode("utf-8") - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) - for token_id, foken_data in added_tokens_decoder.items(): - token_id = int(token_id) - token = foken_data["content"].encode("utf-8") - if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: - if tokens[token_id] != token: - logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') - tokens[token_id] = token - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - if foken_data.get("special"): - toktypes[token_id] = SentencePieceTokenTypes.CONTROL - - tokenizer_file = self.dir_model / 'tokenizer.json' - if tokenizer_file.is_file(): - with open(tokenizer_file, "r", encoding="utf-8") as f: - tokenizer_json = json.load(f) - added_tokens = tokenizer_json.get("added_tokens", []) - for foken_data in added_tokens: - token_id = int(foken_data["id"]) - token = foken_data["content"].encode("utf-8") - if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: - if tokens[token_id] != token: - logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') - tokens[token_id] = token - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - if foken_data.get("special"): - toktypes[token_id] = SentencePieceTokenTypes.CONTROL - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - n_embd = self.find_hparam(["hidden_size", "n_embd"]) - n_head = self.find_hparam(["num_attention_heads", "n_head"]) - n_head_kv = self.find_hparam(["num_key_value_heads", "n_head_kv"]) - rms_eps = self.find_hparam(["rms_norm_eps"]) - max_pos_embds = self.find_hparam(["n_positions", "max_position_embeddings"]) - orig_max_pos_embds = self.find_hparam(["original_max_position_embeddings"]) - rot_pct = self.hparams.get("partial_rotary_factor", 1.0) - rope_dims = int(rot_pct * n_embd) // n_head - - self.gguf_writer.add_context_length(max_pos_embds) - self.gguf_writer.add_rope_scaling_orig_ctx_len(orig_max_pos_embds) - self.gguf_writer.add_embedding_length(n_embd) - self.gguf_writer.add_feed_forward_length(self.find_hparam(["intermediate_size"])) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(n_head) - self.gguf_writer.add_head_count_kv(n_head_kv) - self.gguf_writer.add_layer_norm_rms_eps(rms_eps) - self.gguf_writer.add_rope_dimension_count(rope_dims) - self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("full_attention", self.rope_parameters)["rope_theta"]) - self.gguf_writer.add_file_type(self.ftype) - sliding_window = self.hparams.get("sliding_window") - # use zero value of sliding_window to distinguish Phi-4 from other PHI3 models - if sliding_window is None: - sliding_window = 0 - self.gguf_writer.add_sliding_window(sliding_window) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - n_embd = self.find_hparam(["hidden_size", "n_embd"]) - n_head = self.find_hparam(["num_attention_heads", "n_head"]) - max_pos_embds = self.find_hparam(["n_positions", "max_position_embeddings"]) - orig_max_pos_embds = self.find_hparam(["original_max_position_embeddings"]) - rot_pct = self.hparams.get("partial_rotary_factor", 1.0) - rope_dims = int(rot_pct * n_embd) // n_head - - # write rope scaling for long context (128k) model - rope_scaling = self.find_hparam(['rope_scaling'], True) - if rope_scaling is None: - return - - scale = max_pos_embds / orig_max_pos_embds - - rope_scaling_type = rope_scaling.get('rope_type', rope_scaling.get('type', '')).lower() - if len(rope_scaling_type) == 0: - raise KeyError('Missing the required key rope_scaling.type') - - if rope_scaling_type == 'su' or rope_scaling_type == 'longrope': - attn_factor = math.sqrt(1 + math.log(scale) / math.log(orig_max_pos_embds)) if scale > 1.0 else 1.0 - elif rope_scaling_type == 'yarn': - attn_factor = 0.1 * math.log(scale) + 1.0 if scale > 1.0 else 1.0 - else: - raise NotImplementedError(f'The rope scaling type {rope_scaling_type} is not supported yet') - - self.gguf_writer.add_rope_scaling_attn_factors(attn_factor) - - long_factors = rope_scaling.get('long_factor', None) - short_factors = rope_scaling.get('short_factor', None) - - if long_factors is None or short_factors is None: - raise KeyError('Missing the required key rope_scaling.long_factor or rope_scaling_short_factor') - - if len(long_factors) != len(short_factors) or len(long_factors) != rope_dims / 2: - raise ValueError(f'The length of rope long and short factors must be {rope_dims / 2}. long_factors = {len(long_factors)}, short_factors = {len(short_factors)}.') - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_LONG), torch.tensor(long_factors, dtype=torch.float32)) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FACTORS_SHORT), torch.tensor(short_factors, dtype=torch.float32)) - - -@ModelBase.register("Phi4ForCausalLMV") -class Phi4VisionMmprojModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - - self.vision_total_layers = int(self.find_vparam(self.n_block_keys)) - if self.vision_total_layers < 2: - raise ValueError( - f"Phi-4 vision mmproj conversion requires at least 2 vision layers, got {self.vision_total_layers}" - ) - - # Phi-4 uses SigLIP2 hidden_states[-2], so export one fewer encoder block and - # drop post-layernorm/head weights. This makes the GGUF runtime output match - # the feature map consumed by the patched siglip.cpp Phi-4 projector path. - self.vision_export_layers = self.vision_total_layers - 1 - self.vision_last_layer_idx = self.vision_total_layers - 1 - - for key in self.n_block_keys: - if key in self.hparams_vision: - self.hparams_vision[key] = self.vision_export_layers - break - - self.block_count = self.vision_export_layers - self.tensor_map = gguf.get_tensor_name_map(gguf.MODEL_ARCH.MMPROJ, self.block_count) - - patch_size = self.preprocessor_config.get("patch_size") - if patch_size is None: - raise KeyError("Phi-4 vision mmproj conversion requires patch_size in preprocessor_config.json") - - self.hparams_vision["patch_size"] = patch_size - - pos_emb_name = next( - ( - name for name in self.model_tensors - if name.endswith("vision_model.embeddings.position_embedding.weight") - ), - None, - ) - if pos_emb_name is None: - raise KeyError("Phi-4 vision mmproj conversion could not find position_embedding.weight") - - pos_emb_shape = self.model_tensors[pos_emb_name]().shape - base_grid_tokens = int(pos_emb_shape[0]) - grid_side = math.isqrt(base_grid_tokens) - if grid_side * grid_side != base_grid_tokens: - raise ValueError(f"Unexpected Phi-4 position embedding shape: {tuple(pos_emb_shape)}") - - self.hparams_vision["image_size"] = grid_side * patch_size - - min_num_patches = self.preprocessor_config.get("min_num_patches", self.global_config.get("min_num_patches")) - max_num_patches = self.preprocessor_config.get("max_num_patches", self.global_config.get("max_num_patches")) - if min_num_patches is None or max_num_patches is None: - raise KeyError("Phi-4 vision mmproj conversion requires min_num_patches and max_num_patches") - - self.min_pixels = int(min_num_patches) * patch_size * patch_size - self.max_pixels = int(max_num_patches) * patch_size * patch_size - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PHI4) - self.gguf_writer.add_vision_min_pixels(self.min_pixels) - self.gguf_writer.add_vision_max_pixels(self.max_pixels) - self.gguf_writer.add_vision_use_gelu(True) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6)) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - name = name.replace("model.vision_tower.vision_tower.", "vision_tower.") - - if not name.startswith(("vision_tower.", "model.mm_projector.", "mm_projector.")): - return None - - if ".vision_model.head." in name: - return None - - if ".vision_model.post_layernorm." in name: - return None - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.startswith("vision_tower."): - if bid is not None and bid == self.vision_last_layer_idx: - return - - if name.endswith("vision_model.embeddings.patch_embedding.weight"): - assert self.hparams_vision is not None - if data_torch.ndim != 2: - raise ValueError(f"Unexpected Phi-4 patch embedding shape: {tuple(data_torch.shape)}") - - patch_area = self.hparams_vision["patch_size"] ** 2 - in_features = data_torch.shape[1] - if in_features % patch_area != 0: - raise ValueError( - f"Phi-4 patch embedding input dim {in_features} is not divisible by patch area {patch_area}" - ) - - num_channels = in_features // patch_area - patch_size = self.hparams_vision["patch_size"] - data_torch = data_torch.view(data_torch.shape[0], patch_size, patch_size, num_channels) - data_torch = data_torch.permute(0, 3, 1, 2) - - yield from super().modify_tensors(data_torch, name, bid) - return - - if name.startswith(("model.mm_projector.", "mm_projector.")): - local_name = name - local_name = local_name.replace("model.mm_projector.", "") - local_name = local_name.replace("mm_projector.", "") - - if not (local_name.startswith("0.") or local_name.startswith("2.")): - return - - suffix = ".bias" if local_name.endswith(".bias") else ".weight" - mm_idx = int(local_name.split(".", maxsplit=1)[0]) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, mm_idx, suffix=suffix), data_torch) - return - - return - - -@ModelBase.register("PhiMoEForCausalLM") -class PhiMoeModel(Phi3MiniModel): - model_arch = gguf.MODEL_ARCH.PHIMOE - - _experts: list[dict[str, Tensor]] | None = None - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_expert_used_count(self.find_hparam(["num_experts_per_tok", "num_experts_per_token"])) - self.gguf_writer.add_expert_count(self.find_hparam(["num_local_experts", "num_experts"])) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # process the experts separately - if name.find("block_sparse_moe.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["w1", "w2", "w3"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("PlamoForCausalLM") -class PlamoModel(TextModel): - model_arch = gguf.MODEL_ARCH.PLAMO - - def set_vocab(self): - self._set_vocab_sentencepiece() - - def set_gguf_parameters(self): - hparams = self.hparams - - self.gguf_writer.add_context_length(4096) # not in config.json - self.gguf_writer.add_embedding_length(hparams["hidden_size"]) - self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(5) # hparams["num_key_value_heads"]) is wrong - self.gguf_writer.add_layer_norm_rms_eps(hparams["rms_norm_eps"]) - self.gguf_writer.add_file_type(self.ftype) - - def shuffle_attn_q_weight(self, data_torch): - assert data_torch.size() == (5120, 5120) - data_torch = data_torch.reshape(8, 5, 128, 5120) - data_torch = torch.permute(data_torch, (1, 0, 2, 3)) - data_torch = torch.reshape(data_torch, (5120, 5120)) - return data_torch - - def shuffle_attn_output_weight(self, data_torch): - assert data_torch.size() == (5120, 5120) - data_torch = data_torch.reshape(5120, 8, 5, 128) - data_torch = torch.permute(data_torch, (0, 2, 1, 3)) - data_torch = torch.reshape(data_torch, (5120, 5120)) - return data_torch - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - new_name = self.map_tensor_name(name) - - # shuffle for broadcasting of gqa in ggml_mul_mat - if new_name.endswith("attn_q.weight"): - data_torch = self.shuffle_attn_q_weight(data_torch) - elif new_name.endswith("attn_output.weight"): - data_torch = self.shuffle_attn_output_weight(data_torch) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Plamo2ForCausalLM", "PLaMo2ForCausalLM") -class Plamo2Model(TextModel): - model_arch = gguf.MODEL_ARCH.PLAMO2 - - def set_vocab(self): - self._set_vocab_plamo() - - def set_gguf_parameters(self): - hparams = self.hparams - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - - # Which layers are Mamba layers - # PLaMo 2 uses mamba_step to indicate the pattern (e.g., 2 means every other layer) - # This logic matches modeling_plamo.py's is_mamba function - mamba_step = hparams.get("mamba_step", 2) - mamba_enabled = hparams.get("mamba_enabled", True) - num_key_value_heads = [] - num_attention_heads = [] - - if mamba_enabled: - for i in range(self.block_count): - if self.block_count <= (mamba_step // 2): - # use attention in last layer - is_mamba = (i != self.block_count - 1) - else: - is_mamba = (i % mamba_step) != (mamba_step // 2) - if is_mamba: - num_key_value_heads.append(0) - num_attention_heads.append(0) - else: - num_key_value_heads.append(hparams.get("num_key_value_heads", 4)) - num_attention_heads.append(hparams.get("num_attention_heads", 32)) - - if num_key_value_heads and num_attention_heads: - self.gguf_writer.add_head_count_kv(num_key_value_heads) - self.gguf_writer.add_head_count(num_attention_heads) - - self.gguf_writer.add_context_length(hparams.get("max_position_embeddings", 2048)) - self.gguf_writer.add_embedding_length(hparams.get("hidden_size", 4096)) - self.gguf_writer.add_key_length(hparams.get("hidden_size_per_head", 128)) - self.gguf_writer.add_value_length(hparams.get("hidden_size_per_head", 128)) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_layer_norm_rms_eps(hparams.get("rms_norm_eps", 1e-06)) - self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("rope_theta", 10000)) - - # Mamba parameters - self.gguf_writer.add_ssm_state_size(hparams.get("mamba_d_state", 64)) - self.gguf_writer.add_ssm_conv_kernel(hparams.get("mamba_d_conv", 4)) - self.gguf_writer.add_ssm_time_step_rank(hparams.get("mamba_num_heads", 64)) - intermediate_size = hparams.get("mamba_num_heads", 64) * hparams.get("hidden_size_per_head", 128) - self.gguf_writer.add_ssm_inner_size(intermediate_size) - self.gguf_writer.add_ssm_group_count(0) - - # MLP feed forward parameters (for attention layers) - self.gguf_writer.add_feed_forward_length(hparams.get("intermediate_size", 13312)) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.endswith(".A_log"): - data_torch = -torch.exp(data_torch) - elif name.endswith(".dt_bias"): - name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" - elif name.endswith(".dt_norm_weight"): - name = name.rpartition(".dt_norm_weight")[0] + ".dt_norm.weight" - elif name.endswith(".B_norm_weight"): - name = name.rpartition(".B_norm_weight")[0] + ".B_norm.weight" - elif name.endswith(".C_norm_weight"): - name = name.rpartition(".C_norm_weight")[0] + ".C_norm.weight" - elif name.endswith(".k_weight"): - name = name.rpartition(".k_weight")[0] + ".k.weight" - elif name.endswith(".q_weight"): - name = name.rpartition(".q_weight")[0] + ".q.weight" - elif name.endswith(".conv1d.weight"): - data_torch = torch.squeeze(data_torch) # remove (, 1, ) - assert data_torch.ndim == 2 - elif name.endswith(".pre_mixer_norm.weight"): - data_torch += 1.0 - elif name.endswith(".post_mixer_norm.weight"): - data_torch += 1.0 / 5 - elif name.endswith(".pre_mlp_norm.weight"): - data_torch += 1.0 - elif name.endswith(".post_mlp_norm.weight"): - data_torch += 1.0 / (5**1.5) - elif name.endswith(".norm.weight"): - data_torch += 1.0 - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Plamo3ForCausalLM", "PLaMo3ForCausalLM") -class Plamo3Model(TextModel): - model_arch = gguf.MODEL_ARCH.PLAMO3 - - def set_vocab(self): - self._set_vocab_plamo() - - tokenizer_config_path = self.dir_model / "tokenizer_config.json" - tokenizer_config = {} - - if tokenizer_config_path.is_file(): - with open(tokenizer_config_path, encoding="utf-8") as f: - tokenizer_config = json.load(f) - - chat_template = tokenizer_config.get("chat_template") - chat_template_jinja = self.dir_model / "chat_template.jinja" - - if chat_template_jinja.is_file(): - with open(chat_template_jinja, encoding="utf-8") as f: - chat_template = f.read() - - if chat_template: - self.gguf_writer.add_chat_template(chat_template) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - if (sliding_window := self.find_hparam(["window_size", "sliding_window"], optional=True)) is not None: - self.gguf_writer.add_sliding_window(sliding_window) - self.gguf_writer.add_sliding_window_pattern(self.hparams["sliding_window_pattern"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - - if name.endswith(".pre_mixer_norm.weight"): - data_torch = data_torch + 1.0 - elif name.endswith(".post_mixer_norm.weight"): - data_torch = data_torch + 1.0 / 5 - elif name.endswith(".pre_mlp_norm.weight"): - data_torch = data_torch + 1.0 - elif name.endswith(".post_mlp_norm.weight"): - data_torch = data_torch + 1.0 / (5**1.5) - elif name.endswith((".mixer.q_norm.weight", ".mixer.k_norm.weight")): - data_torch = data_torch + 1.0 - elif name.endswith(".norm.weight"): - data_torch = data_torch + 1.0 - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("CodeShellForCausalLM") -class CodeShellModel(TextModel): - model_arch = gguf.MODEL_ARCH.CODESHELL - - def set_gguf_parameters(self): - self.gguf_writer.add_context_length(self.hparams["n_positions"]) - self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) - self.gguf_writer.add_feed_forward_length(4 * self.hparams["n_embd"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(self.hparams["n_head"]) - self.gguf_writer.add_head_count_kv(self.hparams["num_query_groups"]) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_rope_freq_base(10000.0) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) - self.gguf_writer.add_rope_scaling_factor(1.0) - - -@ModelBase.register("KimiLinearModel", "KimiLinearForCausalLM") -class KimiLinearModel(TextModel): - """Kimi-Linear model with hybrid MLA+KDA architecture""" - model_arch = gguf.MODEL_ARCH.KIMI_LINEAR - - _experts: list[dict[str, Tensor]] | None = None - - def set_vocab(self): - try: - self._set_vocab_gpt2() - return - except Exception: - pass - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - tokpre = self.get_vocab_base_pre(tokenizer) - - if tokpre == "kimi-k2": - # Build merges list using the approach similar to HunYuanMoE - merges = [] - vocab = {} - mergeable_ranks = tokenizer.model._mergeable_ranks # ty: ignore[unresolved-attribute] - for token, rank in mergeable_ranks.items(): - vocab[QwenModel.token_bytes_to_string(token)] = rank - if len(token) == 1: - continue - merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) - if len(merged) == 2: - merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) - # Build token list - vocab_size = self.hparams["vocab_size"] - special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] - reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} - tokens: list[str] = [] - toktypes: list[int] = [] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token = reverse_vocab[i] - tokens.append(token) - if i in special_tokens.values(): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.NORMAL) - - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_token_merges(merges) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) - special_vocab.add_to_gguf(self.gguf_writer) - # override eos id in config.json with tiktoken eos id - self.gguf_writer.add_eos_token_id(tokenizer.eos_id) # ty: ignore[unresolved-attribute] - else: - raise NotImplementedError(f"Deepseek pre-tokenizer {tokpre!r} is not supported yet!") - - def set_gguf_parameters(self): - # note: To enable MLA KV cache, attention needs to be converted into MQA (ie: GQA with 1 group) - self.hparams["num_key_value_heads"] = 1 - - super().set_gguf_parameters() - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - - # KDA & MLA params - # Get ssm_d_conv from linear_attn_config.short_conv_kernel_size or ssm_d_conv - linear_attn_config = self.hparams["linear_attn_config"] - # n_head == 0 for KDA layers, n_head > 0 for MLA layers - # full_attention_layers list will be used to distinguish layer type - _num_kv_heads = list() - _full_attn_layers = linear_attn_config["full_attn_layers"] - for il in range(self.hparams["num_hidden_layers"]): - if il + 1 in _full_attn_layers: - _num_kv_heads.append(self.hparams["num_key_value_heads"]) - else: - _num_kv_heads.append(0) - assert len(_num_kv_heads) == self.hparams["num_hidden_layers"] - self.gguf_writer.add_head_count_kv(_num_kv_heads) - - if (ssm_d_conv := linear_attn_config.get("short_conv_kernel_size")) is not None: - self.gguf_writer.add_ssm_conv_kernel(ssm_d_conv) - if (kda_head_dim := linear_attn_config.get("head_dim")) is not None: - self.gguf_writer.add_kda_head_dim(kda_head_dim) - - # MLA params - use add_* methods that handle arch substitution - # Support both HuggingFace naming (q_lora_rank, kv_lora_rank) and internal naming (n_lora_q, n_lora_kv) - if (q_lora_rank := self.find_hparam(["q_lora_rank", "n_lora_q"], optional=True)) is not None: - self.gguf_writer.add_q_lora_rank(q_lora_rank) - # To enable MLA KV cache, MLA needs to be converted into MQA with larger heads, then decompresses to MHA - kv_lora_rank = self.find_hparam(["kv_lora_rank", "n_lora_kv"], optional=False) - self.gguf_writer.add_kv_lora_rank(kv_lora_rank) - - # MLA head dimensions - # Support HuggingFace naming: qk_nope_head_dim, qk_rope_head_dim, v_head_dim - qk_nope_head_dim = self.hparams.get("qk_nope_head_dim") - # Rotation - use qk_rope_head_dim for Kimi - qk_rope_head_dim = self.find_hparam(["qk_rope_head_dim", "n_rot"], optional=False) - self.gguf_writer.add_rope_dimension_count(qk_rope_head_dim) - self.gguf_writer.add_key_length(kv_lora_rank + qk_rope_head_dim) - v_head_dim = self.hparams.get("v_head_dim") - - # Calculate n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim - if (n_embd_head_k_mla := self.find_hparam(["n_embd_head_k_mla"], optional=True)) is not None: - self.gguf_writer.add_key_length_mla(n_embd_head_k_mla) - elif qk_nope_head_dim is not None: - n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim - self.gguf_writer.add_key_length_mla(n_embd_head_k_mla) - - # n_embd_head_v_mla = v_head_dim - if (n_embd_head_v_mla := self.hparams.get("n_embd_head_v_mla")) is not None: - self.gguf_writer.add_value_length_mla(n_embd_head_v_mla) - elif v_head_dim is not None: - self.gguf_writer.add_value_length_mla(v_head_dim) - - # moe_intermediate_size (1024 for Kimi) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) - # num_shared_experts (1 for Kimi) - self.gguf_writer.add_expert_shared_count(self.hparams["num_shared_experts"]) - # first_k_dense_replace (1 for Kimi - first layer uses dense MLP) - self.gguf_writer.add_leading_dense_block_count(self.hparams["first_k_dense_replace"]) - # Routed scaling factor (expert_weights_scale = 2.446 for Kimi) - self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) - - def prepare_tensors(self): - super().prepare_tensors() - if self._experts is not None: - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - logger.info(f"Processing {name}: shape before = {tuple(data_torch.shape)}") - - # Handle KDA conv1d weights - # HuggingFace/vLLM stores as [d_inner, d_conv] (2D), memory layout: conv_step changes fastest - # llama.cpp expects ggml ne = [d_conv, 1, d_inner, 1], memory layout: ne[0]=d_conv changes fastest - # GGUF reverses numpy shape when writing, so numpy (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1] - # Memory layouts match: both have conv_step (d_conv) changing fastest - if name.endswith((".q_conv1d.weight", ".k_conv1d.weight", ".v_conv1d.weight")): - # HF shape: [d_inner, d_conv] e.g. [4096, 4] - # Target numpy shape: (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1] - if data_torch.ndim == 2: - d_inner, d_conv = data_torch.shape - # Reshape to (1, d_inner, 1, d_conv) - memory layout preserved (d_conv fastest) - data_torch = data_torch.reshape(1, d_inner, 1, d_conv) - logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]") - elif data_torch.ndim == 3: - # Already 3D [d_inner, 1, d_conv] from unsqueeze - d_inner, _, d_conv = data_torch.shape - data_torch = data_torch.reshape(1, d_inner, 1, d_conv) - logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, 1, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]") - - # Handle A_log: iHF stores as [1, 1, num_heads, 1] - # llama.cpp expects ggml ne = [1, num_heads, 1, 1] - # GGUF reverses numpy shape: numpy (1, 1, num_heads, 1) -> ggml ne = [1, num_heads, 1, 1] - if name.endswith(".A_log"): - data_torch = -torch.exp(data_torch) - if name.endswith(".dt_bias"): - name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" - logger.info("Changed dt_bias to dt_proj.bias") - - # process the experts separately - if name.find("block_sparse_moe.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - # w1: gate, w2: down, w3: up - for wid, tname in [("w1", gguf.MODEL_TENSOR.FFN_GATE_EXP), - ("w2", gguf.MODEL_TENSOR.FFN_DOWN_EXP), - ("w3", gguf.MODEL_TENSOR.FFN_UP_EXP)]: - datas: list[Tensor] = [] - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - data_torch = torch.stack(datas, dim=0) - new_name = self.format_tensor_name(tname, bid) - yield from super().modify_tensors(data_torch, new_name, bid) - return - - # note: MLA with the absorption optimization, needs these two split and k_b_proj transposed - if name.endswith("kv_b_proj.weight"): - name_kb = name.replace("kv_b_proj", "k_b_proj") - name_vb = name.replace("kv_b_proj", "v_b_proj") - n_head_kv = self.hparams["num_key_value_heads"] - v_head_dim = self.find_hparam(["n_embd_head_v_mla", "v_head_dim"], optional=False) - qk_nope_head_dim = self.hparams["qk_nope_head_dim"] - logger.info("Split kv_b n_head_kv %d\n" % n_head_kv) - assert data_torch.shape[0] == n_head_kv * (v_head_dim + qk_nope_head_dim) - kv_b = data_torch.view(n_head_kv, v_head_dim + qk_nope_head_dim, data_torch.shape[-1]) - k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1) - k_b = k_b.transpose(1, 2) - yield from super().modify_tensors(k_b, name_kb, bid) - yield from super().modify_tensors(v_b, name_vb, bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("InternLM2ForCausalLM") -class InternLM2Model(TextModel): - model_arch = gguf.MODEL_ARCH.INTERNLM2 - - def set_vocab(self): - # (TODO): Is there a better way? - # Copy from _set_vocab_sentencepiece, The only difference is that we will treat the character - # \x00 specially and convert it into an emoji character to prevent it from being mistakenly - # recognized as an empty string in C++. - from sentencepiece import SentencePieceProcessor - from sentencepiece import sentencepiece_model_pb2 as model - - tokenizer_path = self.dir_model / 'tokenizer.model' - - tokens: list[bytes] = [] - scores: list[float] = [] - toktypes: list[int] = [] - - if not tokenizer_path.is_file(): - logger.error(f'Error: Missing {tokenizer_path}') - sys.exit(1) - - sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] - sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) - add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) - - for token_id in range(vocab_size): - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - if text == b"\x00": - # (TODO): fixme - # Hack here and replace the \x00 characters. - logger.warning(f"InternLM2 convert token '{text}' to '🐉'!") - text = "🐉".encode("utf-8") - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - # take care of ununsed raw token - if piece.startswith('[UNUSED'): - toktype = SentencePieceTokenTypes.UNUSED - - tokens.append(text) - scores.append(score) - toktypes.append(toktype) - - added_tokens_file = self.dir_model / 'added_tokens.json' - if added_tokens_file.is_file(): - with open(added_tokens_file, "r", encoding="utf-8") as f: - added_tokens_json = json.load(f) - - for key in added_tokens_json: - tokens.append(key.encode("utf-8")) - scores.append(-1000.0) - toktypes.append(SentencePieceTokenTypes.USER_DEFINED) - - chat_eos_token = '<|im_end|>' - chat_eos_token_id = None - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - added_tokens_decoder = tokenizer_config_json.get("added_tokens_decoder", {}) - for token_id, foken_data in added_tokens_decoder.items(): - token_id = int(token_id) - token = foken_data["content"] - if token == chat_eos_token: - chat_eos_token_id = token_id - token = token.encode("utf-8") - if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: - if tokens[token_id] != token: - logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') - tokens[token_id] = token - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - if foken_data.get("special"): - toktypes[token_id] = SentencePieceTokenTypes.CONTROL - - tokenizer_file = self.dir_model / 'tokenizer.json' - if tokenizer_file.is_file(): - with open(tokenizer_file, "r", encoding="utf-8") as f: - tokenizer_json = json.load(f) - added_tokens = tokenizer_json.get("added_tokens", []) - for foken_data in added_tokens: - token_id = int(foken_data["id"]) - token = foken_data["content"] - if token == chat_eos_token: - chat_eos_token_id = token_id - token = token.encode("utf-8") - if toktypes[token_id] != SentencePieceTokenTypes.UNUSED: - if tokens[token_id] != token: - logger.warning(f'replacing token {token_id}: {tokens[token_id].decode("utf-8")!r} -> {token.decode("utf-8")!r}') - tokens[token_id] = token - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - if foken_data.get("special"): - toktypes[token_id] = SentencePieceTokenTypes.CONTROL - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_add_space_prefix(add_prefix) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - old_eos = special_vocab.special_token_ids["eos"] - if chat_eos_token_id is not None: - # For the chat model, we replace the eos with '<|im_end|>'. - # TODO: this is a hack, should be fixed - # https://github.com/ggml-org/llama.cpp/pull/6745#issuecomment-2067687048 - special_vocab.special_token_ids["eos"] = chat_eos_token_id - logger.warning(f"Replace eos:{old_eos} with a special token:{chat_eos_token_id}" - " in chat mode so that the conversation can end normally.") - - special_vocab.add_to_gguf(self.gguf_writer) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - num_heads = self.hparams["num_attention_heads"] - num_kv_heads = self.hparams["num_key_value_heads"] - n_embd = self.hparams["hidden_size"] - q_per_kv = num_heads // num_kv_heads - head_dim = n_embd // num_heads - num_groups = num_heads // q_per_kv - - if bid is not None and f"model.layers.{bid}.attention.wqkv" in name: - qkv = data_torch - - qkv = qkv.reshape((num_groups, q_per_kv + 2, head_dim, n_embd)) - q, k, v = qkv[:, : q_per_kv], qkv[:, -2], qkv[:, -1] - - # The model weights of q and k equire additional reshape. - q = LlamaModel.permute(q.reshape((-1, q.shape[-1])), num_heads, num_heads) - k = LlamaModel.permute(k.reshape((-1, k.shape[-1])), num_heads, num_kv_heads) - v = v.reshape((-1, v.shape[-1])) - - yield from super().modify_tensors(q, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) - yield from super().modify_tensors(k, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) - yield from super().modify_tensors(v, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("InternLM3ForCausalLM") -class InternLM3Model(TextModel): - model_arch = gguf.MODEL_ARCH.LLAMA - - def set_vocab(self): - tokens, scores, toktypes = self._create_vocab_sentencepiece() - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - if "add_prefix_space" in tokenizer_config_json: - self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) - - if "added_tokens_decoder" in tokenizer_config_json: - for token_id, token_data in tokenizer_config_json["added_tokens_decoder"].items(): - if token_data.get("special"): - token_id = int(token_id) - token = token_data["content"] - special_vocab._set_special_token(token, token_id) - # update eos token - if token == '<|im_end|>' and "eos" in special_vocab.special_token_ids: - special_vocab.special_token_ids["eos"] = token_id - - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(rope_dim) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith(("mlp", "vision_model")): - # skip visual tensors - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("BertModel", "BertForMaskedLM", "CamembertModel", "BertForSequenceClassification") -class BertModel(TextModel): - model_arch = gguf.MODEL_ARCH.BERT - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.vocab_size = None - - if cls_out_labels := self.hparams.get("id2label"): - if len(cls_out_labels) == 2 and cls_out_labels[0] == "LABEL_0": - # Remove dummy labels added by AutoConfig - cls_out_labels = None - self.cls_out_labels = cls_out_labels - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_causal_attention(False) - self._try_set_pooling_type() - - if self.cls_out_labels: - self.gguf_writer.add_classifier_output_labels([v for k, v in sorted(self.cls_out_labels.items())]) - - def set_vocab(self): - tokens, toktypes, tokpre = self.get_vocab_base() - self.vocab_size = len(tokens) - - # we need this to validate the size of the token_type embeddings - # though currently we are passing all zeros to the token_type embeddings - # "Sequence A" or "Sequence B" - self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) - - # convert to phantom space vocab - def phantom(tok, toktype): - if toktype == gguf.TokenType.CONTROL: - return tok - if tok.startswith("##"): - return tok[2:] - return "\u2581" + tok - assert len(tokens) == len(toktypes) - tokens = list(map(phantom, tokens, toktypes)) - - # add vocab to gguf - self.gguf_writer.add_tokenizer_model("bert") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - # handle special tokens - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("bert."): - name = name[5:] - - if name.endswith(".gamma"): - name = name[:-6] + ".weight" - - if name.endswith(".beta"): - name = name[:-5] + ".bias" - - # we are only using BERT for embeddings so we don't need the pooling layer - if name in ("embeddings.position_ids", "pooler.dense.weight", "pooler.dense.bias"): - return None - - if name.startswith("cls.predictions"): - return None - - if name.startswith("cls.seq_relationship"): - return None - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.cls_out_labels: - # For BertForSequenceClassification (direct projection layer) - if name == "classifier.weight": - name = "classifier.out_proj.weight" - - if name == "classifier.bias": - name = "classifier.out_proj.bias" - - yield from super().modify_tensors(data_torch, name, bid) - - def _xlmroberta_tokenizer_init(self) -> None: - # we need the pad_token_id to know how to chop down position_embd matrix - if (pad_token_id := self.hparams.get("pad_token_id")) is not None: - self._position_offset = 1 + pad_token_id - if "max_position_embeddings" in self.hparams: - self.hparams["max_position_embeddings"] -= self._position_offset - else: - self._position_offset = None - - def _xlmroberta_set_vocab(self) -> None: - # to avoid TypeError: Descriptors cannot be created directly - # exception when importing sentencepiece_model_pb2 - os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" - from sentencepiece import SentencePieceProcessor - from sentencepiece import sentencepiece_model_pb2 as model - - tokenizer_path = self.dir_model / 'sentencepiece.bpe.model' - - tokenizer_json = {} - tokenizer_config_json = {} - if not tokenizer_path.is_file(): - tokenizer_path = self.dir_model / 'tokenizer.json' - tokenizer_config_path = self.dir_model / 'tokenizer_config.json' - - if not tokenizer_path.is_file(): - raise FileNotFoundError(f"File not found: {tokenizer_path}") - - from base64 import b64decode - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - - with open(tokenizer_path, "r", encoding="utf-8") as fp: - tokenizer_json = json.load(fp) - - if tokenizer_config_path.is_file(): - with open(tokenizer_config_path, "r", encoding="utf-8") as fp: - tokenizer_config_json = json.load(fp) - - add_prefix = tokenizer.add_prefix_space # ty: ignore[unresolved-attribute] - remove_whitespaces = tokenizer.clean_up_tokenization_spaces # ty: ignore[unresolved-attribute] - precompiled_charsmap = b64decode(tokenizer_json["normalizer"]["precompiled_charsmap"]) - - vocab_size = max(self.hparams.get("vocab_size", 0), tokenizer.vocab_size) # ty: ignore[unresolved-attribute] - else: - sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] - sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) - assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM - - add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix - remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces - precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = max(self.hparams.get("vocab_size", 0), tokenizer.vocab_size()) - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - if isinstance(tokenizer, SentencePieceProcessor): - for token_id in range(tokenizer.vocab_size()): - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - else: - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - unk_token = tokenizer_config_json.get("unk_token") - unk_token_id = added_vocab.get(unk_token, tokenizer_json["model"].get("unk_id", 3)) # ty: ignore[no-matching-overload] - - for token_id in range(tokenizer.vocab_size): # ty: ignore[unresolved-attribute] - piece = tokenizer._convert_id_to_token(token_id) # ty: ignore[unresolved-attribute] - if (piece := tokenizer._convert_id_to_token(token_id)) is not None: # ty: ignore[unresolved-attribute] - text = piece.encode("utf-8") - score = tokenizer_json["model"]["vocab"][token_id][1] - - toktype = SentencePieceTokenTypes.NORMAL - if token_id == unk_token_id: - toktype = SentencePieceTokenTypes.UNKNOWN - elif token_id in tokenizer.all_special_ids: # ty: ignore[unresolved-attribute] - toktype = SentencePieceTokenTypes.CONTROL - elif token_id in added_vocab.values(): - toktype = SentencePieceTokenTypes.USER_DEFINED - # No reliable way to detect this, but jina doesn't have any - # elif tokenizer.IsByte(token_id): - # toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - if isinstance(tokenizer, SentencePieceProcessor): - # realign tokens (see HF tokenizer code) - tokens = [b'', b'', b'', b''] + tokens[3:-1] - scores = [0.0, 0.0, 0.0, 0.0] + scores[3:-1] - toktypes = [ - SentencePieceTokenTypes.CONTROL, - SentencePieceTokenTypes.CONTROL, - SentencePieceTokenTypes.CONTROL, - SentencePieceTokenTypes.UNKNOWN, - ] + toktypes[3:-1] - - if self.model_arch == gguf.MODEL_ARCH.NOMIC_BERT_MOE: - # Add mask token missing from sentencepiece.bpe.model - tokens[250001] = b'' - scores[250001] = 0.0 - toktypes[250001] = SentencePieceTokenTypes.CONTROL - - self.gguf_writer.add_tokenizer_model("t5") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_add_space_prefix(add_prefix) - self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) - self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) - if precompiled_charsmap: - self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - -@ModelBase.register("DistilBertModel", "DistilBertForMaskedLM", "DistilBertForSequenceClassification") -class DistilBertModel(BertModel): - model_arch = gguf.MODEL_ARCH.BERT - - def set_gguf_parameters(self): - self.gguf_writer.add_layer_norm_eps(1e-12) - logger.info("gguf: layer norm epsilon = 1e-12") - super().set_gguf_parameters() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("distilbert."): - name = name[11:] - - # These layers act as MLM head, so we don't need them - if name.startswith("vocab_"): - return None - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("RobertaModel", "RobertaForSequenceClassification") -class RobertaModel(BertModel): - model_arch = gguf.MODEL_ARCH.BERT - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # we need the pad_token_id to know how to chop down position_embd matrix - if (pad_token_id := self.hparams.get("pad_token_id")) is not None: - self._position_offset = 1 + pad_token_id - if "max_position_embeddings" in self.hparams: - self.hparams["max_position_embeddings"] -= self._position_offset - else: - self._position_offset = None - - def set_vocab(self): - """Support BPE tokenizers for roberta models""" - bpe_tok_path = self.dir_model / "tokenizer.json" - if bpe_tok_path.exists(): - self._set_vocab_gpt2() - - # we need this to validate the size of the token_type embeddings - # though currently we are passing all zeros to the token_type embeddings - # "Sequence A" or "Sequence B" - self.gguf_writer.add_token_type_count(self.hparams.get("type_vocab_size", 1)) - - else: - return super().set_vocab() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # if name starts with "roberta.", remove the prefix - # e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main - if name.startswith("roberta."): - name = name[8:] - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # position embeddings start at pad_token_id + 1, so just chop down the weight tensor - if name == "embeddings.position_embeddings.weight": - if self._position_offset is not None: - data_torch = data_torch[self._position_offset:,:] - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("NomicBertModel") -class NomicBertModel(BertModel): - model_arch = gguf.MODEL_ARCH.BERT - - def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, **kwargs: Any): - hparams = kwargs.pop("hparams", None) - if hparams is None: - hparams = ModelBase.load_hparams(dir_model, False) - - self.is_moe = bool(hparams.get("moe_every_n_layers")) - self.model_arch = gguf.MODEL_ARCH.NOMIC_BERT_MOE if self.is_moe else gguf.MODEL_ARCH.NOMIC_BERT - - super().__init__(dir_model, ftype, fname_out, hparams=hparams, **kwargs) - - self._tokenizer_is_xlmroberta = self._is_tokenizer_xlmroberta() - if self._tokenizer_is_xlmroberta: - self._xlmroberta_tokenizer_init() - - npos, mtp = self.hparams["n_positions"], self.hparams.get("max_trained_positions", 2048) - if npos == 8192 and mtp == 2048: - self.hparams["n_positions"] = 2048 # nomic-embed-text v1 and v1.5 are trained for 2048 tokens. - elif npos == 2048 and mtp == 2048: - self.hparams["n_positions"] = 512 # nomic-embed-text-v2-moe is trained for 512 tokens. - else: - raise ValueError(f"unrecognized parameters: n_positions={npos}, max_trained_positions={mtp}") - - assert self.hparams["activation_function"] == "gelu" if self.is_moe else "swiglu" - - # this doesn't do anything in the HF version - assert self.hparams["causal"] is False - # no bias tensors unless MoE - assert self.hparams["qkv_proj_bias"] == self.is_moe - assert self.hparams["mlp_fc1_bias"] == self.is_moe - assert self.hparams["mlp_fc2_bias"] == self.is_moe - - # norm at end of layer - assert self.hparams["prenorm"] is False - # standard RoPE - assert self.hparams["rotary_emb_fraction"] == 1.0 - assert self.hparams["rotary_emb_interleaved"] is False - assert self.hparams["rotary_emb_scale_base"] is None - - def set_vocab(self) -> None: - if self._tokenizer_is_xlmroberta: - return self._xlmroberta_set_vocab() - return super().set_vocab() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # If the tensor is an experts bias tensor, skip it. - if "mlp.experts.bias" in name: - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: torch.Tensor, name: str, bid: int | None) -> Iterable[tuple[str, torch.Tensor]]: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - if "mlp.experts.mlp.w1" in name: - data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"]) - name += ".weight" - - if "mlp.experts.mlp.w2" in name: - data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"]) - data_torch = data_torch.transpose(1, 2) - name += ".weight" - - yield from super().modify_tensors(data_torch, name, bid) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if self.is_moe: - self.gguf_writer.add_moe_every_n_layers(self.hparams["moe_every_n_layers"]) - self.gguf_writer.add_expert_used_count(self.hparams["moe_top_k"]) - - def _is_tokenizer_xlmroberta(self) -> bool: - with open(self.dir_model / "tokenizer.json") as f: - tokenizer_json = json.load(f) - toktyp = tokenizer_json["model"]["type"] - if toktyp == "Unigram": - return True - if toktyp == "WordPiece": - return False - raise ValueError(f"unknown tokenizer: {toktyp}") - - -@ModelBase.register("NeoBERT", "NeoBERTLMHead", "NeoBERTForSequenceClassification") -class NeoBert(BertModel): - model_arch = gguf.MODEL_ARCH.NEO_BERT - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # NeoBERT uses 2/3 of the intermediate size as feed forward length - self.gguf_writer.add_feed_forward_length(int(2 * self.hparams["intermediate_size"] / 3)) - self.gguf_writer.add_rope_freq_base(10000.0) # default value for NeoBERT - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - - f_rms_eps = self.hparams.get("norm_eps", 1e-6) # default value for NeoBERT - self.gguf_writer.add_layer_norm_rms_eps(f_rms_eps) - logger.info(f"gguf: rms norm epsilon = {f_rms_eps}") - - self.gguf_writer.add_pooling_type(gguf.PoolingType.CLS) # https://huggingface.co/chandar-lab/NeoBERT#how-to-use - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("decoder."): - return None - - if name.startswith("model."): - name = name[6:] - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("EuroBertModel", "JinaEmbeddingsV5Model") -class EuroBertModel(TextModel): - model_arch = gguf.MODEL_ARCH.EUROBERT - - def set_vocab(self): - self.gguf_writer.add_add_bos_token(False) - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # EuroBert is bidirectional (encoder) - self.gguf_writer.add_causal_attention(False) - - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - - self._try_set_pooling_type() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("model."): - name = name[6:] - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("XLMRobertaModel", "XLMRobertaForSequenceClassification") -class XLMRobertaModel(BertModel): - model_arch = gguf.MODEL_ARCH.BERT - _lora_files = {} - _lora_names = [] - - def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path, **kwargs: Any): - hparams = kwargs.pop("hparams", None) - if hparams is None: - hparams = ModelBase.load_hparams(dir_model, False) - - if lora_names := hparams.get("lora_adaptations"): - self._lora_names = lora_names - self.model_arch = gguf.MODEL_ARCH.JINA_BERT_V3 - - super().__init__(dir_model, ftype, fname_out, hparams=hparams, **kwargs) - self._xlmroberta_tokenizer_init() - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - if self._lora_names: - for name in self._lora_names: - fname = self.add_prefix_to_filename(self.fname_out, f"lora-{name}-") - self._lora_files[name] = gguf.GGUFWriter(fname, arch=gguf.MODEL_ARCH_NAMES[self.model_arch], endianess=self.endianess, use_temp_file=self.use_temp_file, dry_run=self.dry_run) - - return super().generate_extra_tensors() - - def set_type(self): - for lora_writer in self._lora_files.values(): - lora_writer.add_type(gguf.GGUFType.ADAPTER) - lora_writer.add_string(gguf.Keys.Adapter.TYPE, "lora") - super().set_type() - - def set_vocab(self): - self._xlmroberta_set_vocab() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # if name starts with "roberta.", remove the prefix - # e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main - if name.startswith("roberta."): - name = name[8:] - - # jina-embeddings-v3 - if ".parametrizations." in name: - name = name.replace(".parametrizations.", ".") - if name.endswith(".original"): - name = name[:-9] - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # position embeddings start at pad_token_id + 1, so just chop down the weight tensor - if name == "embeddings.position_embeddings.weight": - if self._position_offset is not None: - data_torch = data_torch[self._position_offset:,:] - - if name.endswith(".0.lora_A") or name.endswith(".0.lora_B"): - if name.startswith("pooler.dense"): - return - - num_loras = data_torch.size(0) - assert num_loras == len(self._lora_names) - - # Split out each LoRA in their own GGUF - for i, lora_writer in enumerate(self._lora_files.values()): - new_name = self.map_tensor_name(name[:-9]) + name[-7:].lower() - data = data_torch[i, :, :] - # Transpose/flip token_embd/types into correct shape - if new_name == "token_embd.weight.lora_b": - data = data.T - elif new_name.startswith("token_types.weight."): - new_name = new_name[:-1] + ("a" if new_name[-1:] == "b" else "b") - lora_writer.add_tensor(new_name, data.float().numpy(), raw_dtype=gguf.GGMLQuantizationType.F32) - - return - - yield from super().modify_tensors(data_torch, name, bid) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # jina-embeddings-v3 - lora_alpha = self.hparams.get("lora_alpha") - if lora_prompt_prefixes := self.hparams.get("task_instructions"): - assert self._lora_files and all(lora_name in lora_prompt_prefixes for lora_name in self._lora_files.keys()) - for lora_name, lora_writer in self._lora_files.items(): - lora_writer.add_float32(gguf.Keys.Adapter.LORA_ALPHA, lora_alpha if lora_alpha is not None else 1.0) - lora_writer.add_string(gguf.Keys.Adapter.LORA_TASK_NAME, lora_name) - if lora_prompt_prefixes: - lora_writer.add_string(gguf.Keys.Adapter.LORA_PROMPT_PREFIX, lora_prompt_prefixes[lora_name]) - - def write(self): - super().write() - for lora_writer in self._lora_files.values(): - lora_writer.write_header_to_file() - lora_writer.write_kv_data_to_file() - lora_writer.write_tensors_to_file(progress=True) - lora_writer.close() - - -@ModelBase.register("GemmaForCausalLM") -class GemmaModel(TextModel): - model_arch = gguf.MODEL_ARCH.GEMMA - - def set_vocab(self): - self._set_vocab_sentencepiece() - - # TODO: these special tokens should be exported only for the CodeGemma family - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False, - special_token_types = ['prefix', 'suffix', 'middle', 'fsep', 'eot']) - special_vocab._set_special_token("prefix", 67) - special_vocab._set_special_token("suffix", 69) - special_vocab._set_special_token("middle", 68) - special_vocab._set_special_token("fsep", 70) - special_vocab._set_special_token("eot", 107) - special_vocab.chat_template = None # do not add it twice - special_vocab.add_to_gguf(self.gguf_writer) - - self.gguf_writer.add_add_space_prefix(False) - - def set_gguf_parameters(self): - hparams = self.hparams - - self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) - self.gguf_writer.add_head_count(hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"] if "num_key_value_heads" in hparams else hparams["num_attention_heads"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) - self.gguf_writer.add_key_length(hparams["head_dim"]) - self.gguf_writer.add_value_length(hparams["head_dim"]) - self.gguf_writer.add_file_type(self.ftype) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # lm_head is not used in llama.cpp, while autoawq will include this tensor in model - # To prevent errors, skip loading lm_head.weight. - if name == "lm_head.weight": - logger.debug(f"Skipping get tensor {name!r} in safetensors so that convert can end normally.") - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # ref: https://github.com/huggingface/transformers/blob/fc37f38915372c15992b540dfcbbe00a916d4fc6/src/transformers/models/gemma/modeling_gemma.py#L89 - if name.endswith("norm.weight"): - data_torch = data_torch + 1 - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Gemma2ForCausalLM") -class Gemma2Model(TextModel): - model_arch = gguf.MODEL_ARCH.GEMMA2 - - def set_vocab(self): - self._set_vocab_sentencepiece() - - self.gguf_writer.add_add_space_prefix(False) - - def set_gguf_parameters(self): - hparams = self.hparams - - self.gguf_writer.add_context_length(hparams["max_position_embeddings"]) - self.gguf_writer.add_embedding_length(hparams["hidden_size"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_feed_forward_length(hparams["intermediate_size"]) - self.gguf_writer.add_head_count(hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"] if "num_key_value_heads" in hparams else hparams["num_attention_heads"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["rms_norm_eps"]) - self.gguf_writer.add_key_length(hparams["head_dim"]) - self.gguf_writer.add_value_length(hparams["head_dim"]) - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_attn_logit_softcapping( - self.hparams["attn_logit_softcapping"] - ) - self.gguf_writer.add_final_logit_softcapping( - self.hparams["final_logit_softcapping"] - ) - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # lm_head is not used in llama.cpp, while autoawq will include this tensor in model - # To prevent errors, skip loading lm_head.weight. - if name == "lm_head.weight": - logger.debug(f"Skipping get tensor {name!r} in safetensors so that convert can end normally.") - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # ref: https://github.com/huggingface/transformers/blob/fc37f38915372c15992b540dfcbbe00a916d4fc6/src/transformers/models/gemma/modeling_gemma.py#L89 - if name.endswith("norm.weight"): - data_torch = data_torch + 1 - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Gemma3ForCausalLM", "Gemma3ForConditionalGeneration") -class Gemma3Model(TextModel): - model_arch = gguf.MODEL_ARCH.GEMMA3 - - def norm_shift(self, name: str) -> float: - return 1.0 if name.endswith("norm.weight") else 0.0 # Gemma3RMSNorm adds 1.0 to the norm value - - def set_vocab(self): - if (self.dir_model / "tokenizer.model").is_file(): - self._set_vocab_sentencepiece() - self.gguf_writer.add_add_space_prefix(False) - else: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - - # some default values are not specified in the hparams - self.gguf_writer.add_context_length(hparams.get("max_position_embeddings", 131072)) - self.gguf_writer.add_head_count(hparams.get("num_attention_heads", 8)) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("rms_norm_eps", 1e-6)) - self.gguf_writer.add_key_length(hparams.get("head_dim", 256)) - self.gguf_writer.add_value_length(hparams.get("head_dim", 256)) - self.gguf_writer.add_rope_freq_base(self.rope_parameters.get("full_attention", self.rope_parameters).get("rope_theta", 1_000_000.0)) # for global layers - # attn_logit_softcapping is removed in Gemma3 - assert hparams.get("attn_logit_softcapping") is None - if (final_logit_softcap := hparams.get("final_logit_softcapping")): - self.gguf_writer.add_final_logit_softcapping(final_logit_softcap) - if hparams.get("sliding_window_pattern") != 1: - self.gguf_writer.add_sliding_window(hparams["sliding_window"]) - self.gguf_writer.add_head_count_kv(hparams.get("num_key_value_heads", 4)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # remove OOV (out-of-vocabulary) rows in token_embd - if "embed_tokens.weight" in name: - n_vocab_real = -1 - if (self.dir_model / "tokenizer.model").is_file(): - tokens = self._create_vocab_sentencepiece()[0] - n_vocab_real = len(tokens) - else: - with open(self.dir_model / "tokenizer.json", "r", encoding="utf-8") as f: - tokenizer_json = json.load(f) - n_vocab_real = len(tokenizer_json["model"]["vocab"]) + len(tokenizer_json["added_tokens"]) - data_torch = data_torch[:n_vocab_real] - - # ref code in Gemma3RMSNorm - # output = output * (1.0 + self.weight.float()) - # note: this is not the case on gemma3n - f_shift = self.norm_shift(name) - if f_shift != 0.0: - data_torch = data_torch + f_shift - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Gemma3TextModel") -class EmbeddingGemma(Gemma3Model): - model_arch = gguf.MODEL_ARCH.GEMMA_EMBEDDING - module_paths = [] - dense_features_dims = {} - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.sentence_transformers_dense_modules: - # read modules.json to determine if model has Dense layers - modules_file = self.dir_model / "modules.json" - if modules_file.is_file(): - with open(modules_file, encoding="utf-8") as modules_json_file: - mods = json.load(modules_json_file) - for mod in mods: - if mod["type"].endswith("Dense"): - mod_path = mod["path"] - # check if model.safetensors file for Dense layer exists - model_tensors_file = self.dir_model / mod_path / "model.safetensors" - if model_tensors_file.is_file(): - self.module_paths.append(mod_path) - # read config.json of the Dense layer to get in/out features - mod_conf_file = self.dir_model / mod_path / "config.json" - if mod_conf_file.is_file(): - with open(mod_conf_file, encoding="utf-8") as mod_conf_json_file: - mod_conf = json.load(mod_conf_json_file) - # hparams dense_2_feat_out and dense_3_feat_in are required when loading model's dense weights - prefix = self._get_dense_prefix(mod_path) - if mod_conf["in_features"] is not None and mod_conf["out_features"] is not None: - self.dense_features_dims[prefix] = (mod_conf["in_features"], mod_conf["out_features"]) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - from safetensors.torch import load_file - module_paths = list(self.module_paths) - for i, module_path in enumerate(module_paths): - tensors_file = self.dir_model / module_path / "model.safetensors" - local_tensors = load_file(tensors_file) - tensor_name = self._get_dense_prefix(module_path) - for name, local_tensor in local_tensors.items(): - if not name.endswith(".weight"): - continue - orig_name = name.replace("linear", tensor_name) - name = self.map_tensor_name(orig_name) - yield name, local_tensor.clone() - - @staticmethod - def _get_dense_prefix(module_path) -> str: - """Get the tensor name prefix for the Dense layer from module path.""" - tensor_name = "dense_2" if module_path == "2_Dense" else "dense_3" - return tensor_name - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # Override the sliding window size as it gets adjusted by the Gemma3TextConfig - # constructor. We want to use the value from the original model's config.json. - # ref: https://github.com/huggingface/transformers/pull/40700 - with open(self.dir_model / "config.json", "r", encoding="utf-8") as f: - config = json.load(f) - orig_sliding_window = config.get("sliding_window") - if orig_sliding_window is None: - raise ValueError("sliding_window not found in model config - this is required for the model") - - logger.info(f"Using original sliding_window from config: {orig_sliding_window} " - f"instead of {self.hparams['sliding_window']}") - self.gguf_writer.add_sliding_window(orig_sliding_window) - if self.sentence_transformers_dense_modules: - for dense, dims in self.dense_features_dims.items(): - logger.info(f"Setting dense layer {dense} in/out features to {dims}") - self.gguf_writer.add_dense_features_dims(dense, dims[0], dims[1]) - - self._try_set_pooling_type() - - -@ModelBase.register("Gemma3ForConditionalGeneration") -class Gemma3VisionModel(MmprojModel): - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GEMMA3) - # default values below are taken from HF transformers code - self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("layer_norm_eps", 1e-6)) - self.gguf_writer.add_vision_use_gelu(True) - # calculate proj_scale_factor (used by tinygemma3 test model) - image_seq_length = self.preprocessor_config.get("image_seq_length", 256) - n_per_side = int(image_seq_length ** 0.5) - image_size = self.hparams["image_size"] - patch_size = self.hparams["patch_size"] - proj_scale_factor = (image_size // patch_size) // n_per_side - if proj_scale_factor > 0 and proj_scale_factor != 4: - # we only need to write this if it's not the default value - # in this case, we are converting a test model - self.gguf_writer.add_vision_projector_scale_factor(proj_scale_factor) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - # related to https://github.com/ggml-org/llama.cpp/issues/13025 - if "input_projection" in name: - return gguf.GGMLQuantizationType.F16 - if ".embeddings." in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "vision_model.head." in name: - # skip redundant tensors for tinygemma3 - return None - - if not name.startswith(("multi_modal_projector.", "vision_tower.", "multimodal_projector.", "vision_model.")): - return None - - name = name.replace("_weight", ".weight") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # correct norm value ; only this "soft_emb_norm" need to be corrected as it's part of Gemma projector - # the other norm values are part of SigLIP model, and they are already correct - # ref code: Gemma3RMSNorm - if "soft_emb_norm.weight" in name: - logger.info(f"Correcting norm value for '{name}'") - data_torch = data_torch + 1 - - yield from super().modify_tensors(data_torch, name, bid) - - -class ConformerAudioModel(MmprojModel): - _batch_norm_tensors: list[dict[str, Tensor]] | None = None - - @staticmethod - def is_audio_tensor(name: str): - return any(p in name for p in ["audio", "codebook", "conformer", "depth_embedding", "depthformer", "depth_linear"]) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ConformerAudioModel.is_audio_tensor(name): - if ".conv" in name or "_conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # fold running_mean, running_var and eps into weight and bias for batch_norm - if "batch_norm" in name: - if self._batch_norm_tensors is None: - self._batch_norm_tensors = [{} for _ in range(self.block_count)] - assert bid is not None - self._batch_norm_tensors[bid][name] = data_torch - - if len(self._batch_norm_tensors[bid]) < 5: - return - - weight = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.weight"] - bias = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.bias"] - running_mean = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.running_mean"] - running_var = self._batch_norm_tensors[bid][f"conformer.layers.{bid}.conv.batch_norm.running_var"] - eps = 1e-5 # default value - - a = weight / torch.sqrt(running_var + eps) - b = bias - running_mean * a - yield from super().modify_tensors(a, f"conformer.layers.{bid}.conv.batch_norm.weight", bid) - yield from super().modify_tensors(b, f"conformer.layers.{bid}.conv.batch_norm.bias", bid) - return - - # reshape conv weights - if name.startswith("conformer.pre_encode.conv.") and name.endswith(".bias"): - data_torch = data_torch[:, None, None] - if "conv.depthwise_conv" in name and name.endswith(".weight"): - assert data_torch.shape[1] == 1 - data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[2]) - if "conv.pointwise_conv" in name and name.endswith(".weight"): - assert data_torch.shape[2] == 1 - data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[1]) - - mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) - yield (mapped_name, data_torch) - - -@ModelBase.register("DeepseekOCRForCausalLM") -class DeepseekOCRVisionModel(MmprojModel): - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.DEEPSEEKOCR) - # default values below are taken from HF tranformers code - self.gguf_writer.add_vision_attention_layernorm_eps(hparams.get("layer_norm_eps", 1e-6)) - self.gguf_writer.add_vision_use_gelu(True) - # calculate proj_scale_factor (used by tinygemma3 test model) - image_seq_length = self.preprocessor_config.get("image_seq_length", 256) - n_per_side = int(image_seq_length ** 0.5) - image_size = self.hparams["image_size"] - patch_size = self.hparams["patch_size"] - proj_scale_factor = (image_size // patch_size) // n_per_side - if proj_scale_factor > 0 and proj_scale_factor != 4: - # we only need to write this if it's not the default value - # in this case, we are converting a test model - self.gguf_writer.add_vision_projector_scale_factor(proj_scale_factor) - # @bluebread: there's no window_size in config but just add it here anyway - self.gguf_writer.add_vision_window_size(self.hparams.get("window_size", 14)) - - # SAM configuration - sam_hparams = hparams['sam'] - self.gguf_writer.add_vision_sam_layers_count(sam_hparams['layers']) - self.gguf_writer.add_vision_sam_embedding_length(sam_hparams['width']) - self.gguf_writer.add_vision_sam_head_count(sam_hparams['heads']) - - def get_vision_config(self) -> dict[str, Any]: - vision_config: dict[str, Any] | None = self.global_config.get("vision_config") - - if not vision_config: - raise ValueError("DeepseekOCR model requires 'vision_config' in the model configuration, but it was not found") - - vision_config['sam'] = vision_config['width']['sam_vit_b'] - vision_config.update(vision_config['width']['clip-l-14-224']) - vision_config['hidden_size'] = vision_config['width'] - vision_config['num_heads'] = vision_config['heads'] - vision_config['intermediate_size'] = vision_config['heads'] * 4 - - return vision_config - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".embeddings." in name or 'pos_embed' in name: - return gguf.GGMLQuantizationType.F32 - if ".rel_pos_h" in name or '.rel_pos_w' in name: - return gguf.GGMLQuantizationType.F32 - if ".neck." in name or ".net_" in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Only process vision-related tensors, skip language model tensors - # Vision components: sam_model, vision_model, projector, image_newline, view_seperator - # Language model components to skip: lm_head, embed_tokens, layers, norm - if name.startswith(("lm_head.", "model.embed_tokens.", "model.layers.", "model.norm.")): - return None - - if name.endswith("pos_embed") or name.endswith("rel_pos_h") or name.endswith("rel_pos_w"): - name += ".weight" - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("Gemma3nForConditionalGeneration") -class Gemma3nVisionAudioModel(ConformerAudioModel): - has_audio_encoder = True - has_vision_encoder = True - - # Double indexed mapping for MobileNetV5 blocks (not supported by tensor_mapping.py) - # This is the only known model having this, so we prefer implementing it outside of tensor_mapping.py - block_tensor_mapping = { - "model.vision_tower.timm_model.blocks.{bid}.{sid}.conv_exp.weight": "v.blk.{bid}.{sid}.conv_exp.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.bn1.weight": "v.blk.{bid}.{sid}.bn1.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.conv_pwl.weight": "v.blk.{bid}.{sid}.conv_pwl.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.bn2.weight": "v.blk.{bid}.{sid}.bn2.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_start.conv.weight": "v.blk.{bid}.{sid}.dw_start.conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_start.bn.weight": "v.blk.{bid}.{sid}.dw_start.bn.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_mid.conv.weight": "v.blk.{bid}.{sid}.dw_mid.conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.dw_mid.bn.weight": "v.blk.{bid}.{sid}.dw_mid.bn.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_exp.conv.weight": "v.blk.{bid}.{sid}.pw_exp.conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_exp.bn.weight": "v.blk.{bid}.{sid}.pw_exp.bn.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_proj.conv.weight": "v.blk.{bid}.{sid}.pw_proj.conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.pw_proj.bn.weight": "v.blk.{bid}.{sid}.pw_proj.bn.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.layer_scale.gamma": "v.blk.{bid}.{sid}.layer_scale.gamma", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.query.proj.weight": "v.blk.{bid}.{sid}.attn.query.proj.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.proj.weight": "v.blk.{bid}.{sid}.attn.key.proj.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.proj.weight": "v.blk.{bid}.{sid}.attn.value.proj.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.output.proj.weight": "v.blk.{bid}.{sid}.attn.output.proj.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.down_conv.weight": "v.blk.{bid}.{sid}.attn.key.down_conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.key.norm.weight": "v.blk.{bid}.{sid}.attn.key.norm.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.down_conv.weight": "v.blk.{bid}.{sid}.attn.value.down_conv.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.attn.value.norm.weight": "v.blk.{bid}.{sid}.attn.value.norm.weight", - "model.vision_tower.timm_model.blocks.{bid}.{sid}.norm.weight": "v.blk.{bid}.{sid}.norm.weight", - } - - def __init__(self, *args, **kwargs): - # Parent init will call find_hparam which now returns 0 for empty keys - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["n_layers"] = 128 # fake value for audio encoder, vision encoder doesn't use it - self.hparams_vision["intermediate_size"] = self.hparams_vision.get("intermediate_size", 2048) * 4 - self.hparams_vision["num_attention_heads"] = self.hparams_vision.get("num_attention_heads", 8) - - # MobileNetV5 does not use image_mean/std - self.preprocessor_config["image_mean"] = [0.0 ,0.0 , 0.0] - self.preprocessor_config["image_std"] = [1.0 ,1.0 ,1.0] - self.hparams_vision["image_size"] = self.preprocessor_config.get( - "size", {"height": 768, "width": 768} - )["height"] - - # Image sequence length (256 tokens = 16x16 for Gemma3n) - image_seq_length = self.preprocessor_config.get("image_seq_length", 256) - image_size = self.hparams_vision["image_size"] - self.hparams_vision["patch_size"] = image_size // image_seq_length - - # remap audio hparams - assert self.hparams_audio is not None - self.hparams_audio["n_layers"] = self.hparams_audio["conf_num_hidden_layers"] - self.hparams_audio["num_attention_heads"] = self.hparams_audio["conf_num_attention_heads"] - self.hparams_audio["feat_in"] = self.hparams_audio["input_feat_size"] - self.hparams_audio["intermediate_size"] = self.hparams_audio.get("intermediate_size", 6144) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # vision params - self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA3NV) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) - - # audio params - assert self.hparams_audio is not None - self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.GEMMA3NA) - self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) - self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - # Force quantization settings for specific tensor types - if "input_projection" in name or "input_proj" in name: - return gguf.GGMLQuantizationType.F16 - if ".embeddings." in name or "stem" in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - def custom_map(self, name: str) -> str: - """Parses names like model.vision_tower.timm_model.blocks.1.2.suffix and applies template mapping.""" - parts = name.split(".") - # MobileNet blocks have at least 7 parts: model, vision_tower, timm_model, blocks, bid, sid, and suffix - if len(parts) >= 7: - bid, sid = parts[4], parts[5] - suffix = ".".join(parts[6:]) - template = f"model.vision_tower.timm_model.blocks.{{bid}}.{{sid}}.{suffix}" - if template in self.block_tensor_mapping: - return self.block_tensor_mapping[template].format(bid=bid, sid=sid) - - raise ValueError(f"Unknown name: {name}") - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if (ConformerAudioModel.is_audio_tensor(name)): - name = name.replace("model.audio_tower.conformer.", "conformer.layers.") - yield from super().modify_tensors(data_torch, name, bid) - - # Gemma3n uses - # - model.embed_vision.* for projection layers - # - model.vision_tower.* for vision encoder - # Skip non-vision tensors - if not (name.startswith("model.embed_vision.") or name.startswith("model.vision_tower.")): - return - - if name.startswith("model.vision_tower.timm_model.blocks."): - # Double-indexed block tensors through custom logic - yield (self.custom_map(name), data_torch) - return - else: - # Route non-repeating (conv_stem, msfa, embedding, etc.) and un-catched through tensor_mapping.py - new_name = self.map_tensor_name(name) - - if new_name.endswith("conv_stem.conv.bias") or new_name.endswith("layer_scale.gamma"): - data_torch = data_torch.unsqueeze(0).unsqueeze(-1).unsqueeze(-1) # [1, C, 1, 1] - - yield from ModelBase.modify_tensors(self, data_torch, new_name, bid) - - -@ModelBase.register("Gemma3nForCausalLM", "Gemma3nForConditionalGeneration") -class Gemma3NModel(Gemma3Model): - model_arch = gguf.MODEL_ARCH.GEMMA3N - - _altup_proj: list[Tensor] = [] - _altup_unembd: list[Tensor] = [] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams["altup_num_inputs"] == 4, "Current conversion only supports 4 altup inputs" - self._altup_proj = [ - torch.Tensor(), # to be replaced - torch.Tensor(), # to be replaced - torch.Tensor(), # to be replaced - ] - self._altup_unembd = [ - torch.Tensor(), # to be replaced - torch.Tensor(), # to be replaced - torch.Tensor(), # to be replaced - ] - - def norm_shift(self, name: str) -> float: - del name - return 0.0 # same value with Gemma3p5RMSNorm scale_shift on python code - - def set_vocab(self): - # For Gemma3n multimodal models, we need the FULL vocab_size (262400) - # which includes special tokens from 262144-262399 for vision/audio. - # The vocab_size_per_layer_input (262144) is only the embedding size per layer. - # Temporarily override the hparams lookup order to prioritize vocab_size. - - # Store original vocab_size_per_layer_input if it exists - vocab_size_per_layer_input = self.hparams.get("vocab_size_per_layer_input") - - # Temporarily remove vocab_size_per_layer_input to force using vocab_size - if vocab_size_per_layer_input is not None: - del self.hparams["vocab_size_per_layer_input"] - - # Call parent set_vocab which will now use vocab_size (262400) - super().set_vocab() - - # Restore vocab_size_per_layer_input for later use - if vocab_size_per_layer_input is not None: - self.hparams["vocab_size_per_layer_input"] = vocab_size_per_layer_input - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_altup_active_idx(self.hparams["altup_active_idx"]) - self.gguf_writer.add_altup_num_inputs(self.hparams["altup_num_inputs"]) - self.gguf_writer.add_embedding_length_per_layer_input(self.hparams["hidden_size_per_layer_input"]) - self.gguf_writer.add_shared_kv_layers(self.hparams["num_kv_shared_layers"]) - - activation_sparsity_scale = [] - for s in self.hparams["activation_sparsity_pattern"]: - normal_dist = torch.distributions.normal.Normal(0, 1) - std_multiplier = normal_dist.icdf(torch.tensor(s, dtype=torch.float32)) - activation_sparsity_scale.append(std_multiplier.item()) - self.gguf_writer.add_activation_sparsity_scale(activation_sparsity_scale) - - sliding_window_pattern = [] - for t in self.hparams["layer_types"]: - sliding_window_pattern.append(t == "sliding_attention") - self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) - - def _stack_matrices(self, matrices: list[Tensor]) -> Tensor | None: - has_all = all(m.numel() > 0 for m in matrices) - if not has_all: - return None - else: - return torch.stack(matrices, dim=0) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith("_scale"): - name = name + ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # TODO: implement self.prediction_coefs.weight.clamp_(...) - - # Pad token embeddings for vision/audio special tokens (262144-262399) - if "embed_tokens.weight" in name or "embed_tokens_per_layer" in name: - # Move to CPU to avoid meta device issues during padding - data_torch = data_torch.to(device="cpu") - - vocab_size = self.hparams.get("vocab_size", 262400) - current_size = data_torch.shape[0] # First dimension is vocab_size - - if current_size < vocab_size: - # Pad with zeros for vision/audio tokens (they get embeddings from vision tower) - padding_size = vocab_size - current_size - tensor_type = "per-layer embeddings" if "per_layer" in name else "token embeddings" - logger.info(f"Padding {tensor_type} shape {list(data_torch.shape)} from {current_size} to {vocab_size} (adding {padding_size} vision/audio token slots)") - - # Create padding with zeros (vision tokens won't use these embeddings) - padding = torch.zeros((padding_size, data_torch.shape[1]), dtype=data_torch.dtype, device=data_torch.device) - data_torch = torch.cat([data_torch, padding], dim=0) - - # Continue with normal processing - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - return - - if "altup_unembed_projections" in name: - data_torch = data_torch.to(device="cpu") - # altup_unembed matrices are [hidden_size, hidden_size], NOT vocab-based - # They should NOT be padded - if ".0." in name: - self._altup_unembd[0] = data_torch - elif ".1." in name: - self._altup_unembd[1] = data_torch - elif ".2." in name: - self._altup_unembd[2] = data_torch - else: - raise ValueError(f"Unknown name: {name}") - out = self._stack_matrices(self._altup_unembd) - if out is not None: - yield from ModelBase.modify_tensors(self, out, "model.altup_unembed_projections.weight", bid) - return - else: - return - - if "altup_projections" in name: - data_torch = data_torch.to(device="cpu") - if ".0." in name: - self._altup_proj[0] = data_torch - elif ".1." in name: - self._altup_proj[1] = data_torch - elif ".2." in name: - self._altup_proj[2] = data_torch - else: - raise ValueError(f"Unknown name: {name}") - out = self._stack_matrices(self._altup_proj) - if out is not None: - yield from ModelBase.modify_tensors(self, out, "model.altup_projections.weight", bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Gemma4ForConditionalGeneration") -class Gemma4Model(Gemma3Model): - model_arch = gguf.MODEL_ARCH.GEMMA4 - - def norm_shift(self, name: str) -> float: - del name # unused - return 0.0 - - def set_vocab(self): - vocab = gguf.LlamaHfVocab(self.dir_model) - tokens = [] - scores = [] - toktypes = [] - visible_tokens = {"<|channel>", "", "<|tool_call>", "", "<|tool_response>", "", "<|\"|>"} - - for text, score, toktype in vocab.all_tokens(): - tokens.append(text) - scores.append(score) - text_str = text.decode() - if text_str in visible_tokens: - # always render these tokens, so that the chat parser can read them - toktypes.append(gguf.TokenType.USER_DEFINED) - logger.info(f"Token '{text_str}' is set to USER_DEFINED") - else: - toktypes.append(toktype) - - assert len(tokens) == vocab.vocab_size - - self.gguf_writer.add_tokenizer_model("gemma4") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab.add_to_gguf(self.gguf_writer) - self.gguf_writer.add_add_space_prefix(False) - self.gguf_writer.add_add_bos_token(True) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - num_kv_shared_layers = self.hparams["num_kv_shared_layers"] - self.gguf_writer.add_shared_kv_layers(num_kv_shared_layers) - - # per-layer embedding is optional - n_pl_embd = self.hparams.get("hidden_size_per_layer_input") or 0 - self.gguf_writer.add_embedding_length_per_layer_input(n_pl_embd) - - swa_layers = [t == "sliding_attention" for t in self.hparams["layer_types"]] - self.gguf_writer.add_sliding_window_pattern(swa_layers) - - head_dim_full = self.hparams["global_head_dim"] - head_dim_swa = self.hparams["head_dim"] - # correct the head dim for global/swa layers - self.gguf_writer.add_key_length(head_dim_full) - self.gguf_writer.add_value_length(head_dim_full) - self.gguf_writer.add_key_length_swa(head_dim_swa) - self.gguf_writer.add_value_length_swa(head_dim_swa) - - expert_intermediate_size = self.find_hparam(["expert_intermediate_size", "moe_intermediate_size"]) - if expert_intermediate_size is not None: - self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size) - - # if use_double_wide_mlp is set, we need to adjust the value for kv shared layers - use_double_wide_mlp = self.hparams.get("use_double_wide_mlp", False) - first_kv_shared_layer_idx = self.block_count - num_kv_shared_layers - if use_double_wide_mlp: - n_ff = self.hparams["intermediate_size"] - n_ff_arr = [n_ff if il < first_kv_shared_layer_idx else n_ff * 2 for il in range(self.block_count)] - self.gguf_writer.add_feed_forward_length(n_ff_arr) - - # handle num_global_key_value_heads - num_key_value_heads_full = self.hparams.get("num_global_key_value_heads") - num_key_value_heads_swa = self.hparams.get("num_key_value_heads") - if num_key_value_heads_full is not None and num_key_value_heads_swa is not None: - value_arr = [num_key_value_heads_swa if is_swa else num_key_value_heads_full for is_swa in swa_layers] - self.gguf_writer.add_head_count_kv(value_arr) - - # handle n_rot differently for global vs swa layers - partial_rotary_factor_swa = self.hparams.get("partial_rotary_factor", 1.0) - n_rot_full = int(head_dim_full) # "proportional" is used, see generate_extra_tensors - n_rot_swa = int(head_dim_swa * partial_rotary_factor_swa) - self.gguf_writer.add_rope_dimension_count(n_rot_full) - self.gguf_writer.add_rope_dimension_count_swa(n_rot_swa) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - # full layer uses "proportional" rope with partial_rotary_factor=0.25 - # the expected ordering is cc000000ss000000 (c = cos, s = sin, 0 = unrotated), - # but ggml neox only supports ccss000000000000, and we cannot rearrange the head because that will break use_alternative_attention - # solution is to set specific freq_factors for the unrotated dims - - # IMPORTANT: this ROPE_FREQS tensor is ONLY used by the full_attention layers - rope_params_full = self.hparams["rope_parameters"]["full_attention"] - assert rope_params_full["rope_type"] == "proportional" - head_dim_full = (self.hparams["global_head_dim"]) - partial_rotary_factor_full = rope_params_full["partial_rotary_factor"] - n_rot_full = int(head_dim_full * partial_rotary_factor_full / 2) - n_unrot_full = int(head_dim_full / 2) - n_rot_full - values = [1.0] * n_rot_full + [1e30] * n_unrot_full - rope_freqs_full = torch.tensor(values, dtype=torch.float32) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), rope_freqs_full) - - def _generate_nvfp4_tensors(self): - # Gemma-4 stores a per-layer router.per_expert_scale ([n_expert]) that scales - # each expert's contribution. It's mathematically equivalent to a per-expert - # scalar on the down_proj output, which is exactly where ffn_down_exps_s is - # applied at inference. Fold it into each expert's NVFP4 weight_scale_2 so the - # existing NVFP4 path produces the right scales. - n_experts = self.find_hparam(["num_local_experts", "num_experts"], optional=True) or 0 - for name in [n for n in self.model_tensors if n.endswith(".router.per_expert_scale")]: - bid_match = re.search(r"\.layers\.(\d+)\.", name) - if bid_match is None: - continue - bid = bid_match.group(1) - prefix = name[: name.index(f".layers.{bid}.") + len(f".layers.{bid}.")] - w2_targets = [f"{prefix}experts.{e}.down_proj.weight_scale_2" for e in range(n_experts)] - present = [w2 in self.model_tensors for w2 in w2_targets] - if not any(present): - continue - assert all(present), f"layer {bid}: partial NVFP4 quantization across experts" - r = self.model_tensors.pop(name) - for e, w2 in enumerate(w2_targets): - s = self.model_tensors[w2] - self.model_tensors[w2] = lambda s=s, r=r, i=e: s() * r()[i] - super()._generate_nvfp4_tensors() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith("per_dim_scale") or name.endswith("layer_scalar"): - name = name + ".weight" - if ".experts." in name and not name.endswith((".weight", ".weight_scale", ".weight_scale_2", ".input_scale")): - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.endswith("router.scale"): - name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_INP, bid, ".scale") - yield (name, data_torch) - return - if ".per_expert_scale" in name: - # convert per-expert scale to FFN down scale - name = self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN_EXP, bid, ".scale") - yield (name, data_torch) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Gemma4ForConditionalGeneration") -class Gemma4VisionAudioModel(MmprojModel): - has_audio_encoder = True - has_vision_encoder = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["image_size"] = 224 # unused, but set to avoid error - - # remap audio hparams - if self.hparams_audio: - self.hparams_audio["feat_in"] = self.hparams_audio.get("input_feat_size", 128) - self.hparams_audio["intermediate_size"] = self.hparams_audio["hidden_size"] * 4 - else: - self.has_audio_encoder = False - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # vision params - self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA4V) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) - - # audio params - if self.hparams_audio: - self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.GEMMA4A) - self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) - self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) - - def is_audio_tensor(self, name: str) -> bool: - return "audio_tower" in name or "embed_audio" in name - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if self.is_audio_tensor(name): - if ".conv" in name or "_conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F32 - if "position_embedding_table" in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - del bid # unused - - if len(data_torch.shape) == 0: - # convert scalar tensors (input/output_mix/max) to 1D tensors - data_torch = data_torch.unsqueeze(0) - - if self.is_audio_tensor(name): - assert self.hparams_audio is not None - name = name.replace("model.audio_tower.", "conformer.") - name = name.replace(".linear.", ".") - if name.endswith("per_dim_key_scale") or name.endswith("per_dim_scale"): - name = name + ".weight" - data_torch = torch.nn.functional.softplus(data_torch) - if "lconv1d.depthwise_conv1d" in name and name.endswith(".weight"): - assert data_torch.shape[1] == 1 - data_torch = data_torch.reshape(data_torch.shape[0], data_torch.shape[2]) - mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) - yield (mapped_name, data_torch) - - else: - name = name.replace("model.vision_tower.encoder.", "vision_model.model.") - name = name.replace(".linear.weight", ".weight") - if name.endswith("layer_scalar") or name.endswith("position_embedding_table"): - name = name + ".weight" - if name.endswith("patch_embedder.input_proj.weight"): - n_embd, ksize_sq_c = data_torch.shape - patch_size = int((ksize_sq_c // 3) ** 0.5) - data_torch = data_torch.reshape(n_embd, patch_size, patch_size, 3) - data_torch = data_torch.permute(0, 3, 1, 2).contiguous() - mapped_name = self.map_tensor_name(name, (".weight", ".bias", ".input_max", ".input_min", ".output_max", ".output_min")) - yield (mapped_name, data_torch) - - -@ModelBase.register("Starcoder2ForCausalLM") -class StarCoder2Model(TextModel): - model_arch = gguf.MODEL_ARCH.STARCODER2 - - -@ModelBase.register("Rwkv6ForCausalLM") -class Rwkv6Model(TextModel): - model_arch = gguf.MODEL_ARCH.RWKV6 - - def set_vocab(self): - self._set_vocab_rwkv_world() - - def set_gguf_parameters(self): - head_size = self.hparams["head_size"] - hidden_size = self.hparams["hidden_size"] - layer_norm_eps = self.hparams["layer_norm_epsilon"] - rescale_every_n_layers = self.hparams["rescale_every"] - intermediate_size = self.hparams["intermediate_size"] if self.hparams["intermediate_size"] is not None else int((hidden_size * 3.5) // 32 * 32) - time_mix_extra_dim = 64 if hidden_size == 4096 else 32 - time_decay_extra_dim = 128 if hidden_size == 4096 else 64 - - # RWKV isn't context limited - self.gguf_writer.add_context_length(1048576) - self.gguf_writer.add_embedding_length(hidden_size) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_layer_norm_eps(layer_norm_eps) - self.gguf_writer.add_rescale_every_n_layers(rescale_every_n_layers) - self.gguf_writer.add_wkv_head_size(head_size) - self.gguf_writer.add_time_mix_extra_dim(time_mix_extra_dim) - self.gguf_writer.add_time_decay_extra_dim(time_decay_extra_dim) - self.gguf_writer.add_feed_forward_length(intermediate_size) - self.gguf_writer.add_file_type(self.ftype) - - # required by llama.cpp, unused - self.gguf_writer.add_head_count(0) - - lerp_weights: dict[int, dict[str, Tensor]] = {} - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - new_name = self.map_tensor_name(name) - - if not (new_name.endswith(".weight") or new_name.endswith(".bias")): - new_name += ".weight" - - if new_name.endswith("time_mix_w1.weight") or new_name.endswith("time_mix_decay_w1.weight") or new_name.endswith("time_mix_decay_w2.weight"): - data_torch = data_torch.transpose(0, 1) - - if new_name.endswith("time_mix_w2.weight"): - data_torch = data_torch.permute(0, 2, 1) - - if new_name.endswith("time_mix_decay.weight") or "lerp" in new_name: - data_torch = data_torch.squeeze() - - try: - rescale_every_n_layers = self.hparams["rescale_every"] - if rescale_every_n_layers > 0: - if new_name.endswith("time_mix_output.weight") or new_name.endswith("channel_mix_value.weight"): - data_torch = data_torch.div_(2 ** int(bid // rescale_every_n_layers)) - except KeyError: - pass - - # concat time_mix_lerp weights to reduce some cpu overhead - # also reduces the number of tensors in the model - if bid is not None and "time_mix_lerp" in new_name and "time_mix_lerp_x" not in new_name: - try: - self.lerp_weights[bid][new_name] = data_torch - except KeyError: - self.lerp_weights[bid] = {new_name: data_torch} - if all(f"blk.{bid}.time_mix_lerp_{i}.weight" in self.lerp_weights[bid].keys() for i in ["w", "k", "v", "r", "g"]): - new_name = f"blk.{bid}.time_mix_lerp_fused.weight" - data = torch.stack([self.lerp_weights[bid][f"blk.{bid}.time_mix_lerp_{i}.weight"].unsqueeze(0) for i in ["w", "k", "v", "r", "g"]], dim=0).unsqueeze(1) - yield (new_name, data) - return - - yield (new_name, data_torch) - - -@ModelBase.register("RWKV6Qwen2ForCausalLM") -class RWKV6Qwen2Model(Rwkv6Model): - model_arch = gguf.MODEL_ARCH.RWKV6QWEN2 - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - num_attention_heads = self.hparams["num_attention_heads"] - num_key_value_heads = self.hparams["num_key_value_heads"] - hidden_size = self.hparams["hidden_size"] - head_size = hidden_size // num_attention_heads - rms_norm_eps = self.hparams["rms_norm_eps"] - intermediate_size = self.hparams["intermediate_size"] - time_mix_extra_dim = self.hparams.get("lora_rank_tokenshift", 64 if hidden_size >= 4096 else 32) - time_decay_extra_dim = self.hparams.get("lora_rank_decay", 128 if hidden_size >= 4096 else 64) - - # RWKV isn't context limited - self.gguf_writer.add_context_length(1048576) - self.gguf_writer.add_embedding_length(hidden_size) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_wkv_head_size(head_size) - self.gguf_writer.add_time_mix_extra_dim(time_mix_extra_dim) - self.gguf_writer.add_time_decay_extra_dim(time_decay_extra_dim) - self.gguf_writer.add_feed_forward_length(intermediate_size) - self.gguf_writer.add_file_type(self.ftype) - - # special parameters for time_mixing in RWKV6QWEN2 - self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) - self.gguf_writer.add_token_shift_count(1) - # RWKV6QWEN2 use grouped key/value like GQA - self.gguf_writer.add_head_count_kv(num_key_value_heads) - - # required by llama.cpp, unused - self.gguf_writer.add_head_count(0) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - for new_name, data in super().modify_tensors(data_torch, name, bid): - if "time_mix_w1" in new_name or "time_mix_w2" in new_name: - data = data.view(5, -1, data.shape[-1]) - # rwkv6qwen2 has a different order of rkvwg instead of the original wkvrg - # permute them here to avoid code changes - data = torch.stack([data[3], data[1], data[2], data[0], data[4]], dim=0).view(-1, data.shape[-1]) - if "w2" in new_name: - data = data.view(5, -1, data.shape[-1]) - yield (new_name, data) - continue - yield (new_name, data) - - -@ModelBase.register("Rwkv7ForCausalLM", "RWKV7ForCausalLM") -class Rwkv7Model(TextModel): - model_arch = gguf.MODEL_ARCH.RWKV7 - - def set_vocab(self): - self._set_vocab_rwkv_world() - - def calc_lora_rank(self, hidden_size, exponent, multiplier): - return max(1, round(hidden_size ** exponent * multiplier / 32)) * 32 - - def set_gguf_parameters(self): - try: - head_size = self.hparams["head_size"] - layer_norm_eps = self.hparams["layer_norm_epsilon"] - except KeyError: - head_size = self.hparams["head_dim"] - layer_norm_eps = self.hparams["norm_eps"] - hidden_size = self.hparams["hidden_size"] - intermediate_size = self.hparams["intermediate_size"] if self.hparams["intermediate_size"] is not None else (hidden_size * 4) - - # ICLR: In-Context-Learning-Rate - try: - lora_rank_decay = self.hparams["lora_rank_decay"] if self.hparams["lora_rank_decay"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) - lora_rank_iclr = self.hparams["lora_rank_iclr"] if self.hparams["lora_rank_iclr"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) - lora_rank_value_residual_mix = self.hparams["lora_rank_value_residual_mix"] if self.hparams["lora_rank_value_residual_mix"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.3) - lora_rank_gate = self.hparams["lora_rank_gate"] if self.hparams["lora_rank_gate"] is not None else self.calc_lora_rank(hidden_size, 0.8, 0.6) - except KeyError: - lora_rank_decay = self.hparams["decay_low_rank_dim"] if self.hparams["decay_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) - lora_rank_iclr = self.hparams["a_low_rank_dim"] if self.hparams["a_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.8) - lora_rank_value_residual_mix = self.hparams["v_low_rank_dim"] if self.hparams["v_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.5, 1.3) - lora_rank_gate = self.hparams["gate_low_rank_dim"] if self.hparams["gate_low_rank_dim"] is not None else self.calc_lora_rank(hidden_size, 0.8, 0.6) - - # RWKV isn't context limited - self.gguf_writer.add_context_length(1048576) - self.gguf_writer.add_embedding_length(hidden_size) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_layer_norm_eps(layer_norm_eps) - self.gguf_writer.add_wkv_head_size(head_size) - self.gguf_writer.add_decay_lora_rank(lora_rank_decay) - self.gguf_writer.add_iclr_lora_rank(lora_rank_iclr) - self.gguf_writer.add_value_residual_mix_lora_rank(lora_rank_value_residual_mix) - self.gguf_writer.add_gate_lora_rank(lora_rank_gate) - self.gguf_writer.add_feed_forward_length(intermediate_size) - self.gguf_writer.add_file_type(self.ftype) - - # required by llama.cpp, unused - self.gguf_writer.add_head_count(0) - - lerp_weights: dict[int, dict[str, Tensor]] = {} - lora_needs_transpose: bool = True - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # unify tensor names here to make life easier - name = name.replace("blocks", "layers").replace("ffn", "feed_forward") - name = name.replace("self_attn", "attention").replace("attn", "attention") - name = name.replace("time_mixer.", "") - - name = name.replace("feed_forward_norm", "ln2") - name = name.replace("g_norm", "ln_x") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # lora layer names in fla-hub's impl - if "_lora.lora" in name: - self.lora_needs_transpose = False - name = name.replace("_lora.lora.0.weight", "1.weight") - name = name.replace("_lora.lora.2.weight", "2.weight") - name = name.replace("_lora.lora.2.bias", "0.weight") - - if "attention.v" in name and "value" not in self.map_tensor_name(name) and bid == 0: - # some models have dummy v0/v1/v2 on first layer while others don't - # ignore them all since they are not used - return - - wkv_has_gate = self.hparams.get("wkv_has_gate", True) - lerp_list = ["r", "w", "k", "v", "a", "g"] if wkv_has_gate else ["r", "w", "k", "v", "a"] - - if bid is not None and "attention.x_" in name: - if "attention.x_x" in name: - # already concatenated - new_name = f"blk.{bid}.time_mix_lerp_fused.weight" - data = data_torch.reshape(len(lerp_list), 1, 1, -1) - yield (new_name, data) - else: - try: - self.lerp_weights[bid][name] = data_torch - except KeyError: - self.lerp_weights[bid] = {name: data_torch} - if all(f"model.layers.{bid}.attention.x_{i}" in self.lerp_weights[bid].keys() for i in lerp_list): - new_name = f"blk.{bid}.time_mix_lerp_fused.weight" - data = torch.stack([self.lerp_weights[bid][f"model.layers.{bid}.attention.x_{i}"] for i in lerp_list], dim=0) - yield (new_name, data) - return - else: - data_torch = data_torch.squeeze() - new_name = self.map_tensor_name(name) - - if not (new_name.endswith(".weight") or new_name.endswith(".bias")): - new_name += ".weight" - - if self.lora_needs_transpose and any( - new_name.endswith(t) for t in [ - "time_mix_w1.weight", "time_mix_w2.weight", - "time_mix_a1.weight", "time_mix_a2.weight", - "time_mix_v1.weight", "time_mix_v2.weight", - "time_mix_g1.weight", "time_mix_g2.weight", - ] - ): - data_torch = data_torch.transpose(0, 1) - - if 'r_k' in new_name: - data_torch = data_torch.flatten() - - if bid == 0 and "time_mix_a" in new_name: - # dummy v0/v1/v2 on first layer - # easiest way to make llama happy - yield (new_name.replace("time_mix_a", "time_mix_v"), data_torch) - - yield (new_name, data_torch) - - -@ModelBase.register("RwkvHybridForCausalLM") -class ARwkv7Model(Rwkv7Model): - model_arch = gguf.MODEL_ARCH.ARWKV7 - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - hidden_size = self.hparams["hidden_size"] - head_size = self.hparams["head_size"] - rms_norm_eps = self.hparams["rms_norm_eps"] - intermediate_size = self.hparams["intermediate_size"] - wkv_has_gate = self.hparams["wkv_has_gate"] - assert self.hparams["wkv_version"] == 7 - - # ICLR: In-Context-Learning-Rate - lora_rank_decay = 64 - lora_rank_iclr = 64 - lora_rank_value_residual_mix = 32 - lora_rank_gate = 128 if wkv_has_gate else 0 - - # RWKV isn't context limited - self.gguf_writer.add_context_length(1048576) - self.gguf_writer.add_embedding_length(hidden_size) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) - self.gguf_writer.add_wkv_head_size(head_size) - self.gguf_writer.add_decay_lora_rank(lora_rank_decay) - self.gguf_writer.add_iclr_lora_rank(lora_rank_iclr) - self.gguf_writer.add_value_residual_mix_lora_rank(lora_rank_value_residual_mix) - self.gguf_writer.add_gate_lora_rank(lora_rank_gate) - self.gguf_writer.add_feed_forward_length(intermediate_size) - self.gguf_writer.add_file_type(self.ftype) - self.gguf_writer.add_token_shift_count(1) - - # required by llama.cpp, unused - self.gguf_writer.add_head_count(0) - - -@ModelBase.register("MaincoderForCausalLM") -class MaincoderModel(TextModel): - model_arch = gguf.MODEL_ARCH.MAINCODER - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - if (head_dim := self.hparams.get("head_dim")) is not None: - self.gguf_writer.add_rope_dimension_count(head_dim) - - -@ModelBase.register("MambaForCausalLM", "MambaLMHeadModel", "FalconMambaForCausalLM") -class MambaModel(TextModel): - model_arch = gguf.MODEL_ARCH.MAMBA - - def __init__(self, dir_model: Path, *args, **kwargs): - # Avoid using AutoConfig for hparams - hparams = kwargs.pop("hparams", None) - if hparams is None: - with open(dir_model / "config.json", "r", encoding="utf-8") as f: - hparams = json.load(f) - super().__init__(dir_model, *args, hparams=hparams, **kwargs) - - def set_vocab(self): - vocab_size = self.hparams["vocab_size"] - # Round vocab size to next multiple of 8 - pad_vocab = self.hparams.get("pad_vocab_size_multiple", 8) - # pad using ceiling division - # ref: https://stackoverflow.com/a/17511341/22827863 - vocab_size = -(vocab_size // -pad_vocab) * pad_vocab - self.hparams["vocab_size"] = vocab_size - - if (self.dir_model / "tokenizer.json").is_file(): - self._set_vocab_gpt2() - elif (self.dir_model / "tokenizer.model").is_file(): - self._set_vocab_sentencepiece() - else: - # Use the GPT-NeoX tokenizer when no tokenizer files are present - self._set_vocab_builtin("gpt-neox", vocab_size) - - def set_gguf_parameters(self): - d_model = self.find_hparam(["hidden_size", "d_model"]) - d_conv = self.find_hparam(["conv_kernel", "d_conv"], optional=True) or 4 - d_inner = self.find_hparam(["intermediate_size", "d_inner"], optional=True) or 2 * d_model - d_state = self.find_hparam(["state_size", "d_state"], optional=True) or 16 - # ceiling division - # ref: https://stackoverflow.com/a/17511341/22827863 - # ref: https://github.com/state-spaces/mamba/blob/ce59daea3a090d011d6476c6e5b97f6d58ddad8b/mamba_ssm/modules/mamba_simple.py#L58 - dt_rank = self.find_hparam(["time_step_rank", "dt_rank"], optional=True) or -(d_model // -16) - rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-5 - use_dt_b_c_norm = False - # For falconmamba we do apply RMS norm on B / DT and C layers - if self.find_hparam(["model_type"], optional=True) in ("falcon_mamba",): - use_dt_b_c_norm = True - # Fail early for models which don't have a block expansion factor of 2 - assert d_inner == 2 * d_model - - self.gguf_writer.add_context_length(2**20) # arbitrary value; for those who use the default - self.gguf_writer.add_embedding_length(d_model) - self.gguf_writer.add_feed_forward_length(0) # unused, but seemingly required when loading - self.gguf_writer.add_head_count(0) # unused, but seemingly required when loading - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_ssm_conv_kernel(d_conv) - self.gguf_writer.add_ssm_inner_size(d_inner) - self.gguf_writer.add_ssm_state_size(d_state) - self.gguf_writer.add_ssm_time_step_rank(dt_rank) - self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) - self.gguf_writer.add_ssm_dt_b_c_rms(use_dt_b_c_norm) # For classic Mamba we don't apply rms norm on B / DT layers - self.gguf_writer.add_file_type(self.ftype) - - _tok_embd = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - output_name = self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT) - tok_embd_name = self.format_tensor_name(gguf.MODEL_TENSOR.TOKEN_EMBD) - - new_name = self.map_tensor_name(name) - - if name.endswith(".A_log"): - logger.debug("A_log --> A ==> " + new_name) - data_torch = -torch.exp(data_torch) - - # [4 1 8192 1] -> [4 8192 1 1] - if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): - data_torch = data_torch.squeeze() - - # assuming token_embd.weight is seen before output.weight - if self._tok_embd is not None and new_name == output_name: - if torch.equal(self._tok_embd, data_torch): - logger.debug(f"{output_name} is equivalent to {tok_embd_name}, omitting") - return - elif new_name == tok_embd_name: - self._tok_embd = data_torch - - yield from super().modify_tensors(data_torch, new_name, bid) - - -@ModelBase.register("Mamba2ForCausalLM") -class Mamba2Model(TextModel): - model_arch = gguf.MODEL_ARCH.MAMBA2 - - def __init__(self, dir_model: Path, *args, **kwargs): - # Avoid using AutoConfig for hparams - # It wrongly assumes all Mamba2 models are Mamba-Codestral-7B-v0.1 - hparams = kwargs.pop("hparams", None) - if hparams is None: - with open(dir_model / "config.json", "r", encoding="utf-8") as f: - hparams = json.load(f) - if "llm_config" in hparams: - hparams["text_config"] = hparams["llm_config"] - super().__init__(dir_model, *args, hparams=hparams, **kwargs) - self.d_model = self.find_hparam(["hidden_size", "d_model", "dim"]) - self.d_inner = self.find_hparam(["mamba_d_ssm", "intermediate_size", "d_inner"], optional=True) or 2 * self.d_model - self.n_group = self.find_hparam(["n_groups"], optional=True) or 1 - - def set_vocab(self): - vocab_size = self.hparams["vocab_size"] - # Round vocab size to next multiple of 16 - pad_vocab = self.hparams.get("pad_vocab_size_multiple", 16) - # pad using ceiling division - # ref: https://stackoverflow.com/a/17511341/22827863 - vocab_size = -(vocab_size // -pad_vocab) * pad_vocab - self.hparams["vocab_size"] = vocab_size - - if (self.dir_model / "tokenizer.model").is_file(): - self._set_vocab_sentencepiece() - elif (self.dir_model / "tokenizer.model.v3").is_file(): - # mamba-codestral - raise NotImplementedError(f"Please rename {self.dir_model / 'tokenizer.model.v3'} to {self.dir_model / 'tokenizer.model'}") - elif (self.dir_model / "tokenizer.json").is_file(): - self._set_vocab_gpt2() - else: - # Use the GPT-NeoX tokenizer when no tokenizer files are present - self._set_vocab_builtin("gpt-neox", vocab_size) - - def set_gguf_parameters(self): - d_conv = self.find_hparam(["conv_kernel", "d_conv"], optional=True) or 4 - d_state = self.find_hparam(["state_size", "d_state"], optional=True) or 128 - head_dim = self.find_hparam(["mamba_d_head", "head_dim"], optional=True) or 64 - - rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-5 - - # Fail early for models which don't have a block expansion factor of 2 - # TODO: does this really matter? - # skip the assertion for FalconH1 Model - if self.model_arch != gguf.MODEL_ARCH.FALCON_H1: - assert self.d_inner == 2 * self.d_model - assert self.d_inner % head_dim == 0 - - self.gguf_writer.add_context_length(2**20) # arbitrary value; for those who use the default - self.gguf_writer.add_embedding_length(self.d_model) - self.gguf_writer.add_feed_forward_length(0) # unused, but seemingly required when loading - self.gguf_writer.add_head_count(0) # unused, but seemingly required when loading - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_ssm_conv_kernel(d_conv) - self.gguf_writer.add_ssm_inner_size(self.d_inner) - self.gguf_writer.add_ssm_state_size(d_state) - self.gguf_writer.add_ssm_time_step_rank(self.d_inner // head_dim) - self.gguf_writer.add_ssm_group_count(self.n_group) - self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) - self.gguf_writer.add_file_type(self.ftype) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith(("model.backbone", "model.lm_head")): - # map Mamba-Codestral-7B-v0.1 tensor names to the names used by Mamba-2 - name = name.removeprefix("model.") - - if name.endswith(".dt_bias"): - name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - new_name = self.map_tensor_name(name) - - if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): - data_torch = data_torch.squeeze() - elif any(self.match_model_tensor_name(new_name, t, bid, suffix="") for t in [ - gguf.MODEL_TENSOR.SSM_A, - gguf.MODEL_TENSOR.SSM_D, - ]): - # unsqueeze A to use similar shape semantics as Mamba-1 - # (D is also unsqueezed, but for more straightforward broadcast internally) - data_torch = data_torch.reshape((*data_torch.shape, 1)) - elif self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_NORM, bid): - data_torch = data_torch.reshape((self.n_group, self.d_inner // self.n_group)) - - if name.endswith(".A_log"): - logger.debug("A_log --> A ==> " + new_name) - data_torch = -torch.exp(data_torch) - - yield (new_name, data_torch) - - -@ModelBase.register("JambaForCausalLM") -class JambaModel(TextModel): - model_arch = gguf.MODEL_ARCH.JAMBA - - def set_vocab(self): - if (self.dir_model / "tokenizer.model").is_file(): - self._set_vocab_sentencepiece() - else: - self._set_vocab_llama_hf() - self.gguf_writer.add_add_space_prefix(False) - - def set_gguf_parameters(self): - d_model = self.find_hparam(["hidden_size", "mamba_d_model"]) - d_conv = self.find_hparam(["mamba_d_conv"], optional=True) or 4 - d_inner = self.hparams["mamba_expand"] * d_model - d_state = self.find_hparam(["mamba_d_state"], optional=True) or 16 - # ceiling division - # ref: https://stackoverflow.com/a/17511341/22827863 - # ref: https://github.com/state-spaces/mamba/blob/ce59daea3a090d011d6476c6e5b97f6d58ddad8b/mamba_ssm/modules/mamba_simple.py#L58 - dt_rank = self.find_hparam(["mamba_dt_rank"], optional=True) or -(d_model // -16) - rms_norm_eps = self.find_hparam(["layer_norm_epsilon", "rms_norm_eps"], optional=True) or 1e-6 - n_kv_head = self.hparams["num_key_value_heads"] - attn_offset = self.hparams["attn_layer_offset"] - attn_period = self.hparams["attn_layer_period"] - n_kv_vec = [0 for _ in range(attn_offset)] + [ - n_kv_head if (i - attn_offset) % attn_period == 0 else 0 for i in range(attn_offset, self.block_count) - ] - - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.find_hparam(["max_position_embeddings", "n_ctx"])) - self.gguf_writer.add_embedding_length(d_model) - self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) - self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) - self.gguf_writer.add_head_count_kv(n_kv_vec) - self.gguf_writer.add_ssm_conv_kernel(d_conv) - self.gguf_writer.add_ssm_inner_size(d_inner) - self.gguf_writer.add_ssm_state_size(d_state) - self.gguf_writer.add_ssm_time_step_rank(dt_rank) - self.gguf_writer.add_layer_norm_rms_eps(rms_norm_eps) - self.gguf_writer.add_expert_count(self.find_hparam(["num_local_experts", "num_experts"])) - self.gguf_writer.add_expert_used_count(self.find_hparam(["num_experts_per_tok", "num_experts_per_token"])) - self.gguf_writer.add_file_type(self.ftype) - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - - # Mini-Jamba - name = name.replace(".moe.", ".feed_forward.") - if bid is not None: - moe_offset = self.hparams["expert_layer_offset"] - moe_period = self.hparams["expert_layer_period"] - - if not (bid >= moe_offset and (bid - moe_offset) % moe_period == 0): - name = name.replace(".experts.0.", ".") - - # process the experts separately - if ".feed_forward.experts." in name: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - - # merge the experts into a single 3d tensor - for wid in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.feed_forward.experts.{xid}.{wid}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - # using the same merged name as qwen2moe - merged_name = f"model.layers.{bid}.mlp.experts.{wid}.weight" - - new_name = self.map_tensor_name(merged_name) - - yield new_name, data_torch - return - - new_name = self.map_tensor_name(name) - - if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.SSM_CONV1D, bid): - data_torch = data_torch.squeeze() - - if name.endswith(".A_log"): - logger.debug("A_log --> A ==> " + new_name) - data_torch = -torch.exp(data_torch) - - yield (new_name, data_torch) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("CohereForCausalLM") -class CommandR2Model(TextModel): - model_arch = gguf.MODEL_ARCH.COMMAND_R - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # max_position_embeddings = 8192 in config.json but model was actually - # trained on 128k context length - # aya-23 models don't have model_max_length specified - self.hparams["max_position_embeddings"] = self.find_hparam(["model_max_length", "max_position_embeddings"]) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_logit_scale(self.hparams["logit_scale"]) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - - -@ModelBase.register("Cohere2ForCausalLM") -class Cohere2Model(TextModel): - model_arch = gguf.MODEL_ARCH.COHERE2 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_logit_scale(self.hparams["logit_scale"]) - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - - rotary_pct = self.hparams["rotary_pct"] - hidden_size = self.hparams["hidden_size"] - num_attention_heads = self.hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(int(rotary_pct * (hidden_size // num_attention_heads))) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Cohere2 runtime in llama.cpp expects no bias tensors; - # the actual weight only contains 0-value tensors as bias, we can skip them - if name.endswith(".bias"): - if torch.any(data_torch != 0): - raise ValueError(f"Bias tensor {name!r} is not zero.") - logger.debug(f"Skipping bias tensor {name!r} for Cohere2 conversion.") - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("OlmoForCausalLM") -@ModelBase.register("OLMoForCausalLM") -class OlmoModel(TextModel): - model_arch = gguf.MODEL_ARCH.OLMO - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_layer_norm_eps(1e-5) - clip_qkv = self.hparams.get("clip_qkv") - if clip_qkv is not None: - self.gguf_writer.add_clamp_kqv(clip_qkv) - - # Same as super class, but permuting q_proj, k_proj - # Copied from: LlamaModel - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - - if name.endswith("q_proj.weight"): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith("k_proj.weight"): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("SeedOssForCausalLM") -class SeedOssModel(TextModel): - model_arch = gguf.MODEL_ARCH.SEED_OSS - - -@ModelBase.register("Olmo2ForCausalLM") -@ModelBase.register("Olmo3ForCausalLM") -class Olmo2Model(TextModel): - model_arch = gguf.MODEL_ARCH.OLMO2 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - if "sliding_window" in self.hparams: - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - - sliding_window_pattern = [] - if "layer_types" in self.hparams: - sliding_window_pattern = [t == "sliding_attention" for t in self.hparams["layer_types"]] - else: - # Olmo2 does not use sliding window attention. - # Olmo3 defaults to using sliding window for all layers except every 4th. - for i in range(self.hparams["num_hidden_layers"]): - sliding_window_pattern.append((i + 1) % 4 != 0) - - self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) - - -@ModelBase.register("OlmoeForCausalLM") -class OlmoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.OLMOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_layer_norm_rms_eps(1e-5) - - _experts: list[dict[str, Tensor]] | None = None - - # Copied from: Qwen2MoeModel - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # process the experts separately - if name.find("experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - # Copied from: Qwen2MoeModel - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("JinaBertModel", "JinaBertForMaskedLM") -class JinaBertV2Model(BertModel): - model_arch = gguf.MODEL_ARCH.JINA_BERT_V2 - - def set_vocab(self): - tokenizer_class = 'BertTokenizer' - with open(self.dir_model / "tokenizer_config.json", "r", encoding="utf-8") as f: - tokenizer_class = json.load(f)['tokenizer_class'] - - if tokenizer_class == 'BertTokenizer': - super().set_vocab() - elif tokenizer_class == 'RobertaTokenizer': - self._set_vocab_gpt2() - self.gguf_writer.add_token_type_count(2) - else: - raise NotImplementedError(f'Tokenizer {tokenizer_class} is not supported for JinaBertModel') - - -@ModelBase.register("OpenELMForCausalLM") -class OpenELMModel(TextModel): - model_arch = gguf.MODEL_ARCH.OPENELM - - @staticmethod - def _make_divisible(v: float | int, divisor: int) -> int: - # ref: https://huggingface.co/apple/OpenELM-270M-Instruct/blob/eb111ff2e6724348e5b905984063d4064d4bc579/configuration_openelm.py#L34-L38 - new_v = max(divisor, int(v + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than 10%. - if new_v < 0.9 * v: - new_v += divisor - return new_v - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - ffn_multipliers: list[float] = self.hparams["ffn_multipliers"] - ffn_dim_divisor: int = self.hparams["ffn_dim_divisor"] - self._n_embd: int = self.hparams["model_dim"] - self._num_kv_heads: list[int] = self.hparams["num_kv_heads"] - self._num_query_heads: list[int] = self.hparams["num_query_heads"] - self._ffn_dims: list[int] = [ - OpenELMModel._make_divisible(multiplier * self._n_embd, ffn_dim_divisor) - for multiplier in ffn_multipliers - ] - assert isinstance(self._num_kv_heads, list) and isinstance(self._num_kv_heads[0], int) - assert isinstance(self._num_query_heads, list) and isinstance(self._num_query_heads[0], int) - - # Uses the tokenizer from meta-llama/Llama-2-7b-hf - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_builtin("llama-spm", self.hparams["vocab_size"]) - - def set_gguf_parameters(self): - n_embd = self._n_embd - head_dim = self.hparams["head_dim"] - rot_pct = 1.0 - assert self.block_count == len(self._num_kv_heads) - assert self.block_count == len(self._num_query_heads) - assert self.block_count == len(self._ffn_dims) - - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.hparams["max_context_length"]) - self.gguf_writer.add_embedding_length(n_embd) - self.gguf_writer.add_feed_forward_length(self._ffn_dims) - self.gguf_writer.add_head_count(self._num_query_heads) - self.gguf_writer.add_head_count_kv(self._num_kv_heads) - self.gguf_writer.add_rope_freq_base(self.hparams["rope_freq_constant"]) - # https://huggingface.co/apple/OpenELM-270M-Instruct/blob/c401df2/modeling_openelm.py#L30 - self.gguf_writer.add_layer_norm_rms_eps(1e-6) - self.gguf_writer.add_rope_dimension_count(int(rot_pct * head_dim)) - self.gguf_writer.add_key_length(head_dim) - self.gguf_writer.add_value_length(head_dim) - self.gguf_writer.add_file_type(self.ftype) - - def find_hparam(self, keys: Iterable[str], optional: bool = False) -> Any: - if "n_layers" in keys: - return self.hparams["num_transformer_layers"] - - return super().find_hparam(keys, optional) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - - # split ff - if bid is not None and name == f"transformer.layers.{bid}.ffn.proj_1.weight": - ff_dim = self._ffn_dims[bid] - yield (self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), data_torch[:ff_dim]) - yield (self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), data_torch[ff_dim:]) - return - - yield (self.map_tensor_name(name), data_torch) - - -@ModelBase.register("ArcticForCausalLM") -class ArcticModel(TextModel): - model_arch = gguf.MODEL_ARCH.ARCTIC - - def set_vocab(self): - # The reason for using a custom implementation here is that the - # snowflake-arctic-instruct model redefined tokens 31998 and 31999 from - # tokenizer.model and used them as BOS and EOS instead of adding new tokens. - from sentencepiece import SentencePieceProcessor - - tokenizer_path = self.dir_model / 'tokenizer.model' - - if not tokenizer_path.is_file(): - logger.error(f'Error: Missing {tokenizer_path}') - sys.exit(1) - - # Read the whole vocabulary from the tokenizer.model file - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - for token_id in range(tokenizer.vocab_size()): - - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - # Use the added_tokens_decoder field from tokeniser_config.json as the source - # of information about added/redefined tokens and modify them accordingly. - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - - if "added_tokens_decoder" in tokenizer_config_json: - added_tokens_decoder = tokenizer_config_json["added_tokens_decoder"] - for token_id, token_json in added_tokens_decoder.items(): - token_id = int(token_id) - if token_id >= vocab_size: - logger.debug(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - - token_content = token_json["content"] - token_type = SentencePieceTokenTypes.USER_DEFINED - token_score = -10000.0 - - # Map unk_token to UNKNOWN, other special tokens to CONTROL - # Set the score to 0.0 as in the original tokenizer.model - if ("special" in token_json) and token_json["special"]: - if token_content == tokenizer_config_json["unk_token"]: - token_type = SentencePieceTokenTypes.UNKNOWN - else: - token_type = SentencePieceTokenTypes.CONTROL - token_score = 0.0 - - logger.info(f"Setting added token {token_id} to '{token_content}' (type: {token_type}, score: {token_score:.2f})") - tokens[token_id] = token_content.encode("utf-8") - toktypes[token_id] = token_type - scores[token_id] = token_score - - self.gguf_writer.add_tokenizer_model("llama") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - self.gguf_writer.add_rope_dimension_count(hparams["hidden_size"] // hparams["num_attention_heads"]) - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - - if name.endswith("q_proj.weight"): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith("k_proj.weight"): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - - # process the experts separately - if name.find("block_sparse_moe.experts") != -1: - n_experts = self.hparams["num_local_experts"] - - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for wid in ["w1", "w2", "w3"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"layers.{bid}.feed_forward.experts.{wid}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("DeepseekForCausalLM") -class DeepseekModel(TextModel): - model_arch = gguf.MODEL_ARCH.DEEPSEEK - - def set_vocab(self): - try: - self._set_vocab_sentencepiece() - except FileNotFoundError: - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - - self.gguf_writer.add_rope_dimension_count(rope_dim) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) - self.gguf_writer.add_expert_weights_scale(1.0) - self.gguf_writer.add_expert_count(hparams["n_routed_experts"]) - self.gguf_writer.add_expert_shared_count(hparams["n_shared_experts"]) - - _experts: list[dict[str, Tensor]] | None = None - - @staticmethod - def permute(weights: Tensor, n_head: int, n_head_kv: int | None): - if n_head_kv is not None and n_head != n_head_kv: - n_head = n_head_kv - return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = DeepseekModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = DeepseekModel.permute(data_torch, n_head, n_kv_head) - - # process the experts separately - if name.find("mlp.experts") != -1: - n_experts = self.hparams["n_routed_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register( - "DeepseekV2ForCausalLM", - "DeepseekV3ForCausalLM", - "KimiVLForConditionalGeneration", - "KimiK25ForConditionalGeneration", - "YoutuForCausalLM", - "YoutuVLForConditionalGeneration", -) -class DeepseekV2Model(TextModel): - model_arch = gguf.MODEL_ARCH.DEEPSEEK2 - - # TODO @ngxson : remove this when we support MTP for deepseek models - skip_mtp = True - - merge_expert = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - hparams: dict = ModelBase.load_hparams(self.dir_model, is_mistral_format=False) - self.origin_hf_arch = hparams.get('architectures', [None])[0] - - # special handling for Deepseek OCR - if self.origin_hf_arch == "DeepseekOCRForCausalLM": - self.model_arch = gguf.MODEL_ARCH.DEEPSEEK2OCR - self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[self.model_arch] - self.gguf_writer.add_architecture() - # default jinja template - self.gguf_writer.add_chat_template("{% for m in messages %}{{m['content']}}{% endfor %}") - - def set_vocab(self): - try: - self._set_vocab_gpt2() - return - except Exception: - pass - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - tokpre = self.get_vocab_base_pre(tokenizer) - - if tokpre == "kimi-k2": - # Build merges list using the approach similar to HunYuanMoE - merges = [] - vocab = {} - mergeable_ranks = tokenizer.model._mergeable_ranks # ty: ignore[unresolved-attribute] - for token, rank in mergeable_ranks.items(): - vocab[QwenModel.token_bytes_to_string(token)] = rank - if len(token) == 1: - continue - merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) - if len(merged) == 2: - merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) - - # Build token list - vocab_size = self.hparams["vocab_size"] - special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] - reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} - tokens: list[str] = [] - toktypes: list[int] = [] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token = reverse_vocab[i] - tokens.append(token) - if i in special_tokens.values(): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.NORMAL) - - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_token_merges(merges) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) - special_vocab.add_to_gguf(self.gguf_writer) - else: - raise NotImplementedError(f"Deepseek pre-tokenizer {tokpre!r} is not supported yet!") - - def set_gguf_parameters(self): - is_ocr = (self.model_arch == gguf.MODEL_ARCH.DEEPSEEK2OCR) - - if is_ocr: - self.hparams['rope_theta'] = self.hparams.get('rope_theta', 10000.0) - else: - # note: deepseek2 using MLA converts into MQA (ie: GQA with 1 group) - self.hparams["num_key_value_heads"] = 1 - - self.hparams['rms_norm_eps'] = self.hparams.get('rms_norm_eps', 1e-6) - - super().set_gguf_parameters() - hparams = self.hparams - - # first_k_dense_replace: number of leading layers using dense FFN instead of MoE - # For non-MoE models (like Youtu), set to n_layer to use dense FFN for all layers - # For MoE models (like DeepSeek-V2), this is the number of leading non-MoE layers - has_moe = hparams.get("n_routed_experts") is not None - first_k_dense_replace = hparams.get("first_k_dense_replace") - if first_k_dense_replace is None: - # Default: if no MoE, all layers are dense; if MoE, none are dense - first_k_dense_replace = hparams["num_hidden_layers"] if not has_moe else 0 - self.gguf_writer.add_leading_dense_block_count(first_k_dense_replace) - kv_lora_rank = hparams.get("kv_lora_rank", 512) - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - if "q_lora_rank" in hparams and hparams["q_lora_rank"] is not None: - self.gguf_writer.add_q_lora_rank(hparams["q_lora_rank"]) - - # note: deepseek2 using MLA converts into MQA with larger heads, then decompresses to MHA - if not is_ocr: - self.gguf_writer.add_kv_lora_rank(kv_lora_rank) - self.gguf_writer.add_key_length(kv_lora_rank + hparams["qk_rope_head_dim"]) - self.gguf_writer.add_value_length(kv_lora_rank) - self.gguf_writer.add_key_length_mla(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) - self.gguf_writer.add_value_length_mla(hparams["v_head_dim"]) - - # MoE parameters (required by C++ code for DEEPSEEK2 arch) - # For non-MoE models like Youtu, use intermediate_size as expert_feed_forward_length - moe_intermediate_size = self.find_hparam(["moe_intermediate_size", "intermediate_size"], optional=False) - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - - if (n_routed_experts := hparams.get("n_routed_experts")) is not None: - self.gguf_writer.add_expert_count(n_routed_experts) - - # expert_shared_count is required by C++ code, default to 0 for non-MoE models - n_shared_experts = hparams.get("n_shared_experts", 0) - self.gguf_writer.add_expert_shared_count(n_shared_experts) - - # When not set, C++ code will use scale_w = false to skip the no-op scaling - if (routed_scaling_factor := hparams.get("routed_scaling_factor")) is not None: - self.gguf_writer.add_expert_weights_scale(routed_scaling_factor) - - if (norm_topk_prob := hparams.get("norm_topk_prob")) is not None and norm_topk_prob: - self.gguf_writer.add_expert_weights_norm(norm_topk_prob) - - self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) - - if (rope_mscale_all := self.rope_parameters.get("mscale_all_dim")) is not None: - # [TAG_DEEPSEEK2_YARN_LOG_MUL_FIX] - # note: for legacy reasons, this is not consistent with the other usages of self.gguf_writer.add_rope_scaling_yarn_log_mul - # ref https://github.com/ggml-org/llama.cpp/pull/17945 - self.gguf_writer.add_rope_scaling_yarn_log_mul(0.1 * rope_mscale_all) - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # skip lm_head.weight if tie_word_embeddings is True - if self.hparams.get("tie_word_embeddings", False): - if name == "lm_head.weight" or name == "model.lm_head.weight": - logger.info("Skipping tied output layer 'lm_head.weight' (will use token_embd.weight)") - return - - # skip Multi-Token Prediction (MTP) layers - if self.skip_mtp: - block_count = self.hparams["num_hidden_layers"] - match = re.match(r"model.layers.(\d+)", name) - if match and int(match.group(1)) >= block_count: - return - - # process the experts separately - if self.merge_expert and name.find("mlp.experts") != -1: - n_experts = self.hparams["n_routed_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - # note: MLA with the absorption optimization, needs these two split and k_b_proj transposed - if name.endswith("kv_b_proj.weight"): - name_kb = name.replace("kv_b_proj", "k_b_proj") - name_vb = name.replace("kv_b_proj", "v_b_proj") - - n_head_kv = self.hparams["num_key_value_heads"] - v_head_dim = self.hparams["v_head_dim"] - qk_nope_head_dim = self.hparams["qk_nope_head_dim"] - - assert data_torch.shape[0] == n_head_kv * (v_head_dim + qk_nope_head_dim) - - kv_b = data_torch.view(n_head_kv, v_head_dim + qk_nope_head_dim, data_torch.shape[-1]) - k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1) - k_b = k_b.transpose(1, 2) - - yield from super().modify_tensors(k_b, name_kb, bid) - yield from super().modify_tensors(v_b, name_vb, bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register( - "Mistral3ForConditionalGeneration", - "Ministral3ForCausalLM", -) -class Mistral3Model(TextModel): - class Ministral3Model(LlamaModel): - model_arch = gguf.MODEL_ARCH.MISTRAL3 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - rope_params = self.rope_parameters - if self.hparams.get("model_type") == "ministral3": - assert rope_params, "ministral3 must have 'rope_parameters' config" - assert rope_params["rope_type"] == "yarn", "ministral3 rope_type must be 'yarn'" - self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"]) - self.gguf_writer.add_attn_temperature_scale(rope_params["llama_4_scaling_beta"]) - - class Mistral4Model(DeepseekV2Model): - model_arch = gguf.MODEL_ARCH.MISTRAL4 - skip_mtp = False # model contains no MTP layers, so no need to skip - merge_expert = False # experts are already stacked as 3D - - def modify_tensors(self, data_torch, name, bid): - if name.endswith(".down_proj") or name.endswith(".gate_up_proj"): - name = name + ".weight" - yield from super().modify_tensors(data_torch, name, bid) - - model_arch = gguf.MODEL_ARCH.MISTRAL3 # unused - impl: TextModel - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.hparams.get("model_type") == "mistral4": - self.impl = Mistral3Model.Mistral4Model(*args, **kwargs) - else: - self.impl = Mistral3Model.Ministral3Model(*args, **kwargs) - - def set_vocab(self): - self.impl.set_vocab() - - def set_gguf_parameters(self): - self.impl.set_gguf_parameters() - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): - yield from self.impl.modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - self.impl.prepare_tensors() - - def write_vocab(self): - self.impl.write_vocab() - - def write(self): - self.impl.write() - - -@ModelBase.register("MiniMaxM2ForCausalLM") -class MiniMaxM2Model(TextModel): - model_arch = gguf.MODEL_ARCH.MINIMAXM2 - _experts_cache: dict[int, dict[str, Tensor]] = {} - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_expert_feed_forward_length(self.find_hparam(["intermediate_size"])) - self.gguf_writer.add_rope_dimension_count(self.find_hparam(["rotary_dim"])) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): - # merge expert weights - if 'experts' in name: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - expert_cache = self._experts_cache.setdefault(bid, {}) - expert_cache[name] = data_torch - expert_weights = ["w1", "w2", "w3"] - - # not enough expert weights to merge - if len(expert_cache) < n_experts * len(expert_weights): - return - - for w_name in expert_weights: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" - datas.append(expert_cache[ename]) - del expert_cache[ename] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" - new_name = self.map_tensor_name(merged_name) - yield from super().modify_tensors(data_torch, new_name, bid) - - del self._experts_cache[bid] - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("MiMoV2FlashForCausalLM", "MiMoV2ForCausalLM") -class MimoV2Model(TextModel): - model_arch = gguf.MODEL_ARCH.MIMO2 - - # MiMo V2-Flash, V2.5 and V2.5-Pro all ship 3 trained MTP layers under model.mtp.layers.{0,1,2}. - # The HF config does not expose the count, so it's hardcoded to match the count found in the safetensors. - _n_nextn = 3 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.block_count = self.hparams["num_hidden_layers"] + self._n_nextn - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - @staticmethod - def _tp_aware_qkv_dequant(weight: Tensor, scale_inv: Tensor, - n_q: int, n_kv: int, hd: int, vhd: int, - bs: int = 128) -> Tensor: - # MiMo-V2.5 (TP=4) and V2.5-Pro (TP=8) ship qkv_proj sharded across TP - # ranks; per rank, rows are stacked as [Q_per | K_per | V_per]. - # weight_scale_inv has ceil(rows_per_rank/bs) block-rows per rank (last - # may extend past rows_per_rank with phantom rows not in the weight). - # Naive repeat_interleave aligns rank 0 only and mis-applies scales to - # later ranks once rows_per_rank isn't a multiple of bs. - # Re-group the per-rank [Q_per|K_per|V_per] rows into a single fused - # [Q | K | V] tensor matching the un-sharded original layout. - q_size = n_q * hd - k_size = n_kv * hd - v_size = n_kv * vhd - total_rows = q_size + k_size + v_size - if weight.shape[0] != total_rows: - raise ValueError(f"qkv_proj weight rows {weight.shape[0]} != q+k+v {total_rows}") - - # detect TP from scale_inv block count, descending order so larger matches first - tp = None - for cand in (8, 4): - if total_rows % cand != 0: - continue - rpr = total_rows // cand - bpr = (rpr + bs - 1) // bs - if scale_inv.shape[0] == cand * bpr: - tp = cand - break - if tp is None: - raise ValueError( - f"qkv_proj: cannot detect TP - scale_inv rows {scale_inv.shape[0]}, " - f"q+k+v {total_rows}") - - q_per = q_size // tp - k_per = k_size // tp - v_per = v_size // tp - rows_per_rank = q_per + k_per + v_per - blocks_per_rank = (rows_per_rank + bs - 1) // bs - - scale_inv = scale_inv.float() - # per-row scale-row index: rank * blocks_per_rank + (rr_in_rank // bs) - row_idx = torch.arange(total_rows) - rr = row_idx % rows_per_rank - rank = row_idx // rows_per_rank - scale_row_idx = rank * blocks_per_rank + (rr // bs) - # gather: (total_rows, n_col_blocks) - scale_per_row_block = scale_inv[scale_row_idx] - # expand col-blocks -> cols: each block-col covers `bs` weight cols - scale_full = scale_per_row_block.repeat_interleave(bs, dim=1) - # crop to weight col count (in case last col-block isn't full) - scale_full = scale_full[:, : weight.shape[1]] - dequant = weight.float() * scale_full - - if tp == 1: - return dequant - - # Re-group per-rank [Q_per|K_per|V_per] rows into unified [Q | K | V] - qs, ks, vs = [], [], [] - for r in range(tp): - base = r * rows_per_rank - qs.append(dequant[base : base + q_per]) - ks.append(dequant[base + q_per : base + q_per + k_per]) - vs.append(dequant[base + q_per + k_per : base + rows_per_rank]) - return torch.cat(qs + ks + vs, dim=0) - - def dequant_model(self): - # Capture raw FP8 (weight, scale_inv) lambdas for qkv_proj BEFORE super - # rewrites them with the existing dequant. Replace super's lambda after - # it runs so scale_inv removal still happens via the standard path. - qkv_overrides: dict[str, tuple[Callable, Callable, int]] = {} - qc = self.hparams.get("quantization_config") - if isinstance(qc, dict) and qc.get("quant_method") == "fp8": - pat = re.compile(r"^model\.layers\.(\d+)\.self_attn\.qkv_proj\.weight_scale_inv$") - for name in list(self.model_tensors.keys()): - m = pat.match(name) - if not m: - continue - weight_name = name.removesuffix("_scale_inv") - if weight_name not in self.model_tensors: - continue - qkv_overrides[weight_name] = ( - self.model_tensors[weight_name], - self.model_tensors[name], - int(m.group(1)), - ) - - super().dequant_model() - - if not qkv_overrides: - return - - n_q = self.hparams["num_attention_heads"] - hd = self.hparams["head_dim"] - vhd = self.hparams["v_head_dim"] - hybrid = self.hparams["hybrid_layer_pattern"] - n_layer_text = self.hparams["num_hidden_layers"] - for weight_name, (w_fn, s_fn, bid) in qkv_overrides.items(): - # MTP layers (bid >= n_layer_text) use SWA-style attention dims - is_swa = True if bid >= n_layer_text else hybrid[bid] == 1 - n_kv = self.hparams["swa_num_key_value_heads" if is_swa else "num_key_value_heads"] - self.model_tensors[weight_name] = ( - lambda w_fn=w_fn, s_fn=s_fn, n_q=n_q, n_kv=n_kv, hd=hd, vhd=vhd: - MimoV2Model._tp_aware_qkv_dequant(w_fn(), s_fn(), n_q, n_kv, hd, vhd) - ) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - assert self.hparams["swa_head_dim"] == self.hparams["head_dim"] - assert self.hparams["swa_num_attention_heads"] == self.hparams["num_attention_heads"] - assert self.hparams["swa_v_head_dim"] == self.hparams["v_head_dim"] - assert self.hparams["topk_method"] == "noaux_tc" - - n_head_kv = self.hparams["num_key_value_heads"] - n_head_kv_swa = self.hparams["swa_num_key_value_heads"] - # Extend the per-layer pattern with SWA entries for the MTP blocks so the - # runtime arrays (sized to extended block_count) are fully populated. - hybrid = list(self.hparams["hybrid_layer_pattern"]) + [1] * self._n_nextn - n_head_kv_arr = [n_head_kv_swa if use_swa == 1 else n_head_kv for use_swa in hybrid] - self.gguf_writer.add_head_count_kv(n_head_kv_arr) - - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - self.gguf_writer.add_sliding_window_pattern(hybrid) - self.gguf_writer.add_value_length(self.hparams["v_head_dim"]) - self.gguf_writer.add_expert_count(self.hparams["n_routed_experts"]) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) - - rope_dim = int(self.hparams["head_dim"] * self.hparams["partial_rotary_factor"]) - self.gguf_writer.add_rope_dimension_count(rope_dim) - - self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("layernorm_epsilon", 1e-5)) - - v_scale = self.hparams.get("attention_value_scale") - if v_scale is not None: - self.gguf_writer.add_attn_value_scale(float(v_scale)) - - self.gguf_writer.add_nextn_predict_layers(self._n_nextn) - - _experts: list[dict[str, Tensor]] | None = None - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "attention_sink" in name and not name.endswith(".weight"): - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch, name, bid): - # Remap MTP/NextN tensors to additional layer slots so the standard tensor map handles them. - # HF: model.mtp.layers.{i}.foo -> model.layers.{n_layer_text + i}.foo - m = re.match(r"^model\.mtp\.layers\.(\d+)\.(.*)$", name) - if m is not None: - mtp_idx = int(m.group(1)) - assert mtp_idx < self._n_nextn, f"MTP layer index {mtp_idx} >= _n_nextn ({self._n_nextn})" - rest = m.group(2) - n_layer_text = self.hparams["num_hidden_layers"] - new_bid = n_layer_text + mtp_idx - name = f"model.layers.{new_bid}.{rest}" - bid = new_bid - - # process the experts separately - if name.find("mlp.experts") != -1: - n_experts = self.hparams["n_routed_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["gate_proj", "up_proj", "down_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename_to_retrieve]) - del self._experts[bid][ename_to_retrieve] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("MiMoV2ForCausalLM") -class MiMoV2VisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - hp = self.hparams_vision - - hp["image_size"] = hp.get("image_size", 560) - hp["num_attention_heads"] = hp.get("num_heads", 32) - hp["num_hidden_layers"] = hp.get("depth", 28) - - self.n_q_heads = int(hp["num_heads"]) - self.num_kv_heads = int(hp.get("num_key_value_heads", 8)) - self.head_dim = int(hp.get("qk_channels", 64)) - self.spatial_merge_size = int(hp["spatial_merge_size"]) - # MiMoV2 vision RMSNorm: HF uses getattr(config, "rms_norm_eps", 1e-6) and the - # field is absent from MiMo-V2.5's vision_config - self.rms_norm_eps = float(hp.get("rms_norm_eps", 1e-6)) - - # fullatt_block_indexes are also reflected in vit_window_attn_types as -1 - self.fullatt_block_indexes = list(hp.get("fullatt_block_indexes") or []) - self.vit_window_attn_types = list(hp.get("vit_window_attn_types") or []) - self.visual_token_window_size = int(hp.get("visual_token_window_size", -1)) - self.use_sink = bool(hp.get("use_sink", False)) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MIMOVL) - self.gguf_writer.add_vision_use_silu(True) - self.gguf_writer.add_vision_head_count_kv(self.num_kv_heads) - self.gguf_writer.add_vision_spatial_merge_size(self.spatial_merge_size) - self.gguf_writer.add_uint32(gguf.Keys.ClipVision.WINDOW_SIZE, self.visual_token_window_size) - self.gguf_writer.add_vision_wa_pattern_mode(self.vit_window_attn_types) - self.gguf_writer.add_vision_attention_layernorm_eps(self.rms_norm_eps) - self.gguf_writer.add_vision_min_pixels(int(self.preprocessor_config["min_pixels"])) - self.gguf_writer.add_vision_max_pixels(int(self.preprocessor_config["max_pixels"])) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - # Sinks must be F32: any sink-style softmax/mask add in ggml requires - # F32, and we fold sinks into a host-built F32 mask at encode time. - if new_name.endswith(".attn_sinks"): - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, _ = item - if not name.startswith("visual."): - return None - return super().filter_tensors(item) - - def modify_tensors(self, data_torch, name, bid): - # Conv3D patch embed: split along the temporal axis (kt=2) into two Conv2D - # weights that the existing qwen2vl-style two-Conv2D path consumes. - if name == "visual.patch_embed.proj.weight": - _, _, kt, _, _ = data_torch.shape - if kt != 2: - raise ValueError(f"unexpected temporal_patch_size: {kt}") - embd_name = gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] - yield (embd_name + ".weight", data_torch[:, :, 0, ...]) - yield (embd_name + ".weight.1", data_torch[:, :, 1, ...]) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Step3p5ForCausalLM") -class Step35Model(TextModel): - model_arch = gguf.MODEL_ARCH.STEP35 - - def set_gguf_parameters(self): - rope_theta = self.hparams.get("rope_theta") - if isinstance(rope_theta, list): - self.hparams["rope_theta"] = float(rope_theta[0]) - self.hparams["local_rope_theta"] = float(rope_theta[1]) - self.rope_parameters["rope_theta"] = self.hparams["rope_theta"] - self.rope_parameters["sliding_attention"] = {"rope_theta": self.hparams["local_rope_theta"]} - - super().set_gguf_parameters() - - layer_types = self.hparams.get("layer_types") or [] - partial_rotary_factors = self.hparams.get("partial_rotary_factors") or [] - attn_other = self.hparams.get("attention_other_setting") or {} - - n_head_base = self.hparams["num_attention_heads"] - n_kv_base = self.hparams["num_attention_groups"] - - n_head_swa = attn_other.get("num_attention_heads", n_head_base) - n_kv_swa = attn_other.get("num_attention_groups", n_kv_base) - - layer_types = layer_types[: self.block_count] - partial_rotary_factors = partial_rotary_factors[: self.block_count] - assert [1.0 if lt == "sliding_attention" else 0.5 for lt in layer_types] == partial_rotary_factors - head_arr = [n_head_swa if lt == "sliding_attention" else n_head_base for lt in layer_types] - kv_arr = [n_kv_swa if lt == "sliding_attention" else n_kv_base for lt in layer_types] - swa_pat = [lt == "sliding_attention" for lt in layer_types] - - self.gguf_writer.add_head_count(head_arr) - self.gguf_writer.add_head_count_kv(kv_arr) - - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - self.gguf_writer.add_sliding_window_pattern(swa_pat) - - self.gguf_writer.add_value_length(self.hparams["head_dim"]) - - # MoE params - self.gguf_writer.add_expert_count(self.hparams["moe_num_experts"]) - self.gguf_writer.add_expert_used_count(self.hparams["moe_top_k"]) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) - self.gguf_writer.add_expert_shared_feed_forward_length(self.hparams["share_expert_dim"]) - - if (moe_router_scaling_factor := self.hparams.get("moe_router_scaling_factor")) is not None: - self.gguf_writer.add_expert_weights_scale(moe_router_scaling_factor) - if (norm_expert_weight := self.hparams.get("norm_expert_weight")) is not None: - self.gguf_writer.add_expert_weights_norm(norm_expert_weight) - - # leading dense blocks - leading_dense = 0 - moe_layers_enum = self.hparams.get("moe_layers_enum") - if isinstance(moe_layers_enum, str) and moe_layers_enum.strip(): - moe_layers = sorted(int(i) for i in moe_layers_enum.strip().split(",")) - if moe_layers: - leading_dense = max(0, moe_layers[0]) - self.gguf_writer.add_leading_dense_block_count(leading_dense) - self.gguf_writer.add_moe_every_n_layers(int(self.hparams.get("moe_every_n_layer", 1))) - - self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("rms_norm_eps", 1e-5)) - - # Optional per-layer SwiGLU clamps. - if (limits := self.hparams.get("swiglu_limits")) is not None: - limits_f = [0.0 if v is None else float(v) for v in limits[: self.block_count]] - self.gguf_writer.add_swiglu_clamp_exp(limits_f) - if (limits_shared := self.hparams.get("swiglu_limits_shared")) is not None: - limits_shared_f = [0.0 if v is None else float(v) for v in limits_shared[: self.block_count]] - self.gguf_writer.add_swiglu_clamp_shexp(limits_shared_f) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Map router bias (expert selection bias) to a GGUF bias tensor - if name.endswith(".moe.router_bias"): - name += ".bias" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): - # remove mtp layers - if (m := re.match(r"model\.layers\.(\d+)\.", name)) is not None: - il = int(m.group(1)) - n_main = int(self.hparams.get("num_hidden_layers", self.block_count)) - if il >= n_main: - return - if name.endswith("norm.weight"): - data_torch += 1.0 - - if name.endswith((".self_attn.g_proj.weight", ".moe.gate.weight", ".moe.up_proj.weight", ".moe.gate_proj.weight", ".moe.down_proj.weight")): - data_torch = data_torch.squeeze().contiguous() - - yield from super().modify_tensors(data_torch, name, bid) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - # Step35 can optionally use Llama-3 style RoPE scaling (HF: rope_scaling.rope_type == "llama3"). - # llama.cpp represents this via a single extra tensor: "rope_freqs.weight" (aka MODEL_TENSOR.ROPE_FREQS). - rope_params = self.rope_parameters.get("full_attention", self.rope_parameters) - rope_type = rope_params.get("rope_type") or "" - if rope_type.lower() != "llama3": - return - - # Step35 configs can carry per-layer rope_theta as a list; for llama3 rope factors we use the base value. - rope_theta = self.hparams.get("rope_theta", 10000.0) - if isinstance(rope_theta, list): - rope_theta = rope_theta[0] - base = float(rope_theta) - if (dim := self.hparams.get("head_dim")) is None: - dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - dim = int(dim) - - freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) - - factor = float(rope_params.get("factor", 8.0)) - low_freq_factor = float(rope_params.get("low_freq_factor", 1.0)) - high_freq_factor = float(rope_params.get("high_freq_factor", 4.0)) - old_context_len = int(rope_params.get("original_max_position_embeddings", self.hparams.get("original_max_position_embeddings", 8192))) - - low_freq_wavelen = old_context_len / low_freq_factor - high_freq_wavelen = old_context_len / high_freq_factor - - rope_factors: list[float] = [] - for freq in freqs: - wavelen = 2 * math.pi / float(freq) - if wavelen < high_freq_wavelen: - rope_factors.append(1.0) - elif wavelen > low_freq_wavelen: - rope_factors.append(factor) - else: - smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) - rope_factors.append(1.0 / ((1.0 - smooth) / factor + smooth)) - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) - - -@ModelBase.register("PanguEmbeddedForCausalLM") -class PanguEmbeddedModel(TextModel): - model_arch = gguf.MODEL_ARCH.PANGU_EMBED - - def set_vocab(self): - self._set_vocab_sentencepiece() - - tokenizer_config_file = self.dir_model / 'tokenizer_config.json' - if tokenizer_config_file.is_file(): - with open(tokenizer_config_file, "r", encoding="utf-8") as f: - tokenizer_config_json = json.load(f) - if "add_prefix_space" in tokenizer_config_json: - self.gguf_writer.add_add_space_prefix(tokenizer_config_json["add_prefix_space"]) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - # PanguEmbedded's hparam loaded from config.json without head_dim - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(rope_dim) - - if hparams.get("head_dim") is None: - self.gguf_writer.add_key_length(rope_dim) - self.gguf_writer.add_value_length(rope_dim) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name == "lm_head.weight": - if self.hparams.get("tie_word_embeddings", False): - logger.info("Skipping tied output layer 'lm_head.weight'") - return - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Dots1ForCausalLM") -class Dots1Model(Qwen2MoeModel): - model_arch = gguf.MODEL_ARCH.DOTS1 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.hparams["num_experts"] = self.hparams["n_routed_experts"] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_leading_dense_block_count(self.hparams["first_k_dense_replace"]) - self.gguf_writer.add_expert_shared_count(self.hparams["n_shared_experts"]) - self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) - self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None): - if "shared_experts" in name: - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("PLMForCausalLM") -class PLMModel(TextModel): - model_arch = gguf.MODEL_ARCH.PLM - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - self.gguf_writer.add_kv_lora_rank(hparams["kv_lora_rank"]) - self.gguf_writer.add_key_length(hparams["qk_nope_head_dim"] + hparams["qk_rope_head_dim"]) - self.gguf_writer.add_value_length(hparams["v_head_dim"]) - self.gguf_writer.add_rope_dimension_count(hparams["qk_rope_head_dim"]) - - def prepare_tensors(self): - super().prepare_tensors() - - -@ModelBase.register("T5WithLMHeadModel") -@ModelBase.register("T5ForConditionalGeneration") -@ModelBase.register("MT5ForConditionalGeneration") -@ModelBase.register("UMT5ForConditionalGeneration") -@ModelBase.register("UMT5Model") -class T5Model(TextModel): - model_arch = gguf.MODEL_ARCH.T5 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.shared_token_embeddings_found = False - - def set_vocab(self): - # to avoid TypeError: Descriptors cannot be created directly - # exception when importing sentencepiece_model_pb2 - os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" - from sentencepiece import SentencePieceProcessor - from sentencepiece import sentencepiece_model_pb2 as model - - tokenizer_path = self.dir_model / 'tokenizer.model' - - # many older models use spiece.model tokenizer model filename - if not tokenizer_path.is_file(): - tokenizer_path = self.dir_model / 'spiece.model' - - if not tokenizer_path.is_file(): - raise FileNotFoundError(f"File not found: {tokenizer_path}") - - sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] - sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) - - # some models like Pile-T5 family use BPE tokenizer instead of Unigram - if sentencepiece_model.trainer_spec.model_type == 2: # BPE - # assure the tokenizer model file name is correct - assert tokenizer_path.name == 'tokenizer.model' - return self._set_vocab_sentencepiece() - else: - assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM - - add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix - remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces - precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - for token_id in range(tokenizer.vocab_size()): - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - added_tokens_file = self.dir_model / 'added_tokens.json' - if added_tokens_file.is_file(): - with open(added_tokens_file, "r", encoding="utf-8") as f: - added_tokens_json = json.load(f) - for key in added_tokens_json: - token_id = added_tokens_json[key] - if token_id >= vocab_size: - logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - - tokens[token_id] = key.encode("utf-8") - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - - if vocab_size > len(tokens): - pad_count = vocab_size - len(tokens) - logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") - for i in range(1, pad_count + 1): - tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) - scores.append(-1000.0) - toktypes.append(SentencePieceTokenTypes.UNUSED) - - self.gguf_writer.add_tokenizer_model("t5") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_add_space_prefix(add_prefix) - self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) - if precompiled_charsmap: - self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - if (n_ctx := self.find_hparam(["n_positions"], optional=True)) is None: - logger.warning("Couldn't find context length in config.json, assuming default value of 512") - n_ctx = 512 - self.gguf_writer.add_context_length(n_ctx) - self.gguf_writer.add_embedding_length(self.hparams["d_model"]) - self.gguf_writer.add_feed_forward_length(self.hparams["d_ff"]) - self.gguf_writer.add_block_count(self.block_count) - if (dec_n_layer := self.hparams.get("num_decoder_layers")) is not None: - self.gguf_writer.add_decoder_block_count(dec_n_layer) - self.gguf_writer.add_head_count(self.hparams["num_heads"]) - self.gguf_writer.add_key_length(self.hparams["d_kv"]) - self.gguf_writer.add_value_length(self.hparams["d_kv"]) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_relative_attn_buckets_count(self.hparams["relative_attention_num_buckets"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_decoder_start_token_id(self.hparams["decoder_start_token_id"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", - # "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored - # in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder - # and decoder and ignore the remaining ones. - if name in ["decoder.embed_tokens.weight", "encoder.embed_tokens.weight", "shared.weight"]: - if not self.shared_token_embeddings_found: - name = "shared.weight" - self.shared_token_embeddings_found = True - else: - logger.debug(f"Skipping shared tensor {name!r} in safetensors so that convert can end normally.") - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("T5EncoderModel") -class T5EncoderModel(TextModel): - model_arch = gguf.MODEL_ARCH.T5ENCODER - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.shared_token_embeddings_found = False - - def set_vocab(self): - # to avoid TypeError: Descriptors cannot be created directly - # exception when importing sentencepiece_model_pb2 - os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" - from sentencepiece import SentencePieceProcessor - from sentencepiece import sentencepiece_model_pb2 as model - - tokenizer_path = self.dir_model / 'tokenizer.model' - - # many older models use spiece.model tokenizer model filename - if not tokenizer_path.is_file(): - tokenizer_path = self.dir_model / 'spiece.model' - - if not tokenizer_path.is_file(): - raise FileNotFoundError(f"File not found: {tokenizer_path}") - - sentencepiece_model = model.ModelProto() # pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] - sentencepiece_model.ParseFromString(open(tokenizer_path, "rb").read()) - - # some models like Pile-T5 family use BPE tokenizer instead of Unigram - if sentencepiece_model.trainer_spec.model_type == 2: # BPE - # assure the tokenizer model file name is correct - assert tokenizer_path.name == 'tokenizer.model' - return self._set_vocab_sentencepiece() - else: - assert sentencepiece_model.trainer_spec.model_type == 1 # UNIGRAM - - add_prefix = sentencepiece_model.normalizer_spec.add_dummy_prefix - remove_whitespaces = sentencepiece_model.normalizer_spec.remove_extra_whitespaces - precompiled_charsmap = sentencepiece_model.normalizer_spec.precompiled_charsmap - - tokenizer = SentencePieceProcessor() - tokenizer.LoadFromFile(str(tokenizer_path)) - - vocab_size = self.hparams.get('vocab_size', tokenizer.vocab_size()) - - tokens: list[bytes] = [f"[PAD{i}]".encode("utf-8") for i in range(vocab_size)] - scores: list[float] = [-10000.0] * vocab_size - toktypes: list[int] = [SentencePieceTokenTypes.UNUSED] * vocab_size - - for token_id in range(tokenizer.vocab_size()): - piece = tokenizer.IdToPiece(token_id) - text = piece.encode("utf-8") - score = tokenizer.GetScore(token_id) - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.IsUnknown(token_id): - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.IsControl(token_id): - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.IsUnused(token_id): - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.IsByte(token_id): - toktype = SentencePieceTokenTypes.BYTE - - tokens[token_id] = text - scores[token_id] = score - toktypes[token_id] = toktype - - added_tokens_file = self.dir_model / 'added_tokens.json' - if added_tokens_file.is_file(): - with open(added_tokens_file, "r", encoding="utf-8") as f: - added_tokens_json = json.load(f) - for key in added_tokens_json: - token_id = added_tokens_json[key] - if token_id >= vocab_size: - logger.warning(f'ignore token {token_id}: id is out of range, max={vocab_size - 1}') - continue - - tokens[token_id] = key.encode("utf-8") - scores[token_id] = -1000.0 - toktypes[token_id] = SentencePieceTokenTypes.USER_DEFINED - - if vocab_size > len(tokens): - pad_count = vocab_size - len(tokens) - logger.debug(f"Padding vocab with {pad_count} token(s) - [PAD1] through [PAD{pad_count}]") - for i in range(1, pad_count + 1): - tokens.append(bytes(f"[PAD{i}]", encoding="utf-8")) - scores.append(-1000.0) - toktypes.append(SentencePieceTokenTypes.UNUSED) - - self.gguf_writer.add_tokenizer_model("t5") - self.gguf_writer.add_tokenizer_pre("default") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_add_space_prefix(add_prefix) - self.gguf_writer.add_remove_extra_whitespaces(remove_whitespaces) - if precompiled_charsmap: - self.gguf_writer.add_precompiled_charsmap(precompiled_charsmap) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - if (n_ctx := self.find_hparam(["n_positions"], optional=True)) is None: - logger.warning("Couldn't find context length in config.json, assuming default value of 512") - n_ctx = 512 - self.gguf_writer.add_context_length(n_ctx) - self.gguf_writer.add_embedding_length(self.hparams["d_model"]) - self.gguf_writer.add_feed_forward_length(self.hparams["d_ff"]) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(self.hparams["num_heads"]) - self.gguf_writer.add_key_length(self.hparams["d_kv"]) - self.gguf_writer.add_value_length(self.hparams["d_kv"]) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_relative_attn_buckets_count(self.hparams["relative_attention_num_buckets"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", - # "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored - # in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder - # and decoder and ignore the remaining ones. - if name in ["decoder.embed_tokens.weight", "encoder.embed_tokens.weight", "shared.weight"]: - if not self.shared_token_embeddings_found: - name = "shared.weight" - self.shared_token_embeddings_found = True - else: - logger.debug(f"Skipping shared tensor {name!r} in safetensors so that convert can end normally.") - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Jais2ForCausalLM") -class Jais2Model(TextModel): - model_arch = gguf.MODEL_ARCH.JAIS2 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - head_dim = hparams.get("head_dim", hparams["hidden_size"] // hparams["num_attention_heads"]) - self.gguf_writer.add_rope_dimension_count(head_dim) - - -@ModelBase.register("JAISLMHeadModel") -class JaisModel(TextModel): - model_arch = gguf.MODEL_ARCH.JAIS - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # SwigLU activation - assert self.hparams["activation_function"] == "swiglu" - # ALiBi position embedding - assert self.hparams["position_embedding_type"] == "alibi" - - # Embeddings scale - self.embeddings_scale = 1.0 - if 'mup_embeddings_scale' in self.hparams: - self.embeddings_scale = self.hparams['mup_embeddings_scale'] - elif 'embeddings_scale' in self.hparams: - self.embeddings_scale = self.hparams['embeddings_scale'] - else: - assert False - - self.width_scale = 1.0 - if 'mup_output_alpha' in self.hparams: - assert 'mup_width_scale' in self.hparams - self.width_scale = self.hparams['mup_output_alpha'] * self.hparams['mup_width_scale'] - elif 'width_scale' in self.hparams: - self.width_scale = self.hparams['width_scale'] - else: - assert False - - self.max_alibi_bias = 8.0 - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.hparams["n_positions"]) - self.gguf_writer.add_embedding_length(self.hparams["n_embd"]) - self.gguf_writer.add_feed_forward_length(self.hparams["n_inner"]) - self.gguf_writer.add_head_count(self.hparams["n_head"]) - self.gguf_writer.add_layer_norm_eps(self.hparams["layer_norm_epsilon"]) - self.gguf_writer.add_file_type(self.ftype) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # we don't need these - if name.endswith((".attn.bias")): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.endswith(("relative_pe.slopes")): - # Calculate max ALiBi bias (this is the inverse of the ALiBi calculation) - # Some other models has max_alibi_bias spelled out explicitly in the hyperparams, - # but Jais's PyTorch model simply precalculates the slope values and places them - # in relative_pes.slopes - n_head_closest_log2 = 2 ** math.floor(math.log2(self.hparams["n_head"])) - first_val = float(data_torch[0].item()) - self.max_alibi_bias = -round(math.log2(first_val) * n_head_closest_log2) - - return - - if name.endswith((".c_attn.weight", ".c_proj.weight", ".c_fc.weight", ".c_fc2.weight")): - data_torch = data_torch.transpose(1, 0) - - new_name = self.map_tensor_name(name) - - if new_name == self.format_tensor_name(gguf.MODEL_TENSOR.TOKEN_EMBD): - yield from super().modify_tensors(data_torch * self.embeddings_scale, new_name, bid) - elif new_name == self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT): - yield from super().modify_tensors(data_torch * self.width_scale, new_name, bid) - else: - yield from super().modify_tensors(data_torch, new_name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - self.gguf_writer.add_max_alibi_bias(self.max_alibi_bias) - - -@ModelBase.register("Glm4ForCausalLM", "Glm4vForConditionalGeneration") -class Glm4Model(TextModel): - model_arch = gguf.MODEL_ARCH.GLM4 - use_mrope = False - partial_rotary_factor = 0.5 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.partial_rotary_factor = self.rope_parameters.get("partial_rotary_factor", 0.5) - if "mrope_section" in self.rope_parameters: - self.use_mrope = True - logger.info("Q/K weight will need to be permuted for M-RoPE") - - def set_vocab(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (rope_dim := self.hparams.get("head_dim")) is None: - rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.partial_rotary_factor)) - - @staticmethod - def normal_to_neox(weights: Tensor, n_head: int, n_head_kv: int, head_dim: int, partial_rotary_factor: float) -> Tensor: - orig_shape = weights.shape - if len(orig_shape) == 1: - weights = weights.unsqueeze(1) # [out_dim, 1] - if len(weights.shape) != 2: - raise ValueError("Only 1D and 2D tensors are supported.") - n_effective_heads = weights.shape[0] // head_dim - if n_head_kv is not None and n_effective_heads != n_head: - if n_effective_heads != n_head_kv: - raise AssertionError(f"Mismatch in effective heads: computed {n_effective_heads}, expected {n_head} or {n_head_kv}") - rotary_dim = int(head_dim * partial_rotary_factor) - if rotary_dim % 2 != 0: - raise ValueError("rotary_dim must be even.") - reshaped = weights.reshape(n_effective_heads, head_dim, -1) - rot_part = reshaped[:, :rotary_dim, :] - non_rot_part = reshaped[:, rotary_dim:, :] - permuted_rot = torch.cat((rot_part[:, ::2, :], rot_part[:, 1::2, :]), dim=1) - combined = torch.cat((permuted_rot, non_rot_part), dim=1) - result = combined.reshape(weights.shape) - return result if len(orig_shape) != 1 else result.squeeze(1) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.use_mrope: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams["num_key_value_heads"] - n_embd = self.hparams["hidden_size"] - head_dim = self.hparams.get("head_dim", n_embd // n_head) - # because llama.cpp M-RoPE kernel only supports Neox ordering, we have to permute the weights here - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = Glm4Model.normal_to_neox(data_torch, n_head, n_head, head_dim, self.partial_rotary_factor) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = Glm4Model.normal_to_neox(data_torch, n_head, n_kv_head, head_dim, self.partial_rotary_factor) - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("GlmOcrForConditionalGeneration") -class GlmOCRModel(Glm4Model): - model_arch = gguf.MODEL_ARCH.GLM4 - use_mrope = False - partial_rotary_factor = 0.5 - - # Note: GLM-OCR is the same as GLM4, but with an extra NextN/MTP prediction layer - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # GLM-OCR has num_hidden_layers + 1 actual layers (including NextN layer) - self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - # NextN/MTP prediction layers - if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: - self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) - - -@ModelBase.register("Glm4MoeForCausalLM", "Glm4vMoeForConditionalGeneration") -class Glm4MoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.GLM4_MOE - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # GLM4_MOE has num_hidden_layers + 1 actual layers (including NextN layer) - self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def set_vocab(self): - return self._set_vocab_glm() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (rope_dim := self.hparams.get("head_dim")) is None: - rope_dim = ( - self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - ) - self.gguf_writer.add_rope_dimension_count( - int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5)) - ) - - # MoE parameters - Use only routed expert count (shared experts handled separately) - if (n_routed_experts := self.hparams.get("n_routed_experts")) is not None: - self.gguf_writer.add_expert_count(n_routed_experts) - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - if (n_shared_experts := self.hparams.get("n_shared_experts")) is not None: - self.gguf_writer.add_expert_shared_count(n_shared_experts) - if (first_k_dense_replace := self.hparams.get("first_k_dense_replace")) is not None: - self.gguf_writer.add_leading_dense_block_count(first_k_dense_replace) - - # Expert gating function (sigmoid for GLM4_MOE) - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) - - # Routed scaling factor - if (routed_scaling_factor := self.hparams.get("routed_scaling_factor")) is not None: - self.gguf_writer.add_expert_weights_scale(routed_scaling_factor) - - # Normalise topk probabilities - if (norm_topk_prob := self.hparams.get("norm_topk_prob")) is not None: - self.gguf_writer.add_expert_weights_norm(norm_topk_prob) - - # NextN/MTP prediction layers - if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: - self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) - - _experts: list[dict[str, Tensor]] | None = None - - # note: unlike GLM4V non-MoE, we don't need to permute Q/K here since GLM4V_MOE uses Neox ordering already - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Handle main token embedding (but not layer-specific NextN embeddings) - if name == "model.embed_tokens.weight" and ".layers." not in name: - yield from super().modify_tensors(data_torch, "token_embd.weight", bid) - return - - # Handle routed experts - if name.find("mlp.experts") != -1: - n_experts = self.hparams["n_routed_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("Glm4MoeLiteForCausalLM") -class Glm4MoeLiteModel(DeepseekV2Model): - model_arch = gguf.MODEL_ARCH.DEEPSEEK2 - - def set_vocab(self): - return self._set_vocab_glm() - - -@ModelBase.register("GlmMoeDsaForCausalLM") -class GlmMoeDsaModel(DeepseekV2Model): - model_arch = gguf.MODEL_ARCH.GLM_DSA - skip_mtp = False - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def set_vocab(self): - return self._set_vocab_glm() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - rope_dim = self.hparams["qk_rope_head_dim"] - partial_rotary_factor = self.hparams.get("partial_rotary_factor", 1.0) - self.gguf_writer.add_rope_dimension_count(int(rope_dim * partial_rotary_factor)) - - # NextN/MTP prediction layers - if (num_nextn_predict_layers := self.hparams.get("num_nextn_predict_layers")) is not None: - self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers) - - # DSA indexer parameters - self.gguf_writer.add_indexer_head_count(self.hparams["index_n_heads"]) - self.gguf_writer.add_indexer_key_length(self.hparams["index_head_dim"]) - self.gguf_writer.add_indexer_top_k(self.hparams["index_topk"]) - - -@ModelBase.register("GlmForCausalLM", "ChatGLMModel", "ChatGLMForConditionalGeneration") -class ChatGLMModel(TextModel): - model_arch = gguf.MODEL_ARCH.CHATGLM - - def set_vocab_chatglm3(self): - dir_model = self.dir_model - hparams = self.hparams - tokens: list[bytes] = [] - toktypes: list[int] = [] - scores: list[float] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) - vocab_size = hparams.get("padded_vocab_size", len(tokenizer.get_vocab())) # ty: ignore[unresolved-attribute] - assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] - role_special_tokens = ["<|system|>", "<|user|>", "<|assistant|>", "<|observation|>"] - special_tokens = ["[MASK]", "[gMASK]", "[sMASK]", "sop", "eop"] + role_special_tokens - for token_id in range(vocab_size): - piece = tokenizer._convert_id_to_token(token_id) # ty: ignore[unresolved-attribute] - if token_id == 0: - piece = "" - elif token_id == 1: - piece = "" - elif token_id == 2: - piece = "" - - text = piece.encode("utf-8") # ty: ignore[unresolved-attribute] - score = 0.0 - # Referencing the tokenizer Python implementation(https://huggingface.co/THUDM/chatglm3-6b/blob/main/tokenization_chatglm.py), - # it is only valid if it is less than tokenizer.tokenizer.sp_model.vocab_size() - if len(piece) != 0 and token_id < tokenizer.tokenizer.sp_model.vocab_size(): # ty: ignore[unresolved-attribute, invalid-argument-type] - score = tokenizer.tokenizer.sp_model.get_score(token_id) # ty: ignore[unresolved-attribute] - - if token_id >= tokenizer.tokenizer.sp_model.vocab_size(): # ty: ignore[unresolved-attribute] - if piece in special_tokens: - toktype = SentencePieceTokenTypes.CONTROL - elif len(piece) == 0: # ty: ignore[invalid-argument-type] - text = f"[PAD{token_id}]".encode("utf-8") - toktype = SentencePieceTokenTypes.UNUSED - else: - toktype = SentencePieceTokenTypes.USER_DEFINED - tokens.append(text) - scores.append(score) - toktypes.append(toktype) - continue - - toktype = SentencePieceTokenTypes.NORMAL - if tokenizer.tokenizer.sp_model.is_unknown(token_id): # ty: ignore[unresolved-attribute] - toktype = SentencePieceTokenTypes.UNKNOWN - elif tokenizer.tokenizer.sp_model.is_control(token_id): # ty: ignore[unresolved-attribute] - toktype = SentencePieceTokenTypes.CONTROL - elif tokenizer.tokenizer.sp_model.is_unused(token_id): # ty: ignore[unresolved-attribute] - toktype = SentencePieceTokenTypes.UNUSED - elif tokenizer.tokenizer.sp_model.is_byte(token_id): # ty: ignore[unresolved-attribute] - toktype = SentencePieceTokenTypes.BYTE - - tokens.append(text) - scores.append(score) - toktypes.append(toktype) - - self.gguf_writer.add_tokenizer_model("llama") - # glm3 needs prefix and suffix formatted as: - # prompt = "[gMASK]sop<|user|>\n" + prompt + "<|assistant|>" - self.gguf_writer.add_tokenizer_pre("chatglm-spm") - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_scores(scores) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, n_vocab=len(tokens)) - special_vocab.add_to_gguf(self.gguf_writer) - - @staticmethod - def token_bytes_to_string(b): - from transformers.models.gpt2.tokenization_gpt2 import bytes_to_unicode # ty: ignore[unresolved-import] - byte_encoder = bytes_to_unicode() - return ''.join([byte_encoder[ord(char)] for char in b.decode('latin-1')]) - - @staticmethod - def bpe(mergeable_ranks: dict[bytes, int], token: bytes, max_rank: int | None = None) -> list[bytes]: - parts = [bytes([b]) for b in token] - while True: - min_idx = None - min_rank = None - for i, pair in enumerate(zip(parts[:-1], parts[1:])): - rank = mergeable_ranks.get(pair[0] + pair[1]) - if rank is not None and (min_rank is None or rank < min_rank): - min_idx = i - min_rank = rank - if min_rank is None or (max_rank is not None and min_rank >= max_rank): - break - assert min_idx is not None - parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2:] - return parts - - def set_vocab(self): - if "THUDM/chatglm3-6b" in self.hparams.get("_name_or_path", ""): - self.set_vocab_chatglm3() - return - - dir_model = self.dir_model - hparams = self.hparams - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True) - vocab_size = hparams.get("padded_vocab_size",hparams["vocab_size"]) - assert max(tokenizer.get_vocab().values()) < vocab_size # ty: ignore[unresolved-attribute] - - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - # only add special tokens when they were not already loaded from config.json - special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|user|>"]) # ty: ignore[unresolved-attribute] - # this one is usually not in config.json anyway - special_vocab._set_special_token("unk", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - n_embed = self.hparams.get("hidden_size", self.hparams.get("n_embed")) - assert n_embed is not None - n_head = self.hparams.get("n_head", self.hparams.get("num_attention_heads")) - assert n_head is not None - n_head_kv = self.hparams.get("multi_query_group_num", self.hparams.get("num_key_value_heads", n_head)) - self.gguf_writer.add_context_length(self.hparams.get("seq_length", n_embed)) - self.gguf_writer.add_embedding_length(n_embed) - self.gguf_writer.add_feed_forward_length(self.hparams.get("ffn_hidden_size", self.hparams.get("intermediate_size", 4 * n_embed))) - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_head_count(n_head) - self.gguf_writer.add_head_count_kv(n_head_kv) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("layernorm_epsilon",1e-5)) - self.gguf_writer.add_file_type(self.ftype) - if "attention_dim" in self.hparams: - rope_dim = self.hparams["attention_dim"] - else: - rope_dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5))) - self.gguf_writer.add_add_bos_token(False) - rope_freq = 10000 - if "rope_ratio" in self.hparams: - rope_freq = rope_freq * self.hparams["rope_ratio"] - self.gguf_writer.add_rope_freq_base(rope_freq) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith(".rotary_pos_emb.inv_freq"): - return None - - name = name.removeprefix("transformer.") - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("NemotronForCausalLM") -class NemotronModel(TextModel): - model_arch = gguf.MODEL_ARCH.NEMOTRON - - def set_vocab(self): - self._set_vocab_sentencepiece() - self.gguf_writer.add_pad_token_id(0) - self.gguf_writer.add_unk_token_id(1) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - f_norm_eps = self.find_hparam(["layer_norm_eps", "layer_norm_epsilon", "norm_epsilon", "norm_eps"]) - self.gguf_writer.add_layer_norm_eps(f_norm_eps) - - # * Partial RoPE - rot_pct = self.find_hparam(["partial_rotary_factor", "rope_pct", "rope_percent"]) - n_embd = self.find_hparam(["hidden_size", "n_embd"]) - n_head = self.find_hparam(["num_attention_heads", "n_head"]) - self.gguf_writer.add_rope_dimension_count(int(rot_pct * n_embd) // n_head) - - # * RopeScaling for Nemotron - if "rope_scaling" not in self.hparams or self.hparams["rope_scaling"] is None: - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - else: - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.LINEAR) - self.gguf_writer.add_rope_scaling_factor(self.hparams["factor"]) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # * Adding +1 to LayerNorm's weights here to implement layernorm1p w/o changing anything on the GGML engine side - # model.layers.{l}.input_layernorm.weight - # model.layers.{l}.post_attention_layernorm.weight - # model.norm.weight - if name.endswith("norm.weight"): - data_torch = data_torch + 1 - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("ExaoneForCausalLM") -class ExaoneModel(TextModel): - model_arch = gguf.MODEL_ARCH.EXAONE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - - assert (hparams["activation_function"] == "silu") - - rotary_factor = self.find_hparam(["partial_rotary_factor", "rope_pct"], optional=True) - rotary_factor = rotary_factor if rotary_factor is not None else 1.0 - self.gguf_writer.add_rope_dimension_count(int(rotary_factor * (hparams["hidden_size"] // hparams["num_attention_heads"]))) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): - if rope_params.get("rope_type", '').lower() == "llama3": - base = self.rope_parameters.get("rope_theta", 10000.0) - if (dim := self.hparams.get("head_dim")) is None: - dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) - - factor = rope_params.get("factor", 8.0) - low_freq_factor = rope_params.get("low_freq_factor", 1.0) - high_freq_factor = rope_params.get("high_freq_factor", 4.0) - old_context_len = self.hparams.get("original_max_position_embeddings", 8192) - - low_freq_wavelen = old_context_len / low_freq_factor - high_freq_wavelen = old_context_len / high_freq_factor - assert low_freq_wavelen != high_freq_wavelen - - rope_factors = [] - for freq in freqs: - wavelen = 2 * math.pi / freq - if wavelen < high_freq_wavelen: - rope_factors.append(1) - elif wavelen > low_freq_wavelen: - rope_factors.append(factor) - else: - smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) - rope_factors.append(1 / ((1 - smooth) / factor + smooth)) - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) - - -@ModelBase.register("Exaone4ForCausalLM") -class Exaone4Model(TextModel): - model_arch = gguf.MODEL_ARCH.EXAONE4 - - def set_vocab(self): - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab.add_to_gguf(self.gguf_writer) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - - if hparams.get("sliding_window") is not None: - self.gguf_writer.add_sliding_window(hparams["sliding_window"]) - if "layer_types" in hparams: - self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]]) - elif "sliding_window_pattern" in hparams: - sliding_window_pattern = [] - if isinstance(hparams["sliding_window_pattern"], str): # e.g. LLLG - for i in range(hparams["num_hidden_layers"]): - sliding_window_pattern.append(hparams["sliding_window_pattern"][i % len(hparams["sliding_window_pattern"])] == "L") - if isinstance(hparams["sliding_window_pattern"], int): # e.g. 4 - for i in range(hparams["num_hidden_layers"]): - sliding_window_pattern.append((i + 1) % hparams["sliding_window_pattern"] != 0) - if len(sliding_window_pattern) == hparams["num_hidden_layers"]: - self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - if rope_params := self.rope_parameters.get("full_attention", self.rope_parameters): - if rope_params.get("rope_type", '').lower() == "llama3": - base = rope_params.get("rope_theta", 10_000.0) - if (dim := self.hparams.get("head_dim")) is None: - dim = self.hparams["hidden_size"] // self.hparams["num_attention_heads"] - freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) - - factor = rope_params.get("factor", 16.0) - low_freq_factor = rope_params.get("low_freq_factor", 1.0) - high_freq_factor = rope_params.get("high_freq_factor", 4.0) - old_context_len = self.hparams.get("original_max_position_embeddings", 8192) - - low_freq_wavelen = old_context_len / low_freq_factor - high_freq_wavelen = old_context_len / high_freq_factor - - rope_factors = [] - for freq in freqs: - wavelen = 2 * math.pi / freq - if wavelen < high_freq_wavelen: - rope_factors.append(1) - elif wavelen > low_freq_wavelen: - rope_factors.append(factor) - else: - smooth = (old_context_len / wavelen - low_freq_factor) / (high_freq_factor - low_freq_factor) - rope_factors.append(1 / ((1 - smooth) / factor + smooth)) - - yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32)) - - -@ModelBase.register("ExaoneMoEForCausalLM") -class ExaoneMoEModel(Exaone4Model): - model_arch = gguf.MODEL_ARCH.EXAONE_MOE - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.block_count = self.hparams["num_hidden_layers"] + self.hparams.get("num_nextn_predict_layers", 0) - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - moe_intermediate_size = self.hparams["moe_intermediate_size"] - num_shared_experts = self.hparams["num_shared_experts"] - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - self.gguf_writer.add_expert_shared_count(num_shared_experts) - self.gguf_writer.add_expert_shared_feed_forward_length(moe_intermediate_size * num_shared_experts) - self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) - self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) - n_dense_layer = self.hparams.get("first_k_dense_replace", self.hparams.get("first_last_k_dense_replace", 0)) - self.gguf_writer.add_leading_dense_block_count(n_dense_layer) - self.gguf_writer.add_nextn_predict_layers(self.hparams.get("num_nextn_predict_layers", 0)) - - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.startswith("mtp."): - if name.find("layers.") != -1: - # `mtp.layers.0.[module_name]` format - name = name.replace(f"mtp.layers.{bid}", f"model.layers.{bid + self.hparams['num_hidden_layers']}") - else: - # mtp fc/norm weights - remapper = { - "mtp.fc": "model.layers.{bid}.eh_proj", - "mtp.pre_fc_norm_embedding": "model.layers.{bid}.enorm", - "mtp.pre_fc_norm_hidden": "model.layers.{bid}.hnorm", - "mtp.norm": "model.layers.{bid}.shared_head.norm", - } - _n = Path(name) - new_name = remapper[_n.stem] + _n.suffix - - # set shared weights for all NextN/MTP layers - for bid in range(self.hparams['num_hidden_layers'], self.block_count): - yield from super().modify_tensors(data_torch, new_name.format(bid=bid), bid) - return - - if name.find("mlp.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - new_name = self.map_tensor_name(merged_name) - - yield from super().modify_tensors(data_torch, new_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("GraniteForCausalLM", "GraniteSpeechForConditionalGeneration") -class GraniteModel(LlamaModel): - """Conversion for IBM's GraniteForCausalLM""" - model_arch = gguf.MODEL_ARCH.GRANITE - - def set_gguf_parameters(self): - """Granite uses standard llama parameters with the following differences: - - - No head_dim support - - New multiplier params: - - attention_scale - - embedding_scale - - residual_scale - - logits_scaling - """ - if head_dim := self.hparams.pop("head_dim", None): - logger.warning("Ignoring head_dim (%s) from config for Granite", head_dim) - super().set_gguf_parameters() - # NOTE: Convert _multiplier params to _scale params for naming - # consistency - if attention_scale := self.hparams.get("attention_multiplier"): - self.gguf_writer.add_attention_scale(attention_scale) - logger.info("gguf: (granite) attention_scale = %s", attention_scale) - if embedding_scale := self.hparams.get("embedding_multiplier"): - self.gguf_writer.add_embedding_scale(embedding_scale) - logger.info("gguf: (granite) embedding_scale = %s", embedding_scale) - if residual_scale := self.hparams.get("residual_multiplier"): - self.gguf_writer.add_residual_scale(residual_scale) - logger.info("gguf: (granite) residual_scale = %s", residual_scale) - if logits_scale := self.hparams.get("logits_scaling"): - self.gguf_writer.add_logit_scale(logits_scale) - logger.info("gguf: (granite) logits_scale = %s", logits_scale) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - if name.startswith("encoder."): - return None - return super().filter_tensors(item) - - -@ModelBase.register("GraniteMoeForCausalLM", "GraniteMoeSharedForCausalLM") -class GraniteMoeModel(GraniteModel): - """Conversion for IBM's GraniteMoeForCausalLM""" - model_arch = gguf.MODEL_ARCH.GRANITE_MOE - - def set_gguf_parameters(self): - """GraniteMoeShared uses GraniteMoe parameters plus the following: - - shared_intermediate_size - """ - super().set_gguf_parameters() - if shared_feed_forward_length := self.hparams.get("shared_intermediate_size"): - self.gguf_writer.add_expert_shared_feed_forward_length(shared_feed_forward_length) - logger.info("gguf: (granitemoeshared) shared_feed_forward_length = %s", shared_feed_forward_length) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - """In modeling_granitemoe, the JetMoe implementation of parallel experts - is used. This essentially merges w1 and w3 into a single tensor with 2x - the hidden size that is then split during forward. To keep compatibility - with existing mixtral support, we pull them apart here. - """ - - if name.endswith("block_sparse_moe.input_linear.weight"): - ffn_dim = self.hparams["intermediate_size"] - assert data_torch.shape[-2] == 2 * ffn_dim, "Merged FFN tensor size must be 2 * intermediate_size" - gate, up = data_torch.split(ffn_dim, dim=-2) - yield from ModelBase.modify_tensors(self, gate, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_EXP, bid), bid) - yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_EXP, bid), bid) - return - - has_experts = bool(self.hparams.get('num_local_experts')) - - if name.endswith("shared_mlp.input_linear.weight"): - ffn_dim = self.hparams["shared_intermediate_size"] - assert data_torch.shape[-2] == 2 * ffn_dim, "Merged FFN tensor size must be 2 * shared_intermediate_size" - gate, up = data_torch.split(ffn_dim, dim=-2) - if has_experts: - yield from ModelBase.modify_tensors(self, gate,self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE_SHEXP, bid), bid) - yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP_SHEXP, bid), bid) - return - yield from ModelBase.modify_tensors(self, gate, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_GATE, bid), bid) - yield from ModelBase.modify_tensors(self, up, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_UP, bid), bid) - return - - if not has_experts and name.endswith("shared_mlp.output_linear.weight"): - yield from ModelBase.modify_tensors(self, data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.FFN_DOWN, bid), bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("GraniteMoeHybridForCausalLM", "BambaForCausalLM") -class GraniteHybridModel(Mamba2Model, GraniteMoeModel): - """GraniteHybrid is a hybrid SSM + Attention model that uses Mamba2 SSM - layers and optionally uses MoE w/ a shared expert""" - model_arch = gguf.MODEL_ARCH.GRANITE_HYBRID - undo_permute = True - - def __init__(self, *args, **kwargs): - - # Hybrid mamba models use a prefix for the mamba-specific params. - # TODO: Extend this if the prefix(es) need to be configurable - self.hparam_prefixes = ["mamba"] - - super().__init__(*args, **kwargs) - - # Lists of which layers use ssm vs attention - self._attn_layers = self.get_attn_layers() - self._ssm_layers = [ - i for i in range(self.block_count) - if i not in self._attn_layers - ] - - # There are some models in this family that are non-hybrid, but keep the - # same parent class by setting all layers to "attention." If this is the - # case, the model architecture needs to be updated to a standard - # "granite" or "granitemoe" model - if not self._ssm_layers: - has_experts = self.find_hparam(["num_experts_per_tok", "num_experts_per_token"], optional=True) - new_arch = ( - gguf.MODEL_ARCH.GRANITE_MOE - if has_experts else - gguf.MODEL_ARCH.GRANITE - ) - self.model_arch = new_arch - self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[new_arch] - self.gguf_writer.add_architecture() - - # n_group and d_inner are used during reshape_tensors for mamba2 - # NOTE: Explicitly include hparam prefix prefix for d_model to - # disambiguate with top-level head_dim - # NOTE 2: If needed for future models, this can be isolated in a method - # to separate the prefix setting and the keys used - self.d_model = self.find_hparam([f"{self.hparam_prefixes[0]}_head_dim", "hidden_size", "d_model"]) - self.n_group = self.find_hparam(["n_groups", "num_groups"]) - self.d_inner = self.find_hparam(["expand", "num_heads"]) * self.d_model - - def get_attn_layers(self): - # Explicit list of layer type names - if layer_types := self.hparams.get("layer_types"): - return [ - i for i, typ in enumerate(layer_types) - if typ == "attention" - ] - - # Layer types indicated by index or period - attn_layers = self.hparams.get("attn_layer_indices", []) - if not attn_layers: - attn_period = self.hparams.get("attn_layer_period") - assert attn_period, "Didn't find attn_layer_indices or attn_layer_period" - attn_offset = self.hparams.get("attn_layer_offset") - assert attn_offset is not None, "No attention layer offset set with attn_layer_period" - attn_layers = [ - i for i in range(self.block_count) - if i % attn_period == attn_offset - ] - return attn_layers - - def find_hparam(self, keys: Iterable[str], *args, **kwargs) -> Any: - prefixed = [] - for pfx in self.hparam_prefixes: - prefixed.extend( - "_".join([pfx, k]) - for k in keys - ) - keys = list(keys) + prefixed - return Mamba2Model.find_hparam(self, keys, *args, **kwargs) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if ( - name.endswith("block_sparse_moe.input_linear.weight") - or "shared_mlp" in name - ): - yield from GraniteMoeModel.modify_tensors(self, data_torch, name, bid) - return - - # Determine whether this is a mamba layer or an attention layer - if bid in self._ssm_layers: - yield from Mamba2Model.modify_tensors(self, data_torch, name, bid) - return - elif bid in self._attn_layers: - yield from GraniteMoeModel.modify_tensors(self, data_torch, name, bid) - return - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - - def set_gguf_parameters(self): - """This method merges params from both parents and some that are - specific to this model. The result is some duplication of how the params - get set. The following warnings are expected during conversion: - - WARNING:Duplicated key name 'granitehybrid.attention.head_count_kv' - WARNING:Duplicated key name 'granitehybrid.context_length' - """ - GraniteMoeModel.set_gguf_parameters(self) - - ## Mamba mixer params ## - self.gguf_writer.add_ssm_conv_kernel(self.find_hparam(["conv_kernel", "d_conv"])) - self.gguf_writer.add_ssm_state_size(self.find_hparam(["state_size", "d_state", "state_dim", "ssm_state_size"])) - self.gguf_writer.add_ssm_group_count(self.n_group) - self.gguf_writer.add_ssm_inner_size(self.d_inner) - # NOTE: The mamba_dt_rank is _not_ the right field for how this is used - # in llama.cpp - self.gguf_writer.add_ssm_time_step_rank(self.find_hparam(["n_heads", "num_heads"])) - - ## Attention params ## - head_count_kv = self.find_hparam(["num_key_value_heads", "n_head_kv"]) - head_count_kv_vec = [ - head_count_kv if i in self._attn_layers else 0 for i in range(self.block_count) - ] - if rope_dim := self.hparams.get("attn_rotary_emb"): - self.gguf_writer.add_rope_dimension_count(rope_dim) - self.gguf_writer.add_head_count_kv(head_count_kv_vec) - - ## If Bamba or non-hybrid, use rope, otherwise don't - use_rope = ( - "BambaForCausalLM" in self.hparams["architectures"] - or not self._ssm_layers - ) - self.gguf_writer.add_rope_scaling_finetuned(use_rope) - if not use_rope: - self.gguf_writer.add_context_length(2**20) - - ## Validation ## - d_head = self.find_hparam(["d_head"], optional=True) or 64 - assert self.hparams.get("hidden_act") in [None, "silu"], "Only SILU activation supported" - assert self.d_inner % d_head == 0, f"SSM inner size {self.d_inner} not a multiple of head dim {d_head}" - - def set_vocab(self): - self.hparams["pad_vocab_size_multiple"] = 8 - Mamba2Model.set_vocab(self) - - -@ModelBase.register("NemotronHForCausalLM") -class NemotronHModel(GraniteHybridModel): - """Hybrid mamba2/attention model from NVIDIA""" - model_arch = gguf.MODEL_ARCH.NEMOTRON_H - is_moe: bool = False - - def __init__(self, *args, **kwargs): - # We have to determine the correct model architecture (MoE vs non-MoE) before - # calling the parent __init__. This is because the parent constructor - # uses self.model_arch to build the tensor name map, and all MoE-specific - # mappings would be missed if it were called with the default non-MoE arch. - hparams = ModelBase.load_hparams(args[0], self.is_mistral_format) - has_moe_params = ( - "num_experts_per_tok" in hparams - or (isinstance(hparams.get("llm_config"), dict) and "num_experts_per_tok" in hparams["llm_config"]) - ) - if has_moe_params: - self.model_arch = gguf.MODEL_ARCH.NEMOTRON_H_MOE - self.is_moe = True - - super().__init__(*args, **kwargs) - - # Save the top-level head_dim for later - self.head_dim = self.hparams.get("head_dim", self.hparams.get("attention_head_dim")) - assert self.head_dim is not None, "Could not find the attention head dim in config" - - # Don't use expand to calculate d_inner - self.d_inner = self.find_hparam(["num_heads"]) * self.d_model - - # Update the ssm / attn / mlp layers - # M: Mamba2, *: Attention, -: MLP - # MoE: - # M: Mamba2, *: Attention, E: Expert - pattern = self.hparams.get("hybrid_override_pattern") or self.hparams.get("layers_block_type") - if pattern is None: - self._ssm_layers = [] - self._mlp_layers = [] - elif isinstance(pattern, str): - self._ssm_layers = [i for i, val in enumerate(pattern) if val == "M"] - self._mlp_layers = [i for i, val in enumerate(pattern) if val == ("E" if self.is_moe else "-")] - else: - self._ssm_layers = [i for i, val in enumerate(pattern) if val == "mamba"] - self._mlp_layers = [i for i, val in enumerate(pattern) if val == "moe"] - - def get_attn_layers(self): - pattern = self.hparams.get("hybrid_override_pattern") or self.hparams.get("layers_block_type") - if pattern is None: - return [] - assert len(pattern) == self.block_count, f"Mismatch between pattern ({len(pattern)}) and block_count ({self.block_count})!" - if isinstance(pattern, str): - return [i for i, val in enumerate(pattern) if val == "*"] - - return [i for i, val in enumerate(pattern) if val == "attention"] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - head_dim = self.head_dim - if head_dim is None: - raise ValueError("Could not find the attention head dim in config") - self.gguf_writer.add_key_length(head_dim) - self.gguf_writer.add_value_length(head_dim) - - # Set feed_forward_length - # NOTE: This will trigger an override warning. This is preferable to - # duplicating all the parent logic - if not self.is_moe: - n_ff = self.find_hparam(["intermediate_size", "n_inner", "hidden_dim"]) - self.gguf_writer.add_feed_forward_length([ - n_ff if i in self._mlp_layers else 0 for i in range(self.block_count) - ]) - else: - moe_intermediate_size = self.hparams["moe_intermediate_size"] - self.gguf_writer.add_feed_forward_length([ - moe_intermediate_size if i in self._mlp_layers else 0 for i in range(self.block_count) - ]) - self.gguf_writer.add_expert_used_count(self.hparams["num_experts_per_tok"]) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) - self.gguf_writer.add_expert_shared_feed_forward_length(self.hparams["moe_shared_expert_intermediate_size"]) - self.gguf_writer.add_expert_count(self.hparams["n_routed_experts"]) - self.gguf_writer.add_expert_shared_count(self.hparams["n_shared_experts"]) - self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"]) - self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"]) - self.gguf_writer.add_expert_group_count(self.hparams["n_group"]) - - # number of experts used per token (top-k) - if (n_experts_used := self.hparams.get("num_experts_per_tok")) is not None: - self.gguf_writer.add_expert_used_count(n_experts_used) - - if (latent_size := self.hparams.get("moe_latent_size")) is not None: - self.gguf_writer.add_moe_latent_size(latent_size) - - def set_vocab(self): - # The NemotronH config uses pattern characters (e.g. '-') that may not - # be supported by the installed transformers version. AutoTokenizer - # internally calls AutoConfig which triggers this parsing failure. - # Using trust_remote_code=True to load the model's own config class. - tokens: list[str] = [] - toktypes: list[int] = [] - - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - - # Pad vocab size (from Mamba2Model/GraniteHybridModel) - self.hparams["pad_vocab_size_multiple"] = 8 # Setting this here since GraniteHybridModel.set_vocab() isn't being invoked now. - # From Mamba2Model.set_vocab(): - vocab_size = self.hparams["vocab_size"] - pad_vocab = self.hparams.get("pad_vocab_size_multiple", 16) - # ref: https://stackoverflow.com/a/17511341/22827863 - vocab_size = -(vocab_size // -pad_vocab) * pad_vocab - self.hparams["vocab_size"] = vocab_size - - assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute] - - tokpre = self.get_vocab_base_pre(tokenizer) - - reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute] - added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute] - - added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute] - - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token: str = reverse_vocab[i] - if token in added_vocab: - if not added_tokens_decoder[i].normalized: - previous_token = token - token = tokenizer.decode(tokenizer.encode(token, add_special_tokens=False)) # ty: ignore[unresolved-attribute, invalid-assignment] - if previous_token != token: - logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer") - - if added_tokens_decoder[i].special or self.does_token_look_special(token): - toktypes.append(gguf.TokenType.CONTROL) - else: - token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces - toktypes.append(gguf.TokenType.USER_DEFINED) - else: - toktypes.append(gguf.TokenType.NORMAL) - tokens.append(token) - - # From TextModel.set_vocab_gpt2(): - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - special_vocab.add_to_gguf(self.gguf_writer) - - # The tokenizer _does_ add a BOS token (via post_processor type - # TemplateProcessing) but does not set add_bos_token to true in the - # config, so we need to explicitly override it here. - if not self.is_moe: - self.gguf_writer.add_add_bos_token(True) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.is_moe and bid is not None: - # Skip Multi-Token Prediction (MTP) tensors. These are used for - # for speculative decoding but we don't include them in this model - # conversion. See https://github.com/ggml-org/llama.cpp/pull/18886 - if name.startswith("mtp."): - logger.info(f"gguf: Skipping MTP (Speculative) layer: {name}") - return - - if name.endswith("mixer.gate.e_score_correction.bias"): - yield from ModelBase.modify_tensors(self, data_torch, name, bid) - return - - if name.endswith("mixer.dt_bias"): - new_name = name.replace("dt_bias", "dt.bias") - yield from ModelBase.modify_tensors(self, data_torch, new_name, bid) - return - - if name.endswith("mixer.conv1d.weight"): - squeezed_data = data_torch.squeeze() - yield from ModelBase.modify_tensors(self, squeezed_data, name, bid) - return - - if name.endswith("mixer.A_log"): - transformed_data = -torch.exp(data_torch) - reshaped_data = transformed_data.squeeze().reshape(-1, 1) - yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) - return - - if name.endswith("mixer.D"): - reshaped_data = data_torch.squeeze().reshape(-1, 1) - yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) - return - - if name.endswith("mixer.norm.weight"): - reshaped_data = data_torch.reshape(self.n_group, -1) - yield from ModelBase.modify_tensors(self, reshaped_data, name, bid) - return - - if name.find("mixer.experts") != -1: - n_experts = self.hparams["n_routed_experts"] - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 2: - # merge the experts into a single tensor - for w_name in ["down_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"backbone.layers.{bid}.mixer.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from ModelBase.modify_tensors(self, data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("LlamaBidirectionalModel") -class LlamaEmbedNemotronModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.LLAMA_EMBED - - -@ModelBase.register("BailingMoeForCausalLM") -class BailingMoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.BAILINGMOE - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - - self.gguf_writer.add_rope_dimension_count(rope_dim) - self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) - self.gguf_writer.add_expert_weights_scale(1.0) - self.gguf_writer.add_expert_shared_count(hparams["num_shared_experts"]) - self.gguf_writer.add_expert_weights_norm(hparams["norm_topk_prob"]) - - _experts: list[dict[str, Tensor]] | None = None - - @staticmethod - def permute(weights: Tensor, n_head: int, n_head_kv: int | None): - if n_head_kv is not None and n_head != n_head_kv: - n_head = n_head_kv - return (weights.reshape(n_head, 2, weights.shape[0] // n_head // 2, *weights.shape[1:]) - .swapaxes(1, 2) - .reshape(weights.shape)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - n_embd = self.hparams["hidden_size"] - if (head_dim := self.hparams.get("head_dim")) is None: - head_dim = n_embd // n_head - - output_name = self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT) - - if name.endswith("attention.dense.weight"): - yield from super().modify_tensors(data_torch, self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_OUT, bid), bid) - return - elif name.endswith("query_key_value.weight"): - q, k, v = data_torch.split([n_head * head_dim, n_kv_head * head_dim, n_kv_head * head_dim], dim=-2) - - yield from super().modify_tensors(BailingMoeModel.permute(q, n_head, n_head), self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_Q, bid), bid) - yield from super().modify_tensors(BailingMoeModel.permute(k, n_head, n_kv_head), self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_K, bid), bid) - yield from super().modify_tensors(v,self.format_tensor_name(gguf.MODEL_TENSOR.ATTN_V, bid), bid) - return - elif name.find("mlp.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - new_name = self.map_tensor_name(merged_name) - - yield from super().modify_tensors(data_torch, new_name, bid) - - return - - new_name = self.map_tensor_name(name) - - if new_name == output_name and self.hparams.get("norm_head"): - data_torch = data_torch.float() - data_torch /= torch.norm(data_torch, p=2, dim=0, keepdim=True) + 1e-7 - - yield from super().modify_tensors(data_torch, new_name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("BailingMoeV2ForCausalLM") -class BailingMoeV2Model(TextModel): - model_arch = gguf.MODEL_ARCH.BAILINGMOE2 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if nextn_layers := self.hparams.get("num_nextn_predict_layers", 0): - self.block_count = self.hparams["num_hidden_layers"] + nextn_layers - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - - self.gguf_writer.add_rope_dimension_count(int(rope_dim * self.hparams.get("partial_rotary_factor", 0.5))) - self.gguf_writer.add_leading_dense_block_count(hparams["first_k_dense_replace"]) - self.gguf_writer.add_vocab_size(hparams["vocab_size"]) - self.gguf_writer.add_expert_feed_forward_length(hparams["moe_intermediate_size"]) - self.gguf_writer.add_expert_shared_feed_forward_length(hparams.get("moe_shared_expert_intermediate_size", hparams["moe_intermediate_size"] * hparams["num_shared_experts"])) - self.gguf_writer.add_expert_weights_scale(hparams["routed_scaling_factor"]) - self.gguf_writer.add_expert_shared_count(hparams["num_shared_experts"]) - self.gguf_writer.add_expert_weights_norm(hparams["norm_topk_prob"]) - - if (nextn_layers := self.hparams.get("num_nextn_predict_layers")) is not None: - self.gguf_writer.add_nextn_predict_layers(nextn_layers) - - _experts: list[dict[str, Tensor]] | None = None - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith(".expert_bias"): - name = name.replace(".expert_bias", ".expert_bias.bias") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "mlp.experts" in name: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("SarvamMoEForCausalLM", "modeling_sarvam_moe.SarvamMoEForCausalLM") -class SarvamMoEModel(BailingMoeV2Model): - model_arch = gguf.MODEL_ARCH.BAILINGMOE2 - # Sarvam-MoE shares the BailingMoeV2 architecture; only differences: - # - full rotary (no partial_rotary_factor) - # - expert bias is zero-mean normalized at load time - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - if (rope_dim := hparams.get("head_dim")) is None: - rope_dim = hparams["hidden_size"] // hparams["num_attention_heads"] - # Override the partial-rotary value written by BailingMoeV2 with the full rotary dim - self.gguf_writer.add_rope_dimension_count(rope_dim) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - if name.endswith(".expert_bias"): - # Sarvam normalizes expert bias to zero mean - inner = gen - - def gen(): - t = inner() - return t - t.mean() - return super().filter_tensors((name, gen)) - - -@ModelBase.register("GroveMoeForCausalLM", "modeling_grove_moe.GroveMoeForCausalLM") -class GroveMoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.GROVEMOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (moe_intermediate_size := self.hparams.get("moe_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") - # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L299 - self.gguf_writer.add_expert_chunk_feed_forward_length(self.hparams.get("head_dim") or 128) - # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L298 - self.gguf_writer.add_experts_per_group(2) - # FIXME?: Hardcoded https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L376 - self.gguf_writer.add_expert_group_scale(0.05) - - _experts: list[dict[str, Tensor]] | None = None - _chunk_experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.endswith(".expert_bias"): - # FIXME?: Unused https://huggingface.co/inclusionAI/GroveMoE-Inst/blob/c4c69e5970d18907b5e6ddccdfd55176fe292df1/modeling_grove_moe.py#L303 - return - - # process the experts separately - if name.find("chunk_experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) // 2 # see add_experts_per_group - assert bid is not None - - if self._chunk_experts is None: - self._chunk_experts = [{} for _ in range(self.block_count)] - - self._chunk_experts[bid][name] = data_torch - - if len(self._chunk_experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.chunk_experts.{xid}.{w_name}.weight" - datas.append(self._chunk_experts[bid][ename]) - del self._chunk_experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.chunk_experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - elif name.find("experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._chunk_experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - chunk_experts = [k for d in self._chunk_experts for k in d.keys()] - if len(chunk_experts) > 0: - raise ValueError(f"Unprocessed adjugate experts: {chunk_experts}") - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("ChameleonForConditionalGeneration") -@ModelBase.register("ChameleonForCausalLM") # obsolete -class ChameleonModel(TextModel): - model_arch = gguf.MODEL_ARCH.CHAMELEON - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_swin_norm(self.hparams.get("swin_norm", False)) - - def set_vocab(self): - self._set_vocab_gpt2() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # ignore image tokenizer for now - # TODO: image support for Chameleon - if name.startswith("model.vqmodel"): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - n_head = self.hparams["num_attention_heads"] - n_kv_head = self.hparams.get("num_key_value_heads") - hidden_dim = self.hparams.get("hidden_size") - - if name.endswith(("q_proj.weight", "q_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_head) - if name.endswith(("k_proj.weight", "k_proj.bias")): - data_torch = LlamaModel.permute(data_torch, n_head, n_kv_head) - if name.endswith(("q_norm.weight", "q_norm.bias")): - data_torch = ChameleonModel._reverse_hf_permute(data_torch, n_head, hidden_dim) - if name.endswith(("k_norm.weight", "k_norm.bias")): - data_torch = ChameleonModel._reverse_hf_permute(data_torch, n_kv_head, hidden_dim) - - yield from super().modify_tensors(data_torch, name, bid) - - # see: https://github.com/huggingface/transformers/blob/72fb02c47dbbe1999ae105319f24631cad6e2e00/src/transformers/models/chameleon/convert_chameleon_weights_to_hf.py#L176-L203 - @staticmethod - def _reverse_hf_permute(data_torch, n_heads, hidden_dim): - head_dim = hidden_dim // n_heads - data_torch = data_torch[0].view(2, head_dim // 2).t().reshape(1, -1) - data_torch = data_torch.repeat_interleave(n_heads, 0) - return data_torch - - -@ModelBase.register("UltravoxModel") -class UltravoxModel(TextModel): - model_arch = gguf.MODEL_ARCH.LLAMA # dummy - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - raise NotImplementedError("Ultravox does not have text decoder. Instead, it uses Llama or other models for text. If you want to get the audio encoder, please use --mmproj argument") - - -@ModelBase.register("GlmasrModel") -class GlmASRWhisperEncoderModel(MmprojModel): - has_vision_encoder = False - has_audio_encoder = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if "hidden_size" not in self.hparams and "intermediate_size" not in self.hparams: - self.hparams["hidden_size"] = self.hparams["d_model"] - self.hparams["intermediate_size"] = self.hparams["encoder_ffn_dim"] - self.hparams["num_attention_heads"] = self.hparams["encoder_attention_heads"] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GLMA) - self.gguf_writer.add_audio_num_mel_bins(self.hparams["num_mel_bins"]) - self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) - self.gguf_writer.add_audio_stack_factor(self.global_config["merge_factor"]) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F16 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith(("model.", "lm_head.")): - # skip language model tensors - return None - - if name.startswith("audio_encoder.whisper."): - name = name.replace("audio_encoder.whisper.","audio_tower.") - if "audio_encoder.layer_norm." in name or "audio_encoder.proj." in name: - name = name.replace("audio_encoder.", "audio_encoder.adapting.") - if name.startswith("audio_encoder.adapting."): - name = name.replace("audio_encoder.adapting.","audio.multi_modal_projector.") - if ".layer_norm." in name: - name = name.replace(".layer_norm.", ".ln_pre.") - if ".0." in name: - name = name.replace(".0.", ".linear_1.") - if ".2." in name: - name = name.replace(".2.", ".linear_2.") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name.startswith("audio_encoder.audio_bos_eos_token."): - yield from super().modify_tensors(data_torch[0], "model.vision.boi", bid) - yield from super().modify_tensors(data_torch[1], "model.vision.eoi", bid) - return - - if name.startswith("audio_encoder.adapting."): - if ".proj." in name: - return - - if "conv1.bias" in name or "conv2.bias" in name: - # transpose conv1 and conv2 bias - data_torch = data_torch.unsqueeze(-1) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Qwen2AudioForConditionalGeneration") -class WhisperEncoderModel(MmprojModel): - has_vision_encoder = False # no vision encoder - has_audio_encoder = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if "hidden_size" not in self.hparams and "intermediate_size" not in self.hparams: - self.hparams["hidden_size"] = self.hparams["d_model"] - self.hparams["intermediate_size"] = self.hparams["encoder_ffn_dim"] - self.hparams["num_attention_heads"] = self.hparams["encoder_attention_heads"] - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.QWEN2A) - self.gguf_writer.add_audio_num_mel_bins(self.hparams["num_mel_bins"]) - self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-5)) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F16 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # prevent clash naming with vision tensors - if name.startswith("multi_modal_projector"): - name = "audio." + name - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "conv1.bias" in name or "conv2.bias" in name: - # transpose conv1 and conv2 bias - data_torch = data_torch.unsqueeze(-1) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("UltravoxModel") -class UltravoxWhisperEncoderModel(WhisperEncoderModel): - has_vision_encoder = False # no vision encoder - has_audio_encoder = True - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.ULTRAVOX) - self.gguf_writer.add_audio_stack_factor(self.global_config["stack_factor"]) - - -@ModelBase.register("MERaLiON2ForConditionalGeneration") -class MERaLiONWhisperEncoderModel(WhisperEncoderModel): - has_vision_encoder = False - has_audio_encoder = True - - def get_audio_config(self) -> dict[str, Any] | None: - return self.global_config.get("speech_config") - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MERALION) - self.gguf_writer.add_audio_stack_factor(self.global_config.get("speech_mlp_scale_factor", 15)) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("text_decoder."): - return None - - if name.startswith("speech_encoder."): - name = name.replace("speech_encoder.", "audio_tower.") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - suffix = "." + name.rsplit(".", 1)[-1] - - if name.startswith("ln_speech."): - yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MM_NORM_PRE, suffix=suffix), data_torch) - return - - if name.startswith("speech_audio_adapter."): - if ".mlp_adapter.0." in name: - yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 0, suffix=suffix), data_torch) - elif ".gate_proj." in name: - yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 1, suffix=suffix), data_torch) - elif ".pool_proj." in name: - yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 2, suffix=suffix), data_torch) - elif ".out_proj." in name: - yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_MMPROJ, 3, suffix=suffix), data_torch) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("VoxtralForConditionalGeneration") -class VoxtralWhisperEncoderModel(WhisperEncoderModel): - has_vision_encoder = False # no vision encoder - has_audio_encoder = True - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.VOXTRAL) - self.gguf_writer.add_audio_stack_factor(4) # == intermediate_size // hidden_size - - -@ModelBase.register("AudioFlamingo3ForConditionalGeneration") -class AudioFlamingo3WhisperEncoderModel(WhisperEncoderModel): - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MUSIC_FLAMINGO) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if ".conv" in name and ".weight" in name: - # Was trained in BF16, being safe, avoiding quantizing to FP16 - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - -@ModelBase.register("FalconH1ForCausalLM") -class FalconH1Model(Mamba2Model): - model_arch = gguf.MODEL_ARCH.FALCON_H1 - - def __init__(self, *args, **kwargs): - # Set the hparam prefixes for Falcon Mamba2 - self.hparam_prefixes = ["mamba"] - - # Initialize the base Mamba2Model - super().__init__(*args, **kwargs) - - # Use Llama conversion for attention - self._transformer_model_class = LlamaModel - - # n_group and d_inner are used during reshape_tensors for mamba2 - self.n_group = self.find_hparam(["n_groups"]) - self.d_inner = self.find_hparam(["mamba_d_ssm"]) - self.d_head = self.find_hparam(["d_head"]) - - # Initialize any Falcon Mamba2 specific attributes - self.has_attention = True # Falcon Mamba2 has attention components - - # Load Falcon-H1 multipliers from hyperparameters - self.attention_in_multiplier = self.find_hparam(["attention_in_multiplier"], optional=True) - self.attention_out_multiplier = self.find_hparam(["attention_out_multiplier"], optional=True) - self.ssm_in_multiplier = self.find_hparam(["ssm_in_multiplier"], optional=True) - self.ssm_out_multiplier = self.find_hparam(["ssm_out_multiplier"], optional=True) - self.mlp_multipliers = self.find_hparam(["mlp_multipliers"], optional=True) - self.ssm_multipliers = self.find_hparam(["ssm_multipliers"], optional=True) - self.intermediate_size = self.find_hparam(["intermediate_size"]) - self.key_multiplier = self.find_hparam(["key_multiplier"], optional=True) - - def find_hparam(self, keys: Iterable[str], *args, **kwargs) -> Any: - prefixed = [] - for pfx in self.hparam_prefixes: - prefixed.extend( - "_".join([pfx, k]) - for k in keys - ) - keys = list(keys) + prefixed - return super().find_hparam(keys, *args, **kwargs) - - def set_vocab(self): - self._set_vocab_gpt2() - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - tensors = list(super().modify_tensors(data_torch, name, bid)) - tensor = tensors[0][1] - - if "down_proj" in name: - tensor = tensor * self.mlp_multipliers[1] - elif "gate_proj" in name: - tensor = tensor * self.mlp_multipliers[0] - elif "k_proj" in name: - tensor = tensor * self.key_multiplier * self.attention_in_multiplier - elif "q_proj" in name: - tensor = tensor * self.attention_in_multiplier - elif "v_proj" in name: - tensor = tensor * self.attention_in_multiplier - elif "o_proj" in name: - tensor = tensor * self.attention_out_multiplier - elif "out_proj" in name: - tensor = tensor * self.ssm_out_multiplier - elif "in_proj" in name: - tensor = tensor * self.ssm_in_multiplier - zxbcdt_multipliers = self.hparams["ssm_multipliers"] - intermediate_size = self.hparams["mamba_d_ssm"] - groups_time_state_size = self.hparams["mamba_n_groups"] * self.hparams["mamba_d_state"] - tensor[:intermediate_size, :] *= zxbcdt_multipliers[0] - tensor[intermediate_size:2 * intermediate_size, :] *= zxbcdt_multipliers[1] - tensor[2 * intermediate_size:2 * intermediate_size + groups_time_state_size, :] *= zxbcdt_multipliers[2] - tensor[2 * intermediate_size + groups_time_state_size:2 * intermediate_size + 2 * groups_time_state_size, :] *= zxbcdt_multipliers[3] - tensor[2 * intermediate_size + 2 * groups_time_state_size:, :] *= zxbcdt_multipliers[4] - elif "lm_head" in name: - tensor = tensor * self.hparams["lm_head_multiplier"] - elif "embed_tokens" in name: - tensor = tensor * self.hparams["embedding_multiplier"] - elif "mamba.norm" in name: - tensor = tensor.reshape(self.n_group, self.d_inner // self.n_group) - - tensors = [(tensors[0][0], tensor)] - return tensors - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - ## General Params ## - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - # Override some Mamba2 defaults - self.gguf_writer.add_block_count(self.block_count) - self.gguf_writer.add_context_length(self.hparams.get("max_position_embeddings", 0)) - self.gguf_writer.add_feed_forward_length(self.hparams["intermediate_size"]) - - ## Attention params ## - self.gguf_writer.add_head_count(self.hparams["num_attention_heads"]) # Override value 0 from Mamba2 - self.gguf_writer.add_head_count_kv(self.hparams["num_key_value_heads"]) - self.gguf_writer.add_key_length(self.hparams["head_dim"]) - self.gguf_writer.add_value_length(self.hparams["head_dim"]) - - ## Validation ## - assert self.hparams.get("hidden_act") in [None, "silu"], "Only SILU activation supported" - assert self.d_inner % self.d_head == 0, f"SSM inner size {self.d_inner} not a multiple of head dim {self.d_head}" - - # Add any other Falcon Mamba2 specific configuration - self.gguf_writer.add_rope_freq_base(self.rope_parameters["rope_theta"]) - - -@ModelBase.register("HunYuanMoEV1ForCausalLM") -class HunYuanMoEModel(TextModel): - model_arch = gguf.MODEL_ARCH.HUNYUAN_MOE - - def set_vocab(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - - # 1. Get the pre-tokenizer identifier hash - tokpre = self.get_vocab_base_pre(tokenizer) - - # 2. Reverse-engineer the merges list from mergeable_ranks - merges = [] - vocab = {} - mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] - for token, rank in mergeable_ranks.items(): - vocab[QwenModel.token_bytes_to_string(token)] = rank - if len(token) == 1: - continue - merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) - if len(merged) == 2: # todo this is an assert in Qwen, why? - merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) - - # 3. Generate the tokens and toktypes lists - vocab_size = self.hparams["vocab_size"] - assert tokenizer.vocab_size == vocab_size # ty: ignore[unresolved-attribute] - special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] - reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} - tokens: list[str] = [] - toktypes: list[int] = [] - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token = reverse_vocab[i] - tokens.append(token) - if i in special_tokens.values(): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.NORMAL) - - # 4. Write all vocab-related fields to the GGUF writer - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_token_merges(merges) - - # 5. Add special tokens and chat templates - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) - special_vocab.add_to_gguf(self.gguf_writer) - # FIX for BOS token: Overwrite incorrect id read from config.json - self.gguf_writer.add_bos_token_id(127959) # <|bos|> - - def set_gguf_parameters(self): - super().set_gguf_parameters() - hparams = self.hparams - - self.gguf_writer.add_expert_shared_feed_forward_length(hparams["intermediate_size"]) - - moe_intermediate_size = hparams["moe_intermediate_size"] - assert all(n == moe_intermediate_size[0] for n in moe_intermediate_size) - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size[0]) - - moe_topk = hparams["moe_topk"] - assert all(topk == moe_topk[0] for topk in moe_topk) - self.gguf_writer.add_expert_used_count(moe_topk[0]) - - moe_shared_expert = hparams["num_shared_expert"] - assert all(n == moe_shared_expert[0] for n in moe_shared_expert) - self.gguf_writer.add_expert_shared_count(moe_shared_expert[0]) - - # Rope - if self.rope_parameters.get("rope_type") == "dynamic": - # HunYuan uses NTK Aware Alpha based scaling. Original implementation: https://www.reddit.com/r/LocalLLaMA/comments/14lz7j5/ntkaware_scaled_rope_allows_llama_models_to_have/ - # 1000 corresponds to a usable context length of 256k (https://github.com/Tencent-Hunyuan/Hunyuan-A13B/blob/main/report/Hunyuan_A13B_Technical_Report.pdf) - alpha = self.rope_parameters.get("alpha", 1000) - base = self.rope_parameters.get("rope_theta", 10000.0) - dim = (hparams["hidden_size"] // hparams["num_attention_heads"]) # 128 - scaled_base = base * (alpha ** (dim / (dim - 2))) # 10000 * (1000 ** (128 / 126)) = 11158839.9251 - self.gguf_writer.add_rope_freq_base(scaled_base) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - self.gguf_writer.add_rope_scaling_factor(1) - # There is no consistent way to calculate ctx from alpha, and the config is incorrectly set to 32k - self.gguf_writer.add_rope_scaling_orig_ctx_len(256 * 1024) # 256k context length - self.gguf_writer.add_context_length(256 * 1024) # 256k context length - - # if any of our assumptions about the values are wrong, something has changed and this may need to be updated - assert alpha == 1000 and base == 10000.0 and dim == 128 and self.hparams["max_position_embeddings"] in [32 * 1024, 256 * 1024] , \ - "HunYuan dynamic RoPE scaling assumptions changed, please update the logic or context length manually" - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name == "lm_head.weight": - if self.hparams.get("tie_word_embeddings", False): - logger.info("Skipping tied output layer 'lm_head.weight'") - return - - if name.find("mlp.experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - if self._experts is not None: - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("LLaDAMoEModel", "LLaDAMoEModelLM") -class LLaDAMoEModel(TextModel): - model_arch = gguf.MODEL_ARCH.LLADA_MOE - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (expert_intermediate_size := self.hparams.get("expert_intermediate_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size) - - self.gguf_writer.add_mask_token_id(156895) - self.gguf_writer.add_causal_attention(False) - self.gguf_writer.add_diffusion_shift_logits(False) - - _experts: list[dict[str, Tensor]] | None = None - - # Copied from: Qwen2MoeModel - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # process the experts separately - if name.find("experts") != -1: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down_proj", "gate_proj", "up_proj"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - # Copied from: Qwen2MoeModel - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("HunYuanDenseV1ForCausalLM") -class HunYuanModel(TextModel): - model_arch = gguf.MODEL_ARCH.HUNYUAN_DENSE - - def _get_eod_token_id(self) -> int | None: - """Get the actual end-of-generation token from config (eod_token_id).""" - return self.hparams.get("eod_token_id") - - def _get_eot_token_id(self) -> int | None: - """Get the end-of-turn token from generation_config.json. - This is the first entry in eos_token_id when it's a list.""" - gen_cfg_path = self.dir_model / "generation_config.json" - if gen_cfg_path.is_file(): - with open(gen_cfg_path, encoding="utf-8") as f: - gen_cfg = json.load(f) - eos = gen_cfg.get("eos_token_id") - if isinstance(eos, list) and len(eos) >= 2: - return eos[0] - return None - - def _fix_special_tokens(self): - """Fix EOS/EOT tokens that are incorrect in upstream configs.""" - eod_id = self._get_eod_token_id() - if eod_id is not None: - self.gguf_writer.add_eos_token_id(eod_id) - eot_id = self._get_eot_token_id() - if eot_id is not None: - self.gguf_writer.add_eot_token_id(eot_id) - - def set_vocab(self): - if (self.dir_model / "tokenizer.json").is_file(): - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - - # HunyuanOCR has pad_token_id=-1 in config.json; exclude pad from SpecialVocab - token_types = None - if (self.hparams.get("pad_token_id") or 0) < 0: - token_types = ('bos', 'eos', 'unk', 'sep', 'cls', 'mask') - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True, special_token_types=token_types) - special_vocab.add_to_gguf(self.gguf_writer) - self._fix_special_tokens() - else: - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True) - - # 1. Get the pre-tokenizer identifier hash - tokpre = self.get_vocab_base_pre(tokenizer) - - # 2. Reverse-engineer the merges list from mergeable_ranks - merges = [] - vocab = {} - mergeable_ranks = tokenizer.mergeable_ranks # ty: ignore[unresolved-attribute] - for token, rank in mergeable_ranks.items(): - vocab[QwenModel.token_bytes_to_string(token)] = rank - if len(token) == 1: - continue - merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank) - if len(merged) == 2: - merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged))) - - # 3. Generate the tokens and toktypes lists - vocab_size = self.hparams["vocab_size"] - assert tokenizer.vocab_size == vocab_size # ty: ignore[unresolved-attribute] - special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute] - reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()} - tokens: list[str] = [] - toktypes: list[int] = [] - for i in range(vocab_size): - if i not in reverse_vocab: - tokens.append(f"[PAD{i}]") - toktypes.append(gguf.TokenType.UNUSED) - else: - token = reverse_vocab[i] - tokens.append(token) - if i in special_tokens.values(): - toktypes.append(gguf.TokenType.CONTROL) - else: - toktypes.append(gguf.TokenType.NORMAL) - - # 4. Write all vocab-related fields to the GGUF writer - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - self.gguf_writer.add_token_merges(merges) - - # 5. Add special tokens and chat templates - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False) - special_vocab.add_to_gguf(self.gguf_writer) - # FIX for BOS token: Overwrite incorrect id read from config.json - if self.hparams['hidden_size'] == 4096: - self.gguf_writer.add_bos_token_id(127958) # only for 7b dense, fix <|bos|> token - self._fix_special_tokens() - - def set_gguf_parameters(self): - # HunyuanOCR has num_experts=1 which is not MoE, prevent parent from writing it - saved_num_experts = self.hparams.pop("num_experts", None) - super().set_gguf_parameters() - if saved_num_experts is not None and saved_num_experts > 1: - self.hparams["num_experts"] = saved_num_experts - hparams = self.hparams - - # Rope - if self.rope_parameters.get("rope_type") in ("dynamic", "xdrope"): - # HunYuan uses NTK Aware Alpha based scaling. Original implementation: https://www.reddit.com/r/LocalLLaMA/comments/14lz7j5/ntkaware_scaled_rope_allows_llama_models_to_have/ - # 1000 corresponds to a usable context length of 256k (https://github.com/Tencent-Hunyuan/Hunyuan-A13B/blob/main/report/Hunyuan_A13B_Technical_Report.pdf) - alpha = self.rope_parameters.get("alpha", 50) - base = self.rope_parameters.get("rope_theta", 10000.0) - dim = hparams["head_dim"] - scaled_base = base * (alpha ** (dim / (dim - 2))) - self.gguf_writer.add_rope_freq_base(scaled_base) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - self.gguf_writer.add_rope_scaling_factor(1) - if self.rope_parameters.get("rope_type") == "dynamic": - # There is no consistent way to calculate ctx from alpha, and the config is incorrectly set to 32k - self.gguf_writer.add_rope_scaling_orig_ctx_len(256 * 1024) # 256k context length - self.gguf_writer.add_context_length(256 * 1024) # 256k context length - - # if any of our assumptions about the values are wrong, something has changed and this may need to be updated - assert base == 10000.0 and self.hparams["max_position_embeddings"] in [32 * 1024, 256 * 1024] , \ - "HunYuan dynamic RoPE scaling assumptions changed, please update the logic or context length manually" - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if name == "lm_head.weight": - if self.hparams.get("tie_word_embeddings", False): - logger.info("Skipping tied output layer 'lm_head.weight'") - return - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("HunYuanVLForConditionalGeneration") -class HunyuanVLVisionModel(MmprojModel): - # Handles both HunyuanOCR and HunyuanVL, which share the HF architecture name - # "HunYuanVLForConditionalGeneration" and the `vit.perceive.*` vision layout. - # Each variant maps to a different projector type in clip.cpp so image - # preprocessing follows the correct code path. - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - # HunyuanOCR / HunyuanVL uses max_image_size instead of image_size - if "image_size" not in self.hparams_vision: - self.hparams_vision["image_size"] = self.hparams_vision.get("max_image_size", 2048) - - @staticmethod - def is_ocr_variant(hparams: dict) -> bool: - """Return True for HunyuanOCR, False for HunyuanVL. - - The projector's output dim must equal the text model's hidden_size by - construction (that's what "projector" means). HunyuanOCR pairs a 1B text - backbone (hidden=1024); HunyuanVL pairs a 4B one (hidden=3072). So the - ViT -> LLM projection dim is a hard architectural signature, not a - magic number. - """ - vision_out = int((hparams.get("vision_config") or {}).get("out_hidden_size", 0)) - return vision_out == 1024 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - vcfg = self.hparams_vision - - if self.is_ocr_variant(self.global_config): - # --- HunyuanOCR --- - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.HUNYUANOCR) - self.gguf_writer.add_vision_use_gelu(True) - self.gguf_writer.add_vision_attention_layernorm_eps(vcfg.get("rms_norm_eps", 1e-5)) - self.gguf_writer.add_vision_spatial_merge_size(vcfg.get("spatial_merge_size", 2)) - self.gguf_writer.add_vision_min_pixels(self.preprocessor_config["min_pixels"]) - self.gguf_writer.add_vision_max_pixels(self.preprocessor_config["max_pixels"]) - return - - # --- HunyuanVL --- - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.HUNYUANVL) - self.gguf_writer.add_vision_use_gelu(str(vcfg["hidden_act"]).lower() == "gelu") - self.gguf_writer.add_vision_attention_layernorm_eps(float(vcfg["rms_norm_eps"])) - self.gguf_writer.add_vision_spatial_merge_size(int(vcfg["spatial_merge_size"])) - self.gguf_writer.add_vision_min_pixels(int(self.preprocessor_config["min_pixels"])) - self.gguf_writer.add_vision_max_pixels(int(self.preprocessor_config["max_pixels"])) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if not name.startswith("vit."): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # strip CLS token (row 0) from position embeddings so resize_position_embeddings works - if "position_embedding" in name: - data_torch = data_torch[1:] # [n_patches+1, n_embd] -> [n_patches, n_embd] - yield from super().modify_tensors(data_torch, name, bid) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - # force conv weights to F32 or F16 to avoid BF16 IM2COL issues on Metal - # Both HunyuanOCR and HunyuanVL emit the ViT -> LLM projection as mm.0/mm.2. - if ("mm.0." in new_name or "mm.2." in new_name) and new_name.endswith(".weight"): - return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - -@ModelBase.register("HunYuanVLForConditionalGeneration") -class HunyuanVLTextModel(HunYuanModel): - # The "HunYuanVLForConditionalGeneration" HF architecture covers both HunyuanOCR - # and HunyuanVL. HunyuanOCR reuses the HunYuan-Dense text backbone (standard RoPE), - # while HunyuanVL introduces a new LLM arch with XD-RoPE. Detect the variant from - # the config and pick the matching GGUF architecture. - model_arch = gguf.MODEL_ARCH.HUNYUAN_VL - - @staticmethod - def _is_ocr_config(hparams: dict) -> bool: - # OCR pairs a 1B text backbone (hidden=1024) with a ViT projector that - # outputs 1024-d; HunyuanVL uses 3072-d. Keep in sync with - # HunyuanVLVisionModel.is_ocr_variant. - return int((hparams.get("vision_config") or {}).get("out_hidden_size", 0)) == 1024 - - def __init__(self, dir_model: Path, *args, **kwargs): - raw_hparams = kwargs.get("hparams") or ModelBase.load_hparams(dir_model, is_mistral_format=False) - if self._is_ocr_config(raw_hparams): - self.model_arch = gguf.MODEL_ARCH.HUNYUAN_DENSE - else: - self.model_arch = gguf.MODEL_ARCH.HUNYUAN_VL - super().__init__(dir_model, *args, **kwargs) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - # Only emit XD-RoPE metadata for the HunyuanVL backbone; HunyuanOCR uses - # the HunYuan-Dense arch which already handles standard rope in super(). - if self.model_arch != gguf.MODEL_ARCH.HUNYUAN_VL: - return - - if self.rope_parameters.get("rope_type") != "xdrope": - return - - # defaults for HunyuanVL. The C++ side later computes: - # freq_base = rope_theta * alpha ** (head_dim / (head_dim - 2)) - self.gguf_writer.add_rope_freq_base(float(self.rope_parameters["rope_theta"])) - self.gguf_writer.add_rope_scaling_alpha(float(self.rope_parameters["alpha"])) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - self.gguf_writer.add_rope_scaling_factor(float(self.rope_parameters.get("factor", 1))) - - ctx_len = int(self.hparams["max_position_embeddings"]) - self.gguf_writer.add_rope_scaling_orig_ctx_len(ctx_len) - self.gguf_writer.add_context_length(ctx_len) - - self.gguf_writer.add_rope_dimension_sections(list(self.rope_parameters["xdrope_section"])) - - -@ModelBase.register("SmolLM3ForCausalLM") -class SmolLM3Model(LlamaModel): - model_arch = gguf.MODEL_ARCH.SMOLLM3 - - -@ModelBase.register("GptOssForCausalLM") -class GptOssModel(TextModel): - model_arch = gguf.MODEL_ARCH.GPT_OSS - - # TODO: remove once MXFP4 is supported more generally - def dequant_model(self): - if self._is_mxfp4: - return - return super().dequant_model() - - def transform_nibble_layout(self, tensor): - assert tensor.dtype == torch.uint8 - assert tensor.shape[-1] == 16 - # swap nibbles - t_lo = tensor & 0x0F - t_hi = tensor & 0xF0 - t_swapped = (t_lo << 4) | (t_hi >> 4) - tensor = t_swapped - # transform aaaa...bbbb... to abababab... - blk_a, blk_b = tensor.chunk(2, dim=-1) - # get a_ - blk_a0 = (blk_a & 0xF0).view(-1, 1) - blk_a1 = (blk_a << 4).view(-1, 1) - blk_a = torch.stack((blk_a0, blk_a1), dim=2).view(tensor.shape) - # get _b - blk_b0 = (blk_b >> 4).view(-1, 1) - blk_b1 = (blk_b & 0x0F).view(-1, 1) - blk_b = torch.stack((blk_b0, blk_b1), dim=2).view(tensor.shape) - # swap once more - out = blk_a | blk_b - out_h = out & 0xF0 - out_l = out & 0x0F - out = (out_h >> 4) | (out_l << 4) - return out - - def repack_mxfp4(self, new_name: str, blocks: Tensor, scales: Tensor): - assert blocks.dtype == torch.uint8 - assert scales.dtype == torch.uint8 - scales = scales.unsqueeze(-1) - assert len(blocks.shape) == 4 - assert len(scales.shape) == 4 - blocks = self.transform_nibble_layout(blocks) - new_data = torch.concat((scales, blocks), dim=-1) - new_shape = [new_data.shape[0], new_data.shape[1], new_data.shape[2] * 32] - logger.info(f"Repacked {new_name} with shape {new_shape} and quantization MXFP4") - # flatten last dim - new_data = new_data.view(new_data.shape[0], new_data.shape[1], new_data.shape[2] * new_data.shape[3]) - new_data = new_data.numpy() - self.gguf_writer.add_tensor(new_name, new_data, raw_dtype=gguf.GGMLQuantizationType.MXFP4) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - blocks0: Tensor = torch.zeros(1) - blocks1: Tensor = torch.zeros(1) - # we assume that tensors are loaded in the correct order - for name, data_torch in self.get_tensors(): - if "mlp.experts.down_proj_blocks" in name: - blocks0 = data_torch - elif "mlp.experts.down_proj_scales" in name: - new_name = self.map_tensor_name(name.replace("_scales", ".weight")) - self.repack_mxfp4(new_name, blocks0, data_torch) - elif "mlp.experts.gate_up_proj_blocks" in name: - blocks0, blocks1 = data_torch[:, ::2, :, :], data_torch[:, 1::2, :, :] - elif "mlp.experts.gate_up_proj_scales" in name: - scales0, scales1 = data_torch[:, ::2, :], data_torch[:, 1::2, :] - new_name_gate = self.map_tensor_name(name.replace("gate_up_proj_scales", "gate_proj.weight")) - new_name_up = self.map_tensor_name(name.replace("gate_up_proj_scales", "up_proj.weight")) - self.repack_mxfp4(new_name_gate, blocks0, scales0) - self.repack_mxfp4(new_name_up, blocks1, scales1) - return [] - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if "sinks" in name: - name += ".weight" - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # correct naming for down_proj - if "down_proj" in name: - if name.endswith("_bias"): - name = name.replace("down_proj_bias", "down_proj.bias") - elif "_blocks" not in name and "_scales" not in name: - logger.warning(f"{name} is not in MXFP4, performance may be degraded") - name = name.replace("down_proj", "down_proj.weight") - data_torch = data_torch.transpose(-1, -2) - else: - # otherwise, it should already be repacked to ggml MXFP4 format - return - - # split the gate_up into gate and up - if "gate_up_proj" in name: - if name.endswith("_bias"): - name_up = name.replace("gate_up_proj_bias", "up_proj.bias") - name_gate = name.replace("gate_up_proj_bias", "gate_proj.bias") - gate_proj_bias, up_proj_bias = data_torch[..., ::2], data_torch[..., 1::2] - yield from super().modify_tensors(gate_proj_bias, name_gate, bid) - yield from super().modify_tensors(up_proj_bias, name_up, bid) - elif "_blocks" not in name and "_scales" not in name: - logger.warning(f"{name} is not in MXFP4, performance may be degraded") - name_up = name.replace("gate_up_proj", "up_proj.weight") - name_gate = name.replace("gate_up_proj", "gate_proj.weight") - data_torch = data_torch.transpose(-1, -2) - gate_proj_weight, up_proj_weight = data_torch[:, ::2, :], data_torch[:, 1::2, :] - yield from super().modify_tensors(gate_proj_weight, name_gate, bid) - yield from super().modify_tensors(up_proj_weight, name_up, bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - def set_vocab(self): - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - self.gguf_writer.add_expert_feed_forward_length(self.hparams["intermediate_size"]) - - -@ModelBase.register("Lfm2ForCausalLM", "LFM2ForCausalLM") -class LFM2Model(TextModel): - model_arch = gguf.MODEL_ARCH.LFM2 - - def _add_feed_forward_length(self): - ff_dim = self.find_hparam(["block_ff_dim", "intermediate_size"]) - auto_adjust_ff_dim = self.hparams["block_auto_adjust_ff_dim"] - ffn_dim_multiplier = self.hparams["block_ffn_dim_multiplier"] - multiple_of = self.hparams["block_multiple_of"] - - if auto_adjust_ff_dim: - ff_dim = int(2 * ff_dim / 3) - # custom dim factor multiplier - if ffn_dim_multiplier is not None: - ff_dim = int(ffn_dim_multiplier * ff_dim) - ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of) - - self.gguf_writer.add_feed_forward_length(ff_dim) - - def set_gguf_parameters(self): - # set num_key_value_heads only for attention layers - self.hparams["num_key_value_heads"] = [ - self.hparams["num_key_value_heads"] if layer_type != "conv" else 0 - for layer_type in self.hparams["layer_types"] - ] - - super().set_gguf_parameters() - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"]) - self.gguf_writer.add_layer_norm_rms_eps(self.hparams["norm_eps"]) - self._add_feed_forward_length() - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if ConformerAudioModel.is_audio_tensor(name): - # skip multimodal tensors - return None - - name = name.replace("lfm.", "model.") # audio - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # conv op requires 2d tensor - if 'conv.conv' in name: - data_torch = data_torch.squeeze(1) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Lfm2Model") -class LFM2ColBertModel(LFM2Model): - model_arch = gguf.MODEL_ARCH.LFM2 - dense_tensor_name = "dense_2" - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if not name.startswith(self.dense_tensor_name): - name = "model." + name - - yield from super().modify_tensors(data_torch, name, bid) - - def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]: - # dense tensor is stored in a separate safetensors file - from safetensors.torch import load_file - tensors_file = self.dir_model / "1_Dense" / "model.safetensors" - assert tensors_file.is_file() - tensor = load_file(tensors_file)["linear.weight"] - self.gguf_writer.add_embedding_length_out(tensor.shape[0]) - yield f"{self.dense_tensor_name}.weight", tensor.clone() - - -@ModelBase.register("Lfm2MoeForCausalLM") -class LFM2MoeModel(TextModel): - model_arch = gguf.MODEL_ARCH.LFM2MOE - - def set_gguf_parameters(self): - # set num_key_value_heads only for attention layers - self.hparams["num_key_value_heads"] = [ - self.hparams["num_key_value_heads"] if layer_type == "full_attention" else 0 - for layer_type in self.hparams["layer_types"] - ] - - super().set_gguf_parameters() - - self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"]) - self.gguf_writer.add_leading_dense_block_count(self.hparams["num_dense_layers"]) - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) - - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"]) - - # cache for experts weights for merging - _experts_cache: dict[int, dict[str, Tensor]] = {} - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.endswith(".expert_bias"): - name = name.replace(".expert_bias", ".expert_bias.bias") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # conv op requires 2d tensor - if 'conv.conv' in name: - data_torch = data_torch.squeeze(1) - - # merge expert weights - if 'experts' in name: - n_experts = self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - expert_cache = self._experts_cache.setdefault(bid, {}) - expert_cache[name] = data_torch - expert_weights = ["w1", "w2", "w3"] - - # not enough expert weights to merge - if len(expert_cache) < n_experts * len(expert_weights): - return - - for w_name in expert_weights: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.feed_forward.experts.{xid}.{w_name}.weight" - datas.append(expert_cache[ename]) - del expert_cache[ename] - - data_torch = torch.stack(datas, dim=0) - merged_name = f"layers.{bid}.feed_forward.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - - del self._experts_cache[bid] - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - assert not self._experts_cache - - -@ModelBase.register("Lfm2VlForConditionalGeneration") -class LFM2VLModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - # TODO(tarek): for dynamic resolution image_size is not specified, setting here for compatibility - self.hparams_vision["image_size"] = 256 - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LFM2) - self.gguf_writer.add_vision_attention_layernorm_eps(self.find_vparam(["layer_norm_eps"])) - self.gguf_writer.add_vision_projector_scale_factor(self.global_config.get("downsample_factor", 2)) - self.gguf_writer.add_vision_use_gelu(True) - # python notation, e.g. for vision_feature_layer == -1, we pick last layer -> vision_feature_layers_to_drop = 0 - vision_feature_layers_to_drop = -(self.global_config.get("vision_feature_layer", -1) + 1) - self.gguf_writer.add_vision_block_count(self.find_vparam(self.n_block_keys) - vision_feature_layers_to_drop) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - name = name.replace("model.vision_tower.", "vision_tower.") - name = name.replace("model.multi_modal_projector.", "multi_modal_projector.") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "patch_embedding.weight" in name: - data_torch = data_torch.view(data_torch.shape[0], 16, 16, 3).permute(0, 3, 1, 2) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Lfm2AudioForConditionalGeneration") -class LFM2AudioModel(ConformerAudioModel): - has_vision_encoder = False - has_audio_encoder = True - model_name = "Lfm2AudioEncoder" - - def get_audio_config(self) -> dict[str, Any] | None: - return self.global_config.get("encoder") - - def set_gguf_parameters(self): - assert self.hparams_audio is not None - self.hparams_audio["hidden_size"] = self.hparams_audio["d_model"] - self.hparams_audio["intermediate_size"] = self.hparams_audio["d_model"] - self.hparams_audio["num_attention_heads"] = self.hparams_audio["n_heads"] - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LFM2A) - self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["feat_in"]) - self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # skip language model tensors - if name.startswith("lfm."): - return None - - # for training only - if any(p in name for p in ["audio_loss_weight"]): - return None - - # for audio output - if any(p in name for p in ["codebook_offsets", "depth_embeddings", "depth_linear", "depthformer"]): - return None - - return super().filter_tensors(item) - - -@ModelBase.register("GraniteSpeechForConditionalGeneration") -class GraniteSpeechMmprojModel(MmprojModel): - has_vision_encoder = False - has_audio_encoder = True - - _batch_norm_tensors: list[dict[str, Tensor]] | None = None - - def get_audio_config(self) -> dict[str, Any] | None: - return self.global_config.get("encoder_config") - - def set_gguf_parameters(self): - assert self.hparams_audio is not None - a = self.hparams_audio - a["hidden_size"] = a["hidden_dim"] - a["intermediate_size"] = a["hidden_dim"] * a["feedforward_mult"] - a["num_attention_heads"] = a["num_heads"] - a["num_hidden_layers"] = a["num_layers"] - - super().set_gguf_parameters() - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.GRANITE_SPEECH) - self.gguf_writer.add_audio_num_mel_bins(a["input_dim"]) - self.gguf_writer.add_audio_attention_layernorm_eps(1e-5) - self.gguf_writer.add_audio_chunk_size(a["context_size"]) - self.gguf_writer.add_audio_conv_kernel_size(a["conv_kernel_size"]) - self.gguf_writer.add_audio_max_pos_emb(a["max_pos_emb"]) - - p = self.global_config - self.gguf_writer.add_audio_projector_window_size(p["window_size"]) - self.gguf_writer.add_audio_projector_downsample_rate(p["downsample_rate"]) - self.gguf_writer.add_audio_projector_head_count(p["projector_config"]["num_attention_heads"]) - - def tensor_force_quant(self, name, new_name, bid, n_dims): - if "encoder" in name or "projector" in name: - if ".conv" in name and ".weight" in name: - return gguf.GGMLQuantizationType.F32 - return super().tensor_force_quant(name, new_name, bid, n_dims) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - if "attention_dists" in name or "num_batches_tracked" in name: - return None - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # fold running_mean, running_var and eps into weight and bias for batch_norm - if "batch_norm" in name and "encoder.layers." in name: - if self._batch_norm_tensors is None: - self._batch_norm_tensors = [{} for _ in range(self.block_count)] - assert bid is not None - self._batch_norm_tensors[bid][name] = data_torch - if len(self._batch_norm_tensors[bid]) < 4: - return - prefix = f"encoder.layers.{bid}.conv.batch_norm" - weight = self._batch_norm_tensors[bid][f"{prefix}.weight"] - bias = self._batch_norm_tensors[bid][f"{prefix}.bias"] - running_mean = self._batch_norm_tensors[bid][f"{prefix}.running_mean"] - running_var = self._batch_norm_tensors[bid][f"{prefix}.running_var"] - eps = 1e-5 - a = weight / torch.sqrt(running_var + eps) - b = bias - running_mean * a - yield from super().modify_tensors(a, f"encoder.layers.{bid}.conv.batch_norm.weight", bid) - yield from super().modify_tensors(b, f"encoder.layers.{bid}.conv.batch_norm.bias", bid) - return - - if ".attn.to_kv.weight" in name: - k_weight, v_weight = data_torch.chunk(2, dim=0) - yield from super().modify_tensors(k_weight, name.replace("to_kv", "to_k"), bid) - yield from super().modify_tensors(v_weight, name.replace("to_kv", "to_v"), bid) - return - - if ("up_conv" in name or "down_conv" in name) and name.endswith(".weight"): - if data_torch.ndim == 3 and data_torch.shape[2] == 1: - data_torch = data_torch.squeeze(2) - - if "depth_conv" in name and name.endswith(".weight"): - if data_torch.ndim == 3 and data_torch.shape[1] == 1: - data_torch = data_torch.squeeze(1) - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Lfm25AudioTokenizer") -class LFM25AudioTokenizer(LFM2Model): - model_arch = gguf.MODEL_ARCH.LFM2 - - def set_vocab(self): - self._set_vocab_none() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_sliding_window(self.hparams["sliding_window"]) - self.gguf_writer.add_embedding_length_out(self.hparams["output_size"]) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # skip language model tensors - if name == "istft.window" or name.startswith("emb.emb"): - return None - - if name.startswith("lin"): - name = name.replace("lin", "dense_2_out") - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("SmallThinkerForCausalLM") -class SmallThinkerModel(TextModel): - model_arch = gguf.MODEL_ARCH.SMALLTHINKER - - def set_gguf_parameters(self): - super().set_gguf_parameters() - if (n_experts := self.hparams.get("moe_num_primary_experts")) is not None: - self.gguf_writer.add_expert_count(n_experts) - if (n_experts_used := self.hparams.get("moe_num_active_primary_experts")) is not None: - self.gguf_writer.add_expert_used_count(n_experts_used) - if (moe_intermediate_size := self.hparams.get("moe_ffn_hidden_size")) is not None: - self.gguf_writer.add_expert_feed_forward_length(moe_intermediate_size) - self.gguf_writer.add_feed_forward_length(moe_intermediate_size) - logger.info(f"gguf: expert feed forward length = {moe_intermediate_size}") - if (self.hparams.get('moe_primary_router_apply_softmax')): - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SOFTMAX) - else: - self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID) - - sliding_window_layout = self.hparams.get("sliding_window_layout") - if sliding_window_layout: - for i in sliding_window_layout: - if i != 0: - sliding_window = self.hparams.get("sliding_window_size") - if sliding_window: - self.gguf_writer.add_sliding_window(sliding_window) - break - - _experts: list[dict[str, Tensor]] | None = None - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # process the experts separately - if name.find("experts") != -1: - n_experts = self.hparams.get("moe_num_primary_experts") or self.find_hparam(["num_local_experts", "num_experts"]) - assert bid is not None - - if self._experts is None: - self._experts = [{} for _ in range(self.block_count)] - - self._experts[bid][name] = data_torch - - if len(self._experts[bid]) >= n_experts * 3: - # merge the experts into a single 3d tensor - for w_name in ["down", "gate", "up"]: - datas: list[Tensor] = [] - - for xid in range(n_experts): - ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{w_name}.weight" - datas.append(self._experts[bid][ename]) - del self._experts[bid][ename] - - data_torch = torch.stack(datas, dim=0) - - merged_name = f"model.layers.{bid}.block_sparse_moe.experts.{w_name}.weight" - - yield from super().modify_tensors(data_torch, merged_name, bid) - return - else: - return - - yield from super().modify_tensors(data_torch, name, bid) - - def prepare_tensors(self): - super().prepare_tensors() - - if self._experts is not None: - # flatten `list[dict[str, Tensor]]` into `list[str]` - experts = [k for d in self._experts for k in d.keys()] - if len(experts) > 0: - raise ValueError(f"Unprocessed experts: {experts}") - - -@ModelBase.register("ModernBertModel", "ModernBertForMaskedLM", "ModernBertForSequenceClassification") -class ModernBertModel(BertModel): - model_arch = gguf.MODEL_ARCH.MODERN_BERT - - def set_vocab(self): - self.gguf_writer.add_add_bos_token(True) - self.gguf_writer.add_add_eos_token(True) - self.gguf_writer.add_add_sep_token(True) - self._set_vocab_gpt2() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_sliding_window(self.hparams["local_attention"]) - if (sliding_window_pattern := self.hparams.get("global_attn_every_n_layers")) is not None: - self.gguf_writer.add_sliding_window_pattern(sliding_window_pattern) - self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE) - self.gguf_writer.add_vocab_size(self.hparams["vocab_size"]) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if name.startswith("model."): - name = name[6:] - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if self.cls_out_labels: - # For BertForSequenceClassification (direct projection layer) - if name == "classifier.weight": - name = "classifier.out_proj.weight" - - if name == "classifier.bias": - name = "classifier.out_proj.bias" - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("ApertusForCausalLM") -class ApertusModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.APERTUS - undo_permute = False - - _alpha_n = {} - _alpha_p = {} - _beta = {} - _eps = {} - - def modify_tensors(self, data_torch, name, bid): - # Handle xIELU activation parameters - n_layers = self.hparams["num_hidden_layers"] - if name.endswith(".act_fn.alpha_n"): - self._alpha_n[bid] = data_torch.to("cpu").float().item() - if (len(self._alpha_n) == n_layers): - self.gguf_writer.add_xielu_alpha_n([self._alpha_n[k] for k in sorted(self._alpha_n)]) - return - if name.endswith(".act_fn.alpha_p"): - self._alpha_p[bid] = data_torch.to("cpu").float().item() - if (len(self._alpha_p) == n_layers): - self.gguf_writer.add_xielu_alpha_p([self._alpha_p[k] for k in sorted(self._alpha_p)]) - return - if name.endswith(".act_fn.beta"): - self._beta[bid] = data_torch.to("cpu").float().item() - if (len(self._beta) == n_layers): - self.gguf_writer.add_xielu_beta([self._beta[k] for k in sorted(self._beta)]) - return - if name.endswith(".act_fn.eps"): - self._eps[bid] = data_torch.to("cpu").float().item() - if (len(self._eps) == n_layers): - self.gguf_writer.add_xielu_eps([self._eps[k] for k in sorted(self._eps)]) - return - - yield from super().modify_tensors(data_torch, name, bid) - - -class MistralModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.MISTRAL3 - model_name = "Mistral" - hf_arch = "" - is_mistral_format = True - undo_permute = False - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # for compatibility, we use LLAMA arch for older models - # TODO: remove this once everyone migrates to newer version of llama.cpp - if "llama_4_scaling" not in self.hparams: - self.model_arch = gguf.MODEL_ARCH.LLAMA - self.gguf_writer.arch = gguf.MODEL_ARCH_NAMES[self.model_arch] - self.gguf_writer.add_architecture() - self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count) - - def dequant_model(self): - # transform quantization config into HF format - quant_config = self.hparams.get("quantization") - if quant_config is not None: - assert quant_config["qformat_weight"] == "fp8_e4m3" - self.hparams["quantization_config"] = { - "activation_scheme": "static", - "quant_method": "fp8", - "weight_block_size": None, - } - return super().dequant_model() - - @staticmethod - def get_community_chat_template(vocab: MistralVocab, templates_dir: Path, is_mistral_format: bool): - assert TokenizerVersion is not None and Tekkenizer is not None and SentencePieceTokenizer is not None, _mistral_import_error_msg - assert isinstance(vocab.tokenizer, (Tekkenizer, SentencePieceTokenizer)), ( - f"Expected Tekkenizer or SentencePieceTokenizer, got {type(vocab.tokenizer)}" - ) - - if vocab.tokenizer.version == TokenizerVersion.v1: - return "mistral-v1" - elif vocab.tokenizer.version == TokenizerVersion.v3 and vocab.tokenizer_type == MistralTokenizerType.spm: - return "mistral-v3" - elif vocab.tokenizer.version == TokenizerVersion.v3 and vocab.tokenizer_type == MistralTokenizerType.tekken: - return "mistral-v3-tekken" - elif vocab.tokenizer.version == TokenizerVersion.v7 and vocab.tokenizer_type == MistralTokenizerType.spm: - return "mistral-v7" - elif vocab.tokenizer.version == TokenizerVersion.v7 and vocab.tokenizer_type == MistralTokenizerType.tekken: - return "mistral-v7-tekken" - elif vocab.tokenizer.version == TokenizerVersion.v11: - template_file = "Mistral-Small-3.2-24B-Instruct-2506.jinja" - elif vocab.tokenizer.version == TokenizerVersion.v13: - template_file = "unsloth-mistral-Devstral-Small-2507.jinja" - else: - err_message = f"Unknown tokenizer type: {vocab.tokenizer_type} and version {vocab.tokenizer.version}" - if is_mistral_format: - err_message += ( - " . Please pass --disable-mistral-community-chat-template argument to the CLI " - "if you want to skip this error and use the Mistral official `mistral-common` pre-processing library." - ) - raise ValueError(err_message) - - template_path = templates_dir / template_file - if not template_path.exists(): - raise FileNotFoundError(f"Template file not found: {template_path}") - - with open(template_path, "r", encoding="utf-8") as f: - template = f.read() - - return template - - def set_gguf_parameters(self): - super().set_gguf_parameters() - MistralModel.set_mistral_config(self.gguf_writer, self.hparams) - - @staticmethod - def set_mistral_config(gguf_writer: gguf.GGUFWriter, hparams: dict): - if "yarn" in hparams: - yarn_params = hparams["yarn"] - mscale_all_dim = 1.0 if not yarn_params["apply_scale"] else 0.0 - gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.YARN) - gguf_writer.add_rope_scaling_factor(yarn_params["factor"]) - gguf_writer.add_rope_scaling_yarn_beta_fast(yarn_params["beta"]) - gguf_writer.add_rope_scaling_yarn_beta_slow(yarn_params["alpha"]) - gguf_writer.add_rope_scaling_yarn_log_mul(mscale_all_dim) - gguf_writer.add_rope_scaling_orig_ctx_len(yarn_params["original_max_position_embeddings"]) - - if "llama_4_scaling" in hparams: - gguf_writer.add_attn_temperature_scale(hparams["llama_4_scaling"]["beta"]) - - -class MistralMoeModel(DeepseekV2Model): - model_arch = gguf.MODEL_ARCH.DEEPSEEK2 - model_name = "Mistral" - hf_arch = "" - is_mistral_format = True - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - logger.info("Using MistralMoeModel") - # remap hparams from Mistral MoE format to DeepseekV2 format - # we do this way to be able to reuse DeepseekV2Model set_gguf_parameters logic - # ref: https://github.com/vllm-project/vllm/blob/b294e28db2c5dee61bc25157664edcada8b90b31/vllm/transformers_utils/configs/mistral.py - config = self.hparams - # Mistral key -> HF key - config_mapping = { - "dim": "hidden_size", - "norm_eps": "rms_norm_eps", - "n_kv_heads": "num_key_value_heads", - "n_layers": "num_hidden_layers", - "n_heads": "num_attention_heads", - "hidden_dim": "intermediate_size", - } - # HF key -> (Mistral key, default value) - top_level_mapping_with_default = { - "model_type": ("model_type", "transformer"), - "hidden_act": ("activation", "silu"), - "tie_word_embeddings": ("tied_embeddings", False), - "max_seq_len": ("max_seq_len", config.get("max_position_embeddings", 128_000)), - "max_position_embeddings": ("max_position_embeddings", 128_000), - } - # mapping top-level keys - for key, new_key in config_mapping.items(): - if key in config: - config[new_key] = config[key] - for new_key, (key, default_value) in top_level_mapping_with_default.items(): - config[new_key] = config.get(key, default_value) - # mapping MoE-specific keys - moe_config_map = { - "route_every_n": "moe_layer_freq", - "first_k_dense_replace": "first_k_dense_replace", - "num_experts_per_tok": "num_experts_per_tok", - "num_experts": "n_routed_experts", - "expert_hidden_dim": "moe_intermediate_size", - "routed_scale": "routed_scaling_factor", - "num_shared_experts": "n_shared_experts", - "num_expert_groups": "n_group", - "num_expert_groups_per_tok": "topk_group", - } - moe = config["moe"] - for key, new_key in moe_config_map.items(): - if key in moe: - config[new_key] = moe[key] - # provide missing values - config["topk_method"] = None - config["norm_topk_prob"] = True - config["scoring_func"] = "softmax" - - def set_vocab(self): - self._set_vocab_mistral() - - def set_gguf_parameters(self): - super().set_gguf_parameters() - MistralModel.set_mistral_config(self.gguf_writer, self.hparams) - yarn_params = self.hparams["yarn"] - self.gguf_writer.add_attn_temperature_length(yarn_params["original_max_position_embeddings"]) - - # [TAG_DEEPSEEK2_YARN_LOG_MUL_FIX] - # note: for legacy reasons, this is not consistent with the other usages of self.gguf_writer.add_rope_scaling_yarn_log_mul - # ref https://github.com/ggml-org/llama.cpp/pull/17945 - self.gguf_writer.add_rope_scaling_yarn_log_mul(0.1) # mscale_all_dim * 0.1 - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # rename certain tensors so that we can reuse DeepseekV2Model modify_tensors logic - if name.endswith(".qscale_act"): - name = name.replace(".qscale_act", ".input_scale") - if name.endswith(".qscale_weight"): - name = name.replace(".qscale_weight", ".weight_scale") - if ".wkv_b." in name: - name = name.replace(".wkv_b.", ".kv_b_proj.") - if ".experts." in name: - name = name.replace(".experts.", ".mlp.experts.") - name = name.replace(".w1.", ".gate_proj.") - name = name.replace(".w2.", ".down_proj.") - name = name.replace(".w3.", ".up_proj.") - name = "model." + name - - return super().filter_tensors((name, gen)) - - -class PixtralModel(LlavaVisionModel): - model_name = "Pixtral" - hf_arch = "" - is_mistral_format = True - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.PIXTRAL) - - self.gguf_writer.add_vision_attention_layernorm_eps( - self.find_hparam(["norm_eps"]) - ) - self.gguf_writer.add_rope_freq_base(self.find_vparam(["rope_theta"])) - - self.gguf_writer.add_vision_use_silu(True) - - # spatial_merge_size - if self.find_vparam(["mm_projector_id"], optional=True) == "patch_merge": - self.gguf_writer.add_vision_spatial_merge_size( - self.find_vparam(["spatial_merge_size"]) - ) - - def map_tensor_name(self, name: str, try_suffixes: Sequence[str] = (".weight", ".bias")) -> str: - if name == "vision_language_adapter.w_in.weight": - return "mm.1.weight" - elif name == "vision_language_adapter.w_in.bias": - return "mm.1.bias" - elif name == "vision_language_adapter.w_out.weight": - return "mm.2.weight" - elif name == "vision_language_adapter.w_out.bias": - return "mm.2.bias" - return super().map_tensor_name(name, try_suffixes) - - -@ModelBase.register("LightOnOCRForConditionalGeneration") -class LightOnOCRVisionModel(LlavaVisionModel): - is_mistral_format = False - use_break_tok = False - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.LIGHTONOCR) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - name = name.replace("model.vision_encoder.", "vision_tower.") - name = name.replace("model.vision_projection.", "multi_modal_projector.") - - return super().filter_tensors((name, gen)) - - -@ModelBase.register("KimiVLForConditionalGeneration") -class KimiVLModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["image_size"] = 64 * 14 # for compatibility - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.KIMIVL) - self.gguf_writer.add_vision_use_gelu(True) - self.gguf_writer.add_vision_projector_scale_factor(2) - # eps is the same as pytorch's default value - assert self.hparams_vision is not None - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-5)) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - is_vision_tensor = "vision_tower" in name or "multi_modal_projector" in name - - if not is_vision_tensor: - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - if "pos_emb.weight" in name: - data_torch = data_torch.view(data_torch.shape[0] * data_torch.shape[1], data_torch.shape[2]) - - if "wqkv" in name: - split_dim = 0 if "weight" in name else -1 - wq, wk, wv = data_torch.chunk(3, dim=split_dim) - yield from super().modify_tensors(wq, name.replace("wqkv", "wq"), bid) - yield from super().modify_tensors(wk, name.replace("wqkv", "wk"), bid) - yield from super().modify_tensors(wv, name.replace("wqkv", "wv"), bid) - else: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("KimiK25ForConditionalGeneration") -class KimiK25Model(MmprojModel): - """Kimi-K2.5 with MoonViT3d vision encoder""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - assert self.hparams_vision is not None, "Kimi-K2.5 requires vision_config in model config" - - self.merge_kernel_size = tuple(self.hparams_vision.get("merge_kernel_size", [2, 2])) - self.patch_size = self.hparams_vision.get("patch_size", 14) - - # Set image_size for compatibility with base class - # Use position embedding dimensions as image_size reference - pos_emb_h = self.hparams_vision.get("init_pos_emb_height", 64) - self.hparams_vision["image_size"] = pos_emb_h * self.patch_size - - def set_gguf_parameters(self): - # Base class MmprojModel.set_gguf_parameters() already writes: - # - vision_block_count, vision_head_count, vision_embedding_length - # - vision_feed_forward_length, vision_patch_size, image_mean, image_std - # via find_vparam() which handles the vt_* prefixed keys in Kimi-K2.5's config - super().set_gguf_parameters() - assert self.hparams_vision is not None - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.KIMIK25) - - # Position embedding parameters (for interpolation) - self.gguf_writer.add_uint32("vision.pos_emb_height", self.hparams_vision.get("init_pos_emb_height", 64)) - self.gguf_writer.add_uint32("vision.pos_emb_width", self.hparams_vision.get("init_pos_emb_width", 64)) - self.gguf_writer.add_uint32("vision.pos_emb_time", self.hparams_vision.get("init_pos_emb_time", 4)) - - # Projector parameters - self.gguf_writer.add_vision_use_gelu(self.hparams_vision.get("projector_hidden_act", "gelu") == "gelu") - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("projector_ln_eps", 1e-5)) - self.gguf_writer.add_vision_projector_scale_factor(self.merge_kernel_size[0]) - - # Image size limits - # Note: in_patch_limit is for images, in_patch_limit_each_frame is for video (not supported yet) - in_patch_limit = self.preprocessor_config.get("in_patch_limit", 16384) - min_patches = 8 # reasonable minimum - pixels_per_patch = self.patch_size ** 2 - self.gguf_writer.add_vision_min_pixels(min_patches * pixels_per_patch) - self.gguf_writer.add_vision_max_pixels(in_patch_limit * pixels_per_patch) - - @staticmethod - def permute(weights: Tensor, n_head: int) -> Tensor: - out_dim, in_dim = weights.shape - head_dim = out_dim // n_head - w = weights.reshape(n_head, head_dim // 4, 2, 2, in_dim) - w = w.permute(0, 2, 1, 3, 4) - return w.reshape(out_dim, in_dim) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Only process vision and projector tensors - is_vision = any(x in name for x in ["vision_tower", "mm_projector"]) - - if not is_vision: - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - assert self.hparams_vision is not None - n_head = self.hparams_vision.get("num_attention_heads", 16) - - # Permute Q/K weights/biases from interleaved to split RoPE format - # This allows using build_rope_2d at runtime without post-permutation. - if "wqkv" in name: - out_dim = data_torch.shape[0] - qkv_dim = out_dim // 3 - head_dim = qkv_dim // n_head - - if "weight" in name: - wq, wk, wv = data_torch[:qkv_dim, :], data_torch[qkv_dim:2 * qkv_dim, :], data_torch[2 * qkv_dim:, :] - wq = self.permute(wq, n_head) - wk = self.permute(wk, n_head) - data_torch = torch.cat([wq, wk, wv], dim=0) - elif "bias" in name: - bq, bk, bv = data_torch[:qkv_dim], data_torch[qkv_dim:2 * qkv_dim], data_torch[2 * qkv_dim:] - bq = bq.reshape(n_head, head_dim // 4, 2, 2).permute(0, 2, 1, 3).reshape(-1) - bk = bk.reshape(n_head, head_dim // 4, 2, 2).permute(0, 2, 1, 3).reshape(-1) - data_torch = torch.cat([bq, bk, bv], dim=0) - - # Temporal embeddings: (T, 1, C) → (T, C) - if "pos_emb.time_weight" in name: - T, _, C = data_torch.shape - data_torch = data_torch.reshape(T, C) - - # PatchMergerMLP tensor name mapping - # proj.0.weight → proj.linear_1.weight - # proj.2.weight → proj.linear_2.weight - if "mm_projector.proj.0." in name: - name = name.replace(".proj.0.", ".proj.linear_1.") - elif "mm_projector.proj.2." in name: - name = name.replace(".proj.2.", ".proj.linear_2.") - - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("CogVLMForCausalLM") -class CogVLMVisionModel(MmprojModel): - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.COGVLM) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if not name.startswith("model.vision."): - return None - - return super().filter_tensors(item) - - -@ModelBase.register("CogVLMForCausalLM") -class CogVLMModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.COGVLM - - -@ModelBase.register("JanusForConditionalGeneration") -class JanusProModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.LLAMA # reuse Llama arch - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip vision, aligner, and generation tensors - skip_prefixes = ( - 'model.vision_model.', - 'model.aligner.', - 'model.vqmodel.', - 'model.generation_embeddings.', - 'model.generation_aligner.', - 'model.generation_head.', - ) - if name.startswith(skip_prefixes): - return None - - return super().filter_tensors(item) - - -@ModelBase.register("JanusForConditionalGeneration") -class JanusProVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - if "intermediate_size" not in self.hparams_vision: - mlp_ratio = self.hparams_vision.get("mlp_ratio") - hidden_size = self.hparams_vision.get("hidden_size") - if mlp_ratio is not None and hidden_size is not None: - self.hparams_vision["intermediate_size"] = int(round(hidden_size * mlp_ratio)) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - assert self.hparams_vision is not None - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.JANUS_PRO) - - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6)) - - hidden_act = str(self.hparams_vision.get("hidden_act", "")).lower() - if hidden_act == "gelu": - self.gguf_writer.add_vision_use_gelu(True) - elif hidden_act == "silu": - self.gguf_writer.add_vision_use_silu(True) - - def _map_aligner_tensor(self, data_torch: Tensor, name: str) -> Iterable[tuple[str, Tensor]]: - """Map aligner tensors to projector format""" - suffix = ".bias" if name.endswith(".bias") else ".weight" - - if name.startswith("model.aligner."): - local_name = name[len("model.aligner."):] - elif name.startswith("aligner."): - local_name = name[len("aligner."):] - else: - raise ValueError(f"Unsupported Janus aligner prefix: {name}") - - if local_name.startswith("fc1."): - mm_index = 0 - elif local_name.startswith("hidden_layers."): - parts = local_name.split(".", 2) - if len(parts) < 3: - raise ValueError(f"Unexpected Janus aligner tensor name: {name}") - mm_index = int(parts[1]) + 1 - else: - raise ValueError(f"Unsupported Janus aligner tensor: {name}") - - tensor_name = self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, mm_index, suffix=suffix) - return [(tensor_name, data_torch)] - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip generation-related components - skip_generation_prefixes = ( - 'model.vqmodel.', - 'vqmodel.', - 'model.generation_embeddings.', - 'generation_embeddings.', - 'model.generation_aligner.', - 'generation_aligner.', - 'model.generation_head.', - 'generation_head.', - ) - if name.startswith(skip_generation_prefixes): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Handle aligner tensors - if name.startswith(('model.aligner.', 'aligner.')): - yield from self._map_aligner_tensor(data_torch, name) - return - - # Handle vision tensors - if name.startswith(('model.vision_model.', 'vision_model.')): - yield from super().modify_tensors(data_torch, name, bid) - return - - return - - -@ModelBase.register("YoutuVLForConditionalGeneration") -class YoutuVLVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["image_size"] = self.hparams_vision.get("image_size", 560) - - def set_gguf_parameters(self): - super().set_gguf_parameters() - - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.YOUTUVL) - self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams.get("layer_norm_eps", 1e-6)) - - # Handle activation function - hidden_act = str(self.hparams.get("hidden_act", "gelu_pytorch_tanh")).lower() - if hidden_act in ("gelu", "gelu_pytorch_tanh", "gelu_fast", "gelu_new", "gelu_accurate"): - self.gguf_writer.add_vision_use_gelu(True) - elif hidden_act == "silu": - self.gguf_writer.add_vision_use_silu(True) - else: - raise ValueError(f"Unsupported activation function for YOUTUVL: {hidden_act}") - - self.gguf_writer.add_vision_spatial_merge_size(self.hparams.get("spatial_merge_size", 2)) - - window_size = self.hparams.get("window_size") - if window_size is not None: - self.gguf_writer.add_vision_window_size(window_size) - # fullatt_block_indexes contains explicit layer indices that use full attention - # e.g., [2, 5, 8, 11] means layers 2, 5, 8, 11 use full attention - # All other layers use window attention - fullatt_block_indexes = self.hparams.get("fullatt_block_indexes") - assert fullatt_block_indexes is not None, "fullatt_block_indexes is required for youtuvl" - # Store the explicit layer indices for YoutuVL (irregular pattern approach) - self.gguf_writer.add_vision_wa_layer_indexes(layers=fullatt_block_indexes) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - # Skip language model tensors - skip_prefixes = ('lm_head.', 'model.layers.', 'model.embed_tokens.', 'model.norm.') - if name.startswith(skip_prefixes): - return None - - return super().filter_tensors(item) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - # Try to map the tensor using TensorNameMap (handles vision encoder and projector) - try: - yield from super().modify_tensors(data_torch, name, bid) - except ValueError: - # If mapping fails, log warning and skip - logger.warning(f"Cannot map tensor: {name}") - return - - -@ModelBase.register("SolarOpenForCausalLM") -class SolarOpenModel(Glm4MoeModel): - model_arch = gguf.MODEL_ARCH.GLM4_MOE - - def set_vocab(self): - from transformers import AutoTokenizer - tokenizer = AutoTokenizer.from_pretrained(self.dir_model) - special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True) - tokens, toktypes, tokpre = self.get_vocab_base() - self.gguf_writer.add_tokenizer_model("gpt2") - self.gguf_writer.add_tokenizer_pre(tokpre) - self.gguf_writer.add_token_list(tokens) - self.gguf_writer.add_token_types(toktypes) - special_vocab._set_special_token("eos", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("eot", tokenizer.get_added_vocab()["<|endoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("unk", tokenizer.get_added_vocab()[""]) # ty: ignore[unresolved-attribute] - special_vocab._set_special_token("bos", tokenizer.get_added_vocab()["<|startoftext|>"]) # ty: ignore[unresolved-attribute] - special_vocab.add_to_gguf(self.gguf_writer) - - -@ModelBase.register("DotsOCRForCausalLM") -class DotsOCRVisionModel(MmprojModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.hparams_vision is not None - self.hparams_vision["image_size"] = 0 # dynamic resolution - - def set_gguf_parameters(self): - super().set_gguf_parameters() - self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.DOTSOCR) - self.gguf_writer.add_vision_min_pixels(self.preprocessor_config["min_pixels"]) - self.gguf_writer.add_vision_max_pixels(self.preprocessor_config["max_pixels"]) - self.gguf_writer.add_vision_attention_layernorm_eps(self.find_vparam(["rms_norm_eps"])) - self.gguf_writer.add_vision_projector_scale_factor(self.find_vparam(["spatial_merge_size"])) - self.gguf_writer.add_vision_use_silu(True) - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - - if not name.startswith("vision_tower."): - return None - - if "vision_tower.blocks." in name and ".mlp." in name: - # note: to avoid naming conflicts in tensor_mapping.py, we need to handle FFN renaming here - # x = F.silu(self.fc1(x)) * self.fc3(x) - # x = self.fc2(x) - # fc1 -> gate, fc2 -> down, fc3 -> up - # mapping original names to Qwen2.5 naming scheme - name = name.replace("vision_tower.blocks.", "visual.blocks.") - name = name.replace(".fc1", ".gate_proj") - name = name.replace(".fc2", ".down_proj") - name = name.replace(".fc3", ".up_proj") - - return super().filter_tensors((name, gen)) - - def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]: - yield from super().modify_tensors(data_torch, name, bid) - - -@ModelBase.register("Sarashina2VisionForCausalLM") -class Sarashina2VLTextModel(LlamaModel): - model_arch = gguf.MODEL_ARCH.LLAMA - - @classmethod - def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None: - name, gen = item - if name.startswith("llm."): - name = name.replace("llm.", "", 1) - elif name.startswith("norm."): - return None - return super().filter_tensors((name, gen)) - - -@ModelBase.register("Sarashina2VisionForCausalLM") -class Sarashina2VLVisionModel(Qwen2VLVisionModel): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.global_config['model_type'] = "qwen2_vl" - - -###### CONVERSION LOGIC ###### - - -# tree of lazy tensors -class LazyTorchTensor(gguf.LazyBase): - _tensor_type = torch.Tensor - # to keep the type-checker happy - dtype: torch.dtype - shape: torch.Size - - # only used when converting a torch.Tensor to a np.ndarray - _dtype_map: dict[torch.dtype, type] = { - torch.float16: np.float16, - torch.float32: np.float32, - torch.uint8: np.uint8, - } - - # only used when byteswapping data. Only correct size is needed - # TODO: uncomment uint64, uint32, and uint16, ref: https://github.com/pytorch/pytorch/issues/58734 - _dtype_byteswap_map: dict[torch.dtype, type] = { - torch.float64: np.float64, - torch.float32: np.float32, - torch.bfloat16: np.float16, - torch.float16: np.float16, - torch.int64: np.int64, - # torch.uint64: np.uint64, - torch.int32: np.int32, - # torch.uint32: np.uint32, - torch.int16: np.int16, - # torch.uint16: np.uint16, - torch.int8: np.int8, - torch.uint8: np.uint8, - torch.bool: np.uint8, - torch.float8_e4m3fn: np.uint8, - torch.float8_e5m2: np.uint8, - } - - # used for safetensors slices - # ref: https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/src/lib.rs#L1046 - # TODO: uncomment U64, U32, and U16, ref: https://github.com/pytorch/pytorch/issues/58734 - _dtype_str_map: dict[str, torch.dtype] = { - "F64": torch.float64, - "F32": torch.float32, - "BF16": torch.bfloat16, - "F16": torch.float16, - # "U64": torch.uint64, - "I64": torch.int64, - # "U32": torch.uint32, - "I32": torch.int32, - # "U16": torch.uint16, - "I16": torch.int16, - "U8": torch.uint8, - "I8": torch.int8, - "BOOL": torch.bool, - "F8_E4M3": torch.float8_e4m3fn, - "F8_E5M2": torch.float8_e5m2, - } - - def numpy(self) -> gguf.LazyNumpyTensor: - dtype = self._dtype_map[self.dtype] - return gguf.LazyNumpyTensor( - meta=gguf.LazyNumpyTensor.meta_with_dtype_and_shape(dtype, self.shape), - args=(self,), - func=(lambda s: s.numpy()) - ) - - @classmethod - def meta_with_dtype_and_shape(cls, dtype: torch.dtype, shape: tuple[int, ...]) -> Tensor: - return torch.empty(size=shape, dtype=dtype, device="meta") +import argparse +import logging +import os +import sys +from pathlib import Path - @classmethod - def from_safetensors_slice(cls, st_slice: Any) -> Tensor: - dtype = cls._dtype_str_map[st_slice.get_dtype()] - shape: tuple[int, ...] = tuple(st_slice.get_shape()) - lazy = cls(meta=cls.meta_with_dtype_and_shape(dtype, shape), args=(st_slice,), func=lambda s: s[...] if len(s.get_shape()) == 0 else s[:]) - return cast(torch.Tensor, lazy) +import torch - @classmethod - def from_local_tensor(cls, t: gguf.utility.LocalTensor) -> Tensor: - def load_tensor(tensor: gguf.utility.LocalTensor) -> Tensor: - def byteswap_tensor(tensor: np.ndarray, dtype: type) -> np.ndarray: - if sys.byteorder == 'big': - # switch data back to big endian - tensor = tensor.view(dtype).byteswap(inplace=False) - return tensor - dtype = cls._dtype_str_map[tensor.dtype] - numpy_dtype = cls._dtype_byteswap_map[dtype] - return torch.from_numpy(byteswap_tensor(tensor.mmap_bytes(), numpy_dtype)).view(dtype).reshape(tensor.shape) - dtype = cls._dtype_str_map[t.dtype] - shape = t.shape - lazy = cls(meta=cls.meta_with_dtype_and_shape(dtype, shape), args=(t,), func=lambda r: load_tensor(r)) - return cast(torch.Tensor, lazy) +if 'NO_LOCAL_GGUF' not in os.environ: + sys.path.insert(1, str(Path(__file__).parent / 'gguf-py')) +import gguf - @classmethod - def from_remote_tensor(cls, remote_tensor: gguf.utility.RemoteTensor): - def byteswap_tensor(tensor: np.ndarray, dtype: type) -> np.ndarray: - if sys.byteorder == 'big': - # switch data back to big endian - tensor = tensor.view(dtype).byteswap(inplace=False) - return tensor - dtype = cls._dtype_str_map[remote_tensor.dtype] - numpy_dtype = cls._dtype_byteswap_map[dtype] - shape = remote_tensor.shape - meta = cls.meta_with_dtype_and_shape(dtype, shape) - lazy = cls(meta=meta, args=(remote_tensor,), func=lambda r: torch.from_numpy(byteswap_tensor(np.frombuffer(r.data(), dtype=numpy_dtype), numpy_dtype)).view(dtype).reshape(shape)) - return cast(torch.Tensor, lazy) +from conversion import ( + ModelBase, + ModelType, + get_model_architecture, + get_model_class, + logger, + print_registered_models, + _mistral_common_installed, + _mistral_import_error_msg, +) - @classmethod - def __torch_function__(cls, func, types, args=(), kwargs=None): - del types # unused - if kwargs is None: - kwargs = {} +def split_str_to_n_bytes(split_str: str) -> int: + if split_str.endswith("K"): + n = int(split_str[:-1]) * 1000 + elif split_str.endswith("M"): + n = int(split_str[:-1]) * 1000 * 1000 + elif split_str.endswith("G"): + n = int(split_str[:-1]) * 1000 * 1000 * 1000 + elif split_str.isnumeric(): + n = int(split_str) + else: + raise ValueError(f"Invalid split size: {split_str}, must be a number, optionally followed by K, M, or G") - if func is torch.Tensor.numpy: - assert len(args) - return args[0].numpy() + if n < 0: + raise ValueError(f"Invalid split size: {split_str}, must be positive") - return cls._wrap_fn(func)(*args, **kwargs) + return n def parse_args() -> argparse.Namespace: @@ -14082,58 +147,12 @@ def parse_args() -> argparse.Namespace: return args -def split_str_to_n_bytes(split_str: str) -> int: - if split_str.endswith("K"): - n = int(split_str[:-1]) * 1000 - elif split_str.endswith("M"): - n = int(split_str[:-1]) * 1000 * 1000 - elif split_str.endswith("G"): - n = int(split_str[:-1]) * 1000 * 1000 * 1000 - elif split_str.isnumeric(): - n = int(split_str) - else: - raise ValueError(f"Invalid split size: {split_str}, must be a number, optionally followed by K, M, or G") - - if n < 0: - raise ValueError(f"Invalid split size: {split_str}, must be positive") - - return n - - -def get_model_architecture(hparams: dict[str, Any], model_type: ModelType) -> str: - # TODO @ngxson : this won't work correctly if the model has both audio & vision encoders - # maybe we should fallback to text model's arch in that case, since not many models have both - text_config = hparams.get("text_config", {}) - vision_config = hparams.get("vision_config", {}) - arch = None - if (arches := hparams.get("architectures")) is not None and len(arches) > 0: - arch = arches[0] - elif "ssm_cfg" in hparams: - # For non-hf Mamba and Mamba2 models - arch = hparams["ssm_cfg"].get("layer", "Mamba") + "ForCausalLM" - - # Step3-VL keeps text config under text_config but uses a custom top-level architecture. - # For text conversion we route to a dedicated text-only class. - # TODO: refactor this later to avoid adding exception here - if model_type == ModelType.TEXT and arch in ("StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM"): - return arch - - # if "architectures" is found in the sub-config, use that instead - if model_type == ModelType.TEXT and text_config.get("architectures") is not None: - arch = text_config["architectures"][0] - elif model_type == ModelType.MMPROJ and vision_config.get("architectures") is not None: - arch = vision_config["architectures"][0] - if arch is None: - raise ValueError("Failed to detect model architecture") - return arch - - def main() -> None: args = parse_args() if args.print_supported_models: logger.error("Supported models:") - ModelBase.print_registered_models() + print_registered_models() sys.exit(0) if args.verbose: @@ -14199,16 +218,19 @@ def main() -> None: model_architecture = get_model_architecture(hparams, model_type) logger.info(f"Model architecture: {model_architecture}") try: - model_class = ModelBase.from_model_architecture(model_architecture, model_type=model_type) + model_class = get_model_class(model_architecture, mmproj=(model_type == ModelType.MMPROJ)) except NotImplementedError: logger.error(f"Model {model_architecture} is not supported") sys.exit(1) elif args.mmproj: assert hparams.get("vision_encoder") is not None, "This model does not support multimodal" + from conversion.pixtral import PixtralModel model_class = PixtralModel elif "moe" in hparams: + from conversion.mistral import MistralMoeModel model_class = MistralMoeModel else: + from conversion.mistral import MistralModel model_class = MistralModel model_instance = model_class(dir_model, output_type, fname_out, diff --git a/convert_hf_to_gguf_update.py b/convert_hf_to_gguf_update.py index 8d73b1f5546..8b2a9454f98 100755 --- a/convert_hf_to_gguf_update.py +++ b/convert_hf_to_gguf_update.py @@ -19,7 +19,7 @@ logger = logging.getLogger("convert_hf_to_gguf_update") sess = requests.Session() -convert_py_pth = pathlib.Path("convert_hf_to_gguf.py") +convert_py_pth = pathlib.Path("conversion/base.py") convert_py = convert_py_pth.read_text(encoding="utf-8") hf_token_pth = pathlib.Path.home() / ".cache" / "huggingface" / "token" hf_token = hf_token_pth.read_text(encoding="utf-8").strip() if hf_token_pth.exists() else None @@ -374,7 +374,7 @@ def get_vocab_base_pre(self, tokenizer) -> str: convert_py_pth.write_text(convert_py, encoding="utf-8") -logger.info("+++ convert_hf_to_gguf.py was updated") +logger.info(f"+++ {convert_py_pth} was updated") # generate tests for each tokenizer model diff --git a/convert_lora_to_gguf.py b/convert_lora_to_gguf.py index ad4751bb96e..1b7334617d1 100755 --- a/convert_lora_to_gguf.py +++ b/convert_lora_to_gguf.py @@ -22,12 +22,11 @@ if 'NO_LOCAL_GGUF' not in os.environ: sys.path.insert(1, str(Path(__file__).parent / 'gguf-py')) import gguf - -# reuse model definitions from convert_hf_to_gguf.py -from convert_hf_to_gguf import LazyTorchTensor, ModelBase - from gguf.constants import GGUFValueType +# reuse model definitions from the conversion/ package +from conversion import LazyTorchTensor, ModelBase, get_model_class + logger = logging.getLogger("lora-to-gguf") @@ -384,7 +383,7 @@ def load_hparams_from_hf(hf_model_id: str) -> tuple[dict[str, Any], Path | None] with torch.inference_mode(): try: - model_class = ModelBase.from_model_architecture(hparams["architectures"][0]) + model_class = get_model_class(hparams["architectures"][0]) except NotImplementedError: logger.error(f"Model {hparams['architectures'][0]} is not supported") sys.exit(1) From 18d1717d623c7f83e35ebb48ee6ba507eb12cc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigbj=C3=B8rn=20Skj=C3=A6ret?= Date: Fri, 15 May 2026 18:38:39 +0200 Subject: [PATCH 119/158] convert : fix Qwen3 ASR conversion (#23081) * fix qwen3asr * fix qwen3asr --- conversion/qwen3vl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conversion/qwen3vl.py b/conversion/qwen3vl.py index 2d20b30dbde..9f11757697f 100644 --- a/conversion/qwen3vl.py +++ b/conversion/qwen3vl.py @@ -183,6 +183,9 @@ def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Ca if name.startswith("model.visual."): name = name.replace("model.visual.", "visual.", 1) + if name.startswith("thinker.audio_tower."): + name = name.replace("thinker.audio_tower.", "audio_tower.", 1) + if "visual." not in name and "audio_tower." not in name: return None From 8be1786707cdac011b90bf0d6ddb67c3eb6877f3 Mon Sep 17 00:00:00 2001 From: Pascal Date: Fri, 15 May 2026 19:25:38 +0200 Subject: [PATCH 120/158] webui: fix theme from --webui-config-file not applied on first load (fresh localStorage) (#22902) --- tools/server/webui/src/lib/stores/settings.svelte.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/server/webui/src/lib/stores/settings.svelte.ts b/tools/server/webui/src/lib/stores/settings.svelte.ts index 0177d79af87..da5e4024e8a 100644 --- a/tools/server/webui/src/lib/stores/settings.svelte.ts +++ b/tools/server/webui/src/lib/stores/settings.svelte.ts @@ -357,6 +357,11 @@ class SettingsStore { for (const [key, value] of Object.entries(webuiSettings)) { if (!this.userOverrides.has(key) && value !== undefined) { setConfigValue(this.config, key, value); + + // theme lives in mode-watcher, not just in config -> propagate + if (key === SETTINGS_KEYS.THEME) { + setMode(value as ColorMode); + } } } } From 72e60f500d739eb9dc26e51cda5fdfe062faf766 Mon Sep 17 00:00:00 2001 From: Xuan-Son Nguyen Date: Fri, 15 May 2026 19:32:47 +0200 Subject: [PATCH 121/158] mtmd: add chunks and fix preproc for qwen3a (#23073) * mtmd: add chunks and fix preproc for qwen3a * add attn_mask * limit mtmd_chunk size (avoid blow up memory) * correct audio tokens * re-order the set_input case * remove attn_mask --- tools/mtmd/clip-graph.h | 7 ++- tools/mtmd/clip.cpp | 33 ++++------- tools/mtmd/clip.h | 1 - tools/mtmd/models/qwen3a.cpp | 104 +++++++++++++++++++++-------------- tools/mtmd/mtmd-audio.cpp | 104 +++++++++++++++++++++++++++++++++++ tools/mtmd/mtmd-audio.h | 9 +++ tools/mtmd/mtmd.cpp | 7 ++- 7 files changed, 197 insertions(+), 68 deletions(-) diff --git a/tools/mtmd/clip-graph.h b/tools/mtmd/clip-graph.h index 39f069501bc..c5e880c71ec 100644 --- a/tools/mtmd/clip-graph.h +++ b/tools/mtmd/clip-graph.h @@ -11,6 +11,10 @@ #define DEFAULT_INTERPOLATION_MODE (GGML_SCALE_MODE_BILINEAR | GGML_SCALE_FLAG_ANTIALIAS) +struct build_vit_opts { + ggml_tensor * attn_mask = nullptr; +}; + struct clip_graph { const clip_model & model; const clip_hparams & hparams; @@ -63,7 +67,8 @@ struct clip_graph { norm_type norm_t, ffn_op_type ffn_t, ggml_tensor * learned_pos_embd, - std::function add_pos); + std::function add_pos, + const build_vit_opts & opts = {}); // build the input after conv2d (inp_raw --> patches) // returns tensor with shape [n_embd, n_patches] diff --git a/tools/mtmd/clip.cpp b/tools/mtmd/clip.cpp index 02f0982797e..9727a738ed8 100644 --- a/tools/mtmd/clip.cpp +++ b/tools/mtmd/clip.cpp @@ -300,7 +300,8 @@ ggml_tensor * clip_graph::build_vit( norm_type norm_t, ffn_op_type ffn_t, ggml_tensor * learned_pos_embd, - std::function add_pos + std::function add_pos, + const build_vit_opts & opts ) { if (learned_pos_embd) { inp = ggml_add(ctx0, inp, learned_pos_embd); @@ -427,7 +428,7 @@ ggml_tensor * clip_graph::build_vit( } cur = build_attn(layer.o_w, layer.o_b, - Qcur, Kcur, Vcur, nullptr, kq_scale, il); + Qcur, Kcur, Vcur, opts.attn_mask, kq_scale, il); cb(cur, "attn_out", il); } @@ -663,6 +664,9 @@ ggml_tensor * clip_graph::build_attn( k = ggml_cast(ctx0, k, GGML_TYPE_F16); v = ggml_cast(ctx0, v, GGML_TYPE_F16); + if (kq_mask) { + kq_mask = ggml_cast(ctx0, kq_mask, GGML_TYPE_F16); + } cur = ggml_flash_attn_ext(ctx0, q, k, v, kq_mask, kq_scale, 0.0f, 0.0f); ggml_flash_attn_ext_set_prec(cur, GGML_PREC_F32); @@ -3244,12 +3248,10 @@ int clip_n_output_tokens(const struct clip_ctx * ctx, struct clip_image_f32 * im } break; case PROJECTOR_TYPE_QWEN3A: { - // 3x stride-2 conv2d: each step is floor((n-1)/2)+1 - int n = img->nx; - n = (n - 1) / 2 + 1; - n = (n - 1) / 2 + 1; - n = (n - 1) / 2 + 1; - n_patches = n; + // chunk_size=100 frames --> 3x stride-2 conv2d --> 13 tokens per chunk + const int chunk_size = 100; + const int tokens_per_chunk = 13; + n_patches = (img->nx / chunk_size) * tokens_per_chunk; } break; case PROJECTOR_TYPE_GLMA: { @@ -4292,21 +4294,6 @@ bool clip_has_audio_encoder(const struct clip_ctx * ctx) { return ctx->model.modality == CLIP_MODALITY_AUDIO; } -bool clip_has_whisper_encoder(const struct clip_ctx * ctx) { - switch (ctx->proj_type()) { - case PROJECTOR_TYPE_ULTRAVOX: - case PROJECTOR_TYPE_QWEN2A: - case PROJECTOR_TYPE_QWEN3A: - case PROJECTOR_TYPE_GLMA: - case PROJECTOR_TYPE_VOXTRAL: - case PROJECTOR_TYPE_MERALION: - case PROJECTOR_TYPE_MUSIC_FLAMINGO: - return true; - default: - return false; - } -} - bool clip_encode_float_image (struct clip_ctx * ctx, int n_threads, float * img, int h, int w, float * vec) { clip_image_f32 clip_img; clip_img.buf.resize(h * w * 3); diff --git a/tools/mtmd/clip.h b/tools/mtmd/clip.h index 5f9dc93c6b0..f643ed6e979 100644 --- a/tools/mtmd/clip.h +++ b/tools/mtmd/clip.h @@ -115,7 +115,6 @@ void clip_image_f32_batch_add_mel(struct clip_image_f32_batch * batch, int n_mel bool clip_has_vision_encoder(const struct clip_ctx * ctx); bool clip_has_audio_encoder(const struct clip_ctx * ctx); -bool clip_has_whisper_encoder(const struct clip_ctx * ctx); struct clip_cap { bool has_vision; diff --git a/tools/mtmd/models/qwen3a.cpp b/tools/mtmd/models/qwen3a.cpp index 1384e5155ee..4de96955d96 100644 --- a/tools/mtmd/models/qwen3a.cpp +++ b/tools/mtmd/models/qwen3a.cpp @@ -1,68 +1,88 @@ #include "models.h" ggml_cgraph * clip_graph_qwen3a::build() { + // Ref implementation: https://github.com/QwenLM/Qwen3-ASR/blob/main/qwen_asr/core/transformers_backend/modeling_qwen3_asr.py + + // inp_raw: [n_frames, n_mel, 1] (nx=n_frames, ny=n_mel) ggml_tensor * inp = build_inp_raw(1); - // conv2d block - // TODO: do we need to split by chunks of n_window each like on transformers impl? - { - inp = ggml_conv_2d(ctx0, model.conv2d_1_w, inp, 2, 2, 1, 1, 1, 1); - inp = ggml_add(ctx0, inp, model.conv2d_1_b); - inp = ggml_gelu_erf(ctx0, inp); + const int64_t n_frames = inp->ne[0]; // total frames, padded to multiple of chunk_size + const int64_t n_mel = inp->ne[1]; // 128 + const int64_t chunk_size = 100; // n_window * 2 (n_window=50 from model config) + const int64_t n_chunks = n_frames / chunk_size; - inp = ggml_conv_2d(ctx0, model.conv2d_2_w, inp, 2, 2, 1, 1, 1, 1); - inp = ggml_add(ctx0, inp, model.conv2d_2_b); - inp = ggml_gelu_erf(ctx0, inp); + GGML_ASSERT(n_frames % chunk_size == 0); // preprocessor should already pad the input + GGML_ASSERT(inp->type == GGML_TYPE_F32); - inp = ggml_conv_2d(ctx0, model.conv2d_3_w, inp, 2, 2, 1, 1, 1, 1); - inp = ggml_add(ctx0, inp, model.conv2d_3_b); - inp = ggml_gelu_erf(ctx0, inp); + // View mel spectrogram as batched 100-frame chunks: [chunk_size, n_mel, 1, n_chunks] + inp = ggml_view_4d(ctx0, inp, + chunk_size, n_mel, 1, n_chunks, + n_frames * (int64_t)sizeof(float), // nb[1]: stride over mel bins + chunk_size * (int64_t)sizeof(float), // nb[2]: stride for C=1 (unused) + chunk_size * (int64_t)sizeof(float), // nb[3]: stride over chunks + 0); + inp = ggml_cont(ctx0, inp); + cb(inp, "inp_chunks", -1); - // inp [n_pos, n_mels/8, channels, 1] (W, H, C, N) - cb(inp, "after_conv_blocks", -1); + // 3 x conv2d + gelu + { + // conv output [OW, OH, C_out, n_chunks] + auto conv_block = [&](ggml_tensor * x, ggml_tensor * w, ggml_tensor * b) { + x = ggml_conv_2d(ctx0, w, x, 2, 2, 1, 1, 1, 1); + if (b) { + x = ggml_add(ctx0, x, ggml_reshape_4d(ctx0, b, 1, 1, x->ne[2], 1)); + } + return ggml_gelu_erf(ctx0, x); + }; - const int64_t n_pos_after_conv = inp->ne[0]; - const int64_t n_mel_after_conv = inp->ne[1]; // 128/8 = 16 + inp = conv_block(inp, model.conv2d_1_w, model.conv2d_1_b); + inp = conv_block(inp, model.conv2d_2_w, model.conv2d_2_b); + inp = conv_block(inp, model.conv2d_3_w, model.conv2d_3_b); + // inp: [OW=13, OH=16, OC=480, n_chunks] + cb(inp, "after_conv_blocks", -1); + } - inp = ggml_cont(ctx0, ggml_permute(ctx0, inp, 0, 2, 3, 1)); - inp = ggml_reshape_2d(ctx0, inp, n_pos_after_conv, n_mel_after_conv * inp->ne[3]); // [n_pos, 7680] - inp = ggml_cont(ctx0, ggml_transpose(ctx0, inp)); // [7680, n_pos] + // permute [OW=25, OH=16, OC=480, n_chunks] -> [OH=16, OC=480, OW=25, n_chunks] + // reshape to [OH*OC=7680, OW*n_chunks] + // feature index h+16*c = c*16+f (matches python code) + inp = ggml_cont(ctx0, ggml_permute(ctx0, inp, 2, 0, 1, 3)); + inp = ggml_reshape_2d(ctx0, inp, inp->ne[0] * inp->ne[1], inp->ne[2] * inp->ne[3]); - // project to n_embd - inp = ggml_mul_mat(ctx0, model.conv_out_w, inp); - if (model.conv_out_b) { - inp = ggml_add(ctx0, inp, model.conv_out_b); - } - cb(inp, "after_conv_out", -1); + // Project to d_model: [d_model, 25*n_chunks] + inp = ggml_mul_mat(ctx0, model.conv_out_w, inp); + if (model.conv_out_b) { + inp = ggml_add(ctx0, inp, model.conv_out_b); } + cb(inp, "after_conv_out", -1); - auto n_pos = inp->ne[1]; + const int64_t n_pos = inp->ne[1]; // 25 * n_chunks - ggml_tensor * pos_embd_selected = ggml_view_2d( - ctx0, model.position_embeddings, - model.position_embeddings->ne[0], n_pos, - model.position_embeddings->nb[1], 0 - ); - ggml_tensor * cur = build_vit( - inp, n_pos, - NORM_TYPE_NORMAL, - hparams.ffn_op, - pos_embd_selected, - nullptr); + // Per-chunk positional embeddings: repeat pos[0:13] for each chunk + // (position indices reset 0..12 per chunk, not sequential across chunks) + { + const int64_t tokens_per_chunk = n_pos / n_chunks; // 13 + ggml_tensor * pos_tmp = ggml_view_2d(ctx0, model.position_embeddings, + model.position_embeddings->ne[0], tokens_per_chunk, + model.position_embeddings->nb[1], 0); + ggml_tensor * tgt = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, + model.position_embeddings->ne[0], n_pos); + inp = ggml_add(ctx0, inp, ggml_repeat(ctx0, pos_tmp, tgt)); + } + ggml_tensor * cur = build_vit(inp, n_pos, + NORM_TYPE_NORMAL, hparams.ffn_op, + nullptr, // pos embd already added above + nullptr); cb(cur, "after_transformer", -1); - // projector + // MLP projector cur = build_ffn(cur, model.mm_1_w, model.mm_1_b, nullptr, nullptr, model.mm_2_w, model.mm_2_b, - FFN_GELU_ERF, - -1); - + FFN_GELU_ERF, -1); cb(cur, "projected", -1); ggml_build_forward_expand(gf, cur); - return gf; } diff --git a/tools/mtmd/mtmd-audio.cpp b/tools/mtmd/mtmd-audio.cpp index 8f0a9875b10..85352904739 100644 --- a/tools/mtmd/mtmd-audio.cpp +++ b/tools/mtmd/mtmd-audio.cpp @@ -609,6 +609,110 @@ bool mtmd_audio_preprocessor_whisper::preprocess(const float * s return true; } +// +// mtmd_audio_preprocessor_qwen3a +// +// Matches the Python WhisperFeatureExtractor called with truncation=False: +// - reflection padding of n_fft/2 samples at each end (center=True) +// - Whisper-style log10 + (max-8)/4 normalization applied to full audio +// - output split into ≤30s (3000 mel frames) windows, each padded to a +// multiple of 200 frames (n_window * 2) for the cgraph batch view +// + +void mtmd_audio_preprocessor_qwen3a::initialize() { + cache.fill_sin_cos_table(hparams.audio_n_fft); + cache.fill_hann_window(hparams.audio_window_len, true); + cache.fill_mel_filterbank_matrix(hparams.n_mel_bins, hparams.audio_n_fft, hparams.audio_sample_rate); +} + +bool mtmd_audio_preprocessor_qwen3a::preprocess(const float * samples, + size_t n_samples, + std::vector & output) { + if (n_samples == 0) { + return false; + } + + GGML_ASSERT(!cache.sin_vals.empty()); + GGML_ASSERT(!cache.cos_vals.empty()); + GGML_ASSERT(!cache.filters.data.empty()); + + // Reflection-pad n_fft/2 samples at each end, matching WhisperFeatureExtractor center=True + const int pad = hparams.audio_n_fft / 2; // = 200 + + std::vector padded(n_samples + 2 * pad, 0.0f); + // Reflect start: padded[0..pad-1] = samples[pad..1] (reversed) + for (int i = 0; i < pad; i++) { + int src = pad - i; // samples[pad], samples[pad-1], ..., samples[1] + padded[i] = (src < (int)n_samples) ? samples[src] : 0.0f; + } + std::copy(samples, samples + n_samples, padded.begin() + pad); + // Reflect end: padded[n+pad..n+2*pad-1] = samples[n-2..n-pad-1] (reversed) + for (int i = 0; i < pad; i++) { + int src = (int)n_samples - 2 - i; // samples[n-2], samples[n-3], ... + padded[n_samples + pad + i] = (src >= 0) ? samples[src] : 0.0f; + } + + filter_params params; + params.n_mel = hparams.n_mel_bins; + params.n_fft_bins = 1 + (hparams.audio_n_fft / 2); + params.hann_window_size = hparams.audio_window_len; + params.hop_length = hparams.audio_hop_len; + params.sample_rate = hparams.audio_sample_rate; + params.no_padding = true; // reflection padding already applied above + params.use_natural_log = false; // log10 + + mtmd_audio_mel mel_full; + bool ok = log_mel_spectrogram(padded.data(), (int)padded.size(), 4, params, cache, mel_full); + if (!ok) { + return false; + } + + // Whisper-style normalization: clamp to (max - 8), scale to [-1, 1] + { + double mmax = -1e20; + for (float v : mel_full.data) { + if (v > mmax) mmax = v; + } + mmax -= 8.0; + for (float & v : mel_full.data) { + v = (std::max((double)v, mmax) + 4.0) / 4.0; + } + } + + // The effective frame count: center-padded STFT gives ~n_samples/hop_length frames. + // We take min(mel_full.n_len, n_samples/hop + 1) to avoid including excess frames. + const int n_eff = std::min(mel_full.n_len, + (int)(n_samples / hparams.audio_hop_len) + 1); + + // Split into inference windows matching n_window_infer=800 from model config. + // Each window is padded to the next multiple of chunk_size for the cgraph. + // The mtmd caller loops over output entries, so long audio is handled automatically. + const int chunk_size = 100; // conv sub-chunk size (n_window * 2, n_window=50) + const int window_size = 800; // mel frames per forward pass (n_window_infer=800) + + for (int off = 0; off < n_eff; off += window_size) { + const int win_eff = std::min(window_size, n_eff - off); + const int n_chunks = (win_eff + chunk_size - 1) / chunk_size; + const int n_padded = n_chunks * chunk_size; + + mtmd_audio_mel out; + out.n_mel = mel_full.n_mel; + out.n_len = n_padded; + out.n_len_org = win_eff; + out.data.assign(out.n_mel * out.n_len, 0.0f); + for (int m = 0; m < out.n_mel; m++) { + const int copy_len = std::min(win_eff, mel_full.n_len - off); + if (copy_len > 0) { + std::copy(mel_full.data.begin() + (size_t)m * mel_full.n_len + off, + mel_full.data.begin() + (size_t)m * mel_full.n_len + off + copy_len, + out.data.begin() + (size_t)m * out.n_len); + } + } + output.push_back(std::move(out)); + } + return true; +} + // // mtmd_audio_preprocessor_conformer // diff --git a/tools/mtmd/mtmd-audio.h b/tools/mtmd/mtmd-audio.h index c1a705de522..98ccb642415 100644 --- a/tools/mtmd/mtmd-audio.h +++ b/tools/mtmd/mtmd-audio.h @@ -96,6 +96,15 @@ struct mtmd_audio_preprocessor_gemma4a : mtmd_audio_preprocessor { mtmd_audio_cache cache; }; +struct mtmd_audio_preprocessor_qwen3a : mtmd_audio_preprocessor { + mtmd_audio_preprocessor_qwen3a(const clip_ctx * ctx) : mtmd_audio_preprocessor(ctx) {} + void initialize() override; + bool preprocess(const float * samples, size_t n_samples, std::vector & output) override; + + private: + mtmd_audio_cache cache; +}; + // // streaming ISTFT - converts spectrogram frames back to audio one frame at a time // diff --git a/tools/mtmd/mtmd.cpp b/tools/mtmd/mtmd.cpp index 1ab8a4c0464..8f12d0b43ea 100644 --- a/tools/mtmd/mtmd.cpp +++ b/tools/mtmd/mtmd.cpp @@ -515,7 +515,6 @@ struct mtmd_context { // set preprocessor switch (proj) { case PROJECTOR_TYPE_QWEN2A: - case PROJECTOR_TYPE_QWEN3A: case PROJECTOR_TYPE_QWEN25O: { // <|audio_bos|> ... (embeddings) ... <|audio_eos|> @@ -523,6 +522,12 @@ struct mtmd_context { aud_end = "<|audio_eos|>"; audio_preproc = std::make_unique(ctx_a); } break; + case PROJECTOR_TYPE_QWEN3A: + { + aud_beg = "<|audio_start|>"; + aud_end = "<|audio_end|>"; + audio_preproc = std::make_unique(ctx_a); + } break; case PROJECTOR_TYPE_VOXTRAL: { // [BEGIN_AUDIO] ... (embeddings) ... From 6831fe470cdfbeeebcf5645e8d5d9acae3a4ddf0 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Fri, 15 May 2026 19:33:12 +0200 Subject: [PATCH 122/158] docs: document `usage` object in server timings response (#23110) * docs: document `usage` object in server timings response Co-Authored-By: julien-agent * Apply suggestion from @julien-c --------- Co-authored-by: julien-agent --- tools/server/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/server/README.md b/tools/server/README.md index 5ba1a58f8c1..2b3a2b1687d 100644 --- a/tools/server/README.md +++ b/tools/server/README.md @@ -1322,6 +1322,22 @@ This provides information on the performance of the server. It also allows calcu The total number of tokens in context is equal to `prompt_n + cache_n + predicted_n` +The response also includes a standard `usage` object: + +```js +{ + // ... + "usage": { + "completion_tokens": 48, + "prompt_tokens": 44, + "total_tokens": 92, + "prompt_tokens_details": { + "cached_tokens": 0 + } + } +} +``` + *Reasoning support* The server supports parsing and returning reasoning via the `reasoning_content` field, similar to Deepseek API. From cfabeb1bad9d789fefa853ed7e336ec98d981b9b Mon Sep 17 00:00:00 2001 From: Pascal Date: Fri, 15 May 2026 19:35:05 +0200 Subject: [PATCH 123/158] tests: add BF16 non-contig coverage for MUL_MAT permutations (#22689) The MUL_MAT test loop iterates over base_types[] to generate non-contig permutation cases (3 standard permutations across n in {1, 8, 16}). BF16 is absent from base_types[], so these 9 cases were never generated for BF16 even though every other type covered by base_types[] tests them. Add the missing 9 cases explicitly: BF16 x F32, m=16, k=256, bs=[2,3], permutations {0,2,1,3}, {0,1,3,2}, {0,3,2,1}, with n in {1, 8, 16}. Suggested-by: @jeffbolznv --- tests/test-backend-ops.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp index c60f6d2c2b0..8a561c038a1 100644 --- a/tests/test-backend-ops.cpp +++ b/tests/test-backend-ops.cpp @@ -8337,6 +8337,18 @@ static std::vector> make_test_cases_eval() { test_cases.emplace_back(new test_mul_mat(type_a, type_b, 16, 8, 256, {1536, 1}, {1, 1})); } } + + // BF16 is absent from base_types: add the 3 standard non-contig permutations explicitly + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 1, 256, {2, 3}, {1, 1}, {0, 2, 1, 3})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 1, 256, {2, 3}, {1, 1}, {0, 1, 3, 2})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 1, 256, {2, 3}, {1, 1}, {0, 3, 2, 1})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 8, 256, {2, 3}, {1, 1}, {0, 2, 1, 3})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 8, 256, {2, 3}, {1, 1}, {0, 1, 3, 2})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 8, 256, {2, 3}, {1, 1}, {0, 3, 2, 1})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, 256, {2, 3}, {1, 1}, {0, 2, 1, 3})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, 256, {2, 3}, {1, 1}, {0, 1, 3, 2})); + test_cases.emplace_back(new test_mul_mat(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, 256, {2, 3}, {1, 1}, {0, 3, 2, 1})); + for (ggml_type type_a : other_types) { for (ggml_type type_b : {GGML_TYPE_F32}) { if (ggml_blck_size(type_a) != 256) { From 1348f67c58f561808136e8a152a9eddec168f221 Mon Sep 17 00:00:00 2001 From: Omer Ozarslan Date: Fri, 15 May 2026 10:38:16 -0700 Subject: [PATCH 124/158] webui: Use lowercase hash for HF checksum check (#23107) --- scripts/webui-download.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/webui-download.cmake b/scripts/webui-download.cmake index 344ba5ecf53..6695c018035 100644 --- a/scripts/webui-download.cmake +++ b/scripts/webui-download.cmake @@ -184,8 +184,8 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) foreach(asset ${ASSETS}) set(download_path "${PUBLIC_DIR}/${asset}") file(SHA256 "${download_path}" asset_hash) - string(TOUPPER "${asset_hash}" EXPECTED_HASH_UPPER) - string(REGEX MATCH "${EXPECTED_HASH_UPPER}[ \\t]+${asset}" CHECKSUM_LINE "${CHECKSUMS_CONTENT}") + string(TOLOWER "${asset_hash}" EXPECTED_HASH_LOWER) + string(REGEX MATCH "${EXPECTED_HASH_LOWER}[ \\t]+${asset}" CHECKSUM_LINE "${CHECKSUMS_CONTENT}") if(NOT CHECKSUM_LINE) message(WARNING "WebUI: checksum verification failed for ${asset}") message(WARNING " downloaded file may not match expected checksum, but will be used") From 49d1701bd24e4cedf6dfec9e50e185111203946b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigbj=C3=B8rn=20Skj=C3=A6ret?= Date: Sat, 16 May 2026 01:09:28 +0200 Subject: [PATCH 125/158] ci : fix release symlinks (#23119) --- .github/workflows/release.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 218d24296be..bb512fd875c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,7 +104,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-macos-${{ matrix.build }}.tar.gz -s ",^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-macos-${{ matrix.build }}.tar.gz -s ",^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -182,7 +182,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-${{ matrix.build }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -259,7 +259,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-${{ matrix.build }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -337,7 +337,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-android-arm64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-android-arm64.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -426,7 +426,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/ReleaseOV/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-${{ env.OPENVINO_VERSION_MAJOR }}-x64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/ReleaseOV/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-${{ env.OPENVINO_VERSION_MAJOR }}-x64.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/ReleaseOV/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -867,7 +867,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -979,7 +979,7 @@ jobs: id: pack_artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 @@ -1240,7 +1240,7 @@ jobs: - name: Pack artifacts run: | cp LICENSE ./build/bin/ - tar -czvf llama-${{ steps.tag.outputs.name }}-bin-${{ matrix.chip_type }}-openEuler-${{ matrix.arch }}${{ matrix.use_acl_graph == 'on' && '-aclgraph' || '' }}.tar.gz --transform "s,^.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . + tar -czvf llama-${{ steps.tag.outputs.name }}-bin-${{ matrix.chip_type }}-openEuler-${{ matrix.arch }}${{ matrix.use_acl_graph == 'on' && '-aclgraph' || '' }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin . - name: Upload artifacts uses: actions/upload-artifact@v6 From 59778f0196a82db32580bb649d5d839355d6d7bf Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Sat, 16 May 2026 02:02:40 +0200 Subject: [PATCH 126/158] ui: Restructure repo to use `tools/ui` folder and `ui` / `UI` / `llama-ui` / `LLAMA_UI` naming (#23064) * webui: Move static build output from `tools/server/public` to `build/ui` directory * refactor: Move to `tools/ui` * refactor: rename CMake variables and preprocessor defines - Rename LLAMA_BUILD_WEBUI -> LLAMA_BUILD_UI (old kept as deprecated) - Rename LLAMA_USE_PREBUILT_WEBUI -> LLAMA_USE_PREBUILT_UI (old kept as deprecated) - Backward compat: old vars auto-forward to new ones with DEPRECATION warning - Rename internal vars: WEBUI_SOURCE -> UI_SOURCE, WEBUI_SOURCE_DIR -> UI_SOURCE_DIR, etc. - Rename HF bucket: LLAMA_WEBUI_HF_BUCKET -> LLAMA_UI_HF_BUCKET - Emit both LLAMA_BUILD_WEBUI and LLAMA_BUILD_UI preprocessor defines - Emit both LLAMA_WEBUI_DEFAULT_ENABLED and LLAMA_UI_DEFAULT_ENABLED * refactor: rename CLI flags (--webui -> --ui) with backward compat - Add --ui/--no-ui (old --webui/--no-webui kept as deprecated aliases) - Add --ui-config (old --webui-config kept as deprecated alias) - Add --ui-config-file (old --webui-config-file kept as deprecated alias) - Add --ui-mcp-proxy/--no-ui-mcp-proxy (old --webui-mcp-proxy kept as deprecated) - Add new env vars: LLAMA_ARG_UI, LLAMA_ARG_UI_CONFIG, LLAMA_ARG_UI_CONFIG_FILE, LLAMA_ARG_UI_MCP_PROXY - C++ struct fields: params.ui, params.ui_config_json, params.ui_mcp_proxy added alongside old fields - Backward compat: old fields synced to new ones in g_params_to_internals * refactor: update C++ server internals with backward compat - Rename json_webui_settings -> json_ui_settings (both kept in server_context_meta) - Rename params.webui usage -> params.ui (both synced, old still works) - JSON API emits both "ui"/"ui_settings" and "webui"/"webui_settings" keys - Server routes use params.ui_mcp_proxy || params.webui_mcp_proxy - Preprocessor guards use #if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) * refactor: rename CI/CD workflows, artifacts, and build script - Rename webui-build.yml -> ui-build.yml; artifact webui-build -> ui-build - Rename webui-publish.yml -> ui-publish.yml; var HF_BUCKET_WEBUI_STATIC_OUTPUT -> HF_BUCKET_UI_STATIC_OUTPUT - Rename server-webui.yml -> server-ui.yml; job webui-build/checks -> ui-build/checks - Update server.yml: job/artifact refs webui-build -> ui-build - Update release.yml: all webui-build/publish refs -> ui-build/publish; HF_TOKEN_WEBUI_STATIC_OUTPUT -> HF_TOKEN_UI_STATIC_OUTPUT - Update server-self-hosted.yml: webui-build -> ui-build - Update build-self-hosted.yml: HF_WEBUI_VERSION -> HF_UI_VERSION - Rename webui-download.cmake -> ui-download.cmake (internal refs updated) - Update labeler.yml: server/webui -> server/ui path label * docs: update CODEOWNERS and server README docs - Update CODEOWNERS: team ggml-org/llama-webui -> ggml-org/llama-ui, path /tools/server/webui/ -> /tools/ui/ - Update server README.md: CLI tables show --ui flags with deprecated --webui aliases - Update server README-dev.md: "WebUI" -> "UI", paths updated to tools/ui/ * fix: Small fixes for UI build * fix: CMake.txt syntax * chore: Formatting * fix: `.editorconfig` for llama-ui * chore: Formatting * refactor: Use `APP_NAME` in Error route * refactor: Cleanup * refactor: Single migration service * make llama-ui a linkable target * fix: UI Build output * fix: Missing change * fix: separate llama-ui npm build output into build/tools/ui/dist subfolder + use cmake npm build instead of downloading ui-build.yml artifacts in CI * refactor: UI workflows cleanup --------- Co-authored-by: Xuan Son Nguyen --- .editorconfig | 2 +- .github/labeler.yml | 4 +- .github/workflows/build-self-hosted.yml | 20 +- .github/workflows/release.yml | 142 +++-- .github/workflows/server-sanitize.yml | 7 + .github/workflows/server-self-hosted.yml | 14 +- .github/workflows/server.yml | 22 +- .../{webui-build.yml => ui-build.yml} | 18 +- .../workflows/{server-webui.yml => ui-ci.yml} | 52 +- .../{webui-publish.yml => ui-publish.yml} | 16 +- .gitignore | 5 +- CMakeLists.txt | 19 +- CODEOWNERS | 4 +- common/arg.cpp | 58 +- common/common.h | 16 +- ...webui-download.cmake => ui-download.cmake} | 57 +- scripts/xxd.cmake | 2 +- tools/CMakeLists.txt | 1 + tools/server/CMakeLists.txt | 127 +---- tools/server/README-dev.md | 6 +- tools/server/README.md | 16 +- tools/server/server-context.cpp | 27 +- tools/server/server-context.h | 3 +- tools/server/server-http.cpp | 23 +- tools/server/server-models.cpp | 7 +- tools/server/server-models.h | 15 +- tools/server/server.cpp | 3 +- tools/server/webui/scripts/post-build.sh | 3 - .../src/lib/constants/localstorage-keys.ts | 6 - tools/{server/webui => ui}/.gitignore | 0 tools/{server/webui => ui}/.npmrc | 0 tools/{server/webui => ui}/.prettierignore | 0 tools/{server/webui => ui}/.prettierrc | 0 .../decorators/ModeWatcherDecorator.svelte | 0 .../TooltipProviderDecorator.svelte | 0 tools/{server/webui => ui}/.storybook/main.ts | 0 .../webui => ui}/.storybook/preview.ts | 0 .../webui => ui}/.storybook/vitest.setup.ts | 0 tools/ui/CMakeLists.txt | 157 ++++++ tools/{server/webui => ui}/README.md | 27 +- tools/{server/webui => ui}/components.json | 0 .../high-level-architecture-simplified.md | 0 .../architecture/high-level-architecture.md | 0 .../webui => ui}/docs/flows/chat-flow.md | 0 .../docs/flows/conversations-flow.md | 0 .../flows/data-flow-simplified-model-mode.md | 0 .../flows/data-flow-simplified-router-mode.md | 0 .../webui => ui}/docs/flows/database-flow.md | 0 .../webui => ui}/docs/flows/mcp-flow.md | 0 .../webui => ui}/docs/flows/models-flow.md | 0 .../webui => ui}/docs/flows/server-flow.md | 0 .../webui => ui}/docs/flows/settings-flow.md | 4 +- tools/{server/webui => ui}/eslint.config.js | 4 +- tools/{server/webui => ui}/package-lock.json | 0 tools/{server/webui => ui}/package.json | 2 +- .../{server/webui => ui}/playwright.config.ts | 2 +- tools/{server/webui => ui}/scripts/dev.sh | 10 +- .../webui => ui}/scripts/install-git-hooks.sh | 28 +- .../scripts/vite-plugin-llama-cpp-build.ts | 51 +- tools/{server/webui => ui}/src/app.css | 0 tools/{server/webui => ui}/src/app.d.ts | 0 tools/{server/webui => ui}/src/app.html | 0 .../src/lib/actions/fade-in-view.svelte.ts | 0 .../src/lib/components/app/SKILL.md | 0 .../components/app/actions/ActionIcon.svelte | 0 .../actions/ActionIconCopyToClipboard.svelte | 0 .../src/lib/components/app/actions/index.ts | 0 .../components/app/badges/BadgeInfo.svelte | 0 .../app/badges/BadgesModality.svelte | 0 .../src/lib/components/app/badges/index.ts | 0 .../ChatAttachmentsList.svelte | 0 .../ChatAttachmentsListItem.svelte | 0 .../ChatAttachmentsListItemMcpPrompt.svelte | 0 .../ChatAttachmentsListItemMcpResource.svelte | 0 ...hatAttachmentsListItemThumbnailFile.svelte | 0 ...atAttachmentsListItemThumbnailImage.svelte | 0 .../ChatAttachmentsPreview.svelte | 0 .../ChatAttachmentsPreviewCurrentItem.svelte | 0 ...tAttachmentsPreviewCurrentItemAudio.svelte | 0 ...tAttachmentsPreviewCurrentItemImage.svelte | 0 ...hatAttachmentsPreviewCurrentItemPdf.svelte | 0 ...atAttachmentsPreviewCurrentItemText.svelte | 0 ...hmentsPreviewCurrentItemUnavailable.svelte | 0 .../ChatAttachmentsPreviewFileInfo.svelte | 0 .../ChatAttachmentsPreviewNavButtons.svelte | 0 ...hatAttachmentsPreviewThumbnailStrip.svelte | 0 .../app/chat/ChatForm/ChatForm.svelte | 0 .../ChatFormActionAddButton.svelte | 0 .../ChatFormActionAddDropdown.svelte | 7 +- .../ChatFormActionAddMcpServersSubmenu.svelte | 0 .../ChatFormActionAddSheet.svelte | 7 +- .../ChatFormActionAddToolsSubmenu.svelte | 3 +- .../ChatFormActionsAdd.svelte | 0 .../ChatFormActionModels.svelte | 0 .../ChatFormActionRecord.svelte | 0 .../ChatFormActionSubmit.svelte | 0 .../ChatFormActions/ChatFormActions.svelte | 0 .../ChatFormFileInputInvisible.svelte | 0 .../ChatForm/ChatFormMcpResourcesList.svelte | 0 .../ChatFormPickerItemHeader.svelte | 0 .../ChatFormPicker/ChatFormPickerList.svelte | 0 .../ChatFormPickerListItem.svelte | 0 .../ChatFormPickerListItemSkeleton.svelte | 0 .../ChatFormPickerPopover.svelte | 0 .../ChatFormPickerMcpPrompts.svelte | 0 .../ChatFormPromptPickerArgumentForm.svelte | 0 .../ChatFormPromptPickerArgumentInput.svelte | 0 .../ChatFormPickerMcpResources.svelte | 0 .../ChatFormPickers/ChatFormPickers.svelte | 0 .../app/chat/ChatForm/ChatFormTextarea.svelte | 0 .../ChatMessage/ChatMessage.svelte | 0 .../ChatMessageAssistant.svelte | 0 .../ChatMessageMcpPrompt.svelte | 0 .../ChatMessageMcpPromptContent.svelte | 0 .../ChatMessageSystem.svelte | 0 .../ChatMessageUser/ChatMessageUser.svelte | 0 .../ChatMessageUserBubble.svelte | 0 .../ChatMessageUserPending.svelte | 0 .../ChatMessageActionCard.svelte | 0 ...hatMessageActionCardContinueRequest.svelte | 0 ...tMessageActionCardPermissionRequest.svelte | 0 .../ChatMessageActionIcons.svelte | 0 ...MessageActionIconsBranchingControls.svelte | 0 .../ChatMessageAgenticContent.svelte | 4 +- .../ChatMessages/ChatMessageEditForm.svelte | 0 .../ChatMessageStatistics.svelte | 0 .../ChatMessageStatisticsBadge.svelte | 0 .../app/chat/ChatMessages/ChatMessages.svelte | 0 .../app/chat/ChatScreen/ChatScreen.svelte | 0 .../ChatScreen/ChatScreenDragOverlay.svelte | 0 .../app/chat/ChatScreen/ChatScreenForm.svelte | 0 .../ChatScreenProcessingInfo.svelte | 0 .../src/lib/components/app/chat/index.ts | 0 .../content/CollapsibleContentBlock.svelte | 0 .../MarkdownContent/MarkdownContent.svelte | 0 .../plugins/rehype/enhance-code-blocks.ts | 0 .../plugins/rehype/enhance-links.ts | 0 .../plugins/rehype/rehype-rtl-support.ts | 0 .../rehype/resolve-attachment-images.ts | 0 .../plugins/rehype/table-html-restorer.ts | 0 .../plugins/remark/literal-html.ts | 0 .../app/content/SyntaxHighlightedCode.svelte | 0 .../src/lib/components/app/content/index.ts | 0 .../DialogChatAttachmentsPreview.svelte | 0 .../app/dialogs/DialogChatError.svelte | 0 .../app/dialogs/DialogCodePreview.svelte | 0 .../app/dialogs/DialogConfirmation.svelte | 0 .../DialogConversationSelection.svelte | 0 .../DialogConversationTitleUpdate.svelte | 0 .../app/dialogs/DialogEmptyFileAlert.svelte | 0 .../app/dialogs/DialogExportSettings.svelte | 0 .../app/dialogs/DialogFileUploadError.svelte | 0 .../dialogs/DialogMcpResourcePreview.svelte | 0 .../dialogs/DialogMcpResourcesBrowser.svelte | 0 .../app/dialogs/DialogMcpServerAddNew.svelte | 0 .../app/dialogs/DialogModelInformation.svelte | 0 .../dialogs/DialogModelNotAvailable.svelte | 0 .../src/lib/components/app/dialogs/index.ts | 0 .../app/forms/InputWithSuggestions.svelte | 0 .../components/app/forms/KeyValuePairs.svelte | 0 .../components/app/forms/SearchInput.svelte | 0 .../src/lib/components/app/forms/index.ts | 0 .../src/lib/components/app/index.ts | 0 .../app/mcp/McpActiveServersAvatars.svelte | 0 .../app/mcp/McpCapabilitiesBadges.svelte | 0 .../app/mcp/McpConnectionLogs.svelte | 0 .../src/lib/components/app/mcp/McpLogo.svelte | 0 .../app/mcp/McpResourcePreview.svelte | 0 .../app/mcp/McpResourceTemplateForm.svelte | 0 .../McpResourcesBrowser.svelte | 0 .../McpResourcesBrowserEmptyState.svelte | 0 .../McpResourcesBrowserHeader.svelte | 0 .../McpResourcesBrowserServerItem.svelte | 0 .../mcp-resources-browser.ts | 0 .../mcp/McpServerCard/McpServerCard.svelte | 0 .../McpServerCard/McpServerCardActions.svelte | 0 .../McpServerCardDeleteDialog.svelte | 0 .../McpServerCardEditForm.svelte | 0 .../McpServerCard/McpServerCardHeader.svelte | 0 .../McpServerCardToolsList.svelte | 0 .../app/mcp/McpServerCardSkeleton.svelte | 0 .../components/app/mcp/McpServerForm.svelte | 3 +- .../app/mcp/McpServerIdentity.svelte | 0 .../components/app/mcp/McpServerInfo.svelte | 0 .../src/lib/components/app/mcp/index.ts | 0 .../app/misc/CodeBlockActions.svelte | 0 .../app/misc/ConversationSelection.svelte | 0 .../app/misc/HorizontalScrollCarousel.svelte | 0 .../app/misc/KeyboardShortcutInfo.svelte | 0 .../components/app/misc/TruncatedText.svelte | 0 .../src/lib/components/app/misc/index.ts | 0 .../components/app/models/ModelBadge.svelte | 0 .../lib/components/app/models/ModelId.svelte | 0 .../app/models/ModelsSelectorDropdown.svelte | 0 .../app/models/ModelsSelectorList.svelte | 0 .../app/models/ModelsSelectorOption.svelte | 0 .../app/models/ModelsSelectorSheet.svelte | 0 .../src/lib/components/app/models/index.ts | 0 .../src/lib/components/app/models/utils.ts | 0 .../app/navigation/DesktopIconStrip.svelte | 0 .../app/navigation/DropdownMenuActions.svelte | 0 .../navigation/DropdownMenuSearchable.svelte | 0 .../SidebarNavigation.svelte | 4 +- .../SidebarNavigationActions.svelte | 0 .../SidebarNavigationConversationItem.svelte | 0 .../SidebarNavigationSearch.svelte | 0 .../lib/components/app/navigation/index.ts | 0 .../app/server/ServerErrorSplash.svelte | 0 .../app/server/ServerLoadingSplash.svelte | 0 .../components/app/server/ServerStatus.svelte | 0 .../src/lib/components/app/server/index.ts | 0 .../settings/SettingsChat/SettingsChat.svelte | 0 .../SettingsChat/SettingsChatFields.svelte | 0 .../SettingsChatImportExportSection.svelte | 0 .../SettingsChatImportExportTab.svelte | 0 ...ettingsChatParameterSourceIndicator.svelte | 0 .../SettingsChat/SettingsChatToolsTab.svelte | 0 .../SettingsChatDesktopSidebar.svelte | 0 .../settings/SettingsChatMobileHeader.svelte | 0 .../app/settings/SettingsFooter.svelte | 0 .../app/settings/SettingsGroup.svelte | 0 .../app/settings/SettingsMcpServers.svelte | 0 .../src/lib/components/app/settings/index.ts | 0 .../alert-dialog/alert-dialog-action.svelte | 0 .../alert-dialog/alert-dialog-cancel.svelte | 0 .../alert-dialog/alert-dialog-content.svelte | 0 .../alert-dialog-description.svelte | 0 .../alert-dialog/alert-dialog-footer.svelte | 0 .../alert-dialog/alert-dialog-header.svelte | 0 .../alert-dialog/alert-dialog-overlay.svelte | 0 .../ui/alert-dialog/alert-dialog-title.svelte | 0 .../alert-dialog/alert-dialog-trigger.svelte | 0 .../lib/components/ui/alert-dialog/index.ts | 0 .../ui/alert/alert-description.svelte | 0 .../components/ui/alert/alert-title.svelte | 0 .../src/lib/components/ui/alert/alert.svelte | 0 .../src/lib/components/ui/alert/index.ts | 0 .../src/lib/components/ui/badge/badge.svelte | 0 .../src/lib/components/ui/badge/index.ts | 0 .../ui/button-group/button-group-root.svelte | 0 .../button-group-separator.svelte | 0 .../lib/components/ui/button-group/index.ts | 0 .../lib/components/ui/button/button.svelte | 0 .../src/lib/components/ui/button/index.ts | 0 .../lib/components/ui/card/card-action.svelte | 0 .../components/ui/card/card-content.svelte | 0 .../ui/card/card-description.svelte | 0 .../lib/components/ui/card/card-footer.svelte | 0 .../lib/components/ui/card/card-header.svelte | 0 .../lib/components/ui/card/card-title.svelte | 0 .../src/lib/components/ui/card/card.svelte | 0 .../src/lib/components/ui/card/index.ts | 0 .../components/ui/checkbox/checkbox.svelte | 0 .../src/lib/components/ui/checkbox/index.ts | 0 .../ui/collapsible/collapsible-content.svelte | 0 .../ui/collapsible/collapsible-trigger.svelte | 0 .../ui/collapsible/collapsible.svelte | 0 .../lib/components/ui/collapsible/index.ts | 0 .../components/ui/dialog/dialog-close.svelte | 0 .../ui/dialog/dialog-content.svelte | 0 .../ui/dialog/dialog-description.svelte | 0 .../components/ui/dialog/dialog-footer.svelte | 0 .../components/ui/dialog/dialog-header.svelte | 0 .../ui/dialog/dialog-overlay.svelte | 0 .../components/ui/dialog/dialog-title.svelte | 0 .../ui/dialog/dialog-trigger.svelte | 0 .../src/lib/components/ui/dialog/index.ts | 0 .../dropdown-menu-checkbox-item.svelte | 0 .../dropdown-menu-content.svelte | 0 .../dropdown-menu-group-heading.svelte | 0 .../dropdown-menu/dropdown-menu-group.svelte | 0 .../dropdown-menu/dropdown-menu-item.svelte | 0 .../dropdown-menu/dropdown-menu-label.svelte | 0 .../dropdown-menu-radio-group.svelte | 0 .../dropdown-menu-radio-item.svelte | 0 .../dropdown-menu-separator.svelte | 0 .../dropdown-menu-shortcut.svelte | 0 .../dropdown-menu-sub-content.svelte | 0 .../dropdown-menu-sub-trigger.svelte | 0 .../dropdown-menu-trigger.svelte | 0 .../lib/components/ui/dropdown-menu/index.ts | 0 .../src/lib/components/ui/input/index.ts | 0 .../src/lib/components/ui/input/input.svelte | 0 .../src/lib/components/ui/label/index.ts | 0 .../src/lib/components/ui/label/label.svelte | 0 .../src/lib/components/ui/popover/index.ts | 0 .../ui/popover/popover-close.svelte | 0 .../ui/popover/popover-content.svelte | 0 .../ui/popover/popover-portal.svelte | 0 .../ui/popover/popover-trigger.svelte | 0 .../lib/components/ui/popover/popover.svelte | 0 .../lib/components/ui/scroll-area/index.ts | 0 .../scroll-area/scroll-area-scrollbar.svelte | 0 .../ui/scroll-area/scroll-area.svelte | 0 .../src/lib/components/ui/select/index.ts | 0 .../ui/select/select-content.svelte | 0 .../ui/select/select-group-heading.svelte | 0 .../components/ui/select/select-group.svelte | 0 .../components/ui/select/select-item.svelte | 0 .../components/ui/select/select-label.svelte | 0 .../select/select-scroll-down-button.svelte | 0 .../ui/select/select-scroll-up-button.svelte | 0 .../ui/select/select-separator.svelte | 0 .../ui/select/select-trigger.svelte | 0 .../src/lib/components/ui/separator/index.ts | 0 .../components/ui/separator/separator.svelte | 0 .../src/lib/components/ui/sheet/index.ts | 0 .../components/ui/sheet/sheet-close.svelte | 0 .../components/ui/sheet/sheet-content.svelte | 0 .../ui/sheet/sheet-description.svelte | 0 .../components/ui/sheet/sheet-footer.svelte | 0 .../components/ui/sheet/sheet-header.svelte | 0 .../components/ui/sheet/sheet-overlay.svelte | 0 .../components/ui/sheet/sheet-title.svelte | 0 .../components/ui/sheet/sheet-trigger.svelte | 0 .../lib/components/ui/sidebar/constants.ts | 0 .../components/ui/sidebar/context.svelte.ts | 0 .../src/lib/components/ui/sidebar/index.ts | 0 .../ui/sidebar/sidebar-content.svelte | 0 .../ui/sidebar/sidebar-footer.svelte | 0 .../ui/sidebar/sidebar-group-action.svelte | 0 .../ui/sidebar/sidebar-group-content.svelte | 0 .../ui/sidebar/sidebar-group-label.svelte | 0 .../ui/sidebar/sidebar-group.svelte | 0 .../ui/sidebar/sidebar-header.svelte | 0 .../ui/sidebar/sidebar-input.svelte | 0 .../ui/sidebar/sidebar-inset.svelte | 0 .../ui/sidebar/sidebar-menu-action.svelte | 0 .../ui/sidebar/sidebar-menu-badge.svelte | 0 .../ui/sidebar/sidebar-menu-button.svelte | 0 .../ui/sidebar/sidebar-menu-item.svelte | 0 .../ui/sidebar/sidebar-menu-skeleton.svelte | 0 .../ui/sidebar/sidebar-menu-sub-button.svelte | 0 .../ui/sidebar/sidebar-menu-sub-item.svelte | 0 .../ui/sidebar/sidebar-menu-sub.svelte | 0 .../components/ui/sidebar/sidebar-menu.svelte | 0 .../ui/sidebar/sidebar-provider.svelte | 0 .../components/ui/sidebar/sidebar-rail.svelte | 0 .../ui/sidebar/sidebar-separator.svelte | 0 .../ui/sidebar/sidebar-trigger.svelte | 0 .../lib/components/ui/sidebar/sidebar.svelte | 0 .../src/lib/components/ui/skeleton/index.ts | 0 .../components/ui/skeleton/skeleton.svelte | 0 .../src/lib/components/ui/switch/index.ts | 0 .../lib/components/ui/switch/switch.svelte | 0 .../src/lib/components/ui/table/index.ts | 0 .../lib/components/ui/table/table-body.svelte | 0 .../components/ui/table/table-caption.svelte | 0 .../lib/components/ui/table/table-cell.svelte | 0 .../components/ui/table/table-footer.svelte | 0 .../lib/components/ui/table/table-head.svelte | 0 .../components/ui/table/table-header.svelte | 0 .../lib/components/ui/table/table-row.svelte | 0 .../src/lib/components/ui/table/table.svelte | 0 .../src/lib/components/ui/textarea/index.ts | 0 .../components/ui/textarea/textarea.svelte | 0 .../src/lib/components/ui/tooltip/index.ts | 0 .../ui/tooltip/tooltip-content.svelte | 0 .../ui/tooltip/tooltip-trigger.svelte | 0 .../src/lib/components/ui/utils.ts | 0 .../webui => ui}/src/lib/constants/agentic.ts | 0 .../src/lib/constants/api-endpoints.ts | 0 .../src/lib/constants/attachment-labels.ts | 0 .../src/lib/constants/attachment-menu.ts | 0 .../src/lib/constants/auto-scroll.ts | 0 .../src/lib/constants/binary-detection.ts | 0 .../webui => ui}/src/lib/constants/cache.ts | 0 .../src/lib/constants/chat-form.ts | 0 tools/ui/src/lib/constants/cli-flags.ts | 6 + .../src/lib/constants/code-blocks.ts | 0 .../webui => ui}/src/lib/constants/code.ts | 0 .../src/lib/constants/context-keys.ts | 0 .../src/lib/constants/css-classes.ts | 0 tools/ui/src/lib/constants/database.ts | 29 + .../lib/constants/floating-ui-constraints.ts | 0 .../src/lib/constants/formatters.ts | 0 .../webui => ui}/src/lib/constants/icons.ts | 0 .../webui => ui}/src/lib/constants/index.ts | 4 +- .../src/lib/constants/key-value-pairs.ts | 0 .../src/lib/constants/latex-protection.ts | 0 .../src/lib/constants/literal-html.ts | 0 .../src/lib/constants/markdown.ts | 0 .../src/lib/constants/max-bundle-size.ts | 0 .../src/lib/constants/mcp-form.ts | 0 .../src/lib/constants/mcp-resource.ts | 0 .../webui => ui}/src/lib/constants/mcp.ts | 5 +- .../src/lib/constants/message-export.ts | 0 .../src/lib/constants/model-id.ts | 0 .../src/lib/constants/precision.ts | 0 .../src/lib/constants/processing-info.ts | 0 .../webui => ui}/src/lib/constants/routes.ts | 0 .../src/lib/constants/settings-keys.ts | 0 .../src/lib/constants/settings-registry.ts | 68 ++- tools/ui/src/lib/constants/storage.ts | 46 ++ .../src/lib/constants/supported-file-types.ts | 2 +- .../src/lib/constants/table-html-restorer.ts | 0 .../src/lib/constants/title-generation.ts | 0 .../webui => ui}/src/lib/constants/tools.ts | 0 .../src/lib/constants/tooltip-config.ts | 0 .../webui => ui}/src/lib/constants/ui.ts | 0 .../src/lib/constants/uri-template.ts | 0 .../webui => ui}/src/lib/constants/url.ts | 0 .../src/lib/constants/viewport.ts | 0 .../src/lib/contexts/chat-actions.context.ts | 0 .../contexts/chat-settings-config.context.ts | 0 .../webui => ui}/src/lib/contexts/index.ts | 0 .../src/lib/contexts/message-edit.context.ts | 0 .../lib/contexts/processing-info.context.ts | 0 .../webui => ui}/src/lib/enums/agentic.ts | 0 .../webui => ui}/src/lib/enums/attachment.ts | 0 .../webui => ui}/src/lib/enums/chat.ts | 0 .../webui => ui}/src/lib/enums/files.ts | 2 +- .../webui => ui}/src/lib/enums/index.ts | 0 .../webui => ui}/src/lib/enums/keyboard.ts | 0 .../{server/webui => ui}/src/lib/enums/mcp.ts | 0 .../webui => ui}/src/lib/enums/model.ts | 0 .../webui => ui}/src/lib/enums/server.ts | 0 .../webui => ui}/src/lib/enums/settings.ts | 0 .../webui => ui}/src/lib/enums/tools.ts | 0 .../{server/webui => ui}/src/lib/enums/ui.ts | 0 .../src/lib/hooks/is-mobile.svelte.ts | 0 .../lib/hooks/use-attachment-menu.svelte.ts | 0 .../src/lib/hooks/use-auto-scroll.svelte.ts | 0 .../lib/hooks/use-draft-messages.svelte.ts | 0 .../hooks/use-keyboard-shortcuts.svelte.ts | 0 .../hooks/use-message-edit-context.svelte.ts | 0 .../lib/hooks/use-models-selector.svelte.ts | 3 +- .../lib/hooks/use-processing-state.svelte.ts | 0 .../lib/hooks/use-scroll-carousel.svelte.ts | 0 .../hooks/use-settings-navigation.svelte.ts | 0 .../src/lib/hooks/use-tools-panel.svelte.ts | 5 +- .../src/lib/services/chat.service.ts | 2 +- .../src/lib/services/database.service.ts | 350 ++++++------ .../webui => ui}/src/lib/services/index.ts | 30 +- .../src/lib/services/mcp.service.ts | 0 .../ui/src/lib/services/migration.service.ts | 524 ++++++++++++++++++ .../src/lib/services/models.service.ts | 0 .../services/parameter-sync.service.spec.ts | 2 +- .../lib/services/parameter-sync.service.ts | 13 +- .../src/lib/services/props.service.ts | 0 .../src/lib/services/router.service.ts | 0 .../src/lib/services/tools.service.ts | 0 .../src/lib/stores/agentic.svelte.ts | 14 +- .../src/lib/stores/chat.svelte.ts | 24 +- .../src/lib/stores/conversations.svelte.ts | 13 +- .../src/lib/stores/draft-messages.svelte.ts | 0 .../src/lib/stores/mcp-resources.svelte.ts | 0 .../webui => ui}/src/lib/stores/mcp.svelte.ts | 9 +- .../src/lib/stores/models.svelte.ts | 16 +- .../src/lib/stores/permissions.svelte.ts | 1 + .../src/lib/stores/persisted.svelte.ts | 0 .../src/lib/stores/server.svelte.ts | 4 +- .../lib/stores/settings-referrer.svelte.ts | 0 .../src/lib/stores/settings.svelte.ts | 37 +- .../src/lib/stores/tools.svelte.ts | 7 +- .../webui => ui}/src/lib/types/agentic.d.ts | 0 .../webui => ui}/src/lib/types/api.d.ts | 2 + .../webui => ui}/src/lib/types/chat.d.ts | 0 .../webui => ui}/src/lib/types/common.d.ts | 0 .../webui => ui}/src/lib/types/database.d.ts | 2 +- .../webui => ui}/src/lib/types/index.ts | 0 .../webui => ui}/src/lib/types/mcp.d.ts | 0 .../webui => ui}/src/lib/types/models.d.ts | 0 .../webui => ui}/src/lib/types/settings.d.ts | 0 .../webui => ui}/src/lib/types/tools.d.ts | 0 .../webui => ui}/src/lib/utils/abort.ts | 0 .../webui => ui}/src/lib/utils/agentic.ts | 0 .../webui => ui}/src/lib/utils/api-fetch.ts | 0 .../webui => ui}/src/lib/utils/api-headers.ts | 0 .../src/lib/utils/api-key-validation.ts | 0 .../src/lib/utils/attachment-display.ts | 0 .../src/lib/utils/attachment-type.ts | 0 .../src/lib/utils/audio-recording.ts | 0 .../src/lib/utils/autoresize-textarea.ts | 0 .../webui => ui}/src/lib/utils/branching.ts | 0 .../src/lib/utils/browser-only.ts | 0 .../webui => ui}/src/lib/utils/cache-ttl.ts | 0 .../webui => ui}/src/lib/utils/clipboard.ts | 0 .../webui => ui}/src/lib/utils/code.ts | 0 .../src/lib/utils/config-helpers.ts | 0 .../src/lib/utils/conversation-utils.ts | 0 .../src/lib/utils/convert-files-to-extra.ts | 0 .../webui => ui}/src/lib/utils/cors-proxy.ts | 0 .../{server/webui => ui}/src/lib/utils/css.ts | 0 .../webui => ui}/src/lib/utils/data-url.ts | 0 .../webui => ui}/src/lib/utils/debounce.ts | 0 .../src/lib/utils/file-preview.ts | 0 .../webui => ui}/src/lib/utils/file-type.ts | 0 .../webui => ui}/src/lib/utils/formatters.ts | 0 .../webui => ui}/src/lib/utils/headers.ts | 0 .../src/lib/utils/image-error-fallback.ts | 0 .../webui => ui}/src/lib/utils/index.ts | 3 - .../src/lib/utils/is-ime-composing.ts | 0 .../src/lib/utils/latex-protection.ts | 0 .../src/lib/utils/legacy-migration.ts | 24 +- .../{server/webui => ui}/src/lib/utils/mcp.ts | 0 .../src/lib/utils/modality-file-validation.ts | 0 .../webui => ui}/src/lib/utils/model-names.ts | 0 .../src/lib/utils/pdf-processing.ts | 0 .../src/lib/utils/portal-to-body.ts | 0 .../webui => ui}/src/lib/utils/precision.ts | 0 .../src/lib/utils/process-uploaded-files.ts | 0 .../webui => ui}/src/lib/utils/redact.ts | 0 .../src/lib/utils/request-helpers.ts | 0 .../webui => ui}/src/lib/utils/sanitize.ts | 0 .../webui => ui}/src/lib/utils/svg-to-png.ts | 0 .../lib/utils/syntax-highlight-language.ts | 0 .../webui => ui}/src/lib/utils/text-files.ts | 0 .../webui => ui}/src/lib/utils/text.ts | 0 .../src/lib/utils/uri-template.ts | 0 .../{server/webui => ui}/src/lib/utils/url.ts | 0 .../webui => ui}/src/lib/utils/uuid.ts | 0 .../webui => ui}/src/lib/utils/viewport.ts | 0 .../webui => ui}/src/lib/utils/webp-to-png.ts | 0 .../src/routes/(chat)/+layout.svelte | 0 .../src/routes/(chat)/+page.svelte | 0 .../webui => ui}/src/routes/(chat)/+page.ts | 0 .../src/routes/(chat)/chat/[id]/+page.svelte | 0 .../src/routes/(chat)/chat/[id]/+page.ts | 0 .../webui => ui}/src/routes/+error.svelte | 3 +- .../webui => ui}/src/routes/+layout.svelte | 0 .../src/routes/mcp-servers/+page.svelte | 0 .../src/routes/settings/+layout.svelte | 0 .../routes/settings/[[section]]/+page.svelte | 0 .../webui => ui}/src/styles/katex-custom.scss | 0 tools/{server/webui => ui}/static/favicon.svg | 0 .../{server/webui => ui}/static/loading.html | 0 tools/{server/webui => ui}/svelte.config.js | 4 +- .../client/components/TestWrapper.svelte | 0 .../tests/client/page.svelte.test.ts | 0 .../webui => ui}/tests/e2e/demo.test.ts | 0 .../tests/stories/ChatMessage.stories.svelte | 0 .../stories/ChatScreenForm.stories.svelte | 0 .../tests/stories/Introduction.mdx | 2 +- .../stories/MarkdownContent.stories.svelte | 0 .../stories/SidebarNavigation.stories.svelte | 0 .../tests/stories/fixtures/ai-tutorial.ts | 0 .../tests/stories/fixtures/api-docs.ts | 0 .../tests/stories/fixtures/assets/1.jpg | Bin .../assets/beautiful-flowers-lotus.webp | Bin .../tests/stories/fixtures/assets/example.pdf | Bin .../tests/stories/fixtures/assets/hf-logo.svg | 0 .../tests/stories/fixtures/blog-post.ts | 0 .../tests/stories/fixtures/data-analysis.ts | 0 .../tests/stories/fixtures/empty.ts | 0 .../tests/stories/fixtures/math-formulas.ts | 0 .../tests/stories/fixtures/readme.ts | 0 .../tests/stories/fixtures/storybook-mocks.ts | 0 .../tests/unit/agentic-sections.test.ts | 24 +- .../tests/unit/agentic-strip.test.ts | 0 .../webui => ui}/tests/unit/clipboard.test.ts | 0 .../tests/unit/latex-protection.test.ts | 0 .../tests/unit/mcp-service.test.ts | 0 .../tests/unit/model-id-parser.test.ts | 0 .../tests/unit/model-names.test.ts | 0 .../tests/unit/reasoning-context.test.ts | 0 .../webui => ui}/tests/unit/redact.test.ts | 0 .../tests/unit/request-helpers.test.ts | 0 .../tests/unit/sanitize-headers.test.ts | 0 .../tests/unit/uri-template.test.ts | 0 tools/{server/webui => ui}/tsconfig.json | 0 tools/ui/ui.cpp | 7 + tools/ui/ui.h | 17 + tools/{server/webui => ui}/vite.config.ts | 0 .../webui => ui}/vitest-setup-client.ts | 0 565 files changed, 1614 insertions(+), 698 deletions(-) rename .github/workflows/{webui-build.yml => ui-build.yml} (67%) rename .github/workflows/{server-webui.yml => ui-ci.yml} (75%) rename .github/workflows/{webui-publish.yml => ui-publish.yml} (84%) rename scripts/{webui-download.cmake => ui-download.cmake} (76%) delete mode 100755 tools/server/webui/scripts/post-build.sh delete mode 100644 tools/server/webui/src/lib/constants/localstorage-keys.ts rename tools/{server/webui => ui}/.gitignore (100%) rename tools/{server/webui => ui}/.npmrc (100%) rename tools/{server/webui => ui}/.prettierignore (100%) rename tools/{server/webui => ui}/.prettierrc (100%) rename tools/{server/webui => ui}/.storybook/decorators/ModeWatcherDecorator.svelte (100%) rename tools/{server/webui => ui}/.storybook/decorators/TooltipProviderDecorator.svelte (100%) rename tools/{server/webui => ui}/.storybook/main.ts (100%) rename tools/{server/webui => ui}/.storybook/preview.ts (100%) rename tools/{server/webui => ui}/.storybook/vitest.setup.ts (100%) create mode 100644 tools/ui/CMakeLists.txt rename tools/{server/webui => ui}/README.md (96%) rename tools/{server/webui => ui}/components.json (100%) rename tools/{server/webui => ui}/docs/architecture/high-level-architecture-simplified.md (100%) rename tools/{server/webui => ui}/docs/architecture/high-level-architecture.md (100%) rename tools/{server/webui => ui}/docs/flows/chat-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/conversations-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/data-flow-simplified-model-mode.md (100%) rename tools/{server/webui => ui}/docs/flows/data-flow-simplified-router-mode.md (100%) rename tools/{server/webui => ui}/docs/flows/database-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/mcp-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/models-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/server-flow.md (100%) rename tools/{server/webui => ui}/docs/flows/settings-flow.md (98%) rename tools/{server/webui => ui}/eslint.config.js (93%) rename tools/{server/webui => ui}/package-lock.json (100%) rename tools/{server/webui => ui}/package.json (98%) rename tools/{server/webui => ui}/playwright.config.ts (70%) rename tools/{server/webui => ui}/scripts/dev.sh (89%) rename tools/{server/webui => ui}/scripts/install-git-hooks.sh (62%) rename tools/{server/webui => ui}/scripts/vite-plugin-llama-cpp-build.ts (64%) rename tools/{server/webui => ui}/src/app.css (100%) rename tools/{server/webui => ui}/src/app.d.ts (100%) rename tools/{server/webui => ui}/src/app.html (100%) rename tools/{server/webui => ui}/src/lib/actions/fade-in-view.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/SKILL.md (100%) rename tools/{server/webui => ui}/src/lib/components/app/actions/ActionIcon.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/actions/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/badges/BadgeInfo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/badges/BadgesModality.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/badges/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailFile.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailImage.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemAudio.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemImage.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemText.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemUnavailable.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddButton.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte (97%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte (98%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte (97%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionsAdd.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionModels.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionRecord.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormMcpResourcesList.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerItemHeader.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpResources.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickers.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPrompt.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPromptContent.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageSystem/ChatMessageSystem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUser.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserBubble.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCard.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardContinueRequest.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardPermissionRequest.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIcons.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIconsBranchingControls.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte (99%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageEditForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics/ChatMessageStatistics.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics/ChatMessageStatisticsBadge.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatScreen/ChatScreenDragOverlay.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatScreen/ChatScreenForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/ChatScreen/ChatScreenProcessingInfo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/chat/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/CollapsibleContentBlock.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/MarkdownContent.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/rehype/enhance-code-blocks.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/rehype/enhance-links.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/rehype/rehype-rtl-support.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/rehype/resolve-attachment-images.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/rehype/table-html-restorer.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/MarkdownContent/plugins/remark/literal-html.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/SyntaxHighlightedCode.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/content/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogChatAttachmentsPreview.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogChatError.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogCodePreview.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogConfirmation.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogConversationSelection.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogConversationTitleUpdate.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogEmptyFileAlert.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogExportSettings.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogFileUploadError.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogMcpResourcePreview.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogMcpResourcesBrowser.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogMcpServerAddNew.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogModelInformation.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/DialogModelNotAvailable.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/dialogs/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/forms/InputWithSuggestions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/forms/KeyValuePairs.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/forms/SearchInput.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/forms/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpActiveServersAvatars.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpConnectionLogs.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpLogo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcePreview.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourceTemplateForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcesBrowser/McpResourcesBrowser.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcesBrowser/McpResourcesBrowserEmptyState.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcesBrowser/McpResourcesBrowserHeader.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcesBrowser/McpResourcesBrowserServerItem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpResourcesBrowser/mcp-resources-browser.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCardActions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCardDeleteDialog.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCardEditForm.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCard/McpServerCardToolsList.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerCardSkeleton.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerForm.svelte (96%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerIdentity.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/McpServerInfo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/mcp/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/CodeBlockActions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/ConversationSelection.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/HorizontalScrollCarousel.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/KeyboardShortcutInfo.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/TruncatedText.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/misc/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelBadge.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelId.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelsSelectorDropdown.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelsSelectorList.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelsSelectorOption.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/ModelsSelectorSheet.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/models/utils.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/DesktopIconStrip.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/DropdownMenuActions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/DropdownMenuSearchable.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte (99%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationItem.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationSearch.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/navigation/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/server/ServerErrorSplash.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/server/ServerLoadingSplash.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/server/ServerStatus.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/server/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChatFields.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportSection.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChatImportExportTab.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChatParameterSourceIndicator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChat/SettingsChatToolsTab.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChatDesktopSidebar.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsChatMobileHeader.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsFooter.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsGroup.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/SettingsMcpServers.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/app/settings/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert-dialog/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert/alert-description.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert/alert-title.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert/alert.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/alert/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/badge/badge.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/badge/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/button-group/button-group-root.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/button-group/button-group-separator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/button-group/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/button/button.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/button/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-action.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-description.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card-title.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/card.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/card/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/checkbox/checkbox.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/checkbox/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/collapsible/collapsible-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/collapsible/collapsible-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/collapsible/collapsible.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/collapsible/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-close.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-description.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-overlay.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-title.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/dialog-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dialog/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/dropdown-menu/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/input/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/input/input.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/label/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/label/label.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/popover-close.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/popover-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/popover-portal.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/popover-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/popover/popover.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/scroll-area/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/scroll-area/scroll-area.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-group-heading.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-group.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-label.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-scroll-down-button.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-scroll-up-button.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-separator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/select/select-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/separator/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/separator/separator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-close.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-description.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-overlay.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-title.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sheet/sheet-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/constants.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/context.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-group-action.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-group-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-group-label.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-group.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-input.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-inset.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-action.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-button.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-menu.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-provider.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-rail.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-separator.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/sidebar/sidebar.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/skeleton/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/skeleton/skeleton.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/switch/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/switch/switch.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-body.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-caption.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-cell.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-footer.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-head.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-header.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table-row.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/table/table.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/textarea/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/textarea/textarea.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/tooltip/index.ts (100%) rename tools/{server/webui => ui}/src/lib/components/ui/tooltip/tooltip-content.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/tooltip/tooltip-trigger.svelte (100%) rename tools/{server/webui => ui}/src/lib/components/ui/utils.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/agentic.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/api-endpoints.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/attachment-labels.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/attachment-menu.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/auto-scroll.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/binary-detection.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/cache.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/chat-form.ts (100%) create mode 100644 tools/ui/src/lib/constants/cli-flags.ts rename tools/{server/webui => ui}/src/lib/constants/code-blocks.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/code.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/context-keys.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/css-classes.ts (100%) create mode 100644 tools/ui/src/lib/constants/database.ts rename tools/{server/webui => ui}/src/lib/constants/floating-ui-constraints.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/formatters.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/icons.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/index.ts (93%) rename tools/{server/webui => ui}/src/lib/constants/key-value-pairs.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/latex-protection.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/literal-html.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/markdown.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/max-bundle-size.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/mcp-form.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/mcp-resource.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/mcp.ts (93%) rename tools/{server/webui => ui}/src/lib/constants/message-export.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/model-id.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/precision.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/processing-info.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/routes.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/settings-keys.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/settings-registry.ts (94%) create mode 100644 tools/ui/src/lib/constants/storage.ts rename tools/{server/webui => ui}/src/lib/constants/supported-file-types.ts (98%) rename tools/{server/webui => ui}/src/lib/constants/table-html-restorer.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/title-generation.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/tools.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/tooltip-config.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/ui.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/uri-template.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/url.ts (100%) rename tools/{server/webui => ui}/src/lib/constants/viewport.ts (100%) rename tools/{server/webui => ui}/src/lib/contexts/chat-actions.context.ts (100%) rename tools/{server/webui => ui}/src/lib/contexts/chat-settings-config.context.ts (100%) rename tools/{server/webui => ui}/src/lib/contexts/index.ts (100%) rename tools/{server/webui => ui}/src/lib/contexts/message-edit.context.ts (100%) rename tools/{server/webui => ui}/src/lib/contexts/processing-info.context.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/agentic.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/attachment.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/chat.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/files.ts (98%) rename tools/{server/webui => ui}/src/lib/enums/index.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/keyboard.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/mcp.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/model.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/server.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/settings.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/tools.ts (100%) rename tools/{server/webui => ui}/src/lib/enums/ui.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/is-mobile.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-attachment-menu.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-auto-scroll.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-draft-messages.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-keyboard-shortcuts.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-message-edit-context.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-models-selector.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/hooks/use-processing-state.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-scroll-carousel.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-settings-navigation.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/hooks/use-tools-panel.svelte.ts (89%) rename tools/{server/webui => ui}/src/lib/services/chat.service.ts (99%) rename tools/{server/webui => ui}/src/lib/services/database.service.ts (51%) rename tools/{server/webui => ui}/src/lib/services/index.ts (92%) rename tools/{server/webui => ui}/src/lib/services/mcp.service.ts (100%) create mode 100644 tools/ui/src/lib/services/migration.service.ts rename tools/{server/webui => ui}/src/lib/services/models.service.ts (100%) rename tools/{server/webui => ui}/src/lib/services/parameter-sync.service.spec.ts (98%) rename tools/{server/webui => ui}/src/lib/services/parameter-sync.service.ts (95%) rename tools/{server/webui => ui}/src/lib/services/props.service.ts (100%) rename tools/{server/webui => ui}/src/lib/services/router.service.ts (100%) rename tools/{server/webui => ui}/src/lib/services/tools.service.ts (100%) rename tools/{server/webui => ui}/src/lib/stores/agentic.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/stores/chat.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/stores/conversations.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/stores/draft-messages.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/stores/mcp-resources.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/stores/mcp.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/stores/models.svelte.ts (98%) rename tools/{server/webui => ui}/src/lib/stores/permissions.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/stores/persisted.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/stores/server.svelte.ts (96%) rename tools/{server/webui => ui}/src/lib/stores/settings-referrer.svelte.ts (100%) rename tools/{server/webui => ui}/src/lib/stores/settings.svelte.ts (93%) rename tools/{server/webui => ui}/src/lib/stores/tools.svelte.ts (99%) rename tools/{server/webui => ui}/src/lib/types/agentic.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/api.d.ts (98%) rename tools/{server/webui => ui}/src/lib/types/chat.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/common.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/database.d.ts (97%) rename tools/{server/webui => ui}/src/lib/types/index.ts (100%) rename tools/{server/webui => ui}/src/lib/types/mcp.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/models.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/settings.d.ts (100%) rename tools/{server/webui => ui}/src/lib/types/tools.d.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/abort.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/agentic.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/api-fetch.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/api-headers.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/api-key-validation.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/attachment-display.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/attachment-type.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/audio-recording.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/autoresize-textarea.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/branching.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/browser-only.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/cache-ttl.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/clipboard.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/code.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/config-helpers.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/conversation-utils.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/convert-files-to-extra.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/cors-proxy.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/css.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/data-url.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/debounce.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/file-preview.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/file-type.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/formatters.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/headers.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/image-error-fallback.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/index.ts (97%) rename tools/{server/webui => ui}/src/lib/utils/is-ime-composing.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/latex-protection.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/legacy-migration.ts (93%) rename tools/{server/webui => ui}/src/lib/utils/mcp.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/modality-file-validation.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/model-names.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/pdf-processing.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/portal-to-body.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/precision.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/process-uploaded-files.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/redact.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/request-helpers.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/sanitize.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/svg-to-png.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/syntax-highlight-language.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/text-files.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/text.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/uri-template.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/url.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/uuid.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/viewport.ts (100%) rename tools/{server/webui => ui}/src/lib/utils/webp-to-png.ts (100%) rename tools/{server/webui => ui}/src/routes/(chat)/+layout.svelte (100%) rename tools/{server/webui => ui}/src/routes/(chat)/+page.svelte (100%) rename tools/{server/webui => ui}/src/routes/(chat)/+page.ts (100%) rename tools/{server/webui => ui}/src/routes/(chat)/chat/[id]/+page.svelte (100%) rename tools/{server/webui => ui}/src/routes/(chat)/chat/[id]/+page.ts (100%) rename tools/{server/webui => ui}/src/routes/+error.svelte (95%) rename tools/{server/webui => ui}/src/routes/+layout.svelte (100%) rename tools/{server/webui => ui}/src/routes/mcp-servers/+page.svelte (100%) rename tools/{server/webui => ui}/src/routes/settings/+layout.svelte (100%) rename tools/{server/webui => ui}/src/routes/settings/[[section]]/+page.svelte (100%) rename tools/{server/webui => ui}/src/styles/katex-custom.scss (100%) rename tools/{server/webui => ui}/static/favicon.svg (100%) rename tools/{server/webui => ui}/static/loading.html (100%) rename tools/{server/webui => ui}/svelte.config.js (89%) rename tools/{server/webui => ui}/tests/client/components/TestWrapper.svelte (100%) rename tools/{server/webui => ui}/tests/client/page.svelte.test.ts (100%) rename tools/{server/webui => ui}/tests/e2e/demo.test.ts (100%) rename tools/{server/webui => ui}/tests/stories/ChatMessage.stories.svelte (100%) rename tools/{server/webui => ui}/tests/stories/ChatScreenForm.stories.svelte (100%) rename tools/{server/webui => ui}/tests/stories/Introduction.mdx (93%) rename tools/{server/webui => ui}/tests/stories/MarkdownContent.stories.svelte (100%) rename tools/{server/webui => ui}/tests/stories/SidebarNavigation.stories.svelte (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/ai-tutorial.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/api-docs.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/assets/1.jpg (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/assets/beautiful-flowers-lotus.webp (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/assets/example.pdf (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/assets/hf-logo.svg (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/blog-post.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/data-analysis.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/empty.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/math-formulas.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/readme.ts (100%) rename tools/{server/webui => ui}/tests/stories/fixtures/storybook-mocks.ts (100%) rename tools/{server/webui => ui}/tests/unit/agentic-sections.test.ts (94%) rename tools/{server/webui => ui}/tests/unit/agentic-strip.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/clipboard.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/latex-protection.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/mcp-service.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/model-id-parser.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/model-names.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/reasoning-context.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/redact.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/request-helpers.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/sanitize-headers.test.ts (100%) rename tools/{server/webui => ui}/tests/unit/uri-template.test.ts (100%) rename tools/{server/webui => ui}/tsconfig.json (100%) create mode 100644 tools/ui/ui.cpp create mode 100644 tools/ui/ui.h rename tools/{server/webui => ui}/vite.config.ts (100%) rename tools/{server/webui => ui}/vitest-setup-client.ts (100%) diff --git a/.editorconfig b/.editorconfig index 7c1af01a1d8..5663b8fdbfc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,7 +45,7 @@ insert_final_newline = unset trim_trailing_whitespace = unset insert_final_newline = unset -[tools/server/webui/**] +[tools/ui/**] indent_style = unset indent_size = unset end_of_line = unset diff --git a/.github/labeler.yml b/.github/labeler.yml index 2120672eee5..60aa51d2cc2 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -73,10 +73,10 @@ android: - changed-files: - any-glob-to-any-file: - examples/llama.android/** -server/webui: +server/ui: - changed-files: - any-glob-to-any-file: - - tools/server/webui/** + - tools/ui/** server: - changed-files: - any-glob-to-any-file: diff --git a/.github/workflows/build-self-hosted.yml b/.github/workflows/build-self-hosted.yml index ee8fd41d7c7..2851c45601f 100644 --- a/.github/workflows/build-self-hosted.yml +++ b/.github/workflows/build-self-hosted.yml @@ -68,6 +68,8 @@ jobs: - name: Determine tag name id: tag uses: ./.github/actions/get-tag-name + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} ggml-ci-nvidia-cuda: needs: determine-tag @@ -81,7 +83,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | nvidia-smi GG_BUILD_CUDA=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp @@ -98,7 +100,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 GGML_VK_DISABLE_COOPMAT2=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp @@ -115,7 +117,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp /mnt/llama.cpp @@ -205,7 +207,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | GG_BUILD_METAL=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp @@ -234,7 +236,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | GG_BUILD_WEBGPU=1 GG_BUILD_WEBGPU_DAWN_PREFIX="$GITHUB_WORKSPACE/dawn" \ bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp @@ -251,7 +253,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp @@ -270,7 +272,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary GG_BUILD_VULKAN=1 bash ./ci/run.sh ~/results/llama.cpp ~/mnt/llama.cpp @@ -291,7 +293,7 @@ jobs: MSYSTEM: UCRT64 CHERE_INVOKING: 1 PATH: C:\msys64\ucrt64\bin;C:\msys64\usr\bin;C:\Windows\System32;${{ env.PATH }} - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | vulkaninfo --summary # Skip python related tests with GG_BUILD_LOW_PERF=1 since Windows MSYS2 UCRT64 currently fails to create @@ -332,7 +334,7 @@ jobs: - name: Test id: ggml-ci env: - HF_WEBUI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} + HF_UI_VERSION: ${{ needs.determine-tag.outputs.tag_name }} run: | source ./openvino_toolkit/setupvars.sh GG_BUILD_OPENVINO=1 GGML_OPENVINO_DEVICE=GPU GG_BUILD_LOW_PERF=1 bash ./ci/run.sh ./tmp/results ./tmp/mnt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb512fd875c..1880c155c85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,13 +36,8 @@ env: CMAKE_ARGS: "-DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_SERVER=ON -DGGML_RPC=ON" jobs: - webui-build: - name: Build WebUI - uses: ./.github/workflows/webui-build.yml macOS-cpu: - needs: - - webui-build strategy: matrix: @@ -71,11 +66,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -113,8 +109,6 @@ jobs: name: llama-bin-macos-${{ matrix.build }}.tar.gz ubuntu-cpu: - needs: - - webui-build strategy: matrix: @@ -135,11 +129,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache if: ${{ matrix.build != 's390x' }} @@ -191,8 +186,6 @@ jobs: name: llama-bin-ubuntu-${{ matrix.build }}.tar.gz ubuntu-vulkan: - needs: - - webui-build strategy: matrix: @@ -211,11 +204,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -268,8 +262,6 @@ jobs: name: llama-bin-ubuntu-vulkan-${{ matrix.build }}.tar.gz android-arm64: - needs: - - webui-build runs-on: ubuntu-latest @@ -283,11 +275,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -346,8 +339,6 @@ jobs: name: llama-bin-android-arm64.tar.gz ubuntu-24-openvino: - needs: - - webui-build runs-on: ubuntu-24.04 @@ -370,11 +361,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -435,8 +427,6 @@ jobs: name: llama-bin-ubuntu-openvino-${{ env.OPENVINO_VERSION_MAJOR }}-x64.tar.gz windows-cpu: - needs: - - webui-build runs-on: windows-2025 @@ -452,11 +442,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -496,8 +487,6 @@ jobs: name: llama-bin-win-cpu-${{ matrix.arch }}.zip windows: - needs: - - webui-build runs-on: windows-2025 @@ -522,11 +511,12 @@ jobs: id: checkout uses: actions/checkout@v6 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -587,8 +577,6 @@ jobs: name: llama-bin-win-${{ matrix.backend }}-${{ matrix.arch }}.zip windows-cuda: - needs: - - webui-build runs-on: windows-2022 @@ -601,11 +589,12 @@ jobs: id: checkout uses: actions/checkout@v6 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: Install ccache uses: ggml-org/ccache-action@v1.2.21 @@ -667,8 +656,6 @@ jobs: name: cudart-llama-bin-win-cuda-${{ matrix.cuda }}-x64.zip windows-sycl: - needs: - - webui-build runs-on: windows-2022 @@ -708,11 +695,12 @@ jobs: Expand-Archive -Path "level-zero-win-sdk.zip" -DestinationPath "C:/level-zero-sdk" -Force "LEVEL_ZERO_V1_SDK_PATH=C:/level-zero-sdk" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -781,8 +769,6 @@ jobs: name: llama-bin-win-sycl-x64.zip ubuntu-24-sycl: - needs: - - webui-build strategy: matrix: @@ -831,11 +817,12 @@ jobs: wget -q "https://github.com/oneapi-src/level-zero/releases/download/v${LEVEL_ZERO_VERSION}/level-zero-devel_${LEVEL_ZERO_VERSION}%2B${LEVEL_ZERO_UBUNTU_VERSION}_amd64.deb" -O level-zero-devel.deb sudo apt-get install -y ./level-zero.deb ./level-zero-devel.deb - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: ccache uses: ggml-org/ccache-action@v1.2.21 @@ -876,8 +863,6 @@ jobs: name: llama-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz ubuntu-22-rocm: - needs: - - webui-build runs-on: ubuntu-22.04 @@ -895,11 +880,12 @@ jobs: with: fetch-depth: 0 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: Free up disk space uses: ggml-org/free-disk-space@v1.3.1 @@ -988,8 +974,6 @@ jobs: name: llama-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz windows-hip: - needs: - - webui-build runs-on: windows-2022 @@ -1007,11 +991,12 @@ jobs: id: checkout uses: actions/checkout@v6 - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: Grab rocWMMA package id: grab_rocwmma @@ -1259,7 +1244,6 @@ jobs: runs-on: ubuntu-slim needs: - - webui-build - windows - windows-cpu - windows-cuda @@ -1404,14 +1388,14 @@ jobs: } } - webui-publish: + ui-publish: if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} needs: - release - uses: ./.github/workflows/webui-publish.yml + uses: ./.github/workflows/ui-publish.yml with: version_tag: ${{ needs.release.outputs.tag_name }} secrets: - hf_token: ${{ secrets.HF_TOKEN_WEBUI_STATIC_OUTPUT }} + hf_token: ${{ secrets.HF_TOKEN_UI_STATIC_OUTPUT }} diff --git a/.github/workflows/server-sanitize.yml b/.github/workflows/server-sanitize.yml index 4c9f447cf8b..53c9968ee96 100644 --- a/.github/workflows/server-sanitize.yml +++ b/.github/workflows/server-sanitize.yml @@ -67,6 +67,13 @@ jobs: fetch-depth: 0 ref: ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha || github.head_ref || github.ref_name }} + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" + - name: Build id: cmake_build run: | diff --git a/.github/workflows/server-self-hosted.yml b/.github/workflows/server-self-hosted.yml index 117c79c2f87..d3b3e4cc7e8 100644 --- a/.github/workflows/server-self-hosted.yml +++ b/.github/workflows/server-self-hosted.yml @@ -39,12 +39,7 @@ concurrency: cancel-in-progress: true jobs: - webui-build: - name: Build WebUI - uses: ./.github/workflows/webui-build.yml - server-metal: - needs: webui-build runs-on: [self-hosted, llama-server, macOS, ARM64] name: server-metal (${{ matrix.wf_name }}) @@ -72,11 +67,12 @@ jobs: fetch-depth: 0 ref: ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha || github.head_ref || github.ref_name }} - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: Build id: cmake_build diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index f12e7b7366f..7b9c5a3a3d8 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -54,12 +54,7 @@ concurrency: cancel-in-progress: true jobs: - webui-build: - name: Build WebUI - uses: ./.github/workflows/webui-build.yml - server: - needs: webui-build runs-on: ubuntu-latest name: server (${{ matrix.wf_name }}) @@ -98,11 +93,12 @@ jobs: fetch-depth: 0 ref: ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha || github.head_ref || github.ref_name }} - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" + cache: "npm" + cache-dependency-path: "tools/ui/package-lock.json" - name: Build id: cmake_build @@ -136,7 +132,6 @@ jobs: SLOW_TESTS=1 pytest -v -x server-windows: - needs: webui-build runs-on: windows-2022 steps: @@ -147,11 +142,10 @@ jobs: fetch-depth: 0 ref: ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha || github.head_ref || github.ref_name }} - - name: Download WebUI build artifact - uses: actions/download-artifact@v7 + - name: Setup Node.js + uses: actions/setup-node@v6 with: - name: webui-build - path: tools/server/public/ + node-version: "24" - name: Build id: cmake_build diff --git a/.github/workflows/webui-build.yml b/.github/workflows/ui-build.yml similarity index 67% rename from .github/workflows/webui-build.yml rename to .github/workflows/ui-build.yml index 7f512a69d3d..511c96fb6fc 100644 --- a/.github/workflows/webui-build.yml +++ b/.github/workflows/ui-build.yml @@ -1,11 +1,11 @@ -name: Build WebUI +name: UI Build on: workflow_call: jobs: build: - name: Build WebUI + name: Build static output runs-on: ubuntu-slim env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} @@ -19,26 +19,26 @@ jobs: with: node-version: "24" cache: "npm" - cache-dependency-path: "tools/server/webui/package-lock.json" + cache-dependency-path: "tools/ui/package-lock.json" - name: Install dependencies run: npm ci - working-directory: tools/server/webui + working-directory: tools/ui - name: Build application run: npm run build - working-directory: tools/server/webui + working-directory: tools/ui - name: Generate checksums run: | - cd tools/server/public + cd build/tools/ui/dist for f in *; do sha256sum "$f" | awk '{print $1, $2}' >> checksums.txt done - - name: Upload built webui + - name: Upload built UI uses: actions/upload-artifact@v6 with: - name: webui-build - path: tools/server/public/ + name: ui-build + path: build/tools/ui/dist/ retention-days: 1 diff --git a/.github/workflows/server-webui.yml b/.github/workflows/ui-ci.yml similarity index 75% rename from .github/workflows/server-webui.yml rename to .github/workflows/ui-ci.yml index 72c2016efe7..43d6e125687 100644 --- a/.github/workflows/server-webui.yml +++ b/.github/workflows/ui-ci.yml @@ -1,4 +1,4 @@ -name: Server WebUI +name: CI (UI) on: workflow_dispatch: @@ -11,15 +11,15 @@ on: branches: - master paths: [ - '.github/workflows/server-webui.yml', - 'tools/server/webui/**.*', + '.github/workflows/ui-ci.yml', + 'tools/ui/**.*', 'tools/server/tests/**.*' ] pull_request: types: [opened, synchronize, reopened] paths: [ - '.github/workflows/server-webui.yml', - 'tools/server/webui/**.*', + '.github/workflows/ui-ci.yml', + 'tools/ui/**.*', 'tools/server/tests/**.*' ] @@ -34,13 +34,13 @@ concurrency: cancel-in-progress: true jobs: - webui-build: - name: Build WebUI - uses: ./.github/workflows/webui-build.yml + ui-build: + name: Build static output + uses: ./.github/workflows/ui-build.yml - webui-checks: - name: WebUI Checks - needs: webui-build + ui-checks: + name: UI Checks + needs: ui-build runs-on: ubuntu-24.04-arm continue-on-error: true steps: @@ -56,43 +56,43 @@ jobs: with: node-version: "24" cache: "npm" - cache-dependency-path: "tools/server/webui/package-lock.json" + cache-dependency-path: "tools/ui/package-lock.json" - name: Install dependencies id: setup if: ${{ steps.node.conclusion == 'success' }} run: npm ci - working-directory: tools/server/webui + working-directory: tools/ui - name: Run type checking if: ${{ always() && steps.setup.conclusion == 'success' }} run: npm run check - working-directory: tools/server/webui + working-directory: tools/ui - name: Run linting if: ${{ always() && steps.setup.conclusion == 'success' }} run: npm run lint - working-directory: tools/server/webui + working-directory: tools/ui - name: Install Playwright browsers id: playwright if: ${{ always() && steps.setup.conclusion == 'success' }} run: npx playwright install --with-deps - working-directory: tools/server/webui + working-directory: tools/ui - name: Run Client tests if: ${{ always() && steps.playwright.conclusion == 'success' }} run: npm run test:client - working-directory: tools/server/webui + working-directory: tools/ui - name: Run Unit tests if: ${{ always() && steps.playwright.conclusion == 'success' }} run: npm run test:unit - working-directory: tools/server/webui + working-directory: tools/ui e2e-tests: name: E2E Tests - needs: webui-build + needs: ui-build runs-on: ubuntu-24.04-arm steps: - name: Checkout code @@ -107,36 +107,36 @@ jobs: with: node-version: "24" cache: "npm" - cache-dependency-path: "tools/server/webui/package-lock.json" + cache-dependency-path: "tools/ui/package-lock.json" - name: Install dependencies id: setup if: ${{ steps.node.conclusion == 'success' }} run: npm ci - working-directory: tools/server/webui + working-directory: tools/ui - name: Build application if: ${{ always() && steps.setup.conclusion == 'success' }} run: npm run build - working-directory: tools/server/webui + working-directory: tools/ui - name: Install Playwright browsers id: playwright if: ${{ always() && steps.setup.conclusion == 'success' }} run: npx playwright install --with-deps - working-directory: tools/server/webui + working-directory: tools/ui - name: Build Storybook if: ${{ always() && steps.playwright.conclusion == 'success' }} run: npm run build-storybook - working-directory: tools/server/webui + working-directory: tools/ui - name: Run UI tests if: ${{ always() && steps.playwright.conclusion == 'success' }} run: npm run test:ui -- --testTimeout=60000 - working-directory: tools/server/webui + working-directory: tools/ui - name: Run E2E tests if: ${{ always() && steps.playwright.conclusion == 'success' }} run: npm run test:e2e - working-directory: tools/server/webui + working-directory: tools/ui diff --git a/.github/workflows/webui-publish.yml b/.github/workflows/ui-publish.yml similarity index 84% rename from .github/workflows/webui-publish.yml rename to .github/workflows/ui-publish.yml index bf0d707a23e..33d7415c9c4 100644 --- a/.github/workflows/webui-publish.yml +++ b/.github/workflows/ui-publish.yml @@ -1,4 +1,4 @@ -name: WebUI Publish +name: UI Publish on: workflow_call: @@ -14,14 +14,14 @@ on: jobs: publish: - name: Publish WebUI Static Output + name: Publish UI Static Output runs-on: ubuntu-24.04-arm permissions: contents: read env: - HF_BUCKET_NAME: ${{ vars.HF_BUCKET_WEBUI_STATIC_OUTPUT }} + HF_BUCKET_NAME: ${{ vars.HF_BUCKET_UI_STATIC_OUTPUT }} steps: - name: Checkout code @@ -29,11 +29,11 @@ jobs: with: fetch-depth: 1 - - name: Download WebUI build artifact + - name: Download UI build artifact uses: actions/download-artifact@v7 with: - name: webui-build - path: tools/server/public/ + name: ui-build + path: build/tools/ui/dist/ - name: Install Hugging Face Hub CLI run: pip install -U huggingface_hub @@ -44,12 +44,12 @@ jobs: - name: Sync built files to Hugging Face bucket (version tag) run: | # Upload the built files to the Hugging Face bucket under the release version - hf buckets sync tools/server/public hf://buckets/ggml-org/${{ env.HF_BUCKET_NAME }}/${{ inputs.version_tag }} --delete --quiet + hf buckets sync build/tools/ui/dist hf://buckets/ggml-org/${{ env.HF_BUCKET_NAME }}/${{ inputs.version_tag }} --delete --quiet - name: Sync built files to Hugging Face bucket (latest) run: | # Also upload to the 'latest' directory for fallback downloads - hf buckets sync tools/server/public hf://buckets/ggml-org/${{ env.HF_BUCKET_NAME }}/latest --delete --quiet + hf buckets sync build/tools/ui/dist hf://buckets/ggml-org/${{ env.HF_BUCKET_NAME }}/latest --delete --quiet - name: Verify upload run: | diff --git a/.gitignore b/.gitignore index 5f53fbf7a61..8dc9d7d0b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,6 @@ /tmp/ /autogen-*.md /common/build-info.cpp -/tools/server/public # Deprecated @@ -93,10 +92,12 @@ !/examples/sycl/*.bat !/examples/sycl/*.sh -# Server Web UI temporary files +# Server Web UI temporary files (+ legacy directory) /tools/server/webui/node_modules /tools/server/webui/dist +/tools/ui/node_modules +/tools/ui/dist # Python diff --git a/CMakeLists.txt b/CMakeLists.txt index 244f4cb4982..44746072313 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,8 +108,23 @@ option(LLAMA_BUILD_TESTS "llama: build tests" option(LLAMA_BUILD_TOOLS "llama: build tools" ${LLAMA_STANDALONE}) option(LLAMA_BUILD_EXAMPLES "llama: build examples" ${LLAMA_STANDALONE}) option(LLAMA_BUILD_SERVER "llama: build server example" ${LLAMA_STANDALONE}) -option(LLAMA_BUILD_WEBUI "llama: build the embedded Web UI for server" ON) -option(LLAMA_USE_PREBUILT_WEBUI "llama: use prebuilt WebUI from HF Bucket when available (requires LLAMA_BUILD_WEBUI=ON)" ON) +# Deprecated: use LLAMA_BUILD_UI instead (kept for backward compat) +option(LLAMA_BUILD_WEBUI "llama: build the embedded Web UI for server (deprecated: use LLAMA_BUILD_UI)" ON) +option(LLAMA_USE_PREBUILT_WEBUI "llama: use prebuilt WebUI from HF Bucket when available (deprecated: use LLAMA_USE_PREBUILT_UI)" ON) + +# New option names +option(LLAMA_BUILD_UI "llama: build the embedded Web UI for server" ON) +option(LLAMA_USE_PREBUILT_UI "llama: use prebuilt UI from HF Bucket when available (requires LLAMA_BUILD_UI=ON)" ON) + +# Backward compat: when old var is set but new one isn't, forward the value +if(DEFINED LLAMA_BUILD_WEBUI AND NOT DEFINED LLAMA_BUILD_UI) + set(LLAMA_BUILD_UI ${LLAMA_BUILD_WEBUI}) + message(DEPRECATION "LLAMA_BUILD_WEBUI is deprecated, use LLAMA_BUILD_UI instead") +endif() +if(DEFINED LLAMA_USE_PREBUILT_WEBUI AND NOT DEFINED LLAMA_USE_PREBUILT_UI) + set(LLAMA_USE_PREBUILT_UI ${LLAMA_USE_PREBUILT_WEBUI}) + message(DEPRECATION "LLAMA_USE_PREBUILT_WEBUI is deprecated, use LLAMA_USE_PREBUILT_UI instead") +endif() option(LLAMA_TOOLS_INSTALL "llama: install tools" ${LLAMA_TOOLS_INSTALL_DEFAULT}) option(LLAMA_TESTS_INSTALL "llama: install tests" ON) diff --git a/CODEOWNERS b/CODEOWNERS index a4395969fe3..f58f0f830fa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,7 +15,7 @@ # ggml-org/llama-common : ggerganov, aldehir, angt, danbev, ngxson, pwilkin # ggml-org/llama-mtmd : ngxson # ggml-org/llama-server : ggerganov, ngxson, allozaur, angt, ServeurpersoCom -# ggml-org/llama-webui : allozaur +# ggml-org/llama-ui : allozaur /.devops/*.Dockerfile @ngxson /.github/actions/ @ggml-org/ci @@ -107,7 +107,7 @@ /tools/rpc/ @ggml-org/ggml-rpc /tools/server/* @ggml-org/llama-server # no subdir /tools/server/tests/ @ggml-org/llama-server -/tools/server/webui/ @ggml-org/llama-webui +/tools/ui/ @ggml-org/llama-ui /tools/tokenize/ @ggerganov /tools/tts/ @ggerganov /vendor/ @ggerganov diff --git a/common/arg.cpp b/common/arg.cpp index 15d5ad77af1..2129a9c7266 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2844,28 +2844,64 @@ common_params_context common_params_parser_init(common_params & params, llama_ex params.api_prefix = value; } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_API_PREFIX")); + // Deprecated: use --ui-config instead (kept for backward compat) add_opt(common_arg( {"--webui-config"}, "JSON", - "JSON that provides default WebUI settings (overrides WebUI defaults)", + "[DEPRECATED: use --ui-config] JSON that provides default WebUI settings (overrides WebUI defaults)", [](common_params & params, const std::string & value) { + params.ui_config_json = value; params.webui_config_json = value; } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_WEBUI_CONFIG")); + + add_opt(common_arg( + {"--ui-config"}, "JSON", + "JSON that provides default UI settings (overrides UI defaults)", + [](common_params & params, const std::string & value) { + params.ui_config_json = value; + params.webui_config_json = value; + } + ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_UI_CONFIG")); + + // Deprecated: use --ui-config-file instead (kept for backward compat) add_opt(common_arg( {"--webui-config-file"}, "PATH", - "JSON file that provides default WebUI settings (overrides WebUI defaults)", + "[DEPRECATED: use --ui-config-file] JSON file that provides default WebUI settings (overrides WebUI defaults)", [](common_params & params, const std::string & value) { - params.webui_config_json = read_file(value); + params.ui_config_json = read_file(value); + params.webui_config_json = params.ui_config_json; } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_WEBUI_CONFIG_FILE")); + + add_opt(common_arg( + {"--ui-config-file"}, "PATH", + "JSON file that provides default UI settings (overrides UI defaults)", + [](common_params & params, const std::string & value) { + params.ui_config_json = read_file(value); + params.webui_config_json = params.ui_config_json; + } + ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_UI_CONFIG_FILE")); + + // Deprecated: use --ui-mcp-proxy instead (kept for backward compat) add_opt(common_arg( {"--webui-mcp-proxy"}, {"--no-webui-mcp-proxy"}, - string_format("experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: %s)", params.webui_mcp_proxy ? "enabled" : "disabled"), + "[DEPRECATED: use --ui-mcp-proxy/--no-ui-mcp-proxy] experimental: whether to enable MCP CORS proxy", [](common_params & params, bool value) { + params.ui_mcp_proxy = value; params.webui_mcp_proxy = value; } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_WEBUI_MCP_PROXY")); + + add_opt(common_arg( + {"--ui-mcp-proxy"}, + {"--no-ui-mcp-proxy"}, + "experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)", + [](common_params & params, bool value) { + params.ui_mcp_proxy = value; + params.webui_mcp_proxy = value; + } + ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_UI_MCP_PROXY")); add_opt(common_arg( {"--tools"}, "TOOL1,TOOL2,...", "experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)\n" @@ -2875,14 +2911,26 @@ common_params_context common_params_parser_init(common_params & params, llama_ex params.server_tools = parse_csv_row(value); } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_TOOLS")); + // Deprecated: use --ui/--no-ui instead (kept for backward compat) add_opt(common_arg( {"--webui"}, {"--no-webui"}, - string_format("whether to enable the Web UI (default: %s)", params.webui ? "enabled" : "disabled"), + "[DEPRECATED: use --ui/--no-ui] whether to enable the Web UI", [](common_params & params, bool value) { + params.ui = value; params.webui = value; } ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_WEBUI")); + + add_opt(common_arg( + {"--ui"}, + {"--no-ui"}, + string_format("whether to enable the Web UI (default: %s)", params.ui ? "enabled" : "disabled"), + [](common_params & params, bool value) { + params.ui = value; + params.webui = value; + } + ).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_UI")); add_opt(common_arg( {"--embedding", "--embeddings"}, string_format("restrict to only support embedding use case; use only with dedicated embedding models (default: %s)", params.embedding ? "enabled" : "disabled"), diff --git a/common/common.h b/common/common.h index 764574e23a0..c6223c4b515 100644 --- a/common/common.h +++ b/common/common.h @@ -604,15 +604,23 @@ struct common_params { std::map default_template_kwargs; - // webui configs -#ifdef LLAMA_WEBUI_DEFAULT_ENABLED - bool webui = LLAMA_WEBUI_DEFAULT_ENABLED != 0; + // UI configs +#ifdef LLAMA_UI_DEFAULT_ENABLED + bool ui = LLAMA_UI_DEFAULT_ENABLED != 0; +#elif defined(LLAMA_WEBUI_DEFAULT_ENABLED) + bool ui = LLAMA_WEBUI_DEFAULT_ENABLED != 0; #else - bool webui = true; // default to enabled when not set + bool ui = true; // default to enabled when not set #endif + + // Deprecated: use ui, ui_mcp_proxy, ui_config_json instead + bool webui = ui; bool webui_mcp_proxy = false; std::string webui_config_json; + bool ui_mcp_proxy = false; + std::string ui_config_json; + // "advanced" endpoints are disabled by default for better security bool endpoint_slots = true; bool endpoint_props = false; // only control POST requests, not GET diff --git a/scripts/webui-download.cmake b/scripts/ui-download.cmake similarity index 76% rename from scripts/webui-download.cmake rename to scripts/ui-download.cmake index 6695c018035..65143642a2c 100644 --- a/scripts/webui-download.cmake +++ b/scripts/ui-download.cmake @@ -1,5 +1,5 @@ -# Download webui assets from Hugging Face Bucket at build time -# Usage: cmake -DPUBLIC_DIR=... -DHF_BUCKET=... -DHF_VERSION=... -DASSETS="a;b;c" -P scripts/webui-download.cmake +# Download UI assets from Hugging Face Bucket at build time +# Usage: cmake -DPUBLIC_DIR=... -DHF_BUCKET=... -DHF_VERSION=... -DASSETS="a;b;c" -P scripts/ui-download.cmake # # Asset provisioning priority: # 1. Pre-built assets already in PUBLIC_DIR (cached from a previous run) @@ -14,7 +14,7 @@ set(HF_VERSION "" CACHE STRING "Version to download (empty = resolve from git) set(ASSETS "" CACHE STRING "Plus-separated list of asset filenames (+)") set(STAMP_FILE "" CACHE STRING "Stamp file to create on success (optional)") set(SOURCE_DIR "" CACHE STRING "Project source root (to resolve version from git)") -set(NPM_DIR "" CACHE STRING "WebUI source directory (to run npm build)") +set(NPM_DIR "" CACHE STRING "UI source directory (to run npm build)") set(HF_ENABLED "" CACHE STRING "Whether to allow HF Bucket download (ON/OFF)") # --------------------------------------------------------------------------- @@ -25,8 +25,8 @@ if("${RESOLVED_VERSION}" STREQUAL "" AND NOT "${SOURCE_DIR}" STREQUAL "") if(EXISTS "${SOURCE_DIR}/cmake/build-info.cmake") include("${SOURCE_DIR}/cmake/build-info.cmake") if(NOT "${BUILD_NUMBER}" STREQUAL "" AND NOT BUILD_NUMBER EQUAL 0) - set(RESOLVED_VERSION "${BUILD_NUMBER}") - message(STATUS "WebUI: resolved version from git: ${RESOLVED_VERSION}") + set(RESOLVED_VERSION "b${BUILD_NUMBER}") + message(STATUS "UI: resolved version from git: ${RESOLVED_VERSION}") endif() endif() endif() @@ -43,7 +43,7 @@ if(NOT "${STAMP_FILE}" STREQUAL "" AND EXISTS "${STAMP_FILE}") file(READ "${STAMP_FILE}" STAMPED_VERSION) string(STRIP "${STAMPED_VERSION}" STAMPED_VERSION) if(NOT "${STAMPED_VERSION}" STREQUAL "${RESOLVED_VERSION}") - message(STATUS "WebUI: version changed (${STAMPED_VERSION} -> ${RESOLVED_VERSION}), re-building") + message(STATUS "UI: version changed (${STAMPED_VERSION} -> ${RESOLVED_VERSION}), re-building") set(FORCE_REBUILD TRUE) endif() endif() @@ -60,7 +60,7 @@ foreach(asset ${ASSETS}) endforeach() if(ALL_EXISTS AND NOT FORCE_REBUILD) - message(STATUS "WebUI: all assets already exist in ${PUBLIC_DIR}, skipping") + message(STATUS "UI: all assets already exist in ${PUBLIC_DIR}, skipping") return() endif() @@ -76,27 +76,27 @@ if(NOT PROVISION_SUCCESS AND NOT "${NPM_DIR}" STREQUAL "") # Check if npm is available before attempting npm build find_program(NPM_EXECUTABLE npm) if(NPM_EXECUTABLE) - message(STATUS "WebUI: building from source in ${NPM_DIR}") + message(STATUS "UI: building from source in ${NPM_DIR}") # Run npm install if node_modules is missing if(NOT EXISTS "${NPM_DIR}/node_modules") - message(STATUS "WebUI: running npm install (first time)") + message(STATUS "UI: running npm install (first time)") execute_process( - COMMAND npm install + COMMAND ${NPM_EXECUTABLE} install WORKING_DIRECTORY "${NPM_DIR}" RESULT_VARIABLE NPM_INSTALL_RESULT OUTPUT_VARIABLE NPM_OUT ERROR_VARIABLE NPM_ERR ) if(NOT NPM_INSTALL_RESULT EQUAL 0) - message(STATUS "WebUI: npm install failed (${NPM_INSTALL_RESULT}), falling back to download") + message(STATUS "UI: npm install failed (${NPM_INSTALL_RESULT}), falling back to download") message(STATUS " stderr: ${NPM_ERR}") endif() endif() # Run the build execute_process( - COMMAND npm run build + COMMAND ${NPM_EXECUTABLE} run build WORKING_DIRECTORY "${NPM_DIR}" RESULT_VARIABLE NPM_BUILD_RESULT OUTPUT_VARIABLE NPM_OUT @@ -114,20 +114,20 @@ if(NOT PROVISION_SUCCESS AND NOT "${NPM_DIR}" STREQUAL "") endforeach() if(ALL_BUILT) - message(STATUS "WebUI: local npm build succeeded") + message(STATUS "UI: local npm build succeeded") set(PROVISION_SUCCESS TRUE) else() - message(STATUS "WebUI: npm build completed but assets missing from ${PUBLIC_DIR}, falling back to download") + message(STATUS "UI: npm build completed but assets missing from ${PUBLIC_DIR}, falling back to download") endif() else() - message(STATUS "WebUI: npm build failed (${NPM_BUILD_RESULT}), falling back to download") + message(STATUS "UI: npm build failed (${NPM_BUILD_RESULT}), falling back to download") message(STATUS " stderr: ${NPM_ERR}") endif() else() - message(STATUS "WebUI: npm not found, skipping npm build and trying HF Bucket download") + message(STATUS "UI: npm not found, skipping npm build and trying HF Bucket download") endif() else() - message(STATUS "WebUI: NPM_DIR (${NPM_DIR}) has no package.json, skipping npm build") + message(STATUS "UI: NPM_DIR (${NPM_DIR}) has no package.json, skipping npm build") endif() endif() @@ -148,7 +148,7 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) string(REGEX REPLACE "^([^:]+):.*$" "\\1" url_label "${entry}") string(REGEX REPLACE "^[^:]+:(.*)$" "\\1" base_url "${entry}") - message(STATUS "WebUI: downloading assets from ${url_label}: ${base_url}") + message(STATUS "UI: downloading assets from ${url_label}: ${base_url}") # Download each asset set(ALL_OK TRUE) @@ -161,11 +161,11 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) list(GET download_status 0 download_result) if(NOT download_result EQUAL 0) list(GET download_status 1 error_message) - message(STATUS "WebUI: failed to download ${asset} from ${url_label}: ${error_message}") + message(STATUS "UI: failed to download ${asset} from ${url_label}: ${error_message}") set(ALL_OK FALSE) break() endif() - message(STATUS "WebUI: downloaded ${asset}") + message(STATUS "UI: downloaded ${asset}") endforeach() if(NOT ALL_OK) @@ -179,7 +179,7 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) ) list(GET checksum_status 0 checksum_result) if(checksum_result EQUAL 0) - message(STATUS "WebUI: verifying checksums...") + message(STATUS "UI: verifying checksums...") file(STRINGS "${PUBLIC_DIR}/checksums.txt" CHECKSUMS_CONTENT) foreach(asset ${ASSETS}) set(download_path "${PUBLIC_DIR}/${asset}") @@ -187,12 +187,13 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) string(TOLOWER "${asset_hash}" EXPECTED_HASH_LOWER) string(REGEX MATCH "${EXPECTED_HASH_LOWER}[ \\t]+${asset}" CHECKSUM_LINE "${CHECKSUMS_CONTENT}") if(NOT CHECKSUM_LINE) - message(WARNING "WebUI: checksum verification failed for ${asset}") - message(WARNING " downloaded file may not match expected checksum, but will be used") + message(WARNING "UI: checksum verification failed for ${asset}") + set(ALL_OK FALSE) + break() endif() endforeach() if(ALL_OK) - message(STATUS "WebUI: all checksums verified") + message(STATUS "UI: all checksums verified") endif() endif() @@ -203,9 +204,9 @@ if(NOT PROVISION_SUCCESS AND HF_ENABLED) endforeach() if(PROVISION_SUCCESS) - message(STATUS "WebUI: provisioning complete") + message(STATUS "UI: provisioning complete") else() - message(WARNING "WebUI: failed to download assets from HF Bucket (${HF_BUCKET})") + message(WARNING "UI: failed to download assets from HF Bucket (${HF_BUCKET})") endif() endif() @@ -217,6 +218,6 @@ if(PROVISION_SUCCESS) file(WRITE "${STAMP_FILE}" "${RESOLVED_VERSION}") endif() else() - message(WARNING "WebUI: no source available. Neither local build (${NPM_DIR}) nor HF Bucket download succeeded.") - message(WARNING "WebUI: building server without embedded WebUI. Set LLAMA_BUILD_WEBUI=OFF to suppress this warning.") + message(WARNING "UI: no source available. Neither local build (${NPM_DIR}) nor HF Bucket download succeeded.") + message(WARNING "UI: building server without embedded UI. Set LLAMA_BUILD_UI=OFF to suppress this warning.") endif() diff --git a/scripts/xxd.cmake b/scripts/xxd.cmake index 14d2753808a..73f6cfff7f2 100644 --- a/scripts/xxd.cmake +++ b/scripts/xxd.cmake @@ -1,5 +1,5 @@ # CMake equivalent of `xxd -i ${INPUT} ${OUTPUT}` -# Usage: cmake -DINPUT=tools/server/public/index.html -DOUTPUT=tools/server/index.html.hpp -P scripts/xxd.cmake +# Usage: cmake -DINPUT=build/tools/ui/dist/index.html -DOUTPUT=build/tools/ui/dist/index.html.hpp -P scripts/xxd.cmake SET(INPUT "" CACHE STRING "Input File") SET(OUTPUT "" CACHE STRING "Output File") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index b433c91d85e..a60d3dab469 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -22,6 +22,7 @@ else() add_subdirectory(perplexity) add_subdirectory(quantize) if (LLAMA_BUILD_SERVER) + add_subdirectory(ui) add_subdirectory(cli) add_subdirectory(server) endif() diff --git a/tools/server/CMakeLists.txt b/tools/server/CMakeLists.txt index 20e4eb37697..57d3e871d99 100644 --- a/tools/server/CMakeLists.txt +++ b/tools/server/CMakeLists.txt @@ -40,136 +40,11 @@ set(TARGET_SRCS server-models.h ) -# Option to specify custom HF bucket for webui (defaults to llama-ui) -# Usage: cmake -B build -DLLAMA_WEBUI_HF_BUCKET=llama-ui -set(LLAMA_WEBUI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt webui assets") - -if (LLAMA_BUILD_WEBUI) - set(PUBLIC_ASSETS - index.html - bundle.js - bundle.css - loading.html - ) - - # Determine source of webui assets (priority: local > HF Bucket) - set(WEBUI_SOURCE "") - set(WEBUI_SOURCE_DIR "") - - # Priority 1: Check for local webui build output - set(LOCAL_WEBUI_DIR "${CMAKE_CURRENT_SOURCE_DIR}/public") - - # Verify all required assets exist before declaring local source valid - set(ALL_ASSETS_PRESENT TRUE) - foreach(asset ${PUBLIC_ASSETS}) - if(NOT EXISTS "${LOCAL_WEBUI_DIR}/${asset}") - set(ALL_ASSETS_PRESENT FALSE) - break() - endif() - endforeach() - - if(ALL_ASSETS_PRESENT) - set(WEBUI_SOURCE "local") - set(WEBUI_SOURCE_DIR "${LOCAL_WEBUI_DIR}") - message(STATUS "WebUI: using local build from ${WEBUI_SOURCE_DIR}") - endif() - - # Priority 2: Build-time asset provisioning (npm build → HF Bucket fallback) - if(NOT WEBUI_SOURCE_DIR) - # Environment variable takes precedence (e.g., from CI workflows) - if(DEFINED ENV{HF_WEBUI_VERSION}) - set(HF_WEBUI_VERSION "$ENV{HF_WEBUI_VERSION}") - # Validate against allowed characters to prevent CMake list separator - # or path-traversal issues in stamp filenames and download URLs - if(NOT HF_WEBUI_VERSION MATCHES "^[A-Za-z0-9._-]+$") - message(FATAL_ERROR "WebUI: invalid HF_WEBUI_VERSION='${HF_WEBUI_VERSION}' - must match ^[A-Za-z0-9._-]+$") - endif() - message(STATUS "WebUI: using HF_WEBUI_VERSION from environment=${HF_WEBUI_VERSION}") - elseif(DEFINED LLAMA_BUILD_NUMBER) - set(HF_WEBUI_VERSION "b${LLAMA_BUILD_NUMBER}") - message(STATUS "WebUI: using LLAMA_BUILD_NUMBER=${HF_WEBUI_VERSION}") - else() - set(HF_WEBUI_VERSION "") - message(STATUS "WebUI: version not specified (will use HF 'latest')") - endif() - - # Stamp file embeds the version tag so a changed build number triggers - # a fresh provision run on the next `cmake --build` without reconfiguring. - if("${HF_WEBUI_VERSION}" STREQUAL "") - set(WEBUI_VERSION_TAG "provisioned") - else() - set(WEBUI_VERSION_TAG "${HF_WEBUI_VERSION}") - endif() - set(WEBUI_STAMP "${CMAKE_CURRENT_BINARY_DIR}/.webui-${WEBUI_VERSION_TAG}.stamp") - - # Join assets with + separator (safe across all platforms, unlike ; and |) - string(REPLACE ";" "+" PUBLIC_ASSETS_JOINED "${PUBLIC_ASSETS}") - - add_custom_command( - OUTPUT ${WEBUI_STAMP} - COMMAND ${CMAKE_COMMAND} - "-DSOURCE_DIR=${PROJECT_SOURCE_DIR}" - "-DPUBLIC_DIR=${CMAKE_CURRENT_SOURCE_DIR}/public" - "-DHF_BUCKET=${LLAMA_WEBUI_HF_BUCKET}" - "-DHF_VERSION=${HF_WEBUI_VERSION}" - "-DHF_ENABLED=${LLAMA_USE_PREBUILT_WEBUI}" - "-DASSETS=${PUBLIC_ASSETS_JOINED}" - "-DSTAMP_FILE=${WEBUI_STAMP}" - "-DNPM_DIR=${CMAKE_CURRENT_SOURCE_DIR}/webui" - -P ${PROJECT_SOURCE_DIR}/scripts/webui-download.cmake - COMMENT "Building/provisioning WebUI assets (npm build -> HF Bucket fallback)" - ) - - set(WEBUI_SOURCE "provisioned") - set(WEBUI_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/public") - endif() - - # Process assets from the determined source - if(WEBUI_SOURCE_DIR) - foreach(asset ${PUBLIC_ASSETS}) - set(input "${WEBUI_SOURCE_DIR}/${asset}") - set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp") - list(APPEND TARGET_SRCS ${output}) - - if(WEBUI_SOURCE STREQUAL "local") - # Local build: files exist at configure time - if(NOT EXISTS "${input}") - message(FATAL_ERROR "WebUI asset not found: ${input}") - endif() - set(dependency "${input}") - else() - # HF Bucket: files are downloaded at build time - set(dependency "${WEBUI_STAMP}") - endif() - - add_custom_command( - DEPENDS ${dependency} - OUTPUT "${output}" - COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake" - ) - set_source_files_properties(${output} PROPERTIES GENERATED TRUE) - endforeach() - - add_definitions(-DLLAMA_BUILD_WEBUI) - add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=1) - message(STATUS "WebUI: embedded with source: ${WEBUI_SOURCE}") - else() - # WebUI source not found - issue warning but don't fail the build - # The server will still build but without webui embedded - message(WARNING "WebUI: no source available. Neither local build (tools/server/public/) nor HF Bucket download succeeded.") - message(WARNING "WebUI: building server without embedded WebUI. Set LLAMA_BUILD_WEBUI=OFF to suppress this warning.") - add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=0) - endif() -else() - # WebUI is disabled at build time - add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=0) -endif() - add_executable(${TARGET} ${TARGET_SRCS}) install(TARGETS ${TARGET} RUNTIME) target_include_directories(${TARGET} PRIVATE ../mtmd) target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(${TARGET} PRIVATE server-context PUBLIC llama-common cpp-httplib ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${TARGET} PRIVATE server-context llama-ui PUBLIC llama-common cpp-httplib ${CMAKE_THREAD_LIBS_INIT}) target_compile_features(${TARGET} PRIVATE cxx_std_17) diff --git a/tools/server/README-dev.md b/tools/server/README-dev.md index a9c1e7385fc..0ff334724a3 100644 --- a/tools/server/README-dev.md +++ b/tools/server/README-dev.md @@ -224,7 +224,7 @@ The SvelteKit-based Web UI is introduced in this PR: https://github.com/ggml-org ### Architecture -The WebUI follows a layered architecture: +The UI follows a layered architecture: ``` Routes → Components → Hooks → Stores → Services → Storage/API @@ -234,7 +234,7 @@ Routes → Components → Hooks → Stores → Services → Storage/API - **Services** - stateless API/database communication (`ChatService`, `ModelsService`, `PropsService`, `DatabaseService`) - **Hooks** - reusable logic (`useModelChangeValidation`, `useProcessingState`) -For detailed architecture diagrams, see [`tools/server/webui/docs/`](webui/docs/): +For detailed architecture diagrams, see [`tools/ui/docs/`](../ui/docs/): - `high-level-architecture.mmd` - full architecture with all modules - `high-level-architecture-simplified.mmd` - simplified overview @@ -246,7 +246,7 @@ For detailed architecture diagrams, see [`tools/server/webui/docs/`](webui/docs/ ```sh # make sure you have Node.js installed -cd tools/server/webui +cd tools/ui npm i # run dev server (with hot reload) diff --git a/tools/server/README.md b/tools/server/README.md index 2b3a2b1687d..2ed7fe16ee2 100644 --- a/tools/server/README.md +++ b/tools/server/README.md @@ -189,11 +189,11 @@ For the full list of features, please refer to [server's changelog](https://gith | `--reuse-port` | allow multiple sockets to bind to the same port (default: disabled)
    (env: LLAMA_ARG_REUSE_PORT) | | `--path PATH` | path to serve static files from (default: )
    (env: LLAMA_ARG_STATIC_PATH) | | `--api-prefix PREFIX` | prefix path the server serves from, without the trailing slash (default: )
    (env: LLAMA_ARG_API_PREFIX) | -| `--webui-config JSON` | JSON that provides default WebUI settings (overrides WebUI defaults)
    (env: LLAMA_ARG_WEBUI_CONFIG) | -| `--webui-config-file PATH` | JSON file that provides default WebUI settings (overrides WebUI defaults)
    (env: LLAMA_ARG_WEBUI_CONFIG_FILE) | -| `--webui-mcp-proxy, --no-webui-mcp-proxy` | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)
    (env: LLAMA_ARG_WEBUI_MCP_PROXY) | +| `--ui-config JSON` / `--webui-config JSON` (deprecated) | JSON that provides default UI settings (overrides UI defaults)
    (env: LLAMA_ARG_UI_CONFIG / LLAMA_ARG_WEBUI_CONFIG) | +| `--ui-config-file PATH` / `--webui-config-file PATH` (deprecated) | JSON file that provides default UI settings (overrides UI defaults)
    (env: LLAMA_ARG_UI_CONFIG_FILE / LLAMA_ARG_WEBUI_CONFIG_FILE) | +| `--ui-mcp-proxy, --no-ui-mcp-proxy` / `--webui-mcp-proxy, --no-webui-mcp-proxy` (deprecated) | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)
    (env: LLAMA_ARG_UI_MCP_PROXY / LLAMA_ARG_WEBUI_MCP_PROXY) | | `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)
    specify "all" to enable all tools
    available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, apply_diff, get_datetime
    (env: LLAMA_ARG_TOOLS) | -| `--webui, --no-webui` | whether to enable the Web UI (default: enabled)
    (env: LLAMA_ARG_WEBUI) | +| `--ui, --no-ui` / `--webui, --no-webui` (deprecated) | whether to enable the Web UI (default: enabled)
    (env: LLAMA_ARG_UI / LLAMA_ARG_WEBUI) | | `--embedding, --embeddings` | restrict to only support embedding use case; use only with dedicated embedding models (default: disabled)
    (env: LLAMA_ARG_EMBEDDINGS) | | `--rerank, --reranking` | enable reranking endpoint on server (default: disabled)
    (env: LLAMA_ARG_RERANKING) | | `--api-key KEY` | API key to use for authentication, multiple keys can be provided as a comma-separated list (default: none)
    (env: LLAMA_API_KEY) | @@ -1831,10 +1831,12 @@ Apart from error types supported by OAI, we also have custom types that are spec ### Custom default Web UI preferences -You can specify default preferences for the web UI using `--webui-config ` or `--webui-config-file `. For example, you can disable pasting long text as attachments and enable rendering Markdown in user messages with this command: +You can specify default preferences for the web UI using `--ui-config ` or `--ui-config-file `. For example, you can disable pasting long text as attachments and enable rendering Markdown in user messages with this command: ```bash -./llama-server -m model.gguf --webui-config '{"pasteLongTextToFileLen": 0, "renderUserContentAsMarkdown": true}' +./llama-server -m model.gguf --ui-config '{"pasteLongTextToFileLen": 0, "renderUserContentAsMarkdown": true}' ``` -You may find available preferences in [settings-config.ts](webui/src/lib/constants/settings-config.ts). +> **Note:** The old flags `--webui-config` and `--webui-config-file` are deprecated but still work as aliases. + +You may find available preferences in [settings-config.ts](../ui/src/lib/constants/settings-config.ts). diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp index d49c986feef..1dc19536866 100644 --- a/tools/server/server-context.cpp +++ b/tools/server/server-context.cpp @@ -671,7 +671,8 @@ struct server_context_impl { server_metrics metrics; - json json_webui_settings = json::object(); + json json_ui_settings = json::object(); // Primary: new name + json json_webui_settings = json::object(); // Deprecated: use json_ui_settings instead (kept for compat) // Necessary similarity of prompt for slot selection float slot_prompt_similarity = 0.0f; @@ -996,13 +997,18 @@ struct server_context_impl { } } - // populate webui settings + // populate UI settings (from either new ui_config_json or deprecated webui_config_json) { - if (!params_base.webui_config_json.empty()) { + const std::string & cfg = !params_base.ui_config_json.empty() + ? params_base.ui_config_json + : params_base.webui_config_json; + if (!cfg.empty()) { try { - json_webui_settings = json::parse(params_base.webui_config_json); + json json_settings = json::parse(cfg); + json_ui_settings = json_settings; + json_webui_settings = json_settings; // deprecated: keep in sync } catch (const std::exception & e) { - SRV_ERR("%s: failed to parse webui config: %s\n", __func__, e.what()); + SRV_ERR("%s: failed to parse UI config: %s\n", __func__, e.what()); return false; } } @@ -3292,7 +3298,8 @@ server_context_meta server_context::get_meta() const { /* has_mtmd */ impl->mctx != nullptr, /* has_inp_image */ impl->chat_params.allow_image, /* has_inp_audio */ impl->chat_params.allow_audio, - /* json_webui_settings */ impl->json_webui_settings, + /* json_ui_settings */ impl->json_ui_settings, + /* json_webui_settings */ impl->json_webui_settings, // Deprecated /* slot_n_ctx */ impl->get_slot_n_ctx(), /* pooling_type */ llama_pooling_type(impl->ctx_tgt), @@ -3814,8 +3821,12 @@ void server_routes::init_routes() { { "endpoint_slots", params.endpoint_slots }, { "endpoint_props", params.endpoint_props }, { "endpoint_metrics", params.endpoint_metrics }, - { "webui", params.webui }, - { "webui_settings", meta->json_webui_settings }, + // New keys + { "ui", params.ui }, + { "ui_settings", meta->json_ui_settings }, + // Deprecated: use ui/ui_settings instead (kept for backward compat) + { "webui", params.webui }, + { "webui_settings", meta->json_webui_settings }, { "chat_template", tmpl_default }, { "chat_template_caps", meta->chat_template_caps }, { "bos_token", meta->bos_token_str }, diff --git a/tools/server/server-context.h b/tools/server/server-context.h index 58dda891441..65853438c93 100644 --- a/tools/server/server-context.h +++ b/tools/server/server-context.h @@ -21,7 +21,8 @@ struct server_context_meta { bool has_mtmd; bool has_inp_image; bool has_inp_audio; - json json_webui_settings; + json json_ui_settings; // Primary: new name + json json_webui_settings; // Deprecated: use json_ui_settings instead (kept for backward compat) int slot_n_ctx; enum llama_pooling_type pooling_type; diff --git a/tools/server/server-http.cpp b/tools/server/server-http.cpp index af4536fdd77..39a21f4ecc7 100644 --- a/tools/server/server-http.cpp +++ b/tools/server/server-http.cpp @@ -1,6 +1,7 @@ #include "common.h" #include "server-http.h" #include "server-common.h" +#include "ui.h" #include @@ -10,14 +11,6 @@ #include #include -#ifdef LLAMA_BUILD_WEBUI -// auto generated files (see README.md for details) -#include "index.html.hpp" -#include "bundle.js.hpp" -#include "bundle.css.hpp" -#include "loading.html.hpp" -#endif - // // HTTP implementation using cpp-httplib // @@ -238,10 +231,11 @@ bool server_http_context::init(const common_params & params) { }; auto middleware_server_state = [this](const httplib::Request & req, httplib::Response & res) { - (void)req; // suppress unused parameter warning when LLAMA_BUILD_WEBUI is not defined + (void)req; // suppress unused parameter warning when LLAMA_BUILD_UI / LLAMA_BUILD_WEBUI is not defined bool ready = is_ready.load(); if (!ready) { -#ifdef LLAMA_BUILD_WEBUI +// Support both old and new preprocessor defines +#if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) auto tmp = string_split(req.path, '.'); if (req.path == "/" || (tmp.size() > 0 && tmp.back() == "html")) { res.status = 503; @@ -305,8 +299,10 @@ bool server_http_context::init(const common_params & params) { // Web UI setup // - if (!params.webui) { - SRV_INF("%s", "the WebUI is disabled\n"); + // Use new `params.ui` field (backed by old `params.webui` for compat) + if (!params.ui) { + SRV_INF("%s", "The UI is disabled\n"); + SRV_INF("%s", "Use --ui/--no-ui (or deprecated --webui/--no-webui) to enable/disable\n"); } else { // register static assets routes if (!params.public_path.empty()) { @@ -317,7 +313,8 @@ bool server_http_context::init(const common_params & params) { return 1; } } else { -#ifdef LLAMA_BUILD_WEBUI +// Support both old and new preprocessor defines +#if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) // using embedded static index.html srv->Get(params.api_prefix + "/", [](const httplib::Request & /*req*/, httplib::Response & res) { // COEP and COOP headers, required by pyodide (python interpreter) diff --git a/tools/server/server-models.cpp b/tools/server/server-models.cpp index 698489a11e2..433d2d8f04e 100644 --- a/tools/server/server-models.cpp +++ b/tools/server/server-models.cpp @@ -1152,14 +1152,17 @@ void server_models_routes::init_routes() { {"role", "router"}, {"max_instances", params.models_max}, {"models_autoload", params.models_autoload}, - // this is a dummy response to make sure webui doesn't break + // this is a dummy response to make sure the UI doesn't break {"model_alias", "llama-server"}, {"model_path", "none"}, {"default_generation_settings", { {"params", json{}}, {"n_ctx", 0}, }}, - {"webui_settings", webui_settings}, + // New key + {"ui_settings", ui_settings}, + // Deprecated: use ui_settings instead (kept for backward compat) + {"webui_settings", webui_settings}, {"build_info", std::string(llama_build_info())}, }); return res; diff --git a/tools/server/server-models.h b/tools/server/server-models.h index f1206c71448..e96d76c9169 100644 --- a/tools/server/server-models.h +++ b/tools/server/server-models.h @@ -175,15 +175,22 @@ struct server_models { struct server_models_routes { common_params params; - json webui_settings = json::object(); + json ui_settings = json::object(); // Primary: new name + json webui_settings = json::object(); // Deprecated: use ui_settings (kept for compat) server_models models; server_models_routes(const common_params & params, int argc, char ** argv) : params(params), models(params, argc, argv) { - if (!this->params.webui_config_json.empty()) { + // Support both new ui_config_json and deprecated webui_config_json + const std::string & cfg = !this->params.ui_config_json.empty() + ? this->params.ui_config_json + : this->params.webui_config_json; + if (!cfg.empty()) { try { - webui_settings = json::parse(this->params.webui_config_json); + json json_settings = json::parse(cfg); + ui_settings = json_settings; + webui_settings = json_settings; // Deprecated: keep in sync } catch (const std::exception & e) { - LOG_ERR("%s: failed to parse webui config: %s\n", __func__, e.what()); + LOG_ERR("%s: failed to parse UI config: %s\n", __func__, e.what()); throw; } } diff --git a/tools/server/server.cpp b/tools/server/server.cpp index 823ae5bda36..a232550789c 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -208,7 +208,8 @@ int main(int argc, char ** argv) { ctx_http.register_gcp_compat(); // CORS proxy (EXPERIMENTAL, only used by the Web UI for MCP) - if (params.webui_mcp_proxy) { + // Supports both new ui_mcp_proxy and deprecated webui_mcp_proxy fields + if (params.ui_mcp_proxy || params.webui_mcp_proxy) { SRV_WRN("%s", "-----------------\n"); SRV_WRN("%s", "CORS proxy is enabled, do not expose server to untrusted environments\n"); SRV_WRN("%s", "This feature is EXPERIMENTAL and may be removed or changed in future versions\n"); diff --git a/tools/server/webui/scripts/post-build.sh b/tools/server/webui/scripts/post-build.sh deleted file mode 100755 index 55e46d5d5c6..00000000000 --- a/tools/server/webui/scripts/post-build.sh +++ /dev/null @@ -1,3 +0,0 @@ -rm -rf ../public/_app; -rm ../public/favicon.svg; -rm -f ../public/index.html.gz; # deprecated, but may still be generated by older versions of the build process diff --git a/tools/server/webui/src/lib/constants/localstorage-keys.ts b/tools/server/webui/src/lib/constants/localstorage-keys.ts deleted file mode 100644 index a04194c46f6..00000000000 --- a/tools/server/webui/src/lib/constants/localstorage-keys.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY = 'LlamaCppWebui.alwaysAllowedTools'; -export const CONFIG_LOCALSTORAGE_KEY = 'LlamaCppWebui.config'; -export const DISABLED_TOOLS_LOCALSTORAGE_KEY = 'LlamaCppWebui.disabledTools'; -export const FAVORITE_MODELS_LOCALSTORAGE_KEY = 'LlamaCppWebui.favoriteModels'; -export const MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY = 'LlamaCppWebui.mcpDefaultEnabled'; -export const USER_OVERRIDES_LOCALSTORAGE_KEY = 'LlamaCppWebui.userOverrides'; diff --git a/tools/server/webui/.gitignore b/tools/ui/.gitignore similarity index 100% rename from tools/server/webui/.gitignore rename to tools/ui/.gitignore diff --git a/tools/server/webui/.npmrc b/tools/ui/.npmrc similarity index 100% rename from tools/server/webui/.npmrc rename to tools/ui/.npmrc diff --git a/tools/server/webui/.prettierignore b/tools/ui/.prettierignore similarity index 100% rename from tools/server/webui/.prettierignore rename to tools/ui/.prettierignore diff --git a/tools/server/webui/.prettierrc b/tools/ui/.prettierrc similarity index 100% rename from tools/server/webui/.prettierrc rename to tools/ui/.prettierrc diff --git a/tools/server/webui/.storybook/decorators/ModeWatcherDecorator.svelte b/tools/ui/.storybook/decorators/ModeWatcherDecorator.svelte similarity index 100% rename from tools/server/webui/.storybook/decorators/ModeWatcherDecorator.svelte rename to tools/ui/.storybook/decorators/ModeWatcherDecorator.svelte diff --git a/tools/server/webui/.storybook/decorators/TooltipProviderDecorator.svelte b/tools/ui/.storybook/decorators/TooltipProviderDecorator.svelte similarity index 100% rename from tools/server/webui/.storybook/decorators/TooltipProviderDecorator.svelte rename to tools/ui/.storybook/decorators/TooltipProviderDecorator.svelte diff --git a/tools/server/webui/.storybook/main.ts b/tools/ui/.storybook/main.ts similarity index 100% rename from tools/server/webui/.storybook/main.ts rename to tools/ui/.storybook/main.ts diff --git a/tools/server/webui/.storybook/preview.ts b/tools/ui/.storybook/preview.ts similarity index 100% rename from tools/server/webui/.storybook/preview.ts rename to tools/ui/.storybook/preview.ts diff --git a/tools/server/webui/.storybook/vitest.setup.ts b/tools/ui/.storybook/vitest.setup.ts similarity index 100% rename from tools/server/webui/.storybook/vitest.setup.ts rename to tools/ui/.storybook/vitest.setup.ts diff --git a/tools/ui/CMakeLists.txt b/tools/ui/CMakeLists.txt new file mode 100644 index 00000000000..9687ca92e55 --- /dev/null +++ b/tools/ui/CMakeLists.txt @@ -0,0 +1,157 @@ +set(TARGET llama-ui) + +# Deprecated: use LLAMA_UI_HF_BUCKET instead +set(LLAMA_WEBUI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt webui assets (deprecated: use LLAMA_UI_HF_BUCKET)") +set(LLAMA_UI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt UI assets") + +# Backward compat: forward old var to new one +if(DEFINED LLAMA_WEBUI_HF_BUCKET AND NOT DEFINED LLAMA_UI_HF_BUCKET) + set(LLAMA_UI_HF_BUCKET ${LLAMA_WEBUI_HF_BUCKET}) +elseif(DEFINED LLAMA_WEBUI_HF_BUCKET AND NOT "${LLAMA_WEBUI_HF_BUCKET}" STREQUAL "${LLAMA_UI_HF_BUCKET}") + message(DEPRECATION "LLAMA_WEBUI_HF_BUCKET is deprecated, use LLAMA_UI_HF_BUCKET instead") +endif() + +set(TARGET_SRCS "") +set(UI_COMPILE_DEFS "") + +# Support both old (LLAMA_BUILD_WEBUI) and new (LLAMA_BUILD_UI) option names +if(LLAMA_BUILD_WEBUI OR LLAMA_BUILD_UI) + if(LLAMA_BUILD_WEBUI AND NOT LLAMA_BUILD_UI) + message(DEPRECATION "LLAMA_BUILD_WEBUI is deprecated, use LLAMA_BUILD_UI instead") + endif() + + set(PUBLIC_ASSETS + index.html + bundle.js + bundle.css + loading.html + ) + + # Determine source of UI assets (priority: local > HF Bucket) + set(UI_SOURCE "") + set(UI_SOURCE_DIR "") + + # Priority 1: Check for local build output + set(LOCAL_UI_DIR "${PROJECT_SOURCE_DIR}/build/tools/ui/dist") + + # Verify all required assets exist before declaring local source valid + set(ALL_ASSETS_PRESENT TRUE) + foreach(asset ${PUBLIC_ASSETS}) + if(NOT EXISTS "${LOCAL_UI_DIR}/${asset}") + set(ALL_ASSETS_PRESENT FALSE) + break() + endif() + endforeach() + + if(ALL_ASSETS_PRESENT) + set(UI_SOURCE "local") + set(UI_SOURCE_DIR "${LOCAL_UI_DIR}") + message(STATUS "UI: using local build from ${UI_SOURCE_DIR}") + endif() + + # Priority 2: Build-time asset provisioning (npm build → HF Bucket fallback) + if(NOT UI_SOURCE_DIR) + # Environment variable takes precedence (e.g., from CI workflows) + # Deprecated: use HF_UI_VERSION instead + if(DEFINED ENV{HF_WEBUI_VERSION}) + set(HF_UI_VERSION "$ENV{HF_WEBUI_VERSION}") + message(DEPRECATION "HF_WEBUI_VERSION env var is deprecated, use HF_UI_VERSION instead") + if(NOT HF_UI_VERSION MATCHES "^[A-Za-z0-9._-]+$") + message(FATAL_ERROR "UI: invalid HF_WEBUI_VERSION='${HF_UI_VERSION}' - must match ^[A-Za-z0-9._-]+$") + endif() + elseif(DEFINED ENV{HF_UI_VERSION}) + set(HF_UI_VERSION "$ENV{HF_UI_VERSION}") + if(NOT HF_UI_VERSION MATCHES "^[A-Za-z0-9._-]+$") + message(FATAL_ERROR "UI: invalid HF_UI_VERSION='${HF_UI_VERSION}' - must match ^[A-Za-z0-9._-]+$") + endif() + elseif(DEFINED LLAMA_BUILD_NUMBER) + set(HF_UI_VERSION "b${LLAMA_BUILD_NUMBER}") + message(STATUS "UI: derived HF_UI_VERSION=b${LLAMA_BUILD_NUMBER}") + else() + set(HF_UI_VERSION "") + message(STATUS "UI: version not specified (will use HF 'latest')") + endif() + + if("${HF_UI_VERSION}" STREQUAL "") + set(UI_VERSION_TAG "provisioned") + else() + set(UI_VERSION_TAG "${HF_UI_VERSION}") + endif() + set(UI_STAMP "${CMAKE_CURRENT_BINARY_DIR}/.ui-${UI_VERSION_TAG}.stamp") + + string(REPLACE ";" "+" PUBLIC_ASSETS_JOINED "${PUBLIC_ASSETS}") + + add_custom_command( + OUTPUT ${UI_STAMP} + COMMAND ${CMAKE_COMMAND} + "-DSOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DPUBLIC_DIR=${PROJECT_SOURCE_DIR}/build/tools/ui/dist" + "-DHF_BUCKET=${LLAMA_UI_HF_BUCKET}" + "-DHF_VERSION=${HF_UI_VERSION}" + "-DHF_ENABLED=${LLAMA_USE_PREBUILT_UI}" + "-DASSETS=${PUBLIC_ASSETS_JOINED}" + "-DSTAMP_FILE=${UI_STAMP}" + "-DNPM_DIR=${PROJECT_SOURCE_DIR}/tools/ui" + -P ${PROJECT_SOURCE_DIR}/scripts/ui-download.cmake + COMMENT "Building/provisioning UI assets (npm build -> HF Bucket fallback)" + ) + + set(UI_SOURCE "provisioned") + set(UI_SOURCE_DIR "${PROJECT_SOURCE_DIR}/build/tools/ui/dist") + endif() + + # Process assets from the determined source + if(UI_SOURCE_DIR) + foreach(asset ${PUBLIC_ASSETS}) + set(input "${UI_SOURCE_DIR}/${asset}") + set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp") + list(APPEND TARGET_SRCS ${output}) + + if(UI_SOURCE STREQUAL "local") + if(NOT EXISTS "${input}") + message(FATAL_ERROR "UI asset not found: ${input}") + endif() + set(dependency "${input}") + else() + set(dependency "${UI_STAMP}") + endif() + + add_custom_command( + DEPENDS ${dependency} + OUTPUT "${output}" + COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake" + ) + set_source_files_properties(${output} PROPERTIES GENERATED TRUE) + endforeach() + + list(APPEND UI_COMPILE_DEFS + LLAMA_BUILD_WEBUI # Deprecated: use LLAMA_BUILD_UI + LLAMA_BUILD_UI + LLAMA_WEBUI_DEFAULT_ENABLED=1 # Deprecated: use LLAMA_UI_DEFAULT_ENABLED + LLAMA_UI_DEFAULT_ENABLED=1 + ) + message(STATUS "UI: embedded with source: ${UI_SOURCE}") + else() + message(WARNING "UI: no source available. Neither local build (build/tools/ui/dist/) nor HF Bucket download succeeded.") + message(WARNING "UI: building server without embedded UI. Set LLAMA_BUILD_UI=OFF to suppress this warning.") + list(APPEND UI_COMPILE_DEFS LLAMA_WEBUI_DEFAULT_ENABLED=0 LLAMA_UI_DEFAULT_ENABLED=0) + endif() +else() + list(APPEND UI_COMPILE_DEFS LLAMA_WEBUI_DEFAULT_ENABLED=0 LLAMA_UI_DEFAULT_ENABLED=0) +endif() + +# Build the static library +add_library(${TARGET} STATIC ui.cpp) + +target_include_directories(${TARGET} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_compile_definitions(${TARGET} PUBLIC ${UI_COMPILE_DEFS}) + +if(TARGET_SRCS) + # List generated .hpp files as sources so CMake tracks them as build dependencies + target_sources(${TARGET} PRIVATE ${TARGET_SRCS}) + set_source_files_properties(${TARGET_SRCS} PROPERTIES HEADER_FILE_ONLY TRUE) +endif() diff --git a/tools/server/webui/README.md b/tools/ui/README.md similarity index 96% rename from tools/server/webui/README.md rename to tools/ui/README.md index 40742b00e1a..abbbabe923d 100644 --- a/tools/server/webui/README.md +++ b/tools/ui/README.md @@ -2,7 +2,7 @@ A modern, feature-rich web interface for llama-server built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities. -The WebUI supports two server operation modes: +Llama UI supports two server operation modes: - **MODEL mode** - Single model operation (standard llama-server) - **ROUTER mode** - Multi-model operation with dynamic model loading/unloading @@ -88,7 +88,7 @@ The WebUI supports two server operation modes: ### 1. Install Dependencies ```bash -cd tools/server/webui +cd tools/ui npm install ``` @@ -112,7 +112,7 @@ npm run dev This starts: -- **Vite dev server** at `http://localhost:5173` - The main WebUI +- **Vite dev server** at `http://localhost:5173` - The main UI frontend app - **Storybook** at `http://localhost:6006` - Component documentation The Vite dev server proxies API requests to `http://localhost:8080` (default llama-server port): @@ -186,7 +186,7 @@ npm run build The build process: 1. **Vite Build** - Bundles all TypeScript, Svelte, and CSS -2. **Static Adapter** - Outputs to `../public` (llama-server's static file directory) +2. **Static Adapter** - Outputs to `../../build/tools/ui/dist` (llama-server's static file directory) 3. **Post-Build Script** - Cleans up intermediate files 4. **Custom Plugin** - Creates `index.html` with: - Inlined favicon as base64 @@ -194,7 +194,7 @@ The build process: - Deterministic output (zeroed timestamps) ```text -tools/server/webui/ → build → tools/server/public/ +tools/ui/ → build → build/tools/ui/dist/ ├── src/ ├── index.html (served by llama-server) ├── static/ └── (favicon inlined) └── ... @@ -205,8 +205,8 @@ tools/server/webui/ → build → tools/server/public/ ```javascript // svelte.config.js adapter: adapter({ - pages: '../public', // Output directory - assets: '../public', // Static assets + pages: '../../build/tools/ui/dist', // Output directory + assets: '../../build/tools/ui/dist', // Static assets fallback: 'index.html', // SPA fallback strict: true }), @@ -217,20 +217,19 @@ output: { ### Integration with llama-server -The WebUI is embedded directly into the llama-server binary: +llama-ui is embedded directly into the llama-server binary: -1. `npm run build` outputs `index.html` to `tools/server/public/` +1. `npm run build` outputs `index.html` to `build/tools/ui/dist/` 2. llama-server compiles this into the binary at build time -3. When accessing `/`, llama-server serves the gzipped HTML -4. All assets are inlined (CSS, JS, fonts, favicon) +3. When accessing `/`, llama-server serves the bundled HTML -This results in a **single portable binary** with the full WebUI included. +This results in a **single portable binary** with the full Llama UI included. --- ## Architecture -The WebUI follows a layered architecture with unidirectional data flow: +Llama UI follows a layered architecture with unidirectional data flow: ```text Routes → Components → Hooks → Stores → Services → Storage/API @@ -659,7 +658,7 @@ npm run check # TypeScript type checking ## Project Structure ```text -tools/server/webui/ +tools/ui/ ├── src/ │ ├── lib/ │ │ ├── components/ # UI components (app/, ui/) diff --git a/tools/server/webui/components.json b/tools/ui/components.json similarity index 100% rename from tools/server/webui/components.json rename to tools/ui/components.json diff --git a/tools/server/webui/docs/architecture/high-level-architecture-simplified.md b/tools/ui/docs/architecture/high-level-architecture-simplified.md similarity index 100% rename from tools/server/webui/docs/architecture/high-level-architecture-simplified.md rename to tools/ui/docs/architecture/high-level-architecture-simplified.md diff --git a/tools/server/webui/docs/architecture/high-level-architecture.md b/tools/ui/docs/architecture/high-level-architecture.md similarity index 100% rename from tools/server/webui/docs/architecture/high-level-architecture.md rename to tools/ui/docs/architecture/high-level-architecture.md diff --git a/tools/server/webui/docs/flows/chat-flow.md b/tools/ui/docs/flows/chat-flow.md similarity index 100% rename from tools/server/webui/docs/flows/chat-flow.md rename to tools/ui/docs/flows/chat-flow.md diff --git a/tools/server/webui/docs/flows/conversations-flow.md b/tools/ui/docs/flows/conversations-flow.md similarity index 100% rename from tools/server/webui/docs/flows/conversations-flow.md rename to tools/ui/docs/flows/conversations-flow.md diff --git a/tools/server/webui/docs/flows/data-flow-simplified-model-mode.md b/tools/ui/docs/flows/data-flow-simplified-model-mode.md similarity index 100% rename from tools/server/webui/docs/flows/data-flow-simplified-model-mode.md rename to tools/ui/docs/flows/data-flow-simplified-model-mode.md diff --git a/tools/server/webui/docs/flows/data-flow-simplified-router-mode.md b/tools/ui/docs/flows/data-flow-simplified-router-mode.md similarity index 100% rename from tools/server/webui/docs/flows/data-flow-simplified-router-mode.md rename to tools/ui/docs/flows/data-flow-simplified-router-mode.md diff --git a/tools/server/webui/docs/flows/database-flow.md b/tools/ui/docs/flows/database-flow.md similarity index 100% rename from tools/server/webui/docs/flows/database-flow.md rename to tools/ui/docs/flows/database-flow.md diff --git a/tools/server/webui/docs/flows/mcp-flow.md b/tools/ui/docs/flows/mcp-flow.md similarity index 100% rename from tools/server/webui/docs/flows/mcp-flow.md rename to tools/ui/docs/flows/mcp-flow.md diff --git a/tools/server/webui/docs/flows/models-flow.md b/tools/ui/docs/flows/models-flow.md similarity index 100% rename from tools/server/webui/docs/flows/models-flow.md rename to tools/ui/docs/flows/models-flow.md diff --git a/tools/server/webui/docs/flows/server-flow.md b/tools/ui/docs/flows/server-flow.md similarity index 100% rename from tools/server/webui/docs/flows/server-flow.md rename to tools/ui/docs/flows/server-flow.md diff --git a/tools/server/webui/docs/flows/settings-flow.md b/tools/ui/docs/flows/settings-flow.md similarity index 98% rename from tools/server/webui/docs/flows/settings-flow.md rename to tools/ui/docs/flows/settings-flow.md index 40ad3bd94d7..260713a17b8 100644 --- a/tools/server/webui/docs/flows/settings-flow.md +++ b/tools/ui/docs/flows/settings-flow.md @@ -58,8 +58,8 @@ sequenceDiagram end end - alt serverStore.props has webuiSettings - settingsStore->>settingsStore: Apply webuiSettings from server + alt serverStore.props has uiSettings + settingsStore->>settingsStore: Apply uiSettings from server Note right of settingsStore: Server-provided UI settings
    (e.g. showRawOutputSwitch) end diff --git a/tools/server/webui/eslint.config.js b/tools/ui/eslint.config.js similarity index 93% rename from tools/server/webui/eslint.config.js rename to tools/ui/eslint.config.js index cd20fb383a4..185da1dabbe 100644 --- a/tools/server/webui/eslint.config.js +++ b/tools/ui/eslint.config.js @@ -29,7 +29,9 @@ export default ts.config( 'no-undef': 'off', 'svelte/no-at-html-tags': 'off', // This app uses hash-based routing (#/) where resolve() from $app/paths does not apply - 'svelte/no-navigation-without-resolve': 'off' + 'svelte/no-navigation-without-resolve': 'off', + // Enforce empty line at end of file + 'eol-last': 'error' } }, { diff --git a/tools/server/webui/package-lock.json b/tools/ui/package-lock.json similarity index 100% rename from tools/server/webui/package-lock.json rename to tools/ui/package-lock.json diff --git a/tools/server/webui/package.json b/tools/ui/package.json similarity index 98% rename from tools/server/webui/package.json rename to tools/ui/package.json index 2338c38402a..5a1cec66645 100644 --- a/tools/server/webui/package.json +++ b/tools/ui/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "bash scripts/dev.sh", - "build": "vite build && ./scripts/post-build.sh", + "build": "vite build", "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", diff --git a/tools/server/webui/playwright.config.ts b/tools/ui/playwright.config.ts similarity index 70% rename from tools/server/webui/playwright.config.ts rename to tools/ui/playwright.config.ts index 26d3be535d1..178fd7ba856 100644 --- a/tools/server/webui/playwright.config.ts +++ b/tools/ui/playwright.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from '@playwright/test'; export default defineConfig({ webServer: { - command: 'npm run build && http-server ../public -p 8181', + command: 'npm run build && http-server ../../build/tools/ui/dist -p 8181', port: 8181, timeout: 120000, reuseExistingServer: false diff --git a/tools/server/webui/scripts/dev.sh b/tools/ui/scripts/dev.sh similarity index 89% rename from tools/server/webui/scripts/dev.sh rename to tools/ui/scripts/dev.sh index 97c14b87328..9256f255aec 100644 --- a/tools/server/webui/scripts/dev.sh +++ b/tools/ui/scripts/dev.sh @@ -2,14 +2,14 @@ # Development script for llama-ui # -# This script starts the webui development servers (Storybook and Vite). +# This script starts the llama-ui development servers (Storybook and Vite). # Note: You need to start llama-server separately. # # Usage: # bash scripts/dev.sh # npm run dev -cd ../../../ +cd ../../ # Check and install git hooks if missing check_and_install_hooks() { @@ -22,13 +22,13 @@ check_and_install_hooks() { if [ "$hooks_missing" = true ]; then echo "🔧 Git hooks missing, installing them..." - cd tools/server/webui + cd tools/ui if bash scripts/install-git-hooks.sh; then echo "✅ Git hooks installed successfully" else echo "⚠️ Failed to install git hooks, continuing anyway..." fi - cd ../../../ + cd ../../ else echo "✅ Git hooks already installed" fi @@ -48,7 +48,7 @@ trap cleanup SIGINT SIGTERM echo "🚀 Starting development servers..." echo "📝 Note: Make sure to start llama-server separately if needed" -cd tools/server/webui +cd tools/ui # Use --insecure-http-parser to handle malformed HTTP responses from llama-server # (some responses have both Content-Length and Transfer-Encoding headers) storybook dev -p 6006 --ci & NODE_OPTIONS="--insecure-http-parser" vite dev --host 0.0.0.0 & diff --git a/tools/server/webui/scripts/install-git-hooks.sh b/tools/ui/scripts/install-git-hooks.sh similarity index 62% rename from tools/server/webui/scripts/install-git-hooks.sh rename to tools/ui/scripts/install-git-hooks.sh index 8aa1014ba36..213feb08dcf 100755 --- a/tools/server/webui/scripts/install-git-hooks.sh +++ b/tools/ui/scripts/install-git-hooks.sh @@ -1,29 +1,29 @@ #!/bin/bash -# Script to install pre-commit hook for webui -# Pre-commit: formats, checks, and builds webui +# Script to install pre-commit hook for llama-ui +# Pre-commit: formats, checks, and builds the UI app REPO_ROOT=$(git rev-parse --show-toplevel) PRE_COMMIT_HOOK="$REPO_ROOT/.git/hooks/pre-commit" -echo "Installing pre-commit hook for webui..." +echo "Installing pre-commit hook for llama-ui..." # Create the pre-commit hook cat > "$PRE_COMMIT_HOOK" << 'EOF' #!/bin/bash -# Check if there are any changes in the webui directory -if git diff --cached --name-only | grep -q "^tools/server/webui/"; then +# Check if there are any changes in the tools/ui directory +if git diff --cached --name-only | grep -q "^tools/ui/"; then REPO_ROOT=$(git rev-parse --show-toplevel) - cd "$REPO_ROOT/tools/server/webui" + cd "$REPO_ROOT/tools/ui" # Check if package.json exists if [ ! -f "package.json" ]; then - echo "Error: package.json not found in tools/server/webui" + echo "Error: package.json not found in tools/ui" exit 1 fi - echo "Formatting and checking webui code..." + echo "Formatting and checking llama-ui code..." # Run the format command npm run format @@ -46,17 +46,17 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then exit 1 fi - echo "✅ Webui code formatted and checked successfully" + echo "✅ llama-ui code formatted and checked successfully" - # Build the webui - echo "Building webui..." + # Build the llama-ui + echo "Building llama-ui..." npm run build if [ $? -ne 0 ]; then echo "❌ npm run build failed" exit 1 fi - echo "✅ Webui built successfully" + echo "✅ llama-ui built successfully" fi exit 0 @@ -70,8 +70,8 @@ if [ $? -eq 0 ]; then echo " Pre-commit: $PRE_COMMIT_HOOK" echo "" echo "The hook will automatically:" - echo " • Format, lint and check webui code before commits" - echo " • Build webui" + echo " • Format, lint and check llama-ui code before commits" + echo " • Build llama-ui" else echo "❌ Failed to make hook executable" exit 1 diff --git a/tools/server/webui/scripts/vite-plugin-llama-cpp-build.ts b/tools/ui/scripts/vite-plugin-llama-cpp-build.ts similarity index 64% rename from tools/server/webui/scripts/vite-plugin-llama-cpp-build.ts rename to tools/ui/scripts/vite-plugin-llama-cpp-build.ts index 0330a1dda12..ddf6fa1e56e 100644 --- a/tools/server/webui/scripts/vite-plugin-llama-cpp-build.ts +++ b/tools/ui/scripts/vite-plugin-llama-cpp-build.ts @@ -1,4 +1,12 @@ -import { readFileSync, writeFileSync, existsSync, readdirSync, copyFileSync } from 'fs'; +import { + readFileSync, + writeFileSync, + existsSync, + readdirSync, + copyFileSync, + rmSync, + unlinkSync +} from 'fs'; import { resolve } from 'path'; import type { Plugin } from 'vite'; @@ -11,28 +19,28 @@ const GUIDE_FOR_FRONTEND = ` --> `.trim(); +const OUTPUT_DIR = '../../build/tools/ui/dist'; + export function llamaCppBuildPlugin(): Plugin { return { name: 'llamacpp:build', apply: 'build', closeBundle() { - // Ensure the SvelteKit adapter has finished writing to ../public setTimeout(() => { try { - const indexPath = resolve('../public/index.html'); + const outDir = resolve(OUTPUT_DIR); + const indexPath = resolve(outDir, 'index.html'); if (!existsSync(indexPath)) return; let content = readFileSync(indexPath, 'utf-8'); + // Inline favicon as base64 data URL const faviconPath = resolve('static/favicon.svg'); - if (existsSync(faviconPath)) { const faviconContent = readFileSync(faviconPath, 'utf-8'); const faviconBase64 = Buffer.from(faviconContent).toString('base64'); const faviconDataUrl = `data:image/svg+xml;base64,${faviconBase64}`; - content = content.replace(/href="[^"]*favicon\.svg"/g, `href="${faviconDataUrl}"`); - console.log('✓ Inlined favicon.svg as base64 data URL'); } @@ -48,17 +56,16 @@ export function llamaCppBuildPlugin(): Plugin { writeFileSync(indexPath, content, 'utf-8'); console.log('✓ Updated index.html'); - // Copy bundle.*.js -> ../public/bundle.js - const immutableDir = resolve('../public/_app/immutable'); - const bundleDir = resolve('../public/_app/immutable/assets'); + // Copy bundle.*.js -> bundle.js at output root + const immutableDir = resolve(outDir, '_app/immutable'); + const bundleDir = resolve(outDir, '_app/immutable/assets'); if (existsSync(immutableDir)) { const jsFiles = readdirSync(immutableDir).filter((f) => f.match(/^bundle\..+\.js$/)); - if (jsFiles.length > 0) { - copyFileSync(resolve(immutableDir, jsFiles[0]), resolve('../public/bundle.js')); + copyFileSync(resolve(immutableDir, jsFiles[0]), resolve(outDir, 'bundle.js')); // Normalize __sveltekit_ to __sveltekit__ in bundle.js - const bundleJsPath = resolve('../public/bundle.js'); + const bundleJsPath = resolve(outDir, 'bundle.js'); let bundleJs = readFileSync(bundleJsPath, 'utf-8'); bundleJs = bundleJs.replace(/__sveltekit_[a-z0-9]+/g, '__sveltekit__'); writeFileSync(bundleJsPath, bundleJs, 'utf-8'); @@ -66,17 +73,29 @@ export function llamaCppBuildPlugin(): Plugin { } } - // Copy bundle.*.css -> ../public/bundle.css + // Copy bundle.*.css -> bundle.css at output root if (existsSync(bundleDir)) { const cssFiles = readdirSync(bundleDir).filter((f) => f.match(/^bundle\..+\.css$/)); - if (cssFiles.length > 0) { - copyFileSync(resolve(bundleDir, cssFiles[0]), resolve('../public/bundle.css')); + copyFileSync(resolve(bundleDir, cssFiles[0]), resolve(outDir, 'bundle.css')); console.log(`✓ Copied ${cssFiles[0]} -> bundle.css`); } } + + // Cleanup: remove _app directory, favicon.svg, and legacy index.html.gz + const appDir = resolve(outDir, '_app'); + if (existsSync(appDir)) { + rmSync(appDir, { recursive: true, force: true }); + console.log('✓ Removed _app directory'); + } + + const faviconOut = resolve(outDir, 'favicon.svg'); + if (existsSync(faviconOut)) { + unlinkSync(faviconOut); + console.log('✓ Removed favicon.svg'); + } } catch (error) { - console.error('Failed to update index.html:', error); + console.error('Failed to process build output:', error); } }, 100); } diff --git a/tools/server/webui/src/app.css b/tools/ui/src/app.css similarity index 100% rename from tools/server/webui/src/app.css rename to tools/ui/src/app.css diff --git a/tools/server/webui/src/app.d.ts b/tools/ui/src/app.d.ts similarity index 100% rename from tools/server/webui/src/app.d.ts rename to tools/ui/src/app.d.ts diff --git a/tools/server/webui/src/app.html b/tools/ui/src/app.html similarity index 100% rename from tools/server/webui/src/app.html rename to tools/ui/src/app.html diff --git a/tools/server/webui/src/lib/actions/fade-in-view.svelte.ts b/tools/ui/src/lib/actions/fade-in-view.svelte.ts similarity index 100% rename from tools/server/webui/src/lib/actions/fade-in-view.svelte.ts rename to tools/ui/src/lib/actions/fade-in-view.svelte.ts diff --git a/tools/server/webui/src/lib/components/app/SKILL.md b/tools/ui/src/lib/components/app/SKILL.md similarity index 100% rename from tools/server/webui/src/lib/components/app/SKILL.md rename to tools/ui/src/lib/components/app/SKILL.md diff --git a/tools/server/webui/src/lib/components/app/actions/ActionIcon.svelte b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/actions/ActionIcon.svelte rename to tools/ui/src/lib/components/app/actions/ActionIcon.svelte diff --git a/tools/server/webui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte b/tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte rename to tools/ui/src/lib/components/app/actions/ActionIconCopyToClipboard.svelte diff --git a/tools/server/webui/src/lib/components/app/actions/index.ts b/tools/ui/src/lib/components/app/actions/index.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/actions/index.ts rename to tools/ui/src/lib/components/app/actions/index.ts diff --git a/tools/server/webui/src/lib/components/app/badges/BadgeInfo.svelte b/tools/ui/src/lib/components/app/badges/BadgeInfo.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/badges/BadgeInfo.svelte rename to tools/ui/src/lib/components/app/badges/BadgeInfo.svelte diff --git a/tools/server/webui/src/lib/components/app/badges/BadgesModality.svelte b/tools/ui/src/lib/components/app/badges/BadgesModality.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/badges/BadgesModality.svelte rename to tools/ui/src/lib/components/app/badges/BadgesModality.svelte diff --git a/tools/server/webui/src/lib/components/app/badges/index.ts b/tools/ui/src/lib/components/app/badges/index.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/badges/index.ts rename to tools/ui/src/lib/components/app/badges/index.ts diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpResource.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailFile.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailFile.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailFile.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailFile.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailImage.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailImage.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailImage.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemThumbnailImage.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItem.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItem.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItem.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItem.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemAudio.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemAudio.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemAudio.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemAudio.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemImage.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemImage.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemImage.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemImage.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemText.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemText.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemText.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemText.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemUnavailable.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemUnavailable.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemUnavailable.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemUnavailable.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewFileInfo.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewNavButtons.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte rename to tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatForm.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddButton.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddButton.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddButton.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddButton.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte similarity index 97% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte index 175eb3c8c7f..e053e6f836b 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddDropdown.svelte @@ -54,7 +54,12 @@ } const attachmentMenu = useAttachmentMenu( - () => ({ hasVisionModality, hasAudioModality, hasMcpPromptsSupport, hasMcpResourcesSupport }), + () => ({ + hasVisionModality, + hasAudioModality, + hasMcpPromptsSupport, + hasMcpResourcesSupport + }), () => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }), () => { dropdownOpen = false; diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddMcpServersSubmenu.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte similarity index 98% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte index 99daa10e3e2..4713ec47758 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddSheet.svelte @@ -46,7 +46,12 @@ let sheetOpen = $state(false); const attachmentMenu = useAttachmentMenu( - () => ({ hasVisionModality, hasAudioModality, hasMcpPromptsSupport, hasMcpResourcesSupport }), + () => ({ + hasVisionModality, + hasAudioModality, + hasMcpPromptsSupport, + hasMcpResourcesSupport + }), () => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }), () => { sheetOpen = false; diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte similarity index 97% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte index ccc35d98f87..b11467da823 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionAddToolsSubmenu.svelte @@ -5,6 +5,7 @@ import * as DropdownMenu from '$lib/components/ui/dropdown-menu'; import * as Tooltip from '$lib/components/ui/tooltip'; import { toolsStore } from '$lib/stores/tools.svelte'; + import { CLI_FLAGS } from '$lib/constants'; import { mcpStore } from '$lib/stores/mcp.svelte'; import { useToolsPanel } from '$lib/hooks/use-tools-panel.svelte'; @@ -33,7 +34,7 @@ - Run llama-server with --tools flag to enable + Run llama-server with {CLI_FLAGS.TOOLS} flag to enable Built-in Tools. diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionsAdd.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionsAdd.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionsAdd.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAdd/ChatFormActionsAdd.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionModels.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionModels.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionModels.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionModels.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionRecord.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionRecord.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionRecord.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionRecord.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionSubmit.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormFileInputInvisible.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormMcpResourcesList.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormMcpResourcesList.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormMcpResourcesList.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormMcpResourcesList.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerItemHeader.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerItemHeader.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerItemHeader.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerItemHeader.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentForm.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentForm.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentForm.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentForm.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpResources.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpResources.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpResources.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpResources.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickers.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickers.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickers.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickers.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte rename to tools/ui/src/lib/components/app/chat/ChatForm/ChatFormTextarea.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageAssistant/ChatMessageAssistant.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPrompt.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPrompt.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPrompt.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPrompt.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPromptContent.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPromptContent.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPromptContent.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageMcpPrompt/ChatMessageMcpPromptContent.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageSystem/ChatMessageSystem.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageSystem/ChatMessageSystem.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageSystem/ChatMessageSystem.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageSystem/ChatMessageSystem.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUser.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUser.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUser.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUser.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserBubble.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserBubble.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserBubble.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserBubble.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageUser/ChatMessageUserPending.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCard.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCard.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCard.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCard.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardContinueRequest.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardContinueRequest.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardContinueRequest.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardContinueRequest.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardPermissionRequest.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardPermissionRequest.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardPermissionRequest.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionCard/ChatMessageActionCardPermissionRequest.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIcons.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIcons.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIcons.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIcons.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIconsBranchingControls.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIconsBranchingControls.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIconsBranchingControls.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageActions/ChatMessageActionIcons/ChatMessageActionIconsBranchingControls.svelte diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte similarity index 99% rename from tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte rename to tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte index e9b77ba2f4f..3a9cc7e9356 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatMessages/ChatMessageAgenticContent.svelte @@ -274,7 +274,9 @@ {:else if section.toolResult}
    {#each section.parsedLines as line, i (i)} -
    {line.text}
    +
    + {line.text} +
    {#if line.image} (Run
    llama-server
    with -
    --webui-mcp-proxy
    +
    {CLI_FLAGS.MCP_PROXY}
    flag) {/if} diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerIdentity.svelte b/tools/ui/src/lib/components/app/mcp/McpServerIdentity.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/mcp/McpServerIdentity.svelte rename to tools/ui/src/lib/components/app/mcp/McpServerIdentity.svelte diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerInfo.svelte b/tools/ui/src/lib/components/app/mcp/McpServerInfo.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/mcp/McpServerInfo.svelte rename to tools/ui/src/lib/components/app/mcp/McpServerInfo.svelte diff --git a/tools/server/webui/src/lib/components/app/mcp/index.ts b/tools/ui/src/lib/components/app/mcp/index.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/mcp/index.ts rename to tools/ui/src/lib/components/app/mcp/index.ts diff --git a/tools/server/webui/src/lib/components/app/misc/CodeBlockActions.svelte b/tools/ui/src/lib/components/app/misc/CodeBlockActions.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/CodeBlockActions.svelte rename to tools/ui/src/lib/components/app/misc/CodeBlockActions.svelte diff --git a/tools/server/webui/src/lib/components/app/misc/ConversationSelection.svelte b/tools/ui/src/lib/components/app/misc/ConversationSelection.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/ConversationSelection.svelte rename to tools/ui/src/lib/components/app/misc/ConversationSelection.svelte diff --git a/tools/server/webui/src/lib/components/app/misc/HorizontalScrollCarousel.svelte b/tools/ui/src/lib/components/app/misc/HorizontalScrollCarousel.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/HorizontalScrollCarousel.svelte rename to tools/ui/src/lib/components/app/misc/HorizontalScrollCarousel.svelte diff --git a/tools/server/webui/src/lib/components/app/misc/KeyboardShortcutInfo.svelte b/tools/ui/src/lib/components/app/misc/KeyboardShortcutInfo.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/KeyboardShortcutInfo.svelte rename to tools/ui/src/lib/components/app/misc/KeyboardShortcutInfo.svelte diff --git a/tools/server/webui/src/lib/components/app/misc/TruncatedText.svelte b/tools/ui/src/lib/components/app/misc/TruncatedText.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/TruncatedText.svelte rename to tools/ui/src/lib/components/app/misc/TruncatedText.svelte diff --git a/tools/server/webui/src/lib/components/app/misc/index.ts b/tools/ui/src/lib/components/app/misc/index.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/misc/index.ts rename to tools/ui/src/lib/components/app/misc/index.ts diff --git a/tools/server/webui/src/lib/components/app/models/ModelBadge.svelte b/tools/ui/src/lib/components/app/models/ModelBadge.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelBadge.svelte rename to tools/ui/src/lib/components/app/models/ModelBadge.svelte diff --git a/tools/server/webui/src/lib/components/app/models/ModelId.svelte b/tools/ui/src/lib/components/app/models/ModelId.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelId.svelte rename to tools/ui/src/lib/components/app/models/ModelId.svelte diff --git a/tools/server/webui/src/lib/components/app/models/ModelsSelectorDropdown.svelte b/tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelsSelectorDropdown.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelectorDropdown.svelte diff --git a/tools/server/webui/src/lib/components/app/models/ModelsSelectorList.svelte b/tools/ui/src/lib/components/app/models/ModelsSelectorList.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelsSelectorList.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelectorList.svelte diff --git a/tools/server/webui/src/lib/components/app/models/ModelsSelectorOption.svelte b/tools/ui/src/lib/components/app/models/ModelsSelectorOption.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelsSelectorOption.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelectorOption.svelte diff --git a/tools/server/webui/src/lib/components/app/models/ModelsSelectorSheet.svelte b/tools/ui/src/lib/components/app/models/ModelsSelectorSheet.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/models/ModelsSelectorSheet.svelte rename to tools/ui/src/lib/components/app/models/ModelsSelectorSheet.svelte diff --git a/tools/server/webui/src/lib/components/app/models/index.ts b/tools/ui/src/lib/components/app/models/index.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/models/index.ts rename to tools/ui/src/lib/components/app/models/index.ts diff --git a/tools/server/webui/src/lib/components/app/models/utils.ts b/tools/ui/src/lib/components/app/models/utils.ts similarity index 100% rename from tools/server/webui/src/lib/components/app/models/utils.ts rename to tools/ui/src/lib/components/app/models/utils.ts diff --git a/tools/server/webui/src/lib/components/app/navigation/DesktopIconStrip.svelte b/tools/ui/src/lib/components/app/navigation/DesktopIconStrip.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/navigation/DesktopIconStrip.svelte rename to tools/ui/src/lib/components/app/navigation/DesktopIconStrip.svelte diff --git a/tools/server/webui/src/lib/components/app/navigation/DropdownMenuActions.svelte b/tools/ui/src/lib/components/app/navigation/DropdownMenuActions.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/navigation/DropdownMenuActions.svelte rename to tools/ui/src/lib/components/app/navigation/DropdownMenuActions.svelte diff --git a/tools/server/webui/src/lib/components/app/navigation/DropdownMenuSearchable.svelte b/tools/ui/src/lib/components/app/navigation/DropdownMenuSearchable.svelte similarity index 100% rename from tools/server/webui/src/lib/components/app/navigation/DropdownMenuSearchable.svelte rename to tools/ui/src/lib/components/app/navigation/DropdownMenuSearchable.svelte diff --git a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte similarity index 99% rename from tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte rename to tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index 105576bb4f5..ddaf4d5b875 100644 --- a/tools/server/webui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -174,7 +174,9 @@
    -

    {APP_NAME}

    +

    + {APP_NAME} +