Skip to content

Commit 4e5d98a

Browse files
committed
feat: implement workflow components and context
- Add WorkflowActions component for exporting SVG and viewing code. - Create WorkflowCanvas component to manage workflow nodes and edges. - Develop WorkflowHeader component for workflow selection and control. - Implement WorkflowInfoPanel to display current workflow status and progress. - Add WorkflowLegend to explain the visual elements in the workflow. - Create WorkflowNode component to represent individual steps in the workflow. - Implement WorkflowOutput to show the results of the workflow execution. - Define workflow configurations in workflows.ts, including multiple workflows with steps and features. - Establish WorkflowContext to manage state and actions related to workflows, including running, pausing, and stopping workflows. - Integrate chat functionality for real-time workflow execution feedback.
1 parent 908480b commit 4e5d98a

27 files changed

Lines changed: 2701 additions & 840 deletions

app/api/chat/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { UIMessage, convertToModelMessages } from 'ai';
33
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
44
import { toAISdkFormat } from "@mastra/ai-sdk";
55
import type { ChunkType, MastraModelOutput } from "@mastra/core/stream";
6-
6+
import { RuntimeContext } from "@mastra/core/runtime-context";
77
export const maxDuration = 30;
88
export async function POST(req: Request) {
99
const { messages }: {
@@ -12,9 +12,10 @@ export async function POST(req: Request) {
1212
const myAgent = mastra.getAgent("weatherAgent");
1313
const stream = await myAgent.stream(messages, { });
1414
const uiMessageStream = createUIMessageStream({
15+
1516
execute: async ({ writer }) => {
1617
const formatted = toAISdkFormat(stream, { from: "agent" })!;
17-
18+
const runtimeContext = new RuntimeContext();
1819
// If the returned object is an async iterable, use for-await
1920
if (Symbol.asyncIterator in formatted) {
2021
for await (const part of formatted as AsyncIterable<any>) {

app/api/completion/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { mastra } from "../../../src/mastra";
2-
2+
import { RuntimeContext } from "@mastra/core/runtime-context";
33
export async function POST(req: Request) {
44
const { prompt } = await req.json();
55
const myAgent = mastra.getAgent("weatherAgent");
66
const stream = await myAgent.stream([{ role: "user", content: prompt }], {
77
format: "aisdk",
88
});
9+
const runtimeContext = new RuntimeContext();
910

1011
return stream.toUIMessageStreamResponse();
1112
}

app/chat/AGENTS.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
1-
<!-- AGENTS-META {"title":"Chat App","version":"1.0.0","applies_to":"app/chat/","last_updated":"2025-11-29T00:00:00Z","status":"stable"} -->
1+
<!-- AGENTS-META {"title":"Chat App","version":"1.1.0","applies_to":"app/chat/","last_updated":"2025-11-29T00:00:00Z","status":"stable"} -->
22
# App/Chat
33

44
## Overview
55

6-
The `/chat` route provides a rich AI chat interface built with **AI Elements** (30 components) integrated with **26+ Mastra agents**. Uses AI SDK v5 patterns with streaming, reasoning display, tool execution, and source citations.
6+
The `/chat` route provides a rich AI chat interface built with **AI Elements** (30 components) integrated with **26+ Mastra agents**. Uses `@ai-sdk/react` with `useChat` and `DefaultChatTransport` to stream responses from Mastra's `/chat` route.
7+
8+
## AI SDK Integration
9+
10+
Uses `useChat` from `@ai-sdk/react` with `DefaultChatTransport`:
11+
12+
```tsx
13+
import { useChat } from "@ai-sdk/react"
14+
import { DefaultChatTransport } from "ai"
15+
16+
const { messages, sendMessage, stop, status } = useChat({
17+
transport: new DefaultChatTransport({
18+
api: "http://localhost:4111/chat",
19+
prepareSendMessagesRequest({ messages }) {
20+
return {
21+
body: {
22+
messages,
23+
resourceId: selectedAgent,
24+
data: { agentId: selectedAgent },
25+
},
26+
}
27+
},
28+
}),
29+
})
30+
```
731

832
## Architecture
933

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"use client"
2+
3+
import {
4+
Suggestions,
5+
Suggestion,
6+
} from "@/src/components/ai-elements/suggestion"
7+
8+
export interface AgentSuggestionsProps {
9+
suggestions: string[]
10+
onSelect: (suggestion: string) => void
11+
disabled?: boolean
12+
}
13+
14+
export function AgentSuggestions({
15+
suggestions,
16+
onSelect,
17+
disabled = false,
18+
}: AgentSuggestionsProps) {
19+
if (suggestions.length === 0) return null
20+
21+
return (
22+
<Suggestions className="py-2">
23+
{suggestions.map((suggestion) => (
24+
<Suggestion
25+
key={suggestion}
26+
suggestion={suggestion}
27+
onClick={onSelect}
28+
disabled={disabled}
29+
/>
30+
))}
31+
</Suggestions>
32+
)
33+
}
34+
35+
export const DEFAULT_SUGGESTIONS: Record<string, string[]> = {
36+
weatherAgent: [
37+
"What's the weather in Tokyo?",
38+
"Will it rain tomorrow in NYC?",
39+
"Weather forecast for London this week",
40+
],
41+
researchAgent: [
42+
"Research the latest AI trends",
43+
"Find papers on transformer architecture",
44+
"Summarize recent developments in quantum computing",
45+
],
46+
stockAnalysisAgent: [
47+
"Analyze AAPL stock performance",
48+
"Compare TSLA vs RIVN",
49+
"What are the top tech stocks today?",
50+
],
51+
copywriterAgent: [
52+
"Write a product description for a smart watch",
53+
"Create a tagline for a coffee brand",
54+
"Draft an email subject line for a sale",
55+
],
56+
dataIngestionAgent: [
57+
"Parse this CSV data",
58+
"Validate data structure",
59+
"Import from JSON format",
60+
],
61+
documentProcessingAgent: [
62+
"Convert PDF to markdown",
63+
"Extract text from this document",
64+
"Chunk this content for RAG",
65+
],
66+
default: [
67+
"What can you help me with?",
68+
"Tell me about your capabilities",
69+
"Show me an example",
70+
],
71+
}
72+
73+
export function getSuggestionsForAgent(agentId: string): string[] {
74+
return DEFAULT_SUGGESTIONS[agentId] || DEFAULT_SUGGESTIONS.default
75+
}

app/chat/components/chat-input.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,42 @@ import {
1111
PromptInputAttachment,
1212
} from "@/src/components/ai-elements/prompt-input"
1313
import { useChatContext } from "@/app/chat/providers/chat-context"
14+
import { AgentSuggestions, getSuggestionsForAgent } from "./agent-suggestions"
1415
import { PaperclipIcon, SquareIcon } from "lucide-react"
16+
import { useMemo } from "react"
1517

1618
export function ChatInput() {
17-
const { sendMessage, stopGeneration, isLoading, status, agentConfig } =
19+
const { sendMessage, stopGeneration, isLoading, status, agentConfig, selectedAgent, messages } =
1820
useChatContext()
1921

2022
const supportsFiles = agentConfig?.features.fileUpload ?? false
23+
const showSuggestions = messages.length === 0 && !isLoading
24+
25+
const suggestions = useMemo(
26+
() => getSuggestionsForAgent(selectedAgent),
27+
[selectedAgent]
28+
)
2129

2230
const handleSubmit = async (message: { text: string; files: unknown[] }) => {
2331
if (message.text.trim()) {
2432
await sendMessage(message.text, message.files as File[])
2533
}
2634
}
2735

36+
const handleSuggestionClick = (suggestion: string) => {
37+
sendMessage(suggestion)
38+
}
39+
2840
return (
2941
<footer className="border-t border-border p-4">
30-
<div className="mx-auto max-w-3xl">
42+
<div className="mx-auto max-w-3xl space-y-3">
43+
{showSuggestions && (
44+
<AgentSuggestions
45+
suggestions={suggestions}
46+
onSelect={handleSuggestionClick}
47+
disabled={isLoading}
48+
/>
49+
)}
3150
<PromptInput
3251
onSubmit={handleSubmit}
3352
className="rounded-lg border shadow-sm"

0 commit comments

Comments
 (0)