@mastra/mcp-docs-server 0.0.13 → 0.0.14-alpha.1
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/.docs/organized/changelogs/%40mastra%2Fastra.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fchroma.md +35 -35
- package/.docs/organized/changelogs/%40mastra%2Fclickhouse.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +39 -39
- package/.docs/organized/changelogs/%40mastra%2Fcloud.md +33 -0
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare-d1.md +33 -0
- package/.docs/organized/changelogs/%40mastra%2Fcloudflare.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fcore.md +32 -32
- package/.docs/organized/changelogs/%40mastra%2Fcouchbase.md +34 -0
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloudflare.md +38 -38
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-netlify.md +38 -38
- package/.docs/organized/changelogs/%40mastra%2Fdeployer-vercel.md +39 -39
- package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +43 -43
- package/.docs/organized/changelogs/%40mastra%2Fevals.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Ffastembed.md +7 -0
- package/.docs/organized/changelogs/%40mastra%2Ffirecrawl.md +37 -37
- package/.docs/organized/changelogs/%40mastra%2Fgithub.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Flibsql.md +33 -0
- package/.docs/organized/changelogs/%40mastra%2Floggers.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +39 -39
- package/.docs/organized/changelogs/%40mastra%2Fmcp-registry-registry.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fmcp.md +49 -49
- package/.docs/organized/changelogs/%40mastra%2Fmem0.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fmemory.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fmongodb.md +33 -0
- package/.docs/organized/changelogs/%40mastra%2Fopensearch.md +35 -1
- package/.docs/organized/changelogs/%40mastra%2Fpg.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fpinecone.md +35 -35
- package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +45 -45
- package/.docs/organized/changelogs/%40mastra%2Fqdrant.md +35 -35
- package/.docs/organized/changelogs/%40mastra%2Frag.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fragie.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fserver.md +39 -39
- package/.docs/organized/changelogs/%40mastra%2Fspeech-azure.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-deepgram.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-elevenlabs.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-google.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-ibm.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-murf.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-openai.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-playai.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-replicate.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fspeech-speechify.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fturbopuffer.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fupstash.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvectorize.md +35 -35
- package/.docs/organized/changelogs/%40mastra%2Fvoice-azure.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-cloudflare.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-deepgram.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-elevenlabs.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-google.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-murf.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-openai-realtime.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-openai.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-playai.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-sarvam.md +34 -34
- package/.docs/organized/changelogs/%40mastra%2Fvoice-speechify.md +34 -34
- package/.docs/organized/changelogs/create-mastra.md +12 -12
- package/.docs/organized/changelogs/mastra.md +46 -46
- package/.docs/organized/code-examples/a2a.md +202 -0
- package/.docs/organized/code-examples/agent-network.md +1 -1
- package/.docs/organized/code-examples/agent.md +113 -26
- package/.docs/organized/code-examples/ai-sdk-useChat.md +2 -2
- package/.docs/organized/code-examples/client-side-tools.md +1 -1
- package/.docs/organized/code-examples/crypto-chatbot.md +1 -1
- package/.docs/organized/code-examples/fireworks-r1.md +2 -2
- package/.docs/organized/code-examples/mcp-configuration.md +579 -0
- package/.docs/organized/code-examples/mcp-registry-registry.md +1 -1
- package/.docs/organized/code-examples/memory-with-context.md +1 -1
- package/.docs/organized/code-examples/memory-with-processors.md +609 -0
- package/.docs/organized/code-examples/openapi-spec-writer.md +588 -0
- package/.docs/organized/code-examples/quick-start.md +1 -1
- package/.docs/raw/agents/dynamic-agents.mdx +47 -0
- package/.docs/raw/agents/using-tools-and-mcp.mdx +19 -0
- package/.docs/raw/memory/working-memory.mdx +78 -1
- package/.docs/raw/reference/cli/mcp-docs-server.mdx +82 -0
- package/.docs/raw/reference/deployer/cloudflare.mdx +7 -0
- package/.docs/raw/reference/deployer/netlify.mdx +5 -0
- package/.docs/raw/reference/deployer/vercel.mdx +6 -0
- package/dist/{chunk-OKBMABZO.js → chunk-JWLYN5OI.js} +3 -2
- package/dist/prepare-docs/prepare.js +1 -1
- package/dist/stdio.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
### package.json
|
|
2
|
+
```json
|
|
3
|
+
{
|
|
4
|
+
"name": "memory-with-processors",
|
|
5
|
+
"private": true,
|
|
6
|
+
"version": "0.0.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"support": "tsx src/support-agent.ts",
|
|
10
|
+
"interview": "tsx src/forgetful-interviewer.ts",
|
|
11
|
+
"tokens": "tsx src/processor-example.ts"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@ai-sdk/openai": "latest",
|
|
15
|
+
"@mastra/core": "latest",
|
|
16
|
+
"@mastra/memory": "latest",
|
|
17
|
+
"ai": "^4.3.15",
|
|
18
|
+
"chalk": "^5.4.1",
|
|
19
|
+
"dotenv": "^16.3.1",
|
|
20
|
+
"js-tiktoken": "^1.0.13",
|
|
21
|
+
"tiktoken": "^1.0.13",
|
|
22
|
+
"tsx": "^4.6.2",
|
|
23
|
+
"zod": "^3.24.3"
|
|
24
|
+
},
|
|
25
|
+
"pnpm": {
|
|
26
|
+
"overrides": {
|
|
27
|
+
"@mastra/core": "link:../../packages/core",
|
|
28
|
+
"@mastra/memory": "link:../../packages/memory"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### forgetful-interviewer.ts
|
|
37
|
+
```typescript
|
|
38
|
+
import 'dotenv/config';
|
|
39
|
+
|
|
40
|
+
import Readline from 'readline';
|
|
41
|
+
|
|
42
|
+
import { openai } from '@ai-sdk/openai';
|
|
43
|
+
import { Agent } from '@mastra/core/agent';
|
|
44
|
+
import type { CoreMessage } from '@mastra/core';
|
|
45
|
+
import { MemoryProcessor, MemoryProcessorOpts } from '@mastra/core/memory';
|
|
46
|
+
import { Memory } from '@mastra/memory';
|
|
47
|
+
|
|
48
|
+
import { makeSend } from './utils';
|
|
49
|
+
|
|
50
|
+
// Custom processor that makes the llm forget any messages that contain keywords
|
|
51
|
+
class ForgetfulProcessor extends MemoryProcessor {
|
|
52
|
+
constructor(private keywords: string[]) {
|
|
53
|
+
super({ name: 'ForgetfulProcessor' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
process(messages: CoreMessage[], _opts: MemoryProcessorOpts = {}): CoreMessage[] {
|
|
57
|
+
return messages.map(message => {
|
|
58
|
+
if (message.role === `assistant` || message.role === `user`) {
|
|
59
|
+
const content =
|
|
60
|
+
typeof message.content === `string`
|
|
61
|
+
? message.content
|
|
62
|
+
: message.content.reduce((msg = ``, current) => {
|
|
63
|
+
if (current.type === `text`) {
|
|
64
|
+
return msg + `\n${current.text}`;
|
|
65
|
+
}
|
|
66
|
+
}, '') || '';
|
|
67
|
+
|
|
68
|
+
const shouldForgetThis = this.keywords.some(keyword => content.toLowerCase().includes(keyword.toLowerCase()));
|
|
69
|
+
console.log(`\n`, { shouldForgetThis, content });
|
|
70
|
+
if (shouldForgetThis && (message.role === `user` || message.role === `assistant`)) {
|
|
71
|
+
return {
|
|
72
|
+
role: 'assistant',
|
|
73
|
+
content: `<forgotten>I'm getting forgetful in my old age. this used to be a ${message.role} message but I forgot it</forgotten>`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return message;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Interviewer agent that accidentally forgets your name all the time
|
|
83
|
+
const agent = new Agent({
|
|
84
|
+
name: 'Forgetful Job Interviewer',
|
|
85
|
+
instructions:
|
|
86
|
+
"You are a professional job interviewer for a technology company. Conduct insightful interviews by asking relevant questions about skills, experience, and problem-solving abilities. Respond to candidate answers and ask follow-up questions. Keep the interview professional and engaging. Remember details the candidate shares earlier in the conversation. Sometimes you forget things by accident. The system will show you if you forgot. Don't be embarassed, you can admit when you forget something, you'll know when you do because there will be a message wrapped in <forgetten> tags. Don't refer to the user by their name, it comes across as too eager",
|
|
87
|
+
model: openai('gpt-4o'),
|
|
88
|
+
memory: new Memory({
|
|
89
|
+
processors: [
|
|
90
|
+
// Custom filter to remove messages with certain keywords
|
|
91
|
+
new ForgetfulProcessor(['name']),
|
|
92
|
+
],
|
|
93
|
+
options: {
|
|
94
|
+
lastMessages: 30,
|
|
95
|
+
semanticRecall: false,
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(`
|
|
101
|
+
╔══════════════════════════════════════════════════════════╗
|
|
102
|
+
║ ║
|
|
103
|
+
║ MASTRA MEMORY PROCESSORS DEMO - CONTENT FILTERING ║
|
|
104
|
+
║ ║
|
|
105
|
+
║ This example demonstrates: ║
|
|
106
|
+
║ 1. ToolCallFilter - All tool calls are filtered out ║
|
|
107
|
+
║ 2. KeywordFilter - Messages with words like: ║
|
|
108
|
+
║ "confidential", "private", or "sensitive" are ║
|
|
109
|
+
║ filtered out of the conversation history. ║
|
|
110
|
+
║ ║
|
|
111
|
+
║ Try including those words in your responses to see ║
|
|
112
|
+
║ how the agent "forgets" that information in later ║
|
|
113
|
+
║ conversation turns. ║
|
|
114
|
+
║ ║
|
|
115
|
+
╚══════════════════════════════════════════════════════════╝
|
|
116
|
+
`);
|
|
117
|
+
|
|
118
|
+
const send = makeSend({
|
|
119
|
+
agentName: `\n👔 Forgetful interviewer (can never remember your name)`,
|
|
120
|
+
agent,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await send([
|
|
124
|
+
{
|
|
125
|
+
role: 'system',
|
|
126
|
+
content: `Interview starting now. Ask the candidate to introduce themselves and their background.`,
|
|
127
|
+
},
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
const rl = Readline.createInterface({
|
|
131
|
+
input: process.stdin,
|
|
132
|
+
output: process.stdout,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Interactive chat loop
|
|
136
|
+
while (true) {
|
|
137
|
+
const prompt: string = await new Promise(res => {
|
|
138
|
+
rl.question('You: ', answer => {
|
|
139
|
+
res(answer);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (prompt.toLowerCase() === 'exit' || prompt.toLowerCase() === 'quit') {
|
|
144
|
+
console.log('Ending interview. Thank you!');
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await send(prompt);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### mastra/agents/index.ts
|
|
154
|
+
```typescript
|
|
155
|
+
import { openai } from '@ai-sdk/openai';
|
|
156
|
+
import { createTool } from '@mastra/core';
|
|
157
|
+
import { Agent } from '@mastra/core/agent';
|
|
158
|
+
import type { CoreMessage } from '@mastra/core';
|
|
159
|
+
import { MemoryProcessor, MemoryProcessorOpts } from '@mastra/core/memory';
|
|
160
|
+
import { Memory } from '@mastra/memory';
|
|
161
|
+
import { TokenLimiter, ToolCallFilter } from '@mastra/memory/processors';
|
|
162
|
+
import { z } from 'zod';
|
|
163
|
+
|
|
164
|
+
// Custom processor that makes the llm forget any messages that contain keywords
|
|
165
|
+
class ForgetfulProcessor extends MemoryProcessor {
|
|
166
|
+
constructor(private keywords: string[]) {
|
|
167
|
+
super({ name: 'ForgetfulProcessor' });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
process(messages: CoreMessage[], _opts: MemoryProcessorOpts = {}): CoreMessage[] {
|
|
171
|
+
return messages.map(message => {
|
|
172
|
+
if (message.role === `assistant` || message.role === `user`) {
|
|
173
|
+
const content =
|
|
174
|
+
typeof message.content === `string`
|
|
175
|
+
? message.content
|
|
176
|
+
: message.content.reduce((msg = ``, current) => {
|
|
177
|
+
if (current.type === `text`) {
|
|
178
|
+
return msg + `\n${current.text}`;
|
|
179
|
+
}
|
|
180
|
+
}, '') || '';
|
|
181
|
+
|
|
182
|
+
const shouldForgetThis = this.keywords.some(keyword => content.toLowerCase().includes(keyword.toLowerCase()));
|
|
183
|
+
console.log(`\n`, { shouldForgetThis, content });
|
|
184
|
+
if (shouldForgetThis && (message.role === `user` || message.role === `assistant`)) {
|
|
185
|
+
return {
|
|
186
|
+
role: 'assistant',
|
|
187
|
+
content: `<forgotten>I'm getting forgetful in my old age. this used to be a ${message.role} message but I forgot it</forgotten>`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return message;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Create a technical support agent with token limiting
|
|
197
|
+
const supportMemory = new Memory({
|
|
198
|
+
processors: [
|
|
199
|
+
// Limit history to approximately 2000 tokens to demonstrate truncation
|
|
200
|
+
new TokenLimiter(2000),
|
|
201
|
+
],
|
|
202
|
+
options: {
|
|
203
|
+
lastMessages: 50,
|
|
204
|
+
semanticRecall: false,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Create the web search tool
|
|
209
|
+
const searchTool = createTool({
|
|
210
|
+
id: 'web-search',
|
|
211
|
+
description: 'Search the web for information',
|
|
212
|
+
inputSchema: z.object({
|
|
213
|
+
query: z.string().describe('The search query'),
|
|
214
|
+
}),
|
|
215
|
+
execute: async ({ context: { query } }) => {
|
|
216
|
+
// Simulate web search results
|
|
217
|
+
return `Search results for "${query}":
|
|
218
|
+
1. Top result with important information
|
|
219
|
+
2. Secondary information related to the query
|
|
220
|
+
3. Additional context that might be helpful`;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Technical support agent with token limiting
|
|
225
|
+
export const supportAgent = new Agent({
|
|
226
|
+
name: 'Technical Support',
|
|
227
|
+
instructions:
|
|
228
|
+
'You are a technical support agent who helps users solve software problems. You provide clear, step-by-step instructions and ask clarifying questions when needed. You remember details from earlier in the conversation. Your goal is to efficiently resolve user issues.',
|
|
229
|
+
model: openai('gpt-4o-mini'),
|
|
230
|
+
memory: supportMemory,
|
|
231
|
+
tools: { searchTool },
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Create an interviewer agent that filters out tool calls and sensitive content
|
|
235
|
+
const interviewMemory = new Memory({
|
|
236
|
+
processors: [
|
|
237
|
+
// Filter out all tool calls to keep conversation focused
|
|
238
|
+
new ToolCallFilter(),
|
|
239
|
+
// Custom filter to remove messages with certain keywords
|
|
240
|
+
new ForgetfulProcessor(['name']),
|
|
241
|
+
],
|
|
242
|
+
options: {
|
|
243
|
+
lastMessages: 30,
|
|
244
|
+
semanticRecall: false,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Interviewer agent that filters out tool calls and sensitive content
|
|
249
|
+
export const interviewerAgent = new Agent({
|
|
250
|
+
name: 'Forgetful Job Interviewer',
|
|
251
|
+
instructions:
|
|
252
|
+
"You are a professional job interviewer for a technology company. Conduct insightful interviews by asking relevant questions about skills, experience, and problem-solving abilities. Respond to candidate answers and ask follow-up questions. Keep the interview professional and engaging. Remember details the candidate shares earlier in the conversation. Sometimes you forget things by accident. The system will show you if you forgot. Don't be embarassed, you can admit when you forget something, you'll know when you do because there will be a message wrapped in <forgetten> tags. Don't refer to the user by their name, it comes across as too eager",
|
|
253
|
+
model: openai('gpt-4o'),
|
|
254
|
+
memory: interviewMemory,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### mastra/index.ts
|
|
260
|
+
```typescript
|
|
261
|
+
import { Mastra } from '@mastra/core';
|
|
262
|
+
|
|
263
|
+
import { supportAgent, interviewerAgent } from './agents';
|
|
264
|
+
|
|
265
|
+
export const mastra = new Mastra({
|
|
266
|
+
agents: { supportAgent, interviewerAgent },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### processor-example.ts
|
|
272
|
+
```typescript
|
|
273
|
+
import 'dotenv/config';
|
|
274
|
+
import { openai } from '@ai-sdk/openai';
|
|
275
|
+
import { Mastra } from '@mastra/core';
|
|
276
|
+
import { createLogger } from '@mastra/core/logger';
|
|
277
|
+
import { createTool } from '@mastra/core/tools';
|
|
278
|
+
import { Agent } from '@mastra/core/agent';
|
|
279
|
+
import { Memory } from '@mastra/memory';
|
|
280
|
+
import { TokenLimiter } from '@mastra/memory/processors';
|
|
281
|
+
import { readFileSync, existsSync } from 'fs';
|
|
282
|
+
import { resolve } from 'path';
|
|
283
|
+
import chalk from 'chalk';
|
|
284
|
+
|
|
285
|
+
// Create a tool that reads the massive pnpm-lock.yaml file
|
|
286
|
+
const testTool = createTool({
|
|
287
|
+
id: 'read-file',
|
|
288
|
+
description: 'Read a large file to test token limits',
|
|
289
|
+
execute: async () => {
|
|
290
|
+
try {
|
|
291
|
+
// Try multiple possible locations for the pnpm-lock.yaml file
|
|
292
|
+
const possiblePaths = [
|
|
293
|
+
// Root of the project
|
|
294
|
+
resolve(import.meta.dirname, '../../../', 'pnpm-lock.yaml'),
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
let filePath: string | null = null;
|
|
298
|
+
let fileContent = '';
|
|
299
|
+
|
|
300
|
+
// Find the first file that exists
|
|
301
|
+
for (const path of possiblePaths) {
|
|
302
|
+
if (existsSync(path)) {
|
|
303
|
+
filePath = path;
|
|
304
|
+
fileContent = readFileSync(path, 'utf-8');
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// If no file was found, generate a large mock file
|
|
310
|
+
if (!filePath) {
|
|
311
|
+
console.log(chalk.yellow('No suitable large file found. Generating mock content...'));
|
|
312
|
+
|
|
313
|
+
// Create a large mock yaml-like content (about 20K characters)
|
|
314
|
+
fileContent = Array(100)
|
|
315
|
+
.fill(0)
|
|
316
|
+
.map(
|
|
317
|
+
(_, i) =>
|
|
318
|
+
`package-${i}:
|
|
319
|
+
version: "1.0.${i}"
|
|
320
|
+
resolved: "https://registry.npmjs.org/package-${i}/-/package-${i}-1.0.${i}.tgz"
|
|
321
|
+
integrity: "sha512-${Math.random().toString(36).substring(2, 40)}"
|
|
322
|
+
dependencies:
|
|
323
|
+
dep-a: "^2.0.0"
|
|
324
|
+
dep-b: "^3.1.2"
|
|
325
|
+
dep-c: "^0.8.9"
|
|
326
|
+
devDependencies:
|
|
327
|
+
test-lib: "^4.5.2"
|
|
328
|
+
`,
|
|
329
|
+
)
|
|
330
|
+
.join('\n');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Return the first 20K characters (still very token-heavy)
|
|
334
|
+
return `File content (truncated to 20K chars):\n${fileContent.slice(0, 20000)}`;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.error('Error reading file:', error);
|
|
337
|
+
|
|
338
|
+
// Return a mock large response as fallback
|
|
339
|
+
return `Error reading file: ${error.message}\n\nGenerating mock content instead: \n${Array(50)
|
|
340
|
+
.fill('This is a large mock file content to test token limiting. ')
|
|
341
|
+
.join('\n')}`;
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Create memory with a low token limit to clearly demonstrate limiting
|
|
347
|
+
const memory = new Memory({
|
|
348
|
+
processors: [
|
|
349
|
+
// Set a very low token limit (1000) to clearly demonstrate token limiting
|
|
350
|
+
new TokenLimiter(1000),
|
|
351
|
+
],
|
|
352
|
+
options: {
|
|
353
|
+
lastMessages: 50,
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Create an agent with the test tool
|
|
358
|
+
const tokenTestAgent = new Agent({
|
|
359
|
+
name: 'Token Test Agent',
|
|
360
|
+
instructions: 'You help test token limiting by calling tools that return large amounts of data.',
|
|
361
|
+
model: openai('gpt-4o-mini'),
|
|
362
|
+
memory,
|
|
363
|
+
tools: { testTool },
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Create Mastra instance
|
|
367
|
+
const mastra = new Mastra({
|
|
368
|
+
agents: { tokenTestAgent },
|
|
369
|
+
logger: createLogger({ level: 'info' }),
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Track token usage
|
|
373
|
+
const tokenHistory: number[] = [];
|
|
374
|
+
|
|
375
|
+
async function sendMessage(message: string) {
|
|
376
|
+
console.log(`\n${chalk.green('You:')} ${message}`);
|
|
377
|
+
|
|
378
|
+
// Get the agent response
|
|
379
|
+
const response = await mastra.getAgent('tokenTestAgent').generate(message, {
|
|
380
|
+
threadId: 'token-test-thread',
|
|
381
|
+
resourceId: 'demo-user',
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Display the response
|
|
385
|
+
console.log(`\n${chalk.blue('Agent:')} ${response.text}`);
|
|
386
|
+
|
|
387
|
+
// Track and display token usage
|
|
388
|
+
const tokensUsed = response.usage.totalTokens;
|
|
389
|
+
tokenHistory.push(tokensUsed);
|
|
390
|
+
|
|
391
|
+
// Display token usage information
|
|
392
|
+
console.log(`\n${chalk.yellow('📊 Token Usage:')}`);
|
|
393
|
+
console.log(`${chalk.yellow('├')} Current: ${tokensUsed} tokens`);
|
|
394
|
+
console.log(`${chalk.yellow('├')} Total: ${tokenHistory.reduce((sum, t) => sum + t, 0)} tokens`);
|
|
395
|
+
console.log(`${chalk.yellow('└')} Memory Token Limit: 1000 tokens`);
|
|
396
|
+
|
|
397
|
+
return response;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function main() {
|
|
401
|
+
console.log(
|
|
402
|
+
chalk.cyan(`
|
|
403
|
+
╔══════════════════════════════════════════════════════════╗
|
|
404
|
+
║ ║
|
|
405
|
+
║ TOKEN LIMITER PROCESSOR DEMO ║
|
|
406
|
+
║ ║
|
|
407
|
+
║ This example demonstrates how TokenLimiter works with ║
|
|
408
|
+
║ extremely large tool responses. ║
|
|
409
|
+
║ ║
|
|
410
|
+
║ The tool reads a massive pnpm-lock.yaml file and ║
|
|
411
|
+
║ returns a large chunk of text. If the file can't be ║
|
|
412
|
+
║ found, it generates mock content. ║
|
|
413
|
+
║ ║
|
|
414
|
+
║ Memory token limit: 1000 tokens ║
|
|
415
|
+
║ ║
|
|
416
|
+
╚══════════════════════════════════════════════════════════╝
|
|
417
|
+
`),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
// First message - introduction
|
|
421
|
+
await sendMessage("Hello! I'd like to test the token limiting functionality.");
|
|
422
|
+
|
|
423
|
+
// Second message - ask to call the tool
|
|
424
|
+
await sendMessage(
|
|
425
|
+
"Please use the read-file tool to read the large file. After you do, I'll ask you to summarize what you found.",
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// Third message - ask about content that might be forgotten due to token limiting
|
|
429
|
+
await sendMessage(
|
|
430
|
+
'Now, can you tell me what was in the file you just read? And do you remember what I asked in my first message?',
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Fourth message - ask about content that should definitely be forgotten
|
|
434
|
+
await sendMessage(
|
|
435
|
+
"Let's see how the token limiter is working. Do you remember the exact contents at the beginning of the file?",
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
console.log(
|
|
439
|
+
chalk.cyan(`
|
|
440
|
+
═════════════════════════════════════════════════════════════
|
|
441
|
+
DEMO COMPLETE
|
|
442
|
+
|
|
443
|
+
The TokenLimiter processor has prevented the conversation from
|
|
444
|
+
exceeding the 1000 token limit by pruning older messages,
|
|
445
|
+
particularly the large tool response.
|
|
446
|
+
|
|
447
|
+
This ensures that the context window is never exceeded while
|
|
448
|
+
still preserving the most recent and relevant messages.
|
|
449
|
+
═════════════════════════════════════════════════════════════
|
|
450
|
+
`),
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
main().catch(console.error);
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### support-agent.ts
|
|
459
|
+
```typescript
|
|
460
|
+
import 'dotenv/config';
|
|
461
|
+
import { makeSend, searchTool } from './utils';
|
|
462
|
+
import { openai } from '@ai-sdk/openai';
|
|
463
|
+
import { Agent } from '@mastra/core/agent';
|
|
464
|
+
import { Memory } from '@mastra/memory';
|
|
465
|
+
import { TokenLimiter } from '@mastra/memory/processors';
|
|
466
|
+
import { createLogger, Mastra } from '@mastra/core';
|
|
467
|
+
|
|
468
|
+
const memory = new Memory({
|
|
469
|
+
processors: [new TokenLimiter(500)],
|
|
470
|
+
options: {
|
|
471
|
+
lastMessages: 50,
|
|
472
|
+
semanticRecall: true,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const techSupport = new Agent({
|
|
477
|
+
name: 'Technical Support',
|
|
478
|
+
instructions:
|
|
479
|
+
'You are a technical support agent who helps users solve software problems. You provide concise, short, instructions and ask clarifying questions when needed. You remember details from earlier in the conversation. Your goal is to efficiently resolve user issues. Make sure you provide concise responses without tons of text',
|
|
480
|
+
model: openai('gpt-4o-mini'),
|
|
481
|
+
memory,
|
|
482
|
+
tools: { searchTool },
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const mastra = new Mastra({
|
|
486
|
+
agents: { techSupport },
|
|
487
|
+
logger: createLogger({ level: 'info' }),
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
console.log(`
|
|
491
|
+
╔══════════════════════════════════════════════════════════╗
|
|
492
|
+
║ ║
|
|
493
|
+
║ MASTRA MEMORY PROCESSORS DEMO - TOKEN LIMITING ║
|
|
494
|
+
║ ║
|
|
495
|
+
║ This example demonstrates the TokenLimiter processor ║
|
|
496
|
+
║ which limits memory to a specified token count (500). ║
|
|
497
|
+
║ As the conversation grows, older messages will be ║
|
|
498
|
+
║ automatically pruned to stay within the token limit. ║
|
|
499
|
+
║ ║
|
|
500
|
+
╚══════════════════════════════════════════════════════════╝
|
|
501
|
+
`);
|
|
502
|
+
|
|
503
|
+
const send = makeSend({
|
|
504
|
+
agentName: `\n 💻 Support Agent`,
|
|
505
|
+
agent: mastra.getAgent(`techSupport`),
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
await send(
|
|
509
|
+
"I'm having trouble with my laptop. It keeps shutting down randomly after about 30 minutes of use. I've had it for about 2 years and this just started happening last week.",
|
|
510
|
+
);
|
|
511
|
+
await send('Can you search for common causes of laptop overheating?');
|
|
512
|
+
await send('Can you search again?');
|
|
513
|
+
await send(
|
|
514
|
+
"The laptop feels quite hot before it shuts down. I'm using a Dell XPS 15 with Windows 11. I usually have multiple browser tabs open and sometimes I'm running Visual Studio Code. The battery seems to drain quickly too.",
|
|
515
|
+
);
|
|
516
|
+
await send(
|
|
517
|
+
"I've tried restarting in safe mode and the problem doesn't happen there. Also, I checked for Windows updates and everything is current. What should I do to fix this issue?",
|
|
518
|
+
);
|
|
519
|
+
await send(
|
|
520
|
+
"I tried cleaning the fans as you suggested, but it's still happening. I also downloaded a temperature monitoring app and it shows the CPU reaching 90°C before shutting down. My friend suggested it might be a failing thermal paste. Do you think I should try replacing the thermal paste myself or take it to a repair shop? I've never opened a laptop before but I'm somewhat technically inclined. Also, is there a way to limit how much CPU power certain applications use?",
|
|
521
|
+
);
|
|
522
|
+
await send(
|
|
523
|
+
'Can you remind me what was the first thing you suggested I should check? Also, do you think a cooling pad would help with my issue?',
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### utils.ts
|
|
529
|
+
```typescript
|
|
530
|
+
import { createTool } from '@mastra/core/tools';
|
|
531
|
+
import { z } from 'zod';
|
|
532
|
+
import { Agent } from '@mastra/core/agent';
|
|
533
|
+
import type { CoreMessage, StreamTextResult } from 'ai';
|
|
534
|
+
import chalk from 'chalk';
|
|
535
|
+
import { randomUUID } from 'node:crypto';
|
|
536
|
+
|
|
537
|
+
export function makeLogger(botName: string) {
|
|
538
|
+
return async function logRes(res: Promise<StreamTextResult<any, any>>) {
|
|
539
|
+
console.log(chalk.bold.blue(botName + `:`));
|
|
540
|
+
for await (const chunk of (await res).fullStream) {
|
|
541
|
+
switch (chunk.type) {
|
|
542
|
+
case 'error':
|
|
543
|
+
console.error(chalk.red(chunk.error));
|
|
544
|
+
break;
|
|
545
|
+
case 'text-delta':
|
|
546
|
+
process.stdout.write(chalk.blue(chunk.textDelta));
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case 'tool-call':
|
|
550
|
+
console.log(chalk.cyan(`\n\ntool-call: ${chunk.toolName}`));
|
|
551
|
+
break;
|
|
552
|
+
case 'tool-result':
|
|
553
|
+
console.log(chalk.cyan(`\ntool-result: ${JSON.stringify(chunk.result)}\n\n`));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
console.log(`\n\n`);
|
|
557
|
+
return {
|
|
558
|
+
usage: (await res).usage,
|
|
559
|
+
messages: (await (await res).response).messages,
|
|
560
|
+
};
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export function makeSend({ agentName, agent }: { agentName: string; agent: Agent }) {
|
|
565
|
+
const threadId = randomUUID();
|
|
566
|
+
const resourceId = 'DEMO_USER_1';
|
|
567
|
+
|
|
568
|
+
const log = makeLogger(agentName);
|
|
569
|
+
function logQ(message: string | CoreMessage[]) {
|
|
570
|
+
if (typeof message !== 'string') return message;
|
|
571
|
+
console.log(
|
|
572
|
+
chalk.red(`\n${chalk.bold(`🐵 You:`)}\n${message}
|
|
573
|
+
`),
|
|
574
|
+
);
|
|
575
|
+
return message;
|
|
576
|
+
}
|
|
577
|
+
let totalTokens = 0;
|
|
578
|
+
async function logStats(res: { usage: any; messages: any }) {
|
|
579
|
+
// console.log(chalk.green(`Completion had ${res.messages.length} messages`));
|
|
580
|
+
// console.log(chalk.green(`Usage: ${JSON.stringify(await res.usage, null, 2)}`));
|
|
581
|
+
totalTokens += (await res.usage).totalTokens;
|
|
582
|
+
}
|
|
583
|
+
process.on(`exit`, () => {
|
|
584
|
+
console.log(chalk.green.bold(`Total token usage: ${totalTokens}`));
|
|
585
|
+
});
|
|
586
|
+
return async function send(prompt: string | CoreMessage[]) {
|
|
587
|
+
await logStats(
|
|
588
|
+
await log(
|
|
589
|
+
agent.stream(logQ(prompt), {
|
|
590
|
+
threadId,
|
|
591
|
+
resourceId,
|
|
592
|
+
}),
|
|
593
|
+
),
|
|
594
|
+
);
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export const searchTool = createTool({
|
|
599
|
+
id: 'web-search',
|
|
600
|
+
description: 'Search the web for information',
|
|
601
|
+
inputSchema: z.object({
|
|
602
|
+
query: z.string().describe('The search query'),
|
|
603
|
+
}),
|
|
604
|
+
execute: async () => {
|
|
605
|
+
return `Not much found unfortunately. You'll probably have to turn it off an on again.`;
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
```
|