Talent Marketplace filter prefill¶
POST /api/v1/talent-intelligence/talent-marketplace/filters interprets a natural-language
search for the Smart Mobility RH/manager back-office. Smart Mobility sends the
filters currently available to the user. AIHub first selects the relevant filters
with LLM_PROFILE_LARGE, loads only their possible values from MariaDB, and then
uses the same profile to suggest values.
AIHub does not discover which filters the interface displays, search or rank people, launch JOM/JPM, persist results, or change existing filter combinations. Smart Mobility owns feature switches, end-user permissions and applying the suggestions. The user reviews them before launching the normal talent search. This implements AIHub ticket NB-9027; the Smart Mobility UI integration is tracked separately in NB-9026.
Request¶
Use the standard API key. A tenant-bound key must match client_id. The SQL
database is resolved from the registered Smart Mobility ClientConfig.databaseSource
for the application's deployment environment, using the existing MariaDB connector.
The caller cannot supply a database name, SQL, filter choices or an LLM override.
This route requires that client registration even when other AIHub workflows use
PostgreSQL; PostgreSQL entity/skill copies are not a replacement for the legacy
MariaDB catalogue. Existing server-side SM_* connection configuration applies.
{
"client_id": "123",
"query": "Find agricultural engineers in the Research department in London",
"locale": "en_GB",
"filters": [
{"key": "search_diplome"},
{"key": "city"},
{"key": "company_entity", "company_entity_id": 42}
]
}
client_id: required decimal string identifying the Smart Mobility setting.query: required nonblank string, at most 4,000 characters. Unicode and legitimate punctuation/code examples are preserved. Shared text controls apply.filters: 1–100 descriptors. Standard filters use their legacy field key, without the HTML[]suffix. Smart Editor usescompany_entityplus a positive integercompany_entity_id. This identifier is forbidden on standard filters. Duplicate identities and unknown keys are rejected.locale: optional exact legacy locale, for exampleen_GBorfr_FR. Omitting it or supplyingnulluses source labels. No locale inference or locale substitution is performed. With an explicit locale, Smart Editor choices and skills lacking that translation are unavailable, as in the legacy UI.
Extra fields are rejected, including client-supplied choices, SQL, types and
cardinality overrides. Smart Editor metadata is checked only for the requested
IDs: the field must belong to the client, be visible, non-default, enabled for
TMKP BO and attached to User, Professional or Job data. No filter-discovery query
is executed. HTML and Number Smart Editor fields are rejected because the
legacy filter form does not render them.
Two-stage interpretation¶
- Resolve the registered tenant and validate its SQL setting. Describe the supplied standard filters from application metadata. Read names, types and cardinality for the supplied Smart Editor IDs in one bound SQL query, without reading items.
- Preselection receives the query and these descriptions, without choices.
Compatible OpenAI
miniuses independent native select/omit decisions, one per filter; code reconstructs original(key, company_entity_id)identities. Other providers/models retain the existingLLM_PROFILE_LARGEstructured call. Unknown identities and duplicates fail before any value query; an empty selection ends the request. Provider errors never trigger a generative retry. - Load possible values only for the selected filters. Unselected catalogues and
their settings are not queried. Read
setting.skill_levelonly when skills are selected. If bothjobandjob_titlesare selected, reuse one job query. Free-text, numeric, date and fixed-enum filters need no value query. - The second
LLM_PROFILE_LARGEcall receives the query and selected catalogue. Validate its selections against that subset and the authoritative values. Return filled filters in the caller's original order.
The tenant AI configuration is loaded once and reused for both calls, including when the existing configuration helpers use PostgreSQL. The two-stage workflow reduces SQL value reads and the second prompt's size, at the cost of an additional LLM call. It does not guarantee lower total latency for every query.
Supported filters and value sources¶
| Input key | Selection kind | Source and native values |
|---|---|---|
search_diplome |
choice, multiple |
Distinct CvDiplome.label values belonging to active users of the client; the label itself is the submitted value. |
skills_exclusion |
skills, multiple |
Client skills in public reference libraries, without a replacement ID; numeric skill IDs and an optional level bounded by setting.skill_level. |
job_titles |
choice, multiple |
Active client jobs with a visible JobStatus; numeric job IDs. |
job |
choice, single |
The same job catalogue, for the existing JPM criterion. |
job_offer |
choice, single |
Client job offers with null/zero validity or validity not yet expired; numeric IDs, translated titles where present. |
direction |
choice, single |
Department names from the legacy customer → user → school/client joins. |
mission |
choice, single |
Client projects having at least one Usermission; numeric mission IDs. |
job_family |
choice, multiple |
Clients owned by the root school's user; numeric legacy family IDs. |
langue |
choice, multiple |
Nonempty language codes with nonempty labels from client users' CvLangue rows. |
contract |
choice, multiple |
Legacy template values CDD, CDI, Interim; setting 94 retains C-AP, CDI, Interim (displayed as CPRO), Stage. |
gender |
choice, single |
Legacy template strings "1" and "2". |
internal_mobility |
choice, single |
Legacy template strings "1" and "0". |
searchc |
text, multiple |
Extracted keyword terms, for the existing Tagify input. |
nationality, domain, country, city, search_school |
text, single |
Free-text values extracted from the query; these widgets have no finite SQL choices. |
age_min, age_max, seniority_min, seniority_max |
number |
Nonnegative integer years; paired lower/upper bounds must be ordered. |
high_potential[enabled] |
choice, single |
The legacy checkbox value "on". |
high_potential[index][view_height], [adaptability], [relational_ability], [self_supporting], [collective_interest] |
range |
Use the full high_potential[index][...] key. Bounds range from 0 to 100 in steps of 5. |
company_entity |
Depends on SQL metadata | Select respects is_multiple; List remains single-select. Their native values are company_entity_item.id. Date returns a date range. Other legacy text-like widgets return text. |
Only catalogues selected by the first LLM call are loaded. SQL uses bound values and fixed application queries; the search text is never interpolated into SQL. Autocomplete substring predicates and the legacy 25/100-result display limits are omitted to expose all available values to the interpretation step. Degree descriptions and employee profiles are not sent to the model. Fixed template enums have no SQL value table.
The catalogue is client-scoped, not viewer-scoped. Unlike the legacy language widget, it cannot exclude the current viewer because no user ID is supplied. Duplicate language codes receive a deterministic label. Caller-side visibility and permission checks remain necessary before sending the filter list and applying the result.
Response¶
The response contains only relevant supplied filters, in request order. Choices
preserve native string or integer values. Standard selections have
company_entity_id: null; Smart Editor selections echo the requested field ID.
{
"filters": [
{
"key": "search_diplome",
"company_entity_id": null,
"kind": "choice",
"values": ["Agricultural engineering degree"]
},
{
"key": "city",
"company_entity_id": null,
"kind": "text",
"values": ["London"]
},
{
"key": "company_entity",
"company_entity_id": 42,
"kind": "choice",
"values": [7]
}
]
}
This synthetic example assumes field 42 is Department and item 7 is Research. Use the actual client's field IDs; SQL verifies their ownership and availability.
Every selection includes key, company_entity_id and kind. Its remaining
payload depends on the kind:
| Kind | Additional payload example |
|---|---|
choice |
"values": [7, 8] or "values": ["Engineering degree"] |
text |
"values": ["London"] |
number |
"value": 5 |
range |
"start": 50, "end": 100 |
date_range |
"start": "2026-01-01", "end": null |
skills |
"values": [{"id": 91, "level": 3}, {"id": 92, "level": null}] |
Smart Mobility maps Smart Editor ranges to value_start/value_end and choice/text
values to the corresponding widget's value. Skill IDs/levels map to the existing
skill criteria; a null level means no mastery requirement was specified. Numeric
range bounds and dates are inclusive widget bounds, not SQL search predicates.
Both models receive their query and filter context as untrusted user-role data under the shared context policy. They are instructed to handle spelling variants and synonyms without merging distinct concepts, preserve negation and abstain when uncertain.
An occupation, job title or role named in the search, even a generic one such as
"a designer", is a current-occupation criterion. The first call always selects
job_titles when the caller supplied it, whatever other criteria the search
contains; it may also select searchc as a fallback. The second call treats the
occupation as inclusive rather than ambiguous: it selects every job_titles
option that is that occupation or one of its specializations, spelling, case or
language variants, or seniority levels. Other criteria never reduce that
selection. A stated specialization or seniority level stays restrictive, and
occupations that merely share a word are excluded. The occupation goes into
searchc only when no job option matches it.
| Search | Selected job titles |
|---|---|
| "je cherche un designer, en CDI, pas ouvert à la mobilité" | Product Designer, UI Designer, UX Designer, Senior UX Designer (plus contract CDI and internal_mobility 0) |
| "I am looking for a UX designer" | UX Designer, Senior UX Designer |
| "Senior UX designer" | Senior UX Designer |
| "a product manager" | Product Manager, not Product Designer or Product Owner |
The example titles are synthetic; actual options come from the client's job
catalogue. A choice filter accepts at most 100 values, so an occupation matching
more than 100 job titles makes the model response invalid (502).
The server rejects unknown filters, invented choices, duplicate selections, wrong kinds, excessive cardinality, invalid skill levels and invalid/reversed ranges. Free text has type/length/control validation rather than catalogue membership. Semantic interpretation quality still requires client acceptance testing.
{"filters": []} is a successful no-match result. If the first model selects no
filters, no value queries or second model call are executed. A selected catalogue
containing only empty choice lists also skips the second model call. Invalid model
output fails the entire request; it is not silently reduced to a partial success.
Limits and errors¶
The selected catalogue loads at most 10,000 options in total. Each model context
allows at most 200,000 serialized characters. Exceeding either budget returns
413 before the corresponding LLM call; catalogues are never silently truncated.
Tenant resolution, connection setup, metadata/configuration loading and selected
value loading each have a 30-second deadline. Each LLM call has a 60-second deadline.
Selection requests at most 4,000 output tokens; prefill requests at most 8,000.
Both use LLM_PROFILE_LARGE through the existing provider connector. No
feature-specific retry is added.
One MariaDB engine and optional SSH tunnel are reused across both read phases. Each SQL read releases its connection and transaction before the next operation; no SQL transaction spans the first LLM call. The engine and tunnel are closed before the second call, also on early return, failure or cancellation.
| Status | Meaning |
|---|---|
403 |
Body client differs from the authenticated tenant. |
404 |
The registered Smart Mobility client or SQL setting is unknown. |
413 |
The complete catalogue exceeds its processing budget. |
422 |
Invalid input, unavailable Smart Editor field or unsupported field type. |
502 |
Empty, malformed or invalid model selections. |
503 |
Missing SQL source configuration or unavailable SQL database. |
504 |
Catalogue loading or inference timed out. |
500 |
Unexpected processing/provider failure; details remain private. |
Errors expose stable codes, not SQL, credentials, search text or provider responses. Operational logs retain the client and exception class without payload contents.
Query and index review¶
The existing client_config_identity_unique MongoDB index covers tenant lookup
by (clientId, dataSource, environment). MariaDB reads follow legacy primary/foreign
keys: setting IDs, user IDs, reference IDs, entity/item IDs and translation
(translatable_id, locale) pairs. Smart Editor metadata uses a bound primary-key
IN lookup over supplied IDs; item reads use its company-entity foreign key.
Degree/language reads join tenant
users to their existing user foreign keys; project eligibility uses an EXISTS
lookup on the mission foreign key. No query joins user profiles into a returned
talent result.
AIHub does not own the legacy MariaDB schema. No index DDL is added to the request path or to AIHub's PostgreSQL migrations. Legacy ORM mappings establish the join relationships, but do not provide a complete production index snapshot. Actual index coverage and execution plans must be checked on representative deployments before proposing additional composite indexes; the synthetic MariaDB validation does not establish production latency.