ragline / main.py
ning8429's picture
Update main.py
cd2686b verified
from fastapi.middleware.cors import CORSMiddleware
from fastapi import FastAPI, Request, Header, BackgroundTasks, HTTPException, status
from fastapi.staticfiles import StaticFiles
from gradio_client import Client
import json
import os
import requests
import base64
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, ImageMessage, TextSendMessage, ImageSendMessage, AudioMessage
)
# 設定 GeminiRAG 的HF網址
# client = Client(os.environ["GeminiRAGapi"])
# 設定 Line Bot 的 API 金鑰和秘密金鑰
line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
line_handler = WebhookHandler(os.environ["CHANNEL_SECRET"])
# 設定是否正在與使用者交談
working_status = os.getenv("DEFALUT_TALKING", default = "true").lower() == "true"
# 建立 FastAPI 應用程式
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
# Server URL
server_url= 'ning8429-ragline.hf.space' # https://ning8429-ragline.hf.space/static/temp.txt
api_img2text_url = 'https://ning8429-flask-docker.hf.space/predict'
api_text2img_url = 'https://ning8429-flask-docker.hf.space/text2img'
# 設定 CORS,允許跨域請求
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 處理根路徑請求
@app.get("/")
def root():
return {"title": "Line Bot"}
# 處理 Line Webhook 請求
@app.post("/webhook")
async def webhook(
request: Request,
background_tasks: BackgroundTasks,
x_line_signature=Header(None),
):
# 取得請求內容
body = await request.body()
try:
# 將處理 Line 事件的任務加入背景工作
background_tasks.add_task(
line_handler.handle, body.decode("utf-8"), x_line_signature
)
except InvalidSignatureError:
# 處理無效的簽章錯誤
raise HTTPException(status_code=400, detail="Invalid signature")
return "ok"
# 處理接收的訊息事件
@line_handler.add(MessageEvent, message=(ImageMessage, TextMessage)) # 可捕捉處理的訊息類型:圖片、文字訊息
def handle_message(event):
global working_status
# 檢查事件是否為圖片訊息
if isinstance(event.message, ImageMessage):
print(f"***** START 圖轉文 *****")
# 回覆已成功接收圖片的訊息
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="已成功接收您的圖片!正在為您搜尋寶寶名稱,請稍候片刻 😊")
)
# 嘗試獲取圖片內容
try:
message_content = line_bot_api.get_message_content(event.message.id)
except Exception as e:
print(f"Error fetching image content: {e}")
# 圖片存進hugging face docker static資料夾
input_name = 'input_'+event.message.id+'.jpg'
path = './static/'+ input_name
with open(path, 'wb') as fd:
for chunk in message_content.iter_content():
print(f"Saving image to {path}")
fd.write(chunk)
img2_url='https://%s/static/'% server_url
# Call Flask API after saving the image
try:
with open(path, 'rb') as image_file:
# Prepare payload with image file and message ID
files = {'image': image_file}
data = {
'message_id': event.message.id,
"choice": "find_similar_words",
"word": None,
"top_k": 3
}
# Make a POST request to Flask API
response = requests.post(api_img2text_url, files=files, data=data)
# Check the response from the external API
if response.status_code == 200:
api_response = response.json()
else:
api_response = "Error calling FLASK API_response.status_code"
except Exception as e:
print(f"Error sending request to external API: {e}")
api_response = "Error calling FLASK API_Exception"
image_messages = []
# image_del_paths = [path] # 10/28 add-初始化要刪除的圖片路徑清單
# 遍歷 CLIP api_response 中的 objects
for i, obj in enumerate(api_response['objects']):
element = obj['element']
encoded_image = obj['images']['encoded_image']
description = obj['images']['description_list'].replace("\n", " ")
print(f"===== 【{path}--> {element}】clip check: 【{description}】 =====")
img_data = base64.b64decode(encoded_image)
# 每個圖像的文件名以message id和索引作為文件名
image_name = f"yolo_{event.message.id}_{i}.jpg"
image_path = './static/' + image_name
# image_del_paths.append(image_path) # 10/28 add-加入新的圖片路徑
# 將圖片保存到 static 資料夾
with open(image_path, 'wb') as file:
file.write(img_data)
# 構建可以從 static 資料夾存取的 URL
output_url = f'https://{server_url}/static/{image_name}'
# 每張圖回傳一張圖片和對應的描述
image_messages.append(
[
ImageSendMessage(
original_content_url=output_url,
preview_image_url=output_url
),
TextSendMessage(text=f"⬆ 這隻可能是💫~\n{description}")
]
)
# 傳送所有圖片和描述,使用 push_message
for message_group in image_messages:
line_bot_api.push_message(
event.source.user_id, # 使用 user_id 發送推送訊息
message_group
)
# # 10/28 add-刪除已傳送的圖片,釋放空間
# for image_del in image_del_paths:
# try:
# os.remove(image_del)
# print(f"Deleted file: {image_del}")
# except Exception as e:
# print(f"Error deleting file {image_del}: {e}")
elif isinstance(event.message, TextMessage):
if event.message.text =="開始使用":
# 回覆提示訊息,不調用外部 API
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="您好😊 請傳送寶寶照片找尋名字或是輸入關鍵字找尋圖片")
)
return # 終止處理,避免調用外部API
# 其他文字訊息的處理邏輯
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="已成功接收您的搜尋文字!正在找尋對應的寶寶,請稍候片刻 😊")
)
# 將文字訊息內容當作參數傳送給外部 API
text_message_content = event.message.text
print(f"***** START 文轉圖 *****")
# 呼叫外部API
try:
data = {
'message_id': event.message.id,
"choice": "find_image_for_word",
"word": text_message_content,
"top_k": 3
}
# 發送 POST 請求至 Flask API,附帶文字內容
response = requests.post(api_text2img_url, data=data)
# 確認 API 回傳結果
if response.status_code == 200:
api_response = response.json()
# 解析 API 回應的資料
encoded_image = api_response["encoded_image"]
description = api_response["description"]
print(f"===== 文轉圖 api_response:{api_response} =====")
# 將 base64 編碼的圖片轉換為圖片URL
img_data = base64.b64decode(encoded_image)
# 每個圖像的文件名以message id和索引作為文件名
image_name = f"text2img_{event.message.id}.jpg"
image_path = './static/' + image_name
# 將圖片保存到 static 資料夾
with open(image_path, 'wb') as file:
file.write(img_data)
# 構建可以從 static 資料夾存取的 URL
output_url = f'https://{server_url}/static/{image_name}'
# 傳送圖片和描述給用戶
line_bot_api.push_message(
event.source.user_id,
[
ImageSendMessage(
original_content_url=output_url,
preview_image_url=output_url
),
TextSendMessage(text=f"⬆️這是根據您的文字找到的寶寶💕\n{description}")
]
)
else:
line_bot_api.push_message(
event.source.user_id,
TextSendMessage(text="Error calling /text2img API: 非預期的狀態碼")
)
except Exception as e:
print(f"Error sending request to external API: {e}")
line_bot_api.push_message(
event.source.user_id,
TextSendMessage(text="Error calling FLASK API: 發送請求時出現異常")
)
else:
# 若不是圖片或文字則回覆提示訊息
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="我們只接受圖片或文字訊息唷")
)
# # 檢查使用者是否輸入 "再見"
# elif event.message.text == "再見":
# # 回覆 "Bye!"
# line_bot_api.reply_message(
# event.reply_token,
# TextSendMessage(text="Bye!")
# )
# return
# # 檢查是否正在與使用者交談
# elif working_status:
# try:
# # 取得使用者輸入的文字
# prompt = event.message.text
# # 使用 GeminiRAGapi
# completion = client.predict(question=prompt, api_name="/predict")
# # 檢查生成結果是否為空
# if (completion != None):
# # 取得生成結果
# out = completion
# else:
# # 回覆 "Gemini沒答案!請換個說法!"
# out = "Gemini沒答案!請換個說法!"
# except:
# # 處理錯誤
# out = "Gemini執行出錯!請換個說法!"
# # 回覆生成結果
# line_bot_api.reply_message(
# event.reply_token,
# TextSendMessage(text=out))
if __name__ == "__main__":
# 啟動 FastAPI 應用程式
uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)
# 註解說明:
# import 導入必要的套件
# line_bot_api 和 line_handler 設定 Line Bot API 和 webhook 處理器
# working_status 設定是否正在與使用者交談
# app 建立 FastAPI 應用程式
# app.add_middleware 設定 CORS
# @app.get("/") 處理根路徑請求
# @app.post("/webhook") 處理 Line Webhook 請求
# @line_handler.add(MessageEvent, message=TextMessage) 處理文字訊息事件
# if __name__ == "__main__": 啟動 FastAPI 應用程式
# 程式碼功能說明:
# 接著會建立 FastAPI 應用程式,並設定 CORS。
# 程式碼會定義兩個函數:
# root() 處理根路徑請求,返回一個簡單的 JSON 訊息。
# webhook() 處理 Line Webhook 請求,將處理 Line 事件的任務加入背景工作,並處理無效的簽章錯誤。
# 程式碼還定義一個函數 handle_message() 來處理文字訊息事件,它會檢查事件類型和訊息類型,並根據使用者輸入執行不同的動作:
# 如果使用者輸入 "再見",回覆 "Bye!"。
# 如果正在與使用者交談,則會使用 Gemini 模型生成文字,並將結果回覆給使用者。
# 最後,程式碼會啟動 FastAPI 應用程式,開始監聽 HTTP 請求。
# 程式碼運行方式:
# 將程式碼存為 main.py 文件。
# 在環境變數中設定 GeminiRAGapi, CHANNEL_ACCESS_TOKEN 和 CHANNEL_SECRET。
# 執行 uvicorn main:app --host 0.0.0.0 --port 7860 --reload 命令啟動 FastAPI 應用程式。
# 使用 Line 帳戶與 Line Bot 進行對話。
# 注意:
# 程式碼中使用os.environ["GeminiRAGapi"], os.environ["CHANNEL_ACCESS_TOKEN"] 和 os.environ["CHANNEL_SECRET"] 來存取環境變數,需要先在環境變數中設定這些值。
# 程式碼中使用 uvicorn 執行 FastAPI 應用程式,需要先安裝 uvicorn 套件。
# 程式碼中使用 linebot 套件,需要先安裝 linebot 套件