sonsus commited on
Commit
c2ba4d5
1 Parent(s): 4674242
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +122 -0
  2. README_kr.md +123 -0
  3. __pycache__/analysis_utils.cpython-311.pyc +0 -0
  4. __pycache__/app.cpython-311.pyc +0 -0
  5. __pycache__/query_comp.cpython-311.pyc +0 -0
  6. __pycache__/view_utils.cpython-311.pyc +0 -0
  7. analysis_utils.py +338 -0
  8. app.py +452 -0
  9. eval_models_list.txt +3 -0
  10. eval_prompt_list.txt +3 -0
  11. guide_mds/input_jsonls_en.md +37 -0
  12. guide_mds/input_jsonls_kr.md +34 -0
  13. modules/__pycache__/nav.cpython-311.pyc +0 -0
  14. modules/nav.py +57 -0
  15. pages/__pycache__/see_results.cpython-311.pyc +0 -0
  16. pages/brief_intro.py +40 -0
  17. pages/quick_start_guide.py +14 -0
  18. pages/see_results.py +365 -0
  19. precommit.sh +5 -0
  20. query_comp.py +137 -0
  21. requirements.txt +14 -0
  22. run.sh +4 -0
  23. streamlit_app_local/.streamlit/config.toml +4 -0
  24. streamlit_app_local/README.md +24 -0
  25. streamlit_app_local/__pycache__/analysis_utils.cpython-311.pyc +0 -0
  26. streamlit_app_local/__pycache__/analysis_utils.cpython-38.pyc +0 -0
  27. streamlit_app_local/__pycache__/app.cpython-311.pyc +0 -0
  28. streamlit_app_local/__pycache__/app.cpython-38.pyc +0 -0
  29. streamlit_app_local/__pycache__/query_comp.cpython-311.pyc +0 -0
  30. streamlit_app_local/__pycache__/view_utils.cpython-311.pyc +0 -0
  31. streamlit_app_local/__pycache__/view_utils.cpython-38.pyc +0 -0
  32. streamlit_app_local/analysis_utils.py +338 -0
  33. streamlit_app_local/app.py +318 -0
  34. streamlit_app_local/eval_models_list.txt +3 -0
  35. streamlit_app_local/eval_prompt_list.txt +5 -0
  36. streamlit_app_local/guide_mds +1 -0
  37. streamlit_app_local/modules/__pycache__/nav.cpython-311.pyc +0 -0
  38. streamlit_app_local/modules/__pycache__/nav.cpython-38.pyc +0 -0
  39. streamlit_app_local/modules/nav.py +46 -0
  40. streamlit_app_local/pages/brief_intro.py +40 -0
  41. streamlit_app_local/pages/see_results.py +391 -0
  42. streamlit_app_local/query_comp.py +135 -0
  43. streamlit_app_local/requirements.txt +5 -0
  44. streamlit_app_local/run.sh +3 -0
  45. streamlit_app_local/user_submit/12-02_14:29:30/1.jsonl +6 -0
  46. streamlit_app_local/user_submit/12-02_14:29:30/2.jsonl +6 -0
  47. streamlit_app_local/user_submit/llm/240413_dora.jsonl +6 -0
  48. streamlit_app_local/user_submit/llm/240413_dora_epoch_5.jsonl +6 -0
  49. streamlit_app_local/user_submit/llm/240413_full.jsonl +6 -0
  50. 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": "오늘 나는 학교에 갔다 왔다. 집에 와서 숙제를 했다. 오늘 숙제를 끝내면 자야겠다."}