"""{{ display_name }} MCP server — generated by mcpify. Do not hand-edit.

Computes embeddings with `sentence-transformers`' `all-mpnet-base-v2` —
run natively, no ONNX/`fastembed`-style indirection needed, since this
target's runtime *is* Python. Imported by both the `search` tool and
`services/populate_embeddings.py`, so the exact same function computes
vectors at generation time and at live-query time — if the two diverged,
cosine similarity would break silently.
"""

from __future__ import annotations

import os
from functools import lru_cache
from typing import TYPE_CHECKING

# `tokenizers`' Rayon thread pool acquires the GIL from its worker threads
# once parallelism is enabled, which registers them with the interpreter
# and makes CPython's shutdown (Py_FinalizeEx, which — since Python
# 3.9 — waits for every such thread, not just non-daemon ones) hang
# forever, since Rayon's global pool never voluntarily exits. Must be set
# before `sentence_transformers`/`tokenizers` is first imported anywhere
# in the process (including `populate_embeddings` and `pytest`), so it's
# set here at module import time rather than inside `_model()`.
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")

if TYPE_CHECKING:
    from sentence_transformers import SentenceTransformer

MODEL_NAME = "sentence-transformers/all-mpnet-base-v2"
CACHE_DIR = ".sentence_transformers_cache"


@lru_cache(maxsize=1)
def _model() -> SentenceTransformer:
    # Imported lazily, not at module level, so commands that never touch
    # embeddings (e.g. `version`, `config`) don't pay sentence-transformers'
    # torch-backed import cost on every CLI invocation.
    from sentence_transformers import SentenceTransformer

    return SentenceTransformer(MODEL_NAME, cache_folder=CACHE_DIR)


def embed(text: str) -> list[float]:
    vector = _model().encode(text, normalize_embeddings=True)
    return vector.tolist()
