@adia-ai/gen-ui 0.8.45 → 0.8.46

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
@@ -1,5 +1,12 @@
1
1
  # Changelog — @adia-ai/gen-ui
2
2
 
3
+ ## [0.8.46] — 2026-08-22
4
+
5
+
6
+ ### Maintenance
7
+ - **`compose/` touched in this release window** (2 file(s), e.g. `free-form-composer/free-form-composer.test.js`) — carried by the entries above.
8
+ - **`corpus/` touched in this release window** (18 file(s), e.g. `corpus/chunk-embeddings.json`) — carried by the entries above.
9
+
3
10
  ## [0.8.45] — 2026-08-20
4
11
 
5
12
  ### Docs
@@ -37,6 +37,40 @@ import { validateAndRepair } from '../../shared/validate-and-repair.js';
37
37
 
38
38
  const MAX_ATTEMPTS = 2;
39
39
 
40
+ // gh#1839 — a stalled upstream connection (TCP established, no data, no
41
+ // error) left `llmAdapter.complete()` calls hanging forever here, which
42
+ // hung the whole eval-diff harness (one intent at a time) and therefore
43
+ // the release pre-flight gate. 120s matches the existing timeout
44
+ // convention in this codebase (`packages/llm/core/server.js`'s
45
+ // `AbortSignal.timeout(120_000)`; `validate/semantic/judge.js`'s
46
+ // AbortController pattern).
47
+ const LLM_CALL_TIMEOUT_MS = 120_000;
48
+
49
+ /**
50
+ * Call `llmAdapter.complete()` with a bounded timeout, retrying once with a
51
+ * fresh connection/signal if the first attempt times out. A single stuck
52
+ * intent must not hang the whole eval run — but a single retry is a
53
+ * reasonable default before giving up on that intent (no prior
54
+ * connection-level retry convention exists in this codebase to follow; the
55
+ * hallucination-guard loop above is a content retry, not a transport one).
56
+ *
57
+ * @param {object} llmAdapter
58
+ * @param {object} args — `{ messages, systemPrompt }`
59
+ * @param {number} [timeoutMs]
60
+ * @returns {Promise<{content: string, [key: string]: any}>}
61
+ */
62
+ export async function completeWithTimeout(llmAdapter, args, timeoutMs = LLM_CALL_TIMEOUT_MS) {
63
+ const attempt = () => llmAdapter.complete({ ...args, signal: AbortSignal.timeout(timeoutMs) });
64
+ try {
65
+ return await attempt();
66
+ } catch (err) {
67
+ const isTimeout = err?.name === 'TimeoutError' || err?.name === 'AbortError';
68
+ if (!isTimeout) throw err;
69
+ // One retry with a fresh signal/connection.
70
+ return await attempt();
71
+ }
72
+ }
73
+
40
74
  /**
41
75
  * Run the free-form composer for a single intent.
42
76
  *
@@ -109,7 +143,7 @@ export async function generateFreeForm({ intent, llmAdapter = null, compositions
109
143
  for (attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
110
144
  let raw;
111
145
  try {
112
- const response = await llmAdapter.complete({
146
+ const response = await completeWithTimeout(llmAdapter, {
113
147
  messages: [{ role: 'user', content: userMessage }],
114
148
  systemPrompt,
115
149
  });
@@ -175,7 +209,7 @@ Note: your previous response wasn't valid JSON. Output ONLY the JSON object —
175
209
  emptyPlanRetryUsed = true;
176
210
  const paraphraseMessage = buildFreeFormParaphraseRetry(intent);
177
211
  try {
178
- const response = await llmAdapter.complete({
212
+ const response = await completeWithTimeout(llmAdapter, {
179
213
  messages: [{ role: 'user', content: paraphraseMessage }],
180
214
  systemPrompt,
181
215
  });