jerrychen428 commited on
Commit
a3acdb5
·
verified ·
1 Parent(s): 013d633

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -81
app.py CHANGED
@@ -6,12 +6,24 @@ import gradio as gr
6
  from google import genai
7
  import resend
8
  import html
 
 
 
 
9
 
10
  class GeminiImageAnalyzer:
11
  def __init__(self, gemini_api_key, resend_api_key):
12
  """初始化Gemini客戶端和Resend"""
 
 
 
 
 
13
  self.gemini_api_key = gemini_api_key
14
- self.client = genai.Client(api_key=gemini_api_key)
 
 
 
15
  resend.api_key = resend_api_key
16
 
17
  def validate_image(self, image_path):
@@ -37,45 +49,40 @@ class GeminiImageAnalyzer:
37
  mime_type = "image/jpeg"
38
  elif file_extension == '.png':
39
  mime_type = "image/png"
 
 
40
 
41
  return encoded_string, mime_type
42
  except Exception as e:
43
  raise Exception(f"圖片編碼失敗: {str(e)}")
44
 
45
- def analyze_image(self, image_path, prompt="請詳細描述這張圖片的內容,並提取圖片中的文字,使用繁體中文"):
46
  """使用Gemini API分析圖片"""
47
  try:
48
  # 驗證圖片
49
  self.validate_image(image_path)
 
50
 
51
  # 編碼圖片
52
  encoded_image, mime_type = self.encode_image(image_path)
 
53
 
54
  # 構建請求內容
55
- contents = [
56
- {
57
- "role": "user",
58
- "parts": [
59
- {"text": prompt},
60
- {
61
- "inline_data": {
62
- "mime_type": mime_type,
63
- "data": encoded_image
64
- }
65
- }
66
- ]
67
- }
68
- ]
69
 
70
  # 調用Gemini API
71
- response = self.client.models.generate_content(
72
- model="gemini-2.0-flash-exp", # 使支援視覺的模型
73
- contents=contents
74
- )
75
-
76
  return response.text
77
 
78
  except Exception as e:
 
79
  return f"分析失敗: {str(e)}"
80
 
81
  def send_analysis_email(self, analysis_result, recipient_email, subject="圖片分析結果"):
@@ -87,23 +94,29 @@ class GeminiImageAnalyzer:
87
  <head>
88
  <meta charset="UTF-8">
89
  <title>圖片分析結果</title>
 
 
 
 
 
 
 
90
  </head>
91
  <body>
92
- <h2>圖片分析結果</h2>
93
- <hr>
94
- <div style="font-family: Arial, sans-serif; line-height: 1.6;">
95
  <p><strong>分析時間:</strong>{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
96
  <h3>分析內容:</h3>
97
- <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; border-left: 4px solid #007bff;">
98
  {html.escape(analysis_result).replace('\n', '<br>')}
99
  </div>
 
100
  </div>
101
- <br>
102
- <p style="color: #666; font-size: 12px;">此郵件由Gemini圖片分析系統自動發送</p>
103
  </body>
104
  </html>
105
  """
106
-
107
  params = {
108
  "from": "Acme <onboarding@resend.dev>",
109
  "to": [recipient_email],
@@ -113,29 +126,26 @@ class GeminiImageAnalyzer:
113
  }
114
 
115
  email_response = resend.Emails.send(params)
 
116
  return f"郵件發送成功!郵件ID: {email_response.get('id', 'Unknown')}"
117
 
118
  except Exception as e:
 
119
  return f"郵件發送失敗: {str(e)}"
120
 
121
  def create_gradio_app():
122
- # 從環境變量獲取API密鑰
123
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
124
- RESEND_API_KEY = os.getenv("RESEND_API_KEY")
125
-
126
- # 檢查API密鑰是否存在
127
- if not GEMINI_API_KEY:
128
- raise ValueError("請在HuggingFace Spaces設定中添加 GEMINI_API_KEY 環境變量")
129
- if not RESEND_API_KEY:
130
- raise ValueError("請在HuggingFace Spaces設定中添加 RESEND_API_KEY 環境變量")
131
-
132
  # 創建分析器實例
133
- analyzer = GeminiImageAnalyzer(GEMINI_API_KEY, RESEND_API_KEY)
134
 
135
- def process_image_and_send_email(image, prompt, recipient_email, email_subject, send_email_flag):
136
  """處理圖片分析並可選發送郵件"""
137
  try:
138
- if image is None:
139
  return "請先上傳圖片", ""
140
 
141
  # 使用默認提示詞如果為空
@@ -143,7 +153,7 @@ def create_gradio_app():
143
  prompt = "請詳細描述這張圖片的內容,並提取圖片中的文字,使用繁體中文"
144
 
145
  # 分析圖片
146
- analysis_result = analyzer.analyze_image(image, prompt)
147
 
148
  email_status = ""
149
  if send_email_flag and recipient_email.strip():
@@ -166,28 +176,16 @@ def create_gradio_app():
166
  return f"處理失敗: {str(e)}", ""
167
 
168
  # 創建Gradio介面
169
- with gr.Blocks(
170
- title="Gemini 圖片分析與郵件發送系統",
171
- theme=gr.themes.Soft(),
172
- css="""
173
- .gradio-container {
174
- max-width: 1200px !important;
175
- }
176
- """
177
- ) as app:
178
- gr.Markdown(
179
- "# 🔍 Gemini 圖片分析與郵件發送系統\n"
180
- "上傳圖片進行AI分析,並可選擇將結果發送到指定郵箱\n"
181
- "---"
182
- )
183
 
184
  with gr.Row():
185
  with gr.Column(scale=1):
186
  # 圖片上傳
187
  image_input = gr.Image(
188
  type="filepath",
189
- label="上傳圖片 (支援 JPG, PNG 格式)",
190
- height=300
191
  )
192
 
193
  # 提示詞輸入
@@ -254,7 +252,6 @@ def create_gradio_app():
254
  - 支援的圖片格式:JPG, JPEG, PNG
255
  - 分析結果會以繁體中文顯示
256
  - 郵件功能為可選,不影響圖片分析功能
257
- - 本應用運行在 HuggingFace Spaces 上
258
  """)
259
 
260
  # 綁定事件
@@ -285,28 +282,9 @@ def create_gradio_app():
285
  lambda x=example: x,
286
  outputs=prompt_input
287
  )
288
-
289
- # 添加頁腳
290
- gr.Markdown(
291
- "---\n"
292
- "💡 **提示**: 如果遇到API錯誤,請檢查環境變量設定是否正確\n"
293
- "🔗 **技術**: 使用 Gemini 2.0 Flash 模型進行圖片分析"
294
- )
295
 
296
  return app
297
 
298
- # HuggingFace Spaces 入口點
299
- if __name__ == "__main__":
300
- try:
301
- app = create_gradio_app()
302
- app.launch() # HuggingFace Spaces 會自動處理部署配置
303
- except Exception as e:
304
- print(f"應用啟動失敗: {e}")
305
- # 創建一個錯誤頁面
306
- error_app = gr.Interface(
307
- fn=lambda: f"配置錯誤: {str(e)}\n\n請檢查以下設定:\n1. GEMINI_API_KEY 環境變量\n2. RESEND_API_KEY 環境變量",
308
- inputs=[],
309
- outputs=gr.Textbox(label="錯誤信息"),
310
- title="配置錯誤"
311
- )
312
- error_app.launch()
 
6
  from google import genai
7
  import resend
8
  import html
9
+ import logging
10
+
11
+ # 設置日誌級別,以查看 Gradio 和底層庫的詳細資訊
12
+ logging.basicConfig(level=logging.INFO)
13
 
14
  class GeminiImageAnalyzer:
15
  def __init__(self, gemini_api_key, resend_api_key):
16
  """初始化Gemini客戶端和Resend"""
17
+ if not gemini_api_key:
18
+ raise ValueError("未找到 Gemini API 金鑰。請將其設置為環境變數。")
19
+ if not resend_api_key:
20
+ raise ValueError("未找到 Resend API 金鑰。請將其設置為環境變數。")
21
+
22
  self.gemini_api_key = gemini_api_key
23
+ # 使用提供的金鑰初始化 Gemini 客戶端
24
+ genai.configure(api_key=gemini_api_key)
25
+ self.client = genai.GenerativeModel(model_name="gemini-1.5-flash")
26
+ # 使用提供的金鑰初始化 Resend
27
  resend.api_key = resend_api_key
28
 
29
  def validate_image(self, image_path):
 
49
  mime_type = "image/jpeg"
50
  elif file_extension == '.png':
51
  mime_type = "image/png"
52
+ else:
53
+ mime_type = "application/octet-stream" # 預設類型
54
 
55
  return encoded_string, mime_type
56
  except Exception as e:
57
  raise Exception(f"圖片編碼失敗: {str(e)}")
58
 
59
+ def analyze_image(self, image_path, prompt):
60
  """使用Gemini API分析圖片"""
61
  try:
62
  # 驗證圖片
63
  self.validate_image(image_path)
64
+ logging.info(f"成功驗證圖片: {image_path}")
65
 
66
  # 編碼圖片
67
  encoded_image, mime_type = self.encode_image(image_path)
68
+ logging.info(f"圖片成功編碼為 {mime_type}")
69
 
70
  # 構建請求內容
71
+ image_part = {
72
+ "mime_type": mime_type,
73
+ "data": base64.b64decode(encoded_image)
74
+ }
75
+
76
+ contents = [prompt, image_part]
 
 
 
 
 
 
 
 
77
 
78
  # 調用Gemini API
79
+ response = self.client.generate_content(contents)
80
+ logging.info("成功調 Gemini API")
81
+
 
 
82
  return response.text
83
 
84
  except Exception as e:
85
+ logging.error(f"分析失敗: {str(e)}")
86
  return f"分析失敗: {str(e)}"
87
 
88
  def send_analysis_email(self, analysis_result, recipient_email, subject="圖片分析結果"):
 
94
  <head>
95
  <meta charset="UTF-8">
96
  <title>圖片分析結果</title>
97
+ <style>
98
+ body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
99
+ .container {{ max-width: 600px; margin: 20px auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }}
100
+ h2 {{ color: #0056b3; }}
101
+ .info-box {{ background-color: #f0f8ff; padding: 15px; border-radius: 5px; border-left: 4px solid #007bff; }}
102
+ .footer {{ margin-top: 20px; font-size: 12px; color: #777; }}
103
+ </style>
104
  </head>
105
  <body>
106
+ <div class="container">
107
+ <h2>圖片分析結果</h2>
108
+ <hr>
109
  <p><strong>分析時間:</strong>{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
110
  <h3>分析內容:</h3>
111
+ <div class="info-box">
112
  {html.escape(analysis_result).replace('\n', '<br>')}
113
  </div>
114
+ <p class="footer">此郵件由Gemini圖片分析系統自動發送</p>
115
  </div>
 
 
116
  </body>
117
  </html>
118
  """
119
+
120
  params = {
121
  "from": "Acme <onboarding@resend.dev>",
122
  "to": [recipient_email],
 
126
  }
127
 
128
  email_response = resend.Emails.send(params)
129
+ logging.info(f"郵件發送成功!郵件ID: {email_response.get('id', 'Unknown')}")
130
  return f"郵件發送成功!郵件ID: {email_response.get('id', 'Unknown')}"
131
 
132
  except Exception as e:
133
+ logging.error(f"郵件發送失敗: {str(e)}")
134
  return f"郵件發送失敗: {str(e)}"
135
 
136
  def create_gradio_app():
137
+ """創建 Gradio 應用介面"""
138
+ # 從環境變數中讀取 API 密鑰
139
+ gemini_api_key = os.getenv("GEMINI_API_KEY")
140
+ resend_api_key = os.getenv("RESEND_API_KEY")
141
+
 
 
 
 
 
142
  # 創建分析器實例
143
+ analyzer = GeminiImageAnalyzer(gemini_api_key, resend_api_key)
144
 
145
+ def process_image_and_send_email(image_file, prompt, recipient_email, email_subject, send_email_flag):
146
  """處理圖片分析並可選發送郵件"""
147
  try:
148
+ if image_file is None:
149
  return "請先上傳圖片", ""
150
 
151
  # 使用默認提示詞如果為空
 
153
  prompt = "請詳細描述這張圖片的內容,並提取圖片中的文字,使用繁體中文"
154
 
155
  # 分析圖片
156
+ analysis_result = analyzer.analyze_image(image_file, prompt)
157
 
158
  email_status = ""
159
  if send_email_flag and recipient_email.strip():
 
176
  return f"處理失敗: {str(e)}", ""
177
 
178
  # 創建Gradio介面
179
+ with gr.Blocks(title="Gemini 圖片分析與郵件發送系統") as app:
180
+ gr.Markdown("# 🔍 Gemini 圖片分析與郵件發送系統")
181
+ gr.Markdown("上傳圖片進行AI分析,並可選擇將結果發送到指定郵箱")
 
 
 
 
 
 
 
 
 
 
 
182
 
183
  with gr.Row():
184
  with gr.Column(scale=1):
185
  # 圖片上傳
186
  image_input = gr.Image(
187
  type="filepath",
188
+ label="上傳圖片 (支援 JPG, PNG 格式)"
 
189
  )
190
 
191
  # 提示詞輸入
 
252
  - 支援的圖片格式:JPG, JPEG, PNG
253
  - 分析結果會以繁體中文顯示
254
  - 郵件功能為可選,不影響圖片分析功能
 
255
  """)
256
 
257
  # 綁定事件
 
282
  lambda x=example: x,
283
  outputs=prompt_input
284
  )
 
 
 
 
 
 
 
285
 
286
  return app
287
 
288
+ # Hugging Face Spaces 上,應用會自動被 `app.py` 啟動,
289
+ # 所以我們只需將主函數的啟動邏輯放在檔案的頂層。
290
+ app = create_gradio_app()