altair-graphql-plugin-ai 7.3.0 → 7.3.2
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/dist/plugin.css +1 -0
- package/dist/plugin.js +318 -0
- package/manifest.json +1 -1
- package/package.json +9 -3
- package/.eslintrc.cjs +0 -18
- package/index.html +0 -97
- package/src/assets/react.svg +0 -1
- package/src/components/Chat/Chat.css +0 -220
- package/src/components/Chat/Chat.tsx +0 -153
- package/src/components/Chat/ChatEmptySession.tsx +0 -38
- package/src/components/Chat/ChatMessage.tsx +0 -86
- package/src/components/Chat/ChatWrapper.tsx +0 -23
- package/src/components/Chat/Spinner.tsx +0 -5
- package/src/dev.tsx +0 -37
- package/src/panel.tsx +0 -194
- package/src/plugin.tsx +0 -31
- package/src/utils.ts +0 -8
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.app.json +0 -27
- package/tsconfig.json +0 -11
- package/tsconfig.node.json +0 -13
- package/vite.config.ts +0 -22
- /package/{public → dist}/vite.svg +0 -0
package/src/dev.tsx
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import ReactDOM from 'react-dom/client';
|
|
3
|
-
import Chat, { ChatProps } from './components/Chat/Chat';
|
|
4
|
-
|
|
5
|
-
const chatProps: ChatProps = {
|
|
6
|
-
loggedIn: true,
|
|
7
|
-
loading: false,
|
|
8
|
-
sendMessageIsPending: true,
|
|
9
|
-
availableCredits: 0,
|
|
10
|
-
activeSession: {
|
|
11
|
-
id: '1',
|
|
12
|
-
isActive: true,
|
|
13
|
-
title: 'title',
|
|
14
|
-
userId: 'user-1',
|
|
15
|
-
},
|
|
16
|
-
messages: [
|
|
17
|
-
{
|
|
18
|
-
id: '1',
|
|
19
|
-
message:
|
|
20
|
-
'Can you generate a GraphQL query to fetch all users with their names and email addresses?',
|
|
21
|
-
role: 'USER',
|
|
22
|
-
sessionId: '1',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: '2',
|
|
26
|
-
message:
|
|
27
|
-
'Certainly! Here is a GraphQL query to fetch all users along with their names and email addresses:\n\n```graphql\nquery {\n users {\n id\n name\n email\n }\n}\n```\n\nMake sure your GraphQL server schema includes a `users` query that returns a list of user objects, each containing `id`, `name`, and `email` fields.',
|
|
28
|
-
role: 'ASSISTANT',
|
|
29
|
-
sessionId: '1',
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
34
|
-
<React.StrictMode>
|
|
35
|
-
<Chat {...chatProps} />
|
|
36
|
-
</React.StrictMode>
|
|
37
|
-
);
|
package/src/panel.tsx
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { createRoot } from 'react-dom/client';
|
|
2
|
-
import { PluginV3Context } from 'altair-graphql-core/build/plugin/v3/context';
|
|
3
|
-
import { AltairV3Panel } from 'altair-graphql-core/build/plugin/v3/panel';
|
|
4
|
-
import Chat, { ChatProps } from './components/Chat/Chat';
|
|
5
|
-
import {
|
|
6
|
-
QueryClient,
|
|
7
|
-
QueryClientProvider,
|
|
8
|
-
useMutation,
|
|
9
|
-
useQuery,
|
|
10
|
-
} from '@tanstack/react-query';
|
|
11
|
-
import toast, { Toaster } from 'react-hot-toast';
|
|
12
|
-
import { IMessage, ISendMessageDto } from 'altair-graphql-core/build/ai/types';
|
|
13
|
-
|
|
14
|
-
const queryClient = new QueryClient({
|
|
15
|
-
defaultOptions: {
|
|
16
|
-
queries: {
|
|
17
|
-
refetchOnWindowFocus: false,
|
|
18
|
-
refetchOnReconnect: false,
|
|
19
|
-
retry: false,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
interface PanelProps {
|
|
25
|
-
context: PluginV3Context;
|
|
26
|
-
}
|
|
27
|
-
const Panel = ({ context }: PanelProps) => {
|
|
28
|
-
const { data: userInfo, isLoading: userInfoIsLoading } = useQuery({
|
|
29
|
-
queryKey: ['userInfo'],
|
|
30
|
-
queryFn: () => context.getUserInfo(),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const { data: activeSession, isLoading: activeSessionIsLoading } = useQuery({
|
|
34
|
-
queryKey: ['activeSession'],
|
|
35
|
-
queryFn: () => context.getActiveAiSession(),
|
|
36
|
-
enabled: !!userInfo?.loggedIn,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const { data: availableCredits } = useQuery({
|
|
40
|
-
queryKey: ['availableCredits'],
|
|
41
|
-
queryFn: () => context.getAvailableCredits(),
|
|
42
|
-
enabled: !!userInfo?.loggedIn,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const { data: messages, isLoading: messagesIsLoading } = useQuery({
|
|
46
|
-
queryKey: ['sessionMessages', activeSession?.id],
|
|
47
|
-
queryFn: () =>
|
|
48
|
-
activeSession
|
|
49
|
-
? context.getAiSessionMessages(activeSession.id)
|
|
50
|
-
: Promise.resolve([]),
|
|
51
|
-
enabled: !!activeSession?.id,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const { mutate: createAiSession, isPending: createSessionIsPending } = useMutation(
|
|
55
|
-
{
|
|
56
|
-
mutationKey: ['createAiSession'],
|
|
57
|
-
mutationFn: () => context.createAiSession(),
|
|
58
|
-
onSettled: async () => {
|
|
59
|
-
await queryClient.invalidateQueries({ queryKey: ['activeSession'] });
|
|
60
|
-
await queryClient.invalidateQueries({ queryKey: ['sessionMessages'] });
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const {
|
|
66
|
-
mutate: sendMessage,
|
|
67
|
-
isPending: sendMessageIsPending,
|
|
68
|
-
error,
|
|
69
|
-
} = useMutation({
|
|
70
|
-
mutationKey: ['sendMessage'],
|
|
71
|
-
mutationFn: async (message: string) => {
|
|
72
|
-
if (!activeSession) {
|
|
73
|
-
throw new Error('No active session');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const windowState = await context.getCurrentWindowState();
|
|
77
|
-
let graphqlQuery = '';
|
|
78
|
-
let graphqlVariables = '';
|
|
79
|
-
let sdl = '';
|
|
80
|
-
if (windowState) {
|
|
81
|
-
graphqlQuery = windowState.query;
|
|
82
|
-
graphqlVariables = windowState.variables;
|
|
83
|
-
sdl = windowState.sdl;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// build message input
|
|
87
|
-
const input: ISendMessageDto = {
|
|
88
|
-
message,
|
|
89
|
-
graphqlQuery,
|
|
90
|
-
graphqlVariables,
|
|
91
|
-
sdl,
|
|
92
|
-
};
|
|
93
|
-
return context.sendMessageToAiSession(activeSession.id, input);
|
|
94
|
-
},
|
|
95
|
-
onMutate: async (message) => {
|
|
96
|
-
// Cancel any outgoing refetches
|
|
97
|
-
// (so they don't overwrite our optimistic update)
|
|
98
|
-
await queryClient.cancelQueries({ queryKey: ['sessionMessages'] });
|
|
99
|
-
|
|
100
|
-
// Snapshot the previous value
|
|
101
|
-
const previousMessages = queryClient.getQueryData<IMessage[]>([
|
|
102
|
-
'sessionMessages',
|
|
103
|
-
]);
|
|
104
|
-
|
|
105
|
-
const sessionId = activeSession?.id ?? '';
|
|
106
|
-
const fakeMessage: IMessage = {
|
|
107
|
-
id: Math.random().toString(),
|
|
108
|
-
message: message,
|
|
109
|
-
role: 'USER',
|
|
110
|
-
sessionId,
|
|
111
|
-
};
|
|
112
|
-
// Optimistically update to the new value
|
|
113
|
-
queryClient.setQueryData<IMessage[]>(['sessionMessages', sessionId], (old) => [
|
|
114
|
-
...(old ?? []),
|
|
115
|
-
fakeMessage,
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
// Return a context object with the snapshotted value
|
|
119
|
-
return { previousMessages };
|
|
120
|
-
},
|
|
121
|
-
// If the mutation fails,
|
|
122
|
-
// use the context returned from onMutate to roll back
|
|
123
|
-
onError: (_err, _message, context) => {
|
|
124
|
-
if (context?.previousMessages) {
|
|
125
|
-
queryClient.setQueryData(['todos'], context.previousMessages);
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
// Always refetch after error or success:
|
|
129
|
-
onSettled: async () => {
|
|
130
|
-
// wait for the refetching to complete before marking as not pending
|
|
131
|
-
await Promise.all([
|
|
132
|
-
queryClient.invalidateQueries({ queryKey: ['sessionMessages'] }),
|
|
133
|
-
queryClient.invalidateQueries({ queryKey: ['availableCredits'] }),
|
|
134
|
-
]);
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
if (error) {
|
|
139
|
-
console.error(error);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const chatProps: ChatProps = {
|
|
143
|
-
loggedIn: !!userInfo?.loggedIn,
|
|
144
|
-
loading:
|
|
145
|
-
userInfoIsLoading ||
|
|
146
|
-
activeSessionIsLoading ||
|
|
147
|
-
messagesIsLoading ||
|
|
148
|
-
createSessionIsPending,
|
|
149
|
-
userInfo,
|
|
150
|
-
activeSession,
|
|
151
|
-
messages,
|
|
152
|
-
sendMessageIsPending,
|
|
153
|
-
availableCredits: availableCredits?.total ?? 0,
|
|
154
|
-
isPro: userInfo?.plan?.id === 'pro',
|
|
155
|
-
onStartNewSession() {
|
|
156
|
-
createAiSession();
|
|
157
|
-
},
|
|
158
|
-
onSendMessage(message) {
|
|
159
|
-
sendMessage(message);
|
|
160
|
-
},
|
|
161
|
-
async onUseQuery(query) {
|
|
162
|
-
const windowState = await context.getCurrentWindowState();
|
|
163
|
-
if (!windowState) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
await context.setQuery(windowState.windowId, query);
|
|
167
|
-
},
|
|
168
|
-
async onRateMessage(messageId, rating) {
|
|
169
|
-
if (!activeSession) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
await context.rateAiSessionMessage(activeSession.id, messageId, rating);
|
|
173
|
-
toast('Thank you for your feedback!');
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<>
|
|
179
|
-
<Chat {...chatProps} />
|
|
180
|
-
<Toaster />
|
|
181
|
-
</>
|
|
182
|
-
);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
export class AiPluginPanel extends AltairV3Panel {
|
|
186
|
-
create(ctx: PluginV3Context, container: HTMLElement): void {
|
|
187
|
-
const root = createRoot(container);
|
|
188
|
-
root.render(
|
|
189
|
-
<QueryClientProvider client={queryClient}>
|
|
190
|
-
<Panel context={ctx} />
|
|
191
|
-
</QueryClientProvider>
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
}
|
package/src/plugin.tsx
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { PluginV3 } from 'altair-graphql-core/build/plugin/v3/plugin';
|
|
2
|
-
import { PluginV3Context } from 'altair-graphql-core/build/plugin/v3/context';
|
|
3
|
-
import { AiPluginPanel } from './panel';
|
|
4
|
-
import { AltairPanelLocation } from 'altair-graphql-core/build/plugin/panel';
|
|
5
|
-
|
|
6
|
-
class AiPlugin extends PluginV3 {
|
|
7
|
-
constructor() {
|
|
8
|
-
super({
|
|
9
|
-
panels: {
|
|
10
|
-
ai: new AiPluginPanel(),
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async initialize(ctx: PluginV3Context) {
|
|
16
|
-
console.log('PLUGIN initialize', ctx);
|
|
17
|
-
console.log('PLUGIN isElectron', await ctx.getCurrentWindowState());
|
|
18
|
-
ctx.on('query.change', (x) => {
|
|
19
|
-
console.log('PLUGIN query.change', x);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
await ctx.createPanel('ai', {
|
|
23
|
-
location: AltairPanelLocation.SIDEBAR,
|
|
24
|
-
width: 400,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async destroy() {}
|
|
29
|
-
}
|
|
30
|
-
console.log('AI plugin loaded!');
|
|
31
|
-
new AiPlugin();
|
package/src/utils.ts
DELETED
package/src/vite-env.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
package/tsconfig.app.json
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"composite": true,
|
|
4
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
-
"target": "ES2020",
|
|
6
|
-
"useDefineForClassFields": true,
|
|
7
|
-
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
8
|
-
"module": "ESNext",
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"resolveJsonModule": true,
|
|
15
|
-
"isolatedModules": true,
|
|
16
|
-
"moduleDetection": "force",
|
|
17
|
-
"noEmit": true,
|
|
18
|
-
"jsx": "react-jsx",
|
|
19
|
-
|
|
20
|
-
/* Linting */
|
|
21
|
-
"strict": true,
|
|
22
|
-
"noUnusedLocals": true,
|
|
23
|
-
"noUnusedParameters": true,
|
|
24
|
-
"noFallthroughCasesInSwitch": true
|
|
25
|
-
},
|
|
26
|
-
"include": ["src"]
|
|
27
|
-
}
|
package/tsconfig.json
DELETED
package/tsconfig.node.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"composite": true,
|
|
4
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
5
|
-
"skipLibCheck": true,
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"moduleResolution": "bundler",
|
|
8
|
-
"allowSyntheticDefaultImports": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"noEmit": true
|
|
11
|
-
},
|
|
12
|
-
"include": ["vite.config.ts"]
|
|
13
|
-
}
|
package/vite.config.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite';
|
|
2
|
-
import react from '@vitejs/plugin-react-swc';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
// https://vitejs.dev/config/
|
|
6
|
-
export default defineConfig({
|
|
7
|
-
plugins: [react()],
|
|
8
|
-
build: {
|
|
9
|
-
rollupOptions: {
|
|
10
|
-
input: {
|
|
11
|
-
plugin: fileURLToPath(new URL('./src/plugin.tsx', import.meta.url)),
|
|
12
|
-
},
|
|
13
|
-
output: {
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
-
manualChunks: false as any,
|
|
16
|
-
inlineDynamicImports: true,
|
|
17
|
-
entryFileNames: '[name].js', // currently does not work for the legacy bundle
|
|
18
|
-
assetFileNames: '[name].[ext]', // currently does not work for images
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
});
|
|
File without changes
|