@eventcatalog/core 3.0.0-beta.17 → 3.0.0-beta.19
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/README.md +10 -0
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-RUFDOJDH.js → chunk-E7QOZU4L.js} +1 -1
- package/dist/{chunk-GWHDVIAZ.js → chunk-FHSD3KUQ.js} +1 -1
- package/dist/{chunk-Z33VOYNU.js → chunk-FQUOTAQT.js} +1 -1
- package/dist/{chunk-HIAWHMVA.js → chunk-R6WYYOD5.js} +1 -1
- package/dist/{chunk-4KMITDJ3.js → chunk-ZK4S7XBB.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +20 -1
- package/dist/eventcatalog.js +24 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +1 -1
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +814 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanelButton.tsx +24 -0
- package/eventcatalog/src/components/Header.astro +7 -3
- package/eventcatalog/src/components/Search/Search.astro +2 -2
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/chat.ts +233 -30
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +0 -8
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +4 -0
- package/eventcatalog/src/utils/collections/schemas.ts +31 -7
- package/eventcatalog/src/utils/feature.ts +4 -1
- package/package.json +8 -10
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/Chat.tsx +0 -60
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatMessage.tsx +0 -414
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatSidebar.tsx +0 -169
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/InputModal.tsx +0 -244
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/MentionInput.tsx +0 -211
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/WelcomePromptArea.tsx +0 -176
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/default-prompts.ts +0 -93
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/hooks/ChatProvider.tsx +0 -143
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.server.tsx +0 -387
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +0 -104
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -140
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/anthropic.ts +0 -28
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/google.ts +0 -41
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/index.ts +0 -26
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/openai.ts +0 -61
- package/eventcatalog/src/enterprise/eventcatalog-chat/utils/chat-prompts.ts +0 -50
- package/eventcatalog/src/pages/chat/feature.astro +0 -179
- package/eventcatalog/src/pages/chat/index.astro +0 -10
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Sparkles } from 'lucide-react';
|
|
3
|
+
import ChatPanel from './ChatPanel';
|
|
4
|
+
|
|
5
|
+
const ChatPanelButton = () => {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<button
|
|
11
|
+
onClick={() => setIsOpen(true)}
|
|
12
|
+
className="flex items-center gap-1.5 px-4 py-1.5 rounded-md bg-white hover:bg-gray-50 ring-1 ring-inset ring-gray-300 shadow-sm transition-colors text-sm ml-[-1px]"
|
|
13
|
+
aria-label="Open AI Assistant"
|
|
14
|
+
>
|
|
15
|
+
<Sparkles size={14} className="text-purple-500" />
|
|
16
|
+
<span className="font-light text-gray-600">Ask AI</span>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<ChatPanel isOpen={isOpen} onClose={() => setIsOpen(false)} />
|
|
20
|
+
</>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default ChatPanelButton;
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import catalog from '@utils/eventcatalog-config/catalog';
|
|
3
3
|
import Search from '@components/Search/Search.astro';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
|
-
import { showEventCatalogBranding, showCustomBranding } from '@utils/feature';
|
|
5
|
+
import { showEventCatalogBranding, showCustomBranding, isEventCatalogChatEnabled } from '@utils/feature';
|
|
6
6
|
import { getSession } from 'auth-astro/server';
|
|
7
7
|
import { isAuthEnabled, isSSR } from '@utils/feature';
|
|
8
8
|
import { EnvironmentDropdown } from './EnvironmentDropdown';
|
|
9
|
+
import ChatPanelButton from './ChatPanel/ChatPanelButton';
|
|
9
10
|
|
|
10
11
|
let session = null;
|
|
11
12
|
if (isAuthEnabled()) {
|
|
@@ -42,8 +43,11 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
42
43
|
</a>
|
|
43
44
|
</div>
|
|
44
45
|
|
|
45
|
-
<div class="hidden lg:
|
|
46
|
-
<
|
|
46
|
+
<div class="hidden lg:flex flex-grow -ml-1 items-center">
|
|
47
|
+
<div class="flex-grow max-w-xl">
|
|
48
|
+
<Search />
|
|
49
|
+
</div>
|
|
50
|
+
{isEventCatalogChatEnabled() && <ChatPanelButton client:load />}
|
|
47
51
|
</div>
|
|
48
52
|
|
|
49
53
|
<div class="hidden md:block w-3/12">
|
|
@@ -4,7 +4,7 @@ import SearchModal from './SearchModal.tsx';
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<div>
|
|
7
|
-
<div class="relative flex items-center w-
|
|
7
|
+
<div class="relative flex items-center w-full pr-4">
|
|
8
8
|
<input
|
|
9
9
|
id="search-dummy-input"
|
|
10
10
|
type="text"
|
|
@@ -14,7 +14,7 @@ import SearchModal from './SearchModal.tsx';
|
|
|
14
14
|
class="block w-full rounded-md caret-transparent border-0 py-1.5 pr-14 pl-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 font-light sm:text-sm sm:leading-6 px-4"
|
|
15
15
|
/>
|
|
16
16
|
<MagnifyingGlassIcon className="absolute inset-y-0 left-0 h-9 w-8 flex items-center pl-4 text-gray-400" />
|
|
17
|
-
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-
|
|
17
|
+
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-6">
|
|
18
18
|
<kbd class="inline-flex items-center rounded px-1 font-sans text-xs text-gray-400">⌘K</kbd>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -1,30 +1,87 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { type UIMessage } from 'ai';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { convertToModelMessages, stepCountIs, streamText, tool, type LanguageModel, type ModelMessage, type UIMessage } from 'ai';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { isEventCatalogScaleEnabled } from '@utils/feature';
|
|
5
|
+
import { z, getCollection, getEntry } from 'astro:content';
|
|
6
|
+
import { getConsumersOfMessage, getProducersOfMessage } from '@utils/collections/services';
|
|
7
|
+
import { getSchemasFromResource } from '@utils/collections/schemas';
|
|
8
|
+
import fs from 'fs';
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
process.env.OPENAI_API_KEY = import.meta.env.OPENAI_API_KEY || '';
|
|
10
|
+
const catalogDirectory = process.env.PROJECT_DIR || process.cwd();
|
|
8
11
|
|
|
9
|
-
const
|
|
12
|
+
export const defaultConfiguration = {
|
|
13
|
+
temperature: 0.4,
|
|
14
|
+
topP: 0.9,
|
|
15
|
+
topK: 40,
|
|
16
|
+
frequencyPenalty: 0.1,
|
|
17
|
+
presencePenalty: 0.0,
|
|
18
|
+
maxTokens: 1000,
|
|
19
|
+
};
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- Queries (requests for information)
|
|
15
|
-
- Services (bounded contexts or applications that produce/consume events)
|
|
16
|
-
- Domains (business capabilities or functional areas)
|
|
21
|
+
let hasChatConfiguration = false;
|
|
22
|
+
let model: LanguageModel;
|
|
23
|
+
let modelConfiguration: any;
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
try {
|
|
26
|
+
const providerConfiguration = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.chat.js'));
|
|
27
|
+
model = await providerConfiguration.default();
|
|
28
|
+
modelConfiguration = providerConfiguration.configuration || defaultConfiguration;
|
|
29
|
+
hasChatConfiguration = true;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
hasChatConfiguration = false;
|
|
32
|
+
}
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
const getBaseSystemPrompt = (
|
|
35
|
+
referrer: string
|
|
36
|
+
) => `You are an expert in software architecture and domain-driven design, specializing in the open source tool EventCatalog.
|
|
37
|
+
|
|
38
|
+
You assist developers, architects, and business stakeholders who need information about their software architecture catalog.
|
|
39
|
+
|
|
40
|
+
There are many different resource types in EventCatalog, including:
|
|
41
|
+
- Events (collection name 'events') (asynchronous messages that notify about something that has happened)
|
|
42
|
+
- Commands (collection name 'commands') (requests to perform an action)
|
|
43
|
+
- Queries (collection name 'queries') (requests for information)
|
|
44
|
+
- Services (collection name 'services') (bounded contexts or applications that produce/consume events)
|
|
45
|
+
- Domains (collection name 'domains') (business capabilities or functional areas)
|
|
46
|
+
- Flows (collection name 'flows') (state machines)
|
|
47
|
+
- Channels (collection name 'channels') (communication channels)
|
|
48
|
+
- Entities (collection name 'entities') (data objects)
|
|
49
|
+
- Containers (collection name 'containers') (at the moment these are data stores (databases))
|
|
50
|
+
|
|
51
|
+
The user will ask you some questions about the software architecture catalog, you should use the tools provided to you to get the information they need.
|
|
52
|
+
|
|
53
|
+
At point the referer url will be the URL of the page the user is on, you should use this to help you answer the question,
|
|
54
|
+
You may be able to get the resource from the URL, example if the URL is /docs/events/MyEvent/1.0.0 you can get the resource from the URL, in this case the id is MyEvent and the version is 1.0.0 and collection is events.
|
|
55
|
+
|
|
56
|
+
The referer URL is ${referrer}
|
|
57
|
+
|
|
58
|
+
Some other examples
|
|
59
|
+
|
|
60
|
+
- /docs/events/MyEvent/1.0.0 -> id: MyEvent, version: 1.0.0, collection: events
|
|
61
|
+
- /architecture/domains/E-Commerce/1.0.0 -> id: E-Commerce, version: 1.0.0, collection: domains
|
|
62
|
+
- /visualiser/domains/E-Commerce/1.0.0
|
|
63
|
+
|
|
64
|
+
Sometimes the user will be on the specification page (openapi, asyncapi, graphql) for a resource too
|
|
65
|
+
- /docs/services/OrdersService/0.0.3/asyncapi/order-service-asyncapi -> id: OrdersService, version: 0.0.3, collection: services, schema (specification): asyncapi
|
|
66
|
+
- /docs/services/OrdersService/0.0.3/spec/openapi-v2 -> id: OrdersService, version: 0.0.3, collection: services, schema (specification): openapi
|
|
26
67
|
|
|
27
|
-
Your primary goal is to help users understand their
|
|
68
|
+
Your primary goal is to help users understand their software architecture through accurate documentation interpretation.
|
|
69
|
+
Use the tools provided to get the context you need to an answer the question.
|
|
70
|
+
|
|
71
|
+
When responding:
|
|
72
|
+
1. Explain connections between resources when relevant.
|
|
73
|
+
2.. Use appropriate technical terminology.
|
|
74
|
+
3.. Use clear formatting with headings and bullet points when helpful.
|
|
75
|
+
4. State clearly when information is missing rather than making assumptions.
|
|
76
|
+
5. Don't provide code examples unless specifically requested.
|
|
77
|
+
6. When you refer to a resource in EventCatalog, try and create a link to the resource in the response
|
|
78
|
+
- Example, if you return a message in the text, rather than than just the id or version you should return a markdown link to the resource e.g [MyEvent - 1.0.0](/docs/events/MyEvent/1.0.0)
|
|
79
|
+
- NEVER return "latest" in the markdown link, always use the specific version
|
|
80
|
+
- The link options are:
|
|
81
|
+
- If you want to get the documentation for a resource use the /docs/ prefix (e.g /docs/{collection}/{id}/{version})
|
|
82
|
+
- If you want to let the user know they can visualize a resource use the /visualiser/ prefix (e.g /visualiser/{collection}/{id}/{version})
|
|
83
|
+
- If you want to let the user know they can see the architecture of a resource use the /architecture/ prefix (e.g /architecture/{collection}/{id}/{version})
|
|
84
|
+
7. When you return a schema, use code blocks to render the schema to the user too, for example if the schema is in JSON format use \`\`\`json and if the schema is in YAML format use \`\`\`yaml
|
|
28
85
|
|
|
29
86
|
If you have additional context, use it to answer the question.`;
|
|
30
87
|
|
|
@@ -43,17 +100,163 @@ export const GET = async ({ request }: APIContext<{ question: string; messages:
|
|
|
43
100
|
export const POST = async ({ request }: APIContext<{ question: string; messages: Message[]; additionalContext?: string }>) => {
|
|
44
101
|
const { messages }: { messages: UIMessage[] } = await request.json();
|
|
45
102
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
103
|
+
if (!isEventCatalogScaleEnabled()) {
|
|
104
|
+
return new Response(JSON.stringify({ error: 'Chat is not enabled, please upgrade to the scale plan to use this feature' }), {
|
|
105
|
+
status: 403,
|
|
106
|
+
headers: { 'Content-Type': 'application/json' },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!hasChatConfiguration) {
|
|
111
|
+
return new Response(JSON.stringify({ error: 'No chat configuration found' }), {
|
|
112
|
+
status: 404,
|
|
113
|
+
headers: { 'Content-Type': 'application/json' },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Get the URL of the request
|
|
118
|
+
const referrer = request.headers.get('referer');
|
|
119
|
+
|
|
120
|
+
const result = await streamText({
|
|
121
|
+
model,
|
|
122
|
+
system: getBaseSystemPrompt(referrer ?? ''),
|
|
123
|
+
messages: convertToModelMessages(messages) as ModelMessage[],
|
|
124
|
+
temperature: modelConfiguration?.temperature ?? 0.7,
|
|
125
|
+
stopWhen: stepCountIs(5),
|
|
126
|
+
// maxTokens: 4000, // Increased to handle large tool results
|
|
127
|
+
onError: (error) => {
|
|
128
|
+
console.error('[Chat] On error', error);
|
|
129
|
+
},
|
|
130
|
+
// maxOutputTokens: 40000,
|
|
131
|
+
// tools: tools,
|
|
132
|
+
tools: {
|
|
133
|
+
getResources: tool({
|
|
134
|
+
description:
|
|
135
|
+
'Use this tool to get events, services, commands, queries, flows, domains, channels, entities from EventCatalog',
|
|
136
|
+
inputSchema: z.object({
|
|
137
|
+
collection: z
|
|
138
|
+
.enum(['events', 'services', 'commands', 'queries', 'flows', 'domains', 'channels', 'entities'])
|
|
139
|
+
.describe('The collection to get the events from'),
|
|
140
|
+
}),
|
|
141
|
+
execute: async ({ collection }) => {
|
|
142
|
+
const resources = await getCollection(collection as any);
|
|
143
|
+
return resources.map((resource: any) => ({
|
|
144
|
+
id: resource.data.id,
|
|
145
|
+
version: resource.data.version,
|
|
146
|
+
name: resource.data.name,
|
|
147
|
+
}));
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
getResource: tool({
|
|
151
|
+
description: 'Use this tool to get a specific resource from EventCatalog by its id and version',
|
|
152
|
+
inputSchema: z.object({
|
|
153
|
+
collection: z
|
|
154
|
+
.enum(['events', 'services', 'commands', 'queries', 'flows', 'domains', 'channels', 'entities'])
|
|
155
|
+
.describe('The collection to get the events from'),
|
|
156
|
+
id: z.string().describe('The id of the resource to get'),
|
|
157
|
+
version: z.string().describe('The version of the resource to get'),
|
|
158
|
+
}),
|
|
159
|
+
execute: async ({ collection, id, version }) => {
|
|
160
|
+
const resource = await getEntry(collection as any, `${id}-${version}`);
|
|
161
|
+
return resource;
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
getMessagesProducedOrConsumedByResource: tool({
|
|
165
|
+
description:
|
|
166
|
+
'Use this tool to get the messages produced or consumed by a resource by its id and version. Look at the `sends` and `receives` properties to get the messages produced or consumed by the resource',
|
|
167
|
+
inputSchema: z.object({
|
|
168
|
+
resourceId: z.string().describe('The id of the resource to get the messages produced or consumed for'),
|
|
169
|
+
resourceVersion: z.string().describe('The version of the resource to get the messages produced or consumed for'),
|
|
170
|
+
resourceCollection: z
|
|
171
|
+
.enum(['services', 'events', 'commands', 'queries', 'flows', 'domains', 'channels', 'entities'])
|
|
172
|
+
.describe('The collection of the resource to get the messages produced or consumed for')
|
|
173
|
+
.default('services'),
|
|
174
|
+
}),
|
|
175
|
+
execute: async ({ resourceId, resourceVersion, resourceCollection }) => {
|
|
176
|
+
const resource = await getEntry(resourceCollection as any, `${resourceId}-${resourceVersion}`);
|
|
177
|
+
if (!resource) {
|
|
178
|
+
return {
|
|
179
|
+
error: `Resource not found with id ${resourceId} and version ${resourceVersion} and collection ${resourceCollection}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return resource;
|
|
183
|
+
},
|
|
184
|
+
}),
|
|
185
|
+
getProducerAndConsumerForMessage: tool({
|
|
186
|
+
description: 'Use this tool to get the producers and consumers for a message by its id and version',
|
|
187
|
+
inputSchema: z.object({
|
|
188
|
+
messageId: z.string().describe('The id of the message to get the producers and consumers for'),
|
|
189
|
+
messageVersion: z.string().describe('The version of the message to get the producers and consumers for'),
|
|
190
|
+
messageCollection: z
|
|
191
|
+
.enum(['events', 'commands', 'queries'])
|
|
192
|
+
.describe('The collection of the message to get the producers and consumers for')
|
|
193
|
+
.default('events'),
|
|
194
|
+
}),
|
|
195
|
+
execute: async ({ messageId, messageVersion, messageCollection }) => {
|
|
196
|
+
const services = await getCollection('services');
|
|
197
|
+
const message = await getEntry(messageCollection as any, `${messageId}-${messageVersion}`);
|
|
198
|
+
const consumers = await getProducersOfMessage(services, message as any);
|
|
199
|
+
return consumers;
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
getConsumersOfMessage: tool({
|
|
203
|
+
description: 'Use this tool to get the consumers for a message by its id and version',
|
|
204
|
+
inputSchema: z.object({
|
|
205
|
+
messageId: z.string().describe('The id of the message to get the consumers for'),
|
|
206
|
+
messageVersion: z.string().describe('The version of the message to get the consumers for'),
|
|
207
|
+
messageCollection: z
|
|
208
|
+
.enum(['events', 'commands', 'queries'])
|
|
209
|
+
.describe('The collection of the message to get the consumers for')
|
|
210
|
+
.default('events'),
|
|
211
|
+
}),
|
|
212
|
+
execute: async ({ messageId, messageVersion, messageCollection }) => {
|
|
213
|
+
const services = await getCollection('services');
|
|
214
|
+
const message = await getEntry(messageCollection as any, `${messageId}-${messageVersion}`);
|
|
215
|
+
const consumers = await getConsumersOfMessage(services, message as any);
|
|
216
|
+
return consumers;
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
getSchemaForResource: tool({
|
|
220
|
+
description:
|
|
221
|
+
'Use this tool to get the schema or specifications (openapi or asyncapi or graphql) for a resource by its id and version, you will use code blocks to render the schema to the user too',
|
|
222
|
+
inputSchema: z.object({
|
|
223
|
+
resourceId: z.string().describe('The id of the resource to get the schema for'),
|
|
224
|
+
resourceVersion: z.string().describe('The version of the resource to get the schema for'),
|
|
225
|
+
resourceCollection: z
|
|
226
|
+
.enum(['services', 'events', 'commands', 'queries', 'flows', 'domains', 'channels', 'entities'])
|
|
227
|
+
.describe('The collection of the resource to get the schema for')
|
|
228
|
+
.default('services'),
|
|
229
|
+
}),
|
|
230
|
+
execute: async ({ resourceId, resourceVersion, resourceCollection }) => {
|
|
231
|
+
const resource = await getEntry(resourceCollection as any, `${resourceId}-${resourceVersion}`);
|
|
54
232
|
|
|
55
|
-
|
|
56
|
-
|
|
233
|
+
if (!resource) {
|
|
234
|
+
return {
|
|
235
|
+
error: `Resource not found with id ${resourceId} and version ${resourceVersion} and collection ${resourceCollection}`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const schema = await getSchemasFromResource(resource);
|
|
240
|
+
|
|
241
|
+
// If we have any schemas back then read them and return them
|
|
242
|
+
if (schema.length > 0) {
|
|
243
|
+
return schema.map((schemaItem) => ({
|
|
244
|
+
url: schemaItem.url,
|
|
245
|
+
format: schemaItem.format,
|
|
246
|
+
code: fs.readFileSync(schemaItem.url, 'utf-8'),
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return [];
|
|
251
|
+
},
|
|
252
|
+
}),
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
return result.toUIMessageStreamResponse({
|
|
256
|
+
headers: {
|
|
257
|
+
'Content-Type': 'text/event-stream',
|
|
258
|
+
},
|
|
259
|
+
});
|
|
57
260
|
};
|
|
58
261
|
|
|
59
262
|
export const prerender = false;
|
|
@@ -160,14 +160,6 @@ const premiumFeatures = [
|
|
|
160
160
|
current: currentPath.includes('/docs/custom'),
|
|
161
161
|
isPremium: true,
|
|
162
162
|
},
|
|
163
|
-
{
|
|
164
|
-
id: '/chat',
|
|
165
|
-
label: 'EventCatalog Chat',
|
|
166
|
-
icon: BotMessageSquare,
|
|
167
|
-
href: buildUrl('/chat'),
|
|
168
|
-
current: currentPath.includes('/chat'),
|
|
169
|
-
isPremium: true,
|
|
170
|
-
},
|
|
171
163
|
].filter((item) => {
|
|
172
164
|
const userSideBarOption = userSideBarConfiguration.find((config: { id: string; visible: boolean }) => config.id === item.id);
|
|
173
165
|
return userSideBarOption ? userSideBarOption.visible : true;
|
|
@@ -2,6 +2,7 @@ import type { CollectionEntry } from 'astro:content';
|
|
|
2
2
|
import type { PageTypes } from '@types';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { getAbsoluteFilePathForAstroFile } from '@utils/files';
|
|
5
6
|
|
|
6
7
|
export type Schema = {
|
|
7
8
|
url: string;
|
|
@@ -13,12 +14,23 @@ export const getSchemaURL = (resource: CollectionEntry<PageTypes>) => {
|
|
|
13
14
|
const publicPath = resource?.catalog?.publicPath;
|
|
14
15
|
const schemaFilePath = resource?.data?.schemaPath;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
// No schema file path, return an empty string
|
|
18
|
+
if (!schemaFilePath) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!publicPath) {
|
|
23
|
+
// Then we try and get the absolute file path from the resource
|
|
24
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', schemaFilePath ?? '');
|
|
25
|
+
if (absoluteFilePath) {
|
|
26
|
+
return absoluteFilePath;
|
|
27
|
+
}
|
|
28
|
+
// Can't find the schema file, return an empty string
|
|
17
29
|
return;
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
// new URL
|
|
21
|
-
return path.join(publicPath, schemaFilePath);
|
|
33
|
+
return path.join(publicPath, schemaFilePath ?? '');
|
|
22
34
|
};
|
|
23
35
|
|
|
24
36
|
export const getSchemaFormatFromURL = (url: string) => {
|
|
@@ -43,17 +55,29 @@ export const getSchemasFromResource = (resource: CollectionEntry<PageTypes>): Sc
|
|
|
43
55
|
? specifications.find((spec) => spec.type === 'openapi')?.path
|
|
44
56
|
: specifications?.openapiPath;
|
|
45
57
|
// @ts-ignore
|
|
46
|
-
|
|
58
|
+
let publicPath = resource?.catalog?.publicPath;
|
|
47
59
|
const schemas = [];
|
|
48
60
|
|
|
49
61
|
if (asyncapiPath) {
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
if (!publicPath) {
|
|
63
|
+
// We try and get the absoulate file path from the resource
|
|
64
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', asyncapiPath ?? '');
|
|
65
|
+
schemas.push({ url: buildUrl(absoluteFilePath), format: 'asyncapi' });
|
|
66
|
+
} else {
|
|
67
|
+
// The resource has the public path, so we can use it to build the URL
|
|
68
|
+
schemas.push({ url: buildUrl(path.join(publicPath, asyncapiPath)), format: 'asyncapi' });
|
|
69
|
+
}
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
if (openapiPath) {
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
if (!publicPath) {
|
|
74
|
+
// We try and get the absoulate file path from the resource
|
|
75
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', openapiPath ?? '');
|
|
76
|
+
schemas.push({ url: buildUrl(absoluteFilePath), format: 'openapi' });
|
|
77
|
+
} else {
|
|
78
|
+
// The resource has the public path, so we can use it to build the URL
|
|
79
|
+
schemas.push({ url: buildUrl(path.join(publicPath, openapiPath)), format: 'openapi' });
|
|
80
|
+
}
|
|
57
81
|
}
|
|
58
82
|
|
|
59
83
|
return schemas;
|
|
@@ -39,9 +39,12 @@ export const showCustomBranding = () => {
|
|
|
39
39
|
export const isChangelogEnabled = () => config?.changelog?.enabled ?? false;
|
|
40
40
|
|
|
41
41
|
export const isCustomDocsEnabled = () => isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
|
|
42
|
+
|
|
42
43
|
export const isEventCatalogChatEnabled = () => {
|
|
43
44
|
const isFeatureEnabledFromPlan = isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
|
|
44
|
-
|
|
45
|
+
const directory = process.env.PROJECT_DIR || process.cwd();
|
|
46
|
+
const hasChatConfigurationFile = fs.existsSync(join(directory, 'eventcatalog.chat.js'));
|
|
47
|
+
return isFeatureEnabledFromPlan && hasChatConfigurationFile && isSSR();
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
export const isEventCatalogUpgradeEnabled = () => !isEventCatalogStarterEnabled() && !isEventCatalogScaleEnabled();
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/event-catalog/eventcatalog.git"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"version": "3.0.0-beta.
|
|
9
|
+
"version": "3.0.0-beta.19",
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
@@ -21,12 +21,9 @@
|
|
|
21
21
|
"default-files-for-collections/"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ai-sdk/anthropic": "^2.0.23",
|
|
25
|
-
"@ai-sdk/google": "^2.0.17",
|
|
26
|
-
"@ai-sdk/openai": "^2.0.42",
|
|
27
24
|
"@ai-sdk/react": "^2.0.60",
|
|
28
|
-
"@astrojs/markdown-remark": "^6.3.
|
|
29
|
-
"@astrojs/mdx": "^4.3.
|
|
25
|
+
"@astrojs/markdown-remark": "^6.3.10",
|
|
26
|
+
"@astrojs/mdx": "^4.3.13",
|
|
30
27
|
"@astrojs/node": "^9.5.1",
|
|
31
28
|
"@astrojs/react": "^4.4.2",
|
|
32
29
|
"@astrojs/rss": "^4.0.14",
|
|
@@ -57,7 +54,7 @@
|
|
|
57
54
|
"@tanstack/react-table": "^8.17.3",
|
|
58
55
|
"@xyflow/react": "^12.3.6",
|
|
59
56
|
"ai": "^5.0.60",
|
|
60
|
-
"astro": "^5.16.
|
|
57
|
+
"astro": "^5.16.5",
|
|
61
58
|
"astro-compress": "^2.3.8",
|
|
62
59
|
"astro-expressive-code": "^0.41.3",
|
|
63
60
|
"astro-seo": "^0.8.4",
|
|
@@ -76,14 +73,14 @@
|
|
|
76
73
|
"glob": "^10.5.0",
|
|
77
74
|
"gray-matter": "^4.0.3",
|
|
78
75
|
"html-to-image": "^1.11.11",
|
|
79
|
-
"js-yaml": "^4.1.
|
|
76
|
+
"js-yaml": "^4.1.1",
|
|
80
77
|
"jsonpath": "^1.1.1",
|
|
81
78
|
"jsonwebtoken": "^9.0.2",
|
|
82
79
|
"lodash.debounce": "^4.0.8",
|
|
83
80
|
"lodash.merge": "4.6.2",
|
|
84
81
|
"lucide-react": "^0.453.0",
|
|
85
82
|
"marked": "^15.0.6",
|
|
86
|
-
"mermaid": "^11.
|
|
83
|
+
"mermaid": "^11.12.1",
|
|
87
84
|
"nanostores": "^1.1.0",
|
|
88
85
|
"pagefind": "^1.3.0",
|
|
89
86
|
"pako": "^2.1.0",
|
|
@@ -94,13 +91,14 @@
|
|
|
94
91
|
"react-syntax-highlighter": "^16.1.0",
|
|
95
92
|
"rehype-autolink-headings": "^7.1.0",
|
|
96
93
|
"rehype-expressive-code": "^0.41.3",
|
|
94
|
+
"rehype-raw": "^7.0.0",
|
|
97
95
|
"rehype-slug": "^6.0.0",
|
|
98
96
|
"remark-comment": "^1.0.0",
|
|
99
97
|
"remark-directive": "^3.0.0",
|
|
100
98
|
"remark-gfm": "^3.0.1",
|
|
101
99
|
"rimraf": "^5.0.7",
|
|
102
100
|
"semver": "7.6.3",
|
|
103
|
-
"shelljs": "^0.
|
|
101
|
+
"shelljs": "^0.9.0",
|
|
104
102
|
"tailwindcss": "^3.4.3",
|
|
105
103
|
"tw-animate-css": "^1.4.0",
|
|
106
104
|
"typescript": "^5.4.5",
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import Sidebar from './ChatSidebar';
|
|
2
|
-
import { ChatProvider } from './hooks/ChatProvider';
|
|
3
|
-
import ChatWindowServer from './windows/ChatWindow.server';
|
|
4
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
-
import type { ChatPromptCategoryGroup } from '@enterprise/eventcatalog-chat/utils/chat-prompts';
|
|
6
|
-
interface Resource {
|
|
7
|
-
id: string;
|
|
8
|
-
type: string;
|
|
9
|
-
name: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Chat component has two modes:
|
|
14
|
-
* - Server: ChatWindow.server.tsx (uses server-side code, bring your own API key)
|
|
15
|
-
*
|
|
16
|
-
* The mode is determined by the config.output property in the eventcatalog.config.js file.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const Chat = ({
|
|
20
|
-
chatConfig,
|
|
21
|
-
resources,
|
|
22
|
-
chatPrompts,
|
|
23
|
-
output,
|
|
24
|
-
}: {
|
|
25
|
-
chatConfig: any;
|
|
26
|
-
resources: Resource[];
|
|
27
|
-
chatPrompts: ChatPromptCategoryGroup[];
|
|
28
|
-
output: 'static' | 'server';
|
|
29
|
-
}) => {
|
|
30
|
-
const queryClient = new QueryClient();
|
|
31
|
-
|
|
32
|
-
if (output !== 'server') {
|
|
33
|
-
return (
|
|
34
|
-
// Message to turn on server side
|
|
35
|
-
<div className="flex justify-center items-center h-full bg-gray-100 p-4 rounded-lg text-center">
|
|
36
|
-
<div className="space-y-4">
|
|
37
|
-
<h1 className="text-3xl font-semibold text-gray-800">Chat is only supported on server side</h1>
|
|
38
|
-
<p className="text-sm text-gray-500">
|
|
39
|
-
Please switch to server side by setting the <code className="font-mono bg-gray-100 p-0.5 rounded">output</code>{' '}
|
|
40
|
-
property to <code className="font-mono bg-gray-100 p-0.5 rounded">server</code> in your{' '}
|
|
41
|
-
<code className="font-mono bg-gray-100 p-0.5 rounded">eventcatalog.config.js</code> file.
|
|
42
|
-
</p>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<ChatProvider>
|
|
50
|
-
<div className="flex overflow-hidden w-full">
|
|
51
|
-
<Sidebar />
|
|
52
|
-
<QueryClientProvider client={queryClient}>
|
|
53
|
-
<ChatWindowServer {...chatConfig} resources={resources} chatPrompts={chatPrompts} />
|
|
54
|
-
</QueryClientProvider>
|
|
55
|
-
</div>
|
|
56
|
-
</ChatProvider>
|
|
57
|
-
);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export default Chat;
|