fix(categories): resolve namespaced entities for HANA backfill + seed classifier descriptions - #2095
Conversation
…A + seed classifier descriptions
The category backfill (scripts/backfill-categories.cjs) and classifier
persist() queried bare short-name entity strings ('Tutorials',
'TutorialCategories'). Those resolve on local SQLite but emit unqualified
TUTORIALS / TUTORIALCATEGORIES SQL against HANA (real tables are
COM_SAP_DEVELOPERS_IMS_*), so the backfill died on its first query via
`cds bind --exec` and only its SQLite unit test ever passed.
- backfill: resolve ims entity OBJECTS via a linked model (resolveImsEntities,
falling back to cds.linked(cds.load('*')) when cds.model is unset in the
standalone runner), and SELECT with explicit .columns('ID').
- classifier persist(): resolve the junction via cds.entities(ns)[junction]
instead of a bare string (loadItemText already resolved this way).
Second, independent blocker: Category.seedDescription was unseeded (CSV ships
only ID/slug/label/sortOrder), so the embedding classifier degraded to
LLM-only. Add baseline seed texts (category-seed-descriptions-defaults.js) and
an idempotent, non-destructive boot-seed (seed-category-descriptions.js, wired
into cds.on('served'), VITEST-gated, fills only empty rows). Kept out of CSV so
deploys can't full-replace the admin-editable column.
Tests: unit (defaults integrity + idempotency + non-destructive) and hybrid
(load+link resolution reaches the namespaced HANA tables; seed runs on HANA).
…eddings category-seed-embeddings.js called embed() with no model arg in loadAll, recomputeStale, and embedAdHoc, so AzureOpenAiEmbeddingClient(undefined) threw "Cannot read properties of undefined (reading 'modelName')" on every classify — the embedding path was dead and every item fell back to LLM-only, even with seedDescriptions populated. Same #2001 class of bug as the freshness pipeline. Resolve the model via resolveEmbeddingSettings() like every other embed() caller (relevance-seed-embeddings.js is the direct analog), memoized at module level so a bulk backfill doesn't re-read ChatSettings per item. Verified against DEV: path=embedding now fires. Both category-seed-embeddings.js and chat-settings-resolver.js are already in the srv-qa cp list, and the resolver imports only @sap/cds → no srv-qa boot-crash risk.
DEV backfill — verified against real HANARan the fixed backfill via Coverage on DEV:
Missions/groups classified almost entirely via the embedding path (0 LLM calls for missions). The remaining gap is items whose cosine score falls below Not a code defect — the fix is correct; the tutorial tail is bounded by AI Core LLM throughput. |
Problem
Two independent bugs kept the Admin UI Categories facet empty on DEV/PROD:
Backfill dies on HANA.
scripts/backfill-categories.cjsand the classifierpersist()queried bare short-name entity strings (Tutorials,TutorialCategories). Those resolve on local SQLite but emit unqualifiedTUTORIALS/TUTORIALCATEGORIESSQL against HANA (real tables areCOM_SAP_DEVELOPERS_IMS_*). Running the sanctioned backfill viacds bind --execfailed on its first query (Could not find table/view TUTORILS). Only the SQLite-based unit test ever passed, so the bug was invisible. In-service self-heal worked (handler namespace context resolves bare names) — hence the lone stray junction row.seedDescriptionunseeded.Categories.csvships onlyID/slug/label/sortOrder, so all 8 category rows had emptyseedDescription→ the embedding classifier degraded to LLM-only everywhere (~1400 AI Core LLM calls, lower quality).Fix
resolveImsEntities, falling back tocds.linked(cds.load('*'))whencds.modelis unset in the standalone runner) andSELECTwith explicit.columns('ID').persist(): resolve the junction viacds.entities(ns)[junction]instead of a bare string (loadItemTextalready did this).category-seed-descriptions-defaults.js+ an idempotent, non-destructive boot-seed (seed-category-descriptions.js, wired intocds.on('served'), VITEST-gated, fills only empty rows). Deliberately not in CSV — a CSV column would full-replace the admin-editable field on every deploy.Tests
test/unit/seed-category-descriptions.test.js): defaults integrity (keys match shipped slugs), idempotency, non-destructive (never clobbers admin edits), self-heal.test/hybrid/backfill-categories-hana-resolution.test.js, guarded byHYBRID_TESTS=true+isSafeForWrites()): the load+link resolution path reaches the namespaced HANA tables (.name === com.sap.developers.ims.<Name>, resolved-object SELECT executes) — the exact standalone branch the bare-string code broke on; and the seed runs against real HANA.Verified against DEV HANA
seedDescriptionvalues.Targets DEV per branching policy.