Skip to content

Commit f28efcd

Browse files
committed
feat: Update to latest version of Mastra
1 parent a78a7ee commit f28efcd

File tree

4 files changed

+456
-86
lines changed

4 files changed

+456
-86
lines changed

integrations/mastra/typescript/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@
6565
"@ag-ui/client": ">=0.0.40",
6666
"@ag-ui/core": ">=0.0.39",
6767
"@copilotkit/runtime": "^1.10.5",
68-
"@mastra/core": ">=0.20.1",
68+
"@mastra/core": ">=0.23.3",
6969
"zod": "^3.25.67"
7070
},
7171
"devDependencies": {
7272
"@ag-ui/client": "workspace:*",
7373
"@ag-ui/core": "workspace:*",
74-
"@mastra/core": "^0.20.2",
74+
"@mastra/core": "^0.23.2",
7575
"@types/jest": "^29.5.14",
7676
"@types/node": "^20.19.24",
7777
"jest": "^29.7.0",

integrations/mastra/typescript/src/mastra.ts

Lines changed: 196 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export interface MastraAgentConfig extends AgentConfig {
4040
interface MastraAgentStreamOptions {
4141
onTextPart?: (text: string) => void;
4242
onFinishMessagePart?: () => void;
43-
onToolCallPart?: (streamPart: { toolCallId: string; toolName: string; args: any }) => void;
44-
onToolResultPart?: (streamPart: { toolCallId: string; result: any }) => void;
43+
onToolCallPart?: (streamPart: { toolCallId: string; toolName: string; args: unknown }) => void;
44+
onToolResultPart?: (streamPart: { toolCallId: string; output: unknown }) => void;
4545
onError?: (error: Error) => void;
4646
onRunFinished?: () => Promise<void>;
4747
}
@@ -145,7 +145,7 @@ export class MastraAgent extends AbstractAgent {
145145
const toolCallResultEvent: ToolCallResultEvent = {
146146
type: EventType.TOOL_CALL_RESULT,
147147
toolCallId: streamPart.toolCallId,
148-
content: JSON.stringify(streamPart.result),
148+
content: safeStringify(streamPart.output),
149149
messageId: randomUUID(),
150150
role: "tool",
151151
};
@@ -268,37 +268,13 @@ export class MastraAgent extends AbstractAgent {
268268
// Process it using the agent's built-in streaming mechanism
269269
if (response && typeof response === "object") {
270270
for await (const chunk of response.fullStream) {
271-
switch (chunk.type) {
272-
case "text-delta": {
273-
onTextPart?.(chunk.payload.text);
274-
break;
275-
}
276-
case "tool-call": {
277-
onToolCallPart?.({
278-
toolCallId: chunk.payload.toolCallId,
279-
toolName: chunk.payload.toolName,
280-
args: chunk.payload.args,
281-
});
282-
break;
283-
}
284-
case "tool-result": {
285-
onToolResultPart?.({
286-
toolCallId: chunk.payload.toolCallId,
287-
result: chunk.payload.result,
288-
});
289-
break;
290-
}
291-
292-
case "error": {
293-
onError?.(new Error(chunk.payload.error as string));
294-
break;
295-
}
296-
297-
case "finish": {
298-
onFinishMessagePart?.();
299-
break;
300-
}
301-
}
271+
handleStreamChunk(chunk, {
272+
onTextPart,
273+
onToolCallPart,
274+
onToolResultPart,
275+
onFinishMessagePart,
276+
onError,
277+
});
302278
}
303279

304280
await onRunFinished?.();
@@ -323,35 +299,28 @@ export class MastraAgent extends AbstractAgent {
323299
if (response && typeof response.processDataStream === "function") {
324300
await response.processDataStream({
325301
onChunk: async (chunk) => {
326-
switch (chunk.type) {
327-
case "text-delta": {
328-
onTextPart?.(chunk.payload.text);
329-
break;
330-
}
331-
case "tool-call": {
332-
onToolCallPart?.({
333-
toolCallId: chunk.payload.toolCallId,
334-
toolName: chunk.payload.toolName,
335-
args: chunk.payload.args,
336-
});
337-
break;
338-
}
339-
case "tool-result": {
340-
onToolResultPart?.({
341-
toolCallId: chunk.payload.toolCallId,
342-
result: chunk.payload.result,
343-
});
344-
break;
345-
}
346-
347-
case "finish": {
348-
onFinishMessagePart?.();
349-
break;
350-
}
351-
}
302+
handleStreamChunk(chunk, {
303+
onTextPart,
304+
onToolCallPart,
305+
onToolResultPart,
306+
onFinishMessagePart,
307+
onError,
308+
});
352309
},
353310
});
354311
await onRunFinished?.();
312+
} else if (response && typeof (response as { fullStream?: unknown }).fullStream === "object") {
313+
const fullStream = (response as unknown as { fullStream: AsyncIterable<unknown> }).fullStream;
314+
for await (const chunk of fullStream) {
315+
handleStreamChunk(chunk, {
316+
onTextPart,
317+
onToolCallPart,
318+
onToolResultPart,
319+
onFinishMessagePart,
320+
onError,
321+
});
322+
}
323+
await onRunFinished?.();
355324
} else {
356325
throw new Error("Invalid response from remote agent");
357326
}
@@ -379,3 +348,170 @@ export class MastraAgent extends AbstractAgent {
379348
return getNetwork(options);
380349
}
381350
}
351+
352+
function handleStreamChunk(
353+
chunk: unknown,
354+
handlers: {
355+
onTextPart?: (text: string) => void;
356+
onToolCallPart?: (streamPart: { toolCallId: string; toolName: string; args: unknown }) => void;
357+
onToolResultPart?: (streamPart: { toolCallId: string; output: unknown }) => void;
358+
onFinishMessagePart?: () => void;
359+
onError?: (error: Error) => void;
360+
},
361+
) {
362+
if (!chunk || typeof chunk !== "object" || !("type" in chunk)) {
363+
return;
364+
}
365+
366+
const type = (chunk as { type: string }).type;
367+
368+
switch (type) {
369+
case "text-delta": {
370+
const text = extractTextDelta(chunk);
371+
if (typeof text === "string" && text.length > 0) {
372+
handlers.onTextPart?.(text);
373+
}
374+
break;
375+
}
376+
377+
case "tool-call": {
378+
const toolCall = extractToolCall(chunk);
379+
if (toolCall) {
380+
handlers.onToolCallPart?.(toolCall);
381+
}
382+
break;
383+
}
384+
385+
case "tool-result": {
386+
const toolResult = extractToolResult(chunk);
387+
if (toolResult) {
388+
handlers.onToolResultPart?.(toolResult);
389+
}
390+
break;
391+
}
392+
393+
case "tool-error": {
394+
handlers.onError?.(
395+
new Error(
396+
safeStringify((chunk as { error?: unknown }).error ?? "Tool execution failed"),
397+
),
398+
);
399+
break;
400+
}
401+
402+
case "error": {
403+
handlers.onError?.(extractError(chunk) ?? new Error("Stream error"));
404+
break;
405+
}
406+
407+
case "finish": {
408+
handlers.onFinishMessagePart?.();
409+
break;
410+
}
411+
412+
default:
413+
break;
414+
}
415+
}
416+
417+
function extractTextDelta(chunk: unknown): string | undefined {
418+
if (!chunk || typeof chunk !== "object") {
419+
return undefined;
420+
}
421+
422+
const candidate = chunk as Record<string, unknown>;
423+
if (typeof candidate.text === "string") {
424+
return candidate.text;
425+
}
426+
if (typeof candidate.delta === "string") {
427+
return candidate.delta;
428+
}
429+
const payload = candidate.payload as Record<string, unknown> | undefined;
430+
if (payload) {
431+
if (typeof payload.text === "string") {
432+
return payload.text;
433+
}
434+
if (typeof payload.delta === "string") {
435+
return payload.delta;
436+
}
437+
}
438+
return undefined;
439+
}
440+
441+
function extractToolCall(chunk: unknown):
442+
| { toolCallId: string; toolName: string; args: unknown }
443+
| undefined {
444+
if (!chunk || typeof chunk !== "object") {
445+
return undefined;
446+
}
447+
448+
const candidate = chunk as Record<string, unknown>;
449+
const toolCallId = getString(candidate.toolCallId) ?? getString(candidate.tool_call_id);
450+
const toolName = getString(candidate.toolName) ?? getString(candidate.tool_name);
451+
const input = candidate.input ?? candidate.args;
452+
const payload = candidate.payload as Record<string, unknown> | undefined;
453+
454+
return toolCallId && toolName
455+
? {
456+
toolCallId,
457+
toolName,
458+
args: input ?? payload?.input ?? payload?.args,
459+
}
460+
: undefined;
461+
}
462+
463+
function extractToolResult(chunk: unknown):
464+
| { toolCallId: string; output: unknown }
465+
| undefined {
466+
if (!chunk || typeof chunk !== "object") {
467+
return undefined;
468+
}
469+
470+
const candidate = chunk as Record<string, unknown>;
471+
const toolCallId = getString(candidate.toolCallId) ?? getString(candidate.tool_call_id);
472+
const output = candidate.output ?? candidate.result;
473+
const payload = candidate.payload as Record<string, unknown> | undefined;
474+
475+
return toolCallId
476+
? {
477+
toolCallId,
478+
output: output ?? payload?.output ?? payload?.result,
479+
}
480+
: undefined;
481+
}
482+
483+
function extractError(chunk: unknown): Error | undefined {
484+
if (!chunk || typeof chunk !== "object") {
485+
return undefined;
486+
}
487+
488+
const candidate = chunk as Record<string, unknown>;
489+
const payload = candidate.payload as Record<string, unknown> | undefined;
490+
const errorValue = candidate.error ?? payload?.error ?? payload?.message;
491+
492+
if (!errorValue) {
493+
return undefined;
494+
}
495+
496+
if (errorValue instanceof Error) {
497+
return errorValue;
498+
}
499+
500+
const message = typeof errorValue === "string" ? errorValue : safeStringify(errorValue);
501+
return new Error(message);
502+
}
503+
504+
function safeStringify(value: unknown): string {
505+
if (typeof value === "string") {
506+
return value;
507+
}
508+
try {
509+
return JSON.stringify(value ?? {});
510+
} catch {
511+
return String(value);
512+
}
513+
}
514+
515+
function getString(value: unknown): string | undefined {
516+
return typeof value === "string" ? value : undefined;
517+
}

0 commit comments

Comments
 (0)