| import gradio as gr |
| from google import genai |
| from google.genai import types |
| import os |
| from dotenv import load_dotenv |
|
|
| |
| load_dotenv() |
|
|
| |
| |
| 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;}' |
|
|
| |
| 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) |
| ''' |
|
|
| |
| 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) |
|
|
|
|
| |
| 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, |
| ) |
| |
| 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}" |
|
|
| |
| 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. |
| """ |
| ) |
| |
| user_input = gr.Textbox( |
| label="あなたの質問:", |
| info="例: PWC大手町オフィスを11時に訪問する予定です。自由が丘からの経路を教えてください。", |
| lines=2 |
| ) |
|
|
| output_text = gr.HTML( |
| label="目的地までの経路:", |
| value='<div>結果</div>' |
| ) |
| |
| submit_button = gr.Button("生成", visible=False) |
|
|
| user_input.change(fn=change_input, inputs=[user_input], outputs=[submit_button]) |
|
|
| |
| |
| |
| submit_button.click( |
| fn=generate_response, |
| inputs=user_input, |
| outputs=output_text |
| ) |
|
|
| |
| if __name__ == "__main__": |
| gsearch.launch(favicon_path="path.ico", css=css, ssr_mode=False) |
|
|