The Mindloom inference service is a stateless FastAPI microservice responsible for one job: running the ML model. It classifies content, extracts keywords, produces embeddings, assigns cluster IDs, and calculates UMAP coordinates — all in a singleDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/No-Country-simulation/G9-LATAM-Team-58/llms.txt
Use this file to discover all available pages before exploring further.
/predict call. It is an internal service, called only by the api/ layer, and is never exposed directly to the web client or to the public internet.
Startup Lifecycle
Before the service can respond to any request, it must load two assets from different sources. This split between build time and runtime is intentional and critical to understand.Docker build: bake the transformer
During
docker build, the intfloat/multilingual-e5-small transformer (~470 MB) is downloaded from HuggingFace and stored in the image layer at HF_HOME=/app/.cache/huggingface. The container can start offline — no HuggingFace download happens at runtime.Container startup: download model.joblib
When the container starts, the FastAPI
lifespan handler calls load_model(), which downloads models/latest.txt from OCI Object Storage to find the active version prefix, then downloads the referenced model.joblib (~0.24 MB).Populate APP_STATE
The loaded artifact dict is stored in
APP_STATE["model"]. A SentenceTransformer is instantiated from meta["embedding_model"] — this reads from the local HuggingFace cache baked in during build, not from the network. The service sets APP_STATE["model_loaded"] = True only after both are ready.APP_STATE Keys
The globalAPP_STATE dict is the runtime state store for the process:
| Key | Type | Contents |
|---|---|---|
model_loaded | bool | True only after startup completes successfully |
model | dict | The full model.joblib artifact dict |
encoder | SentenceTransformer | The loaded intfloat/multilingual-e5-small encoder |
meta | dict | artifact["meta"] — version, dims, prefixes, categories, metrics |
kmeans | KMeans | artifact.get("kmeans") — for cluster assignment |
umap | UMAP | artifact.get("umap_reducer") — for 2D projection |
OCI Authentication
Theoci_client.py module selects its authentication strategy based on the environment:
~/.oci/config.
Environment Variables
| Variable | Required | Purpose |
|---|---|---|
MODEL_BUCKET | In production | OCI Object Storage bucket name containing model.joblib |
MODEL_LOCAL_PATH | No | Dev shortcut — loads a local file and skips OCI entirely |
API Endpoints
POST /predict
Classifies a text document and returns the full set of fields needed to index the content in the database — category, probability, keywords, explainability terms, embedding vector, cluster ID, and map coordinates — all in a single call.- Request
- Response
| Field | Type | Required |
|---|---|---|
text | string | Yes |
"passage: " E5 prefix internally. The returned embedding is the raw 384-dim vector — not the 884-dim classifier feature vector. This is correct: the database stores and searches over the 384-dim space.
POST /embed
Returns a single L2-normalized embedding vector. Used by theapi/ layer’s semantic search path, which then passes the vector to the database for VECTOR_DISTANCE ranking.
- Request
- Response
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Text to encode |
type | "query" | "passage" | Yes | E5 prefix to apply |
type field is a Pydantic Literal["query", "passage"] — any other value returns 422 Unprocessable Entity immediately. There is no default value by design.
GET /health
Returns the service readiness status. This is the endpoint polled by the Docker healthcheck.| Field | Values | Description |
|---|---|---|
status | "ok" | "error" | Reflects whether the model is loaded |
model_loaded | true | false | true only after startup completes |
version | "v1" | Fixed service version string |
Dockerfile uses urllib (not curl, which is absent from the slim image):
GET /model/info
Returns the fullmeta block from the loaded artifact. Returns 503 if the model has not finished loading.
dim and feature_dim are two different numbers. dim (384) is the size of the embedding vector stored in the database and returned in API responses. feature_dim (884) is what the classifier consumes internally. See Model Overview for the full explanation.Network Topology
Troubleshooting
Container restarts in a loop
Container restarts in a loop
load_model() raised an exception during the FastAPI lifespan. Common causes:- Bucket credentials missing: on a new VM, ensure the Instance Principal policy is configured in OCI IAM.
models/latest.txtdoes not exist: the model artifact has not been uploaded yet, or the bucket name inMODEL_BUCKETis incorrect.latest.txtpoints to a version that doesn’t exist: the pointer was updated before the artifact finished uploading. Re-upload the full version and verify the object exists in the bucket before updating the pointer.
Container stays 'unhealthy' after 60 seconds
Container stays 'unhealthy' after 60 seconds
load_model() is taking longer than the start-period allows. This can happen when the bucket is geographically distant or under load. Increase the --start-period value in the HEALTHCHECK directive in the Dockerfile and rebuild the image.KeyError on startup
KeyError on startup
The
model.joblib artifact’s keys do not match what the inference code expects. This happens when a key is renamed or added in the training notebook without updating the inference service. Ensure the artifact was produced by the current version of TechMind_02_Model_Training.ipynb and that all expected keys are present. See Model Overview for the full list of required keys.Docker image is 4× the expected size
Docker image is 4× the expected size
Torch was installed from the default PyPI index on an x86 machine, which resolves to the CUDA build and pulls ~2 GB of unused GPU libraries. Install torch explicitly from the CPU wheel index:This is already the default in the
Dockerfile. The issue only appears when building manually on x86 without following the Dockerfile steps. On aarch64 (the production VM), no CUDA wheels exist in the default index, so this is not a concern in production.