🧠 Embeddings

all-MiniLM-L6-v2 · local, no API key

Concept

Words and sentences can be encoded as vectors (lists of numbers) called embeddings. Two sentences that mean similar things will have vectors pointing in similar directions — cosine similarity measures the angle between them. The all-MiniLM-L6-v2 model runs entirely locally (no API key), making it fast and free.

This demo encodes both sentences and returns their similarity score between 0 (unrelated) and 1 (semantically identical).

Theory & Concepts

When you enter two distinct sentences into this Embeddings demo, you are testing how an AI mathematically measures meaning. At the core of this process is the concept of Embeddings. The demo takes your two text inputs and passes them through a local neural network model (all-MiniLM-L6-v2). This model maps each sentence into a dense vector—a high-dimensional list of floating-point numbers—in a continuous vector space. In this space, the direction of the vector corresponds to the semantic meaning of the text.

Because the embedding model has been trained on massive datasets to pull similar concepts together and push dissimilar ones apart, the resulting vectors capture deep semantic features like topic, context, and tone. However, representing your sentences as lists of numbers is only half the battle. To determine how closely related your two inputs are, the backend needs to compare these two vectors.

This is where the Cosine Similarity calculation in the demo's code comes into play. Using numpy, the backend calculates the dot product of the two vectors divided by the product of their magnitudes. Geometrically, this measures the angle between the two vectors rather than their absolute distance (which could be skewed if one sentence is much longer than the other). The demo then renders this result as a score between 0 and 1. A score close to 1 means your two sentences point in the same direction and have highly similar meanings, while a lower score closer to 0 indicates they are semantically unrelated. This mathematical translation is what allows the system to compare texts based on actual meaning rather than just matching identical keywords.

Request flow

🧑 Browser Two sentences
POST /embeddings Flask route
embeddings_route() app.py
compare_similarity() embeddings.py
get_embedder() embed both texts → cosine similarity → score
🧑 Browser Similarity score rendered

Code flow

flowchart TD A[Browser: text_a + text_b] -->|"POST /embeddings"| B[app.py: embeddings_route] B -->|"text_a, text_b"| C[embeddings.py: compare_similarity] C -->|"text_a"| D[get_embedder: all-MiniLM-L6-v2] C -->|text_b| D D -->|"vector v1"| C D -->|"vector v2"| C C -->|"cosine(v1, v2)"| E[cosine similarity: score 0 to 1] E -->|"float"| C C -->|"score"| B B -->|"JSON result"| A

Backend

Encodes two texts and returns their cosine similarity.
from numpy import dot
from numpy.linalg import norm

from config import get_embedder


def compare_similarity(text_a: str, text_b: str) -> float:
    """Encode two texts and return their cosine similarity (0-1)."""
    # Step 1: Load the sentence-transformers embedding model (all-MiniLM-L6-v2)
    embedder = get_embedder()

    # Step 2: embed_query converts text strings into dense 384-dimensional vector representations
    v1 = embedder.embed_query(text_a)
    v2 = embedder.embed_query(text_b)

    # Step 3: Compute cosine similarity = dot product / (product of vector magnitudes)
    # Result: 1.0 = identical direction/meaning, 0.0 = orthogonal/completely unrelated
    return float(dot(v1, v2) / (norm(v1) * norm(v2)))

API route

Exposes the similarity scorer over HTTP as POST /embeddings.
@bp.route("/embeddings", methods=["POST"])
def embeddings_route():
    """Compare two texts and return their cosine similarity.

    Request body (JSON): ``{ "text_a": "<text>", "text_b": "<text>" }``
    Response (JSON):     ``{ "result": { "similarity": 0.87 } }``
    Error response:      ``{ "error": "<message>" }`` with HTTP 400 or 500
    """
    # Step 1: Parse request JSON payload
    data = request.get_json(force=True)
    text_a = (data.get("text_a") or "").strip()
    text_b = (data.get("text_b") or "").strip()

    # Step 2: Validate that both comparison strings were supplied
    if not text_a or not text_b:
        return jsonify({"error": "Both text_a and text_b are required."}), 400

    # Step 3: Compute cosine similarity and return 4-decimal rounded score
    try:
        score = compare_similarity(text_a, text_b)
        return jsonify({"result": {"similarity": round(score, 4)}})
    except Exception as e:
        # Return HTTP 500 on unexpected errors
        return jsonify({"error": str(e)}), 500