@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 +11 -0
- package/dist/client.js +16 -0
- package/dist/client.test.js +36 -0
- package/dist/search.d.ts +12 -0
- package/dist/search.js +9 -0
- package/package.json +1 -1
- package/src/client.test.ts +43 -0
- package/src/client.ts +23 -0
- package/src/search.ts +18 -0
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
|
*
|
package/dist/client.test.js
CHANGED
|
@@ -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
package/src/client.test.ts
CHANGED
|
@@ -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
|
*/
|