@backstage-community/plugin-mcp-chat 0.1.2 → 0.3.0
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/CHANGELOG.md +63 -0
- package/README.md +11 -2
- package/dist/api/McpChatApi.esm.js +42 -2
- package/dist/api/McpChatApi.esm.js.map +1 -1
- package/dist/components/ChatContainer/ChatContainer.esm.js +13 -2
- package/dist/components/ChatContainer/ChatContainer.esm.js.map +1 -1
- package/dist/components/ChatPage/ChatPage.esm.js +63 -2
- package/dist/components/ChatPage/ChatPage.esm.js.map +1 -1
- package/dist/components/RightPane/ConversationHistory.esm.js +218 -0
- package/dist/components/RightPane/ConversationHistory.esm.js.map +1 -0
- package/dist/components/RightPane/ConversationItem.esm.js +241 -0
- package/dist/components/RightPane/ConversationItem.esm.js.map +1 -0
- package/dist/components/RightPane/ConversationSearchBar.esm.js +104 -0
- package/dist/components/RightPane/ConversationSearchBar.esm.js.map +1 -0
- package/dist/components/RightPane/RightPane.esm.js +150 -48
- package/dist/components/RightPane/RightPane.esm.js.map +1 -1
- package/dist/hooks/useConversations.esm.js +123 -0
- package/dist/hooks/useConversations.esm.js.map +1 -0
- package/dist/index.d.ts +41 -3
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @backstage-community/plugin-mcp-chat
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 207781a: ### Added Conversation History Feature
|
|
8
|
+
|
|
9
|
+
- **Conversation Persistence**: Chat sessions are automatically saved for authenticated users
|
|
10
|
+
- **Starring**: Mark important conversations as favorites for quick access
|
|
11
|
+
- **Search**: Filter conversations by title using client-side search
|
|
12
|
+
- **Delete**: Remove individual conversations or clear all history
|
|
13
|
+
- **AI-Generated Titles**: Conversations get auto-generated titles using the LLM (with fallback to first message)
|
|
14
|
+
|
|
15
|
+
### Backend Improvements
|
|
16
|
+
|
|
17
|
+
- Refactored router into domain-specific modules (status, chat, conversations) for better maintainability
|
|
18
|
+
- Added authentication and validation middleware
|
|
19
|
+
- New API endpoints for conversation management (list, get, delete, star, update title)
|
|
20
|
+
- Added `ChatConversationStore` and `SummarizationService` to public exports
|
|
21
|
+
- Comprehensive unit tests for `ChatConversationStore`
|
|
22
|
+
|
|
23
|
+
### Configuration Options
|
|
24
|
+
|
|
25
|
+
New `conversationHistory` config section with `displayLimit`, `autoSummarize`, and `summarizeTimeout` options.
|
|
26
|
+
|
|
27
|
+
### Notes
|
|
28
|
+
|
|
29
|
+
- Guest users (`user:development/guest`) do not have conversations saved
|
|
30
|
+
- Conversations stored in `mcp_chat_conversations` database table with automatic migrations
|
|
31
|
+
|
|
32
|
+
## 0.2.0
|
|
33
|
+
|
|
34
|
+
### Minor Changes
|
|
35
|
+
|
|
36
|
+
- c330b2c: **BREAKING**: Removed SSE (Server-Sent Events) transport support
|
|
37
|
+
|
|
38
|
+
The deprecated `SSEClientTransport` has been removed in favor of `StreamableHTTPClientTransport`, which is the modern MCP standard.
|
|
39
|
+
|
|
40
|
+
**Migration:**
|
|
41
|
+
|
|
42
|
+
If you had MCP servers configured with `type: sse`, update your configuration:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
# Before (no longer supported)
|
|
46
|
+
mcpServers:
|
|
47
|
+
- id: my-server
|
|
48
|
+
name: My Server
|
|
49
|
+
type: sse
|
|
50
|
+
url: 'http://example.com/sse'
|
|
51
|
+
|
|
52
|
+
# After
|
|
53
|
+
mcpServers:
|
|
54
|
+
- id: my-server
|
|
55
|
+
name: My Server
|
|
56
|
+
url: 'http://example.com/mcp' # type is auto-detected when url is present
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Changes:**
|
|
60
|
+
|
|
61
|
+
- Removed `MCPServerType.SSE` enum value from both frontend and backend
|
|
62
|
+
- Removed SSE transport fallback logic from `MCPClientServiceImpl`
|
|
63
|
+
- Updated configuration schema to only accept `stdio` and `streamable-http` types
|
|
64
|
+
- HTTP servers are now auto-detected when a `url` field is present
|
|
65
|
+
|
|
3
66
|
## 0.1.2
|
|
4
67
|
|
|
5
68
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -11,10 +11,11 @@ The MCP Chat plugin brings conversational AI capabilities directly into your Bac
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- 🤖 **Multi-Provider AI Support**: Works with OpenAI, Claude, Gemini, and Ollama
|
|
14
|
-
- 🔧 **Multi-Server Support**: Connect multiple MCP servers (STDIO,
|
|
14
|
+
- 🔧 **Multi-Server Support**: Connect multiple MCP servers (STDIO, Streamable HTTP)
|
|
15
15
|
- 🛠️ **Tool Management**: Browse and dynamically enable/disable tools from connected MCP servers
|
|
16
16
|
- 💬 **Rich Chat Interface**: Beautiful, responsive chat UI with markdown support
|
|
17
17
|
- ⚡ **Quick Setup**: Configurable QuickStart prompts for common use cases
|
|
18
|
+
- 📜 **Conversation History**: View, search, star, and manage your chat sessions
|
|
18
19
|
|
|
19
20
|
## Supported AI Providers
|
|
20
21
|
|
|
@@ -198,7 +199,7 @@ mcpChat:
|
|
|
198
199
|
category: Catalog
|
|
199
200
|
```
|
|
200
201
|
|
|
201
|
-
For more advanced MCP server configuration examples (including STDIO, Streamable HTTP,
|
|
202
|
+
For more advanced MCP server configuration examples (including STDIO, Streamable HTTP, custom scripts, and arguments), see [SERVER_CONFIGURATION](../../docs/SERVER_CONFIGURATION.md).
|
|
202
203
|
|
|
203
204
|
### Environment Variables
|
|
204
205
|
|
|
@@ -226,9 +227,17 @@ export KUBECONFIG="/path/to/your/kubeconfig.yaml"
|
|
|
226
227
|
- Tool management controls for enabling/disabling specific servers
|
|
227
228
|
|
|
228
229
|
3. **Start Chatting**: Begin a conversation by:
|
|
230
|
+
|
|
229
231
|
- Selecting from the provided quick prompts, or
|
|
230
232
|
- Typing your own queries directly into the chat input field
|
|
231
233
|
|
|
234
|
+
4. **Manage Conversation History**: Access your chat history from the right sidebar:
|
|
235
|
+
- View past conversations ordered by recent activity
|
|
236
|
+
- Star important conversations for quick access
|
|
237
|
+
- Search through conversation titles
|
|
238
|
+
- Delete conversations you no longer need
|
|
239
|
+
- Click any conversation to restore and continue it
|
|
240
|
+
|
|
232
241
|
### Example Queries
|
|
233
242
|
|
|
234
243
|
| Query | MCP Server Required | Purpose |
|
|
@@ -7,7 +7,7 @@ class McpChat {
|
|
|
7
7
|
this.discoveryApi = options.discoveryApi;
|
|
8
8
|
this.fetchApi = options.fetchApi;
|
|
9
9
|
}
|
|
10
|
-
async sendChatMessage(messages, enabledTools = [], signal) {
|
|
10
|
+
async sendChatMessage(messages, enabledTools = [], signal, conversationId) {
|
|
11
11
|
const baseUrl = await this.discoveryApi.getBaseUrl("mcp-chat");
|
|
12
12
|
const response = await this.fetchApi.fetch(`${baseUrl}/chat`, {
|
|
13
13
|
method: "POST",
|
|
@@ -16,7 +16,8 @@ class McpChat {
|
|
|
16
16
|
},
|
|
17
17
|
body: JSON.stringify({
|
|
18
18
|
messages,
|
|
19
|
-
enabledTools
|
|
19
|
+
enabledTools,
|
|
20
|
+
conversationId
|
|
20
21
|
}),
|
|
21
22
|
signal
|
|
22
23
|
});
|
|
@@ -49,6 +50,45 @@ class McpChat {
|
|
|
49
50
|
}
|
|
50
51
|
return response.json();
|
|
51
52
|
}
|
|
53
|
+
async getConversations() {
|
|
54
|
+
const baseUrl = await this.discoveryApi.getBaseUrl("mcp-chat");
|
|
55
|
+
const response = await this.fetchApi.fetch(`${baseUrl}/conversations`);
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw await ResponseError.fromResponse(response);
|
|
58
|
+
}
|
|
59
|
+
return response.json();
|
|
60
|
+
}
|
|
61
|
+
async getConversationById(id) {
|
|
62
|
+
const baseUrl = await this.discoveryApi.getBaseUrl("mcp-chat");
|
|
63
|
+
const response = await this.fetchApi.fetch(
|
|
64
|
+
`${baseUrl}/conversations/${id}`
|
|
65
|
+
);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
throw await ResponseError.fromResponse(response);
|
|
68
|
+
}
|
|
69
|
+
return response.json();
|
|
70
|
+
}
|
|
71
|
+
async deleteConversation(id) {
|
|
72
|
+
const baseUrl = await this.discoveryApi.getBaseUrl("mcp-chat");
|
|
73
|
+
const response = await this.fetchApi.fetch(
|
|
74
|
+
`${baseUrl}/conversations/${id}`,
|
|
75
|
+
{ method: "DELETE" }
|
|
76
|
+
);
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw await ResponseError.fromResponse(response);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async toggleConversationStar(id) {
|
|
82
|
+
const baseUrl = await this.discoveryApi.getBaseUrl("mcp-chat");
|
|
83
|
+
const response = await this.fetchApi.fetch(
|
|
84
|
+
`${baseUrl}/conversations/${id}/star`,
|
|
85
|
+
{ method: "PATCH" }
|
|
86
|
+
);
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw await ResponseError.fromResponse(response);
|
|
89
|
+
}
|
|
90
|
+
return response.json();
|
|
91
|
+
}
|
|
52
92
|
}
|
|
53
93
|
|
|
54
94
|
export { McpChat };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpChatApi.esm.js","sources":["../../src/api/McpChatApi.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport {\n ChatMessage,\n ChatResponse,\n MCPServerStatusData,\n ProviderStatusData,\n ToolsResponse,\n} from '../types';\n\n/**\n * @public\n */\nexport interface McpChatApi {\n sendChatMessage(\n messages: ChatMessage[],\n enabledTools?: string[],\n signal?: AbortSignal,\n ): Promise<ChatResponse>;\n getMCPServerStatus(): Promise<MCPServerStatusData>;\n getAvailableTools(): Promise<ToolsResponse>;\n getProviderStatus(): Promise<ProviderStatusData>;\n}\n\nexport class McpChat implements McpChatApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async sendChatMessage(\n messages: ChatMessage[],\n enabledTools: string[] = [],\n signal?: AbortSignal,\n ): Promise<ChatResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n messages,\n enabledTools,\n }),\n signal,\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getMCPServerStatus(): Promise<MCPServerStatusData> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n const response = await this.fetchApi.fetch(`${baseUrl}/mcp/status`);\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n return response.json();\n }\n\n async getAvailableTools(): Promise<ToolsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/tools`);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getProviderStatus(): Promise<ProviderStatusData> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/provider/status`);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"McpChatApi.esm.js","sources":["../../src/api/McpChatApi.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport {\n ChatMessage,\n ChatResponse,\n ConversationRecord,\n ConversationsResponse,\n MCPServerStatusData,\n ProviderStatusData,\n ToolsResponse,\n} from '../types';\n\n/**\n * @public\n */\nexport interface McpChatApi {\n sendChatMessage(\n messages: ChatMessage[],\n enabledTools?: string[],\n signal?: AbortSignal,\n conversationId?: string,\n ): Promise<ChatResponse>;\n getMCPServerStatus(): Promise<MCPServerStatusData>;\n getAvailableTools(): Promise<ToolsResponse>;\n getProviderStatus(): Promise<ProviderStatusData>;\n getConversations(): Promise<ConversationsResponse>;\n getConversationById(id: string): Promise<ConversationRecord>;\n deleteConversation(id: string): Promise<void>;\n toggleConversationStar(id: string): Promise<{ isStarred: boolean }>;\n}\n\nexport class McpChat implements McpChatApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async sendChatMessage(\n messages: ChatMessage[],\n enabledTools: string[] = [],\n signal?: AbortSignal,\n conversationId?: string,\n ): Promise<ChatResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n messages,\n enabledTools,\n conversationId,\n }),\n signal,\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getMCPServerStatus(): Promise<MCPServerStatusData> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n const response = await this.fetchApi.fetch(`${baseUrl}/mcp/status`);\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n return response.json();\n }\n\n async getAvailableTools(): Promise<ToolsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/tools`);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getProviderStatus(): Promise<ProviderStatusData> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/provider/status`);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getConversations(): Promise<ConversationsResponse> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(`${baseUrl}/conversations`);\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async getConversationById(id: string): Promise<ConversationRecord> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/conversations/${id}`,\n );\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n\n async deleteConversation(id: string): Promise<void> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/conversations/${id}`,\n { method: 'DELETE' },\n );\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n }\n\n async toggleConversationStar(id: string): Promise<{ isStarred: boolean }> {\n const baseUrl = await this.discoveryApi.getBaseUrl('mcp-chat');\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/conversations/${id}/star`,\n { method: 'PATCH' },\n );\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,14 @@ import { QuickStart } from './QuickStart.esm.js';
|
|
|
12
12
|
import { TypingIndicator } from './TypingIndicator.esm.js';
|
|
13
13
|
|
|
14
14
|
const ChatContainer = forwardRef(
|
|
15
|
-
({
|
|
15
|
+
({
|
|
16
|
+
sidebarCollapsed,
|
|
17
|
+
mcpServers,
|
|
18
|
+
messages,
|
|
19
|
+
onMessagesChange,
|
|
20
|
+
conversationId,
|
|
21
|
+
onConversationUpdated
|
|
22
|
+
}, ref) => {
|
|
16
23
|
const theme = useTheme();
|
|
17
24
|
const mcpChatApi = useApi(mcpChatApiRef);
|
|
18
25
|
const [inputValue, setInputValue] = useState("");
|
|
@@ -74,13 +81,17 @@ const ChatContainer = forwardRef(
|
|
|
74
81
|
const response = await mcpChatApi.sendChatMessage(
|
|
75
82
|
apiMessages,
|
|
76
83
|
enabledTools,
|
|
77
|
-
abortControllerRef.current.signal
|
|
84
|
+
abortControllerRef.current.signal,
|
|
85
|
+
conversationId
|
|
78
86
|
);
|
|
79
87
|
if (abortControllerRef.current?.signal.aborted) {
|
|
80
88
|
return;
|
|
81
89
|
}
|
|
82
90
|
setIsTyping(false);
|
|
83
91
|
abortControllerRef.current = null;
|
|
92
|
+
if (response.conversationId && onConversationUpdated) {
|
|
93
|
+
onConversationUpdated(response.conversationId);
|
|
94
|
+
}
|
|
84
95
|
const botResponse = {
|
|
85
96
|
id: (Date.now() + 1).toString(),
|
|
86
97
|
text: response.content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatContainer.esm.js","sources":["../../../src/components/ChatContainer/ChatContainer.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n useEffect,\n useRef,\n useState,\n useImperativeHandle,\n forwardRef,\n} from 'react';\nimport Box from '@mui/material/Box';\nimport IconButton from '@mui/material/IconButton';\nimport TextField from '@mui/material/TextField';\nimport SendIcon from '@mui/icons-material/Send';\nimport { useTheme } from '@mui/material/styles';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { mcpChatApiRef } from '../../api';\nimport type { ChatMessage as ApiChatMessage } from '../../types';\nimport { ChatMessage } from './ChatMessage';\nimport { QuickStart } from './QuickStart';\nimport { TypingIndicator } from './TypingIndicator';\n\ninterface Message {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: Date;\n tools?: string[];\n toolsUsed?: string[];\n toolResponses?: any[]; // Store the actual tool response objects\n}\n\ninterface MCPServer {\n id: string;\n name: string;\n enabled: boolean;\n type?: string;\n hasUrl?: boolean;\n hasNpxCommand?: boolean;\n hasScriptPath?: boolean;\n}\n\ninterface ChatContainerProps {\n sidebarCollapsed: boolean;\n mcpServers: MCPServer[];\n messages: Message[];\n onMessagesChange: (messages: Message[]) => void;\n}\n\nexport interface ChatContainerRef {\n cancelOngoingRequest: () => void;\n}\n\nexport const ChatContainer = forwardRef<ChatContainerRef, ChatContainerProps>(\n ({ sidebarCollapsed, mcpServers, messages, onMessagesChange }, ref) => {\n const theme = useTheme();\n const mcpChatApi = useApi(mcpChatApiRef);\n const [inputValue, setInputValue] = useState('');\n const [isTyping, setIsTyping] = useState(false);\n const messagesEndRef = useRef<null | HTMLDivElement>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n };\n\n // Function to cancel ongoing requests\n const cancelOngoingRequest = () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsTyping(false);\n }\n };\n\n // Expose the cancel function through ref\n useImperativeHandle(\n ref,\n () => ({\n cancelOngoingRequest,\n }),\n [],\n );\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n // Cleanup: cancel any ongoing requests when component unmounts\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n // Shared function to send messages to API\n const sendMessageToAPI = async (messageText: string) => {\n // Cancel any existing request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n // Create new AbortController for this request\n abortControllerRef.current = new AbortController();\n\n const newMessage: Message = {\n id: Date.now().toString(),\n text: messageText,\n isUser: true,\n timestamp: new Date(),\n };\n onMessagesChange([...messages, newMessage]);\n setIsTyping(true);\n\n try {\n // Convert messages to API format including the new message\n const apiMessages: ApiChatMessage[] = [\n ...messages.map(msg => ({\n role: msg.isUser ? ('user' as const) : ('assistant' as const),\n content: msg.text,\n })),\n {\n role: 'user' as const,\n content: messageText,\n },\n ];\n\n // Get enabled tools from MCP servers\n // Backend uses server IDs to filter tools (tool.serverId matches serverConfig.id)\n const enabledTools = mcpServers\n .filter(server => server.enabled)\n .map(server => server.id);\n\n const response = await mcpChatApi.sendChatMessage(\n apiMessages,\n enabledTools,\n abortControllerRef.current.signal,\n );\n\n // Check if request was aborted\n if (abortControllerRef.current?.signal.aborted) {\n return;\n }\n\n setIsTyping(false);\n abortControllerRef.current = null;\n\n const botResponse: Message = {\n id: (Date.now() + 1).toString(),\n text: response.content,\n isUser: false,\n timestamp: new Date(),\n tools: response.toolResponses?.map(tool => tool.toolName) || [],\n toolsUsed: response.toolsUsed || [],\n toolResponses: response.toolResponses || [],\n };\n onMessagesChange([...messages, newMessage, botResponse]);\n } catch (err) {\n // Check if error is due to abortion\n if (err instanceof Error && err.name === 'AbortError') {\n // eslint-disable-next-line no-console\n console.error('Request was cancelled');\n return;\n }\n\n setIsTyping(false);\n abortControllerRef.current = null;\n // eslint-disable-next-line no-console\n console.error('Failed to send message:', err);\n\n let errorMessage =\n 'Sorry, I encountered an error processing your request.';\n\n if (err instanceof Error) {\n if (err.message.includes('404')) {\n errorMessage =\n 'The MCP Chat service is not available. Please check if the backend is running.';\n } else if (err.message.includes('Network')) {\n errorMessage =\n 'Network error. Please check your connection and try again.';\n } else {\n errorMessage = `Error: ${err.message}`;\n }\n }\n\n const errorResponse: Message = {\n id: (Date.now() + 1).toString(),\n text: errorMessage,\n isUser: false,\n timestamp: new Date(),\n tools: [],\n toolsUsed: [],\n toolResponses: [],\n };\n onMessagesChange([...messages, newMessage, errorResponse]);\n }\n };\n\n const handleSuggestionClick = async (suggestion: string) => {\n await sendMessageToAPI(suggestion);\n };\n\n const handleSendMessage = async () => {\n if (inputValue.trim()) {\n const messageText = inputValue;\n setInputValue(''); // Clear input immediately\n await sendMessageToAPI(messageText);\n }\n };\n\n const handleKeyPress = (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n return (\n <Box\n sx={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n marginRight: sidebarCollapsed ? '60px' : '400px',\n transition: 'margin-right 0.3s ease',\n }}\n >\n {messages.length === 0 ? (\n <QuickStart onSuggestionClick={handleSuggestionClick} />\n ) : (\n <Box\n sx={{\n flex: 1,\n overflowY: 'auto',\n padding: theme.spacing(6),\n paddingBottom: theme.spacing(10),\n paddingRight: theme.spacing(14),\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(1),\n backgroundColor: theme.palette.background.default,\n scrollbarGutter: 'stable',\n }}\n >\n {messages.map(message => (\n <ChatMessage key={message.id} message={message} />\n ))}\n {isTyping && <TypingIndicator />}\n <div ref={messagesEndRef} />\n </Box>\n )}\n\n <Box\n sx={{\n marginLeft: '14rem',\n position: 'fixed',\n bottom: 0,\n left: 0,\n right: sidebarCollapsed ? '60px' : '400px',\n padding: theme.spacing(2),\n borderTop: `1px solid ${theme.palette.divider}`,\n borderLeft: `1px solid ${theme.palette.divider}`,\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n backgroundColor: theme.palette.background.paper,\n zIndex: 1000,\n transition: 'right 0.3s ease',\n }}\n >\n <TextField\n sx={{\n marginLeft: theme.spacing(5),\n marginRight: theme.spacing(5),\n flex: 1,\n '& .MuiOutlinedInput-root': {\n borderRadius: theme.spacing(3),\n backgroundColor:\n theme.palette.mode === 'dark'\n ? 'rgba(255, 255, 255, 0.05)'\n : 'rgba(0, 0, 0, 0.05)',\n },\n }}\n placeholder=\"Message Assistant...\"\n variant=\"outlined\"\n multiline\n maxRows={4}\n value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n size=\"small\"\n disabled={isTyping}\n color=\"primary\"\n />\n <IconButton\n sx={{\n backgroundColor:\n !inputValue.trim() || isTyping\n ? theme.palette.action.disabledBackground\n : theme.palette.primary.main,\n color:\n !inputValue.trim() || isTyping\n ? theme.palette.text.disabled\n : theme.palette.primary.contrastText,\n '&:hover': {\n backgroundColor:\n !inputValue.trim() || isTyping\n ? theme.palette.action.disabledBackground\n : theme.palette.primary.dark,\n },\n '&:disabled': {\n backgroundColor: theme.palette.action.disabledBackground,\n color: theme.palette.text.disabled,\n },\n }}\n onClick={handleSendMessage}\n disabled={!inputValue.trim() || isTyping}\n >\n <SendIcon />\n </IconButton>\n </Box>\n </Box>\n );\n },\n);\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatContainer.esm.js","sources":["../../../src/components/ChatContainer/ChatContainer.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n useEffect,\n useRef,\n useState,\n useImperativeHandle,\n forwardRef,\n} from 'react';\nimport Box from '@mui/material/Box';\nimport IconButton from '@mui/material/IconButton';\nimport TextField from '@mui/material/TextField';\nimport SendIcon from '@mui/icons-material/Send';\nimport { useTheme } from '@mui/material/styles';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { mcpChatApiRef } from '../../api';\nimport type { ChatMessage as ApiChatMessage } from '../../types';\nimport { ChatMessage } from './ChatMessage';\nimport { QuickStart } from './QuickStart';\nimport { TypingIndicator } from './TypingIndicator';\n\ninterface Message {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: Date;\n tools?: string[];\n toolsUsed?: string[];\n toolResponses?: any[]; // Store the actual tool response objects\n}\n\ninterface MCPServer {\n id: string;\n name: string;\n enabled: boolean;\n type?: string;\n hasUrl?: boolean;\n hasNpxCommand?: boolean;\n hasScriptPath?: boolean;\n}\n\ninterface ChatContainerProps {\n sidebarCollapsed: boolean;\n mcpServers: MCPServer[];\n messages: Message[];\n onMessagesChange: (messages: Message[]) => void;\n conversationId?: string;\n onConversationUpdated?: (conversationId: string) => void;\n}\n\nexport interface ChatContainerRef {\n cancelOngoingRequest: () => void;\n}\n\nexport const ChatContainer = forwardRef<ChatContainerRef, ChatContainerProps>(\n (\n {\n sidebarCollapsed,\n mcpServers,\n messages,\n onMessagesChange,\n conversationId,\n onConversationUpdated,\n },\n ref,\n ) => {\n const theme = useTheme();\n const mcpChatApi = useApi(mcpChatApiRef);\n const [inputValue, setInputValue] = useState('');\n const [isTyping, setIsTyping] = useState(false);\n const messagesEndRef = useRef<null | HTMLDivElement>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n };\n\n // Function to cancel ongoing requests\n const cancelOngoingRequest = () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsTyping(false);\n }\n };\n\n // Expose the cancel function through ref\n useImperativeHandle(\n ref,\n () => ({\n cancelOngoingRequest,\n }),\n [],\n );\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n // Cleanup: cancel any ongoing requests when component unmounts\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n // Shared function to send messages to API\n const sendMessageToAPI = async (messageText: string) => {\n // Cancel any existing request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n // Create new AbortController for this request\n abortControllerRef.current = new AbortController();\n\n const newMessage: Message = {\n id: Date.now().toString(),\n text: messageText,\n isUser: true,\n timestamp: new Date(),\n };\n onMessagesChange([...messages, newMessage]);\n setIsTyping(true);\n\n try {\n // Convert messages to API format including the new message\n const apiMessages: ApiChatMessage[] = [\n ...messages.map(msg => ({\n role: msg.isUser ? ('user' as const) : ('assistant' as const),\n content: msg.text,\n })),\n {\n role: 'user' as const,\n content: messageText,\n },\n ];\n\n // Get enabled tools from MCP servers\n // Backend uses server IDs to filter tools (tool.serverId matches serverConfig.id)\n const enabledTools = mcpServers\n .filter(server => server.enabled)\n .map(server => server.id);\n\n const response = await mcpChatApi.sendChatMessage(\n apiMessages,\n enabledTools,\n abortControllerRef.current.signal,\n conversationId,\n );\n\n // Check if request was aborted\n if (abortControllerRef.current?.signal.aborted) {\n return;\n }\n\n setIsTyping(false);\n abortControllerRef.current = null;\n\n // Notify parent if conversation was saved\n if (response.conversationId && onConversationUpdated) {\n onConversationUpdated(response.conversationId);\n }\n\n const botResponse: Message = {\n id: (Date.now() + 1).toString(),\n text: response.content,\n isUser: false,\n timestamp: new Date(),\n tools: response.toolResponses?.map(tool => tool.toolName) || [],\n toolsUsed: response.toolsUsed || [],\n toolResponses: response.toolResponses || [],\n };\n onMessagesChange([...messages, newMessage, botResponse]);\n } catch (err) {\n // Check if error is due to abortion\n if (err instanceof Error && err.name === 'AbortError') {\n // eslint-disable-next-line no-console\n console.error('Request was cancelled');\n return;\n }\n\n setIsTyping(false);\n abortControllerRef.current = null;\n // eslint-disable-next-line no-console\n console.error('Failed to send message:', err);\n\n let errorMessage =\n 'Sorry, I encountered an error processing your request.';\n\n if (err instanceof Error) {\n if (err.message.includes('404')) {\n errorMessage =\n 'The MCP Chat service is not available. Please check if the backend is running.';\n } else if (err.message.includes('Network')) {\n errorMessage =\n 'Network error. Please check your connection and try again.';\n } else {\n errorMessage = `Error: ${err.message}`;\n }\n }\n\n const errorResponse: Message = {\n id: (Date.now() + 1).toString(),\n text: errorMessage,\n isUser: false,\n timestamp: new Date(),\n tools: [],\n toolsUsed: [],\n toolResponses: [],\n };\n onMessagesChange([...messages, newMessage, errorResponse]);\n }\n };\n\n const handleSuggestionClick = async (suggestion: string) => {\n await sendMessageToAPI(suggestion);\n };\n\n const handleSendMessage = async () => {\n if (inputValue.trim()) {\n const messageText = inputValue;\n setInputValue(''); // Clear input immediately\n await sendMessageToAPI(messageText);\n }\n };\n\n const handleKeyPress = (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleSendMessage();\n }\n };\n\n return (\n <Box\n sx={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n marginRight: sidebarCollapsed ? '60px' : '400px',\n transition: 'margin-right 0.3s ease',\n }}\n >\n {messages.length === 0 ? (\n <QuickStart onSuggestionClick={handleSuggestionClick} />\n ) : (\n <Box\n sx={{\n flex: 1,\n overflowY: 'auto',\n padding: theme.spacing(6),\n paddingBottom: theme.spacing(10),\n paddingRight: theme.spacing(14),\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(1),\n backgroundColor: theme.palette.background.default,\n scrollbarGutter: 'stable',\n }}\n >\n {messages.map(message => (\n <ChatMessage key={message.id} message={message} />\n ))}\n {isTyping && <TypingIndicator />}\n <div ref={messagesEndRef} />\n </Box>\n )}\n\n <Box\n sx={{\n marginLeft: '14rem',\n position: 'fixed',\n bottom: 0,\n left: 0,\n right: sidebarCollapsed ? '60px' : '400px',\n padding: theme.spacing(2),\n borderTop: `1px solid ${theme.palette.divider}`,\n borderLeft: `1px solid ${theme.palette.divider}`,\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n backgroundColor: theme.palette.background.paper,\n zIndex: 1000,\n transition: 'right 0.3s ease',\n }}\n >\n <TextField\n sx={{\n marginLeft: theme.spacing(5),\n marginRight: theme.spacing(5),\n flex: 1,\n '& .MuiOutlinedInput-root': {\n borderRadius: theme.spacing(3),\n backgroundColor:\n theme.palette.mode === 'dark'\n ? 'rgba(255, 255, 255, 0.05)'\n : 'rgba(0, 0, 0, 0.05)',\n },\n }}\n placeholder=\"Message Assistant...\"\n variant=\"outlined\"\n multiline\n maxRows={4}\n value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n size=\"small\"\n disabled={isTyping}\n color=\"primary\"\n />\n <IconButton\n sx={{\n backgroundColor:\n !inputValue.trim() || isTyping\n ? theme.palette.action.disabledBackground\n : theme.palette.primary.main,\n color:\n !inputValue.trim() || isTyping\n ? theme.palette.text.disabled\n : theme.palette.primary.contrastText,\n '&:hover': {\n backgroundColor:\n !inputValue.trim() || isTyping\n ? theme.palette.action.disabledBackground\n : theme.palette.primary.dark,\n },\n '&:disabled': {\n backgroundColor: theme.palette.action.disabledBackground,\n color: theme.palette.text.disabled,\n },\n }}\n onClick={handleSendMessage}\n disabled={!inputValue.trim() || isTyping}\n >\n <SendIcon />\n </IconButton>\n </Box>\n </Box>\n );\n },\n);\n"],"names":[],"mappings":"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"}
|
|
@@ -45,6 +45,7 @@ import { useProviderStatus } from '../../hooks/useProviderStatus.esm.js';
|
|
|
45
45
|
import { useMcpServers } from '../../hooks/useMcpServers.esm.js';
|
|
46
46
|
import 'react-use/esm/useAsyncRetry';
|
|
47
47
|
import '../../api/index.esm.js';
|
|
48
|
+
import { useConversations } from '../../hooks/useConversations.esm.js';
|
|
48
49
|
|
|
49
50
|
const ChatPage = () => {
|
|
50
51
|
const theme = useTheme();
|
|
@@ -54,9 +55,24 @@ const ChatPage = () => {
|
|
|
54
55
|
error: mcpServersError,
|
|
55
56
|
handleServerToggle
|
|
56
57
|
} = useMcpServers();
|
|
58
|
+
const {
|
|
59
|
+
starredConversations,
|
|
60
|
+
recentConversations,
|
|
61
|
+
loading: conversationsLoading,
|
|
62
|
+
error: conversationsError,
|
|
63
|
+
searchQuery,
|
|
64
|
+
setSearchQuery,
|
|
65
|
+
clearSearch,
|
|
66
|
+
loadConversation,
|
|
67
|
+
refreshConversations,
|
|
68
|
+
deleteConversation,
|
|
69
|
+
toggleStar
|
|
70
|
+
} = useConversations();
|
|
57
71
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
|
58
72
|
const [error, setError] = useState(null);
|
|
59
73
|
const [messages, setMessages] = useState([]);
|
|
74
|
+
const [currentConversationId, setCurrentConversationId] = useState();
|
|
75
|
+
const [selectedConversationId, setSelectedConversationId] = useState();
|
|
60
76
|
const chatContainerRef = useRef(null);
|
|
61
77
|
const combinedError = error || mcpServersError;
|
|
62
78
|
const toggleSidebar = () => {
|
|
@@ -68,10 +84,42 @@ const ChatPage = () => {
|
|
|
68
84
|
}
|
|
69
85
|
setError(null);
|
|
70
86
|
setMessages([]);
|
|
87
|
+
setCurrentConversationId(void 0);
|
|
88
|
+
setSelectedConversationId(void 0);
|
|
71
89
|
};
|
|
72
90
|
const handleMessagesChange = (newMessages) => {
|
|
73
91
|
setMessages(newMessages);
|
|
74
92
|
};
|
|
93
|
+
const handleConversationUpdated = (conversationId) => {
|
|
94
|
+
setCurrentConversationId(conversationId);
|
|
95
|
+
if (!selectedConversationId) {
|
|
96
|
+
setSelectedConversationId(conversationId);
|
|
97
|
+
}
|
|
98
|
+
refreshConversations();
|
|
99
|
+
};
|
|
100
|
+
const handleSelectConversation = async (conversation) => {
|
|
101
|
+
try {
|
|
102
|
+
if (chatContainerRef.current) {
|
|
103
|
+
chatContainerRef.current.cancelOngoingRequest();
|
|
104
|
+
}
|
|
105
|
+
const fullConversation = await loadConversation(conversation.id);
|
|
106
|
+
const uiMessages = fullConversation.messages.map(
|
|
107
|
+
(msg, index) => ({
|
|
108
|
+
id: `${conversation.id}-${index}`,
|
|
109
|
+
text: msg.content,
|
|
110
|
+
isUser: msg.role === "user",
|
|
111
|
+
timestamp: new Date(fullConversation.updatedAt),
|
|
112
|
+
toolsUsed: msg.role === "assistant" ? fullConversation.toolsUsed : void 0
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
setMessages(uiMessages);
|
|
116
|
+
setSelectedConversationId(conversation.id);
|
|
117
|
+
setCurrentConversationId(conversation.id);
|
|
118
|
+
setError(null);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
setError(`Failed to load conversation: ${err}`);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
75
123
|
return /* @__PURE__ */ jsx(Page, { themeId: "tool", children: /* @__PURE__ */ jsx(Content, { noPadding: true, children: /* @__PURE__ */ jsx(
|
|
76
124
|
Box,
|
|
77
125
|
{
|
|
@@ -117,7 +165,9 @@ const ChatPage = () => {
|
|
|
117
165
|
sidebarCollapsed,
|
|
118
166
|
mcpServers,
|
|
119
167
|
messages,
|
|
120
|
-
onMessagesChange: handleMessagesChange
|
|
168
|
+
onMessagesChange: handleMessagesChange,
|
|
169
|
+
conversationId: currentConversationId,
|
|
170
|
+
onConversationUpdated: handleConversationUpdated
|
|
121
171
|
}
|
|
122
172
|
),
|
|
123
173
|
/* @__PURE__ */ jsx(
|
|
@@ -128,7 +178,18 @@ const ChatPage = () => {
|
|
|
128
178
|
onNewChat: handleNewChat,
|
|
129
179
|
mcpServers,
|
|
130
180
|
onServerToggle: handleServerToggle,
|
|
131
|
-
providerStatus
|
|
181
|
+
providerStatus,
|
|
182
|
+
starredConversations,
|
|
183
|
+
recentConversations,
|
|
184
|
+
conversationsLoading,
|
|
185
|
+
conversationsError,
|
|
186
|
+
searchQuery,
|
|
187
|
+
onSearchChange: setSearchQuery,
|
|
188
|
+
onSearchClear: clearSearch,
|
|
189
|
+
onSelectConversation: handleSelectConversation,
|
|
190
|
+
onToggleStar: toggleStar,
|
|
191
|
+
onDeleteConversation: deleteConversation,
|
|
192
|
+
selectedConversationId
|
|
132
193
|
}
|
|
133
194
|
)
|
|
134
195
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPage.esm.js","sources":["../../../src/components/ChatPage/ChatPage.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useState, useRef } from 'react';\nimport { useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport { Content, Page, ResponseErrorPanel } from '@backstage/core-components';\nimport { ChatContainer, type ChatContainerRef } from '../ChatContainer';\nimport { RightPane } from '../RightPane';\nimport { useProviderStatus, useMcpServers } from '../../hooks';\n\ninterface Message {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: Date;\n tools?: string[];\n toolsUsed?: string[];\n toolResponses?: any[];\n}\n\nexport const ChatPage = () => {\n const theme = useTheme();\n\n const providerStatus = useProviderStatus();\n const {\n mcpServers,\n error: mcpServersError,\n handleServerToggle,\n } = useMcpServers();\n const [sidebarCollapsed, setSidebarCollapsed] = useState(true);\n\n const [error, setError] = useState<string | null>(null);\n const [messages, setMessages] = useState<Message[]>([]);\n const chatContainerRef = useRef<ChatContainerRef>(null);\n\n // Combine errors from different sources\n const combinedError = error || mcpServersError;\n\n const toggleSidebar = () => {\n setSidebarCollapsed(!sidebarCollapsed);\n };\n\n const handleNewChat = () => {\n // Cancel any ongoing request first\n if (chatContainerRef.current) {\n chatContainerRef.current.cancelOngoingRequest();\n }\n\n setError(null);\n setMessages([]);\n };\n\n const handleMessagesChange = (newMessages: Message[]) => {\n setMessages(newMessages);\n };\n\n return (\n <Page themeId=\"tool\">\n <Content noPadding>\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: '100vh',\n backgroundColor: theme.palette.background.default,\n }}\n >\n {/* Content Area */}\n <Box\n sx={{\n display: 'flex',\n flex: 1,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n {combinedError ? (\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n padding: '20px',\n }}\n >\n <ResponseErrorPanel\n error={new Error(combinedError)}\n defaultExpanded\n />\n </Box>\n ) : (\n <>\n {/* Chat Container */}\n <ChatContainer\n ref={chatContainerRef}\n sidebarCollapsed={sidebarCollapsed}\n mcpServers={mcpServers}\n messages={messages}\n onMessagesChange={handleMessagesChange}\n />\n\n {/* Sidebar - Right Side */}\n <RightPane\n sidebarCollapsed={sidebarCollapsed}\n onToggleSidebar={toggleSidebar}\n onNewChat={handleNewChat}\n mcpServers={mcpServers}\n onServerToggle={handleServerToggle}\n providerStatus={providerStatus}\n />\n </>\n )}\n </Box>\n </Box>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatPage.esm.js","sources":["../../../src/components/ChatPage/ChatPage.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useState, useRef } from 'react';\nimport { useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport { Content, Page, ResponseErrorPanel } from '@backstage/core-components';\nimport { ChatContainer, type ChatContainerRef } from '../ChatContainer';\nimport { RightPane } from '../RightPane';\nimport {\n useProviderStatus,\n useMcpServers,\n useConversations,\n} from '../../hooks';\nimport type { ConversationRecord } from '../../types';\n\ninterface Message {\n id: string;\n text: string;\n isUser: boolean;\n timestamp: Date;\n tools?: string[];\n toolsUsed?: string[];\n toolResponses?: any[];\n}\n\nexport const ChatPage = () => {\n const theme = useTheme();\n\n const providerStatus = useProviderStatus();\n const {\n mcpServers,\n error: mcpServersError,\n handleServerToggle,\n } = useMcpServers();\n const {\n starredConversations,\n recentConversations,\n loading: conversationsLoading,\n error: conversationsError,\n searchQuery,\n setSearchQuery,\n clearSearch,\n loadConversation,\n refreshConversations,\n deleteConversation,\n toggleStar,\n } = useConversations();\n\n const [sidebarCollapsed, setSidebarCollapsed] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [messages, setMessages] = useState<Message[]>([]);\n const [currentConversationId, setCurrentConversationId] = useState<\n string | undefined\n >();\n const [selectedConversationId, setSelectedConversationId] = useState<\n string | undefined\n >();\n const chatContainerRef = useRef<ChatContainerRef>(null);\n\n // Combine errors from different sources\n const combinedError = error || mcpServersError;\n\n const toggleSidebar = () => {\n setSidebarCollapsed(!sidebarCollapsed);\n };\n\n const handleNewChat = () => {\n // Cancel any ongoing request first\n if (chatContainerRef.current) {\n chatContainerRef.current.cancelOngoingRequest();\n }\n\n setError(null);\n setMessages([]);\n setCurrentConversationId(undefined);\n setSelectedConversationId(undefined);\n };\n\n const handleMessagesChange = (newMessages: Message[]) => {\n setMessages(newMessages);\n };\n\n const handleConversationUpdated = (conversationId: string) => {\n setCurrentConversationId(conversationId);\n if (!selectedConversationId) {\n setSelectedConversationId(conversationId);\n }\n // Refresh the conversation list\n refreshConversations();\n };\n\n const handleSelectConversation = async (conversation: ConversationRecord) => {\n try {\n // Cancel any ongoing request first\n if (chatContainerRef.current) {\n chatContainerRef.current.cancelOngoingRequest();\n }\n\n // Load the full conversation\n const fullConversation = await loadConversation(conversation.id);\n\n // Convert conversation messages to UI messages\n const uiMessages: Message[] = fullConversation.messages.map(\n (msg, index) => ({\n id: `${conversation.id}-${index}`,\n text: msg.content,\n isUser: msg.role === 'user',\n timestamp: new Date(fullConversation.updatedAt),\n toolsUsed:\n msg.role === 'assistant' ? fullConversation.toolsUsed : undefined,\n }),\n );\n\n setMessages(uiMessages);\n setSelectedConversationId(conversation.id);\n setCurrentConversationId(conversation.id);\n setError(null);\n } catch (err) {\n setError(`Failed to load conversation: ${err}`);\n }\n };\n\n return (\n <Page themeId=\"tool\">\n <Content noPadding>\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n height: '100vh',\n backgroundColor: theme.palette.background.default,\n }}\n >\n {/* Content Area */}\n <Box\n sx={{\n display: 'flex',\n flex: 1,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n {combinedError ? (\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n padding: '20px',\n }}\n >\n <ResponseErrorPanel\n error={new Error(combinedError)}\n defaultExpanded\n />\n </Box>\n ) : (\n <>\n {/* Chat Container */}\n <ChatContainer\n ref={chatContainerRef}\n sidebarCollapsed={sidebarCollapsed}\n mcpServers={mcpServers}\n messages={messages}\n onMessagesChange={handleMessagesChange}\n conversationId={currentConversationId}\n onConversationUpdated={handleConversationUpdated}\n />\n\n {/* Sidebar - Right Side */}\n <RightPane\n sidebarCollapsed={sidebarCollapsed}\n onToggleSidebar={toggleSidebar}\n onNewChat={handleNewChat}\n mcpServers={mcpServers}\n onServerToggle={handleServerToggle}\n providerStatus={providerStatus}\n starredConversations={starredConversations}\n recentConversations={recentConversations}\n conversationsLoading={conversationsLoading}\n conversationsError={conversationsError}\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n onSearchClear={clearSearch}\n onSelectConversation={handleSelectConversation}\n onToggleStar={toggleStar}\n onDeleteConversation={deleteConversation}\n selectedConversationId={selectedConversationId}\n />\n </>\n )}\n </Box>\n </Box>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":"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"}
|