ååã¯ã³ã³ã½ã¼ã«ã¢ããªã¨ãã¦AIãã£ãããä½ãã¾ããã
kurima.hatenablog.com
ãã®AIãã£ããã«Flet ã使ã£ã¦GUI ãã¤ãã¦ã¿ã¾ãã
Fletã¯Python ã§æ¸ããFlutterã¨è¨ãã°ä¼ããã§ããããï¼
èªåã¯ä½å¹´ãåããPython ã§ãã¹ã¯ãããã¢ããªãçµãã«ã¯ä½ã使ã£ããããã®ãã¨æ©ã¿ããããçºãããå°ã触ã£ãããã¦ãã¾ããã
ãã£ã±ãTkinter ãªã®ããªããªã©ã¨æãã¤ã¤ããæ´ã£ãç»é¢ãä½ãã®ã«ãã¦ãã¦ãå¿
è¦ãªã¨ããã«éåæãæãã¦ãã¾ããããã¨ãã£ã¦åºã使ããã¦ããªããã®ã使ãæ°ãããªãã¨ãã£ãç¶æ
ã§ããã
ãããªã¨ãã«Fletãè¦ã¤ãããã£ã¨è
°ãæ®ãã¦ä½¿ãããã®ãæ¥ããã¼ï¼ã¨æãã¾ããã
ã¡ãªã¿ã«Streamlit ãããããããããã¨æã£ãã®ã§ããã©ãèªç±åº¦ãå°ã足ããªããã§ãããâ¦ããããç ç²ã«ãã¦æãåã使ãããããã¦ããã¨ãããã®ã ã¨æãã®ã§ä»æ¹ãªãã¨æãã¾ãã
Fletããã®ææ³ã¯ãã®ãããã«ãã¦ã¢ããªãä½ã£ã¦ããã¾ããããã¸ã§ã¯ãã®ä½æãAPI ãã¼ã®åå¾ãããã°ã©ã ããã®API ãã¼ã®åç
§ã«ã¤ãã¦ã¯ååã¨åããªã®ã§è©³ããã¯ååããåç
§ãã ãã
ããã¸ã§ã¯ãã®ä½æ
ããã¸ã§ã¯ããä½æãã¾ãã
$ rye init gemini-chat
$ cd gemini-chat
$ rye sync
ããã±ã¼ã¸ã®ã¤ã³ã¹ãã¼ã«
Gemini API ç¨ã® Python SDK ãå«ã¾ãã google -generativeai ããã±ã¼ã¸ãGUI ã®ä½æã«ä½¿ãfletããã±ã¼ã¸ãã¤ã³ã¹ãã¼ã«ãã¾ãã
$ rye add google-generativeai
$ rye add flet
$ rye sync
Python ãã¡ã¤ã«ã®ä½æ
GUI ã®ãã£ããã¢ããªãä½ãããã§ããã©ãã¡ããã©Fletã®ãã¥ã¼ããªã¢ã« ã«ãã£ããã¢ããªãããã¾ããã®ã§ãã¡ãããã¼ã¹ã¨ãã¦ä½¿ããã¦ãããããã¨æãã¾ãã
flet.dev
以ä¸ã®Python ãã¡ã¤ã«ãä½æãã¾ãã
gemini-chat/src/gemini_chat/main.py
ããã¦ã¾ãã¯ä»¥ä¸ã«ãããã¥ã¼ããªã¢ã« ã®æçµçãªã³ã¼ãããã®ã¾ã¾ã³ãã¼ãã¦main.pyã®å
容ã¨ãã¾ãã
github.com
.env ãã¡ã¤ã«ã®ä½æ
gemini-chat/.env
ã以ä¸ã®å
容ã§ä½æãã¾ãã("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"ã®é¨åã¯åå¾ããAPI ãã¼)
ä½æã®çç±ã¯ååãåç
§ãã¦ãã ããã
GOOGLE_API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
.gitignore ãã¡ã¤ã«ç·¨é
gemini-chat/.gitignore ã«ä»¥ä¸ã追è¨ãã¾ãã
ç·¨éã®çç±ã¯ååãåç
§ãã¦ãã ããã
# env
.env
pyproject.toml ãã¡ã¤ã«ã®ç·¨é
.envãã¡ã¤ã«ããç°å¢å¤æ° ã«å¤ãã»ãããã¦ããã°ã©ã ã®å®è¡ããããããª
ã¹ã¯ãªãã ã
gemini-chat/pyproject.toml
ã«å®ç¾©ãã¾ãã
pyproject.tomlã«ä»¥ä¸ã追è¨ãã¾ãã
Fletã使ã£ãã¢ããªãªã®ã§fletã³ãã³ãã§å®è¡ãã¾ãã
[tool.rye.scripts]
run-main = { cmd = "flet run src/gemini_chat/main.py", env-file = ".env" }
ããã°ã©ã ã®å®è¡
以ä¸ã®ã³ãã³ãã§ããã°ã©ã ãå®è¡ãã¾ãã
$ rye run run-main
ããã°ã©ã ãå®è¡ããã¨Fletã®ãã¥ã¼ããªã¢ã« ã®ãã£ããã表示ããã¾ãã
ããã«å°ãæãå ãã¦AIãã£ããã«ãã¾ãããã
ã¦ã¼ã¶ã¼åå
¥åå¦çã®åé¤
AIãã£ããã§ã¦ã¼ã¶ã¼åã®å
¥åãæ±ããããã®ã¯ç
©ãããã®ã§ã¦ã¼ã¶ã¼åå
¥åã®å¦çãåé¤ãã¦åºå®ã®ã¦ã¼ã¶ã¼åã¨ãªãããã«ãã¾ãããã
ã¦ã¼ã¶ã¼åå
¥åã®ãã¤ã¢ãã°ã®ãJoin chatããã¿ã³ãã¯ãªãã¯ããã¨ãã®å¦çã§ãã main() ã®ä¸ã® join_chat_click() ãåé¤ãã¾ããå
·ä½çã«ã¯ä»¥ä¸ã®ã³ã¼ããåé¤ãã¾ãã
def join_chat_click (e):
if not join_user_name.value:
join_user_name.error_text = "Name cannot be blank!"
join_user_name.update()
else :
page.session.set("user_name" , join_user_name.value)
page.dialog.open = False
new_message.prefix = ft.Text(f"{join_user_name.value}: " )
page.pubsub.send_all(
Message(
user_name=join_user_name.value,
text=f"{join_user_name.value} has joined the chat." ,
message_type="login_message" ,
)
)
page.update()
ã¦ã¼ã¶ã¼åå
¥åã®ãã¤ã¢ãã°ã®è¡¨ç¤ºã®å¦çãåé¤ãã¾ããå
·ä½çã«ã¯ main() ã®ä¸ç¤ã«ãã以ä¸ã®ã³ã¼ããåé¤ãã¾ãã
join_user_name = ft.TextField(
label="Enter your name to join the chat" ,
autofocus=True ,
on_submit=join_chat_click,
)
page.dialog = ft.AlertDialog(
open =True ,
modal=True ,
title=ft.Text("Welcome!" ),
content=ft.Column([join_user_name], width=300 , height=70 , tight=True ),
actions=[ft.ElevatedButton(text="Join chat" , on_click=join_chat_click)],
actions_alignment=ft.MainAxisAlignment.END,
)
åºå®ã®ã¦ã¼ã¶ã¼åãè¨å®ããå¦çã追å ãã¾ããmain() ã®æå¾ã«ä»¥ä¸ã®ã³ã¼ãã追å ãã¾ãã
user_name = "You"
page.session.set("user_name" , user_name)
new_message.prefix = ft.Text(f"{user_name}: " )
page.pubsub.send_all(
Message(
user_name=user_name,
text=f"{user_name} has joined the chat." ,
message_type="login_message" ,
)
)
page.update()
Gemini API ã®ChatSessionä½æå¦çã®è¿½å
Gemini API ã®ChatSessionãä½æããå¦çã追å ãã¾ããmain() ã®æå¾ã«ä»¥ä¸ã®ã³ã¼ãã追å ãã¾ãã
ã¨ã©ã¼ã¡ãã»ã¼ã¸ã®è¡¨ç¤ºã«ã¢ã©ã¼ããã¤ã¢ãã°ã使ãããã«ããã ãã§ChatSessionã®ä½æèªä½ã¯ååã¨åãã§ãã
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY" )
chat_session = None
if GOOGLE_API_KEY is not None :
genai.configure(api_key=GOOGLE_API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash" )
chat_session = model.start_chat(history=[])
else :
def handle_close (e):
print ("handle_close()" )
page.dialog.open = False
page.update()
page.dialog = ft.AlertDialog(
open =True ,
modal=True ,
title=ft.Text("ã¨ã©ã¼" ),
content=ft.Text("ç°å¢å¤æ° GOOGLE_API_KEY ãã»ããããã¦ãã¾ãã" ),
actions=[
ft.TextButton("OK" , on_click=handle_close),
],
actions_alignment=ft.MainAxisAlignment.END,
)
main.py ã®å
é ã« import ã追å ãã¾ãã
import google.generativeai as genai
import os
AIã«ããè¿çå¦çã®è¿½å
AIã«ããè¿çå¦çã追å ãã¾ãã
ã¦ã¼ã¶ã¼ããã®ã¡ãã»ã¼ã¸ãåãã¦AIããã®ã¡ãã»ã¼ã¸ãä½æã»éä¿¡ããAIããã®ã¡ãã»ã¼ã¸ãåããå ´åãã¦ã¼ã¶ã¼ããã¡ãã»ã¼ã¸ãåããå ´åã¨åãããã«å¦çãã¾ãã
ãã®ããã« main() ã®ä¸ã® on_ message() ãç·¨éãã¾ãã以ä¸ã® on_ message() ã
def on_message (message: Message):
if message.message_type == "chat_message" :
m = ChatMessage(message)
elif message.message_type == "login_message" :
m = ft.Text(message.text, italic=True , color=ft.colors.BLACK45, size=12 )
chat.controls.append(m)
page.update()
以ä¸ã®ããã«å¤æ´ãã¾ãã
å
é ã® if ã« "ai_chat_message" ã®å ´åãå ãããæ«å°¾ã«AIããã®ã¡ãã»ã¼ã¸ä½æã»éä¿¡ãå ãã£ã¦ãã¾ãã
def on_message (message: Message):
if (
message.message_type == "chat_message"
or message.message_type == "ai_chat_message"
):
m = ChatMessage(message)
elif message.message_type == "login_message" :
m = ft.Text(message.text, italic=True , color=ft.colors.BLACK45, size=12 )
chat.controls.append(m)
page.update()
if message.message_type == "chat_message" and chat_session is not None :
response = chat_session.send_message(message.text)
page.pubsub.send_all(
Message(
"AI" ,
response.text,
message_type="ai_chat_message" ,
)
)
ããã°ã©ã ã®åå®è¡
å度以ä¸ã®ã³ãã³ãã§ããã°ã©ã ãå®è¡ãã¾ãã
$ rye run run-main
ãã®ããã«AIã¨ãã£ããã§ãã¾ãã
ã¹ã¯ãªã¼ã³ã·ã§ãã
ããã£ã½ããªã£ã¦ãã¾ããã
æçµçãªå
¨ä½ã®ã³ã¼ããè¼ãã¦ããã¾ãã
import flet as ft
import google.generativeai as genai
import os
class Message :
def __init__ (self, user_name: str , text: str , message_type: str ):
self.user_name = user_name
self.text = text
self.message_type = message_type
class ChatMessage (ft.Row):
def __init__ (self, message: Message):
super ().__init__()
self.vertical_alignment = ft.CrossAxisAlignment.START
self.controls = [
ft.CircleAvatar(
content=ft.Text(self.get_initials(message.user_name)),
color=ft.colors.WHITE,
bgcolor=self.get_avatar_color(message.user_name),
),
ft.Column(
[
ft.Text(message.user_name, weight="bold" ),
ft.Text(message.text, selectable=True ),
],
tight=True ,
spacing=5 ,
),
]
def get_initials (self, user_name: str ):
if user_name:
return user_name[:1 ].capitalize()
else :
return "Unknown"
def get_avatar_color (self, user_name: str ):
colors_lookup = [
ft.colors.AMBER,
ft.colors.BLUE,
ft.colors.BROWN,
ft.colors.CYAN,
ft.colors.GREEN,
ft.colors.INDIGO,
ft.colors.LIME,
ft.colors.ORANGE,
ft.colors.PINK,
ft.colors.PURPLE,
ft.colors.RED,
ft.colors.TEAL,
ft.colors.YELLOW,
]
return colors_lookup[hash (user_name) % len (colors_lookup)]
def main (page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.STRETCH
page.title = "Flet Chat"
def send_message_click (e):
if new_message.value != "" :
page.pubsub.send_all(
Message(
page.session.get("user_name" ),
new_message.value,
message_type="chat_message" ,
)
)
new_message.value = ""
new_message.focus()
page.update()
def on_message (message: Message):
if (
message.message_type == "chat_message"
or message.message_type == "ai_chat_message"
):
m = ChatMessage(message)
elif message.message_type == "login_message" :
m = ft.Text(message.text, italic=True , color=ft.colors.BLACK45, size=12 )
chat.controls.append(m)
page.update()
if message.message_type == "chat_message" and chat_session is not None :
response = chat_session.send_message(message.text)
page.pubsub.send_all(
Message(
"AI" ,
response.text,
message_type="ai_chat_message" ,
)
)
page.pubsub.subscribe(on_message)
chat = ft.ListView(
expand=True ,
spacing=10 ,
auto_scroll=True ,
)
new_message = ft.TextField(
hint_text="Write a message..." ,
autofocus=True ,
shift_enter=True ,
min_lines=1 ,
max_lines=5 ,
filled=True ,
expand=True ,
on_submit=send_message_click,
)
page.add(
ft.Container(
content=chat,
border=ft.border.all(1 , ft.colors.OUTLINE),
border_radius=5 ,
padding=10 ,
expand=True ,
),
ft.Row(
[
new_message,
ft.IconButton(
icon=ft.icons.SEND_ROUNDED,
tooltip="Send message" ,
on_click=send_message_click,
),
]
),
)
user_name = "You"
page.session.set("user_name" , user_name)
new_message.prefix = ft.Text(f"{user_name}: " )
page.pubsub.send_all(
Message(
user_name=user_name,
text=f"{user_name} has joined the chat." ,
message_type="login_message" ,
)
)
page.update()
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY" )
chat_session = None
if GOOGLE_API_KEY is not None :
genai.configure(api_key=GOOGLE_API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash" )
chat_session = model.start_chat(history=[])
else :
def handle_close (e):
print ("handle_close()" )
page.dialog.open = False
page.update()
page.dialog = ft.AlertDialog(
open =True ,
modal=True ,
title=ft.Text("ã¨ã©ã¼" ),
content=ft.Text("ç°å¢å¤æ° GOOGLE_API_KEY ãã»ããããã¦ãã¾ãã" ),
actions=[
ft.TextButton("OK" , on_click=handle_close),
],
actions_alignment=ft.MainAxisAlignment.END,
)
ft.app(target=main)