@aiqa/sdk 2.0.0-develop.2 → 2.0.0-develop.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/README.md CHANGED
@@ -165,7 +165,7 @@ while you wait.
165
165
 
166
166
  `addFact()` does not throw on verification failure — an unmatched
167
167
  fact resolves with `is_valid_answer: false` and (where the backend
168
- provides it) `verification_status: "unmatched"` or `"contradicted"`.
168
+ provides it) `verification_status: "unmatched"`.
169
169
  Reject paths are reserved for transport / protocol errors.
170
170
 
171
171
  **Parameters:**
@@ -212,8 +212,7 @@ const result = await sdk.addFact({
212
212
  });
213
213
 
214
214
  if (result.is_valid_answer) {
215
- // verification_status will be "matched" (or "contradicted" if the
216
- // customer explicitly contradicted the expected answer).
215
+ // verification_status will be "matched".
217
216
  console.log("matched:", result.ai_answer);
218
217
  } else {
219
218
  // verification_status will be "unmatched". matched_components /
@@ -366,13 +365,13 @@ processing
366
365
  (same, but for a held unmatched verdict)
367
366
  │
368
367
  ▼
369
- matched | unmatched | contradicted ← terminal
368
+ matched | unmatched ← terminal
370
369
  ```
371
370
 
372
371
  Rules for consumers:
373
372
 
374
373
  - **`addFact()` resolves only on a terminal state** (`matched`,
375
- `unmatched`, `contradicted`). Render progress from the
374
+ `unmatched`). Render progress from the
376
375
  `FACT_VERIFIED` event, render the final verdict from the Promise
377
376
  result.
378
377
  - **Treat unknown `verification_status` strings as `processing`.**
@@ -453,8 +452,8 @@ interface AddFactResult {
453
452
  answer_match_score?: number;
454
453
 
455
454
  /**
456
- * Top-level state at terminal resolution. Always one of `matched`,
457
- * `unmatched`, or `contradicted` because the `addFact()` Promise only
455
+ * Top-level state at terminal resolution. Always one of `matched`
456
+ * or `unmatched` because the `addFact()` Promise only
458
457
  * resolves on terminals. Optional during the rollout window for
459
458
  * backwards compatibility with older backends.
460
459
  */
@@ -484,8 +483,7 @@ type VerificationStatus =
484
483
  | "pending_confirmation_matched"
485
484
  | "pending_confirmation_unmatched"
486
485
  | "matched"
487
- | "unmatched"
488
- | "contradicted";
486
+ | "unmatched";
489
487
  ```
490
488
 
491
489
  The enum is additive: consumers that `switch` on this value MUST
@@ -100,16 +100,15 @@ type QuestionStatus = "searching" | "asked" | "not_asked";
100
100
  * - `pending_confirmation_matched` backend has a matching answer but is
101
101
  * holding a settling window for mid-answer
102
102
  * clarifications (e.g. spellings, "I mean…");
103
- * resolves to `matched`/`unmatched`/`contradicted`
103
+ * resolves to `matched`/`unmatched`
104
104
  * - `pending_confirmation_unmatched` same as above but the held verdict is
105
105
  * `unmatched`; UI can show "we heard X but it
106
106
  * doesn't match, waiting to confirm" instead
107
107
  * of a hard red verdict
108
108
  * - `matched` terminal: answer matched expected
109
109
  * - `unmatched` terminal: budget exhausted with no match
110
- * - `contradicted` terminal (future): customer contradicted expected
111
110
  */
112
- type VerificationStatus = "processing" | "awaiting_question" | "awaiting_answer" | "partial" | "pending_confirmation_matched" | "pending_confirmation_unmatched" | "matched" | "unmatched" | "contradicted";
111
+ type VerificationStatus = "processing" | "awaiting_question" | "awaiting_answer" | "partial" | "pending_confirmation_matched" | "pending_confirmation_unmatched" | "matched" | "unmatched";
113
112
  type ApplicationFact = ApplicationFactInput & {
114
113
  ai_answer?: any;
115
114
  question_status: QuestionStatus;
@@ -143,8 +142,8 @@ interface AddFactResult {
143
142
  answer_match_score?: number;
144
143
  metadata?: Record<string, string>;
145
144
  /**
146
- * Top-level state at terminal resolution. Always one of `matched`,
147
- * `unmatched`, or `contradicted` because the `addFact()` Promise only
145
+ * Top-level state at terminal resolution. Always one of `matched`
146
+ * or `unmatched` because the `addFact()` Promise only
148
147
  * resolves on terminals. Optional during the rollout window for
149
148
  * backwards compatibility with older backends.
150
149
  */
@@ -164,7 +163,7 @@ interface AddFactResult {
164
163
  * Fires on EVERY state transition the verification queue produces —
165
164
  * non-terminal (`partial`, `awaiting_question`, `awaiting_answer`,
166
165
  * `pending_confirmation_matched`, `pending_confirmation_unmatched`,
167
- * `processing`) AND terminal (`matched`, `unmatched`, `contradicted`).
166
+ * `processing`) AND terminal (`matched`, `unmatched`).
168
167
  *
169
168
  * Subscribe with:
170
169
  * sdk.on("FACT_VERIFIED", (payload: FactVerifiedPayload) => {...});
@@ -286,6 +285,13 @@ declare class AiQaSdk implements IAiqaSdk {
286
285
  internalListeners: Map<SdkEvent, Set<(...args: any[]) => void>>;
287
286
  internalEvents: Set<SdkEvent>;
288
287
  private wrappersByEvent;
288
+ /**
289
+ * Trailing-debounce window (ms) applied per fact in {@link addFact}. Defaults
290
+ * to {@link ADD_FACT_DEBOUNCE_MS}; set to 0 to disable debouncing and send
291
+ * every call immediately.
292
+ */
293
+ addFactDebounceMs: number;
294
+ private pendingAddFacts;
289
295
  constructor(applicationId: string, applicationSecret: string, url?: string);
290
296
  on(name: SdkEvent, callback: (...args: any[]) => any): void;
291
297
  off(name: SdkEvent, callback?: (...args: any[]) => any): void;
@@ -295,6 +301,8 @@ declare class AiQaSdk implements IAiqaSdk {
295
301
  getApplication(): Promise<Application>;
296
302
  getFacts(): Promise<ApplicationFact[]>;
297
303
  addFact(fact: ApplicationFactInput): Promise<AddFactResult>;
304
+ private flushAddFact;
305
+ private sendAddFact;
298
306
  flagFact(factId: string): Promise<FlagFactResult>;
299
307
  setConversationProduct(product: string): Promise<SetConversationProductResult>;
300
308
  setConversationLanguage(language: ConversationLanguage): Promise<SetConversationLanguageResult>;
@@ -3397,7 +3397,7 @@ var compareVersions = (left, right) => {
3397
3397
  // package.json
3398
3398
  var package_default = {
3399
3399
  name: "@aiqa/sdk",
3400
- version: "2.0.0-develop.1",
3400
+ version: "2.0.0-develop.2",
3401
3401
  main: "dist/node/node.js",
3402
3402
  module: "dist/node/node.js",
3403
3403
  types: "dist/node/node.d.ts",
@@ -3479,6 +3479,7 @@ var package_default = {
3479
3479
  var SDK_VERSION = package_default.version ?? "0.0.1";
3480
3480
  var DEFAULT_URL = "ws://localhost:3025/ws";
3481
3481
  var DEFAULT_TIMEOUT_MS = 6e4;
3482
+ var ADD_FACT_DEBOUNCE_MS = 500;
3482
3483
  var RUNTIME = typeof window !== "undefined" && typeof window.document !== "undefined" ? "browser" : "node";
3483
3484
  var USER_AGENT = RUNTIME === "browser" ? typeof navigator !== "undefined" ? navigator.userAgent : "browser/unknown" : typeof process !== "undefined" ? `node ${process.version} ${process.platform}` : "node/unknown";
3484
3485
  function isThenable(value2) {
@@ -3524,6 +3525,15 @@ var AiQaSdk = class {
3524
3525
  // same fn twice registers two wrappers and two off() calls clear
3525
3526
  // both.
3526
3527
  this.wrappersByEvent = /* @__PURE__ */ new Map();
3528
+ /**
3529
+ * Trailing-debounce window (ms) applied per fact in {@link addFact}. Defaults
3530
+ * to {@link ADD_FACT_DEBOUNCE_MS}; set to 0 to disable debouncing and send
3531
+ * every call immediately.
3532
+ */
3533
+ this.addFactDebounceMs = ADD_FACT_DEBOUNCE_MS;
3534
+ // In-flight debounce state keyed by fact id: the pending timer, the latest
3535
+ // payload to send when it fires, and every caller awaiting that send.
3536
+ this.pendingAddFacts = /* @__PURE__ */ new Map();
3527
3537
  this.url = url2 ?? DEFAULT_URL;
3528
3538
  this.internalListeners = /* @__PURE__ */ new Map();
3529
3539
  this.internalEvents = /* @__PURE__ */ new Set(["authenticated"]);
@@ -3643,6 +3653,13 @@ var AiQaSdk = class {
3643
3653
  this.socket.disconnect();
3644
3654
  }
3645
3655
  this.wrappersByEvent.clear();
3656
+ for (const entry of this.pendingAddFacts.values()) {
3657
+ clearTimeout(entry.timer);
3658
+ entry.waiters.forEach(
3659
+ (w) => w.reject(new Error("Socket disconnected before addFact was sent"))
3660
+ );
3661
+ }
3662
+ this.pendingAddFacts.clear();
3646
3663
  }
3647
3664
  sendMessage(method, data) {
3648
3665
  return new Promise((resolve, reject) => {
@@ -3692,7 +3709,40 @@ var AiQaSdk = class {
3692
3709
  const result = await this.sendMessage("GET_FACTS" /* GET_FACTS */);
3693
3710
  return result && result.data !== void 0 ? result.data : result;
3694
3711
  }
3695
- async addFact(fact) {
3712
+ addFact(fact) {
3713
+ if (this.addFactDebounceMs <= 0) {
3714
+ return this.sendAddFact(fact);
3715
+ }
3716
+ const key = fact.questionId ?? fact.id;
3717
+ return new Promise((resolve, reject) => {
3718
+ const existing = this.pendingAddFacts.get(key);
3719
+ if (existing) {
3720
+ clearTimeout(existing.timer);
3721
+ existing.latest = fact;
3722
+ existing.waiters.push({ resolve, reject });
3723
+ existing.timer = setTimeout(
3724
+ () => this.flushAddFact(key),
3725
+ this.addFactDebounceMs
3726
+ );
3727
+ return;
3728
+ }
3729
+ this.pendingAddFacts.set(key, {
3730
+ latest: fact,
3731
+ waiters: [{ resolve, reject }],
3732
+ timer: setTimeout(() => this.flushAddFact(key), this.addFactDebounceMs)
3733
+ });
3734
+ });
3735
+ }
3736
+ flushAddFact(key) {
3737
+ const entry = this.pendingAddFacts.get(key);
3738
+ if (!entry) return;
3739
+ this.pendingAddFacts.delete(key);
3740
+ this.sendAddFact(entry.latest).then(
3741
+ (result) => entry.waiters.forEach((w) => w.resolve(result)),
3742
+ (error) => entry.waiters.forEach((w) => w.reject(error))
3743
+ );
3744
+ }
3745
+ async sendAddFact(fact) {
3696
3746
  const payload = {
3697
3747
  ...fact,
3698
3748
  critical: fact.critical ?? false,