@ekodb/ekodb-client 0.14.0 → 0.15.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
@@ -170,6 +170,7 @@ export interface ChatMessageRequest {
170
170
  force_summarize?: boolean;
171
171
  max_iterations?: number;
172
172
  tool_config?: ToolConfig;
173
+ llm_model?: string;
173
174
  }
174
175
  export interface TokenUsage {
175
176
  prompt_tokens: number;
@@ -766,6 +767,20 @@ export declare class EkoDBClient {
766
767
  * Health check - verify the ekoDB server is responding
767
768
  */
768
769
  health(): Promise<boolean>;
770
+ /**
771
+ * Execute a tool via ekoDB's server-side tool pipeline.
772
+ *
773
+ * Calls POST /api/chat/tools/execute which goes through the same
774
+ * execute_tool function as the LLM tool-calling loop — with all
775
+ * collection filtering, permission enforcement, and internal collection
776
+ * blocking. No LLM round-trip.
777
+ *
778
+ * @returns The tool result if executed, or null if the server doesn't
779
+ * support the endpoint (older ekoDB versions).
780
+ */
781
+ executeTool(toolName: string, params: {
782
+ [key: string]: any;
783
+ }, chatId?: string): Promise<any | null>;
769
784
  /**
770
785
  * Create a new chat session
771
786
  */
package/dist/client.js CHANGED
@@ -930,6 +930,43 @@ class EkoDBClient {
930
930
  }
931
931
  }
932
932
  // ========== Chat Methods ==========
933
+ /**
934
+ * Execute a tool via ekoDB's server-side tool pipeline.
935
+ *
936
+ * Calls POST /api/chat/tools/execute which goes through the same
937
+ * execute_tool function as the LLM tool-calling loop — with all
938
+ * collection filtering, permission enforcement, and internal collection
939
+ * blocking. No LLM round-trip.
940
+ *
941
+ * @returns The tool result if executed, or null if the server doesn't
942
+ * support the endpoint (older ekoDB versions).
943
+ */
944
+ async executeTool(toolName, params, chatId) {
945
+ const body = { tool: toolName, params };
946
+ if (chatId) {
947
+ body.chat_id = chatId;
948
+ }
949
+ try {
950
+ const result = await this.makeRequest("POST", "/api/chat/tools/execute", body, 0, true);
951
+ if (result.success) {
952
+ return result.result;
953
+ }
954
+ else {
955
+ throw new Error(result.error || "tool execution failed");
956
+ }
957
+ }
958
+ catch (err) {
959
+ // Server doesn't have the endpoint (404) or route mismatch (405)
960
+ // Parse status from makeRequest error format: "Request failed with status NNN: ..."
961
+ const message = String(err?.message ?? "");
962
+ const match = message.match(/Request failed with status (\d+):/);
963
+ const status = match ? parseInt(match[1], 10) : undefined;
964
+ if (status === 404 || status === 405) {
965
+ return null;
966
+ }
967
+ throw err;
968
+ }
969
+ }
933
970
  /**
934
971
  * Create a new chat session
935
972
  */
@@ -805,6 +805,57 @@ function mockErrorResponse(status, message) {
805
805
  });
806
806
  });
807
807
  // ============================================================================
808
+ // executeTool Tests
809
+ // ============================================================================
810
+ (0, vitest_1.describe)("EkoDBClient executeTool", () => {
811
+ (0, vitest_1.it)("executes tool successfully", async () => {
812
+ const client = createTestClient();
813
+ mockTokenResponse();
814
+ mockJsonResponse({
815
+ success: true,
816
+ result: { count: 42 },
817
+ });
818
+ const result = await client.executeTool("count_records", {
819
+ collection: "users",
820
+ });
821
+ (0, vitest_1.expect)(result).toEqual({ count: 42 });
822
+ });
823
+ (0, vitest_1.it)("passes chat_id when provided", async () => {
824
+ const client = createTestClient();
825
+ mockTokenResponse();
826
+ mockJsonResponse({
827
+ success: true,
828
+ result: { value: "hello" },
829
+ });
830
+ const result = await client.executeTool("kv_get", { key: "greeting" }, "chat_456");
831
+ (0, vitest_1.expect)(result).toEqual({ value: "hello" });
832
+ // Verify the request body included chat_id
833
+ const lastCall = mockFetch.mock.calls[1];
834
+ const body = JSON.parse(lastCall[1].body);
835
+ (0, vitest_1.expect)(body.chat_id).toBe("chat_456");
836
+ (0, vitest_1.expect)(body.tool).toBe("kv_get");
837
+ (0, vitest_1.expect)(body.params).toEqual({ key: "greeting" });
838
+ });
839
+ (0, vitest_1.it)("throws on tool execution failure", async () => {
840
+ const client = createTestClient();
841
+ mockTokenResponse();
842
+ mockJsonResponse({
843
+ success: false,
844
+ error: "permission denied",
845
+ });
846
+ await (0, vitest_1.expect)(client.executeTool("delete_collection", { collection: "system" })).rejects.toThrow("permission denied");
847
+ });
848
+ (0, vitest_1.it)("returns null when server does not support endpoint", async () => {
849
+ const client = createTestClient();
850
+ mockTokenResponse();
851
+ mockErrorResponse(404, "Not Found");
852
+ const result = await client.executeTool("count_records", {
853
+ collection: "users",
854
+ });
855
+ (0, vitest_1.expect)(result).toBeNull();
856
+ });
857
+ });
858
+ // ============================================================================
808
859
  // Chat Models Tests
809
860
  // ============================================================================
810
861
  (0, vitest_1.describe)("EkoDBClient chat models", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekodb/ekodb-client",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Official TypeScript/JavaScript client for ekoDB",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1105,6 +1105,80 @@ describe("Convenience methods", () => {
1105
1105
  });
1106
1106
  });
1107
1107
 
1108
+ // ============================================================================
1109
+ // executeTool Tests
1110
+ // ============================================================================
1111
+
1112
+ describe("EkoDBClient executeTool", () => {
1113
+ it("executes tool successfully", async () => {
1114
+ const client = createTestClient();
1115
+
1116
+ mockTokenResponse();
1117
+ mockJsonResponse({
1118
+ success: true,
1119
+ result: { count: 42 },
1120
+ });
1121
+
1122
+ const result = await client.executeTool("count_records", {
1123
+ collection: "users",
1124
+ });
1125
+
1126
+ expect(result).toEqual({ count: 42 });
1127
+ });
1128
+
1129
+ it("passes chat_id when provided", async () => {
1130
+ const client = createTestClient();
1131
+
1132
+ mockTokenResponse();
1133
+ mockJsonResponse({
1134
+ success: true,
1135
+ result: { value: "hello" },
1136
+ });
1137
+
1138
+ const result = await client.executeTool(
1139
+ "kv_get",
1140
+ { key: "greeting" },
1141
+ "chat_456",
1142
+ );
1143
+
1144
+ expect(result).toEqual({ value: "hello" });
1145
+
1146
+ // Verify the request body included chat_id
1147
+ const lastCall = mockFetch.mock.calls[1];
1148
+ const body = JSON.parse(lastCall[1].body);
1149
+ expect(body.chat_id).toBe("chat_456");
1150
+ expect(body.tool).toBe("kv_get");
1151
+ expect(body.params).toEqual({ key: "greeting" });
1152
+ });
1153
+
1154
+ it("throws on tool execution failure", async () => {
1155
+ const client = createTestClient();
1156
+
1157
+ mockTokenResponse();
1158
+ mockJsonResponse({
1159
+ success: false,
1160
+ error: "permission denied",
1161
+ });
1162
+
1163
+ await expect(
1164
+ client.executeTool("delete_collection", { collection: "system" }),
1165
+ ).rejects.toThrow("permission denied");
1166
+ });
1167
+
1168
+ it("returns null when server does not support endpoint", async () => {
1169
+ const client = createTestClient();
1170
+
1171
+ mockTokenResponse();
1172
+ mockErrorResponse(404, "Not Found");
1173
+
1174
+ const result = await client.executeTool("count_records", {
1175
+ collection: "users",
1176
+ });
1177
+
1178
+ expect(result).toBeNull();
1179
+ });
1180
+ });
1181
+
1108
1182
  // ============================================================================
1109
1183
  // Chat Models Tests
1110
1184
  // ============================================================================
package/src/client.ts CHANGED
@@ -203,6 +203,7 @@ export interface ChatMessageRequest {
203
203
  force_summarize?: boolean;
204
204
  max_iterations?: number;
205
205
  tool_config?: ToolConfig;
206
+ llm_model?: string;
206
207
  }
207
208
 
208
209
  export interface TokenUsage {
@@ -1552,6 +1553,54 @@ export class EkoDBClient {
1552
1553
 
1553
1554
  // ========== Chat Methods ==========
1554
1555
 
1556
+ /**
1557
+ * Execute a tool via ekoDB's server-side tool pipeline.
1558
+ *
1559
+ * Calls POST /api/chat/tools/execute which goes through the same
1560
+ * execute_tool function as the LLM tool-calling loop — with all
1561
+ * collection filtering, permission enforcement, and internal collection
1562
+ * blocking. No LLM round-trip.
1563
+ *
1564
+ * @returns The tool result if executed, or null if the server doesn't
1565
+ * support the endpoint (older ekoDB versions).
1566
+ */
1567
+ async executeTool(
1568
+ toolName: string,
1569
+ params: { [key: string]: any },
1570
+ chatId?: string,
1571
+ ): Promise<any | null> {
1572
+ const body: { [key: string]: any } = { tool: toolName, params };
1573
+ if (chatId) {
1574
+ body.chat_id = chatId;
1575
+ }
1576
+
1577
+ try {
1578
+ const result = await this.makeRequest<{ [key: string]: any }>(
1579
+ "POST",
1580
+ "/api/chat/tools/execute",
1581
+ body,
1582
+ 0,
1583
+ true, // Force JSON for chat operations
1584
+ );
1585
+
1586
+ if (result.success) {
1587
+ return result.result;
1588
+ } else {
1589
+ throw new Error(result.error || "tool execution failed");
1590
+ }
1591
+ } catch (err: any) {
1592
+ // Server doesn't have the endpoint (404) or route mismatch (405)
1593
+ // Parse status from makeRequest error format: "Request failed with status NNN: ..."
1594
+ const message = String(err?.message ?? "");
1595
+ const match = message.match(/Request failed with status (\d+):/);
1596
+ const status = match ? parseInt(match[1], 10) : undefined;
1597
+ if (status === 404 || status === 405) {
1598
+ return null;
1599
+ }
1600
+ throw err;
1601
+ }
1602
+ }
1603
+
1555
1604
  /**
1556
1605
  * Create a new chat session
1557
1606
  */