Skip to content

Conversation

@ssdeanx
Copy link
Owner

@ssdeanx ssdeanx commented Dec 5, 2025

  • Added new API route for chat functionality in r.ts.
  • Integrated agent streaming response using createAgentStreamResponse.
  • Implemented error handling for invalid agent IDs and missing messages.
  • Returned available agents and their count in the GET request.

Summary by Sourcery

Add a new chat API route that streams agent responses and exposes available agents.

New Features:

  • Introduce a POST /api/chat endpoint that streams responses from Mastra agents based on provided messages and options.
  • Expose a GET /api/chat endpoint that returns the list of available agents and their count.

Enhancements:

  • Improve server-side streaming helper documentation to clarify its intended usage context.

- Added new API route for chat functionality in r.ts.
- Integrated agent streaming response using createAgentStreamResponse.
- Implemented error handling for invalid agent IDs and missing messages.
- Returned available agents and their count in the GET request.
Copilot AI review requested due to automatic review settings December 5, 2025 17:48
@continue
Copy link

continue bot commented Dec 5, 2025

Keep this PR in a mergeable state →

Learn more

All Green is an AI agent that automatically:

✅ Addresses code review comments

✅ Fixes failing CI checks

✅ Resolves merge conflicts

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 5, 2025

Reviewer's Guide

Implements a new Next.js API route for chat that streams responses from Mastra agents, including validation of agent IDs and messages, and exposes an endpoint to list available agents, along with a minor docstring tweak in the streaming helper.

Sequence diagram for POST /api/chat streaming flow

sequenceDiagram
    actor Client
    participant ChatAPI as ChatRoute_POST
    participant Mastra as mastra
    participant StreamHelper as createAgentStreamResponse

    Client->>ChatAPI: POST /api/chat with body (messages, agentId?, threadId?, resourceId?, memory?, maxSteps?)
    ChatAPI->>ChatAPI: Parse JSON body into ChatRequestBody

    ChatAPI->>Mastra: getAgents()
    Mastra-->>ChatAPI: agentsMap
    ChatAPI->>ChatAPI: availableAgents = Object.keys(agentsMap)
    ChatAPI->>ChatAPI: agentId = body.agentId || availableAgents[0]

    alt missing_or_invalid_agentId
        ChatAPI-->>Client: 400 JSON { error: Invalid or missing agentId, Available: ... }
    else valid_agentId
        alt missing_messages
            ChatAPI-->>Client: 400 JSON { error: messages required }
        else valid_messages
            ChatAPI->>StreamHelper: createAgentStreamResponse(mastra, agentId, body.messages, options)
            note over StreamHelper: options: { threadId, resourceId, memory, maxSteps (default 50) }
            alt stream_success
                StreamHelper-->>ChatAPI: streaming Response
                ChatAPI-->>Client: 200 streaming Response
            else stream_error
                StreamHelper-->>ChatAPI: throw Error
                ChatAPI-->>Client: 500 JSON { error: error.message or Stream failed }
            end
        end
    end
Loading

File-Level Changes

Change Details Files
Add POST /api/chat/r endpoint that streams agent responses using Mastra and createAgentStreamResponse.
  • Define ChatRequestBody interface to strongly type the POST request payload, including messages, agentId, threadId, resourceId, memory, and maxSteps.
  • Parse and validate the JSON body, ensuring messages are present and non-empty.
  • Resolve available agent IDs dynamically via mastra.getAgents and select a default agent when agentId is not specified.
  • Validate agentId against the available agents and return a 400 JSON error when invalid or missing.
  • Invoke createAgentStreamResponse with the Mastra instance, selected agentId, messages, and optional parameters (threadId, resourceId, memory, maxSteps with a default of 50).
  • Wrap the streaming call in a try/catch and return a 500 JSON error with a safe message on failures.
app/api/chat/r.ts
Add GET /api/chat/r endpoint to expose available agents and their count.
  • Call mastra.getAgents to retrieve the agents map on each GET request.
  • Compute the list of agent IDs and return them along with their count as JSON.
app/api/chat/r.ts
Tweak documentation comment in client-stream-to-ai-sdk helper.
  • Update the guidance comment about usage of the streaming helper to refer to an "API 5stra instance" instead of "SERVER-SIDE mastra instance" (likely a typo to verify).
lib/client-stream-to-ai-sdk.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ssdeanx ssdeanx merged commit 8724386 into main Dec 5, 2025
7 of 9 checks passed
@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Caution

Review failed

The pull request is closed.

Summary by CodeRabbit

  • New Features
    • Streaming chat API endpoint enabling real-time interactions with agents, including message validation and session management.
    • Agent discovery endpoint for retrieving and listing all available agents.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This PR introduces a new chat API route (app/api/chat/r.ts) that provides POST and GET handlers for agent-based streaming conversations, and updates documentation in an existing streaming utility file.

Changes

Cohort / File(s) Summary
New Chat API Route
app/api/chat/r.ts
Introduces POST handler for streaming agent chat with request validation and agent selection, and GET handler to list available agents. Exports maxDuration constant and ChatRequestBody interface.
Documentation Update
lib/client-stream-to-ai-sdk.ts
Updates JSDoc comment referencing "SERVER-SIDE mastra instance" to "API mastra instance"; no functional changes.

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as POST Handler
    participant Mastra as Mastra Utils
    participant Stream as Streaming Response
    
    Client->>API: POST /api/chat/r<br/>(messages, agentId?, threadId, etc.)
    API->>Mastra: getAgents()
    Mastra-->>API: agents[]
    alt agentId provided
        API->>API: Validate agentId<br/>against agents
    else agentId not provided
        API->>API: Select default agent
    end
    alt Validation passes
        API->>Stream: createAgentStreamResponse<br/>(agentId, messages, payload)
        Stream-->>Client: Streaming response
    else Validation fails
        API-->>Client: 500 Error
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Area of attention: The app/api/chat/r.ts file should be reviewed for proper error handling, agentId validation logic, and integration correctness with createAgentStreamResponse function signature expectations.

Possibly related PRs

Suggested reviewers

  • Copilot
  • sourcery-ai

Poem

🐰 A chat route hops into being today,
With agents and streams in a magical way,
Validation and defaults dance hand-in-hand,
While mastra's utilities help us understand,
From POST to GET, the API takes flight!

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cefa8f1 and 534946f.

📒 Files selected for processing (2)
  • app/api/chat/r.ts (1 hunks)
  • lib/client-stream-to-ai-sdk.ts (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Dec 5, 2025

🤖 Hi @ssdeanx, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ssdeanx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new chat API endpoint designed to facilitate real-time conversational interactions. It integrates agent streaming capabilities, allowing for dynamic and responsive communication. The implementation includes comprehensive error handling to ensure stability and provides a mechanism for discovering available agents, enhancing the flexibility and usability of the chat system.

Highlights

  • New Chat API Endpoint: A new API route, /api/chat, has been introduced to handle chat functionalities, supporting both POST requests for chat interactions and GET requests for agent discovery.
  • Agent Streaming Integration: The POST endpoint for chat leverages createAgentStreamResponse to provide streaming responses from a server-side Mastra agent, enabling real-time conversational interactions.
  • Robust Error Handling: The API includes comprehensive error handling for various scenarios, such as invalid or missing agent IDs and empty message arrays, returning appropriate HTTP status codes and error messages.
  • Dynamic Agent Discovery: The API dynamically fetches available agents from the mastra instance, and a GET endpoint is provided to list these agents and their total count, enhancing flexibility.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The JSDoc comment in client-stream-to-ai-sdk.ts was changed to API 5stra instance, which looks like a typo and also removed the clarification about using the server-side Mastra instance; consider restoring/correcting that sentence so the intended usage is clear.
  • The cast mastra as Parameters<typeof createAgentStreamResponse>[0] in the POST handler suggests a type mismatch; it would be cleaner to align the type signature of createAgentStreamResponse with the actual mastra instance or introduce a shared interface rather than relying on a loose cast.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The JSDoc comment in client-stream-to-ai-sdk.ts was changed to `API 5stra instance`, which looks like a typo and also removed the clarification about using the server-side Mastra instance; consider restoring/correcting that sentence so the intended usage is clear.
- The cast `mastra as Parameters<typeof createAgentStreamResponse>[0]` in the POST handler suggests a type mismatch; it would be cleaner to align the type signature of `createAgentStreamResponse` with the actual mastra instance or introduce a shared interface rather than relying on a loose cast.

## Individual Comments

### Comment 1
<location> `lib/client-stream-to-ai-sdk.ts:53` </location>
<code_context>
 /**
  * Creates a streaming Response for Next.js API routes using server-side Mastra agent.
  * 
- * IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
+ * IMPORTANT: This should be used in API 5stra instance,
  * not the client SDK. The client SDK (MastraClient) is for frontend use only.
  * 
</code_context>

<issue_to_address>
**nitpick (typo):** The updated docstring introduces a likely typo in the product name.

`API 5stra instance` looks like an accidental typo for `Mastra` and could confuse readers. Please change this back to something like `SERVER-SIDE Mastra instance` or another intentional phrasing.

```suggestion
 * IMPORTANT: This should be used in API routes with the server-side Mastra instance,
```
</issue_to_address>

### Comment 2
<location> `app/api/chat/r.ts:28-32` </location>
<code_context>
+  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)) {
</code_context>

<issue_to_address>
**issue:** Handling an empty agents list would avoid ambiguous errors when no agents are configured.

When `mastra.getAgents()` returns an empty object, `availableAgents[0]` is `undefined`, so the request fails with a 400 `Invalid or missing agentId` and an empty `Available:` list. Consider explicitly checking `availableAgents.length === 0` and returning a clearer “no agents configured” error (e.g. 503) instead of implying a client error.
</issue_to_address>

### Comment 3
<location> `app/api/chat/r.ts:25` </location>
<code_context>
+}
+
+export async function POST(req: Request) {
+  const body: ChatRequestBody = await req.json();
+  
+  // Get available agents dynamically from mastra
</code_context>

<issue_to_address>
**issue:** JSON parsing errors on the request body are not caught and will surface as unhandled errors.

Because `await req.json()` is called before the `try` block, invalid JSON will throw an unhandled error and bypass your structured error handling. Move `req.json()` inside the `try` (or wrap it in its own try/catch) so malformed bodies return a controlled 4xx response instead of an unstructured error.
</issue_to_address>

### Comment 4
<location> `app/api/chat/r.ts:52-57` </location>
<code_context>
+      memory: body.memory,
+      maxSteps: body.maxSteps ?? 50,
+    });
+  } catch (error) {
+    return Response.json(
+      { error: error instanceof Error ? error.message : "Stream failed" },
+      { status: 500 }
+    );
</code_context>

<issue_to_address>
**🚨 suggestion (security):** Returning raw error messages may expose internal implementation details to clients.

Instead of returning `error.message` to the client, consider logging the full error on the server and sending a generic error string in the JSON body, while keeping the 500 status code unchanged.

```suggestion
  } catch (error) {
    console.error("Error creating agent stream response", error);
    return Response.json(
      { error: "Stream failed" },
      { status: 500 }
    );
  }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

/**
* Creates a streaming Response for Next.js API routes using server-side Mastra agent.
*
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): The updated docstring introduces a likely typo in the product name.

API 5stra instance looks like an accidental typo for Mastra and could confuse readers. Please change this back to something like SERVER-SIDE Mastra instance or another intentional phrasing.

Suggested change
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
* IMPORTANT: This should be used in API routes with the server-side Mastra instance,

Comment on lines +28 to +32
const agentsMap = await mastra.getAgents();
const availableAgents = Object.keys(agentsMap);

// Use first available agent if none specified
const agentId = body.agentId || availableAgents[0];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handling an empty agents list would avoid ambiguous errors when no agents are configured.

When mastra.getAgents() returns an empty object, availableAgents[0] is undefined, so the request fails with a 400 Invalid or missing agentId and an empty Available: list. Consider explicitly checking availableAgents.length === 0 and returning a clearer “no agents configured” error (e.g. 503) instead of implying a client error.

}

export async function POST(req: Request) {
const body: ChatRequestBody = await req.json();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: JSON parsing errors on the request body are not caught and will surface as unhandled errors.

Because await req.json() is called before the try block, invalid JSON will throw an unhandled error and bypass your structured error handling. Move req.json() inside the try (or wrap it in its own try/catch) so malformed bodies return a controlled 4xx response instead of an unstructured error.

Comment on lines +52 to +57
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : "Stream failed" },
{ status: 500 }
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 suggestion (security): Returning raw error messages may expose internal implementation details to clients.

Instead of returning error.message to the client, consider logging the full error on the server and sending a generic error string in the JSON body, while keeping the 500 status code unchanged.

Suggested change
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : "Stream failed" },
{ status: 500 }
);
}
} catch (error) {
console.error("Error creating agent stream response", error);
return Response.json(
{ error: "Stream failed" },
{ status: 500 }
);
}

@github-actions
Copy link

github-actions bot commented Dec 5, 2025

🤖 I'm sorry @ssdeanx, but I was unable to process your request. Please see the logs for more details.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new chat API endpoint with streaming capabilities and an endpoint to list available agents. The implementation is solid, but there are a few areas for improvement. My main feedback concerns error handling in both the POST and GET handlers, which currently don't handle all potential exceptions, leading to possible unhandled promise rejections. I've suggested refactoring them to be more robust. I also noted some minor code duplication that could be refactored for better maintainability and a typo in a comment. Overall, these changes add valuable functionality.

Comment on lines +24 to +58
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 }
);
}

if (!body.messages?.length) {
return Response.json({ error: "messages required" }, { status: 400 });
}

try {
return await createAgentStreamResponse(mastra as Parameters<typeof createAgentStreamResponse>[0], agentId, body.messages, {
threadId: body.threadId,
resourceId: body.resourceId,
memory: body.memory,
maxSteps: body.maxSteps ?? 50,
});
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : "Stream failed" },
{ status: 500 }
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The POST handler has a couple of areas for improvement:

  1. Error Handling: The current try...catch block only wraps the call to createAgentStreamResponse. Any errors occurring before that, such as parsing the request body with req.json() or fetching agents with mastra.getAgents(), will result in unhandled promise rejections. This can lead to non-descriptive 500 errors. It's best practice to wrap the entire function body in a try...catch block to handle all potential errors gracefully. You can also specifically catch SyntaxError to return a 400 for invalid JSON.
  2. Type Assertion: The type assertion mastra as Parameters<typeof createAgentStreamResponse>[0] on line 46 is redundant. The mastra instance is already correctly typed, so this assertion adds unnecessary verbosity.

I've provided a suggestion that addresses both points by restructuring the error handling and simplifying the code.

export async function POST(req: Request) {
  try {
    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 }
      );
    }

    if (!body.messages?.length) {
      return Response.json({ error: "messages required" }, { status: 400 });
    }

    return await createAgentStreamResponse(mastra, agentId, body.messages, {
      threadId: body.threadId,
      resourceId: body.resourceId,
      memory: body.memory,
      maxSteps: body.maxSteps ?? 50,
    });
  } catch (error) {
    if (error instanceof SyntaxError) {
      return Response.json({ error: "Invalid request body" }, { status: 400 });
    }
    return Response.json(
      { error: error instanceof Error ? error.message : "An internal server error occurred" },
      { status: 500 }
    );
  }
}

Comment on lines +60 to +64
export async function GET() {
const agentsMap = await mastra.getAgents();
const availableAgents = Object.keys(agentsMap);
return Response.json({ agents: availableAgents, count: availableAgents.length });
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the POST handler, the GET handler lacks error handling. If mastra.getAgents() throws an exception, it will result in an unhandled promise rejection and a generic 500 error. It's important to wrap the logic in a try...catch block to handle potential failures gracefully.

export async function GET() {
  try {
    const agentsMap = await mastra.getAgents();
    const availableAgents = Object.keys(agentsMap);
    return Response.json({ agents: availableAgents, count: availableAgents.length });
  } catch (error) {
    return Response.json(
      { error: error instanceof Error ? error.message : "Failed to retrieve agents" },
      { status: 500 }
    );
  }
}

Comment on lines +28 to +29
const agentsMap = await mastra.getAgents();
const availableAgents = Object.keys(agentsMap);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for fetching and processing the list of available agents is duplicated in both the POST (lines 28-29) and GET (lines 61-62) handlers. To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, consider extracting this logic into a separate helper function within the module.

* Creates a streaming Response for Next.js API routes using server-side Mastra agent.
*
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
* IMPORTANT: This should be used in API 5stra instance,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There appears to be a typo in this comment. The text API 5stra instance seems incorrect and makes the comment's meaning unclear. It looks like part of the original sentence was accidentally deleted and replaced. Restoring it would improve clarity for future developers.

Suggested change
* IMPORTANT: This should be used in API 5stra instance,
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,

@greptile-apps
Copy link

greptile-apps bot commented Dec 5, 2025

Greptile Overview

Greptile Summary

Adds new chat API route (/api/chat/r.ts) with dynamic agent selection and streaming capabilities using the createAgentStreamResponse utility.

  • Created flexible chat API with GET endpoint to list available agents and POST endpoint for agent streaming
  • Integrated comprehensive error handling for invalid agents and missing messages
  • Added support for memory configuration, thread management, and max steps
  • Fixed documentation typo in streaming utility

Issues Found:

  • Logic error in agent fallback: if no agents exist, availableAgents[0] returns undefined, bypassing validation
  • Minor typo in documentation comment

Confidence Score: 3/5

  • Safe to merge after fixing the empty agent array logic bug
  • Core implementation follows established patterns from existing chat routes. The logic bug with empty agent arrays needs fixing as it could cause runtime errors. The typo is minor but should be corrected. Otherwise, error handling and streaming setup are solid.
  • Pay close attention to app/api/chat/r.ts line 32 for the empty array handling bug

Important Files Changed

File Analysis

Filename Score Overview
app/api/chat/r.ts 3/5 New chat API route with agent streaming, has logic issue with empty agent array handling
lib/client-stream-to-ai-sdk.ts 4/5 Minor typo in documentation comment ("5stra" instead of "Mastra")

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as /api/chat/r
    participant Mastra
    participant Agent
    participant StreamUtil as createAgentStreamResponse
    
    Client->>API: POST /api/chat/r {messages, agentId?, ...}
    API->>Mastra: getAgents()
    Mastra-->>API: agentsMap
    API->>API: Extract agent IDs
    API->>API: Validate agentId or use first available
    
    alt Invalid/Missing agentId
        API-->>Client: 400 {error: "Invalid or missing agentId..."}
    end
    
    alt Missing messages
        API-->>Client: 400 {error: "messages required"}
    end
    
    API->>StreamUtil: createAgentStreamResponse(mastra, agentId, messages, options)
    StreamUtil->>Mastra: getAgent(agentId)
    Mastra-->>StreamUtil: agent
    StreamUtil->>Agent: stream(messages, streamOptions)
    Agent-->>StreamUtil: stream result
    
    alt Has toUIMessageStreamResponse
        StreamUtil->>StreamUtil: stream.toUIMessageStreamResponse()
        StreamUtil-->>API: Response (AI SDK format)
    else Fallback
        StreamUtil->>StreamUtil: Manual transformation with toAISdkFormat
        StreamUtil->>StreamUtil: createUIMessageStream + createUIMessageStreamResponse
        StreamUtil-->>API: Response (AI SDK format)
    end
    
    alt Success
        API-->>Client: 200 Stream Response
    else Error
        API-->>Client: 500 {error: message}
    end
    
    Note over Client,Agent: GET /api/chat/r returns available agents
    Client->>API: GET /api/chat/r
    API->>Mastra: getAgents()
    Mastra-->>API: agentsMap
    API->>API: Extract agent IDs
    API-->>Client: 200 {agents: [...], count: N}
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

* Creates a streaming Response for Next.js API routes using server-side Mastra agent.
*
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
* IMPORTANT: This should be used in API 5stra instance,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: typo: "5stra" should be "Mastra"

Suggested change
* IMPORTANT: This should be used in API 5stra instance,
* IMPORTANT: This should be used in API routes with the SERVER-SIDE Mastra instance,
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/client-stream-to-ai-sdk.ts
Line: 53:53

Comment:
**syntax:** typo: "5stra" should be "Mastra"

```suggestion
 * IMPORTANT: This should be used in API routes with the SERVER-SIDE Mastra instance,
```

How can I resolve this? If you propose a fix, please make it concise.

const availableAgents = Object.keys(agentsMap);

// Use first available agent if none specified
const agentId = body.agentId || availableAgents[0];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: if availableAgents array is empty, availableAgents[0] will be undefined, causing the validation on line 34 to pass incorrectly when no agentId is provided

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/api/chat/r.ts
Line: 32:32

Comment:
**logic:** if `availableAgents` array is empty, `availableAgents[0]` will be `undefined`, causing the validation on line 34 to pass incorrectly when no `agentId` is provided

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new chat API endpoint with agent streaming capabilities using Mastra. The implementation provides dynamic agent selection, proper error handling, and a GET endpoint to list available agents. However, there are concerns regarding naming conventions, error message consistency, and potential security/performance improvements.

Key Changes:

  • Added new chat API route with streaming support and dynamic agent management
  • Fixed documentation typo in the streaming utility function
  • Implemented input validation and error handling for the chat endpoint

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
app/api/chat/r.ts New API route providing POST endpoint for agent streaming chat and GET endpoint for listing agents
lib/client-stream-to-ai-sdk.ts Corrected documentation comment (though introduced a typo in the process)

Comment on lines +28 to +29
const agentsMap = await mastra.getAgents();
const availableAgents = Object.keys(agentsMap);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mastra.getAgents() call is executed on every POST request (line 28) and again on every GET request (line 61). If the agents list is static or changes infrequently, consider caching this result to avoid repeated async calls on each request. This could improve response times, especially under high load.

Copilot uses AI. Check for mistakes.
}

try {
return await createAgentStreamResponse(mastra as Parameters<typeof createAgentStreamResponse>[0], agentId, body.messages, {
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type assertion as Parameters<typeof createAgentStreamResponse>[0] is fragile and suggests a type mismatch between the mastra instance and what createAgentStreamResponse expects. This could indicate:

  1. The mastra import may not have the correct type
  2. There could be runtime type safety issues

Consider either:

  • Ensuring mastra is typed as Mastra from @mastra/core/mastra
  • Adding explicit type validation
  • Removing the type assertion if the types actually align

This type casting bypasses TypeScript's type checking and could hide potential runtime errors.

Suggested change
return await createAgentStreamResponse(mastra as Parameters<typeof createAgentStreamResponse>[0], agentId, body.messages, {
return await createAgentStreamResponse(mastra, agentId, body.messages, {

Copilot uses AI. Check for mistakes.
});
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : "Stream failed" },
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic error handling exposes internal error messages directly to clients. Consider sanitizing error messages in production to avoid leaking implementation details. For example:

catch (error) {
  console.error('Agent stream error:', error);
  return Response.json(
    { error: process.env.NODE_ENV === 'production' 
      ? 'Failed to process chat request' 
      : error instanceof Error ? error.message : "Stream failed" 
    },
    { status: 500 }
  );
}

This prevents potential information disclosure while maintaining helpful error messages during development.

Suggested change
{ error: error instanceof Error ? error.message : "Stream failed" },
{ error: process.env.NODE_ENV === 'production'
? 'Failed to process chat request'
: error instanceof Error ? error.message : "Stream failed"
},

Copilot uses AI. Check for mistakes.
* Creates a streaming Response for Next.js API routes using server-side Mastra agent.
*
* IMPORTANT: This should be used in API routes with the SERVER-SIDE mastra instance,
* IMPORTANT: This should be used in API 5stra instance,
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in documentation comment: "API 5stra instance" should be "API routes with the SERVER-SIDE Mastra instance".

Suggested change
* IMPORTANT: This should be used in API 5stra instance,
* IMPORTANT: This should be used in API routes with the SERVER-SIDE Mastra instance,

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +64
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;
}

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 }
);
}

if (!body.messages?.length) {
return Response.json({ error: "messages required" }, { status: 400 });
}

try {
return await createAgentStreamResponse(mastra as Parameters<typeof createAgentStreamResponse>[0], agentId, body.messages, {
threadId: body.threadId,
resourceId: body.resourceId,
memory: body.memory,
maxSteps: body.maxSteps ?? 50,
});
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : "Stream failed" },
{ status: 500 }
);
}
}

export async function GET() {
const agentsMap = await mastra.getAgents();
const availableAgents = Object.keys(agentsMap);
return Response.json({ agents: availableAgents, count: availableAgents.length });
} No newline at end of file
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file name r.ts is unclear and non-standard for Next.js API routes. According to Next.js conventions, API route files should be named route.ts (for App Router) or use descriptive names. Since app/api/chat/route.ts already exists, consider either:

  1. Replacing the existing route.ts with this implementation
  2. Using a more descriptive name like stream-route.ts or organizing into a subdirectory like app/api/chat/stream/route.ts

The current name r.ts is ambiguous and makes the codebase harder to navigate.

Copilot uses AI. Check for mistakes.
}

if (!body.messages?.length) {
return Response.json({ error: "messages required" }, { status: 400 });
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "messages required" is inconsistent with the more detailed error messages used elsewhere in the file (line 36). Consider using a more descriptive and consistent format: "Missing required field: messages" or "messages array is required and cannot be empty".

Suggested change
return Response.json({ error: "messages required" }, { status: 400 });
return Response.json({ error: "Missing required field: messages. The messages array is required and cannot be empty." }, { status: 400 });

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants