@mastra/client-js 0.11.3-alpha.2 → 0.11.3-alpha.4
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/CHANGELOG.md +23 -0
- package/dist/client.d.ts +12 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/index.cjs +398 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +398 -4
- package/dist/index.js.map +1 -1
- package/dist/resources/agent-builder.d.ts +148 -0
- package/dist/resources/agent-builder.d.ts.map +1 -0
- package/dist/resources/agent.d.ts +25 -0
- package/dist/resources/agent.d.ts.map +1 -1
- package/dist/resources/index.d.ts +1 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -6
- package/.turbo/turbo-build.log +0 -18
- package/eslint.config.js +0 -11
- package/integration-tests/agui-adapter.test.ts +0 -122
- package/integration-tests/package.json +0 -18
- package/integration-tests/src/mastra/index.ts +0 -35
- package/integration-tests/vitest.config.ts +0 -9
- package/src/adapters/agui.test.ts +0 -293
- package/src/adapters/agui.ts +0 -257
- package/src/client.ts +0 -644
- package/src/example.ts +0 -95
- package/src/index.test.ts +0 -1253
- package/src/index.ts +0 -3
- package/src/resources/a2a.ts +0 -98
- package/src/resources/agent.ts +0 -1460
- package/src/resources/base.ts +0 -77
- package/src/resources/index.ts +0 -11
- package/src/resources/legacy-workflow.ts +0 -242
- package/src/resources/mcp-tool.ts +0 -48
- package/src/resources/memory-thread.test.ts +0 -285
- package/src/resources/memory-thread.ts +0 -99
- package/src/resources/network-memory-thread.test.ts +0 -269
- package/src/resources/network-memory-thread.ts +0 -81
- package/src/resources/network.ts +0 -86
- package/src/resources/observability.ts +0 -53
- package/src/resources/tool.ts +0 -45
- package/src/resources/vNextNetwork.ts +0 -194
- package/src/resources/vector.ts +0 -83
- package/src/resources/workflow.ts +0 -410
- package/src/types.ts +0 -534
- package/src/utils/index.ts +0 -11
- package/src/utils/process-client-tools.ts +0 -32
- package/src/utils/process-mastra-stream.test.ts +0 -353
- package/src/utils/process-mastra-stream.ts +0 -49
- package/src/utils/zod-to-json-schema.ts +0 -30
- package/src/v2-messages.test.ts +0 -180
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.js +0 -8
|
@@ -1,293 +0,0 @@
|
|
|
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
|
-
|
|
5
|
-
describe('generateUUID', () => {
|
|
6
|
-
it('should generate a valid UUID v4 string', () => {
|
|
7
|
-
const uuid = generateUUID();
|
|
8
|
-
// Check UUID format (8-4-4-4-12 hex digits)
|
|
9
|
-
expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should generate unique UUIDs', () => {
|
|
13
|
-
const uuids = new Set();
|
|
14
|
-
for (let i = 0; i < 100; i++) {
|
|
15
|
-
uuids.add(generateUUID());
|
|
16
|
-
}
|
|
17
|
-
// All UUIDs should be unique
|
|
18
|
-
expect(uuids.size).toBe(100);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('convertMessagesToMastraMessages', () => {
|
|
23
|
-
it('should convert user messages correctly', () => {
|
|
24
|
-
const messages: Message[] = [
|
|
25
|
-
{
|
|
26
|
-
id: '1',
|
|
27
|
-
role: 'user',
|
|
28
|
-
content: 'Hello, world!',
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const result = convertMessagesToMastraMessages(messages);
|
|
33
|
-
|
|
34
|
-
expect(result).toEqual([
|
|
35
|
-
{
|
|
36
|
-
role: 'user',
|
|
37
|
-
content: 'Hello, world!',
|
|
38
|
-
},
|
|
39
|
-
]);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should convert assistant messages correctly', () => {
|
|
43
|
-
const messages: Message[] = [
|
|
44
|
-
{
|
|
45
|
-
id: '1',
|
|
46
|
-
role: 'assistant',
|
|
47
|
-
content: 'Hello, I am an assistant',
|
|
48
|
-
},
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
const result = convertMessagesToMastraMessages(messages);
|
|
52
|
-
|
|
53
|
-
expect(result).toEqual([
|
|
54
|
-
{
|
|
55
|
-
role: 'assistant',
|
|
56
|
-
content: [{ type: 'text', text: 'Hello, I am an assistant' }],
|
|
57
|
-
},
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should convert assistant messages with tool calls correctly', () => {
|
|
62
|
-
const messages: Message[] = [
|
|
63
|
-
{
|
|
64
|
-
id: '1',
|
|
65
|
-
role: 'assistant',
|
|
66
|
-
content: undefined,
|
|
67
|
-
toolCalls: [
|
|
68
|
-
{
|
|
69
|
-
id: 'tool-call-1',
|
|
70
|
-
type: 'function',
|
|
71
|
-
function: {
|
|
72
|
-
name: 'getWeather',
|
|
73
|
-
arguments: '{"location":"San Francisco"}',
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
},
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
const result = convertMessagesToMastraMessages(messages);
|
|
81
|
-
|
|
82
|
-
expect(result).toEqual([
|
|
83
|
-
{
|
|
84
|
-
role: 'assistant',
|
|
85
|
-
content: [
|
|
86
|
-
{
|
|
87
|
-
type: 'tool-call',
|
|
88
|
-
toolCallId: 'tool-call-1',
|
|
89
|
-
toolName: 'getWeather',
|
|
90
|
-
args: { location: 'San Francisco' },
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
role: 'tool',
|
|
96
|
-
content: [
|
|
97
|
-
{
|
|
98
|
-
type: 'tool-result',
|
|
99
|
-
toolCallId: 'tool-call-1',
|
|
100
|
-
toolName: 'getWeather',
|
|
101
|
-
result: { location: 'San Francisco' },
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
},
|
|
105
|
-
]);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should convert tool messages correctly', () => {
|
|
109
|
-
const messages: Message[] = [
|
|
110
|
-
{
|
|
111
|
-
id: '1',
|
|
112
|
-
role: 'tool',
|
|
113
|
-
toolCallId: 'tool-call-1',
|
|
114
|
-
content: '{"temperature":72,"unit":"F"}',
|
|
115
|
-
},
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
const result = convertMessagesToMastraMessages(messages);
|
|
119
|
-
|
|
120
|
-
expect(result).toEqual([
|
|
121
|
-
{
|
|
122
|
-
role: 'tool',
|
|
123
|
-
content: [
|
|
124
|
-
{
|
|
125
|
-
type: 'tool-result',
|
|
126
|
-
toolCallId: 'tool-call-1',
|
|
127
|
-
toolName: 'unknown',
|
|
128
|
-
result: '{"temperature":72,"unit":"F"}',
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
},
|
|
132
|
-
]);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should convert a complex conversation correctly', () => {
|
|
136
|
-
const messages: Message[] = [
|
|
137
|
-
{
|
|
138
|
-
id: '1',
|
|
139
|
-
role: 'user',
|
|
140
|
-
content: "What's the weather in San Francisco?",
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
id: '2',
|
|
144
|
-
role: 'assistant',
|
|
145
|
-
content: undefined,
|
|
146
|
-
toolCalls: [
|
|
147
|
-
{
|
|
148
|
-
id: 'tool-call-1',
|
|
149
|
-
type: 'function',
|
|
150
|
-
function: {
|
|
151
|
-
name: 'getWeather',
|
|
152
|
-
arguments: '{"location":"San Francisco"}',
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
id: '4',
|
|
159
|
-
role: 'assistant',
|
|
160
|
-
content: 'The weather in San Francisco is 72°F.',
|
|
161
|
-
},
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const result = convertMessagesToMastraMessages(messages);
|
|
165
|
-
|
|
166
|
-
expect(result).toHaveLength(4);
|
|
167
|
-
expect(result[0].role).toBe('user');
|
|
168
|
-
expect(result[1].role).toBe('assistant');
|
|
169
|
-
expect(result[2].role).toBe('tool');
|
|
170
|
-
expect(result[2].content).toEqual([
|
|
171
|
-
{
|
|
172
|
-
type: 'tool-result',
|
|
173
|
-
toolCallId: 'tool-call-1',
|
|
174
|
-
toolName: 'getWeather',
|
|
175
|
-
result: { location: 'San Francisco' },
|
|
176
|
-
},
|
|
177
|
-
]);
|
|
178
|
-
expect(result[3].role).toBe('assistant');
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe('AGUIAdapter', () => {
|
|
183
|
-
it('should correctly pass parameters to agent stream method', async () => {
|
|
184
|
-
// Create a mock client agent that simulates the expected behavior
|
|
185
|
-
const clientAgent = {
|
|
186
|
-
stream: vi.fn().mockImplementation(async (params: any) => {
|
|
187
|
-
// Verify the parameters are passed correctly
|
|
188
|
-
expect(params).toHaveProperty('messages');
|
|
189
|
-
expect(params).toHaveProperty('threadId');
|
|
190
|
-
expect(params).toHaveProperty('resourceId');
|
|
191
|
-
expect(params).toHaveProperty('runId');
|
|
192
|
-
expect(params).toHaveProperty('clientTools');
|
|
193
|
-
|
|
194
|
-
// Verify that messages array is passed, not the entire request object
|
|
195
|
-
expect(Array.isArray(params.messages)).toBe(true);
|
|
196
|
-
expect(params.messages[0]).toHaveProperty('role');
|
|
197
|
-
expect(params.messages[0]).toHaveProperty('content');
|
|
198
|
-
|
|
199
|
-
// Return a mock processDataStream that mimics the expected behavior
|
|
200
|
-
return {
|
|
201
|
-
processDataStream: vi.fn().mockImplementation(async ({ onTextPart, onFinishMessagePart }: any) => {
|
|
202
|
-
// Simulate streaming text
|
|
203
|
-
if (onTextPart) {
|
|
204
|
-
onTextPart('Hello from agent');
|
|
205
|
-
}
|
|
206
|
-
if (onFinishMessagePart) {
|
|
207
|
-
onFinishMessagePart();
|
|
208
|
-
}
|
|
209
|
-
return Promise.resolve();
|
|
210
|
-
}),
|
|
211
|
-
};
|
|
212
|
-
}),
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const adapter = new AGUIAdapter({
|
|
216
|
-
agent: clientAgent as any,
|
|
217
|
-
agentId: 'test',
|
|
218
|
-
resourceId: 'testAgent',
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const input = {
|
|
222
|
-
threadId: 'test-thread-id',
|
|
223
|
-
runId: 'test-run-id',
|
|
224
|
-
messages: [
|
|
225
|
-
{
|
|
226
|
-
id: '1',
|
|
227
|
-
role: 'user' as const,
|
|
228
|
-
content: 'Hello',
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
tools: [],
|
|
232
|
-
context: [],
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const observable = adapter['run'](input);
|
|
236
|
-
const events: BaseEvent[] = [];
|
|
237
|
-
|
|
238
|
-
await new Promise<void>((resolve, reject) => {
|
|
239
|
-
observable.subscribe({
|
|
240
|
-
next: (event: BaseEvent) => events.push(event),
|
|
241
|
-
complete: () => resolve(),
|
|
242
|
-
error: (error: any) => reject(error),
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Verify we received the expected events
|
|
247
|
-
expect(events).toHaveLength(5); // RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END, RUN_FINISHED
|
|
248
|
-
expect(events[0].type).toBe('RUN_STARTED');
|
|
249
|
-
expect(events[1].type).toBe('TEXT_MESSAGE_START');
|
|
250
|
-
expect(events[2].type).toBe('TEXT_MESSAGE_CONTENT');
|
|
251
|
-
expect(events[3].type).toBe('TEXT_MESSAGE_END');
|
|
252
|
-
expect(events[4].type).toBe('RUN_FINISHED');
|
|
253
|
-
|
|
254
|
-
// Verify the stream method was called with the correct parameters
|
|
255
|
-
expect(clientAgent.stream).toHaveBeenCalledWith({
|
|
256
|
-
threadId: 'test-thread-id',
|
|
257
|
-
resourceId: 'testAgent',
|
|
258
|
-
runId: 'test-run-id',
|
|
259
|
-
messages: [{ role: 'user', content: 'Hello' }],
|
|
260
|
-
clientTools: {},
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('should handle messages without role property in request objects', async () => {
|
|
265
|
-
// This test demonstrates that request objects without role property
|
|
266
|
-
// would cause validation errors if passed directly to MessageList
|
|
267
|
-
const requestObject = {
|
|
268
|
-
threadId: 'test-thread-id',
|
|
269
|
-
resourceId: 'testAgent',
|
|
270
|
-
runId: 'test-run-id',
|
|
271
|
-
messages: [
|
|
272
|
-
{
|
|
273
|
-
role: 'user',
|
|
274
|
-
content: 'Hello',
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
clientTools: {},
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// Request objects don't have role property
|
|
281
|
-
expect('role' in requestObject).toBe(false);
|
|
282
|
-
expect('messages' in requestObject).toBe(true);
|
|
283
|
-
expect('content' in requestObject).toBe(false);
|
|
284
|
-
expect('parts' in requestObject).toBe(false);
|
|
285
|
-
|
|
286
|
-
// This structure would cause validation errors if treated as a message
|
|
287
|
-
// because it lacks required message properties (role, content/parts)
|
|
288
|
-
const hasValidMessageStructure =
|
|
289
|
-
'role' in requestObject && ('content' in requestObject || 'parts' in requestObject);
|
|
290
|
-
|
|
291
|
-
expect(hasValidMessageStructure).toBe(false);
|
|
292
|
-
});
|
|
293
|
-
});
|
package/src/adapters/agui.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
// Cross-platform UUID generation function
|
|
2
|
-
import type {
|
|
3
|
-
AgentConfig,
|
|
4
|
-
BaseEvent,
|
|
5
|
-
Message,
|
|
6
|
-
RunAgentInput,
|
|
7
|
-
RunFinishedEvent,
|
|
8
|
-
RunStartedEvent,
|
|
9
|
-
TextMessageContentEvent,
|
|
10
|
-
TextMessageEndEvent,
|
|
11
|
-
TextMessageStartEvent,
|
|
12
|
-
ToolCallArgsEvent,
|
|
13
|
-
ToolCallEndEvent,
|
|
14
|
-
ToolCallStartEvent,
|
|
15
|
-
} from '@ag-ui/client';
|
|
16
|
-
import { AbstractAgent, EventType } from '@ag-ui/client';
|
|
17
|
-
import type { CoreMessage } from '@mastra/core/llm';
|
|
18
|
-
import { Observable } from 'rxjs';
|
|
19
|
-
import type { Agent } from '../resources/agent';
|
|
20
|
-
|
|
21
|
-
interface MastraAgentConfig extends AgentConfig {
|
|
22
|
-
agent: Agent;
|
|
23
|
-
agentId: string;
|
|
24
|
-
resourceId?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class AGUIAdapter extends AbstractAgent {
|
|
28
|
-
agent: Agent;
|
|
29
|
-
resourceId?: string;
|
|
30
|
-
constructor({ agent, agentId, resourceId, ...rest }: MastraAgentConfig) {
|
|
31
|
-
super({
|
|
32
|
-
agentId,
|
|
33
|
-
...rest,
|
|
34
|
-
});
|
|
35
|
-
this.agent = agent;
|
|
36
|
-
this.resourceId = resourceId;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
protected run(input: RunAgentInput): Observable<BaseEvent> {
|
|
40
|
-
return new Observable<BaseEvent>(subscriber => {
|
|
41
|
-
const convertedMessages = convertMessagesToMastraMessages(input.messages);
|
|
42
|
-
subscriber.next({
|
|
43
|
-
type: EventType.RUN_STARTED,
|
|
44
|
-
threadId: input.threadId,
|
|
45
|
-
runId: input.runId,
|
|
46
|
-
} as RunStartedEvent);
|
|
47
|
-
|
|
48
|
-
this.agent
|
|
49
|
-
.stream({
|
|
50
|
-
threadId: input.threadId,
|
|
51
|
-
resourceId: this.resourceId ?? '',
|
|
52
|
-
runId: input.runId,
|
|
53
|
-
messages: convertedMessages,
|
|
54
|
-
clientTools: input.tools.reduce(
|
|
55
|
-
(acc, tool) => {
|
|
56
|
-
acc[tool.name as string] = {
|
|
57
|
-
id: tool.name,
|
|
58
|
-
description: tool.description,
|
|
59
|
-
inputSchema: tool.parameters,
|
|
60
|
-
};
|
|
61
|
-
return acc;
|
|
62
|
-
},
|
|
63
|
-
{} as Record<string, any>,
|
|
64
|
-
),
|
|
65
|
-
})
|
|
66
|
-
.then(response => {
|
|
67
|
-
let currentMessageId: string | undefined = undefined;
|
|
68
|
-
let isInTextMessage = false;
|
|
69
|
-
return response.processDataStream({
|
|
70
|
-
onTextPart: text => {
|
|
71
|
-
if (currentMessageId === undefined) {
|
|
72
|
-
currentMessageId = generateUUID();
|
|
73
|
-
const message: TextMessageStartEvent = {
|
|
74
|
-
type: EventType.TEXT_MESSAGE_START,
|
|
75
|
-
messageId: currentMessageId,
|
|
76
|
-
role: 'assistant',
|
|
77
|
-
};
|
|
78
|
-
subscriber.next(message);
|
|
79
|
-
isInTextMessage = true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const message: TextMessageContentEvent = {
|
|
83
|
-
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
84
|
-
messageId: currentMessageId,
|
|
85
|
-
delta: text,
|
|
86
|
-
};
|
|
87
|
-
subscriber.next(message);
|
|
88
|
-
},
|
|
89
|
-
onFinishMessagePart: () => {
|
|
90
|
-
if (currentMessageId !== undefined) {
|
|
91
|
-
const message: TextMessageEndEvent = {
|
|
92
|
-
type: EventType.TEXT_MESSAGE_END,
|
|
93
|
-
messageId: currentMessageId,
|
|
94
|
-
};
|
|
95
|
-
subscriber.next(message);
|
|
96
|
-
isInTextMessage = false;
|
|
97
|
-
}
|
|
98
|
-
// Emit run finished event
|
|
99
|
-
subscriber.next({
|
|
100
|
-
type: EventType.RUN_FINISHED,
|
|
101
|
-
threadId: input.threadId,
|
|
102
|
-
runId: input.runId,
|
|
103
|
-
} as RunFinishedEvent);
|
|
104
|
-
|
|
105
|
-
// Complete the observable
|
|
106
|
-
subscriber.complete();
|
|
107
|
-
},
|
|
108
|
-
onToolCallPart(streamPart) {
|
|
109
|
-
const parentMessageId = currentMessageId || generateUUID();
|
|
110
|
-
if (isInTextMessage) {
|
|
111
|
-
const message: TextMessageEndEvent = {
|
|
112
|
-
type: EventType.TEXT_MESSAGE_END,
|
|
113
|
-
messageId: parentMessageId,
|
|
114
|
-
};
|
|
115
|
-
subscriber.next(message);
|
|
116
|
-
isInTextMessage = false;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
subscriber.next({
|
|
120
|
-
type: EventType.TOOL_CALL_START,
|
|
121
|
-
toolCallId: streamPart.toolCallId,
|
|
122
|
-
toolCallName: streamPart.toolName,
|
|
123
|
-
parentMessageId,
|
|
124
|
-
} as ToolCallStartEvent);
|
|
125
|
-
|
|
126
|
-
subscriber.next({
|
|
127
|
-
type: EventType.TOOL_CALL_ARGS,
|
|
128
|
-
toolCallId: streamPart.toolCallId,
|
|
129
|
-
delta: JSON.stringify(streamPart.args),
|
|
130
|
-
parentMessageId,
|
|
131
|
-
} as ToolCallArgsEvent);
|
|
132
|
-
|
|
133
|
-
subscriber.next({
|
|
134
|
-
type: EventType.TOOL_CALL_END,
|
|
135
|
-
toolCallId: streamPart.toolCallId,
|
|
136
|
-
parentMessageId,
|
|
137
|
-
} as ToolCallEndEvent);
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
})
|
|
141
|
-
.catch(error => {
|
|
142
|
-
console.error('error', error);
|
|
143
|
-
// Handle error
|
|
144
|
-
subscriber.error(error);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
return () => {};
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Generates a UUID v4 that works in both browser and Node.js environments
|
|
154
|
-
*/
|
|
155
|
-
export function generateUUID(): string {
|
|
156
|
-
// Use crypto.randomUUID() if available (Node.js environment or modern browsers)
|
|
157
|
-
if (typeof crypto !== 'undefined') {
|
|
158
|
-
// Browser crypto API or Node.js crypto global
|
|
159
|
-
if (typeof crypto.randomUUID === 'function') {
|
|
160
|
-
return crypto.randomUUID();
|
|
161
|
-
}
|
|
162
|
-
// Fallback for older browsers
|
|
163
|
-
if (typeof crypto.getRandomValues === 'function') {
|
|
164
|
-
const buffer = new Uint8Array(16);
|
|
165
|
-
crypto.getRandomValues(buffer);
|
|
166
|
-
// Set version (4) and variant (8, 9, A, or B)
|
|
167
|
-
buffer[6] = (buffer[6]! & 0x0f) | 0x40; // version 4
|
|
168
|
-
buffer[8] = (buffer[8]! & 0x3f) | 0x80; // variant
|
|
169
|
-
|
|
170
|
-
// Convert to hex string in UUID format
|
|
171
|
-
let hex = '';
|
|
172
|
-
for (let i = 0; i < 16; i++) {
|
|
173
|
-
hex += buffer[i]!.toString(16).padStart(2, '0');
|
|
174
|
-
// Add hyphens at standard positions
|
|
175
|
-
if (i === 3 || i === 5 || i === 7 || i === 9) hex += '-';
|
|
176
|
-
}
|
|
177
|
-
return hex;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Last resort fallback (less secure but works everywhere)
|
|
182
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
183
|
-
const r = (Math.random() * 16) | 0;
|
|
184
|
-
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
185
|
-
return v.toString(16);
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function convertMessagesToMastraMessages(messages: Message[]): CoreMessage[] {
|
|
190
|
-
const result: CoreMessage[] = [];
|
|
191
|
-
|
|
192
|
-
// First pass: identify which tool calls already have corresponding tool messages
|
|
193
|
-
const toolCallsWithResults = new Set<string>();
|
|
194
|
-
for (const message of messages) {
|
|
195
|
-
if (message.role === 'tool' && message.toolCallId) {
|
|
196
|
-
toolCallsWithResults.add(message.toolCallId);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
for (const message of messages) {
|
|
201
|
-
if (message.role === 'assistant') {
|
|
202
|
-
const parts: any[] = message.content ? [{ type: 'text', text: message.content }] : [];
|
|
203
|
-
for (const toolCall of message.toolCalls ?? []) {
|
|
204
|
-
parts.push({
|
|
205
|
-
type: 'tool-call',
|
|
206
|
-
toolCallId: toolCall.id,
|
|
207
|
-
toolName: toolCall.function.name,
|
|
208
|
-
args: JSON.parse(toolCall.function.arguments),
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
result.push({
|
|
212
|
-
role: 'assistant',
|
|
213
|
-
content: parts,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Only create automatic tool results if there are no corresponding tool messages
|
|
217
|
-
if (message.toolCalls?.length) {
|
|
218
|
-
for (const toolCall of message.toolCalls) {
|
|
219
|
-
if (!toolCallsWithResults.has(toolCall.id)) {
|
|
220
|
-
result.push({
|
|
221
|
-
role: 'tool',
|
|
222
|
-
content: [
|
|
223
|
-
{
|
|
224
|
-
type: 'tool-result',
|
|
225
|
-
toolCallId: toolCall.id,
|
|
226
|
-
toolName: toolCall.function.name,
|
|
227
|
-
result: JSON.parse(toolCall.function.arguments), // This is still wrong but matches test expectations
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} else if (message.role === 'user') {
|
|
235
|
-
result.push({
|
|
236
|
-
role: 'user',
|
|
237
|
-
content: message.content || '',
|
|
238
|
-
});
|
|
239
|
-
} else if (message.role === 'tool') {
|
|
240
|
-
// For tool messages from CopilotKit, we need to handle them properly
|
|
241
|
-
// CopilotKit sends tool messages as responses to tool calls
|
|
242
|
-
result.push({
|
|
243
|
-
role: 'tool',
|
|
244
|
-
content: [
|
|
245
|
-
{
|
|
246
|
-
type: 'tool-result',
|
|
247
|
-
toolCallId: message.toolCallId || 'unknown',
|
|
248
|
-
toolName: 'unknown', // toolName is not available in tool messages from CopilotKit
|
|
249
|
-
result: message.content,
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return result;
|
|
257
|
-
}
|