Download app.py from fudii0921/gmap: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/fudii0921/gmap/resolve/main/app.py
- Command line
-
hf download hf://spaces/fudii0921/gmap/app.py
-
curl -L -o app.py https://huggingface.co/spaces/fudii0921/gmap/resolve/main/app.py
10.6 kB
| 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) | |