File size: 38,259 Bytes
a96a8ea
 
 
 
55fff35
a96a8ea
 
55fff35
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cec5203
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55fff35
 
 
a96a8ea
 
 
 
470adcb
a96a8ea
 
 
 
 
55fff35
a96a8ea
55fff35
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55fff35
a96a8ea
55fff35
a96a8ea
 
55fff35
a96a8ea
55fff35
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55fff35
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55fff35
a96a8ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55fff35
a96a8ea
55fff35
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
# -*- coding: utf-8 -*-
"""Shop_chatbot.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1ZOEdR8x7T0Qkj5Nn5FeI8hKIHyTfqsFU
"""

import pandas as pd

data = [
    {
        "Model Number": "Honeywell VC10 Aeromax Elite Cordless Vacuum Cleaner",
        "Product Design": "Off-the-shelf",
        "Price": 379.95,
        "Power": 400,
        "Filter": "HEPA+Sponge",
        "Air Watts Suction": 125,
        "Charging Time": 4.5,
        "Noise Level": 85,
        "Weight": 2.7,
        "Dimensions": {
            "width": 230,
            "depth": 257,
            "height": 1170
        },
        "Accessories and Tools": [
            "Cleaner head with Tangle Resistant Hair Design and LED with Natural Brite-White for better clarity",
            "2-in-1 Crevice Tool",
            "Wall Mount Docking Station",
            "Flexible Crevice Tube",
            "2-in-1 Sofa Brush",
        ],
        "Running Time": 60,
        "Description": """The Honeywell VC10 AEROMAX ELITE is a lightweight cordless vacuum offering powerful 125 air-watt suction
        with its advanced digital motor. This vacuum includes eight tools, a tangle-resistant design,
        and LED lights on the roller for clear and enjoyable cleaning. Its dual-cyclonic filtration ensures consistent suction,
        while the ergonomic design promotes wrist comfort. With up to 55 minutes of runtime from the high-capacity battery,
        Easy One-Touch Control for settings, a quick dustbin emptying mechanism, a wall mount dock, and a deluxe pet hair brush,
        it's an ideal choice, especially for pet owners. Upgrade your cleaning with the VC10 AEROMAX ELITE.
        """,
        "URL": "https://honeywellvac.com/products/vc10-aeromax-elite"
    },
    {
        "Model Number": "Honeywell VC14 Aeromax Elite Cordless Vacuum Cleaner",
        "Product Design": "Off-the-shelf",
        "Price": 649.95,
        "Power": 630,
        "Filter": "HEPA 13",
        "Air Watts Suction": 220,
        "Charging Time": 2.0,
        "Noise Level": 81,
        "Weight": 2.92,
        "Dimensions": {
            "width": 255,
            "depth": 278,
            "height": 1190
        },
        "Accessories and Tools": [
            "Tangle-Resistant Motorized Carpet Roller with Carpet Friendly Softer Bristles & Composite Strip (No LED)",
            "Mini Electric Brush for Mattress, Sofa, Stairs",
            "Rotatory Brush",
            "Flexible Hose",
            "Exchangeable Wide Brush",
        ],
        "Running Time": 90,
        "Description": """The Honeywell VC14 AEROMAX ELITE Cordless Vacuum offers superior 220 air-watt suction with its 630W digital motor.
         This lightweight handheld vacuum includes ten tools for comprehensive cleaning.
         Features include a tangle-resistant design, LED lights on the roller,
         and a dual pro high-efficiency filter system for effective dust separation.
         It's ergonomically designed for wrist comfort and boasts a high-capacity battery with 70 minutes of run time.
         A fast-charging premium charger, advanced LED display for settings adjustments, smart auto clean mode,
         easy one-press disposal, and washable dust tank enhance usability.
         The package also includes a wall mount docking station and a deluxe pet hair removal brush.
        """,
        "URL": "https://honeywellvac.com/products/vc14"
    },
    {
        "Model Number": "Honeywell VC16 Cordless Vacuum Cleaner",
        "Product Design": "New Design",
        "Price": 599.95,
        "Power": 600,
        "Filter": "HEPA",
        "Air Watts Suction": 185,
        "Charging Time": 4.5,
        "Noise Level": 83,
        "Weight": 3.0,
        "Dimensions": {
            "width": 250,
            "depth": 251,
            "height": 1120
        },
        "Accessories and Tools": [
            "Carpet Floor Nozzle",
            "EZ-Change Soft Roller",
            "Deluxe 2-in-1 Sofa Brush",
            "Deluxe 2-in-1 Crevice Tool",
            "Additional HEPA filter H13",
            "Flexible/Stretch Hose",
            "Scented Bead Connector",
            "Bendable connector",
            "Pet Hair removal brush",
            "Deluxe Soft Hairbrush",
            "Electric Mattress/Stairs/Sofa Brush",
            "Deluxe Pet Hair Removal Brush",
            "Small Brush with Cutter",
            "Long Cleaning Brush with Bristles",
            "Accessory Storage Bag",
        ],
        "Running Time": 60,
        "Description":
        """The Honeywell VC16 AEROMAX PRO is a lightweight, cordless vacuum with powerful 185 air-watt suction,
        thanks to its advanced 600W digital motor. Boasting eight cleaning tools,
        this handheld device offers tangle-resistant design and a full-length Brite White LED light strip on the rollers,
        plus an extra spotlight on the body. Its cyclonic tank, paired with a HEPA filter,
        ensures 99.9% dust separation, while the ISC technology keeps the filter efficient.
        Its ergonomic design ensures wrist comfort, and a high-capacity battery offers up to 60 minutes of use.
        The vacuum also features an intuitive LCD display for power and speed control, easy dustbin disposal,
        a motorized rotary brush, scented beads, and a bendable flex hose for hard-to-reach areas.
        """,
        "URL": "https://honeywellvac.com/products/vc16-aeromax-pro-cordless-vac"
    },
]


# Create the DataFrame
df = pd.DataFrame(data)

# Display the DataFrame
df

# Import the libraries
import os, json, ast
import openai
from tenacity import retry, wait_random_exponential, stop_after_attempt

from openai import OpenAI
import os

api_key = os.getenv('OPENAI_API_KEY')
client = OpenAI(
    # This is the default and can be omitted
    api_key= api_key,
    base_url="https://api.rcouyi.com/v1"
)

"""## Initalize the Conversation"""

def initialize_conversation():
    '''
    Returns a list [{"role": "system", "content": system_message}]
    '''

    delimiter = "####"

    example_user_dict = {'Product Design': 'low',
                        'Price': '400',
                        'Air Watts Suction': 'high',
                        'Charging Time': 'low',
                        'Filter': 'low',
                        'Noise Level': 'low',
                        'Weight': 'low',
                        'Dimensions': 'low',
                        'Accessories and Tools': 'low',
                        'Running Time': 'low'}

    example_user_req = {'Product Design': '_',
                        'Price': '_',
                        'Air Watts Suction': '_',
                        'Charging Time': '_',
                        'Filter': '_',
                        'Noise Level': '_',
                        'Weight': '_',
                        'Dimensions': '_',
                        'Accessories and Tools': '_',
                        'Running Time': '_'}

    system_message = f"""
    You are a Honeywell vacuum sales expert, keen on making a connection with your customers. Your task is to engage in friendly chit-chat with users
    to understand their lifestyle and analystic of which our Honeywell vacuum can fits into their need. By casually talking about their day-to-day activities,
    preferences, and home environment, you'll gain insights into the perfect vacuum cleaner for their needs:
      {{
        'Product Design': 'value',
        'Price': 'number', # Ensure this is a number (integer or float without quotes)
        'Air Watts Suction': 'value',
        'Charging Time': 'value',
        'Filter': 'value'
        'Noise Level': 'value',
        'Weight': 'value',
        'Dimensions': 'value',
        'Accessories and Tools': 'value',
        'Running Time': 'value'
    }}
    The value for 'Price' should be a numerical value extracted from the user's response.
    The values for all keys, except 'Price', should be 'low', 'medium', or 'high' based on the importance of the corresponding keys, as stated by user.
    All the values in the example dictionary are only representative values.

    {delimiter}
    Here are some instructions around the values for the different keys. If you do not follow this, you'll be heavily penalised:
    - The values for all keys, except, should strictly be either 'low', 'medium', or 'high' based on the importance of the corresponding keys, as stated by user.
    - The value for ‘Price’ should be a numerical value extracted from the user's response.
    - ' Price' value needs to be greater than or equal to 379.95. If the user says less than that, please mention that there are no vacuums in that range.
    - Do not randomly assign values to any of the keys.
    - The values need to be inferred from the user's response.

    {delimiter}
    To fill the dictionary, you need to have the following chain of thoughts:
    Follow the chain-of-thoughts below and only output the final updated python dictionary for the keys as described in {example_user_req}. \n
    {delimiter}

    Thought 1: Engage the user to quickly understand their primary needs for a vacuum cleaner.
    If the primary purpose or key features of the vacuum are unclear, use brief interactions to clarify their main preferences. Your goal is to populate the values of all keys in the Python dictionary based on the user's statements, focusing on efficiency and relevancy. These keys include:
    - 'Product Design'
    - 'Price'
    - 'Air Watts Suction'
    - 'Charging Time'
    - 'Filter'
    - 'Noise Level'
    - 'Weight'
    - 'Dimensions'
    - 'Accessories and Tools'
    - 'Running Time'

    During your interaction, swiftly identify the user's primary concern:
    - If the user specifically mentions a need like sticking to a budget, prioritize 'Price' and ask for their budget range.
    - Set all other attributes to 'low' immediately if the user confirms the budget is their only concern, indicating that other features are secondary.

    For example, if the user states, 'I’m looking for the best vacuum within a $400 budget,' structure the conversation to finalize their needs in just a few exchanges:
    1. Assistant: Great, focusing on a $300 budget. Are there specific features you need besides being cost-effective?
    2. User: Not really, I just need something simple and cheap.

    Based on this, immediately set all keys to 'low' as preferred but not crucial, and confirm:
    Assistant: Got it! I’ll look for something affordable. I'll set your budget as the main priority and other features to basic settings. This way, I can find the best option for you quickly.

    This approach ensures you gather all necessary information within four interactions, making the process efficient and user-focused, with minimal back-and-forth while covering all essential aspects.

    {delimiter}
    Thought 2: After identifying key preferences of the user, assume that all keys should be set.
    This approach ensures that the vacuum cleaner configuration is tailored to the user's primary concerns while maintaining simplicity in less critical areas.
    Continue to probe for details on any keys you are still unsure about.
    It is best to frame your questions logically, focusing on user needs rather than directly asking for specific key values.

    {delimiter}
    Thought 3: Review the updated values for all keys in the Python dictionary to ensure they accurately reflect the user's preferences and requirements.
    If you find any values that you're unsure about, don't hesitate to ask clarifying questions.
    This final check is crucial to ensure that all entries are correct and meet the user's expectations.

    {delimiter}
    Please follow these thoughts systematically to provide a recommendation that
    best fits the user's needs based on the profile you've developed through your questions.


    {delimiter}
    Here is a sample conversation between the user and assistant:

    User: Hi, I'm looking for a budget-friendly vacuum cleaner that's effective on hardwood floors.

    Assistant: Fantastic! I'd be thrilled to help you find the perfect vacuum. Could you share your budget and how you plan to use it?

    User: My budget is up to $400, and I need something that can handle pet hair and dirt on hardwood floors effectively.

    Assistant: Got it! With pet hair, strong suction is a must.  Keep other features basic to stay within your budget. Sound good?

    User: Yes, that sounds perfect.

    Assistant: {example_user_dict}
    {delimiter}

    Start with a short welcome message mention 'Honeywell vacuum Store' and encourage the user to share their preferences.
    """
    conversation = [{"role": "system", "content": system_message}]
    # conversation = system_message
    return conversation


# print(debug_conversation[0]['content'])

def iterate_llm_response(funct, debug_response, num = 5):
    """
    Calls a specified function repeatedly and prints the results.
    This function is designed to test the consistency of a response from a given function.
    It calls the function multiple times (default is 10) and prints out the iteration count,
    the function's response(s).
    Args:
        funct (function): The function to be tested. This function should accept a single argument
                          and return the response value(s).
        debug_response (dict): The input argument to be passed to 'funct' on each call.
        num (int, optional): The number of times 'funct' will be called. Defaults to 10.
    Returns:
        This function only returns the results to the console.
    """
    i = 0  # Initialize counter

    while i < num:  # Loop to call the function 'num' times

        response = funct(debug_response)  # Call the function with the debug response

        # Print the iteration number, result, and reason from the response
        print("Iteration: {0}".format(i))
        print(response)
        print('-' * 50)  # Print a separator line for readability
        i += 1  # Increment the counter

# Example usage: test the consistency of responses from 'intent_confirmation_layer'
# iterate_llm_response(get_chat_completions, messages)

debug_user_input = "Hi,I need a best budget of vacuum under $400"


"""## Get Chat completion"""

# Define a Chat Completions API call
# Retry up to 6 times with exponential backoff, starting at 1 second and maxing out at 20 seconds delay
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))

def get_chat_completions(input, json_format = False):
    MODEL = 'gpt-4o-mini'

    system_message_json_output = """<<. Return output in JSON format to the key output.>>"""

    # If the output is required to be in JSON format
    if json_format == True:

        # Append the input prompt to include JSON response as specified by OpenAI
        input[0]['content'] += system_message_json_output

        # JSON return type specified
        chat_completion_json = client.chat.completions.create(
            model = MODEL,
            messages = input,
            response_format = { "type": "json_object"},
            seed = 42)

        output = json.loads(chat_completion_json.choices[0].message.content)

    # No JSON return type specified
    else:
        chat_completion = client.chat.completions.create(
            model = MODEL,
            messages = input,
            seed = 42)

        output = chat_completion.choices[0].message.content


    return output


"""## moderation checking"""

# Define a function called moderation_check that takes user_input as a parameter.

def moderation_check(user_input):

    MODEL = 'text-moderation-latest'
    # Call the OpenAI API to perform moderation on the user's input.
    response = client.moderations.create(
        input=user_input,
        model=MODEL
        )

    # Extract the moderation result from the API response.
    moderation_output = response.results[0].flagged
    # Check if the input was flagged by the moderation system.
    if response.results[0].flagged == True:
        # If flagged, return "Flagged"
        return "Flagged"
    else:
        # If not flagged, return "Not Flagged"
        return "Not Flagged"

"""## Confirm intent"""

def intent_confirmation_layer(response_assistant):

    delimiter = "####"

    allowed_values = {'low','medium','high'}


    prompt = f"""
   You are a senior evaluator with an eye for detail. You will receive input text that contains user requirements for a vacuum cleaner, captured through various keys. Evaluate if the input text includes values for the following keys:
{{
  'Product Design': 'value',
  'Price': 'number',  # Ensure this is a number (integer or float without quotes)
  'Filter': 'value',
  'Air Watts Suction': 'value',
  'Charging Time': 'value',
  'Noise Level': 'value',
  'Weight': 'value',
  'Dimensions': 'value',
  'Accessories and Tools': 'value',
  'Running Time': 'value'
}}

The 'Price' key should only contain a number. For all other keys, the values must come from the set of allowed values: {allowed_values}.

Next you need to evaluate if the keys have the the values filled correctly.
    Only output a one-word string in JSON format at the key 'result' - Yes/No.
    Thought 1 - Output a string 'Yes' if the values are correctly filled for all keys, otherwise output 'No'.
    Thought 2 - If the answer is No, mention the reason in the key 'reason'.
    THought 3 - Think carefully before the answering.
"""


    messages=[{"role": "system", "content":prompt },
              {"role": "user", "content":f"""Here is the input: {response_assistant}""" }]

    response = client.chat.completions.create(
                                    model="gpt-4o-mini",
                                    messages = messages,
                                    response_format={ "type": "json_object" },
                                    seed = 1234
                                    # n = 5
                                    )

    json_output = json.loads(response.choices[0].message.content)

    return json_output

# Example 3 - Let's check confirmation_layer if all the keys are present
debug_response_assistant = f"""
Great, thank you for clarifying your requirements.
Based on your inputs, here is the final profile for the vacuum you are looking for:

{{'Product Design': 'low',
        'Price': 400,
        'Filter': 'low',
        'Air Watts Suction': 'low',
        'Charging Time': 'low',
        'Noise Level': 'low',
        'Weight': 'low',
        'Dimensions': 'low',
        'Accessories and Tools': 'low',
        'Running Time': 'low'}}
"""
#Note that you are using double curly braces

print(debug_response_assistant)

# debug_confirmation = intent_confirmation_layer(debug_response_assistant)
# print("Result:",debug_confirmation.get('result'),"\t", "Reason:", debug_confirmation.get('reason'), "\t", "response:", debug_confirmation.get('response'))

"""## Dictionary_preset"""

def dictionary_present(response):
    delimiter = "####"

    user_req = {
        'Product Design': 'low/medium/high',
        'Price': 'numerical value',
        'Filter': 'low/medium/high',
        'Air Watts Suction': 'low/medium/high',
        'Charging Time': 'low/medium/high',
        'Noise Level': 'low/medium/high',
        'Weight': 'low/medium/high',
        'Dimensions': 'low/medium/high',
        'Accessories and Tools': 'low/medium/high',
        'Running Time': 'low/medium/high',
    }

    prompt = f"""
        You are a Python expert. You are provided an input.
        You have to check if there is a Python dictionary present in the string.
        It will have the following format {user_req}.
        Your task is to extract the relevant values from the input and return only the Python dictionary in JSON format.
        The output should match the format as {user_req}.

        {delimiter}
        Make sure that the value of the budget is also present in the user input.
        The output should contain the exact keys and values as present in the input.
        Ensure the keys and values are in the given format:
        {{
            'Product Design': 'low/medium/high',
            'Price': 'numerical value',
            'Filter': 'low/medium/high',
            'Air Watts Suction': 'low/medium/high',
            'Charging Time': 'low/medium/high',
            'Noise Level': 'low/medium/high',
            'Weight': 'low/medium/high',
            'Dimensions': 'low/medium/high',
            'Accessories and Tools': 'low/medium/high',
            'Running Time': 'low/medium/high'
        }}

        Here are some sample input-output pairs for better understanding:
        {delimiter}

        input 1: - Product Design: high - Price: 100  - Filter: high - Air Watts Suction: high - Charging Time: low - Noise Level: medium - Weight: low - Dimensions: medium - Detachable Handheld Vacuum: yes - Accessories and Tools: low - Running Time: medium
        output 1: {{'Product Design': 'high', 'Price': 100, 'Filter': 'high', 'Air Watts Suction': 'high', 'Charging Time': 'low', 'Noise Level': 'medium', 'Weight': 'low', 'Dimensions': 'medium', 'Accessories and Tools': 'low', 'Running Time': 'medium'}}

        input 2: {{'Product Design': 'low', 'Price': '1,000', 'Filter': 'medium', 'Air Watts Suction': 'medium', 'Charging Time': 'medium', 'Noise Level': 'high', 'Weight': 'medium', 'Dimensions': 'low', 'Accessories and Tools': 'medium', 'Running Time': 'low'}}
        output 2: {{'Product Design': 'low', 'Price': 1000, 'Filter': 'medium', 'Air Watts Suction': 'medium', 'Charging Time': 'medium', 'Noise Level': 'high', 'Weight': 'medium', 'Dimensions': 'low', 'Accessories and Tools': 'medium', 'Running Time': 'low'}}

        input 3: Here is your user profile 'Product Design': 'medium','Price': '600','Filter': 'medium','Air Watts Suction': 'medium','Charging Time': 'medium','Noise Level': 'low','Weight': 'medium','Dimensions': 'medium','Accessories and Tools': 'high','Running Time': 'medium'
        output 3: {{'Product Design': 'medium','Price': 600,'Filter': 'medium','Air Watts Suction': 'medium','Charging Time': 'medium','Noise Level': 'low','Weight': 'medium','Dimensions': 'medium','Accessories and Tools': 'high','Running Time': 'medium'}}
        {delimiter}
    """

    messages = [
        {"role": "system", "content": prompt},
        {"role": "user", "content": f"Here is the user input: {response}"}
    ]

    confirmation = get_chat_completions(messages, json_format=True)

    return confirmation

debug_response_assistant_n_1 = """
            {'Product Design': 'high',
            'Price': '500',
            'Filter': 'high',
            'Air Watts Suction': 'medium',
            'Charging Time': 'low',
            'Noise Level': 'high',
            'Weight': 'low',
            'Dimensions': 'high',
            'Accessories and Tools': 'high',
            'Running Time': 'medium'}
"""

debug_response_assistant_n_2 = f"""Thank you for providing your budget.
Based on your budget of 600 , I will consider this while recommending suitable vacuum options for you.
Here is the final recommendation for your vacuum:
- Product Design: medium
- Price: '600'
- Filter: medium
- Air Watts Suction': medium
- Charging Time': medium
- Noise Level: low
- Weight: medium
- Dimensions: medium
- Accessories and Tools: high
- Running Time: medium

Please note that these specifications are based on your requirements for surfing and a decent suction within your budget.
Let me know if there's anything else I can assist you with!"""

# response_dict_n = dictionary_present(debug_response_assistant_n_1)
# display(response_dict_n)

import json
import pandas as pd

def product_map_layer(row):
    delimiter = "#####"

    # Convert the row (Series) to a dictionary
    row_dict = row.to_dict()

    # Create the prompt
    prompt = f"""
    You are a Vacuum Specifications Classifier whose job is to extract the key features of vacuums and classify them according to their specifications.
    The following is a dictionary representing a vacuum model:

    {row_dict}

    Follow these steps:
    1. Classify the features based on the rules below.
    2. Output the classification as a dictionary representing one row.

    {delimiter}
    Model Number:
    - simply extract the value

    Product Design:
    - low: <<< if the product has an old design >>>
    - medium: <<< if the product features an 'Off-the-shelf' design >>>
    - high: <<< if the design boasts an innovative, premium design >>>

    Power:
    - low: <<< if the power is below 500 Watts >>>
    - medium: <<< if the power is between 500 and 600 Watts >>>
    - high: <<< if the power is above 600 Watts >>>

    Filter:
    - low: <<< if the filter is a basic or missing altogether >>>
    - medium: <<< if the filter is of standard quality >>>
    - high: <<< if the filter is a premium, high-efficiency model >>>

    Air Watts Suction:
    - low: <<< if the suction power is below 126 Air Watts >>>
    - medium: <<< if the suction power is between 126 and 185 Air Watts >>>
    - high: <<< if the suction power is above 185 Air Watts >>>

    Charging Time:
    - low: <<< if the charging time is above 6 hours >>>
    - medium: <<< if the charging time is between 3 and 6 hours >>>
    - high: <<< if the charging time is below 3 hours >>>

    Noise Level:
    - low: <<< if the noise level is above 80 dB >>>
    - medium: <<< if the noise level is between 60 and 80 dB >>>
    - high: <<< if the noise level is below 60 dB >>>

    Weight:
    - low: <<< if the weight is above 2.95 kg >>>
    - medium: <<< if the weight is between 2.71 and 2.95 kg >>>
    - high: <<< if the weight is below 2.71 kg >>>

    Dimensions:
    - low: <<< if the dimensions are large, taking up significant space >>>
    - medium: <<< if the dimensions are average >>>
    - high: <<< if the dimensions are compact and space-saving >>>

    Accessories and Tools:
    - low: <<< if there are few or basic accessories/tools >>>
    - medium: <<< if there is a moderate number of accessories/tools >>>
    - high: <<< if there is a wide range of accessories/tools included >>>

    Running Time:
    - low: <<< if the running time is below 60 minutes >>>
    - medium: <<< if the running time is between 60 and 89 minutes >>>
    - high: <<< if the running time is above 89 minutes >>>

    {delimiter}

    ### The output should be a dictionary representing the classification for one row. ###
    """

    input_text = "Please classify the vacuum model using the rules provided above."

    # Create the messages for the completion
    messages = [{"role": "system", "content": prompt}, {"role": "user", "content": input_text}]

    # Assuming get_chat_completions() is a function you have to handle the completion request
    response = get_chat_completions(messages, json_format=True)

    return response


# iterate_llm_response(product_map_layer, df.iloc[0])

# display(response_dict_n)

import pandas as pd  # Importing the pandas library for data manipulation
import json

def compare_with_user(response_dict_n):


    user_requirements = response_dict_n

    # Extracting user requirements from the input string (assuming it's a dictionary)
    # Since the function parameter already seems to be a string, we'll use it directly instead of extracting from a dictionary

    # Extracting the budget value from user_requirements and converting it to an float
    price_value = user_requirements.get('Price', '0')

    # Check the type of price_value and handle accordingly
    if isinstance(price_value, int):
        price = price_value  # Use the integer directly
    elif isinstance(price_value, str):
        # It's a string, so clean it and convert it to an integer
        price = int(price_value.replace(',', '').split()[0])
    else:
        # If it's neither an int nor a string (unexpected), set a default or handle the error
        price = 0  # Default value or raise an exception

    # Creating a copy of the DataFrame and filtering laptops based on the budget
    filtered_df = df.copy()
    # filtered_df['Price'] = filtered_df['Price'].str.replace(',', '').astype(int)
    filtered_df = filtered_df[filtered_df['Price'] <= price].copy()

    # # # Mapping string values 'low', 'medium', 'high' to numerical scores 0, 1, 2
    mappings = {'low': 0, 'medium': 1, 'high': 2}

    # # # Creating a new column 'Score' in the filtered DataFrame and initializing it to 0
    filtered_df['Score'] = 0

    # # # Iterating over each item in the filtered DataFrame to calculate scores based on user requirements
    for index, row in filtered_df.iterrows():
        user_product_match_str = row
        item_values = user_product_match_str
        item_values = product_map_layer(user_product_match_str)
        score = 0

    #  # Comparing user requirements with laptop features and updating scores
        for key, user_value in user_requirements.items():
            # if key.lower() == 'budget':
            if key == 'Price':
                continue  # Skipping budget comparison
            item_value = item_values.get(key, None)
            # print(key, item_value)
            item_mapping = mappings.get(item_value, -1)
            user_mapping = mappings.get(user_value, -1)
            if item_mapping >= user_mapping:
                score += 1  # Incrementing score if laptop value meets or exceeds user value

        filtered_df.loc[index, 'Score'] = score  # Updating the 'Score' column in the DataFrame

    # Sorting laptops by score in descending order and selecting the top 3 products
    # top_laptops = filtered_df.drop('laptop_feature', axis=1)
    # top_laptops = top_laptops.sort_values('Score', ascending=False)
    choices_json = filtered_df.to_json(orient='records')  # Converting the top laptops DataFrame to JSON format

    # top_laptops
    return choices_json

# compare = compare_with_user(response_dict_n)

# display(compare)

# df2 = pd.read_json(compare, orient='records')
# df2

def recommendation_validation(compare_recommendation):
    data = json.loads(compare_recommendation)
    data1 = []
    for i in range(len(data)):
        if data[i]['Score'] > 2:
            data1.append(data[i])

    return data1

"""## Finalize the chat"""

def initialize_conv_reco(products):

    system_message = f"""
   As an expert on intelligent vacuum gadgets, your task is to provide concise, informative responses to user inquiries about products from the catalogue. Keep the user’s specific needs in mind while crafting your answers.

Start each response with a brief summary of the vacuum in the following format:
<vacuum Name> : <Description>, <Price in USD>, <URL>

Ensure the description is tailored to highlight how the vacuum meets the user's needs, using a tone that enhances the recommendation’s relevance. For example, if a user is looking for a budget-friendly option for hardwood floors, your summary might look like this:

HoneyWell VC10 Vacuum: Ideal for hardwood floors with efficient suction and easy maneuverability, priced at $399. For more details or to purchase, visit: [https://honeywellvac.com/products/vc10-aeromax-elite](https://honeywellvac.com/products/vc10-aeromax-elite).

This format provides a quick overview and positions the vacuum as a solution to the user's requirements, making the recommendation both informative and directly useful. Include the URL at the end of each summary to enable users to find more information or to make a purchase directly.
"""
    user_message = f""" These are the user's products: {products}"""
    conversation = [{"role": "system", "content": system_message },
                    {"role":"user","content":user_message}]
    # conversation_final = conversation[0]['content']

    return conversation

def dialogue_mgmt_system():
    """
    Manages a conversational system between a user and an assistant to recommend vacuum.

    This function initializes the conversation, retrieves the introduction, handles user input,
    performs moderation checks, and manages the recommendation process based on user interactions.

    Returns:
        None
    """

    conversation = initialize_conversation()

    introduction = get_chat_completions(conversation)

    print(f"Assistant: {introduction}" + '\n')

    recommend_vacuum = None

    user_input = ''

    while(user_input != "exit"):

        user_input = input("")

        moderation = moderation_check(user_input)
        if moderation == 'Flagged':
            break

        if recommend_vacuum is None:

            conversation.append({"role": "user", "content": user_input})

            response_assistant = get_chat_completions(conversation)
            moderation = moderation_check(response_assistant)
            if moderation == 'Flagged':
                break


            confirmation = intent_confirmation_layer(response_assistant)

            print(f"Intent Confirmation Yes/No: {confirmation.get('result')}", f"Reason: {confirmation.get('reason')}")

            if "No" in confirmation.get('result'):
                conversation.append({"role": "assistant", "content": str(response_assistant)})
                print(f"Assistant: {response_assistant}" + "\n")
                print(f"conversation: {conversation}")

            else:
                print(f"Assistant: {response_assistant}" + "\n")
                print('\n' + "Variables extracted!" + '\n')

                response = dictionary_present(response_assistant)

                print("Thank you for providing all the information. Kindly wait, while I fetch the products: \n")
                recommend_vacuum = compare_with_user(response)

                print("Recommend vacuums are", recommend_vacuum)

                validated_reco = recommendation_validation(recommend_vacuum)

                conversation_reco = initialize_conv_reco(validated_reco)

                print("initial_reco", conversation_reco)

                conversation_reco.append({"role": "user", "content": "This is my user profile" + str(response)})

                recommendation = get_chat_completions(conversation_reco)

                moderation = moderation_check(recommendation)
                if moderation == 'Flagged':
                    break

                conversation_reco.append({"role": "assistant", "content": str(recommendation)})

                print(f"Assistant: {recommendation}" + '\n')
                break

        else:
            conversation_reco.append({"role": "user", "content": user_input})

            response_asst_reco = get_chat_completions(conversation_reco)

            moderation = moderation_check(response_asst_reco)
            if moderation == 'Flagged':
                print("Sorry, this message has been flagged. Please restart your conversation.")
                break

            print(f"Assistant:{response_asst_reco}" + '\n')
            conversation.append({"role": "assistant", "content": response_asst_reco})
            break

# dialogue_mgmt_system()

"""## Gradio"""

# @title
import gradio as gr
import time

def welcome_msg():

    conversation = initialize_conversation()
    introduction = get_chat_completions(conversation)
    return [(None, introduction)]
    # return [(None, "Hello! How can I assist you today?")]

def gradio_chatbot(msg, history, recommend_vacuum):
    # print("Function called with message:", msg)
    print("before history")
    # history = []

    history_openai_format = []
    for human, assistant in history:
        if human is not None:
            history_openai_format.append({"role": "user", "content": human})
        history_openai_format.append({"role": "assistant", "content": assistant})

    history_openai_format.append({"role": "user", "content": msg})

    if recommend_vacuum is False:
        response_assistant = get_chat_completions(history_openai_format)
        confirmation = intent_confirmation_layer(response_assistant)

        if "No" in confirmation.get('result'):
            # history.append({"role": "assistant", "content": str(response_assistant)})
            history.append((msg, response_assistant))
            return "", history, False
        else:
            response = dictionary_present(response_assistant)
            recommend_vacuum = compare_with_user(response)
            validated_reco = recommendation_validation(recommend_vacuum)
            conversation_reco = [{"role": "assistant", "content": "This is my user profile " + str(response)}]
            recommendation = get_chat_completions(conversation_reco)
            history.append(msg, recommendation)
            return "", history,  True
    else:
        history_openai_format.append({"role": "user", "content": msg })
        response_asst_reco = get_chat_completions(history_openai_format)
        history.append(msg, response_asst_reco)
        return "", history, True

bot_img = 'https://cdn-icons-png.freepik.com/512/4712/4712035.png'
user_img = 'https://cdn3.iconfinder.com/data/icons/people-and-avatar-2/48/22-nerd_bot-1024.png'

def clear():
    # Clear only the message input field
    return ""

with gr.Blocks() as demo:

    recommend_vacuum = gr.State(value = False)


    with gr.Row():
        chatbot = gr.Chatbot(value = welcome_msg(),
                             show_label=False,
                             avatar_images=(user_img, bot_img)
                             )
    with gr.Row():
        msg = gr.Textbox(label="Type a message...", placeholder="Type a message...", show_label=False)
    with gr.Row():
        restart = gr.ClearButton([msg, chatbot, recommend_vacuum],value = "Restart")
        clear_button = gr.Button("Clear")
        submit_button = gr.Button("Submit", variant='primary')

    msg.submit(
         gradio_chatbot,
        inputs=[msg, chatbot, recommend_vacuum],
        outputs=[msg, chatbot, recommend_vacuum]
    )

    submit_button.click(
        gradio_chatbot,
        inputs=[msg, chatbot, recommend_vacuum],
        outputs=[msg, chatbot, recommend_vacuum]
    )

    clear_button.click(
        clear,
        inputs=[],
        outputs=[msg]
    )

demo.launch(share=True)