shukdevdatta123 commited on
Commit
6e588d4
·
verified ·
1 Parent(s): a066301

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -826
app.py DELETED
@@ -1,826 +0,0 @@
1
- import gradio as gr
2
- import base64
3
- import requests
4
- import io
5
- from PIL import Image
6
- import json
7
- import os
8
- from together import Together
9
- import tempfile
10
- import uuid
11
- import time
12
-
13
- def encode_image_to_base64(image_path):
14
- """Convert image to base64 encoding"""
15
- with open(image_path, "rb") as image_file:
16
- return base64.b64encode(image_file.read()).decode('utf-8')
17
-
18
- def analyze_single_image(client, img_path):
19
- """Analyze a single image to identify ingredients"""
20
- system_prompt = """You are a culinary expert AI assistant that specializes in identifying ingredients in images.
21
- Your task is to analyze the provided image and list all the food ingredients you can identify.
22
- Be specific and detailed about what you see. Only list ingredients, don't suggest recipes yet."""
23
-
24
- user_prompt = "Please identify all the food ingredients visible in this image. List each ingredient on a new line."
25
-
26
- try:
27
- with open(img_path, "rb") as image_file:
28
- base64_image = base64.b64encode(image_file.read()).decode('utf-8')
29
-
30
- content = [
31
- {"type": "text", "text": user_prompt},
32
- {
33
- "type": "image_url",
34
- "image_url": {
35
- "url": f"data:image/jpeg;base64,{base64_image}"
36
- }
37
- }
38
- ]
39
-
40
- response = client.chat.completions.create(
41
- model="meta-llama/Llama-Vision-Free",
42
- messages=[
43
- {"role": "system", "content": system_prompt},
44
- {"role": "user", "content": content}
45
- ],
46
- max_tokens=500,
47
- temperature=0.2
48
- )
49
-
50
- return response.choices[0].message.content
51
- except Exception as e:
52
- return f"Error analyzing image: {str(e)}"
53
-
54
- def format_ingredients_html(all_ingredients):
55
- """Format the identified ingredients in HTML"""
56
- html_content = """
57
- <div class="ingredients-identified">
58
- <h2>📋 Ingredients Identified</h2>
59
- """
60
-
61
- for i, ingredients in enumerate(all_ingredients):
62
- html_content += f"""
63
- <div class="ingredient-group">
64
- <h3>Image {i+1} Ingredients</h3>
65
- <ul class="ingredient-list">
66
- """
67
-
68
- # Split ingredients by new line and create list items
69
- ingredient_lines = ingredients.strip().split('\n')
70
- for line in ingredient_lines:
71
- if line.strip():
72
- html_content += f"<li>{line.strip()}</li>\n"
73
-
74
- html_content += """
75
- </ul>
76
- </div>
77
- """
78
-
79
- html_content += "</div>"
80
- return html_content
81
-
82
- def format_recipe_to_html(recipe_text):
83
- """Convert the recipe text to formatted HTML"""
84
- # Initialize the HTML content with the recipe suggestions header
85
- html_content = """
86
- <div class="recipe-suggestions">
87
- <h2>🍽️ Recipe Suggestions</h2>
88
- """
89
-
90
- # Split the text by recipe (assume recipes are separated by a recipe name heading)
91
- recipe_sections = []
92
- current_recipe = ""
93
- lines = recipe_text.split('\n')
94
-
95
- # Process lines to identify recipe sections
96
- for line in lines:
97
- if line.strip().startswith(("Recipe ", "# ", "## ", "### ")):
98
- # If we've collected some content for a recipe, save it
99
- if current_recipe:
100
- recipe_sections.append(current_recipe)
101
- current_recipe = ""
102
-
103
- # Add line to current recipe
104
- current_recipe += line + "\n"
105
-
106
- # Add the last recipe if exists
107
- if current_recipe:
108
- recipe_sections.append(current_recipe)
109
-
110
- # Process each recipe section into HTML
111
- for recipe in recipe_sections:
112
- if not recipe.strip():
113
- continue
114
-
115
- html_content += '<div class="recipe-card">'
116
-
117
- lines = recipe.split('\n')
118
- in_ingredients = False
119
- in_instructions = False
120
-
121
- for line in lines:
122
- # Handle recipe title
123
- if any(x in line.lower() for x in ["recipe ", "# recipe"]) or line.strip().startswith(("# ", "## ")):
124
- title = line.replace("#", "").replace("Recipe:", "").replace("Recipe", "").strip()
125
- html_content += f'<h3 class="recipe-title">{title}</h3>\n'
126
- continue
127
-
128
- # Handle description
129
- if "description" in line.lower() and ":" in line:
130
- description = line.split(":", 1)[1].strip()
131
- html_content += f'<p class="recipe-description">{description}</p>\n'
132
- continue
133
-
134
- # Start ingredients section
135
- if "ingredients" in line.lower() and not in_ingredients:
136
- in_ingredients = True
137
- in_instructions = False
138
- html_content += '<div class="recipe-ingredients">\n'
139
- html_content += '<h4>Ingredients</h4>\n<ul>\n'
140
- continue
141
-
142
- # Start instructions section
143
- if any(x in line.lower() for x in ["instructions", "directions", "steps", "preparation"]) and not in_instructions:
144
- if in_ingredients:
145
- html_content += '</ul>\n</div>\n'
146
- in_ingredients = False
147
-
148
- in_instructions = True
149
- html_content += '<div class="recipe-instructions">\n'
150
- html_content += '<h4>Instructions</h4>\n<ol>\n'
151
- continue
152
-
153
- # Handle cooking time
154
- if "cooking time" in line.lower() or "prep time" in line.lower() or "time" in line.lower():
155
- if in_ingredients:
156
- html_content += '</ul>\n</div>\n'
157
- in_ingredients = False
158
- if in_instructions:
159
- html_content += '</ol>\n</div>\n'
160
- in_instructions = False
161
-
162
- time_info = line.strip()
163
- html_content += f'<p class="recipe-time"><strong>⏱️ {time_info}</strong></p>\n'
164
- continue
165
-
166
- # Handle difficulty level
167
- if "difficulty" in line.lower():
168
- difficulty = line.strip()
169
- html_content += f'<p class="recipe-difficulty"><strong>🔍 {difficulty}</strong></p>\n'
170
- continue
171
-
172
- # Handle nutritional info
173
- if "nutritional" in line.lower():
174
- if in_ingredients:
175
- html_content += '</ul>\n</div>\n'
176
- in_ingredients = False
177
- if in_instructions:
178
- html_content += '</ol>\n</div>\n'
179
- in_instructions = False
180
-
181
- html_content += '<div class="recipe-nutrition">\n'
182
- html_content += f'<h4>{line.strip()}</h4>\n<ul>\n'
183
- continue
184
-
185
- # Process ingredient line
186
- if in_ingredients and line.strip() and not line.lower().startswith(("ingredients", "instructions")):
187
- item = line.strip()
188
- if item.startswith("- "):
189
- item = item[2:]
190
- elif item.startswith("* "):
191
- item = item[2:]
192
-
193
- if item:
194
- html_content += f'<li>{item}</li>\n'
195
- continue
196
-
197
- # Process instruction line
198
- if in_instructions and line.strip() and not line.lower().startswith(("ingredients", "instructions")):
199
- step = line.strip()
200
- if step.startswith("- "):
201
- step = step[2:]
202
- elif step.startswith("* "):
203
- step = step[2:]
204
- elif step.startswith(". "):
205
- step = step[2:]
206
- elif step[0].isdigit() and step[1] in [".", ")"]:
207
- step = step[2:].strip()
208
-
209
- if step:
210
- html_content += f'<li>{step}</li>\n'
211
- continue
212
-
213
- # Process nutrition line
214
- if "nutritional" in line.lower() and line.strip() and not line.startswith(("ingredients", "instructions")):
215
- item = line.strip()
216
- if item.startswith("- "):
217
- item = item[2:]
218
- elif item.startswith("* "):
219
- item = item[2:]
220
-
221
- if item and not item.lower().startswith("nutritional"):
222
- html_content += f'<li>{item}</li>\n'
223
- continue
224
-
225
- # Close any open sections
226
- if in_ingredients:
227
- html_content += '</ul>\n</div>\n'
228
- if in_instructions:
229
- html_content += '</ol>\n</div>\n'
230
-
231
- html_content += '</div>\n' # Close recipe card
232
-
233
- html_content += '</div>\n' # Close recipe suggestions div
234
-
235
- return html_content
236
-
237
- def get_recipe_suggestions(api_key, image_paths, num_recipes=3, dietary_restrictions="None", cuisine_preference="Any"):
238
- """Get recipe suggestions based on the uploaded images of ingredients"""
239
- if not api_key:
240
- return "Please provide your Together API key."
241
-
242
- if not image_paths or len(image_paths) == 0:
243
- return "Please upload at least one image of ingredients."
244
-
245
- try:
246
- client = Together(api_key=api_key)
247
-
248
- all_ingredients = []
249
- for img_path in image_paths:
250
- ingredients_text = analyze_single_image(client, img_path)
251
- all_ingredients.append(ingredients_text)
252
-
253
- combined_ingredients = "\n\n".join([f"Image {i+1} ingredients:\n{ingredients}"
254
- for i, ingredients in enumerate(all_ingredients)])
255
-
256
- system_prompt = """You are a culinary expert AI assistant that specializes in creating recipes based on available ingredients.
257
- You will be provided with lists of ingredients identified from multiple images. Your task is to suggest creative,
258
- detailed recipes that use as many of the identified ingredients as possible.
259
-
260
- For each recipe suggestion, include:
261
- 1. Recipe name
262
- 2. Brief description of the dish
263
- 3. Complete ingredients list (including estimated quantities and any additional staple ingredients that might be needed)
264
- 4. Step-by-step cooking instructions
265
- 5. Approximate cooking time
266
- 6. Difficulty level (Easy, Medium, Advanced)
267
- 7. Nutritional highlights
268
-
269
- Format each recipe clearly with headings for each section. Make sure to separate recipes clearly.
270
- Consider any dietary restrictions and cuisine preferences mentioned by the user."""
271
-
272
- user_prompt = f"""Based on the following ingredients identified from multiple images, suggest {num_recipes} creative and delicious recipes.
273
-
274
- {combined_ingredients}
275
-
276
- Dietary restrictions to consider: {dietary_restrictions}
277
- Cuisine preference: {cuisine_preference}
278
-
279
- Please be creative with your recipe suggestions and try to use ingredients from multiple images if possible."""
280
-
281
- response = client.chat.completions.create(
282
- model="meta-llama/Llama-Vision-Free",
283
- messages=[
284
- {"role": "system", "content": system_prompt},
285
- {"role": "user", "content": user_prompt}
286
- ],
287
- max_tokens=20000,
288
- temperature=0.7
289
- )
290
-
291
- recipe_text = response.choices[0].message.content
292
-
293
- # Format ingredients and recipes as HTML
294
- ingredients_html = format_ingredients_html(all_ingredients)
295
- recipes_html = format_recipe_to_html(recipe_text)
296
-
297
- # Combine HTML content
298
- html_content = ingredients_html + "<hr>" + recipes_html
299
-
300
- # Create a downloadable HTML file with styling
301
- full_html = create_downloadable_html(ingredients_html, recipes_html, dietary_restrictions, cuisine_preference)
302
-
303
- # Generate a unique filename for the downloadable HTML
304
- timestamp = int(time.time())
305
- file_name = f"recipes_{timestamp}.html"
306
- file_path = os.path.join(tempfile.gettempdir(), file_name)
307
-
308
- with open(file_path, "w", encoding="utf-8") as f:
309
- f.write(full_html)
310
-
311
- return [html_content, file_path]
312
- except Exception as e:
313
- return [f"Error: {str(e)}", None]
314
-
315
- def create_downloadable_html(ingredients_html, recipes_html, dietary_restrictions, cuisine_preference):
316
- """Create a complete HTML document with styling for download"""
317
- html = f"""<!DOCTYPE html>
318
- <html lang="en">
319
- <head>
320
- <meta charset="UTF-8">
321
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
322
- <title>Your Personalized Recipes</title>
323
- <style>
324
- @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
325
- :root {{
326
- --primary-color: #FF6F61;
327
- --secondary-color: #4BB543;
328
- --accent-color: #F0A500;
329
- --background-color: #F4F4F9;
330
- --text-color: #333333;
331
- --card-background: #FFFFFF;
332
- --border-radius: 12px;
333
- --box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 20px;
334
- }}
335
- body {{
336
- font-family: 'Poppins', sans-serif;
337
- background-color: var(--background-color);
338
- color: var(--text-color);
339
- margin: 0;
340
- padding: 0;
341
- line-height: 1.6;
342
- }}
343
- .container {{
344
- max-width: 1000px;
345
- margin: 0 auto;
346
- padding: 20px;
347
- }}
348
- header {{
349
- background-color: var(--primary-color);
350
- color: white;
351
- padding: 40px 20px;
352
- text-align: center;
353
- border-radius: 0 0 20px 20px;
354
- margin-bottom: 30px;
355
- }}
356
- h1 {{
357
- font-size: 2.5em;
358
- margin-bottom: 10px;
359
- }}
360
- .recipe-info {{
361
- display: flex;
362
- justify-content: center;
363
- gap: 20px;
364
- margin-bottom: 20px;
365
- flex-wrap: wrap;
366
- }}
367
- .info-badge {{
368
- background-color: rgba(255, 255, 255, 0.2);
369
- padding: 8px 16px;
370
- border-radius: 20px;
371
- font-size: 0.9em;
372
- }}
373
- .ingredients-identified, .recipe-suggestions {{
374
- background-color: var(--card-background);
375
- border-radius: var(--border-radius);
376
- padding: 25px;
377
- margin-bottom: 30px;
378
- box-shadow: var(--box-shadow);
379
- }}
380
- h2 {{
381
- color: var(--primary-color);
382
- border-bottom: 2px solid var(--primary-color);
383
- padding-bottom: 10px;
384
- margin-top: 0;
385
- }}
386
- .ingredient-group {{
387
- margin-bottom: 20px;
388
- }}
389
- h3 {{
390
- color: var(--accent-color);
391
- margin-bottom: 10px;
392
- }}
393
- .ingredient-list {{
394
- list-style-type: disc;
395
- padding-left: 20px;
396
- }}
397
- .recipe-card {{
398
- background-color: #f9f9f9;
399
- border-radius: var(--border-radius);
400
- padding: 20px;
401
- margin-bottom: 30px;
402
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
403
- }}
404
- .recipe-title {{
405
- color: var(--secondary-color);
406
- font-size: 1.8em;
407
- margin-top: 0;
408
- margin-bottom: 15px;
409
- }}
410
- .recipe-description {{
411
- font-style: italic;
412
- margin-bottom: 20px;
413
- color: #666;
414
- }}
415
- .recipe-ingredients, .recipe-instructions, .recipe-nutrition {{
416
- margin-bottom: 20px;
417
- }}
418
- .recipe-ingredients h4, .recipe-instructions h4, .recipe-nutrition h4 {{
419
- color: var(--primary-color);
420
- margin-bottom: 10px;
421
- }}
422
- .recipe-ingredients ul {{
423
- list-style-type: disc;
424
- }}
425
- .recipe-instructions ol {{
426
- padding-left: 20px;
427
- }}
428
- .recipe-instructions li {{
429
- margin-bottom: 10px;
430
- }}
431
- .recipe-time, .recipe-difficulty {{
432
- color: #666;
433
- margin: 10px 0;
434
- }}
435
- hr {{
436
- border: 0;
437
- height: 1px;
438
- background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0));
439
- margin: 30px 0;
440
- }}
441
- footer {{
442
- text-align: center;
443
- margin-top: 50px;
444
- padding: 20px;
445
- color: #666;
446
- font-size: 0.9em;
447
- }}
448
- </style>
449
- </head>
450
- <body>
451
- <header>
452
- <h1>🍲 Your Personalized Recipes</h1>
453
- <div class="recipe-info">
454
- <span class="info-badge">Dietary: {dietary_restrictions}</span>
455
- <span class="info-badge">Cuisine: {cuisine_preference}</span>
456
- <span class="info-badge">Generated: {time.strftime("%Y-%m-%d")}</span>
457
- </div>
458
- </header>
459
-
460
- <div class="container">
461
- {ingredients_html}
462
-
463
- <hr>
464
-
465
- {recipes_html}
466
-
467
- <footer>
468
- <p>Generated by Visual Recipe Assistant</p>
469
- <p>Powered by Meta's Llama-Vision-Free Model & Together AI</p>
470
- </footer>
471
- </div>
472
- </body>
473
- </html>
474
- """
475
- return html
476
-
477
- def update_gallery(files):
478
- """Update the gallery with uploaded image paths"""
479
- if not files or len(files) == 0:
480
- return gr.update(visible=False)
481
- return gr.update(value=files, visible=True)
482
-
483
- def process_recipe_request(api_key, files, num_recipes, dietary_restrictions, cuisine_preference):
484
- """Process the recipe request with uploaded files"""
485
- if not files:
486
- return ["Please upload at least one image of ingredients.", None]
487
- return get_recipe_suggestions(api_key, files, num_recipes, dietary_restrictions, cuisine_preference)
488
-
489
- custom_css = """
490
- @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
491
- :root {
492
- --primary-color: #2C786C; /* Rich teal */
493
- --primary-light: #40A799; /* Lighter teal for hover */
494
- --secondary-color: #F58634; /* Warm orange */
495
- --accent-color: #FAB95B; /* Soft amber */
496
- --background-color: #F8F9FA; /* Clean off-white */
497
- --text-color: #2A2D34; /* Deep charcoal */
498
- --card-background: #FFFFFF; /* White for cards */
499
- --border-radius: 12px;
500
- --font-family: 'Poppins', sans-serif;
501
- --box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 20px;
502
- --hover-shadow: rgba(0, 0, 0, 0.15) 0px 8px 30px;
503
- }
504
- body {
505
- font-family: var(--font-family);
506
- background-color: var(--background-color);
507
- color: var(--text-color);
508
- margin: 0;
509
- padding: 0;
510
- }
511
- .container {
512
- width: 100%;
513
- margin: 0 auto;
514
- padding: 20px;
515
- }
516
- .app-header {
517
- background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
518
- color: white;
519
- padding: 60px 20px;
520
- text-align: center;
521
- border-radius: 0 0 30px 30px;
522
- box-shadow: var(--box-shadow);
523
- width: 100%;
524
- }
525
- .app-title {
526
- font-size: 2.8em;
527
- font-weight: 700;
528
- margin-bottom: 10px;
529
- color: navajowhite;
530
- }
531
- .app-subtitle {
532
- font-size: 1.3em;
533
- font-weight: 400;
534
- max-width: 800px;
535
- margin: 0 auto;
536
- color: white;
537
- }
538
- .input-section, .output-section {
539
- background-color: var(--card-background);
540
- border-radius: var(--border-radius);
541
- padding: 30px;
542
- box-shadow: var(--box-shadow);
543
- margin-bottom: 30px;
544
- width: 100%;
545
- }
546
- .section-header {
547
- font-size: 1.6em;
548
- font-weight: 600;
549
- color: var(--text-color);
550
- margin-bottom: 20px;
551
- border-bottom: 2px solid var(--primary-color);
552
- padding-bottom: 10px;
553
- }
554
- .section-header2 {
555
- font-size: 1.6em;
556
- font-weight: 600;
557
- color: var(--text-color);
558
- border-bottom: 2px solid var(--primary-color);
559
- padding-bottom: 10px;
560
- }
561
- .image-upload-container {
562
- padding: 40px;
563
- text-align: center;
564
- background-color: white;
565
- transition: all 0.3s ease;
566
- }
567
- .image-upload-container:hover {
568
- border-color: var(--primary-color);
569
- background-color: rgba(44, 120, 108, 0.08);
570
- }
571
- button.primary-button {
572
- background-color: var(--primary-color);
573
- color: white;
574
- border: none;
575
- padding: 16px 32px;
576
- border-radius: 6px;
577
- font-size: 1.1em;
578
- cursor: pointer;
579
- transition: all 0.3s ease;
580
- width: 100%;
581
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
582
- }
583
- button.primary-button:hover {
584
- background-color: #E15F52;
585
- box-shadow: var(--hover-shadow);
586
- }
587
- button.download-button {
588
- background-color: var(--secondary-color);
589
- color: white;
590
- border: none;
591
- padding: 12px 24px;
592
- border-radius: 6px;
593
- font-size: 1em;
594
- cursor: pointer;
595
- transition: all 0.3s ease;
596
- margin-top: 15px;
597
- display: flex;
598
- align-items: center;
599
- justify-content: center;
600
- gap: 8px;
601
- margin-left: auto;
602
- margin-right: auto;
603
- margin-bottom: 25px !important;
604
- }
605
- button.download-button:hover {
606
- background-color: #3da037;
607
- box-shadow: var(--hover-shadow);
608
- }
609
- .gallery-container {
610
- display: grid;
611
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
612
- gap: 20px;
613
- margin-top: 30px;
614
- }
615
- .gallery-item {
616
- border-radius: var(--border-radius);
617
- overflow: hidden;
618
- box-shadow: var(--box-shadow);
619
- transition: transform 0.3s ease;
620
- aspect-ratio: 1 / 1;
621
- object-fit: cover;
622
- }
623
- .gallery-item:hover {
624
- transform: scale(1.05);
625
- box-shadow: var(--hover-shadow);
626
- }
627
- .recipe-output {
628
- font-size: 1.2em;
629
- line-height: 1.7;
630
- color: var(--text-color);
631
- max-height: 600px;
632
- overflow-y: auto;
633
- padding-right: 15px;
634
- }
635
- .ingredients-identified, .recipe-suggestions {
636
- background-color: #f9f9f9;
637
- border-radius: var(--border-radius);
638
- padding: 20px;
639
- margin-bottom: 20px;
640
- }
641
- .recipe-card {
642
- background-color: white;
643
- border-radius: var(--border-radius);
644
- padding: 20px;
645
- margin-bottom: 20px;
646
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
647
- }
648
- .recipe-title {
649
- color: var(--secondary-color);
650
- font-size: 1.8em;
651
- margin-top: 0;
652
- margin-bottom: 15px;
653
- }
654
- .recipe-description {
655
- font-style: italic;
656
- margin-bottom: 20px;
657
- color: #666;
658
- }
659
- .recipe-ingredients, .recipe-instructions, .recipe-nutrition {
660
- margin-bottom: 20px;
661
- }
662
- .recipe-ingredients h4, .recipe-instructions h4, .recipe-nutrition h4 {
663
- color: var(--primary-color);
664
- margin-bottom: 10px;
665
- }
666
- .footer {
667
- background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
668
- padding: 40px 20px;
669
- text-align: center;
670
- color: var(--text-color);
671
- font-size: 1.1em;
672
- box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
673
- width: 100%;
674
- }
675
- .footer-content {
676
- max-width: 800px;
677
- margin: 0 auto;
678
- }
679
- .footer-brand {
680
- font-weight: 700;
681
- color: navajowhite;
682
- }
683
- .footer-links a {
684
- color: white;
685
- text-decoration: none;
686
- margin: 0 15px;
687
- transition: color 0.3s ease;
688
- }
689
- .footer-links a:hover {
690
- color: var(--secondary-color);
691
- }
692
- """
693
-
694
- html_header = """
695
- <div class="app-header">
696
- <div class="app-title">🍲 Visual Recipe Assistant</div>
697
- <div class="app-subtitle">Upload images of ingredients you have on hand and get personalized recipe suggestions powered by AI</div>
698
- </div>
699
- """
700
-
701
- html_footer = """
702
- <div class="footer">
703
- <div class="footer-content">
704
- <p><span class="footer-brand">🍲 Visual Recipe Assistant</span></p>
705
- <p>Powered by Meta's Llama-Vision-Free Model & Together AI</p>
706
- <p>Upload multiple ingredient images for more creative recipe combinations</p>
707
- <div class="footer-links">
708
- <a href="#" onclick="document.getElementById('how-to-use').scrollIntoView({ behavior: 'smooth' }); return false;">How It Works</a>
709
- <a href="https://api.whatsapp.com/send/?phone=%2B8801719296601&text&type=phone_number&app_absent=0" target="_blank">Contact Us</a>
710
- </div>
711
- </div>
712
- </div>
713
- """
714
-
715
- with gr.Blocks(css=custom_css) as app:
716
- gr.HTML(html_header)
717
-
718
- with gr.Accordion("How It Works", open=False, elem_id="how-to-use"):
719
- gr.Markdown("""
720
- ### Getting Started:
721
- 1. Enter your Together Ai API key.
722
- 2. Upload multiple ingredient images.
723
- 3. Select Number of Recipes Suggestions.
724
- 4. Select Dietary Restrictions.
725
- 5. Cuisine Preference
726
-
727
- ### Results:
728
- - Click Get Recipe Suggestions.
729
- - Wait for few minutes to get full suggestions.
730
- - You can download the suggested recipe.
731
- """)
732
-
733
- # Store the generated html file path for download
734
- html_file_path = gr.State(None)
735
-
736
- with gr.Row():
737
- with gr.Column(scale=1):
738
- with gr.Group(elem_classes="input-section"):
739
- gr.HTML('<h3 class="section-header">API Configuration</h3>')
740
- api_key_input = gr.Textbox(
741
- label="Together API Key",
742
- placeholder="Enter your Together API key here...",
743
- type="password",
744
- elem_classes="input-group"
745
- )
746
-
747
- gr.HTML('<h3 class="section-header">Upload Ingredients</h3>')
748
- file_upload = gr.File(
749
- label="Upload images of ingredients",
750
- file_types=["image"],
751
- file_count="multiple",
752
- elem_classes="image-upload-container"
753
- )
754
-
755
- image_input = gr.Gallery(
756
- label="Uploaded Ingredients",
757
- elem_id="ingredient-gallery",
758
- columns=3,
759
- rows=2,
760
- height="auto",
761
- visible=False
762
- )
763
-
764
- gr.HTML('<h3 class="section-header2">Recipe Preferences</h3>')
765
- with gr.Row():
766
- num_recipes = gr.Slider(
767
- minimum=1,
768
- maximum=5,
769
- value=3,
770
- step=1,
771
- label="Number of Recipe Suggestions",
772
- elem_classes="input-group"
773
- )
774
-
775
- with gr.Row():
776
- with gr.Column():
777
- dietary_restrictions = gr.Dropdown(
778
- choices=["None", "Vegetarian", "Vegan", "Gluten-Free", "Dairy-Free", "Low-Carb", "Keto", "Paleo"],
779
- value="None",
780
- label="Dietary Restrictions",
781
- elem_classes="input-group"
782
- )
783
-
784
- with gr.Column():
785
- cuisine_preference = gr.Dropdown(
786
- choices=["Any", "Italian", "Asian", "Mexican", "Mediterranean", "Indian", "American", "French", "Middle Eastern"],
787
- value="Any",
788
- label="Cuisine Preference",
789
- elem_classes="input-group"
790
- )
791
-
792
- submit_button = gr.Button("Get Recipe Suggestions", elem_classes="primary-button")
793
-
794
- with gr.Column(scale=1):
795
- with gr.Group(elem_classes="output-section"):
796
- gr.HTML('<h3 class="section-header">Your Personalized Recipes</h3>')
797
- output = gr.HTML(elem_classes="recipe-output")
798
- download_button = gr.Button("📥 Download Recipes (See below to download)", elem_classes="download-button", visible=False, elem_id="download-button")
799
-
800
- gr.HTML(html_footer)
801
-
802
- # Update functions
803
- def process_and_update_file_path(api_key, files, num_recipes, dietary_restrictions, cuisine_preference):
804
- """Process recipe request and update file path state"""
805
- result = process_recipe_request(api_key, files, num_recipes, dietary_restrictions, cuisine_preference)
806
- html_content = result[0]
807
- file_path = result[1]
808
- return html_content, file_path, gr.update(visible=file_path is not None)
809
-
810
- file_upload.change(fn=update_gallery, inputs=file_upload, outputs=image_input)
811
-
812
- submit_button.click(
813
- fn=process_and_update_file_path,
814
- inputs=[api_key_input, file_upload, num_recipes, dietary_restrictions, cuisine_preference],
815
- outputs=[output, html_file_path, download_button]
816
- )
817
-
818
- # Handle download button click
819
- download_button.click(
820
- fn=lambda x: x,
821
- inputs=html_file_path,
822
- outputs=gr.File(label="Download Recipe HTML")
823
- )
824
-
825
- if __name__ == "__main__":
826
- app.launch()