Skip to content

Commit 2bc17ac

Browse files
authored
Merge pull request #114 from ssdeanx/develop
Develop
2 parents c524fc7 + a6c5687 commit 2bc17ac

23 files changed

Lines changed: 1933 additions & 1463 deletions

File tree

app/api/chat/r.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ export async function POST(req: Request) {
3030
const body: ChatRequestBody = await req.json()
3131

3232
// Get available agents dynamically from mastra
33-
const agentsMap = await mastra.getAgents()
33+
const agentsMap = await mastra.listAgents()
34+
3435
const availableAgents = Object.keys(agentsMap)
35-
36+
const agent = await mastra.getAgent()
3637
// Prefer explicit top-level agentId, then nested data.agentId (network-style),
3738
// otherwise fall back to the first available agent
3839
const agentId = body.agentId ?? body.data?.agentId ?? availableAgents[0]
@@ -76,7 +77,7 @@ export async function POST(req: Request) {
7677
}
7778

7879
export async function GET() {
79-
const agentsMap = await mastra.getAgents()
80+
const agentsMap = await mastra.listAgents()
8081
const availableAgents = Object.keys(agentsMap)
8182
return Response.json({
8283
agents: availableAgents,

app/api/chat/route.ts

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,94 @@
11
import { mastra } from '../../../src/mastra'
2-
import { RequestContext } from '@mastra/core/request-context'
2+
import {
3+
RequestContext,
4+
MASTRA_RESOURCE_ID_KEY,
5+
MASTRA_THREAD_ID_KEY,
6+
} from '@mastra/core/request-context'
37
import { toAISdkStream } from '@mastra/ai-sdk'
48
import { createUIMessageStream, createUIMessageStreamResponse } from 'ai'
9+
import type { UIMessage } from 'ai'
510

611
export async function POST(req: Request) {
7-
const { messages, data, id } = await req.json()
12+
const body = await req.json()
13+
const { messages, data, id } = body
814

9-
const agentId: string =
15+
// Agent selection logic
16+
const agentId =
1017
(typeof data?.agentId === 'string' && data.agentId.length > 0
1118
? data.agentId
1219
: undefined) ??
1320
(typeof id === 'string' && id.length > 0 ? id : undefined) ??
1421
'weatherAgent'
1522

16-
const myAgent = mastra.getAgent(agentId)
23+
const agent = mastra.getAgent(agentId)
1724

25+
if (!agent) {
26+
return Response.json(
27+
{ error: `Agent "${agentId}" not found` },
28+
{ status: 404 }
29+
)
30+
}
31+
32+
// Extract multi-tenancy IDs
33+
const threadId = body.threadId ?? data?.threadId
34+
const resourceId = body.resourceId ?? data?.resourceId
35+
36+
// Create RequestContext for multi-tenancy isolation
1837
const requestContext = new RequestContext()
38+
if (resourceId) requestContext.set(MASTRA_RESOURCE_ID_KEY, resourceId)
39+
if (threadId) requestContext.set(MASTRA_THREAD_ID_KEY, threadId)
1940

20-
if (data) {
41+
// Merge other data into context if present
42+
if (data && typeof data === 'object') {
2143
for (const [key, value] of Object.entries(data)) {
22-
requestContext.set(key, value)
44+
if (
45+
key !== 'agentId' &&
46+
key !== 'threadId' &&
47+
key !== 'resourceId'
48+
) {
49+
requestContext.set(key, value)
50+
}
2351
}
2452
}
2553

26-
const stream = await myAgent.stream(messages, { requestContext })
54+
// Prepare stream options
55+
const streamOptions = {
56+
threadId,
57+
resourceId,
58+
memory: body.memory ?? data?.memory,
59+
requestContext,
60+
}
61+
62+
const stream = await agent.stream(messages, streamOptions)
2763

2864
const uiStream = createUIMessageStream({
29-
originalMessages: messages,
65+
originalMessages: messages as UIMessage[],
3066
execute: async ({ writer }) => {
31-
const aiStream = toAISdkStream(stream, { from: 'agent' }) as any
67+
const aiStream = toAISdkStream(stream, {
68+
from: 'agent',
69+
sendReasoning: true,
70+
sendSources: true,
71+
})
3272

33-
if (typeof aiStream[Symbol.asyncIterator] === 'function') {
34-
for await (const part of aiStream as AsyncIterable<any>) {
35-
await writer.write(part)
36-
}
37-
} else if (typeof aiStream.getReader === 'function') {
38-
const reader = aiStream.getReader()
73+
// Support both ReadableStream and AsyncIterable (robust bridge)
74+
if (
75+
aiStream &&
76+
typeof (aiStream as any).getReader === 'function'
77+
) {
78+
const reader = (aiStream as ReadableStream<any>).getReader()
3979
try {
4080
while (true) {
4181
const { value, done } = await reader.read()
42-
if (done) {
43-
break
44-
}
82+
if (done) break
4583
await writer.write(value)
4684
}
4785
} finally {
4886
reader.releaseLock?.()
4987
}
88+
} else if (aiStream && Symbol.asyncIterator in aiStream) {
89+
for await (const part of aiStream as AsyncIterable<any>) {
90+
await writer.write(part)
91+
}
5092
}
5193
},
5294
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { MastraClient } from '@mastra/client-js'
2+
3+
const mastraClient = new MastraClient({
4+
baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || 'http://localhost:4111',
5+
})
6+
7+
export async function GET(
8+
request: Request,
9+
{ params }: { params: Promise<{ runId: string }> }
10+
): Promise<Response> {
11+
const { runId } = await params
12+
const { searchParams } = new URL(request.url)
13+
const transportId = searchParams.get('transportId') || ''
14+
15+
try {
16+
const logs = await mastraClient.getLogForRun({
17+
runId,
18+
transportId,
19+
})
20+
return Response.json(logs)
21+
} catch (error) {
22+
console.error('Error fetching run logs:', error)
23+
return Response.json(
24+
{ error: 'Failed to fetch run logs', details: String(error) },
25+
{ status: 500 }
26+
)
27+
}
28+
}

app/api/mastra/logs/route.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { MastraClient } from '@mastra/client-js'
2+
3+
const mastraClient = new MastraClient({
4+
baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || 'http://localhost:4111',
5+
})
6+
7+
export async function GET(request: Request): Promise<Response> {
8+
const { searchParams } = new URL(request.url)
9+
const action = searchParams.get('action')
10+
11+
try {
12+
if (action === 'transports') {
13+
const transports = await mastraClient.listLogTransports()
14+
return Response.json({ transports })
15+
}
16+
17+
const transportId = searchParams.get('transportId') || undefined
18+
const logs = await mastraClient.listLogs({
19+
transportId: transportId ?? '',
20+
})
21+
22+
return Response.json({ logs })
23+
} catch (error) {
24+
console.error('Error fetching logs:', error)
25+
return Response.json(
26+
{ error: 'Failed to fetch logs', details: String(error) },
27+
{ status: 500 }
28+
)
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { MastraClient } from '@mastra/client-js'
2+
3+
const mastraClient = new MastraClient({
4+
baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || 'http://localhost:4111',
5+
})
6+
7+
export async function GET(request: Request): Promise<Response> {
8+
const { searchParams } = new URL(request.url)
9+
const agentId = searchParams.get('agentId')
10+
11+
if (!agentId) {
12+
return Response.json(
13+
{ error: 'agentId query parameter is required' },
14+
{ status: 400 }
15+
)
16+
}
17+
18+
try {
19+
const status = await mastraClient.getMemoryStatus(agentId)
20+
return Response.json(status)
21+
} catch (error) {
22+
console.error('Error fetching memory status:', error)
23+
return Response.json(
24+
{ error: 'Failed to fetch memory status', details: String(error) },
25+
{ status: 500 }
26+
)
27+
}
28+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { MastraClient } from '@mastra/client-js'
2+
3+
const mastraClient = new MastraClient({
4+
baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || 'http://localhost:4111',
5+
})
6+
7+
export async function GET(request: Request): Promise<Response> {
8+
const { searchParams } = new URL(request.url)
9+
const agentId = searchParams.get('agentId')
10+
const threadId = searchParams.get('threadId')
11+
const resourceId = searchParams.get('resourceId') || undefined
12+
13+
if (!agentId || !threadId) {
14+
return Response.json(
15+
{ error: 'agentId and threadId query parameters are required' },
16+
{ status: 400 }
17+
)
18+
}
19+
20+
try {
21+
const workingMemory = await mastraClient.getWorkingMemory({
22+
agentId,
23+
threadId,
24+
resourceId,
25+
})
26+
return Response.json(workingMemory)
27+
} catch (error) {
28+
console.error('Error fetching working memory:', error)
29+
return Response.json(
30+
{
31+
error: 'Failed to fetch working memory',
32+
details: String(error),
33+
},
34+
{ status: 500 }
35+
)
36+
}
37+
}
38+
39+
export async function PUT(request: Request): Promise<Response> {
40+
const body = await request.json()
41+
const { agentId, threadId, workingMemory, resourceId } = body as {
42+
agentId: string
43+
threadId: string
44+
workingMemory: string
45+
resourceId?: string
46+
}
47+
48+
if (!agentId || !threadId || workingMemory === undefined) {
49+
return Response.json(
50+
{
51+
error: 'agentId, threadId, and workingMemory are required in the request body',
52+
},
53+
{ status: 400 }
54+
)
55+
}
56+
57+
try {
58+
const result = await mastraClient.updateWorkingMemory({
59+
agentId,
60+
threadId,
61+
workingMemory,
62+
resourceId,
63+
})
64+
return Response.json(result)
65+
} catch (error) {
66+
console.error('Error updating working memory:', error)
67+
return Response.json(
68+
{
69+
error: 'Failed to update working memory',
70+
details: String(error),
71+
},
72+
{ status: 500 }
73+
)
74+
}
75+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { MastraClient } from '@mastra/client-js'
2+
3+
const mastraClient = new MastraClient({
4+
baseUrl: process.env.NEXT_PUBLIC_MASTRA_API_URL || 'http://localhost:4111',
5+
})
6+
7+
export async function POST(
8+
request: Request,
9+
{ params }: { params: Promise<{ threadId: string }> }
10+
): Promise<Response> {
11+
const { threadId } = await params
12+
const body = await request.json()
13+
const { newThreadId, title, metadata, options } = body as {
14+
newThreadId?: string
15+
title?: string
16+
metadata?: Record<string, unknown>
17+
options?: {
18+
messageLimit?: number
19+
messageFilter?: {
20+
startDate?: string
21+
endDate?: string
22+
}
23+
}
24+
}
25+
26+
try {
27+
const thread = mastraClient.getMemoryThread({ threadId })
28+
29+
const cloneOptions: Record<string, unknown> = {}
30+
if (newThreadId) cloneOptions.newThreadId = newThreadId
31+
if (title) cloneOptions.title = title
32+
if (metadata) cloneOptions.metadata = metadata
33+
if (options) {
34+
const opts: Record<string, unknown> = {}
35+
if (options.messageLimit) opts.messageLimit = options.messageLimit
36+
if (options.messageFilter) {
37+
opts.messageFilter = {
38+
startDate: options.messageFilter.startDate
39+
? new Date(options.messageFilter.startDate)
40+
: undefined,
41+
endDate: options.messageFilter.endDate
42+
? new Date(options.messageFilter.endDate)
43+
: undefined,
44+
}
45+
}
46+
cloneOptions.options = opts
47+
}
48+
49+
const result = await thread.clone(cloneOptions)
50+
return Response.json(result)
51+
} catch (error) {
52+
console.error('Error cloning thread:', error)
53+
return Response.json(
54+
{ error: 'Failed to clone thread', details: String(error) },
55+
{ status: 500 }
56+
)
57+
}
58+
}

0 commit comments

Comments
 (0)