@garrix82/reactgenie-dsl 1.0.4 → 1.0.6

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.
@@ -1,8 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContextAwareDescriptorPromptGen = exports.DescriptorPromptGen = exports.BasicPromptGen = exports.ExampleParse = void 0;
3
+ exports.ContextAwareDescriptorPromptGen = exports.DescriptorPromptGen = exports.BasicPromptGen = exports.normalizeExampleParses = exports.normalizeExampleParse = exports.ExampleParse = exports.buildExampleId = exports.normalizeExampleLocale = void 0;
4
4
  const prompt_res_1 = require("./prompt-res");
5
5
  const context_selector_1 = require("./context-selector");
6
+ function isLocalizedExampleUtterance(value) {
7
+ if (!value || typeof value !== "object") {
8
+ return false;
9
+ }
10
+ const candidate = value;
11
+ return (typeof candidate.en === "string" &&
12
+ candidate.en.trim().length > 0 &&
13
+ typeof candidate.de === "string" &&
14
+ candidate.de.trim().length > 0);
15
+ }
16
+ function normalizeExampleLocale(locale) {
17
+ return String(locale || "").trim().toLowerCase().startsWith("de") ? "de" : "en";
18
+ }
19
+ exports.normalizeExampleLocale = normalizeExampleLocale;
20
+ function stableHash(seed) {
21
+ let hash = 2166136261;
22
+ for (let index = 0; index < seed.length; index += 1) {
23
+ hash ^= seed.charCodeAt(index);
24
+ hash = Math.imul(hash, 16777619);
25
+ }
26
+ return (hash >>> 0).toString(16);
27
+ }
28
+ function buildExampleId(exampleParsed) {
29
+ return `example_${stableHash((0, context_selector_1.normalize)(exampleParsed))}`;
30
+ }
31
+ exports.buildExampleId = buildExampleId;
6
32
  const DEFAULT_PROMPT_SELECTION_CONFIG = {
7
33
  enabled: true,
8
34
  topNClassDescriptions: 12,
@@ -11,18 +37,52 @@ const DEFAULT_PROMPT_SELECTION_CONFIG = {
11
37
  olderSummaryMaxChars: 600,
12
38
  cacheSize: 64,
13
39
  };
40
+ const DEFAULT_HYBRID_RETRIEVAL_CONFIG = {
41
+ lexicalWeight: 0.25,
42
+ embeddingWeight: 0.75,
43
+ lexicalTopK: 20,
44
+ embeddingTopK: 20,
45
+ mergedTopK: 16,
46
+ maxExamplesPerRoot: 2,
47
+ screenLocalVisibleBoost: 0.12,
48
+ screenIndependentBoost: 0.05,
49
+ deicticTapBoost: 0.2,
50
+ deicticVisibleBoost: 0.1,
51
+ unrelatedScreenLocalPenalty: 0.2,
52
+ unrelatedScreenIndependentPenalty: 0.2,
53
+ recentRootBoost: 0.06,
54
+ ambiguityThreshold: 0.05,
55
+ };
14
56
  class ExampleParse {
15
- constructor(user_utterance, example_parsed) {
16
- this.user_utterance = user_utterance;
17
- this.example_parsed = example_parsed;
57
+ constructor(userUtterance, exampleParsed, exampleId) {
58
+ this.userUtterance = userUtterance;
59
+ this.exampleParsed = exampleParsed;
60
+ this.exampleId = exampleId || buildExampleId(exampleParsed);
61
+ }
62
+ getUserUtterance(locale = "en") {
63
+ return this.userUtterance[locale];
18
64
  }
19
65
  }
20
66
  exports.ExampleParse = ExampleParse;
67
+ function normalizeExampleParse(example) {
68
+ if (example instanceof ExampleParse) {
69
+ return example;
70
+ }
71
+ if (!isLocalizedExampleUtterance(example.userUtterance)) {
72
+ throw new Error("Localized examples must define both en and de utterances.");
73
+ }
74
+ return new ExampleParse(example.userUtterance, example.exampleParsed);
75
+ }
76
+ exports.normalizeExampleParse = normalizeExampleParse;
77
+ function normalizeExampleParses(examples) {
78
+ return examples.map((example) => normalizeExampleParse(example));
79
+ }
80
+ exports.normalizeExampleParses = normalizeExampleParses;
21
81
  class BasicPromptGen {
22
82
  constructor(class_descriptions, examples, extraPrompt) {
23
83
  this.class_descriptions = class_descriptions;
24
- this.examples = examples;
25
84
  this.extraPrompt = extraPrompt;
85
+ this.examples = normalizeExampleParses(examples);
26
86
  }
27
87
  prompt_basic() {
28
88
  let prompt = `${prompt_res_1.class_prequel}${prompt_res_1.pre_section_separator}` +
@@ -40,14 +100,14 @@ class BasicPromptGen {
40
100
  zero_shot_prompt_basic() {
41
101
  return (0, prompt_res_1.parseCodePrompt)(this.prompt_basic());
42
102
  }
43
- prompt(user_utterance) {
103
+ prompt(user_utterance, english_translation) {
44
104
  let prompt = this.prompt_basic();
45
- prompt += `${(0, prompt_res_1.user_interaction_prompt)(user_utterance)}`;
105
+ prompt += `${(0, prompt_res_1.user_interaction_prompt)(user_utterance, english_translation)}`;
46
106
  return prompt;
47
107
  }
48
- zero_shot_prompt(user_utterance) {
108
+ zero_shot_prompt(user_utterance, english_translation) {
49
109
  let prompt = this.zero_shot_prompt_basic();
50
- prompt += `${(0, prompt_res_1.user_interaction_prompt)(user_utterance)}`;
110
+ prompt += `${(0, prompt_res_1.user_interaction_prompt)(user_utterance, english_translation)}`;
51
111
  return prompt;
52
112
  }
53
113
  response_prompt(user_utterance, parsed, result) {
@@ -63,21 +123,45 @@ class DescriptorPromptGen extends BasicPromptGen {
63
123
  }
64
124
  exports.DescriptorPromptGen = DescriptorPromptGen;
65
125
  class ContextAwareDescriptorPromptGen {
66
- constructor(class_descriptors, examples, extraPrompt, config) {
126
+ constructor(class_descriptors, examples, extraPrompt, config, retrievalOptions) {
67
127
  this.extraPrompt = extraPrompt;
68
128
  this.recentTurns = [];
69
129
  this.olderTurnsSummary = "";
70
130
  this.runtimeUiContext = null;
71
131
  this.allClassNames = class_descriptors.map((c) => c.className);
72
132
  this.allClassDescriptions = class_descriptors.map((c) => c.description());
73
- this.allExamples = examples;
74
- this.allExampleRootClasses = examples.map((example) => ContextAwareDescriptorPromptGen.extractRootClass(example.example_parsed));
133
+ this.allClassDescriptionByName = new Map(this.allClassNames.map((className, index) => [
134
+ className,
135
+ this.allClassDescriptions[index],
136
+ ]));
137
+ this.allExamples = ContextAwareDescriptorPromptGen.assignExampleIds(normalizeExampleParses(examples));
138
+ this.exampleMetadata = this.allExamples.map((example) => this.buildExampleMetadata(example));
75
139
  this.config = {
76
140
  ...DEFAULT_PROMPT_SELECTION_CONFIG,
77
141
  ...config,
78
142
  };
143
+ this.retrievalConfig = {
144
+ ...DEFAULT_HYBRID_RETRIEVAL_CONFIG,
145
+ ...(retrievalOptions?.config || {}),
146
+ };
147
+ this.embeddingProvider = retrievalOptions?.embeddingProvider;
148
+ this.groundingProvider = retrievalOptions?.groundingProvider;
149
+ this.embeddingIndexByExampleKey = new Map((retrievalOptions?.embeddingIndex || []).map((entry) => [
150
+ ContextAwareDescriptorPromptGen.exampleLookupKey(entry.exampleId, entry.locale),
151
+ entry,
152
+ ]));
153
+ this.embeddingIndexByParsedKey = new Map((retrievalOptions?.embeddingIndex || []).map((entry) => [
154
+ ContextAwareDescriptorPromptGen.parsedLookupKey(entry.exampleParsed, entry.locale),
155
+ entry,
156
+ ]));
157
+ this.groundingCache = new context_selector_1.BoundedCache(this.config.cacheSize);
79
158
  this.selectionCache = new context_selector_1.BoundedCache(this.config.cacheSize);
80
159
  }
160
+ getEmbeddingIndexEntry(example) {
161
+ const locale = this.getActiveLocale();
162
+ return (this.embeddingIndexByExampleKey.get(ContextAwareDescriptorPromptGen.exampleLookupKey(example.exampleId, locale)) ||
163
+ this.embeddingIndexByParsedKey.get(ContextAwareDescriptorPromptGen.parsedLookupKey(example.exampleParsed, locale)));
164
+ }
81
165
  setRuntimeContext(context) {
82
166
  if (!context) {
83
167
  this.runtimeUiContext = null;
@@ -87,8 +171,54 @@ class ContextAwareDescriptorPromptGen {
87
171
  tapClassName: context.tapClassName,
88
172
  visibleClassNames: Array.from(new Set((context.visibleClassNames || []).filter((entry) => !!entry))),
89
173
  isDeicticUtterance: !!context.isDeicticUtterance,
174
+ englishTranslation: context.englishTranslation?.trim() || undefined,
175
+ locale: normalizeExampleLocale(context.locale),
176
+ awaitEnglishTranslation: context.awaitEnglishTranslation,
90
177
  };
91
178
  }
179
+ async resolveEnglishTranslation(user_utterance, english_translation) {
180
+ const explicit = english_translation?.trim() || this.runtimeUiContext?.englishTranslation?.trim() || "";
181
+ if (explicit) {
182
+ if (this.runtimeUiContext && explicit !== this.runtimeUiContext.englishTranslation) {
183
+ this.runtimeUiContext = {
184
+ ...this.runtimeUiContext,
185
+ englishTranslation: explicit,
186
+ };
187
+ }
188
+ return explicit;
189
+ }
190
+ const awaitTranslation = this.runtimeUiContext?.awaitEnglishTranslation;
191
+ if (!awaitTranslation || this.getActiveLocale() === "en") {
192
+ return undefined;
193
+ }
194
+ const waitStartedAt = Date.now();
195
+ try {
196
+ const translated = await awaitTranslation(user_utterance);
197
+ const trimmed = translated?.trim() || undefined;
198
+ if (trimmed && this.runtimeUiContext) {
199
+ this.runtimeUiContext = {
200
+ ...this.runtimeUiContext,
201
+ englishTranslation: trimmed,
202
+ };
203
+ }
204
+ if (trimmed) {
205
+ console.info("[ReactGenieDSL] async translation resolved for prompt preparation", {
206
+ locale: this.getActiveLocale(),
207
+ waitedMs: Date.now() - waitStartedAt,
208
+ utterance: user_utterance,
209
+ });
210
+ }
211
+ return trimmed;
212
+ }
213
+ catch {
214
+ console.warn("[ReactGenieDSL] async translation failed during prompt preparation", {
215
+ locale: this.getActiveLocale(),
216
+ waitedMs: Date.now() - waitStartedAt,
217
+ utterance: user_utterance,
218
+ });
219
+ return undefined;
220
+ }
221
+ }
92
222
  static normalizeClassName(className) {
93
223
  return className.replace(/\[\]$/g, "");
94
224
  }
@@ -96,8 +226,67 @@ class ContextAwareDescriptorPromptGen {
96
226
  const match = parsedExpression.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*\./);
97
227
  return match?.[1] || "";
98
228
  }
229
+ static exampleLookupKey(exampleId, locale) {
230
+ return `${exampleId}::${locale}`;
231
+ }
232
+ static parsedLookupKey(exampleParsed, locale) {
233
+ return `${(0, context_selector_1.normalize)(exampleParsed)}::${locale}`;
234
+ }
235
+ static assignExampleIds(examples) {
236
+ const collisionCount = new Map();
237
+ return examples.map((example) => {
238
+ const baseId = buildExampleId(example.exampleParsed);
239
+ const collisionIndex = collisionCount.get(baseId) || 0;
240
+ collisionCount.set(baseId, collisionIndex + 1);
241
+ const exampleId = collisionIndex === 0 ? baseId : `${baseId}_${collisionIndex + 1}`;
242
+ return new ExampleParse(example.userUtterance, example.exampleParsed, exampleId);
243
+ });
244
+ }
99
245
  static isScreenIndependentExample(example) {
100
- return !/\b(?:AllCurrent|Current|getCurrent|current)\s*\(/.test(example.example_parsed);
246
+ return !/\b(?:AllCurrent|Current|getCurrent|current)\s*\(/.test(example.exampleParsed);
247
+ }
248
+ static isScreenLocalExample(example) {
249
+ return /\b(?:AllCurrent|Current|getCurrent|current)\s*\(/.test(example.exampleParsed);
250
+ }
251
+ buildExampleMetadata(example) {
252
+ const rootClass = ContextAwareDescriptorPromptGen.extractRootClass(example.exampleParsed);
253
+ const helperClasses = this.allClassNames.filter((className) => className !== rootClass &&
254
+ new RegExp(`\\b${className}\\b`).test(example.exampleParsed));
255
+ return {
256
+ exampleId: example.exampleId,
257
+ rootClass,
258
+ helperClasses,
259
+ isScreenLocal: ContextAwareDescriptorPromptGen.isScreenLocalExample(example),
260
+ isScreenIndependent: ContextAwareDescriptorPromptGen.isScreenIndependentExample(example),
261
+ };
262
+ }
263
+ getActiveLocale() {
264
+ return normalizeExampleLocale(this.runtimeUiContext?.locale);
265
+ }
266
+ async prepareForUtterance(user_utterance) {
267
+ const resolvedEnglishTranslation = await this.resolveEnglishTranslation(user_utterance);
268
+ const grounding = await this.prepareGrounding(user_utterance, resolvedEnglishTranslation);
269
+ if (!this.config.enabled) {
270
+ return;
271
+ }
272
+ const retrievalQuery = this.resolveRetrievalQuery(user_utterance, resolvedEnglishTranslation, grounding);
273
+ const cacheKey = this.selectionCacheKeyForUtterance(user_utterance, retrievalQuery);
274
+ if (this.selectionFromCache(cacheKey)) {
275
+ return;
276
+ }
277
+ let selection;
278
+ try {
279
+ selection = await this.computeSelectionAsync(user_utterance, retrievalQuery);
280
+ }
281
+ catch (error) {
282
+ console.warn("[ReactGenieDSL] embedding retrieval failed during prompt preparation", {
283
+ utterance: user_utterance,
284
+ locale: this.getActiveLocale(),
285
+ error: error instanceof Error ? error.message : String(error),
286
+ });
287
+ selection = this.computeSelectionSync(user_utterance, retrievalQuery);
288
+ }
289
+ this.cacheSelection(cacheKey, selection);
101
290
  }
102
291
  getAllowedRootClassesForDeicticContext() {
103
292
  const context = this.runtimeUiContext;
@@ -161,20 +350,134 @@ class ContextAwareDescriptorPromptGen {
161
350
  lines.push('- user: "update this status to done" parsed: Task.Current().setStatus(value: "done")');
162
351
  return lines.join("\n");
163
352
  }
164
- buildPromptBasic(classDescriptions, examples, historyContext, uiContext) {
353
+ static joinUniqueNormalizedLines(values) {
354
+ const seen = new Set();
355
+ const ordered = [];
356
+ values.forEach((value) => {
357
+ const trimmed = value?.trim();
358
+ if (!trimmed) {
359
+ return;
360
+ }
361
+ const key = (0, context_selector_1.normalize)(trimmed);
362
+ if (!key || seen.has(key)) {
363
+ return;
364
+ }
365
+ seen.add(key);
366
+ ordered.push(trimmed);
367
+ });
368
+ return ordered.join("\n");
369
+ }
370
+ groundingCacheKeyForUtterance(user_utterance, english_translation) {
371
+ const context = this.runtimeUiContext;
372
+ const base = ContextAwareDescriptorPromptGen.joinUniqueNormalizedLines([
373
+ english_translation,
374
+ context?.englishTranslation,
375
+ user_utterance,
376
+ ]);
377
+ if (!context) {
378
+ return `${base}::locale=${this.getActiveLocale()}`;
379
+ }
380
+ const visibleKey = [...context.visibleClassNames].sort().join(",");
381
+ return `${base}::locale=${this.getActiveLocale()}::tap=${context.tapClassName || ""}::visible=${visibleKey}::deictic=${context.isDeicticUtterance ? 1 : 0}`;
382
+ }
383
+ async prepareGrounding(user_utterance, english_translation) {
384
+ if (!this.groundingProvider) {
385
+ console.info("[ReactGenieDSL] grounding skipped: no grounding provider configured", {
386
+ utterance: user_utterance,
387
+ locale: this.getActiveLocale(),
388
+ });
389
+ return null;
390
+ }
391
+ const cacheKey = this.groundingCacheKeyForUtterance(user_utterance, english_translation);
392
+ const cached = this.groundingCache.get(cacheKey);
393
+ if (cached !== undefined) {
394
+ console.info("[ReactGenieDSL] grounding cache hit", {
395
+ utterance: user_utterance,
396
+ locale: this.getActiveLocale(),
397
+ entityHintCount: cached?.entityHints?.length || 0,
398
+ retrievalQuery: cached?.retrievalQuery || null,
399
+ });
400
+ return cached;
401
+ }
402
+ const grounded = (await this.groundingProvider.ground({
403
+ utterance: user_utterance,
404
+ englishTranslation: this.runtimeUiContext?.englishTranslation || english_translation,
405
+ locale: this.getActiveLocale(),
406
+ runtimeUiContext: this.runtimeUiContext,
407
+ })) || null;
408
+ console.info("[ReactGenieDSL] grounding prepared", {
409
+ utterance: user_utterance,
410
+ locale: this.getActiveLocale(),
411
+ entityHintCount: grounded?.entityHints?.length || 0,
412
+ retrievalQuery: grounded?.retrievalQuery || null,
413
+ });
414
+ this.groundingCache.set(cacheKey, grounded);
415
+ return grounded;
416
+ }
417
+ getCachedGrounding(user_utterance, english_translation) {
418
+ if (!this.groundingProvider) {
419
+ return null;
420
+ }
421
+ const cacheKey = this.groundingCacheKeyForUtterance(user_utterance, english_translation);
422
+ const cached = this.groundingCache.get(cacheKey);
423
+ return cached === undefined ? null : cached;
424
+ }
425
+ buildGroundedEntityHintsBlock(grounding) {
426
+ const hints = grounding?.entityHints || [];
427
+ if (hints.length === 0) {
428
+ console.info("[ReactGenieDSL] prompt grounding block omitted: no entity hints");
429
+ return "";
430
+ }
431
+ const ground_promt = "Grounding Hints:\n" +
432
+ "// Based on the provided domain grounding hints, identify the most relevant entity type and label that aligns with the user query.\n" +
433
+ "// If you use a grounded candidate for a DSL filter, copy only the candidate label text exactly as the filter value.\n" +
434
+ "// Do not include list punctuation such as trailing semicolons or score text in the filter value.\n" +
435
+ "// Do not shorten, paraphrase, or partially match the label.\n";
436
+ const lines = [ground_promt];
437
+ hints.forEach((hint) => {
438
+ if (!hint?.entityType || !Array.isArray(hint.candidates) || hint.candidates.length === 0) {
439
+ return;
440
+ }
441
+ if (lines[lines.length - 1] !== ground_promt) {
442
+ lines.push("");
443
+ }
444
+ lines.push(`entity_type: ${hint.entityType}`);
445
+ hint.candidates.forEach((candidate) => {
446
+ const label = candidate.canonicalLabel?.trim() || candidate.label?.trim();
447
+ if (!label) {
448
+ return;
449
+ }
450
+ const score = typeof candidate.score === "number" && Number.isFinite(candidate.score)
451
+ ? candidate.score.toFixed(2)
452
+ : "n/a";
453
+ lines.push(`- label: ${label}; score: ${score}.`);
454
+ });
455
+ });
456
+ const block = lines.join("\n");
457
+ console.info("[ReactGenieDSL] prompt grounding block injected", {
458
+ entityHintCount: hints.length,
459
+ retrievalQuery: grounding?.retrievalQuery || null,
460
+ preview: block.slice(0, 240),
461
+ });
462
+ return block;
463
+ }
464
+ buildPromptBasic(classDescriptions, examples, historyContext, uiContext, grounding, englishTranslation) {
165
465
  let prompt = `${prompt_res_1.class_prequel}${prompt_res_1.pre_section_separator}` +
166
466
  `${prompt_res_1.array_definition}${prompt_res_1.class_separator}` +
167
467
  `${classDescriptions.join(prompt_res_1.class_separator)}${prompt_res_1.post_section_separator}` +
168
468
  `${prompt_res_1.example_prequel}${prompt_res_1.pre_section_separator}` +
169
- `${(0, prompt_res_1.example_parses)(examples)}${prompt_res_1.post_section_separator}` +
170
- `${prompt_res_1.accessor_parameter_rules}${prompt_res_1.post_section_separator}` +
171
- `${prompt_res_1.user_interaction_prequel}${prompt_res_1.pre_section_separator}`;
469
+ `${(0, prompt_res_1.example_parses)(examples, this.getActiveLocale())}${prompt_res_1.post_section_separator}`;
470
+ if (uiContext) {
471
+ prompt += `${uiContext}${prompt_res_1.post_section_separator}`;
472
+ }
172
473
  if (historyContext) {
173
474
  prompt += `${historyContext}${prompt_res_1.post_section_separator}`;
174
475
  }
175
- if (uiContext) {
176
- prompt += `${uiContext}${prompt_res_1.post_section_separator}`;
476
+ const groundedHints = this.buildGroundedEntityHintsBlock(grounding);
477
+ if (groundedHints) {
478
+ prompt += `${groundedHints}${prompt_res_1.post_section_separator}`;
177
479
  }
480
+ prompt += `${prompt_res_1.user_interaction_prequel}${prompt_res_1.pre_section_separator}`;
178
481
  if (this.extraPrompt !== undefined) {
179
482
  prompt += `${this.extraPrompt}${prompt_res_1.pre_section_separator}`;
180
483
  }
@@ -229,34 +532,56 @@ class ContextAwareDescriptorPromptGen {
229
532
  }
230
533
  this.selectionCache.set(key, selection);
231
534
  }
232
- selectionCacheKeyForUtterance(user_utterance) {
535
+ resolveRetrievalQuery(user_utterance, english_translation, grounding) {
536
+ const translation = (this.runtimeUiContext?.englishTranslation ||
537
+ english_translation)?.trim();
538
+ return (ContextAwareDescriptorPromptGen.joinUniqueNormalizedLines([
539
+ grounding?.retrievalQuery,
540
+ translation,
541
+ user_utterance,
542
+ ]) || user_utterance);
543
+ }
544
+ selectionCacheKeyForUtterance(user_utterance, retrievalQuery = user_utterance) {
233
545
  const context = this.runtimeUiContext;
234
546
  if (!context) {
235
- return user_utterance;
547
+ return retrievalQuery;
236
548
  }
237
549
  const preferredVisibleRoots = this.getPreferredRootClassesForVisibleScreenContext();
550
+ const visibleKey = [...context.visibleClassNames].sort().join(",");
238
551
  if (!context.isDeicticUtterance && preferredVisibleRoots.size === 0) {
239
- return user_utterance;
552
+ return `${retrievalQuery}::locale=${this.getActiveLocale()}`;
240
553
  }
241
- const visibleKey = [...context.visibleClassNames].sort().join(",");
242
- return `${user_utterance}::tap=${context.tapClassName || ""}::visible=${visibleKey}::deictic=${context.isDeicticUtterance ? 1 : 0}`;
554
+ return `${retrievalQuery}::locale=${this.getActiveLocale()}::tap=${context.tapClassName || ""}::visible=${visibleKey}::deictic=${context.isDeicticUtterance ? 1 : 0}`;
243
555
  }
244
- buildSelectionFromExampleIndexes(exampleIndexes) {
556
+ buildSelectionFromExampleIndexes(exampleIndexes, rankedEntries) {
245
557
  if (exampleIndexes.length === 0) {
246
558
  return {
247
559
  classDescriptions: [],
248
560
  examples: [],
249
561
  };
250
562
  }
251
- const uniqueExampleIndexes = Array.from(new Set(exampleIndexes)).sort((a, b) => a - b);
252
- const examples = uniqueExampleIndexes.map((index) => this.allExamples[index]);
253
- const exampleRootClasses = new Set(uniqueExampleIndexes
254
- .map((index) => this.allExampleRootClasses[index])
255
- .filter((className) => className.length > 0));
563
+ const uniqueExampleIndexes = Array.from(new Set(exampleIndexes));
564
+ const helperClasses = new Set();
565
+ uniqueExampleIndexes.forEach((index) => {
566
+ this.exampleMetadata[index].helperClasses.forEach((className) => helperClasses.add(className));
567
+ });
568
+ const helperExampleIndexes = rankedEntries
569
+ .filter((entry) => helperClasses.has(this.exampleMetadata[entry.index].rootClass))
570
+ .map((entry) => entry.index);
571
+ const expandedExampleIndexes = Array.from(new Set([...uniqueExampleIndexes, ...helperExampleIndexes]));
572
+ const examples = expandedExampleIndexes.map((index) => this.allExamples[index]);
573
+ const requiredClassNames = new Set();
574
+ expandedExampleIndexes.forEach((index) => {
575
+ const metadata = this.exampleMetadata[index];
576
+ if (metadata.rootClass) {
577
+ requiredClassNames.add(metadata.rootClass);
578
+ }
579
+ metadata.helperClasses.forEach((className) => requiredClassNames.add(className));
580
+ });
256
581
  const classDescriptions = this.allClassNames
257
- .map((className, index) => ({ className, index }))
258
- .filter((entry) => exampleRootClasses.has(entry.className))
259
- .map((entry) => this.allClassDescriptions[entry.index]);
582
+ .filter((className) => requiredClassNames.has(className))
583
+ .map((className) => this.allClassDescriptionByName.get(className))
584
+ .filter(Boolean);
260
585
  return {
261
586
  classDescriptions,
262
587
  examples,
@@ -267,90 +592,202 @@ class ContextAwareDescriptorPromptGen {
267
592
  .filter((entry) => ContextAwareDescriptorPromptGen.isScreenIndependentExample(this.allExamples[entry.index]))
268
593
  .slice(0, this.config.topMExamples)
269
594
  .map((entry) => entry.index);
270
- return this.buildSelectionFromExampleIndexes(rescuedExampleIndexes);
595
+ return this.buildSelectionFromExampleIndexes(rescuedExampleIndexes, rankedExamples);
271
596
  }
272
- computeSelection(user_utterance) {
597
+ getRecentSuccessfulRootClasses() {
598
+ return new Set(this.recentTurns
599
+ .filter((turn) => turn.success && turn.parsed)
600
+ .map((turn) => ContextAwareDescriptorPromptGen.extractRootClass(turn.parsed || ""))
601
+ .filter((className) => className.length > 0));
602
+ }
603
+ computeLexicalRankings(user_utterance, useHybridWeights = false) {
604
+ const rankedExamples = (0, context_selector_1.rankByQuery)(user_utterance, this.allExamples, (example) => `${example.getUserUtterance(this.getActiveLocale())}\n${example.exampleParsed}`, this.retrievalConfig.lexicalTopK);
605
+ const normalized = (0, context_selector_1.minMaxNormalizeScores)(rankedExamples);
606
+ return normalized.map((entry) => {
607
+ const rankedEntry = this.toRankedEntry(entry.index, entry.score, 0);
608
+ return {
609
+ ...rankedEntry,
610
+ score: useHybridWeights ? rankedEntry.score : entry.score,
611
+ };
612
+ });
613
+ }
614
+ computeEmbeddingRankings(utteranceEmbedding) {
615
+ const scores = this.allExamples
616
+ .map((example, index) => {
617
+ const embeddingEntry = this.getEmbeddingIndexEntry(example);
618
+ if (!embeddingEntry) {
619
+ return { index, score: 0 };
620
+ }
621
+ return {
622
+ index,
623
+ score: (0, context_selector_1.cosineSimilarity)(utteranceEmbedding, embeddingEntry.embedding),
624
+ };
625
+ });
626
+ const normalized = (0, context_selector_1.minMaxNormalizeScores)(scores)
627
+ .sort((a, b) => (b.score !== a.score ? b.score - a.score : a.index - b.index))
628
+ .slice(0, this.retrievalConfig.embeddingTopK);
629
+ return normalized.map((entry) => this.toRankedEntry(entry.index, 0, entry.score));
630
+ }
631
+ toRankedEntry(index, lexicalScore, embeddingScore) {
632
+ const metadata = this.exampleMetadata[index];
633
+ return {
634
+ index,
635
+ score: lexicalScore * this.retrievalConfig.lexicalWeight +
636
+ embeddingScore * this.retrievalConfig.embeddingWeight,
637
+ lexicalScore,
638
+ embeddingScore,
639
+ rootClass: metadata.rootClass,
640
+ helperClasses: metadata.helperClasses,
641
+ isScreenLocal: metadata.isScreenLocal,
642
+ isScreenIndependent: metadata.isScreenIndependent,
643
+ };
644
+ }
645
+ rerankEntries(entries) {
646
+ const context = this.runtimeUiContext;
647
+ const recentRoots = this.getRecentSuccessfulRootClasses();
648
+ const visibleRoots = new Set((context?.visibleClassNames || []).map((className) => ContextAwareDescriptorPromptGen.normalizeClassName(className)));
649
+ const tapRoot = context?.tapClassName
650
+ ? ContextAwareDescriptorPromptGen.normalizeClassName(context.tapClassName)
651
+ : undefined;
652
+ return entries
653
+ .map((entry) => {
654
+ let score = entry.score;
655
+ if (context?.isDeicticUtterance) {
656
+ if (tapRoot && entry.rootClass === tapRoot) {
657
+ score += this.retrievalConfig.deicticTapBoost;
658
+ }
659
+ else if (visibleRoots.has(entry.rootClass)) {
660
+ score += this.retrievalConfig.deicticVisibleBoost;
661
+ }
662
+ else if (entry.isScreenIndependent && (tapRoot || visibleRoots.size > 0)) {
663
+ score -= this.retrievalConfig.unrelatedScreenIndependentPenalty;
664
+ }
665
+ else {
666
+ score -= this.retrievalConfig.deicticTapBoost;
667
+ }
668
+ }
669
+ else {
670
+ const singleVisibleRoot = visibleRoots.size === 1 ? Array.from(visibleRoots)[0] : undefined;
671
+ if (entry.isScreenLocal && visibleRoots.has(entry.rootClass)) {
672
+ score += this.retrievalConfig.screenLocalVisibleBoost;
673
+ if (singleVisibleRoot && entry.rootClass === singleVisibleRoot) {
674
+ score += 0.2;
675
+ }
676
+ }
677
+ else if (entry.isScreenLocal && visibleRoots.size > 0) {
678
+ score -= this.retrievalConfig.unrelatedScreenLocalPenalty;
679
+ if (singleVisibleRoot && entry.rootClass !== singleVisibleRoot) {
680
+ score -= 0.6;
681
+ }
682
+ }
683
+ if (entry.isScreenIndependent) {
684
+ score += this.retrievalConfig.screenIndependentBoost;
685
+ }
686
+ }
687
+ if (recentRoots.has(entry.rootClass)) {
688
+ score += this.retrievalConfig.recentRootBoost;
689
+ }
690
+ return {
691
+ ...entry,
692
+ score,
693
+ };
694
+ })
695
+ .sort((a, b) => (b.score !== a.score ? b.score - a.score : a.index - b.index));
696
+ }
697
+ mergeRankedEntries(lexicalEntries, embeddingEntries) {
698
+ const byIndex = new Map();
699
+ lexicalEntries.forEach((entry) => {
700
+ byIndex.set(entry.index, entry);
701
+ });
702
+ embeddingEntries.forEach((entry) => {
703
+ const existing = byIndex.get(entry.index);
704
+ if (!existing) {
705
+ byIndex.set(entry.index, entry);
706
+ return;
707
+ }
708
+ byIndex.set(entry.index, this.toRankedEntry(entry.index, existing.lexicalScore, entry.embeddingScore));
709
+ });
710
+ return this.rerankEntries(Array.from(byIndex.values())).slice(0, this.retrievalConfig.mergedTopK);
711
+ }
712
+ selectExampleIndexes(rankedEntries) {
713
+ const selected = [];
714
+ const perRootCounts = new Map();
715
+ for (const entry of rankedEntries) {
716
+ const rootClass = entry.rootClass || `__index_${entry.index}`;
717
+ const currentCount = perRootCounts.get(rootClass) || 0;
718
+ if (currentCount >= this.retrievalConfig.maxExamplesPerRoot) {
719
+ continue;
720
+ }
721
+ selected.push(entry.index);
722
+ perRootCounts.set(rootClass, currentCount + 1);
723
+ if (selected.length >= this.config.topMExamples) {
724
+ break;
725
+ }
726
+ }
727
+ return selected;
728
+ }
729
+ computeSelectionFromRankedEntries(rankedEntries) {
730
+ const allowedDeicticRoots = this.getAllowedRootClassesForDeicticContext();
731
+ if (!!this.runtimeUiContext?.isDeicticUtterance && allowedDeicticRoots.size === 0) {
732
+ return this.rescueSelectionWithoutVisibleContext("", rankedEntries);
733
+ }
734
+ const candidateEntries = allowedDeicticRoots.size > 0
735
+ ? rankedEntries.filter((entry) => allowedDeicticRoots.has(entry.rootClass))
736
+ : rankedEntries;
737
+ const selectedIndexes = this.selectExampleIndexes(candidateEntries);
738
+ return this.buildSelectionFromExampleIndexes(selectedIndexes, candidateEntries);
739
+ }
740
+ computeSelectionSync(user_utterance, retrievalQuery = this.resolveRetrievalQuery(user_utterance, undefined, this.getCachedGrounding(user_utterance))) {
741
+ const rankedEntries = this.rerankEntries(this.computeLexicalRankings(retrievalQuery, false));
742
+ return this.computeSelectionFromRankedEntries(rankedEntries);
743
+ }
744
+ async computeSelectionAsync(user_utterance, retrievalQuery = this.resolveRetrievalQuery(user_utterance, undefined, this.getCachedGrounding(user_utterance))) {
745
+ const lexicalEntries = this.computeLexicalRankings(retrievalQuery, true);
746
+ if (!this.embeddingProvider || this.embeddingIndexByExampleKey.size === 0) {
747
+ return this.computeSelectionFromRankedEntries(lexicalEntries);
748
+ }
749
+ const utteranceEmbedding = await this.embeddingProvider.embed(retrievalQuery);
750
+ const embeddingEntries = this.computeEmbeddingRankings(utteranceEmbedding);
751
+ const rankedEntries = this.mergeRankedEntries(lexicalEntries, embeddingEntries);
752
+ return this.computeSelectionFromRankedEntries(rankedEntries);
753
+ }
754
+ computeSelection(user_utterance, english_translation) {
273
755
  if (!this.config.enabled) {
274
756
  return {
275
757
  classDescriptions: this.allClassDescriptions,
276
758
  examples: this.allExamples,
277
759
  };
278
760
  }
279
- const cacheKey = this.selectionCacheKeyForUtterance(user_utterance);
761
+ const retrievalQuery = this.resolveRetrievalQuery(user_utterance, english_translation, this.getCachedGrounding(user_utterance, english_translation));
762
+ const cacheKey = this.selectionCacheKeyForUtterance(user_utterance, retrievalQuery);
280
763
  const cached = this.selectionFromCache(cacheKey);
281
764
  if (cached) {
282
765
  return cached;
283
766
  }
284
- const allowedDeicticRootClasses = this.getAllowedRootClassesForDeicticContext();
285
- const preferredVisibleRootClasses = this.getPreferredRootClassesForVisibleScreenContext();
286
- const contextRootClasses = allowedDeicticRootClasses.size > 0
287
- ? allowedDeicticRootClasses
288
- : preferredVisibleRootClasses;
289
- const isDeicticUtterance = !!this.runtimeUiContext?.isDeicticUtterance;
290
- const isContextConstrained = contextRootClasses.size > 0;
291
- const rankingQuery = isContextConstrained
292
- ? `${user_utterance} ${Array.from(contextRootClasses).join(" ")}`
293
- : user_utterance;
294
- const rankedClassDescriptions = (0, context_selector_1.rankByQuery)(rankingQuery, this.allClassDescriptions, (description) => description, isContextConstrained
295
- ? this.allClassDescriptions.length
296
- : this.config.topNClassDescriptions, {
297
- allowZeroScoreBackfill: !isContextConstrained,
298
- });
299
- const candidateClassEntries = isContextConstrained
300
- ? rankedClassDescriptions.filter((entry) => contextRootClasses.has(this.allClassNames[entry.index]))
301
- : rankedClassDescriptions;
302
- const classIndexes = candidateClassEntries
303
- .slice(0, this.config.topNClassDescriptions)
304
- .map((entry) => entry.index)
305
- .sort((a, b) => a - b);
306
- const selectedClassDescriptions = classIndexes.map((index) => this.allClassDescriptions[index]);
307
- const rankedExamples = (0, context_selector_1.rankByQuery)(rankingQuery, this.allExamples, (example) => `${example.user_utterance}\n${example.example_parsed}`, isContextConstrained ? this.allExamples.length : this.config.topMExamples, {
308
- allowZeroScoreBackfill: !isContextConstrained,
309
- });
310
- const candidateExampleEntries = isContextConstrained
311
- ? rankedExamples.filter((entry) => contextRootClasses.has(this.allExampleRootClasses[entry.index]))
312
- : rankedExamples;
313
- if (isDeicticUtterance && contextRootClasses.size === 0) {
314
- const rescuedSelection = this.rescueSelectionWithoutVisibleContext(user_utterance, rankedExamples);
315
- this.cacheSelection(cacheKey, rescuedSelection);
316
- return rescuedSelection;
317
- }
318
- const exampleIndexes = candidateExampleEntries
319
- .slice(0, this.config.topMExamples)
320
- .map((entry) => entry.index)
321
- .sort((a, b) => a - b);
322
- const selectedExamples = exampleIndexes.map((index) => this.allExamples[index]);
323
- const selection = {
324
- classDescriptions: selectedClassDescriptions.length > 0
325
- ? selectedClassDescriptions
326
- : isDeicticUtterance
327
- ? []
328
- : this.allClassDescriptions,
329
- examples: selectedExamples.length > 0
330
- ? selectedExamples
331
- : isContextConstrained
332
- ? []
333
- : this.allExamples,
334
- };
767
+ const selection = this.computeSelectionSync(user_utterance, retrievalQuery);
335
768
  this.cacheSelection(cacheKey, selection);
336
769
  return selection;
337
770
  }
338
- zeroShotPromptForSelection(user_utterance, selection) {
771
+ zeroShotPromptForSelection(user_utterance, selection, english_translation) {
339
772
  const historyContext = this.buildHistoryContext();
340
773
  const uiContext = this.buildRuntimeUiContextBlock();
341
- const basic = this.buildPromptBasic(selection.classDescriptions, selection.examples, historyContext, uiContext);
342
- return `${(0, prompt_res_1.parseCodePrompt)(basic)}${(0, prompt_res_1.user_interaction_prompt)(user_utterance)}`;
774
+ const grounding = this.getCachedGrounding(user_utterance, english_translation);
775
+ const basic = this.buildPromptBasic(selection.classDescriptions, selection.examples, historyContext, uiContext, grounding, english_translation);
776
+ const resolvedEnglishTranslation = this.runtimeUiContext?.englishTranslation || english_translation;
777
+ return `${(0, prompt_res_1.parseCodePrompt)(basic)}${(0, prompt_res_1.user_interaction_prompt)(user_utterance, resolvedEnglishTranslation)}`;
343
778
  }
344
- prompt(user_utterance) {
345
- const selection = this.computeSelection(user_utterance);
779
+ prompt(user_utterance, english_translation) {
780
+ const selection = this.computeSelection(user_utterance, english_translation);
346
781
  const historyContext = this.buildHistoryContext();
347
782
  const uiContext = this.buildRuntimeUiContextBlock();
348
- const basic = this.buildPromptBasic(selection.classDescriptions, selection.examples, historyContext, uiContext);
349
- return `${basic}${(0, prompt_res_1.user_interaction_prompt)(user_utterance)}`;
783
+ const grounding = this.getCachedGrounding(user_utterance, english_translation);
784
+ const basic = this.buildPromptBasic(selection.classDescriptions, selection.examples, historyContext, uiContext, grounding, english_translation);
785
+ const resolvedEnglishTranslation = this.runtimeUiContext?.englishTranslation || english_translation;
786
+ return `${basic}${(0, prompt_res_1.user_interaction_prompt)(user_utterance, resolvedEnglishTranslation)}`;
350
787
  }
351
- zero_shot_prompt(user_utterance) {
352
- const selection = this.computeSelection(user_utterance);
353
- return this.zeroShotPromptForSelection(user_utterance, selection);
788
+ zero_shot_prompt(user_utterance, english_translation) {
789
+ const selection = this.computeSelection(user_utterance, english_translation);
790
+ return this.zeroShotPromptForSelection(user_utterance, selection, english_translation);
354
791
  }
355
792
  response_prompt(user_utterance, parsed, result) {
356
793
  return (0, prompt_res_1.agent_response_prompt)(user_utterance, parsed, result);