gmap / app.py
fudii0921's picture
Update app.py
ad7f7e7 verified
import gradio as gr
from google import genai
from google.genai import types
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configure the Gemini client
# NOTE: Make sure your GEMINI_API_KEY is set in your .env file
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
css = 'footer {visibility: hidden;} #header {display: flex; justify-content: space-between; align-items: center; font-size: 24px; font-weight: bold;} #logo {width: 50px; height: 50px;}'
# Define the grounding tool
grounding_tool = types.Tool(
google_search=types.GoogleSearch()
)
template = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PwC大手町オフィス アクセス案内</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #2d2d2d;
--accent-color: #e0301e;
--bg-color: #f4f7f6;
--card-bg: #ffffff;
--text-color: #333333;
--sub-text: #666666;
}
.access-card {
background-color: var(--card-bg);
width: 100%;
max-width: 400px;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
overflow: hidden;
margin: 20px;
display: flex;
flex-direction: column;
}
.card-header {
/*background-color: var(--primary-color);*/
background-color: #99CCFF;
color: white;
padding: 24px;
text-align: center;
}
.card-header h2 {
margin: 0;
font-size: 1.2rem;
letter-spacing: 0.05em;
font-weight: 700;
}
.card-header p {
margin: 8px 0 0;
font-size: 0.85rem;
opacity: 0.8;
}
.card-body {
padding: 24px;
}
.route-info {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
padding: 16px;
background: #f9f9f9;
border-radius: 12px;
}
.station {
text-align: center;
flex: 1;
}
.station-name {
display: block;
font-weight: 700;
font-size: 1rem;
margin-bottom: 4px;
}
.station-label {
font-size: 0.7rem;
color: var(--sub-text);
background: #eee;
padding: 2px 8px;
border-radius: 10px;
}
.arrow-time {
text-align: center;
color: var(--accent-color);
font-size: 0.8rem;
font-weight: bold;
padding: 0 10px;
}
.arrow-time i {
display: block;
font-size: 1.2rem;
margin-bottom: 4px;
}
.schedule-box {
border-left: 3px solid var(--accent-color);
padding-left: 16px;
margin-bottom: 24px;
}
.schedule-item {
margin-bottom: 12px;
}
.schedule-item:last-child {
margin-bottom: 0;
}
.time-label {
font-size: 0.8rem;
color: var(--sub-text);
display: block;
}
.time-value {
font-size: 1.1rem;
font-weight: 700;
}
.address-box {
font-size: 0.85rem;
color: var(--sub-text);
margin-bottom: 24px;
line-height: 1.6;
border-top: 1px solid #eee;
padding-top: 16px;
}
.address-box strong {
color: var(--text-color);
display: block;
margin-bottom: 4px;
}
.btn-map {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
padding: 16px;
background: linear-gradient(135deg, #d93a1e 0%, #b92b14 100%);
color: white;
text-decoration: none;
border-radius: 50px;
font-weight: bold;
font-size: 1rem;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 4px 15px rgba(224, 48, 30, 0.3);
box-sizing: border-box;
}
.btn-map:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(224, 48, 30, 0.4);
}
.btn-map i {
margin-right: 8px;
}
</style>
</head>
<body>
<div style="display: flex; justify-content: center; align-items: center;">
<div class="access-card">
<div class="card-header">
<h2>Access Guide</h2>
<p>PwC大手町オフィスへのご案内</p>
</div>
<div class="card-body">
<div class="route-info">
<div class="station">
<span class="station-name">亀有駅</span>
<span class="station-label">START</span>
</div>
<div class="arrow-time">
<i class="fas fa-train"></i>
約25-30分
</div>
<div class="station">
<span class="station-name">大手町</span>
<span class="station-label">GOAL</span>
</div>
</div>
<div class="schedule-box">
<div class="schedule-item">
<span class="time-label">推奨出発時刻</span>
<div class="time-value">10:30 頃 <span style="font-size:0.8rem; font-weight:normal; color:#666;">(亀有発)</span></div>
</div>
<div class="schedule-item">
<span class="time-label">到着予定時刻</span>
<div class="time-value">11:00 <span style="font-size:0.8rem; font-weight:normal; color:#666;">(オフィス着)</span></div>
</div>
</div>
<div class="address-box">
<i class="fas fa-map-marker-alt" style="color:#999; margin-right:5px;"></i> <strong>PwC大手町オフィス</strong>
東京都千代田区大手町1-1-1<br>
大手町パークビルディング
</div>
<a href="https://www.google.com/maps/dir/?api=1&origin=亀有駅&destination=PwC大手町オフィス+大手町パークビルディング" target="_blank" class="btn-map">
<i class="fas fa-map-marked-alt"></i> Googleマップで経路を見る
</a>
</div>
</div>
</div>
</body>
</html>
"""
'''
google_maps_tool = types.Tool(
google_maps=types.GoogleMaps()
)
# チャットモデルにツールを渡して、場所に関する質問をする
response = client.chat(
model="google/gemini-pro",
messages=[
{
"role": "user",
"content": "東京タワーの住所はどこですか?"
}
],
tools=[google_maps_tool]
)
# 応答を出力
print(response.text)
'''
# Define the generation settings
config = types.GenerateContentConfig(
tools=[grounding_tool]
)
def change_input(prompt):
if len(prompt) > 0:
return gr.update(visible=True)
else:
return gr.update(visible=False)
# --- Python function for the Gradio app ---
def generate_response(prompt):
"""
This function takes a user's prompt, calls the Gemini API
with the Google Search tool, and returns the response text.
"""
try:
final_prompt = prompt + f'尚、訪問先の住所を調べ、必ず、到着時刻を含めて、訪問先までのgoogle mapの経路のURL<a href target="_blank" rel="noopener">目的地</a>形式で教えてください。urlはhttps://www.google.com/maps/dir/%E4%BA%80%E6%9C%89%E9%A7%85/%E6%9D%B1%E4%BA%AC%E9%83%BD%E5%8D%83%E4%BB%A3%E7%94%B0%E5%8C%BA%E5%A4%A7%E6%89%8B%E7%94%BA1-1-1+%E5%A4%A7%E6%89%8B%E7%94%BA%E3%83%91%E3%83%BC%E3%82%AF%E3%83%93%E3%83%AB%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0/@35.7223676,139.7267896,12z/data=!3m1!4b1!4m14!4m13!1m5!1m1!1s0x60188fc1f92c3cf3:0x1b439c2c8f8d689b!2m2!1d139.845832!2d35.760278!1m5!1m1!1s0x60188c0e2719d3df:0x8544d6731e84a275!2m2!1d139.762585!2d35.686749!3e3?entry=ttu&g_ep=EgoyMDI1MDgxMi4wIKXMDSoASAFQAw%3D%3D形式にしてください。必ず、答えは{template}に基づいてHTML形式にしてください'
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=final_prompt,
config=config,
)
#print(response.text)
final_html = response.text
final_html = final_html.replace("```html", "")
final_html = final_html.replace("```", "")
return final_html
except Exception as e:
return f"An error occurred: {e}"
# --- Gradio UI Block ---
with gr.Blocks(title="Gemini API with Google Search") as gsearch:
gr.Markdown(
"""
# 🗾 Map Search Tool
日本語で質問すると、Gemini は Google 検索を使用して根拠のある回答を提供します。
Ask a question in Japanese, and Gemini will use Google Search to provide a grounded response.
"""
)
# Input component for the user's question
user_input = gr.Textbox(
label="あなたの質問:",
info="例: PWC大手町オフィスを11時に訪問する予定です。自由が丘からの経路を教えてください。",
lines=2
)
output_text = gr.HTML(
label="目的地までの経路:",
value='<div>結果</div>'
)
# Button to trigger the API call
submit_button = gr.Button("生成", visible=False)
user_input.change(fn=change_input, inputs=[user_input], outputs=[submit_button])
# Connect the components: when the button is clicked,
# the generate_response function is called with the user_input,
# and the result is displayed in the output_text box.
submit_button.click(
fn=generate_response,
inputs=user_input,
outputs=output_text
)
# Launch the Gradio app
if __name__ == "__main__":
gsearch.launch(favicon_path="path.ico", css=css, ssr_mode=False)