@ekodb/ekodb-client 0.21.0 → 0.22.0

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/client.d.ts CHANGED
@@ -895,6 +895,17 @@ export declare class EkoDBClient {
895
895
  * Unblocks ekoDB's tool loop so it can feed the result to the LLM.
896
896
  */
897
897
  submitChatToolResult(chatId: string, callId: string, success: boolean, result?: any, error?: string): Promise<void>;
898
+ /**
899
+ * Send a client tool keepalive (liveness ping) for an in-flight SSE chat stream.
900
+ *
901
+ * This is NOT a result: it does not unblock the tool loop or feed anything to the
902
+ * LLM. It simply resets the server's per-tool wait deadline (governed by
903
+ * `client_tool_timeout_secs`, default 60s) so that slow user confirmations or
904
+ * long-running client tools don't get the turn timed out before
905
+ * {@link submitChatToolResult} arrives. Send it periodically while a client tool
906
+ * is still working. See ekoDB#530.
907
+ */
908
+ submitChatToolKeepalive(chatId: string, callId: string): Promise<void>;
898
909
  /**
899
910
  * Send a message in an existing chat session via SSE streaming.
900
911
  *
package/dist/client.js CHANGED
@@ -1249,6 +1249,22 @@ class EkoDBClient {
1249
1249
  ...(error !== undefined && { error }),
1250
1250
  }, 0, true);
1251
1251
  }
1252
+ /**
1253
+ * Send a client tool keepalive (liveness ping) for an in-flight SSE chat stream.
1254
+ *
1255
+ * This is NOT a result: it does not unblock the tool loop or feed anything to the
1256
+ * LLM. It simply resets the server's per-tool wait deadline (governed by
1257
+ * `client_tool_timeout_secs`, default 60s) so that slow user confirmations or
1258
+ * long-running client tools don't get the turn timed out before
1259
+ * {@link submitChatToolResult} arrives. Send it periodically while a client tool
1260
+ * is still working. See ekoDB#530.
1261
+ */
1262
+ async submitChatToolKeepalive(chatId, callId) {
1263
+ await this.makeRequest("POST", `/api/chat/${encodeURIComponent(chatId)}/tool-result`, {
1264
+ call_id: callId,
1265
+ keepalive: true,
1266
+ }, 0, true);
1267
+ }
1252
1268
  /**
1253
1269
  * Send a message in an existing chat session via SSE streaming.
1254
1270
  *
@@ -8,6 +8,7 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const vitest_1 = require("vitest");
10
10
  const client_1 = require("./client");
11
+ const search_1 = require("./search");
11
12
  // Mock fetch globally
12
13
  const mockFetch = vitest_1.vi.fn();
13
14
  global.fetch = mockFetch;
@@ -345,6 +346,22 @@ function mockErrorResponse(status, message) {
345
346
  (0, vitest_1.expect)(result.total).toBe(2);
346
347
  });
347
348
  // Note: textSearch and hybridSearch require specific mock setup - covered by integration tests
349
+ (0, vitest_1.it)("builds a vector search query with a metadata filter", () => {
350
+ const filter = {
351
+ type: "Condition",
352
+ content: { field: "category", operator: "Eq", value: "ml" },
353
+ };
354
+ const query = new search_1.SearchQueryBuilder("test")
355
+ .vector([0.1, 0.2, 0.3])
356
+ .filters(filter)
357
+ .build();
358
+ (0, vitest_1.expect)(query.filters).toEqual(filter);
359
+ (0, vitest_1.expect)(query.vector).toEqual([0.1, 0.2, 0.3]);
360
+ });
361
+ (0, vitest_1.it)("omits filters when not set", () => {
362
+ const query = new search_1.SearchQueryBuilder("test").build();
363
+ (0, vitest_1.expect)(query.filters).toBeUndefined();
364
+ });
348
365
  });
349
366
  // ============================================================================
350
367
  // KV Batch Operations Tests
@@ -2318,6 +2335,25 @@ function mockErrorResponse(status, message) {
2318
2335
  });
2319
2336
  });
2320
2337
  // ============================================================================
2338
+ // submitChatToolKeepalive Tests
2339
+ // ============================================================================
2340
+ (0, vitest_1.describe)("submitChatToolKeepalive", () => {
2341
+ (0, vitest_1.it)("sends keepalive to correct endpoint", async () => {
2342
+ const client = createTestClient();
2343
+ mockTokenResponse();
2344
+ mockJsonResponse({});
2345
+ await client.submitChatToolKeepalive("chat-123", "call-456");
2346
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
2347
+ const call = mockFetch.mock.calls[1];
2348
+ (0, vitest_1.expect)(call[0]).toContain("/api/chat/chat-123/tool-result");
2349
+ const body = JSON.parse(call[1].body);
2350
+ (0, vitest_1.expect)(body.call_id).toBe("call-456");
2351
+ (0, vitest_1.expect)(body.keepalive).toBe(true);
2352
+ (0, vitest_1.expect)(body.success).toBeUndefined();
2353
+ (0, vitest_1.expect)(body.result).toBeUndefined();
2354
+ });
2355
+ });
2356
+ // ============================================================================
2321
2357
  // subscribeSSE Tests
2322
2358
  // ============================================================================
2323
2359
  (0, vitest_1.describe)("subscribeSSE", () => {
package/dist/search.d.ts CHANGED
@@ -52,6 +52,12 @@ export interface SearchQuery {
52
52
  select_fields?: string[];
53
53
  /** Exclude these fields from results */
54
54
  exclude_fields?: string[];
55
+ /**
56
+ * Metadata pre-filter for text/vector/hybrid search. A canonical QueryExpression
57
+ * (the same shape produced by `QueryBuilder.build().filter`); only records
58
+ * matching the filter are considered as candidates before ranking.
59
+ */
60
+ filters?: any;
55
61
  }
56
62
  /**
57
63
  * Search result with score and matched fields
@@ -177,6 +183,12 @@ export declare class SearchQueryBuilder {
177
183
  * Exclude specific fields from results
178
184
  */
179
185
  excludeFields(fields: string[]): this;
186
+ /**
187
+ * Set a metadata pre-filter for text/vector/hybrid search. Accepts a canonical
188
+ * QueryExpression (the same shape produced by `QueryBuilder.build().filter`);
189
+ * only records matching the filter are considered before ranking.
190
+ */
191
+ filters(filter: any): this;
180
192
  /**
181
193
  * Build the final SearchQuery object
182
194
  */
package/dist/search.js CHANGED
@@ -176,6 +176,15 @@ class SearchQueryBuilder {
176
176
  this.query.exclude_fields = fields;
177
177
  return this;
178
178
  }
179
+ /**
180
+ * Set a metadata pre-filter for text/vector/hybrid search. Accepts a canonical
181
+ * QueryExpression (the same shape produced by `QueryBuilder.build().filter`);
182
+ * only records matching the filter are considered before ranking.
183
+ */
184
+ filters(filter) {
185
+ this.query.filters = filter;
186
+ return this;
187
+ }
179
188
  /**
180
189
  * Build the final SearchQuery object
181
190
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekodb/ekodb-client",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Official TypeScript/JavaScript client for ekoDB",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
9
9
  import { EkoDBClient, SerializationFormat, extractRecordId } from "./client";
10
+ import { SearchQueryBuilder } from "./search";
10
11
 
11
12
  // Mock fetch globally
12
13
  const mockFetch = vi.fn();
@@ -489,6 +490,25 @@ describe("EkoDBClient search", () => {
489
490
  });
490
491
 
491
492
  // Note: textSearch and hybridSearch require specific mock setup - covered by integration tests
493
+
494
+ it("builds a vector search query with a metadata filter", () => {
495
+ const filter = {
496
+ type: "Condition",
497
+ content: { field: "category", operator: "Eq", value: "ml" },
498
+ };
499
+ const query = new SearchQueryBuilder("test")
500
+ .vector([0.1, 0.2, 0.3])
501
+ .filters(filter)
502
+ .build();
503
+
504
+ expect(query.filters).toEqual(filter);
505
+ expect(query.vector).toEqual([0.1, 0.2, 0.3]);
506
+ });
507
+
508
+ it("omits filters when not set", () => {
509
+ const query = new SearchQueryBuilder("test").build();
510
+ expect(query.filters).toBeUndefined();
511
+ });
492
512
  });
493
513
 
494
514
  // ============================================================================
@@ -3049,6 +3069,29 @@ describe("submitChatToolResult", () => {
3049
3069
  });
3050
3070
  });
3051
3071
 
3072
+ // ============================================================================
3073
+ // submitChatToolKeepalive Tests
3074
+ // ============================================================================
3075
+
3076
+ describe("submitChatToolKeepalive", () => {
3077
+ it("sends keepalive to correct endpoint", async () => {
3078
+ const client = createTestClient();
3079
+ mockTokenResponse();
3080
+ mockJsonResponse({});
3081
+
3082
+ await client.submitChatToolKeepalive("chat-123", "call-456");
3083
+
3084
+ expect(mockFetch).toHaveBeenCalledTimes(2);
3085
+ const call = mockFetch.mock.calls[1];
3086
+ expect(call[0]).toContain("/api/chat/chat-123/tool-result");
3087
+ const body = JSON.parse(call[1].body);
3088
+ expect(body.call_id).toBe("call-456");
3089
+ expect(body.keepalive).toBe(true);
3090
+ expect(body.success).toBeUndefined();
3091
+ expect(body.result).toBeUndefined();
3092
+ });
3093
+ });
3094
+
3052
3095
  // ============================================================================
3053
3096
  // subscribeSSE Tests
3054
3097
  // ============================================================================
package/src/client.ts CHANGED
@@ -2014,6 +2014,29 @@ export class EkoDBClient {
2014
2014
  );
2015
2015
  }
2016
2016
 
2017
+ /**
2018
+ * Send a client tool keepalive (liveness ping) for an in-flight SSE chat stream.
2019
+ *
2020
+ * This is NOT a result: it does not unblock the tool loop or feed anything to the
2021
+ * LLM. It simply resets the server's per-tool wait deadline (governed by
2022
+ * `client_tool_timeout_secs`, default 60s) so that slow user confirmations or
2023
+ * long-running client tools don't get the turn timed out before
2024
+ * {@link submitChatToolResult} arrives. Send it periodically while a client tool
2025
+ * is still working. See ekoDB#530.
2026
+ */
2027
+ async submitChatToolKeepalive(chatId: string, callId: string): Promise<void> {
2028
+ await this.makeRequest(
2029
+ "POST",
2030
+ `/api/chat/${encodeURIComponent(chatId)}/tool-result`,
2031
+ {
2032
+ call_id: callId,
2033
+ keepalive: true,
2034
+ },
2035
+ 0,
2036
+ true,
2037
+ );
2038
+ }
2039
+
2017
2040
  /**
2018
2041
  * Send a message in an existing chat session via SSE streaming.
2019
2042
  *
package/src/search.ts CHANGED
@@ -59,6 +59,14 @@ export interface SearchQuery {
59
59
  select_fields?: string[];
60
60
  /** Exclude these fields from results */
61
61
  exclude_fields?: string[];
62
+
63
+ // Metadata pre-filter
64
+ /**
65
+ * Metadata pre-filter for text/vector/hybrid search. A canonical QueryExpression
66
+ * (the same shape produced by `QueryBuilder.build().filter`); only records
67
+ * matching the filter are considered as candidates before ranking.
68
+ */
69
+ filters?: any;
62
70
  }
63
71
 
64
72
  /**
@@ -275,6 +283,16 @@ export class SearchQueryBuilder {
275
283
  return this;
276
284
  }
277
285
 
286
+ /**
287
+ * Set a metadata pre-filter for text/vector/hybrid search. Accepts a canonical
288
+ * QueryExpression (the same shape produced by `QueryBuilder.build().filter`);
289
+ * only records matching the filter are considered before ranking.
290
+ */
291
+ filters(filter: any): this {
292
+ this.query.filters = filter;
293
+ return this;
294
+ }
295
+
278
296
  /**
279
297
  * Build the final SearchQuery object
280
298
  */