気がついたら、Streamlitのチャット入力が。添付ファイルに対応していました!
1.43.0からっぽいです。
出たらやるしかない!Bedrockの画像添付とドキュメント添付で実際に試しました!
これをもとに機能追加していきます
import boto3
import streamlit as st
MODEL_ID = "us.anthropic.claude-3-haiku-20240307-v1:0"
st.title("Bedrock Chat")
# messagesをセッションに保存
if "messages" not in st.session_state:
st.session_state.messages = []
messages = st.session_state.messages
# 会話のやり取りを表示
for message in messages:
with st.chat_message(message["role"]):
for content in message["content"]:
if "text" in content:
st.write(content["text"])
# ユーザー入力
if prompt := st.chat_input():
# ユーザー入力の表示
with st.chat_message("user"):
st.write(prompt)
# Converse APIに送信するメッセージを作成
user_message = {"role": "user", "content": [{"text": prompt}]}
messages.append(user_message)
# Converse API呼び出し
client = boto3.client("bedrock-runtime")
response = client.converse(modelId=MODEL_ID, messages=messages)
assistant_message = response["output"]["message"]
messages.append(assistant_message)
# Bedrockの返答を表示
with st.chat_message("assistant"):
st.write(assistant_message["content"][0]["text"])
画像のインプットに対応する
st.chat_input()
のaccept_file
を指定すると、ファイルの添付が可能になります。さらに、file_type
で対応するファイルの形式を指定できます。
BedrockのConverse APIが対応している画像フォーマットに絞って添付できるようにします。
+ IMAGE_FORMAT = ["png", "jpeg", "gif", "webp"]
- if prompt := st.chat_input():
+ if prompt := st.chat_input(accept_file="multiple", file_type=IMAGE_FORMAT):
画面はこのように変わります。
ファイル添付を有効にするとprompt
の型が変わります。ユーザー入力のテキストはprompt.text
に変更なります。
with st.chat_message("user"):
- st.write(prompt)
+ st.write(prompt.text)
- user_message = {"role": "user", "content": [{"text": prompt}]}
+ user_message = {"role": "user", "content": [{"text": prompt.text}]}
アップロードしたファイルはprompt.files
で取得できます。
for file in prompt.files:
file.name # ファイル名
file.type # ファイルのMimeType
file.getvalue() # ファイルのバイナリ
st.file_uploader
のドキュメントが参考になります。
画面へ表示する際はfile
をst.image()
に渡すだけでOKです。
with st.chat_message("user"):
st.write(prompt.text)
+ for file in prompt.files:
+ st.image(file)
添付された画像をConverse APIへ送信するmessagesに追加します。
file.type
にはMIMEタイプがセットされているので、/
以降を切り取ってformat
に指定します。
(例:「image/jpeg」→「jpeg」)
細かい点ですが、「jpeg」はOKですが「jpg」はバリデーションエラーになります。(大文字もだめ)
そのため、ファイル名のから拡張子を取るよりMIMEタイプの後ろを取ったほうが良さそうです
+ for file in prompt.files:
+ user_message["content"].append(
+ {
+ "image": {
+ "format": file.type.split("/")[1],
+ "source": {"bytes": file.getvalue()},
+ }
+ }
+ )
これで画像の添付ができるようになりました。
ソースの全体はこちらです。
import boto3
import streamlit as st
MODEL_ID = "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
st.title("Bedrock Chat")
# 対応画像フォーマット
IMAGE_FORMAT = ["png", "jpeg", "gif", "webp"]
# messagesをセッションに保存
if "messages" not in st.session_state:
st.session_state.messages = []
messages = st.session_state.messages
# 会話のやり取りを表示
for message in messages:
with st.chat_message(message["role"]):
for content in message["content"]:
if "text" in content:
st.write(content["text"])
elif "image" in content:
st.image(content["image"]["source"]["bytes"])
# ユーザー入力
if prompt := st.chat_input(accept_file="multiple", file_type=IMAGE_FORMAT):
# ユーザー入力の表示
with st.chat_message("user"):
st.write(prompt.text)
for file in prompt.files:
st.image(file)
# Converse APIに送信するメッセージを作成
user_message = {"role": "user", "content": [{"text": prompt.text}]}
for file in prompt.files:
user_message["content"].append(
{
"image": {
"format": file.type.split("/")[1],
"source": {"bytes": file.getvalue()},
}
}
)
messages.append(user_message)
# Converse API呼び出し
client = boto3.client("bedrock-runtime")
response = client.converse(modelId=MODEL_ID, messages=messages)
assistant_message = response["output"]["message"]
messages.append(assistant_message)
# Bedrockの返答を表示
with st.chat_message("assistant"):
st.write(assistant_message["content"][0]["text"])
ドキュメントのインプットに対応する
BedrockのConverse APIは画像だけでなく、PDFなどのファイルの入力にも対応しています。画像の入力と組み合わせることも可能です。
ドキュメントが対応しているフォーマットを定義し、st.chat_input
のfile_type
に追加します。
+ # 対応ドキュメントフォーマット
+ DOCUMENT_FORMAT = ["pdf", "csv", "doc", "docx", "xls", "xlsx", "html", "txt", "md"]
- if prompt := st.chat_input(accept_file="multiple", file_type=IMAGE_FORMAT):
+ if prompt := st.chat_input(
+ accept_file="multiple", file_type=IMAGE_FORMAT + DOCUMENT_FORMAT
+ ):
ドキュメント部分のコンテンツをConverse APIへ送信するmessagesに追加します。
画像(image)と異なり、formatは拡張子を取得します。また、documentの場合はname
属性があります。このname
は入力規則があり、日本語文字が指定できません(バリデーションエラーになります)。そのため、uuidで生成するようにしました。
for file in prompt.files:
+ if (file_format := f.type.split("/")[1]) in IMAGE_FORMAT:
user_message["content"].append(
{
"image": {
"format": file_format,
"source": {"bytes": file.getvalue()},
}
}
)
+ elif (ext := os.path.splitext(f.name)[1][1:]) in DOCUMENT_FORMAT:
+ user_message["content"].append(
+ {
+ "document": {
+ "format": ext,
+ "name": str(uuid.uuid4()),
+ "source": {"bytes": file.getvalue()},
+ }
+ }
+ )
最後に画面に表示する部分を追加します。ファイル名ぐらいしか出せなですが。
# 会話のやり取りを表示
for message in messages:
with st.chat_message(message["role"]):
for content in message["content"]:
if "text" in content:
st.write(content["text"])
elif "image" in content:
st.image(content["image"]["source"]["bytes"])
+ elif "document" in content:
+ st.write(content["document"]["name"])
# ユーザー入力の表示
with st.chat_message("user"):
st.write(prompt.text)
for file in prompt.files:
if file.type.split("/")[1] in IMAGE_FORMAT:
st.image(file)
+ elif os.path.splitext(f.name)[1][1:] in DOCUMENT_FORMAT:
+ st.write(file.name)
できました。
import os
import uuid
import boto3
import streamlit as st
MODEL_ID = "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
st.title("Bedrock Chat")
# 対応画像フォーマット
IMAGE_FORMAT = ["png", "jpeg", "gif", "webp"]
# 対応ドキュメントフォーマット
DOCUMENT_FORMAT = ["pdf", "csv", "doc", "docx", "xls", "xlsx", "html", "txt", "md"]
# messagesをセッションに保存
if "messages" not in st.session_state:
st.session_state.messages = []
messages = st.session_state.messages
# 会話のやり取りを表示
for message in messages:
with st.chat_message(message["role"]):
for content in message["content"]:
if "text" in content:
st.write(content["text"])
elif "image" in content:
st.image(content["image"]["source"]["bytes"])
elif "document" in content:
st.write(content["document"]["name"])
# ユーザー入力
if prompt := st.chat_input(
accept_file="multiple", file_type=IMAGE_FORMAT + DOCUMENT_FORMAT
):
# ユーザー入力の表示
with st.chat_message("user"):
st.write(prompt.text)
for file in prompt.files:
if file.type.split("/")[1] in IMAGE_FORMAT:
st.image(file)
elif os.path.splitext(f.name)[1][1:] in DOCUMENT_FORMAT:
st.write(file.name)
# Converse APIに送信するメッセージを作成
user_message = {"role": "user", "content": [{"text": prompt.text}]}
for file in prompt.files:
if (file_format := f.type.split("/")[1]) in IMAGE_FORMAT:
user_message["content"].append(
{
"image": {
"format": file_format,
"source": {"bytes": file.getvalue()},
}
}
)
elif (ext := os.path.splitext(f.name)[1][1:]) in DOCUMENT_FORMAT:
user_message["content"].append(
{
"document": {
"format": ext,
"name": str(uuid.uuid4()),
"source": {"bytes": file.getvalue()},
}
}
)
messages.append(user_message)
# Converse API呼び出し
client = boto3.client("bedrock-runtime")
response = client.converse(modelId=MODEL_ID, messages=messages)
assistant_message = response["output"]["message"]
messages.append(assistant_message)
# Bedrockの返答を表示
with st.chat_message("assistant"):
st.write(assistant_message["content"][0]["text"])
どうぞご活用ください!