@sweetoburrito/backstage-plugin-ai-assistant 0.4.1 → 0.6.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.
@@ -16,22 +16,40 @@ import Typography from '@mui/material/Typography';
16
16
  import MenuIcon from '@mui/icons-material/Menu';
17
17
  import AddIcon from '@mui/icons-material/Add';
18
18
  import { useApi } from '@backstage/core-plugin-api';
19
+ import { signalApiRef } from '@backstage/plugin-signals-react';
19
20
 
20
21
  const AiAssistantPage = () => {
21
22
  const chatApi = useApi(chatApiRef);
23
+ const signalApi = useApi(signalApiRef);
22
24
  const theme = useTheme();
23
25
  const [conversationId, setConversationId] = useState();
24
26
  const { value: conversationHistory } = useAsync(
25
27
  () => chatApi.getConversations(),
26
28
  [chatApi]
27
29
  );
28
- const [conversations, { set }] = useList([]);
30
+ const [conversations, { set, updateAt }] = useList([]);
29
31
  useEffect(() => {
30
32
  if (!conversationHistory) {
31
33
  return;
32
34
  }
33
35
  set(conversationHistory);
34
36
  }, [conversationHistory, set]);
37
+ useEffect(() => {
38
+ const subscription = signalApi.subscribe(`ai-assistant.chat.conversation-details-update`, ({ conversation }) => {
39
+ set((currentConversations) => {
40
+ const index = currentConversations.findIndex(
41
+ (c) => c.id === conversation.id
42
+ );
43
+ if (index !== -1) {
44
+ const newConversations = [...currentConversations];
45
+ newConversations[index] = conversation;
46
+ return newConversations;
47
+ }
48
+ return [conversation, ...currentConversations];
49
+ });
50
+ });
51
+ return () => subscription.unsubscribe();
52
+ }, [signalApi, set, updateAt]);
35
53
  const [open, setOpen] = useState(false);
36
54
  const toggleDrawer = (drawerOpen) => () => {
37
55
  setOpen(drawerOpen);
@@ -1 +1 @@
1
- {"version":3,"file":"AiAssistantPage.esm.js","sources":["../../../src/components/AiAssistantPage/AiAssistantPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Conversation } from '../Conversation';\nimport type { Conversation as ConversationType } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { useAsync, useList } from 'react-use';\nimport { chatApiRef } from '../../api/chat';\nimport { useTheme } from '@mui/material/styles';\n\nimport Stack from '@mui/material/Stack';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Drawer from '@mui/material/Drawer';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItem from '@mui/material/ListItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nimport MenuIcon from '@mui/icons-material/Menu';\nimport AddIcon from '@mui/icons-material/Add';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport const AiAssistantPage = () => {\n const chatApi = useApi(chatApiRef);\n const theme = useTheme();\n\n const [conversationId, setConversationId] = useState<string>();\n\n const { value: conversationHistory } = useAsync(\n () => chatApi.getConversations(),\n [chatApi],\n );\n\n const [conversations, { set }] = useList<ConversationType>([]);\n\n useEffect(() => {\n if (!conversationHistory) {\n return;\n }\n\n set(conversationHistory);\n }, [conversationHistory, set]);\n\n const [open, setOpen] = useState(false);\n\n const toggleDrawer = (drawerOpen: boolean) => () => {\n setOpen(drawerOpen);\n };\n\n const openNewChat = () => {\n setConversationId(undefined);\n };\n\n return (\n <>\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n height=\"100vh\"\n maxHeight=\"100vh\"\n boxSizing=\"border-box\"\n >\n <Stack direction=\"row\" spacing={2} justifyContent=\"flex-end\">\n <Tooltip title=\"New Chat\">\n <IconButton onClick={openNewChat}>\n <AddIcon />\n </IconButton>\n </Tooltip>\n\n {conversations.length > 0 && (\n <Tooltip title=\"Chat History\">\n <IconButton onClick={toggleDrawer(true)}>\n <MenuIcon />\n </IconButton>\n </Tooltip>\n )}\n </Stack>\n <Conversation\n conversationId={conversationId}\n setConversationId={setConversationId}\n />\n </Stack>\n <Drawer anchor=\"right\" open={open} onClose={toggleDrawer(false)}>\n <Box\n sx={{ width: 300 }}\n role=\"presentation\"\n onClick={toggleDrawer(false)}\n >\n <List>\n {conversations.map(conversation => (\n <ListItem\n key={conversation.id}\n sx={{\n fontSize: theme.typography.body1.fontSize,\n }}\n >\n <ListItemButton\n sx={{\n justifyContent: 'flex-start !important',\n padding: `${theme.spacing(1)} !important`,\n borderRadius: `${theme.spacing(1)} !important`,\n backgroundColor:\n conversationId === conversation.id\n ? `${theme.palette.action.selected} !important`\n : 'transparent !important',\n }}\n onClick={() => setConversationId(conversation.id)}\n >\n <Typography\n variant=\"body1\"\n sx={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n width: '100%',\n }}\n >\n {conversation.title}\n </Typography>\n </ListItemButton>\n </ListItem>\n ))}\n </List>\n </Box>\n </Drawer>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AiAssistantPage.esm.js","sources":["../../../src/components/AiAssistantPage/AiAssistantPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Conversation } from '../Conversation';\nimport type { Conversation as ConversationType } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { useAsync, useList } from 'react-use';\nimport { chatApiRef } from '../../api/chat';\nimport { useTheme } from '@mui/material/styles';\n\nimport Stack from '@mui/material/Stack';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Drawer from '@mui/material/Drawer';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItem from '@mui/material/ListItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nimport MenuIcon from '@mui/icons-material/Menu';\nimport AddIcon from '@mui/icons-material/Add';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { signalApiRef } from '@backstage/plugin-signals-react';\n\nexport const AiAssistantPage = () => {\n const chatApi = useApi(chatApiRef);\n const signalApi = useApi(signalApiRef);\n\n const theme = useTheme();\n\n const [conversationId, setConversationId] = useState<string>();\n\n const { value: conversationHistory } = useAsync(\n () => chatApi.getConversations(),\n [chatApi],\n );\n\n const [conversations, { set, updateAt }] = useList<ConversationType>([]);\n\n useEffect(() => {\n if (!conversationHistory) {\n return;\n }\n\n set(conversationHistory);\n }, [conversationHistory, set]);\n\n useEffect(() => {\n const subscription = signalApi.subscribe<{\n conversation: ConversationType;\n }>(`ai-assistant.chat.conversation-details-update`, ({ conversation }) => {\n set(currentConversations => {\n const index = currentConversations.findIndex(\n c => c.id === conversation.id,\n );\n\n if (index !== -1) {\n const newConversations = [...currentConversations];\n newConversations[index] = conversation;\n return newConversations;\n }\n return [conversation, ...currentConversations];\n });\n });\n\n return () => subscription.unsubscribe();\n }, [signalApi, set, updateAt]);\n\n const [open, setOpen] = useState(false);\n\n const toggleDrawer = (drawerOpen: boolean) => () => {\n setOpen(drawerOpen);\n };\n\n const openNewChat = () => {\n setConversationId(undefined);\n };\n\n return (\n <>\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n height=\"100vh\"\n maxHeight=\"100vh\"\n boxSizing=\"border-box\"\n >\n <Stack direction=\"row\" spacing={2} justifyContent=\"flex-end\">\n <Tooltip title=\"New Chat\">\n <IconButton onClick={openNewChat}>\n <AddIcon />\n </IconButton>\n </Tooltip>\n\n {conversations.length > 0 && (\n <Tooltip title=\"Chat History\">\n <IconButton onClick={toggleDrawer(true)}>\n <MenuIcon />\n </IconButton>\n </Tooltip>\n )}\n </Stack>\n <Conversation\n conversationId={conversationId}\n setConversationId={setConversationId}\n />\n </Stack>\n <Drawer anchor=\"right\" open={open} onClose={toggleDrawer(false)}>\n <Box\n sx={{ width: 300 }}\n role=\"presentation\"\n onClick={toggleDrawer(false)}\n >\n <List>\n {conversations.map(conversation => (\n <ListItem\n key={conversation.id}\n sx={{\n fontSize: theme.typography.body1.fontSize,\n }}\n >\n <ListItemButton\n sx={{\n justifyContent: 'flex-start !important',\n padding: `${theme.spacing(1)} !important`,\n borderRadius: `${theme.spacing(1)} !important`,\n backgroundColor:\n conversationId === conversation.id\n ? `${theme.palette.action.selected} !important`\n : 'transparent !important',\n }}\n onClick={() => setConversationId(conversation.id)}\n >\n <Typography\n variant=\"body1\"\n sx={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n width: '100%',\n }}\n >\n {conversation.title}\n </Typography>\n </ListItemButton>\n </ListItem>\n ))}\n </List>\n </Box>\n </Drawer>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useApi, errorApiRef } from '@backstage/core-plugin-api';
3
3
  import { chatApiRef } from '../../api/chat.esm.js';
4
- import { useLocalStorage, useAsync, useList, useAsyncFn } from 'react-use';
5
- import { useState, useRef, useEffect } from 'react';
4
+ import { useLocalStorage, useAsync, useAsyncFn } from 'react-use';
5
+ import { useState, useRef, useEffect, useCallback } from 'react';
6
6
  import { signalApiRef } from '@backstage/plugin-signals-react';
7
7
  import Typography from '@mui/material/Typography';
8
8
  import TextField from '@mui/material/TextField';
@@ -26,7 +26,6 @@ const Conversation = ({
26
26
  "modelId",
27
27
  void 0
28
28
  );
29
- const [messageSubscriber, setMessageSubscriber] = useState();
30
29
  const { value: models, loading: loadingModels } = useAsync(
31
30
  () => chatApi.getModels(),
32
31
  [chatApi]
@@ -35,24 +34,58 @@ const Conversation = ({
35
34
  () => chatApi.getConversation(conversationId),
36
35
  [chatApi, conversationId]
37
36
  );
38
- const [messages, { push, set, updateAt }] = useList([]);
39
- const messagesRef = useRef(messages);
37
+ const [messages, setMessages] = useState([]);
40
38
  useEffect(() => {
41
- messagesRef.current = messages;
42
- }, [messages]);
43
- useEffect(() => {
44
- if (!history) {
39
+ if (!history || !history.length) {
45
40
  return;
46
41
  }
47
- set(history);
48
- }, [history, set]);
42
+ setMessages(history);
43
+ }, [history, setMessages]);
44
+ const handleMessageUpdate = useCallback(
45
+ (newMessages) => {
46
+ setMessages((prev) => {
47
+ const updated = [...prev];
48
+ newMessages.forEach((message) => {
49
+ const index = updated.findIndex((m) => m.id === message.id);
50
+ if (index === -1) {
51
+ updated.push(message);
52
+ } else {
53
+ updated[index] = message;
54
+ }
55
+ });
56
+ return updated;
57
+ });
58
+ },
59
+ [setMessages]
60
+ );
61
+ useEffect(() => {
62
+ if (!conversationId) {
63
+ return void 0;
64
+ }
65
+ const subscriber = signalApi.subscribe(
66
+ `ai-assistant.chat.conversation-stream:${conversationId}`,
67
+ (event) => {
68
+ handleMessageUpdate(event.messages);
69
+ }
70
+ );
71
+ return () => {
72
+ subscriber.unsubscribe();
73
+ };
74
+ }, [conversationId, signalApi, handleMessageUpdate]);
75
+ useEffect(() => {
76
+ if (!conversationId) {
77
+ setMessages([]);
78
+ }
79
+ }, [conversationId]);
49
80
  useEffect(() => {
50
81
  if (models && models.length && !modelId) {
51
82
  setModelId(models[0]);
52
83
  }
53
84
  }, [models, modelId, setModelId]);
54
- const [{ loading: sending, value: messageResponse }, sendMessage] = useAsyncFn(async () => {
55
- const newMessages = [{ role: "user", content: input }];
85
+ const [{ loading: sending }, sendMessage] = useAsyncFn(async () => {
86
+ const newMessages = [
87
+ { role: "human", content: input, metadata: {} }
88
+ ];
56
89
  if (!modelId) {
57
90
  errorApi.post({
58
91
  name: "NoModelError",
@@ -62,32 +95,13 @@ const Conversation = ({
62
95
  }
63
96
  setInput("");
64
97
  inputRef.current?.focus();
65
- newMessages.forEach((message) => push(message));
98
+ setMessages((prev) => [...prev, ...newMessages]);
66
99
  const response = await chatApi.sendMessage({
67
100
  conversationId,
68
101
  modelId,
69
102
  messages: newMessages
70
103
  });
71
104
  setConversationId(response.conversationId);
72
- response.messages.forEach((message) => push(message));
73
- const mostRecentMessage = response.messages.at(-1);
74
- if (mostRecentMessage) {
75
- messageSubscriber?.unsubscribe();
76
- const { id: messageId } = mostRecentMessage;
77
- setMessageSubscriber(
78
- signalApi.subscribe(
79
- `ai-assistant.chat.message-stream:${messageId}`,
80
- (message) => {
81
- const index = messagesRef.current.findIndex(
82
- (m) => m.id === message.id
83
- );
84
- if (index !== -1) {
85
- updateAt(index, message);
86
- }
87
- }
88
- )
89
- );
90
- }
91
105
  return response;
92
106
  }, [
93
107
  input,
@@ -99,12 +113,6 @@ const Conversation = ({
99
113
  errorApi,
100
114
  setInput
101
115
  ]);
102
- useEffect(() => {
103
- if (!messageResponse || !conversationId || conversationId === messageResponse.conversationId) {
104
- return;
105
- }
106
- setConversationId(messageResponse.conversationId);
107
- }, [messageResponse, setConversationId, conversationId]);
108
116
  const messageEndRef = useRef(null);
109
117
  useEffect(() => {
110
118
  messageEndRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -125,16 +133,6 @@ const Conversation = ({
125
133
  height: "100%",
126
134
  minHeight: 0,
127
135
  children: [
128
- /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 2, alignItems: "center", children: /* @__PURE__ */ jsx(
129
- Autocomplete,
130
- {
131
- options: models,
132
- defaultValue: modelId,
133
- onChange: (_, value) => setModelId(value || void 0),
134
- sx: { width: 300 },
135
- renderInput: (params) => /* @__PURE__ */ jsx(TextField, { ...params, label: "Models" })
136
- }
137
- ) }),
138
136
  messages && /* @__PURE__ */ jsxs(
139
137
  Stack,
140
138
  {
@@ -151,7 +149,21 @@ const Conversation = ({
151
149
  }
152
150
  },
153
151
  children: [
154
- messages.map((message) => /* @__PURE__ */ jsx(MessageCard, { message }, message.id)),
152
+ messages.map((message, idx) => /* @__PURE__ */ jsx(
153
+ MessageCard,
154
+ {
155
+ message,
156
+ loading: false
157
+ },
158
+ message.id ?? idx
159
+ )),
160
+ (messages[messages.length - 1]?.role === "human" || messages[messages.length - 1]?.role === "tool") && /* @__PURE__ */ jsx(
161
+ MessageCard,
162
+ {
163
+ message: { content: "", role: "ai", metadata: {} },
164
+ loading: true
165
+ }
166
+ ),
155
167
  /* @__PURE__ */ jsx("div", { ref: messageEndRef })
156
168
  ]
157
169
  }
@@ -182,6 +194,17 @@ const Conversation = ({
182
194
  }
183
195
  }
184
196
  ),
197
+ /* @__PURE__ */ jsx(
198
+ Autocomplete,
199
+ {
200
+ options: models,
201
+ defaultValue: modelId,
202
+ onChange: (_, value) => setModelId(value || void 0),
203
+ sx: { width: 180 },
204
+ size: "small",
205
+ renderInput: (params) => /* @__PURE__ */ jsx(TextField, { ...params, label: "Models" })
206
+ }
207
+ ),
185
208
  /* @__PURE__ */ jsx(
186
209
  Button,
187
210
  {
@@ -1 +1 @@
1
- {"version":3,"file":"Conversation.esm.js","sources":["../../../src/components/Conversation/Conversation.tsx"],"sourcesContent":["import { useApi, errorApiRef } from '@backstage/core-plugin-api';\nimport { chatApiRef } from '../../api/chat';\nimport { useAsync, useAsyncFn, useList, useLocalStorage } from 'react-use';\nimport { useEffect, useRef, useState } from 'react';\nimport {\n signalApiRef,\n SignalSubscriber,\n} from '@backstage/plugin-signals-react';\n\nimport Typography from '@mui/material/Typography';\nimport TextField from '@mui/material/TextField';\nimport Stack from '@mui/material/Stack';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Paper from '@mui/material/Paper';\nimport NorthIcon from '@mui/icons-material/North';\nimport Button from '@mui/material/Button';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { MessageCard } from '../MessageCard';\n\ntype ConversationOptions = {\n conversationId: string | undefined;\n setConversationId: (id: string) => void;\n};\n\nexport const Conversation = ({\n conversationId,\n setConversationId,\n}: ConversationOptions) => {\n const chatApi = useApi(chatApiRef);\n const errorApi = useApi(errorApiRef);\n const signalApi = useApi(signalApiRef);\n\n const [input, setInput] = useState('');\n const inputRef = useRef<HTMLInputElement>(null);\n\n const [modelId, setModelId] = useLocalStorage<string | undefined>(\n 'modelId',\n undefined,\n );\n\n const [messageSubscriber, setMessageSubscriber] = useState<\n SignalSubscriber | undefined\n >();\n\n const { value: models, loading: loadingModels } = useAsync(\n () => chatApi.getModels(),\n [chatApi],\n );\n const { value: history, loading: loadingHistory } = useAsync(\n () => chatApi.getConversation(conversationId),\n [chatApi, conversationId],\n );\n\n const [messages, { push, set, updateAt }] = useList<Message>([]);\n const messagesRef = useRef(messages);\n\n useEffect(() => {\n messagesRef.current = messages;\n }, [messages]);\n\n useEffect(() => {\n if (!history) {\n return;\n }\n\n set(history);\n }, [history, set]);\n\n useEffect(() => {\n if (models && models.length && !modelId) {\n setModelId(models[0]);\n }\n }, [models, modelId, setModelId]);\n\n const [{ loading: sending, value: messageResponse }, sendMessage] =\n useAsyncFn(async () => {\n const newMessages: Message[] = [{ role: 'user', content: input }];\n\n if (!modelId) {\n errorApi.post({\n name: 'NoModelError',\n message:\n 'No model has been selected for this conversation. Please select a model before sending a message',\n });\n return undefined;\n }\n\n setInput('');\n inputRef.current?.focus();\n\n newMessages.forEach(message => push(message));\n\n const response = await chatApi.sendMessage({\n conversationId,\n modelId,\n messages: newMessages,\n });\n\n setConversationId(response.conversationId);\n\n response.messages.forEach(message => push(message));\n\n // Get last item in response.messages\n const mostRecentMessage = response.messages.at(-1);\n\n if (mostRecentMessage) {\n messageSubscriber?.unsubscribe();\n\n const { id: messageId } = mostRecentMessage;\n\n setMessageSubscriber(\n signalApi.subscribe(\n `ai-assistant.chat.message-stream:${messageId}`,\n (message: Required<Message>) => {\n const index = messagesRef.current.findIndex(\n m => m.id === message.id,\n );\n if (index !== -1) {\n updateAt(index, message);\n }\n },\n ),\n );\n }\n\n return response;\n }, [\n input,\n inputRef,\n chatApi,\n setConversationId,\n conversationId,\n modelId,\n errorApi,\n setInput,\n ]);\n\n useEffect(() => {\n if (\n !messageResponse ||\n !conversationId ||\n conversationId === messageResponse.conversationId\n ) {\n return;\n }\n\n setConversationId(messageResponse.conversationId);\n }, [messageResponse, setConversationId, conversationId]);\n\n const messageEndRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n messageEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n if (loadingHistory && loadingModels) {\n return <Typography>Loading...</Typography>;\n }\n\n if (!models) {\n return <Typography>No models available</Typography>;\n }\n\n return (\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n boxSizing=\"border-box\"\n height=\"100%\"\n minHeight={0}\n >\n <Stack direction=\"row\" spacing={2} alignItems=\"center\">\n <Autocomplete\n options={models}\n defaultValue={modelId}\n onChange={(_, value) => setModelId(value || undefined)}\n sx={{ width: 300 }}\n renderInput={params => <TextField {...params} label=\"Models\" />}\n />\n </Stack>\n {messages && (\n <Stack\n spacing={1}\n flex={1}\n sx={{\n overflowY: 'auto',\n pr: 1,\n '&::-webkit-scrollbar': {\n background: 'transparent',\n },\n '&::-webkit-scrollbar-track': {\n background: 'transparent',\n },\n }}\n >\n {messages.map(message => (\n <MessageCard key={message.id} message={message} />\n ))}\n <div ref={messageEndRef} />\n </Stack>\n )}\n\n <Paper elevation={2} sx={{ padding: 1 }}>\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n justifyContent=\"start\"\n >\n <TextField\n multiline\n maxRows={3}\n variant=\"standard\"\n sx={{ flex: 1 }}\n value={input}\n onChange={e => setInput(e.target.value)}\n inputRef={inputRef}\n onKeyDown={e => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n }}\n />\n <Button\n variant=\"contained\"\n disabled={sending || !input.trim()}\n onClick={sendMessage}\n >\n <NorthIcon />\n </Button>\n </Stack>\n </Paper>\n </Stack>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Conversation.esm.js","sources":["../../../src/components/Conversation/Conversation.tsx"],"sourcesContent":["import { useApi, errorApiRef } from '@backstage/core-plugin-api';\nimport { chatApiRef } from '../../api/chat';\nimport { useAsync, useAsyncFn, useLocalStorage } from 'react-use';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { signalApiRef } from '@backstage/plugin-signals-react';\n\nimport Typography from '@mui/material/Typography';\nimport TextField from '@mui/material/TextField';\nimport Stack from '@mui/material/Stack';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Paper from '@mui/material/Paper';\nimport NorthIcon from '@mui/icons-material/North';\nimport Button from '@mui/material/Button';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { MessageCard } from '../MessageCard';\n\ntype ConversationOptions = {\n conversationId: string | undefined;\n setConversationId: (id: string) => void;\n};\n\nexport const Conversation = ({\n conversationId,\n setConversationId,\n}: ConversationOptions) => {\n const chatApi = useApi(chatApiRef);\n const errorApi = useApi(errorApiRef);\n const signalApi = useApi(signalApiRef);\n\n const [input, setInput] = useState('');\n const inputRef = useRef<HTMLInputElement>(null);\n\n const [modelId, setModelId] = useLocalStorage<string | undefined>(\n 'modelId',\n undefined,\n );\n\n const { value: models, loading: loadingModels } = useAsync(\n () => chatApi.getModels(),\n [chatApi],\n );\n\n const { value: history, loading: loadingHistory } = useAsync(\n () => chatApi.getConversation(conversationId),\n [chatApi, conversationId],\n );\n\n const [messages, setMessages] = useState<Message[]>([]);\n\n useEffect(() => {\n if (!history || !history.length) {\n return;\n }\n\n setMessages(history);\n }, [history, setMessages]);\n\n const handleMessageUpdate = useCallback(\n (newMessages: Required<Message>[]) => {\n setMessages(prev => {\n const updated = [...prev];\n\n newMessages.forEach(message => {\n const index = updated.findIndex(m => m.id === message.id);\n\n if (index === -1) {\n updated.push(message);\n } else {\n updated[index] = message;\n }\n });\n\n return updated;\n });\n },\n [setMessages],\n );\n\n useEffect(() => {\n if (!conversationId) {\n return undefined;\n }\n\n const subscriber = signalApi.subscribe(\n `ai-assistant.chat.conversation-stream:${conversationId}`,\n (event: { messages: Required<Message>[] }) => {\n handleMessageUpdate(event.messages);\n },\n );\n\n return () => {\n subscriber.unsubscribe();\n };\n }, [conversationId, signalApi, handleMessageUpdate]);\n\n useEffect(() => {\n if (!conversationId) {\n setMessages([]);\n }\n }, [conversationId]);\n\n useEffect(() => {\n if (models && models.length && !modelId) {\n setModelId(models[0]);\n }\n }, [models, modelId, setModelId]);\n\n const [{ loading: sending }, sendMessage] = useAsyncFn(async () => {\n const newMessages: Message[] = [\n { role: 'human', content: input, metadata: {} },\n ];\n\n if (!modelId) {\n errorApi.post({\n name: 'NoModelError',\n message:\n 'No model has been selected for this conversation. Please select a model before sending a message',\n });\n return undefined;\n }\n\n setInput('');\n inputRef.current?.focus();\n\n setMessages(prev => [...prev, ...newMessages]);\n\n const response = await chatApi.sendMessage({\n conversationId,\n modelId,\n messages: newMessages,\n });\n\n setConversationId(response.conversationId);\n\n return response;\n }, [\n input,\n inputRef,\n chatApi,\n setConversationId,\n conversationId,\n modelId,\n errorApi,\n setInput,\n ]);\n\n const messageEndRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n messageEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n if (loadingHistory && loadingModels) {\n return <Typography>Loading...</Typography>;\n }\n\n if (!models) {\n return <Typography>No models available</Typography>;\n }\n\n return (\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n boxSizing=\"border-box\"\n height=\"100%\"\n minHeight={0}\n >\n {messages && (\n <Stack\n spacing={1}\n flex={1}\n sx={{\n overflowY: 'auto',\n pr: 1,\n '&::-webkit-scrollbar': {\n background: 'transparent',\n },\n '&::-webkit-scrollbar-track': {\n background: 'transparent',\n },\n }}\n >\n {messages.map((message, idx) => (\n <MessageCard\n key={message.id ?? idx}\n message={message}\n loading={false}\n />\n ))}\n {(messages[messages.length - 1]?.role === 'human' ||\n messages[messages.length - 1]?.role === 'tool') && (\n <MessageCard\n message={{ content: '', role: 'ai', metadata: {} }}\n loading\n />\n )}\n <div ref={messageEndRef} />\n </Stack>\n )}\n\n <Paper elevation={2} sx={{ padding: 1 }}>\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n justifyContent=\"start\"\n >\n <TextField\n multiline\n maxRows={3}\n variant=\"standard\"\n sx={{ flex: 1 }}\n value={input}\n onChange={e => setInput(e.target.value)}\n inputRef={inputRef}\n onKeyDown={e => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n }}\n />\n <Autocomplete\n options={models}\n defaultValue={modelId}\n onChange={(_, value) => setModelId(value || undefined)}\n sx={{ width: 180 }}\n size=\"small\"\n renderInput={params => <TextField {...params} label=\"Models\" />}\n />\n <Button\n variant=\"contained\"\n disabled={sending || !input.trim()}\n onClick={sendMessage}\n >\n <NorthIcon />\n </Button>\n </Stack>\n </Paper>\n </Stack>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAqBO,MAAM,eAAe,CAAC;AAAA,EAC3B,cAAA;AAAA,EACA;AACF,CAAA,KAA2B;AACzB,EAAA,MAAM,OAAA,GAAU,OAAO,UAAU,CAAA;AACjC,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,EAAE,CAAA;AACrC,EAAA,MAAM,QAAA,GAAW,OAAyB,IAAI,CAAA;AAE9C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,eAAA;AAAA,IAC5B,SAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,OAAA,EAAS,eAAc,GAAI,QAAA;AAAA,IAChD,MAAM,QAAQ,SAAA,EAAU;AAAA,IACxB,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAS,OAAA,EAAS,gBAAe,GAAI,QAAA;AAAA,IAClD,MAAM,OAAA,CAAQ,eAAA,CAAgB,cAAc,CAAA;AAAA,IAC5C,CAAC,SAAS,cAAc;AAAA,GAC1B;AAEA,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,QAAA,CAAoB,EAAE,CAAA;AAEtD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,CAAQ,MAAA,EAAQ;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,WAAA,CAAY,OAAO,CAAA;AAAA,EACrB,CAAA,EAAG,CAAC,OAAA,EAAS,WAAW,CAAC,CAAA;AAEzB,EAAA,MAAM,mBAAA,GAAsB,WAAA;AAAA,IAC1B,CAAC,WAAA,KAAqC;AACpC,MAAA,WAAA,CAAY,CAAA,IAAA,KAAQ;AAClB,QAAA,MAAM,OAAA,GAAU,CAAC,GAAG,IAAI,CAAA;AAExB,QAAA,WAAA,CAAY,QAAQ,CAAA,OAAA,KAAW;AAC7B,UAAA,MAAM,QAAQ,OAAA,CAAQ,SAAA,CAAU,OAAK,CAAA,CAAE,EAAA,KAAO,QAAQ,EAAE,CAAA;AAExD,UAAA,IAAI,UAAU,EAAA,EAAI;AAChB,YAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,UACtB,CAAA,MAAO;AACL,YAAA,OAAA,CAAQ,KAAK,CAAA,GAAI,OAAA;AAAA,UACnB;AAAA,QACF,CAAC,CAAA;AAED,QAAA,OAAO,OAAA;AAAA,MACT,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,aAAa,SAAA,CAAU,SAAA;AAAA,MAC3B,yCAAyC,cAAc,CAAA,CAAA;AAAA,MACvD,CAAC,KAAA,KAA6C;AAC5C,QAAA,mBAAA,CAAoB,MAAM,QAAQ,CAAA;AAAA,MACpC;AAAA,KACF;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,UAAA,CAAW,WAAA,EAAY;AAAA,IACzB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,cAAA,EAAgB,SAAA,EAAW,mBAAmB,CAAC,CAAA;AAEnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,WAAA,CAAY,EAAE,CAAA;AAAA,IAChB;AAAA,EACF,CAAA,EAAG,CAAC,cAAc,CAAC,CAAA;AAEnB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,MAAA,IAAU,MAAA,CAAO,MAAA,IAAU,CAAC,OAAA,EAAS;AACvC,MAAA,UAAA,CAAW,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,IACtB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,UAAU,CAAC,CAAA;AAEhC,EAAA,MAAM,CAAC,EAAE,OAAA,EAAS,OAAA,IAAW,WAAW,CAAA,GAAI,WAAW,YAAY;AACjE,IAAA,MAAM,WAAA,GAAyB;AAAA,MAC7B,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,KAAA,EAAO,QAAA,EAAU,EAAC;AAAE,KAChD;AAEA,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK;AAAA,QACZ,IAAA,EAAM,cAAA;AAAA,QACN,OAAA,EACE;AAAA,OACH,CAAA;AACD,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,QAAA,CAAS,SAAS,KAAA,EAAM;AAExB,IAAA,WAAA,CAAY,UAAQ,CAAC,GAAG,IAAA,EAAM,GAAG,WAAW,CAAC,CAAA;AAE7C,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,WAAA,CAAY;AAAA,MACzC,cAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA,EAAU;AAAA,KACX,CAAA;AAED,IAAA,iBAAA,CAAkB,SAAS,cAAc,CAAA;AAEzC,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,EAAG;AAAA,IACD,KAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,iBAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,aAAA,GAAgB,OAAuB,IAAI,CAAA;AAEjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,aAAA,CAAc,OAAA,EAAS,cAAA,CAAe,EAAE,QAAA,EAAU,UAAU,CAAA;AAAA,EAC9D,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,IAAI,kBAAkB,aAAA,EAAe;AACnC,IAAA,uBAAO,GAAA,CAAC,cAAW,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,EAC/B;AAEA,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,uBAAO,GAAA,CAAC,cAAW,QAAA,EAAA,qBAAA,EAAmB,CAAA;AAAA,EACxC;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,CAAA;AAAA,MACT,OAAA,EAAS,CAAA;AAAA,MACT,IAAA,EAAM,CAAA;AAAA,MACN,SAAA,EAAU,YAAA;AAAA,MACV,MAAA,EAAO,MAAA;AAAA,MACP,SAAA,EAAW,CAAA;AAAA,MAEV,QAAA,EAAA;AAAA,QAAA,QAAA,oBACC,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAS,CAAA;AAAA,YACT,IAAA,EAAM,CAAA;AAAA,YACN,EAAA,EAAI;AAAA,cACF,SAAA,EAAW,MAAA;AAAA,cACX,EAAA,EAAI,CAAA;AAAA,cACJ,sBAAA,EAAwB;AAAA,gBACtB,UAAA,EAAY;AAAA,eACd;AAAA,cACA,4BAAA,EAA8B;AAAA,gBAC5B,UAAA,EAAY;AAAA;AACd,aACF;AAAA,YAEC,QAAA,EAAA;AAAA,cAAA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,EAAS,GAAA,qBACtB,GAAA;AAAA,gBAAC,WAAA;AAAA,gBAAA;AAAA,kBAEC,OAAA;AAAA,kBACA,OAAA,EAAS;AAAA,iBAAA;AAAA,gBAFJ,QAAQ,EAAA,IAAM;AAAA,eAItB,CAAA;AAAA,cAAA,CACC,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,EAAG,IAAA,KAAS,OAAA,IACxC,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,EAAG,SAAS,MAAA,qBACxC,GAAA;AAAA,gBAAC,WAAA;AAAA,gBAAA;AAAA,kBACC,OAAA,EAAS,EAAE,OAAA,EAAS,EAAA,EAAI,MAAM,IAAA,EAAM,QAAA,EAAU,EAAC,EAAE;AAAA,kBACjD,OAAA,EAAO;AAAA;AAAA,eACT;AAAA,8BAEF,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,aAAA,EAAe;AAAA;AAAA;AAAA,SAC3B;AAAA,wBAGF,GAAA,CAAC,SAAM,SAAA,EAAW,CAAA,EAAG,IAAI,EAAE,OAAA,EAAS,GAAE,EACpC,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,KAAA;AAAA,YACV,OAAA,EAAS,CAAA;AAAA,YACT,UAAA,EAAW,QAAA;AAAA,YACX,cAAA,EAAe,OAAA;AAAA,YAEf,QAAA,EAAA;AAAA,8BAAA,GAAA;AAAA,gBAAC,SAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAS,IAAA;AAAA,kBACT,OAAA,EAAS,CAAA;AAAA,kBACT,OAAA,EAAQ,UAAA;AAAA,kBACR,EAAA,EAAI,EAAE,IAAA,EAAM,CAAA,EAAE;AAAA,kBACd,KAAA,EAAO,KAAA;AAAA,kBACP,QAAA,EAAU,CAAA,CAAA,KAAK,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,kBACtC,QAAA;AAAA,kBACA,WAAW,CAAA,CAAA,KAAK;AACd,oBAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAC,EAAE,QAAA,EAAU;AACpC,sBAAA,CAAA,CAAE,cAAA,EAAe;AACjB,sBAAA,WAAA,EAAY;AAAA,oBACd;AAAA,kBACF;AAAA;AAAA,eACF;AAAA,8BACA,GAAA;AAAA,gBAAC,YAAA;AAAA,gBAAA;AAAA,kBACC,OAAA,EAAS,MAAA;AAAA,kBACT,YAAA,EAAc,OAAA;AAAA,kBACd,UAAU,CAAC,CAAA,EAAG,KAAA,KAAU,UAAA,CAAW,SAAS,MAAS,CAAA;AAAA,kBACrD,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAI;AAAA,kBACjB,IAAA,EAAK,OAAA;AAAA,kBACL,aAAa,CAAA,MAAA,qBAAU,GAAA,CAAC,aAAW,GAAG,MAAA,EAAQ,OAAM,QAAA,EAAS;AAAA;AAAA,eAC/D;AAAA,8BACA,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,OAAA,EAAQ,WAAA;AAAA,kBACR,QAAA,EAAU,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,EAAK;AAAA,kBACjC,OAAA,EAAS,WAAA;AAAA,kBAET,8BAAC,SAAA,EAAA,EAAU;AAAA;AAAA;AACb;AAAA;AAAA,SACF,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -9,12 +9,12 @@ const Card = ({ children, role }) => {
9
9
  Paper,
10
10
  {
11
11
  sx: {
12
- alignSelf: role === "user" ? "end" : "start",
12
+ alignSelf: role === "human" ? "end" : "start",
13
13
  maxWidth: "70%",
14
14
  width: "70%",
15
15
  borderRadius: 2,
16
16
  border: "double transparent",
17
- borderWidth: role !== "user" ? "2px" : 0,
17
+ borderWidth: role !== "human" ? "2px" : 0,
18
18
  backgroundImage: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}), linear-gradient(to right, ${theme.palette.primary.main}, ${theme.palette.secondary.main})`,
19
19
  backgroundOrigin: "border-box",
20
20
  backgroundClip: "content-box, border-box",
@@ -1 +1 @@
1
- {"version":3,"file":"Card.esm.js","sources":["../../../src/components/MessageCard/Card.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport Box from '@mui/material/Box';\nimport { PropsWithChildren } from 'react';\nimport { useTheme } from '@mui/material/styles';\n\ntype CardProps = PropsWithChildren & {\n role: string;\n};\n\nexport const Card = ({ children, role }: CardProps) => {\n const theme = useTheme();\n return (\n <Paper\n sx={{\n alignSelf: role === 'user' ? 'end' : 'start',\n maxWidth: '70%',\n width: '70%',\n borderRadius: 2,\n border: 'double transparent',\n borderWidth: role !== 'user' ? '2px' : 0,\n backgroundImage: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}), linear-gradient(to right, ${theme.palette.primary.main}, ${theme.palette.secondary.main})`,\n backgroundOrigin: 'border-box',\n backgroundClip: 'content-box, border-box',\n wordBreak: 'break-word',\n }}\n elevation={2}\n >\n <Box p={1}>{children}</Box>\n </Paper>\n );\n};\n"],"names":[],"mappings":";;;;;AASO,MAAM,IAAA,GAAO,CAAC,EAAE,QAAA,EAAU,MAAK,KAAiB;AACrD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAI;AAAA,QACF,SAAA,EAAW,IAAA,KAAS,MAAA,GAAS,KAAA,GAAQ,OAAA;AAAA,QACrC,QAAA,EAAU,KAAA;AAAA,QACV,KAAA,EAAO,KAAA;AAAA,QACP,YAAA,EAAc,CAAA;AAAA,QACd,MAAA,EAAQ,oBAAA;AAAA,QACR,WAAA,EAAa,IAAA,KAAS,MAAA,GAAS,KAAA,GAAQ,CAAA;AAAA,QACvC,eAAA,EAAiB,mBAAmB,KAAA,CAAM,OAAA,CAAQ,WAAW,KAAK,CAAA,EAAA,EAAK,MAAM,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,6BAAA,EAAgC,KAAA,CAAM,QAAQ,OAAA,CAAQ,IAAI,KAAK,KAAA,CAAM,OAAA,CAAQ,UAAU,IAAI,CAAA,CAAA,CAAA;AAAA,QAChM,gBAAA,EAAkB,YAAA;AAAA,QAClB,cAAA,EAAgB,yBAAA;AAAA,QAChB,SAAA,EAAW;AAAA,OACb;AAAA,MACA,SAAA,EAAW,CAAA;AAAA,MAEX,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAI,CAAA,EAAG,CAAA,EAAI,QAAA,EAAS;AAAA;AAAA,GACvB;AAEJ;;;;"}
1
+ {"version":3,"file":"Card.esm.js","sources":["../../../src/components/MessageCard/Card.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport Box from '@mui/material/Box';\nimport { PropsWithChildren } from 'react';\nimport { useTheme } from '@mui/material/styles';\n\ntype CardProps = PropsWithChildren & {\n role: string;\n};\n\nexport const Card = ({ children, role }: CardProps) => {\n const theme = useTheme();\n return (\n <Paper\n sx={{\n alignSelf: role === 'human' ? 'end' : 'start',\n maxWidth: '70%',\n width: '70%',\n borderRadius: 2,\n border: 'double transparent',\n borderWidth: role !== 'human' ? '2px' : 0,\n backgroundImage: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}), linear-gradient(to right, ${theme.palette.primary.main}, ${theme.palette.secondary.main})`,\n backgroundOrigin: 'border-box',\n backgroundClip: 'content-box, border-box',\n wordBreak: 'break-word',\n }}\n elevation={2}\n >\n <Box p={1}>{children}</Box>\n </Paper>\n );\n};\n"],"names":[],"mappings":";;;;;AASO,MAAM,IAAA,GAAO,CAAC,EAAE,QAAA,EAAU,MAAK,KAAiB;AACrD,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAI;AAAA,QACF,SAAA,EAAW,IAAA,KAAS,OAAA,GAAU,KAAA,GAAQ,OAAA;AAAA,QACtC,QAAA,EAAU,KAAA;AAAA,QACV,KAAA,EAAO,KAAA;AAAA,QACP,YAAA,EAAc,CAAA;AAAA,QACd,MAAA,EAAQ,oBAAA;AAAA,QACR,WAAA,EAAa,IAAA,KAAS,OAAA,GAAU,KAAA,GAAQ,CAAA;AAAA,QACxC,eAAA,EAAiB,mBAAmB,KAAA,CAAM,OAAA,CAAQ,WAAW,KAAK,CAAA,EAAA,EAAK,MAAM,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,6BAAA,EAAgC,KAAA,CAAM,QAAQ,OAAA,CAAQ,IAAI,KAAK,KAAA,CAAM,OAAA,CAAQ,UAAU,IAAI,CAAA,CAAA,CAAA;AAAA,QAChM,gBAAA,EAAkB,YAAA;AAAA,QAClB,cAAA,EAAgB,yBAAA;AAAA,QAChB,SAAA,EAAW;AAAA,OACb;AAAA,MACA,SAAA,EAAW,CAAA;AAAA,MAEX,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAI,CAAA,EAAG,CAAA,EAAI,QAAA,EAAS;AAAA;AAAA,GACvB;AAEJ;;;;"}
@@ -12,14 +12,14 @@ import AccordionDetails from '@mui/material/AccordionDetails';
12
12
  import Stack from '@mui/material/Stack';
13
13
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
14
14
 
15
- const MessageCard = ({ message }) => {
15
+ const MessageCard = ({ message, loading }) => {
16
16
  const { content, role } = message;
17
17
  const theme = useTheme();
18
18
  const hasThinking = useMemo(() => {
19
19
  return content.startsWith("<think>");
20
20
  }, [content]);
21
21
  const thinking = useMemo(() => {
22
- return role === "assistant" && content.includes("<think>") && !content.includes("</think>");
22
+ return role === "ai" && content.includes("<think>") && !content.includes("</think>");
23
23
  }, [content, role]);
24
24
  const thoughtProcess = useMemo(() => {
25
25
  const closedMatch = content.match(/<think>(.*?)<\/think>/s);
@@ -35,9 +35,6 @@ const MessageCard = ({ message }) => {
35
35
  const [, contentResponse] = content.split("</think>");
36
36
  return contentResponse ?? "";
37
37
  }, [content, hasThinking]);
38
- const loading = useMemo(() => {
39
- return role === "assistant" && content === "";
40
- }, [content, role]);
41
38
  if (loading) {
42
39
  return /* @__PURE__ */ jsx(Card, { role, children: /* @__PURE__ */ jsx(
43
40
  Skeleton,
@@ -48,6 +45,20 @@ const MessageCard = ({ message }) => {
48
45
  }
49
46
  ) });
50
47
  }
48
+ if (message.role === "tool") {
49
+ return /* @__PURE__ */ jsxs(
50
+ Typography,
51
+ {
52
+ variant: "caption",
53
+ sx: { fontStyle: "italic", color: theme.palette.text.secondary },
54
+ children: [
55
+ "Used tool",
56
+ message.metadata.name ? ` ${message.metadata.name}` : "",
57
+ " to enhance response..."
58
+ ]
59
+ }
60
+ );
61
+ }
51
62
  return /* @__PURE__ */ jsxs(Card, { role, children: [
52
63
  hasThinking && /* @__PURE__ */ jsxs(Accordion, { sx: { paddingX: 1 }, children: [
53
64
  /* @__PURE__ */ jsx(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}), children: thinking && !response ? /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [
@@ -63,7 +74,14 @@ const MessageCard = ({ message }) => {
63
74
  }
64
75
  ) })
65
76
  ] }),
66
- /* @__PURE__ */ jsx(Markdown, { children: response }),
77
+ message.content ? /* @__PURE__ */ jsx(Markdown, { children: message.content }) : /* @__PURE__ */ jsx(
78
+ Skeleton,
79
+ {
80
+ variant: "text",
81
+ height: theme.typography.caption.fontSize,
82
+ width: 40
83
+ }
84
+ ),
67
85
  /* @__PURE__ */ jsx("br", {})
68
86
  ] });
69
87
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MessageCard.esm.js","sources":["../../../src/components/MessageCard/MessageCard.tsx"],"sourcesContent":["import type { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport Markdown from 'react-markdown';\nimport { useTheme } from '@mui/material/styles';\nimport { useMemo } from 'react';\nimport { Card } from './Card';\n\nimport Skeleton from '@mui/material/Skeleton';\nimport Typography from '@mui/material/Typography';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Accordion from '@mui/material/Accordion';\nimport AccordionSummary from '@mui/material/AccordionSummary';\nimport AccordionDetails from '@mui/material/AccordionDetails';\nimport Stack from '@mui/material/Stack';\n\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nexport type MessageCardProps = {\n message: Message;\n};\n\nexport const MessageCard = ({ message }: MessageCardProps) => {\n const { content, role } = message;\n\n const theme = useTheme();\n\n const hasThinking = useMemo(() => {\n return content.startsWith('<think>');\n }, [content]);\n\n const thinking = useMemo<boolean>(() => {\n return (\n role === 'assistant' &&\n content.includes('<think>') &&\n !content.includes('</think>')\n );\n }, [content, role]);\n\n const thoughtProcess = useMemo(() => {\n // Try to match <think>...</think>\n const closedMatch = content.match(/<think>(.*?)<\\/think>/s);\n if (closedMatch) return closedMatch[1].trim();\n\n // If </think> is missing but <think> is present, get everything after <think>\n const openMatch = content.match(/<think>(.*)/s);\n if (openMatch) return openMatch[1].trim();\n\n return '';\n }, [content]);\n\n const response = useMemo<string>(() => {\n if (!hasThinking) {\n return content;\n }\n const [, contentResponse] = content.split('</think>');\n return contentResponse ?? '';\n }, [content, hasThinking]);\n\n const loading = useMemo(() => {\n return role === 'assistant' && content === '';\n }, [content, role]);\n\n if (loading) {\n return (\n <Card role={role}>\n <Skeleton\n variant=\"text\"\n height={theme.typography.body1.fontSize}\n width={210}\n />\n </Card>\n );\n }\n\n return (\n <Card role={role}>\n {hasThinking && (\n <Accordion sx={{ paddingX: 1 }}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n {thinking && !response ? (\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <CircularProgress size={theme.typography.caption.fontSize} />\n <Typography variant=\"caption\">Thinking</Typography>\n </Stack>\n ) : (\n <Typography variant=\"caption\">Thought Process</Typography>\n )}\n </AccordionSummary>\n <AccordionDetails>\n {thoughtProcess ? (\n <Markdown>{thoughtProcess}</Markdown>\n ) : (\n <Skeleton\n variant=\"text\"\n height={theme.typography.caption.fontSize}\n width={40}\n />\n )}\n </AccordionDetails>\n </Accordion>\n )}\n <Markdown>{response}</Markdown>\n <br />\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageCard.esm.js","sources":["../../../src/components/MessageCard/MessageCard.tsx"],"sourcesContent":["import type { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport Markdown from 'react-markdown';\nimport { useTheme } from '@mui/material/styles';\nimport { useMemo } from 'react';\nimport { Card } from './Card';\n\nimport Skeleton from '@mui/material/Skeleton';\nimport Typography from '@mui/material/Typography';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Accordion from '@mui/material/Accordion';\nimport AccordionSummary from '@mui/material/AccordionSummary';\nimport AccordionDetails from '@mui/material/AccordionDetails';\nimport Stack from '@mui/material/Stack';\n\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nexport type MessageCardProps = {\n message: Message;\n loading: boolean;\n};\n\nexport const MessageCard = ({ message, loading }: MessageCardProps) => {\n const { content, role } = message;\n\n const theme = useTheme();\n\n const hasThinking = useMemo(() => {\n return content.startsWith('<think>');\n }, [content]);\n\n const thinking = useMemo<boolean>(() => {\n return (\n role === 'ai' &&\n content.includes('<think>') &&\n !content.includes('</think>')\n );\n }, [content, role]);\n\n const thoughtProcess = useMemo(() => {\n // Try to match <think>...</think>\n const closedMatch = content.match(/<think>(.*?)<\\/think>/s);\n if (closedMatch) return closedMatch[1].trim();\n\n // If </think> is missing but <think> is present, get everything after <think>\n const openMatch = content.match(/<think>(.*)/s);\n if (openMatch) return openMatch[1].trim();\n\n return '';\n }, [content]);\n\n const response = useMemo<string>(() => {\n if (!hasThinking) {\n return content;\n }\n const [, contentResponse] = content.split('</think>');\n return contentResponse ?? '';\n }, [content, hasThinking]);\n\n if (loading) {\n return (\n <Card role={role}>\n <Skeleton\n variant=\"text\"\n height={theme.typography.body1.fontSize}\n width={210}\n />\n </Card>\n );\n }\n\n if (message.role === 'tool') {\n return (\n <Typography\n variant=\"caption\"\n sx={{ fontStyle: 'italic', color: theme.palette.text.secondary }}\n >\n Used tool{message.metadata.name ? ` ${message.metadata.name}` : ''} to\n enhance response...\n </Typography>\n );\n }\n\n return (\n <Card role={role}>\n {hasThinking && (\n <Accordion sx={{ paddingX: 1 }}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n {thinking && !response ? (\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <CircularProgress size={theme.typography.caption.fontSize} />\n <Typography variant=\"caption\">Thinking</Typography>\n </Stack>\n ) : (\n <Typography variant=\"caption\">Thought Process</Typography>\n )}\n </AccordionSummary>\n <AccordionDetails>\n {thoughtProcess ? (\n <Markdown>{thoughtProcess}</Markdown>\n ) : (\n <Skeleton\n variant=\"text\"\n height={theme.typography.caption.fontSize}\n width={40}\n />\n )}\n </AccordionDetails>\n </Accordion>\n )}\n\n {message.content ? (\n <Markdown>{message.content}</Markdown>\n ) : (\n <Skeleton\n variant=\"text\"\n height={theme.typography.caption.fontSize}\n width={40}\n />\n )}\n <br />\n </Card>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAqBO,MAAM,WAAA,GAAc,CAAC,EAAE,OAAA,EAAS,SAAQ,KAAwB;AACrE,EAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAK,GAAI,OAAA;AAE1B,EAAA,MAAM,QAAQ,QAAA,EAAS;AAEvB,EAAA,MAAM,WAAA,GAAc,QAAQ,MAAM;AAChC,IAAA,OAAO,OAAA,CAAQ,WAAW,SAAS,CAAA;AAAA,EACrC,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,QAAA,GAAW,QAAiB,MAAM;AACtC,IAAA,OACE,IAAA,KAAS,QACT,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,IAC1B,CAAC,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA;AAAA,EAEhC,CAAA,EAAG,CAAC,OAAA,EAAS,IAAI,CAAC,CAAA;AAElB,EAAA,MAAM,cAAA,GAAiB,QAAQ,MAAM;AAEnC,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM,wBAAwB,CAAA;AAC1D,IAAA,IAAI,WAAA,EAAa,OAAO,WAAA,CAAY,CAAC,EAAE,IAAA,EAAK;AAG5C,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,KAAA,CAAM,cAAc,CAAA;AAC9C,IAAA,IAAI,SAAA,EAAW,OAAO,SAAA,CAAU,CAAC,EAAE,IAAA,EAAK;AAExC,IAAA,OAAO,EAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,QAAA,GAAW,QAAgB,MAAM;AACrC,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,OAAO,OAAA;AAAA,IACT;AACA,IAAA,MAAM,GAAG,eAAe,CAAA,GAAI,OAAA,CAAQ,MAAM,UAAU,CAAA;AACpD,IAAA,OAAO,eAAA,IAAmB,EAAA;AAAA,EAC5B,CAAA,EAAG,CAAC,OAAA,EAAS,WAAW,CAAC,CAAA;AAEzB,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,uBACE,GAAA,CAAC,QAAK,IAAA,EACJ,QAAA,kBAAA,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,MAAA;AAAA,QACR,MAAA,EAAQ,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM,QAAA;AAAA,QAC/B,KAAA,EAAO;AAAA;AAAA,KACT,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,uBACE,IAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,SAAA;AAAA,QACR,EAAA,EAAI,EAAE,SAAA,EAAW,QAAA,EAAU,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,SAAA,EAAU;AAAA,QAChE,QAAA,EAAA;AAAA,UAAA,WAAA;AAAA,UACW,QAAQ,QAAA,CAAS,IAAA,GAAO,IAAI,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,CAAA,GAAK,EAAA;AAAA,UAAG;AAAA;AAAA;AAAA,KAErE;AAAA,EAEJ;AAEA,EAAA,uBACE,IAAA,CAAC,QAAK,IAAA,EACH,QAAA,EAAA;AAAA,IAAA,WAAA,yBACE,SAAA,EAAA,EAAU,EAAA,EAAI,EAAE,QAAA,EAAU,GAAE,EAC3B,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,UAAA,kBAAY,GAAA,CAAC,cAAA,EAAA,EAAe,GAC3C,QAAA,EAAA,QAAA,IAAY,CAAC,QAAA,mBACZ,IAAA,CAAC,SAAM,SAAA,EAAU,KAAA,EAAM,OAAA,EAAS,CAAA,EAAG,YAAW,QAAA,EAC5C,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,KAAA,CAAM,UAAA,CAAW,QAAQ,QAAA,EAAU,CAAA;AAAA,wBAC3D,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,QAAA,EAAA,UAAA,EAAQ;AAAA,OAAA,EACxC,oBAEA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,6BAAe,CAAA,EAEjD,CAAA;AAAA,0BACC,gBAAA,EAAA,EACE,QAAA,EAAA,cAAA,mBACC,GAAA,CAAC,QAAA,EAAA,EAAU,0BAAe,CAAA,mBAE1B,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAQ,MAAA;AAAA,UACR,MAAA,EAAQ,KAAA,CAAM,UAAA,CAAW,OAAA,CAAQ,QAAA;AAAA,UACjC,KAAA,EAAO;AAAA;AAAA,OACT,EAEJ;AAAA,KAAA,EACF,CAAA;AAAA,IAGD,QAAQ,OAAA,mBACP,GAAA,CAAC,QAAA,EAAA,EAAU,QAAA,EAAA,OAAA,CAAQ,SAAQ,CAAA,mBAE3B,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,MAAA;AAAA,QACR,MAAA,EAAQ,KAAA,CAAM,UAAA,CAAW,OAAA,CAAQ,QAAA;AAAA,QACjC,KAAA,EAAO;AAAA;AAAA,KACT;AAAA,wBAED,IAAA,EAAA,EAAG;AAAA,GAAA,EACN,CAAA;AAEJ;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweetoburrito/backstage-plugin-ai-assistant",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "@backstage/theme": "backstage:^",
38
38
  "@mui/icons-material": "^6.5.0",
39
39
  "@mui/material": "^6.5.0",
40
- "@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.4.0",
40
+ "@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.5.0",
41
41
  "react-markdown": "^10.1.0",
42
42
  "react-use": "^17.2.4"
43
43
  },