chat-agent-toolkit 1.2.49 → 1.2.50

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.
@@ -0,0 +1,188 @@
1
+ /**
2
+ * @fileoverview Mastra RAG (Retrieval-Augmented Generation)
3
+ *
4
+ * Document processing, chunking, embedding, and vector-store retrieval.
5
+ * Uses Vercel AI SDK embeddings with pluggable vector backends.
6
+ */
7
+
8
+ import { embed, embedMany } from "ai";
9
+
10
+ export interface RAGConfig {
11
+ embeddingModel: any;
12
+ chunkSize?: number;
13
+ chunkOverlap?: number;
14
+ topK?: number;
15
+ }
16
+
17
+ export interface RAGDocument {
18
+ id: string;
19
+ content: string;
20
+ metadata?: Record<string, any>;
21
+ }
22
+
23
+ export interface RAGChunk {
24
+ id: string;
25
+ documentId: string;
26
+ text: string;
27
+ embedding: number[];
28
+ metadata?: Record<string, any>;
29
+ }
30
+
31
+ export interface RAGRetrievalResult {
32
+ chunk: RAGChunk;
33
+ score: number;
34
+ }
35
+
36
+ /**
37
+ * In-memory RAG pipeline with document chunking, embedding, and retrieval.
38
+ * For production, swap the vector store with Pinecone, Qdrant, or pgvector.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { MastraRAG } from "chat-agent-toolkit/mastra";
43
+ * import { openai } from "@ai-sdk/openai";
44
+ *
45
+ * const rag = new MastraRAG({
46
+ * embeddingModel: openai.embedding("text-embedding-3-small"),
47
+ * chunkSize: 512,
48
+ * topK: 5,
49
+ * });
50
+ *
51
+ * await rag.ingest([
52
+ * { id: "doc1", content: "Long document text..." },
53
+ * ]);
54
+ *
55
+ * const results = await rag.retrieve("What is the main topic?");
56
+ * ```
57
+ */
58
+ export class MastraRAG {
59
+ private config: Required<RAGConfig>;
60
+ private chunks: RAGChunk[] = [];
61
+
62
+ constructor(config: RAGConfig) {
63
+ this.config = {
64
+ embeddingModel: config.embeddingModel,
65
+ chunkSize: config.chunkSize ?? 512,
66
+ chunkOverlap: config.chunkOverlap ?? 64,
67
+ topK: config.topK ?? 5,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Split text into overlapping chunks of configured size.
73
+ */
74
+ private splitIntoChunks(text: string): string[] {
75
+ const chunks: string[] = [];
76
+ const words = text.split(/\s+/);
77
+ const { chunkSize, chunkOverlap } = this.config;
78
+
79
+ let start = 0;
80
+ while (start < words.length) {
81
+ const end = Math.min(start + chunkSize, words.length);
82
+ chunks.push(words.slice(start, end).join(" "));
83
+ start += chunkSize - chunkOverlap;
84
+ }
85
+
86
+ return chunks;
87
+ }
88
+
89
+ /**
90
+ * Ingest documents: chunk, embed, and store.
91
+ */
92
+ async ingest(documents: RAGDocument[]): Promise<number> {
93
+ let totalChunks = 0;
94
+
95
+ for (const doc of documents) {
96
+ const textChunks = this.splitIntoChunks(doc.content);
97
+
98
+ const { embeddings } = await embedMany({
99
+ model: this.config.embeddingModel,
100
+ values: textChunks,
101
+ });
102
+
103
+ for (let i = 0; i < textChunks.length; i++) {
104
+ this.chunks.push({
105
+ id: `${doc.id}_chunk_${i}`,
106
+ documentId: doc.id,
107
+ text: textChunks[i],
108
+ embedding: embeddings[i],
109
+ metadata: doc.metadata,
110
+ });
111
+ }
112
+
113
+ totalChunks += textChunks.length;
114
+ }
115
+
116
+ return totalChunks;
117
+ }
118
+
119
+ /**
120
+ * Retrieve relevant chunks for a query using cosine similarity.
121
+ */
122
+ async retrieve(query: string): Promise<RAGRetrievalResult[]> {
123
+ if (this.chunks.length === 0) return [];
124
+
125
+ const { embedding } = await embed({
126
+ model: this.config.embeddingModel,
127
+ value: query,
128
+ });
129
+
130
+ const scored = this.chunks.map((chunk) => ({
131
+ chunk,
132
+ score: cosineSimilarity(embedding, chunk.embedding),
133
+ }));
134
+
135
+ scored.sort((a, b) => b.score - a.score);
136
+
137
+ return scored.slice(0, this.config.topK);
138
+ }
139
+
140
+ /**
141
+ * Retrieve and format context string for LLM consumption.
142
+ */
143
+ async getContext(query: string): Promise<string> {
144
+ const results = await this.retrieve(query);
145
+ if (results.length === 0) return "";
146
+
147
+ return results
148
+ .map((r, i) => `[${i + 1}] (score: ${r.score.toFixed(3)}) ${r.chunk.text}`)
149
+ .join("\n\n");
150
+ }
151
+
152
+ /**
153
+ * Get total chunk count in the store.
154
+ */
155
+ get size(): number {
156
+ return this.chunks.length;
157
+ }
158
+
159
+ /**
160
+ * Clear all stored chunks.
161
+ */
162
+ clear(): void {
163
+ this.chunks = [];
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Factory for creating a configured RAG pipeline.
169
+ */
170
+ export function createRAGPipeline(config: RAGConfig): MastraRAG {
171
+ return new MastraRAG(config);
172
+ }
173
+
174
+ function cosineSimilarity(a: number[], b: number[]): number {
175
+ let dotProduct = 0;
176
+ let normA = 0;
177
+ let normB = 0;
178
+
179
+ for (let i = 0; i < a.length; i++) {
180
+ dotProduct += a[i] * b[i];
181
+ normA += a[i] * a[i];
182
+ normB += b[i] * b[i];
183
+ }
184
+
185
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
186
+ if (denominator === 0) return 0;
187
+ return dotProduct / denominator;
188
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @fileoverview Mastra Telemetry & Instance Factory
3
+ *
4
+ * Creates configured Mastra instances with OpenTelemetry-compatible
5
+ * tracing, logging, and agent registration.
6
+ */
7
+
8
+ import { Mastra } from "@mastra/core";
9
+
10
+ export interface TelemetryConfig {
11
+ serviceName: string;
12
+ enabled?: boolean;
13
+ sampling?: number;
14
+ exporterUrl?: string;
15
+ }
16
+
17
+ export interface MastraInstanceConfig {
18
+ agents?: Record<string, any>;
19
+ workflows?: Record<string, any>;
20
+ telemetry?: TelemetryConfig;
21
+ storage?: any;
22
+ }
23
+
24
+ /**
25
+ * Create a fully configured Mastra instance with agents, workflows,
26
+ * and telemetry. Central entry point for Mastra framework setup.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { createMastraInstance } from "chat-agent-toolkit/mastra";
31
+ *
32
+ * const mastra = createMastraInstance({
33
+ * agents: { assistant, researcher },
34
+ * workflows: { researchWorkflow },
35
+ * telemetry: {
36
+ * serviceName: "my-app",
37
+ * enabled: true,
38
+ * },
39
+ * });
40
+ *
41
+ * const agent = mastra.getAgent("assistant");
42
+ * const result = await agent.generate("Hello");
43
+ * ```
44
+ */
45
+ export function createMastraInstance(config: MastraInstanceConfig): Mastra {
46
+ const mastraConfig: any = {};
47
+
48
+ if (config.agents) {
49
+ mastraConfig.agents = config.agents;
50
+ }
51
+
52
+ if (config.workflows) {
53
+ mastraConfig.workflows = config.workflows;
54
+ }
55
+
56
+ if (config.telemetry) {
57
+ mastraConfig.telemetry = {
58
+ serviceName: config.telemetry.serviceName,
59
+ enabled: config.telemetry.enabled ?? true,
60
+ sampling: {
61
+ default: config.telemetry.sampling ?? 1.0,
62
+ },
63
+ };
64
+ }
65
+
66
+ if (config.storage) {
67
+ mastraConfig.storage = config.storage;
68
+ }
69
+
70
+ return new Mastra(mastraConfig);
71
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @fileoverview Mastra Workflow Builder
3
+ *
4
+ * Graph-based deterministic workflows with typed steps, chaining,
5
+ * branching, and parallel execution support.
6
+ */
7
+
8
+ import { createWorkflow, createStep } from "@mastra/core/workflows";
9
+ import { z } from "zod";
10
+
11
+ export interface WorkflowStepConfig<TInput = any, TOutput = any> {
12
+ id: string;
13
+ inputSchema: z.ZodType<TInput>;
14
+ outputSchema: z.ZodType<TOutput>;
15
+ execute: (params: { inputData: TInput }) => Promise<TOutput>;
16
+ }
17
+
18
+ /**
19
+ * Create a typed workflow step.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const fetchStep = createWorkflowStep({
24
+ * id: "fetch-sources",
25
+ * inputSchema: z.object({ query: z.string() }),
26
+ * outputSchema: z.object({ sources: z.array(z.string()) }),
27
+ * execute: async ({ inputData }) => {
28
+ * const sources = await searchWeb(inputData.query);
29
+ * return { sources };
30
+ * },
31
+ * });
32
+ * ```
33
+ */
34
+ export function createWorkflowStep<TInput, TOutput>(
35
+ config: WorkflowStepConfig<TInput, TOutput>
36
+ ) {
37
+ return createStep({
38
+ id: config.id,
39
+ inputSchema: config.inputSchema,
40
+ outputSchema: config.outputSchema,
41
+ execute: config.execute,
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Create a research workflow: fetch → analyze → summarize.
47
+ * Pre-built pipeline for common research tasks.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const workflow = createResearchWorkflow({
52
+ * fetchFn: async (topic) => fetchSources(topic),
53
+ * analyzeFn: async (sources) => analyzeContent(sources),
54
+ * summarizeFn: async (analysis) => generateSummary(analysis),
55
+ * });
56
+ *
57
+ * const run = await workflow.createRunAsync();
58
+ * const result = await run.start({ inputData: { topic: "quantum computing" } });
59
+ * ```
60
+ */
61
+ export function createResearchWorkflow(handlers: {
62
+ fetchFn: (topic: string) => Promise<string[]>;
63
+ analyzeFn: (sources: string[]) => Promise<string>;
64
+ summarizeFn: (analysis: string) => Promise<string>;
65
+ }) {
66
+ const fetchStep = createStep({
67
+ id: "fetch-sources",
68
+ inputSchema: z.object({ topic: z.string() }),
69
+ outputSchema: z.object({ sources: z.array(z.string()) }),
70
+ execute: async ({ inputData }) => {
71
+ const sources = await handlers.fetchFn(inputData.topic);
72
+ return { sources };
73
+ },
74
+ });
75
+
76
+ const analyzeStep = createStep({
77
+ id: "analyze-content",
78
+ inputSchema: z.object({ sources: z.array(z.string()) }),
79
+ outputSchema: z.object({ analysis: z.string() }),
80
+ execute: async ({ inputData }) => {
81
+ const analysis = await handlers.analyzeFn(inputData.sources);
82
+ return { analysis };
83
+ },
84
+ });
85
+
86
+ const summarizeStep = createStep({
87
+ id: "summarize",
88
+ inputSchema: z.object({ analysis: z.string() }),
89
+ outputSchema: z.object({ summary: z.string() }),
90
+ execute: async ({ inputData }) => {
91
+ const summary = await handlers.summarizeFn(inputData.analysis);
92
+ return { summary };
93
+ },
94
+ });
95
+
96
+ return createWorkflow({
97
+ id: "research-workflow",
98
+ inputSchema: z.object({ topic: z.string() }),
99
+ outputSchema: z.object({ summary: z.string() }),
100
+ })
101
+ .then(fetchStep)
102
+ .then(analyzeStep)
103
+ .then(summarizeStep)
104
+ .commit();
105
+ }
106
+
107
+ /**
108
+ * Create a RAG workflow: chunk → embed → retrieve → generate.
109
+ * End-to-end retrieval-augmented generation pipeline.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const workflow = createRAGWorkflow({
114
+ * chunkFn: async (doc) => splitIntoChunks(doc),
115
+ * embedFn: async (chunks) => embedChunks(chunks),
116
+ * retrieveFn: async (query) => findRelevant(query),
117
+ * generateFn: async (context, query) => llmGenerate(context, query),
118
+ * });
119
+ * ```
120
+ */
121
+ export function createRAGWorkflow(handlers: {
122
+ chunkFn: (document: string) => Promise<string[]>;
123
+ embedFn: (chunks: string[]) => Promise<number[][]>;
124
+ retrieveFn: (query: string) => Promise<string[]>;
125
+ generateFn: (context: string[], query: string) => Promise<string>;
126
+ }) {
127
+ const retrieveStep = createStep({
128
+ id: "retrieve",
129
+ inputSchema: z.object({ query: z.string() }),
130
+ outputSchema: z.object({ context: z.array(z.string()), query: z.string() }),
131
+ execute: async ({ inputData }) => {
132
+ const context = await handlers.retrieveFn(inputData.query);
133
+ return { context, query: inputData.query };
134
+ },
135
+ });
136
+
137
+ const generateStep = createStep({
138
+ id: "generate",
139
+ inputSchema: z.object({ context: z.array(z.string()), query: z.string() }),
140
+ outputSchema: z.object({ answer: z.string() }),
141
+ execute: async ({ inputData }) => {
142
+ const answer = await handlers.generateFn(inputData.context, inputData.query);
143
+ return { answer };
144
+ },
145
+ });
146
+
147
+ return createWorkflow({
148
+ id: "rag-workflow",
149
+ inputSchema: z.object({ query: z.string() }),
150
+ outputSchema: z.object({ answer: z.string() }),
151
+ })
152
+ .then(retrieveStep)
153
+ .then(generateStep)
154
+ .commit();
155
+ }
@@ -3,8 +3,7 @@
3
3
  * @description Research library module.
4
4
  *
5
5
  * Note: To use these search handlers, you need to provide search functions
6
- * (searchSearxng, searchTavily, etc.) from extract-webpage package.
7
- * See extract-webpage/src/search/index.ts for an example.
6
+ * (searchSearxng, searchTavily, etc.) from search-web-api package.
8
7
  */
9
8
  import MetaSearchAgent from "./metaSearchAgent";
10
9
  import {
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * @module research/agents/markdown-to-html
3
- * @description Converts Markdown to HTML with Highlight.js syntax highlighting.
3
+ * @description Converts Markdown to HTML with Prism.js syntax highlighting.
4
4
  */
5
5
  import { marked } from "marked";
6
- import hljs from "highlight.js";
6
+ import Prism from "prismjs";
7
+ import loadLanguages from "prismjs/components/index.js";
7
8
  import { encode, decode } from "html-entities";
8
9
 
10
+ loadLanguages();
11
+
9
12
  // Inline onclick: self-contained per button, works in dangerouslySetInnerHTML contexts
10
13
  const COPY_ONCLICK = [
11
14
  "(function(b){",
@@ -28,22 +31,22 @@ marked.use({
28
31
  let highlighted: string;
29
32
 
30
33
  try {
31
- if (language && hljs.getLanguage(language)) {
32
- highlighted = hljs.highlight(text, { language }).value;
34
+ if (language && Prism.languages[language]) {
35
+ highlighted = Prism.highlight(text, Prism.languages[language], language);
33
36
  } else {
34
- highlighted = hljs.highlightAuto(text).value;
37
+ highlighted = encode(text);
35
38
  }
36
39
  } catch (e) {
37
40
  highlighted = encode(text);
38
41
  }
39
42
 
40
- return `<figure class="code-block"><button class="code-copy-btn" type="button" onclick="${COPY_ONCLICK}">Copy</button><pre><code class="hljs language-${language}">${highlighted}</code></pre></figure>`;
43
+ return `<figure class="code-block"><button class="code-copy-btn" type="button" onclick="${COPY_ONCLICK}">Copy</button><pre><code class="language-${language}">${highlighted}</code></pre></figure>`;
41
44
  },
42
45
  },
43
46
  });
44
47
 
45
48
  /**
46
- * Convert markdown text to HTML with Highlight.js syntax highlighting.
49
+ * Convert markdown text to HTML with Prism.js syntax highlighting.
47
50
  * Unescapes HTML entities like `&amp;` \u2192 `&`.
48
51
  */
49
52
  export async function convertMarkdownToHTMLEscaped(