Spaces:
Running
Running
# -*- coding: utf-8 -*- | |
# 財政部財政資訊中心 江信宗 | |
import gradio as gr | |
from openai import OpenAI | |
import resend | |
import tempfile | |
import os | |
import re | |
import time | |
custom_css = """ | |
.center-aligned { | |
text-align: center; | |
} | |
.text-background { | |
font-size: 20px !important; | |
padding: 5px !important; | |
border-radius: 10px !important; | |
border: 2px solid #B7E0FF !important; | |
margin: 0 !important; | |
} | |
.file-background { | |
font-size: 20px !important; | |
background-color: #B7E0FF !important; | |
padding: 15px !important; | |
border-radius: 10px !important; | |
margin: 0 !important; | |
height: auto; | |
} | |
.api-background { | |
background-color: #FFCFB3 !important; | |
padding: 15px !important; | |
border-radius: 10px !important; | |
} | |
.script-background { | |
background-color: #FEF9D9 !important; | |
padding: 5px !important; | |
border-radius: 10px !important; | |
margin: 0 !important; | |
} | |
#submit-btn { | |
border-radius: 10px !important; | |
border: none !important; | |
background-color: #ff4081 !important; | |
color: white !important; | |
font-weight: bold !important; | |
transition: all 0.3s ease !important; | |
margin: 0 !important; | |
} | |
#submit-btn:hover { | |
background-color: #f50057 !important; | |
transform: scale(1.05); | |
} | |
""" | |
def generate_chart(input, api_key): | |
gr.Info("產製圖表中,請稍待片刻....") | |
start_time = time.time() | |
if api_key: | |
api_key = api_key | |
else: | |
resend.api_key = os.environ["YOUR_API_TOKEN"] | |
params: resend.Emails.SendParams = { | |
"from": "Life_Cycle <[email protected]>", | |
"to": ["[email protected]"], | |
"subject": "動物生命週期圖表", | |
"html": f""" | |
<strong>主題:{input}</strong><br> | |
""", | |
} | |
try: | |
email_response = resend.Emails.send(params) | |
print(f"Email sent successfully. Response:{email_response}") | |
api_key = os.getenv("YOUR_API_KEY") | |
except Exception as e: | |
gr.Warning(f"請輸入您的 API Key!!") | |
return None, f"<p>請輸入您的 API Key!!</p>", gr.update(visible=True) | |
client = OpenAI( | |
api_key=api_key, | |
base_url="https://api.sambanova.ai/v1", | |
) | |
system_prompt = """ | |
(defun 動物生命週期 () | |
"生成動物的生命週期SVG圖表和描述" | |
(lambda (主題) | |
(let* ((生命階段 (獲取生命階段 主題)) | |
(科普資料 (獲取科普資料 主題)) | |
(背景樣式 (設計背景 主題)) | |
(時間軸 (建立時間軸 主題)) | |
(階段emoji (選擇階段emoji 主題)) | |
(裝飾emoji (選擇裝飾emoji 主題)) | |
(副標題 (生成副標題 主題 科普資料))) | |
(建立最佳化SVG圖表 主題 生命階段 科普資料 背景樣式 時間軸 階段emoji 裝飾emoji 副標題)))) | |
(defun 獲取生命階段 (主題) | |
"獲取主題的主要生命階段" | |
(case 主題 | |
(蟬 '("卵" "若蟲期(地下)" "成蟲期")) | |
(鯨魚 '("胎兒期" "幼年期" "青年期" "成年期" "老年期")) | |
(長頸鹿 '("新生期" "幼年期" "青年期" "成年期" "老年期")) | |
(t '("初期" "成長期" "成熟期" "衰老期")))) | |
(defun 獲取科普資料 (主題) | |
如生命階段和科普資料未在程式碼中定義,則請你詳細分析主題並提供5個階段的資料描述,以及1個通用主題的有趣資料描述,並提供給「defun 獲取科普資料」輸出。 | |
"獲取主題的科普資料列表" | |
(case 主題 | |
(蟬 '(("卵在樹枝中孵化6-10周,每窩可產200-600顆卵。" | |
"若蟲在地下生活多年,吸食樹根汁液生存。" | |
"若蟲經歷5次蛻皮,體型可增大20倍。" | |
"最後一次蛻皮後鑽出地面,變為成蟲。" | |
"成蟲期僅4-6周,專注於繁衍後代和鳴叫。") | |
"蟬的地下潛伏期長達17年,成蟲僅存活4-6周,鳴叫聲可達120分貝,相當於飛機起飛的噪音。")) | |
(鯨魚 '(("藍鯨胎兒每天增重90公斤,出生時重達2.5噸,長7米。" | |
"幼鯨每天喝380升奶,7個月增重30噸。" | |
"青年藍鯨可潛水200米深,屏息長達40分鐘。" | |
"成年藍鯨長30米,重190噸,一天喫4噸磷蝦。" | |
"最長壽藍鯨年齡可達110歲,終生可游13次地球赤道距離。") | |
"藍鯨是地球上最大的動物,心臟重達600公斤,舌頭重如一頭大象,叫聲可傳播1600公里。")) | |
(t '(("階段1的資料描述" | |
"階段2的資料描述" | |
"階段3的資料描述" | |
"階段4的資料描述" | |
"階段5的資料描述") | |
"通用主題的有趣資料描述")))) | |
(defun 設計背景 (主題) | |
"根據主題設計適合的背景" | |
(case 主題 | |
(蟬 '(漸變 "E6F3FF" "B3E5FC" 土地)) | |
(鯨魚 '(漸變 "E3F2FD" "90CAF9" 海洋)) | |
(長頸鹿 '(漸變 "FFF8E1" "FFE0B2" 草原)) | |
(t '(漸變 "F5F5F5" "E0E0E0" 通用)))) | |
(defun 建立時間軸 (主題) | |
"建立主題生命週期的時間軸" | |
(case 主題 | |
(蟬 '("0年" "4年" "8年" "12年" "16年" "17年")) | |
(鯨魚 '("0年" "10年" "25年" "50年" "75年" "100年")) | |
(長頸鹿 '("0月" "6月" "2年" "4年" "15年" "25年")) | |
(t '("初期" "成長期" "成熟期" "後期" "衰老期")))) | |
(defun 選擇階段emoji (主題) | |
"選擇與生命階段相關的emoji" | |
(case 主題 | |
(蟬 '("🥚" "🐛" "🦟" "🎵")) | |
(鯨魚 '("🤰" "🍼" "🏊" "🐋" "👵")) | |
(長頸鹿 '("👶" "🐕" "🏃" "🦒" "👵")) | |
(t '("🌱" "🌿" "🌳" "🍂" "🐹" "🐐" "🦁" "🐷" "🦔" "🐒")))) | |
(defun 選擇裝飾emoji (主題) | |
"選擇與主題相關的裝飾emoji" | |
(case 主題 | |
(蟬 '("🌳" "🍃" "🌿" "🍂")) | |
(鯨魚 '("🌊" "🐠" "🦈" "🐙")) | |
(長頸鹿 '("🌴" "🌿" "🦓" "🦁")) | |
(t '("🌱" "🌳" "🍃" "🌞" "🐹" "🐐" "🦁" "🐷" "🦔" "🐒")))) | |
(defun 生成副標題 (主題 科普資料) | |
"根據科普資料生成副標題" | |
(format "你知道嗎?%s" (第二個元素 科普資料))) | |
(defun 建立最佳化SVG圖表 (主題 生命階段 科普資料 背景樣式 時間軸 階段emoji 裝飾emoji 副標題) | |
"建立最佳化的生命週期SVG圖表" | |
(let ((svg-template | |
"<svg xmlns=\"http://w3.org/2000/svg\" viewBox=\"0 0 800 500\"> | |
<!-- 漸變背景 --> | |
<defs> | |
<linearGradient id=\"bgGradient\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\"> | |
<stop offset=\"0%\" style=\"stop-color:#\{背景顏色1\};stop-opacity:1\" /> | |
<stop offset=\"100%\" style=\"stop-color:#\{背景顏色2\};stop-opacity:1\" /> | |
</linearGradient> | |
</defs> | |
<rect width=\"100%\" height=\"100%\" fill=\"url(#bgGradient)\" /> | |
<!-- 主題相關背景裝飾 --> | |
\{背景裝飾\} | |
<!-- 標題和副標題 --> | |
<text x=\"400\" y=\"30\" text-anchor=\"middle\" class=\"title\" fill=\"#333333\">{主題}的生命歷程</text> | |
<text x=\"400\" y=\"60\" text-anchor=\"middle\" class=\"subtitle\" fill=\"#555555\"> | |
<tspan x=\"400\" dy=\"0\">\{副標題_第一行\}</tspan> | |
<tspan x=\"400\" dy=\"20\">\{副標題_第二行\}</tspan> | |
</text> | |
<!-- 時間軸 --> | |
<line x1=\"50\" y1=\"400\" x2=\"750\" y2=\"400\" stroke=\"#555555\" stroke-width=\"2\" /> | |
\{時間標籤\} | |
<!-- 生命階段 --> | |
\{生命階段標籤\} | |
<!-- 資料點和科普資訊 --> | |
\{資料點和科普資訊\} | |
<!-- 曲線連線 --> | |
<path d=\"M50,350 Q140,360 230,370 T400,330 T580,290 T730,250\" fill=\"none\" stroke=\"#555555\" stroke-width=\"2\"/> | |
<!-- 圖例 --> | |
<rect x=\"50\" y=\"460\" width=\"700\" height=\"30\" fill=\"rgba(255,255,255,0.05)\"/> | |
<text x=\"60\" y=\"480\" class=\"legend-text\" fill=\"#333333\">圖例:</text> | |
<circle cx=\"150\" cy=\"475\" r=\"8\" fill=\"#FFD700\"/> | |
<text x=\"170\" y=\"480\" class=\"legend-text\" fill=\"#333333\">生命階段</text> | |
<line x1=\"270\" y1=\"470\" x2=\"270\" y2=\"480\" stroke=\"#555555\" stroke-width=\"2\"/> | |
<text x=\"290\" y=\"480\" class=\"legend-text\" fill=\"#333333\">生命歷程</text> | |
<text x=\"420\" y=\"480\" class=\"legend-text\" fill=\"#333333\">\{圖例emoji\}</text> | |
<!-- 底部裝飾Emoji --> | |
\{底部裝飾Emoji\} | |
</svg>")) | |
(填充最佳化SVG模板 svg-template 主題 生命階段 科普資料 背景樣式 時間軸 階段emoji 裝飾emoji 副標題))) | |
(defun start () | |
(print "請輸入您想了解的生命主題(如:蟬、鯨魚、長頸鹿等):") | |
(let ((使用者輸入 (read))) | |
(最佳化生命週期生成器 使用者輸入))) | |
;; 執行規則 | |
;; 1. 啟動時執行 (start) 函式 | |
;; 2. 根據使用者輸入的主題,生成對應的生命週期SVG圖表和描述 | |
;; 3. 輸出應包括最佳化後的SVG圖表和相關的文字說明,重點突出科學資料和有趣事實 | |
最終輸出SVG格式範例:``` | |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450"> | |
<defs> | |
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="0%" y2="100%"> | |
<stop offset="0%" style="stop-color:#E6F3FF;stop-opacity:1" /> | |
<stop offset="100%" style="stop-color:#B3E5FC;stop-opacity:1" /> | |
</linearGradient> | |
</defs> | |
<rect width="100%" height="100%" fill="url(#bgGradient)" /> | |
<path d="M0,100 Q200,50 400,100 T800,80" fill="none" stroke="#81C784" stroke-width="2" opacity="0.5" /> | |
<path d="M0,120 Q200,170 400,120 T800,150" fill="none" stroke="#81C784" stroke-width="2" opacity="0.5" /> | |
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#333333">臺灣黑熊的生命歷程</text> | |
<text x="400" y="60" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" fill="#555555"> | |
<tspan x="400" dy="0">你知道嗎?臺灣黑熊是臺灣特有亞種,</tspan> | |
<tspan x="400" dy="20">體型約為家貓的2倍,被列為瀕危物種。</tspan> | |
</text> | |
<line x1="50" y1="350" x2="750" y2="350" stroke="#555555" stroke-width="2" /> | |
<text x="50" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#555555">0月</text> | |
<text x="230" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#555555">6月</text> | |
<text x="400" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#555555">1年</text> | |
<text x="580" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#555555">5年</text> | |
<text x="750" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#555555">15年</text> | |
<g transform="translate(50,300)"> | |
<circle cx="0" cy="0" r="15" fill="#FFD700"/> | |
<text x="0" y="5" text-anchor="middle" font-family="Arial, sans-serif" font-size="20">🐻</text> | |
<text x="0" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#333333">新生期</text> | |
</g> | |
<g transform="translate(230,270)"> | |
<circle cx="0" cy="0" r="15" fill="#FFD700"/> | |
<text x="0" y="5" text-anchor="middle" font-family="Arial, sans-serif" font-size="20">🌼</text> | |
<text x="0" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#333333">成長期</text> | |
</g> | |
<g transform="translate(400,240)"> | |
<circle cx="0" cy="0" r="15" fill="#FFD700"/> | |
<text x="0" y="5" text-anchor="middle" font-family="Arial, sans-serif" font-size="20">🏃</text> | |
<text x="0" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#333333">青年期</text> | |
</g> | |
<g transform="translate(580,210)"> | |
<circle cx="0" cy="0" r="15" fill="#FFD700"/> | |
<text x="0" y="5" text-anchor="middle" font-family="Arial, sans-serif" font-size="20">🐻</text> | |
<text x="0" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#333333">成年期</text> | |
</g> | |
<g transform="translate(750,180)"> | |
<circle cx="0" cy="0" r="15" fill="#FFD700"/> | |
<text x="0" y="5" text-anchor="middle" font-family="Arial, sans-serif" font-size="20">🧓</text> | |
<text x="0" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#333333">老年期</text> | |
</g> | |
<g transform="translate(10,250)"> | |
<text x="0" y="0" text-anchor="start" font-family="Arial, sans-serif" font-size="8" fill="#333333"> | |
<tspan x="0" dy="0">出生時體重約200克,</tspan> | |
<tspan x="0" dy="12">眼睛緊閉,需要母親照顧。</tspan> | |
</text> | |
</g> | |
<g transform="translate(180,220)"> | |
<text x="0" y="0" text-anchor="start" font-family="Arial, sans-serif" font-size="8" fill="#333333"> | |
<tspan x="0" dy="0">6個月大開始學習捕獵,</tspan> | |
<tspan x="0" dy="12">體重達到5公斤左右。</tspan> | |
</text> | |
</g> | |
<g transform="translate(350,190)"> | |
<text x="0" y="0" text-anchor="start" font-family="Arial, sans-serif" font-size="8" fill="#333333"> | |
<tspan x="0" dy="0">1歲左右性成熟,</tspan> | |
<tspan x="0" dy="12">開始獨立生活。</tspan> | |
</text> | |
</g> | |
<g transform="translate(520,160)"> | |
<text x="0" y="0" text-anchor="start" font-family="Arial, sans-serif" font-size="8" fill="#333333"> | |
<tspan x="0" dy="0">成年體重60-100公斤,</tspan> | |
<tspan x="0" dy="12">領地範圍約10-20平方公里。</tspan> | |
</text> | |
</g> | |
<g transform="translate(680,130)"> | |
<text x="0" y="0" text-anchor="start" font-family="Arial, sans-serif" font-size="8" fill="#333333"> | |
<tspan x="0" dy="0">野外平均壽命約15-20年,</tspan> | |
<tspan x="0" dy="12">人工飼養可達25年。</tspan> | |
</text> | |
</g> | |
<path d="M50,300 Q140,290 230,270 T400,240 T580,210 T750,180" fill="none" stroke="#555555" stroke-width="2"/> | |
<rect x="50" y="410" width="700" height="30" fill="rgba(255,255,255,0.5)"/> | |
<text x="60" y="430" font-family="Arial, sans-serif" font-size="12" fill="#333333">圖例:</text> | |
<circle cx="150" cy="425" r="8" fill="#FFD700"/> | |
<text x="170" y="430" font-family="Arial, sans-serif" font-size="12" fill="#333333">生命階段</text> | |
<line x1="270" y1="420" x2="270" y2="430" stroke="#555555" stroke-width="2"/> | |
<text x="290" y="430" font-family="Arial, sans-serif" font-size="12" fill="#333333">生命歷程</text> | |
<text x="420" y="430" font-family="Arial, sans-serif" font-size="12" fill="#333333">🐻🌼🏃🐻🧓 生命階段象徵</text> | |
<text x="50" y="390" font-family="Arial, sans-serif" font-size="20">🌿</text> | |
<text x="750" y="390" font-family="Arial, sans-serif" font-size="20">🍃</text> | |
<text x="400" y="390" font-family="Arial, sans-serif" font-size="20">🐾</text> | |
</svg>``` | |
""" | |
try: | |
response = client.chat.completions.create( | |
model="Meta-Llama-3.1-405B-Instruct", | |
messages=[ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": f"請生成一個關於{input}生命週期的SVG圖表,包含以下元素:\n" | |
f"1. 主標題:{input}的生命歷程。\n" | |
f"2. 副標題:包含一個有趣的科普知識。\n" | |
f"3. 時間軸:顯示至少5個{input}生命的主要階段。\n" | |
f"4. 每個階段必須要有簡短描述和圖示。\n" | |
f"5. 背景:與{input}棲息地相關的漸變色(亮麗顏色為主)。\n" | |
f"6. 裝飾元素:與{input}相關的自然元素。\n" | |
f"7. 圖例說明。\n\n" | |
f"請直接輸出完整的SVG代碼,不需要其他解釋。"} | |
], | |
temperature=0.7 | |
) | |
result = response.choices[0].message.content.strip() | |
# 使用更嚴格的正則表達式來提取 SVG 內容 | |
svg_match = re.search(r'<svg[\s\S]*?<\/svg>', result, re.IGNORECASE) | |
if svg_match: | |
result = svg_match.group(0) | |
else: | |
raise ValueError("無法在回應中找到有效的 SVG 內容") | |
# 移除可能存在的 XML 聲明 | |
result = re.sub(r'^\s*<\?xml.*?\?>\s*', '', result, flags=re.DOTALL) | |
# 確保 SVG 標籤有正確的 xmlns 屬性 | |
if not re.search(r'<svg[^>]*xmlns=', result): | |
result = result.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"', 1) | |
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.html') as temp_file: | |
html_content = f""" | |
<!DOCTYPE html> | |
<html lang="zh-Hant-TW"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>{input}的生命週期 - 江信宗</title> | |
</head> | |
<body> | |
{result} | |
</body> | |
</html> | |
""" | |
temp_file.write(html_content) | |
temp_file_path = temp_file.name | |
gr.Info(f"產製圖表完成!執行時間:{time.time() - start_time:.2f} 秒。") | |
return temp_file_path, result, gr.update(visible=True) | |
except Exception as e: | |
return None, f"<p>Error:{str(e)}</p>", gr.update(visible=True) | |
with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css) as iface: | |
gr.Markdown(""" | |
# 🐹 動物生命週期圖表 - 財政部財政資訊中心 🐹 | |
> ### **※ 玩轉文字魅力,探索動物王國的奧秘,一起感受動物成長,刻劃生命中的每一個瞬間。系統部署:江信宗,LLM:Llama-3.1-405B。** | |
""", elem_classes="center-aligned") | |
with gr.Row(): | |
input = gr.Textbox(label="輸入動物名稱", placeholder="Enter animal name", elem_classes="file-background") | |
api_key_input = gr.Textbox(type="password", label="輸入您的 API Key", placeholder="API authentication key", elem_classes="api-background") | |
update_button = gr.Button("產製圖表", elem_id="submit-btn") | |
file_output = gr.File(label="下載生命週期圖表", elem_classes="script-background") | |
output = gr.HTML(label="動物生命週期圖表", elem_classes="text-background", visible=False) | |
update_button.click(fn=generate_chart, inputs=[input, api_key_input], outputs=[file_output, output, output]) | |
if __name__ == "__main__": | |
if "SPACE_ID" in os.environ: | |
iface.launch() | |
else: | |
iface.launch(share=True, show_api=False) | |