Spaces:
Running
Running
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, render_template, request, jsonify
|
2 |
+
from gradio_client import Client, handle_file
|
3 |
+
import os
|
4 |
+
import json
|
5 |
+
import random # For demo data variation
|
6 |
+
|
7 |
+
app = Flask(__name__)
|
8 |
+
app.config['UPLOAD_FOLDER'] = 'uploads'
|
9 |
+
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB max upload size
|
10 |
+
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
11 |
+
|
12 |
+
|
13 |
+
API_TOKEN_HF = os.environ['API_TOKEN_HF']
|
14 |
+
|
15 |
+
# --- Demo Data ---
|
16 |
+
# This will be used for initial page load and if the API call fails or is mocked
|
17 |
+
BASE_AROMA_NAMES_ORDER = [ # Keep this order consistent with frontend
|
18 |
+
"Rose", "Ocean Breeze", "Fresh Cut Grass", "Lemon Zest", "Lavender",
|
19 |
+
"Sweet Orange", "Cool Mint", "Vanilla Bean", "Wild Berry", "Spring Rain"
|
20 |
+
]
|
21 |
+
|
22 |
+
client = Client("doevent/gemini_others", hf_token=API_TOKEN_HF, download_files="uploads")
|
23 |
+
|
24 |
+
def get_demo_perfume_data():
|
25 |
+
perfume_name = random.choice(["Demo Garden", "Demo Whisper", "Demo Flare", "Demo Dream"])
|
26 |
+
slogan = random.choice([
|
27 |
+
"Awaken your senses.", "Dare to dream.", "The essence of you.", "An unforgettable journey."
|
28 |
+
])
|
29 |
+
|
30 |
+
# Create a random set of aromas and doses
|
31 |
+
num_active_aromas = random.randint(2, 10)
|
32 |
+
active_aromas_sample = random.sample(BASE_AROMA_NAMES_ORDER, num_active_aromas)
|
33 |
+
|
34 |
+
aromas_data = []
|
35 |
+
total_dose_for_demo = 0 # To simulate filling the flask
|
36 |
+
for i, name in enumerate(BASE_AROMA_NAMES_ORDER):
|
37 |
+
if name in active_aromas_sample and total_dose_for_demo < 0.8: # Limit total demo dose
|
38 |
+
dose = round(random.uniform(0.1, 0.3), 2)
|
39 |
+
aromas_data.append({"name": name, "dose": dose})
|
40 |
+
total_dose_for_demo += dose
|
41 |
+
# else: # We can include all aromas with 0 dose if needed by frontend for table population
|
42 |
+
# aromas_data.append({"name": name, "dose": 0.0})
|
43 |
+
|
44 |
+
return {
|
45 |
+
"perfume_name": perfume_name,
|
46 |
+
"slogan": slogan,
|
47 |
+
"aromas": aromas_data # This list might only contain active aromas
|
48 |
+
}
|
49 |
+
|
50 |
+
|
51 |
+
@app.route('/')
|
52 |
+
def index():
|
53 |
+
initial_data = get_demo_perfume_data()
|
54 |
+
return render_template('index.html', initial_data=initial_data, base_aromas_ordered=BASE_AROMA_NAMES_ORDER)
|
55 |
+
|
56 |
+
|
57 |
+
@app.route('/analyze_image', methods=['POST'])
|
58 |
+
def analyze_image_route():
|
59 |
+
if 'imageFile' not in request.files:
|
60 |
+
return jsonify({"error": "No image file provided"}), 400
|
61 |
+
|
62 |
+
file = request.files['imageFile']
|
63 |
+
if file.filename == '':
|
64 |
+
return jsonify({"error": "No image selected"}), 400
|
65 |
+
|
66 |
+
if file:
|
67 |
+
try:
|
68 |
+
filename = "uploaded_image_" + file.filename # Add some uniqueness if needed
|
69 |
+
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
70 |
+
file.save(image_path)
|
71 |
+
|
72 |
+
custom_prompt_for_perfume = (
|
73 |
+
"Based on the visual elements, mood, and potential theme of this image, "
|
74 |
+
"invent a unique perfume. I need the following information structured as a JSON object:\n"
|
75 |
+
"1. 'perfume_name': A creative and fitting name for the perfume.\n"
|
76 |
+
"2. 'slogan': A short, catchy slogan for this perfume.\n"
|
77 |
+
"3. 'aromas': A list of 2 to 10 base aroma notes. For each note, provide its 'name' "
|
78 |
+
f"(chosen from this list: {', '.join(BASE_AROMA_NAMES_ORDER)}, or a very similar common scent if appropriate) "
|
79 |
+
"and its 'dose' as a decimal proportion (e.g., 0.3 for 30%). "
|
80 |
+
"Ideally, the total dose should be equal to a full bottle of 10.0 for 100%"
|
81 |
+
"\nExample of 'aromas' list: [{'name': 'Rose', 'dose': 0.4}, {'name': 'Lavender', 'dose': 0.2}]"
|
82 |
+
)
|
83 |
+
|
84 |
+
api_result_raw = client.predict(
|
85 |
+
files_list=[handle_file(image_path)],
|
86 |
+
message=custom_prompt_for_perfume,
|
87 |
+
model_name="gemini-2.0-flash",
|
88 |
+
temperature=0.7,
|
89 |
+
max_output_tokens=1024,
|
90 |
+
system_prompt_load= "",
|
91 |
+
type_mime_name="application/json",
|
92 |
+
output_schema=None,
|
93 |
+
google_search_opt=False,
|
94 |
+
other_options_json=None,
|
95 |
+
thinking_mode=False,
|
96 |
+
thinking_budget=None,
|
97 |
+
api_name="/predict"
|
98 |
+
)
|
99 |
+
|
100 |
+
|
101 |
+
os.remove(image_path) # Clean up uploaded file
|
102 |
+
|
103 |
+
# print("Raw API Result:", api_result_raw)
|
104 |
+
api_result_raw = api_result_raw[0]["candidates"][0]["content"]["parts"][0]['text']
|
105 |
+
print("Raw API Result:", api_result_raw)
|
106 |
+
return api_result_raw
|
107 |
+
|
108 |
+
except Exception as e:
|
109 |
+
print(f"Error during API call or processing: {e}")
|
110 |
+
# In case of any error, return demo data for now for frontend testing
|
111 |
+
# os.remove(image_path) # Ensure cleanup even on error
|
112 |
+
error_data = get_demo_perfume_data()
|
113 |
+
error_data["api_error"] = f"An error occurred: {str(e)}. Displaying demo data."
|
114 |
+
return jsonify(error_data), 500 # Send 500 to indicate server-side error
|
115 |
+
|
116 |
+
return jsonify({"error": "Invalid file"}), 400
|
117 |
+
|
118 |
+
|
119 |
+
if __name__ == '__main__':
|
120 |
+
app.run(debug=True) # Use a different port if 5000 is common
|