@ecodrix/erix-client 1.0.2 → 1.0.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/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export type JsonValue = string | number | boolean | null | JsonValue[] | {
24
24
  [key: string]: JsonValue;
25
25
  };
26
26
  export interface ErixClientOptions {
27
- /** Full URL of the deployed erix-store instance, e.g. https://erix-store.onrender.com */
27
+ /** Full URL of the deployed erix-store instance, e.g. https://api.erix.ecodrix.com */
28
28
  baseUrl: string;
29
29
  /** Shared secret generated at deploy time — must match ERIX_API_KEY on the server */
30
30
  apiKey: string;
@@ -73,6 +73,12 @@ export interface CacheSetOptions {
73
73
  */
74
74
  staleFor?: number;
75
75
  }
76
+ export interface SemanticGetResult<T = JsonValue> {
77
+ value: T;
78
+ key: string;
79
+ similarity: number;
80
+ isExact: boolean;
81
+ }
76
82
  export interface QueueEventHandlers<T = JsonValue> {
77
83
  onAdded?: (job: JobV2<T>) => void;
78
84
  onActive?: (job: JobV2<T>) => void;
@@ -208,6 +214,81 @@ export declare class ErixClient {
208
214
  /** Get cache statistics (hit rate, size, evictions) */
209
215
  stats: () => Promise<unknown>;
210
216
  };
217
+ semantic: {
218
+ /**
219
+ * Store a value with its embedding for similarity lookups.
220
+ * @param key Unique cache key
221
+ * @param text Text to embed (the query/question this entry answers)
222
+ * @param value The value to cache
223
+ * @param ttlMs Optional TTL in milliseconds
224
+ * @param tags Optional tags for bulk invalidation
225
+ */
226
+ set: (key: string, text: string, value: JsonValue, ttlMs?: number, tags?: string[]) => Promise<void>;
227
+ /**
228
+ * Exact key lookup in semantic cache.
229
+ */
230
+ get: <T = JsonValue>(key: string) => Promise<T | null>;
231
+ /**
232
+ * Find the most similar cached entry to a query string.
233
+ * Returns null if no entry exceeds the similarity threshold.
234
+ *
235
+ * @example
236
+ * // Stored: "What are your pricing plans?" → { starter: '$29', pro: '$99' }
237
+ * // Query: "How much does it cost?"
238
+ * const result = await client.semantic.search("How much does it cost?");
239
+ * // → { value: {...}, similarity: 0.97, isExact: false }
240
+ */
241
+ search: <T = JsonValue>(query: string, threshold?: number) => Promise<SemanticGetResult<T> | null>;
242
+ /** Invalidate all semantic cache entries with a specific tag */
243
+ invalidateByTag: (tag: string) => Promise<number>;
244
+ /** Delete a single semantic cache entry */
245
+ del: (key: string) => Promise<void>;
246
+ /** Get semantic cache stats */
247
+ stats: () => Promise<{
248
+ success: boolean;
249
+ size: number;
250
+ keys: string[];
251
+ }>;
252
+ };
253
+ analytics: {
254
+ /**
255
+ * Get live usage counts for this tenant (from in-memory buffer).
256
+ * Updated in real time — no waiting for the 10s DB flush.
257
+ */
258
+ usage: () => Promise<Record<string, number>>;
259
+ /**
260
+ * Get anomaly detector stats — mean, stddev, and sample count
261
+ * for each monitored metric of this tenant.
262
+ */
263
+ anomalies: () => Promise<{
264
+ success: boolean;
265
+ metrics: Array<{
266
+ metric: string;
267
+ mean: number;
268
+ stddev: number;
269
+ n: number;
270
+ }>;
271
+ }>;
272
+ /**
273
+ * Subscribe to real-time anomaly alerts via SSE.
274
+ * The server fires an event whenever a metric exceeds 3σ.
275
+ *
276
+ * @example
277
+ * const sub = client.analytics.subscribeAlerts((alert) => {
278
+ * console.log(`ALERT: ${alert.message}`);
279
+ * });
280
+ * sub.close();
281
+ */
282
+ subscribeAlerts: (onAlert: (alert: {
283
+ metric: string;
284
+ current: number;
285
+ mean: number;
286
+ zScore: number;
287
+ message: string;
288
+ }) => void, onError?: (err: Error) => void) => {
289
+ close: () => void;
290
+ };
291
+ };
211
292
  /**
212
293
  * Opens a persistent SSE connection and dispatches named events to a handler.
213
294
  * Returns a controller with `.close()` to terminate the connection.
package/dist/index.js CHANGED
@@ -227,6 +227,100 @@ export class ErixClient {
227
227
  return res.stats;
228
228
  },
229
229
  };
230
+ // ─── Semantic Cache (AI layer) ────────────────────────────────────────
231
+ this.semantic = {
232
+ /**
233
+ * Store a value with its embedding for similarity lookups.
234
+ * @param key Unique cache key
235
+ * @param text Text to embed (the query/question this entry answers)
236
+ * @param value The value to cache
237
+ * @param ttlMs Optional TTL in milliseconds
238
+ * @param tags Optional tags for bulk invalidation
239
+ */
240
+ set: async (key, text, value, ttlMs, tags) => {
241
+ await this.req("POST", `/semantic/${encodeURIComponent(key)}`, {
242
+ text,
243
+ value,
244
+ ttlMs,
245
+ tags,
246
+ });
247
+ },
248
+ /**
249
+ * Exact key lookup in semantic cache.
250
+ */
251
+ get: async (key) => {
252
+ try {
253
+ const res = await this.req("GET", `/semantic/${encodeURIComponent(key)}`);
254
+ return res.value ?? null;
255
+ }
256
+ catch {
257
+ return null;
258
+ }
259
+ },
260
+ /**
261
+ * Find the most similar cached entry to a query string.
262
+ * Returns null if no entry exceeds the similarity threshold.
263
+ *
264
+ * @example
265
+ * // Stored: "What are your pricing plans?" → { starter: '$29', pro: '$99' }
266
+ * // Query: "How much does it cost?"
267
+ * const result = await client.semantic.search("How much does it cost?");
268
+ * // → { value: {...}, similarity: 0.97, isExact: false }
269
+ */
270
+ search: async (query, threshold) => {
271
+ try {
272
+ const res = await this.req("POST", "/semantic/search", { query, threshold });
273
+ return res;
274
+ }
275
+ catch {
276
+ return null;
277
+ }
278
+ },
279
+ /** Invalidate all semantic cache entries with a specific tag */
280
+ invalidateByTag: async (tag) => {
281
+ const res = await this.req("DELETE", `/semantic/tags/${encodeURIComponent(tag)}`);
282
+ return res.count;
283
+ },
284
+ /** Delete a single semantic cache entry */
285
+ del: async (key) => {
286
+ await this.req("DELETE", `/semantic/${encodeURIComponent(key)}`);
287
+ },
288
+ /** Get semantic cache stats */
289
+ stats: async () => {
290
+ return this.req("GET", "/semantic/_stats");
291
+ },
292
+ };
293
+ // ─── Analytics ────────────────────────────────────────────────────────
294
+ this.analytics = {
295
+ /**
296
+ * Get live usage counts for this tenant (from in-memory buffer).
297
+ * Updated in real time — no waiting for the 10s DB flush.
298
+ */
299
+ usage: async () => {
300
+ const res = await this.req("GET", "/analytics/usage");
301
+ return res.usage;
302
+ },
303
+ /**
304
+ * Get anomaly detector stats — mean, stddev, and sample count
305
+ * for each monitored metric of this tenant.
306
+ */
307
+ anomalies: async () => {
308
+ return this.req("GET", "/analytics/anomalies");
309
+ },
310
+ /**
311
+ * Subscribe to real-time anomaly alerts via SSE.
312
+ * The server fires an event whenever a metric exceeds 3σ.
313
+ *
314
+ * @example
315
+ * const sub = client.analytics.subscribeAlerts((alert) => {
316
+ * console.log(`ALERT: ${alert.message}`);
317
+ * });
318
+ * sub.close();
319
+ */
320
+ subscribeAlerts: (onAlert, onError) => {
321
+ return this.openEventStream("/analytics/anomalies/stream", (_event, data) => onAlert(data), onError);
322
+ },
323
+ };
230
324
  this.baseUrl = options.baseUrl.replace(/\/$/, ""); // strip trailing slash
231
325
  this.timeoutMs = options.timeoutMs ?? 5000;
232
326
  this.headers = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecodrix/erix-client",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Type-safe HTTP client for the erix-store in-memory data service. Zero dependencies — uses native fetch.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",