|
import json |
|
import os |
|
import random |
|
import time |
|
|
|
import pandas as pd |
|
import requests |
|
import streamlit as st |
|
|
|
|
|
|
|
with open("models_info.json", "r") as json_file: |
|
MODELS_INFO = json.load(json_file) |
|
with open("test.csv", "r") as file: |
|
QUESTION_DF = pd.read_csv(file) |
|
MODELS = list(MODELS_INFO.keys()) |
|
NUM_QUESTION = 100 |
|
|
|
|
|
|
|
def get_leaderboard(): |
|
try: |
|
response = requests.get(os.environ['DARABASE_URL']) |
|
response_data = response.json() |
|
return response_data |
|
except Exception as e: |
|
print(f"An unexpected error occurred: {e}") |
|
return "Error" |
|
|
|
|
|
def create_leaderboard_df(): |
|
|
|
ranking = get_leaderboard() |
|
|
|
if ranking == "Error": |
|
st.error("リーダーボードを取得できませんでした。") |
|
print("リーダーボードを取得できませんでした。") |
|
return pd.DataFrame() |
|
else: |
|
|
|
ranks, model_names, ratings, organizations, licenses = [], [], [], [], [] |
|
|
|
for i in range(len(ranking)): |
|
ranks.append(i + 1) |
|
model_names.append(MODELS_INFO[ranking[i]["model"]][0]) |
|
ratings.append(ranking[i]["rating"]) |
|
organizations.append(MODELS_INFO[ranking[i]["model"]][2]) |
|
licenses.append(MODELS_INFO[ranking[i]["model"]][1]) |
|
|
|
return pd.DataFrame({ |
|
"ランク" : ranks, |
|
"🤖 モデル" : model_names, |
|
"⭐️ Eloレーティング" : ratings, |
|
"🏢 組織" : organizations, |
|
"📃 ライセンス" : licenses |
|
}) |
|
|
|
|
|
@st.cache_data |
|
def get_answer(model_name, question_id): |
|
try: |
|
params = {'modelName': model_name, 'questionId': question_id} |
|
response = requests.get(os.environ['ANSWER_URL'], params=params) |
|
response_data = response.json() |
|
return response_data["answer"] |
|
except Exception as e: |
|
print(f"An unexpected error occurred: {e}") |
|
return "Error" |
|
|
|
|
|
def send_choice(question_id, model_a, model_b, winner, language): |
|
|
|
if not question_id or not model_a or not model_b or not winner or not language: |
|
st.error("データが入力されていないため、回答を送信できませんでした。") |
|
print("質問と回答を取得してください。") |
|
return "Error" |
|
try: |
|
data = { |
|
"question_id": question_id, |
|
"model_a": model_a, |
|
"model_b": model_b, |
|
"winner": winner, |
|
"language": language, |
|
"tstamp": time.time(), |
|
} |
|
headers = { |
|
'Content-Type': 'application/json' |
|
} |
|
response = requests.post(os.environ['DARABASE_URL'], headers=headers, data=json.dumps(data)) |
|
response_data = response.text |
|
return response_data |
|
except Exception as e: |
|
print(f"An unexpected error occurred: {e}") |
|
return "Error" |
|
|
|
|
|
|
|
|
|
def handle_init_state(): |
|
if "chat_history_a" not in st.session_state: |
|
st.session_state["chat_history_a"] = [] |
|
if "chat_history_b" not in st.session_state: |
|
st.session_state["chat_history_b"] = [] |
|
if "question_id" not in st.session_state: |
|
st.session_state["question_id"] = None |
|
if "model_a" not in st.session_state: |
|
st.session_state["model_a"] = None |
|
if "model_b" not in st.session_state: |
|
st.session_state["model_b"] = None |
|
if "question" not in st.session_state: |
|
st.session_state["question"] = None |
|
|
|
if "question_loaded" not in st.session_state: |
|
st.session_state["question_loaded"] = False |
|
|
|
if "answer_sent" not in st.session_state: |
|
st.session_state["answer_sent"] = False |
|
|
|
|
|
def handle_init_question(): |
|
|
|
if st.session_state.question_loaded: |
|
st.session_state.question_loaded = False |
|
st.session_state.chat_history_a = [] |
|
st.session_state.chat_history_b = [] |
|
st.error("ボタンを連打しないでください。") |
|
print("既に質問と回答を取得しています。") |
|
else: |
|
|
|
st.session_state.question_loaded = True |
|
st.success("質問と回答を取得しています。しばらくお待ちください。") |
|
|
|
st.session_state.question_id = random.randint(1, NUM_QUESTION) |
|
st.session_state.question = QUESTION_DF["input"][st.session_state.question_id - 1] |
|
st.session_state.chat_history_a.append({"role": "user", "content": st.session_state.question}) |
|
st.session_state.chat_history_b.append({"role": "user", "content": st.session_state.question}) |
|
|
|
random.shuffle(MODELS) |
|
st.session_state.model_a = MODELS[0] |
|
st.session_state.model_b = MODELS[1] |
|
answer_a = get_answer(st.session_state.model_a, st.session_state.question_id) |
|
answer_b = get_answer(st.session_state.model_b, st.session_state.question_id) |
|
|
|
st.session_state.chat_history_a.append({"role": "assistant", "content": answer_a}) |
|
st.session_state.chat_history_b.append({"role": "assistant", "content": answer_b}) |
|
st.success("質問と回答を取得しました。回答を選択してください。") |
|
print("質問と回答を取得しました。") |
|
|
|
|
|
def handle_send_choice(winner): |
|
|
|
if st.session_state.answer_sent: |
|
st.error("既に回答を送信しています。") |
|
print("既に回答を送信しています。") |
|
else: |
|
|
|
st.session_state.answer_sent = True |
|
|
|
response = send_choice( |
|
question_id=st.session_state.question_id, |
|
model_a=st.session_state.model_a, |
|
model_b=st.session_state.model_b, |
|
winner=winner, |
|
language="Japanese" |
|
) |
|
|
|
if response == "Error": |
|
st.error("予期せぬエラーが発生しました。") |
|
else: |
|
st.success("選択肢は正常に送信されました。") |
|
|
|
st.session_state.question_loaded = False |
|
|
|
|
|
|
|
def main(): |
|
|
|
st.set_page_config( |
|
page_title="日本語チャットボットアリーナ", |
|
page_icon="🏆", |
|
layout="wide", |
|
) |
|
|
|
|
|
handle_init_state() |
|
|
|
st.markdown("# 🏆 日本語チャットボットアリーナ") |
|
st.markdown("## 📖 説明") |
|
st.markdown("| [Twitter](https://twitter.com/yutohub) | [GitHub](https://github.com/yutohub) | [ブログ](https://zenn.dev/yutohub) |") |
|
st.markdown("日本語チャットボットアリーナは、日本語に対応しているLLMの評価のためのクラウドソーシングプラットフォームです。[LMSYS Chatbot Arena](https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard) を参考に、日本語に対応しているLLMのリーダーボードを作成することを目的としています。また、一部の質問と回答は、 [ELYZA-tasks-100](https://huggingface.co/datasets/elyza/ELYZA-tasks-100) や [Northern-System-Service/gpt4-autoeval](https://github.com/Northern-System-Service/gpt4-autoeval) を利用しています。") |
|
st.markdown(""" > **注意事項:** |
|
> |
|
> 日本語チャットボットアリーナが提供する情報によって生じたいかなる損害についても、サービス提供者は一切の責任を負いません。 |
|
> 日本語チャットボットアリーナは開発中であり、予告なく停止または終了する可能性があります。 |
|
> また、ユーザーの回答を収集し、Creative Commons Attribution (CC-BY) または同様のライセンスの下で配布する権利を留保しています。 |
|
""") |
|
|
|
|
|
st.markdown("## ⚔️ チャットボットアリーナ ⚔️") |
|
st.markdown(" 2つの匿名モデル (ChatGPT、Llama など) の回答を見て、より良いモデルに投票してください。") |
|
with st.expander(f"🔍 展開するとアリーナに参加している {len(MODELS)} 個のモデルの一覧が表示されます。"): |
|
st.write(MODELS) |
|
model_a, model_b = st.columns([1, 1]) |
|
with model_a: |
|
st.markdown("### モデル A") |
|
if not st.session_state.chat_history_a: |
|
st.markdown("質問を取得してください。") |
|
else: |
|
for message in st.session_state.chat_history_a: |
|
with st.chat_message(message["role"]): |
|
st.write(message["content"]) |
|
|
|
if st.session_state.answer_sent: |
|
with st.chat_message("assistant"): |
|
st.markdown(f"`{st.session_state.model_a}` が回答しました、") |
|
with model_b: |
|
st.markdown("### モデル B") |
|
if not st.session_state.chat_history_b: |
|
st.markdown("質問を取得してください。") |
|
else: |
|
for message in st.session_state.chat_history_b: |
|
with st.chat_message(message["role"]): |
|
st.write(message["content"]) |
|
|
|
if st.session_state.answer_sent: |
|
with st.chat_message("assistant"): |
|
st.markdown(f"`{st.session_state.model_b}` が回答しました。") |
|
|
|
load_question = st.button( |
|
label="質問を取得", |
|
on_click=handle_init_question, |
|
|
|
disabled=st.session_state.answer_sent or st.session_state.question_loaded, |
|
type="primary", |
|
use_container_width=True |
|
) |
|
|
|
choice_1, choice_2, choice_3, choice_4 = st.columns([1, 1, 1, 1]) |
|
with choice_1: |
|
choice_1 = st.button( |
|
label="👈 Aの方が良い", |
|
on_click=handle_send_choice, |
|
args=("model_a",), |
|
disabled=not st.session_state.question_loaded, |
|
use_container_width=True |
|
) |
|
with choice_2: |
|
choice_2 = st.button( |
|
label="👉 Bの方が良い", |
|
on_click=handle_send_choice, |
|
args=("model_b",), |
|
disabled=not st.session_state.question_loaded, |
|
use_container_width=True |
|
) |
|
with choice_3: |
|
choice_3 = st.button( |
|
label="🤝 どちらも良い", |
|
on_click=handle_send_choice, |
|
args=("tie",), |
|
disabled=not st.session_state.question_loaded, |
|
use_container_width=True |
|
) |
|
with choice_4: |
|
choice_4 = st.button( |
|
label="👎 どちらも悪い", |
|
on_click=handle_send_choice, |
|
args=("tie (bothbad)",), |
|
disabled=not st.session_state.question_loaded, |
|
use_container_width=True |
|
) |
|
|
|
|
|
st.markdown("## 🏆 リーダーボード") |
|
st.markdown(f"合計で {len(MODELS)} 個のモデルがアリーナに参加しています。30 分毎にリーダーボードが更新されます。") |
|
|
|
if st.session_state.answer_sent: |
|
|
|
leaderboard = create_leaderboard_df() |
|
st.dataframe( |
|
data=leaderboard, |
|
height=(len(MODELS) + 1) * 35 + 3, |
|
use_container_width=True, |
|
hide_index=True, |
|
) |
|
else: |
|
st.markdown(""" |
|
> まずは、「⚔️ チャットボットアリーナ ⚔️」に回答を送信してください。 |
|
> 回答を送信すると、リーダーボードが表示されます。 |
|
""") |
|
|
|
|
|
st.markdown("## 📚 引用") |
|
st.markdown(""" |
|
``` |
|
@misc{elyzatasks100, |
|
title={ELYZA-tasks-100: 日本語instructionモデル評価データセット}, |
|
url={https://huggingface.co/elyza/ELYZA-tasks-100}, |
|
author={Akira Sasaki and Masato Hirakawa and Shintaro Horie and Tomoaki Nakamura}, |
|
year={2023}, |
|
} |
|
``` |
|
|
|
[(c) 2023 Northern System Service Co., Ltd.](https://github.com/Northern-System-Service/gpt4-autoeval/blob/main/LICENSE) |
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|