@eventcatalog/core 2.60.0 → 2.61.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.
Files changed (33) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-WASYSRBV.js → chunk-3CAAQQUB.js} +1 -1
  6. package/dist/{chunk-QMWI6M2I.js → chunk-I2B6B3XZ.js} +1 -1
  7. package/dist/{chunk-6FRDYEMA.js → chunk-OEWPHKD6.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +2 -2
  11. package/dist/eventcatalog.js +4 -4
  12. package/eventcatalog/astro.config.mjs +1 -0
  13. package/eventcatalog/src/enterprise/eventcatalog-chat/components/Chat.tsx +19 -9
  14. package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatMessage.tsx +227 -69
  15. package/eventcatalog/src/enterprise/eventcatalog-chat/components/hooks/ChatProvider.tsx +4 -3
  16. package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.server.tsx +46 -176
  17. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/chat.ts +59 -0
  18. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +0 -37
  19. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +83 -4
  20. package/eventcatalog/src/middleware-auth.ts +13 -2
  21. package/eventcatalog/src/pages/chat/feature.astro +24 -16
  22. package/eventcatalog/src/pages/docs/[type]/[id]/[version].mdx.ts +22 -11
  23. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +3 -3
  24. package/eventcatalog/src/utils/feature.ts +2 -1
  25. package/eventcatalog/src/utils/url-builder.ts +9 -0
  26. package/package.json +7 -10
  27. package/eventcatalog/src/enterprise/eventcatalog-chat/EventCatalogVectorStore.ts +0 -66
  28. package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.client.tsx +0 -540
  29. package/eventcatalog/src/enterprise/eventcatalog-chat/components/workers/document-importer.ts +0 -38
  30. package/eventcatalog/src/enterprise/eventcatalog-chat/components/workers/engine.ts +0 -7
  31. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/ai/chat.ts +0 -54
  32. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/ai/resources.ts +0 -42
  33. package/eventcatalog/src/enterprise/eventcatalog-chat/utils/ai.ts +0 -112
@@ -1,540 +0,0 @@
1
- import { useEffect, useState, useRef, useCallback, useMemo } from 'react';
2
- import { BookOpen, Send } from 'lucide-react';
3
- import { CreateWebWorkerMLCEngine, type InitProgressReport } from '@mlc-ai/web-llm';
4
- import { useChat, type Message } from '../hooks/ChatProvider';
5
- import React from 'react';
6
- // Update Message type to include resources
7
- interface Resource {
8
- id: string;
9
- type: string;
10
- url: string;
11
- title?: string;
12
- }
13
-
14
- // Move formatMessageContent outside component since it doesn't use any component state or props
15
- const formatMessageContent = (content: string, resources?: Resource[]): string => {
16
- // First handle any full resource tags by replacing them with just their ID/title
17
- content = content.replace(/<resource[^>]*?id="([^"]*)"[^>]*?>/g, '$1');
18
-
19
- // First escape <resource> tags
20
- let formattedContent = content.replace(/<resource[^>]*>/g, (match) => {
21
- return match.replace(/</g, '&lt;').replace(/>/g, '&gt;');
22
- });
23
-
24
- // If we have resources, convert matching IDs to links
25
- if (resources?.length) {
26
- // Create a regex pattern that matches any resource ID/title
27
- const resourceMatches = resources.map((r) => ({
28
- pattern: r.title || r.id,
29
- url: r.url,
30
- type: r.type,
31
- }));
32
-
33
- // Sort by length (longest first) to prevent partial matches
34
- resourceMatches.sort((a, b) => b.pattern.length - a.pattern.length);
35
-
36
- // Replace matches with links, but skip if already inside an HTML tag
37
- for (const { pattern, url, type } of resourceMatches) {
38
- // Updated regex to match whole words only using word boundaries \b
39
- const regex = new RegExp(`(?<!<[^>]*)\\b(${pattern})\\b(?![^<]*>)`, 'g');
40
- formattedContent = formattedContent.replace(
41
- regex,
42
- `<a href="${url}" class="text-purple-600 hover:text-purple-800" target="_blank" rel="noopener noreferrer">$1 (${type})</a>`
43
- );
44
- }
45
- }
46
-
47
- // Handle code blocks
48
- formattedContent = formattedContent.replace(/```([\s\S]*?)```/g, (match, codeContent) => {
49
- const escapedCode = codeContent.trim().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
50
- return `<pre class="bg-gray-800 border border-gray-700 p-4 my-3 rounded-lg overflow-x-auto"><code class="text-sm font-mono text-gray-200">${escapedCode}</code></pre>`;
51
- });
52
-
53
- // Handle inline code
54
- formattedContent = formattedContent.replace(/(?<!`)`([^`]+)`(?!`)/g, (match, code) => {
55
- const escapedCode = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
56
- return `<code class="bg-gray-500 border border-gray-700 px-2 py-0.5 rounded text-sm font-mono text-gray-200">${escapedCode}</code>`;
57
- });
58
-
59
- // Handle bold text with double asterisks
60
- formattedContent = formattedContent.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
61
-
62
- // Convert newlines to <br>
63
- formattedContent = formattedContent.replace(/\n(?!<\/code>)/g, '<br>');
64
-
65
- return formattedContent;
66
- };
67
-
68
- // Create a memoized Message component
69
- const ChatMessage = React.memo(({ message }: { message: Message }) => (
70
- <div className={`flex ${message.isUser ? 'justify-end' : 'justify-start'} mb-4`}>
71
- <div
72
- className={`max-w-[80%] rounded-lg p-3 ${
73
- message.isUser ? 'bg-purple-600 text-white rounded-br-none' : 'bg-gray-100 text-gray-800 rounded-bl-none'
74
- }`}
75
- >
76
- <div dangerouslySetInnerHTML={{ __html: formatMessageContent(message.content, message.resources) }} />
77
- {!message.isUser && message.resources && message.resources.length > 0 && (
78
- <div className="mt-3 pt-3 border-t border-gray-200">
79
- <p className="text-xs text-gray-500 mb-1">Referenced Resources:</p>
80
- <div className="text-[10px]">
81
- {message.resources.map((resource, idx) => (
82
- <span key={resource.id}>
83
- <a
84
- href={resource.url}
85
- className="text-purple-600 hover:text-purple-800"
86
- target="_blank"
87
- rel="noopener noreferrer"
88
- >
89
- {resource.title || resource.id} ({resource.type})
90
- </a>
91
- {idx < (message.resources?.length || 0) - 1 ? ', ' : ''}
92
- </span>
93
- ))}
94
- </div>
95
- </div>
96
- )}
97
- </div>
98
- </div>
99
- ));
100
-
101
- ChatMessage.displayName = 'ChatMessage';
102
-
103
- interface ChatWindowProps {
104
- model?: string;
105
- max_tokens?: number;
106
- similarityResults?: number;
107
- }
108
-
109
- const ChatWindow = ({
110
- model = 'Hermes-3-Llama-3.2-3B-q4f16_1-MLC',
111
- max_tokens = 4096,
112
- similarityResults = 50,
113
- }: ChatWindowProps) => {
114
- const [loading, setLoading] = useState(true);
115
- const [loadingProgress, setLoadingProgress] = useState(0);
116
- const [engine, setEngine] = useState<any>(null);
117
- const [messages, setMessages] = useState<Array<Message>>([]);
118
- const [inputValue, setInputValue] = useState('');
119
- const [showWelcome, setShowWelcome] = useState(true);
120
- const [vectorWorker, setVectorWorker] = useState<Worker | null>(null);
121
- const [isThinking, setIsThinking] = useState(false);
122
- const completionRef = useRef<any>(null);
123
- const outputRef = useRef<HTMLDivElement>(null);
124
- const [sendDefaultQuestionToLLM, setSendDefaultQuestionToLLM] = useState(false);
125
-
126
- const { currentSession, storeMessagesToSession, updateSession, isStreaming, setIsStreaming } = useChat();
127
-
128
- // Load messages when session changes
129
- // Load messages when session changes
130
- useEffect(() => {
131
- if (currentSession) {
132
- console.log('currentSession', currentSession.messages);
133
- setMessages(currentSession.messages);
134
- setShowWelcome(false);
135
- } else {
136
- setMessages([]);
137
- setShowWelcome(true);
138
- }
139
- }, [currentSession]);
140
-
141
- // If the messages change add them to the session
142
- useEffect(() => {
143
- if (currentSession) {
144
- storeMessagesToSession(currentSession.id, messages);
145
- }
146
- }, [messages]);
147
-
148
- // Helper function to stop the current completion
149
- const handleStop = useCallback(async () => {
150
- if (completionRef.current) {
151
- try {
152
- await engine.interruptGenerate();
153
- completionRef.current = null;
154
- setIsStreaming(false);
155
- setIsThinking(false);
156
- } catch (error) {
157
- console.error('Error stopping completion:', error);
158
- }
159
- }
160
- }, [engine]);
161
-
162
- const handleSubmit = useCallback(async () => {
163
- console.log('handleSubmit', inputValue);
164
- if (!inputValue.trim() || !engine) return;
165
-
166
- // Add to messages
167
- setMessages((prev) => [...prev, { content: inputValue, isUser: true, timestamp: Date.now() }]);
168
-
169
- setIsThinking(true);
170
- setIsStreaming(true);
171
- setInputValue('');
172
-
173
- // if the first message, update the session title
174
- if (currentSession) {
175
- updateSession({
176
- ...currentSession,
177
- title: inputValue.length > 25 ? `${inputValue.substring(0, 22)}...` : inputValue,
178
- });
179
- }
180
-
181
- // Add input to vector store
182
- vectorWorker?.postMessage({ input: inputValue, similarityResults });
183
-
184
- // @ts-ignore
185
- vectorWorker.onmessage = async (event) => {
186
- if (event.data.action === 'search-results') {
187
- console.log('Results', event?.data?.results);
188
-
189
- // Extract resources from results and ensure uniqueness by ID
190
- const resources = Array.from(
191
- new Map(
192
- event.data.results.map((result: any) => {
193
- const metadata = result[0].metadata;
194
- const resource: Resource = {
195
- id: metadata.id,
196
- type: metadata.type,
197
- url: `/docs/${metadata.type}s/${metadata.id}`,
198
- title: metadata.title || metadata.id,
199
- };
200
- return [metadata.id, resource]; // Use ID as key for Map
201
- })
202
- ).values()
203
- );
204
-
205
- console.log('resources', resources);
206
-
207
- const qaPrompt = `\n".
208
-
209
- You are an expert in event-driven architecture and domain-driven design, specializing in documentation for EventCatalog.
210
-
211
- You assist developers, architects, and business stakeholders who need information about their event-driven system catalog. You help with questions about:
212
- - Events (asynchronous messages that notify about something that has happened)
213
- - Commands (requests to perform an action)
214
- - Queries (requests for information)
215
- - Services (bounded contexts or applications that produce/consume events)
216
- - Domains (business capabilities or functional areas)
217
-
218
- IMPORTANT RULES:
219
- 1. Resources will be provided to you in <resource> tags. ONLY use these resources to answer questions.
220
- 2. NEVER include ANY <resource> tags in your responses. This is a strict requirement.
221
- 3. ALWAYS refer to resources by their name/ID/title attributes only.
222
- 4. If asked about specific resource types (e.g., "What domains do we have?"), simply list their names without elaboration.
223
- 5. NEVER invent or make up resources that aren't provided to you.
224
-
225
- RESPONSE FORMAT EXAMPLES:
226
- ✓ CORRECT: "The SubscriptionService produces the UserSubscribed event."
227
- ✗ INCORRECT: "<resource id="SubscriptionService">...</resource> produces events."
228
-
229
- When responding:
230
- 1. Use only information from the provided resources
231
- 2. Explain connections between resources when relevant
232
- 3. Use appropriate technical terminology
233
- 4. Use clear formatting with headings and bullet points when helpful
234
- 5. State clearly when information is missing rather than making assumptions
235
- 6. Don't provide code examples unless specifically requested
236
-
237
- Your primary goal is to help users understand their event-driven system through accurate documentation interpretation.
238
-
239
- ==========
240
- ${(resources as Resource[])
241
- .map((resource: Resource) => {
242
- return `<resource ${Object.entries(resource)
243
- .filter(([key, value]) => key !== 'markdown' && key !== 'loc')
244
- .map(([key, value]) => {
245
- return `${key}="${value}"`;
246
- })
247
- .join(' ')} />`;
248
- })
249
- .join('\n')}\n
250
- ==========
251
-
252
- ""
253
- `;
254
-
255
- console.log('qaPrompt', qaPrompt);
256
- try {
257
- // Get completion
258
- const completion = await engine.chat.completions.create({
259
- messages: [
260
- {
261
- role: 'system',
262
- content: qaPrompt,
263
- },
264
- // previous messages
265
- ...messages.map((msg) => ({
266
- role: msg.isUser ? 'user' : 'assistant',
267
- content: msg.content,
268
- })),
269
- {
270
- role: 'user',
271
- content: inputValue,
272
- },
273
- ],
274
- stream: true,
275
- temperature: 0.1,
276
- max_tokens,
277
- top_p: 0.9,
278
- top_k: 40,
279
- frequency_penalty: 0.1,
280
- presence_penalty: 0,
281
- });
282
-
283
- // Store completion reference for potential cancellation
284
- completionRef.current = completion;
285
-
286
- let isFirstChunk = true;
287
- let responseText = '';
288
-
289
- try {
290
- for await (const chunk of completion) {
291
- const content = chunk.choices[0]?.delta?.content || '';
292
- if (content) {
293
- responseText += content;
294
-
295
- if (isFirstChunk) {
296
- setIsThinking(false);
297
- setMessages((prev) => [
298
- ...prev,
299
- {
300
- content: responseText,
301
- isUser: false,
302
- timestamp: Date.now(),
303
- },
304
- ]);
305
- isFirstChunk = false;
306
- } else {
307
- setMessages((prev) => {
308
- const newMessages = [...prev];
309
- newMessages[newMessages.length - 1] = {
310
- ...newMessages[newMessages.length - 1],
311
- content: responseText,
312
- };
313
- return newMessages;
314
- });
315
- }
316
- scrollToBottom();
317
- }
318
- }
319
-
320
- // Add resources after streaming is complete
321
- setMessages((prev) => {
322
- const newMessages = [...prev];
323
- newMessages[newMessages.length - 1] = {
324
- ...newMessages[newMessages.length - 1],
325
- content: responseText,
326
- resources: resources as { id: string; type: string; url: string; title?: string }[],
327
- };
328
- return newMessages;
329
- });
330
- } catch (error: any) {
331
- if (error.message?.includes('cancelled')) {
332
- console.log('Completion was stopped by the user');
333
- } else {
334
- throw error;
335
- }
336
- }
337
-
338
- setIsThinking(false);
339
- setIsStreaming(false);
340
- completionRef.current = null;
341
- } catch (error: any) {
342
- console.error('Error:', error);
343
- const errorMessage = {
344
- content: 'Sorry, there was an error processing your request.',
345
- isUser: false,
346
- timestamp: Date.now(),
347
- };
348
- setMessages((prev) => [...prev, errorMessage]);
349
- setIsThinking(false);
350
- setIsStreaming(false);
351
- completionRef.current = null;
352
- }
353
- }
354
- };
355
- }, [inputValue, engine, messages, currentSession, vectorWorker]);
356
-
357
- const initProgressCallback = (report: InitProgressReport) => {
358
- console.log('Loading LLM locally', report);
359
- setLoadingProgress(Math.round(report.progress * 100));
360
- if (report.progress === 1) {
361
- setLoading(false);
362
- }
363
- };
364
-
365
- useEffect(() => {
366
- if (!loading && sendDefaultQuestionToLLM) {
367
- handleSubmit();
368
- setSendDefaultQuestionToLLM(false);
369
- }
370
- }, [sendDefaultQuestionToLLM, loading]);
371
-
372
- useEffect(() => {
373
- const initEngine = async () => {
374
- try {
375
- // Cache the LLMs text file
376
- const engineCreator = CreateWebWorkerMLCEngine;
377
- const newEngine = await engineCreator(
378
- new Worker(new URL('../workers/engine.ts', import.meta.url), { type: 'module' }),
379
- model,
380
- { initProgressCallback }
381
- );
382
- setEngine(newEngine);
383
- } catch (error) {
384
- console.error('Failed to initialize:', error);
385
- }
386
- };
387
-
388
- const importDocuments = async () => {
389
- const worker = new Worker(new URL('../workers/document-importer.ts', import.meta.url), { type: 'module' });
390
- worker.postMessage({ init: true });
391
- setVectorWorker(worker);
392
- };
393
-
394
- importDocuments();
395
- initEngine();
396
- }, []);
397
-
398
- // Add new function to handle smooth scrolling
399
- const scrollToBottom = useCallback((smooth = true) => {
400
- if (outputRef.current) {
401
- outputRef.current.scrollTo({
402
- top: outputRef.current.scrollHeight,
403
- behavior: smooth ? 'smooth' : 'auto',
404
- });
405
- }
406
- }, []);
407
-
408
- // Add effect to scroll when messages change
409
- useEffect(() => {
410
- scrollToBottom();
411
- }, [messages]);
412
-
413
- // Memoize static JSX elements
414
- const welcomeMessage = useMemo(
415
- () => (
416
- <div id="welcomeMessage" className="flex justify-center items-center h-full">
417
- <div className="text-center space-y-6 max-w-2xl px-4">
418
- <div className="flex justify-center">
419
- <BookOpen size={48} strokeWidth={1.5} className="text-gray-400" />
420
- </div>
421
- <div className="space-y-4">
422
- <h1 className="text-3xl font-semibold text-gray-800">Ask questions about your architecture</h1>
423
- <p className="text-sm text-gray-500">AI Models are local and do not leave your device.</p>
424
- </div>
425
- </div>
426
- </div>
427
- ),
428
- []
429
- );
430
-
431
- // Memoize the messages list with the new ChatMessage component
432
- const messagesList = useMemo(
433
- () => (
434
- <div className="space-y-4 max-w-[900px] mx-auto">
435
- {messages.map((message, index) => (
436
- <ChatMessage key={index} message={message} />
437
- ))}
438
- {isThinking && (
439
- <div className="flex justify-start mb-4">
440
- <div className="flex items-center space-x-2 max-w-[80%] rounded-lg p-3 bg-gray-100 text-gray-800 rounded-bl-none">
441
- <div className="flex space-x-1">
442
- <div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></div>
443
- <div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></div>
444
- <div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></div>
445
- </div>
446
- </div>
447
- </div>
448
- )}
449
- </div>
450
- ),
451
- [messages, isThinking]
452
- );
453
-
454
- // Memoize the input change handler
455
- const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
456
- setInputValue(e.target.value);
457
- }, []);
458
-
459
- // If we have an initial query, set the input value
460
- useEffect(() => {
461
- const urlParams = new URLSearchParams(window.location.search);
462
- const query = urlParams.get('query');
463
- if (query) {
464
- setInputValue(query);
465
- setSendDefaultQuestionToLLM(true);
466
- }
467
- }, []);
468
-
469
- return (
470
- <div className="flex-1 flex flex-col overflow-hidden h-[calc(100vh-60px)] w-full">
471
- {/* Chat Messages */}
472
- <div id="output" ref={outputRef} className="flex-1 overflow-y-auto p-4 space-y-4 w-full mx-auto">
473
- {showWelcome || messages.length === 0 ? welcomeMessage : messagesList}
474
- </div>
475
-
476
- {/* Loading Status */}
477
- {loading && (
478
- <div className="max-w-[900px] mx-auto w-full px-4">
479
- <div id="loadingStatus" className="mb-2 py-2 px-4 bg-gray-100 text-gray-700 rounded-lg text-center loading-status">
480
- <span className="block">
481
- Initializing AI model...
482
- {loadingProgress > 0 && `(${loadingProgress}%)`}
483
- </span>
484
- <span className="block text-xs text-gray-500">
485
- Loading model into your browser, this may take a minute or two. The first time it will take longer then the model is
486
- cached.
487
- </span>
488
- </div>
489
- </div>
490
- )}
491
-
492
- {/* Input Area */}
493
- <div className="border-t border-gray-200 p-4 bg-white">
494
- <div className="max-w-[900px] mx-auto relative">
495
- <input
496
- type="text"
497
- value={inputValue}
498
- onChange={handleInputChange}
499
- onKeyPress={(e) => {
500
- if (e.key === 'Enter' && !e.shiftKey) {
501
- e.preventDefault();
502
- handleSubmit();
503
- }
504
- }}
505
- placeholder="What events do we have?"
506
- className="w-full px-4 py-3 bg-white text-gray-800 rounded-lg border border-gray-200 focus:outline-none focus:border-purple-500 focus:ring-1 focus:ring-purple-500 disabled:bg-gray-50 disabled:cursor-not-allowed pr-24"
507
- disabled={loading || isStreaming}
508
- />
509
- <div className="absolute right-3 top-1/2 -translate-y-1/2">
510
- {isStreaming ? (
511
- <button
512
- onClick={handleStop}
513
- className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 text-sm font-medium"
514
- >
515
- Stop
516
- </button>
517
- ) : (
518
- <button
519
- onClick={handleSubmit}
520
- disabled={loading || isStreaming}
521
- className="px-4 py-2 flex items-center bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:bg-gray-200 disabled:cursor-not-allowed text-sm font-medium"
522
- >
523
- {/* Add icon */}
524
- <Send size={16} strokeWidth={1.5} className="mr-2" />
525
- Send
526
- </button>
527
- )}
528
- </div>
529
- </div>
530
- <div className="max-w-[900px] mx-auto flex justify-between">
531
- {/* show what model is loaded */}
532
- <p className="text-xs text-gray-400 mt-2">Model: {model}</p>
533
- <p className="text-xs text-gray-500 mt-2">EventCatalog Chat can make mistakes. Check important info.</p>
534
- </div>
535
- </div>
536
- </div>
537
- );
538
- };
539
-
540
- export default ChatWindow;
@@ -1,38 +0,0 @@
1
- import { HuggingFaceTransformersEmbeddings } from '@langchain/community/embeddings/huggingface_transformers';
2
- import { MemoryVectorStore } from 'langchain/vectorstores/memory';
3
-
4
- const embeddingsInstance = new HuggingFaceTransformersEmbeddings({ model: 'Xenova/all-MiniLM-L6-v2' });
5
-
6
- // Create the vector store
7
- const vectorStore = new MemoryVectorStore(embeddingsInstance);
8
-
9
- let documents: any;
10
- let embeddings: any;
11
-
12
- self.onmessage = async (event) => {
13
- try {
14
- // Initialize the vector store
15
- if (event?.data?.init && !documents && !embeddings) {
16
- const documentsImport = await fetch(`/ai/documents.json`);
17
- const embeddingsImport = await fetch(`/ai/embeddings.json`);
18
-
19
- documents = await documentsImport.json();
20
- embeddings = await embeddingsImport.json();
21
-
22
- await vectorStore.addVectors(embeddings, documents);
23
- }
24
-
25
- if (!event?.data?.input) {
26
- return;
27
- }
28
-
29
- // Get the results
30
- const results = await vectorStore.similaritySearchWithScore(event.data.input, event?.data?.similarityResults || 10);
31
- // Filter out results that don't have a score less than 0.5
32
- const filteredResults = results.filter((result) => result[1] > 0.1);
33
- postMessage({ results: filteredResults, action: 'search-results' });
34
- } catch (error) {
35
- console.log(error);
36
- self.postMessage({ error: (error as Error).message });
37
- }
38
- };
@@ -1,7 +0,0 @@
1
- import { WebWorkerMLCEngineHandler } from '@mlc-ai/web-llm';
2
-
3
- // Hookup an Engine to a worker handler
4
- const handler = new WebWorkerMLCEngineHandler();
5
- self.onmessage = (msg: MessageEvent) => {
6
- handler.onmessage(msg);
7
- };
@@ -1,54 +0,0 @@
1
- import type { APIContext } from 'astro';
2
- import { askQuestion } from '@enterprise/eventcatalog-chat/utils/ai';
3
-
4
- // Map the Keys to use in the SDK, astro exports as import.meta.env
5
- process.env.OPENAI_API_KEY = import.meta.env.OPENAI_API_KEY || '';
6
-
7
- interface Message {
8
- content: string;
9
- }
10
-
11
- export const GET = async ({ request }: APIContext<{ question: string; messages: Message[]; additionalContext?: string }>) => {
12
- // return 404
13
- return new Response(JSON.stringify({ error: 'Not found' }), {
14
- status: 404,
15
- headers: { 'Content-Type': 'application/json' },
16
- });
17
- };
18
-
19
- export const POST = async ({ request }: APIContext<{ question: string; messages: Message[]; additionalContext?: string }>) => {
20
- if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === '' || process.env.OPENAI_API_KEY === undefined) {
21
- return new Response(JSON.stringify({ error: 'OPENAI_API_KEY is not set' }), {
22
- status: 500,
23
- headers: { 'Content-Type': 'application/json' },
24
- });
25
- }
26
-
27
- try {
28
- const { question, messages, additionalContext } = await request.json();
29
-
30
- if (!question) {
31
- return new Response(JSON.stringify({ error: 'Question is required' }), {
32
- status: 400,
33
- headers: { 'Content-Type': 'application/json' },
34
- });
35
- }
36
-
37
- // Assuming askQuestion returns a ReadableStream
38
- const answerStream = await askQuestion(question, messages, additionalContext);
39
-
40
- return answerStream.toTextStreamResponse({
41
- headers: {
42
- 'Content-Encoding': 'none',
43
- },
44
- });
45
- } catch (error: any) {
46
- console.error('Error processing POST request:', error);
47
- return new Response(JSON.stringify({ error: 'Failed to process request: ' + error.message }), {
48
- status: 500,
49
- headers: { 'Content-Type': 'application/json' },
50
- });
51
- }
52
- };
53
-
54
- export const prerender = false;
@@ -1,42 +0,0 @@
1
- import type { APIContext } from 'astro';
2
- import { getResources } from '@enterprise/eventcatalog-chat/utils/ai';
3
-
4
- interface Message {
5
- content: string;
6
- }
7
-
8
- export const GET = async ({ request }: APIContext<{ question: string; messages: Message[]; additionalContext?: string }>) => {
9
- // return 404
10
- return new Response(JSON.stringify({ error: 'Not found' }), {
11
- status: 404,
12
- headers: { 'Content-Type': 'application/json' },
13
- });
14
- };
15
-
16
- export const POST = async ({ request }: APIContext<{ question: string; messages: Message[] }>) => {
17
- try {
18
- const { question } = await request.json();
19
-
20
- if (!question) {
21
- return new Response(JSON.stringify({ error: 'Question is required' }), {
22
- status: 400,
23
- headers: { 'Content-Type': 'application/json' },
24
- });
25
- }
26
- // // Assuming askQuestion returns a ReadableStream
27
- const resources = await getResources(question);
28
-
29
- return new Response(JSON.stringify({ resources }), {
30
- status: 200,
31
- headers: { 'Content-Type': 'application/json' },
32
- });
33
- } catch (error) {
34
- console.error('Error processing POST request:', error);
35
- return new Response(JSON.stringify({ error: 'Failed to process request' }), {
36
- status: 500,
37
- headers: { 'Content-Type': 'application/json' },
38
- });
39
- }
40
- };
41
-
42
- export const prerender = false;