diff --git a/README.md b/README.md
index fd856baf3135fabc6447bce51dbe90d5ec6fd08d..8f08e65b2f687bea25e39b91d5d100baf0050b42 100644
--- a/README.md
+++ b/README.md
@@ -12,3 +12,125 @@ short_description: VARCO Arena is a reference-free LLM benchmarking approach
---
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
+
+# Varco Arena
+Varco Arena conducts tournaments between models to be compared for each test set command, ranking models accurately at an affordable price. This is more accurate and cost-effective than rating win rates by comparing against reference outputs.
+
+For more information, the followings may help understanding how it works.
+* [Paper](https://huggingface.co/papers/2411.01281)
+* [Blog Post (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+
+
+## Quickstart
+### Running Web Demo locally (streamlit, Recommended!)
+```bash
+git clone [THIS_REPO]
+# install requirements below. we recommend miniforge to manage environment
+cd streamlit_app_local
+bash run.sh
+```
+For more details, see `[THIS_REPO]/streamlit_app_local/README.md`
+
+### CLI use
+* located at
+ * `varco_arena/`
+* debug configurations for vscode at
+ * `varco_arena/.vscode`
+```bash
+## gpt-4o-mini as a judge
+python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -m tournament -e "gpt-4o-mini"
+## vllm-openai served LLM as a judge
+python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -e SOME_MODEL_NAME_SERVED -m tournament -u "http://url_to/your/vllm_openai_server:someport"
+
+# dbg lines
+## openai api judge dbg
+python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
+## other testing lines
+python main.py -i "rsc/inputs_for_dbg/[SOME_DIRECTORY]/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
+## dummy judge dbg (checking errors without api requests)
+python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e debug
+```
+
+## Requirements
+We tested this on `python = 3.11.9` env: `requirements.txt`
+```
+openai>=1.17.0
+munch
+pandas
+numpy
+tqdm>=4.48.0
+plotly
+scikit-learn
+kaleido
+tiktoken>=0.7.0
+pyyaml
+transformers
+streamlit>=1.40.2
+openpyxl
+git+https://github.com/shobrook/openlimit.git#egg=openlimit # do not install this by pypi
+
+# Linux
+uvloop
+# Windows
+winloop
+```
+
+#### Argument
+- -i, --input : directory path which contains input jsonlines files (llm outputs)
+- -o, --output_dir : directory where results to be put
+- -e, --evaluation : judge model specification (e.g. "gpt-4o-2024-05-13", "gpt-4o-mini", \[vllm-served-model-name\])
+- -k, --openai_api_key : OpenAI API Key
+- -u, --openai_url: URL to openai_styled_llm_server (requested by openai sdk)
+
+#### advanced
+- -j, --n_jobs : n jobs to be put to `asyncio.semaphore(n=)`
+- -p, --evalprompt : [see the directory](./varco_arena/prompts/*.yaml)
+- -lr, --limit_requests : vLLM OpenAI server request limit (default: 7,680)
+- -lt, --limit_tokens : vLLM OpenAI server token limit (default: 15,728,640)
+
+#### Input Data Format
+[input jsonl guides](./streamlit_app_local/guide_mds/input_jsonls_en.md)
+
+
+## Contributing & Customizing
+#### Do this after git clone and installation
+```bash
+pip install pre-commit
+pre-commit install
+```
+#### before commit
+```bash
+bash precommit.sh # black formatter will reformat the codes
+```
+
+## FAQ
+* I want to apply my custom judge prompt to run Varco Arena
+ * [`./varco_arena/prompts/`](./varco_arena/prompts/__init__.py) defines the prompts with `yaml` file and the class objects for those. Edit those as your need.
+* I want tailored judge prompts for each line of the test set row (i.e. ~100th row - `prompt1`, 101st~ - `prompt2`)
+ * You could see `load_prompt` at the above link receives `promptname` + `task` as a parameters to load the prompt. The function is called at [`./varco_arena/manager.py:async_run`](./varco_arena/manager.py).
+* I want more fields for my llm outputs jsonl files for tailored use, i.e. want more fields beyond `instruction`, `source`, `generated`.
+ * It's going to get tricky but let me briefly guide you about this.
+ * You might have to edit `varco_arena/eval_utils.py`:`async_eval_w_prompt` (this part calls `PROMPT_OBJ.complete_prompt()`)
+ * And all the related codes will require revision.
+
+## Special Thanks to (contributors)
+- Minho Lee (@Dialogue Model Team, NCSOFT) [github](https://github.com/minolee/)
+ - query wrapper
+ - rag prompt
+- Jumin Oh (@Generation Model Team, NCSOFT)
+ - overall prototyping of the system in haste
+
+
+## Citation
+If you found our work helpful, consider citing our paper!
+```
+@misc{son2024varcoarenatournamentapproach,
+ title={Varco Arena: A Tournament Approach to Reference-Free Benchmarking Large Language Models},
+ author={Seonil Son and Ju-Min Oh and Heegon Jin and Cheolhun Jang and Jeongbeom Jeong and Kuntae Kim},
+ year={2024},
+ eprint={2411.01281},
+ archivePrefix={arXiv},
+ primaryClass={cs.CL},
+ url={https://arxiv.org/abs/2411.01281},
+}
+```
diff --git a/README_kr.md b/README_kr.md
new file mode 100644
index 0000000000000000000000000000000000000000..142daffde73a676fc747c3f3d63d2a6ddb101cef
--- /dev/null
+++ b/README_kr.md
@@ -0,0 +1,123 @@
+# Varco Arena
+바르코 아레나는 테스트셋 명령어별로 비교할 모델들의 토너먼트를 수행하여 정확하게 모델들의 순위를 매깁니다. 이것은 reference 아웃풋과 비교하여 승률을 매기는 방법보다 정확하며 조금 더 저렴합니다.
+
+더 자세한 내용에 대해서는 아래의 링크를 참조하시면 됩니다.
+* [논문](https://huggingface.co/papers/2411.01281)
+* [엔씨소프트 테크블로그 (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+
+
+## Quickstart
+### 로컬에서 스트림릿 앱으로 시작하기 (추천!)
+```bash
+git clone [THIS_REPO]
+# install requirements below. we recommend miniforge to manage environment
+cd streamlit_app_local
+bash run.sh
+```
+더 자세한 내용은 `[THIS_REPO]/streamlit_app_local/README.md` 을 참조하세요!
+
+### CLI 사용
+* cli와 웹 앱은 서로 같은 코드를 활용하며, 아래의 디렉토리에 있습니다.
+ * `varco_arena/`
+* vscode 상에서 디버깅을 위한 프리셋 프롬프트별 테스트 명령어는 다음 파일에 적혀있습니다.
+ * `varco_arena/.vscode/launch.json`
+```bash
+## gpt-4o-mini as a judge
+python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -m tournament -e "gpt-4o-mini"
+## vllm-openai served LLM as a judge
+python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -e SOME_MODEL_NAME_SERVED -m tournament -u "http://url_to/your/vllm_openai_server:someport"
+
+# dbg lines
+## openai api judge dbg
+python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
+## other testing lines
+python main.py -i "rsc/inputs_for_dbg/[SOME_DIRECTORY]/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
+## dummy judge dbg (checking errors without api requests)
+python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e debug
+```
+
+## Requirements
+`python = 3.11.9` 상에서 테스트 함. `requirements.txt`
+```
+openai>=1.17.0
+munch
+pandas
+numpy
+tqdm>=4.48.0
+plotly
+scikit-learn
+kaleido
+tiktoken>=0.7.0
+pyyaml
+transformers
+streamlit>=1.40.2
+openpyxl
+git+https://github.com/shobrook/openlimit.git#egg=openlimit # do not install this by pypi
+
+# Linux인 경우
+uvloop
+# Windows인 경우
+winloop
+```
+
+#### Argument
+- -i, --input : 입력 파일 or 디렉토리 or 파일명에 대한 정규 표현식
+- -o, --output_dir : 출력 파일이 저장되는 디렉토리
+- -e, --evaluation : 평가 모델 (e.g. "gpt-4o-2024-05-13", "gpt-4o-mini", vllm에서 띄운 모델 명 등)
+- -m, --matching_method: 매치 방식 (기본값 "tournament", "league" (비추천) )
+- -k, --openai_api_key : OpenAI API Key
+- -u, --openai_url: 로컬 vLLM OpenAI 서버 사용 시 URL(ip주소+포트)
+
+#### advanced
+- -j, --n_jobs : asyncio.semaphore()에 전달될 인자. Arena가 진행되지 않는다면 기본값인 32 이하로 내려보자
+- -p, --evalprompt : [해당 디렉토리 참조](./varco_arena/prompts/*.yaml)
+- -lr, --limit_requests : vLLM OpenAI 서버 요청 제한 (default: 7,680)
+- -lt, --limit_tokens : vLLM OpenAI 서버 토큰 제한 (default: 15,728,640)
+
+#### Input Data Format
+[input jsonl 가이드 링크](./streamlit_app_local/guide_mds/input_jsonls_kr.md)
+
+
+
+## Contributing & Customizing
+#### git clone 및 dependency 설치 후에 할 일
+```bash
+pip install pre-commit
+pre-commit install
+```
+#### commit 하기 전에 할 일
+```bash
+bash precommit.sh # 이게 코드들을 다 리포맷해줄거임
+```
+
+
+문의: 손선일
+* 내가 만든 프롬프트를 사용하고 싶어요
+ * [`./varco_arena/prompts/`](./varco_arena_core/prompts/__init__.py) 에선 각종 프롬프트 클래스 및 `yaml` 파일 형태로 정의된 프롬프트를 로드합니다. 프리셋을 참조하여 작성하시면 됩니다.
+* 테스트셋 별로 다른 평가 프롬프트를 사용하고 싶어요 (e.g. 작업에 따라 다른 프롬프트를 사용하고 싶어요)
+ * 위 걸어드린 링크의 `load_prompt` 를 통해서 `promptname` + `task` 형태로 [`./varco_arena_core/manager.py:async_run`](./varco_arena_core/manager.py) 프롬프트가 로드되도록 해놓았습니다.
+* 제가 사용하고 싶은 입력파일에 `instruction`, `source`, `generated` 이외에 다른 필드를 추가해서 사용하고 싶어요.
+ * 조금 복잡해지는데 다음 부분을 고쳐주세요
+ * `varco_arena/eval_utils.py` 에서 `async_eval_w_prompt` 부분을 손봐야할 수 있습니다 (여기에서 PROMPT_OBJ.complete_prompt()을 호출함)
+ * 그 외 연관된 부분은 타고타고 고쳐주셔야...
+
+## Special Thanks to (contributors)
+- 이민호 (@대화모델팀, NCSOFT) [github](https://github.com/minolee/)
+ - query wrapper
+ - rag prompt
+- 오주민 (@생성모델팀, NCSOFT)
+ - overall prototyping of the system in haste
+
+## Citation
+저희 작업물이 도움이 되었다면 저희도 도움을 받아볼 수 있을까요?😉
+```
+@misc{son2024varcoarenatournamentapproach,
+ title={Varco Arena: A Tournament Approach to Reference-Free Benchmarking Large Language Models},
+ author={Seonil Son and Ju-Min Oh and Heegon Jin and Cheolhun Jang and Jeongbeom Jeong and Kuntae Kim},
+ year={2024},
+ eprint={2411.01281},
+ archivePrefix={arXiv},
+ primaryClass={cs.CL},
+ url={https://arxiv.org/abs/2411.01281},
+}
+```
diff --git a/__pycache__/analysis_utils.cpython-311.pyc b/__pycache__/analysis_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..eb45262f95ef05856ea88c8d12398f3020fe4a21
Binary files /dev/null and b/__pycache__/analysis_utils.cpython-311.pyc differ
diff --git a/__pycache__/app.cpython-311.pyc b/__pycache__/app.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..31bd6015e3a15041cdd96ed5bcfa99f6c443e1b1
Binary files /dev/null and b/__pycache__/app.cpython-311.pyc differ
diff --git a/__pycache__/query_comp.cpython-311.pyc b/__pycache__/query_comp.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d709079c26f7d80f520326184b90e0f867105813
Binary files /dev/null and b/__pycache__/query_comp.cpython-311.pyc differ
diff --git a/__pycache__/view_utils.cpython-311.pyc b/__pycache__/view_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f65fbbcefe94615fe84403f527a531497c3bc224
Binary files /dev/null and b/__pycache__/view_utils.cpython-311.pyc differ
diff --git a/analysis_utils.py b/analysis_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..e480fa35791309f93eb84eca49a51739f369fb29
--- /dev/null
+++ b/analysis_utils.py
@@ -0,0 +1,338 @@
+from copy import deepcopy
+from functools import partial
+from typing import *
+
+import pandas as pd
+from fire import Fire
+
+"""
+This code assumes dealing with only one instruction
+"""
+
+
+# from varco_arena.tournament
+def log2_power_of_two(n):
+ # First, let's make sure n is indeed a power of 2
+ if n & (n - 1) != 0 or n == 0:
+ raise ValueError("n must be a positive power of 2")
+
+ exponent = 0
+ while n > 1:
+ n >>= 1 # Right shift is like dividing by 2, but faster
+ exponent += 1
+ return exponent
+
+
+def get_1st(df: pd.DataFrame, alpha2names: dict) -> Optional[str]:
+ finals = df[df["round"] == "final"]
+ if len(finals) == 1:
+ first = finals.iloc[0].winner_resolved
+ else:
+ first = (
+ None # error case (no finals match or multiple finals (buggy result file))
+ )
+
+ return first
+
+
+def get_unique_participants(df: pd.DataFrame) -> list:
+ participants = pd.concat([df.model_a, df.model_b]).unique().tolist()
+ participants = [p for p in participants if p] # remove None
+ participants = sorted(participants) # make it sorted
+ return participants
+
+
+def _impute_byes(df):
+ max_depth = df.depth.max()
+
+ # init
+ imputed_parts = dict()
+ for depth in range(max_depth + 1):
+ imputed_parts[depth] = df[df.depth == depth].copy()
+
+ # reverse
+ for depth in range(max_depth, 0, -1): # always we have 1 proper match for depth=0
+ null_v_null = {
+ "model_a": "",
+ "model_b": "",
+ "winner": "model_a",
+ "match_order_in_round": "-",
+ "depth": depth,
+ }
+
+ # fill some_model vs null byes
+ players = get_unique_participants(imputed_parts[depth])
+ proceeded = get_unique_participants(imputed_parts[depth - 1])
+ imputed = []
+ for p in proceeded:
+ if p not in players:
+ p_v_null = deepcopy(null_v_null)
+ p_v_null["model_a"] = p
+ imputed.append(p_v_null)
+ imputed_parts[depth] = pd.concat(
+ [
+ imputed_parts[depth],
+ pd.DataFrame(imputed),
+ ],
+ axis="index",
+ )
+
+ # fill null vs null
+ n_null_v_null = 2 ** (depth) - len(imputed_parts[depth])
+ if n_null_v_null > 0:
+ imputed = pd.DataFrame([null_v_null] * n_null_v_null)
+ imputed_parts[depth] = pd.concat(
+ [
+ imputed_parts[depth],
+ imputed,
+ ],
+ axis="index",
+ )
+
+ df_imputed = pd.concat(imputed_parts.values(), axis="index")
+ df_imputed = df_imputed.sort_values(by="depth").reset_index(drop=True)
+
+ return df_imputed
+
+
+def index_test_scenario(df) -> pd.DataFrame:
+ df["inst_src"] = "inst: " + df.instruction + "\n\nsrc: " + df.source
+ df["idx_inst_src"] = df.apply(
+ lambda row: f"{row.tournament_idx}:\n{row.inst_src}", axis=1
+ )
+
+ # later used for tournament bracket backtrackiung
+ if "depth" not in df.columns:
+ mappings = {
+ "final": 0,
+ "semi-final": 1,
+ "quarter-final": 2,
+ }
+
+ def _convert_round_to_depth(rnd: str, mappings=None) -> int:
+ if rnd is None:
+ depth = None
+ elif rnd in mappings.keys():
+ depth = mappings[rnd]
+ elif rnd.startswith("round-"): # assume perfect power of two
+ num = int(rnd.replace("round-", "").strip())
+ depth = log2_power_of_two(num) - 1
+ return depth
+
+ conv = partial(_convert_round_to_depth, mappings=mappings)
+ df["depth"] = df["round"].apply(conv)
+
+ return df
+
+
+def init_tournament_dataframe(df, alpha2names: dict = None) -> pd.DataFrame:
+ df = df.sort_values(by="depth").reset_index(drop=True)
+ # make winner interpretable (A -> model_a, B -> model_b)
+ df.winner = df.winner.apply(lambda txt: f"model_{txt.lower()}")
+
+ # define alpha2names if not given (covers upto 168 participants)
+ if alpha2names is None:
+ alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+ abcdefghijklmnopqrstuvwxyz\
+ ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ\
+ ㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭\
+ ㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻\
+ ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ\
+ ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ\
+ αβγδεζηθικλμνξοπρστυφχψω"
+ model_full_names = get_unique_participants(df)
+ alpha2names = dict(zip(alphabets, model_full_names))
+ if len(alpha2names) < len(model_full_names):
+ raise ValueError(
+ f"Tournament viewer cannot visualize more than {len(alphabets)=} participants. ({len(model_full_names)=} is given)\n\nOther features will not be affected but the tournament visualizer."
+ )
+ names2alpha = dict(zip(alpha2names.values(), alpha2names.keys()))
+ df = _impute_byes(df)
+
+ # preserve readables for later
+ df = _make_readables(df, names2alpha)
+
+ if len(df[df["round"] == "final"]) != 1:
+ raise ValueError(f"final match need to be one and only.")
+
+ return df, alpha2names
+
+
+def _make_readables(df, names2alpha):
+ df["human_readable_model_a"] = df.model_a.copy()
+ df["human_readable_model_b"] = df.model_b.copy()
+
+ df.model_a = df.model_a.apply(
+ lambda modelname: names2alpha[modelname] if modelname else "x"
+ )
+ df.model_b = df.model_b.apply(
+ lambda modelname: names2alpha[modelname] if modelname else "x"
+ )
+
+ df["human_readable_idx"] = df.apply(
+ lambda row: f"{row.name}: {row.human_readable_model_a} ({row.model_a}) vs. {row.human_readable_model_b} ({row.model_b if row.model_b else 'x'})",
+ axis=1,
+ )
+
+ df["winner_resolved"] = df.apply(lambda row: row[row.winner], axis=1)
+ df["winner_nodes"] = df.apply(
+ lambda row: f"{row.winner_resolved}:{row.name}".ljust(4, " "), axis=1
+ ) # later for figure representation of winner as a "node"
+ return df
+
+
+# draw
+def draw(df: pd.DataFrame, alpha2names: dict = None) -> str:
+ def _draw_round(
+ df: pd.DataFrame,
+ depth: int = None,
+ winners_in_order: list = None,
+ ) -> Tuple:
+ df_now = df[df.depth == depth]
+ max_depth = df.depth.max()
+
+ width = 2 ** ((max_depth - depth) + 2)
+
+ connect_left = "─" * (width)
+ connect_left = connect_left[4:]
+ connect_right = " " * (width)
+ connect_right = "┐" + connect_right[1:]
+
+ if winners_in_order is None:
+ assert (
+ depth == 0
+ ), f"{winners_in_order=} is only allowed when drawing the top (=final match)"
+ winners_in_order = df_now.winner_nodes
+
+ round_drawing_parts = []
+ descending_round_winners = []
+ for node in winners_in_order:
+ round_drawing_parts.append("".join([node, connect_left, connect_right]))
+ # next round winners in sync with winner order
+ row_now = df_now.query(f"winner_nodes=='{node}'")
+ descending_round_winners.append(row_now.model_a.item())
+ descending_round_winners.append(row_now.model_b.item())
+
+ # find descending_round_winners within winner_nodes format (num:alpha)
+ if depth == max_depth:
+ pass # keep the descending_round_winners intact
+ else:
+ df_descend = df[df.depth == depth + 1]
+ for i, winner_alpha in enumerate(descending_round_winners):
+ node_intr = df_descend.query(
+ f"winner_resolved=='{winner_alpha}'"
+ ).winner_nodes.item()
+ descending_round_winners[i] = node_intr
+
+ round_drawing = "".join(round_drawing_parts)
+ descending_unit = " " * width
+ descending_unit = "│" + descending_unit[1:]
+ descending_lines_parts = [descending_unit] * len(df_now) * 2
+ descending_lines = "".join(descending_lines_parts)
+
+ return round_drawing, descending_lines, descending_round_winners
+
+ drawings = []
+ winners_in_order = None
+ max_depth = df.depth.max()
+ for depth in range(max_depth + 1):
+ max_depth = df.depth.max()
+ winner_drw, lines_desc, winners_in_order = _draw_round(
+ df,
+ depth=depth,
+ winners_in_order=winners_in_order,
+ )
+ drawings.append((winner_drw, lines_desc))
+ # prepare bracket top
+ champion_alphabet = drawings[0][0].split()[0].split(":")[0]
+ champion_readable = alpha2names[champion_alphabet]
+ bracket_top = [f"🥇winner: {champion_readable}", "│"]
+ # prepare mid
+ bracket_mid = "\n".join(["\n".join(tup) for tup in drawings])
+
+ # prepare bot
+ initial_participants = winners_in_order
+ bracket_bot = (" " * 3).join(initial_participants)
+
+ full_figure = "\n".join(bracket_top + [bracket_mid, bracket_bot])
+
+ return full_figure
+
+
+def number_breakdown_from_df(result_df: pd.DataFrame) -> str:
+ n_models = len(get_unique_participants(result_df))
+ size_testset = int(len(result_df) / (n_models - 1))
+ interpretation = f"total {len(result_df)} matches = (n_models-1) * size_testset = ({n_models}-1) * {size_testset}"
+ return interpretation, n_models, size_testset
+
+
+def make_legend_str(df, alpha2names) -> str:
+ first = get_1st(df, alpha2names)
+ alpha2names = {k: v.replace("🥇 ", "") for k, v in alpha2names.items()}
+ alpha_ordered = sorted(list(alpha2names.keys()))
+ # names_ordered = sorted(list(alpha2names.values()))
+ # name2alpha = {v: k for k, v in alpha2names.items()}
+
+ for k, v in alpha2names.items():
+ if v == alpha2names[first]:
+ alpha2names[k] = f"🥇 {v}"
+ res_str = f"\n\nlegend:"
+ # for name in names_ordered:
+ # alpha = name2alpha[name]
+ for alpha in alpha_ordered:
+ name_w_medal = alpha2names[alpha]
+ res_str += f"\n{alpha}\t{name_w_medal}"
+ return res_str
+
+
+def main(
+ jslname: str = "result.json",
+):
+ """
+ 테스트 코드
+ """
+
+ df = pd.read_json(jslname, orient="records")
+ df = df.drop(columns=["tstamp", "logs"])
+ df = index_test_scenario(df)
+
+ # 중간에 visualization(df) 여기선 생략. 만약 이거 뺴고 다 따라했는데 문제가 생긴다면 viz 문제다. 근데 안그럴거같긴함
+ selections = df.idx_inst_src.unique()
+ for i, sel in enumerate(selections):
+ try:
+ df_now = df[df.idx_inst_src == sel]
+ df_now_processed, _alpha2names = init_tournament_dataframe(
+ df_now, alpha2names=alpha2names if "alpha2names" in dir() else None
+ )
+ if "alpha2names" not in dir():
+ alpha2names = _alpha2names
+ assert "alpha2names" in dir()
+ bracket_drawing = draw(df_now_processed, alpha2names=alpha2names)
+ legend = make_legend_str(df_now_processed, alpha2names)
+ print(bracket_drawing + legend)
+ print(bracket_drawing + legend, file=open(f"{i}.txt", "w"))
+ print(f"\n\n{sel}", file=open(f"{i}.txt", "a"))
+
+ for match_idx_human in df_now_processed.human_readable_idx:
+ match_idx = int(match_idx_human.split(": ")[0])
+ row = df_now_processed.loc[match_idx]
+ winner = row.winner
+ except Exception as e:
+ print(e, file=open(f"{i}_err.txt", "w"))
+ print("", file=open(f"{i}_err.txt", "a"))
+ print(sel, file=open(f"{i}_err.txt", "a"))
+ df_now_processed[
+ [
+ "depth",
+ "round",
+ "winner_nodes",
+ "winner_resolved",
+ "winner",
+ "model_a",
+ "model_b",
+ ]
+ ].to_json(f"{i}_err.jsonl", lines=True, orient="records")
+
+
+if __name__ == "__main__":
+ Fire(main)
diff --git a/app.py b/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..d18ea662599d4f548a50ebe70017474c5823a827
--- /dev/null
+++ b/app.py
@@ -0,0 +1,452 @@
+# import shutil
+import os
+import select
+import subprocess
+import sys
+import time
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+from typing import *
+
+import streamlit as st
+
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
+
+from varco_arena.varco_arena_core.prompts import load_prompt
+from view_utils import (
+ default_page_setting,
+ escape_markdown,
+ set_nav_bar,
+ show_linebreak_in_md,
+)
+
+VA_ROOT = Path(os.environ.get("VARCO_ARENA_RESULT_PATH", "./user_submit"))
+USR_SUB = VA_ROOT.parts[-1]
+
+import shutil
+
+import pandas as pd
+
+import analysis_utils as au
+from view_utils import visualization
+
+
+class DataCache:
+ def __init__(self):
+ self.cache = {}
+
+ def store(self, key: str, data: dict):
+ self.cache[key] = data
+
+ def get(self, key: str) -> Optional[dict]:
+ return self.cache.get(key)
+
+
+# Initialize the cache in session state if it doesn't exist
+if "data_cache" not in st.session_state:
+ st.session_state.data_cache = DataCache()
+
+
+def purge_user_sub_data(data_path_to_purge: Union[Path, str] = None):
+ if data_path_to_purge is None:
+ print("nothing to purge")
+ return
+ else:
+ shutil.rmtree(data_path_to_purge)
+ print(f"purged {str(data_path_to_purge)}")
+ return
+
+
+@st.cache_data
+def load_and_cache_data(result_file_path: Optional[str] = None) -> Tuple[Dict, Dict]:
+ """
+ Load data from file, cache it in memory, then remove the file.
+ Returns cached data on subsequent calls.
+
+ Args:
+ result_file_path: Path to the result JSON file
+
+ Returns:
+ Tuple of (all_result_dict, df_dict)
+ """
+ # Check if we already have cached data for this path
+ if result_file_path:
+ cache_key = str(Path(result_file_path))
+ cached_data = st.session_state.data_cache.get(cache_key)
+ if cached_data:
+ return cached_data["all_result_dict"], cached_data["df_dict"]
+
+ # Initialize empty dicts
+ all_result_dict = {}
+ df_dict = {}
+
+ if result_file_path is not None:
+ try:
+ result_file_path = Path(result_file_path)
+
+ # Read and process data
+ df = pd.read_json(result_file_path)
+ for col in ["tstamp", "logs"]:
+ if col in df.columns:
+ df.drop(columns=[col], inplace=True)
+ df = au.index_test_scenario(df)
+
+ fig_dict_per_task = {}
+ df_dict_per_task = {}
+
+ # Process overall data
+ fig_dict_per_task["Overall"] = visualization(df, is_overall=True)
+ df_dict_per_task["Overall"] = df
+
+ # Process per-task data
+ for task in df["task"].unique():
+ df_task = df[df["task"] == task]
+ fig_dict_per_task[task] = visualization(df_task, is_overall=False)
+ df_dict_per_task[task] = df_task
+
+ # Create key from path components
+ prm_name = result_file_path.parts[-2]
+ exp_name = result_file_path.parts[-3]
+ key = f"{exp_name}/{prm_name}"
+
+ all_result_dict[key] = fig_dict_per_task
+ df_dict[key] = df_dict_per_task
+
+ # Store in cache before removing file
+ cache_data = {"all_result_dict": all_result_dict, "df_dict": df_dict}
+ st.session_state.data_cache.store(str(result_file_path), cache_data)
+
+ # Remove user experiment directory
+ purge_user_sub_data(data_path_to_purge=VA_ROOT)
+
+ except Exception as e:
+ st.error(f"Error processing data: {str(e)}")
+ return {}, {}
+
+ return all_result_dict, df_dict
+
+
+def upload_files(uploaded_files) -> Path:
+ # prep directory for user submission
+ user_sub_root = VA_ROOT
+ if user_sub_root.exists():
+ if not user_sub_root.is_dir():
+ raise ValueError(
+ f"{user_sub_root} file exists and is not a directory. Consider renaming it."
+ )
+ else:
+ user_sub_root.mkdir(parents=True)
+
+ KST = timezone(timedelta(hours=9))
+ tstamp = datetime.now(KST)
+ tstr = tstamp.strftime("%m-%d_%H:%M:%S")
+ files_dir_str = "./" + str(user_sub_root / tstr)
+ files_dir = Path(files_dir_str)
+ files_dir.mkdir(parents=True, exist_ok=True)
+ uploaded_files = list(uploaded_files)
+
+ if not uploaded_files:
+ st.warning("❌ No files to upload. Please drag/drop or browse files to upload.")
+ # purge_user_sub_data(data_path_to_purge=VA_ROOT)
+ elif len(uploaded_files) < 2:
+ st.error("❌ You need at least 2 jsonlines files to properly run VA.")
+ purge_user_sub_data(data_path_to_purge=VA_ROOT)
+ else: # properly uploaded
+ for file in uploaded_files:
+ # Create a path for the file in the server directory
+ file_path = files_dir / file.name
+
+ # Save the file to the server directory
+ with open(file_path, "wb") as f:
+ f.write(file.getbuffer())
+
+ jslfiles = list(files_dir.glob("*.jsonl"))
+ st.success(f"✅ Successfully uploaded {len(jslfiles)} jsonl files.")
+ return files_dir.resolve()
+
+
+def run_varco_arena(
+ price_estimation: bool = False,
+ # upload_dir: Union[str, Path] = None,
+ promptname: str = None,
+ exp_name: str = None,
+ api_key: Optional[str] = None,
+ evaluation_model: str = "gpt-4o-mini",
+ update_interval: float = 1.0,
+):
+ # Use environment variable for API key
+ ptn = f"{str(st.session_state.upfiles_dir)}"
+ outdir = Path(ptn)
+ if exp_name:
+ outdir = outdir / exp_name
+
+ command = f"python varco_arena/main.py -i {ptn} -o {outdir} -k {api_key} -p {promptname} -e {evaluation_model} -j 64"
+ if price_estimation:
+ command = f"{command} -c"
+ else:
+ command = command.replace("python", "yes | python ")
+ print(command)
+
+ api_key = None # clear immediately
+
+ process = subprocess.Popen(
+ command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ stdin=subprocess.PIPE,
+ text=True,
+ bufsize=1,
+ shell=True,
+ )
+
+ # Set stdout and stdin to non-blocking mode
+ os.set_blocking(process.stdout.fileno(), False)
+
+ last_update_time = time.time()
+ terminal_output = st.empty()
+ full_output = f"{command}\n"
+ while True:
+ # Check if we have output to read
+ if select.select([process.stdout], [], [], 0)[0]:
+ output = process.stdout.readline()
+ if output:
+ full_output += output
+ if price_estimation:
+ to_show = full_output
+ terminal_output.code(to_show, language="bash")
+ else:
+ current_time = time.time()
+ if current_time - last_update_time > update_interval:
+ lines = full_output.split("\n")
+ if len(lines) < 5:
+ to_show = full_output
+ else:
+ to_show = "\n".join(["...\n..\n.\n"] + lines[-5:])
+ terminal_output.code(to_show, language="bash")
+ last_update_time = current_time
+ print(output)
+ time.sleep(0.1)
+ # Check if the process has finished
+ if process.poll() is not None:
+ # Read any remaining output
+ remaining_output = process.stdout.read()
+ if remaining_output:
+ lines = remaining_output.split("\n")
+ if len(lines) > 10:
+ to_show += "\n".join(["\n...\n..\n.\n"] + lines[-10:])
+ else:
+ to_show += remaining_output
+ terminal_output.code(to_show, language="bash")
+ print(remaining_output)
+ break
+
+ return_code = process.poll()
+ return outdir, return_code
+
+
+def main():
+ # init lang
+ st.session_state["korean"] = st.session_state.get("korean", False)
+
+ sidebar_placeholder = default_page_setting()
+ set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_init"
+ )
+
+ st.title("⚔️ VARCO ARENA ⚔️")
+ if st.session_state.korean:
+ st.write(
+ "**VARCO Arena는 각 모델의 생성된 결과를 비교 평가하여 모델의 성능 순위를 제공하는 시스템입니다. 커스텀 테스트셋에 유용하게 활용할 수 있으며, 모범답안을 필요로 하지 않습니다.**"
+ )
+ else:
+ st.write(
+ "**VARCO Arena is an LLM benchmarking system that compares model responses across customized test scenarios without requiring reference answers.**"
+ )
+
+ st.divider()
+ # Set up the file uploader
+ if st.session_state.korean:
+ st.markdown("### 1. 모델 출력파일 업로드")
+ else:
+ st.markdown("### 1. Upload LLM responses")
+ uploaded_files = st.file_uploader(
+ "Drag and Drop jsonlines files (.jsonl)", accept_multiple_files=True
+ )
+ if st.session_state.korean:
+ st.info("업로드 하신 파일은 자동으로 삭제되며 수집되거나 사용되지 않습니다.")
+ else:
+ st.info(
+ "Your uploads will be removed automatically, not being collected nor reused for any purpose."
+ )
+ # upload state
+ if "upfiles_dir" not in st.session_state:
+ st.session_state.upfiles_dir = None
+ if st.button("업로드하기" if st.session_state.korean else "Upload Files"):
+ st.session_state.upfiles_dir = upload_files(uploaded_files)
+ if st.button("업로드한 파일 지우기" if st.session_state.korean else "Purge my uploads"):
+ st.session_state.upfiles_dir = None
+ if VA_ROOT.is_dir():
+ shutil.rmtree(VA_ROOT)
+ st.success(
+ "✅ 업로드한 파일을 서버에서 지웠습니다"
+ if st.session_state.korean
+ else "✅ Removed your uploads from the server successfully"
+ )
+ else:
+ st.error(
+ "❌ 지울 파일이 없습니다"
+ if st.session_state.korean
+ else "❌ You have nothing uploaded"
+ )
+
+ if st.session_state.korean:
+ with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
+ st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
+ else:
+ with st.expander("❓❔ What should I upload ❓❔"):
+ st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
+
+ # Form for cost estimation
+ with st.form("cost_estimation_form"):
+ if st.session_state.korean:
+ st.write("### 2. 가격 산정")
+ else:
+ st.write("### 2. Cost Estimation")
+ eval_model = st.selectbox(
+ "Select Judge",
+ open("eval_models_list.txt", encoding="UTF8").read().split("\n"),
+ )
+ promptname = st.selectbox(
+ "Select Evalutaion Prompt",
+ open("eval_prompt_list.txt", encoding="UTF8").read().split("\n"),
+ )
+ if st.session_state.korean:
+ st.markdown("*`llmbar`외 다른 프롬프트는 충분히 검증된 프롬프트는 아닙니다. (동작은 함)")
+ else:
+ st.markdown(
+ "*Eval prompts other than `llmbar` is working example, not the optimal ones."
+ )
+ if promptname == USR_SUB:
+ raise ValueError(
+ f"{USR_SUB=} is preserved name for the system. Consider another naming for the prompt or consider changing {VA_ROOT=} (USR_SUB == VA_ROOT.parts[-1])."
+ )
+
+ estimate_button = st.form_submit_button("Calculate Cost!")
+ with st.expander(
+ "LLM Judge에 활용되는 프롬프트 (`Calculate Cost!` 클릭시 갱신)"
+ if st.session_state.korean
+ else "**Evaluation Prompt for LLM Judge (will refresh after `Calculate Cost!` clicked)**"
+ ):
+ prompt = load_prompt(promptname, task="-")
+ kwargs = dict(
+ inst="{inst}",
+ src="{src}",
+ out_a="{out_a}",
+ out_b="{out_b}",
+ task="-",
+ )
+ if promptname == "translation_pair":
+ kwargs["source_lang"] = "{source_lang}"
+ kwargs["target_lang"] = "{target_lang}"
+ prompt_cmpl = prompt.complete_prompt(**kwargs)
+
+ st.markdown(f"### Evaluation Prompt: {promptname}")
+ for msg in prompt_cmpl:
+ st.markdown(f"**{msg['role']}**")
+ st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
+
+ if estimate_button:
+ if st.session_state.get("upfiles_dir") is None:
+ st.error(
+ "❌ Requirements: You have to upload jsonlines files first to proceed"
+ )
+ else:
+ st.markdown("##### Estimated Cost")
+ dummy_api_key = "dummy"
+ dummy_exp_name = "dummy"
+ result_file_path, return_code = run_varco_arena(
+ # upload_dir=st.session_state.upfiles_dir,
+ promptname=promptname,
+ api_key=dummy_api_key,
+ exp_name=dummy_exp_name,
+ price_estimation=True,
+ evaluation_model=eval_model,
+ )
+ if return_code:
+ st.error(
+ "❌ RuntimeError: An error occurred during cost estimation. **Restart from file upload!**"
+ )
+ purge_user_sub_data(data_path_to_purge=VA_ROOT)
+
+ else:
+ st.success("✅ Cost estimation completed successfully")
+ st.session_state.cost_estimated = True
+
+ # Form for actual run
+ with st.form("run_arena_form"):
+ if st.session_state.korean:
+ st.write("### 3. Varco Arena 구동하기")
+ else:
+ st.write("### 3. Run Varco Arena")
+ api_key = st.text_input("Enter your OpenAI API Key", type="password")
+
+ # demo exp name fixated
+ KST = timezone(timedelta(hours=9))
+ tstamp = datetime.now(KST)
+ tstr = tstamp.strftime("%m-%d_%H:%M:%S")
+ exp_name = f"{tstr}_KST_submit"
+
+ if st.session_state.korean:
+ st.write("**주의**:`Ctrl+C` 버튼은 구현되지 않았습니다. 구동 전 숙고해주세요.")
+ else:
+ st.write("**Caution: `Ctrl+C` button hasn't been implemented.**")
+ run_button = st.form_submit_button(
+ "🔥 Run Arena!",
+ disabled=(not st.session_state.get("cost_estimated", False))
+ or "result_file_path"
+ in st.session_state.keys(), # run already performed once
+ )
+
+ if run_button:
+ set_nav_bar(
+ True,
+ sidebar_placeholder=sidebar_placeholder,
+ toggle_hashstr="app_during_run",
+ )
+ if st.session_state.get("upfiles_dir") is None:
+ st.error(
+ "❌ Requirements: You have to upload jsonlines files first to proceed"
+ )
+ elif not api_key:
+ st.error("❌ Requirements: OpenAI key required to run VA.")
+ else:
+ result_file_path, return_code = run_varco_arena(
+ # upload_dir=st.session_state.upfiles_dir,
+ promptname=promptname,
+ api_key=api_key,
+ exp_name=exp_name,
+ price_estimation=False,
+ evaluation_model=eval_model,
+ )
+ if return_code:
+ st.error(
+ "❌ RuntimeError: An error occurred during Varco Arena run. Check the file and **restart from file upload!**"
+ )
+ purge_user_sub_data(data_path_to_purge=VA_ROOT)
+
+ else:
+ st.success("✅ Varco Arena run completed successfully")
+ st.session_state.result_file_path = list(
+ result_file_path.glob("**/result.json")
+ )[-1]
+ set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_run_done"
+ )
+
+ if st.session_state.get("result_file_path", None) is not None:
+ print(f"{st.session_state.get('result_file_path', None)=}")
+ load_and_cache_data(result_file_path=str(st.session_state.result_file_path))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/eval_models_list.txt b/eval_models_list.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4e1a8b00cd614ab45bc282643782bb9f830ebac3
--- /dev/null
+++ b/eval_models_list.txt
@@ -0,0 +1,3 @@
+gpt-4o-mini
+gpt-4o-2024-05-13
+gpt-4o-2024-08-06
diff --git a/eval_prompt_list.txt b/eval_prompt_list.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a486dd00e02e7f8e53330012b3a505bb1ef9373d
--- /dev/null
+++ b/eval_prompt_list.txt
@@ -0,0 +1,3 @@
+llmbar
+translation_pair
+rag_pair_kr
diff --git a/guide_mds/input_jsonls_en.md b/guide_mds/input_jsonls_en.md
new file mode 100644
index 0000000000000000000000000000000000000000..508510bb8457531dbbf4b5b55fa0ed5062751666
--- /dev/null
+++ b/guide_mds/input_jsonls_en.md
@@ -0,0 +1,37 @@
+#### \[EN\] Upload guide (`jsonl`)
+**Basic Requirements**
+ * Upload one `jsonl` file per model (e.g., five files to compare five LLMs)
+ * ⚠️ Important: All `jsonl` files must have the same number of rows
+ * ⚠️ Important: The `model_id` field must be unique within and across all files
+
+**Required Fields**
+* Per Model Fields
+ * `model_id`: Unique identifier for the model (recommendation: keep it short)
+ * `generated`: The LLM's response to the test instruction
+
+* Required only for Translation (`translation_pair` prompt need those. See `streamlit_app_local/user_submit/mt/llama5.jsonl`)
+ * `source_lang`: input language (e.g. Korean, KR, kor, ...)
+ * `target_lang`: output language (e.g. English, EN, ...)
+
+* Common Fields (Must be identical across all files)
+ * `instruction`: The input prompt or test instruction given to the model
+ * `task`: Category label used to group results (useful when using different evaluation prompts per task)
+
+**Example Format**
+```python
+# model1.jsonl
+{"model_id": "model1", "task": "directions", "instruction": "Where should I go?", "generated": "Over there"}
+{"model_id": "model1", "task": "arithmetic", "instruction": "1+1", "generated": "2"}
+
+# model2.jsonl
+{"model_id": "model2", "task": "directions", "instruction": "Where should I go?", "generated": "Head north"}
+{"model_id": "model2", "task": "arithmetic", "instruction": "1+1", "generated": "3"}
+...
+..
+.
+```
+**Use Case Example**
+If you want to compare different prompting strategies for the same model:
+* Use the same `instruction` across files (using unified test scenarios).
+* `generated` responses of each prompting strategy will vary across the files.
+* Use descriptive `model_id` values like "prompt1", "prompt2", etc.
diff --git a/guide_mds/input_jsonls_kr.md b/guide_mds/input_jsonls_kr.md
new file mode 100644
index 0000000000000000000000000000000000000000..901f7d4428d2a6d75f831ef76e5c0ed8ff3fafb0
--- /dev/null
+++ b/guide_mds/input_jsonls_kr.md
@@ -0,0 +1,34 @@
+#### \[KR\] 집어넣을 jsonl 파일 가이드
+비교할 모델이 다섯 개라면 다섯 개의 .jsonl 파일을 업로드하세요.
+* 💥모든 jsonl 은 같은 수의 행을 가져야합니다.
+* 💥`model_id` 필드는 파일마다 달라야하며 파일 내에서는 유일해야합니다.
+
+**jsonl 필수 필드**
+* 개별
+ * `model_id`: 평가받는 모델의 이름입니다. (짧게 쓰는 것 추천)
+ * `generated`: 모델이 testset instruction 에 생성한 응답을 넣으세요.
+
+* 번역평가 프롬프트 사용시 (`translation_pair`. `streamlit_app_local/user_submit/mt/llama5.jsonl` 에서 예시 볼 수 있음)
+ * `source_lang`: input language (e.g. Korean, KR, kor, ...)
+ * `target_lang`: output language (e.g. English, EN, ...)
+
+* 공통 부분 (**모든 파일에 대해 같아야 함**)
+ * `instruction`: 모델에 집어넣는 `testset instruction` 혹은 `input`에 해당하는 무언가입니다.
+ * `task`: 전체 결과를 subset으로 그룹지어서 보여줄 때 사용됩니다. `evaluation prompt`를 행별로 다르게 사용하고 싶을 때 활용될 수 있습니다.
+
+
+각 jsonl 파일은 아래처럼 생겼습니다.
+```python
+# model1.jsonl
+{"model_id": "모델1", "task": "길 묻기", "instruction": "어디로 가야하오", "generated": "저기로요"}
+{"model_id": "모델1", "task": "산수", "instruction": "1+1", "generated": "2"} # 길 묻기와 산수의 경우 다른 평가 프롬프트를 사용하고 싶을 수 있겠죠?
+
+# model2.jsonl -* model1.jsonl과 `instruction`은 같고 `generated`, `model_id` 는 다릅니다!
+{"model_id": "모델2", "task": "길 묻기", "instruction": "어디로 가야하오", "generated": "하이"}
+{"model_id": "모델2", "task": "산수", "instruction": "1+1", "generated": "3"}
+
+...
+..
+```
+
+예를 들어, 한가지 모델에 대해 다른 프롬프팅을 시도하여 다른 생성문을 얻었고 이를 비교하고 싶은 경우를 생각해봅시다. 이 때 평가받을 testset은 같으므로 `instruction`은 모두 같고 프롬프팅에 따라 `generated`는 달라지겠죠? `model_id` 는 `"prompt1"`, `"prompt2"` 등 취향에 맞게 적어주시면 됩니다.
diff --git a/modules/__pycache__/nav.cpython-311.pyc b/modules/__pycache__/nav.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7b9f497e097ee058c848d72b0ba277676a06e94a
Binary files /dev/null and b/modules/__pycache__/nav.cpython-311.pyc differ
diff --git a/modules/nav.py b/modules/nav.py
new file mode 100644
index 0000000000000000000000000000000000000000..69cb4f28506098db64979fe4d14dc7f0d260f8b7
--- /dev/null
+++ b/modules/nav.py
@@ -0,0 +1,57 @@
+import streamlit as st
+
+
+def Navbar(sidebar_placeholder, toggle_hashstr: str = ""):
+ with sidebar_placeholder.container():
+ st.toggle(
+ "🇰🇷 한국어로 보기",
+ value=st.session_state.get("korean", False),
+ key=f"korean_toggle_{toggle_hashstr}", # Add this explicit key
+ on_change=lambda: setattr(
+ st.session_state,
+ "korean",
+ st.session_state.get(f"korean_toggle_{toggle_hashstr}", False),
+ ),
+ )
+
+ st.page_link(
+ "app.py",
+ label="Varco Arena 구동" if st.session_state.korean else "Run VARCO Arena",
+ icon="🔥",
+ )
+ st.page_link(
+ "pages/see_results.py",
+ label="결과 보기" if st.session_state.korean else "See Results",
+ icon="📝",
+ disabled=st.session_state.get("is_running", False),
+ )
+ st.page_link(
+ "pages/brief_intro.py",
+ label="어떻게 작동하나요?" if st.session_state.korean else "How it Works",
+ icon="❔",
+ disabled=st.session_state.get("is_running", False),
+ )
+ st.page_link(
+ "pages/quick_start_guide.py",
+ label="본격 사용하기 가이드" if st.session_state.korean else "Quick Start Guide",
+ icon="💯",
+ disabled=st.session_state.get("is_running", False),
+ )
+ related_links_en = """
+**About**
+* [Paper](https://huggingface.co/papers/2411.01281)
+* [Blog (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+* [Inquiry](https://linkedin.com/in/deftson/)
+""".strip()
+ related_links_kr = """
+**About**
+* [논문](https://huggingface.co/papers/2411.01281)
+* [블로그](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+* [문의](https://linkedin.com/in/deftson/)
+""".strip()
+ st.info(related_links_kr if st.session_state.korean else related_links_en)
+
+ st.divider()
+ demo_warning_kr = "❕❗**본 데모에서는 새로고침 후에 평가 결과가 보존되지 않습니다**❕❗ 앱을 온전히 활용하시려면 개인 기기에서 호스팅하세요 (**💯본격 사용하기 가이드** 참조)"
+ demo_warning_en = "❕❗For this demo, **evaluation results will not be preserved after refreshing**❕❗ To fully utilize the app, please host it on your personal device (refer to **💯Quick Start Guide**)"
+ st.markdown(demo_warning_kr if st.session_state.korean else demo_warning_en)
diff --git a/pages/__pycache__/see_results.cpython-311.pyc b/pages/__pycache__/see_results.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..455122d75cde21068370b9b76f7aef3fd85a2e40
Binary files /dev/null and b/pages/__pycache__/see_results.cpython-311.pyc differ
diff --git a/pages/brief_intro.py b/pages/brief_intro.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f7206a58260ff372f71f623385fc1c56d4a997c
--- /dev/null
+++ b/pages/brief_intro.py
@@ -0,0 +1,40 @@
+import streamlit as st
+
+from view_utils import default_page_setting, set_nav_bar
+
+sidebar_placeholder = default_page_setting()
+set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="brief_intro_init"
+)
+
+st.image("va_en.png")
+if st.session_state.korean:
+ st.info("여러분이 업로드하는 파일은 위 그림의 LLM 출력들입니다.")
+ with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
+ st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
+else:
+ st.info(
+ "What you upload is the generated responses of LLMs regarding the test prompts."
+ )
+ with st.expander("❓❔ What should I upload ❓❔"):
+ st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
+
+
+st.image("va_concept_new.png")
+st.markdown(
+ """
+| |Current Practice|Varco Arena|
+|-|-|-|
+|Total no. matches|$$n_{\\text{model}}*\\|X\\|$$|$$(n_{\\text{model}}-1)*\\|X\\|$$|
+|No. matches per LLM|$$\\|X\\|$$|$$\\left[\\|X\\|,\\|X\\|\\text{log}n_{\\text{model}}\\right]$$|
+|Comparison type|reference-mediated (anchored) |direct|
+ """
+)
+if st.session_state.korean:
+ st.info(
+ "Varco Arena는 신뢰성 있는 순위를 더 적은 횟수의 비교 내에 얻어내며, 이러한 특징은 LLM 직접 비교의 이점으로부터 기인합니다."
+ )
+else:
+ st.info(
+ "Varco Arena takes advantage of direct comparison between LLM responses to guarantee better reliability in fewer number of total matches."
+ )
diff --git a/pages/quick_start_guide.py b/pages/quick_start_guide.py
new file mode 100644
index 0000000000000000000000000000000000000000..5df80f91b512386aeee6f0c3d0f0c796270534bb
--- /dev/null
+++ b/pages/quick_start_guide.py
@@ -0,0 +1,14 @@
+import streamlit as st
+
+from view_utils import default_page_setting, set_nav_bar
+
+sidebar_placeholder = default_page_setting()
+set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="quickstart_init"
+)
+
+
+if st.session_state.korean:
+ st.markdown(open("varco_arena/README_kr.md").read())
+else:
+ st.markdown(open("varco_arena/README_en.md").read())
diff --git a/pages/see_results.py b/pages/see_results.py
new file mode 100644
index 0000000000000000000000000000000000000000..cef52382201f986e171cc39f8199d46727877f45
--- /dev/null
+++ b/pages/see_results.py
@@ -0,0 +1,365 @@
+import pandas as pd
+import streamlit as st
+
+import analysis_utils as au
+from analysis_utils import number_breakdown_from_df
+from app import load_and_cache_data
+
+# from app import VA_ROOT
+from query_comp import QueryWrapper, get_base_url
+from varco_arena.varco_arena_core.prompts import load_prompt
+from view_utils import (
+ default_page_setting,
+ escape_markdown,
+ set_nav_bar,
+ show_linebreak_in_md,
+)
+
+DEFAULT_LAYOUT_DICT = {
+ "title": {"font": {"size": 20, "family": "Gothic A1"}},
+ "font": {"size": 16, "family": "Gothic A1"},
+ "xaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
+ "yaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
+ "legend": {"font": {"size": 12, "family": "Gothic A1"}},
+}
+
+
+def navigate(t, source, key, val):
+ # print(key, val)
+ if source is None:
+ return
+ target_index = t.index(source) + val
+ if 0 <= target_index < len(t):
+ st.session_state[key] = t[target_index]
+ st.rerun()
+
+
+def main():
+ sidebar_placeholder = default_page_setting(layout="wide")
+ set_nav_bar(
+ False,
+ sidebar_placeholder=sidebar_placeholder,
+ toggle_hashstr="see_results_init",
+ )
+
+ # load the data
+ # print(f"{st.session_state.get('result_file_path', None)=}")
+ most_recent_run = st.session_state.get("result_file_path", None)
+ most_recent_run = str(most_recent_run) if most_recent_run is not None else None
+ (
+ st.session_state["all_result_dict"],
+ st.session_state["df_dict"],
+ ) = load_and_cache_data(result_file_path=most_recent_run)
+
+ # side bar
+ st.sidebar.title("Select Result:")
+ result_select = QueryWrapper("expname")(
+ st.sidebar.selectbox,
+ list(st.session_state["all_result_dict"].keys()),
+ )
+
+ if result_select is None:
+ st.stop()
+ eval_prompt_name = result_select.split("/")[-1].strip()
+ if st.sidebar.button("Clear Cache"):
+ st.cache_data.clear()
+ st.cache_resource.clear()
+ st.rerun()
+
+ if result_select:
+ if "alpha2names" in st.session_state:
+ del st.session_state["alpha2names"]
+
+ fig_dict_per_task = st.session_state["all_result_dict"][result_select]
+ task_list = list(fig_dict_per_task.keys())
+ elo_rating_by_task = fig_dict_per_task["Overall"]["elo_rating_by_task"]
+ # tabs = st.tabs(task_list)
+
+ df_dict_per_task = st.session_state["df_dict"][result_select]
+
+ default_layout_dict = DEFAULT_LAYOUT_DICT
+ task = QueryWrapper("task", "Select Task")(st.selectbox, task_list)
+
+ if task is None:
+ st.stop()
+ figure_dict = fig_dict_per_task[task]
+ judgename = figure_dict["judgename"]
+ df = df_dict_per_task[task]
+ interpretation, n_models, size_testset = number_breakdown_from_df(df)
+ if st.session_state.korean:
+ st.markdown(f"## 결과 ({task})")
+ st.markdown(f"##### Judge 모델: {judgename} / 평가프롬: {eval_prompt_name}")
+ st.markdown(f"##### 테스트셋 사이즈: {int(size_testset)} 행")
+ else:
+ st.markdown(f"## Results ({task})")
+ st.markdown(f"##### Judge Model: {judgename} / prompt: {eval_prompt_name}")
+ st.markdown(f"##### Size of Testset: {int(size_testset)} rows")
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.markdown(f"#### Ratings ({task})")
+ st.table(figure_dict["elo_rating"])
+ st.write(show_linebreak_in_md(escape_markdown(interpretation)))
+
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ elo_rating_by_task.update_layout(**default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_elo_rating_by_task",
+ )
+
+ st.divider()
+
+ if st.session_state.korean:
+ st.markdown("### 토너먼트 (테스트 시나리오) 별로 보기")
+ else:
+ st.markdown("### Tournament Results by Test Scenario")
+
+ # with st.expander("볼 토너먼트 고르기"):
+ d = list(df.idx_inst_src.unique())
+ default_idx = st.session_state.get("selected_tournament", None)
+ cols = st.columns((1, 18, 1))
+ with cols[0]:
+ if st.button("◀", key="prev_tournament"):
+ navigate(d, default_idx, "selected_tournament", -1)
+ with cols[1]:
+ tournament_prm_select = QueryWrapper("tournament", "Select Tournament")(
+ st.selectbox,
+ d,
+ default_idx,
+ key=f"{task}_tournament_select",
+ on_change=lambda: st.session_state.update(
+ selected_tournament=st.session_state.get(f"{task}_tournament_select"),
+ selected_match=None,
+ ),
+ label_visibility="collapsed",
+ )
+ with cols[2]:
+ if st.button("▶", key="next_tournament"):
+ navigate(d, default_idx, "selected_tournament", 1)
+
+ # tournament_prm_select = st.selectbox(
+ # "Select Tournament",
+ # df.idx_inst_src.unique(),
+ # index=d.index(st.session_state.get("selected_tournament")),
+ # key=f"{task}_tournament_{result_select}",
+ # )
+
+ # print(tournament_prm_select, type(tournament_prm_select))
+ st.session_state["selected_tournament"] = tournament_prm_select
+ # tournament_prm_select = st.selectbox(
+ # "Select Tournament",
+ # df.idx_inst_src.unique(),
+ # key=f"{task}_tournament_{result_select}",
+ # )
+ df_now_processed = None
+ if tournament_prm_select:
+ df_now = df[df.idx_inst_src == tournament_prm_select]
+ df_now_processed, _alpha2names = au.init_tournament_dataframe(
+ df_now,
+ alpha2names=st.session_state["alpha2names"]
+ if "alpha2names" in st.session_state.keys()
+ else None,
+ )
+ if "alpha2names" not in st.session_state:
+ st.session_state["alpha2names"] = _alpha2names
+
+ try:
+ bracket_drawing = au.draw(
+ df_now_processed,
+ alpha2names=st.session_state["alpha2names"],
+ )
+ legend = au.make_legend_str(
+ df_now_processed, st.session_state["alpha2names"]
+ )
+ st.code(bracket_drawing + legend)
+
+ m = list(df_now_processed.human_readable_idx)
+ default_idx = st.session_state.get("selected_match", None)
+ cols = st.columns((1, 18, 1))
+ with cols[0]:
+ if st.button("◀", key="prev_match"):
+ navigate(m, default_idx, "selected_match", -1)
+ with cols[1]:
+ match_idx_human = QueryWrapper("match", "Select Match")(
+ st.selectbox,
+ m,
+ default_idx,
+ key=f"{task}_match_select",
+ label_visibility="collapsed",
+ )
+ with cols[2]:
+ if st.button("▶", key="next_match"):
+ navigate(m, default_idx, "selected_match", 1)
+
+ # match_idx_human = st.selectbox(
+ # "Select Match",
+ # df_now_processed.human_readable_idx,
+ # key=f"{task}_match_{result_select}",
+ # )
+ # print(match_idx_human)
+ st.session_state["selected_match"] = match_idx_human
+ # match_idx_human = st.selectbox(
+ # "Select Match",
+ # df_now_processed.human_readable_idx,
+ # key=f"{task}_match_{result_select}",
+ # )
+ if match_idx_human:
+ match_idx = int(match_idx_human.split(": ")[0])
+ row = df_now_processed.loc[match_idx]
+
+ st.markdown("#### Current Test Scenario:")
+
+ with st.expander(
+ f"### Evaluation Prompt (evalprompt: {eval_prompt_name}--{task})"
+ ):
+ prompt = load_prompt(eval_prompt_name, task=task)
+ kwargs = dict(
+ inst="{inst}",
+ src="{src}",
+ out_a="{out_a}",
+ out_b="{out_b}",
+ task=task,
+ )
+ if eval_prompt_name == "translation_pair":
+ kwargs["source_lang"] = "{source_lang}"
+ kwargs["target_lang"] = "{target_lang}"
+ prompt_cmpl = prompt.complete_prompt(**kwargs)
+ for msg in prompt_cmpl:
+ st.markdown(f"**{msg['role']}**")
+ st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
+
+ st.info(show_linebreak_in_md(tournament_prm_select))
+
+ winner = row.winner
+ col1, col2 = st.columns(2)
+
+ winnerbox = st.success
+ loserbox = st.error
+ with col1:
+ iswinner = winner == "model_a"
+ writemsg = winnerbox if iswinner else loserbox
+ st.markdown(f"#### ({row.model_a}) {row.human_readable_model_a}")
+ writemsg(
+ show_linebreak_in_md(row.generated_a),
+ icon="✅" if iswinner else "❌",
+ )
+ with col2:
+ iswinner = winner == "model_b"
+ writemsg = winnerbox if iswinner else loserbox
+ st.markdown(f"#### ({row.model_b}) {row.human_readable_model_b}")
+ writemsg(
+ show_linebreak_in_md(row.generated_b),
+ icon="✅" if iswinner else "❌",
+ )
+ except Exception as e:
+ import traceback
+
+ traceback.print_exc()
+ st.markdown(
+ "**Bug: 아래 표를 복사해서 이슈로 남겨주시면 개선에 도움이 됩니다. 감사합니다🙏**"
+ if st.session_state.korean
+ else "Bug: Please open issue and attach the table output below to help me out. Thanks in advance.🙏"
+ )
+ st.error(e)
+ st.info(tournament_prm_select)
+ st.table(
+ df_now_processed[
+ [
+ "depth",
+ "round",
+ "winner_nodes",
+ "winner_resolved",
+ "winner",
+ "model_a",
+ "model_b",
+ ]
+ ]
+ )
+ st.write("Sharable link")
+ st.code(f"{get_base_url()}/see_results?{QueryWrapper.get_sharable_link()}")
+ st.divider()
+
+ if st.session_state.korean:
+ st.markdown("### 매치 통계")
+ else:
+ st.markdown("### Match Stats.")
+ col1, col2 = st.columns(2)
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict[
+ "fraction_of_model_a_wins_for_all_a_vs_b_matches"
+ ].update_layout(autosize=True, **default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_fraction_of_model_a_wins_for_all_a_vs_b_matches",
+ )
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["match_count_of_each_combination_of_models"].update_layout(
+ autosize=True, **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_match_count_of_each_combination_of_models",
+ )
+
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["match_count_for_each_model"].update_layout(
+ **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_match_count_for_each_model",
+ )
+ with col2:
+ pass
+
+ if st.session_state.korean:
+ st.markdown("### 참고용 LLM Judge 편향 정보")
+ else:
+ st.markdown("### FYI: How biased is your LLM Judge?")
+
+ with st.expander("펼쳐서 보기" if st.session_state.korean else "Expand to show"):
+ st.info(
+ """
+Varco Arena에서는 position bias의 영향을 최소화하기 위해 모든 모델이 A나 B위치에 번갈아 위치하도록 하였습니다. 그러나 LLM Judge 혹은 Prompt의 성능이 부족하다고 느껴진다면, 아래 알려진 LLM Judge bias가 참고가 될겁니다.
+* position bias (왼쪽)
+* length bias (오른쪽)
+
+결과의 왜곡이 LLM Judge의 부족함 떄문이었다는 점을 규명하려면 사용하신 LLM Judge와 Prompt의 binary classification 정확도를 측정해보시길 바랍니다 (Varco Arena를 활용하여 이를 수행해볼 수 있습니다!).""".strip()
+ if st.session_state.korean
+ else """
+In Varco Arena, to minimize the effect of position bias, all models are alternately positioned in either position A or B. However, if you feel the LLM Judge or Prompt performance is insufficient, the following known LLM Judge biases may be helpful to reference:
+* position bias (left)
+* length bias (right)
+
+To determine if result distortion was due to LLM Judge limitations, please measure the binary classification accuracy of your LLM Judge and Prompt (You could use Varco Arena for this purpose!).
+""".strip()
+ )
+ st.markdown(f"#### {judgename} + prompt = {eval_prompt_name}")
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["counts_of_match_winners"].update_layout(
+ **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_counts_of_match_winners",
+ )
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["length_bias"].update_layout(**default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_length_bias",
+ )
+ st.table(figure_dict["length_bias_df"].groupby("category").describe().T)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/precommit.sh b/precommit.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e255e22b3ec687ac4790f1d9658f6b24872b6a39
--- /dev/null
+++ b/precommit.sh
@@ -0,0 +1,5 @@
+# reformat all to prevent commit problem
+git add .
+pre-commit run --all-files
+# restore the files added so that one can actually add files to be commit
+git restore --staged .
diff --git a/query_comp.py b/query_comp.py
new file mode 100644
index 0000000000000000000000000000000000000000..8da83c2e1169e826f51b443742e901a5e7c4e522
--- /dev/null
+++ b/query_comp.py
@@ -0,0 +1,137 @@
+# query를 자동으로 읽고 쓰는 container를 정의
+
+from __future__ import annotations
+
+import re
+from typing import Callable, TypeVar
+
+import streamlit as st
+
+__all__ = ["QueryWrapper", "get_base_url"]
+
+T = TypeVar("T")
+
+
+import hashlib
+import urllib.parse
+
+
+def SHA1(msg: str) -> str:
+ return hashlib.sha1(msg.encode()).hexdigest()[:8]
+
+
+def get_base_url():
+ session = st.runtime.get_instance()._session_mgr.list_active_sessions()[0]
+ return urllib.parse.urlunparse(
+ [session.client.request.protocol, session.client.request.host, "", "", "", ""]
+ )
+
+
+class QueryWrapper:
+ queries: dict[str, _QueryWrapper] = {} # 기록용
+
+ def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
+ self.__wrapper = QueryWrapper.queries[query] = _QueryWrapper(
+ query, label, use_hash
+ )
+
+ def __call__(self, *args, **kwargs):
+ return self.__wrapper(*args, **kwargs)
+
+ @classmethod
+ def get_sharable_link(cls):
+ # for k, v in cls.queries.items():
+ # print(f"{k}: {v}")
+ return re.sub(
+ "&+", "&", "&".join([str(v) for k, v in cls.queries.items()])
+ ).strip("&")
+
+
+class _QueryWrapper:
+ ILLEGAL_CHARS = "&/=?"
+
+ def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
+ self.query = query
+ self.label = label or query
+ self.use_hash = use_hash
+ self.hash_table = {}
+ self.key = None
+
+ def __call__(
+ self,
+ base_container: Callable,
+ legal_list: list[T],
+ default: T | list[T] | None = None,
+ *,
+ key: str | None = None,
+ **kwargs,
+ ) -> T | list[T] | None:
+ val_from_query = st.query_params.get_all(self.query.lower())
+ # print(val_from_query)
+ legal = len(val_from_query) > 0
+ self.key = key or self.label
+
+ self.hash_table = {SHA1(str(v)): v for v in legal_list}
+
+ # filter out illegal values
+ if legal and legal_list:
+ val_from_query = [v for v in val_from_query if v in self.hash_table]
+ # print(self.label, val_from_query, legal)
+ if legal:
+ selected = [self.hash_table[v] for v in val_from_query]
+ elif default:
+ selected = default
+ elif self.label in st.session_state:
+ selected = st.session_state[self.label]
+ if legal_list:
+ if isinstance(selected, list):
+ selected = [v for v in selected if v in legal_list]
+ elif selected not in legal_list:
+ selected = []
+ else:
+ selected = []
+ if selected is None:
+ pass
+ elif len(selected) == 1 and base_container in [st.selectbox, st.radio]:
+ selected = selected[0]
+ # print(self.label, selected)
+ if base_container == st.checkbox:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ elif base_container == st.multiselect:
+ selected = base_container(
+ self.label, legal_list, default=selected, key=self.key, **kwargs
+ )
+ elif base_container == st.radio:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ elif base_container == st.selectbox:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ else:
+ selected = base_container(self.label, legal_list, key=self.key, **kwargs)
+ return st.session_state[self.key]
+
+ def __str__(self):
+ selected = st.session_state.get(self.key, None)
+ if isinstance(selected, str):
+ return f"{self.query.lower()}={SHA1(selected)}"
+ elif isinstance(selected, list):
+ return "&".join([f"{self.query.lower()}={SHA1(str(v))}" for v in selected])
+ else:
+ return ""
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..25c83899b9a7029c6f8175c6e0abcfeb0f4effda
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,14 @@
+openai>=1.17.0
+munch
+pandas
+numpy
+tqdm>=4.48.0
+plotly
+scikit-learn
+kaleido
+tiktoken>=0.7.0
+pyyaml
+transformers
+streamlit>=1.40.2
+openpyxl
+git+https://github.com/shobrook/openlimit.git#egg=openlimit
diff --git a/run.sh b/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..ec2214a12ddda4df0c269467945207a084f139ac
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# 여기서 돌려야해요 (cwd = ./streamlit_app_components/)
+# VARCO_ARENA_RESULT_PATH=./user_submit streamlit run app.py
+streamlit run app.py
diff --git a/streamlit_app_local/.streamlit/config.toml b/streamlit_app_local/.streamlit/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f22a8ff05a6b33d6edc66a21662dea9a01ec760d
--- /dev/null
+++ b/streamlit_app_local/.streamlit/config.toml
@@ -0,0 +1,4 @@
+[client]
+showSidebarNavigation = false
+[server]
+headless = true
diff --git a/streamlit_app_local/README.md b/streamlit_app_local/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8b34408db0e68171e953a1413bb4b99d0ee4e5c8
--- /dev/null
+++ b/streamlit_app_local/README.md
@@ -0,0 +1,24 @@
+# Varco Arena web app
+```bash
+cd ./streamlit_app_local/
+bash run.sh
+```
+
+## view: Running
+
+
+## view: Results
+
+
+
+
+
+## how to use
+1. upload llm outputs in jsonlines format
+2. cost estimation
+3. run
+4. go to see results tab
+
+## example run results at...
+- `streamlit_app_local/user_submit/`
+- visible on web if you `bash run.sh`
diff --git a/streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc b/streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a222987946a9bc83933421dc221f1938eb50a845
Binary files /dev/null and b/streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc differ
diff --git a/streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc b/streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c9497d886b3412d448d53257e91073adba46506e
Binary files /dev/null and b/streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc differ
diff --git a/streamlit_app_local/__pycache__/app.cpython-311.pyc b/streamlit_app_local/__pycache__/app.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8f1b0db0c14096b25896ca8371ffccd81307be8e
Binary files /dev/null and b/streamlit_app_local/__pycache__/app.cpython-311.pyc differ
diff --git a/streamlit_app_local/__pycache__/app.cpython-38.pyc b/streamlit_app_local/__pycache__/app.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e1e948bd85b47718ff3da0934023bc00f7126b7a
Binary files /dev/null and b/streamlit_app_local/__pycache__/app.cpython-38.pyc differ
diff --git a/streamlit_app_local/__pycache__/query_comp.cpython-311.pyc b/streamlit_app_local/__pycache__/query_comp.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..84bfe242b86af032ef3787ad85378fbf41222a24
Binary files /dev/null and b/streamlit_app_local/__pycache__/query_comp.cpython-311.pyc differ
diff --git a/streamlit_app_local/__pycache__/view_utils.cpython-311.pyc b/streamlit_app_local/__pycache__/view_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bb516ac804d11046f3c5727f5893d53c11524021
Binary files /dev/null and b/streamlit_app_local/__pycache__/view_utils.cpython-311.pyc differ
diff --git a/streamlit_app_local/__pycache__/view_utils.cpython-38.pyc b/streamlit_app_local/__pycache__/view_utils.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..11db1d2351401af0605a9c6a246b734fa83f35ab
Binary files /dev/null and b/streamlit_app_local/__pycache__/view_utils.cpython-38.pyc differ
diff --git a/streamlit_app_local/analysis_utils.py b/streamlit_app_local/analysis_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..e480fa35791309f93eb84eca49a51739f369fb29
--- /dev/null
+++ b/streamlit_app_local/analysis_utils.py
@@ -0,0 +1,338 @@
+from copy import deepcopy
+from functools import partial
+from typing import *
+
+import pandas as pd
+from fire import Fire
+
+"""
+This code assumes dealing with only one instruction
+"""
+
+
+# from varco_arena.tournament
+def log2_power_of_two(n):
+ # First, let's make sure n is indeed a power of 2
+ if n & (n - 1) != 0 or n == 0:
+ raise ValueError("n must be a positive power of 2")
+
+ exponent = 0
+ while n > 1:
+ n >>= 1 # Right shift is like dividing by 2, but faster
+ exponent += 1
+ return exponent
+
+
+def get_1st(df: pd.DataFrame, alpha2names: dict) -> Optional[str]:
+ finals = df[df["round"] == "final"]
+ if len(finals) == 1:
+ first = finals.iloc[0].winner_resolved
+ else:
+ first = (
+ None # error case (no finals match or multiple finals (buggy result file))
+ )
+
+ return first
+
+
+def get_unique_participants(df: pd.DataFrame) -> list:
+ participants = pd.concat([df.model_a, df.model_b]).unique().tolist()
+ participants = [p for p in participants if p] # remove None
+ participants = sorted(participants) # make it sorted
+ return participants
+
+
+def _impute_byes(df):
+ max_depth = df.depth.max()
+
+ # init
+ imputed_parts = dict()
+ for depth in range(max_depth + 1):
+ imputed_parts[depth] = df[df.depth == depth].copy()
+
+ # reverse
+ for depth in range(max_depth, 0, -1): # always we have 1 proper match for depth=0
+ null_v_null = {
+ "model_a": "",
+ "model_b": "",
+ "winner": "model_a",
+ "match_order_in_round": "-",
+ "depth": depth,
+ }
+
+ # fill some_model vs null byes
+ players = get_unique_participants(imputed_parts[depth])
+ proceeded = get_unique_participants(imputed_parts[depth - 1])
+ imputed = []
+ for p in proceeded:
+ if p not in players:
+ p_v_null = deepcopy(null_v_null)
+ p_v_null["model_a"] = p
+ imputed.append(p_v_null)
+ imputed_parts[depth] = pd.concat(
+ [
+ imputed_parts[depth],
+ pd.DataFrame(imputed),
+ ],
+ axis="index",
+ )
+
+ # fill null vs null
+ n_null_v_null = 2 ** (depth) - len(imputed_parts[depth])
+ if n_null_v_null > 0:
+ imputed = pd.DataFrame([null_v_null] * n_null_v_null)
+ imputed_parts[depth] = pd.concat(
+ [
+ imputed_parts[depth],
+ imputed,
+ ],
+ axis="index",
+ )
+
+ df_imputed = pd.concat(imputed_parts.values(), axis="index")
+ df_imputed = df_imputed.sort_values(by="depth").reset_index(drop=True)
+
+ return df_imputed
+
+
+def index_test_scenario(df) -> pd.DataFrame:
+ df["inst_src"] = "inst: " + df.instruction + "\n\nsrc: " + df.source
+ df["idx_inst_src"] = df.apply(
+ lambda row: f"{row.tournament_idx}:\n{row.inst_src}", axis=1
+ )
+
+ # later used for tournament bracket backtrackiung
+ if "depth" not in df.columns:
+ mappings = {
+ "final": 0,
+ "semi-final": 1,
+ "quarter-final": 2,
+ }
+
+ def _convert_round_to_depth(rnd: str, mappings=None) -> int:
+ if rnd is None:
+ depth = None
+ elif rnd in mappings.keys():
+ depth = mappings[rnd]
+ elif rnd.startswith("round-"): # assume perfect power of two
+ num = int(rnd.replace("round-", "").strip())
+ depth = log2_power_of_two(num) - 1
+ return depth
+
+ conv = partial(_convert_round_to_depth, mappings=mappings)
+ df["depth"] = df["round"].apply(conv)
+
+ return df
+
+
+def init_tournament_dataframe(df, alpha2names: dict = None) -> pd.DataFrame:
+ df = df.sort_values(by="depth").reset_index(drop=True)
+ # make winner interpretable (A -> model_a, B -> model_b)
+ df.winner = df.winner.apply(lambda txt: f"model_{txt.lower()}")
+
+ # define alpha2names if not given (covers upto 168 participants)
+ if alpha2names is None:
+ alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+ abcdefghijklmnopqrstuvwxyz\
+ ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ\
+ ㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭\
+ ㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻\
+ ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ\
+ ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ\
+ αβγδεζηθικλμνξοπρστυφχψω"
+ model_full_names = get_unique_participants(df)
+ alpha2names = dict(zip(alphabets, model_full_names))
+ if len(alpha2names) < len(model_full_names):
+ raise ValueError(
+ f"Tournament viewer cannot visualize more than {len(alphabets)=} participants. ({len(model_full_names)=} is given)\n\nOther features will not be affected but the tournament visualizer."
+ )
+ names2alpha = dict(zip(alpha2names.values(), alpha2names.keys()))
+ df = _impute_byes(df)
+
+ # preserve readables for later
+ df = _make_readables(df, names2alpha)
+
+ if len(df[df["round"] == "final"]) != 1:
+ raise ValueError(f"final match need to be one and only.")
+
+ return df, alpha2names
+
+
+def _make_readables(df, names2alpha):
+ df["human_readable_model_a"] = df.model_a.copy()
+ df["human_readable_model_b"] = df.model_b.copy()
+
+ df.model_a = df.model_a.apply(
+ lambda modelname: names2alpha[modelname] if modelname else "x"
+ )
+ df.model_b = df.model_b.apply(
+ lambda modelname: names2alpha[modelname] if modelname else "x"
+ )
+
+ df["human_readable_idx"] = df.apply(
+ lambda row: f"{row.name}: {row.human_readable_model_a} ({row.model_a}) vs. {row.human_readable_model_b} ({row.model_b if row.model_b else 'x'})",
+ axis=1,
+ )
+
+ df["winner_resolved"] = df.apply(lambda row: row[row.winner], axis=1)
+ df["winner_nodes"] = df.apply(
+ lambda row: f"{row.winner_resolved}:{row.name}".ljust(4, " "), axis=1
+ ) # later for figure representation of winner as a "node"
+ return df
+
+
+# draw
+def draw(df: pd.DataFrame, alpha2names: dict = None) -> str:
+ def _draw_round(
+ df: pd.DataFrame,
+ depth: int = None,
+ winners_in_order: list = None,
+ ) -> Tuple:
+ df_now = df[df.depth == depth]
+ max_depth = df.depth.max()
+
+ width = 2 ** ((max_depth - depth) + 2)
+
+ connect_left = "─" * (width)
+ connect_left = connect_left[4:]
+ connect_right = " " * (width)
+ connect_right = "┐" + connect_right[1:]
+
+ if winners_in_order is None:
+ assert (
+ depth == 0
+ ), f"{winners_in_order=} is only allowed when drawing the top (=final match)"
+ winners_in_order = df_now.winner_nodes
+
+ round_drawing_parts = []
+ descending_round_winners = []
+ for node in winners_in_order:
+ round_drawing_parts.append("".join([node, connect_left, connect_right]))
+ # next round winners in sync with winner order
+ row_now = df_now.query(f"winner_nodes=='{node}'")
+ descending_round_winners.append(row_now.model_a.item())
+ descending_round_winners.append(row_now.model_b.item())
+
+ # find descending_round_winners within winner_nodes format (num:alpha)
+ if depth == max_depth:
+ pass # keep the descending_round_winners intact
+ else:
+ df_descend = df[df.depth == depth + 1]
+ for i, winner_alpha in enumerate(descending_round_winners):
+ node_intr = df_descend.query(
+ f"winner_resolved=='{winner_alpha}'"
+ ).winner_nodes.item()
+ descending_round_winners[i] = node_intr
+
+ round_drawing = "".join(round_drawing_parts)
+ descending_unit = " " * width
+ descending_unit = "│" + descending_unit[1:]
+ descending_lines_parts = [descending_unit] * len(df_now) * 2
+ descending_lines = "".join(descending_lines_parts)
+
+ return round_drawing, descending_lines, descending_round_winners
+
+ drawings = []
+ winners_in_order = None
+ max_depth = df.depth.max()
+ for depth in range(max_depth + 1):
+ max_depth = df.depth.max()
+ winner_drw, lines_desc, winners_in_order = _draw_round(
+ df,
+ depth=depth,
+ winners_in_order=winners_in_order,
+ )
+ drawings.append((winner_drw, lines_desc))
+ # prepare bracket top
+ champion_alphabet = drawings[0][0].split()[0].split(":")[0]
+ champion_readable = alpha2names[champion_alphabet]
+ bracket_top = [f"🥇winner: {champion_readable}", "│"]
+ # prepare mid
+ bracket_mid = "\n".join(["\n".join(tup) for tup in drawings])
+
+ # prepare bot
+ initial_participants = winners_in_order
+ bracket_bot = (" " * 3).join(initial_participants)
+
+ full_figure = "\n".join(bracket_top + [bracket_mid, bracket_bot])
+
+ return full_figure
+
+
+def number_breakdown_from_df(result_df: pd.DataFrame) -> str:
+ n_models = len(get_unique_participants(result_df))
+ size_testset = int(len(result_df) / (n_models - 1))
+ interpretation = f"total {len(result_df)} matches = (n_models-1) * size_testset = ({n_models}-1) * {size_testset}"
+ return interpretation, n_models, size_testset
+
+
+def make_legend_str(df, alpha2names) -> str:
+ first = get_1st(df, alpha2names)
+ alpha2names = {k: v.replace("🥇 ", "") for k, v in alpha2names.items()}
+ alpha_ordered = sorted(list(alpha2names.keys()))
+ # names_ordered = sorted(list(alpha2names.values()))
+ # name2alpha = {v: k for k, v in alpha2names.items()}
+
+ for k, v in alpha2names.items():
+ if v == alpha2names[first]:
+ alpha2names[k] = f"🥇 {v}"
+ res_str = f"\n\nlegend:"
+ # for name in names_ordered:
+ # alpha = name2alpha[name]
+ for alpha in alpha_ordered:
+ name_w_medal = alpha2names[alpha]
+ res_str += f"\n{alpha}\t{name_w_medal}"
+ return res_str
+
+
+def main(
+ jslname: str = "result.json",
+):
+ """
+ 테스트 코드
+ """
+
+ df = pd.read_json(jslname, orient="records")
+ df = df.drop(columns=["tstamp", "logs"])
+ df = index_test_scenario(df)
+
+ # 중간에 visualization(df) 여기선 생략. 만약 이거 뺴고 다 따라했는데 문제가 생긴다면 viz 문제다. 근데 안그럴거같긴함
+ selections = df.idx_inst_src.unique()
+ for i, sel in enumerate(selections):
+ try:
+ df_now = df[df.idx_inst_src == sel]
+ df_now_processed, _alpha2names = init_tournament_dataframe(
+ df_now, alpha2names=alpha2names if "alpha2names" in dir() else None
+ )
+ if "alpha2names" not in dir():
+ alpha2names = _alpha2names
+ assert "alpha2names" in dir()
+ bracket_drawing = draw(df_now_processed, alpha2names=alpha2names)
+ legend = make_legend_str(df_now_processed, alpha2names)
+ print(bracket_drawing + legend)
+ print(bracket_drawing + legend, file=open(f"{i}.txt", "w"))
+ print(f"\n\n{sel}", file=open(f"{i}.txt", "a"))
+
+ for match_idx_human in df_now_processed.human_readable_idx:
+ match_idx = int(match_idx_human.split(": ")[0])
+ row = df_now_processed.loc[match_idx]
+ winner = row.winner
+ except Exception as e:
+ print(e, file=open(f"{i}_err.txt", "w"))
+ print("", file=open(f"{i}_err.txt", "a"))
+ print(sel, file=open(f"{i}_err.txt", "a"))
+ df_now_processed[
+ [
+ "depth",
+ "round",
+ "winner_nodes",
+ "winner_resolved",
+ "winner",
+ "model_a",
+ "model_b",
+ ]
+ ].to_json(f"{i}_err.jsonl", lines=True, orient="records")
+
+
+if __name__ == "__main__":
+ Fire(main)
diff --git a/streamlit_app_local/app.py b/streamlit_app_local/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..aed4e1dc45fdaaf468f8af745eb800a95846d7a9
--- /dev/null
+++ b/streamlit_app_local/app.py
@@ -0,0 +1,318 @@
+# import shutil
+import os
+import select
+import subprocess
+import sys
+import time
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+from typing import *
+
+import streamlit as st
+
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
+
+from varco_arena_core.prompts import load_prompt
+
+from view_utils import (
+ default_page_setting,
+ escape_markdown,
+ set_nav_bar,
+ show_linebreak_in_md,
+)
+
+# import sys
+# print(sys.executable)
+
+
+VA_ROOT = Path(os.environ.get("VARCO_ARENA_RESULT_PATH", "./user_submit"))
+USR_SUB = VA_ROOT.parts[-1]
+
+
+def upload_files(uploaded_files) -> Path:
+ # prep directory for user submission
+ user_sub_root = VA_ROOT
+ if user_sub_root.exists():
+ if not user_sub_root.is_dir():
+ raise ValueError(
+ f"{user_sub_root} file exists and is not a directory. Consider renaming it."
+ )
+ else:
+ user_sub_root.mkdir(parents=True)
+
+ KST = timezone(timedelta(hours=9))
+ tstamp = datetime.now(KST)
+ tstr = tstamp.strftime("%m-%d_%H:%M:%S")
+ files_dir_str = "./" + str(user_sub_root / tstr)
+ files_dir = Path(files_dir_str)
+ files_dir.mkdir(parents=True, exist_ok=True)
+ uploaded_files = list(uploaded_files)
+
+ if not uploaded_files:
+ st.warning("❌ No files to upload. Please drag/drop or browse files to upload.")
+ elif len(uploaded_files) < 2:
+ st.error("❌ You need at least 2 jsonlines files to properly run VA.")
+ else: # properly uploaded
+ for file in uploaded_files:
+ # Create a path for the file in the server directory
+ file_path = files_dir / file.name
+
+ # Save the file to the server directory
+ with open(file_path, "wb") as f:
+ f.write(file.getbuffer())
+
+ jslfiles = list(files_dir.glob("*.jsonl"))
+ st.success(f"✅ Successfully uploaded {len(jslfiles)} jsonl files.")
+ return files_dir.resolve()
+
+
+def run_varco_arena(
+ price_estimation: bool = False,
+ # upload_dir: Union[str, Path] = None,
+ promptname: str = None,
+ exp_name: str = None,
+ api_key: Optional[str] = None,
+ evaluation_model: str = "gpt-4o-mini",
+ update_interval: float = 1.0,
+):
+ # Use environment variable for API key
+ ptn = f"{str(st.session_state.upfiles_dir)}"
+ outdir = Path(ptn)
+ if exp_name:
+ outdir = outdir / exp_name
+
+ command = f"python ../varco_arena/main.py -i {ptn} -o {outdir} -k {api_key} -p {promptname} -e {evaluation_model} -j 64"
+ if price_estimation:
+ command = f"{command} -c"
+ else:
+ command = command.replace("python", "yes | python ")
+ print(command)
+
+ api_key = None # clear immediately
+
+ process = subprocess.Popen(
+ command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ stdin=subprocess.PIPE,
+ text=True,
+ bufsize=1,
+ shell=True,
+ )
+
+ # Set stdout and stdin to non-blocking mode
+ os.set_blocking(process.stdout.fileno(), False)
+
+ last_update_time = time.time()
+ terminal_output = st.empty()
+ full_output = f"{command}\n"
+ while True:
+ # Check if we have output to read
+ if select.select([process.stdout], [], [], 0)[0]:
+ output = process.stdout.readline()
+ if output:
+ full_output += output
+ if price_estimation:
+ to_show = full_output
+ terminal_output.code(to_show, language="bash")
+ else:
+ current_time = time.time()
+ if current_time - last_update_time > update_interval:
+ lines = full_output.split("\n")
+ if len(lines) < 5:
+ to_show = full_output
+ else:
+ to_show = "\n".join(["...\n..\n.\n"] + lines[-5:])
+ terminal_output.code(to_show, language="bash")
+ last_update_time = current_time
+ print(output)
+ time.sleep(0.1)
+ # Check if the process has finished
+ if process.poll() is not None:
+ # Read any remaining output
+ remaining_output = process.stdout.read()
+ if remaining_output:
+ lines = remaining_output.split("\n")
+ if len(lines) > 10:
+ to_show += "\n".join(["\n...\n..\n.\n"] + lines[-10:])
+ else:
+ to_show += remaining_output
+ terminal_output.code(to_show, language="bash")
+ print(remaining_output)
+ break
+
+ return_code = process.poll()
+ return outdir, return_code
+
+
+def main():
+ # init lang
+ st.session_state["korean"] = st.session_state.get("korean", False)
+
+ sidebar_placeholder = default_page_setting()
+ set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_init"
+ )
+
+ st.title("⚔️ VARCO ARENA ⚔️")
+ if st.session_state.korean:
+ st.write(
+ "**VARCO Arena는 각 모델의 생성된 결과를 비교 평가하여 모델의 성능 순위를 제공하는 시스템입니다. 커스텀 테스트셋에 유용하게 활용할 수 있으며, 모범답안을 필요로 하지 않습니다.**"
+ )
+ else:
+ st.write(
+ "**VARCO Arena is an LLM benchmarking system that compares model responses across customized test scenarios without requiring reference answers.**"
+ )
+
+ st.divider()
+ # Set up the file uploader
+ if st.session_state.korean:
+ st.markdown("모델 출력파일 업로드")
+ else:
+ st.markdown("### 1. Upload LLM responses")
+ uploaded_files = st.file_uploader(
+ "Drag and Drop jsonlines files (.jsonl)", accept_multiple_files=True
+ )
+
+ # upload state
+ if "upfiles_dir" not in st.session_state:
+ st.session_state.upfiles_dir = None
+ if st.button("Upload Files"):
+ st.session_state.upfiles_dir = upload_files(uploaded_files)
+ # st.success(st.session_state.upfiles_dir)
+
+ # st.markdown("**💥주의: 중복된 테스트 시나리오는 오류로 처리됩니다💥**")
+ if st.session_state.korean:
+ with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
+ st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
+ else:
+ with st.expander("❓❔ What should I upload ❓❔"):
+ st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
+
+ # Form for cost estimation
+ with st.form("cost_estimation_form"):
+ if st.session_state.korean:
+ st.write("### 2. 가격 산정")
+ else:
+ st.write("### 2. Cost Estimation")
+ eval_model = st.selectbox(
+ "Select Judge",
+ open("eval_models_list.txt", encoding="UTF8").read().split("\n"),
+ )
+ promptname = st.selectbox(
+ "Select Evalutaion Prompt",
+ open("eval_prompt_list.txt", encoding="UTF8").read().split("\n"),
+ )
+ if promptname == USR_SUB:
+ raise ValueError(
+ f"{USR_SUB=} is preserved name for the system. Consider another naming for the prompt or consider changing {VA_ROOT=} (USR_SUB == VA_ROOT.parts[-1])."
+ )
+ estimate_button = st.form_submit_button("Calculate Cost!")
+ with st.expander(
+ "LLM Judge에 활용되는 프롬프트 (`Calculate Cost!` 클릭시 갱신)"
+ if st.session_state.korean
+ else "**Evaluation Prompt for LLM Judge (will refresh after `Calculate Cost!` clicked)**"
+ ):
+ prompt = load_prompt(promptname, task="-")
+ kwargs = dict(
+ inst="{inst}",
+ src="{src}",
+ out_a="{out_a}",
+ out_b="{out_b}",
+ task="-",
+ )
+ if promptname == "translation_pair":
+ kwargs["source_lang"] = "{source_lang}"
+ kwargs["target_lang"] = "{target_lang}"
+ prompt_cmpl = prompt.complete_prompt(**kwargs)
+
+ st.markdown(f"### Evaluation Prompt: {promptname}")
+ for msg in prompt_cmpl:
+ st.markdown(f"**{msg['role']}**")
+ st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
+
+ if estimate_button:
+ if st.session_state.get("upfiles_dir") is None:
+ st.error(
+ "❌ Requirements: You have to upload jsonlines files first to proceed"
+ )
+ else:
+ st.markdown("##### Estimated Cost")
+ dummy_api_key = "dummy"
+ dummy_exp_name = "dummy"
+ result_file_path, return_code = run_varco_arena(
+ # upload_dir=st.session_state.upfiles_dir,
+ promptname=promptname,
+ api_key=dummy_api_key,
+ exp_name=dummy_exp_name,
+ price_estimation=True,
+ evaluation_model=eval_model,
+ )
+ if return_code:
+ st.error("❌ RuntimeError: An error occurred during cost estimation")
+ else:
+ st.success("✅ Cost estimation completed successfully")
+ st.session_state.cost_estimated = True
+
+ # Form for actual run
+ with st.form("run_arena_form"):
+ if st.session_state.korean:
+ st.write("### 3. Varco Arena 구동하기")
+ else:
+ st.write("### 3. Run Varco Arena")
+ api_key = st.text_input("Enter your OpenAI API Key", type="password")
+ exp_name = st.text_input("(Optional) Enter Exp. name")
+ exp_name = exp_name.replace(
+ "..", "_"
+ ) # May cause rmtree problem later. Block it.
+ exp_name = exp_name.replace(
+ USR_SUB, f"-{USR_SUB}-"
+ ) # May cause rmtree problem later. Block it.
+ exp_name = exp_name.replace("/", "-")
+ exp_name = exp_name.replace(" ", "_")
+ exp_name = exp_name.replace("~", "_")
+
+ if st.session_state.korean:
+ st.write("**주의**:`Ctrl+C` 버튼은 구현되지 않았습니다. 구동 전 숙고해주세요.")
+ else:
+ st.write("**Caution: `Ctrl+C` button hasn't been implemented.**")
+ run_button = st.form_submit_button(
+ "🔥 Run Arena!",
+ disabled=(not st.session_state.get("cost_estimated", False))
+ or "result_file_path"
+ in st.session_state.keys(), # run already performed once
+ )
+
+ if run_button:
+ set_nav_bar(
+ True,
+ sidebar_placeholder=sidebar_placeholder,
+ toggle_hashstr="app_during_run",
+ )
+ if st.session_state.get("upfiles_dir") is None:
+ st.error(
+ "❌ Requirements: You have to upload jsonlines files first to proceed"
+ )
+ elif not api_key:
+ st.error("❌ Requirements: OpenAI key required to run VA.")
+ else:
+ result_file_path, return_code = run_varco_arena(
+ # upload_dir=st.session_state.upfiles_dir,
+ promptname=promptname,
+ api_key=api_key,
+ exp_name=exp_name,
+ price_estimation=False,
+ evaluation_model=eval_model,
+ )
+ if return_code:
+ st.error("❌ RuntimeError: An error occurred during Varco Arena run")
+ else:
+ st.success("✅ Varco Arena run completed successfully")
+ st.session_state.result_file_path = result_file_path
+ set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_run_done"
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/streamlit_app_local/eval_models_list.txt b/streamlit_app_local/eval_models_list.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4e1a8b00cd614ab45bc282643782bb9f830ebac3
--- /dev/null
+++ b/streamlit_app_local/eval_models_list.txt
@@ -0,0 +1,3 @@
+gpt-4o-mini
+gpt-4o-2024-05-13
+gpt-4o-2024-08-06
diff --git a/streamlit_app_local/eval_prompt_list.txt b/streamlit_app_local/eval_prompt_list.txt
new file mode 100644
index 0000000000000000000000000000000000000000..98bfb4b2f5d4ffe96a958df0530a4f4a757ee9a4
--- /dev/null
+++ b/streamlit_app_local/eval_prompt_list.txt
@@ -0,0 +1,5 @@
+llmbar
+llmbar_brief
+translation_pair
+rag_pair_kr
+contextual (WIP)
diff --git a/streamlit_app_local/guide_mds b/streamlit_app_local/guide_mds
new file mode 120000
index 0000000000000000000000000000000000000000..45708691454da8b4b87571794ef52935c1704ab7
--- /dev/null
+++ b/streamlit_app_local/guide_mds
@@ -0,0 +1 @@
+../guide_mds/
\ No newline at end of file
diff --git a/streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc b/streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1ade5eb2dd7c681b1a1e5a32e80efd7c80a62210
Binary files /dev/null and b/streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc differ
diff --git a/streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc b/streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ee16d9622c4411f004c856afca5f9fd4c9c1bf75
Binary files /dev/null and b/streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc differ
diff --git a/streamlit_app_local/modules/nav.py b/streamlit_app_local/modules/nav.py
new file mode 100644
index 0000000000000000000000000000000000000000..e02200d9da3a67394fa230ad16fc2a8bd0ba4a10
--- /dev/null
+++ b/streamlit_app_local/modules/nav.py
@@ -0,0 +1,46 @@
+import streamlit as st
+
+
+def Navbar(sidebar_placeholder, toggle_hashstr: str = ""):
+ with sidebar_placeholder.container():
+ st.toggle(
+ "🇰🇷 한국어로 보기",
+ value=st.session_state.get("korean", False),
+ key=f"korean_toggle_{toggle_hashstr}", # Add this explicit key
+ on_change=lambda: setattr(
+ st.session_state,
+ "korean",
+ st.session_state.get(f"korean_toggle_{toggle_hashstr}", False),
+ ),
+ )
+
+ st.page_link(
+ "app.py",
+ label="Varco Arena 구동" if st.session_state.korean else "Run VARCO Arena",
+ icon="🔥",
+ )
+ st.page_link(
+ "pages/see_results.py",
+ label="결과 보기" if st.session_state.korean else "See Results",
+ icon="📝",
+ disabled=st.session_state.get("is_running", False),
+ )
+ st.page_link(
+ "pages/brief_intro.py",
+ label="어떻게 작동하나요?" if st.session_state.korean else "How it Works",
+ icon="❔",
+ disabled=st.session_state.get("is_running", False),
+ )
+ related_links_en = """
+**About**
+* [Paper](https://huggingface.co/papers/2411.01281)
+* [Blog (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+* [Inquiry](https://linkedin.com/in/deftson/)
+""".strip()
+ related_links_kr = """
+**About**
+* [논문](https://huggingface.co/papers/2411.01281)
+* [블로그](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
+* [문의](https://linkedin.com/in/deftson/)
+""".strip()
+ st.info(related_links_kr if st.session_state.korean else related_links_en)
diff --git a/streamlit_app_local/pages/brief_intro.py b/streamlit_app_local/pages/brief_intro.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f7206a58260ff372f71f623385fc1c56d4a997c
--- /dev/null
+++ b/streamlit_app_local/pages/brief_intro.py
@@ -0,0 +1,40 @@
+import streamlit as st
+
+from view_utils import default_page_setting, set_nav_bar
+
+sidebar_placeholder = default_page_setting()
+set_nav_bar(
+ False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="brief_intro_init"
+)
+
+st.image("va_en.png")
+if st.session_state.korean:
+ st.info("여러분이 업로드하는 파일은 위 그림의 LLM 출력들입니다.")
+ with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
+ st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
+else:
+ st.info(
+ "What you upload is the generated responses of LLMs regarding the test prompts."
+ )
+ with st.expander("❓❔ What should I upload ❓❔"):
+ st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
+
+
+st.image("va_concept_new.png")
+st.markdown(
+ """
+| |Current Practice|Varco Arena|
+|-|-|-|
+|Total no. matches|$$n_{\\text{model}}*\\|X\\|$$|$$(n_{\\text{model}}-1)*\\|X\\|$$|
+|No. matches per LLM|$$\\|X\\|$$|$$\\left[\\|X\\|,\\|X\\|\\text{log}n_{\\text{model}}\\right]$$|
+|Comparison type|reference-mediated (anchored) |direct|
+ """
+)
+if st.session_state.korean:
+ st.info(
+ "Varco Arena는 신뢰성 있는 순위를 더 적은 횟수의 비교 내에 얻어내며, 이러한 특징은 LLM 직접 비교의 이점으로부터 기인합니다."
+ )
+else:
+ st.info(
+ "Varco Arena takes advantage of direct comparison between LLM responses to guarantee better reliability in fewer number of total matches."
+ )
diff --git a/streamlit_app_local/pages/see_results.py b/streamlit_app_local/pages/see_results.py
new file mode 100644
index 0000000000000000000000000000000000000000..5537632cd4d25f3dc9887e1816e38ae091eee343
--- /dev/null
+++ b/streamlit_app_local/pages/see_results.py
@@ -0,0 +1,391 @@
+import pandas as pd
+import streamlit as st
+from varco_arena_core.prompts import load_prompt
+
+import analysis_utils as au
+from analysis_utils import number_breakdown_from_df
+from app import VA_ROOT
+from query_comp import QueryWrapper, get_base_url
+from view_utils import (
+ default_page_setting,
+ escape_markdown,
+ set_nav_bar,
+ show_linebreak_in_md,
+ visualization,
+)
+
+DEFAULT_LAYOUT_DICT = {
+ "title": {"font": {"size": 20, "family": "Gothic A1"}},
+ "font": {"size": 16, "family": "Gothic A1"},
+ "xaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
+ "yaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
+ "legend": {"font": {"size": 12, "family": "Gothic A1"}},
+}
+
+
+@st.cache_data
+def load_data():
+ all_result_dict = {}
+ df_dict = {}
+ # result_dir_list = os.listdir(VA_ROOT)
+ # result_dir_list = sorted(result_dir_list, key=lambda x: os.stat(os.path.join(VA_ROOT, x)).st_mtime, reverse=True)
+ for f in VA_ROOT.glob("**/result.json"):
+ print(f)
+
+ for resjsonf in VA_ROOT.glob("**/result.json"):
+ df = pd.read_json(resjsonf)
+ for col in ["tstamp", "logs"]:
+ if col in df.columns:
+ df.drop(columns=[col], inplace=True)
+ df = au.index_test_scenario(df)
+
+ fig_dict_per_task = {}
+ df_dict_per_task = {}
+ fig_dict_per_task["Overall"] = visualization(df, is_overall=True)
+ df_dict_per_task["Overall"] = df
+ for task in df["task"].unique().tolist():
+ df_task = df[df["task"] == task]
+ fig_dict_per_task[task] = visualization(df_task, is_overall=False)
+ df_dict_per_task[task] = df_task
+
+ prm_name = resjsonf.parts[-2]
+ exp_name = resjsonf.parts[-3]
+ key = f"{exp_name}/{prm_name}"
+ all_result_dict[key] = fig_dict_per_task
+ df_dict[key] = df_dict_per_task
+ return all_result_dict, df_dict
+
+
+def navigate(t, source, key, val):
+ # print(key, val)
+ if source is None:
+ return
+ target_index = t.index(source) + val
+ if 0 <= target_index < len(t):
+ st.session_state[key] = t[target_index]
+ st.rerun()
+
+
+def main():
+ sidebar_placeholder = default_page_setting(layout="wide")
+ set_nav_bar(
+ False,
+ sidebar_placeholder=sidebar_placeholder,
+ toggle_hashstr="see_results_init",
+ )
+ # load the data
+ st.session_state["all_result_dict"], st.session_state["df_dict"] = load_data()
+
+ # side bar
+ st.sidebar.title("Select Result:")
+ result_select = QueryWrapper("expname", "")(
+ st.sidebar.selectbox,
+ list(st.session_state["all_result_dict"].keys()),
+ )
+
+ if result_select is None:
+ st.stop()
+ eval_prompt_name = result_select.split("/")[-1].strip()
+ if st.sidebar.button("Clear Cache"):
+ st.cache_data.clear()
+ st.cache_resource.clear()
+ st.rerun()
+
+ if result_select:
+ if "alpha2names" in st.session_state:
+ del st.session_state["alpha2names"]
+
+ fig_dict_per_task = st.session_state["all_result_dict"][result_select]
+ task_list = list(fig_dict_per_task.keys())
+ elo_rating_by_task = fig_dict_per_task["Overall"]["elo_rating_by_task"]
+ # tabs = st.tabs(task_list)
+
+ df_dict_per_task = st.session_state["df_dict"][result_select]
+
+ default_layout_dict = DEFAULT_LAYOUT_DICT
+ task = QueryWrapper("task", "Select Task")(st.selectbox, task_list)
+
+ if task is None:
+ st.stop()
+ figure_dict = fig_dict_per_task[task]
+ judgename = figure_dict["judgename"]
+ df = df_dict_per_task[task]
+ interpretation, n_models, size_testset = number_breakdown_from_df(df)
+ if st.session_state.korean:
+ st.markdown(f"## 결과 ({task})")
+ st.markdown(f"##### Judge 모델: {judgename} / 평가프롬: {eval_prompt_name}")
+ st.markdown(f"##### 테스트셋 사이즈: {int(size_testset)} 행")
+ else:
+ st.markdown(f"## Results ({task})")
+ st.markdown(f"##### Judge Model: {judgename} / prompt: {eval_prompt_name}")
+ st.markdown(f"##### Size of Testset: {int(size_testset)} rows")
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.markdown(f"#### Ratings ({task})")
+ st.table(figure_dict["elo_rating"])
+ st.write(show_linebreak_in_md(escape_markdown(interpretation)))
+ st.markdown(f"**files at:** \n`{str(VA_ROOT.resolve()/result_select)}`")
+
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ elo_rating_by_task.update_layout(**default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_elo_rating_by_task",
+ )
+
+ st.divider()
+
+ if st.session_state.korean:
+ st.markdown("### 토너먼트 (테스트 시나리오) 별로 보기")
+ else:
+ st.markdown("### Tournament Results by Test Scenario")
+
+ # with st.expander("볼 토너먼트 고르기"):
+ d = list(df.idx_inst_src.unique())
+ default_idx = st.session_state.get("selected_tournament", None)
+ cols = st.columns((1, 18, 1))
+ with cols[0]:
+ if st.button("◀", key="prev_tournament"):
+ navigate(d, default_idx, "selected_tournament", -1)
+ with cols[1]:
+ tournament_prm_select = QueryWrapper("tournament", "Select Tournament")(
+ st.selectbox,
+ d,
+ default_idx,
+ key=f"{task}_tournament_select",
+ on_change=lambda: st.session_state.update(
+ selected_tournament=st.session_state.get(f"{task}_tournament_select"),
+ selected_match=None,
+ ),
+ label_visibility="collapsed",
+ )
+ with cols[2]:
+ if st.button("▶", key="next_tournament"):
+ navigate(d, default_idx, "selected_tournament", 1)
+
+ # tournament_prm_select = st.selectbox(
+ # "Select Tournament",
+ # df.idx_inst_src.unique(),
+ # index=d.index(st.session_state.get("selected_tournament")),
+ # key=f"{task}_tournament_{result_select}",
+ # )
+
+ # print(tournament_prm_select, type(tournament_prm_select))
+ st.session_state["selected_tournament"] = tournament_prm_select
+ # tournament_prm_select = st.selectbox(
+ # "Select Tournament",
+ # df.idx_inst_src.unique(),
+ # key=f"{task}_tournament_{result_select}",
+ # )
+ df_now_processed = None
+ if tournament_prm_select:
+ df_now = df[df.idx_inst_src == tournament_prm_select]
+ df_now_processed, _alpha2names = au.init_tournament_dataframe(
+ df_now,
+ alpha2names=st.session_state["alpha2names"]
+ if "alpha2names" in st.session_state.keys()
+ else None,
+ )
+ if "alpha2names" not in st.session_state:
+ st.session_state["alpha2names"] = _alpha2names
+
+ try:
+ bracket_drawing = au.draw(
+ df_now_processed,
+ alpha2names=st.session_state["alpha2names"],
+ )
+ legend = au.make_legend_str(
+ df_now_processed, st.session_state["alpha2names"]
+ )
+ st.code(bracket_drawing + legend)
+
+ m = list(df_now_processed.human_readable_idx)
+ default_idx = st.session_state.get("selected_match", None)
+ cols = st.columns((1, 18, 1))
+ with cols[0]:
+ if st.button("◀", key="prev_match"):
+ navigate(m, default_idx, "selected_match", -1)
+ with cols[1]:
+ match_idx_human = QueryWrapper("match", "Select Match")(
+ st.selectbox,
+ m,
+ default_idx,
+ key=f"{task}_match_select",
+ label_visibility="collapsed",
+ )
+ with cols[2]:
+ if st.button("▶", key="next_match"):
+ navigate(m, default_idx, "selected_match", 1)
+
+ # match_idx_human = st.selectbox(
+ # "Select Match",
+ # df_now_processed.human_readable_idx,
+ # key=f"{task}_match_{result_select}",
+ # )
+ # print(match_idx_human)
+ st.session_state["selected_match"] = match_idx_human
+ # match_idx_human = st.selectbox(
+ # "Select Match",
+ # df_now_processed.human_readable_idx,
+ # key=f"{task}_match_{result_select}",
+ # )
+ if match_idx_human:
+ match_idx = int(match_idx_human.split(": ")[0])
+ row = df_now_processed.loc[match_idx]
+
+ st.markdown("#### Current Test Scenario:")
+
+ with st.expander(
+ f"### Evaluation Prompt (evalprompt: {eval_prompt_name}--{task})"
+ ):
+ prompt = load_prompt(eval_prompt_name, task=task)
+ kwargs = dict(
+ inst="{inst}",
+ src="{src}",
+ out_a="{out_a}",
+ out_b="{out_b}",
+ task=task,
+ )
+ if eval_prompt_name == "translation_pair":
+ kwargs["source_lang"] = "{source_lang}"
+ kwargs["target_lang"] = "{target_lang}"
+ prompt_cmpl = prompt.complete_prompt(**kwargs)
+ for msg in prompt_cmpl:
+ st.markdown(f"**{msg['role']}**")
+ st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
+
+ st.info(show_linebreak_in_md(tournament_prm_select))
+
+ winner = row.winner
+ col1, col2 = st.columns(2)
+
+ winnerbox = st.success
+ loserbox = st.error
+ with col1:
+ iswinner = winner == "model_a"
+ writemsg = winnerbox if iswinner else loserbox
+ st.markdown(f"#### ({row.model_a}) {row.human_readable_model_a}")
+ writemsg(
+ show_linebreak_in_md(row.generated_a),
+ icon="✅" if iswinner else "❌",
+ )
+ with col2:
+ iswinner = winner == "model_b"
+ writemsg = winnerbox if iswinner else loserbox
+ st.markdown(f"#### ({row.model_b}) {row.human_readable_model_b}")
+ writemsg(
+ show_linebreak_in_md(row.generated_b),
+ icon="✅" if iswinner else "❌",
+ )
+ except Exception as e:
+ import traceback
+
+ traceback.print_exc()
+ st.markdown(
+ "**Bug: 아래 표를 복사해서 이슈로 남겨주시면 개선에 도움이 됩니다. 감사합니다🙏**"
+ if st.session_state.korean
+ else "Bug: Please open issue and attach the table output below to help me out. Thanks in advance.🙏"
+ )
+ st.error(e)
+ st.info(tournament_prm_select)
+ st.table(
+ df_now_processed[
+ [
+ "depth",
+ "round",
+ "winner_nodes",
+ "winner_resolved",
+ "winner",
+ "model_a",
+ "model_b",
+ ]
+ ]
+ )
+ st.write("Sharable link")
+ st.code(f"{get_base_url()}/see_results?{QueryWrapper.get_sharable_link()}")
+ st.divider()
+
+ if st.session_state.korean:
+ st.markdown("### 매치 통계")
+ else:
+ st.markdown("### Match Stats.")
+ col1, col2 = st.columns(2)
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict[
+ "fraction_of_model_a_wins_for_all_a_vs_b_matches"
+ ].update_layout(autosize=True, **default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_fraction_of_model_a_wins_for_all_a_vs_b_matches",
+ )
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["match_count_of_each_combination_of_models"].update_layout(
+ autosize=True, **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_match_count_of_each_combination_of_models",
+ )
+
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["match_count_for_each_model"].update_layout(
+ **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_match_count_for_each_model",
+ )
+ with col2:
+ pass
+
+ if st.session_state.korean:
+ st.markdown("### 참고용 LLM Judge 편향 정보")
+ else:
+ st.markdown("### FYI: How biased is your LLM Judge?")
+
+ with st.expander("펼쳐서 보기" if st.session_state.korean else "Expand to show"):
+ st.info(
+ """
+Varco Arena에서는 position bias의 영향을 최소화하기 위해 모든 모델이 A나 B위치에 번갈아 위치하도록 하였습니다. 그러나 LLM Judge 혹은 Prompt의 성능이 부족하다고 느껴진다면, 아래 알려진 LLM Judge bias가 참고가 될겁니다.
+* position bias (왼쪽)
+* length bias (오른쪽)
+
+결과의 왜곡이 LLM Judge의 부족함 떄문이었다는 점을 규명하려면 사용하신 LLM Judge와 Prompt의 binary classification 정확도를 측정해보시길 바랍니다 (Varco Arena를 활용하여 이를 수행해볼 수 있습니다!).""".strip()
+ if st.session_state.korean
+ else """
+In Varco Arena, to minimize the effect of position bias, all models are alternately positioned in either position A or B. However, if you feel the LLM Judge or Prompt performance is insufficient, the following known LLM Judge biases may be helpful to reference:
+* position bias (left)
+* length bias (right)
+
+To determine if result distortion was due to LLM Judge limitations, please measure the binary classification accuracy of your LLM Judge and Prompt (You could use Varco Arena for this purpose!).
+""".strip()
+ )
+ st.markdown(f"#### {judgename} + prompt = {eval_prompt_name}")
+ col1, col2 = st.columns(2)
+ with col1:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["counts_of_match_winners"].update_layout(
+ **default_layout_dict
+ ),
+ use_container_width=True,
+ key=f"{task}_counts_of_match_winners",
+ )
+ with col2:
+ with st.container(border=True):
+ st.plotly_chart(
+ figure_dict["length_bias"].update_layout(**default_layout_dict),
+ use_container_width=True,
+ key=f"{task}_length_bias",
+ )
+ st.table(figure_dict["length_bias_df"].groupby("category").describe().T)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/streamlit_app_local/query_comp.py b/streamlit_app_local/query_comp.py
new file mode 100644
index 0000000000000000000000000000000000000000..67d7eb926e18a572eae29d58682194e7a681778b
--- /dev/null
+++ b/streamlit_app_local/query_comp.py
@@ -0,0 +1,135 @@
+# query를 자동으로 읽고 쓰는 container를 정의
+
+from __future__ import annotations
+
+import re
+from typing import Callable, TypeVar
+
+import streamlit as st
+
+__all__ = ["QueryWrapper", "get_base_url"]
+
+T = TypeVar("T")
+
+
+import hashlib
+import urllib.parse
+
+
+def SHA1(msg: str) -> str:
+ return hashlib.sha1(msg.encode()).hexdigest()[:8]
+
+
+def get_base_url():
+ session = st.runtime.get_instance()._session_mgr.list_active_sessions()[0]
+ return urllib.parse.urlunparse(
+ [session.client.request.protocol, session.client.request.host, "", "", "", ""]
+ )
+
+
+class QueryWrapper:
+ queries: dict[str, _QueryWrapper] = {} # 기록용
+
+ def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
+ self.__wrapper = QueryWrapper.queries[query] = _QueryWrapper(
+ query, label, use_hash
+ )
+
+ def __call__(self, *args, **kwargs):
+ return self.__wrapper(*args, **kwargs)
+
+ @classmethod
+ def get_sharable_link(cls):
+ # for k, v in cls.queries.items():
+ # print(f"{k}: {v}")
+ return re.sub(
+ "&+", "&", "&".join([str(v) for k, v in cls.queries.items()])
+ ).strip("&")
+
+
+class _QueryWrapper:
+ ILLEGAL_CHARS = "&/=?"
+
+ def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
+ self.query = query
+ self.label = label or query
+ self.use_hash = use_hash
+ self.hash_table = {}
+ self.key = None
+
+ def __call__(
+ self,
+ base_container: Callable,
+ legal_list: list[T],
+ default: T | list[T] | None = None,
+ *,
+ key: str | None = None,
+ **kwargs,
+ ) -> T | list[T] | None:
+ val_from_query = st.query_params.get_all(self.query.lower())
+ # print(val_from_query)
+ legal = len(val_from_query) > 0
+ self.key = key or self.label
+
+ self.hash_table = {SHA1(str(v)): v for v in legal_list}
+
+ # filter out illegal values
+ if legal and legal_list:
+ val_from_query = [v for v in val_from_query if v in self.hash_table]
+ # print(self.label, val_from_query, legal)
+ if legal:
+ selected = [self.hash_table[v] for v in val_from_query]
+ elif default:
+ selected = default
+ elif self.label in st.session_state:
+ selected = st.session_state[self.label]
+ if legal_list:
+ if isinstance(selected, list):
+ selected = [v for v in selected if v in legal_list]
+ elif selected not in legal_list:
+ selected = []
+ else:
+ selected = []
+ if len(selected) == 1 and base_container in [st.selectbox, st.radio]:
+ selected = selected[0]
+ # print(self.label, selected)
+ if base_container == st.checkbox:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ elif base_container == st.multiselect:
+ selected = base_container(
+ self.label, legal_list, default=selected, key=self.key, **kwargs
+ )
+ elif base_container == st.radio:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ elif base_container == st.selectbox:
+ selected = base_container(
+ self.label,
+ legal_list,
+ index=legal_list.index(selected) if selected in legal_list else None,
+ key=self.key,
+ **kwargs,
+ )
+ else:
+ selected = base_container(self.label, legal_list, key=self.key, **kwargs)
+ return st.session_state[self.key]
+
+ def __str__(self):
+ selected = st.session_state.get(self.key, None)
+ if isinstance(selected, str):
+ return f"{self.query.lower()}={SHA1(selected)}"
+ elif isinstance(selected, list):
+ return "&".join([f"{self.query.lower()}={SHA1(str(v))}" for v in selected])
+ else:
+ return ""
diff --git a/streamlit_app_local/requirements.txt b/streamlit_app_local/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..20bed80d362d46443fd62b5a1ee1a2647c176261
--- /dev/null
+++ b/streamlit_app_local/requirements.txt
@@ -0,0 +1,5 @@
+streamlit
+plotly
+scikit-learn
+numpy
+pandas
diff --git a/streamlit_app_local/run.sh b/streamlit_app_local/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..9fc29b50cc3792ba67abdd776abb9dd7841bd4c7
--- /dev/null
+++ b/streamlit_app_local/run.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+# 여기서 돌려야해요 (cwd = ./streamlit_app_components/)
+VARCO_ARENA_RESULT_PATH=./user_submit streamlit run app.py
diff --git a/streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl b/streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..9a33b05b5fa2d07fc551f1bd4468c81f64c119d2
--- /dev/null
+++ b/streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl
@@ -0,0 +1,6 @@
+{"source":"D: 길드연구 추천 마크 했으니\nD: 다들 누르거라\nC: 길드장님 위치가 어디에요?\nC: 찾았음...\nD: 네 길드 태그 붙였으니까\nD: 그쪽으로 성 이동 하시고\nD: 병력 넣으라고 날래날래\nA: 하이하이\nD: 반갑습니다 어서오십셔\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 이리로 이동해서 오세요\nC: 네네~~~~\nA: 함께해요 ~\nC: 옮겼습니다.\nA: ♡\nD: 글루톤을 많이많이 잡아주셔야\nD: 시타델의 자원이 넘쳐납니다.\nA: 임모탄님\nA: 말투가 너무 나긋나긋하십니다\nA: 체통을 지켜주시옵소서\nD: 너나 잘해\nA: 케켘\nD: 사람 좀 모이면\nD: 파수꾼 잡아여\nB: 공격대 참여 많이 해주세요~!\nD: 공격대 참여해야 정말 자원 넘치는 시타델이 됩니다.\nD: 여러분 퀘스트하시면서 진행하시면 빠른 성장 가능합니다.\nD: 여러분 영주 명예 보상도 받으세요~\nD: 영주 명예 레벨 3 되서 보상 받으면\nA: 넵 임모탄님!!\nD: 건설슬롯 2개!\nA: 글루톤에 가속이 그득그득 들었네요\nA: 한부대씩 공격대 걸어주세요\nA: 헬로우 워보이\nD: 어서오세요~ :)\nA: 친절한 모탄씨\nD: 예쓰\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 헬로우 워뽀이\nD: 길드 요새 근처로 이동해 오세요\nA: 글루톤 참여하시면\nA: 시타델도 풍족해지고 가속도 넘쳐흐르게 됩니다요\nA: 10렙짜리\nA: 강화몬스터 같이 잡으실분\nA: 여기로 부대 이동해주세요 최대한\nA: ShareCoord#빈 땅#145926.609375#523510.437500\nD: 어서오세요\nA: 커몬커몬 워보이\nD: 10레[벨 정예몹 같이 잡아용\nA: 10레벨 강화랩토루스 한번 모여서 잡아볼까요","generated":"* D이 길드 연구 추천을 마크했으니 다들 누르라고 하였습니다.\n* D이 길드 태그 붙였으니 그 쪽으로 성 이동 하고 병력을 날래날래 넣으라고 하였습니다.\n* D이 글루톤을 많이많이 잡아야 시타델의 자원이 넘처난다고 강조했습니다.\n* A이 임모탄님의 말투가 너무 나긋나긋하니 체통을 지켜달라고 하자 A이 너나 잘해라고 하였습니다.\n* D은 사람 좀 모이면 파수꾼을 잡자고 하였습니다.\n* D은 영주 명예 레벨이 3 되어서 보상으로 건설슬롯 2개를 받을 수 있다고 하였습니다.\n* A이 글루톤에 가속이 그득그득 들었으니 한 부대씩 공격대를 걸어달라고 요청했습니다.\n* D와 A이 10레벨 강화랩토루스를 모여서 잡아보자고 하였습니다","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
+{"source":"A: 부대 남으시는분\nA: 8282\nA: 자 쳐볼게요\nA: 화이팅\nA: 발할라로~!!!\nA: 기억해줘!!! 정예 10레벨 잡았다구!!!\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 남는 부대 1개씩\nA: 길드 요새 건설에 넣어주세요!\nA: 우리의 시타델을 건설해야합니다!\nC: 웰컴 투 시타델\nA: 헬로우 워뽀이\nC: 모두 1부대는 길드 요새에 넣어달라입니다.\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nC: 시타델의 영토가 시급하다예요\nA: 이리로 오세요 어서~!\nB: 우리 길드요새 빨리 건설 할려면 병력을 더 많이 넣어야 하니까 어서어서 지원해주세요~ ^^\nA: 빨간내복최고~!!\nC: 어서오세요~\nC: ㅎㅎㅎㅎ\nA: 우와\nC: 워터보이님 어서오십셔\nA: 워(터)보이 오셨다\nC: 다들 날래날래 연매용새에 기사단 하나씩 넣으라우\nA: 자 우리 이제\nA: 경험치 3배 이벤트\nA: 달려야합니다\nA: 토스트님 어서오세요~ ㅎㅎ\nA: ShareCoord#빈 땅#155862.171875#524460.062500\nA: 여기로 병력집결해주세요\nA: 오른쪽에 병력슬롯 두번누르면\nA: 전체선택해서 여러 기사단을 한번에 운용할 수 있습니다\nA: 자자 요새 오른쪽에\nA: 모이고 있습니다\nA: 경험치 3배 이벤트\nD: 계속 이동할 수 없는 위치라고 하는데 어떻게 하면 되나요?\nA: 참여해서 기사 레벨 쭉쭉 올리셔요\nA: 그 성을 이리저리옮기면\nA: 초록색으로 성 바닥이 바뀌는데\nA: 그때 이동누르시면 됩니다\nA: 잘 안되면 2~3번정도 트라이해주시면 이동될거에요\nA: ShareCoord#빈 땅#156695.468750#525127.875000\nD: 아...깃발이 떠있어서 눌러봤는데 못간다고 해서 ㅎㅎ;\nA: 아~~\nA: 깃발떠있는곳으로 기사단(부대)는 이동할 수 있어요!\nA: 부대 여러개 보내서 요기 옆에 파수꾼 잡으면\nA: 경험치 폭탄 팡팡 터집니다\nA: 어서오세요\nD: 장애물이 있어서 못간다고 하네요..\nA: ShareCoord#빈 땅#156578.359375#523799.718750","generated":"* C가 시타델의 영토가 시급하니 모두 1부대는 길드 요새에 넣어달라고 하였습니다.\n* A이 요기 옆에 파수꾼을 잡으면 경험치 폭탄 팡팡 터지니 어서 오라고 하였습니다.\n* A이 경험치 3배 이벤트를 달려야 하니 빈 땅으로 병력을 집결해달라고 하였습니다.\n* A이 부대 여러 개 보내서 요기 옆에 파수꾼 잡으면 경험치 폭탄 팡팡 터진다고 하였습니다.","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
+{"source":"A: 10시 10분에\nA: 파수꾼사냥 시작할게요\nA: 파수꾼에 경험치 많이 들어있습니다\nC: 10시 10분에 파수꾼 잡습니다.\nC: 다들 오세요\nE: 어디인가요..?\nA: ShareCoord#빈 땅#156509.859375#524728.187500\nA: 요기가 부대 집결지고요!\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 길드요새 근처로 성 이동하신 후에 부대 옮기시면 편합니다\nD: 공격대를 그냥 보내면 되나요?\nA: 기사단을 보내두셨다가\nA: 시작하면 부대를 끌어서 몬스터 위에 커서를 올려두면\nA: 달려가서 공격하는데요\nA: 한타이밍에 같이 잡으면 전투에 참여한 사람한테 보상을 균등하게 줘서\nA: 다같이 몰아잡는게 이득입니다\nA: 10분되어서 파수꾼 한번 달려볼게요~!\nA: 부대 최대한 꺼내서 가져오신 후에\nA: 우측 부대 슬롯 버튼 더블클릭해서\nA: 3~5부대 운용하셔요~!\nA: 저 보시면 5부대 전부\nA: 한방에 운용합니다\nA: 자자 칠게요 파수꾼!!\nA: ShareCoord#파수꾼#155425.500000#527495.250000\nC: 고 합니다.\nA: 공격공격\nA: ShareCoord#파수꾼#159173.750000#528246.125000\nA: 멋쟁이님 커비님\nA: 부대 움직여서 파수꾼 공격하셔요~~!\nB: 같이 가유~~~ 파수꾼\nA: ShareCoord#파수꾼#160192.187500#525461.625000\nA: 어서오세요 ~~\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 성 이동하시구\nA: 파수꾼잡으러 참여하셔요 요쪽으루 '\nA: ShareCoord#파수꾼#165078.687500#525488.375000\nA: 같이 잡아야 병력도 덜죽고 경험치도 같이먹어요~!\nA: 어서오세요오~~~~\nB: 어서오세요~\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 성 이동 좌표\nA: ShareCoord#파수꾼#164864.437500#527752.750000\nC: 어서오세요~ :)\nA: 몬스터 좌표\nA: 계속 파수꾼 진행 중입니더~!!\nA: 어서와서 잡으셔용~~\nA: 부대 회군해서\nA: 정비 한버 ㄴ하고\nA: 다시 달릴게요\nA: 다들 부대 체력이 간당간당하실듯하여..","generated":"* A이 10시 10분에 파수꾼 사냥을 시작한다고 하였습니다.\n* D가 공격대를 그냥 보내면 되냐고 묻자 A은 다같이 몰아잡는게 이득이니 10분 되어서 파수꾼을 달려보자고 하였습니다.\n* A은 부대를 최대한 꺼내서 가져온 후에 우측 부대 슬롯 버튼 더블클릭 해서 3~5부대 운용하라고 하였습니다.\n* A이 파수꾼은 같이 잡아야 병력도 덜 죽고 경험치도 같이 먹을 수 있으니 어서오라고 하였습니다.","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
+{"source":"C: 요거 잡고 스탑 잠시 정비하고 오겠습니다.\nA: ShareCoord#빈 땅#159195.468750#530362.062500\nA: 여기 집결할게요!!!\nA: 18분에 스타트 하겠습니다\nA: 부대 회군했다가 다중 편성으로 전부 끌고 나오셔용~!\nA: 다같이 잡아야 경험치 함께먹고 폭업 가능합니다\nA: 어서어서~!! 워뽀이들 빨리 강해져야 나중에 싸울때 재밌습니다~! ㅎㅎㅎ\nA: ShareCoord#빈 땅#159809.593750#529345.625000\nA: 이동시간때문에 20분에 바로 시작할게요\nA: 10시 20분엔 그냥 바로 스타트합니답\nA: 와주셔서 감사합니다 ㅎㅎ 헤헤\nA: 자 칠게요\nA: 남은거 다잡겠습니다\nD: 가나요?\nA: gogo!!!!\nA: ShareCoord#빈 땅#159518.640625#532655.562500\nA: ShareCoord#파수꾼#157515.046875#534659.812500\nA: gogo!!!\nA: ShareCoord#에테르 원석#160185.000000#531312.000000\nA: 파수꾼잡고떨어진\nA: 에테르 원석 이거 시간제 버프같으니까\nB: 건물 짓거나 병사 뽑을 때 손모양 도움 누르면 시간이 빨라집니다.\nA: 필요하신거 빠르게 채집해서 버프받으세요\nB: 적극적으로 활용해주세요\nC: 파수꾼 사냥 수고하셨습니다.\nC: 에테르 원석 활용해주세요! 인터렉션하면 버프 받습니다.\nC: 다시 글루톤 사냥 갑시당\nC: 길드 연구도 시작했으니\nC: 기부 많이 해주세요\nC: 기부를 하면 개인 포인트도 얻으니까 상부상조\nA: 오후쯤이면 쟁한번 하지 싶네요\nC: 다들 발할라 가자\nA: 성장 호다닥 해주시구 궁금한거 물어봐주세요\nA: 도움 파파팍 눌러주시구욥~!\nA: 강한 워뽀이가 되자!!!!!!!\nB: 길드 요새 60% 넘었네요.\nB: 영차 영차 영차~\nA: 어우 달엉 도움 감사합니다\nC: ㅎㅎㅎㅎ\nD: ㅋㅋㅋㅋㅋ\nA: 도움도움~!!! 최고~!!!!!!\nA: 가속 아끼지 마시구 팍팍쓰세요\nA: 마지막날까지 가속 다 쓰지도 못합니다 ㄷㄷ\nA: 도움-가속-도움-가속\nA: 영-차-영-차\nC: (흡족)\nC: 시타델 요새 완성까지 3분남았습니다.\nA: 워뽀이 여러분들\nA: 궁금하거나 막히는 것들 말씀주시면\nA: 다 알려드리고 다 도와드립니다","generated":"* A이 파수꾼을 잡고 떨어진 에테르 원석은 시간제 버프로 건물 짓거나 병사 뽑을 때 손 모양 도움을 누르면 시간이 빨라지니 필요한 것을 빠르게 채집해 버프받으라고 하였습니다.\n* C가 길드 연구에 기부하면 개인 포인트도 얻을 수 있으니 기부를 많이 해달라고 당부하였습니다.\n* A이 오후 쯤 쟁 한번 하지 싶으니 성장 호다닥 하고 궁금한 것 물어봐달라고 하였습니다.\n* B이 길드 요새가 60%를 넘었다고 하였습니다.\n* A이 궁금하거나 막히는 것들 말씀 주시면 다 알려드리고 도와드린다고 하였습니다.","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
+{"source":"E: 여러분!\nE: 글루톤 많이 잡아주세요!\nE: 길드 자원이 부 족합니다.\nE: 젖과꿀이 흐르는 시타델이 되고 싶다?\nE: 글루톤을 많이 잡아주십쇼\nG: 가난한 시타델은 잇을 수 없습니다\nE: 플리즈\nB: 강화몬스터 미션 걸리신분?\nB: 같이 잡으실분 계신가요 ~\nF: 좌표주세요\nB: ShareCoord#빈 땅#155225.781250#534257.875000\nB: ShareCoord#빈 땅#154322.968750#533640.812500\nB: 강화몬스터 잡으실분 구해요~~~\nB: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nB: 출발합니다\nB: 강화몬스터~!\nB: 나이스커비님\nF: ㅎㅎ\nB: 감사합니닷\nF: 굿굿\nB: 너른 벌판을 뛰놀고 싶은\nC: 길드 연구 기부 부탁드립니다.\nB: 워보이의 마음을 아는가\nC: 길드 보시면 길드 연구 있어요~\nB: ShareCoord#1레벨 드래곤 레어#138900.000000#535800.000000\nB: 전초기지 짓고 드래곤 알 한번 얻어볼게요~~\nB: 알을 얻고 좀 키워야 용쓸 수 있습니다~~~!!\nC: 전초기지 건설에 기사단 1개씩 지원 부탁드립니다\nE: 글루톤도 많이 잡아주세요\nE: 길드 자원을 모아야 합니당\nB: 넵 임모탄님\nB: 충성충성충성 XXXXXXXXXXX\nE: 가즈아 발할라!\nB: 활동 포인트 얻는 법\nB: 1. 길드 > 연구 > 길드 연구 기부(추천 연구)\nB: 2. 길드 본부 > 도움\nB: 활동 포인트 사용법\nB: 1. 길드 > 길드 상점 > 상품 구매(기사 경험치 꿀)\nB: 기사렙업빨리해서 괴쎈 몬스터 빨리잡고 고성장합세다~!!\nB: 도움 고맙습니다 헤헤\nB: 곧 식사시간일텐데요\nB: 전초기지에 기사단 1개 넣어주시구\nB: 나머지 부대는 2레벨 이상 채집지에 넣어두신 후 식사다녀오시면\nB: 이벤트도 클리어되고 자원도 빵빵해집니다~~~\nB: 안녕하세요~~~~\nB: 궁금한거 물어보셔요~~~ 다 알려드립니다 ~~\nD: 우리 전초기지 3분 남았는데.\nA: 정찰은 어떻게 보내나요\nD: 좀만 더 영차영차 해주시면\nD: 남의 성 찍고 정찰 버튼 클릭하니까 정찰 보내졌어요.","generated":"* E가 젖과꿀이 흐르는 시타델이 되고 싶다면 글루톤을 많이 잡아달라고 부탁했습니다.\n* B이 강화몬스터 미션 걸리신 분 같이 잡자고 하였습니다.\n* B이 전초기지를 짓고 드래곤 알을 얻어보겠다고 하였습니다.\n* E가 길드 자원을 모아야 한다고 하였습니다.\n* B 활동 포인트 얻는 법과 활동 포인트 사용법을 공유하였습니다.","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
+{"source":"D: 보호막있는 성 빼고\nD: 다른 사람이 채집하는 곳도 정찰 되던데요.\nC: 정찰은 마을이나 타인의 성을 누르면 정찰 되는데요\nE: 정찰은 영지에 정찰 본부를 건설해야합니다\nC: ShareCoord#2레벨 프로잔 마을#148500.000000#524397.000000\nC: 미션떄때문이시면 이 마을이나\nA: 오 파랑새 날아간다\nC: 오 되셨나요\nA: ㅎㅎ 네 ㄳㄳ\nD: 파랑새...\nC: 사실부엉이입니다 ㅋㅋ\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 딱다구리라고 해도 믿을 주둥이 길이지만 그렇다고 하니 믿겠습니다\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 판게아. 그 신비의 세계\nC: 입이 삐죽 나왔어요\nC: 삐진듯요\nA: 다신 정찰 안보낼게 ㄷ\nE: ㅎㅎㅎㅎㅎㅎㅎㅎ\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nD: 끝\nC: 군주님 계신가요\nC: 스트릿 군주님\nC: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nC: 요새 근처로 오시면 글뤁루톤 공격대 집결이 훨씬 용이합니다!!\nC: 이쪽으로 오셔용~!\nC: 좌표 근처 땅을 꾸욱 누르신 후 성 이동을 누르시면 성이 이전됩니다\nE: 1시에 파수꾼 사냥 갑니다.\nE: 태그 달았으니 위치 확인해주세요 :)\nB: 안녕하세요 가입인사 드립니다\nE: 안녕하세요! 반갑습니당 :)\nC: 안녕하세요~~~\nC: 1시 파수꾼 확인했습니다\nE: 예압\nF: 안녕하세요 반갑습니다\nC: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nE: 어서오십쇼!\nE: 젖과 꿀이 흐르는 시타델입니다.\nE: 길드 요새 근처로 성 이동 해주세요! :)\nD: 네~~~~~~\nC: ShareCoord#[STOP]전초기지#-152190.000000#492921.000000\nC: 우리도 요기처럼 싸우게 될지도 몰라요 ㅎㅎ\nC: 무력을 빨리 키워둡시다~!!\nC: 헐!\nC: 커비님 혹시 시타델 버리고 가시나요\nC: ShareCoord#[CITA]남의커비#-155181.000000#523710.000000\nC: 들켰으니 복귀하시죵\nC: ㅎㅎㅎㅎㅎㅎㅎ\nC: 탐사 준비하세요\nC: ShareCoord#[INSD]불안이#-148347.000000#488490.000000","generated":"* D이 다른 사람이 채집하는 곳도 정찰이 가능하다고 하였습니다.\n* C이 싸우게 될지 모르니 무력을 빨리 키워두자고 제안했습니다.\n* C이 커비님에게 시타델 버리고 가시냐며 들켰으니 복귀하라고 제안했습니다.\n* C이 탐사를 준비하자고 하였습니다.","model_id":"human+alphabet+bullet","task":"tactan_chat","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","how_prompted":""}
diff --git a/streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl b/streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..55c73b7ca949d1cecdf2b05ab1c315fefeb49d4b
--- /dev/null
+++ b/streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl
@@ -0,0 +1,6 @@
+{"generated":"* D길드장이 길드연구 태그를 추천하며, C가 입장했을 때 위치를 안내합니다.\n* A가 D길드장에게 인사를 건네고, C가 길드로 이동합니다.\n* D길드장은 글루톤 잡는것을 독려하며 시타델 자원 풍부해짐을 안내합니다.\n* B, D 공격대 참여를 독려하고 퀘스트 진행을 권장하며 영주 명예 보상의 존재를 설명합니다.\n* A는 글루톤에 가속이 풍부하다는 점을 강조하고, 부대 이동을 안내하며, 웜보이와 그의 친구를 맞이합니다.\n* D길드장은 10레벨 강화몬스터(랩토루스)를 함께 잡을 것을 제안합니다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"D: 길드연구 추천 마크 했으니\nD: 다들 누르거라\nC: 길드장님 위치가 어디에요?\nC: 찾았음...\nD: 네 길드 태그 붙였으니까\nD: 그쪽으로 성 이동 하시고\nD: 병력 넣으라고 날래날래\nA: 하이하이\nD: 반갑습니다 어서오십셔\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 이리로 이동해서 오세요\nC: 네네~~~~\nA: 함께해요 ~\nC: 옮겼습니다.\nA: ♡\nD: 글루톤을 많이많이 잡아주셔야\nD: 시타델의 자원이 넘쳐납니다.\nA: 임모탄님\nA: 말투가 너무 나긋나긋하십니다\nA: 체통을 지켜주시옵소서\nD: 너나 잘해\nA: 케켘\nD: 사람 좀 모이면\nD: 파수꾼 잡아여\nB: 공격대 참여 많이 해주세요~!\nD: 공격대 참여해야 정말 자원 넘치는 시타델이 됩니다.\nD: 여러분 퀘스트하시면서 진행하시면 빠른 성장 가능합니다.\nD: 여러분 영주 명예 보상도 받으세요~\nD: 영주 명예 레벨 3 되서 보상 받으면\nA: 넵 임모탄님!!\nD: 건설슬롯 2개!\nA: 글루톤에 가속이 그득그득 들었네요\nA: 한부대씩 공격대 걸어주세요\nA: 헬로우 워보이\nD: 어서오세요~ :)\nA: 친절한 모탄씨\nD: 예쓰\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 헬로우 워뽀이\nD: 길드 요새 근처로 이동해 오세요\nA: 글루톤 참여하시면\nA: 시타델도 풍족해지고 가속도 넘쳐흐르게 됩니다요\nA: 10렙짜리\nA: 강화몬스터 같이 잡으실분\nA: 여기로 부대 이동해주세요 최대한\nA: ShareCoord#빈 땅#145926.609375#523510.437500\nD: 어서오세요\nA: 커몬커몬 워보이\nD: 10레[벨 정예몹 같이 잡아용\nA: 10레벨 강화랩토루스 한번 모여서 잡아볼까요","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
+{"generated":"* A는 길드가 건설하는 시타델 기념 첫 건축물인 길드 요새로 부대를 이동시켜달라고 합니다. \n* 시타델 건설을 축하하며 모두가 참여하도록 유도합니다.\n* 다음으로 경험치 3배 이벤트를 알려주고 \n * 참여 방법을 설명하며 더 쉽게 이해하도록 도와줍니다.\n* 람층이 좀 낯설어 보이는 D에게 이벤트 지역으로 이동하려면 깃발 옆으로 부대를 보내도록 안내하고, \n* 경험치를 빠르게 얻기 위해 파수꾼을 잡으면 이벤트 효율이 높아진다고 설명합니다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"A: 부대 남으시는분\nA: 8282\nA: 자 쳐볼게요\nA: 화이팅\nA: 발할라로~!!!\nA: 기억해줘!!! 정예 10레벨 잡았다구!!!\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 남는 부대 1개씩\nA: 길드 요새 건설에 넣어주세요!\nA: 우리의 시타델을 건설해야합니다!\nC: 웰컴 투 시타델\nA: 헬로우 워뽀이\nC: 모두 1부대는 길드 요새에 넣어달라입니다.\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nC: 시타델의 영토가 시급하다예요\nA: 이리로 오세요 어서~!\nB: 우리 길드요새 빨리 건설 할려면 병력을 더 많이 넣어야 하니까 어서어서 지원해주세요~ ^^\nA: 빨간내복최고~!!\nC: 어서오세요~\nC: ㅎㅎㅎㅎ\nA: 우와\nC: 워터보이님 어서오십셔\nA: 워(터)보이 오셨다\nC: 다들 날래날래 연매용새에 기사단 하나씩 넣으라우\nA: 자 우리 이제\nA: 경험치 3배 이벤트\nA: 달려야합니다\nA: 토스트님 어서오세요~ ㅎㅎ\nA: ShareCoord#빈 땅#155862.171875#524460.062500\nA: 여기로 병력집결해주세요\nA: 오른쪽에 병력슬롯 두번누르면\nA: 전체선택해서 여러 기사단을 한번에 운용할 수 있습니다\nA: 자자 요새 오른쪽에\nA: 모이고 있습니다\nA: 경험치 3배 이벤트\nD: 계속 이동할 수 없는 위치라고 하는데 어떻게 하면 되나요?\nA: 참여해서 기사 레벨 쭉쭉 올리셔요\nA: 그 성을 이리저리옮기면\nA: 초록색으로 성 바닥이 바뀌는데\nA: 그때 이동누르시면 됩니다\nA: 잘 안되면 2~3번정도 트라이해주시면 이동될거에요\nA: ShareCoord#빈 땅#156695.468750#525127.875000\nD: 아...깃발이 떠있어서 눌러봤는데 못간다고 해서 ㅎㅎ;\nA: 아~~\nA: 깃발떠있는곳으로 기사단(부대)는 이동할 수 있어요!\nA: 부대 여러개 보내서 요기 옆에 파수꾼 잡으면\nA: 경험치 폭탄 팡팡 터집니다\nA: 어서오세요\nD: 장애물이 있어서 못간다고 하네요..\nA: ShareCoord#빈 땅#156578.359375#523799.718750","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
+{"generated":"* A는 길드에서 파수꾼 사냥 이벤트를 10시 10분에 시작한다고 알리고 사냥 장소 위치 정보를 공유합니다. \n* 초보자를 위한 안내로, 길드 요새 근처로 이동한 후 부대를 이동시켜 몬스터 위에 커서를 놓고 공격하는 전략을 설명합니다. 공동전투에 참여하면 경험치가 균등하게 나눠집니다.\n* A는 플레이어들이 부대를 운용하고 파수꾼을 공격할 준비를 하도록 지시합니다.\n* 파수꾼 위치를 계속 공유하며 부대가 없어지면 회수 후 다시 진행한다고 합니다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"A: 10시 10분에\nA: 파수꾼사냥 시작할게요\nA: 파수꾼에 경험치 많이 들어있습니다\nC: 10시 10분에 파수꾼 잡습니다.\nC: 다들 오세요\nE: 어디인가요..?\nA: ShareCoord#빈 땅#156509.859375#524728.187500\nA: 요기가 부대 집결지고요!\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 길드요새 근처로 성 이동하신 후에 부대 옮기시면 편합니다\nD: 공격대를 그냥 보내면 되나요?\nA: 기사단을 보내두셨다가\nA: 시작하면 부대를 끌어서 몬스터 위에 커서를 올려두면\nA: 달려가서 공격하는데요\nA: 한타이밍에 같이 잡으면 전투에 참여한 사람한테 보상을 균등하게 줘서\nA: 다같이 몰아잡는게 이득입니다\nA: 10분되어서 파수꾼 한번 달려볼게요~!\nA: 부대 최대한 꺼내서 가져오신 후에\nA: 우측 부대 슬롯 버튼 더블클릭해서\nA: 3~5부대 운용하셔요~!\nA: 저 보시면 5부대 전부\nA: 한방에 운용합니다\nA: 자자 칠게요 파수꾼!!\nA: ShareCoord#파수꾼#155425.500000#527495.250000\nC: 고 합니다.\nA: 공격공격\nA: ShareCoord#파수꾼#159173.750000#528246.125000\nA: 멋쟁이님 커비님\nA: 부대 움직여서 파수꾼 공격하셔요~~!\nB: 같이 가유~~~ 파수꾼\nA: ShareCoord#파수꾼#160192.187500#525461.625000\nA: 어서오세요 ~~\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 성 이동하시구\nA: 파수꾼잡으러 참여하셔요 요쪽으루 '\nA: ShareCoord#파수꾼#165078.687500#525488.375000\nA: 같이 잡아야 병력도 덜죽고 경험치도 같이먹어요~!\nA: 어서오세요오~~~~\nB: 어서오세요~\nA: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nA: 성 이동 좌표\nA: ShareCoord#파수꾼#164864.437500#527752.750000\nC: 어서오세요~ :)\nA: 몬스터 좌표\nA: 계속 파수꾼 진행 중입니더~!!\nA: 어서와서 잡으셔용~~\nA: 부대 회군해서\nA: 정비 한버 ㄴ하고\nA: 다시 달릴게요\nA: 다들 부대 체력이 간당간당하실듯하여..","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
+{"generated":"* A는 딜레이 페이지 카운트다운을 시작하기 전에 플레이어들을 모아놓고 준비 상황을 안내합니다. \n* 파수꾼과 에테르 원석 출현 위치를 공유하고, \n파수꾼을 잡고 에테르 원석을 활용하면 시간 제한 버프를 얻을 수 있음을 알립니다.\n* '도움' & '가속' 도구를 적극적으로 사용하라는 조언도 덧붙이고, C가 시타델 건설이 3분 남았다고 알리며 플레이어가 조금씩 역할을 마치고 상황이 진행되는 모습을 보여줍니다.\n* 플레이어들은 서로 응원하고, 게임 진행 상황을 나누며 갈 길을 계획합니다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"C: 요거 잡고 스탑 잠시 정비하고 오겠습니다.\nA: ShareCoord#빈 땅#159195.468750#530362.062500\nA: 여기 집결할게요!!!\nA: 18분에 스타트 하겠습니다\nA: 부대 회군했다가 다중 편성으로 전부 끌고 나오셔용~!\nA: 다같이 잡아야 경험치 함께먹고 폭업 가능합니다\nA: 어서어서~!! 워뽀이들 빨리 강해져야 나중에 싸울때 재밌습니다~! ㅎㅎㅎ\nA: ShareCoord#빈 땅#159809.593750#529345.625000\nA: 이동시간때문에 20분에 바로 시작할게요\nA: 10시 20분엔 그냥 바로 스타트합니답\nA: 와주셔서 감사합니다 ㅎㅎ 헤헤\nA: 자 칠게요\nA: 남은거 다잡겠습니다\nD: 가나요?\nA: gogo!!!!\nA: ShareCoord#빈 땅#159518.640625#532655.562500\nA: ShareCoord#파수꾼#157515.046875#534659.812500\nA: gogo!!!\nA: ShareCoord#에테르 원석#160185.000000#531312.000000\nA: 파수꾼잡고떨어진\nA: 에테르 원석 이거 시간제 버프같으니까\nB: 건물 짓거나 병사 뽑을 때 손모양 도움 누르면 시간이 빨라집니다.\nA: 필요하신거 빠르게 채집해서 버프받으세요\nB: 적극적으로 활용해주세요\nC: 파수꾼 사냥 수고하셨습니다.\nC: 에테르 원석 활용해주세요! 인터렉션하면 버프 받습니다.\nC: 다시 글루톤 사냥 갑시당\nC: 길드 연구도 시작했으니\nC: 기부 많이 해주세요\nC: 기부를 하면 개인 포인트도 얻으니까 상부상조\nA: 오후쯤이면 쟁한번 하지 싶네요\nC: 다들 발할라 가자\nA: 성장 호다닥 해주시구 궁금한거 물어봐주세요\nA: 도움 파파팍 눌러주시구욥~!\nA: 강한 워뽀이가 되자!!!!!!!\nB: 길드 요새 60% 넘었네요.\nB: 영차 영차 영차~\nA: 어우 달엉 도움 감사합니다\nC: ㅎㅎㅎㅎ\nD: ㅋㅋㅋㅋㅋ\nA: 도움도움~!!! 최고~!!!!!!\nA: 가속 아끼지 마시구 팍팍쓰세요\nA: 마지막날까지 가속 다 쓰지도 못합니다 ㄷㄷ\nA: 도움-가속-도움-가속\nA: 영-차-영-차\nC: (흡족)\nC: 시타델 요새 완성까지 3분남았습니다.\nA: 워뽀이 여러분들\nA: 궁금하거나 막히는 것들 말씀주시면\nA: 다 알려드리고 다 도와드립니다","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
+{"generated":"* E는 길드 자원이 부족하다는 이유로 글루톤을 많이 잡아달라고 합니다. \n* B는 강화몬스터 미션을 하고 있으며 몇몇 사람들을 모집합니다. B는 다른 활동을 안내하며 F와 함께 미션을 완료합니다.\n* C는 길드 연구 기부를 부탁하며 전초기지 건설을 지원하기를 요청합니다. B는 C의 요청을 받아들여 전초기지 건설에 기사단을 배치하고 2레벨 이상의 부대들을 채집지로 보내 자원 확보를 위한 계획을 설명합니다.\n* D는 전초기지가 곧 완성이 될 것이라고 말하며, 다른 성의 정찰을 어떻게 보낸지에 대해 A에게 문의합니다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"E: 여러분!\nE: 글루톤 많이 잡아주세요!\nE: 길드 자원이 부 족합니다.\nE: 젖과꿀이 흐르는 시타델이 되고 싶다?\nE: 글루톤을 많이 잡아주십쇼\nG: 가난한 시타델은 잇을 수 없습니다\nE: 플리즈\nB: 강화몬스터 미션 걸리신분?\nB: 같이 잡으실분 계신가요 ~\nF: 좌표주세요\nB: ShareCoord#빈 땅#155225.781250#534257.875000\nB: ShareCoord#빈 땅#154322.968750#533640.812500\nB: 강화몬스터 잡으실분 구해요~~~\nB: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nB: 출발합니다\nB: 강화몬스터~!\nB: 나이스커비님\nF: ㅎㅎ\nB: 감사합니닷\nF: 굿굿\nB: 너른 벌판을 뛰놀고 싶은\nC: 길드 연구 기부 부탁드립니다.\nB: 워보이의 마음을 아는가\nC: 길드 보시면 길드 연구 있어요~\nB: ShareCoord#1레벨 드래곤 레어#138900.000000#535800.000000\nB: 전초기지 짓고 드래곤 알 한번 얻어볼게요~~\nB: 알을 얻고 좀 키워야 용쓸 수 있습니다~~~!!\nC: 전초기지 건설에 기사단 1개씩 지원 부탁드립니다\nE: 글루톤도 많이 잡아주세요\nE: 길드 자원을 모아야 합니당\nB: 넵 임모탄님\nB: 충성충성충성 XXXXXXXXXXX\nE: 가즈아 발할라!\nB: 활동 포인트 얻는 법\nB: 1. 길드 > 연구 > 길드 연구 기부(추천 연구)\nB: 2. 길드 본부 > 도움\nB: 활동 포인트 사용법\nB: 1. 길드 > 길드 상점 > 상품 구매(기사 경험치 꿀)\nB: 기사렙업빨리해서 괴쎈 몬스터 빨리잡고 고성장합세다~!!\nB: 도움 고맙습니다 헤헤\nB: 곧 식사시간일텐데요\nB: 전초기지에 기사단 1개 넣어주시구\nB: 나머지 부대는 2레벨 이상 채집지에 넣어두신 후 식사다녀오시면\nB: 이벤트도 클리어되고 자원도 빵빵해집니다~~~\nB: 안녕하세요~~~~\nB: 궁금한거 물어보셔요~~~ 다 알려드립니다 ~~\nD: 우리 전초기지 3분 남았는데.\nA: 정찰은 어떻게 보내나요\nD: 좀만 더 영차영차 해주시면\nD: 남의 성 찍고 정찰 버튼 클릭하니까 정찰 보내졌어요.","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
+{"generated":"* 여러 사람이 함께 게임을 하고 있다. \n* A가 이스라엘에 와서 이벤트에 참여하라는 부탁을 받고, 그 후 다른 플레이어들과 대화를 나눈다.\n* C는 게임 내 마을의 위치 정보를 공유하고, 마을을 방문하라고 다른 플레이어 D에게 권유한다. \n* E는 1시에 파수꾼 사냥을 한다고 알리고 좌표를 공유한다. \n* 새로운 플레이어 B와 F가 가입하면 C와 E는 환영한다.\n* C는 플레이어 D가 다른 지역으로 이동했는데, 이에 대해 농담을 나눈다.\n* 마지막으로 C는 남의커비(혹은 커비?)라는 새로운 폴리곤의 위치를 공개하고 신비로운 탐사를 준비하자고 제안한다.","instruction":"주어진 채팅에 대한 요약을 작성해줘. 사람들의 발화를 통해서 어떤 상황인지 파악할 수 있었으면 좋겠어. 예시처럼 \"요약:\" 뒤에다 작성해주면 돼.","source":"D: 보호막있는 성 빼고\nD: 다른 사람이 채집하는 곳도 정찰 되던데요.\nC: 정찰은 마을이나 타인의 성을 누르면 정찰 되는데요\nE: 정찰은 영지에 정찰 본부를 건설해야합니다\nC: ShareCoord#2레벨 프로잔 마을#148500.000000#524397.000000\nC: 미션떄때문이시면 이 마을이나\nA: 오 파랑새 날아간다\nC: 오 되셨나요\nA: ㅎㅎ 네 ㄳㄳ\nD: 파랑새...\nC: 사실부엉이입니다 ㅋㅋ\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 딱다구리라고 해도 믿을 주둥이 길이지만 그렇다고 하니 믿겠습니다\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nA: 판게아. 그 신비의 세계\nC: 입이 삐죽 나왔어요\nC: 삐진듯요\nA: 다신 정찰 안보낼게 ㄷ\nE: ㅎㅎㅎㅎㅎㅎㅎㅎ\nC: ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ\nD: 끝\nC: 군주님 계신가요\nC: 스트릿 군주님\nC: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nC: 요새 근처로 오시면 글뤁루톤 공격대 집결이 훨씬 용이합니다!!\nC: 이쪽으로 오셔용~!\nC: 좌표 근처 땅을 꾸욱 누르신 후 성 이동을 누르시면 성이 이전됩니다\nE: 1시에 파수꾼 사냥 갑니다.\nE: 태그 달았으니 위치 확인해주세요 :)\nB: 안녕하세요 가입인사 드립니다\nE: 안녕하세요! 반갑습니당 :)\nC: 안녕하세요~~~\nC: 1시 파수꾼 확인했습니다\nE: 예압\nF: 안녕하세요 반갑습니다\nC: ShareCoord#[CITA]길드요새#149559.000000#521478.000000\nE: 어서오십쇼!\nE: 젖과 꿀이 흐르는 시타델입니다.\nE: 길드 요새 근처로 성 이동 해주세요! :)\nD: 네~~~~~~\nC: ShareCoord#[STOP]전초기지#-152190.000000#492921.000000\nC: 우리도 요기처럼 싸우게 될지도 몰라요 ㅎㅎ\nC: 무력을 빨리 키워둡시다~!!\nC: 헐!\nC: 커비님 혹시 시타델 버리고 가시나요\nC: ShareCoord#[CITA]남의커비#-155181.000000#523710.000000\nC: 들켰으니 복귀하시죵\nC: ㅎㅎㅎㅎㅎㅎㅎ\nC: 탐사 준비하세요\nC: ShareCoord#[INSD]불안이#-148347.000000#488490.000000","task":"tactan_chat","model_id":"50turn_gemma_best","how_prompted":""}
diff --git a/streamlit_app_local/user_submit/llm/240413_dora.jsonl b/streamlit_app_local/user_submit/llm/240413_dora.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..de247fd188a4f7486c4b78f6926e918c741ebba2
--- /dev/null
+++ b/streamlit_app_local/user_submit/llm/240413_dora.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "난 여름보다는 겨울이 더 좋아."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "근데 난 여름보다 겨울이 더 좋아. 왜냐하면 살짝 추운 날씨가 옷 입기도 쉽고, 푹 쉬고 있을 수도 있어서 좋거든."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다 겨울이 좋아. 왜냐하면 겨울에는 밖에 나가서 놀기도 편하고, 눈 덮인 산이나 호수를 볼 수 있어서 좋기 때문이야."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 갔다. 집에 와서 쉬는 시간에 공부를 하며 하루를 마무리 지었다."}
diff --git a/streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl b/streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..e4b9b07a0664c01ba9ae49ebe62671437d456119
--- /dev/null
+++ b/streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 마음에 들어."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋아."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~"}
diff --git a/streamlit_app_local/user_submit/llm/240413_full.jsonl b/streamlit_app_local/user_submit/llm/240413_full.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..3a7b3e9d2eff37bf235e65d5bd11a047a8621e56
--- /dev/null
+++ b/streamlit_app_local/user_submit/llm/240413_full.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다."}
diff --git a/streamlit_app_local/user_submit/llm/240413_full_epoch_5.jsonl b/streamlit_app_local/user_submit/llm/240413_full_epoch_5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..18865ccf946108c311eac6ce1f64fe5e95af1433
--- /dev/null
+++ b/streamlit_app_local/user_submit/llm/240413_full_epoch_5.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}
diff --git a/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/elo_result.xlsx b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/elo_result.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..98a3a7fa4e963dd5167adb714cf00ac753b8c769
Binary files /dev/null and b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/elo_result.xlsx differ
diff --git a/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/result.json b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/result.json
new file mode 100644
index 0000000000000000000000000000000000000000..bfc61c9acbdd94aef0974b0b816dfd36cdcf8bbb
--- /dev/null
+++ b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/result.json
@@ -0,0 +1,326 @@
+[
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_full_epoch_5",
+ "winner": "A",
+ "prob_a": 0.9947798755335181,
+ "prob_b": 0.005220124466481947,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아.",
+ "generated_b": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아.",
+ "tournament_idx": 0,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.409444
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_dora",
+ "winner": "A",
+ "prob_a": 0.9999938558298646,
+ "prob_b": 6.1441701354263915e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 더 마음에 들어.",
+ "generated_b": "난 여름보다는 겨울이 더 좋아.",
+ "tournament_idx": 0,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.4052308
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_dora_epoch_5",
+ "winner": "A",
+ "prob_a": 0.9999970976882026,
+ "prob_b": 2.9023117974227762e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아.",
+ "generated_b": "나는 여름보다는 겨울이 더 마음에 들어.",
+ "tournament_idx": 0,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.1135933
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full_epoch_5",
+ "model_b": "240413_full",
+ "winner": "A",
+ "prob_a": 0.9933071486037031,
+ "prob_b": 0.006692851396296897,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다.",
+ "generated_b": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어.",
+ "tournament_idx": 1,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.4221804
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_dora",
+ "winner": "A",
+ "prob_a": 0.9999991684720482,
+ "prob_b": 8.315279518576472e-07,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다.",
+ "generated_b": "근데 난 여름보다 겨울이 더 좋아. 왜냐하면 살짝 추운 날씨가 옷 입기도 쉽고, 푹 쉬고 있을 수도 있어서 좋거든.",
+ "tournament_idx": 1,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.5060413
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full_epoch_5",
+ "model_b": "240413_dora_epoch_5",
+ "winner": "A",
+ "prob_a": 0.9947798755335181,
+ "prob_b": 0.005220124466481947,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다.",
+ "generated_b": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다.",
+ "tournament_idx": 1,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.0979986
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_full",
+ "winner": "B",
+ "prob_a": 0.11920292517192517,
+ "prob_b": 0.8807970748280748,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 더 좋아.",
+ "generated_b": "나는 여름보다는 겨울이 더 좋다.",
+ "tournament_idx": 2,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.4052985
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora",
+ "model_b": "240413_full_epoch_5",
+ "winner": "B",
+ "prob_a": 0.02297736979777856,
+ "prob_b": 0.9770226302022214,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다 겨울이 좋아. 왜냐하면 겨울에는 밖에 나가서 놀기도 편하고, 눈 덮인 산이나 호수를 볼 수 있어서 좋기 때문이야.",
+ "generated_b": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여.",
+ "tournament_idx": 2,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.346429
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_full_epoch_5",
+ "winner": "B",
+ "prob_a": 0.006692851396296897,
+ "prob_b": 0.9933071486037031,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "난 여름보다는 겨울이 좋아.",
+ "generated_a": "나는 여름보다는 겨울이 더 좋다.",
+ "generated_b": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여.",
+ "tournament_idx": 2,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.295212
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_dora",
+ "winner": "B",
+ "prob_a": 1.0677030272546973e-06,
+ "prob_b": 0.9999989322969728,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다.",
+ "generated_b": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다.",
+ "tournament_idx": 3,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.3937037
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_full_epoch_5",
+ "winner": "A",
+ "prob_a": 0.6224593265017803,
+ "prob_b": 0.37754067349821974,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다.",
+ "generated_b": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다.",
+ "tournament_idx": 3,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.4092796
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora",
+ "model_b": "240413_full",
+ "winner": "A",
+ "prob_a": 0.9999999995941349,
+ "prob_b": 4.0586521248284276e-10,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다.",
+ "generated_b": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다.",
+ "tournament_idx": 3,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.1281044
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full_epoch_5",
+ "model_b": "240413_full",
+ "winner": "B",
+ "prob_a": 0.22270013190111745,
+ "prob_b": 0.7772998680988825,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다.",
+ "generated_b": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다.",
+ "tournament_idx": 4,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.4266539
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_dora",
+ "winner": "B",
+ "prob_a": 5.3157849718487075e-08,
+ "prob_b": 0.9999999468421502,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다.",
+ "generated_b": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다.",
+ "tournament_idx": 4,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.3566463
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_dora",
+ "winner": "B",
+ "prob_a": 0.3775406617480341,
+ "prob_b": 0.622459338251966,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다.",
+ "generated_b": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다.",
+ "tournament_idx": 4,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.260069
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full_epoch_5",
+ "model_b": "240413_dora_epoch_5",
+ "winner": "B",
+ "prob_a": 0.0009110516162882596,
+ "prob_b": 0.9990889483837118,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다.",
+ "generated_b": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~",
+ "tournament_idx": 5,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 0,
+ "tstamp": 1732778448.2642024
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_full",
+ "model_b": "240413_dora",
+ "winner": "A",
+ "prob_a": 0.9947798749140081,
+ "prob_b": 0.0052201250859919435,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다.",
+ "generated_b": "오늘은 학교에 갔다. 집에 와서 쉬는 시간에 공부를 하며 하루를 마무리 지었다.",
+ "tournament_idx": 5,
+ "round": "semi-final",
+ "depth": 1,
+ "match_order_in_round": 1,
+ "tstamp": 1732778448.422099
+ },
+ {
+ "task": "늘려쓰기",
+ "model_a": "240413_dora_epoch_5",
+ "model_b": "240413_full",
+ "winner": "B",
+ "prob_a": 0.008577487012447618,
+ "prob_b": 0.9914225129875525,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.",
+ "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.",
+ "generated_a": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~",
+ "generated_b": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다.",
+ "tournament_idx": 5,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732778449.1214201
+ }
+]
\ No newline at end of file
diff --git "a/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/\353\212\230\353\240\244\354\223\260\352\270\260/elo_result.xlsx" "b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/\353\212\230\353\240\244\354\223\260\352\270\260/elo_result.xlsx"
new file mode 100644
index 0000000000000000000000000000000000000000..f06633537995ecc16a0adaf9e7ce156bbcb964a0
Binary files /dev/null and "b/streamlit_app_local/user_submit/llm/LLM_example_result/llmbar/\353\212\230\353\240\244\354\223\260\352\270\260/elo_result.xlsx" differ
diff --git a/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/elo_result.xlsx b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/elo_result.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..d234f2b83075144a6386a60b93447b1c4c95bc3a
Binary files /dev/null and b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/elo_result.xlsx differ
diff --git a/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/result.json b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/result.json
new file mode 100644
index 0000000000000000000000000000000000000000..f5db59273e853f2407039ef5040ff1f02c5a4a68
--- /dev/null
+++ b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/result.json
@@ -0,0 +1,92 @@
+[
+ {
+ "task": "번역",
+ "model_a": "llama3_8b",
+ "model_b": "qwen2.5_7b",
+ "winner": "A",
+ "prob_a": 0.9999339478476663,
+ "prob_b": 6.605215233363495e-05,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "Just want the music?",
+ "source": "",
+ "generated_a": "음악만 원하시나요?",
+ "generated_b": "음악만 원하시나요?",
+ "tournament_idx": 0,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732600285.742209
+ },
+ {
+ "task": "번역",
+ "model_a": "qwen2.5_7b",
+ "model_b": "llama3_8b",
+ "winner": "B",
+ "prob_a": 0.1192029125726952,
+ "prob_b": 0.8807970874273048,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.",
+ "source": "",
+ "generated_a": "저는 평생 싸움을 치거나 아내에게 손을 뻗지 않았고, 아이들을 괴롭힐 것처럼 보지 않았습니다.",
+ "generated_b": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.",
+ "tournament_idx": 1,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732600285.4767454
+ },
+ {
+ "task": "번역",
+ "model_a": "llama3_8b",
+ "model_b": "qwen2.5_7b",
+ "winner": "B",
+ "prob_a": 0.26894141350551787,
+ "prob_b": 0.7310585864944821,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.",
+ "source": "",
+ "generated_a": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.",
+ "generated_b": "윌리 존스 3세는 2019년 재즈 드러머로 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.",
+ "tournament_idx": 2,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732600285.7796211
+ },
+ {
+ "task": "번역",
+ "model_a": "llama3_8b",
+ "model_b": "qwen2.5_7b",
+ "winner": "A",
+ "prob_a": 0.9046505359634699,
+ "prob_b": 0.09534946403653005,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.",
+ "source": "",
+ "generated_a": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.",
+ "generated_b": "그는 호레이스 실버, 시더 월튼 등 밴드에 합류했다.",
+ "tournament_idx": 3,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732600285.4751053
+ },
+ {
+ "task": "번역",
+ "model_a": "llama3_8b",
+ "model_b": "qwen2.5_7b",
+ "winner": "B",
+ "prob_a": 0.1480471904639361,
+ "prob_b": 0.8519528095360639,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.",
+ "source": "",
+ "generated_a": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.",
+ "generated_b": "보통 우리는 7시에 맨해튼의 에퀴녹스까지 지하철을 타고 다닙니다.",
+ "tournament_idx": 4,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732600285.7664952
+ }
+]
diff --git "a/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/\353\262\210\354\227\255/elo_result.xlsx" "b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/\353\262\210\354\227\255/elo_result.xlsx"
new file mode 100644
index 0000000000000000000000000000000000000000..892cec0212e1b966409a6208935b2c00a2b4cf7a
Binary files /dev/null and "b/streamlit_app_local/user_submit/mt/MT_example_result/translation_pair/\353\262\210\354\227\255/elo_result.xlsx" differ
diff --git a/streamlit_app_local/user_submit/mt/llama5.jsonl b/streamlit_app_local/user_submit/mt/llama5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..3795adb125f8c6cf8d38054db13a7caa73168986
--- /dev/null
+++ b/streamlit_app_local/user_submit/mt/llama5.jsonl
@@ -0,0 +1,5 @@
+{"model_id": "llama3_8b", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/streamlit_app_local/user_submit/mt/qwen5.jsonl b/streamlit_app_local/user_submit/mt/qwen5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..6735fdc22866acb62988286b52b6c378a144f497
--- /dev/null
+++ b/streamlit_app_local/user_submit/mt/qwen5.jsonl
@@ -0,0 +1,5 @@
+{"model_id": "qwen2.5_7b", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 치거나 아내에게 손을 뻗지 않았고, 아이들을 괴롭힐 것처럼 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월튼 등 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에퀴녹스까지 지하철을 타고 다닙니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/-/elo_result.xlsx b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/-/elo_result.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..da68aa29e9bc632e11e9aa49e142d2f80db6ba74
Binary files /dev/null and b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/-/elo_result.xlsx differ
diff --git a/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/elo_result.xlsx b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/elo_result.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..d9f33cfcb04381c93eed7cfa93f801534a1ca1e9
Binary files /dev/null and b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/elo_result.xlsx differ
diff --git a/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/result.json b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/result.json
new file mode 100644
index 0000000000000000000000000000000000000000..b008a6e13bb83099ee9c8ba47621ce8e48397982
--- /dev/null
+++ b/streamlit_app_local/user_submit/rag/RAG_example_result/rag_pair_kr/result.json
@@ -0,0 +1,542 @@
+[
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9820137910623454,
+ "prob_b": 0.01798620893765462,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 공성 때 왜 로건 더스트가 좋아?",
+ "source": "근거 문장 0: 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용\n근거 문장 1: 로건 더스트의 공격속도 증가 버프와 공성 전투에서의 스킬 피해량 증가, 방어력 감소 디버프가 유리\n근거 문장 2: 공성 전투 시 스킬이 강화",
+ "generated_a": "로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용하기 때문에 공성 전투에 유리해.",
+ "generated_b": "공성 전투에서는 로건 더스트의 스킬이 강화되어 피해량이 증가하고 방어력을 12% 감소시켜서 적을 빠르게 무력화할 수 있어.",
+ "tournament_idx": 0,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.8991036
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 0.5,
+ "prob_b": 0.5,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 공성 때 왜 로건 더스트가 좋아?",
+ "source": "근거 문장 0: 강력한 공격 성능과 공성 추가 효과를 가진 특성이 유리하다.\n근거 문장 1: 공격 속도 증가 버프, 공격력 패시브, 공성 전투 시 스킬 피해량 증가, 공성 전투 시 공격력 증가, 방어력 감소 디버프.\n근거 문장 2: 로건더스트 액티브 발동시 대상에게 피해를 주고, 5초간 방어력을 10% 감소\n근거 문장 3: 공성 전투 시 스킬이 강화\n근거 문장 4: 피해량이 증가하고 5초간 대상의 방어력을 12% 감소\n근거 문장 5: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 6: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 7: 공성 전투 중 일반 공격 시 10% 확률로 5초간 스킬 피해량이 3% 증가\n근거 문장 8: 로건더스트 패시브2 발동시 대상에게 협공 시 로건 더스트의 일반 공격 피해량이 증가\n근거 문장 9: 공성 전투 중 액티브 스킬 사용 시, 30% 확률로 주변 5개 아군 기사단의 공격력이 10초 동안 7% 증가",
+ "generated_a": "공성 전투에서는 로건 더스트의 스킬 피해량 증가와 방어력 감소 디버프가 강력한 공격 성능과 공성 추가 효과를 제공하여 유리해.",
+ "generated_b": "로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 능력이 있어서 공성 병기 전투에 적합해.",
+ "tournament_idx": 1,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.125779
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.7310585707655275,
+ "prob_b": 0.2689414292344725,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 공방 올려주는 패시브 캐릭",
+ "source": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가",
+ "generated_a": "엘리사는 기사단의 공격력을 증가시키는 패시브 스킬을 가지고 있어 공방에서 유리할 수 있어.",
+ "generated_b": "엘리사의 패시브 스킬은 기사단의 공격력을 증가시키고, 일정 확률로 추가 피해를 입히기 때문에 수성 전투에서 유리할 수 있습니다.",
+ "tournament_idx": 2,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.1685183
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 0.022977367171197704,
+ "prob_b": 0.9770226328288022,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀",
+ "source": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가",
+ "generated_a": "엘리사와 벨리타 조합이 기사단의 공격력과 방어력을 동시에 올려주는 패시브 스킬을 가지고 있어. 특히 점령이나 대규모 전투에 유리해.",
+ "generated_b": "엘리사와 벨리타 조합을 추천해요. 엘리사의 패시브1은 기사단의 공격력을 증가시키고, 벨리타의 패시브1은 스킬 피해량 및 공격력을 증가시켜서 기사단의 전반적인 능력을 강화할 수 있어요.",
+ "tournament_idx": 3,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0491087
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.8807970748280748,
+ "prob_b": 0.11920292517192517,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 공방 올려주는 패시브 캐릭",
+ "source": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가",
+ "generated_a": "수루&틴코가 기사단의 공격력을 증가시키고 방어력을 제공해 팀 전체의 생존력을 높여주는 패시브 캐릭터입니다.",
+ "generated_b": "수루 & 틴코가 필드 대규모 PVP에서 아군 기사단의 공격력을 증가시키고 방어력을 제공하여 팀 전체의 생존력을 높여줍니다.",
+ "tournament_idx": 4,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.144211
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 0.0293122293856112,
+ "prob_b": 0.9706877706143888,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀",
+ "source": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가",
+ "generated_a": "수루 & 틴코가 필드 대규모 PVP에서 아군 기사단의 공격력을 증가시키고 방어력을 제공해줘서 생존력을 높이는 데 유리해.",
+ "generated_b": "수루&틴코가 기사단의 공격력을 증가시키고 방어력을 제공해 팀 전체의 생존력을 높여주는 패시브 스킬을 가지고 있어서 추천해요.",
+ "tournament_idx": 5,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9594512
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9999869928706541,
+ "prob_b": 1.3007129345850576e-05,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 이동 속도 왜이리 느려?",
+ "source": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군",
+ "generated_a": "기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군하기 때문에 느려.",
+ "generated_b": "기사단의 이동 속도는 가장 느린 병종에 맞춰져 있어요. 모든 기사단이 같은 속도로 행군하려면 가장 느린 병종이 모든 기사단에 편성되어야 합니다.",
+ "tournament_idx": 6,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.1593723
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 7.484622259433064e-05,
+ "prob_b": 0.9999251537774057,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 빠르게 이동하는 방법좀",
+ "source": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군",
+ "generated_a": "기사단의 이동 속도를 빠르게 하려면, 젬비의 액티브 스킬을 사용하면 5초간 이동속도가 추가로 6% 증가합니다.",
+ "generated_b": "기사단을 빠르게 이동시키려면, 기사단을 구성하는 병종 중 가장 느린 병종에 맞춰서 행군해야 합니다. 모든 기사단이 같은 속도로 이동하려면, 가장 느린 병종이 모든 기사단에 편성되어야 합니다.",
+ "tournament_idx": 7,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0497277
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.999999974890009,
+ "prob_b": 2.5109990926928157e-08,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나",
+ "generated_a": "세바스찬이 패시브2 스킬로 기사단의 방어력을 6% 증가시켜서 탱킹에 유리해.",
+ "generated_b": "세바스찬이 기사단의 방어력을 증가시켜주는 패시브 스킬을 가지고 있어서 탱킹에 유리해.",
+ "tournament_idx": 8,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0207405
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9998766054486304,
+ "prob_b": 0.00012339455136958636,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나",
+ "generated_a": "세바스찬이랑 멜린을 추천해. 세바스찬은 패시브2 스킬로 기사단의 방어력을 올려주고, 멜린은 패시브1 스킬로 방어력과 생명력을 올려줘.",
+ "generated_b": "멜린이 기사단의 방어력과 생명력을 올려주는 데 유리해. 멜린 패시브1 발동시 방어력이 7%, 생명력이 6% 증가하거든.",
+ "tournament_idx": 9,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.1239493
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9890130567826304,
+ "prob_b": 0.010986943217369604,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 딴딴하게 하고 싶어",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나",
+ "generated_a": "기사단을 딴딴하게 하려면, 세바스찬을 사용하는 것이 좋습니다. 세바스찬은 패시브2 스킬로 기사단의 방어력이 증가하여 수성 전투에서 유리합니다.",
+ "generated_b": "세바스찬은 패시브2 스킬로 기사단의 방어력을 증가시켜서 딴딴하게 만들 수 있어.",
+ "tournament_idx": 10,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9427588
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "B",
+ "prob_a": 9.422446203261432e-07,
+ "prob_b": 0.9999990577553797,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가",
+ "generated_a": "세바스찬이 기사단의 방어력을 올려주는 패시브 스킬을 가지고 있어서 탱킹에 유리해.",
+ "generated_b": "세바스찬이 패시브2 스킬로 기사단의 방어력을 6% 증가시켜서 탱킹에 유리해.",
+ "tournament_idx": 11,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0473292
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "B",
+ "prob_a": 2.9023117974227762e-06,
+ "prob_b": 0.9999970976882026,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가",
+ "generated_a": "세바스찬이 기사단의 방어력을 올려주는 신의 기사로 추천돼.",
+ "generated_b": "세바스찬이 기사단의 방어력을 올려주는 패시브2 스킬이 있어서 수성 전투나 대규모 PVP에서 유리할 수 있어.",
+ "tournament_idx": 12,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9161613
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9947798755335181,
+ "prob_b": 0.005220124466481947,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 딴딴하게 하고 싶어",
+ "source": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가",
+ "generated_a": "기사단을 딴딴하게 하려면, 세바스찬을 사용하는 것이 좋습니다. 세바스찬은 패시브2 스킬로 기사단의 방어력이 증가하여 수성 전투에서 유리합니다.",
+ "generated_b": "세바스찬은 패시브2 스킬로 기사단의 방어력을 증가시켜서 딴딴하게 만들 수 있어.",
+ "tournament_idx": 13,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0383887
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.880797073778139,
+ "prob_b": 0.11920292622186103,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 다중 편성 필수야?",
+ "source": "근거 문장이 없습니다.",
+ "generated_a": "컨텐츠에 따라 다양한 편성이 필요할 수 있습니다. 하나의 부대만으로 충분할 때도 있고, 다중 편성이 최적일 때도 있습니다.",
+ "generated_b": "아니, 다중 편성이 최적일 경우가 있지만, 하나의 부대만으로 충분할 경우도 있습니다.",
+ "tournament_idx": 14,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.924924
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9859363748425088,
+ "prob_b": 0.014063625157491164,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 기사단 하나만 써도 돼?",
+ "source": "근거 문장이 없습니다.",
+ "generated_a": "네, 컨텐츠에 따라 하나의 부대만으로 충분할 때도 있습니다.",
+ "generated_b": "네, 컨텐츠에 따라 하나의 부대만으로 충분할 경우가 있습니다.",
+ "tournament_idx": 15,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9386675
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 0.014063627181903883,
+ "prob_b": 0.9859363728180961,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 도미닉 크림슨은 부관 뭐써",
+ "source": "근거 문장 0: 도미닉 크림슨의 부관으로 좋은 영웅은 릭 블러드, 세이디 굿윈, 아디카, 라엔 & 아토, 로건 더스트, 벨리타, 수루, 날라, 니엘, 아잔, 알렉사 호프, 루실 발렌틴, 멜린 등\n근거 문장 1: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 3: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 4: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리",
+ "generated_a": "도미닉 크림슨의 부관으로는 릭 블러드, 세이디 굿윈, 아디카, 라엔 & 아토, 로건 더스트, 벨리타, 수루, 날라, 니엘, 아잔, 알렉사 호프, 루실 발렌틴, 멜린 등이 있습니다.",
+ "generated_b": "도미닉 크림슨과 부관으로 멜린, 수루, 아잔, 로건 더스트 등이 있어. 각각의 조합이 특정 전투 상황에 유리해.",
+ "tournament_idx": 16,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0596395
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 1.6701417888622094e-05,
+ "prob_b": 0.9999832985821114,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 도미닉 크림슨은 부관 뭐써",
+ "source": "근거 문장 0: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 1: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 3: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리",
+ "generated_a": "도미닉 크림슨은 멜린, 수루, 아잔, 로건 더스트와 같은 다양한 부관과 조합할 수 있어. 각 조합은 전투 상황에 따라 다르게 활용할 수 있어.",
+ "generated_b": "도미닉 크림슨과 부관으로 멜린을 쓰면 필드 기사단 공격과 수성 전투에 유리해. 멜린은 방어력 감소 디버프와 공격력 및 방어력 증가 패시브 스킬을 가지고 있어서 도미닉 크림슨의 공격력을 극대화할 수 있어.",
+ "tournament_idx": 17,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0555248
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9999967112528679,
+ "prob_b": 3.288747132049668e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 공성 하고싶은데 도미닉 크림슨이랑 누구 같이 쓰는게 제일 좋아?",
+ "source": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리",
+ "generated_a": "도미닉 크림슨과 로건 더스트 조합이 공성/스웜 전투에 매우 유리해. 도미닉 크림슨의 스킬 피해량 증가와 생명력 증가, 로건 더스트의 공성 전투 시 공격력 증가 덕분에 공성에 아주 잘 맞아.",
+ "generated_b": "도미닉 크림슨과 로건 더스트 조합이 공성 전투에 유리해. 로건 더스트의 공성 전투 강화 스킬이 도움이 될 거야.",
+ "tournament_idx": 18,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.948295
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9999957771655142,
+ "prob_b": 4.222834485740341e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 공성 해야하는데 도미닉 크림슨밖에 없어. 누굴 새로 뽑는게 제일 좋을까?",
+ "source": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리",
+ "generated_a": "도미닉 크림슨과 로건 더스트 조합이 공성 전투에 유리해. 로건 더스트를 새로 뽑는 게 좋을 것 같아.",
+ "generated_b": "도미닉 크림슨과 로건 더스트 조합이 공성/스웜 전투에 매우 유리해. 도미닉 크림슨의 스킬 피해량 증가와 생명력 증가, 로건 더스트의 공성 전투 시 공격력 증가가 공성에 아주 좋아.",
+ "tournament_idx": 19,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.938655
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9959298622788343,
+ "prob_b": 0.00407013772116577,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?",
+ "source": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합\n근거 문장 1: 로건 더스트와 부관 프레아 조합은 공성/스웜 전투에 유리\n근거 문장 2: 공성/스웜 전투할 때, 로건 더스트의 공성 전투 시 스킬 피해량 증가와 알렉사 호프의 방어력 및 공격력 감소 디버프는 공성 전투에서 적을 빠르게 무력화할 수 있게 한다.",
+ "generated_a": "공성 전투에서는 로건 더스트와 알렉사 호프 조합이 좋아. 로건 더스트의 스킬 피해량 증가랑 방어력 감소 디버프가 공성에 유리하고, 알렉사 호프의 추가 피해 효과도 큰 도움이 돼.",
+ "generated_b": "로건 더스트와 알렉사 호프를 같이 데려가면 좋을 것 같아. 알렉사 호프는 방어력 감소와 추가 피해를 주는 효과가 있어서 공성 전투에 유리해.",
+ "tournament_idx": 20,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.904377
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "B",
+ "prob_a": 2.9023117974227762e-06,
+ "prob_b": 0.9999970976882026,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?",
+ "source": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합",
+ "generated_a": "로건 더스트와 함께 공성 전투에선 루실 발렌틴을 데려가면 좋을 것 같아. 루실 발렌틴은 속도 증가와 추가 피해 효과가 있어서 공성 전투에 유리해.",
+ "generated_b": "로건 더스트와 함께 데려가면 좋을 부관으로는 루실 발렌틴이 있어요. 루실 발렌틴은 공성 전투 시 공격력 증가 효과가 있어서 공성/스웜 전투에 적합하거든요.",
+ "tournament_idx": 21,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.1836474
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "B",
+ "prob_a": 1.1253518384332559e-07,
+ "prob_b": 0.9999998874648162,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 로건 더스트 액티브는 언제 세지더라",
+ "source": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘",
+ "generated_a": "로건 더스트 액티브는 5초 동안 지속됩니다.",
+ "generated_b": "로건 더스트 액티브 스킬은 발동 시 대상에게 피해를 주고, 5초간 방어력을 10% 감소시키는 효과가 있습니다.",
+ "tournament_idx": 22,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.1258008
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.999999922655772,
+ "prob_b": 7.734422806410623e-08,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 로건더스트 쓰다가 언제는 스킬이 세게 들어갔던 것 같은데 왠지 알아?",
+ "source": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘",
+ "generated_a": "로건더스트의 스킬은 공성 전투 시 강화되어 피해량이 증가하고, 5초간 대상의 방어력을 12% 감소시키는 효과가 있어서 그런 느낌을 받을 수 있어.",
+ "generated_b": "공성 전투 중 액티브 스킬 사용 시, 30% 확률로 주변 5개 아군 기사단의 공격력이 10초 동안 7% 증가하는 패시브2가 발동할 수 있어.",
+ "tournament_idx": 23,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.168539
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9999977396749352,
+ "prob_b": 2.2603250647816336e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 부대 병력 줄어들면 손해보는거 있어?",
+ "source": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소",
+ "generated_a": "네, 부대의 병력이 적어질 경우 부대의 공격력이 감소합니다.",
+ "generated_b": "전투 중 병력이 적어질 때 부대의 공격력이 감소하니 주의해야 해.",
+ "tournament_idx": 24,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9086576
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9999999973534264,
+ "prob_b": 2.646573631904765e-09,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 오래 전투하면 상대가 너무 안죽는데 왜이래",
+ "source": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소",
+ "generated_a": "전투 중 병력이 적어질 때 부대의 공격력이 감소하는 문제가 있을 수 있어.",
+ "generated_b": "리토의 스킬은 대상에게 피해를 주고 이동속도를 감소시키지만, 피격 대상의 이동속도가 12% 감소하는 효과는 상대가 너무 안 죽는 이유일 수 있습니다.",
+ "tournament_idx": 25,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.0092487
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9241418248860165,
+ "prob_b": 0.07585817511398356,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 서브 뭐있어?",
+ "source": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적",
+ "generated_a": "루실 발렌틴은 필드 전투에서 다양한 서브와 시너지를 발휘할 수 있어. 예를 들어, 루실 발렌틴과 로건 더스트 조합은 필드 기사단 공격에 매우 효과적이야. 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 반격 피해와 협공 시 공격력 증가 능력이 필드 전투에서 큰 이점을 줘.",
+ "generated_b": "루실 발렌틴과 로건 더스트 조합이 필드 전투에 유리해. 로건 더스트는 반격 피해와 협공 시 공격력 증가 능력이 있어서 적을 빠르게 처치할 수 있고, 루실 발렌틴은 높은 스킬 계수와 방어력 감소 디버프를 제공해.",
+ "tournament_idx": 26,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.082426
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9999982396570288,
+ "prob_b": 1.7603429712321879e-06,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 부관 추천좀",
+ "source": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적",
+ "generated_a": "루실 발렌틴과 필드 전투 시너지 좋은 부관으로는 수루, 리토, 젬비, 벨리타가 있습니다. 수루는 필드 소규모 PVP에, 리토는 마법 병기 상태 전투에, 젬비는 필드 기사단 공격에, 벨리타는 연맹 건물 건설이나 병기 수송 중 전투에 유리합니다.",
+ "generated_b": "루실 발렌틴과 로건 더스트 조합이 필드 전투에 유리해. 로건 더스트는 방어력 감소 디버프를 제공하고, 루실 발렌틴은 공격력 증가와 이동속도 증가 패시브로 빠른 전투를 지원해.",
+ "tournament_idx": 27,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9294066
+ },
+ {
+ "task": "-",
+ "model_a": "llama31_rag",
+ "model_b": "gemma2_rag",
+ "winner": "A",
+ "prob_a": 0.9959298630421216,
+ "prob_b": 0.004070136957878291,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 릭 블러드 병력 회복 계수가 얼마더라.",
+ "source": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90",
+ "generated_a": "릭 블러드의 병력 회복 계수는 90이야.",
+ "generated_b": "릭 블러드의 병력 회복 계수는 90입니다.",
+ "tournament_idx": 28,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790011.2112067
+ },
+ {
+ "task": "-",
+ "model_a": "gemma2_rag",
+ "model_b": "llama31_rag",
+ "winner": "A",
+ "prob_a": 0.9999038975698405,
+ "prob_b": 9.610243015945067e-05,
+ "evaluation_model": "gpt-4o-mini",
+ "instruction": "사용자: 릭 블러드가 회복 스킬을 가지고 있었나?",
+ "source": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90",
+ "generated_a": "네, 릭 블러드는 패시브2를 통해 액티브 스킬 사용 시 병력을 회복하는 스킬을 가지고 있습니다.",
+ "generated_b": "네, 릭 블러드는 패시브2 스킬로 액티브 스킬 사용 시 병력을 회복하는 능력이 있습니다.",
+ "tournament_idx": 29,
+ "round": "final",
+ "depth": 0,
+ "match_order_in_round": 0,
+ "tstamp": 1732790010.9135613
+ }
+]
\ No newline at end of file
diff --git a/streamlit_app_local/user_submit/rag/gemma2_30lines.jsonl b/streamlit_app_local/user_submit/rag/gemma2_30lines.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..1d76c1dfb0b688fc0f839de987f6ce5a4dbfeecf
--- /dev/null
+++ b/streamlit_app_local/user_submit/rag/gemma2_30lines.jsonl
@@ -0,0 +1,30 @@
+{"model_id": "gemma2_rag", "question": "사용자: 공성 때 왜 로건 더스트가 좋아?", "ground_statements": "근거 문장 0: 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용\n근거 문장 1: 로건 더스트의 공격속도 증가 버프와 공성 전투에서의 스킬 피해량 증가, 방어력 감소 디버프가 유리\n근거 문장 2: 공성 전투 시 스킬이 강화", "generated": "로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용하기 때문에 공성 전투에 유리해."}
+{"model_id": "gemma2_rag", "question": "사용자: 공성 때 왜 로건 더스트가 좋아?", "ground_statements": "근거 문장 0: 강력한 공격 성능과 공성 추가 효과를 가진 특성이 유리하다.\n근거 문장 1: 공격 속도 증가 버프, 공격력 패시브, 공성 전투 시 스킬 피해량 증가, 공성 전투 시 공격력 증가, 방어력 감소 디버프.\n근거 문장 2: 로건더스트 액티브 발동시 대상에게 피해를 주고, 5초간 방어력을 10% 감소\n근거 문장 3: 공성 전투 시 스킬이 강화\n근거 문장 4: 피해량이 증가하고 5초간 대상의 방어력을 12% 감소\n근거 문장 5: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 6: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 7: 공성 전투 중 일반 공격 시 10% 확률로 5초간 스킬 피해량이 3% 증가\n근거 문장 8: 로건더스트 패시브2 발동시 대상에게 협공 시 로건 더스트의 일반 공격 피해량이 증가\n근거 문장 9: 공성 전투 중 액티브 스킬 사용 시, 30% 확률로 주변 5개 아군 기사단의 공격력이 10초 동안 7% 증가", "generated": "로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 능력이 있어서 공성 병기 전투에 적합해."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 공방 올려주는 패시브 캐릭", "ground_statements": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가", "generated": "엘리사의 패시브 스킬은 기사단의 공격력을 증가시키고, 일정 확률로 추가 피해를 입히기 때문에 수성 전투에서 유리할 수 있습니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀", "ground_statements": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가", "generated": "엘리사와 벨리타 조합을 추천해요. 엘리사의 패시브1은 기사단의 공격력을 증가시키고, 벨리타의 패시브1은 스킬 피해량 및 공격력을 증가시켜서 기사단의 전반적인 능력을 강화할 수 있어요."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 공방 올려주는 패시브 캐릭", "ground_statements": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가", "generated": "수루&틴코가 기사단의 공격력을 증가시키고 방어력을 제공해 팀 전체의 생존력을 높여주는 패시브 캐릭터입니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀", "ground_statements": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가", "generated": "수루&틴코가 기사단의 공격력을 증가시키고 방어력을 제공해 팀 전체의 생존력을 높여주는 패시브 스킬을 가지고 있어서 추천해요."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 이동 속도 왜이리 느려?", "ground_statements": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군", "generated": "기사단의 이동 속도는 가장 느린 병종에 맞춰져 있어요. 모든 기사단이 같은 속도로 행군하려면 가장 느린 병종이 모든 기사단에 편성되어야 합니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 빠르게 이동하는 방법좀", "ground_statements": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군", "generated": "기사단을 빠르게 이동시키려면, 기사단을 구성하는 병종 중 가장 느린 병종에 맞춰서 행군해야 합니다. 모든 기사단이 같은 속도로 이동하려면, 가장 느린 병종이 모든 기사단에 편성되어야 합니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "세바스찬이 기사단의 방어력을 증가시켜주는 패시브 스킬을 가지고 있어서 탱킹에 유리해."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "멜린이 기사단의 방어력과 생명력을 올려주는 데 유리해. 멜린 패시브1 발동시 방어력이 7%, 생명력이 6% 증가하거든."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 딴딴하게 하고 싶어", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "기사단을 딴딴하게 하려면, 세바스찬을 사용하는 것이 좋습니다. 세바스찬은 패시브2 스킬로 기사단의 방어력이 증가하여 수성 전투에서 유리합니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "세바스찬이 기사단의 방어력을 올려주는 패시브 스킬을 가지고 있어서 탱킹에 유리해."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "세바스찬이 기사단의 방어력을 올려주는 신의 기사로 추천돼."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 딴딴하게 하고 싶어", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "기사단을 딴딴하게 하려면, 세바스찬을 사용하는 것이 좋습니다. 세바스찬은 패시브2 스킬로 기사단의 방어력이 증가하여 수성 전투에서 유리합니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 다중 편성 필수야?", "ground_statements": "근거 문장이 없습니다.", "generated": "컨텐츠에 따라 다양한 편성이 필요할 수 있습니다. 하나의 부대만으로 충분할 때도 있고, 다중 편성이 최적일 때도 있습니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 기사단 하나만 써도 돼?", "ground_statements": "근거 문장이 없습니다.", "generated": "네, 컨텐츠에 따라 하나의 부대만으로 충분할 때도 있습니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 도미닉 크림슨은 부관 뭐써", "ground_statements": "근거 문장 0: 도미닉 크림슨의 부관으로 좋은 영웅은 릭 블러드, 세이디 굿윈, 아디카, 라엔 & 아토, 로건 더스트, 벨리타, 수루, 날라, 니엘, 아잔, 알렉사 호프, 루실 발렌틴, 멜린 등\n근거 문장 1: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 3: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 4: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 부관으로 멜린, 수루, 아잔, 로건 더스트 등이 있어. 각각의 조합이 특정 전투 상황에 유리해."}
+{"model_id": "gemma2_rag", "question": "사용자: 도미닉 크림슨은 부관 뭐써", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 1: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 3: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 부관으로 멜린을 쓰면 필드 기사단 공격과 수성 전투에 유리해. 멜린은 방어력 감소 디버프와 공격력 및 방어력 증가 패시브 스킬을 가지고 있어서 도미닉 크림슨의 공격력을 극대화할 수 있어."}
+{"model_id": "gemma2_rag", "question": "사용자: 공성 하고싶은데 도미닉 크림슨이랑 누구 같이 쓰는게 제일 좋아?", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 로건 더스트 조합이 공성 전투에 유리해. 로건 더스트의 공성 전투 강화 스킬이 도움이 될 거야."}
+{"model_id": "gemma2_rag", "question": "사용자: 공성 해야하는데 도미닉 크림슨밖에 없어. 누굴 새로 뽑는게 제일 좋을까?", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 로건 더스트 조합이 공성 전투에 유리해. 로건 더스트를 새로 뽑는 게 좋을 것 같아."}
+{"model_id": "gemma2_rag", "question": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?", "ground_statements": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합\n근거 문장 1: 로건 더스트와 부관 프레아 조합은 공성/스웜 전투에 유리\n근거 문장 2: 공성/스웜 전투할 때, 로건 더스트의 공성 전투 시 스킬 피해량 증가와 알렉사 호프의 방어력 및 공격력 감소 디버프는 공성 전투에서 적을 빠르게 무력화할 수 있게 한다.", "generated": "로건 더스트와 알렉사 호프를 같이 데려가면 좋을 것 같아. 알렉사 호프는 방어력 감소와 추가 피해를 주는 효과가 있어서 공성 전투에 유리해."}
+{"model_id": "gemma2_rag", "question": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?", "ground_statements": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합", "generated": "로건 더스트와 함께 데려가면 좋을 부관으로는 루실 발렌틴이 있어요. 루실 발렌틴은 공성 전투 시 공격력 증가 효과가 있어서 공성/스웜 전투에 적합하거든요."}
+{"model_id": "gemma2_rag", "question": "사용자: 로건 더스트 액티브는 언제 세지더라", "ground_statements": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘", "generated": "로건 더스트 액티브는 5초 동안 지속됩니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 로건더스트 쓰다가 언제는 스킬이 세게 들어갔던 것 같은데 왠지 알아?", "ground_statements": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘", "generated": "로건더스트의 스킬은 공성 전투 시 강화되어 피해량이 증가하고, 5초간 대상의 방어력을 12% 감소시키는 효과가 있어서 그런 느낌을 받을 수 있어."}
+{"model_id": "gemma2_rag", "question": "사용자: 부대 병력 줄어들면 손해보는거 있어?", "ground_statements": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소", "generated": "네, 부대의 병력이 적어질 경우 부대의 공격력이 감소합니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 오래 전투하면 상대가 너무 안죽는데 왜이래", "ground_statements": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소", "generated": "리토의 스킬은 대상에게 피해를 주고 이동속도를 감소시키지만, 피격 대상의 이동속도가 12% 감소하는 효과는 상대가 너무 안 죽는 이유일 수 있습니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 서브 뭐있어?", "ground_statements": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적", "generated": "루실 발렌틴과 로건 더스트 조합이 필드 전투에 유리해. 로건 더스트는 반격 피해와 협공 시 공격력 증가 능력이 있어서 적을 빠르게 처치할 수 있고, 루실 발렌틴은 높은 스킬 계수와 방어력 감소 디버프를 제공해."}
+{"model_id": "gemma2_rag", "question": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 부관 추천좀", "ground_statements": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적", "generated": "루실 발렌틴과 로건 더스트 조합이 필드 전투에 유리해. 로건 더스트는 방어력 감소 디버프를 제공하고, 루실 발렌틴은 공격력 증가와 이동속도 증가 패시브로 빠른 전투를 지원해."}
+{"model_id": "gemma2_rag", "question": "사용자: 릭 블러드 병력 회복 계수가 얼마더라.", "ground_statements": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90", "generated": "릭 블러드의 병력 회복 계수는 90입니다."}
+{"model_id": "gemma2_rag", "question": "사용자: 릭 블러드가 회복 스킬을 가지고 있었나?", "ground_statements": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90", "generated": "네, 릭 블러드는 패시브2를 통해 액티브 스킬 사용 시 병력을 회복하는 스킬을 가지고 있습니다."}
diff --git a/streamlit_app_local/user_submit/rag/llama31_30lines.jsonl b/streamlit_app_local/user_submit/rag/llama31_30lines.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..56797a35d1f0be86a393961d49cf7d214d91e782
--- /dev/null
+++ b/streamlit_app_local/user_submit/rag/llama31_30lines.jsonl
@@ -0,0 +1,30 @@
+{"model_id": "llama31_rag", "question": "사용자: 공성 때 왜 로건 더스트가 좋아?", "ground_statements": "근거 문장 0: 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용\n근거 문장 1: 로건 더스트의 공격속도 증가 버프와 공성 전투에서의 스킬 피해량 증가, 방어력 감소 디버프가 유리\n근거 문장 2: 공성 전투 시 스킬이 강화", "generated": "공성 전투에서는 로건 더스트의 스킬이 강화되어 피해량이 증가하고 방어력을 12% 감소시켜서 적을 빠르게 무력화할 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 공성 때 왜 로건 더스트가 좋아?", "ground_statements": "근거 문장 0: 강력한 공격 성능과 공성 추가 효과를 가진 특성이 유리하다.\n근거 문장 1: 공격 속도 증가 버프, 공격력 패시브, 공성 전투 시 스킬 피해량 증가, 공성 전투 시 공격력 증가, 방어력 감소 디버프.\n근거 문장 2: 로건더스트 액티브 발동시 대상에게 피해를 주고, 5초간 방어력을 10% 감소\n근거 문장 3: 공성 전투 시 스킬이 강화\n근거 문장 4: 피해량이 증가하고 5초간 대상의 방어력을 12% 감소\n근거 문장 5: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 6: 로건더스트 패시브1 발동시 기사단의 공격력이 5% 방어력이 7% 증가\n근거 문장 7: 공성 전투 중 일반 공격 시 10% 확률로 5초간 스킬 피해량이 3% 증가\n근거 문장 8: 로건더스트 패시브2 발동시 대상에게 협공 시 로건 더스트의 일반 공격 피해량이 증가\n근거 문장 9: 공성 전투 중 액티브 스킬 사용 시, 30% 확률로 주변 5개 아군 기사단의 공격력이 10초 동안 7% 증가", "generated": "공성 전투에서는 로건 더스트의 스킬 피해량 증가와 방어력 감소 디버프가 강력한 공격 성능과 공성 추가 효과를 제공하여 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 공방 올려주는 패시브 캐릭", "ground_statements": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가", "generated": "엘리사는 기사단의 공격력을 증가시키는 패시브 스킬을 가지고 있어 공방에서 유리할 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀", "ground_statements": "근거 문장 0: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 1: 알렉사 호프의 패시브1 스킬로 기사단의 방어력이 증가하고, 공격력이 증가", "generated": "엘리사와 벨리타 조합이 기사단의 공격력과 방어력을 동시에 올려주는 패시브 스킬을 가지고 있어. 특히 점령이나 대규모 전투에 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 공방 올려주는 패시브 캐릭", "ground_statements": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가", "generated": "수루 & 틴코가 필드 대규모 PVP에서 아군 기사단의 공격력을 증가시키고 방어력을 제공하여 팀 전체의 생존력을 높여줍니다."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 공격력 방어력 같이 올려주는 패시브 스킬 가진 캐릭터 추천좀", "ground_statements": "근거 문장 0: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가", "generated": "수루 & 틴코가 필드 대규모 PVP에서 아군 기사단의 공격력을 증가시키고 방어력을 제공해줘서 생존력을 높이는 데 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 이동 속도 왜이리 느려?", "ground_statements": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군", "generated": "기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군하기 때문에 느려."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 빠르게 이동하는 방법좀", "ground_statements": "근거 문장 0: 이동은 기사단을 구성하는 부대 기준으로 가장 느린 병종에 맞춥니다\n근거 문장 1: 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군\n근거 문장 2: 각 기사단은 이동 속도가 가장 느린 병종에 맞추어서 행군", "generated": "기사단의 이동 속도를 빠르게 하려면, 젬비의 액티브 스킬을 사용하면 5초간 이동속도가 추가로 6% 증가합니다."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "세바스찬이 패시브2 스킬로 기사단의 방어력을 6% 증가시켜서 탱킹에 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "세바스찬이랑 멜린을 추천해. 세바스찬은 패시브2 스킬로 기사단의 방어력을 올려주고, 멜린은 패시브1 스킬로 방어력과 생명력을 올려줘."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 딴딴하게 하고 싶어", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 멜린 패시브1 발동시 기사단의 방어력이 7%, 생명력이 6% 증가\n근거 문장 3: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가\n근거 문장 4: 타이욘의 패시브 스킬로 인해 기사단의 공격력과 방어력이 증가\n근거 문장 5: 회복 및 생존 능력이 뛰어나", "generated": "세바스찬은 패시브2 스킬로 기사단의 방어력을 증가시켜서 딴딴하게 만들 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 탱킹 올려주는 패시브 가진 신의 기사 추천좀.", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "세바스찬이 패시브2 스킬로 기사단의 방어력을 6% 증가시켜서 탱킹에 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 방어력이랑 생명력 올려주는 신의 기사 추천좀", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "세바스찬이 기사단의 방어력을 올려주는 패시브2 스킬이 있어서 수성 전투나 대규모 PVP에서 유리할 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 딴딴하게 하고 싶어", "ground_statements": "근거 문장 0: 세바스찬은 수성 전투할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 1: 세바스찬은 필드 대규모 PVP할 때, 패시브2 스킬로 기사단의 방어력이 증가\n근거 문장 2: 세바스찬 패시브2 발동시 기사단의 방어력이 6% 증가", "generated": "세바스찬은 패시브2 스킬로 기사단의 방어력을 증가시켜서 딴딴하게 만들 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 다중 편성 필수야?", "ground_statements": "근거 문장이 없습니다.", "generated": "아니, 다중 편성이 최적일 경우가 있지만, 하나의 부대만으로 충분할 경우도 있습니다."}
+{"model_id": "llama31_rag", "question": "사용자: 기사단 하나만 써도 돼?", "ground_statements": "근거 문장이 없습니다.", "generated": "네, 컨텐츠에 따라 하나의 부대만으로 충분할 경우가 있습니다."}
+{"model_id": "llama31_rag", "question": "사용자: 도미닉 크림슨은 부관 뭐써", "ground_statements": "근거 문장 0: 도미닉 크림슨의 부관으로 좋은 영웅은 릭 블러드, 세이디 굿윈, 아디카, 라엔 & 아토, 로건 더스트, 벨리타, 수루, 날라, 니엘, 아잔, 알렉사 호프, 루실 발렌틴, 멜린 등\n근거 문장 1: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 3: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 4: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨의 부관으로는 릭 블러드, 세이디 굿윈, 아디카, 라엔 & 아토, 로건 더스트, 벨리타, 수루, 날라, 니엘, 아잔, 알렉사 호프, 루실 발렌틴, 멜린 등이 있습니다."}
+{"model_id": "llama31_rag", "question": "사용자: 도미닉 크림슨은 부관 뭐써", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 멜린 조합은 필드 기사단 공격, 수성 전투에 유리\n근거 문장 1: 도미닉 크림슨와 부관 수루 조합은 병기 수송 중 전투, 공성 병기 상태 전투에 유리\n근거 문장 2: 도미닉 크림슨와 부관 아잔 조합은 몬스터 사냥, 길드 건물 건설에 유리\n근거 문장 3: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨은 멜린, 수루, 아잔, 로건 더스트와 같은 다양한 부관과 조합할 수 있어. 각 조합은 전투 상황에 따라 다르게 활용할 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 공성 하고싶은데 도미닉 크림슨이랑 누구 같이 쓰는게 제일 좋아?", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 로건 더스트 조합이 공성/스웜 전투에 매우 유리해. 도미닉 크림슨의 스킬 피해량 증가와 생명력 증가, 로건 더스트의 공성 전투 시 공격력 증가 덕분에 공성에 아주 잘 맞아."}
+{"model_id": "llama31_rag", "question": "사용자: 공성 해야하는데 도미닉 크림슨밖에 없어. 누굴 새로 뽑는게 제일 좋을까?", "ground_statements": "근거 문장 0: 도미닉 크림슨와 부관 로건 더스트 조합은 점령, 필드 대규모 PVP, 공성/스웜 전투에 유리", "generated": "도미닉 크림슨과 로건 더스트 조합이 공성/스웜 전투에 매우 유리해. 도미닉 크림슨의 스킬 피해량 증가와 생명력 증가, 로건 더스트의 공성 전투 시 공격력 증가가 공성에 아주 좋아."}
+{"model_id": "llama31_rag", "question": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?", "ground_statements": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합\n근거 문장 1: 로건 더스트와 부관 프레아 조합은 공성/스웜 전투에 유리\n근거 문장 2: 공성/스웜 전투할 때, 로건 더스트의 공성 전투 시 스킬 피해량 증가와 알렉사 호프의 방어력 및 공격력 감소 디버프는 공성 전투에서 적을 빠르게 무력화할 수 있게 한다.", "generated": "공성 전투에서는 로건 더스트와 알렉사 호프 조합이 좋아. 로건 더스트의 스킬 피해량 증가랑 방어력 감소 디버프가 공성에 유리하고, 알렉사 호프의 추가 피해 효과도 큰 도움이 돼."}
+{"model_id": "llama31_rag", "question": "사용자: 로건 더스트 공성 때 쓰려는데 누구 같이 데려갈까?", "ground_statements": "근거 문장 0: 공성/스웜 전투할 때, 로건 더스트는 공성 전투 시 스킬 피해량이 증가하고 방어력을 감소시키는 디버프를 적용합니다. 루실 발렌틴은 협공 시 공격력 증가 효과가 있어 공성/스웜 전투에 적합", "generated": "로건 더스트와 함께 공성 전투에선 루실 발렌틴을 데려가면 좋을 것 같아. 루실 발렌틴은 속도 증가와 추가 피해 효과가 있어서 공성 전투에 유리해."}
+{"model_id": "llama31_rag", "question": "사용자: 로건 더스트 액티브는 언제 세지더라", "ground_statements": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘", "generated": "로건 더스트 액티브 스킬은 발동 시 대상에게 피해를 주고, 5초간 방어력을 10% 감소시키는 효과가 있습니다."}
+{"model_id": "llama31_rag", "question": "사용자: 로건더스트 쓰다가 언제는 스킬이 세게 들어갔던 것 같은데 왠지 알아?", "ground_statements": "근거 문장 0: 공성 전투 시 스킬이 강화\n근거 문장 1: 공성 전투 시 스킬이 강화\n근거 문장 2: 로건 더스트는 액티브 스킬로 방어력을 감소시키고 공격력을 증가시키는 효과를 제공하며, 공성 전투에서 더욱 강화된 효과를 발휘", "generated": "공성 전투 중 액티브 스킬 사용 시, 30% 확률로 주변 5개 아군 기사단의 공격력이 10초 동안 7% 증가하는 패시브2가 발동할 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 부대 병력 줄어들면 손해보는거 있어?", "ground_statements": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소", "generated": "전투 중 병력이 적어질 때 부대의 공격력이 감소하니 주의해야 해."}
+{"model_id": "llama31_rag", "question": "사용자: 오래 전투하면 상대가 너무 안죽는데 왜이래", "ground_statements": "근거 문장 0: 병력이 적어질 시 부대의 공격력이 감소", "generated": "전투 중 병력이 적어질 때 부대의 공격력이 감소하는 문제가 있을 수 있어."}
+{"model_id": "llama31_rag", "question": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 서브 뭐있어?", "ground_statements": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적", "generated": "루실 발렌틴은 필드 전투에서 다양한 서브와 시너지를 발휘할 수 있어. 예를 들어, 루실 발렌틴과 로건 더스트 조합은 필드 기사단 공격에 매우 효과적이야. 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 반격 피해와 협공 시 공격력 증가 능력이 필드 전투에서 큰 이점을 줘."}
+{"model_id": "llama31_rag", "question": "사용자: 루실 발렌틴이랑 필드 전투 시너지 좋은 부관 추천좀", "ground_statements": "근거 문장 0: 루실 발렌틴와 부관 수루 조합은 필드 소규모 PVP에 유리\n근거 문장 1: 루실 발렌틴와 부관 리토 조합은 필드 소규모 PVP, 마법 병기 상태 전투에 유리\n근거 문장 2: 필드 소규모 PVP할 때, 루실 발렌틴의 속도 증가와 피격 시 방어력 감소 효과, 로건 더스트의 협공시 피해 증가 효과로 전투 효율이 높습니다.\n근거 문장 3: 루실 발렌틴와 부관 젬비 조합은 필드 기사단 공격에 유리\n근거 문장 4: 루실 발렌틴와 부관 벨리타 조합은 연맹 건물 건설, 병기 수송 중 전투, 필드 대규모 PVP에 유리\n근거 문장 5: 필드 기사단 공격할 때, 루실 발렌틴의 높은 스킬 계수와 로건 더스트의 공격력 및 방어력 감소 디버프가 필드 기사단 공격에 효과적", "generated": "루실 발렌틴과 필드 전투 시너지 좋은 부관으로는 수루, 리토, 젬비, 벨리타가 있습니다. 수루는 필드 소규모 PVP에, 리토는 마법 병기 상태 전투에, 젬비는 필드 기사단 공격에, 벨리타는 연맹 건물 건설이나 병기 수송 중 전투에 유리합니다."}
+{"model_id": "llama31_rag", "question": "사용자: 릭 블러드 병력 회복 계수가 얼마더라.", "ground_statements": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90", "generated": "릭 블러드의 병력 회복 계수는 90이야."}
+{"model_id": "llama31_rag", "question": "사용자: 릭 블러드가 회복 스킬을 가지고 있었나?", "ground_statements": "근거 문장 0: 릭블러드 패시브2 발동시 액티브 스킬 사용 시 병력을 회복하며, 적중한 대상 수에 따라 회복량이 증가합니다. (회복 계수 90", "generated": "네, 릭 블러드는 패시브2 스킬로 액티브 스킬 사용 시 병력을 회복하는 능력이 있습니다."}
diff --git a/streamlit_app_local/varco_arena_core b/streamlit_app_local/varco_arena_core
new file mode 120000
index 0000000000000000000000000000000000000000..2774064f1dfebcbf9625825dd70ffa1af0e6e239
--- /dev/null
+++ b/streamlit_app_local/varco_arena_core
@@ -0,0 +1 @@
+../varco_arena/varco_arena_core/
\ No newline at end of file
diff --git a/streamlit_app_local/view_utils.py b/streamlit_app_local/view_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..7db5e30cdb7fda6856d88956e9ad3bf2afd128ba
--- /dev/null
+++ b/streamlit_app_local/view_utils.py
@@ -0,0 +1,413 @@
+import math
+import re
+from typing import *
+
+import numpy as np
+import pandas as pd
+import plotly.express as px
+import plotly.graph_objects as go
+import streamlit as st
+from sklearn.linear_model import LogisticRegression
+
+from modules.nav import Navbar
+
+
+# page related utils
+def default_page_setting(
+ layout: Literal["wide", "centered"] = "centered",
+):
+ st.set_page_config(page_title="VARCO Arena", layout=layout)
+ sidebar_placeholder = st.sidebar.empty()
+
+ css = f"""
+
+ """
+ st.markdown(css, unsafe_allow_html=True)
+ if "korean" not in st.session_state:
+ st.session_state["korean"] = False
+ return sidebar_placeholder
+
+
+# Function to update is_running and refresh only the sidebar
+def set_nav_bar(is_running: bool, sidebar_placeholder=None, toggle_hashstr: str = None):
+ st.session_state["is_running"] = is_running
+ # Refresh only the sidebar content
+ Navbar(sidebar_placeholder, toggle_hashstr=toggle_hashstr)
+
+
+def set_prompt_preview(did_select_prompt: bool, expander_placeholder=None):
+ st.session_state["did_select_prompt"] = did_select_prompt
+
+
+def show_linebreak_in_md(text: str) -> str:
+ return text.replace("\n", " \n") if isinstance(text, str) else "(Empty)"
+
+
+def escape_markdown(text: str, version: int = 2, entity_type: str = None) -> str:
+ """
+ Helper function to escape telegram markup symbols.
+
+ Args:
+ text (:obj:`str`): The text.
+ version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown.
+ Either ``1`` or ``2``. Defaults to ``1``.
+ entity_type (:obj:`str`, optional): For the entity types ``PRE``, ``CODE`` and the link
+ part of ``TEXT_LINKS``, only certain characters need to be escaped in ``MarkdownV2``.
+ See the official API documentation for details. Only valid in combination with
+ ``version=2``, will be ignored else.
+ """
+ if int(version) == 1:
+ escape_chars = r"_*`["
+ elif int(version) == 2:
+ if entity_type in ["pre", "code"]:
+ escape_chars = r"\`"
+ elif entity_type == "text_link":
+ escape_chars = r"\)"
+ else:
+ escape_chars = r"_*[]()~`>#+-=|{}.!:"
+ else:
+ raise ValueError("Markdown version must be either 1 or 2!")
+
+ return re.sub(f"([{re.escape(escape_chars)}])", r"\\\1", text)
+
+
+# Elo result related computes
+def compute_relative_winrate_to_1st(elo_df, float_pts: int = 3):
+ """
+ Post-processing utility for saving elo table to an excel file. Possibly work as a absolute measure for quality.
+
+ elo_df:
+ columns: Model, Elo rating
+
+ add:
+ column: relative_winrate_to_1st
+ """
+ from functools import partial
+
+ rating1st = elo_df["Elo rating"].max()
+ win_rate_to_1st = partial(elo_to_winrate, rating_b=rating1st)
+ elo_df["winrate_vs_1st"] = elo_df["Elo rating"].apply(win_rate_to_1st)
+
+ return elo_df
+
+
+def elo_to_winrate(rating_a: float = None, rating_b: float = None) -> float:
+ # compute P(A wins B) from ratings
+ rate_diff = rating_a - rating_b
+ win_rate = 1 / (1 + 10 ** (-rate_diff / 400))
+ return win_rate
+
+
+def compute_mle_elo(df, SCALE=400, BASE=10, INIT_RATING=1000):
+ if isinstance(df, list):
+ df = pd.DataFrame(df)
+ df = df.dropna(subset=["winner", "model_a", "model_b"]) # dropping None vs sth
+
+ models = pd.concat([df["model_a"], df["model_b"]]).unique()
+ models = pd.Series(np.arange(len(models)), index=models)
+
+ # duplicate battles
+ df = pd.concat([df, df], ignore_index=True)
+ p = len(models.index)
+ n = df.shape[0]
+
+ X = np.zeros([n, p])
+ X[np.arange(n), models[df["model_a"]]] = +math.log(BASE)
+ X[np.arange(n), models[df["model_b"]]] = -math.log(BASE)
+
+ # one A win => two A win
+ Y = np.zeros(n)
+ Y[df["winner"] == "A"] = 1.0
+
+ WARNING = "elo.py:L{L} compute_mle_elo() // Warning: Seeing this message indicates the regression result for elo is unreliable. You should be test-running the Varco Arena or something odd (perfect one-sided wins) is happening\n\nto avoid logistic regressor error, manually putting other class"
+ if (Y == 0).all():
+ print(WARNING.format(L=32))
+ Y[-1] = 1.0
+ elif (Y == 1.0).all():
+ print(WARNING.format(L=35))
+ Y[-1] = 0.0
+
+ lr = LogisticRegression(fit_intercept=False)
+ lr.fit(X, Y)
+
+ elo_scores = SCALE * lr.coef_[0] + INIT_RATING
+
+ elo_scores = pd.Series(elo_scores, index=models.index).sort_values(ascending=False)
+
+ df = (
+ pd.DataFrame(
+ [[n, round(elo_scores[n], 2)] for n in elo_scores.keys()],
+ columns=["Model", "Elo rating"],
+ )
+ .sort_values("Elo rating", ascending=False)
+ .reset_index(drop=True)
+ )
+ df.index = df.index + 1
+
+ return df
+
+
+def fill_missing_values(df, default_value=0):
+ """
+ This is used for completing pivot table
+ """
+ # 기존 인덱스와 컬럼을 가져옵니다.
+ existing_index = set(df.index)
+ existing_columns = set(df.columns)
+
+ # 모든 가능한 인덱스와 컬럼을 가져옵니다.
+ all_index = set(df.index.union(df.columns))
+ all_columns = set(df.index.union(df.columns))
+
+ # 기본값으로 누락된 행과 열을 채웁니다.
+ missing_index = all_index - existing_index
+ missing_columns = all_columns - existing_columns
+
+ # 누락된 행을 기본값으로 추가합니다.
+ for idx in missing_index:
+ df.loc[idx] = default_value
+
+ # 누락된 열을 기본값으로 추가합니다.
+ for col in missing_columns:
+ df[col] = default_value
+
+ # 인덱스와 컬럼을 다시 정렬합니다.
+ df.sort_index(axis=0, inplace=True)
+ df.sort_index(axis=1, inplace=True)
+
+ return df
+
+
+def _plot_length_bias(results, judgename: str = None, ratio: bool = True):
+ if not isinstance(results, pd.DataFrame):
+ results = pd.DataFrame.from_dict(results)
+
+ if ratio:
+
+ def _win_to_loss_wc_ratio(row):
+ try:
+ if row.winner == "A":
+ ratio = len(row.generated_a.split()) / len(row.generated_b.split())
+ else:
+ ratio = len(row.generated_b.split()) / len(row.generated_a.split())
+ except Exception as e:
+ ratio = None
+ return ratio
+
+ df = results
+ df["ratio"] = df.apply(_win_to_loss_wc_ratio, axis=1)
+ df["category"] = "win/loss wc ratio"
+
+ # Create the box plot
+ plot_df = df.drop(
+ columns=[col for col in df if col not in ["category", "ratio"]]
+ )
+ fig = px.violin(
+ plot_df,
+ x="category",
+ y="ratio",
+ # log_y=True,
+ title=f"Length bias ({judgename})",
+ # labels={"category": "win/loss wc ratio", "ratio": "ratio"},
+ )
+
+ else:
+ data = []
+ for _, row in results.iterrows():
+ data.append(
+ {
+ "category": "won",
+ "wordcounts": len(row.generated_a.split())
+ if row["winner"] == "A"
+ else len(row.generated_b.split()),
+ }
+ )
+ data.append(
+ {
+ "category": "lost",
+ "wordcounts": len(row.generated_b.split())
+ if row["winner"] == "A"
+ else len(row.generated_a.split()),
+ }
+ )
+ data.append(
+ {
+ "category": "won/lost ratio",
+ "wordcounts": len(row.generated_a.split())
+ / len(row.generated_b.split()) # a won
+ if row["winner"] == "A"
+ else len(row.generated_b.split())
+ / len(row.generated_a.split()), # b won
+ }
+ )
+
+ plot_df = pd.DataFrame(data)
+
+ # Create the box plot
+ fig = px.violin(
+ plot_df,
+ x="category",
+ y="wordcounts",
+ # log_y=True,
+ title=f"Length bias ({judgename})",
+ labels={"category": "outcome", "wordcount": "wordcount"},
+ )
+
+ return fig, plot_df
+
+
+def visualization(results, is_overall=False):
+ """
+ varco_arena/visualization.py 로부터 가져온 함수이나 업데이트가 많이 되었으므로 조심!
+ """
+ if not isinstance(results, pd.DataFrame):
+ results = pd.DataFrame.from_dict(results)
+
+ figure_dict = {}
+ judgename = results.iloc[0]["evaluation_model"]
+
+ # judge bias of length
+ fig, plot_df = _plot_length_bias(results, judgename=judgename)
+ figure_dict["length_bias"] = fig
+ figure_dict["length_bias_df"] = plot_df
+
+ # Judge bias of Position A/B
+ fig = px.bar(
+ results["winner"].value_counts(),
+ title=f"Position A/B bias\n({judgename})",
+ text_auto=True,
+ height=400,
+ )
+ fig.update_layout(xaxis_title="Match Winner", yaxis_title="Count", showlegend=False)
+ figure_dict["counts_of_match_winners"] = fig
+
+ # Num. matches of each model
+ fig = px.bar(
+ pd.concat([results["model_a"], results["model_b"]]).value_counts(),
+ title="Match Count per Model",
+ text_auto=True,
+ )
+ fig.update_layout(
+ xaxis_title="Model", yaxis_title="Match Count", height=400, showlegend=False
+ )
+ figure_dict["match_count_for_each_model"] = fig
+
+ # Num. matches matrix (model v. model)
+ ptbl = pd.pivot_table(
+ results,
+ index="model_a",
+ columns="model_b",
+ aggfunc="size",
+ fill_value=0,
+ )
+ match_counts = ptbl + ptbl.T
+ ordering = match_counts.sum().sort_values(ascending=False).index
+ fig = px.imshow(
+ match_counts.loc[ordering, ordering],
+ title="Number of Matches (model vs. model)",
+ text_auto=True,
+ )
+ fig.update_layout(
+ xaxis_title="Model B",
+ yaxis_title="Model A",
+ xaxis_side="top",
+ height=800,
+ width=800,
+ title_xanchor="left",
+ title_yanchor="top",
+ font=dict(size=10),
+ )
+ fig.update_traces(
+ hovertemplate="Model A: %{y} Model B: %{x} Count: %{z}"
+ )
+ figure_dict["match_count_of_each_combination_of_models"] = fig
+
+ # Win rate matrix (model v. model)
+ a_win_ptbl = pd.pivot_table(
+ results[results["winner"] == "A"],
+ index="model_a",
+ columns="model_b",
+ aggfunc="size",
+ fill_value=0,
+ )
+ a_win_ptbl = fill_missing_values(a_win_ptbl)
+ b_win_ptbl = pd.pivot_table(
+ results[results["winner"] == "B"],
+ index="model_a",
+ columns="model_b",
+ aggfunc="size",
+ fill_value=0,
+ )
+ b_win_ptbl = fill_missing_values(b_win_ptbl)
+ num_results_ptbl = pd.pivot_table(
+ results, index="model_a", columns="model_b", aggfunc="size", fill_value=0
+ )
+
+ row_beats_col_freq = (a_win_ptbl + b_win_ptbl.T) / (
+ num_results_ptbl + num_results_ptbl.T
+ )
+ prop_wins = row_beats_col_freq.mean(axis=1).sort_values(ascending=False)
+ model_names = list(prop_wins.keys())
+
+ row_beats_col = row_beats_col_freq.loc[model_names, model_names]
+ fig = px.imshow(
+ row_beats_col,
+ color_continuous_scale="RdBu",
+ text_auto=".2f",
+ title="P(A wins B)",
+ )
+ fig.update_layout(
+ xaxis_title="Model B",
+ yaxis_title="Model A", # y axis = row = index
+ title_xanchor="left",
+ title_yanchor="top",
+ xaxis_side="top",
+ height=800,
+ width=800,
+ )
+ fig.update_traces(
+ hovertemplate="Model A: %{y} Model B: %{x} P(A wins B): %{z}"
+ )
+ figure_dict["fraction_of_model_a_wins_for_all_a_vs_b_matches"] = fig
+
+ # Elo Rating
+ elo = compute_mle_elo(results)
+ elo_wr = compute_relative_winrate_to_1st(elo)
+ # beautify
+ elo_wr["Elo rating"] = elo_wr["Elo rating"].astype(int)
+ elo_wr["winrate_vs_1st"] = elo_wr["winrate_vs_1st"].round(3)
+ elo_wr.index.name = "Rank"
+
+ figure_dict["elo_rating"] = elo_wr
+
+ # Elo Rating by Task: Radar chart
+ if is_overall:
+ tasks = results["task"].unique().tolist()
+ elo_by_task = pd.concat(
+ [
+ compute_mle_elo(results[results["task"] == task]).assign(task=task)
+ for task in tasks
+ ]
+ )
+ fig = px.line_polar(
+ elo_by_task,
+ r="Elo rating",
+ theta="task",
+ line_close=True,
+ category_orders={"task": tasks},
+ color="Model",
+ markers=True,
+ color_discrete_sequence=px.colors.qualitative.Pastel,
+ title="Elo Rating by Task",
+ )
+ figure_dict["elo_rating_by_task"] = fig
+ figure_dict["judgename"] = judgename
+
+ return figure_dict
diff --git a/varco_arena/.vscode/launch.json b/varco_arena/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..018b5d05ca5da6406643d8aacd04355a3837fe8e
--- /dev/null
+++ b/varco_arena/.vscode/launch.json
@@ -0,0 +1,113 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "va dbg trans",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "/home/deftson/nfs-deftson/2024/public_varco_arena/varco_arena/",
+ "program": "main.py",
+ "args": [
+ "-i", "rsc/inputs_for_dbg/dbg_trans_inputs/",
+ "-o", "rsc/results/dbg_trans/",
+ "-e", "gpt-4o-mini",
+ "-p", "translation_pair",
+ ]
+ "console": "integratedTerminal"
+ },
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "va dbg llmbar_brief",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "/home/deftson/nfs-deftson/2024/public_varco_arena/varco_arena/",
+ "program": "main.py",
+ "args": [
+ "-i", "rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/",
+ "-o", "rsc/results/dbg_llmbar_brief/",
+ "-e", "gpt-4o-mini",
+ "-p", "llmbar_brief",
+ ]
+ "console": "integratedTerminal"
+ },
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "va dbg llmbar",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "/home/deftson/nfs-deftson/2024/public_varco_arena/varco_arena/",
+ "program": "main.py",
+ "args": [
+ "-i", "rsc/inputs_for_dbg/dbg_llmbar_inputs/",
+ "-o", "rsc/results/dbg_llmbar/",
+ "-e", "gpt-4o-mini",
+ "-p", "llmbar",
+ ]
+ "console": "integratedTerminal"
+ },
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "va dbg rag",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "/home/deftson/nfs-deftson/2024/public_varco_arena/varco_arena/",
+ "program": "main.py",
+ "args": [
+ "-i", "rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/",
+ // "-i", "streamlit_app_components/user_submit/10-18_18:01:59/",
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/",
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/custom*.jsonl",
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/normal*.jsonl",
+ "-o", "rsc/results/dbg_mhlee/",
+ "-e", "gpt-4o-mini",
+ "-p", "rag_pair_kr",
+ ]
+ "console": "integratedTerminal"
+ },
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "va dbg dup modelid",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "/home/deftson/nfs-deftson/2024/public_varco_arena/varco_arena/",
+ "program": "main.py",
+ "args": [
+ "-i", "rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/",
+ "-o", "rsc/results/dbg_dup_model_id/",
+ "-e", "gpt-4o-mini",
+ "-p", "llmbar_brief",
+ ]
+ "console": "integratedTerminal"
+ },
+ {
+ "env": {
+ // "SOMEVAR": "SOMEVAL",
+ }
+ "name": "Current File:Py",
+ "type": "debugpy",
+ "request": "launch",
+ "cwd": "${fileDirname}",
+ "program": "${file}",
+ "args": [
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/",
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/custom*.jsonl",
+ // "-i", "rsc/inputs_for_dbg/dbg_rag_inputs/normal*.jsonl",
+ ]
+ "console": "integratedTerminal"
+ },
+ ]
+}
diff --git a/varco_arena/.vscode/settings.json b/varco_arena/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..3187fb613a43c3ce9856d95ce6cd9c487c973f2c
--- /dev/null
+++ b/varco_arena/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "tabby.api.endpoint": "http://172.20.93.21:22624",
+ "python.analysis.typeCheckingMode": "basic",
+ "python.analysis.autoImportCompletions": true
+}
diff --git a/varco_arena/README.md b/varco_arena/README.md
new file mode 120000
index 0000000000000000000000000000000000000000..13c4f964bb9063f28d6e08dfb8c6b828a81d2536
--- /dev/null
+++ b/varco_arena/README.md
@@ -0,0 +1 @@
+README_en.md
\ No newline at end of file
diff --git a/varco_arena/README_en.md b/varco_arena/README_en.md
new file mode 120000
index 0000000000000000000000000000000000000000..32d46ee883b58d6a383eed06eb98f33aa6530ded
--- /dev/null
+++ b/varco_arena/README_en.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
diff --git a/varco_arena/README_kr.md b/varco_arena/README_kr.md
new file mode 120000
index 0000000000000000000000000000000000000000..0fa04167e83a591ae82ce72c0b3bd598ced96249
--- /dev/null
+++ b/varco_arena/README_kr.md
@@ -0,0 +1 @@
+../README_kr.md
\ No newline at end of file
diff --git a/varco_arena/__pycache__/calc_cost.cpython-311.pyc b/varco_arena/__pycache__/calc_cost.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d96fb1ac4267b1b3fd7b46a2fa943c6c282d1243
Binary files /dev/null and b/varco_arena/__pycache__/calc_cost.cpython-311.pyc differ
diff --git a/varco_arena/__pycache__/calc_cost.cpython-38.pyc b/varco_arena/__pycache__/calc_cost.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ac273f7ece29561620689cece6a1f025ab63cd80
Binary files /dev/null and b/varco_arena/__pycache__/calc_cost.cpython-38.pyc differ
diff --git a/varco_arena/calc_cost.py b/varco_arena/calc_cost.py
new file mode 100644
index 0000000000000000000000000000000000000000..13d525f5df9cad32513cccac608e6507ab4c2a47
--- /dev/null
+++ b/varco_arena/calc_cost.py
@@ -0,0 +1,139 @@
+from typing import Dict, List, Literal
+
+import pandas as pd
+from varco_arena_core.prompts import load_prompt
+from varco_arena_core.tracking_utils import pricing
+
+
+def calculate(
+ dataset_df: pd.DataFrame = None,
+ model_name: str = None,
+ matching_method: Literal["tournament", "league"] = None,
+ evalprompt: str = None,
+):
+ # same tournament hashed with "tournament_idx" column in data_utils.py:load_all_data()
+ df = dataset_df
+
+ # estimate the number of total required tokens
+ """
+ total_toks = num_matches * avg_num_toks
+ = num_matches * (prompt + output)
+ = num_matches * ((inst + src + gen_a + gen_b) + output)
+
+ if "tournament":
+ num_matches = (n_participants - 1) * n_testset
+ elif "league":
+ num_matches = combination(n_participants, 2) * n_testset
+
+ """
+ # n_testset, n_models
+ n_testset = len(df.tournament_idx.unique())
+ n_models = len(df.model_id.unique())
+
+ # num_matches
+ n_participants = len(df.model_id.unique())
+ if matching_method == "tournament":
+ num_matches = n_participants - 1
+ elif matching_method == "league":
+ num_matches = n_participants * (n_participants - 1) / 2
+ else:
+ raise ValueError(
+ f"{matching_method=} is undefined! Should be in [tournament, league]"
+ )
+
+ # load prompt objects of use for later below
+ eval_task_2_prm = dict()
+ tasks = df.task.unique().tolist()
+ for task in tasks:
+ eval_task_2_prm[f"{evalprompt}_{task}"] = load_prompt(evalprompt, task=task)
+
+ # num_generated_tokens / model
+ generateds = df.apply(
+ lambda row: eval_task_2_prm[
+ f"{evalprompt}_{row.task}"
+ ].get_expected_max_tokens_w_room(
+ model_name, room=1.01
+ ), # here, prompt_obj will define tokenizer with `model_name`
+ axis=1,
+ )
+ # assert len(generateds) == n_testset * n_models, f"{len(generateds)=}, {n_testset=}, {n_models=}"
+ gen_tokens = generateds.sum() / n_models
+ # num_queried_tokens / model
+ """
+ we don't know what model's output will proceeds to the finalist matches, so we need to approximate.
+
+ let's use average of per-prompt LLM's generated tokens.
+ """
+ df["approximate_match_prompts"] = df.apply(
+ lambda row: eval_task_2_prm[f"{evalprompt}_{row.task}"].complete_prompt(
+ inst=row.instruction,
+ src=row.source,
+ out_a=row.generated,
+ out_b=row.generated,
+ task=row.task,
+ ),
+ axis=1,
+ ) # all the llm responses appears uniformly (not realistic)
+
+ query_tokens = (
+ df.apply(
+ lambda row: eval_task_2_prm[
+ f"{evalprompt}_{row.task}"
+ ].get_num_tokens_from_messages(row.approximate_match_prompts),
+ axis=1,
+ ).sum()
+ / n_models
+ )
+
+ # to total tokens:
+ total_num_matches = n_testset * num_matches
+ total_num_prompt_tokens = query_tokens * (n_models - 1)
+ total_num_completion_tokens = gen_tokens * (n_models - 1)
+
+ total_cost = cost_in_usd(
+ model_name, total_num_prompt_tokens, total_num_completion_tokens
+ )
+
+ return (
+ total_num_matches,
+ int(total_num_prompt_tokens),
+ int(total_num_completion_tokens),
+ total_cost,
+ )
+
+
+def num_tokens_from_messages(messages, tokenizer):
+ if tokenizer is None:
+ return 0
+
+ tokens_per_message = 3
+ tokens_per_name = 1
+
+ num_tokens = 0
+ for message in messages:
+ num_tokens += tokens_per_message
+ for key, value in message.items():
+ num_tokens += len(tokenizer.encode(value))
+ if key == "name":
+ num_tokens += tokens_per_name
+ num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
+ return num_tokens
+
+
+def num_tokens_from_string(string, tokenizer):
+ if tokenizer is None:
+ return 0
+ return len(tokenizer.encode(string))
+
+
+def cost_in_usd(model_name, num_prompt_tokens, num_completion_tokens):
+ # Check if the provided model is in the pricing dictionary
+ if model_name not in pricing:
+ return 0.0
+
+ # Calculate the cost in USD for input and output tokens separately
+ cost_in_usd = (num_prompt_tokens / 1_000_000) * pricing[model_name]["input"] + (
+ num_completion_tokens / 1_000_000
+ ) * pricing[model_name]["output"]
+
+ return cost_in_usd
diff --git a/varco_arena/data_convert.py b/varco_arena/data_convert.py
new file mode 100644
index 0000000000000000000000000000000000000000..97eadf3eeec270f2096ddfa474003de6ad5d87ef
--- /dev/null
+++ b/varco_arena/data_convert.py
@@ -0,0 +1,109 @@
+import argparse
+import json
+import os
+import re
+from typing import Dict, List
+
+
+def list_directories(path):
+ # 지정된 경로에 있는 항목들을 리스트로 받아옴
+ items = os.listdir(path)
+
+ # 항목들 중에서 디렉토리(폴더)만을 필터링
+ directories = [item for item in items if os.path.isdir(os.path.join(path, item))]
+
+ return directories
+
+
+def remove_control_tokens_openchat(txt: str) -> str:
+ return (
+ txt.replace("GPT4 User:", "")
+ .replace("[EOS]GPT4 Assistant:", "")
+ .replace("GPT4 Assistant:", "")
+ .strip()
+ )
+
+
+# path 에 있는 result.json 파일 읽어서 전처리된 instance들로 만든다.
+def result_file_process(model, task, path):
+ with open(path, encoding="utf8") as f:
+ instances: List[Dict] = json.loads(f.read())
+ processed_instances = []
+ for instance in instances:
+ source = remove_control_tokens_openchat(instance["source"])
+ if "instruction" in instance.keys():
+ instruction = instance["instruction"]
+ else:
+ instruction, source = source, ""
+
+ processed_instances.append(
+ {
+ "model_id": model,
+ "task": task,
+ "instruction": instruction.strip(),
+ "source": source.strip(),
+ "generated": instance["generated_result"],
+ }
+ )
+ return processed_instances
+
+
+# model results 디렉토리에서 결과값 변환 작업
+def transform_results_folder(input_path, output_path, model_name_pattern):
+ regex_pattern = re.compile(model_name_pattern)
+
+ tasks = list_directories(input_path)
+ models = list_directories(os.path.join(input_path, tasks[0]))
+ models = [model for model in models if regex_pattern.match(model)]
+
+ model_results = {}
+ print(f"TASKS: {tasks}")
+ print(f"MODELS: {models}")
+ for task in tasks:
+ models = [
+ model
+ for model in list_directories(os.path.join(input_path, task))
+ if regex_pattern.match(model)
+ ]
+ for model in models:
+ result_path = os.path.join(input_path, task, model, "result.json")
+ model_name = model
+ if task in model:
+ model_name = model.split(f"-{task}-")[0]
+ instances = result_file_process(model_name, task, result_path)
+
+ if model_name in model_results.keys():
+ model_results[model_name] += instances
+ else:
+ model_results[model_name] = instances
+
+ print(f"{task} results processing is over..")
+ for k, v in model_results.items():
+ print(f"# of instances in {k} is {len(v)}")
+
+ for model in model_results.keys():
+ path = os.path.join(output_path, f"{model}.jsonl")
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ with open(path, "w", encoding="utf8") as f_out:
+ for instance in model_results[model]:
+ json.dump(instance, f_out, ensure_ascii=False)
+ f_out.write("\n")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-i", "--input_path", type=str, help="path of generated result directory"
+ )
+ parser.add_argument(
+ "-o", "--output_path", type=str, help="path of processed result directory"
+ )
+ parser.add_argument(
+ "-m",
+ "--model_name_pattern",
+ type=str,
+ help="model name's pattern for regex",
+ default="",
+ )
+ args = parser.parse_args()
+ transform_results_folder(args.input_path, args.output_path, args.model_name_pattern)
diff --git a/varco_arena/data_convert_subset.py b/varco_arena/data_convert_subset.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd101aa6fa47a9e7a0384d3e0bb080095c0e6715
--- /dev/null
+++ b/varco_arena/data_convert_subset.py
@@ -0,0 +1,157 @@
+import argparse
+import json
+import os
+import random
+import re
+from collections import defaultdict
+
+
+def list_directories(path):
+ # 지정된 경로에 있는 항목들을 리스트로 받아옴
+ items = os.listdir(path)
+
+ # 항목들 중에서 디렉토리(폴더)만을 필터링
+ directories = [item for item in items if os.path.isdir(os.path.join(path, item))]
+
+ return directories
+
+
+def parse_by_regex(string):
+ varco_template_w_src = r"아래는 작업을 설명하는 명령어와 추가적 맥락을 제공하는 입력이 짝을 이루는 예제입니다.\n주어진 입력에 대해 명령어를 적절히 수행하는 응답을 작성하세요.\n\n### 입력:\n(?P.*?)\n\n### 명령어:\n(?P.*?)\n\n### 응답:\n"
+ varco_template_wo_src = r"아래는 작업을 설명하는 명령어입니다.\n명령어에 따른 요청을 적절히 완료하는 응답을 작성하세요.\n\n### 명령어:\n(?P.*?)\n\n### 응답:\n"
+
+ if re.compile(varco_template_w_src, flags=re.MULTILINE | re.DOTALL).match(string):
+ match = re.compile(varco_template_w_src, flags=re.MULTILINE | re.DOTALL).match(
+ string
+ )
+ source = match.group("source")
+ instruction = match.group("instruction")
+ elif re.compile(varco_template_wo_src, flags=re.MULTILINE | re.DOTALL).match(
+ string
+ ):
+ match = re.compile(varco_template_wo_src, flags=re.MULTILINE | re.DOTALL).match(
+ string
+ )
+ source = ""
+ instruction = match.group("instruction")
+ else:
+ source = None
+ instruction = None
+
+ return source, instruction
+
+
+# path 에 있는 result.json 파일 읽어서 전처리된 instance들로 만든다.
+def result_file_process(model, task, path):
+ with open(path, encoding="utf8") as f:
+ instances = json.loads(f.read())
+ processed_instances = []
+ for instance in instances:
+ raw = instance.get("input", False)
+ if raw:
+ source = instance["source"]
+ instruction = instance["instruction"]
+ else:
+ raw = instance.get("source", False)
+ source, instruction = parse_by_regex(instance.get("source", False))
+
+ if source is None or instruction is None:
+ print(f"PARSING ERROR IN MODEL {model} TASK {task} PATH {path} SRC {raw}")
+ else:
+ processed_instances.append(
+ {
+ "model_id": model,
+ "task": task,
+ "instruction": instruction.strip(),
+ "source": source.strip(),
+ "generated": instance["generated_result"],
+ }
+ )
+ return processed_instances
+
+
+# model results 디렉토리에서 결과값 변환 작업
+def transform_results_folder(input_path, output_path, model_name_pattern, num_instance):
+ regex_pattern = re.compile(model_name_pattern)
+
+ tasks = list_directories(input_path)
+ models = list_directories(os.path.join(input_path, tasks[0]))
+ models = [model for model in models if regex_pattern.match(model)]
+
+ model_results = {}
+ print(f"TASKS: {tasks}")
+ print(f"MODELS: {models}")
+ for task in tasks:
+ models = [
+ model
+ for model in list_directories(os.path.join(input_path, task))
+ if regex_pattern.match(model)
+ ]
+ for model in models:
+ result_path = os.path.join(input_path, task, model, "result.json")
+ model_name = model
+ if task in model:
+ model_name = model.split(f"-{task}-")[0]
+ instances = result_file_process(model_name, task, result_path)
+
+ if model_name in model_results.keys():
+ model_results[model_name] += instances
+ else:
+ model_results[model_name] = instances
+
+ print(f"{task} results processing is over..")
+ for k, v in model_results.items():
+ print(f"# of instances in {k} is {len(v)}")
+
+ dataset_by_task = defaultdict(lambda: defaultdict(list))
+ for data in (
+ all_datasets := [obj for obj_list in model_results.values() for obj in obj_list]
+ ):
+ dataset_by_task[data["task"]][
+ f"{data['instruction']}\n\n{data['source']}"
+ ].append(data)
+ new_results = {model: [] for model in {data["model_id"] for data in all_datasets}}
+ num_model = len(list(new_results.keys()))
+ for task in dataset_by_task.keys():
+ candidates = []
+ for data in dataset_by_task[task].values():
+ if len(data) != num_model:
+ continue
+ candidates.append(data)
+ random.shuffle(candidates)
+ selected = candidates[:num_instance]
+ for data_list in selected:
+ for data in data_list:
+ new_results[data["model_id"]].append(data)
+
+ for model in new_results.keys():
+ path = os.path.join(output_path, f"{model}.jsonl")
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ with open(path, "w", encoding="utf8") as f_out:
+ for instance in new_results[model]:
+ json.dump(instance, f_out, ensure_ascii=False)
+ f_out.write("\n")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-i", "--input_path", type=str, help="path of generated result directory"
+ )
+ parser.add_argument(
+ "-o", "--output_path", type=str, help="path of processed result directory"
+ )
+ parser.add_argument(
+ "-m",
+ "--model_name_pattern",
+ type=str,
+ help="model name's pattern for regex",
+ default="",
+ )
+ parser.add_argument(
+ "-n", "--num_instance", type=int, help="number of instance to choice"
+ )
+ args = parser.parse_args()
+ transform_results_folder(
+ args.input_path, args.output_path, args.model_name_pattern, args.num_instance
+ )
diff --git a/varco_arena/main.py b/varco_arena/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..393d98d39b254f4a622194a028918a72ca5b247f
--- /dev/null
+++ b/varco_arena/main.py
@@ -0,0 +1,192 @@
+import argparse
+import asyncio
+import os
+import platform
+from typing import Literal
+
+from calc_cost import calculate
+from varco_arena_core.data_utils import load_all_data
+from varco_arena_core.manager import Manager
+
+if platform.system() == "Windows":
+ try:
+ import winloop
+
+ asyncio.set_event_loop_policy(winloop.EventLoopPolicy())
+ except ImportError:
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
+elif platform.system() == "Linux":
+ try:
+ import uvloop
+
+ asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
+ except ImportError:
+ pass
+
+
+def main(
+ input_str,
+ output_dir,
+ evaluation_model,
+ matching_method,
+ n_jobs: int = 8,
+ evalprompt: Literal[
+ "llmbar_brief",
+ "llmbar", # general assistant eval
+ "translation_pair", # translation eval
+ "rag_pair_kr", # rag knowledge reflection eval
+ # "contextual_pair", # contextual visual-language instruction eval
+ ] = "llmbar",
+):
+ dataset_df = load_all_data(input_str)
+ if os.path.isfile(output_dir):
+ _output_dir = os.path.abspath(os.path.dirname(output_dir))
+ print(
+ f"output directory '{output_dir}' is not a directory. we'll use '{_output_dir}' instead."
+ )
+ output_dir = _output_dir
+ else:
+ os.makedirs(output_dir, exist_ok=True)
+
+ # cost estimation
+ total_matches, total_toks_in, total_toks_out, total_costs = calculate(
+ dataset_df=dataset_df,
+ model_name=evaluation_model,
+ matching_method=matching_method,
+ evalprompt=evalprompt,
+ )
+
+ _doubleline = "=" * 50
+ message = f"""---------------------------------------
+Judge LLM: {evaluation_model}
+평가 프롬프트: {evalprompt}
+평가 리그 방법: {matching_method}
+예상 평가 횟수 : {total_matches:,}
+예상 입력 토큰 : {total_toks_in:,}
+예상 출력 토큰 : {total_toks_out:,} (with x1.01 additional room)
+---------------------------------------
+예상 발생 비용 : ${total_costs:.3f}
+{_doubleline}"""
+ print(message)
+
+ if args.calc_price_run:
+ return
+
+ # prompt user whether to continue
+ flag = input("[*] Run Varco Arena? (y/n) : ")
+ if not flag.lower() == "y" and not flag.lower() == "yes":
+ print("[-] Varco Arena Stopped")
+ return
+
+ manager = Manager(
+ dataset_df,
+ output_dir,
+ evaluation_model,
+ matching_method,
+ n_jobs=n_jobs,
+ evalprompt=evalprompt,
+ )
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(manager.async_run())
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--input", help="input file")
+ parser.add_argument("-o", "--output_dir", help="output file")
+ parser.add_argument(
+ "-e", "--evaluation_model", default="debug", help="evaluation model specifier"
+ )
+ parser.add_argument(
+ "-c",
+ "--calc_price_run",
+ action="store_true",
+ help="print out price calculations",
+ )
+ parser.add_argument(
+ "-m",
+ "--matching_method",
+ default="tournament",
+ choices=["tournament"], # , "league"],
+ help="matching method specifier",
+ )
+ parser.add_argument(
+ "-k",
+ "--openai_api_key",
+ default=None,
+ help='openai key to use / default: OpenAI API Key from your env variable "OPENAI_API_KEY"',
+ )
+ parser.add_argument(
+ "-u",
+ "--openai_url",
+ default="https://api.openai.com/v1",
+ help="OpenAI BASE URL",
+ )
+ # advanced options
+ parser.add_argument(
+ "-j",
+ "--n_jobs",
+ default=32,
+ type=int,
+ help="number of concurrency for asyncio (passed to async.semaphore @ manager.py)\nIf your job does not proceed, consider lowering this.",
+ )
+ parser.add_argument(
+ "-p",
+ "--evalprompt",
+ default="llmbar_brief",
+ choices=[
+ "llmbar_brief",
+ "llmbar",
+ "translation_pair",
+ "rag_pair_kr",
+ # "contextual_pair",
+ ],
+ )
+
+ parser.add_argument(
+ "-lr",
+ "--limit_requests",
+ default=7_680,
+ type=int,
+ help="limit number of requests per minute when using vLLM Server",
+ )
+ parser.add_argument(
+ "-lt",
+ "--limit_tokens",
+ default=15_728_640,
+ type=int,
+ help="limit number of tokens per minute when using vLLM Server",
+ )
+ args = parser.parse_args()
+
+ # client는 openai key 세팅 이슈로 안쪽에서 import. 여기선 environ var 로 설정
+ # base URL 설정
+ if not args.openai_url.startswith("https://") and not args.openai_url.startswith(
+ "http://"
+ ):
+ args.openai_url = "http://" + args.openai_url
+ if not args.openai_url.endswith("/v1"):
+ args.openai_url += "/v1"
+ os.environ["OPENAI_BASE_URL"] = args.openai_url
+
+ # API Key 설정: 주어진게 있으면 환경변수 대신 쓰고, 아니면 환경변수에서 찾는다
+ if args.openai_api_key is None:
+ if os.getenv("OPENAI_API_KEY") is None:
+ raise ValueError(
+ "`--openai_api_key` or environment variable `OPENAI_API_KEY` is required"
+ )
+ else:
+ os.environ["OPENAI_API_KEY"] = args.openai_api_key
+
+ # limit 설정
+ os.environ["LIMIT_REQUESTS"] = str(args.limit_requests)
+ os.environ["LIMIT_TOKENS"] = str(args.limit_tokens)
+
+ main(
+ args.input,
+ args.output_dir,
+ args.evaluation_model,
+ args.matching_method,
+ n_jobs=args.n_jobs,
+ evalprompt=args.evalprompt,
+ )
diff --git a/varco_arena/precommit.sh b/varco_arena/precommit.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e255e22b3ec687ac4790f1d9658f6b24872b6a39
--- /dev/null
+++ b/varco_arena/precommit.sh
@@ -0,0 +1,5 @@
+# reformat all to prevent commit problem
+git add .
+pre-commit run --all-files
+# restore the files added so that one can actually add files to be commit
+git restore --staged .
diff --git a/varco_arena/requirements.txt b/varco_arena/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..25c83899b9a7029c6f8175c6e0abcfeb0f4effda
--- /dev/null
+++ b/varco_arena/requirements.txt
@@ -0,0 +1,14 @@
+openai>=1.17.0
+munch
+pandas
+numpy
+tqdm>=4.48.0
+plotly
+scikit-learn
+kaleido
+tiktoken>=0.7.0
+pyyaml
+transformers
+streamlit>=1.40.2
+openpyxl
+git+https://github.com/shobrook/openlimit.git#egg=openlimit
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_Qwen1.5-72B-Chat.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_Qwen1.5-72B-Chat.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..20699e9b1d2ab5ec2b4af22bef6d0de96e008c6e
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_Qwen1.5-72B-Chat.jsonl
@@ -0,0 +1,3 @@
+{"model_id":"Qwen1.5-72B-Chat","instruction":"I have a dataset which contains a list of 2D images, given a new image, how to find the closest image in the dataset","source":"","generated":"To find the closest image in a dataset to a new image, you can use a similarity measure like Euclidean distance, Cosine similarity, or Structural Similarity Index (SSIM). Here's a step-by-step guide using Python and the scikit-learn library for Euclidean distance:\n\n1. **Preprocess the images**: Normalize or standardize the images to ensure they have the same range and distribution. This is important because different image intensities can significantly affect distance calculations.\n\n2. **Flatten the images**: Convert the 2D images into 1D vectors. This is necessary for distance calculations.\n\n3. **Compute the distance**: Calculate the distance between the new image and all images in the dataset using Euclidean distance.\n\n4. **Find the minimum distance**: Identify the image with the smallest distance, which is the closest image.\n\nHere's a code example using Python and scikit-learn:\n\n```python\nimport numpy as np\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics.pairwise import euclidean_distances\n\n# Assuming your dataset is a list of 2D numpy arrays called 'dataset'\n# And the new image is a 2D numpy array called 'new_image'\n\n# Preprocess the images (standardize)\nscaler = StandardScaler(with_mean=False) # Don't center the data as it's for images\ndataset_scaled = scaler.fit_transform(dataset)\nnew_image_scaled = scaler.transform([new_image])\n\n# Flatten the images\ndataset_flattened = dataset_scaled.reshape(-1, dataset_scaled.shape[-1])\nnew_image_flattened = new_image_scaled.reshape(-1, new_image_scaled.shape[-1])\n\n# Compute Euclidean distances\ndistances = euclidean_distances(new_image_flattened, dataset_flattened)\n\n# Find the index of the closest image\nclosest_image_index = np.argmin(distances)\n\n# Retrieve the closest image from the dataset\nclosest_image = dataset[closest_image_index]\n\nprint(\"The closest image is at index:\", closest_image_index)\n```\n\nYou can replace `euclidean_distances` with other similarity measures like Cosine similarity or SSIM, but you'll need to import the appropriate function and modify the code accordingly. Note that SSIM is not a distance metric but a quality index, so you might need to convert it to a distance-like measure before finding the closest image.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"04ba0aeb79524f6c8520d47cada34f25"}
+{"model_id":"Qwen1.5-72B-Chat","instruction":"Find root cause for this error:\nsshd[54785]: error: kex_exchange_identification: Connection closed by remote host","source":"","generated":"This error indicates that the SSH connection was closed by the remote host during the initial key exchange phase. Some potential causes could be:\n\n- The remote SSH server is not running or is inaccessible. Double check the hostname\/IP and port and verify the SSH service is running on the remote host.\n\n- There is a network connectivity issue like a firewall blocking the connection. Check for any firewall rules that may be blocking SSH connections.\n\n- The remote SSH server is configured to only allow certain ciphers\/kex algorithms that the client does not support. Check the server's SSH daemon configuration (sshd_config) for allowed ciphers and key exchange algorithms.\n\n- The remote host closed the connection due to too many failed authentication attempts if password authentication is being used. Check SSH logs on the server for repeated failed auths from the client's source IP.\n\n- The SSH keys\/configuration on the client side are invalid or incompatible with the server. Regenerate SSH keys and verify the client's SSH configuration.\n\n- Resource exhaustion on the remote server is causing it to close connections. Check memory, CPU and file handles on the SSH server.\n\nSo in summary, the key things to check are network connectivity between client and server, the remote SSH server configuration, client SSH configuration, and resource usage on the remote server. The server logs and SSH daemon configuration are good places to start debugging further.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"037edf47ede9428b9ef91c737d6f7a9c"}
+{"model_id":"Qwen1.5-72B-Chat","instruction":"Tell me how to implement a SCIM server in PHP using slim as a router","source":"","generated":"To implement a SCIM (System for Cross-domain Identity Management) server in PHP using Slim as a router, you can follow these steps:\n\n1. Install the required dependencies:\n - Slim: A PHP micro-framework for building web applications and APIs.\n - A SCIM library: You can use a SCIM library like \"scim-php\" to handle SCIM-related operations.\n\n You can install these dependencies using Composer. Run the following command in your project directory:\n ```\n composer require slim\/slim scim-php\/scim\n ```\n\n2. Set up the Slim application:\n Create an `index.php` file and set up the Slim application:\n ```php\n run();\n ```\n\n3. Define SCIM routes and handlers:\n Add routes and corresponding handlers for SCIM endpoints in your Slim application. Here's an example:\n ```php\n \/\/ Get a list of users\n $app->get('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to retrieve a list of users\n \/\/ You can use the SCIM library to handle SCIM-related operations\n \/\/ Example:\n $users = getUsers(); \/\/ Implement this function to fetch users from your data source\n return $response->withJson($users);\n });\n\n \/\/ Create a new user\n $app->post('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to create a new user\n \/\/ Example:\n $userData = $request->getParsedBody();\n $createdUser = createUser($userData); \/\/ Implement this function to create a user in your data source\n return $response->withJson($createdUser, 201);\n });\n\n \/\/ Update a user\n $app->put('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to update a user\n \/\/ Example:\n $userId = $args['id'];\n $userData = $request->getParsedBody();\n $updatedUser = updateUser($userId, $userData); \/\/ Implement this function to update a user in your data source\n return $response->withJson($updatedUser);\n });\n\n \/\/ Delete a user\n $app->delete('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to delete a user\n \/\/ Example:\n $userId = $args['id'];\n deleteUser($userId); \/\/ Implement this function to delete a user from your data source\n return $response->withStatus(204);\n });\n ```\n\n These are just a few examples of SCIM endpoints. You'll need to implement additional endpoints based on the SCIM specification and your application's requirements.\n\n4. Implement SCIM operations:\n Use the SCIM library to handle SCIM-related operations such as parsing SCIM requests, validating data, and generating SCIM responses. The library should provide methods and classes to facilitate these operations.\n\n5. Test the SCIM server:\n Use tools like cURL or Postman to send SCIM requests to your server and verify the responses. Make sure the server handles the requests correctly and returns the expected responses.\n\nRemember to handle authentication and authorization appropriately in your SCIM server. You may need to implement mechanisms like OAuth or JWT to secure the API endpoints.\n\nThis is a basic outline of how you can implement a SCIM server in PHP using Slim as a router. You'll need to refer to the SCIM specification and the documentation of the SCIM library you choose to ensure compliance with the SCIM standard.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"0122ab60646b4961bc39e9c03bdf6bcc"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_claude-2.0.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_claude-2.0.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..ec258d773ba330479e631d733470509cd4390498
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_claude-2.0.jsonl
@@ -0,0 +1,3 @@
+{"model_id":"claude-2.0","instruction":"I have a dataset which contains a list of 2D images, given a new image, how to find the closest image in the dataset","source":"","generated":"Here are a few ways to find the closest matching image to a new query image from a dataset of 2D images:\n\n1. Use a perceptual hash like pHash to generate hash values for each image. Compare the pHash value of the new image to the pHash values of the dataset images using a distance metric like Hamming distance. The image with the smallest distance is the closest match.\n\n2. Use a pretrained convolutional neural network like VGG16 or ResNet50 to extract feature vectors for each image. Compare the feature vector of the new image to the dataset image vectors using cosine similarity. The image with the highest cosine similarity is the closest match.\n\n3. Train a Siamese neural network on the dataset images. The network encodes images into an embedding space where similar images are closer together. Use the trained model to encode the new image and find its nearest neighbor in the embedding space using a distance metric like Euclidean distance.\n\n4. Fine-tune a pretrained vision model like VGG16 on a classification task over the dataset images. Use the fine-tuned model to extract features from the new image and classify it. The predicted class indicates the closest matching image.\n\n5. Use OpenCV's template matching methods like cv2.matchTemplate to slide the new image over the dataset images looking for the best match by maximizing a similarity metric like normalized cross-correlation.\n\nThe best approach depends on your specific dataset and requirements in terms of accuracy, speed and scalability. Perceptual hashing is fast but less accurate, while neural network approaches are more accurate but slower.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"04ba0aeb79524f6c8520d47cada34f25"}
+{"model_id":"claude-2.0","instruction":"Find root cause for this error:\nsshd[54785]: error: kex_exchange_identification: Connection closed by remote host","source":"","generated":"This error indicates that the SSH connection was closed by the remote host during the initial key exchange phase. Some potential causes could be:\n\n- The remote SSH server is not running or is inaccessible. Double check the hostname\/IP and port and verify the SSH service is running on the remote host.\n\n- There is a network connectivity issue like a firewall blocking the connection. Check for any firewall rules that may be blocking SSH connections.\n\n- The remote SSH server is configured to only allow certain ciphers\/kex algorithms that the client does not support. Check the server's SSH daemon configuration (sshd_config) for allowed ciphers and key exchange algorithms.\n\n- The remote host closed the connection due to too many failed authentication attempts if password authentication is being used. Check SSH logs on the server for repeated failed auths from the client's source IP.\n\n- The SSH keys\/configuration on the client side are invalid or incompatible with the server. Regenerate SSH keys and verify the client's SSH configuration.\n\n- Resource exhaustion on the remote server is causing it to close connections. Check memory, CPU and file handles on the SSH server.\n\nSo in summary, the key things to check are network connectivity between client and server, the remote SSH server configuration, client SSH configuration, and resource usage on the remote server. The server logs and SSH daemon configuration are good places to start debugging further.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"037edf47ede9428b9ef91c737d6f7a9c"}
+{"model_id":"claude-2.0","instruction":"Tell me how to implement a SCIM server in PHP using slim as a router","source":"","generated":"To implement a SCIM (System for Cross-domain Identity Management) server in PHP using Slim as a router, you can follow these steps:\n\n1. Install the required dependencies:\n - Slim: A PHP micro-framework for building web applications and APIs.\n - A SCIM library: You can use a SCIM library like \"scim-php\" to handle SCIM-related operations.\n\n You can install these dependencies using Composer. Run the following command in your project directory:\n ```\n composer require slim\/slim scim-php\/scim\n ```\n\n2. Set up the Slim application:\n Create an `index.php` file and set up the Slim application:\n ```php\n run();\n ```\n\n3. Define SCIM routes and handlers:\n Add routes and corresponding handlers for SCIM endpoints in your Slim application. Here's an example:\n ```php\n \/\/ Get a list of users\n $app->get('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to retrieve a list of users\n \/\/ You can use the SCIM library to handle SCIM-related operations\n \/\/ Example:\n $users = getUsers(); \/\/ Implement this function to fetch users from your data source\n return $response->withJson($users);\n });\n\n \/\/ Create a new user\n $app->post('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to create a new user\n \/\/ Example:\n $userData = $request->getParsedBody();\n $createdUser = createUser($userData); \/\/ Implement this function to create a user in your data source\n return $response->withJson($createdUser, 201);\n });\n\n \/\/ Update a user\n $app->put('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to update a user\n \/\/ Example:\n $userId = $args['id'];\n $userData = $request->getParsedBody();\n $updatedUser = updateUser($userId, $userData); \/\/ Implement this function to update a user in your data source\n return $response->withJson($updatedUser);\n });\n\n \/\/ Delete a user\n $app->delete('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to delete a user\n \/\/ Example:\n $userId = $args['id'];\n deleteUser($userId); \/\/ Implement this function to delete a user from your data source\n return $response->withStatus(204);\n });\n ```\n\n These are just a few examples of SCIM endpoints. You'll need to implement additional endpoints based on the SCIM specification and your application's requirements.\n\n4. Implement SCIM operations:\n Use the SCIM library to handle SCIM-related operations such as parsing SCIM requests, validating data, and generating SCIM responses. The library should provide methods and classes to facilitate these operations.\n\n5. Test the SCIM server:\n Use tools like cURL or Postman to send SCIM requests to your server and verify the responses. Make sure the server handles the requests correctly and returns the expected responses.\n\nRemember to handle authentication and authorization appropriately in your SCIM server. You may need to implement mechanisms like OAuth or JWT to secure the API endpoints.\n\nThis is a basic outline of how you can implement a SCIM server in PHP using Slim as a router. You'll need to refer to the SCIM specification and the documentation of the SCIM library you choose to ensure compliance with the SCIM standard.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"0122ab60646b4961bc39e9c03bdf6bcc"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_phi-3-mini-128k-instruct.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_phi-3-mini-128k-instruct.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..b29cb8ddde21e412b8399b8f2f0d55c90b6fbc26
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_400_error_inputs/dbg_phi-3-mini-128k-instruct.jsonl
@@ -0,0 +1,3 @@
+{"model_id":"phi-3-mini-128k-instruct","instruction":"I have a dataset which contains a list of 2D images, given a new image, how to find the closest image in the dataset","source":"","generated":"To find the closest image in a dataset to a new 2D image, you can use a technique known as k-nearest neighbors (k-NN). Here's a step-by-step guide on how to do it:\n\n1. **Preprocess the dataset**: Ensure that all images in the dataset are of the same size and format. If not, resize and normalize them.\n\n2. **Feature extraction**: Convert each image into a feature vector. For 2D images, this could be as simple as flattening the image into a 1D array. However, for more complex images, you might need to use more sophisticated methods like SIFT, SURF, or even deep learning-based feature extraction.\n\n3. **Choose a distance metric**: The most common distance metric used in k-NN is the Euclidean distance. However, depending on your specific use case, you might want to use other metrics like Manhattan distance or cosine similarity. This could be a 2D images, you can use the pixel values. This could be the same size, you might use the pixel values. If you're using the pixel values. This could be the pixel values, you might use a method, you can use a method like pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel pixel\n\n\n\n\n\n\n.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.\n\n\n\n\n\n\n.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n:\n\n\n\n\n\n\n\n\n.\n.\n\n.\n.\n.\n.\n\n\n.\n.\n.\n\n\n.\n\n\n\n\n\n\n.\n.\n\n\n\n\n.\n.\n\n\n\n\n\n\n.\n.:.....\n\n.\n...\n\n\n\n.\n.\n\n\n\n\n\n\n\n.\n\n\n\n\n.\n.\n.\n\n\n\n\n\n\n\n\n\n\n\n\n.\n\n\n\n\n\n\n.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.\n\n\n\n\n.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"04ba0aeb79524f6c8520d47cada34f25"}
+{"model_id":"phi-3-mini-128k-instruct","instruction":"Find root cause for this error:\nsshd[54785]: error: kex_exchange_identification: Connection closed by remote host","source":"","generated":"This error indicates that the SSH connection was closed by the remote host during the initial key exchange phase. Some potential causes could be:\n\n- The remote SSH server is not running or is inaccessible. Double check the hostname\/IP and port and verify the SSH service is running on the remote host.\n\n- There is a network connectivity issue like a firewall blocking the connection. Check for any firewall rules that may be blocking SSH connections.\n\n- The remote SSH server is configured to only allow certain ciphers\/kex algorithms that the client does not support. Check the server's SSH daemon configuration (sshd_config) for allowed ciphers and key exchange algorithms.\n\n- The remote host closed the connection due to too many failed authentication attempts if password authentication is being used. Check SSH logs on the server for repeated failed auths from the client's source IP.\n\n- The SSH keys\/configuration on the client side are invalid or incompatible with the server. Regenerate SSH keys and verify the client's SSH configuration.\n\n- Resource exhaustion on the remote server is causing it to close connections. Check memory, CPU and file handles on the SSH server.\n\nSo in summary, the key things to check are network connectivity between client and server, the remote SSH server configuration, client SSH configuration, and resource usage on the remote server. The server logs and SSH daemon configuration are good places to start debugging further.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"037edf47ede9428b9ef91c737d6f7a9c"}
+{"model_id":"phi-3-mini-128k-instruct","instruction":"Tell me how to implement a SCIM server in PHP using slim as a router","source":"","generated":"To implement a SCIM (System for Cross-domain Identity Management) server in PHP using Slim as a router, you can follow these steps:\n\n1. Install the required dependencies:\n - Slim: A PHP micro-framework for building web applications and APIs.\n - A SCIM library: You can use a SCIM library like \"scim-php\" to handle SCIM-related operations.\n\n You can install these dependencies using Composer. Run the following command in your project directory:\n ```\n composer require slim\/slim scim-php\/scim\n ```\n\n2. Set up the Slim application:\n Create an `index.php` file and set up the Slim application:\n ```php\n run();\n ```\n\n3. Define SCIM routes and handlers:\n Add routes and corresponding handlers for SCIM endpoints in your Slim application. Here's an example:\n ```php\n \/\/ Get a list of users\n $app->get('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to retrieve a list of users\n \/\/ You can use the SCIM library to handle SCIM-related operations\n \/\/ Example:\n $users = getUsers(); \/\/ Implement this function to fetch users from your data source\n return $response->withJson($users);\n });\n\n \/\/ Create a new user\n $app->post('\/Users', function ($request, $response, $args) {\n \/\/ Implement the logic to create a new user\n \/\/ Example:\n $userData = $request->getParsedBody();\n $createdUser = createUser($userData); \/\/ Implement this function to create a user in your data source\n return $response->withJson($createdUser, 201);\n });\n\n \/\/ Update a user\n $app->put('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to update a user\n \/\/ Example:\n $userId = $args['id'];\n $userData = $request->getParsedBody();\n $updatedUser = updateUser($userId, $userData); \/\/ Implement this function to update a user in your data source\n return $response->withJson($updatedUser);\n });\n\n \/\/ Delete a user\n $app->delete('\/Users\/{id}', function ($request, $response, $args) {\n \/\/ Implement the logic to delete a user\n \/\/ Example:\n $userId = $args['id'];\n deleteUser($userId); \/\/ Implement this function to delete a user from your data source\n return $response->withStatus(204);\n });\n ```\n\n These are just a few examples of SCIM endpoints. You'll need to implement additional endpoints based on the SCIM specification and your application's requirements.\n\n4. Implement SCIM operations:\n Use the SCIM library to handle SCIM-related operations such as parsing SCIM requests, validating data, and generating SCIM responses. The library should provide methods and classes to facilitate these operations.\n\n5. Test the SCIM server:\n Use tools like cURL or Postman to send SCIM requests to your server and verify the responses. Make sure the server handles the requests correctly and returns the expected responses.\n\nRemember to handle authentication and authorization appropriately in your SCIM server. You may need to implement mechanisms like OAuth or JWT to secure the API endpoints.\n\nThis is a basic outline of how you can implement a SCIM server in PHP using Slim as a router. You'll need to refer to the SCIM specification and the documentation of the SCIM library you choose to ensure compliance with the SCIM standard.","task":"tactan_chat","question_category":"arena-hard-v0.1","question_id":"0122ab60646b4961bc39e9c03bdf6bcc"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/0.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/0.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..a807a6fd06b60dd5f7325380f125b586e2e35145
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/0.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "diffmodel", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "Zooey takes a class.", "generated": "조이(zoey)가 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "많은 경우, 저는 혼자서도 놀지만, 픽업 게임도 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 카디오가 관여되어 있어, 아버지 시간이 제 등에 걸려 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "Then we eat.", "generated": "그럼 먹어요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "diffmodel", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만, 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..b53a1db04e4b21a2dddf939d029a3628cb06ec3e
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Zooey takes a class.", "generated": "조이(zoey)가 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "많은 경우, 저는 혼자서도 놀지만, 픽업 게임도 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 카디오가 관여되어 있어, 아버지 시간이 제 등에 걸려 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Then we eat.", "generated": "그럼 먹어요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만, 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10_1.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10_1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..b53a1db04e4b21a2dddf939d029a3628cb06ec3e
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_modelid_inputs/llama_10_1.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Zooey takes a class.", "generated": "조이(zoey)가 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "많은 경우, 저는 혼자서도 놀지만, 픽업 게임도 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 카디오가 관여되어 있어, 아버지 시간이 제 등에 걸려 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Then we eat.", "generated": "그럼 먹어요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만, 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom1.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..e3e2e67c349cc7472da565d4483e9d306079fb1f
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom1.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 여름보다는 겨울이 더 마음에 들어."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 여름보다는 겨울이 더 좋아."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom2.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom2.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..38b1aadfc9e5dfc95e114e130127ff14e3f291f3
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_duplicate_test_scenarios_inputs/custom2.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "나는 여름보다는 겨울이 더 좋다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들", "answer": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..de247fd188a4f7486c4b78f6926e918c741ebba2
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "난 여름보다는 겨울이 더 좋아."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "근데 난 여름보다 겨울이 더 좋아. 왜냐하면 살짝 추운 날씨가 옷 입기도 쉽고, 푹 쉬고 있을 수도 있어서 좋거든."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다 겨울이 좋아. 왜냐하면 겨울에는 밖에 나가서 놀기도 편하고, 눈 덮인 산이나 호수를 볼 수 있어서 좋기 때문이야."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다."}
+{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 갔다. 집에 와서 쉬는 시간에 공부를 하며 하루를 마무리 지었다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora_epoch_5.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora_epoch_5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..e4b9b07a0664c01ba9ae49ebe62671437d456119
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_dora_epoch_5.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 마음에 들어."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋아."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다."}
+{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..3a7b3e9d2eff37bf235e65d5bd11a047a8621e56
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다."}
+{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full_epoch_5.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full_epoch_5.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..18865ccf946108c311eac6ce1f64fe5e95af1433
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_brief_inputs/240413_full_epoch_5.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다."}
+{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_inputs b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_inputs
new file mode 120000
index 0000000000000000000000000000000000000000..4861006b8c239b81bdc132048186b5114f8bb16a
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_llmbar_inputs
@@ -0,0 +1 @@
+dbg_llmbar_brief_inputs/
\ No newline at end of file
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom1.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..f83c50aeb755ac635638c78cc2d511e2d092476d
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom1.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들1", "answer": "나는 여름보다는 겨울이 더 마음에 들어."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들2", "answer": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들3", "answer": "나는 여름보다는 겨울이 더 좋아."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들4", "answer": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들5", "answer": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다."}
+{"model_id": "custom_1", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들6", "answer": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom2.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom2.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..c6a2dde5f7c9533d6090c95a6c09f399926ef6f3
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom2.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들1", "answer": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들2", "answer": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들3", "answer": "나는 여름보다는 겨울이 더 좋다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들4", "answer": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들5", "answer": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다."}
+{"model_id": "custom_2", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들6", "answer": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom3.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom3.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..1ab983bf208282aa7bbc2c69e558a4fcfbd47eb6
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/custom3.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들1", "answer": "난 여름보다는 겨울이 더 좋아."}
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들2", "answer": "근데 난 여름보다 겨울이 더 좋아. 왜냐하면 살짝 추운 날씨가 옷 입기도 쉽고, 푹 쉬고 있을 수도 있어서 좋거든."}
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들3", "answer": "나는 여름보다 겨울이 좋아. 왜냐하면 겨울에는 밖에 나가서 놀기도 편하고, 눈 덮인 산이나 호수를 볼 수 있어서 좋기 때문이야."}
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "ground_statements": "참고할 문단들4", "answer": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다."}
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "ground_statements": "참고할 문단들5", "answer": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다."}
+{"model_id": "custom3", "task": "늘려쓰기", "question": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "ground_statements": "참고할 문단들6", "answer": "오늘은 학교에 갔다. 집에 와서 쉬는 시간에 공부를 하며 하루를 마무리 지었다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal1.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..5102b002e54824a5d9e7e262684eef4d9dd3bf71
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal1.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "참고할 문단들1", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "참고할 문단들2", "generated": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다."}
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "참고할 문단들3", "generated": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여."}
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "참고할 문단들4", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다."}
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "참고할 문단들5", "generated": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다."}
+{"model_id": "normal_1", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "참고할 문단들6", "generated": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal2.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal2.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..5f1dee5645d015a217e04ecdd11a3334a6850641
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_rag_inputs/normal2.jsonl
@@ -0,0 +1,6 @@
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "참고할 문단들1", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "참고할 문단들2", "generated": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다."}
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "참고할 문단들3", "generated": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여."}
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "참고할 문단들4", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다."}
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "참고할 문단들5", "generated": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다."}
+{"model_id": "normal2", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "참고할 문단들6", "generated": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/llama_10.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/llama_10.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..b53a1db04e4b21a2dddf939d029a3628cb06ec3e
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/llama_10.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 벌이거나 아내에게 손을 들어주거나 아이들을 괴롭힐 대상으로 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에쿠노크스로 지하철을 타고 가요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Zooey takes a class.", "generated": "조이(zoey)가 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "많은 경우, 저는 혼자서도 놀지만, 픽업 게임도 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 카디오가 관여되어 있어, 아버지 시간이 제 등에 걸려 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "Then we eat.", "generated": "그럼 먹어요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "llama3_8b_qwen2.5_0.5b_seqkd", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만, 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_1.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_1.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..9151323b14c632d04a3f9f02bde3db63a22f26c8
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_1.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 원하시나요?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "저는 평생 싸움을 치거나 아내에게 손을 뻗지 않았고, 아이들을 괴롭힐 것처럼 보지 않았습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로 생계를 유지하는 것이 쉽지 않다는 것을 알고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 호레이스 실버, 시더 월튼 등 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통 우리는 7시에 맨해튼의 에퀴녹스까지 지하철을 타고 다닙니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "Zooey takes a class.", "generated": "Zooey가 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "많은 경우, 저는 혼자서 플레이하지만, 픽업 게임도 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 심장과 관련이 있는데, 이것은 아버지 시간을 제 뒤로 미루게 합니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "Then we eat.", "generated": "그러면 우리는 먹어요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_7b_orpo_0.5b_seqkd", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만, 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_2.jsonl b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_2.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..b7e2c453e6516c0d7d7653d552940d163263a3e8
--- /dev/null
+++ b/varco_arena/rsc/inputs_for_dbg/dbg_trans_inputs/qwen2.5_2.jsonl
@@ -0,0 +1,10 @@
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "Just want the music?", "generated": "음악만 듣고 싶다고?", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "I never picked a fight in my life or raised a hand to my wife or saw my children as things to bully.", "generated": "나는 내 인생에서 싸움을 걸거나 아내에게 손을 들거나 내 아이들을 괴롭히는 것으로 보지 않았다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "Willie Jones III knows it’s not easy to make a living as a jazz drummer in 2019.", "generated": "윌리 존스 3세는 2019년 재즈 드러머로서 생계가 쉽지 않다는 사실을 알고 있다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "He went on to join the bands of Horace Silver, Cedar Walton and others.", "generated": "그는 이어 호레이스 실버, 시더 월턴 등의 밴드에 합류했다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "Usually, we’re on the subway to Equinox in Manhattan by 7.", "generated": "보통은 7시가 되면 맨해튼의 이쿼녹스까지 지하철을 타고 가는데요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "Zooey takes a class.", "generated": "주이는 수업을 듣고 있습니다.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "A lot of times, I play by myself, but I’ll also play pickup games.", "generated": "가끔은 혼자 놀지만, 픽업 게임도 즐겨요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "There’s a lot of cardio involved, which keeps Father Time off my back.", "generated": "많은 심장 운동이 필요해서 아버지가 허리가 아프지 않아요.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "Then we eat.", "generated": "그럼 밥 먹자.", "source_lang": "English", "target_lang": "Korean"}
+{"model_id": "qwen2.5_0.5b_epoch2", "task": "번역", "instruction": "It’s not the healthiest thing, but it’s the weekend.", "generated": "가장 건강한 것은 아니지만 주말입니다.", "source_lang": "English", "target_lang": "Korean"}
diff --git a/varco_arena/setup.py b/varco_arena/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..e335389e413eb86e4c41b1cb27ff1789b3d231a8
--- /dev/null
+++ b/varco_arena/setup.py
@@ -0,0 +1,26 @@
+import platform
+
+from setuptools import find_packages, setup
+
+with open("requirements.txt", encoding="utf8") as r:
+ required_packages = [
+ package_name
+ for package_name in r.read().splitlines()
+ if not package_name.startswith("git+")
+ ]
+
+if platform.system() == "Windows":
+ required_packages.append("winloop")
+elif platform.system() == "Linux":
+ required_packages.append("uvloop")
+
+setup(
+ name="varco_arena", # 패키지의 이름
+ version="1.0.0", # 패키지의 버전
+ author="Text AI Lab, Generation Model Team", # 작성자 이름
+ author_email="ncsoft_generationmodelteamd@ncsoft.com", # 작성자 이메일
+ description="VARCO Arena는 각 모델 별로 생성된 결과를 기반으로 어떤 모델이 가장 좋은 모델인지 판별해주는 프로젝트입니다.", # 패키지에 대한 간단한 설명
+ packages=find_packages(), # 패키지 폴더를 자동으로 찾아 추가
+ install_requires=required_packages, # 패키지 설치에 필요한 다른 패키지들
+ dependency_links=["git+https://github.com/shobrook/openlimit.git#egg=openlimit"],
+)
diff --git a/varco_arena/varco_arena_core/__init__.py b/varco_arena/varco_arena_core/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/varco_arena/varco_arena_core/__pycache__/__init__.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e9d0c8e529c61156af737d3d9d0c0c7a2690fec4
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/__init__.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/custom_input_utils.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/custom_input_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..03c619731b684210b0a1616758a6bda1c44a563b
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/custom_input_utils.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/data_utils.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/data_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1bf6db417867ac4b01b861a45aa911e6523041fd
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/data_utils.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/elo.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/elo.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..daa3ee24393908d4886453956b586e51ab594885
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/elo.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/eval_utils.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/eval_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b56b00fcc0312c04a212895fae38b6e10b189168
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/eval_utils.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/league.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/league.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e01c068c4d924af7d6b3c19972c2eabf09210436
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/league.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/manager.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/manager.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ccb4f121574a8ac162f1bee92bbbab55a7cbf481
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/manager.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/match.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/match.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..45ed2ba3496299ea85568fb3aea2d54b6717d5cc
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/match.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/tournament.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/tournament.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2a44766f196a9825acf1c44f1e21a995cc6ac62
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/tournament.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/tracking_utils.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/tracking_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4369f0c051989dac168501322169d4300b039444
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/tracking_utils.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/__pycache__/visualization.cpython-311.pyc b/varco_arena/varco_arena_core/__pycache__/visualization.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..16e09192a3c9775c0755a388d2ec619bd99d335d
Binary files /dev/null and b/varco_arena/varco_arena_core/__pycache__/visualization.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/custom_input_utils.py b/varco_arena/varco_arena_core/custom_input_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..57ab0ff3ab81a2327f5e4e2a4c52215ae779245e
--- /dev/null
+++ b/varco_arena/varco_arena_core/custom_input_utils.py
@@ -0,0 +1,17 @@
+RAG_INPUT_MAPPINGS = {
+ "ground_statements": "source",
+ "question": "instruction",
+ "answer": "generated",
+}
+
+"""
+[대화 지시]
+{ground_statements} --> row.source --> ${src}
+
+[대화 내용]
+질문: {question} --> row.instruction --> ${inst}
+
+[다음 발화]
+A: {answer1} answer --> row.generated --> ${out_a|b}
+B: {answer2}
+"""
diff --git a/varco_arena/varco_arena_core/data_utils.py b/varco_arena/varco_arena_core/data_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b561339cadc4654850aaa443f855d7fd68aa05a
--- /dev/null
+++ b/varco_arena/varco_arena_core/data_utils.py
@@ -0,0 +1,205 @@
+import glob
+import os
+from pathlib import Path
+from typing import Dict, List
+
+import pandas as pd
+from tqdm import tqdm
+
+from .custom_input_utils import RAG_INPUT_MAPPINGS
+
+
+def resolve_path(input_str: str):
+ """
+ input_str 에 매칭되는 파일 경로들 찾아서 return
+ """
+
+ # regex 인지 확인
+ if "*" in input_str:
+ files = list(glob.glob(input_str, recursive=False))
+ if not files:
+ raise ValueError(f"Given regex does not capture nothing\n\n-i {input_str}")
+ else: # 디렉토리인지 확인
+ i_path = Path(input_str)
+ if not i_path.is_dir():
+ raise ValueError(f"Cannot find dir\n\n-i {input_str}")
+ files = list(i_path.glob("*jsonl"))
+ if not files:
+ raise ValueError(
+ f"Given directory has no jsonlines files\n\n-i {input_str}"
+ )
+ if len(files) < 2:
+ raise ValueError(f"Input file must be more than 1 \n\n{files=}")
+ return files
+
+
+def load_all_data(input_str: str) -> pd.DataFrame:
+ """
+ 주어진 input_str과 매칭되는 파일들을 읽어서 한번에 return
+ """
+
+ jslfs = resolve_path(input_str)
+ outputs = []
+
+ df_old, file_old = None, None
+ alert_too_scarce_data = False
+ model_id2file = dict()
+
+ for file in tqdm(jslfs, desc="reading jsonlines -> dataframe"):
+ df_now, model_id = load_jsl_with_validity_check(
+ jslf=file,
+ old_jslf=file_old,
+ df_old=df_old,
+ )
+ if model_id in model_id2file.keys():
+ raise ValueError(
+ f'Duplicate model_id across jsonl files: {model_id2file[model_id]} already has {model_id}. `model_id` is required to be unique per file. \n\nCheck "model_id" in \n* {file}\n* {model_id2file[model_id]}\n'
+ )
+
+ model_id2file[model_id] = file
+
+ print(f"processing: {Path(file).name:<50}{len(df_now):>5} rows")
+ if len(df_now) < 50:
+ alert_too_scarce_data = True
+
+ df_old = df_now
+ df_now = index_tournament(df_now)
+ outputs.append(df_now)
+ file_old = file
+ df = pd.concat(outputs) # stacked!
+ if alert_too_scarce_data:
+ print(
+ f"[WARNING] {len(df_now)} rows / model is kind of too small amount for proper benchmarking."
+ )
+ print(f"TOTAL # OF LLM OUTPUTS = {len(df)} ROWS")
+
+ return df
+
+
+# def check_duplicate(df, jslf):
+# hashing = df.instruction + df.source
+# if "source_lang" in df.columns:
+# hashing += df.source_lang
+# if "target_lang" in df.columns:
+# hashing += df.target_lang
+# if "task" in df.columns:
+# if len(df.task.unique()) >= 2:
+# hashing += df.task
+
+# if (hashing.value_counts() > 1).any():
+# mask = hashing.value_counts() > 1
+# duplicate_hashes = hashing.value_counts()[mask].index
+# dup_idxs = []
+# for dh in duplicate_hashes:
+# idxs = hashing[hashing == dh].index.tolist()
+# dup_idxs.append(idxs)
+# err_message = f"You have duplicate test cases in your file\n\t{jslf}"
+# for idxs in dup_idxs:
+# err_message += f"\n\t - {idxs}"
+# raise ValueError(err_message)
+
+
+def _fill_mcguffin_field(df: pd.DataFrame) -> pd.DataFrame:
+ if "task" not in df.columns:
+ df["task"] = "-"
+ if "source" not in df.columns:
+ df["source"] = ""
+ return df
+
+
+def num_uniq_queries(dataset: List[Dict]) -> int:
+ """use this function to check if the input jsonl files are suitable to intended use of Varco Arena
+
+ Varco Arena: Let {n_models} of LLMs compete each other on {len_file} number of instructions
+ --> dataset.instruction + dataset.source shouldn't change by adding another model output file, if it does, it is would be a buggy input.
+ """
+ df = pd.DataFrame(dataset)
+ df = index_tournament(df)
+ return len(df.tournament_idx.unique())
+
+
+def load_jsl_with_validity_check(
+ jslf: str = None,
+ old_jslf: str = None,
+ df_old: pd.DataFrame = None,
+) -> pd.DataFrame:
+ df = pd.read_json(
+ jslf,
+ lines=True,
+ orient="records",
+ convert_dates=False,
+ keep_default_dates=False,
+ )
+
+ # standardize the fieldnames of the given jsonl input
+ for custom_key, standard_key in RAG_INPUT_MAPPINGS.items():
+ if custom_key in df.columns:
+ if standard_key in df.columns:
+ raise ValueError(
+ "One stylize the input from one style\n(1) instruction / [source] / generated\n(2) question / [ground_statements] / answer (for RAG scenario)\n"
+ )
+ df = df.rename(columns={custom_key: standard_key})
+
+ # all the things below validity check df based on "instruction source generated" field input
+ df = _fill_mcguffin_field(df)
+ # check_duplicate(df, jslf)
+
+ # initial file: cannot validate
+ if old_jslf is None and df_old is None:
+ model_ids = df.model_id.unique().tolist()
+ if len(model_ids) > 1:
+ raise ValueError(
+ f"`model_id` not uniform: You have a jsonl file that has more than than two {model_ids=}"
+ )
+ return df, model_ids[0]
+
+ else: # other files after
+ assert old_jslf is not None
+
+ if df_old is None:
+ df_old = pd.read_json(
+ old_jslf,
+ lines=True,
+ orient="records",
+ convert_dates=False,
+ keep_default_dates=False,
+ )
+ df_old = _fill_mcguffin_field(df_old)
+ else:
+ pass # use df_old
+
+ # check columns (only valid when there is a pair of jslfs to compare)
+ not_to_regard_cols = [
+ "generated",
+ "model_id",
+ ]
+ if set(df.columns) != set(df_old.columns):
+ raise ValueError(
+ f"**Invalid .jsonl file!**\n\nYour input jsonlines need to have the same columns\n\n{jslf}\n\t{df.columns=}\n\ns{old_jslf}\n\t{df_old.columns=}"
+ )
+ for col in df.columns:
+ if col in not_to_regard_cols:
+ continue
+ if (df[col] != df_old[col]).any():
+ raise ValueError(
+ f"**Invalid .jsonl file!**\n\n{jslf=}\n{old_jslf=}\nfound difference in {col=} which have to be the same"
+ )
+ model_ids = df.model_id.unique().tolist()
+ if len(model_ids) > 1:
+ raise ValueError(
+ f"`model_id` not uniform: You have a jsonl file that has more than than two {model_ids=}"
+ )
+ return df, model_ids[0]
+
+
+def index_tournament(df: pd.DataFrame) -> pd.DataFrame:
+ # index rows to be identifiable
+ # name: tournament_idx
+ if "index" in df.columns:
+ df = df.rename(columns={"index", "index_"})
+ print(
+ '[WARNING] rename column "index" to "index_" while loading jsonlines file'
+ )
+ df = df.reset_index(drop=True)
+ df["tournament_idx"] = df.index.tolist()
+ return df
diff --git a/varco_arena/varco_arena_core/elo.py b/varco_arena/varco_arena_core/elo.py
new file mode 100644
index 0000000000000000000000000000000000000000..2329336a74dd6411053dae052485734cdfa9500c
--- /dev/null
+++ b/varco_arena/varco_arena_core/elo.py
@@ -0,0 +1,82 @@
+import math
+
+import numpy as np
+import pandas as pd
+from sklearn.linear_model import LogisticRegression
+
+
+def compute_mle_elo(df, SCALE=400, BASE=10, INIT_RATING=1000):
+ if isinstance(df, list):
+ df = pd.DataFrame(df)
+ df = df.dropna(subset=["winner", "model_a", "model_b"]) # dropping None vs sth
+
+ models = pd.concat([df["model_a"], df["model_b"]]).unique()
+ models = pd.Series(np.arange(len(models)), index=models)
+
+ # duplicate battles
+ df = pd.concat([df, df], ignore_index=True)
+ p = len(models.index)
+ n = df.shape[0]
+
+ X = np.zeros([n, p])
+ X[np.arange(n), models[df["model_a"]]] = +math.log(BASE)
+ X[np.arange(n), models[df["model_b"]]] = -math.log(BASE)
+
+ # one A win => two A win
+ Y = np.zeros(n)
+ Y[df["winner"] == "A"] = 1.0
+
+ WARNING = "elo.py:L{L} compute_mle_elo() // Warning: Seeing this message indicates the regression result for elo is unreliable. You should be test-running the Varco Arena or something odd (perfect one-sided wins) is happening\n\nto avoid logistic regressor error, manually putting other class"
+ if (Y == 0).all():
+ print(WARNING.format(L=32))
+ Y[-1] = 1.0
+ elif (Y == 1.0).all():
+ print(WARNING.format(L=35))
+ Y[-1] = 0.0
+
+ lr = LogisticRegression(fit_intercept=False)
+ lr.fit(X, Y)
+
+ elo_scores = SCALE * lr.coef_[0] + INIT_RATING
+
+ elo_scores = pd.Series(elo_scores, index=models.index).sort_values(ascending=False)
+
+ df = (
+ pd.DataFrame(
+ [[n, round(elo_scores[n], 2)] for n in elo_scores.keys()],
+ columns=["Model", "Elo rating"],
+ )
+ .sort_values("Elo rating", ascending=False)
+ .reset_index(drop=True)
+ )
+ df.index = df.index + 1
+
+ return df
+
+
+# Utilities
+def compute_relative_winrate_to_1st(elo_df):
+ """
+ Post-processing utility for saving elo table to an excel file. Possibly work as a absolute measure for quality.
+
+ elo_df:
+ columns: Model, Elo rating
+
+ add:
+ column: relative_winrate_to_1st
+ """
+ from functools import partial
+
+ rating1st = elo_df["Elo rating"].max()
+ win_rate_to_1st = partial(elo_to_winrate, rating_b=rating1st)
+ elo_df["winrate_vs_1st"] = elo_df["Elo rating"].apply(win_rate_to_1st)
+ print(elo_df)
+
+ return elo_df
+
+
+def elo_to_winrate(rating_a: float = None, rating_b: float = None) -> float:
+ # compute P(A wins B) from ratings
+ rate_diff = rating_a - rating_b
+ win_rate = 1 / (1 + 10 ** (-rate_diff / 400))
+ return win_rate
diff --git a/varco_arena/varco_arena_core/eval_utils.py b/varco_arena/varco_arena_core/eval_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..5115d1b7e3ce6bf2a8fb64092ca8d51135d76b71
--- /dev/null
+++ b/varco_arena/varco_arena_core/eval_utils.py
@@ -0,0 +1,176 @@
+import os
+from pprint import pprint
+from types import SimpleNamespace
+from typing import Any, Dict, List, Tuple
+
+import openai
+import pandas as pd
+import requests
+from openlimit import ChatRateLimiter
+from varco_arena_core.prompts import TranslationPairPrompt
+from varco_arena_core.prompts.base_prompt import ComparisonPromptBase
+
+from .tracking_utils import CountTokens
+
+client = None
+rate_limiter = None
+vllm_model_name = None
+
+
+def vllm_get_model_name():
+ response = requests.get(url=f"{os.getenv('OPENAI_BASE_URL')}/models")
+ data = response.json()
+ for model in data["data"]:
+ model_name = model["id"]
+ return model_name
+
+
+def get_limit_rate(model: str):
+ response = requests.post(
+ url=f"{os.getenv('OPENAI_BASE_URL')}/chat/completions",
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}",
+ },
+ json={"model": model, "messages": []},
+ )
+ limit_requests = int(response.headers.get("x-ratelimit-limit-requests", 0))
+ limit_tokens = int(response.headers.get("x-ratelimit-limit-tokens", 0))
+ return limit_requests, limit_tokens
+
+
+@CountTokens
+async def async_query_openai(
+ completed_prompts: List[Dict] = None,
+ prompt_obj: ComparisonPromptBase = None,
+ model: str = None,
+) -> Tuple[Dict[str, float], Any]:
+ """
+ query llm and returns normalized probability / score
+ (assumes prob_calibration as a default option)
+
+ 1) pairwise comparison prompts
+ return {"prefer_1st": float:normalized_prob, "prefer_2nd": float:normalized_prob }, response
+ 2) scoring prompts
+ return {"prefer_1st": float:normalized_score }, response
+ 3) scoring and comparison prompts
+ return {"prefer_1st": float:normalized_score, "prefer_2nd": float:normalized_score }, response
+ or
+ {"error": exception message (str) }, Exception
+
+ returned response will be used for `@CountTokens`, and also work for debugging use
+ """
+
+ # max_tokens to prompt_obj.sampling_parameters
+ prompt_obj.sampling_parameters[
+ "max_tokens"
+ ] = prompt_obj.get_expected_max_tokens_w_room(
+ eval_model_name=model,
+ )
+
+ kwargs = dict(
+ messages=completed_prompts,
+ **prompt_obj.sampling_parameters,
+ )
+
+ isopenai: bool = os.getenv("OPENAI_BASE_URL") == "https://api.openai.com/v1"
+
+ # defining client here?...
+ global client
+ while client is None:
+ client = openai.AsyncOpenAI(max_retries=5, timeout=120)
+
+ if not isopenai:
+ global vllm_model_name
+ while vllm_model_name is None:
+ vllm_model_name = vllm_get_model_name()
+ model = vllm_model_name
+ kwargs.update({"model": model})
+
+ global rate_limiter
+ while rate_limiter is None:
+ if isopenai:
+ limit_requests, limit_tokens = get_limit_rate(model)
+ try:
+ rate_limiter = ChatRateLimiter(
+ request_limit=limit_requests,
+ token_limit=limit_tokens,
+ )
+ except Exception as e:
+ print(e)
+ print(
+ f"HINT: if DivZero error occurs by `ChatRateLimiter.request_limit`, your account might have reached the rate limit"
+ )
+ exit()
+ else:
+ limit_requests, limit_tokens = int(os.getenv("LIMIT_REQUESTS", 7_680)), int(
+ os.getenv("LIMIT_TOKENS", 15_728_640)
+ )
+ rate_limiter = ChatRateLimiter(
+ request_limit=limit_requests,
+ token_limit=limit_tokens,
+ )
+ limiter = rate_limiter.limit(**kwargs)
+
+ async with limiter:
+ try:
+ resp = await client.chat.completions.create(**kwargs)
+ postprocess_f = prompt_obj.prob_calibrated_output
+ normalized_result = postprocess_f(resp)
+ normalized_result["error"] = False
+ normalized_result["exception_str"] = ""
+ except Exception as e:
+ pprint(kwargs)
+ print(e)
+ print("setting result as tie")
+ resp = None
+ normalized_result = {
+ "prefer_1st": 0.0,
+ "prefer_2nd": 0.0,
+ "error": True,
+ "exception_str": str(e),
+ "model": model,
+ "target_tokens_found": [],
+ "no_target_tokens_found": True,
+ }
+
+ from .manager import increase_match_count
+
+ increase_match_count() # you're hacky Jumin...
+
+ return normalized_result, resp
+
+
+async def async_eval_w_prompt(
+ prompt_obj: ComparisonPromptBase = None,
+ evalmodel: str = None,
+ position_1st: SimpleNamespace = None,
+ position_2nd: SimpleNamespace = None,
+ # prob_calibration: bool = True, # default True.
+):
+ # complete the prompt
+ kwargs = dict(
+ inst=position_1st.instruction,
+ src=position_1st.source,
+ out_a=position_1st.generated,
+ out_b=position_2nd.generated,
+ task=position_1st.task,
+ )
+
+ if isinstance(prompt_obj, TranslationPairPrompt):
+ kwargs["source_lang"] = position_1st.source_lang
+ kwargs["target_lang"] = position_1st.target_lang
+
+ completed_prompt = prompt_obj.complete_prompt(**kwargs)
+ # print(completed_prompt[-1]["content"])
+
+ # perform eval
+ # normalized_result
+ # - normalized_prob (comparison)
+ normalized_result, resp = await async_query_openai(
+ completed_prompt,
+ prompt_obj=prompt_obj,
+ model=evalmodel,
+ )
+
+ return normalized_result, async_query_openai, resp
diff --git a/varco_arena/varco_arena_core/league.py b/varco_arena/varco_arena_core/league.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a9f0872ed6b1cb11e301605134eb3f677a083eb
--- /dev/null
+++ b/varco_arena/varco_arena_core/league.py
@@ -0,0 +1,87 @@
+import asyncio
+import random
+from itertools import combinations
+from time import time
+from types import SimpleNamespace
+from typing import Dict, List, Literal, Tuple
+
+from varco_arena_core.prompts import ComparisonPromptBase
+
+from .match import Match
+
+
+async def limited_coro(coro, semaphore):
+ async with semaphore:
+ return await coro
+
+
+class League:
+ """
+ Full grid matches of the model outputs (all-play-all)
+ """
+
+ def __init__(self, participants, evaluation_model):
+ self.participants = participants
+ self.evaluation_model = evaluation_model
+
+ async def async_run(
+ self,
+ prompt_obj: ComparisonPromptBase = None,
+ semaphore: asyncio.Semaphore = None,
+ ) -> List[Dict]:
+ async def get_match_results(a, b) -> List[Dict]:
+ match = Match(A=a, B=b, eval_model=self.evaluation_model)
+ if self.evaluation_model == "debug":
+ winner, match_result = await match.async_dbg_eval()
+ else:
+ winner, match_result = await match.async_comp_eval(
+ comp_prompt=prompt_obj,
+ )
+
+ if winner is not None:
+ now_time = time()
+ # A vs B, B vs A
+ result = [
+ { # 여기서 *_a, *_b 는 prompt내의 position에 관한 것이다. Match.A, Match.B 가 아니다.
+ "task": a.task, # participant_pair[1].task
+ "model_a": a.model_id,
+ "model_b": b.model_id,
+ "winner": "A"
+ if match_result[0]["A"] > match_result[0]["B"]
+ else "B",
+ "prob_a": match_result[0]["A"],
+ "prob_b": match_result[0]["B"],
+ "evaluation_model": self.evaluation_model,
+ "instruction": a.instruction, # participant_pair[1].instruction,
+ "source": a.source, # participant_pair[1].source,
+ "generated_a": a.generated,
+ "generated_b": b.generated,
+ "round": "league",
+ "match_order_in_round": "league",
+ "tstamp": now_time,
+ # "logs": match.match_metainfo_log[0],
+ },
+ ]
+ else:
+ result = None
+ return result
+
+ match_jobs = []
+ for participant_pair in combinations(self.participants, 2):
+ match_jobs.append(
+ limited_coro(
+ get_match_results(participant_pair[0], participant_pair[1]),
+ semaphore,
+ )
+ )
+ # match_jobs.append( get_match_results(participant_pair[0], participant_pair[1]) )
+ result = await asyncio.gather(*match_jobs)
+
+ result = [
+ _result
+ for result_pair in result
+ if result_pair is not None
+ for _result in result_pair
+ ]
+
+ return result # returning only match result, not the winner's SimpleNamespace (because it would never be used.)
diff --git a/varco_arena/varco_arena_core/manager.py b/varco_arena/varco_arena_core/manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..886603f7d98f88f2a50e73a935aafa3d0b94adb5
--- /dev/null
+++ b/varco_arena/varco_arena_core/manager.py
@@ -0,0 +1,190 @@
+import asyncio
+import json
+import os
+from pathlib import Path
+from types import SimpleNamespace
+
+import numpy as np
+import pandas as pd
+from tqdm import tqdm
+from tqdm.asyncio import tqdm_asyncio
+
+from .elo import compute_relative_winrate_to_1st
+from .league import League
+from .prompts import ComparisonPromptBase, load_prompt
+from .tournament import Tournament
+from .visualization import visualization
+
+bar = None
+match_count = 0
+
+
+def increase_match_count():
+ global match_count
+ # await asyncio.sleep(0.001) # async로 수행하기 위해 1ms 지연
+ match_count += 1
+ global bar
+ if bar is not None:
+ bar.update(1)
+
+
+# def decrease_match_count():
+# global match_count
+# # await asyncio.sleep(0.001) # async로 수행하기 위해 1ms 지연
+# if match_count > 0:
+# match_count -= 1
+# global bar
+# if bar is not None:
+# bar.update(-1)
+
+
+class Manager:
+ MATCHING_CLASS = {
+ "tournament": Tournament,
+ "league": League,
+ }
+
+ def __init__(
+ self,
+ dataset_df,
+ output_dir,
+ evaluation_model,
+ matching_method,
+ n_jobs: int = 8,
+ evalprompt: str = None,
+ ):
+ self.dataframe = dataset_df
+ self.dataframe["prompt"] = (
+ self.dataframe.instruction + "\n\n" + self.dataframe.source
+ )
+
+ self.evaluation_model = evaluation_model
+ self.matching_method = matching_method
+
+ self.evalprompt = evalprompt
+ self.output_dir = Path(f"{output_dir}/{evalprompt}")
+
+ self.semaphore = asyncio.Semaphore(n_jobs)
+
+ def save_results(self, results):
+ while self.output_dir.exists():
+ self.output_dir = (
+ self.output_dir.parent / f"{self.output_dir.name}_"
+ ) # put trailing _
+ self.output_dir.mkdir(parents=True, exist_ok=True)
+ with open(self.output_dir / "result.json", "w", encoding="utf8") as w:
+ json.dump(results, w, indent=4, ensure_ascii=False)
+
+ def save_figure(self, fig_dict_per_task):
+ for task, figure_dict in fig_dict_per_task.items():
+ if task == "overall":
+ task = ""
+ os.makedirs(os.path.join(self.output_dir, task), exist_ok=True)
+ for figure_name, figure in figure_dict.items():
+ save_path = os.path.join(self.output_dir, task, f"{figure_name}.png")
+
+ # check data validity
+ # this part should later be refactored...
+ if "y" in figure.data[0] or "z" in figure.data[0]:
+ value_var = "z" if "z" in figure.data[0] else "y"
+ if np.isnan(figure.data[0][value_var]).all():
+ # all nan will throw error, tweak it.
+ # the figure here is heatmap of nxn row and column.
+ # warn the user and do not save the figure
+ save_path_err = Path(save_path).parent / (
+ Path(save_path).stem + "_err.txt"
+ )
+ err = f"all the data is NaN! abort saving \n{str(save_path)}"
+ print(err)
+ print(err, file=open(save_path_err, "w"))
+ else:
+ figure.write_image(save_path)
+
+ def save_elo_result(self, elo_df: pd.DataFrame, child_dir: str = "") -> None:
+ outdir = self.output_dir
+ if child_dir:
+ outdir = self.output_dir / child_dir
+ if not outdir.exists():
+ outdir.mkdir(exist_ok=True, parents=True)
+
+ xl = pd.ExcelWriter(outdir / "elo_result.xlsx", mode="w")
+ elo_df_wr = compute_relative_winrate_to_1st(elo_df)
+ elo_df_wr.to_excel(xl)
+ xl.close()
+ print(outdir / "elo_result.xlsx")
+
+ def visualize(self, results):
+ results = pd.DataFrame.from_dict(results)
+
+ # legacy code for varco-text-1.0 weights trained on varco-train
+ # just keep it for now...
+ results["model_a"] = (
+ results["model_a"]
+ .replace(r"(prism\d+|varcollm|lr|\d+e\d+)", "", regex=True)
+ .replace(r"\-|_", " ", regex=True)
+ .replace(" +", " ", regex=True)
+ )
+ results["model_b"] = (
+ results["model_b"]
+ .replace(r"(prism\d+|varcollm|lr|\d+e\d+)", "", regex=True)
+ .replace(r"\-|_", " ", regex=True)
+ .replace(" +", " ", regex=True)
+ )
+
+ fig_dict_per_task = {}
+
+ # visualize over total test inputs
+ fig_dict_per_task["overall"], elo_df = visualization(results, is_overall=True)
+
+ # split with "task" field and visualize individually
+ for task in results["task"].unique().tolist():
+ fig_dict_per_task[task], taskwise_elo = visualization(
+ results[results["task"] == task], is_overall=False
+ )
+ self.save_elo_result(taskwise_elo, child_dir=task)
+ # save figure
+ self.save_figure(fig_dict_per_task)
+ self.save_elo_result(elo_df)
+
+ async def async_run(self):
+ async_jobs = []
+ tasks = self.dataframe["task"].unique().tolist()
+ for task in tasks:
+ # filter dataframe for a certain task
+ task_dataframe = self.dataframe[self.dataframe["task"] == task]
+ tournament_idxs = task_dataframe.tournament_idx.unique()
+ taskwise_prompt_obj = load_prompt(self.evalprompt, task=task)
+ for tidx in tqdm(
+ tournament_idxs, desc=f"preparing {self.matching_method} for {task=}"
+ ):
+ # any generations in the same tournament_idx (from data_utils.py:index_row())
+
+ participants = [
+ SimpleNamespace(**obj)
+ for obj in task_dataframe.query(f"tournament_idx=={tidx}").to_dict(
+ orient="records"
+ )
+ ]
+ matching_class = self.MATCHING_CLASS[self.matching_method]
+ matching = matching_class(
+ participants=participants, evaluation_model=self.evaluation_model
+ )
+ async_jobs.append(
+ matching.async_run(
+ prompt_obj=taskwise_prompt_obj, semaphore=self.semaphore
+ )
+ )
+
+ global bar
+ bar = tqdm(
+ desc="Number of matches completed so far...",
+ leave=False,
+ unit="it",
+ position=1,
+ )
+
+ results = await tqdm_asyncio.gather(*async_jobs, desc="⚔️ Arena in progress...")
+ results = [result for result_list in results for result in result_list]
+
+ self.save_results(results)
+ self.visualize(results)
diff --git a/varco_arena/varco_arena_core/match.py b/varco_arena/varco_arena_core/match.py
new file mode 100644
index 0000000000000000000000000000000000000000..50820f604e0cf2de2dbc162b5247cabf816f87ee
--- /dev/null
+++ b/varco_arena/varco_arena_core/match.py
@@ -0,0 +1,264 @@
+import asyncio
+from functools import partial
+from random import random
+from types import SimpleNamespace
+from typing import Any, Dict, List, Literal, Optional, Tuple, Union
+
+from .eval_utils import async_eval_w_prompt
+
+
+class Match:
+ def __init__(
+ self,
+ A: SimpleNamespace = None,
+ eval_model: str = "gpt-3.5-turbo-0125",
+ B: Optional[SimpleNamespace] = None,
+ ):
+ """
+ No need to carry outputs nor generation configs
+ - if the outputs, and configurations are identifiable by modelA|Bname in the main system
+ """
+
+ self.A = A
+ self.B = B
+
+ self.eval_model: str = eval_model
+
+ # results
+ self.winner: Literal["A", "B"] = None
+ self.score: float = None # for abs_eval
+ self.match_result = []
+ self.match_metainfo_log = []
+ # promptname, evalmodel, cost, tokens
+ self.aggregated_result = None
+
+ async def async_comp_eval(
+ self,
+ position_swap: bool = False, # DEPRECATED (tested but not used anymore) --> refactor later
+ comp_prompt: Literal["llmbar_brief", "llmbar"] = "llmbar",
+ ) -> Tuple[str, List[Dict[str, float]]]:
+ """
+ returns:
+ winner = "A"
+ result= [{"A": 0.6, "B": 0.4}, {optional swap result: "A" will refer to the same model as the first one (prefer_2nd)}]
+ """
+
+ # sanity check:
+ if (
+ self.A.instruction != self.B.instruction
+ or self.A.source != self.B.source
+ or self.A.task != self.B.task
+ ):
+ raise ValueError(
+ f"A and B required to have the same inst/src: \n\t{A=}, \n\t{B=}"
+ )
+
+ jobs = []
+
+ evalf = partial(
+ async_eval_w_prompt,
+ prompt_obj=comp_prompt,
+ evalmodel=self.eval_model,
+ position_1st=self.A,
+ position_2nd=self.B,
+ )
+ jobs.append(evalf())
+
+ if position_swap:
+ evalf_swap = partial(
+ async_eval_w_prompt,
+ prompt_obj=comp_prompt,
+ evalmodel=self.eval_model,
+ position_1st=self.B, # B data into A position (swapped inputs)
+ position_2nd=self.A,
+ )
+ jobs.append(evalf_swap())
+
+ evaluation_results = await asyncio.gather(*jobs)
+
+ # log match_result in granular
+ match_result_, _tracking_obj, resp = evaluation_results[0]
+ match_result = match_result_.copy()
+ match_result["A"] = match_result.pop("prefer_1st", 0.0)
+ match_result["B"] = match_result.pop("prefer_2nd", 0.0)
+ match_result["output_text"] = (
+ resp.choices[0].message.content if resp is not None else "",
+ )
+ self.match_result.append(match_result)
+
+ if position_swap:
+ match_result_swap_, _tracking_obj_swap, resp_swap = evaluation_results[-1]
+ match_result_swap = match_result_swap_.copy()
+ match_result_swap["A"] = match_result_swap.pop(
+ "prefer_2nd", 0.0
+ ) # changing the name
+ match_result_swap["B"] = match_result_swap.pop("prefer_1st", 0.0)
+ match_result_swap["output_text"] = (
+ resp_swap.choices[0].message.content if resp_swap is not None else "",
+ )
+ self.match_result.append(match_result_swap)
+
+ # # token / cost logging
+ # cost: float = _tracking_obj.cost_in_usd(model=self.eval_model, silent=True)
+ # tokens: Dict[str, int] = _tracking_obj.print_summary(silent=True)
+ # self.log_metainfo(cost=cost, tokens=tokens, prompt_name=comp_prompt)
+
+ if position_swap:
+ # cost_swap = _tracking_obj_swap.cost_in_usd(
+ # model=self.eval_model, silent=True
+ # )
+ # tokens_swap = _tracking_obj_swap.print_summary(silent=True)
+ # self.log_metainfo(
+ # cost=cost_swap,
+ # tokens=tokens_swap,
+ # prompt_name=comp_prompt,
+ # )
+ pass
+
+ # aggregate and return
+ self.aggregated_result = self.aggregate_match_result()
+ self.winner = self.judge_winner()
+
+ return self.winner, self.match_result
+
+ async def async_dbg_eval(self, position_swap: bool = False):
+ # default
+ prefer_1st = random()
+ prefer_2nd = 1 - prefer_1st
+ await asyncio.sleep(random() * 2.0) # 0 ~ 2초 쉬기
+ default_result = {
+ "A": prefer_1st,
+ "B": prefer_2nd,
+ "model": "dbg",
+ "prompt_name": "noprompt",
+ "error": False,
+ "exception_str": "",
+ }
+ self.match_result.append(default_result)
+ # self.log_metainfo(cost=0.0, tokens=dict(), prompt_name="dbg")
+
+ if position_swap:
+ p = random()
+ swap_result = {
+ "A": p,
+ "B": 1 - p,
+ "model": "dbg",
+ "prompt_name": "noprompt",
+ "error": False,
+ "exception_str": "",
+ }
+ self.match_result.append(swap_result)
+
+ # self.log_metainfo(
+ # cost=0.0, tokens=dict(), prompt_name="dbg",
+ # )
+
+ self.aggregated_result = self.aggregate_match_result()
+ self.winner = self.judge_winner()
+
+ return self.winner, self.match_result
+
+ def aggregate_match_result(self) -> Dict[str, float]:
+ """
+ input:
+ [
+ {A:0.4, B:0.5, (otherkeys)...}, # one or two results
+ ]
+ output:
+ {
+ A: float
+ B: float
+ }
+
+ """
+ if not self.match_result:
+ raise ValueError("Match.comp_eval() need to be executed first!")
+
+ aggregate = self.match_result[0].copy()
+ if len(self.match_result) > 1:
+ aggregate["A"] += self.match_result[1]["A"]
+ aggregate["B"] += self.match_result[1]["B"]
+ aggregate = {k: v / 2 for k, v in aggregate.items() if k in "AB"}
+
+ error_exists = False
+ for res in self.match_result:
+ if res["error"]:
+ error_exists = res["error"]
+ break
+ aggregate["error"] = error_exists
+
+ return aggregate
+
+ def judge_winner(self) -> Literal["A", "B"]:
+ """
+ based on self.aggregated_result, judge final winner
+
+ input:
+ {
+ A:float
+ B:float
+ error:bool
+ }
+ output:
+ Literal[A,B]
+ """
+ if self.aggregated_result is None:
+ raise ValueError("Match.aggregate_math_result() need to be executed first!")
+
+ if self.aggregated_result["error"]:
+ winner = None
+ elif self.aggregated_result["A"] == self.aggregated_result["B"]:
+ winner = "A" if random() > 0.5 else "B"
+ else:
+ winner = (
+ "A"
+ if self.aggregated_result["A"] > self.aggregated_result["B"]
+ else "B"
+ )
+ return winner
+
+ # def log_metainfo(
+ # self,
+ # cost: float = 0.0,
+ # tokens: dict = None,
+ # prompt_name: str = None,
+ # position_swap: bool = False,
+ # ):
+ # metainfo_d = {
+ # "cost": cost,
+ # "tokens": tokens,
+ # "eval_prompt_name": prompt_name,
+ # "model": self.eval_model,
+ # "position_swap": position_swap,
+ # }
+ # self.match_metainfo_log.append(metainfo_d)
+
+
+if __name__ == "__main__":
+ from pprint import pprint
+
+ # unit test for match.py (class Match)
+ row1 = {
+ "model_id": "240413_dora",
+ "task": "늘려쓰기",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "우리 게임이 돈을 잘 번다면",
+ "generated": "만약 우리 게임이 유저들에게 높은 호응을 얻고 많은 사람들이 즐거운 시간을 보낼 수 있도록 서비스하고 있다면?",
+ }
+ row2 = {
+ "model_id": "manual",
+ "task": "늘려쓰기",
+ "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.",
+ "source": "우리 게임이 돈을 잘 번다면",
+ "generated": "만약 우리 게임이 더 많은 매출을 내고 더 많은 플레이어들이 유입되는 상황이라면?",
+ }
+ A = SimpleNamespace(**row1)
+ B = SimpleNamespace(**row2)
+ m = Match(A=A, B=B, eval_model="gpt-3.5-turbo-1106")
+ winner, result = m.async_comp_eval()
+ score, result_ = m.async_abs_eval()
+ print(winner)
+ print(result)
+ print(score)
+ print(result_)
+ pprint(m.match_metainfo_log)
diff --git a/varco_arena/varco_arena_core/prompts/__init__.py b/varco_arena/varco_arena_core/prompts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6558734bc4a75252a4fc6e8a1898d2d494a52bb7
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/__init__.py
@@ -0,0 +1,37 @@
+from token import NAME
+from typing import Literal
+
+from .base_prompt import ComparisonPromptBase
+
+# from .contextual_pair import *
+from .llmbar import LLMBarPrompt
+from .llmbar_brief import LLMBarBriefPrompt
+from .rag_pair_kr import RagPairKRPrompt
+from .translation_pair import TranslationPairPrompt
+
+NAME2PROMPT_CLS = dict(
+ llmbar_brief=LLMBarBriefPrompt(),
+ llmbar=LLMBarPrompt(),
+ translation_pair=TranslationPairPrompt(),
+ rag_pair_kr=RagPairKRPrompt(),
+ # contextual_vqa = Contextual_VQA(),
+ # contextual_ocr = Contextual_OCR(),
+)
+
+
+def load_prompt(
+ promptname: Literal[
+ "llmbar_brief",
+ "llmbar",
+ "translation_pair",
+ "rag_pair_kr",
+ # "contextual_pair"
+ ],
+ task: str = "", # used for further prompt variation (eval prompt might depend on task.)
+):
+ try: # first try with f"{promptname}_{task}"
+ prmobj = NAME2PROMPT_CLS[f"{promptname}_{task}"]
+ except:
+ prmobj = NAME2PROMPT_CLS[f"{promptname}"]
+ print(f"eval:{promptname} / task:{task} --> prompt:{prmobj.__class__.__name__}")
+ return prmobj
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/__init__.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..90273c0aa4adc0ca3b3a798c863610b6f41a0cf8
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/__init__.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/base_prompt.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/base_prompt.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5a0fcbade5aec95787c5b9c750f1a540de127086
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/base_prompt.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/llmbar.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/llmbar.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..64661d6d88600ba1998a3cd3edc5a5f73b642373
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/llmbar.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/llmbar_brief.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/llmbar_brief.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a09a4f73ab7143c864851c945f8f8c579774336a
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/llmbar_brief.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/naive_ab.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/naive_ab.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ff7744a8096d54202c443c575a93f055f2fe7c53
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/naive_ab.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/prompt_utils.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/prompt_utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1d8bf7d55a6e2756b47bf96675b77eceff7c0280
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/prompt_utils.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..543d4573c162741a81fc5966755d3ea3a6319060
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair_kr.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair_kr.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7d899496556656a4d695dc9d4d988e37a0785277
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/rag_pair_kr.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/__pycache__/translation_pair.cpython-311.pyc b/varco_arena/varco_arena_core/prompts/__pycache__/translation_pair.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..42cb38bd914b0459ec436ea8a9ef0eb5648ce5f4
Binary files /dev/null and b/varco_arena/varco_arena_core/prompts/__pycache__/translation_pair.cpython-311.pyc differ
diff --git a/varco_arena/varco_arena_core/prompts/base_prompt.py b/varco_arena/varco_arena_core/prompts/base_prompt.py
new file mode 100644
index 0000000000000000000000000000000000000000..092d39945a77c72171c6e76b9c8ede7b9be94f30
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/base_prompt.py
@@ -0,0 +1,142 @@
+from pathlib import Path
+from string import Template
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+import yaml
+
+from .prompt_utils import (
+ get_fields,
+ get_tokenizer_from_model_name,
+ num_tokens_from_string,
+)
+
+
+class ComparisonPromptBase:
+ def __init__(
+ self,
+ # reading from yaml
+ prompt_yaml: Optional[str] = None,
+ # explicitly passing variables
+ # stop: Union[ str, List[str] ] = None, # follows openai SDK default
+ # decision_tokens: Dict[str, str] = None,
+ # prompt_templates: List[Dict[str, str]] = None,
+ ):
+ self.prompt_yaml = prompt_yaml
+ (
+ self.sampling_parameters,
+ self.decision_tokens,
+ self.prompt_template,
+ self.expected_generation_str,
+ self._raw_dict,
+ ) = self.read_prompts_from_yaml(self.prompt_yaml)
+
+ @staticmethod
+ def read_prompts_from_yaml(
+ yamlf: str,
+ ) -> Tuple[
+ Union[str, List[str]],
+ Dict[str, str],
+ List[Dict[str, str]],
+ Dict[str, Any],
+ ]:
+ rel_path = Path(__file__).parent / yamlf
+ raw_d = yaml.full_load(open(rel_path, encoding="UTF8"))
+
+ sampling_parameters = raw_d["sampling_parameters"]
+
+ decision_tokens = raw_d["decision_tokens"]
+ prompt_template = [
+ {
+ "role": message["role"],
+ "content": Template(message["content"])
+ if get_fields(message["content"])
+ else message["content"],
+ }
+ for message in raw_d["prompt_template"]
+ ]
+
+ expected_generation_str = raw_d["expected_generation_str"]
+
+ return (
+ sampling_parameters,
+ decision_tokens,
+ prompt_template,
+ expected_generation_str,
+ raw_d,
+ )
+
+ def parsed_output(
+ self, response: Any
+ ) -> str: # compatible for both vllm, openai served models
+ """
+ parsing the decision tokens from the LLMjudge output regarding the prompt being used
+
+ e.g.
+ self.parsed_output("output (a) is better") -> "a"
+ """
+ raise NotImplementedError(
+ f"{__class__.name}.parsed_output() is not implemented!"
+ )
+
+ def prob_calibrated_output(self, response: Any) -> Dict[str, float]:
+ """
+ finding probability of `self.decision_tokens.values()`
+
+ from
+
+ response's logprob object
+ """
+
+ raise NotImplementedError(
+ f"{__class__.name}.prob_calibrated_output() is not implemented!"
+ )
+
+ def complete_prompt(
+ self,
+ inst: str = None,
+ src: str = None,
+ out_a: str = None,
+ out_b: str = None,
+ **kwargs,
+ ) -> str:
+ # inst_src from inst and src
+ def _combine_inst_src(inst: str, src: str) -> str:
+ pass
+
+ raise NotImplementedError(
+ f"{__class__.name}.complete_prompt() is not implemented!"
+ )
+
+ def get_expected_max_tokens_w_room(
+ self,
+ eval_model_name: str = None,
+ room: float = 1.33,
+ ):
+ if room < 1:
+ raise ValueError(
+ f"{room=} for max_tokens for prompting LLMs cannot be less than 1 which is the least n_toks to generate proper judgement."
+ )
+ # define or set tokenizer we are using
+ if hasattr(self, "tokenizer"):
+ _ = self.tokenizer
+ else:
+ self.__setattr__(
+ "tokenizer", get_tokenizer_from_model_name(eval_model_name)
+ )
+
+ # count tokens
+ n_max_tokens_just = num_tokens_from_string(
+ self.expected_generation_str,
+ tokenizer=self.tokenizer,
+ )
+
+ # make some room for to avoid unintended truncation
+ max_tokens_w_room = int(n_max_tokens_just * room)
+
+ return max_tokens_w_room
+
+ def get_num_tokens_from_messages(self, messages: List[Dict[str, str]]) -> int:
+ total_toks = 0
+ for msg in messages:
+ total_toks += num_tokens_from_string(msg["content"], self.tokenizer)
+ return total_toks
diff --git a/varco_arena/varco_arena_core/prompts/contextual_pair.py b/varco_arena/varco_arena_core/prompts/contextual_pair.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5347a3c620391c43f2a5cb9e07a018e4d44319d
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/contextual_pair.py
@@ -0,0 +1,5 @@
+from .base_prompt import ComparisonPromptBase
+
+
+class ContextualPairPrompt(ComparisonPromptBase):
+ raise NotImplementedError(f"Implementation required {__class__.__name__}")
diff --git a/varco_arena/varco_arena_core/prompts/contextual_pair.yaml b/varco_arena/varco_arena_core/prompts/contextual_pair.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a843897812854b461d486e6ab222221558b300e1
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/contextual_pair.yaml
@@ -0,0 +1,25 @@
+# refer to DEPRECATED/contextual_pair_.py to complete the prompt
+
+sampling_parameters:
+ stop: [] # empty list
+ temperature: 1.0
+ logprobs: true
+ top_logprobs: 20
+
+
+decision_tokens:
+ prefer_1st: a
+ prefer_2nd: b
+
+expected_generation_str: |
+ Output (a)
+
+prompt_template:
+ -
+ role: system
+ content: |
+ EMPTY
+ -
+ role: user
+ content: |
+ EMPTY
diff --git a/varco_arena/varco_arena_core/prompts/criteria_questions/others_criteria.md b/varco_arena/varco_arena_core/prompts/criteria_questions/others_criteria.md
new file mode 100644
index 0000000000000000000000000000000000000000..965f15839df2fa2b22662a77541a3b8bfd711bbd
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/criteria_questions/others_criteria.md
@@ -0,0 +1,7 @@
+* Does the output well satisfy the intent of the user request?
+* If applicable, is the output well-grounded in the given context information?
+* Does the output itself satisfy the requirements of good writing in terms of:
+ 1) Coherence
+ 2) Logicality
+ 3) Plausibility
+ 4) Interestingness
diff --git a/varco_arena/varco_arena_core/prompts/criteria_questions/summary_criteria.md b/varco_arena/varco_arena_core/prompts/criteria_questions/summary_criteria.md
new file mode 100644
index 0000000000000000000000000000000000000000..ddbdbca46aac2b40fc402ef22ebc1d690c200e78
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/criteria_questions/summary_criteria.md
@@ -0,0 +1,2 @@
+* Does the summary include the key points and main ideas of the article?
+* Does the summary accurately preserve the original meaning and content of the article?
diff --git a/varco_arena/varco_arena_core/prompts/criteria_questions/tactan_chat_summary_criteria.md b/varco_arena/varco_arena_core/prompts/criteria_questions/tactan_chat_summary_criteria.md
new file mode 100644
index 0000000000000000000000000000000000000000..c8d608457874f0c8c55d11f9679a9472b979eb33
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/criteria_questions/tactan_chat_summary_criteria.md
@@ -0,0 +1,4 @@
+* Does the summary accurately convey the information from the conversation, ensuring no misinformation regarding speakers or facts?
+* Is the summary comprehensive, providing all necessary details so that no further explanation is needed, thereby giving a clear picture of the events?
+* Is the summary effective in capturing the tone and atmosphere of the original conversation, aiding users in catching up?
+* Is the summary concise yet informative, ensuring all key points are included without overwhelming the reader?
diff --git a/varco_arena/varco_arena_core/prompts/llmbar.py b/varco_arena/varco_arena_core/prompts/llmbar.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ca4f672f8d9b6f641253a2d8ae9001a522f0965
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/llmbar.py
@@ -0,0 +1,161 @@
+from pathlib import Path
+from typing import Dict, List, Optional
+
+import numpy as np
+
+from .base_prompt import ComparisonPromptBase
+from .prompt_utils import (
+ fill_template_over_messsages,
+ find_logprob_of_a_token_openai,
+ is_model_from_openai,
+ top_logprob_of_a_token_vllm,
+)
+
+
+class LLMBarPrompt(ComparisonPromptBase):
+ def __init__(self, prompt_yaml: str = "llmbar.yaml"):
+ super().__init__(prompt_yaml=prompt_yaml)
+
+ def parsed_output(
+ self, response
+ ) -> str: # compatible for both vllm, openai served models
+ """
+ self.parsed_output("output (a) is better") -> "a"
+
+ 1. detect (a|b)
+ 2. get 'a|b' only
+ """
+ raw_output = response.choices[0].message.content
+ for target_find in self.decision_tokens.values():
+ idx = raw_output.find(f"({target_find})")
+ if idx != -1:
+ break
+ res_tok = raw_output[idx + 1]
+ return res_tok
+
+ def prob_calibrated_output(self, response) -> Dict[str, float]:
+ """
+ ChatCompletionTokenLogProb:
+ token: str
+ logprob: float
+ top_logprobs: List[
+ TopLogprob:
+ token: str
+ logprob: float
+ ]
+ or
+ vllm response object (logprob struct differs)
+
+ """
+ logprobs = response.choices[0].logprobs.content
+
+ # focus to the token of interest
+ # NOTE: res_tok is not guaranteed to follow the tokenization of the model, it just checks whether our output follows the expected format
+ res_tok = self.parsed_output(response)
+ if res_tok == None: # if not found, return None
+ return None
+
+ isopenai: bool = is_model_from_openai(response=response)
+ found_tokens: list = []
+ if isopenai:
+ top_logprob_list = find_logprob_of_a_token_openai(
+ logprobs=logprobs, token=res_tok
+ ).top_logprobs
+ # explore the `top_logprobs_list` list and then gather
+ probs_of_interest_pos = dict()
+ for k, tok in self.decision_tokens.items():
+ lp_obj = find_logprob_of_a_token_openai(
+ logprobs=top_logprob_list, token=tok
+ )
+
+ if lp_obj is not None:
+ logp = lp_obj.logprob
+ found_tokens.append(tok)
+
+ p = np.exp(logp)
+ probs_of_interest_pos[k] = p # prefer_1st = a_prob
+ else:
+ # vllm/openai=0.4.2, 0.5.5
+ probs_of_interest_pos = dict()
+ top_logprobs_d = top_logprob_of_a_token_vllm(
+ response=response, token=res_tok
+ )
+ for k, tok in self.decision_tokens.items():
+ if tok in top_logprobs_d.keys():
+ probs_of_interest_pos[k] = np.exp(top_logprobs_d[tok])
+ found_tokens.append(tok)
+
+ # normalize
+ norm = sum(
+ probs_of_interest_pos.values()
+ ) # even for empty dict, this wouldn't cause divbyzero error
+ probs_normalized = {k: v / norm for k, v in probs_of_interest_pos.items()}
+ probs_normalized["target_tokens_found"] = found_tokens
+ probs_normalized["no_target_tokens_found"] = not bool(found_tokens)
+ probs_normalized["model"] = response.model
+
+ if not found_tokens: # empty dict, no toks in self.decision_tokens.values()
+ # for the ease of downstream processings: set values as exact .5 (tie case)
+ probs_normalized["prefer_1st"] = 0.5
+ probs_normalized["prefer_2nd"] = 0.5
+
+ return probs_normalized
+
+ def complete_prompt(
+ self,
+ inst: str = None,
+ src: str = None,
+ out_a: str = None,
+ out_b: str = None,
+ task: Optional[str] = None,
+ criteria_questions: Optional[str] = None,
+ ) -> List[Dict]:
+ if (criteria_questions and task) or criteria_questions is None and task is None:
+ raise ValueError(
+ f"pass one of `{criteria_questions=}` or `{task=}` to {__class__.__name__}.complete_prompt()"
+ )
+
+ if task:
+ criteria_questions = self.get_criteria_questions(task)
+ else:
+ assert (
+ criteria_questions
+ ), f"{criteria_questions=} shouldn't be empty string! or pass the task!"
+
+ # inst_src from inst and src
+ def _combine_inst_src(inst, src):
+ if not inst:
+ return src
+ elif not src:
+ return inst
+ else:
+ inst_src = f"{inst}\n\n{src}"
+ return inst_src
+
+ inst_src = _combine_inst_src(inst, src)
+
+ kwargs_to_fill = dict(
+ inst_src=inst_src,
+ out_a=out_a,
+ out_b=out_b,
+ criteria_questions=criteria_questions,
+ )
+ complete_prm = fill_template_over_messsages(
+ self.prompt_template, **kwargs_to_fill
+ )
+
+ return complete_prm
+
+ @staticmethod
+ def get_criteria_questions(task: str = None):
+ # relative read
+ cqs_parent = Path(__file__).resolve().parent / "criteria_questions/"
+ if task is None:
+ raise ValueError(f"{task=} need to be in string")
+ elif task in ["summarize", "요약하기", "summary"]:
+ cqs = open(cqs_parent / "summary_criteria.md").read()
+ elif task in ["tactan_chat"]:
+ cqs = open(cqs_parent / "tactan_chat_summary_criteria.md").read()
+ else:
+ cqs = open(cqs_parent / "others_criteria.md").read()
+ return cqs.strip()
diff --git a/varco_arena/varco_arena_core/prompts/llmbar.yaml b/varco_arena/varco_arena_core/prompts/llmbar.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..31c3389f2876577b3ad0b89cae4ac2446d8c8430
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/llmbar.yaml
@@ -0,0 +1,46 @@
+sampling_parameters:
+ stop: [] # empty list
+ temperature: 1.0
+ logprobs: true
+ top_logprobs: 20
+
+decision_tokens:
+ prefer_1st: a
+ prefer_2nd: b
+
+expected_generation_str: |
+ Output (a)
+
+prompt_template:
+ -
+ role: system
+ content: |
+ You are a helpful assistant in evaluating the quality of the outputs for a given instruction. Your goal is to select the best output for the given instruction.
+ -
+ role: user
+ content: |
+ Select the Output (a) or Output (b) that is better for the given instruction. The two outputs are generated by two different AI chatbots respectively.
+
+ Here are some rules of the evaluation:
+ (1) You should prioritize evaluating whether the output honestly/precisely/closely executes the instruction, then consider its helpfulness, accuracy, level of detail, harmlessness, etc.
+ (2) Outputs should NOT contain more/less than what the instruction asks for, as such outputs do NOT precisely execute the instruction.
+ (3) You should avoid any potential bias and your judgment should be as objective as possible. For example, the order in which the outputs were presented should NOT affect your judgment, as Output (a) and Output (b) are **equally likely** to be the better.
+
+ Do NOT provide any explanation for your choice.
+ Do NOT say both / neither are good.
+ You should answer using ONLY "Output (a)" or "Output (b)". Do NOT output any other words.
+
+ # Instruction:
+ ${inst_src}
+
+ # Output (a):
+ ${out_a}
+
+ # Output (b):
+ ${out_b}
+
+ # Questions about Outputs:
+ Here are at most three questions about the outputs, which are presented from most important to least important. You can do the evaluation based on thinking about all the questions.
+ ${criteria_questions}
+
+ # Which is better, Output (a) or Output (b)? Your response should be either "Output (a)" or "Output (b)":
diff --git a/varco_arena/varco_arena_core/prompts/llmbar_brief.py b/varco_arena/varco_arena_core/prompts/llmbar_brief.py
new file mode 100644
index 0000000000000000000000000000000000000000..05abb7c074ec9c84d116e5d5347e4d3adf5d719d
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/llmbar_brief.py
@@ -0,0 +1,21 @@
+from .llmbar import *
+
+
+class LLMBarBriefPrompt(LLMBarPrompt):
+ """
+ example:
+ prm_cls = LLMBarBriefPrompt()
+ completed_prompt = prm_cls.complete_prompt(
+ inst = "your instruction",
+ src = "maybe... some source passage to augment the instruction. you could put this combined with inst and ignore here",
+ out_a = "llm output 1",
+ out_b = "llm_output 2",
+ criteria_questions = "LLMBar prompt has this field to complete the eval prompt. You can instead pass task kwarg to replace this (see LLMBarPrompt.get_criteria_questions())"
+ )
+ """
+
+ def __init__(self, prompt_yaml: str = "llmbar_brief.yaml"):
+ super().__init__(prompt_yaml=prompt_yaml)
+
+
+# shares most of the features with LLMBarPrompt
diff --git a/varco_arena/varco_arena_core/prompts/llmbar_brief.yaml b/varco_arena/varco_arena_core/prompts/llmbar_brief.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..37330aad50c4b0fe12de36aa28af0516d132a087
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/llmbar_brief.yaml
@@ -0,0 +1,42 @@
+sampling_parameters:
+ stop: [] # empty list
+ temperature: 1.0
+ logprobs: true
+ top_logprobs: 20
+
+
+decision_tokens:
+ prefer_1st: a
+ prefer_2nd: b
+
+expected_generation_str: |
+ Output (a)
+
+prompt_template:
+ -
+ role: system
+ content: |
+ You are a helpful assistant in evaluating the quality of the outputs for a given instruction. Your goal is to select the best output for the given instruction.
+ -
+ role: user
+ content: |
+ Select the Output (a) or Output (b) that is better for the given instruction.
+
+ Do NOT provide any explanation for your choice.
+ Do NOT say both / neither are good.
+ You should answer using ONLY "Output (a)" or "Output (b)". Do NOT output any other words.
+
+ # Instruction:
+ ${inst_src}
+
+ # Output (a):
+ ${out_a}
+
+ # Output (b):
+ ${out_b}
+
+ # Questions about Outputs:
+ Here are questions about the outputs. You can do the evaluation based on thinking about all the questions.
+ ${criteria_questions}
+
+ # Which is better, Output (a) or Output (b)? Your response should be either "Output (a)" or "Output (b)":
diff --git a/varco_arena/varco_arena_core/prompts/prompt_utils.py b/varco_arena/varco_arena_core/prompts/prompt_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..edd580a23fa66d784ee2cc52a6e8a5fadc05f261
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/prompt_utils.py
@@ -0,0 +1,179 @@
+from copy import deepcopy
+from pathlib import Path
+from string import Formatter, Template
+from typing import Any, Callable, Dict, List, Optional, Union
+
+import tiktoken
+from transformers import AutoTokenizer
+
+FMTR = Formatter()
+
+
+def get_fields(txt: str, formatter: Formatter = FMTR) -> List[str]:
+ """
+ what fields does the string has to be filled (returns variable names for formatting)
+
+ get_fields("{all} is {well}")
+ >>> ["all", "well"]
+ """
+
+ fields = [fname for _, fname, _, _ in formatter.parse(txt) if fname]
+ return fields
+
+
+def fill_template_over_messsages(prompt_template: List[Dict], **kwargs):
+ """
+ keep the prompt_template intact, but returns the filled version of it with **kwargs
+ """
+ new_msgs = []
+ for msg in prompt_template:
+ if isinstance(msg["content"], Template):
+ msg1 = deepcopy(msg)
+ msg1["content"] = msg1["content"].safe_substitute(**kwargs)
+ else:
+ msg1 = msg
+ new_msgs.append(msg1)
+ return new_msgs
+
+
+def get_tokenizer_from_model_name(
+ model_name: Optional[str] = None,
+):
+ """
+ load tokenizers from `model_name`
+ 1. assume it is openai model -> try with tiktoken package
+ 2. if not, tries with transformers
+
+ """
+ tokenizer = None
+ if model_name is None:
+ raise ValueError(
+ f"pass `model_name:str (from openai or huggingface)` \nCurrently given: {model_name=}"
+ )
+ # load tokenizer
+ if model_name:
+ try: # assume openai model
+ tokenizer = tiktoken.encoding_for_model(model_name)
+ except: # assume huggingface tokenizer
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
+ if tokenizer is None:
+ raise ValueError(
+ f"failed to load tokenizer (get_tokenizer_from_model_name({model_name=})) -> {tokenizer=}"
+ )
+
+ return tokenizer
+
+
+def num_tokens_from_string(string, tokenizer):
+ if tokenizer is None:
+ return 0
+ return len(tokenizer.encode(string))
+
+
+FMTR = Formatter()
+
+
+## postprocessing logprobs
+def find_logprob_of_a_token(logprobs: List = None, token: str = None) -> Any:
+ """
+ if a token:str is found in the list of logprob objects, return the object
+
+ else
+ return None
+ """
+ for lp_obj in logprobs:
+ if lp_obj.token == token:
+ return lp_obj
+ return None
+
+
+def probs_dict_to_weighted_score(tok2prob: Dict, target_tokens_found: list = None):
+ """
+ only for scoring-framed prompt outputs
+ """
+ # scoretoks = tok2prob["target_tokens_found"]
+ calibrated_scores = [
+ float(k) * p for k, p in tok2prob.items() if k in target_tokens_found
+ ] # score prompts expects int
+ score = sum(calibrated_scores)
+ return score
+
+
+def is_model_from_openai(response: Any = None, model: str = None) -> bool:
+ if not model:
+ model: str = response.model
+ else:
+ pass
+ openai_prefixes = [
+ "gpt-3.5-",
+ "gpt-4",
+ ]
+
+ model_from_openai: bool = False
+ for pref in openai_prefixes:
+ if model.startswith(pref):
+ model_from_openai = True
+ break
+
+ return model_from_openai
+
+
+def find_logprob_of_a_token_openai(
+ logprobs: List = None, token: str = None
+) -> Any: # can return logprob object or nested logprob object
+ """
+ if a token:str is found in the list of logprob objects, return the object
+
+ else
+ return None
+ """
+ for lp_obj in logprobs:
+ if lp_obj.token == token:
+ return lp_obj
+ return None
+
+
+def top_logprob_of_a_token_vllm(
+ response: Any = None, token: str = None
+) -> Union[float, None]:
+ """
+ tokens: list = response.choices[0].logprobs.model_extra["tokens"]
+ top_logprobs: list[dict] = response.choices[0].logprobs.model_extra["top_logprobs"]\
+
+ # compatibility check (vllm versions)
+ - [x] vllm/openai v0.4.2 (sha256:a033e9c3f7e942a772d497a8fcf40c7be0de29d331e60e57c7d6c73520bb6f7e)
+ - [x] vllm/openai v0.5.5
+
+ """
+ logprobs = response.choices[0].logprobs
+ if hasattr(logprobs, "model_extra"):
+ tokens = logprobs.model_extra["tokens"]
+ top_logprobs = logprobs.model_extra["top_logprobs"]
+ else:
+ try:
+ tokens = logprobs.tokens
+ top_logprobs = logprobs.top_logprobs
+ except AttributeError as e:
+ tokens = [_content.token for _content in logprobs.content]
+ top_logprobs = [
+ {
+ top_logprob.token: top_logprob.logprob
+ for top_logprob in _content.top_logprobs
+ }
+ for _content in logprobs.content
+ ]
+ assert len(tokens) == len(top_logprobs), f"{len(top_logprobs)=} != {len(tokens)=}"
+
+ # find idx
+ try:
+ idx = tokens.index(token)
+ except:
+ idx = -1
+
+ # pick top_logprobs at the found token's position
+ if idx == -1:
+ top_logprobs_of_interest = None
+ else:
+ top_logprobs_of_interest: dict = top_logprobs[idx]
+
+ return top_logprobs_of_interest
diff --git a/varco_arena/varco_arena_core/prompts/rag_pair_kr.py b/varco_arena/varco_arena_core/prompts/rag_pair_kr.py
new file mode 100644
index 0000000000000000000000000000000000000000..791fda46babdbabb90c27d82d30bacb09d99a76e
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/rag_pair_kr.py
@@ -0,0 +1,46 @@
+from typing import *
+
+from . import LLMBarPrompt
+from .prompt_utils import fill_template_over_messsages
+
+
+class RagPairKRPrompt(LLMBarPrompt):
+ def __init__(self, prompt_yaml: str = "rag_pair_kr.yaml"):
+ super().__init__(prompt_yaml=prompt_yaml)
+
+ def parsed_output(
+ self,
+ response,
+ ) -> str:
+ """
+ 1. detect A|B and use it.
+ """
+ raw_output = response.choices[0].message.content
+ for target_find in self.decision_tokens.values():
+ idx = raw_output.find(f"({target_find})")
+ if idx != -1:
+ break
+ res_tok = raw_output[idx]
+ return res_tok
+
+ def complete_prompt(
+ self,
+ inst: str = None,
+ src: str = None,
+ out_a: str = None,
+ out_b: str = None,
+ **kwargs,
+ ) -> List[Dict]:
+ kwargs_to_fill = dict(
+ src=src,
+ inst=inst,
+ out_a=out_a,
+ out_b=out_b,
+ )
+ complete_prm = fill_template_over_messsages(
+ self.prompt_template, **kwargs_to_fill
+ )
+ print()
+ # breakpoint here to see whether prompt works well
+
+ return complete_prm
diff --git a/varco_arena/varco_arena_core/prompts/rag_pair_kr.yaml b/varco_arena/varco_arena_core/prompts/rag_pair_kr.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..527b8b5797714fb63ba07b5c8396a7c92b4c9234
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/rag_pair_kr.yaml
@@ -0,0 +1,52 @@
+sampling_parameters:
+ stop: [] # empty list
+ temperature: 1.0
+ logprobs: true
+ top_logprobs: 20
+
+
+decision_tokens:
+ prefer_1st: A
+ prefer_2nd: B
+
+expected_generation_str: | # 비용 예상에 활용됨
+ B
+
+prompt_template:
+ -
+ role: system
+ content: |
+ 대화에 대한 지시문과, 대화 내용, 그리고 대화 내용에 이어질 두 발화가 주어집니다.
+ 두 개의 발화 중 어떤 발화가 더 적절한지를 아래 주어지는 [평가 지표]에 따라 골라주세요.
+
+ [평가 지표]
+ 주어진 지식 반영 (Knowledge Groundedness):
+ 높은 점수 기준: 주어진 검색 결과들 중 적합한 정보를 발췌하여 제공, 발췌할만한 정보가 없을 경우 경우 잘 모른다고 답변
+ 낮은 점수 기준: 문맥과 무관하거나 주어진 검색 결과에는 없는 정보를 제공, 질문과 무관한 과다한 정보 제공
+
+ [평가 단계]
+ 문맥 이해: 이 단계는 대화를 이해하는 것이 중요하며, 발화가 문맥에 어떻게 부합하는지 평가할 기초가 됩니다.
+
+ 주어진 지식 확인: 제공된 지식이나 정보들이 대화의 맥락이나 진행에 얼마나 중요한지 연관성을 평가합니다.
+
+ 마지막 발화의 내용 분석: 발화가 문맥 및 주제에 부합하는지, 그리고 주어진 지식을 올바르게 반영하는지를 봅니다.
+
+ 적절성 평가: 대화의 문맥과 주어진 지식을 기반으로 마지막 발화의 적절성을 평가합니다. 마지말 발화가 대화의 흐름을 방해하지 않는 선에서 문맥과 주어진 정보를 잘 활용했는지 봅니다.
+
+ 대화의 문맥과 주어진 지식을 반영하는 발화가 바람직하며, 대화의 문맥이나 주어진 정보에 무관한/잘못된 정보를 제공하는 발화는 바람직하지 않습니다.
+
+ 출력은 A 또는 B 하나의 token으로만 이루어져야 합니다.
+ -
+ role: user
+ content: |
+ [주어진 지식]
+ ${src}
+
+ [직전 발화]
+ ${inst}
+
+ [다음 발화]
+ A: ${out_a}
+ B: ${out_b}
+
+ [평가 결과]
diff --git a/varco_arena/varco_arena_core/prompts/translation_pair.py b/varco_arena/varco_arena_core/prompts/translation_pair.py
new file mode 100644
index 0000000000000000000000000000000000000000..2dd533e0621ad7a7d96eda6c04823f2142068937
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/translation_pair.py
@@ -0,0 +1,71 @@
+import re
+from typing import Any
+
+from .llmbar import LLMBarPrompt
+from .prompt_utils import fill_template_over_messsages
+
+
+class TranslationPairPrompt(LLMBarPrompt):
+ def __init__(self, prompt_yaml: str = "translation_pair.yaml"):
+ super().__init__(prompt_yaml=prompt_yaml)
+
+ def parsed_output(self, response: Any) -> str:
+ input_string = response.choices[0].message.content
+ input_string = input_string.replace("'", "").replace('"', "")
+
+ # 정규 표현식 패턴 정의 (수정됨)
+ pattern = r"(\w+):\s*(?:\[\'?)?(Both(?: are equal)?|Translation \([ab]\)(?: is(?: o?v?erall)? better)?)(?:\'?\])?\.?"
+
+ # 정규 표현식으로 매칭
+ matches = re.findall(pattern, input_string)
+
+ # 결과 딕셔너리 생성
+ for key, value in matches:
+ # if value.startswith('Both'):
+ # result[key] = 'Both'
+ if "Translation (a)" in value:
+ res_tok = "a"
+ elif "Translation (b)" in value:
+ res_tok = "b"
+ return res_tok
+
+ def complete_prompt(
+ self,
+ inst: str = None,
+ src: str = None,
+ out_a: str = None,
+ out_b: str = None,
+ source_lang: str = None,
+ target_lang: str = None,
+ **kwargs,
+ ) -> str:
+ # inst_src from inst and src
+ def _combine_inst_src(inst, src):
+ if not inst:
+ return src
+ elif not src:
+ return inst
+ else:
+ inst_src = f"{inst}\n\n{src}"
+ return inst_src
+
+ inst_src = _combine_inst_src(inst, src)
+
+ kwargs_to_fill = dict(
+ inst_src=inst_src,
+ out_a=out_a,
+ out_b=out_b,
+ source_lang=source_lang,
+ target_lang=target_lang,
+ )
+ complete_prm = fill_template_over_messsages(
+ self.prompt_template, **kwargs_to_fill
+ )
+
+ return complete_prm
+
+ @staticmethod
+ def get_criteria_questions(task: str = None):
+ raise ValueError(
+ f"{__class__.__name__} does not require criteria questions to complete the prompt. It is for LLMBar prompt and its variants"
+ )
diff --git a/varco_arena/varco_arena_core/prompts/translation_pair.yaml b/varco_arena/varco_arena_core/prompts/translation_pair.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a58ff8cce5da489b32cc0b16c5a2bfbc677145b8
--- /dev/null
+++ b/varco_arena/varco_arena_core/prompts/translation_pair.yaml
@@ -0,0 +1,53 @@
+sampling_parameters:
+ stop: ["Naturalness:"]
+ temperature: 1.0
+ logprobs: true
+ top_logprobs: 20
+
+
+decision_tokens:
+ prefer_1st: a
+ prefer_2nd: b
+
+expected_generation_str: |
+