@mastra/client-js 0.10.17-alpha.1 → 0.10.17-alpha.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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +36 -0
- package/dist/index.cjs +42 -3
- package/dist/index.d.cts +31 -3
- package/dist/index.d.ts +31 -3
- package/dist/index.js +42 -3
- package/package.json +2 -2
- package/src/index.test.ts +108 -0
- package/src/resources/agent.ts +13 -3
- package/src/resources/memory-thread.test.ts +285 -0
- package/src/resources/memory-thread.ts +18 -0
- package/src/resources/network-memory-thread.test.ts +269 -0
- package/src/resources/network-memory-thread.ts +18 -0
- package/src/types.ts +3 -3
- package/src/v2-messages.test.ts +180 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/client-js@0.10.17-alpha.
|
|
2
|
+
> @mastra/client-js@0.10.17-alpha.3 build /home/runner/work/mastra/mastra/client-sdks/client-js
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
[34mCLI[39m Cleaning output folder
|
|
10
10
|
[34mESM[39m Build start
|
|
11
11
|
[34mCJS[39m Build start
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m75.21 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 2334ms
|
|
14
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m75.52 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 2348ms
|
|
16
16
|
[34mDTS[39m Build start
|
|
17
|
-
[32mDTS[39m ⚡️ Build success in
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
17
|
+
[32mDTS[39m ⚡️ Build success in 17829ms
|
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m47.19 KB[39m
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m47.19 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @mastra/client-js
|
|
2
2
|
|
|
3
|
+
## 0.10.17-alpha.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9802f42: Added types and tests to ensure client-js and hono endpoints can save memory messages where the input is either a v1 or v2 mastra message
|
|
8
|
+
- 1ac8f6b: deduplicate message
|
|
9
|
+
- @mastra/core@0.12.0-alpha.3
|
|
10
|
+
|
|
11
|
+
## 0.10.17-alpha.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- aa2715b: process stream response error handling
|
|
16
|
+
- b8efbb9: feat: add flexible deleteMessages method to memory API
|
|
17
|
+
- Added `memory.deleteMessages(input)` method that accepts multiple input types:
|
|
18
|
+
- Single message ID as string: `deleteMessages('msg-123')`
|
|
19
|
+
- Array of message IDs: `deleteMessages(['msg-1', 'msg-2'])`
|
|
20
|
+
- Message object with id property: `deleteMessages({ id: 'msg-123' })`
|
|
21
|
+
- Array of message objects: `deleteMessages([{ id: 'msg-1' }, { id: 'msg-2' }])`
|
|
22
|
+
- Implemented in all storage adapters (LibSQL, PostgreSQL, Upstash, InMemory)
|
|
23
|
+
- Added REST API endpoint: `POST /api/memory/messages/delete`
|
|
24
|
+
- Updated client SDK: `thread.deleteMessages()` accepts all input types
|
|
25
|
+
- Updates thread timestamps when messages are deleted
|
|
26
|
+
- Added comprehensive test coverage and documentation
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [27cc97a]
|
|
29
|
+
- Updated dependencies [41daa63]
|
|
30
|
+
- Updated dependencies [254a36b]
|
|
31
|
+
- Updated dependencies [0b89602]
|
|
32
|
+
- Updated dependencies [4d37822]
|
|
33
|
+
- Updated dependencies [ff9c125]
|
|
34
|
+
- Updated dependencies [b8efbb9]
|
|
35
|
+
- Updated dependencies [71466e7]
|
|
36
|
+
- Updated dependencies [0c99fbe]
|
|
37
|
+
- @mastra/core@0.12.0-alpha.2
|
|
38
|
+
|
|
3
39
|
## 0.10.17-alpha.1
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -741,7 +741,12 @@ var Agent = class extends BaseResource {
|
|
|
741
741
|
this.processChatResponse({
|
|
742
742
|
stream: streamForProcessing,
|
|
743
743
|
update: ({ message }) => {
|
|
744
|
-
messages.
|
|
744
|
+
const existingIndex = messages.findIndex((m) => m.id === message.id);
|
|
745
|
+
if (existingIndex !== -1) {
|
|
746
|
+
messages[existingIndex] = message;
|
|
747
|
+
} else {
|
|
748
|
+
messages.push(message);
|
|
749
|
+
}
|
|
745
750
|
},
|
|
746
751
|
onFinish: async ({ finishReason, message }) => {
|
|
747
752
|
if (finishReason === "tool-calls") {
|
|
@@ -801,10 +806,12 @@ var Agent = class extends BaseResource {
|
|
|
801
806
|
this.processStreamResponse(
|
|
802
807
|
{
|
|
803
808
|
...processedParams,
|
|
804
|
-
messages: [...messageArray, ...messages, lastMessage]
|
|
809
|
+
messages: [...messageArray, ...messages.filter((m) => m.id !== lastMessage.id), lastMessage]
|
|
805
810
|
},
|
|
806
811
|
writable
|
|
807
|
-
)
|
|
812
|
+
).catch((error) => {
|
|
813
|
+
console.error("Error processing stream response:", error);
|
|
814
|
+
});
|
|
808
815
|
}
|
|
809
816
|
}
|
|
810
817
|
} else {
|
|
@@ -814,6 +821,8 @@ var Agent = class extends BaseResource {
|
|
|
814
821
|
}
|
|
815
822
|
},
|
|
816
823
|
lastMessage: void 0
|
|
824
|
+
}).catch((error) => {
|
|
825
|
+
console.error("Error processing stream response:", error);
|
|
817
826
|
});
|
|
818
827
|
} catch (error) {
|
|
819
828
|
console.error("Error processing stream response:", error);
|
|
@@ -977,6 +986,21 @@ var MemoryThread = class extends BaseResource {
|
|
|
977
986
|
});
|
|
978
987
|
return this.request(`/api/memory/threads/${this.threadId}/messages/paginated?${query.toString()}`);
|
|
979
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Deletes one or more messages from the thread
|
|
991
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
992
|
+
* message object with id property, or array of message objects
|
|
993
|
+
* @returns Promise containing deletion result
|
|
994
|
+
*/
|
|
995
|
+
deleteMessages(messageIds) {
|
|
996
|
+
const query = new URLSearchParams({
|
|
997
|
+
agentId: this.agentId
|
|
998
|
+
});
|
|
999
|
+
return this.request(`/api/memory/messages/delete?${query.toString()}`, {
|
|
1000
|
+
method: "POST",
|
|
1001
|
+
body: { messageIds }
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
980
1004
|
};
|
|
981
1005
|
|
|
982
1006
|
// src/resources/vector.ts
|
|
@@ -1725,6 +1749,21 @@ var NetworkMemoryThread = class extends BaseResource {
|
|
|
1725
1749
|
});
|
|
1726
1750
|
return this.request(`/api/memory/network/threads/${this.threadId}/messages?${query.toString()}`);
|
|
1727
1751
|
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Deletes one or more messages from the thread
|
|
1754
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
1755
|
+
* message object with id property, or array of message objects
|
|
1756
|
+
* @returns Promise containing deletion result
|
|
1757
|
+
*/
|
|
1758
|
+
deleteMessages(messageIds) {
|
|
1759
|
+
const query = new URLSearchParams({
|
|
1760
|
+
networkId: this.networkId
|
|
1761
|
+
});
|
|
1762
|
+
return this.request(`/api/memory/network/messages/delete?${query.toString()}`, {
|
|
1763
|
+
method: "POST",
|
|
1764
|
+
body: { messageIds }
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1728
1767
|
};
|
|
1729
1768
|
|
|
1730
1769
|
// src/resources/vNextNetwork.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -164,14 +164,14 @@ interface GetVectorIndexResponse {
|
|
|
164
164
|
count: number;
|
|
165
165
|
}
|
|
166
166
|
interface SaveMessageToMemoryParams {
|
|
167
|
-
messages: MastraMessageV1[];
|
|
167
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
168
168
|
agentId: string;
|
|
169
169
|
}
|
|
170
170
|
interface SaveNetworkMessageToMemoryParams {
|
|
171
|
-
messages: MastraMessageV1[];
|
|
171
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
172
172
|
networkId: string;
|
|
173
173
|
}
|
|
174
|
-
type SaveMessageToMemoryResponse = MastraMessageV1[];
|
|
174
|
+
type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
|
|
175
175
|
interface CreateMemoryThreadParams {
|
|
176
176
|
title?: string;
|
|
177
177
|
metadata?: Record<string, any>;
|
|
@@ -601,6 +601,20 @@ declare class MemoryThread extends BaseResource {
|
|
|
601
601
|
* @returns Promise containing paginated thread messages with pagination metadata (total, page, perPage, hasMore)
|
|
602
602
|
*/
|
|
603
603
|
getMessagesPaginated({ selectBy, ...rest }: GetMemoryThreadMessagesPaginatedParams): Promise<GetMemoryThreadMessagesPaginatedResponse>;
|
|
604
|
+
/**
|
|
605
|
+
* Deletes one or more messages from the thread
|
|
606
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
607
|
+
* message object with id property, or array of message objects
|
|
608
|
+
* @returns Promise containing deletion result
|
|
609
|
+
*/
|
|
610
|
+
deleteMessages(messageIds: string | string[] | {
|
|
611
|
+
id: string;
|
|
612
|
+
} | {
|
|
613
|
+
id: string;
|
|
614
|
+
}[]): Promise<{
|
|
615
|
+
success: boolean;
|
|
616
|
+
message: string;
|
|
617
|
+
}>;
|
|
604
618
|
}
|
|
605
619
|
|
|
606
620
|
declare class Vector extends BaseResource {
|
|
@@ -998,6 +1012,20 @@ declare class NetworkMemoryThread extends BaseResource {
|
|
|
998
1012
|
* @returns Promise containing thread messages and UI messages
|
|
999
1013
|
*/
|
|
1000
1014
|
getMessages(params?: GetMemoryThreadMessagesParams): Promise<GetMemoryThreadMessagesResponse>;
|
|
1015
|
+
/**
|
|
1016
|
+
* Deletes one or more messages from the thread
|
|
1017
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
1018
|
+
* message object with id property, or array of message objects
|
|
1019
|
+
* @returns Promise containing deletion result
|
|
1020
|
+
*/
|
|
1021
|
+
deleteMessages(messageIds: string | string[] | {
|
|
1022
|
+
id: string;
|
|
1023
|
+
} | {
|
|
1024
|
+
id: string;
|
|
1025
|
+
}[]): Promise<{
|
|
1026
|
+
success: boolean;
|
|
1027
|
+
message: string;
|
|
1028
|
+
}>;
|
|
1001
1029
|
}
|
|
1002
1030
|
|
|
1003
1031
|
declare class VNextNetwork extends BaseResource {
|
package/dist/index.d.ts
CHANGED
|
@@ -164,14 +164,14 @@ interface GetVectorIndexResponse {
|
|
|
164
164
|
count: number;
|
|
165
165
|
}
|
|
166
166
|
interface SaveMessageToMemoryParams {
|
|
167
|
-
messages: MastraMessageV1[];
|
|
167
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
168
168
|
agentId: string;
|
|
169
169
|
}
|
|
170
170
|
interface SaveNetworkMessageToMemoryParams {
|
|
171
|
-
messages: MastraMessageV1[];
|
|
171
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
172
172
|
networkId: string;
|
|
173
173
|
}
|
|
174
|
-
type SaveMessageToMemoryResponse = MastraMessageV1[];
|
|
174
|
+
type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
|
|
175
175
|
interface CreateMemoryThreadParams {
|
|
176
176
|
title?: string;
|
|
177
177
|
metadata?: Record<string, any>;
|
|
@@ -601,6 +601,20 @@ declare class MemoryThread extends BaseResource {
|
|
|
601
601
|
* @returns Promise containing paginated thread messages with pagination metadata (total, page, perPage, hasMore)
|
|
602
602
|
*/
|
|
603
603
|
getMessagesPaginated({ selectBy, ...rest }: GetMemoryThreadMessagesPaginatedParams): Promise<GetMemoryThreadMessagesPaginatedResponse>;
|
|
604
|
+
/**
|
|
605
|
+
* Deletes one or more messages from the thread
|
|
606
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
607
|
+
* message object with id property, or array of message objects
|
|
608
|
+
* @returns Promise containing deletion result
|
|
609
|
+
*/
|
|
610
|
+
deleteMessages(messageIds: string | string[] | {
|
|
611
|
+
id: string;
|
|
612
|
+
} | {
|
|
613
|
+
id: string;
|
|
614
|
+
}[]): Promise<{
|
|
615
|
+
success: boolean;
|
|
616
|
+
message: string;
|
|
617
|
+
}>;
|
|
604
618
|
}
|
|
605
619
|
|
|
606
620
|
declare class Vector extends BaseResource {
|
|
@@ -998,6 +1012,20 @@ declare class NetworkMemoryThread extends BaseResource {
|
|
|
998
1012
|
* @returns Promise containing thread messages and UI messages
|
|
999
1013
|
*/
|
|
1000
1014
|
getMessages(params?: GetMemoryThreadMessagesParams): Promise<GetMemoryThreadMessagesResponse>;
|
|
1015
|
+
/**
|
|
1016
|
+
* Deletes one or more messages from the thread
|
|
1017
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
1018
|
+
* message object with id property, or array of message objects
|
|
1019
|
+
* @returns Promise containing deletion result
|
|
1020
|
+
*/
|
|
1021
|
+
deleteMessages(messageIds: string | string[] | {
|
|
1022
|
+
id: string;
|
|
1023
|
+
} | {
|
|
1024
|
+
id: string;
|
|
1025
|
+
}[]): Promise<{
|
|
1026
|
+
success: boolean;
|
|
1027
|
+
message: string;
|
|
1028
|
+
}>;
|
|
1001
1029
|
}
|
|
1002
1030
|
|
|
1003
1031
|
declare class VNextNetwork extends BaseResource {
|
package/dist/index.js
CHANGED
|
@@ -735,7 +735,12 @@ var Agent = class extends BaseResource {
|
|
|
735
735
|
this.processChatResponse({
|
|
736
736
|
stream: streamForProcessing,
|
|
737
737
|
update: ({ message }) => {
|
|
738
|
-
messages.
|
|
738
|
+
const existingIndex = messages.findIndex((m) => m.id === message.id);
|
|
739
|
+
if (existingIndex !== -1) {
|
|
740
|
+
messages[existingIndex] = message;
|
|
741
|
+
} else {
|
|
742
|
+
messages.push(message);
|
|
743
|
+
}
|
|
739
744
|
},
|
|
740
745
|
onFinish: async ({ finishReason, message }) => {
|
|
741
746
|
if (finishReason === "tool-calls") {
|
|
@@ -795,10 +800,12 @@ var Agent = class extends BaseResource {
|
|
|
795
800
|
this.processStreamResponse(
|
|
796
801
|
{
|
|
797
802
|
...processedParams,
|
|
798
|
-
messages: [...messageArray, ...messages, lastMessage]
|
|
803
|
+
messages: [...messageArray, ...messages.filter((m) => m.id !== lastMessage.id), lastMessage]
|
|
799
804
|
},
|
|
800
805
|
writable
|
|
801
|
-
)
|
|
806
|
+
).catch((error) => {
|
|
807
|
+
console.error("Error processing stream response:", error);
|
|
808
|
+
});
|
|
802
809
|
}
|
|
803
810
|
}
|
|
804
811
|
} else {
|
|
@@ -808,6 +815,8 @@ var Agent = class extends BaseResource {
|
|
|
808
815
|
}
|
|
809
816
|
},
|
|
810
817
|
lastMessage: void 0
|
|
818
|
+
}).catch((error) => {
|
|
819
|
+
console.error("Error processing stream response:", error);
|
|
811
820
|
});
|
|
812
821
|
} catch (error) {
|
|
813
822
|
console.error("Error processing stream response:", error);
|
|
@@ -971,6 +980,21 @@ var MemoryThread = class extends BaseResource {
|
|
|
971
980
|
});
|
|
972
981
|
return this.request(`/api/memory/threads/${this.threadId}/messages/paginated?${query.toString()}`);
|
|
973
982
|
}
|
|
983
|
+
/**
|
|
984
|
+
* Deletes one or more messages from the thread
|
|
985
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
986
|
+
* message object with id property, or array of message objects
|
|
987
|
+
* @returns Promise containing deletion result
|
|
988
|
+
*/
|
|
989
|
+
deleteMessages(messageIds) {
|
|
990
|
+
const query = new URLSearchParams({
|
|
991
|
+
agentId: this.agentId
|
|
992
|
+
});
|
|
993
|
+
return this.request(`/api/memory/messages/delete?${query.toString()}`, {
|
|
994
|
+
method: "POST",
|
|
995
|
+
body: { messageIds }
|
|
996
|
+
});
|
|
997
|
+
}
|
|
974
998
|
};
|
|
975
999
|
|
|
976
1000
|
// src/resources/vector.ts
|
|
@@ -1719,6 +1743,21 @@ var NetworkMemoryThread = class extends BaseResource {
|
|
|
1719
1743
|
});
|
|
1720
1744
|
return this.request(`/api/memory/network/threads/${this.threadId}/messages?${query.toString()}`);
|
|
1721
1745
|
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Deletes one or more messages from the thread
|
|
1748
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
1749
|
+
* message object with id property, or array of message objects
|
|
1750
|
+
* @returns Promise containing deletion result
|
|
1751
|
+
*/
|
|
1752
|
+
deleteMessages(messageIds) {
|
|
1753
|
+
const query = new URLSearchParams({
|
|
1754
|
+
networkId: this.networkId
|
|
1755
|
+
});
|
|
1756
|
+
return this.request(`/api/memory/network/messages/delete?${query.toString()}`, {
|
|
1757
|
+
method: "POST",
|
|
1758
|
+
body: { messageIds }
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1722
1761
|
};
|
|
1723
1762
|
|
|
1724
1763
|
// src/resources/vNextNetwork.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/client-js",
|
|
3
|
-
"version": "0.10.17-alpha.
|
|
3
|
+
"version": "0.10.17-alpha.3",
|
|
4
4
|
"description": "The official TypeScript library for the Mastra Client API",
|
|
5
5
|
"author": "",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"rxjs": "7.8.1",
|
|
35
35
|
"zod": "^3.25.67",
|
|
36
36
|
"zod-to-json-schema": "^3.24.5",
|
|
37
|
-
"@mastra/core": "0.12.0-alpha.
|
|
37
|
+
"@mastra/core": "0.12.0-alpha.3"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"zod": "^3.0.0"
|
package/src/index.test.ts
CHANGED
|
@@ -310,6 +310,114 @@ describe('MastraClient Resources', () => {
|
|
|
310
310
|
}
|
|
311
311
|
});
|
|
312
312
|
|
|
313
|
+
it('should stream responses with tool calls', async () => {
|
|
314
|
+
const firstMockChunk = `0:"test "
|
|
315
|
+
0:"response"
|
|
316
|
+
9:{"toolCallId":"tool1","toolName":"testTool","args":{"arg1":"value1"}}
|
|
317
|
+
e:{"finishReason":"tool-calls","usage":{"promptTokens":1,"completionTokens":1},"isContinued":false}
|
|
318
|
+
d:{"finishReason":"tool-calls","usage":{"promptTokens":2,"completionTokens":2}}
|
|
319
|
+
`;
|
|
320
|
+
|
|
321
|
+
const secondMockChunk = `0:"final response"
|
|
322
|
+
e:{"finishReason":"stop","usage":{"promptTokens":2,"completionTokens":2},"isContinued":false}
|
|
323
|
+
d:{"finishReason":"stop","usage":{"promptTokens":2,"completionTokens":2}}
|
|
324
|
+
`;
|
|
325
|
+
|
|
326
|
+
const firstResponseBody = new ReadableStream({
|
|
327
|
+
start(controller) {
|
|
328
|
+
controller.enqueue(new TextEncoder().encode(firstMockChunk));
|
|
329
|
+
controller.close();
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const secondResponseBody = new ReadableStream({
|
|
334
|
+
start(controller) {
|
|
335
|
+
controller.enqueue(new TextEncoder().encode(secondMockChunk));
|
|
336
|
+
controller.close();
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
(global.fetch as any)
|
|
341
|
+
.mockResolvedValueOnce(
|
|
342
|
+
new Response(firstResponseBody, {
|
|
343
|
+
status: 200,
|
|
344
|
+
headers: new Headers({ 'Content-Type': 'text/event-stream' }),
|
|
345
|
+
}),
|
|
346
|
+
)
|
|
347
|
+
.mockResolvedValueOnce(
|
|
348
|
+
new Response(secondResponseBody, {
|
|
349
|
+
status: 200,
|
|
350
|
+
headers: new Headers({ 'Content-Type': 'text/event-stream' }),
|
|
351
|
+
}),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const response = await agent.stream({
|
|
355
|
+
messages: [
|
|
356
|
+
{
|
|
357
|
+
role: 'user',
|
|
358
|
+
content: 'test',
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
clientTools: {
|
|
362
|
+
testTool: {
|
|
363
|
+
id: 'testTool',
|
|
364
|
+
description: 'Test Tool',
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
arg1: { type: 'string' },
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
execute: async () => {
|
|
372
|
+
return 'test result';
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
expect(response.body).toBeInstanceOf(ReadableStream);
|
|
379
|
+
const reader = response?.body?.getReader();
|
|
380
|
+
expect(reader).toBeDefined();
|
|
381
|
+
|
|
382
|
+
let output = '';
|
|
383
|
+
if (reader) {
|
|
384
|
+
while (true) {
|
|
385
|
+
const { value, done } = await reader.read();
|
|
386
|
+
if (done) break;
|
|
387
|
+
output += new TextDecoder().decode(value);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
expect(global.fetch).toHaveBeenCalledTimes(2);
|
|
392
|
+
|
|
393
|
+
const [secondUrl, secondConfig] = (global.fetch as any).mock.calls[1];
|
|
394
|
+
expect(secondUrl).toBe(`${clientOptions.baseUrl}/api/agents/test-agent/stream`);
|
|
395
|
+
|
|
396
|
+
const secondRequestBody = JSON.parse(secondConfig.body);
|
|
397
|
+
expect(secondRequestBody.messages).toHaveLength(2);
|
|
398
|
+
expect(secondRequestBody.messages[0].content).toBe('test');
|
|
399
|
+
expect(secondRequestBody.messages[1].content).toBe('test response');
|
|
400
|
+
expect(secondRequestBody.messages[1].parts).toEqual([
|
|
401
|
+
{
|
|
402
|
+
type: 'text',
|
|
403
|
+
text: 'test response',
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
type: 'tool-invocation',
|
|
407
|
+
toolInvocation: {
|
|
408
|
+
state: 'result',
|
|
409
|
+
step: 0,
|
|
410
|
+
toolCallId: 'tool1',
|
|
411
|
+
toolName: 'testTool',
|
|
412
|
+
args: {
|
|
413
|
+
arg1: 'value1',
|
|
414
|
+
},
|
|
415
|
+
result: 'test result',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
]);
|
|
419
|
+
});
|
|
420
|
+
|
|
313
421
|
it('should get agent tool', async () => {
|
|
314
422
|
const mockResponse = {
|
|
315
423
|
id: 'tool1',
|
package/src/resources/agent.ts
CHANGED
|
@@ -632,7 +632,13 @@ export class Agent extends BaseResource {
|
|
|
632
632
|
this.processChatResponse({
|
|
633
633
|
stream: streamForProcessing,
|
|
634
634
|
update: ({ message }) => {
|
|
635
|
-
messages.
|
|
635
|
+
const existingIndex = messages.findIndex(m => m.id === message.id);
|
|
636
|
+
|
|
637
|
+
if (existingIndex !== -1) {
|
|
638
|
+
messages[existingIndex] = message;
|
|
639
|
+
} else {
|
|
640
|
+
messages.push(message);
|
|
641
|
+
}
|
|
636
642
|
},
|
|
637
643
|
onFinish: async ({ finishReason, message }) => {
|
|
638
644
|
if (finishReason === 'tool-calls') {
|
|
@@ -711,10 +717,12 @@ export class Agent extends BaseResource {
|
|
|
711
717
|
this.processStreamResponse(
|
|
712
718
|
{
|
|
713
719
|
...processedParams,
|
|
714
|
-
messages: [...messageArray, ...messages, lastMessage],
|
|
720
|
+
messages: [...messageArray, ...messages.filter(m => m.id !== lastMessage.id), lastMessage],
|
|
715
721
|
},
|
|
716
722
|
writable,
|
|
717
|
-
)
|
|
723
|
+
).catch(error => {
|
|
724
|
+
console.error('Error processing stream response:', error);
|
|
725
|
+
});
|
|
718
726
|
}
|
|
719
727
|
}
|
|
720
728
|
} else {
|
|
@@ -724,6 +732,8 @@ export class Agent extends BaseResource {
|
|
|
724
732
|
}
|
|
725
733
|
},
|
|
726
734
|
lastMessage: undefined,
|
|
735
|
+
}).catch(error => {
|
|
736
|
+
console.error('Error processing stream response:', error);
|
|
727
737
|
});
|
|
728
738
|
} catch (error) {
|
|
729
739
|
console.error('Error processing stream response:', error);
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { describe, expect, beforeEach, it, vi } from 'vitest';
|
|
2
|
+
import { MemoryThread } from './memory-thread';
|
|
3
|
+
import type { ClientOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
// Mock fetch globally
|
|
6
|
+
global.fetch = vi.fn();
|
|
7
|
+
|
|
8
|
+
describe('MemoryThread', () => {
|
|
9
|
+
let thread: MemoryThread;
|
|
10
|
+
const clientOptions: ClientOptions = {
|
|
11
|
+
baseUrl: 'http://localhost:4111',
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: 'Bearer test-key',
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const threadId = 'test-thread-id';
|
|
17
|
+
const agentId = 'test-agent-id';
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
thread = new MemoryThread(clientOptions, threadId, agentId);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const mockFetchResponse = (data: any) => {
|
|
25
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
26
|
+
ok: true,
|
|
27
|
+
status: 200,
|
|
28
|
+
json: async () => data,
|
|
29
|
+
headers: new Headers({
|
|
30
|
+
'content-type': 'application/json',
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('get', () => {
|
|
36
|
+
it('should retrieve thread details', async () => {
|
|
37
|
+
const mockThread = {
|
|
38
|
+
id: threadId,
|
|
39
|
+
title: 'Test Thread',
|
|
40
|
+
metadata: { test: true },
|
|
41
|
+
createdAt: new Date().toISOString(),
|
|
42
|
+
updatedAt: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
mockFetchResponse(mockThread);
|
|
46
|
+
|
|
47
|
+
const result = await thread.get();
|
|
48
|
+
|
|
49
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
50
|
+
`http://localhost:4111/api/memory/threads/${threadId}?agentId=${agentId}`,
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
headers: expect.objectContaining({
|
|
53
|
+
Authorization: 'Bearer test-key',
|
|
54
|
+
}),
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
expect(result).toEqual(mockThread);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('update', () => {
|
|
62
|
+
it('should update thread properties', async () => {
|
|
63
|
+
const updateParams = {
|
|
64
|
+
title: 'Updated Title',
|
|
65
|
+
metadata: { updated: true },
|
|
66
|
+
resourceid: 'resource-1',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const mockUpdatedThread = {
|
|
70
|
+
id: threadId,
|
|
71
|
+
...updateParams,
|
|
72
|
+
createdAt: new Date().toISOString(),
|
|
73
|
+
updatedAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
mockFetchResponse(mockUpdatedThread);
|
|
77
|
+
|
|
78
|
+
const result = await thread.update(updateParams);
|
|
79
|
+
|
|
80
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
81
|
+
`http://localhost:4111/api/memory/threads/${threadId}?agentId=${agentId}`,
|
|
82
|
+
expect.objectContaining({
|
|
83
|
+
method: 'PATCH',
|
|
84
|
+
headers: expect.objectContaining({
|
|
85
|
+
Authorization: 'Bearer test-key',
|
|
86
|
+
}),
|
|
87
|
+
body: JSON.stringify(updateParams),
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
expect(result).toEqual(mockUpdatedThread);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('delete', () => {
|
|
95
|
+
it('should delete the thread', async () => {
|
|
96
|
+
const mockResponse = { result: 'Thread deleted' };
|
|
97
|
+
mockFetchResponse(mockResponse);
|
|
98
|
+
|
|
99
|
+
const result = await thread.delete();
|
|
100
|
+
|
|
101
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
102
|
+
`http://localhost:4111/api/memory/threads/${threadId}?agentId=${agentId}`,
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
method: 'DELETE',
|
|
105
|
+
headers: expect.objectContaining({
|
|
106
|
+
Authorization: 'Bearer test-key',
|
|
107
|
+
}),
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
expect(result).toEqual(mockResponse);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('getMessages', () => {
|
|
115
|
+
it('should retrieve thread messages', async () => {
|
|
116
|
+
const mockMessages = {
|
|
117
|
+
messages: [
|
|
118
|
+
{ id: 'msg-1', content: 'Hello', role: 'user' },
|
|
119
|
+
{ id: 'msg-2', content: 'Hi there', role: 'assistant' },
|
|
120
|
+
],
|
|
121
|
+
uiMessages: [
|
|
122
|
+
{ id: 'msg-1', content: 'Hello', role: 'user' },
|
|
123
|
+
{ id: 'msg-2', content: 'Hi there', role: 'assistant' },
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
mockFetchResponse(mockMessages);
|
|
128
|
+
|
|
129
|
+
const result = await thread.getMessages();
|
|
130
|
+
|
|
131
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
132
|
+
`http://localhost:4111/api/memory/threads/${threadId}/messages?agentId=${agentId}`,
|
|
133
|
+
expect.objectContaining({
|
|
134
|
+
headers: expect.objectContaining({
|
|
135
|
+
Authorization: 'Bearer test-key',
|
|
136
|
+
}),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
expect(result).toEqual(mockMessages);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should retrieve thread messages with limit', async () => {
|
|
143
|
+
const mockMessages = {
|
|
144
|
+
messages: [{ id: 'msg-1', content: 'Hello', role: 'user' }],
|
|
145
|
+
uiMessages: [{ id: 'msg-1', content: 'Hello', role: 'user' }],
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
mockFetchResponse(mockMessages);
|
|
149
|
+
|
|
150
|
+
const result = await thread.getMessages({ limit: 5 });
|
|
151
|
+
|
|
152
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
153
|
+
`http://localhost:4111/api/memory/threads/${threadId}/messages?agentId=${agentId}&limit=5`,
|
|
154
|
+
expect.objectContaining({
|
|
155
|
+
headers: expect.objectContaining({
|
|
156
|
+
Authorization: 'Bearer test-key',
|
|
157
|
+
}),
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
expect(result).toEqual(mockMessages);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('deleteMessages', () => {
|
|
165
|
+
it('should delete a single message by string ID', async () => {
|
|
166
|
+
const messageId = 'test-message-id';
|
|
167
|
+
const mockResponse = { success: true, message: '1 message deleted successfully' };
|
|
168
|
+
|
|
169
|
+
mockFetchResponse(mockResponse);
|
|
170
|
+
|
|
171
|
+
const result = await thread.deleteMessages(messageId);
|
|
172
|
+
|
|
173
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
174
|
+
`http://localhost:4111/api/memory/messages/delete?agentId=${agentId}`,
|
|
175
|
+
expect.objectContaining({
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: expect.objectContaining({
|
|
178
|
+
'content-type': 'application/json',
|
|
179
|
+
Authorization: 'Bearer test-key',
|
|
180
|
+
}),
|
|
181
|
+
body: JSON.stringify({ messageIds: messageId }),
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
184
|
+
expect(result).toEqual(mockResponse);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should delete multiple messages by array of string IDs', async () => {
|
|
188
|
+
const messageIds = ['msg-1', 'msg-2', 'msg-3'];
|
|
189
|
+
const mockResponse = { success: true, message: '3 messages deleted successfully' };
|
|
190
|
+
|
|
191
|
+
mockFetchResponse(mockResponse);
|
|
192
|
+
|
|
193
|
+
const result = await thread.deleteMessages(messageIds);
|
|
194
|
+
|
|
195
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
196
|
+
`http://localhost:4111/api/memory/messages/delete?agentId=${agentId}`,
|
|
197
|
+
expect.objectContaining({
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: expect.objectContaining({
|
|
200
|
+
'content-type': 'application/json',
|
|
201
|
+
Authorization: 'Bearer test-key',
|
|
202
|
+
}),
|
|
203
|
+
body: JSON.stringify({ messageIds }),
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
expect(result).toEqual(mockResponse);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should delete a message by object with id property', async () => {
|
|
210
|
+
const messageObj = { id: 'test-message-id' };
|
|
211
|
+
const mockResponse = { success: true, message: '1 message deleted successfully' };
|
|
212
|
+
|
|
213
|
+
mockFetchResponse(mockResponse);
|
|
214
|
+
|
|
215
|
+
const result = await thread.deleteMessages(messageObj);
|
|
216
|
+
|
|
217
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
218
|
+
`http://localhost:4111/api/memory/messages/delete?agentId=${agentId}`,
|
|
219
|
+
expect.objectContaining({
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: expect.objectContaining({
|
|
222
|
+
'content-type': 'application/json',
|
|
223
|
+
Authorization: 'Bearer test-key',
|
|
224
|
+
}),
|
|
225
|
+
body: JSON.stringify({ messageIds: messageObj }),
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
expect(result).toEqual(mockResponse);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should delete messages by array of objects', async () => {
|
|
232
|
+
const messageObjs = [{ id: 'msg-1' }, { id: 'msg-2' }];
|
|
233
|
+
const mockResponse = { success: true, message: '2 messages deleted successfully' };
|
|
234
|
+
|
|
235
|
+
mockFetchResponse(mockResponse);
|
|
236
|
+
|
|
237
|
+
const result = await thread.deleteMessages(messageObjs);
|
|
238
|
+
|
|
239
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
240
|
+
`http://localhost:4111/api/memory/messages/delete?agentId=${agentId}`,
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
method: 'POST',
|
|
243
|
+
headers: expect.objectContaining({
|
|
244
|
+
'content-type': 'application/json',
|
|
245
|
+
Authorization: 'Bearer test-key',
|
|
246
|
+
}),
|
|
247
|
+
body: JSON.stringify({ messageIds: messageObjs }),
|
|
248
|
+
}),
|
|
249
|
+
);
|
|
250
|
+
expect(result).toEqual(mockResponse);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle empty array', async () => {
|
|
254
|
+
const messageIds: string[] = [];
|
|
255
|
+
|
|
256
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
257
|
+
ok: false,
|
|
258
|
+
status: 400,
|
|
259
|
+
statusText: 'Bad Request',
|
|
260
|
+
json: async () => ({ error: 'messageIds array cannot be empty' }),
|
|
261
|
+
headers: new Headers({
|
|
262
|
+
'content-type': 'application/json',
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await expect(thread.deleteMessages(messageIds)).rejects.toThrow();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should handle bulk delete errors', async () => {
|
|
270
|
+
const messageIds = ['msg-1', 'msg-2'];
|
|
271
|
+
|
|
272
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
273
|
+
ok: false,
|
|
274
|
+
status: 500,
|
|
275
|
+
statusText: 'Internal Server Error',
|
|
276
|
+
json: async () => ({ error: 'Database error' }),
|
|
277
|
+
headers: new Headers({
|
|
278
|
+
'content-type': 'application/json',
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await expect(thread.deleteMessages(messageIds)).rejects.toThrow();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
@@ -78,4 +78,22 @@ export class MemoryThread extends BaseResource {
|
|
|
78
78
|
});
|
|
79
79
|
return this.request(`/api/memory/threads/${this.threadId}/messages/paginated?${query.toString()}`);
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Deletes one or more messages from the thread
|
|
84
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
85
|
+
* message object with id property, or array of message objects
|
|
86
|
+
* @returns Promise containing deletion result
|
|
87
|
+
*/
|
|
88
|
+
deleteMessages(
|
|
89
|
+
messageIds: string | string[] | { id: string } | { id: string }[],
|
|
90
|
+
): Promise<{ success: boolean; message: string }> {
|
|
91
|
+
const query = new URLSearchParams({
|
|
92
|
+
agentId: this.agentId,
|
|
93
|
+
});
|
|
94
|
+
return this.request(`/api/memory/messages/delete?${query.toString()}`, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
body: { messageIds },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
81
99
|
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { describe, expect, beforeEach, it, vi } from 'vitest';
|
|
2
|
+
import { NetworkMemoryThread } from './network-memory-thread';
|
|
3
|
+
import type { ClientOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
// Mock fetch globally
|
|
6
|
+
global.fetch = vi.fn();
|
|
7
|
+
|
|
8
|
+
describe('NetworkMemoryThread', () => {
|
|
9
|
+
let thread: NetworkMemoryThread;
|
|
10
|
+
const clientOptions: ClientOptions = {
|
|
11
|
+
baseUrl: 'http://localhost:4111',
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: 'Bearer test-key',
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const threadId = 'test-thread-id';
|
|
17
|
+
const networkId = 'test-network-id';
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
thread = new NetworkMemoryThread(clientOptions, threadId, networkId);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const mockFetchResponse = (data: any) => {
|
|
25
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
26
|
+
ok: true,
|
|
27
|
+
status: 200,
|
|
28
|
+
json: async () => data,
|
|
29
|
+
headers: new Headers({
|
|
30
|
+
'content-type': 'application/json',
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('get', () => {
|
|
36
|
+
it('should retrieve thread details', async () => {
|
|
37
|
+
const mockThread = {
|
|
38
|
+
id: threadId,
|
|
39
|
+
title: 'Test Thread',
|
|
40
|
+
metadata: { test: true },
|
|
41
|
+
createdAt: new Date().toISOString(),
|
|
42
|
+
updatedAt: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
mockFetchResponse(mockThread);
|
|
46
|
+
|
|
47
|
+
const result = await thread.get();
|
|
48
|
+
|
|
49
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
50
|
+
`http://localhost:4111/api/memory/network/threads/${threadId}?networkId=${networkId}`,
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
headers: expect.objectContaining({
|
|
53
|
+
Authorization: 'Bearer test-key',
|
|
54
|
+
}),
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
expect(result).toEqual(mockThread);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('update', () => {
|
|
62
|
+
it('should update thread properties', async () => {
|
|
63
|
+
const updateParams = {
|
|
64
|
+
title: 'Updated Title',
|
|
65
|
+
metadata: { updated: true },
|
|
66
|
+
resourceid: 'resource-1',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const mockUpdatedThread = {
|
|
70
|
+
id: threadId,
|
|
71
|
+
...updateParams,
|
|
72
|
+
createdAt: new Date().toISOString(),
|
|
73
|
+
updatedAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
mockFetchResponse(mockUpdatedThread);
|
|
77
|
+
|
|
78
|
+
const result = await thread.update(updateParams);
|
|
79
|
+
|
|
80
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
81
|
+
`http://localhost:4111/api/memory/network/threads/${threadId}?networkId=${networkId}`,
|
|
82
|
+
expect.objectContaining({
|
|
83
|
+
method: 'PATCH',
|
|
84
|
+
headers: expect.objectContaining({
|
|
85
|
+
Authorization: 'Bearer test-key',
|
|
86
|
+
}),
|
|
87
|
+
body: JSON.stringify(updateParams),
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
expect(result).toEqual(mockUpdatedThread);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('delete', () => {
|
|
95
|
+
it('should delete the thread', async () => {
|
|
96
|
+
const mockResponse = { result: 'Thread deleted' };
|
|
97
|
+
mockFetchResponse(mockResponse);
|
|
98
|
+
|
|
99
|
+
const result = await thread.delete();
|
|
100
|
+
|
|
101
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
102
|
+
`http://localhost:4111/api/memory/network/threads/${threadId}?networkId=${networkId}`,
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
method: 'DELETE',
|
|
105
|
+
headers: expect.objectContaining({
|
|
106
|
+
Authorization: 'Bearer test-key',
|
|
107
|
+
}),
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
expect(result).toEqual(mockResponse);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('getMessages', () => {
|
|
115
|
+
it('should retrieve thread messages', async () => {
|
|
116
|
+
const mockMessages = {
|
|
117
|
+
messages: [
|
|
118
|
+
{ id: 'msg-1', content: 'Hello', role: 'user' },
|
|
119
|
+
{ id: 'msg-2', content: 'Hi there', role: 'assistant' },
|
|
120
|
+
],
|
|
121
|
+
uiMessages: [
|
|
122
|
+
{ id: 'msg-1', content: 'Hello', role: 'user' },
|
|
123
|
+
{ id: 'msg-2', content: 'Hi there', role: 'assistant' },
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
mockFetchResponse(mockMessages);
|
|
128
|
+
|
|
129
|
+
const result = await thread.getMessages();
|
|
130
|
+
|
|
131
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
132
|
+
`http://localhost:4111/api/memory/network/threads/${threadId}/messages?networkId=${networkId}`,
|
|
133
|
+
expect.objectContaining({
|
|
134
|
+
headers: expect.objectContaining({
|
|
135
|
+
Authorization: 'Bearer test-key',
|
|
136
|
+
}),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
expect(result).toEqual(mockMessages);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should retrieve thread messages with limit', async () => {
|
|
143
|
+
const mockMessages = {
|
|
144
|
+
messages: [{ id: 'msg-1', content: 'Hello', role: 'user' }],
|
|
145
|
+
uiMessages: [{ id: 'msg-1', content: 'Hello', role: 'user' }],
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
mockFetchResponse(mockMessages);
|
|
149
|
+
|
|
150
|
+
const result = await thread.getMessages({ limit: 5 });
|
|
151
|
+
|
|
152
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
153
|
+
`http://localhost:4111/api/memory/network/threads/${threadId}/messages?networkId=${networkId}&limit=5`,
|
|
154
|
+
expect.objectContaining({
|
|
155
|
+
headers: expect.objectContaining({
|
|
156
|
+
Authorization: 'Bearer test-key',
|
|
157
|
+
}),
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
expect(result).toEqual(mockMessages);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('deleteMessages', () => {
|
|
165
|
+
it('should delete a single message by string ID', async () => {
|
|
166
|
+
const messageId = 'test-message-id';
|
|
167
|
+
const mockResponse = { success: true, message: '1 message deleted successfully' };
|
|
168
|
+
|
|
169
|
+
mockFetchResponse(mockResponse);
|
|
170
|
+
|
|
171
|
+
const result = await thread.deleteMessages(messageId);
|
|
172
|
+
|
|
173
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
174
|
+
`http://localhost:4111/api/memory/network/messages/delete?networkId=${networkId}`,
|
|
175
|
+
expect.objectContaining({
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: expect.objectContaining({
|
|
178
|
+
'content-type': 'application/json',
|
|
179
|
+
Authorization: 'Bearer test-key',
|
|
180
|
+
}),
|
|
181
|
+
body: JSON.stringify({ messageIds: messageId }),
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
184
|
+
expect(result).toEqual(mockResponse);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should delete multiple messages by array of string IDs', async () => {
|
|
188
|
+
const messageIds = ['msg-1', 'msg-2', 'msg-3'];
|
|
189
|
+
const mockResponse = { success: true, message: '3 messages deleted successfully' };
|
|
190
|
+
|
|
191
|
+
mockFetchResponse(mockResponse);
|
|
192
|
+
|
|
193
|
+
const result = await thread.deleteMessages(messageIds);
|
|
194
|
+
|
|
195
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
196
|
+
`http://localhost:4111/api/memory/network/messages/delete?networkId=${networkId}`,
|
|
197
|
+
expect.objectContaining({
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: expect.objectContaining({
|
|
200
|
+
'content-type': 'application/json',
|
|
201
|
+
Authorization: 'Bearer test-key',
|
|
202
|
+
}),
|
|
203
|
+
body: JSON.stringify({ messageIds }),
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
expect(result).toEqual(mockResponse);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should delete a message by object with id property', async () => {
|
|
210
|
+
const messageObj = { id: 'test-message-id' };
|
|
211
|
+
const mockResponse = { success: true, message: '1 message deleted successfully' };
|
|
212
|
+
|
|
213
|
+
mockFetchResponse(mockResponse);
|
|
214
|
+
|
|
215
|
+
const result = await thread.deleteMessages(messageObj);
|
|
216
|
+
|
|
217
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
218
|
+
`http://localhost:4111/api/memory/network/messages/delete?networkId=${networkId}`,
|
|
219
|
+
expect.objectContaining({
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: expect.objectContaining({
|
|
222
|
+
'content-type': 'application/json',
|
|
223
|
+
Authorization: 'Bearer test-key',
|
|
224
|
+
}),
|
|
225
|
+
body: JSON.stringify({ messageIds: messageObj }),
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
expect(result).toEqual(mockResponse);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should delete messages by array of objects', async () => {
|
|
232
|
+
const messageObjs = [{ id: 'msg-1' }, { id: 'msg-2' }];
|
|
233
|
+
const mockResponse = { success: true, message: '2 messages deleted successfully' };
|
|
234
|
+
|
|
235
|
+
mockFetchResponse(mockResponse);
|
|
236
|
+
|
|
237
|
+
const result = await thread.deleteMessages(messageObjs);
|
|
238
|
+
|
|
239
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
240
|
+
`http://localhost:4111/api/memory/network/messages/delete?networkId=${networkId}`,
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
method: 'POST',
|
|
243
|
+
headers: expect.objectContaining({
|
|
244
|
+
'content-type': 'application/json',
|
|
245
|
+
Authorization: 'Bearer test-key',
|
|
246
|
+
}),
|
|
247
|
+
body: JSON.stringify({ messageIds: messageObjs }),
|
|
248
|
+
}),
|
|
249
|
+
);
|
|
250
|
+
expect(result).toEqual(mockResponse);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle delete messages errors', async () => {
|
|
254
|
+
const messageId = 'non-existent-id';
|
|
255
|
+
|
|
256
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
257
|
+
ok: false,
|
|
258
|
+
status: 404,
|
|
259
|
+
statusText: 'Not Found',
|
|
260
|
+
json: async () => ({ error: 'Message not found' }),
|
|
261
|
+
headers: new Headers({
|
|
262
|
+
'content-type': 'application/json',
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await expect(thread.deleteMessages(messageId)).rejects.toThrow();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -60,4 +60,22 @@ export class NetworkMemoryThread extends BaseResource {
|
|
|
60
60
|
});
|
|
61
61
|
return this.request(`/api/memory/network/threads/${this.threadId}/messages?${query.toString()}`);
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deletes one or more messages from the thread
|
|
66
|
+
* @param messageIds - Can be a single message ID (string), array of message IDs,
|
|
67
|
+
* message object with id property, or array of message objects
|
|
68
|
+
* @returns Promise containing deletion result
|
|
69
|
+
*/
|
|
70
|
+
deleteMessages(
|
|
71
|
+
messageIds: string | string[] | { id: string } | { id: string }[],
|
|
72
|
+
): Promise<{ success: boolean; message: string }> {
|
|
73
|
+
const query = new URLSearchParams({
|
|
74
|
+
networkId: this.networkId,
|
|
75
|
+
});
|
|
76
|
+
return this.request(`/api/memory/network/messages/delete?${query.toString()}`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: { messageIds },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
63
81
|
}
|
package/src/types.ts
CHANGED
|
@@ -198,16 +198,16 @@ export interface GetVectorIndexResponse {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
export interface SaveMessageToMemoryParams {
|
|
201
|
-
messages: MastraMessageV1[];
|
|
201
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
202
202
|
agentId: string;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
export interface SaveNetworkMessageToMemoryParams {
|
|
206
|
-
messages: MastraMessageV1[];
|
|
206
|
+
messages: (MastraMessageV1 | MastraMessageV2)[];
|
|
207
207
|
networkId: string;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
export type SaveMessageToMemoryResponse = MastraMessageV1[];
|
|
210
|
+
export type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
|
|
211
211
|
|
|
212
212
|
export interface CreateMemoryThreadParams {
|
|
213
213
|
title?: string;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import type { MastraMessageV1, MastraMessageV2 } from '@mastra/core';
|
|
3
|
+
import { MastraClient } from './client';
|
|
4
|
+
|
|
5
|
+
describe('V2 Message Format Support', () => {
|
|
6
|
+
let client: MastraClient;
|
|
7
|
+
const agentId = 'test-agent';
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
global.fetch = vi.fn();
|
|
11
|
+
client = new MastraClient({
|
|
12
|
+
baseUrl: 'http://localhost:3000',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should send v1 messages successfully', async () => {
|
|
17
|
+
const v1Messages: MastraMessageV1[] = [
|
|
18
|
+
{
|
|
19
|
+
id: 'msg-v1-1',
|
|
20
|
+
role: 'user',
|
|
21
|
+
content: 'Hello from v1!',
|
|
22
|
+
type: 'text',
|
|
23
|
+
createdAt: new Date(),
|
|
24
|
+
threadId: 'thread-123',
|
|
25
|
+
resourceId: 'resource-123',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
30
|
+
ok: true,
|
|
31
|
+
json: async () => v1Messages,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = await client.saveMessageToMemory({
|
|
35
|
+
agentId,
|
|
36
|
+
messages: v1Messages,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual(v1Messages);
|
|
40
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
41
|
+
expect.stringContaining('/api/memory/save-messages'),
|
|
42
|
+
expect.objectContaining({
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body: JSON.stringify({ agentId, messages: v1Messages }),
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should send v2 messages successfully', async () => {
|
|
50
|
+
const v2Messages: MastraMessageV2[] = [
|
|
51
|
+
{
|
|
52
|
+
id: 'msg-v2-1',
|
|
53
|
+
role: 'assistant',
|
|
54
|
+
createdAt: new Date(),
|
|
55
|
+
threadId: 'thread-123',
|
|
56
|
+
resourceId: 'resource-123',
|
|
57
|
+
content: {
|
|
58
|
+
format: 2,
|
|
59
|
+
parts: [{ type: 'text', text: 'Hello from v2!' }],
|
|
60
|
+
content: 'Hello from v2!',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
66
|
+
ok: true,
|
|
67
|
+
json: async () => v2Messages,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await client.saveMessageToMemory({
|
|
71
|
+
agentId,
|
|
72
|
+
messages: v2Messages,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(result).toEqual(v2Messages);
|
|
76
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
77
|
+
expect.stringContaining('/api/memory/save-messages'),
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
method: 'POST',
|
|
80
|
+
body: JSON.stringify({ agentId, messages: v2Messages }),
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should send mixed v1 and v2 messages successfully', async () => {
|
|
86
|
+
const mixedMessages: (MastraMessageV1 | MastraMessageV2)[] = [
|
|
87
|
+
{
|
|
88
|
+
id: 'msg-v1-1',
|
|
89
|
+
role: 'user',
|
|
90
|
+
content: 'Question in v1 format',
|
|
91
|
+
type: 'text',
|
|
92
|
+
createdAt: new Date(),
|
|
93
|
+
threadId: 'thread-123',
|
|
94
|
+
resourceId: 'resource-123',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'msg-v2-1',
|
|
98
|
+
role: 'assistant',
|
|
99
|
+
createdAt: new Date(),
|
|
100
|
+
threadId: 'thread-123',
|
|
101
|
+
resourceId: 'resource-123',
|
|
102
|
+
content: {
|
|
103
|
+
format: 2,
|
|
104
|
+
parts: [
|
|
105
|
+
{ type: 'text', text: 'Answer in v2 format' },
|
|
106
|
+
{
|
|
107
|
+
type: 'tool-invocation',
|
|
108
|
+
toolInvocation: {
|
|
109
|
+
state: 'result' as const,
|
|
110
|
+
toolCallId: 'call-123',
|
|
111
|
+
toolName: 'calculator',
|
|
112
|
+
args: { a: 1, b: 2 },
|
|
113
|
+
result: '3',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
toolInvocations: [
|
|
118
|
+
{
|
|
119
|
+
state: 'result' as const,
|
|
120
|
+
toolCallId: 'call-123',
|
|
121
|
+
toolName: 'calculator',
|
|
122
|
+
args: { a: 1, b: 2 },
|
|
123
|
+
result: '3',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
131
|
+
ok: true,
|
|
132
|
+
json: async () => mixedMessages,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = await client.saveMessageToMemory({
|
|
136
|
+
agentId,
|
|
137
|
+
messages: mixedMessages,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(result).toEqual(mixedMessages);
|
|
141
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
142
|
+
expect.stringContaining('/api/memory/save-messages'),
|
|
143
|
+
expect.objectContaining({
|
|
144
|
+
method: 'POST',
|
|
145
|
+
body: JSON.stringify({ agentId, messages: mixedMessages }),
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle v2 messages with attachments', async () => {
|
|
151
|
+
const v2MessageWithAttachments: MastraMessageV2 = {
|
|
152
|
+
id: 'msg-v2-att',
|
|
153
|
+
role: 'user',
|
|
154
|
+
createdAt: new Date(),
|
|
155
|
+
threadId: 'thread-123',
|
|
156
|
+
resourceId: 'resource-123',
|
|
157
|
+
content: {
|
|
158
|
+
format: 2,
|
|
159
|
+
parts: [
|
|
160
|
+
{ type: 'text', text: 'Check out this image:' },
|
|
161
|
+
{ type: 'file', data: 'data:image/png;base64,iVBORw0...', mimeType: 'image/png' },
|
|
162
|
+
],
|
|
163
|
+
experimental_attachments: [{ url: 'data:image/png;base64,iVBORw0...', contentType: 'image/png' }],
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
(global.fetch as any).mockResolvedValueOnce({
|
|
168
|
+
ok: true,
|
|
169
|
+
json: async () => [v2MessageWithAttachments],
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const result = await client.saveMessageToMemory({
|
|
173
|
+
agentId,
|
|
174
|
+
messages: [v2MessageWithAttachments],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(result).toHaveLength(1);
|
|
178
|
+
expect(result[0]).toEqual(v2MessageWithAttachments);
|
|
179
|
+
});
|
|
180
|
+
});
|