{% extends "admin/_layout.html" %} {% from "_macros.html" import icon %} {% set section = "extraction" %} {% block title %}{{ t("Extraction") }} - {{ brand.name }}{% endblock %} {% block heading %}{{ t("Extraction and search") }}{% endblock %} {% block admin_content %}

{{ t("What happens to a file between the upload and the model, and how anything is found again afterwards. The two are the same pipeline: what is extracted decides what there is to search.") }}

{% if saved %}
{{ icon("check", "icon--sm") }} {{ saved }}
{% endif %} {# --- Extraction ----------------------------------------------------------- #}

{{ t("What a file may cost") }}

{{ t("Every number here is a trade, and a large one usually breaks a request rather than being slow — the text of a whole book does not fit in a context window, and a model handed it fails the request outright rather than reading the first half.") }}

{{ t("Megabytes, before anything is done to it.") }}

{{ t("Characters. Roughly four to a token, so 120,000 is about 30,000 tokens — already most of a small context window. The rest is cut and the model is told so.") }}

{{ t("Extraction is slow and happens once, at upload. Beyond this the file is still stored; only its text stops.") }}

{{ t("Pixels. Images are re-encoded before they are sent, because a phone photo is several megabytes of base64.") }}

{{ t("Hours. A file chosen in the composer and never sent. Swept at startup.") }}

{{ tm("One extension per line, .env or env. Only needed to pick a media type — whether the bytes decode as text is what actually decides, so an unlisted extension already works.") }}

{{ t("Off, a scanned PDF is stored with an explanation saying why it contributes nothing — there is no OCR here. That is usually what somebody wants: the file is still attached and still downloadable.") }}

{# --- Semantic search ------------------------------------------------------ #}

{{ t("Searching by meaning") }}

{{ tm('Keyword search finds a document that uses your words. This finds one that means the same thing — "how do I get in" reaching a note about passwords. Both run and the two rankings are fused, so nothing that keyword search found is lost.') }}

{% if not models %}
{{ icon("warning", "icon--sm") }} {{ tm("No model is marked as producing embeddings. Tick %(capability)s on one under Models — usually a small dedicated model such as nomic-embed-text or bge-m3, not a chat model.", capability=t("embeddings")) }}
{% endif %} {% if missing_model %}
{{ icon("warning", "icon--sm") }} {{ tm("%(model)s is selected but is not available — its model or its connection has gone, or it is no longer marked for embeddings. Search has fallen back to keywords.", model=missing_model) }}
{% endif %}

{{ tm("%(none)s is not a degraded mode: it is the keyword search this has always had, with nothing stored and nothing sent anywhere. Changing the model changes what a vector means, so anything already indexed is ignored until it is rebuilt.", none=t("None")) }}

{{ t("Lower this if the endpoint refuses large requests; raise it if a rebuild is slow and the far side has room.") }}

{#- The index itself (services/index): what is waiting, what failed, how many pieces and vectors there are, and the button that reads everything again. -#}

{{ t("The search index") }}

{{ t("Files and chats are indexed as they change. A rebuild reads everything again — after changing the embedding model, or when a search misses something it should find.") }}

{% if index %}
{{ t("Waiting") }}
{{ index.pending }}
{{ t("Done") }}
{{ index.states.get("done", 0) }}
{{ t("Failed") }}
{{ index.states.get("error", 0) }}
{{ t("Pieces") }}
{{ index.pieces }}
{{ t("With a vector") }}
{{ index.vectors }}

{% if index.running %}{{ t("The indexer is running.") }}{% else %}{{ t("The indexer is not running.") }}{% endif %} {% if not index.vectors_available %} {{ t("Search by meaning is not available on this server (sqlite-vec is not installed); keywords still work.") }}{% endif %} {% if index.last_error %} {{ t("Last error: %(error)s", error=index.last_error) }}{% endif %}

{% else %}

{{ t("The index could not be read.") }}

{% endif %}
{% endblock %}