@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 +15 -0
- package/dist/client.js +37 -0
- package/dist/client.test.js +51 -0
- package/package.json +1 -1
- package/src/client.test.ts +74 -0
- package/src/client.ts +49 -0
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
|
*/
|
package/dist/client.test.js
CHANGED
|
@@ -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
package/src/client.test.ts
CHANGED
|
@@ -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
|
*/
|