1
1
<!DOCTYPE html>
2
2
< html lang ="en ">
3
+
3
4
< head >
4
5
< meta charset ="UTF-8 ">
5
6
< meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
148
149
box-shadow : 0 4px 6px rgba (0 , 0 , 0 , 0.1 );
149
150
}
150
151
151
- .json-output {
152
- background-color : # f9f9f9 ;
153
- border : 1px solid # ddd ;
154
- padding : 10px ;
155
- font-family : monospace;
156
- white-space : pre-wrap;
157
- max-height : 300px ;
152
+ .json-output {
153
+ background-color : # f9f9f9 ;
154
+ border : 1px solid # ddd ;
155
+ padding : 10px ;
156
+ font-family : monospace;
157
+ white-space : pre-wrap;
158
+ max-height : 300px ;
158
159
max-width : 100% ;
159
- overflow-y : auto;
160
- margin-top : 20px ;
161
- }
160
+ overflow-y : auto;
161
+ margin-top : 20px ;
162
+ }
162
163
163
164
.user-hints h3 {
164
165
margin-top : 0 ;
171
172
}
172
173
</ style >
173
174
</ head >
175
+
174
176
< body >
175
177
176
178
< header class ="header ">
@@ -182,8 +184,14 @@ <h1 class="title">Product Ingestion & Search</h1>
182
184
< h3 > How to Use:</ h3 >
183
185
< ul >
184
186
< li > Upload a CSV file for product ingestion. After uploading, the product IDs will be displayed.</ li >
185
- < li > Use the Vector Search section to search for products by category or search query. Click the category buttons or enter a custom query.</ li >
186
- < li > Use RAG (Retrieval Augmented Generation Search) to get smarter, context-driven results by blending search data with generated insights.</ li >
187
+ < li >
188
+ Use the Vector Search section to search for products by category or search query. Click the category
189
+ buttons or enter a custom query.
190
+ </ li >
191
+ < li >
192
+ Use RAG (Retrieval Augmented Generation Search) to get smarter, context-driven results by blending
193
+ search data with generated insights.
194
+ </ li >
187
195
</ ul >
188
196
</ section >
189
197
@@ -460,105 +468,100 @@ <h3>${product.name}</h3>
460
468
}
461
469
}
462
470
463
- async function ragStreamingSearch ( category ) {
464
- const query = category || document . getElementById ( 'ragStreamingSearchQuery' ) . value ;
465
- const ragStreamingSearchButton = document . getElementById ( 'ragStreamingSearchButton' ) ;
466
- const resultContainer = document . getElementById ( 'ragStreamingSearchResult' ) ;
471
+ async function ragStreamingSearch ( category ) {
472
+ const query = category || document . getElementById ( 'ragStreamingSearchQuery' ) . value ;
473
+ const searchButton = document . getElementById ( 'ragStreamingSearchButton' ) ;
474
+ const resultContainer = document . getElementById ( 'ragStreamingSearchResult' ) ;
467
475
const jsonOutputContainer = document . getElementById ( 'ragStreamingJsonOutput' ) ;
468
476
469
- resultContainer . innerHTML = '' ;
470
- jsonOutputContainer . textContent = '' ;
471
-
472
- lockSectionButtons ( 'ragStreamingSearchSection' ) ;
473
- ragStreamingSearchButton . classList . add ( 'is-loading' ) ;
474
-
475
- const startTime = Date . now ( ) ;
476
- let jsonOutput = '' ;
477
- let parsedResults = [ ] ;
478
- let accumulatedData = '' ;
479
-
480
- try {
481
- const response = await fetch ( `/api/products/search/rag/streaming?searchQuery=${ encodeURIComponent ( query ) } ` ) ;
482
- const reader = response . body . getReader ( ) ;
483
- const decoder = new TextDecoder ( "utf-8" ) ;
484
-
485
- while ( true ) {
486
- const { done, value } = await reader . read ( ) ;
487
- if ( done ) break ;
488
- const chunk = decoder . decode ( value , { stream : true } ) ;
489
- accumulatedData += chunk ;
490
- jsonOutput += chunk ;
491
- jsonOutputContainer . textContent = jsonOutput ;
492
-
493
- // Attempt to parse the JSON data
494
- try {
495
- // Check if the accumulated data ends with a valid JSON structure
496
- const lastBraceIndex = accumulatedData . lastIndexOf ( '}' ) ;
497
- const lastBracketIndex = accumulatedData . lastIndexOf ( ']' ) ;
498
- const endIndex = Math . max ( lastBraceIndex , lastBracketIndex ) ;
499
-
500
- if ( endIndex !== - 1 ) {
501
- const jsonString = accumulatedData . substring ( 0 , endIndex + 1 ) ;
502
- const parsedChunk = JSON . parse ( jsonString ) ;
503
- parsedResults . push ( parsedChunk ) ;
504
- accumulatedData = accumulatedData . substring ( endIndex + 1 ) ; // Remove the parsed part
505
- }
506
- } catch ( e ) {
507
- // Ignore parsing errors
508
- console . error ( 'Error parsing chunk:' , e ) ;
509
- }
510
- }
511
-
512
- const endTime = Date . now ( ) ;
513
- const duration = ( endTime - startTime ) / 1000 ; // Convert to seconds
514
-
515
- if ( parsedResults . length === 0 ) {
516
- resultContainer . innerHTML = '<div class="notification is-danger"><strong>No results found</strong></div>' ;
517
- } else {
518
- const products = parsedResults [ 0 ] ;
519
- const transformedResult = {
520
- noResult : false ,
521
- minRelevance : 0.8 ,
522
- relevantSourcesCount : products . length ,
523
- products : products . map ( product => ( {
524
- id : product . Id ,
525
- name : product . Name ,
526
- description : product . Description ,
527
- price : product . Price ,
528
- priceCurrency : product . PriceCurrency ,
529
- supplyAbility : product . SupplyAbility ,
530
- minimumOrder : product . MinimumOrder
531
- } ) )
532
- } ;
533
-
534
- resultContainer . innerHTML = `
535
- <div>
536
- <p><strong>Min Relevance:</strong> ${ transformedResult . minRelevance } </p>
537
- <p><strong>Relevant Sources Count:</strong> ${ transformedResult . relevantSourcesCount } </p>
538
- <p><strong>Request Duration:</strong> ${ duration . toFixed ( 2 ) } seconds</p>
539
- </div>
540
- <div class="products-container">
541
- ${ transformedResult . products . map ( product => `
542
- <div class="product-card">
543
- <h3>${ product . name } </h3>
544
- <p class="description">${ product . description } </p>
545
- <p class="price">${ product . price } ${ product . priceCurrency } </p>
546
- <p>Supply Ability: ${ product . supplyAbility } </p>
547
- <p>Minimum Order: ${ product . minimumOrder } </p>
548
- </div>
549
- ` ) . join ( '' ) }
477
+ resetUI ( resultContainer , jsonOutputContainer , searchButton ) ;
478
+ lockSectionButtons ( 'ragStreamingSearchSection' ) ;
479
+
480
+ const startTime = Date . now ( ) ;
481
+ let result = '' ;
482
+
483
+ try {
484
+ const response = await fetch ( `/api/products/search/rag/streaming?searchQuery=${ encodeURIComponent ( query ) } ` ) ;
485
+ const reader = response . body . getReader ( ) ;
486
+ const decoder = new TextDecoder ( "utf-8" ) ;
487
+
488
+ result = await processStream ( reader , decoder , jsonOutputContainer , result ) ;
489
+
490
+ displayResults ( result , resultContainer , startTime ) ;
491
+ } catch ( error ) {
492
+ console . error ( error ) ;
493
+ resultContainer . innerHTML = '<div class="notification is-danger"><strong>Error occurred while searching.</strong></div>' ;
494
+ } finally {
495
+ searchButton . classList . remove ( 'is-loading' ) ;
496
+ unlockSectionButtons ( 'ragStreamingSearchSection' ) ;
497
+ }
498
+ }
499
+
500
+ function resetUI ( resultContainer , jsonOutputContainer , searchButton ) {
501
+ resultContainer . innerHTML = '' ;
502
+ jsonOutputContainer . textContent = '' ;
503
+ searchButton . classList . add ( 'is-loading' ) ;
504
+ }
505
+
506
+ async function processStream ( reader , decoder , jsonOutputContainer ) {
507
+ let streamContent = '' ;
508
+
509
+ while ( true ) {
510
+ const { done, value } = await reader . read ( ) ;
511
+ if ( done ) break ;
512
+
513
+ const chunk = decoder . decode ( value , { stream : true } ) ;
514
+ jsonOutputContainer . textContent += chunk ;
515
+ streamContent += chunk ;
516
+ }
517
+
518
+ return streamContent ;
519
+ }
520
+
521
+ function displayResults ( result , resultContainer , startTime ) {
522
+ const duration = ( ( Date . now ( ) - startTime ) / 1000 ) . toFixed ( 2 ) ;
523
+
524
+ if ( result . length === 0 ) {
525
+ resultContainer . innerHTML = '<div class="notification is-danger"><strong>No results found</strong></div>' ;
526
+ return ;
527
+ }
528
+
529
+ const products = JSON . parse ( result ) ;
530
+ const transformedResult = {
531
+ noResult : false ,
532
+ minRelevance : 0.8 ,
533
+ relevantSourcesCount : products . length ,
534
+ products : products . map ( product => ( {
535
+ id : product . Id ,
536
+ name : product . Name ,
537
+ description : product . Description ,
538
+ price : product . Price ,
539
+ priceCurrency : product . PriceCurrency ,
540
+ supplyAbility : product . SupplyAbility ,
541
+ minimumOrder : product . MinimumOrder
542
+ } ) )
543
+ } ;
544
+
545
+ resultContainer . innerHTML = `
546
+ <div>
547
+ <p><strong>Min Relevance:</strong> ${ transformedResult . minRelevance } </p>
548
+ <p><strong>Relevant Sources Count:</strong> ${ transformedResult . relevantSourcesCount } </p>
549
+ <p><strong>Request Duration:</strong> ${ duration } seconds</p>
550
+ </div>
551
+ <div class="products-container">
552
+ ${ transformedResult . products . map ( product => `
553
+ <div class="product-card">
554
+ <h3>${ product . name } </h3>
555
+ <p class="description">${ product . description } </p>
556
+ <p class="price">${ product . price } ${ product . priceCurrency } </p>
557
+ <p>Supply Ability: ${ product . supplyAbility } </p>
558
+ <p>Minimum Order: ${ product . minimumOrder } </p>
550
559
</div>
551
- ` ;
552
- }
553
- } catch ( error ) {
554
- console . error ( 'Error occurred while searching:' , error ) ;
555
- resultContainer . innerHTML = '<div class="notification is-danger"><strong>Error occurred while searching.</strong></div>' ;
556
- } finally {
557
- ragStreamingSearchButton . classList . remove ( 'is-loading' ) ;
558
- unlockSectionButtons ( 'ragStreamingSearchSection' ) ;
559
- }
560
- }
560
+ ` ) . join ( '' ) }
561
+ </div>` ;
562
+ }
561
563
562
564
</ script >
563
565
</ body >
564
- </ html >
566
+
567
+ </ html >
0 commit comments