You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Oct 14, 2026. It is now read-only.
Repository navigation
Commit 9b672af
Browse filesBrowse the repository at this point in the historyBrowse files
Tanase Butcaru
committed
fix(data-access): reject sparse type lists, name rejected values, clear unregistered entity types (LLMO-7445)
Copy file name to clipboardExpand all lines: docs/adr/0002-embedding-client-and-semantic-index-utils.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,7 +40,7 @@ Following ADR-0001's test (*true for every consumer → shared; specific to one
40
40
41
41
**Consumers own:**
42
42
- Caching. The durable query-embedding cache (`semantic_query_embedding`) and its key policy (`hash(normalizedText)+model+dims`) live in the data-access helpers/consumer, not the client — staleness/keying is a consumer concern.
43
-
-Model/dimension values come from `SEMANTIC_MATCHING_CONFIG` (`embeddingModel`, `embeddingDims`) in`data-access`, which owns the stored index; consumers import it and pass it as `model` / `dims`. It is a code constant, not env config: the label is persisted on every row and in the cache key, so writer and reader must never disagree, and changing it is a re-embed. `embeddingDims` must match the `vector(N)` columns. The helpers still accept any `model` / `dims`, so a same-dims model change can run the online re-embed below.
43
+
-Passing the model/dimension on every write and search. They pass the shared `SEMANTIC_MATCHING_CONFIG` (`embeddingModel`, `embeddingDims`), which`data-access` defines because it owns the stored index, and do not hardcode the values. It is a code constant, not env config: the label is persisted on every row and in the cache key, so writer and reader agree as long as they run the same data-access version, and changing it is a re-embed plus upgrading both. `embeddingDims` must match the `vector(N)` columns. The helpers still accept any `model` / `dims`, so a same-dims model change can run the online re-embed below.
44
44
- Translating a client failure into an HTTP response (api-service returns 503; the audit-worker write path degrades best-effort).
45
45
46
46
### 4. Semantic-index helpers live in `data-access`, mirroring the URL index
@@ -49,6 +49,8 @@ Following ADR-0001's test (*true for every consumer → shared; specific to one
49
49
50
50
**Type registries.**`OPPORTUNITY_SEMANTIC_SOURCE_TYPES` (kinds of embedded text: `topic`, later `claim`) and `OPPORTUNITY_SEMANTIC_ENTITY_TYPES` (opportunity types, from `OPPORTUNITY_TYPES`) are exported from this layer and are the only values the writer stores and the reader accepts; anything else is a `ValidationError`. The reader takes `sourceTypes` (required, non-empty) and optional `entityTypes` (omitted or empty = all), deduped, so a list can never exceed its registry. The DB does not duplicate the registries: the RPC only rejects an empty source-type list and NULL elements. A team that wants its opportunities searchable adds its source kind and/or opportunity type to the registries, indexes with them, and callers opt in by passing them.
51
51
52
+
Adding an entity type: (1) if the opportunity type is new, add it to `OPPORTUNITY_TYPES` and release `spacecat-shared-utils`; (2) add it to `OPPORTUNITY_SEMANTIC_ENTITY_TYPES`, bump data-access's utils pin, and release data-access; (3) bump data-access in the writer (audit-worker) and the reader (api-service). The writer rejects the type until step 3, so index only after it. A new source kind is the same minus step 1.
53
+
52
54
## Consequences
53
55
54
56
-`gpt-client` gains an embeddings surface and its own `AZURE_EMBEDDING_*` config (documented in its README); the intended cost, not a drawback.
`OPPORTUNITY_SEMANTIC_SOURCE_TYPES` (kinds of embedded text) and `OPPORTUNITY_SEMANTIC_ENTITY_TYPES` (opportunity types) are the registries for `source_type` and `entity_type`: the writer and reader reject any value outside them. To index a new kind or opportunity type, add it to the registry first.
182
182
183
-
`SEMANTIC_MATCHING_CONFIG` (`embeddingModel`, `embeddingDims`) is the embedding generation the writer and reader both use; pass it as `model` / `dims` rather than hardcoding the values. It is a code constant, not env config, so the two sides can never disagree.
183
+
`SEMANTIC_MATCHING_CONFIG` (`embeddingModel`, `embeddingDims`) is the embedding generation the writer and reader both use; pass it as `model` / `dims` rather than hardcoding the values. It is a code constant, not env config, so writer and reader agree as long as both run the same data-access version; changing it means a re-embed plus upgrading both consumers.
184
184
185
185
The batched helpers group their calls internally (search in groups of up to 20 vectors, cache reads/touches in groups of 50 hashes), so callers pass whole lists rather than looping per item.
awaitexpect(call({sourceTypes: ['topic','claim']})).to.be.rejectedWith(ValidationError,'sourceTypes must only contain: topic');
528
538
awaitexpect(call({sourceTypes: ['*']})).to.be.rejectedWith(ValidationError,'sourceTypes must only contain');
529
539
awaitexpect(call({entityTypes: 'cited-analysis'})).to.be.rejectedWith(ValidationError,'entityTypes must be an array');
530
-
awaitexpect(call({entityTypes: ['cited-analysis',null]})).to.be.rejectedWith(ValidationError,'entityTypes must only contain: cited-analysis, reddit-analysis, youtube-analysis');
540
+
awaitexpect(call({entityTypes: ['cited-analysis',null]})).to.be.rejectedWith(ValidationError,'entityTypes must only contain: cited-analysis, reddit-analysis, youtube-analysis (got [null])');
0 commit comments