hazelhh commited on
Commit
6c8ae75
·
verified ·
1 Parent(s): 8000dc6

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +150 -232
main.py CHANGED
@@ -1,6 +1,11 @@
1
- from fastapi import FastAPI, Request, Header, BackgroundTasks, HTTPException, status
 
 
2
  from fastapi.middleware.cors import CORSMiddleware
 
3
  from fastapi.staticfiles import StaticFiles
 
 
4
  from linebot import LineBotApi, WebhookHandler
5
  from linebot.exceptions import InvalidSignatureError
6
  from linebot.models import (
@@ -10,62 +15,36 @@ from linebot.models import (
10
  ImageSendMessage,
11
  ImageMessage,
12
  )
13
- from google import genai
14
- from google.genai import types
15
- from PIL import Image
16
- from collections import defaultdict
17
- import os
18
- import io
19
- import requests
20
  import uvicorn
21
- import logging
22
- import base64
23
  from langchain_core.prompts import ChatPromptTemplate
24
  from langchain_core.tools import tool
25
  from langchain_google_genai import ChatGoogleGenerativeAI
26
  from langchain.agents import AgentExecutor, create_tool_calling_agent
27
- from pydantic import BaseModel, Field
28
- from typing import List
29
 
30
- # ==========================# 環境設定與工具函式# ==========================#
31
- # 設置日誌記錄,級別為 INFO,格式包含時間、級別和訊息
32
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
33
 
34
- # 檢查必要的環境變數是否都已設定
35
- if not all(k in os.environ for k in ["CHANNEL_ACCESS_TOKEN", "CHANNEL_SECRET", "GOOGLE_API_KEY", "HF_SPACE"]):
36
- logging.error("Missing environment variables. Please set CHANNEL_ACCESS_TOKEN, CHANNEL_SECRET, GOOGLE_API_KEY, and HF_SPACE.")
37
- raise ValueError("Missing environment variables. Please set CHANNEL_ACCESS_TOKEN, CHANNEL_SECRET, GOOGLE_API_KEY, and HF_SPACE.")
38
 
39
- # 獲取環境變數中的金鑰和 URL
40
  google_api = os.environ["GOOGLE_API_KEY"]
41
- line_channel_access_token = os.environ["CHANNEL_ACCESS_TOKEN"]
42
- line_channel_secret = os.environ["CHANNEL_SECRET"]
43
- HF_SPACE_URL = os.environ["HF_SPACE"]
44
-
45
- # Line Bot API 設定
46
- line_bot_api = LineBotApi(line_channel_access_token)
47
- line_handler = WebhookHandler(line_channel_secret)
48
-
49
- # Google AI API 設定
50
  genai_client = genai.Client(api_key=google_api)
51
 
52
- # 使用者狀態追蹤,用來儲存已上傳的衣物圖片 URL
53
- user_states = defaultdict(lambda: {
54
- "upper_body_images": [],
55
- "lower_body_images": [],
56
- "current_mode": None,
57
- })
58
 
59
- MAX_IMAGES_PER_TYPE = 3
60
- IMAGIN_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent"
61
 
62
  # 建立 FastAPI 應用程式
63
  app = FastAPI()
64
-
65
- # 設定靜態文件服務,用於託管圖片
66
  app.mount("/static", StaticFiles(directory="static"), name="static")
67
 
68
- # 設定 CORS 跨域請求
69
  app.add_middleware(
70
  CORSMiddleware,
71
  allow_origins=["*"],
@@ -74,134 +53,138 @@ app.add_middleware(
74
  allow_headers=["*"],
75
  )
76
 
77
- def get_base64_from_url(image_url: str):
78
- """從 URL 下載圖片並轉換為 Base64 編碼。"""
79
- try:
80
- response = requests.get(image_url)
81
- response.raise_for_status()
82
- return base64.b64encode(response.content).decode('utf-8')
83
- except requests.exceptions.RequestException as e:
84
- logging.error(f"Error fetching image from URL: {image_url}, error: {e}")
85
- return None
86
-
87
- def save_image_locally(image_binary: bytes):
88
- """
89
- 將二進位圖片資料儲存到本地,並返回一個可供外部存取的 URL。
90
- """
91
- try:
92
- # 確保 'static' 資料夾存在
93
- if not os.path.exists("static"):
94
- os.makedirs("static")
95
-
96
- image = Image.open(io.BytesIO(image_binary))
97
- # 隨機生成一個檔案名以避免衝突
98
- file_name = f"static/{os.urandom(16).hex()}.png"
99
- image.save(file_name, format="PNG")
100
-
101
- image_url = os.path.join(HF_SPACE_URL, file_name)
102
- logging.info(f"Image successfully saved locally: {image_url}")
103
- return image_url
104
- except Exception as e:
105
- logging.error(f"Error saving image locally: {e}")
106
- return None
107
-
108
  def get_image_url_from_line(message_id):
109
  """
110
  從 Line 訊息 ID 獲取圖片內容並儲存到暫存檔案。
111
  """
112
  try:
113
  message_content = line_bot_api.get_message_content(message_id)
114
- # 取得二進位圖片資料
115
- image_binary = message_content.content
116
- # 使用 save_image_locally 函式儲存圖片並取得 URL
117
- return save_image_locally(image_binary)
 
 
118
  except Exception as e:
119
  print(f"❌ 圖片取得失敗:{e}")
120
  return None
121
 
122
- # ==========================# LangChain 工具定義# ==========================#
123
- # 定義工具的輸入模型,明確指定參數的型別和描述
124
- class OutfitInput(BaseModel):
125
- """用於生成穿搭圖片的輸入參數。"""
126
- upper_body_urls: List[str] = Field(..., description="上衣圖片的 URL 列表。")
127
- lower_body_urls: List[str] = Field(..., description="褲子/裙子圖片的 URL 列表。")
 
128
 
129
- @tool(args_schema=OutfitInput)
130
- def generate_outfit_from_clothes(upper_body_urls: list, lower_body_urls: list) -> str:
 
131
  """
132
- 這個工具可以根據提供的上衣和褲子/裙子圖片 URLs,生成一套全新的穿搭圖片。
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  Args:
135
- upper_body_urls: 一組上衣圖片的 URL 列表。
136
- lower_body_urls: 一組褲子/裙子圖片的 URL 列表。
137
 
138
  Returns:
139
  回傳生成圖片的 URL。
140
  """
141
- logging.info("Attempting to generate a new outfit image.")
142
- prompt = "使用提供的上衣和褲子/裙子圖片,生成一套完整且時尚的穿搭圖片。請將衣服呈現在一個有模特兒穿著或是在平面上呈現的完整畫面中。風格應與提供的衣物相符。"
143
-
144
- all_images_base64 = (
145
- [get_base64_from_url(url) for url in upper_body_urls] +
146
- [get_base64_from_url(url) for url in lower_body_urls]
147
- )
148
-
149
- payload = {
150
- "contents": [
151
- {
152
- "parts": [
153
- {"text": prompt}
154
- ] + [
155
- {"inlineData": {"mimeType": "image/jpeg", "data": img_base64}} for img_base64 in all_images_base64
156
- ]
157
- }
158
- ],
159
- "generationConfig": {
160
- "responseModalities": ['IMAGE']
161
- }
162
- }
 
 
 
163
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  try:
165
- response = requests.post(f"{IMAGIN_API_URL}?key={os.environ['GOOGLE_API_KEY']}", json=payload, timeout=120)
166
- response.raise_for_status()
167
- response_data = response.json()
168
- if 'candidates' in response_data and response_data['candidates']:
169
- image_part = response_data['candidates'][0]['content']['parts'][0]
170
- if image_part and 'inlineData' in image_part:
171
- generated_image_base64 = image_part['inlineData']['data']
172
- generated_image_url = save_image_locally(base64.b64decode(generated_image_base64))
173
- if generated_image_url:
174
- logging.info(f"Successfully generated and saved new outfit image: {generated_image_url}")
175
- return generated_image_url
176
- else:
177
- logging.error("Failed to save generated outfit image locally.")
178
- return None
179
- else:
180
- logging.error("Gemini image response format is invalid for outfit generation.")
181
- return None
182
  else:
183
- logging.error(f"Gemini outfit generation response has no candidates: {response.text}")
184
- return None
185
- except requests.exceptions.RequestException as e:
186
- logging.error(f"Gemini outfit generation API request failed: {e}")
187
- return None
 
 
 
 
 
 
188
 
189
- # ==========================# LangChain 代理人設定# ==========================#
190
  # 結合所有工具
191
- tools = [generate_outfit_from_clothes]
 
192
  # 建立 LLM 模型實例
193
  llm = ChatGoogleGenerativeAI(google_api_key=google_api, model="gemini-2.5-flash", temperature=0.2)
 
194
  # 建立提示模板
195
  prompt_template = ChatPromptTemplate([
196
- ("system", "你是一個強大的圖像生成與問答助理,可以根據用戶的請求使用提供的工具。當你執行 generate_outfit_from_clothes 工具成功後會獲得一個 URL,然後你回答的 output 要包含有這個 URL 的完整資訊。如果工具有產生錯誤訊息請解讀並回應。"),
 
197
  ("user", "{input}"),
198
  ("placeholder", "{agent_scratchpad}"),
199
  ])
 
200
  # 建立代理人
201
  agent = create_tool_calling_agent(llm, tools, prompt_template)
202
  agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
203
 
204
- # ==========================# FastAPI 路由# ==========================#
 
 
 
205
  @app.get("/")
206
  def root():
207
  return {"title": "Line Bot"}
@@ -227,118 +210,53 @@ def handle_message(event):
227
 
228
  # 處理圖片上傳
229
  if event.message.type == "image":
230
- image_url = get_image_url_from_line(event.message.id)
231
- if image_url:
232
- mode = user_states[user_id]["current_mode"]
233
- if not mode:
234
- line_bot_api.reply_message(
235
- event.reply_token, TextSendMessage(text="請先輸入「上衣」或「褲子」來選擇要上傳的衣服類型。")
236
- )
237
- return
238
-
239
- if mode == "upper":
240
- if len(user_states[user_id]["upper_body_images"]) < MAX_IMAGES_PER_TYPE:
241
- user_states[user_id]["upper_body_images"].append(image_url)
242
- reply_text = f"已接收上衣。上衣: {len(user_states[user_id]['upper_body_images'])}/{MAX_IMAGES_PER_TYPE},褲子/裙子: {len(user_states[user_id]['lower_body_images'])}/{MAX_IMAGES_PER_TYPE}。"
243
- line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text))
244
- else:
245
- line_bot_api.reply_message(event.reply_token, TextSendMessage(text="上衣數量已滿。"))
246
- else: # mode == "lower"
247
- if len(user_states[user_id]["lower_body_images"]) < MAX_IMAGES_PER_TYPE:
248
- user_states[user_id]["lower_body_images"].append(image_url)
249
- reply_text = f"已接收褲子/裙子。上衣: {len(user_states[user_id]['upper_body_images'])}/{MAX_IMAGES_PER_TYPE},褲子/裙子: {len(user_states[user_id]['lower_body_images'])}/{MAX_IMAGES_PER_TYPE}。"
250
- line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text))
251
- else:
252
- line_bot_api.reply_message(event.reply_token, TextSendMessage(text="褲子/裙子數量已滿。"))
253
-
254
- # 檢查是否所有圖片都已上傳完畢
255
- if (len(user_states[user_id]["upper_body_images"]) == MAX_IMAGES_PER_TYPE and
256
- len(user_states[user_id]["lower_body_images"]) == MAX_IMAGES_PER_TYPE):
257
- line_bot_api.push_message(
258
- user_id,
259
- TextSendMessage(text="所有衣物圖片已收集完畢!\n\n現在您可以輸入「**生成穿搭**」或「**圖片推薦**」來獲得一套新的圖片穿搭。")
260
- )
261
  else:
262
  line_bot_api.reply_message(
263
  event.reply_token, TextSendMessage(text="沒有接收到圖片~")
264
  )
265
-
266
  # 處理文字訊息
267
  elif event.message.type == "text":
268
- user_text = event.message.text.lower()
269
- reply_token = event.reply_token
270
-
271
- # 處理重置功能
272
- if user_text in ["重置", "重來", "重新開始", "再一次"]:
273
- user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None})[user_id]
274
- line_bot_api.reply_message(
275
- reply_token, TextSendMessage(text="狀態已重置。請先輸入「上衣」或「褲子」來開始。")
276
- )
277
- return
278
-
279
- # 處理衣物上傳模式切換
280
- if user_text == "上衣":
281
- user_states[user_id]["current_mode"] = "upper"
282
- line_bot_api.reply_message(
283
- reply_token, TextSendMessage(text=f"請上傳三件上衣圖片,您已上傳 {len(user_states[user_id]['upper_body_images'])}/{MAX_IMAGES_PER_TYPE} 張。")
284
- )
285
- return
286
- elif user_text in ["褲子", "裙子"]:
287
- user_states[user_id]["current_mode"] = "lower"
288
- line_bot_api.reply_message(
289
- reply_token, TextSendMessage(text=f"請上傳三件褲子/裙子圖片,您已上傳 {len(user_states[user_id]['lower_body_images'])}/{MAX_IMAGES_PER_TYPE} 張。")
290
- )
291
- return
292
-
293
- # 處理圖片生成穿搭
294
- if user_text in ["生成穿搭", "圖片推薦"]:
295
- if (len(user_states[user_id]["upper_body_images"]) == MAX_IMAGES_PER_TYPE and
296
- len(user_states[user_id]["lower_body_images"]) == MAX_IMAGES_PER_TYPE):
297
- try:
298
- line_bot_api.reply_message(reply_token, TextSendMessage(text="好的,正在為您生成一套新的穿搭圖片,請稍候..."))
299
-
300
- # 直接呼叫生成工具,並傳入已收集的圖片 URLs
301
- generated_image_url = generate_outfit_from_clothes(
302
- user_states[user_id]["upper_body_images"],
303
- user_states[user_id]["lower_body_images"]
304
- )
305
-
306
- if generated_image_url:
307
- line_bot_api.push_message(
308
- user_id,
309
- ImageSendMessage(original_content_url=generated_image_url, preview_image_url=generated_image_url)
310
- )
311
- line_bot_api.push_message(
312
- user_id,
313
- TextSendMessage(text="這是根據您的衣物生成的圖片推薦。如果想再次使用,請輸入「重置」。")
314
- )
315
- else:
316
- line_bot_api.push_message(user_id, TextSendMessage(text="圖片生成失敗,請稍後再試。"))
317
-
318
- except Exception as e:
319
- logging.error(f"Error generating outfit image: {e}")
320
- line_bot_api.push_message(user_id, TextSendMessage(text=f"圖片生成失敗,請稍後再試。錯誤:{e}"))
321
-
322
- # 生成後重置狀態以便下一次使用
323
- user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None})[user_id]
324
- return
325
- else:
326
- line_bot_api.reply_message(
327
- reply_token, TextSendMessage(text="請先上傳三件上衣和三件褲子/裙子圖片,再輸入「生成穿搭」來獲得圖片推薦。")
328
- )
329
- return
330
 
331
- # 如果都不是特定指令,則交給代理人處理
332
- agent_input = {"input": user_text}
 
 
 
 
 
 
 
 
333
  try:
334
  # 運行代理人
335
  response = agent_executor.invoke(agent_input)
336
  out = response["output"]
337
- line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
 
 
 
 
 
 
 
 
 
 
 
338
  except Exception as e:
339
  print(f"代理人執行出錯: {e}")
340
  out = f"代理人執行出錯!錯誤訊息:{e}"
341
  line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
342
 
343
  if __name__ == "__main__":
344
- uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)
 
1
+ import os
2
+ import io
3
+ from collections import defaultdict
4
  from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi import FastAPI, Request, Header, BackgroundTasks, HTTPException
6
  from fastapi.staticfiles import StaticFiles
7
+ from google import genai
8
+ from google.genai import types
9
  from linebot import LineBotApi, WebhookHandler
10
  from linebot.exceptions import InvalidSignatureError
11
  from linebot.models import (
 
15
  ImageSendMessage,
16
  ImageMessage,
17
  )
18
+ import PIL.Image
 
 
 
 
 
 
19
  import uvicorn
20
+
21
+ # LangChain 相關匯入
22
  from langchain_core.prompts import ChatPromptTemplate
23
  from langchain_core.tools import tool
24
  from langchain_google_genai import ChatGoogleGenerativeAI
25
  from langchain.agents import AgentExecutor, create_tool_calling_agent
 
 
26
 
 
 
 
27
 
28
+ # ==========================
29
+ # 環境設定與工具函式
30
+ # ==========================
 
31
 
32
+ # 設置 Google AI API 金鑰
33
  google_api = os.environ["GOOGLE_API_KEY"]
 
 
 
 
 
 
 
 
 
34
  genai_client = genai.Client(api_key=google_api)
35
 
36
+ # 設置 Line Bot 的 API 金鑰和秘密金鑰
37
+ line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
38
+ line_handler = WebhookHandler(os.environ["CHANNEL_SECRET"])
 
 
 
39
 
40
+ # 使用字典模擬用戶訊息歷史存儲
41
+ user_message_history = defaultdict(list)
42
 
43
  # 建立 FastAPI 應用程式
44
  app = FastAPI()
 
 
45
  app.mount("/static", StaticFiles(directory="static"), name="static")
46
 
47
+ # 設定 CORS
48
  app.add_middleware(
49
  CORSMiddleware,
50
  allow_origins=["*"],
 
53
  allow_headers=["*"],
54
  )
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  def get_image_url_from_line(message_id):
57
  """
58
  從 Line 訊息 ID 獲取圖片內容並儲存到暫存檔案。
59
  """
60
  try:
61
  message_content = line_bot_api.get_message_content(message_id)
62
+ file_path = f"/tmp/{message_id}.png"
63
+ with open(file_path, "wb") as f:
64
+ for chunk in message_content.iter_content():
65
+ f.write(chunk)
66
+ print(f"✅ 圖片成功儲存到:{file_path}")
67
+ return file_path
68
  except Exception as e:
69
  print(f"❌ 圖片取得失敗:{e}")
70
  return None
71
 
72
+ def store_user_message(user_id, message_type, message_content):
73
+ """
74
+ 儲存用戶的訊息。
75
+ """
76
+ user_message_history[user_id].append(
77
+ {"type": message_type, "content": message_content}
78
+ )
79
 
80
+ def get_previous_message(user_id):
81
+ """
82
+ 獲取用戶的上一則訊息。
83
  """
84
+ if user_id in user_message_history and len(user_message_history[user_id]) > 0:
85
+ return user_message_history[user_id][-1]
86
+ return {"type": "text", "content": "No message!"}
87
+
88
+
89
+ # ==========================
90
+ # LangChain 工具定義
91
+ # ==========================
92
+
93
+ @tool
94
+ def generate_and_upload_image(prompt: str) -> str:
95
+ """
96
+ 這個工具可以根據文字提示生成圖片,並將其上傳到伺服器。
97
 
98
  Args:
99
+ prompt: 用於生成圖片的文字提示。
 
100
 
101
  Returns:
102
  回傳生成圖片的 URL。
103
  """
104
+ try:
105
+ response = genai_client.models.generate_content(
106
+ model="gemini-2.0-flash-preview-image-generation",
107
+ contents=prompt,
108
+ config=types.GenerateContentConfig(response_modalities=['Text', 'Image'])
109
+ )
110
+
111
+ image_binary = None
112
+ for part in response.candidates[0].content.parts:
113
+ if part.inline_data is not None:
114
+ image_binary = part.inline_data.data
115
+ break
116
+
117
+ if image_binary:
118
+ image = PIL.Image.open(io.BytesIO(image_binary))
119
+ # 隨機生成一個檔案名以避免衝突
120
+ file_name = f"static/{os.urandom(16).hex()}.png"
121
+ image.save(file_name, format="PNG")
122
+
123
+ image_url = os.path.join(os.getenv("HF_SPACE"), file_name)
124
+ return image_url
125
+
126
+ return "圖片生成失敗。"
127
+ except Exception as e:
128
+ return f"圖片生成與上傳失敗: {e}"
129
 
130
+ @tool
131
+ def analyze_image_with_text(image_path: str, user_text: str) -> str:
132
+ """
133
+ 這個工具可以根據圖片和文字提示來回答問題。
134
+
135
+ Args:
136
+ image_path: 圖片在本地端儲存的路徑。
137
+ user_text: 針對圖片提出的文字問題。
138
+
139
+ Returns:
140
+ 模型針對圖片和文字提示給出的回應。
141
+ """
142
  try:
143
+ if not os.path.exists(image_path):
144
+ return "圖片路徑無效,無法進行分析。"
145
+
146
+ img_user = PIL.Image.open(image_path)
147
+ response = genai_client.models.generate_content(
148
+ model="gemini-2.5-flash",
149
+ contents=[img_user, user_text]
150
+ )
151
+ if (response.text != None):
152
+ out = response.text
 
 
 
 
 
 
 
153
  else:
154
+ out = "Gemini沒答案!請換個說法!"
155
+ except Exception as e:
156
+ # 處理錯誤
157
+ out = f"Gemini執行出錯: {e}"
158
+
159
+ return out
160
+
161
+
162
+ # ==========================
163
+ # LangChain 代理人設定
164
+ # ==========================
165
 
 
166
  # 結合所有工具
167
+ tools = [generate_and_upload_image, analyze_image_with_text]
168
+
169
  # 建立 LLM 模型實例
170
  llm = ChatGoogleGenerativeAI(google_api_key=google_api, model="gemini-2.5-flash", temperature=0.2)
171
+
172
  # 建立提示模板
173
  prompt_template = ChatPromptTemplate([
174
+ ("system", "你是一個強大的圖像生成與問答助理,可以根據用戶的請求使用提供的工具。當你執行 generate_and_upload_image 工具\
175
+ 成功後會獲得一個 URL,然後你回答的 output 要包含有這個 URL 的完整資訊。如果工具有產生錯誤訊息請解讀並回應。"),
176
  ("user", "{input}"),
177
  ("placeholder", "{agent_scratchpad}"),
178
  ])
179
+
180
  # 建立代理人
181
  agent = create_tool_calling_agent(llm, tools, prompt_template)
182
  agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
183
 
184
+ # ==========================
185
+ # FastAPI 路由
186
+ # ==========================
187
+
188
  @app.get("/")
189
  def root():
190
  return {"title": "Line Bot"}
 
210
 
211
  # 處理圖片上傳
212
  if event.message.type == "image":
213
+ image_path = get_image_url_from_line(event.message.id)
214
+ if image_path:
215
+ store_user_message(user_id, "image", image_path)
216
+ line_bot_api.reply_message(
217
+ event.reply_token, TextSendMessage(text="圖片已接收成功囉,幫我輸入你想詢問的問題喔~")
218
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  else:
220
  line_bot_api.reply_message(
221
  event.reply_token, TextSendMessage(text="沒有接收到圖片~")
222
  )
223
+
224
  # 處理文字訊息
225
  elif event.message.type == "text":
226
+ user_text = event.message.text
227
+ previous_message = get_previous_message(user_id)
228
+ print(previous_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ # 根據上一則訊息類型,動態傳遞給代理人
231
+ if previous_message["type"] == "image":
232
+ image_path = previous_message["content"]
233
+ agent_input = {
234
+ "input": f"請根據這張圖片回答問題。圖片的路徑是 {image_path},我的問題是:{user_text}"
235
+ }
236
+ # 清除上一則圖片訊息,避免重複觸發
237
+ user_message_history[user_id].pop()
238
+ else:
239
+ agent_input = {"input": user_text}
240
  try:
241
  # 運行代理人
242
  response = agent_executor.invoke(agent_input)
243
  out = response["output"]
244
+ if 'https' in out:
245
+ img_tmp = 'https'+out.split('https')[1]
246
+ image_url = img_tmp.split('png')[0]+'png'
247
+ line_bot_api.push_message(
248
+ event.source.user_id,
249
+ [
250
+ TextSendMessage(text="✨ 這是我為你生成的圖片喔~"),
251
+ ImageSendMessage(original_content_url=image_url, preview_image_url=image_url)
252
+ ]
253
+ )
254
+ else:
255
+ line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
256
  except Exception as e:
257
  print(f"代理人執行出錯: {e}")
258
  out = f"代理人執行出錯!錯誤訊息:{e}"
259
  line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
260
 
261
  if __name__ == "__main__":
262
+ uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True)