Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit c7c7936

Browse files
authored
Merge pull request #256 from Cloud-Code-AI/255-ai-search-improvements
fix: updated the ai search version
2 parents 5535b94 + ec5d775 commit c7c7936

File tree

7 files changed

+439
-325
lines changed

7 files changed

+439
-325
lines changed

docs/src/app/aiSearch/page.tsx

Lines changed: 160 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,44 @@ import { generateEmbedding } from '@/lib/aisearch/embeddings'
2121
import { getDbWorker } from '@/lib/aisearch/dbWorker'
2222

2323
function cosineSimilarity(a: number[], b: number[]): number {
24-
if (a.some(isNaN) || b.some(isNaN)) {
25-
console.error("NaN values detected in vectors:");
26-
console.error("Vector A NaN indices:", a.map((val, i) => isNaN(val) ? i : null).filter(x => x !== null));
27-
console.error("Vector B NaN indices:", b.map((val, i) => isNaN(val) ? i : null).filter(x => x !== null));
28-
throw new Error("Invalid vectors containing NaN values");
29-
}
30-
31-
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
32-
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
33-
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
34-
return dotProduct / (magnitudeA * magnitudeB);
24+
if (a.some(isNaN) || b.some(isNaN)) {
25+
console.error("NaN values detected in vectors:");
26+
console.error("Vector A NaN indices:", a.map((val, i) => isNaN(val) ? i : null).filter(x => x !== null));
27+
console.error("Vector B NaN indices:", b.map((val, i) => isNaN(val) ? i : null).filter(x => x !== null));
28+
throw new Error("Invalid vectors containing NaN values");
29+
}
30+
31+
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
32+
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
33+
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
34+
return dotProduct / (magnitudeA * magnitudeB);
3535
}
3636

3737
export default function Home() {
3838
const [query, setQuery] = useState('')
3939
const [aiResponse, setAiResponse] = useState('')
40-
const [loaderText, setLoaderText] = useState('Loading database ...')
40+
const [loaderText, setLoaderText] = useState('Loading database ...')
4141
const [isLoading, setIsLoading] = useState(false)
4242
const [error, setError] = useState<string | null>(null)
4343
const recommendedArticles = getRecommendedArticles()
4444
const searchConfig = getSearchConfig()
4545
const headerConfig = getHeaderConfig()
4646
const config = getAkiradocsConfig()
4747
const [sources, setSources] = useState<Source[]>([])
48-
const handleGenerateEmbedding = useCallback(async (text: string) => {
49-
try {
50-
setIsLoading(true);
51-
// console.log("Loading model for embedding");
52-
const embedding = await generateEmbedding(text, (progress) => {});
53-
return embedding;
54-
} catch (error) {
55-
console.error('Error generating embedding:', error);
56-
throw error;
57-
}
58-
finally {
59-
setLoaderText('Searching database for relevant information ...')
60-
}
61-
}, []);
48+
const handleGenerateEmbedding = useCallback(async (text: string) => {
49+
try {
50+
setIsLoading(true);
51+
// console.log("Loading model for embedding");
52+
const embedding = await generateEmbedding(text, (progress) => { });
53+
return embedding;
54+
} catch (error) {
55+
console.error('Error generating embedding:', error);
56+
throw error;
57+
}
58+
finally {
59+
setLoaderText('Searching database for relevant information ...')
60+
}
61+
}, []);
6262

6363
// If AI Search is disabled, show the disabled message
6464
if (!config.navigation.header.items.find((item: any) => item.href === '/aiSearch')?.show) {
@@ -104,96 +104,98 @@ export default function Home() {
104104
setIsLoading(true)
105105
setError(null)
106106
setSources([])
107-
108-
const startTime = performance.now()
109-
110-
try {
111-
// Generate embedding for the query
112-
const queryEmbedding = await handleGenerateEmbedding(query);
113-
// console.log("Query embedding:", queryEmbedding)
114-
// Get database worker
115-
const worker = await getDbWorker();
116-
117-
// Get all documents
118-
const allDocs = await worker.db.query(`
107+
108+
const startTime = performance.now()
109+
110+
try {
111+
// Generate embedding for the query
112+
const queryEmbedding = await handleGenerateEmbedding(query);
113+
// console.log("Query embedding:", queryEmbedding)
114+
// Get database worker
115+
const worker = await getDbWorker();
116+
117+
// Get all documents
118+
const allDocs = await worker.db.query(`
119119
SELECT path, content, embedding
120120
FROM documents
121121
WHERE embedding IS NOT NULL
122122
`);
123123

124-
// Calculate similarity scores and filter results
125-
const similarityThreshold = 0.5;
126-
const scoredDocs = allDocs
127-
.map((doc: any) => {
128-
// Clean the embedding string and parse it
129-
const cleanEmbeddingStr = doc.embedding.replace(/[\[\]]/g, ''); // Remove square brackets
130-
const embeddingArray = cleanEmbeddingStr
131-
.split(',')
132-
.map((val: string) => {
133-
const parsed = parseFloat(val.trim());
134-
if (isNaN(parsed)) {
135-
console.error(`Invalid embedding value found: "${val}"`);
136-
}
137-
return parsed;
138-
});
139-
140-
return {
141-
...doc,
142-
similarity_score: cosineSimilarity(queryEmbedding, embeddingArray)
143-
};
144-
})
145-
.filter((doc: any) => doc.similarity_score > similarityThreshold)
146-
.sort((a: any, b: any) => b.similarity_score - a.similarity_score)
147-
.slice(0, 5);
148-
149-
console.log("RAG top 5 results:", scoredDocs);
150-
151-
// If no relevant documents found, return early
152-
if (scoredDocs.length === 0) {
153-
setAiResponse("I cannot answer this question from the given documentation. The available content doesn't seem relevant enough to provide a accurate answer.");
154-
setIsLoading(false);
155-
return;
156-
}
157-
158-
setLoaderText('Loading the AI model ...')
159-
160-
// Combine relevant documents into context
161-
const docsContext = scoredDocs
162-
.map((doc: any) => `
124+
// Calculate similarity scores and filter results
125+
const similarityThreshold = 0.5;
126+
const scoredDocs = allDocs
127+
.map((doc: any) => {
128+
// Clean the embedding string and parse it
129+
const cleanEmbeddingStr = doc.embedding.replace(/[\[\]]/g, ''); // Remove square brackets
130+
const embeddingArray = cleanEmbeddingStr
131+
.split(',')
132+
.map((val: string) => {
133+
const parsed = parseFloat(val.trim());
134+
if (isNaN(parsed)) {
135+
console.error(`Invalid embedding value found: "${val}"`);
136+
}
137+
return parsed;
138+
});
139+
140+
return {
141+
...doc,
142+
similarity_score: cosineSimilarity(queryEmbedding, embeddingArray)
143+
};
144+
})
145+
.filter((doc: any) => doc.similarity_score > similarityThreshold)
146+
.sort((a: any, b: any) => b.similarity_score - a.similarity_score)
147+
.slice(0, 5);
148+
149+
console.log("RAG top 5 results:", scoredDocs);
150+
151+
// If no relevant documents found, return early
152+
if (scoredDocs.length === 0) {
153+
setAiResponse("I cannot answer this question from the given documentation. The available content doesn't seem relevant enough to provide a accurate answer.");
154+
setIsLoading(false);
155+
return;
156+
}
157+
158+
setLoaderText('Loading the AI model ...')
159+
160+
// Combine relevant documents into context
161+
const docsContext = scoredDocs
162+
.map((doc: any) => `
163163
Source: ${doc.path}
164164
--- Content ---
165165
${doc.content}
166166
--- End of Content ---
167167
`)
168-
.join('\n');
168+
.join('\n');
169169

170170
const engine = await CreateMLCEngine(
171171
"Llama-3.2-1B-Instruct-q4f16_1-MLC",
172-
{ initProgressCallback: (progress: any) => {
173-
console.log(progress)
174-
setLoaderText(`Loading the AI model ${Math.round(progress.progress * 100)}% ...`)
175-
} },
176172
{
177-
context_window_size: 20000,
173+
initProgressCallback: (progress: any) => {
174+
console.log(progress)
175+
setLoaderText(`Loading the AI model ${Math.round(progress.progress * 100)}% ...`)
176+
}
177+
},
178+
{
179+
context_window_size: 20000,
178180
}
179181
);
180182

181183

182184

183-
const engineLoadTime = performance.now() // Track engine load time
184-
console.log(`Time taken for engine initialization: ${(engineLoadTime - startTime) / 1000}s`)
185-
setLoaderText('Processing information and generating AI response ...')
185+
const engineLoadTime = performance.now() // Track engine load time
186+
console.log(`Time taken for engine initialization: ${(engineLoadTime - startTime) / 1000}s`)
187+
setLoaderText('Processing information and generating AI response ...')
186188
const messages = [
187-
{
188-
role: "system",
189-
content: `You are a technical documentation assistant for AkiraDocs. Your purpose is to:
189+
{
190+
role: "system",
191+
content: `You are a technical documentation assistant for AkiraDocs. Your purpose is to:
190192
1. Provide accurate, helpful answers using ONLY the provided documentation
191193
2. Stay positive and factual based on the documentation provided.
192194
3. Make sure the markdown answer is pretty, clean and easy to read.`
193195
},
194-
{
195-
role: "user",
196-
content: `
196+
{
197+
role: "user",
198+
content: `
197199
Please provide a helpful answer which is short and concise to the following question using only the provided documentation.
198200
199201
Question: ${query}
@@ -221,41 +223,43 @@ export default function Home() {
221223
}
222224
];
223225

224-
// console.log("Messages:", messages)
226+
// console.log("Messages:", messages)
225227

226-
const chunks = await engine.chat.completions.create({
228+
const chunks = await engine.chat.completions.create({
227229
messages: messages as ChatCompletionMessageParam[],
228230
stream: true,
229-
stream_options: { include_usage: true },
230-
max_tokens: 500,
231-
temperature: 0.7,
232-
top_p: 0.95,
233-
frequency_penalty: 0.5,
234-
presence_penalty: 0.5,
231+
stream_options: { include_usage: false },
232+
max_tokens: 300,
233+
temperature: 0.5,
234+
top_p: 0.8,
235+
frequency_penalty: 0.3,
236+
presence_penalty: 0.3,
235237
});
236238

237239
let aiContent = "";
238240
for await (const chunk of chunks) {
239241
const newContent = chunk.choices[0]?.delta.content || "";
240242
aiContent += newContent;
241-
242-
// Process partial content for streaming
243-
const { cleanResponse } = extractSources(aiContent);
244-
setAiResponse(cleanResponse);
245-
}
246-
247-
// Only extract and set sources after streaming is complete
248-
const { sources } = extractSources(aiContent);
243+
244+
// Process partial content for streaming
245+
const { cleanResponse } = extractSources(aiContent);
246+
setAiResponse(cleanResponse);
247+
setIsLoading(false)
248+
}
249+
250+
251+
// Only extract and set sources after streaming is complete
252+
const { sources } = extractSources(aiContent);
249253
setSources(sources);
250-
251-
const endTime = performance.now() // Track total time
252-
console.log(`Total time taken for AI search: ${(endTime - startTime) / 1000}s`)
253254

254-
} catch (error) {
255-
console.error('Search error:', error);
256-
setError(error instanceof Error ? error.message : 'An error occurred');
255+
const endTime = performance.now() // Track total time
256+
console.log(`Total time taken for AI search: ${(endTime - startTime) / 1000}s`)
257+
258+
} catch (error) {
259+
console.error('Search error:', error);
260+
setError(error instanceof Error ? error.message : 'An error occurred');
257261
} finally {
258-
setIsLoading(false);
262+
setIsLoading(false);
259263
}
260264
}
261265

@@ -265,49 +269,49 @@ export default function Home() {
265269

266270
return (
267271
<div className="flex flex-col min-h-screen">
268-
<Header {...headerConfig} currentLocale={`en`} currentType={`aiSearch`}/>
269-
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8">
270-
271-
<div className="max-w-4xl mx-auto">
272-
<SearchHeader
273-
logo={searchConfig.logo}
274-
title={searchConfig.title}
275-
description={searchConfig.description}
276-
/>
277-
<div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 justify-center items-center mb-12">
278-
<SearchBar
279-
query={query}
280-
onQueryChange={setQuery}
281-
onSubmit={handleSearch}
272+
<Header {...headerConfig} currentLocale={`en`} currentType={`aiSearch`} />
273+
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8">
274+
275+
<div className="max-w-4xl mx-auto">
276+
<SearchHeader
277+
logo={searchConfig.logo}
278+
title={searchConfig.title}
279+
description={searchConfig.description}
282280
/>
283-
<LegacyDocsToggle/>
284-
</div>
285-
286-
<AnimatePresence>
287-
{isLoading ? (
288-
<div className="flex flex-col justify-center items-center space-y-4 py-12">
289-
<AILoader />
290-
<p className="text-muted-foreground text-sm animate-pulse">
291-
{loaderText}
292-
</p>
293-
</div>
294-
) : error ? (
295-
<div className="text-center p-4 rounded-lg bg-red-50 text-red-800">
296-
<p className="text-lg font-medium mb-2">Error</p>
297-
<p>{error}</p>
298-
</div>
299-
) : aiResponse ? (
300-
<AIResponse
301-
response={aiResponse}
302-
sources={sources}
303-
onBack={handleBack}
281+
<div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 justify-center items-center mb-12">
282+
<SearchBar
283+
query={query}
284+
onQueryChange={setQuery}
285+
onSubmit={handleSearch}
304286
/>
305-
) : recommendedArticles && (
306-
<RecommendedArticles articles={recommendedArticles} />
307-
)}
308-
</AnimatePresence>
287+
<LegacyDocsToggle />
288+
</div>
289+
290+
<AnimatePresence>
291+
{isLoading ? (
292+
<div className="flex flex-col justify-center items-center space-y-4 py-12">
293+
<AILoader />
294+
<p className="text-muted-foreground text-sm animate-pulse">
295+
{loaderText}
296+
</p>
297+
</div>
298+
) : error ? (
299+
<div className="text-center p-4 rounded-lg bg-red-50 text-red-800">
300+
<p className="text-lg font-medium mb-2">Error</p>
301+
<p>{error}</p>
302+
</div>
303+
) : aiResponse ? (
304+
<AIResponse
305+
response={aiResponse}
306+
sources={sources}
307+
onBack={handleBack}
308+
/>
309+
) : recommendedArticles && (
310+
<RecommendedArticles articles={recommendedArticles} />
311+
)}
312+
</AnimatePresence>
313+
</div>
309314
</div>
310315
</div>
311-
</div>
312316
)
313-
}
317+
}

0 commit comments

Comments
 (0)