@sweetoburrito/backstage-plugin-ai-assistant 0.3.2 → 0.4.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.
@@ -45,7 +45,15 @@ const createChatService = ({
45
45
  conversationId: data.conversationId
46
46
  };
47
47
  };
48
- return { getModels, getConversation, sendMessage };
48
+ const getConversations = async () => {
49
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
50
+ const response = await fetchApi.fetch(
51
+ `${assistantBaseUrl}/chat/conversations`
52
+ );
53
+ const data = await response.json();
54
+ return data.conversations;
55
+ };
56
+ return { getModels, getConversation, sendMessage, getConversations };
49
57
  };
50
58
 
51
59
  export { chatApiRef, createChatService };
@@ -1 +1 @@
1
- {"version":3,"file":"chat.esm.js","sources":["../../src/api/chat.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\ntype SendMessageOptions = {\n conversationId?: string;\n modelId: string;\n messages: Message[];\n stream?: boolean;\n};\n\nexport type ChatApi = {\n getModels: () => Promise<string[]>;\n getConversation: (id?: string) => Promise<Message[]>;\n sendMessage: (options: SendMessageOptions) => Promise<{\n messages: Message[];\n conversationId: string;\n }>;\n};\n\ntype ChatApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const chatApiRef = createApiRef<ChatApi>({\n id: 'plugin.ai-assistant.chat',\n});\n\nexport const createChatService = ({\n fetchApi,\n discoveryApi,\n}: ChatApiOptions): ChatApi => {\n const getModels: ChatApi['getModels'] = async (): Promise<string[]> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/models`);\n const data = await response.json();\n return data.models;\n };\n\n const getConversation: ChatApi['getConversation'] = async id => {\n if (!id) return [];\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/${id}`);\n\n const data = await response.json();\n\n return data.conversation as Message[];\n };\n\n const sendMessage: ChatApi['sendMessage'] = async ({\n conversationId,\n modelId,\n messages,\n stream,\n }) => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/message`, {\n method: 'POST',\n body: JSON.stringify({\n conversationId,\n modelId,\n messages,\n stream,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const data = await response.json();\n\n return {\n messages: data.messages as Message[],\n conversationId: data.conversationId as string,\n };\n };\n\n return { getModels, getConversation, sendMessage };\n};\n"],"names":[],"mappings":";;AAyBO,MAAM,aAAa,YAAA,CAAsB;AAAA,EAC9C,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,oBAAoB,CAAC;AAAA,EAChC,QAAA;AAAA,EACA;AACF,CAAA,KAA+B;AAC7B,EAAA,MAAM,YAAkC,YAA+B;AACrE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,OAAA,CAAS,CAAA;AAClE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,eAAA,GAA8C,OAAM,EAAA,KAAM;AAC9D,IAAA,IAAI,CAAC,EAAA,EAAI,OAAO,EAAC;AACjB,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA,CAAM,GAAG,gBAAgB,CAAA,MAAA,EAAS,EAAE,CAAA,CAAE,CAAA;AAEtE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,cAAsC,OAAO;AAAA,IACjD,cAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF,KAAM;AACJ,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AACrE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,aAAA,CAAA,EAAiB;AAAA,MACxE,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,cAAA;AAAA,QACA,OAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACD,CAAA;AAAA,MACD,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA;AAClB,KACD,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO;AAAA,MACL,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,gBAAgB,IAAA,CAAK;AAAA,KACvB;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,eAAA,EAAiB,WAAA,EAAY;AACnD;;;;"}
1
+ {"version":3,"file":"chat.esm.js","sources":["../../src/api/chat.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport {\n Conversation,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\ntype SendMessageOptions = {\n conversationId?: string;\n modelId: string;\n messages: Message[];\n stream?: boolean;\n};\n\nexport type ChatApi = {\n getModels: () => Promise<string[]>;\n getConversation: (id?: string) => Promise<Message[]>;\n sendMessage: (options: SendMessageOptions) => Promise<{\n messages: Message[];\n conversationId: string;\n }>;\n getConversations: () => Promise<Conversation[]>;\n};\n\ntype ChatApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const chatApiRef = createApiRef<ChatApi>({\n id: 'plugin.ai-assistant.chat',\n});\n\nexport const createChatService = ({\n fetchApi,\n discoveryApi,\n}: ChatApiOptions): ChatApi => {\n const getModels: ChatApi['getModels'] = async (): Promise<string[]> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/models`);\n const data = await response.json();\n return data.models;\n };\n\n const getConversation: ChatApi['getConversation'] = async id => {\n if (!id) return [];\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/${id}`);\n\n const data = await response.json();\n\n return data.conversation as Message[];\n };\n\n const sendMessage: ChatApi['sendMessage'] = async ({\n conversationId,\n modelId,\n messages,\n stream,\n }) => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/message`, {\n method: 'POST',\n body: JSON.stringify({\n conversationId,\n modelId,\n messages,\n stream,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const data = await response.json();\n\n return {\n messages: data.messages as Message[],\n conversationId: data.conversationId as string,\n };\n };\n\n const getConversations: ChatApi['getConversations'] = async () => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(\n `${assistantBaseUrl}/chat/conversations`,\n );\n\n const data = await response.json();\n\n return data.conversations as Conversation[];\n };\n\n return { getModels, getConversation, sendMessage, getConversations };\n};\n"],"names":[],"mappings":";;AA6BO,MAAM,aAAa,YAAA,CAAsB;AAAA,EAC9C,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,oBAAoB,CAAC;AAAA,EAChC,QAAA;AAAA,EACA;AACF,CAAA,KAA+B;AAC7B,EAAA,MAAM,YAAkC,YAA+B;AACrE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,OAAA,CAAS,CAAA;AAClE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,eAAA,GAA8C,OAAM,EAAA,KAAM;AAC9D,IAAA,IAAI,CAAC,EAAA,EAAI,OAAO,EAAC;AACjB,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA,CAAM,GAAG,gBAAgB,CAAA,MAAA,EAAS,EAAE,CAAA,CAAE,CAAA;AAEtE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,cAAsC,OAAO;AAAA,IACjD,cAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF,KAAM;AACJ,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AACrE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,aAAA,CAAA,EAAiB;AAAA,MACxE,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,cAAA;AAAA,QACA,OAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACD,CAAA;AAAA,MACD,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA;AAClB,KACD,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO;AAAA,MACL,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,gBAAgB,IAAA,CAAK;AAAA,KACvB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,mBAAgD,YAAY;AAChE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA;AAAA,MAC9B,GAAG,gBAAgB,CAAA,mBAAA;AAAA,KACrB;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd,CAAA;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,eAAA,EAAiB,WAAA,EAAa,gBAAA,EAAiB;AACrE;;;;"}
@@ -0,0 +1,113 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { Conversation } from '../Conversation/Conversation.esm.js';
4
+ import { useAsync, useList } from 'react-use';
5
+ import { chatApiRef } from '../../api/chat.esm.js';
6
+ import { useTheme } from '@mui/material/styles';
7
+ import Stack from '@mui/material/Stack';
8
+ import IconButton from '@mui/material/IconButton';
9
+ import Tooltip from '@mui/material/Tooltip';
10
+ import Drawer from '@mui/material/Drawer';
11
+ import List from '@mui/material/List';
12
+ import ListItemButton from '@mui/material/ListItemButton';
13
+ import ListItem from '@mui/material/ListItem';
14
+ import Box from '@mui/material/Box';
15
+ import Typography from '@mui/material/Typography';
16
+ import MenuIcon from '@mui/icons-material/Menu';
17
+ import AddIcon from '@mui/icons-material/Add';
18
+ import { useApi } from '@backstage/core-plugin-api';
19
+
20
+ const AiAssistantPage = () => {
21
+ const chatApi = useApi(chatApiRef);
22
+ const theme = useTheme();
23
+ const [conversationId, setConversationId] = useState();
24
+ const { value: conversationHistory } = useAsync(
25
+ () => chatApi.getConversations(),
26
+ [chatApi]
27
+ );
28
+ const [conversations, { set }] = useList([]);
29
+ useEffect(() => {
30
+ if (!conversationHistory) {
31
+ return;
32
+ }
33
+ set(conversationHistory);
34
+ }, [conversationHistory, set]);
35
+ const [open, setOpen] = useState(false);
36
+ const toggleDrawer = (drawerOpen) => () => {
37
+ setOpen(drawerOpen);
38
+ };
39
+ const openNewChat = () => {
40
+ setConversationId(void 0);
41
+ };
42
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ /* @__PURE__ */ jsxs(
44
+ Stack,
45
+ {
46
+ padding: 2,
47
+ spacing: 2,
48
+ flex: 1,
49
+ height: "100vh",
50
+ maxHeight: "100vh",
51
+ boxSizing: "border-box",
52
+ children: [
53
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 2, justifyContent: "flex-end", children: [
54
+ /* @__PURE__ */ jsx(Tooltip, { title: "New Chat", children: /* @__PURE__ */ jsx(IconButton, { onClick: openNewChat, children: /* @__PURE__ */ jsx(AddIcon, {}) }) }),
55
+ conversations.length > 0 && /* @__PURE__ */ jsx(Tooltip, { title: "Chat History", children: /* @__PURE__ */ jsx(IconButton, { onClick: toggleDrawer(true), children: /* @__PURE__ */ jsx(MenuIcon, {}) }) })
56
+ ] }),
57
+ /* @__PURE__ */ jsx(
58
+ Conversation,
59
+ {
60
+ conversationId,
61
+ setConversationId
62
+ }
63
+ )
64
+ ]
65
+ }
66
+ ),
67
+ /* @__PURE__ */ jsx(Drawer, { anchor: "right", open, onClose: toggleDrawer(false), children: /* @__PURE__ */ jsx(
68
+ Box,
69
+ {
70
+ sx: { width: 300 },
71
+ role: "presentation",
72
+ onClick: toggleDrawer(false),
73
+ children: /* @__PURE__ */ jsx(List, { children: conversations.map((conversation) => /* @__PURE__ */ jsx(
74
+ ListItem,
75
+ {
76
+ sx: {
77
+ fontSize: theme.typography.body1.fontSize
78
+ },
79
+ children: /* @__PURE__ */ jsx(
80
+ ListItemButton,
81
+ {
82
+ sx: {
83
+ justifyContent: "flex-start !important",
84
+ padding: `${theme.spacing(1)} !important`,
85
+ borderRadius: `${theme.spacing(1)} !important`,
86
+ backgroundColor: conversationId === conversation.id ? `${theme.palette.action.selected} !important` : "transparent !important"
87
+ },
88
+ onClick: () => setConversationId(conversation.id),
89
+ children: /* @__PURE__ */ jsx(
90
+ Typography,
91
+ {
92
+ variant: "body1",
93
+ sx: {
94
+ overflow: "hidden",
95
+ textOverflow: "ellipsis",
96
+ whiteSpace: "nowrap",
97
+ width: "100%"
98
+ },
99
+ children: conversation.title
100
+ }
101
+ )
102
+ }
103
+ )
104
+ },
105
+ conversation.id
106
+ )) })
107
+ }
108
+ ) })
109
+ ] });
110
+ };
111
+
112
+ export { AiAssistantPage };
113
+ //# sourceMappingURL=AiAssistantPage.esm.js.map
@@ -0,0 +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":";;;;;;;;;;;;;;;;;;;AAqBO,MAAM,kBAAkB,MAAM;AACnC,EAAA,MAAM,OAAA,GAAU,OAAO,UAAU,CAAA;AACjC,EAAA,MAAM,QAAQ,QAAA,EAAS;AAEvB,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,QAAA,EAAiB;AAE7D,EAAA,MAAM,EAAE,KAAA,EAAO,mBAAA,EAAoB,GAAI,QAAA;AAAA,IACrC,MAAM,QAAQ,gBAAA,EAAiB;AAAA,IAC/B,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,CAAC,eAAe,EAAE,GAAA,EAAK,CAAA,GAAI,OAAA,CAA0B,EAAE,CAAA;AAE7D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,mBAAA,EAAqB;AACxB,MAAA;AAAA,IACF;AAEA,IAAA,GAAA,CAAI,mBAAmB,CAAA;AAAA,EACzB,CAAA,EAAG,CAAC,mBAAA,EAAqB,GAAG,CAAC,CAAA;AAE7B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AAEtC,EAAA,MAAM,YAAA,GAAe,CAAC,UAAA,KAAwB,MAAM;AAClD,IAAA,OAAA,CAAQ,UAAU,CAAA;AAAA,EACpB,CAAA;AAEA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,iBAAA,CAAkB,MAAS,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAS,CAAA;AAAA,QACT,IAAA,EAAM,CAAA;AAAA,QACN,MAAA,EAAO,OAAA;AAAA,QACP,SAAA,EAAU,OAAA;AAAA,QACV,SAAA,EAAU,YAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,SAAM,SAAA,EAAU,KAAA,EAAM,OAAA,EAAS,CAAA,EAAG,gBAAe,UAAA,EAChD,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,UAAA,EACb,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,SAAS,WAAA,EACnB,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,CAAA,EACX,CAAA,EACF,CAAA;AAAA,YAEC,cAAc,MAAA,GAAS,CAAA,oBACtB,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAM,cAAA,EACb,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAS,aAAa,IAAI,CAAA,EACpC,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAS,GACZ,CAAA,EACF;AAAA,WAAA,EAEJ,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,YAAA;AAAA,YAAA;AAAA,cACC,cAAA;AAAA,cACA;AAAA;AAAA;AACF;AAAA;AAAA,KACF;AAAA,oBACA,GAAA,CAAC,UAAO,MAAA,EAAO,OAAA,EAAQ,MAAY,OAAA,EAAS,YAAA,CAAa,KAAK,CAAA,EAC5D,QAAA,kBAAA,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAI;AAAA,QACjB,IAAA,EAAK,cAAA;AAAA,QACL,OAAA,EAAS,aAAa,KAAK,CAAA;AAAA,QAE3B,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EACE,QAAA,EAAA,aAAA,CAAc,GAAA,CAAI,CAAA,YAAA,qBACjB,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,EAAA,EAAI;AAAA,cACF,QAAA,EAAU,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM;AAAA,aACnC;AAAA,YAEA,QAAA,kBAAA,GAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBACC,EAAA,EAAI;AAAA,kBACF,cAAA,EAAgB,uBAAA;AAAA,kBAChB,OAAA,EAAS,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,WAAA,CAAA;AAAA,kBAC5B,YAAA,EAAc,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,WAAA,CAAA;AAAA,kBACjC,eAAA,EACE,mBAAmB,YAAA,CAAa,EAAA,GAC5B,GAAG,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,QAAQ,CAAA,WAAA,CAAA,GAChC;AAAA,iBACR;AAAA,gBACA,OAAA,EAAS,MAAM,iBAAA,CAAkB,YAAA,CAAa,EAAE,CAAA;AAAA,gBAEhD,QAAA,kBAAA,GAAA;AAAA,kBAAC,UAAA;AAAA,kBAAA;AAAA,oBACC,OAAA,EAAQ,OAAA;AAAA,oBACR,EAAA,EAAI;AAAA,sBACF,QAAA,EAAU,QAAA;AAAA,sBACV,YAAA,EAAc,UAAA;AAAA,sBACd,UAAA,EAAY,QAAA;AAAA,sBACZ,KAAA,EAAO;AAAA,qBACT;AAAA,oBAEC,QAAA,EAAA,YAAA,CAAa;AAAA;AAAA;AAChB;AAAA;AACF,WAAA;AAAA,UA5BK,YAAA,CAAa;AAAA,SA8BrB,CAAA,EACH;AAAA;AAAA,KACF,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
@@ -0,0 +1,2 @@
1
+ export { AiAssistantPage } from './AiAssistantPage.esm.js';
2
+ //# sourceMappingURL=index.esm.js.map
@@ -13,13 +13,15 @@ import NorthIcon from '@mui/icons-material/North';
13
13
  import Button from '@mui/material/Button';
14
14
  import { MessageCard } from '../MessageCard/MessageCard.esm.js';
15
15
 
16
- const Conversation = () => {
16
+ const Conversation = ({
17
+ conversationId,
18
+ setConversationId
19
+ }) => {
17
20
  const chatApi = useApi(chatApiRef);
18
21
  const errorApi = useApi(errorApiRef);
19
22
  const signalApi = useApi(signalApiRef);
20
23
  const [input, setInput] = useState("");
21
24
  const inputRef = useRef(null);
22
- const [conversationId, setConversationId] = useLocalStorage("conversationId", void 0);
23
25
  const [modelId, setModelId] = useLocalStorage(
24
26
  "modelId",
25
27
  void 0
@@ -98,7 +100,7 @@ const Conversation = () => {
98
100
  setInput
99
101
  ]);
100
102
  useEffect(() => {
101
- if (!messageResponse || conversationId === messageResponse.conversationId) {
103
+ if (!messageResponse || !conversationId || conversationId === messageResponse.conversationId) {
102
104
  return;
103
105
  }
104
106
  setConversationId(messageResponse.conversationId);
@@ -119,8 +121,9 @@ const Conversation = () => {
119
121
  padding: 2,
120
122
  spacing: 2,
121
123
  flex: 1,
122
- height: "100vh",
123
124
  boxSizing: "border-box",
125
+ height: "100%",
126
+ minHeight: 0,
124
127
  children: [
125
128
  /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 2, alignItems: "center", children: /* @__PURE__ */ jsx(
126
129
  Autocomplete,
@@ -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\nexport const Conversation = () => {\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 [conversationId, setConversationId] = useLocalStorage<\n string | undefined\n >('conversationId', undefined);\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 (!messageResponse || conversationId === messageResponse.conversationId) {\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 height=\"100vh\"\n boxSizing=\"border-box\"\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, 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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"}
@@ -21,7 +21,7 @@ const aiAssistantPlugin = createPlugin({
21
21
  const AiAssistantPage = aiAssistantPlugin.provide(
22
22
  createRoutableExtension({
23
23
  name: "AiAssistantPage",
24
- component: () => import('./components/Conversation/index.esm.js').then((m) => m.Conversation),
24
+ component: () => import('./components/AiAssistantPage/index.esm.js').then((m) => m.AiAssistantPage),
25
25
  mountPoint: rootRouteRef
26
26
  })
27
27
  );
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n createRoutableExtension,\n createApiFactory,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { chatApiRef, createChatService } from './api/chat';\n\nexport const aiAssistantPlugin = createPlugin({\n id: 'ai-assistant',\n routes: {\n root: rootRouteRef,\n },\n apis: [\n createApiFactory({\n api: chatApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: options => createChatService(options),\n }),\n ],\n});\n\nexport const AiAssistantPage = aiAssistantPlugin.provide(\n createRoutableExtension({\n name: 'AiAssistantPage',\n component: () =>\n import('./components/Conversation').then(m => m.Conversation),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;;AAWO,MAAM,oBAAoB,YAAA,CAAa;AAAA,EAC5C,EAAA,EAAI,cAAA;AAAA,EACJ,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,gBAAA,CAAiB;AAAA,MACf,GAAA,EAAK,UAAA;AAAA,MACL,IAAA,EAAM;AAAA,QACJ,YAAA,EAAc,eAAA;AAAA,QACd,QAAA,EAAU;AAAA,OACZ;AAAA,MACA,OAAA,EAAS,CAAA,OAAA,KAAW,iBAAA,CAAkB,OAAO;AAAA,KAC9C;AAAA;AAEL,CAAC;AAEM,MAAM,kBAAkB,iBAAA,CAAkB,OAAA;AAAA,EAC/C,uBAAA,CAAwB;AAAA,IACtB,IAAA,EAAM,iBAAA;AAAA,IACN,SAAA,EAAW,MACT,OAAO,wCAA2B,EAAE,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,YAAY,CAAA;AAAA,IAC9D,UAAA,EAAY;AAAA,GACb;AACH;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n createRoutableExtension,\n createApiFactory,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { chatApiRef, createChatService } from './api/chat';\n\nexport const aiAssistantPlugin = createPlugin({\n id: 'ai-assistant',\n routes: {\n root: rootRouteRef,\n },\n apis: [\n createApiFactory({\n api: chatApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: options => createChatService(options),\n }),\n ],\n});\n\nexport const AiAssistantPage = aiAssistantPlugin.provide(\n createRoutableExtension({\n name: 'AiAssistantPage',\n component: () =>\n import('./components/AiAssistantPage').then(m => m.AiAssistantPage),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;;AAWO,MAAM,oBAAoB,YAAA,CAAa;AAAA,EAC5C,EAAA,EAAI,cAAA;AAAA,EACJ,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,gBAAA,CAAiB;AAAA,MACf,GAAA,EAAK,UAAA;AAAA,MACL,IAAA,EAAM;AAAA,QACJ,YAAA,EAAc,eAAA;AAAA,QACd,QAAA,EAAU;AAAA,OACZ;AAAA,MACA,OAAA,EAAS,CAAA,OAAA,KAAW,iBAAA,CAAkB,OAAO;AAAA,KAC9C;AAAA;AAEL,CAAC;AAEM,MAAM,kBAAkB,iBAAA,CAAkB,OAAA;AAAA,EAC/C,uBAAA,CAAwB;AAAA,IACtB,IAAA,EAAM,iBAAA;AAAA,IACN,SAAA,EAAW,MACT,OAAO,2CAA8B,EAAE,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,eAAe,CAAA;AAAA,IACpE,UAAA,EAAY;AAAA,GACb;AACH;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweetoburrito/backstage-plugin-ai-assistant",
3
- "version": "0.3.2",
3
+ "version": "0.4.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.2.2",
40
+ "@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.3.0",
41
41
  "react-markdown": "^10.1.0",
42
42
  "react-use": "^17.2.4"
43
43
  },
@@ -1,2 +0,0 @@
1
- export { Conversation } from './Conversation.esm.js';
2
- //# sourceMappingURL=index.esm.js.map