ragline / main.py
Kuomin62's picture
Update main.py
0ace500 verified
raw
history blame
10.6 kB
import os
import uvicorn
import requests
from bs4 import BeautifulSoup
import google.generativeai as genai
from fastapi import FastAPI, Request, Header, BackgroundTasks, HTTPException
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage
import tempfile
import base64
from PIL import Image
import io
# --- 設定部分 ---
# 建立 FastAPI 應用程式
app = FastAPI()
# 初始化全域變數
web_content = ""
line_bot_api = None
line_handler = None
text_model = None
image_model = None
TARGET_URL = "https://huggingface.co/spaces/AlanRex/AITEST" # 要分享的網址
def initialize_services():
"""初始化各項服務"""
global web_content, line_bot_api, line_handler, text_model, image_model
# 設定 Google AI API 金鑰
google_api_key = os.getenv("GOOGLE_API_KEY")
if google_api_key:
genai.configure(api_key=google_api_key)
print("Google AI API 已設定")
else:
print("警告: 未找到 GOOGLE_API_KEY 環境變數")
# 設定 Line Bot 的 API 金鑰和秘密金鑰
channel_access_token = os.getenv("CHANNEL_ACCESS_TOKEN")
channel_secret = os.getenv("CHANNEL_SECRET")
if channel_access_token and channel_secret:
line_bot_api = LineBotApi(channel_access_token)
line_handler = WebhookHandler(channel_secret)
print("Line Bot API 已設定")
else:
print("警告: 未找到 Line Bot 環境變數")
try:
# 使用 requests 取得網頁的 HTML 內容
print(f"正在抓取網頁內容: {TARGET_URL}")
response = requests.get(TARGET_URL, timeout=10)
# 檢查是否成功取得網頁 (HTTP status code 200)
response.raise_for_status()
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(response.text, 'html.parser')
# 取得網頁中所有的文字內容,並移除多餘的空白
web_content = soup.get_text(separator=' ', strip=True)
print(f"成功抓取網頁內容,長度: {len(web_content)} 字元")
except requests.exceptions.RequestException as e:
# 處理抓取網頁時發生的錯誤
print(f"Error fetching the URL: {e}")
web_content = "無法取得網頁內容,請檢查網址或連線。"
# 設定生成文字的角色扮演與限制
system_instruction = web_content + "\n\n您是一位問答助手。請僅限使用以上提供的內容來回答問題。請以簡潔明確的方式回答,並且能夠根據內容描述相關的視覺元素。"
# 建立模型實例
if google_api_key:
try:
# 文字生成模型
text_model = genai.GenerativeModel(
model_name="gemini-2.5-flash",
system_instruction=system_instruction
)
# 圖片生成模型 (使用 Imagen 3)
image_model = genai.GenerativeModel("imagen-3.0-generate-001")
print("Gemini 模型已初始化")
except Exception as e:
print(f"初始化 Gemini 模型時出錯: {e}")
text_model = None
image_model = None
def generate_image_from_content(user_prompt):
"""根據網站內容和用戶提問生成圖片"""
try:
if not text_model or not image_model:
return None, "AI 模型尚未準備就緒"
# 先用文字模型分析內容並產生圖片描述
analysis_prompt = f"""
基於網站內容,請為以下問題產生一個適合的圖片描述(英文):
問題:{user_prompt}
請產生一個清楚、具體的圖片描述,描述應該:
1. 與網站內容相關
2. 適合視覺化呈現
3. 使用英文描述
4. 長度在50-100字之間
只回傳圖片描述,不需要其他解釋。
"""
response = text_model.generate_content(analysis_prompt)
if not response or not response.text:
return None, "無法產生圖片描述"
image_description = response.text.strip()
print(f"圖片描述:{image_description}")
# 使用圖片描述生成圖片
image_response = image_model.generate_content([image_description])
if not image_response or not image_response.candidates:
return None, "無法生成圖片"
# 取得生成的圖片
generated_image = image_response.candidates[0]
if hasattr(generated_image, 'content') and hasattr(generated_image.content, 'parts'):
image_part = generated_image.content.parts[0]
if hasattr(image_part, 'inline_data'):
image_data = image_part.inline_data.data
# 將 base64 數據轉換為圖片
image_bytes = base64.b64decode(image_data)
# 上傳圖片到臨時服務 (這裡需要實作圖片上傳邏輯)
# 由於 Line Bot 需要公開的圖片網址,您需要將圖片上傳到雲端服務
# 這裡提供一個範例架構
return image_bytes, "圖片生成成功"
return None, "圖片數據格式不正確"
except Exception as e:
print(f"生成圖片時出錯: {e}")
return None, f"生成圖片時出錯: {str(e)}"
def upload_image_to_cloud(image_bytes):
"""
上傳圖片到雲端服務並回傳公開網址
這裡需要實作實際的上傳邏輯,例如上傳到:
- Imgur
- Cloudinary
- AWS S3
- Google Cloud Storage
等服務
"""
# 範例:這裡應該實作實際的上傳邏輯
# 暫時回傳一個佔位網址
return "https://example.com/placeholder.jpg"
# 處理根路徑請求
@app.get("/")
def root():
return {
"title": "Line Bot with Image Generation",
"status": "running",
"target_url": TARGET_URL,
"web_content_loaded": len(web_content) > 0,
"line_bot_configured": line_bot_api is not None,
"gemini_configured": text_model is not None,
"image_generation_enabled": image_model is not None
}
# 健康檢查端點
@app.get("/health")
def health_check():
return {"status": "healthy"}
# 處理 Line Webhook 請求
@app.post("/webhook")
async def webhook(
request: Request,
background_tasks: BackgroundTasks,
x_line_signature=Header(None)
):
if not line_handler:
raise HTTPException(status_code=500, detail="Line Bot not configured")
# 取得請求內容
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"
# 處理文字訊息事件
def setup_message_handler():
"""設定訊息處理器"""
if not line_handler:
return
@line_handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
# 檢查是否為文字訊息
if event.message.type != "text":
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="請輸入文字訊息。")
)
return
# 取得使用者輸入的文字
prompt = event.message.text
if not text_model:
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="抱歉,AI 模型尚未準備就緒。")
)
return
try:
# 先生成文字回應
generation_config = genai.types.GenerationConfig(
max_output_tokens=1000,
temperature=0.3,
top_p=0.5
)
text_response = text_model.generate_content(
contents=prompt,
generation_config=generation_config
)
# 嘗試生成相關圖片
image_bytes, image_status = generate_image_from_content(prompt)
messages = []
# 添加文字回應
if text_response and text_response.text:
messages.append(TextSendMessage(text=text_response.text))
else:
messages.append(TextSendMessage(text="抱歉,無法產生適當的回應。"))
# 如果成功生成圖片,添加圖片訊息
if image_bytes:
# 注意:這裡需要實作圖片上傳功能
# Line Bot 需要公開可訪問的圖片網址
# 上傳圖片並取得網址
image_url = upload_image_to_cloud(image_bytes)
# 創建圖片訊息
# originalContentUrl: 原始圖片網址
# previewImageUrl: 預覽圖片網址 (通常使用相同網址)
messages.append(ImageSendMessage(
original_content_url=image_url,
preview_image_url=image_url
))
messages.append(TextSendMessage(text="✨ 根據內容為您生成了相關圖片"))
else:
messages.append(TextSendMessage(text=f"📝 文字回應已生成,圖片生成失敗:{image_status}"))
# 發送所有訊息
line_bot_api.reply_message(event.reply_token, messages)
except Exception as e:
print(f"處理訊息時出錯: {e}")
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text="處理您的訊息時發生錯誤,請稍後再試。")
)
# 應用程式啟動事件
@app.on_event("startup")
async def startup_event():
"""應用程式啟動時執行的初始化"""
print("正在初始化服務...")
initialize_services()
setup_message_handler()
print("服務初始化完成")
if __name__ == "__main__":
# 啟動 FastAPI 應用程式
port = int(os.getenv("PORT", 7860))
uvicorn.run("main:app", host="0.0.0.0", port=port, reload=False)