Batch processing¶
AI Hub exposes two batch families:
- matching batches read active entities from MongoDB and calculate JOM/JPM scores;
- import batches read source data, standardize it and persist users, jobs, opportunities or trainings.
All endpoints below validate the administrator API key. They use
AI_HUB_ADMIN_API_KEY when configured; otherwise they can fall back to a
MongoDB key with isAdmin: true only without DATABASE_URL. Database
compatibility rules still determine which operations are available.
Endpoints¶
Matching¶
| Endpoint | Processing direction | Required feature |
|---|---|---|
GET /admin/api/v1/matching/batches/jom/users |
active users against opportunities | use_matching.jom |
GET /admin/api/v1/matching/batches/jom/opportunities |
active opportunities against users | use_matching.jom |
GET /admin/api/v1/matching/batches/jpm |
active users against jobs | use_matching.jpm |
Matching query parameters are client_id, data_source, environment,
number_of_threads and, when relevant, pwc_staging_or_prod.
curl --get 'http://localhost:8000/admin/api/v1/matching/batches/jom/users' \
--header 'X-API-Key: <admin-api-key>' \
--data-urlencode 'client_id=725' \
--data-urlencode 'data_source=smartmobility' \
--data-urlencode 'environment=integration' \
--data-urlencode 'number_of_threads=16'
Import and standardization¶
| Endpoint | Supported source | Required feature |
|---|---|---|
POST /admin/api/v1/entity-imports/users |
Smart Mobility SQL or PwC JSON | JOM or JPM |
POST /admin/api/v1/entity-imports/opportunities |
Smart Mobility SQL or PwC JSON | JOM |
POST /admin/api/v1/entity-imports/jobs |
Smart Mobility SQL | JPM |
POST /admin/api/v1/entity-imports/trainings |
Smart Mobility SQL | JPM |
Common parameters are client_id, data_source, environment,
deactivate_entities and number_of_threads. Each endpoint also accepts an
entity-ID filter and a separate list of IDs for which skill inference should be
forced. User, job and opportunity imports can clear deactivated matching scores
through clean_deactivated_entities_scores; training import deliberately does
not expose that parameter.
curl --request POST 'http://localhost:8000/admin/api/v1/entity-imports/trainings?client_id=725&data_source=smartmobility&environment=integration&number_of_threads=8' \
--header 'X-API-Key: <admin-api-key>'
Deactivation and filtered imports
When deactivate_entities=true, the target collection is marked inactive
before imported records are upserted. Deactivation and score cleanup run
only after every source record has been prepared, so a malformed source
record fails the background operation while leaving existing entities and scores
untouched. Combining that option with an
import_*_ids filter means only the selected records are reactivated. Use
deactivate_entities=false for a partial refresh unless this behavior is
intentional.
Execution model¶
The HTTP routes return 202 with a task_id after preflight validation.
GET /admin/api/v1/operations/{task_id} reports running, success or fail
while the task is retained in the current container's worker memory. Active tasks
and the latest 1,000 completed outcomes per worker are retained without database
persistence; evicted or lost states return 404. The
application-owned task manager executes the workflow after acceptance, with
bounded running and pending work. See background operations
for notification recipients, capacity and shutdown behavior.
The same manager owns MongoDB skill inference, opportunity synchronization with check_only=false,
JPM job-score publication, configuration normalization, PwC exports and
administrative migrations. Inference keeps its existing eight-worker bound and
records batch successes and failed entity IDs in the consolidated report.
PostgreSQL user inference retains its separate durable reconciliation jobs.
Shared client imports and full processing¶
POST /admin/api/v1/clients defaults initialisation_import to true and imports and standardizes
jobs, opportunities, trainings, then users sequentially, awaiting each batch
before starting the next. These initial imports run even when JOM or JPM is
disabled, without changing those feature flags or calculating matching scores.
Standardization still performs configured enrichment, embeddings, persistence
and email reporting. Source support remains as listed in the import table above.
POST /admin/api/v1/batch-processing/client/imports calls the same
import_client_entities orchestrator in src/admin/services/client_initialization.py.
POST /admin/api/v1/batch-processing/client/full awaits that orchestrator, then
reuses process_one_client, also called by
POST /admin/api/v1/batch-processing/client/matchings.
The shared import order is jobs → opportunities → trainings → users. Matching then runs JOM users → JOM opportunities → JPM, respecting configured matching flags. Imports and full processing require MongoDB; matching alone also supports PostgreSQL. The dedicated single-entity import endpoints keep their feature requirements. A global import error or cancellation prevents later stages; per-entity failures retain existing batch isolation. Independent matching phases continue after a reported phase failure.
Batch context¶
Each entity import batch opens one BatchProcessingContext. It reuses tenant
configuration, AI configuration, the MongoDB connection, an optional department
lookup and one Datalake writer. The shared import orchestrator closes each entity's
context before starting the next import. Matching maintenance can share a context
across its matching batches.
run_bounded_batch accepts either an async MongoDB cursor or an in-memory
iterable. A fixed number of workers process entities concurrently while a queue
holds at most two waiting entities per worker.
Matching input¶
Matching entities are streamed from async MongoDB cursors, so producer memory stays bounded. Embedding preparation runs once per matching type and only fills missing entity, opportunity or job embeddings. Client matching reuses the same prepared JOM context for both processing directions, including in full processing.
SQL import input¶
Smart Mobility import connectors stream SQLAlchemy results in partitions of 1,000 rows into column buffers, without an intermediate list of row dictionaries. Pandas infers types once over complete columns, preserving nullable values and joins across partition boundaries. DataFrames and the source records remain materialized for whole-source grouping and the existing deactivation workflow; this does not make the complete import constant in memory.
Smart Mobility import filters are passed to the database driver as bound parameters, including variable-length identifier lists.
An initial preparation pass validates records and counts selected entities before starting workers. A second pass feeds normalized records lazily into the bounded queue. This trades a second normalization pass for avoiding retention of all normalized users/opportunities alongside their source records, while preserving preparation failures and progress totals. PwC transformation shares the active opportunity lookup instead of copying it into multiple processes.
PostgreSQL existing-data initialization¶
Alembic does not enumerate existing business rows. PostgreSQL deployments use durable administrator runs to fill the asynchronous job tables in bounded keyset pages:
Endpoint suffix under /admin/api/v1/postgres-initialization |
Queued work |
|---|---|
/skills |
missing skill-title embeddings |
/users |
missing profiles, skill gaps and experience inference jobs |
/opportunities |
missing opportunity enrichments and embeddings |
/jobs |
missing job embeddings |
/trainings |
missing training enrichments and embeddings |
/all |
all five families in the order above |
Every POST accepts an optional client_id corresponding to setting.id.
Without it, all LIVE and DEMO settings are included. The response contains
a durable jobId; GET /admin/api/v1/postgres-initialization/runs/{job_id}
returns its steps. A step is complete after it has inserted the required child
job rows. It deliberately does not wait for the entity workers to consume that
backlog.
Concurrency settings¶
| Variable or parameter | Default | Purpose |
|---|---|---|
number_of_threads |
8 |
requested async entity-processing concurrency |
BATCH_MAX_CONCURRENCY |
32 |
server-side ceiling for request concurrency |
BATCH_DATALAKE_UPLOAD_CONCURRENCY |
8 |
shared Datalake upload workers |
number_of_threads must be positive. The API returns 422 before loading SQL
data when it is invalid. Values above the server ceiling are clamped and logged.
Datalake behavior¶
Every batch reuses one lazily initialized Azure Blob client. Standardized
imports pass the shared writer through all four entity standardizers; matching
passes it to pool/score processing. The bounded upload queue is drained when the
batch context closes. When BLOB_ENDPOINT is absent or empty, all Datalake
writes are silently skipped and no storage client or upload worker is started.
Unitary imports and other direct writes reuse a separate lazy Azure client per
storage account and application event loop. Each direct save still awaits its
upload; it does not enter the batch queue. The unified lifespan closes these
clients after background processing stops. Standalone callers use
connector_clients_scope() from src.common.connectors.shared_clients.
An absent, empty or whitespace-only BLOB_ENDPOINT skips the write before
accessing that scope or constructing any storage client, for both direct and
batch paths. Upload failure reporting and one success log per stored blob are
preserved.
File granularity and paths remain compatible:
- imports write one JSON object per entity below
message/standardized; - matching writes one JSON object per entity below
processing/poolandprocessing/scores.
Single-entity HTTP and consumer flows do not provide a batch writer and retain the direct-save fallback.
Lexi generation batches¶
Lexi skill descriptions, skill levels, mission suggestions and skill suggestions use a fixed number of async workers, limited by their existing concurrency settings. Input order is preserved in the returned results, and waiting items do not each allocate a task. On an ordinary failure, the other items still run; the first exception is propagated after they finish. This can delay an error response, but leaves no unfinished sibling work behind it. Cancellation stops and awaits all workers.
Results, progress and errors¶
Every completed entity logs entity x/total; a filtered import can show ? as
the total because filtering is lazy. The producer logs every 100 queued records,
and the final line contains duration, successes and failures.
Failures are isolated per entity and their IDs are collected in the in-memory
batch outcome, together with timing and success counts. Administrative operations
normally consolidate these results in a terminal email. Client creation, the four
individual entity imports, the three matching batches and client import/matching/full
workflows instead isolate the report of each stage
with run_client_batch_with_report, then await its email before the next stage.
Imports are labelled Import; matching reports distinguish JOM users,
JOM opportunities and JPM. Reports preserve succeeded, partial_failure,
no_data, skipped and failed outcomes. Cancelled work receives a terminal
cancellation report rather than a successful stage report.
Stage reports are merged into the overall task outcome. The full workflow always sends a terminal summary, including on success; other milestone workflows send an additional terminal report on failure or cancellation. Notification failures are isolated per recipient and do not change the processing outcome. Direct service callers outside the administrative manager retain existing per-batch emails. Batch outcomes are not persisted in a database.
The messages below are workflow results included in the completion report; they are no longer synchronous HTTP responses:
An unknown tenant returns 404. The former nightly matching scheduler has been
removed; launch batches through these APIs or the full client workflow rather
than relying on nightly_batch scheduling.
Batched MongoDB vector lookups share the per-worker
MONGO_VECTOR_SEARCH_CONCURRENCY budget (default 8). Their fixed worker
count prevents entity batch concurrency from multiplying the number of active
vector queries. Filters, indexes and scoring rules are unchanged. See
configuration
for failure, cancellation and standalone-script behavior.