@mastra/client-js 0.0.0-transpile-packages-20250730132657 → 0.0.0-transpile-packages-20250731152758
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 +4 -0
- package/CHANGELOG.md +141 -3
- package/dist/adapters/agui.d.ts +23 -0
- package/dist/adapters/agui.d.ts.map +1 -0
- package/dist/client.d.ts +265 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/index.cjs +44 -3
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -1299
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -3
- package/dist/index.js.map +1 -0
- package/dist/resources/a2a.d.ts +44 -0
- package/dist/resources/a2a.d.ts.map +1 -0
- package/dist/resources/agent.d.ts +112 -0
- package/dist/resources/agent.d.ts.map +1 -0
- package/dist/resources/base.d.ts +13 -0
- package/dist/resources/base.d.ts.map +1 -0
- package/dist/resources/index.d.ts +11 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/legacy-workflow.d.ts +87 -0
- package/dist/resources/legacy-workflow.d.ts.map +1 -0
- package/dist/resources/mcp-tool.d.ts +27 -0
- package/dist/resources/mcp-tool.d.ts.map +1 -0
- package/dist/resources/memory-thread.d.ts +53 -0
- package/dist/resources/memory-thread.d.ts.map +1 -0
- package/dist/resources/network-memory-thread.d.ts +47 -0
- package/dist/resources/network-memory-thread.d.ts.map +1 -0
- package/dist/resources/network.d.ts +30 -0
- package/dist/resources/network.d.ts.map +1 -0
- package/dist/resources/tool.d.ts +23 -0
- package/dist/resources/tool.d.ts.map +1 -0
- package/dist/resources/vNextNetwork.d.ts +42 -0
- package/dist/resources/vNextNetwork.d.ts.map +1 -0
- package/dist/resources/vector.d.ts +48 -0
- package/dist/resources/vector.d.ts.map +1 -0
- package/dist/resources/workflow.d.ts +154 -0
- package/dist/resources/workflow.d.ts.map +1 -0
- package/dist/types.d.ts +422 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/process-client-tools.d.ts +3 -0
- package/dist/utils/process-client-tools.d.ts.map +1 -0
- package/dist/utils/zod-to-json-schema.d.ts +105 -0
- package/dist/utils/zod-to-json-schema.d.ts.map +1 -0
- package/integration-tests/agui-adapter.test.ts +122 -0
- package/integration-tests/package.json +18 -0
- package/integration-tests/src/mastra/index.ts +35 -0
- package/integration-tests/vitest.config.ts +9 -0
- package/package.json +10 -7
- package/src/adapters/agui.test.ts +145 -3
- package/src/index.test.ts +108 -0
- package/src/index.ts +1 -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 +7 -7
- package/src/v2-messages.test.ts +180 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +22 -0
- package/dist/index.d.cts +0 -1299
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ZodSchema } from 'zod';
|
|
2
|
+
export declare function zodToJsonSchema<T extends ZodSchema | any>(zodSchema: T): T | ({
|
|
3
|
+
anyOf: import("zod-to-json-schema").JsonSchema7DateType[];
|
|
4
|
+
} & {
|
|
5
|
+
title?: string;
|
|
6
|
+
default?: any;
|
|
7
|
+
description?: string;
|
|
8
|
+
markdownDescription?: string;
|
|
9
|
+
} & {
|
|
10
|
+
$schema?: string | undefined;
|
|
11
|
+
definitions?: {
|
|
12
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
13
|
+
} | undefined;
|
|
14
|
+
}) | ({
|
|
15
|
+
type: "object" | "array";
|
|
16
|
+
} & {
|
|
17
|
+
title?: string;
|
|
18
|
+
default?: any;
|
|
19
|
+
description?: string;
|
|
20
|
+
markdownDescription?: string;
|
|
21
|
+
} & {
|
|
22
|
+
$schema?: string | undefined;
|
|
23
|
+
definitions?: {
|
|
24
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
25
|
+
} | undefined;
|
|
26
|
+
}) | ({
|
|
27
|
+
type: ("string" | "number" | "boolean" | "integer" | "null") | ("string" | "number" | "boolean" | "integer" | "null")[];
|
|
28
|
+
} & {
|
|
29
|
+
title?: string;
|
|
30
|
+
default?: any;
|
|
31
|
+
description?: string;
|
|
32
|
+
markdownDescription?: string;
|
|
33
|
+
} & {
|
|
34
|
+
$schema?: string | undefined;
|
|
35
|
+
definitions?: {
|
|
36
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
37
|
+
} | undefined;
|
|
38
|
+
}) | ({
|
|
39
|
+
anyOf: import("zod-to-json-schema").JsonSchema7Type[];
|
|
40
|
+
} & {
|
|
41
|
+
title?: string;
|
|
42
|
+
default?: any;
|
|
43
|
+
description?: string;
|
|
44
|
+
markdownDescription?: string;
|
|
45
|
+
} & {
|
|
46
|
+
$schema?: string | undefined;
|
|
47
|
+
definitions?: {
|
|
48
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
49
|
+
} | undefined;
|
|
50
|
+
}) | (import("zod-to-json-schema").JsonSchema7UndefinedType & {
|
|
51
|
+
title?: string;
|
|
52
|
+
default?: any;
|
|
53
|
+
description?: string;
|
|
54
|
+
markdownDescription?: string;
|
|
55
|
+
} & {
|
|
56
|
+
$schema?: string | undefined;
|
|
57
|
+
definitions?: {
|
|
58
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
59
|
+
} | undefined;
|
|
60
|
+
}) | (import("zod-to-json-schema").JsonSchema7AnyType & {
|
|
61
|
+
title?: string;
|
|
62
|
+
default?: any;
|
|
63
|
+
description?: string;
|
|
64
|
+
markdownDescription?: string;
|
|
65
|
+
} & {
|
|
66
|
+
$schema?: string | undefined;
|
|
67
|
+
definitions?: {
|
|
68
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
69
|
+
} | undefined;
|
|
70
|
+
}) | ({
|
|
71
|
+
anyOf: [import("zod-to-json-schema").JsonSchema7Type, import("zod-to-json-schema").JsonSchema7NullType];
|
|
72
|
+
} & {
|
|
73
|
+
title?: string;
|
|
74
|
+
default?: any;
|
|
75
|
+
description?: string;
|
|
76
|
+
markdownDescription?: string;
|
|
77
|
+
} & {
|
|
78
|
+
$schema?: string | undefined;
|
|
79
|
+
definitions?: {
|
|
80
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
81
|
+
} | undefined;
|
|
82
|
+
}) | ({
|
|
83
|
+
type: [string, "null"];
|
|
84
|
+
} & {
|
|
85
|
+
title?: string;
|
|
86
|
+
default?: any;
|
|
87
|
+
description?: string;
|
|
88
|
+
markdownDescription?: string;
|
|
89
|
+
} & {
|
|
90
|
+
$schema?: string | undefined;
|
|
91
|
+
definitions?: {
|
|
92
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
93
|
+
} | undefined;
|
|
94
|
+
}) | (import("zod-to-json-schema").JsonSchema7AllOfType & {
|
|
95
|
+
title?: string;
|
|
96
|
+
default?: any;
|
|
97
|
+
description?: string;
|
|
98
|
+
markdownDescription?: string;
|
|
99
|
+
} & {
|
|
100
|
+
$schema?: string | undefined;
|
|
101
|
+
definitions?: {
|
|
102
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
103
|
+
} | undefined;
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=zod-to-json-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-to-json-schema.d.ts","sourceRoot":"","sources":["../../src/utils/zod-to-json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGhC,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,GAAG,GAAG,EAAE,SAAS,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAMtE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import type { BaseEvent } from '@ag-ui/client';
|
|
5
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
6
|
+
import { AGUIAdapter } from '../src/adapters/agui';
|
|
7
|
+
|
|
8
|
+
// Helper to find an available port
|
|
9
|
+
async function getAvailablePort(): Promise<number> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const server = createServer();
|
|
12
|
+
server.listen(0, () => {
|
|
13
|
+
const { port } = server.address() as { port: number };
|
|
14
|
+
server.close(() => resolve(port));
|
|
15
|
+
});
|
|
16
|
+
server.on('error', reject);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('AGUIAdapter Integration Tests', () => {
|
|
21
|
+
let mastraServer: ReturnType<typeof spawn>;
|
|
22
|
+
let port: number;
|
|
23
|
+
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
port = await getAvailablePort();
|
|
26
|
+
|
|
27
|
+
// Run mastra dev from the integration tests directory using the built CLI
|
|
28
|
+
const cliPath = path.resolve(import.meta.dirname, '..', '..', '..', 'packages', 'cli', 'dist', 'index.js');
|
|
29
|
+
mastraServer = spawn('node', [cliPath, 'dev', '--port', port.toString()], {
|
|
30
|
+
cwd: path.resolve(import.meta.dirname),
|
|
31
|
+
stdio: 'pipe',
|
|
32
|
+
detached: true, // Run in a new process group so we can kill it and children
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Wait for server to be ready
|
|
36
|
+
await new Promise<void>((resolve, reject) => {
|
|
37
|
+
let output = '';
|
|
38
|
+
mastraServer.stdout?.on('data', data => {
|
|
39
|
+
output += data.toString();
|
|
40
|
+
console.log(output);
|
|
41
|
+
if (output.includes('http://localhost:')) {
|
|
42
|
+
resolve();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
mastraServer.stderr?.on('data', data => {
|
|
46
|
+
console.error('Mastra server error:', data.toString());
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
setTimeout(() => reject(new Error('Mastra server failed to start')), 10000);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
// Kill the server and its process group
|
|
55
|
+
if (mastraServer?.pid) {
|
|
56
|
+
try {
|
|
57
|
+
process.kill(-mastraServer.pid, 'SIGTERM');
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error('Failed to kill Mastra server:', e);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should correctly pass parameters to agent stream method with real server', async () => {
|
|
65
|
+
// Create a client agent that communicates with the real server
|
|
66
|
+
const { Agent: ClientAgent } = await import('../src/resources/agent');
|
|
67
|
+
const clientAgent = new ClientAgent(
|
|
68
|
+
{
|
|
69
|
+
baseUrl: `http://localhost:${port}`,
|
|
70
|
+
apiKey: 'test-key',
|
|
71
|
+
},
|
|
72
|
+
'test',
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const adapter = new AGUIAdapter({
|
|
76
|
+
agent: clientAgent,
|
|
77
|
+
agentId: 'test',
|
|
78
|
+
resourceId: 'testAgent',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const input = {
|
|
82
|
+
threadId: 'test-thread-id',
|
|
83
|
+
runId: 'test-run-id',
|
|
84
|
+
messages: [
|
|
85
|
+
{
|
|
86
|
+
id: '1',
|
|
87
|
+
role: 'user' as const,
|
|
88
|
+
content: 'Hello',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
tools: [],
|
|
92
|
+
context: [],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const observable = adapter['run'](input);
|
|
96
|
+
const events: BaseEvent[] = [];
|
|
97
|
+
|
|
98
|
+
await new Promise<void>((resolve, reject) => {
|
|
99
|
+
observable.subscribe({
|
|
100
|
+
next: (event: BaseEvent) => events.push(event),
|
|
101
|
+
complete: () => resolve(),
|
|
102
|
+
error: (error: any) => reject(error),
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Verify we received the expected events
|
|
107
|
+
expect(events).toHaveLength(7); // RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT (x3), TEXT_MESSAGE_END, RUN_FINISHED
|
|
108
|
+
expect(events[0].type).toBe('RUN_STARTED');
|
|
109
|
+
expect(events[1].type).toBe('TEXT_MESSAGE_START');
|
|
110
|
+
expect(events[2].type).toBe('TEXT_MESSAGE_CONTENT');
|
|
111
|
+
expect(events[3].type).toBe('TEXT_MESSAGE_CONTENT');
|
|
112
|
+
expect(events[4].type).toBe('TEXT_MESSAGE_CONTENT');
|
|
113
|
+
expect(events[5].type).toBe('TEXT_MESSAGE_END');
|
|
114
|
+
expect(events[6].type).toBe('RUN_FINISHED');
|
|
115
|
+
|
|
116
|
+
// Verify the content was streamed correctly
|
|
117
|
+
const contentEvents = events.filter(e => e.type === 'TEXT_MESSAGE_CONTENT') as any[];
|
|
118
|
+
expect(contentEvents[0].delta).toBe('Hello');
|
|
119
|
+
expect(contentEvents[1].delta).toBe(' from');
|
|
120
|
+
expect(contentEvents[2].delta).toBe(' agent');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mastra/client-js-integration-tests",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "vitest run",
|
|
7
|
+
"test:watch": "vitest"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@ag-ui/client": "^0.0.27",
|
|
11
|
+
"@mastra/client-js": "workspace:*",
|
|
12
|
+
"@mastra/core": "workspace:*",
|
|
13
|
+
"ai": "^4.3.19"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"vitest": "^3.2.4"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Mastra } from '@mastra/core';
|
|
2
|
+
import { Agent } from '@mastra/core/agent';
|
|
3
|
+
import { MockLanguageModelV1 } from 'ai/test';
|
|
4
|
+
import { simulateReadableStream } from 'ai';
|
|
5
|
+
|
|
6
|
+
const mockModel = new MockLanguageModelV1({
|
|
7
|
+
doStream: async () => ({
|
|
8
|
+
stream: simulateReadableStream({
|
|
9
|
+
chunks: [
|
|
10
|
+
{ type: 'text-delta', textDelta: 'Hello' },
|
|
11
|
+
{ type: 'text-delta', textDelta: ' from' },
|
|
12
|
+
{ type: 'text-delta', textDelta: ' agent' },
|
|
13
|
+
{
|
|
14
|
+
type: 'finish',
|
|
15
|
+
finishReason: 'stop',
|
|
16
|
+
logprobs: undefined,
|
|
17
|
+
usage: { completionTokens: 3, promptTokens: 10 },
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
}),
|
|
21
|
+
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const testAgent = new Agent({
|
|
26
|
+
name: 'test',
|
|
27
|
+
instructions: 'You are a test agent',
|
|
28
|
+
model: mockModel,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const mastra = new Mastra({
|
|
32
|
+
agents: {
|
|
33
|
+
test: testAgent,
|
|
34
|
+
},
|
|
35
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/client-js",
|
|
3
|
-
"version": "0.0.0-transpile-packages-
|
|
3
|
+
"version": "0.0.0-transpile-packages-20250731152758",
|
|
4
4
|
"description": "The official TypeScript library for the Mastra Client API",
|
|
5
5
|
"author": "",
|
|
6
6
|
"type": "module",
|
|
@@ -27,32 +27,35 @@
|
|
|
27
27
|
"homepage": "https://github.com/mastra-ai/mastra/tree/main/client-sdks/client-js#readme",
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ag-ui/client": "^0.0.
|
|
30
|
+
"@ag-ui/client": "^0.0.35",
|
|
31
31
|
"@ai-sdk/ui-utils": "^1.2.11",
|
|
32
32
|
"@lukeed/uuid": "^2.0.1",
|
|
33
33
|
"json-schema": "^0.4.0",
|
|
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.0.0-transpile-packages-
|
|
37
|
+
"@mastra/core": "0.0.0-transpile-packages-20250731152758"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"zod": "^3.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@babel/preset-env": "^7.
|
|
43
|
+
"@babel/preset-env": "^7.28.0",
|
|
44
44
|
"@babel/preset-typescript": "^7.27.1",
|
|
45
45
|
"@tsconfig/recommended": "^1.0.9",
|
|
46
46
|
"@types/json-schema": "^7.0.15",
|
|
47
47
|
"@types/node": "^20.19.0",
|
|
48
|
+
"ai": "^4.3.19",
|
|
48
49
|
"tsup": "^8.5.0",
|
|
49
50
|
"typescript": "^5.8.3",
|
|
50
51
|
"vitest": "^3.2.4",
|
|
51
|
-
"@internal/lint": "0.0.0-transpile-packages-
|
|
52
|
+
"@internal/lint": "0.0.0-transpile-packages-20250731152758"
|
|
52
53
|
},
|
|
53
54
|
"scripts": {
|
|
54
|
-
"build": "tsup
|
|
55
|
+
"build": "tsup --silent --config tsup.config.ts",
|
|
55
56
|
"dev": "pnpm build --watch",
|
|
56
|
-
"test": "vitest run"
|
|
57
|
+
"test": "vitest run && pnpm run test:integration",
|
|
58
|
+
"test:unit": "vitest run",
|
|
59
|
+
"test:integration": "cd integration-tests && pnpm test"
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { Message } from '@ag-ui/client';
|
|
2
|
-
import { describe, it, expect } from 'vitest';
|
|
3
|
-
import { generateUUID, convertMessagesToMastraMessages } from './agui';
|
|
1
|
+
import type { Message, BaseEvent } from '@ag-ui/client';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { generateUUID, convertMessagesToMastraMessages, AGUIAdapter } from './agui';
|
|
4
|
+
import { Agent } from '@mastra/core/agent';
|
|
5
|
+
import { MockLanguageModelV1 } from 'ai/test';
|
|
6
|
+
import { simulateReadableStream } from 'ai';
|
|
4
7
|
|
|
5
8
|
describe('generateUUID', () => {
|
|
6
9
|
it('should generate a valid UUID v4 string', () => {
|
|
@@ -178,3 +181,142 @@ describe('convertMessagesToMastraMessages', () => {
|
|
|
178
181
|
expect(result[3].role).toBe('assistant');
|
|
179
182
|
});
|
|
180
183
|
});
|
|
184
|
+
|
|
185
|
+
describe('AGUIAdapter', () => {
|
|
186
|
+
it('should correctly pass parameters to agent stream method', async () => {
|
|
187
|
+
// Create a real agent with MockLanguageModelV1
|
|
188
|
+
const mockModel = new MockLanguageModelV1({
|
|
189
|
+
doStream: async () => ({
|
|
190
|
+
stream: simulateReadableStream({
|
|
191
|
+
chunks: [
|
|
192
|
+
{ type: 'text-delta', textDelta: 'Hello' },
|
|
193
|
+
{ type: 'text-delta', textDelta: ' from' },
|
|
194
|
+
{ type: 'text-delta', textDelta: ' agent' },
|
|
195
|
+
{
|
|
196
|
+
type: 'finish',
|
|
197
|
+
finishReason: 'stop',
|
|
198
|
+
logprobs: undefined,
|
|
199
|
+
usage: { completionTokens: 3, promptTokens: 10 },
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
}),
|
|
203
|
+
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const agent = new Agent({
|
|
208
|
+
name: 'Test Agent',
|
|
209
|
+
instructions: 'You are a test agent',
|
|
210
|
+
model: mockModel,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Create a mock client agent that simulates the expected behavior
|
|
214
|
+
const clientAgent = {
|
|
215
|
+
stream: vi.fn().mockImplementation(async (params: any) => {
|
|
216
|
+
// Verify the parameters are passed correctly
|
|
217
|
+
expect(params).toHaveProperty('messages');
|
|
218
|
+
expect(params).toHaveProperty('threadId');
|
|
219
|
+
expect(params).toHaveProperty('resourceId');
|
|
220
|
+
expect(params).toHaveProperty('runId');
|
|
221
|
+
expect(params).toHaveProperty('clientTools');
|
|
222
|
+
|
|
223
|
+
// Verify that messages array is passed, not the entire request object
|
|
224
|
+
expect(Array.isArray(params.messages)).toBe(true);
|
|
225
|
+
expect(params.messages[0]).toHaveProperty('role');
|
|
226
|
+
expect(params.messages[0]).toHaveProperty('content');
|
|
227
|
+
|
|
228
|
+
// Return a mock processDataStream that mimics the expected behavior
|
|
229
|
+
return {
|
|
230
|
+
processDataStream: vi.fn().mockImplementation(async ({ onTextPart, onFinishMessagePart }: any) => {
|
|
231
|
+
// Simulate streaming text
|
|
232
|
+
if (onTextPart) {
|
|
233
|
+
onTextPart('Hello from agent');
|
|
234
|
+
}
|
|
235
|
+
if (onFinishMessagePart) {
|
|
236
|
+
onFinishMessagePart();
|
|
237
|
+
}
|
|
238
|
+
return Promise.resolve();
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
}),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const adapter = new AGUIAdapter({
|
|
245
|
+
agent: clientAgent as any,
|
|
246
|
+
agentId: 'test',
|
|
247
|
+
resourceId: 'testAgent',
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const input = {
|
|
251
|
+
threadId: 'test-thread-id',
|
|
252
|
+
runId: 'test-run-id',
|
|
253
|
+
messages: [
|
|
254
|
+
{
|
|
255
|
+
id: '1',
|
|
256
|
+
role: 'user' as const,
|
|
257
|
+
content: 'Hello',
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
tools: [],
|
|
261
|
+
context: [],
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const observable = adapter['run'](input);
|
|
265
|
+
const events: BaseEvent[] = [];
|
|
266
|
+
|
|
267
|
+
await new Promise<void>((resolve, reject) => {
|
|
268
|
+
observable.subscribe({
|
|
269
|
+
next: (event: BaseEvent) => events.push(event),
|
|
270
|
+
complete: () => resolve(),
|
|
271
|
+
error: (error: any) => reject(error),
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Verify we received the expected events
|
|
276
|
+
expect(events).toHaveLength(5); // RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END, RUN_FINISHED
|
|
277
|
+
expect(events[0].type).toBe('RUN_STARTED');
|
|
278
|
+
expect(events[1].type).toBe('TEXT_MESSAGE_START');
|
|
279
|
+
expect(events[2].type).toBe('TEXT_MESSAGE_CONTENT');
|
|
280
|
+
expect(events[3].type).toBe('TEXT_MESSAGE_END');
|
|
281
|
+
expect(events[4].type).toBe('RUN_FINISHED');
|
|
282
|
+
|
|
283
|
+
// Verify the stream method was called with the correct parameters
|
|
284
|
+
expect(clientAgent.stream).toHaveBeenCalledWith({
|
|
285
|
+
threadId: 'test-thread-id',
|
|
286
|
+
resourceId: 'testAgent',
|
|
287
|
+
runId: 'test-run-id',
|
|
288
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
289
|
+
clientTools: {},
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should handle messages without role property in request objects', async () => {
|
|
294
|
+
// This test demonstrates that request objects without role property
|
|
295
|
+
// would cause validation errors if passed directly to MessageList
|
|
296
|
+
const requestObject = {
|
|
297
|
+
threadId: 'test-thread-id',
|
|
298
|
+
resourceId: 'testAgent',
|
|
299
|
+
runId: 'test-run-id',
|
|
300
|
+
messages: [
|
|
301
|
+
{
|
|
302
|
+
role: 'user',
|
|
303
|
+
content: 'Hello',
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
clientTools: {},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// Request objects don't have role property
|
|
310
|
+
expect('role' in requestObject).toBe(false);
|
|
311
|
+
expect('messages' in requestObject).toBe(true);
|
|
312
|
+
expect('content' in requestObject).toBe(false);
|
|
313
|
+
expect('parts' in requestObject).toBe(false);
|
|
314
|
+
|
|
315
|
+
// This structure would cause validation errors if treated as a message
|
|
316
|
+
// because it lacks required message properties (role, content/parts)
|
|
317
|
+
const hasValidMessageStructure =
|
|
318
|
+
'role' in requestObject && ('content' in requestObject || 'parts' in requestObject);
|
|
319
|
+
|
|
320
|
+
expect(hasValidMessageStructure).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
});
|
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/index.ts
CHANGED
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);
|