-
Notifications
You must be signed in to change notification settings - Fork 2
feat: update AGENTS and README metadata, enhance chat API #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,64 +1,21 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { mastra } from "@/src/mastra"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createAgentStreamResponse } from "@/lib/client-stream-to-ai-sdk"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { UIMessage } from "ai"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const maxDuration = 60; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ChatRequestBody { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages: UIMessage[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| agentId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resourceId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memory?: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread?: string | { id: string; resourceId?: string }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resource?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options?: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastMessages?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| semanticRecall?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workingMemory?: { enabled?: boolean }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxSteps?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { mastra } from "../../../src/mastra"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { RuntimeContext } from "@mastra/core/runtime-context"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+2
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Import path uses relative path instead of alias. The import uses -import { mastra } from "../../../src/mastra";
+import { mastra } from "@/src/mastra";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
3
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The import { mastra } from "../../../src/mastra";
import { RuntimeContext } from "@mastra/core/runtime-context";
export const maxDuration = 60;
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const maxDuration = 60; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No validation for required messages field - will throw if undefined/missing from request body
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/api/chat/route.ts
Line: 5:5
Comment:
**logic:** No validation for required `messages` field - will throw if undefined/missing from request body
How can I resolve this? If you propose a fix, please make it concise.
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation for required fields. The code assumes messages exists but doesn't validate it, which could lead to runtime errors if the request body is malformed. Add validation to check that messages is present and is an array before processing.
| const { messages, data } = await req.json(); | |
| const { messages, data } = await req.json(); | |
| if (!Array.isArray(messages)) { | |
| return new Response( | |
| JSON.stringify({ error: "Missing or invalid 'messages' field. Must be an array." }), | |
| { status: 400, headers: { "Content-Type": "application/json" } } | |
| ); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation for request body.
As per coding guidelines, API routes should validate user input server-side using a validation library like zod or yup. The messages and data fields are destructured without validation, which could lead to runtime errors or unexpected behavior.
+import { z } from "zod";
+
+const ChatRequestSchema = z.object({
+ messages: z.array(z.object({
+ role: z.string(),
+ content: z.string(),
+ })),
+ data: z.record(z.unknown()).optional(),
+});
+
export async function POST(req: Request) {
- const { messages, data } = await req.json();
+ const body = await req.json();
+ const { messages, data } = ChatRequestSchema.parse(body);
const myAgent = mastra.getAgent("weatherAgent");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function POST(req: Request) { | |
| const body: ChatRequestBody = await req.json(); | |
| // Get available agents dynamically from mastra | |
| const agentsMap = await mastra.getAgents(); | |
| const availableAgents = Object.keys(agentsMap); | |
| // Use first available agent if none specified | |
| const agentId = body.agentId || availableAgents[0]; | |
| if (!agentId || !availableAgents.includes(agentId)) { | |
| return Response.json( | |
| { error: `Invalid or missing agentId. Available: ${availableAgents.join(", ")}` }, | |
| { status: 400 } | |
| ); | |
| } | |
| const { messages, data } = await req.json(); | |
| const myAgent = mastra.getAgent("weatherAgent"); | |
| import { z } from "zod"; | |
| const ChatRequestSchema = z.object({ | |
| messages: z.array(z.object({ | |
| role: z.string(), | |
| content: z.string(), | |
| })), | |
| data: z.record(z.unknown()).optional(), | |
| }); | |
| export async function POST(req: Request) { | |
| const body = await req.json(); | |
| const { messages, data } = ChatRequestSchema.parse(body); | |
| const myAgent = mastra.getAgent("weatherAgent"); |
🤖 Prompt for AI Agents
In app/api/chat/route.ts around lines 4 to 6, the request body is destructured
into messages and data without validation; add server-side validation (use zod
or yup) by defining a schema for the expected shape (e.g., messages as an array
of message objects and data as an optional object), parse the incoming JSON with
the schema (use safeParse/validate) and if validation fails return a 400
response with an error message; on success use the typed/validated values for
the rest of the handler to avoid runtime errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Hardcoded agent name reduces flexibility.
The agent is hardcoded to "weatherAgent". Consider accepting the agent ID from the request body or environment variable to support different agents, similar to how createAgentStreamResponse in lib/client-stream-to-ai-sdk.ts accepts agentId as a parameter.
🤖 Prompt for AI Agents
In app/api/chat/route.ts around line 6 the agent is hardcoded as "weatherAgent";
update the handler to accept an agentId (e.g., from the request body or a query
param) and pass that to mastra.getAgent instead of the literal string; validate
the incoming agentId (non-empty string), fall back to a sensible default or to
an environment variable like process.env.DEFAULT_AGENT if not provided, and
ensure any errors for unknown/missing agents are handled and logged before
proceeding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Agent ID mismatch - weatherAgent doesn't exist. The agent is registered with ID weather-agent (see src/mastra/agents/weather-agent.ts:18). This will cause runtime errors.
| const myAgent = mastra.getAgent("weatherAgent"); | |
| const myAgent = mastra.getAgent("weather-agent"); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/api/chat/route.ts
Line: 6:6
Comment:
**logic:** Agent ID mismatch - `weatherAgent` doesn't exist. The agent is registered with ID `weather-agent` (see `src/mastra/agents/weather-agent.ts:18`). This will cause runtime errors.
```suggestion
const myAgent = mastra.getAgent("weather-agent");
```
How can I resolve this? If you propose a fix, please make it concise.
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The chat API is hardcoded to use only "weatherAgent", removing the ability to select different agents dynamically. The previous implementation supported dynamic agent selection via agentId parameter. This is a breaking change that significantly reduces the API's flexibility and contradicts the PR description which mentions "streamline agent selection" - this completely removes it instead.
| const { messages, data } = await req.json(); | |
| const myAgent = mastra.getAgent("weatherAgent"); | |
| const { messages, data, agentId } = await req.json(); | |
| const myAgent = mastra.getAgent(agentId ?? "weatherAgent"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No try-catch error handling - unhandled exceptions will crash the API route instead of returning proper error responses
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/api/chat/route.ts
Line: 4:20
Comment:
**logic:** No try-catch error handling - unhandled exceptions will crash the API route instead of returning proper error responses
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The chat API has been hardcoded to only use the weatherAgent. This significantly reduces the flexibility of this endpoint, which previously supported dynamic agent selection. While the PR description mentions this is a simplification, it makes the generic /api/chat route less reusable. To restore flexibility while keeping the new RuntimeContext logic, I suggest selecting the agent based on a property in the data object from the request body. This also includes adding a check to ensure the agent exists, providing a clearer error message to the client if it doesn't.
export async function POST(req: Request) {
const { messages, data } = await req.json();
const agentId = data?.agentId ?? "weatherAgent";
const agent = mastra.getAgent(agentId);
if (!agent) {
const availableAgents = Object.keys(await mastra.getAgents());
return Response.json(
{ error: `Agent '${agentId}' not found. Available agents: ${availableAgents.join(', ')}` },
{ status: 400 }
);
}
const runtimeContext = new RuntimeContext();
if (data) {
const { agentId: _, ...contextData } = data;
for (const [key, value] of Object.entries(contextData)) {
runtimeContext.set(key, value);
}
}
const stream = await agent.stream(messages, {
runtimeContext,
format: "aisdk",
});
return stream.toUIMessageStreamResponse();
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for async operations.
Per coding guidelines, use try...catch blocks for handling errors in asynchronous operations. The route lacks error handling for JSON parsing, agent retrieval, and streaming failures.
export async function POST(req: Request) {
+ try {
const { messages, data } = await req.json();
const myAgent = mastra.getAgent("weatherAgent");
const runtimeContext = new RuntimeContext();
if (data) {
for (const [key, value] of Object.entries(data)) {
runtimeContext.set(key, value);
}
}
const stream = await myAgent.stream(messages, {
runtimeContext,
format: "aisdk",
});
return stream.toUIMessageStreamResponse();
+ } catch (error) {
+ console.error("Chat API error:", error);
+ return new Response(
+ JSON.stringify({ error: "Failed to process chat request" }),
+ { status: 500, headers: { "Content-Type": "application/json" } }
+ );
+ }
}🤖 Prompt for AI Agents
In app/api/chat/route.ts around lines 4 to 21, the POST handler performs
multiple asynchronous operations (req.json(), mastra.getAgent, agent.stream,
stream.toUIMessageStreamResponse) without error handling; wrap the body in a
try...catch, validate and guard req.json() output, check that myAgent exists
before using it, await and catch failures from myAgent.stream and
stream.toUIMessageStreamResponse, log the caught error for diagnostics, and
return an appropriate HTTP Response (e.g., JSON error message with a 400 or 500
status as appropriate) from the catch block so failures are surfaced to clients
instead of crashing the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Removed all dynamic agent selection, error handling, validation, and the GET endpoint. The previous implementation supported any agent via agentId parameter and had proper error handling. Now it only works with one hardcoded agent. Is this intentional? This breaks existing functionality for multi-agent chat.
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/api/chat/route.ts
Line: 1:21
Comment:
**logic:** Removed all dynamic agent selection, error handling, validation, and the GET endpoint. The previous implementation supported any agent via `agentId` parameter and had proper error handling. Now it only works with one hardcoded agent. Is this intentional? This breaks existing functionality for multi-agent chat.
How can I resolve this? If you propose a fix, please make it concise.
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplified API removed error handling that was present in the previous implementation. Consider wrapping the stream call in a try-catch block to handle potential errors gracefully and return appropriate error responses to the client.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,8 @@ import { | |||||
| createUIMessageStreamResponse, | ||||||
| } from "ai"; | ||||||
| import { toAISdkFormat } from "@mastra/ai-sdk"; | ||||||
| import type { Mastra } from "@mastra/core/mastra"; | ||||||
| import type { AgentExecutionOptions } from "@mastra/core/agent"; | ||||||
| import type { MastraModelOutput } from "@mastra/core/stream"; | ||||||
|
|
||||||
| export interface StreamToAISdkOptions { | ||||||
|
|
@@ -16,34 +18,34 @@ export interface AgentStreamOptions { | |||||
| format?: "aisdk" | "mastra"; | ||||||
| threadId?: string; | ||||||
| resourceId?: string; | ||||||
| memory?: { | ||||||
| thread?: string | { id: string; resourceId?: string }; | ||||||
| resource?: string; | ||||||
| options?: { | ||||||
| lastMessages?: number; | ||||||
| semanticRecall?: boolean; | ||||||
| workingMemory?: { enabled?: boolean }; | ||||||
| }; | ||||||
| }; | ||||||
| memory?: AgentExecutionOptions["memory"]; | ||||||
| maxSteps?: number; | ||||||
| } | ||||||
|
|
||||||
| type MastraAgent = { | ||||||
| stream: ( | ||||||
| messages: unknown, | ||||||
| options?: { | ||||||
| format?: string; | ||||||
| threadId?: string; | ||||||
| resourceId?: string; | ||||||
| memory?: AgentStreamOptions["memory"]; | ||||||
| maxSteps?: number; | ||||||
| } | ||||||
| ) => Promise<MastraModelOutput & { toUIMessageStreamResponse?: () => Response }>; | ||||||
| }; | ||||||
| type StreamResult = MastraModelOutput & { toUIMessageStreamResponse?: () => Response }; | ||||||
|
|
||||||
| function isReadableStream<T>(value: unknown): value is ReadableStream<T> { | ||||||
| return ( | ||||||
| typeof value === "object" && | ||||||
| value !== null && | ||||||
| typeof (value as ReadableStream<T>).getReader === "function" | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| type MastraInstance = { | ||||||
| getAgent: (id: string) => MastraAgent; | ||||||
| } & Record<string, any>; | ||||||
| async function* asyncIterableFromReadableStream<T>( | ||||||
| stream: ReadableStream<T> | ||||||
| ): AsyncIterable<T> { | ||||||
| const reader = stream.getReader(); | ||||||
| try { | ||||||
| while (true) { | ||||||
| const { done, value } = await reader.read(); | ||||||
| if (done) break; | ||||||
| yield value; | ||||||
| } | ||||||
| } finally { | ||||||
| reader.releaseLock(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Creates a streaming Response for Next.js API routes using server-side Mastra agent. | ||||||
|
|
@@ -55,7 +57,7 @@ type MastraInstance = { | |||||
| * ```ts | ||||||
| * // app/api/chat/route.ts | ||||||
| * import { mastra } from "@/src/mastra"; | ||||||
| * import { createAgentStreamResponse } from "@/lib/client-stream-to-ai-sdk"; | ||||||
| * import { createAgentStreamResponse } from "@/lib/server/agent-stream"; | ||||||
|
||||||
| * import { createAgentStreamResponse } from "@/lib/server/agent-stream"; | |
| * import { createAgentStreamResponse } from "@/lib/client-stream-to-ai-sdk"; |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code calls agent.stream() once and stores the result, which is good. However, when format === "aisdk" but toUIMessageStreamResponse is not available, the code falls through to the manual transformation that may attempt to process an already-consumed stream. Consider checking if the stream can be reused or restructuring to avoid potential stream consumption issues.
| if (streamOptions.format === "aisdk" && stream.toUIMessageStreamResponse) { | |
| if (streamOptions.format === "aisdk" && typeof stream.toUIMessageStreamResponse === "function") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Type assertion is acceptable but consider stronger typing.
The cast value as Parameters<typeof writer.write>[0] works but is somewhat opaque. Consider defining an explicit type for the stream chunk if this pattern is used elsewhere.
🤖 Prompt for AI Agents
In lib/client-stream-to-ai-sdk.ts around lines 109 to 111, the loop casts each
iterated value with an opaque assertion (value as Parameters<typeof
writer.write>[0]); define an explicit chunk type (e.g., type StreamChunk = ...
or export an interface) that matches writer.write's parameter and use that type
for the iterable (for await (const value of iterable as
AsyncIterable<StreamChunk>)) and for writer.write(value) so the cast is removed
and the types are clear and reusable across the codebase.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,12 @@ | ||
| import { MastraClient } from "@mastra/client-js"; | ||
|
|
||
| /** | ||
| * Client-side Mastra SDK instance for frontend use. | ||
| * Use this in React components to interact with the Mastra API. | ||
| * | ||
| * For server-side streaming in API routes, import createAgentStreamResponse | ||
| * directly from "@/lib/client-stream-to-ai-sdk" instead. | ||
| */ | ||
|
Comment on lines
+3
to
+9
|
||
| export const mastraClient = new MastraClient({ | ||
| baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || "http://localhost:4111", | ||
| retries: 3, | ||
|
|
@@ -9,6 +16,3 @@ export const mastraClient = new MastraClient({ | |
| headers: {}, | ||
| credentials: "same-origin", | ||
| }); | ||
|
|
||
| export { createAgentStreamResponse } from "./client-stream-to-ai-sdk"; | ||
| export type { StreamToAISdkOptions } from "./client-stream-to-ai-sdk"; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor grammar fix: use hyphen in compound modifier.
Per static analysis, "code-agent focused" should use a hyphen when used as a compound adjective.
📝 Committable suggestion
🧰 Tools
🪛 LanguageTool
[grammar] ~125-~125: Use a hyphen to join words.
Context: .../mastra/AGENTS.md`: top-level code-agent focused docs (this file is mirrored to s...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents