Spaces:
Running
Running
others
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README.md +122 -0
- README_kr.md +123 -0
- __pycache__/analysis_utils.cpython-311.pyc +0 -0
- __pycache__/app.cpython-311.pyc +0 -0
- __pycache__/query_comp.cpython-311.pyc +0 -0
- __pycache__/view_utils.cpython-311.pyc +0 -0
- analysis_utils.py +338 -0
- app.py +452 -0
- eval_models_list.txt +3 -0
- eval_prompt_list.txt +3 -0
- guide_mds/input_jsonls_en.md +37 -0
- guide_mds/input_jsonls_kr.md +34 -0
- modules/__pycache__/nav.cpython-311.pyc +0 -0
- modules/nav.py +57 -0
- pages/__pycache__/see_results.cpython-311.pyc +0 -0
- pages/brief_intro.py +40 -0
- pages/quick_start_guide.py +14 -0
- pages/see_results.py +365 -0
- precommit.sh +5 -0
- query_comp.py +137 -0
- requirements.txt +14 -0
- run.sh +4 -0
- streamlit_app_local/.streamlit/config.toml +4 -0
- streamlit_app_local/README.md +24 -0
- streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc +0 -0
- streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc +0 -0
- streamlit_app_local/__pycache__/app.cpython-311.pyc +0 -0
- streamlit_app_local/__pycache__/app.cpython-38.pyc +0 -0
- streamlit_app_local/__pycache__/query_comp.cpython-311.pyc +0 -0
- streamlit_app_local/__pycache__/view_utils.cpython-311.pyc +0 -0
- streamlit_app_local/__pycache__/view_utils.cpython-38.pyc +0 -0
- streamlit_app_local/analysis_utils.py +338 -0
- streamlit_app_local/app.py +318 -0
- streamlit_app_local/eval_models_list.txt +3 -0
- streamlit_app_local/eval_prompt_list.txt +5 -0
- streamlit_app_local/guide_mds +1 -0
- streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc +0 -0
- streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc +0 -0
- streamlit_app_local/modules/nav.py +46 -0
- streamlit_app_local/pages/brief_intro.py +40 -0
- streamlit_app_local/pages/see_results.py +391 -0
- streamlit_app_local/query_comp.py +135 -0
- streamlit_app_local/requirements.txt +5 -0
- streamlit_app_local/run.sh +3 -0
- streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl +6 -0
- streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl +6 -0
- streamlit_app_local/user_submit/llm/240413_dora.jsonl +6 -0
- streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl +6 -0
- streamlit_app_local/user_submit/llm/240413_full.jsonl +6 -0
- streamlit_app_local/user_submit/llm/240413_full_epoch_5.jsonl +6 -0
README.md
CHANGED
@@ -12,3 +12,125 @@ short_description: VARCO Arena is a reference-free LLM benchmarking approach
|
|
12 |
---
|
13 |
|
14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
---
|
13 |
|
14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
15 |
+
|
16 |
+
# Varco Arena
|
17 |
+
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.
|
18 |
+
|
19 |
+
For more information, the followings may help understanding how it works.
|
20 |
+
* [Paper](https://huggingface.co/papers/2411.01281)
|
21 |
+
* [Blog Post (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
22 |
+
|
23 |
+
|
24 |
+
## Quickstart
|
25 |
+
### Running Web Demo locally (streamlit, Recommended!)
|
26 |
+
```bash
|
27 |
+
git clone [THIS_REPO]
|
28 |
+
# install requirements below. we recommend miniforge to manage environment
|
29 |
+
cd streamlit_app_local
|
30 |
+
bash run.sh
|
31 |
+
```
|
32 |
+
For more details, see `[THIS_REPO]/streamlit_app_local/README.md`
|
33 |
+
|
34 |
+
### CLI use
|
35 |
+
* located at
|
36 |
+
* `varco_arena/`
|
37 |
+
* debug configurations for vscode at
|
38 |
+
* `varco_arena/.vscode`
|
39 |
+
```bash
|
40 |
+
## gpt-4o-mini as a judge
|
41 |
+
python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -m tournament -e "gpt-4o-mini"
|
42 |
+
## vllm-openai served LLM as a judge
|
43 |
+
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"
|
44 |
+
|
45 |
+
# dbg lines
|
46 |
+
## openai api judge dbg
|
47 |
+
python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
|
48 |
+
## other testing lines
|
49 |
+
python main.py -i "rsc/inputs_for_dbg/[SOME_DIRECTORY]/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
|
50 |
+
## dummy judge dbg (checking errors without api requests)
|
51 |
+
python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e debug
|
52 |
+
```
|
53 |
+
|
54 |
+
## Requirements
|
55 |
+
We tested this on `python = 3.11.9` env: `requirements.txt`
|
56 |
+
```
|
57 |
+
openai>=1.17.0
|
58 |
+
munch
|
59 |
+
pandas
|
60 |
+
numpy
|
61 |
+
tqdm>=4.48.0
|
62 |
+
plotly
|
63 |
+
scikit-learn
|
64 |
+
kaleido
|
65 |
+
tiktoken>=0.7.0
|
66 |
+
pyyaml
|
67 |
+
transformers
|
68 |
+
streamlit>=1.40.2
|
69 |
+
openpyxl
|
70 |
+
git+https://github.com/shobrook/openlimit.git#egg=openlimit # do not install this by pypi
|
71 |
+
|
72 |
+
# Linux
|
73 |
+
uvloop
|
74 |
+
# Windows
|
75 |
+
winloop
|
76 |
+
```
|
77 |
+
|
78 |
+
#### Argument
|
79 |
+
- -i, --input : directory path which contains input jsonlines files (llm outputs)
|
80 |
+
- -o, --output_dir : directory where results to be put
|
81 |
+
- -e, --evaluation : judge model specification (e.g. "gpt-4o-2024-05-13", "gpt-4o-mini", \[vllm-served-model-name\])
|
82 |
+
- -k, --openai_api_key : OpenAI API Key
|
83 |
+
- -u, --openai_url: URL to openai_styled_llm_server (requested by openai sdk)
|
84 |
+
|
85 |
+
#### advanced
|
86 |
+
- -j, --n_jobs : n jobs to be put to `asyncio.semaphore(n=)`
|
87 |
+
- -p, --evalprompt : [see the directory](./varco_arena/prompts/*.yaml)
|
88 |
+
- -lr, --limit_requests : vLLM OpenAI server request limit (default: 7,680)
|
89 |
+
- -lt, --limit_tokens : vLLM OpenAI server token limit (default: 15,728,640)
|
90 |
+
|
91 |
+
#### Input Data Format
|
92 |
+
[input jsonl guides](./streamlit_app_local/guide_mds/input_jsonls_en.md)
|
93 |
+
|
94 |
+
|
95 |
+
## Contributing & Customizing
|
96 |
+
#### Do this after git clone and installation
|
97 |
+
```bash
|
98 |
+
pip install pre-commit
|
99 |
+
pre-commit install
|
100 |
+
```
|
101 |
+
#### before commit
|
102 |
+
```bash
|
103 |
+
bash precommit.sh # black formatter will reformat the codes
|
104 |
+
```
|
105 |
+
|
106 |
+
## FAQ
|
107 |
+
* I want to apply my custom judge prompt to run Varco Arena
|
108 |
+
* [`./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.
|
109 |
+
* I want tailored judge prompts for each line of the test set row (i.e. ~100th row - `prompt1`, 101st~ - `prompt2`)
|
110 |
+
* 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).
|
111 |
+
* I want more fields for my llm outputs jsonl files for tailored use, i.e. want more fields beyond `instruction`, `source`, `generated`.
|
112 |
+
* It's going to get tricky but let me briefly guide you about this.
|
113 |
+
* You might have to edit `varco_arena/eval_utils.py`:`async_eval_w_prompt` (this part calls `PROMPT_OBJ.complete_prompt()`)
|
114 |
+
* And all the related codes will require revision.
|
115 |
+
|
116 |
+
## Special Thanks to (contributors)
|
117 |
+
- Minho Lee (@Dialogue Model Team, NCSOFT) [github](https://github.com/minolee/)
|
118 |
+
- query wrapper
|
119 |
+
- rag prompt
|
120 |
+
- Jumin Oh (@Generation Model Team, NCSOFT)
|
121 |
+
- overall prototyping of the system in haste
|
122 |
+
|
123 |
+
|
124 |
+
## Citation
|
125 |
+
If you found our work helpful, consider citing our paper!
|
126 |
+
```
|
127 |
+
@misc{son2024varcoarenatournamentapproach,
|
128 |
+
title={Varco Arena: A Tournament Approach to Reference-Free Benchmarking Large Language Models},
|
129 |
+
author={Seonil Son and Ju-Min Oh and Heegon Jin and Cheolhun Jang and Jeongbeom Jeong and Kuntae Kim},
|
130 |
+
year={2024},
|
131 |
+
eprint={2411.01281},
|
132 |
+
archivePrefix={arXiv},
|
133 |
+
primaryClass={cs.CL},
|
134 |
+
url={https://arxiv.org/abs/2411.01281},
|
135 |
+
}
|
136 |
+
```
|
README_kr.md
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Varco Arena
|
2 |
+
바르코 아레나는 테스트셋 명령어별로 비교할 모델들의 토너먼트를 수행하여 정확하게 모델들의 순위를 매깁니다. 이것은 reference 아웃풋과 비교하여 승률을 매기는 방법보다 정확하며 조금 더 저렴합니다.
|
3 |
+
|
4 |
+
더 자세한 내용에 대해서는 아래의 링크를 참조하시면 됩니다.
|
5 |
+
* [논문](https://huggingface.co/papers/2411.01281)
|
6 |
+
* [엔씨소프트 테크블로그 (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
7 |
+
|
8 |
+
|
9 |
+
## Quickstart
|
10 |
+
### 로컬에서 스트림릿 앱으로 시작하기 (추천!)
|
11 |
+
```bash
|
12 |
+
git clone [THIS_REPO]
|
13 |
+
# install requirements below. we recommend miniforge to manage environment
|
14 |
+
cd streamlit_app_local
|
15 |
+
bash run.sh
|
16 |
+
```
|
17 |
+
더 자세한 내용은 `[THIS_REPO]/streamlit_app_local/README.md` 을 참조하세요!
|
18 |
+
|
19 |
+
### CLI 사용
|
20 |
+
* cli와 웹 앱은 서로 같은 코드를 활용하며, 아래의 디렉토리에 있습니다.
|
21 |
+
* `varco_arena/`
|
22 |
+
* vscode 상에서 디버깅을 위한 프리셋 프롬프트별 테스트 명령어는 다음 파일에 적혀있습니다.
|
23 |
+
* `varco_arena/.vscode/launch.json`
|
24 |
+
```bash
|
25 |
+
## gpt-4o-mini as a judge
|
26 |
+
python main.py -i "./some/dirpath/to/jsonl/files" -o SOME_REL_PATH_TO_CREATE -m tournament -e "gpt-4o-mini"
|
27 |
+
## vllm-openai served LLM as a judge
|
28 |
+
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"
|
29 |
+
|
30 |
+
# dbg lines
|
31 |
+
## openai api judge dbg
|
32 |
+
python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
|
33 |
+
## other testing lines
|
34 |
+
python main.py -i "rsc/inputs_for_dbg/[SOME_DIRECTORY]/" -o SOME_WANTED_TARGET_DIR -e gpt-4o-mini
|
35 |
+
## dummy judge dbg (checking errors without api requests)
|
36 |
+
python main.py -i "rsc/inputs_for_dbg/dbg_400_error_inputs/" -o SOME_WANTED_TARGET_DIR -e debug
|
37 |
+
```
|
38 |
+
|
39 |
+
## Requirements
|
40 |
+
`python = 3.11.9` 상에서 테스트 함. `requirements.txt`
|
41 |
+
```
|
42 |
+
openai>=1.17.0
|
43 |
+
munch
|
44 |
+
pandas
|
45 |
+
numpy
|
46 |
+
tqdm>=4.48.0
|
47 |
+
plotly
|
48 |
+
scikit-learn
|
49 |
+
kaleido
|
50 |
+
tiktoken>=0.7.0
|
51 |
+
pyyaml
|
52 |
+
transformers
|
53 |
+
streamlit>=1.40.2
|
54 |
+
openpyxl
|
55 |
+
git+https://github.com/shobrook/openlimit.git#egg=openlimit # do not install this by pypi
|
56 |
+
|
57 |
+
# Linux인 경우
|
58 |
+
uvloop
|
59 |
+
# Windows인 경우
|
60 |
+
winloop
|
61 |
+
```
|
62 |
+
|
63 |
+
#### Argument
|
64 |
+
- -i, --input : 입력 파일 or 디렉토리 or 파일명에 대한 정규 표현식
|
65 |
+
- -o, --output_dir : 출력 파일이 저장되는 디렉토리
|
66 |
+
- -e, --evaluation : 평가 모델 (e.g. "gpt-4o-2024-05-13", "gpt-4o-mini", vllm에서 띄운 모델 명 등)
|
67 |
+
- -m, --matching_method: 매치 방식 (기본값 "tournament", "league" (비추천) )
|
68 |
+
- -k, --openai_api_key : OpenAI API Key
|
69 |
+
- -u, --openai_url: 로컬 vLLM OpenAI 서버 사용 시 URL(ip주소+포트)
|
70 |
+
|
71 |
+
#### advanced
|
72 |
+
- -j, --n_jobs : asyncio.semaphore()에 전달될 인자. Arena가 진행되지 않는다면 기본값인 32 이하로 내려보자
|
73 |
+
- -p, --evalprompt : [해당 디렉토리 참조](./varco_arena/prompts/*.yaml)
|
74 |
+
- -lr, --limit_requests : vLLM OpenAI 서버 요청 제한 (default: 7,680)
|
75 |
+
- -lt, --limit_tokens : vLLM OpenAI 서버 토큰 제한 (default: 15,728,640)
|
76 |
+
|
77 |
+
#### Input Data Format
|
78 |
+
[input jsonl 가이드 링크](./streamlit_app_local/guide_mds/input_jsonls_kr.md)
|
79 |
+
|
80 |
+
|
81 |
+
|
82 |
+
## Contributing & Customizing
|
83 |
+
#### git clone 및 dependency 설치 후에 할 일
|
84 |
+
```bash
|
85 |
+
pip install pre-commit
|
86 |
+
pre-commit install
|
87 |
+
```
|
88 |
+
#### commit 하기 전에 할 일
|
89 |
+
```bash
|
90 |
+
bash precommit.sh # 이게 코드들을 다 리포맷해줄거임
|
91 |
+
```
|
92 |
+
|
93 |
+
|
94 |
+
문의: 손선일
|
95 |
+
* 내가 만든 프롬프트를 사용하고 싶어요
|
96 |
+
* [`./varco_arena/prompts/`](./varco_arena_core/prompts/__init__.py) 에선 각종 프롬프트 클래스 및 `yaml` 파일 형태로 정의된 프롬프트를 로드합니다. 프리셋을 참조하여 작성하시면 됩니다.
|
97 |
+
* 테스트셋 별로 다른 평가 프롬프트를 사용하고 싶어요 (e.g. 작업에 따라 다른 프롬프트를 사용하고 싶어요)
|
98 |
+
* 위 걸어드린 링크의 `load_prompt` 를 통해서 `promptname` + `task` 형태로 [`./varco_arena_core/manager.py:async_run`](./varco_arena_core/manager.py) 프롬프트가 로드되도록 해놓았습니다.
|
99 |
+
* 제가 사용하고 싶은 입력파일에 `instruction`, `source`, `generated` 이외에 다른 필드를 추가해서 사용하고 싶어요.
|
100 |
+
* 조금 복잡해지는데 다음 부분을 고쳐주세요
|
101 |
+
* `varco_arena/eval_utils.py` 에서 `async_eval_w_prompt` 부분을 손봐야할 수 있습니다 (여기에서 PROMPT_OBJ.complete_prompt()을 호출함)
|
102 |
+
* 그 외 연관된 부분은 타고타고 고쳐주셔야...
|
103 |
+
|
104 |
+
## Special Thanks to (contributors)
|
105 |
+
- 이민호 (@대화모델팀, NCSOFT) [github](https://github.com/minolee/)
|
106 |
+
- query wrapper
|
107 |
+
- rag prompt
|
108 |
+
- 오주민 (@생성모델팀, NCSOFT)
|
109 |
+
- overall prototyping of the system in haste
|
110 |
+
|
111 |
+
## Citation
|
112 |
+
저희 작업물이 도움이 되었다면 저희도 도움을 받아볼 수 있을까요?😉
|
113 |
+
```
|
114 |
+
@misc{son2024varcoarenatournamentapproach,
|
115 |
+
title={Varco Arena: A Tournament Approach to Reference-Free Benchmarking Large Language Models},
|
116 |
+
author={Seonil Son and Ju-Min Oh and Heegon Jin and Cheolhun Jang and Jeongbeom Jeong and Kuntae Kim},
|
117 |
+
year={2024},
|
118 |
+
eprint={2411.01281},
|
119 |
+
archivePrefix={arXiv},
|
120 |
+
primaryClass={cs.CL},
|
121 |
+
url={https://arxiv.org/abs/2411.01281},
|
122 |
+
}
|
123 |
+
```
|
__pycache__/analysis_utils.cpython-311.pyc
ADDED
Binary file (17.7 kB). View file
|
|
__pycache__/app.cpython-311.pyc
ADDED
Binary file (22.3 kB). View file
|
|
__pycache__/query_comp.cpython-311.pyc
ADDED
Binary file (7.99 kB). View file
|
|
__pycache__/view_utils.cpython-311.pyc
ADDED
Binary file (18.3 kB). View file
|
|
analysis_utils.py
ADDED
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from copy import deepcopy
|
2 |
+
from functools import partial
|
3 |
+
from typing import *
|
4 |
+
|
5 |
+
import pandas as pd
|
6 |
+
from fire import Fire
|
7 |
+
|
8 |
+
"""
|
9 |
+
This code assumes dealing with only one instruction
|
10 |
+
"""
|
11 |
+
|
12 |
+
|
13 |
+
# from varco_arena.tournament
|
14 |
+
def log2_power_of_two(n):
|
15 |
+
# First, let's make sure n is indeed a power of 2
|
16 |
+
if n & (n - 1) != 0 or n == 0:
|
17 |
+
raise ValueError("n must be a positive power of 2")
|
18 |
+
|
19 |
+
exponent = 0
|
20 |
+
while n > 1:
|
21 |
+
n >>= 1 # Right shift is like dividing by 2, but faster
|
22 |
+
exponent += 1
|
23 |
+
return exponent
|
24 |
+
|
25 |
+
|
26 |
+
def get_1st(df: pd.DataFrame, alpha2names: dict) -> Optional[str]:
|
27 |
+
finals = df[df["round"] == "final"]
|
28 |
+
if len(finals) == 1:
|
29 |
+
first = finals.iloc[0].winner_resolved
|
30 |
+
else:
|
31 |
+
first = (
|
32 |
+
None # error case (no finals match or multiple finals (buggy result file))
|
33 |
+
)
|
34 |
+
|
35 |
+
return first
|
36 |
+
|
37 |
+
|
38 |
+
def get_unique_participants(df: pd.DataFrame) -> list:
|
39 |
+
participants = pd.concat([df.model_a, df.model_b]).unique().tolist()
|
40 |
+
participants = [p for p in participants if p] # remove None
|
41 |
+
participants = sorted(participants) # make it sorted
|
42 |
+
return participants
|
43 |
+
|
44 |
+
|
45 |
+
def _impute_byes(df):
|
46 |
+
max_depth = df.depth.max()
|
47 |
+
|
48 |
+
# init
|
49 |
+
imputed_parts = dict()
|
50 |
+
for depth in range(max_depth + 1):
|
51 |
+
imputed_parts[depth] = df[df.depth == depth].copy()
|
52 |
+
|
53 |
+
# reverse
|
54 |
+
for depth in range(max_depth, 0, -1): # always we have 1 proper match for depth=0
|
55 |
+
null_v_null = {
|
56 |
+
"model_a": "",
|
57 |
+
"model_b": "",
|
58 |
+
"winner": "model_a",
|
59 |
+
"match_order_in_round": "-",
|
60 |
+
"depth": depth,
|
61 |
+
}
|
62 |
+
|
63 |
+
# fill some_model vs null byes
|
64 |
+
players = get_unique_participants(imputed_parts[depth])
|
65 |
+
proceeded = get_unique_participants(imputed_parts[depth - 1])
|
66 |
+
imputed = []
|
67 |
+
for p in proceeded:
|
68 |
+
if p not in players:
|
69 |
+
p_v_null = deepcopy(null_v_null)
|
70 |
+
p_v_null["model_a"] = p
|
71 |
+
imputed.append(p_v_null)
|
72 |
+
imputed_parts[depth] = pd.concat(
|
73 |
+
[
|
74 |
+
imputed_parts[depth],
|
75 |
+
pd.DataFrame(imputed),
|
76 |
+
],
|
77 |
+
axis="index",
|
78 |
+
)
|
79 |
+
|
80 |
+
# fill null vs null
|
81 |
+
n_null_v_null = 2 ** (depth) - len(imputed_parts[depth])
|
82 |
+
if n_null_v_null > 0:
|
83 |
+
imputed = pd.DataFrame([null_v_null] * n_null_v_null)
|
84 |
+
imputed_parts[depth] = pd.concat(
|
85 |
+
[
|
86 |
+
imputed_parts[depth],
|
87 |
+
imputed,
|
88 |
+
],
|
89 |
+
axis="index",
|
90 |
+
)
|
91 |
+
|
92 |
+
df_imputed = pd.concat(imputed_parts.values(), axis="index")
|
93 |
+
df_imputed = df_imputed.sort_values(by="depth").reset_index(drop=True)
|
94 |
+
|
95 |
+
return df_imputed
|
96 |
+
|
97 |
+
|
98 |
+
def index_test_scenario(df) -> pd.DataFrame:
|
99 |
+
df["inst_src"] = "inst: " + df.instruction + "\n\nsrc: " + df.source
|
100 |
+
df["idx_inst_src"] = df.apply(
|
101 |
+
lambda row: f"{row.tournament_idx}:\n{row.inst_src}", axis=1
|
102 |
+
)
|
103 |
+
|
104 |
+
# later used for tournament bracket backtrackiung
|
105 |
+
if "depth" not in df.columns:
|
106 |
+
mappings = {
|
107 |
+
"final": 0,
|
108 |
+
"semi-final": 1,
|
109 |
+
"quarter-final": 2,
|
110 |
+
}
|
111 |
+
|
112 |
+
def _convert_round_to_depth(rnd: str, mappings=None) -> int:
|
113 |
+
if rnd is None:
|
114 |
+
depth = None
|
115 |
+
elif rnd in mappings.keys():
|
116 |
+
depth = mappings[rnd]
|
117 |
+
elif rnd.startswith("round-"): # assume perfect power of two
|
118 |
+
num = int(rnd.replace("round-", "").strip())
|
119 |
+
depth = log2_power_of_two(num) - 1
|
120 |
+
return depth
|
121 |
+
|
122 |
+
conv = partial(_convert_round_to_depth, mappings=mappings)
|
123 |
+
df["depth"] = df["round"].apply(conv)
|
124 |
+
|
125 |
+
return df
|
126 |
+
|
127 |
+
|
128 |
+
def init_tournament_dataframe(df, alpha2names: dict = None) -> pd.DataFrame:
|
129 |
+
df = df.sort_values(by="depth").reset_index(drop=True)
|
130 |
+
# make winner interpretable (A -> model_a, B -> model_b)
|
131 |
+
df.winner = df.winner.apply(lambda txt: f"model_{txt.lower()}")
|
132 |
+
|
133 |
+
# define alpha2names if not given (covers upto 168 participants)
|
134 |
+
if alpha2names is None:
|
135 |
+
alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
136 |
+
abcdefghijklmnopqrstuvwxyz\
|
137 |
+
ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ\
|
138 |
+
㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭\
|
139 |
+
㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻\
|
140 |
+
ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ\
|
141 |
+
ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ\
|
142 |
+
αβγδεζηθικλμνξοπρστυφχψω"
|
143 |
+
model_full_names = get_unique_participants(df)
|
144 |
+
alpha2names = dict(zip(alphabets, model_full_names))
|
145 |
+
if len(alpha2names) < len(model_full_names):
|
146 |
+
raise ValueError(
|
147 |
+
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."
|
148 |
+
)
|
149 |
+
names2alpha = dict(zip(alpha2names.values(), alpha2names.keys()))
|
150 |
+
df = _impute_byes(df)
|
151 |
+
|
152 |
+
# preserve readables for later
|
153 |
+
df = _make_readables(df, names2alpha)
|
154 |
+
|
155 |
+
if len(df[df["round"] == "final"]) != 1:
|
156 |
+
raise ValueError(f"final match need to be one and only.")
|
157 |
+
|
158 |
+
return df, alpha2names
|
159 |
+
|
160 |
+
|
161 |
+
def _make_readables(df, names2alpha):
|
162 |
+
df["human_readable_model_a"] = df.model_a.copy()
|
163 |
+
df["human_readable_model_b"] = df.model_b.copy()
|
164 |
+
|
165 |
+
df.model_a = df.model_a.apply(
|
166 |
+
lambda modelname: names2alpha[modelname] if modelname else "x"
|
167 |
+
)
|
168 |
+
df.model_b = df.model_b.apply(
|
169 |
+
lambda modelname: names2alpha[modelname] if modelname else "x"
|
170 |
+
)
|
171 |
+
|
172 |
+
df["human_readable_idx"] = df.apply(
|
173 |
+
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'})",
|
174 |
+
axis=1,
|
175 |
+
)
|
176 |
+
|
177 |
+
df["winner_resolved"] = df.apply(lambda row: row[row.winner], axis=1)
|
178 |
+
df["winner_nodes"] = df.apply(
|
179 |
+
lambda row: f"{row.winner_resolved}:{row.name}".ljust(4, " "), axis=1
|
180 |
+
) # later for figure representation of winner as a "node"
|
181 |
+
return df
|
182 |
+
|
183 |
+
|
184 |
+
# draw
|
185 |
+
def draw(df: pd.DataFrame, alpha2names: dict = None) -> str:
|
186 |
+
def _draw_round(
|
187 |
+
df: pd.DataFrame,
|
188 |
+
depth: int = None,
|
189 |
+
winners_in_order: list = None,
|
190 |
+
) -> Tuple:
|
191 |
+
df_now = df[df.depth == depth]
|
192 |
+
max_depth = df.depth.max()
|
193 |
+
|
194 |
+
width = 2 ** ((max_depth - depth) + 2)
|
195 |
+
|
196 |
+
connect_left = "─" * (width)
|
197 |
+
connect_left = connect_left[4:]
|
198 |
+
connect_right = " " * (width)
|
199 |
+
connect_right = "┐" + connect_right[1:]
|
200 |
+
|
201 |
+
if winners_in_order is None:
|
202 |
+
assert (
|
203 |
+
depth == 0
|
204 |
+
), f"{winners_in_order=} is only allowed when drawing the top (=final match)"
|
205 |
+
winners_in_order = df_now.winner_nodes
|
206 |
+
|
207 |
+
round_drawing_parts = []
|
208 |
+
descending_round_winners = []
|
209 |
+
for node in winners_in_order:
|
210 |
+
round_drawing_parts.append("".join([node, connect_left, connect_right]))
|
211 |
+
# next round winners in sync with winner order
|
212 |
+
row_now = df_now.query(f"winner_nodes=='{node}'")
|
213 |
+
descending_round_winners.append(row_now.model_a.item())
|
214 |
+
descending_round_winners.append(row_now.model_b.item())
|
215 |
+
|
216 |
+
# find descending_round_winners within winner_nodes format (num:alpha)
|
217 |
+
if depth == max_depth:
|
218 |
+
pass # keep the descending_round_winners intact
|
219 |
+
else:
|
220 |
+
df_descend = df[df.depth == depth + 1]
|
221 |
+
for i, winner_alpha in enumerate(descending_round_winners):
|
222 |
+
node_intr = df_descend.query(
|
223 |
+
f"winner_resolved=='{winner_alpha}'"
|
224 |
+
).winner_nodes.item()
|
225 |
+
descending_round_winners[i] = node_intr
|
226 |
+
|
227 |
+
round_drawing = "".join(round_drawing_parts)
|
228 |
+
descending_unit = " " * width
|
229 |
+
descending_unit = "│" + descending_unit[1:]
|
230 |
+
descending_lines_parts = [descending_unit] * len(df_now) * 2
|
231 |
+
descending_lines = "".join(descending_lines_parts)
|
232 |
+
|
233 |
+
return round_drawing, descending_lines, descending_round_winners
|
234 |
+
|
235 |
+
drawings = []
|
236 |
+
winners_in_order = None
|
237 |
+
max_depth = df.depth.max()
|
238 |
+
for depth in range(max_depth + 1):
|
239 |
+
max_depth = df.depth.max()
|
240 |
+
winner_drw, lines_desc, winners_in_order = _draw_round(
|
241 |
+
df,
|
242 |
+
depth=depth,
|
243 |
+
winners_in_order=winners_in_order,
|
244 |
+
)
|
245 |
+
drawings.append((winner_drw, lines_desc))
|
246 |
+
# prepare bracket top
|
247 |
+
champion_alphabet = drawings[0][0].split()[0].split(":")[0]
|
248 |
+
champion_readable = alpha2names[champion_alphabet]
|
249 |
+
bracket_top = [f"🥇winner: {champion_readable}", "│"]
|
250 |
+
# prepare mid
|
251 |
+
bracket_mid = "\n".join(["\n".join(tup) for tup in drawings])
|
252 |
+
|
253 |
+
# prepare bot
|
254 |
+
initial_participants = winners_in_order
|
255 |
+
bracket_bot = (" " * 3).join(initial_participants)
|
256 |
+
|
257 |
+
full_figure = "\n".join(bracket_top + [bracket_mid, bracket_bot])
|
258 |
+
|
259 |
+
return full_figure
|
260 |
+
|
261 |
+
|
262 |
+
def number_breakdown_from_df(result_df: pd.DataFrame) -> str:
|
263 |
+
n_models = len(get_unique_participants(result_df))
|
264 |
+
size_testset = int(len(result_df) / (n_models - 1))
|
265 |
+
interpretation = f"total {len(result_df)} matches = (n_models-1) * size_testset = ({n_models}-1) * {size_testset}"
|
266 |
+
return interpretation, n_models, size_testset
|
267 |
+
|
268 |
+
|
269 |
+
def make_legend_str(df, alpha2names) -> str:
|
270 |
+
first = get_1st(df, alpha2names)
|
271 |
+
alpha2names = {k: v.replace("🥇 ", "") for k, v in alpha2names.items()}
|
272 |
+
alpha_ordered = sorted(list(alpha2names.keys()))
|
273 |
+
# names_ordered = sorted(list(alpha2names.values()))
|
274 |
+
# name2alpha = {v: k for k, v in alpha2names.items()}
|
275 |
+
|
276 |
+
for k, v in alpha2names.items():
|
277 |
+
if v == alpha2names[first]:
|
278 |
+
alpha2names[k] = f"🥇 {v}"
|
279 |
+
res_str = f"\n\nlegend:"
|
280 |
+
# for name in names_ordered:
|
281 |
+
# alpha = name2alpha[name]
|
282 |
+
for alpha in alpha_ordered:
|
283 |
+
name_w_medal = alpha2names[alpha]
|
284 |
+
res_str += f"\n{alpha}\t{name_w_medal}"
|
285 |
+
return res_str
|
286 |
+
|
287 |
+
|
288 |
+
def main(
|
289 |
+
jslname: str = "result.json",
|
290 |
+
):
|
291 |
+
"""
|
292 |
+
테스트 코드
|
293 |
+
"""
|
294 |
+
|
295 |
+
df = pd.read_json(jslname, orient="records")
|
296 |
+
df = df.drop(columns=["tstamp", "logs"])
|
297 |
+
df = index_test_scenario(df)
|
298 |
+
|
299 |
+
# 중간에 visualization(df) 여기선 생략. 만약 이거 뺴고 다 따라했는데 문제가 생긴다면 viz 문제다. 근데 안그럴거같긴함
|
300 |
+
selections = df.idx_inst_src.unique()
|
301 |
+
for i, sel in enumerate(selections):
|
302 |
+
try:
|
303 |
+
df_now = df[df.idx_inst_src == sel]
|
304 |
+
df_now_processed, _alpha2names = init_tournament_dataframe(
|
305 |
+
df_now, alpha2names=alpha2names if "alpha2names" in dir() else None
|
306 |
+
)
|
307 |
+
if "alpha2names" not in dir():
|
308 |
+
alpha2names = _alpha2names
|
309 |
+
assert "alpha2names" in dir()
|
310 |
+
bracket_drawing = draw(df_now_processed, alpha2names=alpha2names)
|
311 |
+
legend = make_legend_str(df_now_processed, alpha2names)
|
312 |
+
print(bracket_drawing + legend)
|
313 |
+
print(bracket_drawing + legend, file=open(f"{i}.txt", "w"))
|
314 |
+
print(f"\n\n{sel}", file=open(f"{i}.txt", "a"))
|
315 |
+
|
316 |
+
for match_idx_human in df_now_processed.human_readable_idx:
|
317 |
+
match_idx = int(match_idx_human.split(": ")[0])
|
318 |
+
row = df_now_processed.loc[match_idx]
|
319 |
+
winner = row.winner
|
320 |
+
except Exception as e:
|
321 |
+
print(e, file=open(f"{i}_err.txt", "w"))
|
322 |
+
print("", file=open(f"{i}_err.txt", "a"))
|
323 |
+
print(sel, file=open(f"{i}_err.txt", "a"))
|
324 |
+
df_now_processed[
|
325 |
+
[
|
326 |
+
"depth",
|
327 |
+
"round",
|
328 |
+
"winner_nodes",
|
329 |
+
"winner_resolved",
|
330 |
+
"winner",
|
331 |
+
"model_a",
|
332 |
+
"model_b",
|
333 |
+
]
|
334 |
+
].to_json(f"{i}_err.jsonl", lines=True, orient="records")
|
335 |
+
|
336 |
+
|
337 |
+
if __name__ == "__main__":
|
338 |
+
Fire(main)
|
app.py
ADDED
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import shutil
|
2 |
+
import os
|
3 |
+
import select
|
4 |
+
import subprocess
|
5 |
+
import sys
|
6 |
+
import time
|
7 |
+
from datetime import datetime, timedelta, timezone
|
8 |
+
from pathlib import Path
|
9 |
+
from typing import *
|
10 |
+
|
11 |
+
import streamlit as st
|
12 |
+
|
13 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
14 |
+
|
15 |
+
from varco_arena.varco_arena_core.prompts import load_prompt
|
16 |
+
from view_utils import (
|
17 |
+
default_page_setting,
|
18 |
+
escape_markdown,
|
19 |
+
set_nav_bar,
|
20 |
+
show_linebreak_in_md,
|
21 |
+
)
|
22 |
+
|
23 |
+
VA_ROOT = Path(os.environ.get("VARCO_ARENA_RESULT_PATH", "./user_submit"))
|
24 |
+
USR_SUB = VA_ROOT.parts[-1]
|
25 |
+
|
26 |
+
import shutil
|
27 |
+
|
28 |
+
import pandas as pd
|
29 |
+
|
30 |
+
import analysis_utils as au
|
31 |
+
from view_utils import visualization
|
32 |
+
|
33 |
+
|
34 |
+
class DataCache:
|
35 |
+
def __init__(self):
|
36 |
+
self.cache = {}
|
37 |
+
|
38 |
+
def store(self, key: str, data: dict):
|
39 |
+
self.cache[key] = data
|
40 |
+
|
41 |
+
def get(self, key: str) -> Optional[dict]:
|
42 |
+
return self.cache.get(key)
|
43 |
+
|
44 |
+
|
45 |
+
# Initialize the cache in session state if it doesn't exist
|
46 |
+
if "data_cache" not in st.session_state:
|
47 |
+
st.session_state.data_cache = DataCache()
|
48 |
+
|
49 |
+
|
50 |
+
def purge_user_sub_data(data_path_to_purge: Union[Path, str] = None):
|
51 |
+
if data_path_to_purge is None:
|
52 |
+
print("nothing to purge")
|
53 |
+
return
|
54 |
+
else:
|
55 |
+
shutil.rmtree(data_path_to_purge)
|
56 |
+
print(f"purged {str(data_path_to_purge)}")
|
57 |
+
return
|
58 |
+
|
59 |
+
|
60 |
+
@st.cache_data
|
61 |
+
def load_and_cache_data(result_file_path: Optional[str] = None) -> Tuple[Dict, Dict]:
|
62 |
+
"""
|
63 |
+
Load data from file, cache it in memory, then remove the file.
|
64 |
+
Returns cached data on subsequent calls.
|
65 |
+
|
66 |
+
Args:
|
67 |
+
result_file_path: Path to the result JSON file
|
68 |
+
|
69 |
+
Returns:
|
70 |
+
Tuple of (all_result_dict, df_dict)
|
71 |
+
"""
|
72 |
+
# Check if we already have cached data for this path
|
73 |
+
if result_file_path:
|
74 |
+
cache_key = str(Path(result_file_path))
|
75 |
+
cached_data = st.session_state.data_cache.get(cache_key)
|
76 |
+
if cached_data:
|
77 |
+
return cached_data["all_result_dict"], cached_data["df_dict"]
|
78 |
+
|
79 |
+
# Initialize empty dicts
|
80 |
+
all_result_dict = {}
|
81 |
+
df_dict = {}
|
82 |
+
|
83 |
+
if result_file_path is not None:
|
84 |
+
try:
|
85 |
+
result_file_path = Path(result_file_path)
|
86 |
+
|
87 |
+
# Read and process data
|
88 |
+
df = pd.read_json(result_file_path)
|
89 |
+
for col in ["tstamp", "logs"]:
|
90 |
+
if col in df.columns:
|
91 |
+
df.drop(columns=[col], inplace=True)
|
92 |
+
df = au.index_test_scenario(df)
|
93 |
+
|
94 |
+
fig_dict_per_task = {}
|
95 |
+
df_dict_per_task = {}
|
96 |
+
|
97 |
+
# Process overall data
|
98 |
+
fig_dict_per_task["Overall"] = visualization(df, is_overall=True)
|
99 |
+
df_dict_per_task["Overall"] = df
|
100 |
+
|
101 |
+
# Process per-task data
|
102 |
+
for task in df["task"].unique():
|
103 |
+
df_task = df[df["task"] == task]
|
104 |
+
fig_dict_per_task[task] = visualization(df_task, is_overall=False)
|
105 |
+
df_dict_per_task[task] = df_task
|
106 |
+
|
107 |
+
# Create key from path components
|
108 |
+
prm_name = result_file_path.parts[-2]
|
109 |
+
exp_name = result_file_path.parts[-3]
|
110 |
+
key = f"{exp_name}/{prm_name}"
|
111 |
+
|
112 |
+
all_result_dict[key] = fig_dict_per_task
|
113 |
+
df_dict[key] = df_dict_per_task
|
114 |
+
|
115 |
+
# Store in cache before removing file
|
116 |
+
cache_data = {"all_result_dict": all_result_dict, "df_dict": df_dict}
|
117 |
+
st.session_state.data_cache.store(str(result_file_path), cache_data)
|
118 |
+
|
119 |
+
# Remove user experiment directory
|
120 |
+
purge_user_sub_data(data_path_to_purge=VA_ROOT)
|
121 |
+
|
122 |
+
except Exception as e:
|
123 |
+
st.error(f"Error processing data: {str(e)}")
|
124 |
+
return {}, {}
|
125 |
+
|
126 |
+
return all_result_dict, df_dict
|
127 |
+
|
128 |
+
|
129 |
+
def upload_files(uploaded_files) -> Path:
|
130 |
+
# prep directory for user submission
|
131 |
+
user_sub_root = VA_ROOT
|
132 |
+
if user_sub_root.exists():
|
133 |
+
if not user_sub_root.is_dir():
|
134 |
+
raise ValueError(
|
135 |
+
f"{user_sub_root} file exists and is not a directory. Consider renaming it."
|
136 |
+
)
|
137 |
+
else:
|
138 |
+
user_sub_root.mkdir(parents=True)
|
139 |
+
|
140 |
+
KST = timezone(timedelta(hours=9))
|
141 |
+
tstamp = datetime.now(KST)
|
142 |
+
tstr = tstamp.strftime("%m-%d_%H:%M:%S")
|
143 |
+
files_dir_str = "./" + str(user_sub_root / tstr)
|
144 |
+
files_dir = Path(files_dir_str)
|
145 |
+
files_dir.mkdir(parents=True, exist_ok=True)
|
146 |
+
uploaded_files = list(uploaded_files)
|
147 |
+
|
148 |
+
if not uploaded_files:
|
149 |
+
st.warning("❌ No files to upload. Please drag/drop or browse files to upload.")
|
150 |
+
# purge_user_sub_data(data_path_to_purge=VA_ROOT)
|
151 |
+
elif len(uploaded_files) < 2:
|
152 |
+
st.error("❌ You need at least 2 jsonlines files to properly run VA.")
|
153 |
+
purge_user_sub_data(data_path_to_purge=VA_ROOT)
|
154 |
+
else: # properly uploaded
|
155 |
+
for file in uploaded_files:
|
156 |
+
# Create a path for the file in the server directory
|
157 |
+
file_path = files_dir / file.name
|
158 |
+
|
159 |
+
# Save the file to the server directory
|
160 |
+
with open(file_path, "wb") as f:
|
161 |
+
f.write(file.getbuffer())
|
162 |
+
|
163 |
+
jslfiles = list(files_dir.glob("*.jsonl"))
|
164 |
+
st.success(f"✅ Successfully uploaded {len(jslfiles)} jsonl files.")
|
165 |
+
return files_dir.resolve()
|
166 |
+
|
167 |
+
|
168 |
+
def run_varco_arena(
|
169 |
+
price_estimation: bool = False,
|
170 |
+
# upload_dir: Union[str, Path] = None,
|
171 |
+
promptname: str = None,
|
172 |
+
exp_name: str = None,
|
173 |
+
api_key: Optional[str] = None,
|
174 |
+
evaluation_model: str = "gpt-4o-mini",
|
175 |
+
update_interval: float = 1.0,
|
176 |
+
):
|
177 |
+
# Use environment variable for API key
|
178 |
+
ptn = f"{str(st.session_state.upfiles_dir)}"
|
179 |
+
outdir = Path(ptn)
|
180 |
+
if exp_name:
|
181 |
+
outdir = outdir / exp_name
|
182 |
+
|
183 |
+
command = f"python varco_arena/main.py -i {ptn} -o {outdir} -k {api_key} -p {promptname} -e {evaluation_model} -j 64"
|
184 |
+
if price_estimation:
|
185 |
+
command = f"{command} -c"
|
186 |
+
else:
|
187 |
+
command = command.replace("python", "yes | python ")
|
188 |
+
print(command)
|
189 |
+
|
190 |
+
api_key = None # clear immediately
|
191 |
+
|
192 |
+
process = subprocess.Popen(
|
193 |
+
command,
|
194 |
+
stdout=subprocess.PIPE,
|
195 |
+
stderr=subprocess.STDOUT,
|
196 |
+
stdin=subprocess.PIPE,
|
197 |
+
text=True,
|
198 |
+
bufsize=1,
|
199 |
+
shell=True,
|
200 |
+
)
|
201 |
+
|
202 |
+
# Set stdout and stdin to non-blocking mode
|
203 |
+
os.set_blocking(process.stdout.fileno(), False)
|
204 |
+
|
205 |
+
last_update_time = time.time()
|
206 |
+
terminal_output = st.empty()
|
207 |
+
full_output = f"{command}\n"
|
208 |
+
while True:
|
209 |
+
# Check if we have output to read
|
210 |
+
if select.select([process.stdout], [], [], 0)[0]:
|
211 |
+
output = process.stdout.readline()
|
212 |
+
if output:
|
213 |
+
full_output += output
|
214 |
+
if price_estimation:
|
215 |
+
to_show = full_output
|
216 |
+
terminal_output.code(to_show, language="bash")
|
217 |
+
else:
|
218 |
+
current_time = time.time()
|
219 |
+
if current_time - last_update_time > update_interval:
|
220 |
+
lines = full_output.split("\n")
|
221 |
+
if len(lines) < 5:
|
222 |
+
to_show = full_output
|
223 |
+
else:
|
224 |
+
to_show = "\n".join(["...\n..\n.\n"] + lines[-5:])
|
225 |
+
terminal_output.code(to_show, language="bash")
|
226 |
+
last_update_time = current_time
|
227 |
+
print(output)
|
228 |
+
time.sleep(0.1)
|
229 |
+
# Check if the process has finished
|
230 |
+
if process.poll() is not None:
|
231 |
+
# Read any remaining output
|
232 |
+
remaining_output = process.stdout.read()
|
233 |
+
if remaining_output:
|
234 |
+
lines = remaining_output.split("\n")
|
235 |
+
if len(lines) > 10:
|
236 |
+
to_show += "\n".join(["\n...\n..\n.\n"] + lines[-10:])
|
237 |
+
else:
|
238 |
+
to_show += remaining_output
|
239 |
+
terminal_output.code(to_show, language="bash")
|
240 |
+
print(remaining_output)
|
241 |
+
break
|
242 |
+
|
243 |
+
return_code = process.poll()
|
244 |
+
return outdir, return_code
|
245 |
+
|
246 |
+
|
247 |
+
def main():
|
248 |
+
# init lang
|
249 |
+
st.session_state["korean"] = st.session_state.get("korean", False)
|
250 |
+
|
251 |
+
sidebar_placeholder = default_page_setting()
|
252 |
+
set_nav_bar(
|
253 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_init"
|
254 |
+
)
|
255 |
+
|
256 |
+
st.title("⚔️ VARCO ARENA ⚔️")
|
257 |
+
if st.session_state.korean:
|
258 |
+
st.write(
|
259 |
+
"**VARCO Arena는 각 모델의 생성된 결과를 비교 평가하여 모델의 성능 순위를 제공하는 시스템입니다. 커스텀 테스트셋에 유용하게 활용할 수 있으며, 모범답안을 필요로 하지 않습니다.**"
|
260 |
+
)
|
261 |
+
else:
|
262 |
+
st.write(
|
263 |
+
"**VARCO Arena is an LLM benchmarking system that compares model responses across customized test scenarios without requiring reference answers.**"
|
264 |
+
)
|
265 |
+
|
266 |
+
st.divider()
|
267 |
+
# Set up the file uploader
|
268 |
+
if st.session_state.korean:
|
269 |
+
st.markdown("### 1. 모델 출력파일 업로드")
|
270 |
+
else:
|
271 |
+
st.markdown("### 1. Upload LLM responses")
|
272 |
+
uploaded_files = st.file_uploader(
|
273 |
+
"Drag and Drop jsonlines files (.jsonl)", accept_multiple_files=True
|
274 |
+
)
|
275 |
+
if st.session_state.korean:
|
276 |
+
st.info("업로드 하신 파일은 자동으로 삭제되며 수집되거나 사용되지 않습니다.")
|
277 |
+
else:
|
278 |
+
st.info(
|
279 |
+
"Your uploads will be removed automatically, not being collected nor reused for any purpose."
|
280 |
+
)
|
281 |
+
# upload state
|
282 |
+
if "upfiles_dir" not in st.session_state:
|
283 |
+
st.session_state.upfiles_dir = None
|
284 |
+
if st.button("업로드하기" if st.session_state.korean else "Upload Files"):
|
285 |
+
st.session_state.upfiles_dir = upload_files(uploaded_files)
|
286 |
+
if st.button("업로드한 파일 지우기" if st.session_state.korean else "Purge my uploads"):
|
287 |
+
st.session_state.upfiles_dir = None
|
288 |
+
if VA_ROOT.is_dir():
|
289 |
+
shutil.rmtree(VA_ROOT)
|
290 |
+
st.success(
|
291 |
+
"✅ 업로드한 파일을 서버에서 지웠습니다"
|
292 |
+
if st.session_state.korean
|
293 |
+
else "✅ Removed your uploads from the server successfully"
|
294 |
+
)
|
295 |
+
else:
|
296 |
+
st.error(
|
297 |
+
"❌ 지울 파일이 없습니다"
|
298 |
+
if st.session_state.korean
|
299 |
+
else "❌ You have nothing uploaded"
|
300 |
+
)
|
301 |
+
|
302 |
+
if st.session_state.korean:
|
303 |
+
with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
|
304 |
+
st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
|
305 |
+
else:
|
306 |
+
with st.expander("❓❔ What should I upload ❓❔"):
|
307 |
+
st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
|
308 |
+
|
309 |
+
# Form for cost estimation
|
310 |
+
with st.form("cost_estimation_form"):
|
311 |
+
if st.session_state.korean:
|
312 |
+
st.write("### 2. 가격 산정")
|
313 |
+
else:
|
314 |
+
st.write("### 2. Cost Estimation")
|
315 |
+
eval_model = st.selectbox(
|
316 |
+
"Select Judge",
|
317 |
+
open("eval_models_list.txt", encoding="UTF8").read().split("\n"),
|
318 |
+
)
|
319 |
+
promptname = st.selectbox(
|
320 |
+
"Select Evalutaion Prompt",
|
321 |
+
open("eval_prompt_list.txt", encoding="UTF8").read().split("\n"),
|
322 |
+
)
|
323 |
+
if st.session_state.korean:
|
324 |
+
st.markdown("*`llmbar`외 다른 프롬프트는 충분히 검증된 프롬프트는 아닙니다. (동작은 함)")
|
325 |
+
else:
|
326 |
+
st.markdown(
|
327 |
+
"*Eval prompts other than `llmbar` is working example, not the optimal ones."
|
328 |
+
)
|
329 |
+
if promptname == USR_SUB:
|
330 |
+
raise ValueError(
|
331 |
+
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])."
|
332 |
+
)
|
333 |
+
|
334 |
+
estimate_button = st.form_submit_button("Calculate Cost!")
|
335 |
+
with st.expander(
|
336 |
+
"LLM Judge에 활용되는 프롬프트 (`Calculate Cost!` 클릭시 갱신)"
|
337 |
+
if st.session_state.korean
|
338 |
+
else "**Evaluation Prompt for LLM Judge (will refresh after `Calculate Cost!` clicked)**"
|
339 |
+
):
|
340 |
+
prompt = load_prompt(promptname, task="-")
|
341 |
+
kwargs = dict(
|
342 |
+
inst="{inst}",
|
343 |
+
src="{src}",
|
344 |
+
out_a="{out_a}",
|
345 |
+
out_b="{out_b}",
|
346 |
+
task="-",
|
347 |
+
)
|
348 |
+
if promptname == "translation_pair":
|
349 |
+
kwargs["source_lang"] = "{source_lang}"
|
350 |
+
kwargs["target_lang"] = "{target_lang}"
|
351 |
+
prompt_cmpl = prompt.complete_prompt(**kwargs)
|
352 |
+
|
353 |
+
st.markdown(f"### Evaluation Prompt: {promptname}")
|
354 |
+
for msg in prompt_cmpl:
|
355 |
+
st.markdown(f"**{msg['role']}**")
|
356 |
+
st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
|
357 |
+
|
358 |
+
if estimate_button:
|
359 |
+
if st.session_state.get("upfiles_dir") is None:
|
360 |
+
st.error(
|
361 |
+
"❌ Requirements: You have to upload jsonlines files first to proceed"
|
362 |
+
)
|
363 |
+
else:
|
364 |
+
st.markdown("##### Estimated Cost")
|
365 |
+
dummy_api_key = "dummy"
|
366 |
+
dummy_exp_name = "dummy"
|
367 |
+
result_file_path, return_code = run_varco_arena(
|
368 |
+
# upload_dir=st.session_state.upfiles_dir,
|
369 |
+
promptname=promptname,
|
370 |
+
api_key=dummy_api_key,
|
371 |
+
exp_name=dummy_exp_name,
|
372 |
+
price_estimation=True,
|
373 |
+
evaluation_model=eval_model,
|
374 |
+
)
|
375 |
+
if return_code:
|
376 |
+
st.error(
|
377 |
+
"❌ RuntimeError: An error occurred during cost estimation. **Restart from file upload!**"
|
378 |
+
)
|
379 |
+
purge_user_sub_data(data_path_to_purge=VA_ROOT)
|
380 |
+
|
381 |
+
else:
|
382 |
+
st.success("✅ Cost estimation completed successfully")
|
383 |
+
st.session_state.cost_estimated = True
|
384 |
+
|
385 |
+
# Form for actual run
|
386 |
+
with st.form("run_arena_form"):
|
387 |
+
if st.session_state.korean:
|
388 |
+
st.write("### 3. Varco Arena 구동하기")
|
389 |
+
else:
|
390 |
+
st.write("### 3. Run Varco Arena")
|
391 |
+
api_key = st.text_input("Enter your OpenAI API Key", type="password")
|
392 |
+
|
393 |
+
# demo exp name fixated
|
394 |
+
KST = timezone(timedelta(hours=9))
|
395 |
+
tstamp = datetime.now(KST)
|
396 |
+
tstr = tstamp.strftime("%m-%d_%H:%M:%S")
|
397 |
+
exp_name = f"{tstr}_KST_submit"
|
398 |
+
|
399 |
+
if st.session_state.korean:
|
400 |
+
st.write("**주의**:`Ctrl+C` 버튼은 구현되지 않았습니다. 구동 전 숙고해주세요.")
|
401 |
+
else:
|
402 |
+
st.write("**Caution: `Ctrl+C` button hasn't been implemented.**")
|
403 |
+
run_button = st.form_submit_button(
|
404 |
+
"🔥 Run Arena!",
|
405 |
+
disabled=(not st.session_state.get("cost_estimated", False))
|
406 |
+
or "result_file_path"
|
407 |
+
in st.session_state.keys(), # run already performed once
|
408 |
+
)
|
409 |
+
|
410 |
+
if run_button:
|
411 |
+
set_nav_bar(
|
412 |
+
True,
|
413 |
+
sidebar_placeholder=sidebar_placeholder,
|
414 |
+
toggle_hashstr="app_during_run",
|
415 |
+
)
|
416 |
+
if st.session_state.get("upfiles_dir") is None:
|
417 |
+
st.error(
|
418 |
+
"❌ Requirements: You have to upload jsonlines files first to proceed"
|
419 |
+
)
|
420 |
+
elif not api_key:
|
421 |
+
st.error("❌ Requirements: OpenAI key required to run VA.")
|
422 |
+
else:
|
423 |
+
result_file_path, return_code = run_varco_arena(
|
424 |
+
# upload_dir=st.session_state.upfiles_dir,
|
425 |
+
promptname=promptname,
|
426 |
+
api_key=api_key,
|
427 |
+
exp_name=exp_name,
|
428 |
+
price_estimation=False,
|
429 |
+
evaluation_model=eval_model,
|
430 |
+
)
|
431 |
+
if return_code:
|
432 |
+
st.error(
|
433 |
+
"❌ RuntimeError: An error occurred during Varco Arena run. Check the file and **restart from file upload!**"
|
434 |
+
)
|
435 |
+
purge_user_sub_data(data_path_to_purge=VA_ROOT)
|
436 |
+
|
437 |
+
else:
|
438 |
+
st.success("✅ Varco Arena run completed successfully")
|
439 |
+
st.session_state.result_file_path = list(
|
440 |
+
result_file_path.glob("**/result.json")
|
441 |
+
)[-1]
|
442 |
+
set_nav_bar(
|
443 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_run_done"
|
444 |
+
)
|
445 |
+
|
446 |
+
if st.session_state.get("result_file_path", None) is not None:
|
447 |
+
print(f"{st.session_state.get('result_file_path', None)=}")
|
448 |
+
load_and_cache_data(result_file_path=str(st.session_state.result_file_path))
|
449 |
+
|
450 |
+
|
451 |
+
if __name__ == "__main__":
|
452 |
+
main()
|
eval_models_list.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
gpt-4o-mini
|
2 |
+
gpt-4o-2024-05-13
|
3 |
+
gpt-4o-2024-08-06
|
eval_prompt_list.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
llmbar
|
2 |
+
translation_pair
|
3 |
+
rag_pair_kr
|
guide_mds/input_jsonls_en.md
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#### \[EN\] Upload guide (`jsonl`)
|
2 |
+
**Basic Requirements**
|
3 |
+
* Upload one `jsonl` file per model (e.g., five files to compare five LLMs)
|
4 |
+
* ⚠️ Important: All `jsonl` files must have the same number of rows
|
5 |
+
* ⚠️ Important: The `model_id` field must be unique within and across all files
|
6 |
+
|
7 |
+
**Required Fields**
|
8 |
+
* Per Model Fields
|
9 |
+
* `model_id`: Unique identifier for the model (recommendation: keep it short)
|
10 |
+
* `generated`: The LLM's response to the test instruction
|
11 |
+
|
12 |
+
* Required only for Translation (`translation_pair` prompt need those. See `streamlit_app_local/user_submit/mt/llama5.jsonl`)
|
13 |
+
* `source_lang`: input language (e.g. Korean, KR, kor, ...)
|
14 |
+
* `target_lang`: output language (e.g. English, EN, ...)
|
15 |
+
|
16 |
+
* Common Fields (Must be identical across all files)
|
17 |
+
* `instruction`: The input prompt or test instruction given to the model
|
18 |
+
* `task`: Category label used to group results (useful when using different evaluation prompts per task)
|
19 |
+
|
20 |
+
**Example Format**
|
21 |
+
```python
|
22 |
+
# model1.jsonl
|
23 |
+
{"model_id": "model1", "task": "directions", "instruction": "Where should I go?", "generated": "Over there"}
|
24 |
+
{"model_id": "model1", "task": "arithmetic", "instruction": "1+1", "generated": "2"}
|
25 |
+
|
26 |
+
# model2.jsonl
|
27 |
+
{"model_id": "model2", "task": "directions", "instruction": "Where should I go?", "generated": "Head north"}
|
28 |
+
{"model_id": "model2", "task": "arithmetic", "instruction": "1+1", "generated": "3"}
|
29 |
+
...
|
30 |
+
..
|
31 |
+
.
|
32 |
+
```
|
33 |
+
**Use Case Example**
|
34 |
+
If you want to compare different prompting strategies for the same model:
|
35 |
+
* Use the same `instruction` across files (using unified test scenarios).
|
36 |
+
* `generated` responses of each prompting strategy will vary across the files.
|
37 |
+
* Use descriptive `model_id` values like "prompt1", "prompt2", etc.
|
guide_mds/input_jsonls_kr.md
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#### \[KR\] 집어넣을 jsonl 파일 가이드
|
2 |
+
비교할 모델이 다섯 개라면 다섯 개의 .jsonl 파일을 업로드하세요.
|
3 |
+
* 💥모든 jsonl 은 같은 수의 행을 가져야합니다.
|
4 |
+
* 💥`model_id` 필드는 파일마다 달라야하며 파일 내에서는 유일해야합니다.
|
5 |
+
|
6 |
+
**jsonl 필수 필드**
|
7 |
+
* 개별
|
8 |
+
* `model_id`: 평가받는 모델의 이름입니다. (짧게 쓰는 것 추천)
|
9 |
+
* `generated`: 모델이 testset instruction 에 생성한 응답을 넣으세요.
|
10 |
+
|
11 |
+
* 번역평가 프롬프트 사용시 (`translation_pair`. `streamlit_app_local/user_submit/mt/llama5.jsonl` 에서 예시 볼 수 있음)
|
12 |
+
* `source_lang`: input language (e.g. Korean, KR, kor, ...)
|
13 |
+
* `target_lang`: output language (e.g. English, EN, ...)
|
14 |
+
|
15 |
+
* 공통 부분 (**모든 파일에 대해 같아야 함**)
|
16 |
+
* `instruction`: 모델에 집어넣는 `testset instruction` 혹은 `input`에 해당하는 무언가입니다.
|
17 |
+
* `task`: 전체 결과를 subset으로 그룹지어서 보여줄 때 사용됩니다. `evaluation prompt`를 행별로 다르게 사용하고 싶을 때 활용될 수 있습니다.
|
18 |
+
|
19 |
+
|
20 |
+
각 jsonl 파일은 아래처럼 생겼습니다.
|
21 |
+
```python
|
22 |
+
# model1.jsonl
|
23 |
+
{"model_id": "모델1", "task": "길 묻기", "instruction": "어디로 가야하오", "generated": "저기로요"}
|
24 |
+
{"model_id": "모델1", "task": "산수", "instruction": "1+1", "generated": "2"} # 길 묻기와 산수의 경우 다른 평가 프롬프트를 사용하고 싶을 수 있겠죠?
|
25 |
+
|
26 |
+
# model2.jsonl -* model1.jsonl과 `instruction`은 같고 `generated`, `model_id` 는 다릅니다!
|
27 |
+
{"model_id": "모델2", "task": "길 묻기", "instruction": "어디로 가야하오", "generated": "하이"}
|
28 |
+
{"model_id": "모델2", "task": "산수", "instruction": "1+1", "generated": "3"}
|
29 |
+
|
30 |
+
...
|
31 |
+
..
|
32 |
+
```
|
33 |
+
|
34 |
+
예를 들어, 한가지 모델에 대해 다른 프롬프팅을 시도하여 다른 생성문을 얻었고 이를 비교하고 싶은 경우를 생각해봅시다. 이 때 평가받을 testset은 같으므로 `instruction`은 모두 같고 프롬프팅에 따라 `generated`는 달라지겠죠? `model_id` 는 `"prompt1"`, `"prompt2"` 등 취향에 맞게 적어주시면 됩니다.
|
modules/__pycache__/nav.cpython-311.pyc
ADDED
Binary file (3.8 kB). View file
|
|
modules/nav.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
|
4 |
+
def Navbar(sidebar_placeholder, toggle_hashstr: str = ""):
|
5 |
+
with sidebar_placeholder.container():
|
6 |
+
st.toggle(
|
7 |
+
"🇰🇷 한국어로 보기",
|
8 |
+
value=st.session_state.get("korean", False),
|
9 |
+
key=f"korean_toggle_{toggle_hashstr}", # Add this explicit key
|
10 |
+
on_change=lambda: setattr(
|
11 |
+
st.session_state,
|
12 |
+
"korean",
|
13 |
+
st.session_state.get(f"korean_toggle_{toggle_hashstr}", False),
|
14 |
+
),
|
15 |
+
)
|
16 |
+
|
17 |
+
st.page_link(
|
18 |
+
"app.py",
|
19 |
+
label="Varco Arena 구동" if st.session_state.korean else "Run VARCO Arena",
|
20 |
+
icon="🔥",
|
21 |
+
)
|
22 |
+
st.page_link(
|
23 |
+
"pages/see_results.py",
|
24 |
+
label="결과 보기" if st.session_state.korean else "See Results",
|
25 |
+
icon="📝",
|
26 |
+
disabled=st.session_state.get("is_running", False),
|
27 |
+
)
|
28 |
+
st.page_link(
|
29 |
+
"pages/brief_intro.py",
|
30 |
+
label="어떻게 작동하나요?" if st.session_state.korean else "How it Works",
|
31 |
+
icon="❔",
|
32 |
+
disabled=st.session_state.get("is_running", False),
|
33 |
+
)
|
34 |
+
st.page_link(
|
35 |
+
"pages/quick_start_guide.py",
|
36 |
+
label="본격 사용하기 가이드" if st.session_state.korean else "Quick Start Guide",
|
37 |
+
icon="💯",
|
38 |
+
disabled=st.session_state.get("is_running", False),
|
39 |
+
)
|
40 |
+
related_links_en = """
|
41 |
+
**About**
|
42 |
+
* [Paper](https://huggingface.co/papers/2411.01281)
|
43 |
+
* [Blog (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
44 |
+
* [Inquiry](https://linkedin.com/in/deftson/)
|
45 |
+
""".strip()
|
46 |
+
related_links_kr = """
|
47 |
+
**About**
|
48 |
+
* [논문](https://huggingface.co/papers/2411.01281)
|
49 |
+
* [블로그](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
50 |
+
* [문의](https://linkedin.com/in/deftson/)
|
51 |
+
""".strip()
|
52 |
+
st.info(related_links_kr if st.session_state.korean else related_links_en)
|
53 |
+
|
54 |
+
st.divider()
|
55 |
+
demo_warning_kr = "❕❗**본 데모에서는 새로고침 후에 평가 결과가 보존되지 않습니다**❕❗ 앱을 온전히 활용하시려면 개인 기기에서 호스팅하세요 (**💯본격 사용하기 가이드** 참조)"
|
56 |
+
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**)"
|
57 |
+
st.markdown(demo_warning_kr if st.session_state.korean else demo_warning_en)
|
pages/__pycache__/see_results.cpython-311.pyc
ADDED
Binary file (26.3 kB). View file
|
|
pages/brief_intro.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
from view_utils import default_page_setting, set_nav_bar
|
4 |
+
|
5 |
+
sidebar_placeholder = default_page_setting()
|
6 |
+
set_nav_bar(
|
7 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="brief_intro_init"
|
8 |
+
)
|
9 |
+
|
10 |
+
st.image("va_en.png")
|
11 |
+
if st.session_state.korean:
|
12 |
+
st.info("여러분이 업로드하는 파일은 위 그림의 LLM 출력들입니다.")
|
13 |
+
with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
|
14 |
+
st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
|
15 |
+
else:
|
16 |
+
st.info(
|
17 |
+
"What you upload is the generated responses of LLMs regarding the test prompts."
|
18 |
+
)
|
19 |
+
with st.expander("❓❔ What should I upload ❓❔"):
|
20 |
+
st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
|
21 |
+
|
22 |
+
|
23 |
+
st.image("va_concept_new.png")
|
24 |
+
st.markdown(
|
25 |
+
"""
|
26 |
+
| |Current Practice|Varco Arena|
|
27 |
+
|-|-|-|
|
28 |
+
|Total no. matches|$$n_{\\text{model}}*\\|X\\|$$|$$(n_{\\text{model}}-1)*\\|X\\|$$|
|
29 |
+
|No. matches per LLM|$$\\|X\\|$$|$$\\left[\\|X\\|,\\|X\\|\\text{log}n_{\\text{model}}\\right]$$|
|
30 |
+
|Comparison type|reference-mediated (anchored) |direct|
|
31 |
+
"""
|
32 |
+
)
|
33 |
+
if st.session_state.korean:
|
34 |
+
st.info(
|
35 |
+
"Varco Arena는 신뢰성 있는 순위를 더 적은 횟수의 비교 내에 얻어내며, 이러한 특징은 LLM 직접 비교의 이점으로부터 기인합니다."
|
36 |
+
)
|
37 |
+
else:
|
38 |
+
st.info(
|
39 |
+
"Varco Arena takes advantage of direct comparison between LLM responses to guarantee better reliability in fewer number of total matches."
|
40 |
+
)
|
pages/quick_start_guide.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
from view_utils import default_page_setting, set_nav_bar
|
4 |
+
|
5 |
+
sidebar_placeholder = default_page_setting()
|
6 |
+
set_nav_bar(
|
7 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="quickstart_init"
|
8 |
+
)
|
9 |
+
|
10 |
+
|
11 |
+
if st.session_state.korean:
|
12 |
+
st.markdown(open("varco_arena/README_kr.md").read())
|
13 |
+
else:
|
14 |
+
st.markdown(open("varco_arena/README_en.md").read())
|
pages/see_results.py
ADDED
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import streamlit as st
|
3 |
+
|
4 |
+
import analysis_utils as au
|
5 |
+
from analysis_utils import number_breakdown_from_df
|
6 |
+
from app import load_and_cache_data
|
7 |
+
|
8 |
+
# from app import VA_ROOT
|
9 |
+
from query_comp import QueryWrapper, get_base_url
|
10 |
+
from varco_arena.varco_arena_core.prompts import load_prompt
|
11 |
+
from view_utils import (
|
12 |
+
default_page_setting,
|
13 |
+
escape_markdown,
|
14 |
+
set_nav_bar,
|
15 |
+
show_linebreak_in_md,
|
16 |
+
)
|
17 |
+
|
18 |
+
DEFAULT_LAYOUT_DICT = {
|
19 |
+
"title": {"font": {"size": 20, "family": "Gothic A1"}},
|
20 |
+
"font": {"size": 16, "family": "Gothic A1"},
|
21 |
+
"xaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
|
22 |
+
"yaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
|
23 |
+
"legend": {"font": {"size": 12, "family": "Gothic A1"}},
|
24 |
+
}
|
25 |
+
|
26 |
+
|
27 |
+
def navigate(t, source, key, val):
|
28 |
+
# print(key, val)
|
29 |
+
if source is None:
|
30 |
+
return
|
31 |
+
target_index = t.index(source) + val
|
32 |
+
if 0 <= target_index < len(t):
|
33 |
+
st.session_state[key] = t[target_index]
|
34 |
+
st.rerun()
|
35 |
+
|
36 |
+
|
37 |
+
def main():
|
38 |
+
sidebar_placeholder = default_page_setting(layout="wide")
|
39 |
+
set_nav_bar(
|
40 |
+
False,
|
41 |
+
sidebar_placeholder=sidebar_placeholder,
|
42 |
+
toggle_hashstr="see_results_init",
|
43 |
+
)
|
44 |
+
|
45 |
+
# load the data
|
46 |
+
# print(f"{st.session_state.get('result_file_path', None)=}")
|
47 |
+
most_recent_run = st.session_state.get("result_file_path", None)
|
48 |
+
most_recent_run = str(most_recent_run) if most_recent_run is not None else None
|
49 |
+
(
|
50 |
+
st.session_state["all_result_dict"],
|
51 |
+
st.session_state["df_dict"],
|
52 |
+
) = load_and_cache_data(result_file_path=most_recent_run)
|
53 |
+
|
54 |
+
# side bar
|
55 |
+
st.sidebar.title("Select Result:")
|
56 |
+
result_select = QueryWrapper("expname")(
|
57 |
+
st.sidebar.selectbox,
|
58 |
+
list(st.session_state["all_result_dict"].keys()),
|
59 |
+
)
|
60 |
+
|
61 |
+
if result_select is None:
|
62 |
+
st.stop()
|
63 |
+
eval_prompt_name = result_select.split("/")[-1].strip()
|
64 |
+
if st.sidebar.button("Clear Cache"):
|
65 |
+
st.cache_data.clear()
|
66 |
+
st.cache_resource.clear()
|
67 |
+
st.rerun()
|
68 |
+
|
69 |
+
if result_select:
|
70 |
+
if "alpha2names" in st.session_state:
|
71 |
+
del st.session_state["alpha2names"]
|
72 |
+
|
73 |
+
fig_dict_per_task = st.session_state["all_result_dict"][result_select]
|
74 |
+
task_list = list(fig_dict_per_task.keys())
|
75 |
+
elo_rating_by_task = fig_dict_per_task["Overall"]["elo_rating_by_task"]
|
76 |
+
# tabs = st.tabs(task_list)
|
77 |
+
|
78 |
+
df_dict_per_task = st.session_state["df_dict"][result_select]
|
79 |
+
|
80 |
+
default_layout_dict = DEFAULT_LAYOUT_DICT
|
81 |
+
task = QueryWrapper("task", "Select Task")(st.selectbox, task_list)
|
82 |
+
|
83 |
+
if task is None:
|
84 |
+
st.stop()
|
85 |
+
figure_dict = fig_dict_per_task[task]
|
86 |
+
judgename = figure_dict["judgename"]
|
87 |
+
df = df_dict_per_task[task]
|
88 |
+
interpretation, n_models, size_testset = number_breakdown_from_df(df)
|
89 |
+
if st.session_state.korean:
|
90 |
+
st.markdown(f"## 결과 ({task})")
|
91 |
+
st.markdown(f"##### Judge 모델: {judgename} / 평가프롬: {eval_prompt_name}")
|
92 |
+
st.markdown(f"##### 테스트셋 사이즈: {int(size_testset)} 행")
|
93 |
+
else:
|
94 |
+
st.markdown(f"## Results ({task})")
|
95 |
+
st.markdown(f"##### Judge Model: {judgename} / prompt: {eval_prompt_name}")
|
96 |
+
st.markdown(f"##### Size of Testset: {int(size_testset)} rows")
|
97 |
+
col1, col2 = st.columns(2)
|
98 |
+
with col1:
|
99 |
+
with st.container(border=True):
|
100 |
+
st.markdown(f"#### Ratings ({task})")
|
101 |
+
st.table(figure_dict["elo_rating"])
|
102 |
+
st.write(show_linebreak_in_md(escape_markdown(interpretation)))
|
103 |
+
|
104 |
+
with col2:
|
105 |
+
with st.container(border=True):
|
106 |
+
st.plotly_chart(
|
107 |
+
elo_rating_by_task.update_layout(**default_layout_dict),
|
108 |
+
use_container_width=True,
|
109 |
+
key=f"{task}_elo_rating_by_task",
|
110 |
+
)
|
111 |
+
|
112 |
+
st.divider()
|
113 |
+
|
114 |
+
if st.session_state.korean:
|
115 |
+
st.markdown("### 토너먼트 (테스트 시나리오) 별로 보기")
|
116 |
+
else:
|
117 |
+
st.markdown("### Tournament Results by Test Scenario")
|
118 |
+
|
119 |
+
# with st.expander("볼 토너먼트 고르기"):
|
120 |
+
d = list(df.idx_inst_src.unique())
|
121 |
+
default_idx = st.session_state.get("selected_tournament", None)
|
122 |
+
cols = st.columns((1, 18, 1))
|
123 |
+
with cols[0]:
|
124 |
+
if st.button("◀", key="prev_tournament"):
|
125 |
+
navigate(d, default_idx, "selected_tournament", -1)
|
126 |
+
with cols[1]:
|
127 |
+
tournament_prm_select = QueryWrapper("tournament", "Select Tournament")(
|
128 |
+
st.selectbox,
|
129 |
+
d,
|
130 |
+
default_idx,
|
131 |
+
key=f"{task}_tournament_select",
|
132 |
+
on_change=lambda: st.session_state.update(
|
133 |
+
selected_tournament=st.session_state.get(f"{task}_tournament_select"),
|
134 |
+
selected_match=None,
|
135 |
+
),
|
136 |
+
label_visibility="collapsed",
|
137 |
+
)
|
138 |
+
with cols[2]:
|
139 |
+
if st.button("▶", key="next_tournament"):
|
140 |
+
navigate(d, default_idx, "selected_tournament", 1)
|
141 |
+
|
142 |
+
# tournament_prm_select = st.selectbox(
|
143 |
+
# "Select Tournament",
|
144 |
+
# df.idx_inst_src.unique(),
|
145 |
+
# index=d.index(st.session_state.get("selected_tournament")),
|
146 |
+
# key=f"{task}_tournament_{result_select}",
|
147 |
+
# )
|
148 |
+
|
149 |
+
# print(tournament_prm_select, type(tournament_prm_select))
|
150 |
+
st.session_state["selected_tournament"] = tournament_prm_select
|
151 |
+
# tournament_prm_select = st.selectbox(
|
152 |
+
# "Select Tournament",
|
153 |
+
# df.idx_inst_src.unique(),
|
154 |
+
# key=f"{task}_tournament_{result_select}",
|
155 |
+
# )
|
156 |
+
df_now_processed = None
|
157 |
+
if tournament_prm_select:
|
158 |
+
df_now = df[df.idx_inst_src == tournament_prm_select]
|
159 |
+
df_now_processed, _alpha2names = au.init_tournament_dataframe(
|
160 |
+
df_now,
|
161 |
+
alpha2names=st.session_state["alpha2names"]
|
162 |
+
if "alpha2names" in st.session_state.keys()
|
163 |
+
else None,
|
164 |
+
)
|
165 |
+
if "alpha2names" not in st.session_state:
|
166 |
+
st.session_state["alpha2names"] = _alpha2names
|
167 |
+
|
168 |
+
try:
|
169 |
+
bracket_drawing = au.draw(
|
170 |
+
df_now_processed,
|
171 |
+
alpha2names=st.session_state["alpha2names"],
|
172 |
+
)
|
173 |
+
legend = au.make_legend_str(
|
174 |
+
df_now_processed, st.session_state["alpha2names"]
|
175 |
+
)
|
176 |
+
st.code(bracket_drawing + legend)
|
177 |
+
|
178 |
+
m = list(df_now_processed.human_readable_idx)
|
179 |
+
default_idx = st.session_state.get("selected_match", None)
|
180 |
+
cols = st.columns((1, 18, 1))
|
181 |
+
with cols[0]:
|
182 |
+
if st.button("◀", key="prev_match"):
|
183 |
+
navigate(m, default_idx, "selected_match", -1)
|
184 |
+
with cols[1]:
|
185 |
+
match_idx_human = QueryWrapper("match", "Select Match")(
|
186 |
+
st.selectbox,
|
187 |
+
m,
|
188 |
+
default_idx,
|
189 |
+
key=f"{task}_match_select",
|
190 |
+
label_visibility="collapsed",
|
191 |
+
)
|
192 |
+
with cols[2]:
|
193 |
+
if st.button("▶", key="next_match"):
|
194 |
+
navigate(m, default_idx, "selected_match", 1)
|
195 |
+
|
196 |
+
# match_idx_human = st.selectbox(
|
197 |
+
# "Select Match",
|
198 |
+
# df_now_processed.human_readable_idx,
|
199 |
+
# key=f"{task}_match_{result_select}",
|
200 |
+
# )
|
201 |
+
# print(match_idx_human)
|
202 |
+
st.session_state["selected_match"] = match_idx_human
|
203 |
+
# match_idx_human = st.selectbox(
|
204 |
+
# "Select Match",
|
205 |
+
# df_now_processed.human_readable_idx,
|
206 |
+
# key=f"{task}_match_{result_select}",
|
207 |
+
# )
|
208 |
+
if match_idx_human:
|
209 |
+
match_idx = int(match_idx_human.split(": ")[0])
|
210 |
+
row = df_now_processed.loc[match_idx]
|
211 |
+
|
212 |
+
st.markdown("#### Current Test Scenario:")
|
213 |
+
|
214 |
+
with st.expander(
|
215 |
+
f"### Evaluation Prompt (evalprompt: {eval_prompt_name}--{task})"
|
216 |
+
):
|
217 |
+
prompt = load_prompt(eval_prompt_name, task=task)
|
218 |
+
kwargs = dict(
|
219 |
+
inst="{inst}",
|
220 |
+
src="{src}",
|
221 |
+
out_a="{out_a}",
|
222 |
+
out_b="{out_b}",
|
223 |
+
task=task,
|
224 |
+
)
|
225 |
+
if eval_prompt_name == "translation_pair":
|
226 |
+
kwargs["source_lang"] = "{source_lang}"
|
227 |
+
kwargs["target_lang"] = "{target_lang}"
|
228 |
+
prompt_cmpl = prompt.complete_prompt(**kwargs)
|
229 |
+
for msg in prompt_cmpl:
|
230 |
+
st.markdown(f"**{msg['role']}**")
|
231 |
+
st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
|
232 |
+
|
233 |
+
st.info(show_linebreak_in_md(tournament_prm_select))
|
234 |
+
|
235 |
+
winner = row.winner
|
236 |
+
col1, col2 = st.columns(2)
|
237 |
+
|
238 |
+
winnerbox = st.success
|
239 |
+
loserbox = st.error
|
240 |
+
with col1:
|
241 |
+
iswinner = winner == "model_a"
|
242 |
+
writemsg = winnerbox if iswinner else loserbox
|
243 |
+
st.markdown(f"#### ({row.model_a}) {row.human_readable_model_a}")
|
244 |
+
writemsg(
|
245 |
+
show_linebreak_in_md(row.generated_a),
|
246 |
+
icon="✅" if iswinner else "❌",
|
247 |
+
)
|
248 |
+
with col2:
|
249 |
+
iswinner = winner == "model_b"
|
250 |
+
writemsg = winnerbox if iswinner else loserbox
|
251 |
+
st.markdown(f"#### ({row.model_b}) {row.human_readable_model_b}")
|
252 |
+
writemsg(
|
253 |
+
show_linebreak_in_md(row.generated_b),
|
254 |
+
icon="✅" if iswinner else "❌",
|
255 |
+
)
|
256 |
+
except Exception as e:
|
257 |
+
import traceback
|
258 |
+
|
259 |
+
traceback.print_exc()
|
260 |
+
st.markdown(
|
261 |
+
"**Bug: 아래 표를 복사해서 이슈로 남겨주시면 개선에 도움이 됩니다. 감사합니다🙏**"
|
262 |
+
if st.session_state.korean
|
263 |
+
else "Bug: Please open issue and attach the table output below to help me out. Thanks in advance.🙏"
|
264 |
+
)
|
265 |
+
st.error(e)
|
266 |
+
st.info(tournament_prm_select)
|
267 |
+
st.table(
|
268 |
+
df_now_processed[
|
269 |
+
[
|
270 |
+
"depth",
|
271 |
+
"round",
|
272 |
+
"winner_nodes",
|
273 |
+
"winner_resolved",
|
274 |
+
"winner",
|
275 |
+
"model_a",
|
276 |
+
"model_b",
|
277 |
+
]
|
278 |
+
]
|
279 |
+
)
|
280 |
+
st.write("Sharable link")
|
281 |
+
st.code(f"{get_base_url()}/see_results?{QueryWrapper.get_sharable_link()}")
|
282 |
+
st.divider()
|
283 |
+
|
284 |
+
if st.session_state.korean:
|
285 |
+
st.markdown("### 매치 통계")
|
286 |
+
else:
|
287 |
+
st.markdown("### Match Stats.")
|
288 |
+
col1, col2 = st.columns(2)
|
289 |
+
col1, col2 = st.columns(2)
|
290 |
+
with col1:
|
291 |
+
with st.container(border=True):
|
292 |
+
st.plotly_chart(
|
293 |
+
figure_dict[
|
294 |
+
"fraction_of_model_a_wins_for_all_a_vs_b_matches"
|
295 |
+
].update_layout(autosize=True, **default_layout_dict),
|
296 |
+
use_container_width=True,
|
297 |
+
key=f"{task}_fraction_of_model_a_wins_for_all_a_vs_b_matches",
|
298 |
+
)
|
299 |
+
with col2:
|
300 |
+
with st.container(border=True):
|
301 |
+
st.plotly_chart(
|
302 |
+
figure_dict["match_count_of_each_combination_of_models"].update_layout(
|
303 |
+
autosize=True, **default_layout_dict
|
304 |
+
),
|
305 |
+
use_container_width=True,
|
306 |
+
key=f"{task}_match_count_of_each_combination_of_models",
|
307 |
+
)
|
308 |
+
|
309 |
+
with col1:
|
310 |
+
with st.container(border=True):
|
311 |
+
st.plotly_chart(
|
312 |
+
figure_dict["match_count_for_each_model"].update_layout(
|
313 |
+
**default_layout_dict
|
314 |
+
),
|
315 |
+
use_container_width=True,
|
316 |
+
key=f"{task}_match_count_for_each_model",
|
317 |
+
)
|
318 |
+
with col2:
|
319 |
+
pass
|
320 |
+
|
321 |
+
if st.session_state.korean:
|
322 |
+
st.markdown("### 참고용 LLM Judge 편향 정보")
|
323 |
+
else:
|
324 |
+
st.markdown("### FYI: How biased is your LLM Judge?")
|
325 |
+
|
326 |
+
with st.expander("펼쳐서 보기" if st.session_state.korean else "Expand to show"):
|
327 |
+
st.info(
|
328 |
+
"""
|
329 |
+
Varco Arena에서는 position bias의 영향을 최소화하기 위해 모든 모델이 A나 B위치에 번갈아 위치하도록 하였습니다. 그러나 LLM Judge 혹은 Prompt의 성능이 부족하다고 느껴진다면, 아래 알려진 LLM Judge bias가 참고가 될겁니다.
|
330 |
+
* position bias (왼쪽)
|
331 |
+
* length bias (오른쪽)
|
332 |
+
|
333 |
+
결과의 왜곡이 LLM Judge의 부족함 떄문이었다는 점을 규명하려면 사용하신 LLM Judge와 Prompt의 binary classification 정확도를 측정해보시길 바랍니다 (Varco Arena를 활용하여 이를 수행해볼 수 있습니다!).""".strip()
|
334 |
+
if st.session_state.korean
|
335 |
+
else """
|
336 |
+
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:
|
337 |
+
* position bias (left)
|
338 |
+
* length bias (right)
|
339 |
+
|
340 |
+
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!).
|
341 |
+
""".strip()
|
342 |
+
)
|
343 |
+
st.markdown(f"#### {judgename} + prompt = {eval_prompt_name}")
|
344 |
+
col1, col2 = st.columns(2)
|
345 |
+
with col1:
|
346 |
+
with st.container(border=True):
|
347 |
+
st.plotly_chart(
|
348 |
+
figure_dict["counts_of_match_winners"].update_layout(
|
349 |
+
**default_layout_dict
|
350 |
+
),
|
351 |
+
use_container_width=True,
|
352 |
+
key=f"{task}_counts_of_match_winners",
|
353 |
+
)
|
354 |
+
with col2:
|
355 |
+
with st.container(border=True):
|
356 |
+
st.plotly_chart(
|
357 |
+
figure_dict["length_bias"].update_layout(**default_layout_dict),
|
358 |
+
use_container_width=True,
|
359 |
+
key=f"{task}_length_bias",
|
360 |
+
)
|
361 |
+
st.table(figure_dict["length_bias_df"].groupby("category").describe().T)
|
362 |
+
|
363 |
+
|
364 |
+
if __name__ == "__main__":
|
365 |
+
main()
|
precommit.sh
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# reformat all to prevent commit problem
|
2 |
+
git add .
|
3 |
+
pre-commit run --all-files
|
4 |
+
# restore the files added so that one can actually add files to be commit
|
5 |
+
git restore --staged .
|
query_comp.py
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# query를 자동으로 읽고 쓰는 container를 정의
|
2 |
+
|
3 |
+
from __future__ import annotations
|
4 |
+
|
5 |
+
import re
|
6 |
+
from typing import Callable, TypeVar
|
7 |
+
|
8 |
+
import streamlit as st
|
9 |
+
|
10 |
+
__all__ = ["QueryWrapper", "get_base_url"]
|
11 |
+
|
12 |
+
T = TypeVar("T")
|
13 |
+
|
14 |
+
|
15 |
+
import hashlib
|
16 |
+
import urllib.parse
|
17 |
+
|
18 |
+
|
19 |
+
def SHA1(msg: str) -> str:
|
20 |
+
return hashlib.sha1(msg.encode()).hexdigest()[:8]
|
21 |
+
|
22 |
+
|
23 |
+
def get_base_url():
|
24 |
+
session = st.runtime.get_instance()._session_mgr.list_active_sessions()[0]
|
25 |
+
return urllib.parse.urlunparse(
|
26 |
+
[session.client.request.protocol, session.client.request.host, "", "", "", ""]
|
27 |
+
)
|
28 |
+
|
29 |
+
|
30 |
+
class QueryWrapper:
|
31 |
+
queries: dict[str, _QueryWrapper] = {} # 기록용
|
32 |
+
|
33 |
+
def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
|
34 |
+
self.__wrapper = QueryWrapper.queries[query] = _QueryWrapper(
|
35 |
+
query, label, use_hash
|
36 |
+
)
|
37 |
+
|
38 |
+
def __call__(self, *args, **kwargs):
|
39 |
+
return self.__wrapper(*args, **kwargs)
|
40 |
+
|
41 |
+
@classmethod
|
42 |
+
def get_sharable_link(cls):
|
43 |
+
# for k, v in cls.queries.items():
|
44 |
+
# print(f"{k}: {v}")
|
45 |
+
return re.sub(
|
46 |
+
"&+", "&", "&".join([str(v) for k, v in cls.queries.items()])
|
47 |
+
).strip("&")
|
48 |
+
|
49 |
+
|
50 |
+
class _QueryWrapper:
|
51 |
+
ILLEGAL_CHARS = "&/=?"
|
52 |
+
|
53 |
+
def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
|
54 |
+
self.query = query
|
55 |
+
self.label = label or query
|
56 |
+
self.use_hash = use_hash
|
57 |
+
self.hash_table = {}
|
58 |
+
self.key = None
|
59 |
+
|
60 |
+
def __call__(
|
61 |
+
self,
|
62 |
+
base_container: Callable,
|
63 |
+
legal_list: list[T],
|
64 |
+
default: T | list[T] | None = None,
|
65 |
+
*,
|
66 |
+
key: str | None = None,
|
67 |
+
**kwargs,
|
68 |
+
) -> T | list[T] | None:
|
69 |
+
val_from_query = st.query_params.get_all(self.query.lower())
|
70 |
+
# print(val_from_query)
|
71 |
+
legal = len(val_from_query) > 0
|
72 |
+
self.key = key or self.label
|
73 |
+
|
74 |
+
self.hash_table = {SHA1(str(v)): v for v in legal_list}
|
75 |
+
|
76 |
+
# filter out illegal values
|
77 |
+
if legal and legal_list:
|
78 |
+
val_from_query = [v for v in val_from_query if v in self.hash_table]
|
79 |
+
# print(self.label, val_from_query, legal)
|
80 |
+
if legal:
|
81 |
+
selected = [self.hash_table[v] for v in val_from_query]
|
82 |
+
elif default:
|
83 |
+
selected = default
|
84 |
+
elif self.label in st.session_state:
|
85 |
+
selected = st.session_state[self.label]
|
86 |
+
if legal_list:
|
87 |
+
if isinstance(selected, list):
|
88 |
+
selected = [v for v in selected if v in legal_list]
|
89 |
+
elif selected not in legal_list:
|
90 |
+
selected = []
|
91 |
+
else:
|
92 |
+
selected = []
|
93 |
+
if selected is None:
|
94 |
+
pass
|
95 |
+
elif len(selected) == 1 and base_container in [st.selectbox, st.radio]:
|
96 |
+
selected = selected[0]
|
97 |
+
# print(self.label, selected)
|
98 |
+
if base_container == st.checkbox:
|
99 |
+
selected = base_container(
|
100 |
+
self.label,
|
101 |
+
legal_list,
|
102 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
103 |
+
key=self.key,
|
104 |
+
**kwargs,
|
105 |
+
)
|
106 |
+
elif base_container == st.multiselect:
|
107 |
+
selected = base_container(
|
108 |
+
self.label, legal_list, default=selected, key=self.key, **kwargs
|
109 |
+
)
|
110 |
+
elif base_container == st.radio:
|
111 |
+
selected = base_container(
|
112 |
+
self.label,
|
113 |
+
legal_list,
|
114 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
115 |
+
key=self.key,
|
116 |
+
**kwargs,
|
117 |
+
)
|
118 |
+
elif base_container == st.selectbox:
|
119 |
+
selected = base_container(
|
120 |
+
self.label,
|
121 |
+
legal_list,
|
122 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
123 |
+
key=self.key,
|
124 |
+
**kwargs,
|
125 |
+
)
|
126 |
+
else:
|
127 |
+
selected = base_container(self.label, legal_list, key=self.key, **kwargs)
|
128 |
+
return st.session_state[self.key]
|
129 |
+
|
130 |
+
def __str__(self):
|
131 |
+
selected = st.session_state.get(self.key, None)
|
132 |
+
if isinstance(selected, str):
|
133 |
+
return f"{self.query.lower()}={SHA1(selected)}"
|
134 |
+
elif isinstance(selected, list):
|
135 |
+
return "&".join([f"{self.query.lower()}={SHA1(str(v))}" for v in selected])
|
136 |
+
else:
|
137 |
+
return ""
|
requirements.txt
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
openai>=1.17.0
|
2 |
+
munch
|
3 |
+
pandas
|
4 |
+
numpy
|
5 |
+
tqdm>=4.48.0
|
6 |
+
plotly
|
7 |
+
scikit-learn
|
8 |
+
kaleido
|
9 |
+
tiktoken>=0.7.0
|
10 |
+
pyyaml
|
11 |
+
transformers
|
12 |
+
streamlit>=1.40.2
|
13 |
+
openpyxl
|
14 |
+
git+https://github.com/shobrook/openlimit.git#egg=openlimit
|
run.sh
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
# 여기서 돌려야해요 (cwd = ./streamlit_app_components/)
|
3 |
+
# VARCO_ARENA_RESULT_PATH=./user_submit streamlit run app.py
|
4 |
+
streamlit run app.py
|
streamlit_app_local/.streamlit/config.toml
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[client]
|
2 |
+
showSidebarNavigation = false
|
3 |
+
[server]
|
4 |
+
headless = true
|
streamlit_app_local/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Varco Arena web app
|
2 |
+
```bash
|
3 |
+
cd ./streamlit_app_local/
|
4 |
+
bash run.sh
|
5 |
+
```
|
6 |
+
|
7 |
+
## view: Running
|
8 |
+
![run_page](streamlit_app_local/page_run.png)
|
9 |
+
|
10 |
+
## view: Results
|
11 |
+
![result_page1](./page_result_1.png)
|
12 |
+
![result_page2](./page_result_2.png)
|
13 |
+
![result_page3](./page_result_3.png)
|
14 |
+
![result_page4](./page_result_4.png)
|
15 |
+
|
16 |
+
## how to use
|
17 |
+
1. upload llm outputs in jsonlines format
|
18 |
+
2. cost estimation
|
19 |
+
3. run
|
20 |
+
4. go to see results tab
|
21 |
+
|
22 |
+
## example run results at...
|
23 |
+
- `streamlit_app_local/user_submit/`
|
24 |
+
- visible on web if you `bash run.sh`
|
streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc
ADDED
Binary file (17.7 kB). View file
|
|
streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc
ADDED
Binary file (9.12 kB). View file
|
|
streamlit_app_local/__pycache__/app.cpython-311.pyc
ADDED
Binary file (15.9 kB). View file
|
|
streamlit_app_local/__pycache__/app.cpython-38.pyc
ADDED
Binary file (6.32 kB). View file
|
|
streamlit_app_local/__pycache__/query_comp.cpython-311.pyc
ADDED
Binary file (8 kB). View file
|
|
streamlit_app_local/__pycache__/view_utils.cpython-311.pyc
ADDED
Binary file (18.3 kB). View file
|
|
streamlit_app_local/__pycache__/view_utils.cpython-38.pyc
ADDED
Binary file (9.91 kB). View file
|
|
streamlit_app_local/analysis_utils.py
ADDED
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from copy import deepcopy
|
2 |
+
from functools import partial
|
3 |
+
from typing import *
|
4 |
+
|
5 |
+
import pandas as pd
|
6 |
+
from fire import Fire
|
7 |
+
|
8 |
+
"""
|
9 |
+
This code assumes dealing with only one instruction
|
10 |
+
"""
|
11 |
+
|
12 |
+
|
13 |
+
# from varco_arena.tournament
|
14 |
+
def log2_power_of_two(n):
|
15 |
+
# First, let's make sure n is indeed a power of 2
|
16 |
+
if n & (n - 1) != 0 or n == 0:
|
17 |
+
raise ValueError("n must be a positive power of 2")
|
18 |
+
|
19 |
+
exponent = 0
|
20 |
+
while n > 1:
|
21 |
+
n >>= 1 # Right shift is like dividing by 2, but faster
|
22 |
+
exponent += 1
|
23 |
+
return exponent
|
24 |
+
|
25 |
+
|
26 |
+
def get_1st(df: pd.DataFrame, alpha2names: dict) -> Optional[str]:
|
27 |
+
finals = df[df["round"] == "final"]
|
28 |
+
if len(finals) == 1:
|
29 |
+
first = finals.iloc[0].winner_resolved
|
30 |
+
else:
|
31 |
+
first = (
|
32 |
+
None # error case (no finals match or multiple finals (buggy result file))
|
33 |
+
)
|
34 |
+
|
35 |
+
return first
|
36 |
+
|
37 |
+
|
38 |
+
def get_unique_participants(df: pd.DataFrame) -> list:
|
39 |
+
participants = pd.concat([df.model_a, df.model_b]).unique().tolist()
|
40 |
+
participants = [p for p in participants if p] # remove None
|
41 |
+
participants = sorted(participants) # make it sorted
|
42 |
+
return participants
|
43 |
+
|
44 |
+
|
45 |
+
def _impute_byes(df):
|
46 |
+
max_depth = df.depth.max()
|
47 |
+
|
48 |
+
# init
|
49 |
+
imputed_parts = dict()
|
50 |
+
for depth in range(max_depth + 1):
|
51 |
+
imputed_parts[depth] = df[df.depth == depth].copy()
|
52 |
+
|
53 |
+
# reverse
|
54 |
+
for depth in range(max_depth, 0, -1): # always we have 1 proper match for depth=0
|
55 |
+
null_v_null = {
|
56 |
+
"model_a": "",
|
57 |
+
"model_b": "",
|
58 |
+
"winner": "model_a",
|
59 |
+
"match_order_in_round": "-",
|
60 |
+
"depth": depth,
|
61 |
+
}
|
62 |
+
|
63 |
+
# fill some_model vs null byes
|
64 |
+
players = get_unique_participants(imputed_parts[depth])
|
65 |
+
proceeded = get_unique_participants(imputed_parts[depth - 1])
|
66 |
+
imputed = []
|
67 |
+
for p in proceeded:
|
68 |
+
if p not in players:
|
69 |
+
p_v_null = deepcopy(null_v_null)
|
70 |
+
p_v_null["model_a"] = p
|
71 |
+
imputed.append(p_v_null)
|
72 |
+
imputed_parts[depth] = pd.concat(
|
73 |
+
[
|
74 |
+
imputed_parts[depth],
|
75 |
+
pd.DataFrame(imputed),
|
76 |
+
],
|
77 |
+
axis="index",
|
78 |
+
)
|
79 |
+
|
80 |
+
# fill null vs null
|
81 |
+
n_null_v_null = 2 ** (depth) - len(imputed_parts[depth])
|
82 |
+
if n_null_v_null > 0:
|
83 |
+
imputed = pd.DataFrame([null_v_null] * n_null_v_null)
|
84 |
+
imputed_parts[depth] = pd.concat(
|
85 |
+
[
|
86 |
+
imputed_parts[depth],
|
87 |
+
imputed,
|
88 |
+
],
|
89 |
+
axis="index",
|
90 |
+
)
|
91 |
+
|
92 |
+
df_imputed = pd.concat(imputed_parts.values(), axis="index")
|
93 |
+
df_imputed = df_imputed.sort_values(by="depth").reset_index(drop=True)
|
94 |
+
|
95 |
+
return df_imputed
|
96 |
+
|
97 |
+
|
98 |
+
def index_test_scenario(df) -> pd.DataFrame:
|
99 |
+
df["inst_src"] = "inst: " + df.instruction + "\n\nsrc: " + df.source
|
100 |
+
df["idx_inst_src"] = df.apply(
|
101 |
+
lambda row: f"{row.tournament_idx}:\n{row.inst_src}", axis=1
|
102 |
+
)
|
103 |
+
|
104 |
+
# later used for tournament bracket backtrackiung
|
105 |
+
if "depth" not in df.columns:
|
106 |
+
mappings = {
|
107 |
+
"final": 0,
|
108 |
+
"semi-final": 1,
|
109 |
+
"quarter-final": 2,
|
110 |
+
}
|
111 |
+
|
112 |
+
def _convert_round_to_depth(rnd: str, mappings=None) -> int:
|
113 |
+
if rnd is None:
|
114 |
+
depth = None
|
115 |
+
elif rnd in mappings.keys():
|
116 |
+
depth = mappings[rnd]
|
117 |
+
elif rnd.startswith("round-"): # assume perfect power of two
|
118 |
+
num = int(rnd.replace("round-", "").strip())
|
119 |
+
depth = log2_power_of_two(num) - 1
|
120 |
+
return depth
|
121 |
+
|
122 |
+
conv = partial(_convert_round_to_depth, mappings=mappings)
|
123 |
+
df["depth"] = df["round"].apply(conv)
|
124 |
+
|
125 |
+
return df
|
126 |
+
|
127 |
+
|
128 |
+
def init_tournament_dataframe(df, alpha2names: dict = None) -> pd.DataFrame:
|
129 |
+
df = df.sort_values(by="depth").reset_index(drop=True)
|
130 |
+
# make winner interpretable (A -> model_a, B -> model_b)
|
131 |
+
df.winner = df.winner.apply(lambda txt: f"model_{txt.lower()}")
|
132 |
+
|
133 |
+
# define alpha2names if not given (covers upto 168 participants)
|
134 |
+
if alpha2names is None:
|
135 |
+
alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
136 |
+
abcdefghijklmnopqrstuvwxyz\
|
137 |
+
ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ\
|
138 |
+
㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭\
|
139 |
+
㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻\
|
140 |
+
ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ\
|
141 |
+
ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ\
|
142 |
+
αβγδεζηθικλμνξοπρστυφχψω"
|
143 |
+
model_full_names = get_unique_participants(df)
|
144 |
+
alpha2names = dict(zip(alphabets, model_full_names))
|
145 |
+
if len(alpha2names) < len(model_full_names):
|
146 |
+
raise ValueError(
|
147 |
+
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."
|
148 |
+
)
|
149 |
+
names2alpha = dict(zip(alpha2names.values(), alpha2names.keys()))
|
150 |
+
df = _impute_byes(df)
|
151 |
+
|
152 |
+
# preserve readables for later
|
153 |
+
df = _make_readables(df, names2alpha)
|
154 |
+
|
155 |
+
if len(df[df["round"] == "final"]) != 1:
|
156 |
+
raise ValueError(f"final match need to be one and only.")
|
157 |
+
|
158 |
+
return df, alpha2names
|
159 |
+
|
160 |
+
|
161 |
+
def _make_readables(df, names2alpha):
|
162 |
+
df["human_readable_model_a"] = df.model_a.copy()
|
163 |
+
df["human_readable_model_b"] = df.model_b.copy()
|
164 |
+
|
165 |
+
df.model_a = df.model_a.apply(
|
166 |
+
lambda modelname: names2alpha[modelname] if modelname else "x"
|
167 |
+
)
|
168 |
+
df.model_b = df.model_b.apply(
|
169 |
+
lambda modelname: names2alpha[modelname] if modelname else "x"
|
170 |
+
)
|
171 |
+
|
172 |
+
df["human_readable_idx"] = df.apply(
|
173 |
+
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'})",
|
174 |
+
axis=1,
|
175 |
+
)
|
176 |
+
|
177 |
+
df["winner_resolved"] = df.apply(lambda row: row[row.winner], axis=1)
|
178 |
+
df["winner_nodes"] = df.apply(
|
179 |
+
lambda row: f"{row.winner_resolved}:{row.name}".ljust(4, " "), axis=1
|
180 |
+
) # later for figure representation of winner as a "node"
|
181 |
+
return df
|
182 |
+
|
183 |
+
|
184 |
+
# draw
|
185 |
+
def draw(df: pd.DataFrame, alpha2names: dict = None) -> str:
|
186 |
+
def _draw_round(
|
187 |
+
df: pd.DataFrame,
|
188 |
+
depth: int = None,
|
189 |
+
winners_in_order: list = None,
|
190 |
+
) -> Tuple:
|
191 |
+
df_now = df[df.depth == depth]
|
192 |
+
max_depth = df.depth.max()
|
193 |
+
|
194 |
+
width = 2 ** ((max_depth - depth) + 2)
|
195 |
+
|
196 |
+
connect_left = "─" * (width)
|
197 |
+
connect_left = connect_left[4:]
|
198 |
+
connect_right = " " * (width)
|
199 |
+
connect_right = "┐" + connect_right[1:]
|
200 |
+
|
201 |
+
if winners_in_order is None:
|
202 |
+
assert (
|
203 |
+
depth == 0
|
204 |
+
), f"{winners_in_order=} is only allowed when drawing the top (=final match)"
|
205 |
+
winners_in_order = df_now.winner_nodes
|
206 |
+
|
207 |
+
round_drawing_parts = []
|
208 |
+
descending_round_winners = []
|
209 |
+
for node in winners_in_order:
|
210 |
+
round_drawing_parts.append("".join([node, connect_left, connect_right]))
|
211 |
+
# next round winners in sync with winner order
|
212 |
+
row_now = df_now.query(f"winner_nodes=='{node}'")
|
213 |
+
descending_round_winners.append(row_now.model_a.item())
|
214 |
+
descending_round_winners.append(row_now.model_b.item())
|
215 |
+
|
216 |
+
# find descending_round_winners within winner_nodes format (num:alpha)
|
217 |
+
if depth == max_depth:
|
218 |
+
pass # keep the descending_round_winners intact
|
219 |
+
else:
|
220 |
+
df_descend = df[df.depth == depth + 1]
|
221 |
+
for i, winner_alpha in enumerate(descending_round_winners):
|
222 |
+
node_intr = df_descend.query(
|
223 |
+
f"winner_resolved=='{winner_alpha}'"
|
224 |
+
).winner_nodes.item()
|
225 |
+
descending_round_winners[i] = node_intr
|
226 |
+
|
227 |
+
round_drawing = "".join(round_drawing_parts)
|
228 |
+
descending_unit = " " * width
|
229 |
+
descending_unit = "│" + descending_unit[1:]
|
230 |
+
descending_lines_parts = [descending_unit] * len(df_now) * 2
|
231 |
+
descending_lines = "".join(descending_lines_parts)
|
232 |
+
|
233 |
+
return round_drawing, descending_lines, descending_round_winners
|
234 |
+
|
235 |
+
drawings = []
|
236 |
+
winners_in_order = None
|
237 |
+
max_depth = df.depth.max()
|
238 |
+
for depth in range(max_depth + 1):
|
239 |
+
max_depth = df.depth.max()
|
240 |
+
winner_drw, lines_desc, winners_in_order = _draw_round(
|
241 |
+
df,
|
242 |
+
depth=depth,
|
243 |
+
winners_in_order=winners_in_order,
|
244 |
+
)
|
245 |
+
drawings.append((winner_drw, lines_desc))
|
246 |
+
# prepare bracket top
|
247 |
+
champion_alphabet = drawings[0][0].split()[0].split(":")[0]
|
248 |
+
champion_readable = alpha2names[champion_alphabet]
|
249 |
+
bracket_top = [f"🥇winner: {champion_readable}", "│"]
|
250 |
+
# prepare mid
|
251 |
+
bracket_mid = "\n".join(["\n".join(tup) for tup in drawings])
|
252 |
+
|
253 |
+
# prepare bot
|
254 |
+
initial_participants = winners_in_order
|
255 |
+
bracket_bot = (" " * 3).join(initial_participants)
|
256 |
+
|
257 |
+
full_figure = "\n".join(bracket_top + [bracket_mid, bracket_bot])
|
258 |
+
|
259 |
+
return full_figure
|
260 |
+
|
261 |
+
|
262 |
+
def number_breakdown_from_df(result_df: pd.DataFrame) -> str:
|
263 |
+
n_models = len(get_unique_participants(result_df))
|
264 |
+
size_testset = int(len(result_df) / (n_models - 1))
|
265 |
+
interpretation = f"total {len(result_df)} matches = (n_models-1) * size_testset = ({n_models}-1) * {size_testset}"
|
266 |
+
return interpretation, n_models, size_testset
|
267 |
+
|
268 |
+
|
269 |
+
def make_legend_str(df, alpha2names) -> str:
|
270 |
+
first = get_1st(df, alpha2names)
|
271 |
+
alpha2names = {k: v.replace("🥇 ", "") for k, v in alpha2names.items()}
|
272 |
+
alpha_ordered = sorted(list(alpha2names.keys()))
|
273 |
+
# names_ordered = sorted(list(alpha2names.values()))
|
274 |
+
# name2alpha = {v: k for k, v in alpha2names.items()}
|
275 |
+
|
276 |
+
for k, v in alpha2names.items():
|
277 |
+
if v == alpha2names[first]:
|
278 |
+
alpha2names[k] = f"🥇 {v}"
|
279 |
+
res_str = f"\n\nlegend:"
|
280 |
+
# for name in names_ordered:
|
281 |
+
# alpha = name2alpha[name]
|
282 |
+
for alpha in alpha_ordered:
|
283 |
+
name_w_medal = alpha2names[alpha]
|
284 |
+
res_str += f"\n{alpha}\t{name_w_medal}"
|
285 |
+
return res_str
|
286 |
+
|
287 |
+
|
288 |
+
def main(
|
289 |
+
jslname: str = "result.json",
|
290 |
+
):
|
291 |
+
"""
|
292 |
+
테스트 코드
|
293 |
+
"""
|
294 |
+
|
295 |
+
df = pd.read_json(jslname, orient="records")
|
296 |
+
df = df.drop(columns=["tstamp", "logs"])
|
297 |
+
df = index_test_scenario(df)
|
298 |
+
|
299 |
+
# 중간에 visualization(df) 여기선 생략. 만약 이거 뺴고 다 따라했는데 문제가 생긴다면 viz 문제다. 근데 안그럴거같긴함
|
300 |
+
selections = df.idx_inst_src.unique()
|
301 |
+
for i, sel in enumerate(selections):
|
302 |
+
try:
|
303 |
+
df_now = df[df.idx_inst_src == sel]
|
304 |
+
df_now_processed, _alpha2names = init_tournament_dataframe(
|
305 |
+
df_now, alpha2names=alpha2names if "alpha2names" in dir() else None
|
306 |
+
)
|
307 |
+
if "alpha2names" not in dir():
|
308 |
+
alpha2names = _alpha2names
|
309 |
+
assert "alpha2names" in dir()
|
310 |
+
bracket_drawing = draw(df_now_processed, alpha2names=alpha2names)
|
311 |
+
legend = make_legend_str(df_now_processed, alpha2names)
|
312 |
+
print(bracket_drawing + legend)
|
313 |
+
print(bracket_drawing + legend, file=open(f"{i}.txt", "w"))
|
314 |
+
print(f"\n\n{sel}", file=open(f"{i}.txt", "a"))
|
315 |
+
|
316 |
+
for match_idx_human in df_now_processed.human_readable_idx:
|
317 |
+
match_idx = int(match_idx_human.split(": ")[0])
|
318 |
+
row = df_now_processed.loc[match_idx]
|
319 |
+
winner = row.winner
|
320 |
+
except Exception as e:
|
321 |
+
print(e, file=open(f"{i}_err.txt", "w"))
|
322 |
+
print("", file=open(f"{i}_err.txt", "a"))
|
323 |
+
print(sel, file=open(f"{i}_err.txt", "a"))
|
324 |
+
df_now_processed[
|
325 |
+
[
|
326 |
+
"depth",
|
327 |
+
"round",
|
328 |
+
"winner_nodes",
|
329 |
+
"winner_resolved",
|
330 |
+
"winner",
|
331 |
+
"model_a",
|
332 |
+
"model_b",
|
333 |
+
]
|
334 |
+
].to_json(f"{i}_err.jsonl", lines=True, orient="records")
|
335 |
+
|
336 |
+
|
337 |
+
if __name__ == "__main__":
|
338 |
+
Fire(main)
|
streamlit_app_local/app.py
ADDED
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import shutil
|
2 |
+
import os
|
3 |
+
import select
|
4 |
+
import subprocess
|
5 |
+
import sys
|
6 |
+
import time
|
7 |
+
from datetime import datetime, timedelta, timezone
|
8 |
+
from pathlib import Path
|
9 |
+
from typing import *
|
10 |
+
|
11 |
+
import streamlit as st
|
12 |
+
|
13 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
14 |
+
|
15 |
+
from varco_arena_core.prompts import load_prompt
|
16 |
+
|
17 |
+
from view_utils import (
|
18 |
+
default_page_setting,
|
19 |
+
escape_markdown,
|
20 |
+
set_nav_bar,
|
21 |
+
show_linebreak_in_md,
|
22 |
+
)
|
23 |
+
|
24 |
+
# import sys
|
25 |
+
# print(sys.executable)
|
26 |
+
|
27 |
+
|
28 |
+
VA_ROOT = Path(os.environ.get("VARCO_ARENA_RESULT_PATH", "./user_submit"))
|
29 |
+
USR_SUB = VA_ROOT.parts[-1]
|
30 |
+
|
31 |
+
|
32 |
+
def upload_files(uploaded_files) -> Path:
|
33 |
+
# prep directory for user submission
|
34 |
+
user_sub_root = VA_ROOT
|
35 |
+
if user_sub_root.exists():
|
36 |
+
if not user_sub_root.is_dir():
|
37 |
+
raise ValueError(
|
38 |
+
f"{user_sub_root} file exists and is not a directory. Consider renaming it."
|
39 |
+
)
|
40 |
+
else:
|
41 |
+
user_sub_root.mkdir(parents=True)
|
42 |
+
|
43 |
+
KST = timezone(timedelta(hours=9))
|
44 |
+
tstamp = datetime.now(KST)
|
45 |
+
tstr = tstamp.strftime("%m-%d_%H:%M:%S")
|
46 |
+
files_dir_str = "./" + str(user_sub_root / tstr)
|
47 |
+
files_dir = Path(files_dir_str)
|
48 |
+
files_dir.mkdir(parents=True, exist_ok=True)
|
49 |
+
uploaded_files = list(uploaded_files)
|
50 |
+
|
51 |
+
if not uploaded_files:
|
52 |
+
st.warning("❌ No files to upload. Please drag/drop or browse files to upload.")
|
53 |
+
elif len(uploaded_files) < 2:
|
54 |
+
st.error("❌ You need at least 2 jsonlines files to properly run VA.")
|
55 |
+
else: # properly uploaded
|
56 |
+
for file in uploaded_files:
|
57 |
+
# Create a path for the file in the server directory
|
58 |
+
file_path = files_dir / file.name
|
59 |
+
|
60 |
+
# Save the file to the server directory
|
61 |
+
with open(file_path, "wb") as f:
|
62 |
+
f.write(file.getbuffer())
|
63 |
+
|
64 |
+
jslfiles = list(files_dir.glob("*.jsonl"))
|
65 |
+
st.success(f"✅ Successfully uploaded {len(jslfiles)} jsonl files.")
|
66 |
+
return files_dir.resolve()
|
67 |
+
|
68 |
+
|
69 |
+
def run_varco_arena(
|
70 |
+
price_estimation: bool = False,
|
71 |
+
# upload_dir: Union[str, Path] = None,
|
72 |
+
promptname: str = None,
|
73 |
+
exp_name: str = None,
|
74 |
+
api_key: Optional[str] = None,
|
75 |
+
evaluation_model: str = "gpt-4o-mini",
|
76 |
+
update_interval: float = 1.0,
|
77 |
+
):
|
78 |
+
# Use environment variable for API key
|
79 |
+
ptn = f"{str(st.session_state.upfiles_dir)}"
|
80 |
+
outdir = Path(ptn)
|
81 |
+
if exp_name:
|
82 |
+
outdir = outdir / exp_name
|
83 |
+
|
84 |
+
command = f"python ../varco_arena/main.py -i {ptn} -o {outdir} -k {api_key} -p {promptname} -e {evaluation_model} -j 64"
|
85 |
+
if price_estimation:
|
86 |
+
command = f"{command} -c"
|
87 |
+
else:
|
88 |
+
command = command.replace("python", "yes | python ")
|
89 |
+
print(command)
|
90 |
+
|
91 |
+
api_key = None # clear immediately
|
92 |
+
|
93 |
+
process = subprocess.Popen(
|
94 |
+
command,
|
95 |
+
stdout=subprocess.PIPE,
|
96 |
+
stderr=subprocess.STDOUT,
|
97 |
+
stdin=subprocess.PIPE,
|
98 |
+
text=True,
|
99 |
+
bufsize=1,
|
100 |
+
shell=True,
|
101 |
+
)
|
102 |
+
|
103 |
+
# Set stdout and stdin to non-blocking mode
|
104 |
+
os.set_blocking(process.stdout.fileno(), False)
|
105 |
+
|
106 |
+
last_update_time = time.time()
|
107 |
+
terminal_output = st.empty()
|
108 |
+
full_output = f"{command}\n"
|
109 |
+
while True:
|
110 |
+
# Check if we have output to read
|
111 |
+
if select.select([process.stdout], [], [], 0)[0]:
|
112 |
+
output = process.stdout.readline()
|
113 |
+
if output:
|
114 |
+
full_output += output
|
115 |
+
if price_estimation:
|
116 |
+
to_show = full_output
|
117 |
+
terminal_output.code(to_show, language="bash")
|
118 |
+
else:
|
119 |
+
current_time = time.time()
|
120 |
+
if current_time - last_update_time > update_interval:
|
121 |
+
lines = full_output.split("\n")
|
122 |
+
if len(lines) < 5:
|
123 |
+
to_show = full_output
|
124 |
+
else:
|
125 |
+
to_show = "\n".join(["...\n..\n.\n"] + lines[-5:])
|
126 |
+
terminal_output.code(to_show, language="bash")
|
127 |
+
last_update_time = current_time
|
128 |
+
print(output)
|
129 |
+
time.sleep(0.1)
|
130 |
+
# Check if the process has finished
|
131 |
+
if process.poll() is not None:
|
132 |
+
# Read any remaining output
|
133 |
+
remaining_output = process.stdout.read()
|
134 |
+
if remaining_output:
|
135 |
+
lines = remaining_output.split("\n")
|
136 |
+
if len(lines) > 10:
|
137 |
+
to_show += "\n".join(["\n...\n..\n.\n"] + lines[-10:])
|
138 |
+
else:
|
139 |
+
to_show += remaining_output
|
140 |
+
terminal_output.code(to_show, language="bash")
|
141 |
+
print(remaining_output)
|
142 |
+
break
|
143 |
+
|
144 |
+
return_code = process.poll()
|
145 |
+
return outdir, return_code
|
146 |
+
|
147 |
+
|
148 |
+
def main():
|
149 |
+
# init lang
|
150 |
+
st.session_state["korean"] = st.session_state.get("korean", False)
|
151 |
+
|
152 |
+
sidebar_placeholder = default_page_setting()
|
153 |
+
set_nav_bar(
|
154 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_init"
|
155 |
+
)
|
156 |
+
|
157 |
+
st.title("⚔️ VARCO ARENA ⚔️")
|
158 |
+
if st.session_state.korean:
|
159 |
+
st.write(
|
160 |
+
"**VARCO Arena는 각 모델의 생성된 결과를 비교 평가하여 모델의 성능 순위를 제공하는 시스템입니다. 커스텀 테스트셋에 유용하게 활용할 수 있으며, 모범답안을 필요로 하지 않습니다.**"
|
161 |
+
)
|
162 |
+
else:
|
163 |
+
st.write(
|
164 |
+
"**VARCO Arena is an LLM benchmarking system that compares model responses across customized test scenarios without requiring reference answers.**"
|
165 |
+
)
|
166 |
+
|
167 |
+
st.divider()
|
168 |
+
# Set up the file uploader
|
169 |
+
if st.session_state.korean:
|
170 |
+
st.markdown("모델 출력파일 업로드")
|
171 |
+
else:
|
172 |
+
st.markdown("### 1. Upload LLM responses")
|
173 |
+
uploaded_files = st.file_uploader(
|
174 |
+
"Drag and Drop jsonlines files (.jsonl)", accept_multiple_files=True
|
175 |
+
)
|
176 |
+
|
177 |
+
# upload state
|
178 |
+
if "upfiles_dir" not in st.session_state:
|
179 |
+
st.session_state.upfiles_dir = None
|
180 |
+
if st.button("Upload Files"):
|
181 |
+
st.session_state.upfiles_dir = upload_files(uploaded_files)
|
182 |
+
# st.success(st.session_state.upfiles_dir)
|
183 |
+
|
184 |
+
# st.markdown("**💥주의: 중복된 테스트 시나리오는 오류로 처리됩니다💥**")
|
185 |
+
if st.session_state.korean:
|
186 |
+
with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
|
187 |
+
st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
|
188 |
+
else:
|
189 |
+
with st.expander("❓❔ What should I upload ❓❔"):
|
190 |
+
st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
|
191 |
+
|
192 |
+
# Form for cost estimation
|
193 |
+
with st.form("cost_estimation_form"):
|
194 |
+
if st.session_state.korean:
|
195 |
+
st.write("### 2. 가격 산정")
|
196 |
+
else:
|
197 |
+
st.write("### 2. Cost Estimation")
|
198 |
+
eval_model = st.selectbox(
|
199 |
+
"Select Judge",
|
200 |
+
open("eval_models_list.txt", encoding="UTF8").read().split("\n"),
|
201 |
+
)
|
202 |
+
promptname = st.selectbox(
|
203 |
+
"Select Evalutaion Prompt",
|
204 |
+
open("eval_prompt_list.txt", encoding="UTF8").read().split("\n"),
|
205 |
+
)
|
206 |
+
if promptname == USR_SUB:
|
207 |
+
raise ValueError(
|
208 |
+
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])."
|
209 |
+
)
|
210 |
+
estimate_button = st.form_submit_button("Calculate Cost!")
|
211 |
+
with st.expander(
|
212 |
+
"LLM Judge에 활용되는 프롬프트 (`Calculate Cost!` 클릭시 갱신)"
|
213 |
+
if st.session_state.korean
|
214 |
+
else "**Evaluation Prompt for LLM Judge (will refresh after `Calculate Cost!` clicked)**"
|
215 |
+
):
|
216 |
+
prompt = load_prompt(promptname, task="-")
|
217 |
+
kwargs = dict(
|
218 |
+
inst="{inst}",
|
219 |
+
src="{src}",
|
220 |
+
out_a="{out_a}",
|
221 |
+
out_b="{out_b}",
|
222 |
+
task="-",
|
223 |
+
)
|
224 |
+
if promptname == "translation_pair":
|
225 |
+
kwargs["source_lang"] = "{source_lang}"
|
226 |
+
kwargs["target_lang"] = "{target_lang}"
|
227 |
+
prompt_cmpl = prompt.complete_prompt(**kwargs)
|
228 |
+
|
229 |
+
st.markdown(f"### Evaluation Prompt: {promptname}")
|
230 |
+
for msg in prompt_cmpl:
|
231 |
+
st.markdown(f"**{msg['role']}**")
|
232 |
+
st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
|
233 |
+
|
234 |
+
if estimate_button:
|
235 |
+
if st.session_state.get("upfiles_dir") is None:
|
236 |
+
st.error(
|
237 |
+
"❌ Requirements: You have to upload jsonlines files first to proceed"
|
238 |
+
)
|
239 |
+
else:
|
240 |
+
st.markdown("##### Estimated Cost")
|
241 |
+
dummy_api_key = "dummy"
|
242 |
+
dummy_exp_name = "dummy"
|
243 |
+
result_file_path, return_code = run_varco_arena(
|
244 |
+
# upload_dir=st.session_state.upfiles_dir,
|
245 |
+
promptname=promptname,
|
246 |
+
api_key=dummy_api_key,
|
247 |
+
exp_name=dummy_exp_name,
|
248 |
+
price_estimation=True,
|
249 |
+
evaluation_model=eval_model,
|
250 |
+
)
|
251 |
+
if return_code:
|
252 |
+
st.error("❌ RuntimeError: An error occurred during cost estimation")
|
253 |
+
else:
|
254 |
+
st.success("✅ Cost estimation completed successfully")
|
255 |
+
st.session_state.cost_estimated = True
|
256 |
+
|
257 |
+
# Form for actual run
|
258 |
+
with st.form("run_arena_form"):
|
259 |
+
if st.session_state.korean:
|
260 |
+
st.write("### 3. Varco Arena 구동하기")
|
261 |
+
else:
|
262 |
+
st.write("### 3. Run Varco Arena")
|
263 |
+
api_key = st.text_input("Enter your OpenAI API Key", type="password")
|
264 |
+
exp_name = st.text_input("(Optional) Enter Exp. name")
|
265 |
+
exp_name = exp_name.replace(
|
266 |
+
"..", "_"
|
267 |
+
) # May cause rmtree problem later. Block it.
|
268 |
+
exp_name = exp_name.replace(
|
269 |
+
USR_SUB, f"-{USR_SUB}-"
|
270 |
+
) # May cause rmtree problem later. Block it.
|
271 |
+
exp_name = exp_name.replace("/", "-")
|
272 |
+
exp_name = exp_name.replace(" ", "_")
|
273 |
+
exp_name = exp_name.replace("~", "_")
|
274 |
+
|
275 |
+
if st.session_state.korean:
|
276 |
+
st.write("**주의**:`Ctrl+C` 버튼은 구현되지 않았습니다. 구동 전 숙고해주세요.")
|
277 |
+
else:
|
278 |
+
st.write("**Caution: `Ctrl+C` button hasn't been implemented.**")
|
279 |
+
run_button = st.form_submit_button(
|
280 |
+
"🔥 Run Arena!",
|
281 |
+
disabled=(not st.session_state.get("cost_estimated", False))
|
282 |
+
or "result_file_path"
|
283 |
+
in st.session_state.keys(), # run already performed once
|
284 |
+
)
|
285 |
+
|
286 |
+
if run_button:
|
287 |
+
set_nav_bar(
|
288 |
+
True,
|
289 |
+
sidebar_placeholder=sidebar_placeholder,
|
290 |
+
toggle_hashstr="app_during_run",
|
291 |
+
)
|
292 |
+
if st.session_state.get("upfiles_dir") is None:
|
293 |
+
st.error(
|
294 |
+
"❌ Requirements: You have to upload jsonlines files first to proceed"
|
295 |
+
)
|
296 |
+
elif not api_key:
|
297 |
+
st.error("❌ Requirements: OpenAI key required to run VA.")
|
298 |
+
else:
|
299 |
+
result_file_path, return_code = run_varco_arena(
|
300 |
+
# upload_dir=st.session_state.upfiles_dir,
|
301 |
+
promptname=promptname,
|
302 |
+
api_key=api_key,
|
303 |
+
exp_name=exp_name,
|
304 |
+
price_estimation=False,
|
305 |
+
evaluation_model=eval_model,
|
306 |
+
)
|
307 |
+
if return_code:
|
308 |
+
st.error("❌ RuntimeError: An error occurred during Varco Arena run")
|
309 |
+
else:
|
310 |
+
st.success("✅ Varco Arena run completed successfully")
|
311 |
+
st.session_state.result_file_path = result_file_path
|
312 |
+
set_nav_bar(
|
313 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="app_run_done"
|
314 |
+
)
|
315 |
+
|
316 |
+
|
317 |
+
if __name__ == "__main__":
|
318 |
+
main()
|
streamlit_app_local/eval_models_list.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
gpt-4o-mini
|
2 |
+
gpt-4o-2024-05-13
|
3 |
+
gpt-4o-2024-08-06
|
streamlit_app_local/eval_prompt_list.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
llmbar
|
2 |
+
llmbar_brief
|
3 |
+
translation_pair
|
4 |
+
rag_pair_kr
|
5 |
+
contextual (WIP)
|
streamlit_app_local/guide_mds
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
../guide_mds/
|
streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc
ADDED
Binary file (2.85 kB). View file
|
|
streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc
ADDED
Binary file (889 Bytes). View file
|
|
streamlit_app_local/modules/nav.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
|
4 |
+
def Navbar(sidebar_placeholder, toggle_hashstr: str = ""):
|
5 |
+
with sidebar_placeholder.container():
|
6 |
+
st.toggle(
|
7 |
+
"🇰🇷 한국어로 보기",
|
8 |
+
value=st.session_state.get("korean", False),
|
9 |
+
key=f"korean_toggle_{toggle_hashstr}", # Add this explicit key
|
10 |
+
on_change=lambda: setattr(
|
11 |
+
st.session_state,
|
12 |
+
"korean",
|
13 |
+
st.session_state.get(f"korean_toggle_{toggle_hashstr}", False),
|
14 |
+
),
|
15 |
+
)
|
16 |
+
|
17 |
+
st.page_link(
|
18 |
+
"app.py",
|
19 |
+
label="Varco Arena 구동" if st.session_state.korean else "Run VARCO Arena",
|
20 |
+
icon="🔥",
|
21 |
+
)
|
22 |
+
st.page_link(
|
23 |
+
"pages/see_results.py",
|
24 |
+
label="결과 보기" if st.session_state.korean else "See Results",
|
25 |
+
icon="📝",
|
26 |
+
disabled=st.session_state.get("is_running", False),
|
27 |
+
)
|
28 |
+
st.page_link(
|
29 |
+
"pages/brief_intro.py",
|
30 |
+
label="어떻게 작동하나요?" if st.session_state.korean else "How it Works",
|
31 |
+
icon="❔",
|
32 |
+
disabled=st.session_state.get("is_running", False),
|
33 |
+
)
|
34 |
+
related_links_en = """
|
35 |
+
**About**
|
36 |
+
* [Paper](https://huggingface.co/papers/2411.01281)
|
37 |
+
* [Blog (KR)](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
38 |
+
* [Inquiry](https://linkedin.com/in/deftson/)
|
39 |
+
""".strip()
|
40 |
+
related_links_kr = """
|
41 |
+
**About**
|
42 |
+
* [논문](https://huggingface.co/papers/2411.01281)
|
43 |
+
* [블로그](https://ncsoft.github.io/ncresearch/12cc62c1ea0d981971a8923401e8fe6a0f18563d)
|
44 |
+
* [문의](https://linkedin.com/in/deftson/)
|
45 |
+
""".strip()
|
46 |
+
st.info(related_links_kr if st.session_state.korean else related_links_en)
|
streamlit_app_local/pages/brief_intro.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
from view_utils import default_page_setting, set_nav_bar
|
4 |
+
|
5 |
+
sidebar_placeholder = default_page_setting()
|
6 |
+
set_nav_bar(
|
7 |
+
False, sidebar_placeholder=sidebar_placeholder, toggle_hashstr="brief_intro_init"
|
8 |
+
)
|
9 |
+
|
10 |
+
st.image("va_en.png")
|
11 |
+
if st.session_state.korean:
|
12 |
+
st.info("여러분이 업로드하는 파일은 위 그림의 LLM 출력들입니다.")
|
13 |
+
with st.expander("❓❔ 무엇을 업로드 하나요❓❔"):
|
14 |
+
st.info(open("guide_mds/input_jsonls_kr.md", encoding="UTF8").read())
|
15 |
+
else:
|
16 |
+
st.info(
|
17 |
+
"What you upload is the generated responses of LLMs regarding the test prompts."
|
18 |
+
)
|
19 |
+
with st.expander("❓❔ What should I upload ❓❔"):
|
20 |
+
st.info(open("guide_mds/input_jsonls_en.md", encoding="UTF8").read())
|
21 |
+
|
22 |
+
|
23 |
+
st.image("va_concept_new.png")
|
24 |
+
st.markdown(
|
25 |
+
"""
|
26 |
+
| |Current Practice|Varco Arena|
|
27 |
+
|-|-|-|
|
28 |
+
|Total no. matches|$$n_{\\text{model}}*\\|X\\|$$|$$(n_{\\text{model}}-1)*\\|X\\|$$|
|
29 |
+
|No. matches per LLM|$$\\|X\\|$$|$$\\left[\\|X\\|,\\|X\\|\\text{log}n_{\\text{model}}\\right]$$|
|
30 |
+
|Comparison type|reference-mediated (anchored) |direct|
|
31 |
+
"""
|
32 |
+
)
|
33 |
+
if st.session_state.korean:
|
34 |
+
st.info(
|
35 |
+
"Varco Arena는 신뢰성 있는 순위를 더 적은 횟수의 비교 내에 얻어내며, 이러한 특징은 LLM 직접 비교의 이점으로부터 기인합니다."
|
36 |
+
)
|
37 |
+
else:
|
38 |
+
st.info(
|
39 |
+
"Varco Arena takes advantage of direct comparison between LLM responses to guarantee better reliability in fewer number of total matches."
|
40 |
+
)
|
streamlit_app_local/pages/see_results.py
ADDED
@@ -0,0 +1,391 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import streamlit as st
|
3 |
+
from varco_arena_core.prompts import load_prompt
|
4 |
+
|
5 |
+
import analysis_utils as au
|
6 |
+
from analysis_utils import number_breakdown_from_df
|
7 |
+
from app import VA_ROOT
|
8 |
+
from query_comp import QueryWrapper, get_base_url
|
9 |
+
from view_utils import (
|
10 |
+
default_page_setting,
|
11 |
+
escape_markdown,
|
12 |
+
set_nav_bar,
|
13 |
+
show_linebreak_in_md,
|
14 |
+
visualization,
|
15 |
+
)
|
16 |
+
|
17 |
+
DEFAULT_LAYOUT_DICT = {
|
18 |
+
"title": {"font": {"size": 20, "family": "Gothic A1"}},
|
19 |
+
"font": {"size": 16, "family": "Gothic A1"},
|
20 |
+
"xaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
|
21 |
+
"yaxis": {"tickfont": {"size": 12, "family": "Gothic A1"}},
|
22 |
+
"legend": {"font": {"size": 12, "family": "Gothic A1"}},
|
23 |
+
}
|
24 |
+
|
25 |
+
|
26 |
+
@st.cache_data
|
27 |
+
def load_data():
|
28 |
+
all_result_dict = {}
|
29 |
+
df_dict = {}
|
30 |
+
# result_dir_list = os.listdir(VA_ROOT)
|
31 |
+
# result_dir_list = sorted(result_dir_list, key=lambda x: os.stat(os.path.join(VA_ROOT, x)).st_mtime, reverse=True)
|
32 |
+
for f in VA_ROOT.glob("**/result.json"):
|
33 |
+
print(f)
|
34 |
+
|
35 |
+
for resjsonf in VA_ROOT.glob("**/result.json"):
|
36 |
+
df = pd.read_json(resjsonf)
|
37 |
+
for col in ["tstamp", "logs"]:
|
38 |
+
if col in df.columns:
|
39 |
+
df.drop(columns=[col], inplace=True)
|
40 |
+
df = au.index_test_scenario(df)
|
41 |
+
|
42 |
+
fig_dict_per_task = {}
|
43 |
+
df_dict_per_task = {}
|
44 |
+
fig_dict_per_task["Overall"] = visualization(df, is_overall=True)
|
45 |
+
df_dict_per_task["Overall"] = df
|
46 |
+
for task in df["task"].unique().tolist():
|
47 |
+
df_task = df[df["task"] == task]
|
48 |
+
fig_dict_per_task[task] = visualization(df_task, is_overall=False)
|
49 |
+
df_dict_per_task[task] = df_task
|
50 |
+
|
51 |
+
prm_name = resjsonf.parts[-2]
|
52 |
+
exp_name = resjsonf.parts[-3]
|
53 |
+
key = f"{exp_name}/{prm_name}"
|
54 |
+
all_result_dict[key] = fig_dict_per_task
|
55 |
+
df_dict[key] = df_dict_per_task
|
56 |
+
return all_result_dict, df_dict
|
57 |
+
|
58 |
+
|
59 |
+
def navigate(t, source, key, val):
|
60 |
+
# print(key, val)
|
61 |
+
if source is None:
|
62 |
+
return
|
63 |
+
target_index = t.index(source) + val
|
64 |
+
if 0 <= target_index < len(t):
|
65 |
+
st.session_state[key] = t[target_index]
|
66 |
+
st.rerun()
|
67 |
+
|
68 |
+
|
69 |
+
def main():
|
70 |
+
sidebar_placeholder = default_page_setting(layout="wide")
|
71 |
+
set_nav_bar(
|
72 |
+
False,
|
73 |
+
sidebar_placeholder=sidebar_placeholder,
|
74 |
+
toggle_hashstr="see_results_init",
|
75 |
+
)
|
76 |
+
# load the data
|
77 |
+
st.session_state["all_result_dict"], st.session_state["df_dict"] = load_data()
|
78 |
+
|
79 |
+
# side bar
|
80 |
+
st.sidebar.title("Select Result:")
|
81 |
+
result_select = QueryWrapper("expname", "")(
|
82 |
+
st.sidebar.selectbox,
|
83 |
+
list(st.session_state["all_result_dict"].keys()),
|
84 |
+
)
|
85 |
+
|
86 |
+
if result_select is None:
|
87 |
+
st.stop()
|
88 |
+
eval_prompt_name = result_select.split("/")[-1].strip()
|
89 |
+
if st.sidebar.button("Clear Cache"):
|
90 |
+
st.cache_data.clear()
|
91 |
+
st.cache_resource.clear()
|
92 |
+
st.rerun()
|
93 |
+
|
94 |
+
if result_select:
|
95 |
+
if "alpha2names" in st.session_state:
|
96 |
+
del st.session_state["alpha2names"]
|
97 |
+
|
98 |
+
fig_dict_per_task = st.session_state["all_result_dict"][result_select]
|
99 |
+
task_list = list(fig_dict_per_task.keys())
|
100 |
+
elo_rating_by_task = fig_dict_per_task["Overall"]["elo_rating_by_task"]
|
101 |
+
# tabs = st.tabs(task_list)
|
102 |
+
|
103 |
+
df_dict_per_task = st.session_state["df_dict"][result_select]
|
104 |
+
|
105 |
+
default_layout_dict = DEFAULT_LAYOUT_DICT
|
106 |
+
task = QueryWrapper("task", "Select Task")(st.selectbox, task_list)
|
107 |
+
|
108 |
+
if task is None:
|
109 |
+
st.stop()
|
110 |
+
figure_dict = fig_dict_per_task[task]
|
111 |
+
judgename = figure_dict["judgename"]
|
112 |
+
df = df_dict_per_task[task]
|
113 |
+
interpretation, n_models, size_testset = number_breakdown_from_df(df)
|
114 |
+
if st.session_state.korean:
|
115 |
+
st.markdown(f"## 결과 ({task})")
|
116 |
+
st.markdown(f"##### Judge 모델: {judgename} / 평가프롬: {eval_prompt_name}")
|
117 |
+
st.markdown(f"##### 테스트셋 사이즈: {int(size_testset)} 행")
|
118 |
+
else:
|
119 |
+
st.markdown(f"## Results ({task})")
|
120 |
+
st.markdown(f"##### Judge Model: {judgename} / prompt: {eval_prompt_name}")
|
121 |
+
st.markdown(f"##### Size of Testset: {int(size_testset)} rows")
|
122 |
+
col1, col2 = st.columns(2)
|
123 |
+
with col1:
|
124 |
+
with st.container(border=True):
|
125 |
+
st.markdown(f"#### Ratings ({task})")
|
126 |
+
st.table(figure_dict["elo_rating"])
|
127 |
+
st.write(show_linebreak_in_md(escape_markdown(interpretation)))
|
128 |
+
st.markdown(f"**files at:** \n`{str(VA_ROOT.resolve()/result_select)}`")
|
129 |
+
|
130 |
+
with col2:
|
131 |
+
with st.container(border=True):
|
132 |
+
st.plotly_chart(
|
133 |
+
elo_rating_by_task.update_layout(**default_layout_dict),
|
134 |
+
use_container_width=True,
|
135 |
+
key=f"{task}_elo_rating_by_task",
|
136 |
+
)
|
137 |
+
|
138 |
+
st.divider()
|
139 |
+
|
140 |
+
if st.session_state.korean:
|
141 |
+
st.markdown("### 토너먼트 (테스트 시나리오) 별로 보기")
|
142 |
+
else:
|
143 |
+
st.markdown("### Tournament Results by Test Scenario")
|
144 |
+
|
145 |
+
# with st.expander("볼 토너먼트 고르기"):
|
146 |
+
d = list(df.idx_inst_src.unique())
|
147 |
+
default_idx = st.session_state.get("selected_tournament", None)
|
148 |
+
cols = st.columns((1, 18, 1))
|
149 |
+
with cols[0]:
|
150 |
+
if st.button("◀", key="prev_tournament"):
|
151 |
+
navigate(d, default_idx, "selected_tournament", -1)
|
152 |
+
with cols[1]:
|
153 |
+
tournament_prm_select = QueryWrapper("tournament", "Select Tournament")(
|
154 |
+
st.selectbox,
|
155 |
+
d,
|
156 |
+
default_idx,
|
157 |
+
key=f"{task}_tournament_select",
|
158 |
+
on_change=lambda: st.session_state.update(
|
159 |
+
selected_tournament=st.session_state.get(f"{task}_tournament_select"),
|
160 |
+
selected_match=None,
|
161 |
+
),
|
162 |
+
label_visibility="collapsed",
|
163 |
+
)
|
164 |
+
with cols[2]:
|
165 |
+
if st.button("▶", key="next_tournament"):
|
166 |
+
navigate(d, default_idx, "selected_tournament", 1)
|
167 |
+
|
168 |
+
# tournament_prm_select = st.selectbox(
|
169 |
+
# "Select Tournament",
|
170 |
+
# df.idx_inst_src.unique(),
|
171 |
+
# index=d.index(st.session_state.get("selected_tournament")),
|
172 |
+
# key=f"{task}_tournament_{result_select}",
|
173 |
+
# )
|
174 |
+
|
175 |
+
# print(tournament_prm_select, type(tournament_prm_select))
|
176 |
+
st.session_state["selected_tournament"] = tournament_prm_select
|
177 |
+
# tournament_prm_select = st.selectbox(
|
178 |
+
# "Select Tournament",
|
179 |
+
# df.idx_inst_src.unique(),
|
180 |
+
# key=f"{task}_tournament_{result_select}",
|
181 |
+
# )
|
182 |
+
df_now_processed = None
|
183 |
+
if tournament_prm_select:
|
184 |
+
df_now = df[df.idx_inst_src == tournament_prm_select]
|
185 |
+
df_now_processed, _alpha2names = au.init_tournament_dataframe(
|
186 |
+
df_now,
|
187 |
+
alpha2names=st.session_state["alpha2names"]
|
188 |
+
if "alpha2names" in st.session_state.keys()
|
189 |
+
else None,
|
190 |
+
)
|
191 |
+
if "alpha2names" not in st.session_state:
|
192 |
+
st.session_state["alpha2names"] = _alpha2names
|
193 |
+
|
194 |
+
try:
|
195 |
+
bracket_drawing = au.draw(
|
196 |
+
df_now_processed,
|
197 |
+
alpha2names=st.session_state["alpha2names"],
|
198 |
+
)
|
199 |
+
legend = au.make_legend_str(
|
200 |
+
df_now_processed, st.session_state["alpha2names"]
|
201 |
+
)
|
202 |
+
st.code(bracket_drawing + legend)
|
203 |
+
|
204 |
+
m = list(df_now_processed.human_readable_idx)
|
205 |
+
default_idx = st.session_state.get("selected_match", None)
|
206 |
+
cols = st.columns((1, 18, 1))
|
207 |
+
with cols[0]:
|
208 |
+
if st.button("◀", key="prev_match"):
|
209 |
+
navigate(m, default_idx, "selected_match", -1)
|
210 |
+
with cols[1]:
|
211 |
+
match_idx_human = QueryWrapper("match", "Select Match")(
|
212 |
+
st.selectbox,
|
213 |
+
m,
|
214 |
+
default_idx,
|
215 |
+
key=f"{task}_match_select",
|
216 |
+
label_visibility="collapsed",
|
217 |
+
)
|
218 |
+
with cols[2]:
|
219 |
+
if st.button("▶", key="next_match"):
|
220 |
+
navigate(m, default_idx, "selected_match", 1)
|
221 |
+
|
222 |
+
# match_idx_human = st.selectbox(
|
223 |
+
# "Select Match",
|
224 |
+
# df_now_processed.human_readable_idx,
|
225 |
+
# key=f"{task}_match_{result_select}",
|
226 |
+
# )
|
227 |
+
# print(match_idx_human)
|
228 |
+
st.session_state["selected_match"] = match_idx_human
|
229 |
+
# match_idx_human = st.selectbox(
|
230 |
+
# "Select Match",
|
231 |
+
# df_now_processed.human_readable_idx,
|
232 |
+
# key=f"{task}_match_{result_select}",
|
233 |
+
# )
|
234 |
+
if match_idx_human:
|
235 |
+
match_idx = int(match_idx_human.split(": ")[0])
|
236 |
+
row = df_now_processed.loc[match_idx]
|
237 |
+
|
238 |
+
st.markdown("#### Current Test Scenario:")
|
239 |
+
|
240 |
+
with st.expander(
|
241 |
+
f"### Evaluation Prompt (evalprompt: {eval_prompt_name}--{task})"
|
242 |
+
):
|
243 |
+
prompt = load_prompt(eval_prompt_name, task=task)
|
244 |
+
kwargs = dict(
|
245 |
+
inst="{inst}",
|
246 |
+
src="{src}",
|
247 |
+
out_a="{out_a}",
|
248 |
+
out_b="{out_b}",
|
249 |
+
task=task,
|
250 |
+
)
|
251 |
+
if eval_prompt_name == "translation_pair":
|
252 |
+
kwargs["source_lang"] = "{source_lang}"
|
253 |
+
kwargs["target_lang"] = "{target_lang}"
|
254 |
+
prompt_cmpl = prompt.complete_prompt(**kwargs)
|
255 |
+
for msg in prompt_cmpl:
|
256 |
+
st.markdown(f"**{msg['role']}**")
|
257 |
+
st.info(show_linebreak_in_md(escape_markdown(msg["content"])))
|
258 |
+
|
259 |
+
st.info(show_linebreak_in_md(tournament_prm_select))
|
260 |
+
|
261 |
+
winner = row.winner
|
262 |
+
col1, col2 = st.columns(2)
|
263 |
+
|
264 |
+
winnerbox = st.success
|
265 |
+
loserbox = st.error
|
266 |
+
with col1:
|
267 |
+
iswinner = winner == "model_a"
|
268 |
+
writemsg = winnerbox if iswinner else loserbox
|
269 |
+
st.markdown(f"#### ({row.model_a}) {row.human_readable_model_a}")
|
270 |
+
writemsg(
|
271 |
+
show_linebreak_in_md(row.generated_a),
|
272 |
+
icon="✅" if iswinner else "❌",
|
273 |
+
)
|
274 |
+
with col2:
|
275 |
+
iswinner = winner == "model_b"
|
276 |
+
writemsg = winnerbox if iswinner else loserbox
|
277 |
+
st.markdown(f"#### ({row.model_b}) {row.human_readable_model_b}")
|
278 |
+
writemsg(
|
279 |
+
show_linebreak_in_md(row.generated_b),
|
280 |
+
icon="✅" if iswinner else "❌",
|
281 |
+
)
|
282 |
+
except Exception as e:
|
283 |
+
import traceback
|
284 |
+
|
285 |
+
traceback.print_exc()
|
286 |
+
st.markdown(
|
287 |
+
"**Bug: 아래 표를 복사해서 이슈로 남겨주시면 개선에 도움이 됩니다. 감사합니다🙏**"
|
288 |
+
if st.session_state.korean
|
289 |
+
else "Bug: Please open issue and attach the table output below to help me out. Thanks in advance.🙏"
|
290 |
+
)
|
291 |
+
st.error(e)
|
292 |
+
st.info(tournament_prm_select)
|
293 |
+
st.table(
|
294 |
+
df_now_processed[
|
295 |
+
[
|
296 |
+
"depth",
|
297 |
+
"round",
|
298 |
+
"winner_nodes",
|
299 |
+
"winner_resolved",
|
300 |
+
"winner",
|
301 |
+
"model_a",
|
302 |
+
"model_b",
|
303 |
+
]
|
304 |
+
]
|
305 |
+
)
|
306 |
+
st.write("Sharable link")
|
307 |
+
st.code(f"{get_base_url()}/see_results?{QueryWrapper.get_sharable_link()}")
|
308 |
+
st.divider()
|
309 |
+
|
310 |
+
if st.session_state.korean:
|
311 |
+
st.markdown("### 매치 통계")
|
312 |
+
else:
|
313 |
+
st.markdown("### Match Stats.")
|
314 |
+
col1, col2 = st.columns(2)
|
315 |
+
col1, col2 = st.columns(2)
|
316 |
+
with col1:
|
317 |
+
with st.container(border=True):
|
318 |
+
st.plotly_chart(
|
319 |
+
figure_dict[
|
320 |
+
"fraction_of_model_a_wins_for_all_a_vs_b_matches"
|
321 |
+
].update_layout(autosize=True, **default_layout_dict),
|
322 |
+
use_container_width=True,
|
323 |
+
key=f"{task}_fraction_of_model_a_wins_for_all_a_vs_b_matches",
|
324 |
+
)
|
325 |
+
with col2:
|
326 |
+
with st.container(border=True):
|
327 |
+
st.plotly_chart(
|
328 |
+
figure_dict["match_count_of_each_combination_of_models"].update_layout(
|
329 |
+
autosize=True, **default_layout_dict
|
330 |
+
),
|
331 |
+
use_container_width=True,
|
332 |
+
key=f"{task}_match_count_of_each_combination_of_models",
|
333 |
+
)
|
334 |
+
|
335 |
+
with col1:
|
336 |
+
with st.container(border=True):
|
337 |
+
st.plotly_chart(
|
338 |
+
figure_dict["match_count_for_each_model"].update_layout(
|
339 |
+
**default_layout_dict
|
340 |
+
),
|
341 |
+
use_container_width=True,
|
342 |
+
key=f"{task}_match_count_for_each_model",
|
343 |
+
)
|
344 |
+
with col2:
|
345 |
+
pass
|
346 |
+
|
347 |
+
if st.session_state.korean:
|
348 |
+
st.markdown("### 참고용 LLM Judge 편향 정보")
|
349 |
+
else:
|
350 |
+
st.markdown("### FYI: How biased is your LLM Judge?")
|
351 |
+
|
352 |
+
with st.expander("펼쳐서 보기" if st.session_state.korean else "Expand to show"):
|
353 |
+
st.info(
|
354 |
+
"""
|
355 |
+
Varco Arena에서는 position bias의 영향을 최소화하기 위해 모든 모델이 A나 B위치에 번갈아 위치하도록 하였습니다. 그러나 LLM Judge 혹은 Prompt의 성능이 부족하다고 느껴진다면, 아래 알려진 LLM Judge bias가 참고가 될겁니다.
|
356 |
+
* position bias (왼쪽)
|
357 |
+
* length bias (오른쪽)
|
358 |
+
|
359 |
+
결과의 왜곡이 LLM Judge의 부족함 떄문이었다는 점을 규명하려면 사용하신 LLM Judge와 Prompt의 binary classification 정확도를 측정해보시길 바랍니다 (Varco Arena를 활용하여 이를 수행해볼 수 있습니다!).""".strip()
|
360 |
+
if st.session_state.korean
|
361 |
+
else """
|
362 |
+
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:
|
363 |
+
* position bias (left)
|
364 |
+
* length bias (right)
|
365 |
+
|
366 |
+
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!).
|
367 |
+
""".strip()
|
368 |
+
)
|
369 |
+
st.markdown(f"#### {judgename} + prompt = {eval_prompt_name}")
|
370 |
+
col1, col2 = st.columns(2)
|
371 |
+
with col1:
|
372 |
+
with st.container(border=True):
|
373 |
+
st.plotly_chart(
|
374 |
+
figure_dict["counts_of_match_winners"].update_layout(
|
375 |
+
**default_layout_dict
|
376 |
+
),
|
377 |
+
use_container_width=True,
|
378 |
+
key=f"{task}_counts_of_match_winners",
|
379 |
+
)
|
380 |
+
with col2:
|
381 |
+
with st.container(border=True):
|
382 |
+
st.plotly_chart(
|
383 |
+
figure_dict["length_bias"].update_layout(**default_layout_dict),
|
384 |
+
use_container_width=True,
|
385 |
+
key=f"{task}_length_bias",
|
386 |
+
)
|
387 |
+
st.table(figure_dict["length_bias_df"].groupby("category").describe().T)
|
388 |
+
|
389 |
+
|
390 |
+
if __name__ == "__main__":
|
391 |
+
main()
|
streamlit_app_local/query_comp.py
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# query를 자동으로 읽고 쓰는 container를 정의
|
2 |
+
|
3 |
+
from __future__ import annotations
|
4 |
+
|
5 |
+
import re
|
6 |
+
from typing import Callable, TypeVar
|
7 |
+
|
8 |
+
import streamlit as st
|
9 |
+
|
10 |
+
__all__ = ["QueryWrapper", "get_base_url"]
|
11 |
+
|
12 |
+
T = TypeVar("T")
|
13 |
+
|
14 |
+
|
15 |
+
import hashlib
|
16 |
+
import urllib.parse
|
17 |
+
|
18 |
+
|
19 |
+
def SHA1(msg: str) -> str:
|
20 |
+
return hashlib.sha1(msg.encode()).hexdigest()[:8]
|
21 |
+
|
22 |
+
|
23 |
+
def get_base_url():
|
24 |
+
session = st.runtime.get_instance()._session_mgr.list_active_sessions()[0]
|
25 |
+
return urllib.parse.urlunparse(
|
26 |
+
[session.client.request.protocol, session.client.request.host, "", "", "", ""]
|
27 |
+
)
|
28 |
+
|
29 |
+
|
30 |
+
class QueryWrapper:
|
31 |
+
queries: dict[str, _QueryWrapper] = {} # 기록용
|
32 |
+
|
33 |
+
def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
|
34 |
+
self.__wrapper = QueryWrapper.queries[query] = _QueryWrapper(
|
35 |
+
query, label, use_hash
|
36 |
+
)
|
37 |
+
|
38 |
+
def __call__(self, *args, **kwargs):
|
39 |
+
return self.__wrapper(*args, **kwargs)
|
40 |
+
|
41 |
+
@classmethod
|
42 |
+
def get_sharable_link(cls):
|
43 |
+
# for k, v in cls.queries.items():
|
44 |
+
# print(f"{k}: {v}")
|
45 |
+
return re.sub(
|
46 |
+
"&+", "&", "&".join([str(v) for k, v in cls.queries.items()])
|
47 |
+
).strip("&")
|
48 |
+
|
49 |
+
|
50 |
+
class _QueryWrapper:
|
51 |
+
ILLEGAL_CHARS = "&/=?"
|
52 |
+
|
53 |
+
def __init__(self, query: str, label: str | None = None, use_hash: bool = True):
|
54 |
+
self.query = query
|
55 |
+
self.label = label or query
|
56 |
+
self.use_hash = use_hash
|
57 |
+
self.hash_table = {}
|
58 |
+
self.key = None
|
59 |
+
|
60 |
+
def __call__(
|
61 |
+
self,
|
62 |
+
base_container: Callable,
|
63 |
+
legal_list: list[T],
|
64 |
+
default: T | list[T] | None = None,
|
65 |
+
*,
|
66 |
+
key: str | None = None,
|
67 |
+
**kwargs,
|
68 |
+
) -> T | list[T] | None:
|
69 |
+
val_from_query = st.query_params.get_all(self.query.lower())
|
70 |
+
# print(val_from_query)
|
71 |
+
legal = len(val_from_query) > 0
|
72 |
+
self.key = key or self.label
|
73 |
+
|
74 |
+
self.hash_table = {SHA1(str(v)): v for v in legal_list}
|
75 |
+
|
76 |
+
# filter out illegal values
|
77 |
+
if legal and legal_list:
|
78 |
+
val_from_query = [v for v in val_from_query if v in self.hash_table]
|
79 |
+
# print(self.label, val_from_query, legal)
|
80 |
+
if legal:
|
81 |
+
selected = [self.hash_table[v] for v in val_from_query]
|
82 |
+
elif default:
|
83 |
+
selected = default
|
84 |
+
elif self.label in st.session_state:
|
85 |
+
selected = st.session_state[self.label]
|
86 |
+
if legal_list:
|
87 |
+
if isinstance(selected, list):
|
88 |
+
selected = [v for v in selected if v in legal_list]
|
89 |
+
elif selected not in legal_list:
|
90 |
+
selected = []
|
91 |
+
else:
|
92 |
+
selected = []
|
93 |
+
if len(selected) == 1 and base_container in [st.selectbox, st.radio]:
|
94 |
+
selected = selected[0]
|
95 |
+
# print(self.label, selected)
|
96 |
+
if base_container == st.checkbox:
|
97 |
+
selected = base_container(
|
98 |
+
self.label,
|
99 |
+
legal_list,
|
100 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
101 |
+
key=self.key,
|
102 |
+
**kwargs,
|
103 |
+
)
|
104 |
+
elif base_container == st.multiselect:
|
105 |
+
selected = base_container(
|
106 |
+
self.label, legal_list, default=selected, key=self.key, **kwargs
|
107 |
+
)
|
108 |
+
elif base_container == st.radio:
|
109 |
+
selected = base_container(
|
110 |
+
self.label,
|
111 |
+
legal_list,
|
112 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
113 |
+
key=self.key,
|
114 |
+
**kwargs,
|
115 |
+
)
|
116 |
+
elif base_container == st.selectbox:
|
117 |
+
selected = base_container(
|
118 |
+
self.label,
|
119 |
+
legal_list,
|
120 |
+
index=legal_list.index(selected) if selected in legal_list else None,
|
121 |
+
key=self.key,
|
122 |
+
**kwargs,
|
123 |
+
)
|
124 |
+
else:
|
125 |
+
selected = base_container(self.label, legal_list, key=self.key, **kwargs)
|
126 |
+
return st.session_state[self.key]
|
127 |
+
|
128 |
+
def __str__(self):
|
129 |
+
selected = st.session_state.get(self.key, None)
|
130 |
+
if isinstance(selected, str):
|
131 |
+
return f"{self.query.lower()}={SHA1(selected)}"
|
132 |
+
elif isinstance(selected, list):
|
133 |
+
return "&".join([f"{self.query.lower()}={SHA1(str(v))}" for v in selected])
|
134 |
+
else:
|
135 |
+
return ""
|
streamlit_app_local/requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
plotly
|
3 |
+
scikit-learn
|
4 |
+
numpy
|
5 |
+
pandas
|
streamlit_app_local/run.sh
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
# 여기서 돌려야해요 (cwd = ./streamlit_app_components/)
|
3 |
+
VARCO_ARENA_RESULT_PATH=./user_submit streamlit run app.py
|
streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"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":""}
|
2 |
+
{"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":""}
|
3 |
+
{"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":""}
|
4 |
+
{"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":""}
|
5 |
+
{"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":""}
|
6 |
+
{"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":""}
|
streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"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":""}
|
2 |
+
{"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":""}
|
3 |
+
{"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":""}
|
4 |
+
{"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":""}
|
5 |
+
{"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":""}
|
6 |
+
{"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":""}
|
streamlit_app_local/user_submit/llm/240413_dora.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "난 여름보다는 겨울이 더 좋아."}
|
2 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "근데 난 여름보다 겨울이 더 좋아. 왜냐하면 살짝 추운 날씨가 옷 입기도 쉽고, 푹 쉬고 있을 수도 있어서 좋거든."}
|
3 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다 겨울이 좋아. 왜냐하면 겨울에는 밖에 나가서 놀기도 편하고, 눈 덮인 산이나 호수를 볼 수 있어서 좋기 때문이야."}
|
4 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교라는 곳에 방문했다. 그리고 집에 돌아와서는 바로 숙제 문제집을 풀기 시작했는데, 이게 지금까지 했던 것 중에서 가장 집중이 잘 됐다. 지금은 숙제도 마치고 슬슬 잠자리에 드는 중이다."}
|
5 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 하루 종일 학교에 갔다 왔다. 집에 와서 잠깐 앉아 휴식을 취하고, 숙제도 마저 다 끝냈다. 오늘은 책도 많이 읽고, 마음도 편하게 쉬어야겠다."}
|
6 |
+
{"model_id": "240413_dora", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 갔다. 집에 와서 쉬는 시간에 공부를 하며 하루를 마무리 지었다."}
|
streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 마음에 들어."}
|
2 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 아무래도 여름보다는 겨울이 더 좋다. 이유는 물론 추워서이기도 하지만, 내가 피부로 느끼는 것들 때문이다. 특히나 눈은 정말 하얗고 깨끗하다. 이것은 겨울에만 볼 수 있는 가장 아름다운 풍경 중 하나다."}
|
3 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋아."}
|
4 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와서 열심히 공부했는데 내일부터는 또 다른 숙제들이 기다리고 있을 것이다."}
|
5 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "아 오늘은 정말 바쁜 하루였다. 학교에 가서 열심히 공부했다. 그리고 집에 돌아와서는 숙제도 다 끝내놓고 자기로 했다."}
|
6 |
+
{"model_id": "240413_dora_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 했다. 그리고 오늘 숙제도 다 마치고, 이제 잘 준비하려고 한다~"}
|
streamlit_app_local/user_submit/llm/240413_full.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
|
2 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울을 훨씬 더 선호해. 여름의 높은 기온은 나를 지치게 만들거든. 시원한 바람과 함께 따뜻한 이불 속에서 푹 쉬다가 다시 새로운 마음으로 다시 시작하고 싶어."}
|
3 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 더 좋다."}
|
4 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘의 일상 기록:\n오늘 나는 학교에 갔다. 집에 돌아와서 공부를 했다. 오늘의 숙제도 끝마치고 이제 곧 잘 준비할 시간이다."}
|
5 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 정말 하루 종일 바빴다. 학교를 갔고, 집에 돌아와서 바로 공부를 시작했다. 오늘 할 숙제를 모두 마쳤고, 이제는 잠을 자기 위해 자리에 누워야겠다."}
|
6 |
+
{"model_id": "240413_full", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔었다. 집에 와서 공부를 시작했다. 오늘 과제도 다 끝마치고 이제 잠자리에 들 시간이다."}
|
streamlit_app_local/user_submit/llm/240413_full_epoch_5.jsonl
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋은 것 같아."}
|
2 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보다는 겨울이 훨씬 더 좋다. 겨울에는 추운 날씨 덕분에 산책이나 운동을 하기에도 편하고, 겨울의 정취를 느낄 수 있어서 좋다. 그리고 겨울에는 눈이 내릴 때의 분위기가 정말 낭만적이다."}
|
3 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "난 여름보다는 겨울이 좋아.", "generated": "나는 여름보단 겨울이 더 좋더라. 겨울에는 차가운 바람이 얼굴을 톡톡 두드리고, 눈이 내리면 세상이 하얗고 아름답게 보여."}
|
4 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 내용을 유지한 채, 어휘를 더 길게 바꾸거나 사소한 내용을 추가하여 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 방문했다. 집에 돌아와 열심히 공부했다. 오늘의 과제도 모두 끝마치고, 이제는 잠에 들 시간이다."}
|
5 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 중간 중간에 새로운 내용이나 문장을 추가하여 길게 늘려서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘은 학교에 가는 날이었다. 집에서 아침을 먹고 학교에 도착했다. 수업이 시작되었고, 나는 열공 모드로 들어갔다. 숙제도 다 끝냈고 이제 자야겠다."}
|
6 |
+
{"model_id": "240413_full_epoch_5", "task": "늘려쓰기", "instruction": "입력으로 주어진 글의 어투를 바꾸거나, 문장을 여러개로 나누어서 다시 써주세요.", "source": "오늘의 일기:\n오늘 나는 학교에 갔다. 집에 와서 공부를 했다. 오늘 숙제도 끝내고 자야겠다.", "generated": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}
|