@adia-ai/a2ui-retrieval 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
7
7
 
8
8
  _No pending changes._
9
9
 
10
+ ## [0.3.3] - 2026-05-07
11
+
12
+ **Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` ranges stay at `^0.3.0` (patch-cut asymmetry — caret floats `0.3.x`).
13
+
14
+ ### Changed
15
+
16
+ - **`feedback/dialog-recorder.isRecording()`** — extended to honor
17
+ the new `A2UI_COMPOSE_TRACE` env var (companion to the
18
+ `compose:trace` flag in `@adia-ai/a2ui-compose/core/generator.js`).
19
+ When tracing is on, `isRecording()` returns true so engines
20
+ populate `_debug` payloads (which the trace consumes); without
21
+ this, traces would be empty when feedback recording is off.
22
+ (closes backlog #89)
23
+
24
+ ## [0.3.2] - 2026-05-06
25
+
26
+ **9-package lockstep patch cut to v0.3.2.** All lockstep members share
27
+ one version per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy).
28
+ Internal `@adia-ai/*` dep ranges unchanged at `^0.3.0`.
29
+
30
+ ### No source changes
31
+
32
+ This package's source is byte-identical to v0.3.1. The cut bumps
33
+ version only.
34
+
35
+ ### Changed
36
+
37
+ - `version`: `0.3.1` → `0.3.2`.
38
+
10
39
  ## [0.3.1] - 2026-05-06
11
40
 
12
41
  **9-package lockstep patch cut.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
@@ -70,10 +70,36 @@ async function _loadIndex() {
70
70
  }
71
71
 
72
72
  function _resolveEmbed(providerName, model) {
73
- if (providerName === 'voyage') return voyage({ model });
74
- if (providerName === 'openai') return openai({ model });
75
- const auto = detectProvider();
76
- return auto?.embed || null;
73
+ // The index's recorded (provider, model) is the source of truth — query
74
+ // embeddings MUST be generated by the same model the corpus was indexed
75
+ // with. If they're different models (e.g. voyage-3-lite vs.
76
+ // text-embedding-3-small), the cosine similarity is meaningless, and even
77
+ // same-provider/different-model (e.g. text-embedding-3-small@1536 vs
78
+ // text-embedding-3-large@3072) will emit different dim vectors which
79
+ // cosine() short-circuits to 0 — silent retrieval failure.
80
+ //
81
+ // Fail loud: do NOT silently fall back to a different provider's auto-pick.
82
+ // Caller (available()) returns false → keyword-only retrieval, which is
83
+ // the right behavior, but with a console.warn so the cause is visible.
84
+ let fn = null;
85
+ if (providerName === 'voyage') fn = voyage({ model });
86
+ else if (providerName === 'openai') fn = openai({ model });
87
+ else {
88
+ // No provider recorded in index header (legacy index?). Fall back to
89
+ // auto-detect — this preserves the old behavior for indexes that pre-date
90
+ // the provider/model header convention.
91
+ const auto = detectProvider();
92
+ return auto?.embed || null;
93
+ }
94
+ if (!fn && typeof console !== 'undefined') {
95
+ console.warn(
96
+ `[chunk-embedding-retriever] index was built with provider=${providerName} model=${model}, ` +
97
+ `but the corresponding API key is not set. Embeddings will be unavailable; falling back to ` +
98
+ `keyword-only retrieval. Set the matching API key, or rebuild the index with the available ` +
99
+ `provider via \`npm run build:embeddings:chunks\`.`
100
+ );
101
+ }
102
+ return fn;
77
103
  }
78
104
 
79
105
  export async function available() {
@@ -71,11 +71,31 @@ async function _loadIndex() {
71
71
 
72
72
  /** Resolve the embed function matching the index's provider. */
73
73
  function _resolveEmbed(providerName, model) {
74
- if (providerName === 'voyage') return voyage({ model });
75
- if (providerName === 'openai') return openai({ model });
76
- // Unknown or null fall through to whatever's available.
77
- const auto = detectProvider();
78
- return auto?.embed || null;
74
+ // The index's recorded (provider, model) is the source of truth — query
75
+ // embeddings MUST be generated by the same model the corpus was indexed
76
+ // with. Cross-provider mixing produces meaningless cosine scores; even
77
+ // same-provider/different-model emits different-dim vectors which cosine()
78
+ // short-circuits to 0 — silent retrieval failure. Fail loud (warn + null)
79
+ // when the recorded provider's key is unset, instead of auto-picking a
80
+ // different provider that would silently corrupt similarity rankings.
81
+ // (See chunk-embedding-retriever.js for the parallel implementation.)
82
+ let fn = null;
83
+ if (providerName === 'voyage') fn = voyage({ model });
84
+ else if (providerName === 'openai') fn = openai({ model });
85
+ else {
86
+ // No provider recorded in index header (legacy index) — fall through.
87
+ const auto = detectProvider();
88
+ return auto?.embed || null;
89
+ }
90
+ if (!fn && typeof console !== 'undefined') {
91
+ console.warn(
92
+ `[embedding-retriever] index was built with provider=${providerName} model=${model}, ` +
93
+ `but the corresponding API key is not set. Embeddings will be unavailable; falling back ` +
94
+ `to keyword-only retrieval. Set the matching API key, or rebuild the index with the ` +
95
+ `available provider via \`npm run build:embeddings\`.`
96
+ );
97
+ }
98
+ return fn;
79
99
  }
80
100
 
81
101
  /**
@@ -173,7 +173,13 @@ export async function recordTurn(record) {
173
173
  }
174
174
  }
175
175
 
176
- /** True when logging is on. Useful for guarding expensive capture work. */
176
+ /** True when logging is on. Useful for guarding expensive capture work.
177
+ *
178
+ * Also returns true when `A2UI_COMPOSE_TRACE` is set, since the compose-trace
179
+ * flag in `compose/core/generator.js` consumes the same `_debug` payload —
180
+ * if we don't force-enable here, traces would be empty when the dialog
181
+ * recorder is off (the common case).
182
+ */
177
183
  export function isRecording() {
178
- return ENABLED;
184
+ return ENABLED || !!process.env.A2UI_COMPOSE_TRACE;
179
185
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/a2ui-retrieval",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "AdiaUI A2UI retrieval layer \u2014 catalog lookup, intent classification, domain routing, pattern + anti-pattern matching, clarity + context assembly. Consumed by the compose engine and any A2UI-protocol tooling that needs to reason about user intent against the catalog.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -41,4 +41,4 @@
41
41
  "optionalDependencies": {
42
42
  "@adia-ai/a2ui-corpus": "^0.3.0"
43
43
  }
44
- }
44
+ }